using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using EnhancedPing.Core; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using TMPro; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("EnhancedPing")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+7191e90729a136227fa75d774a7fa881c5a0faf1")] [assembly: AssemblyProduct("EnhancedPing")] [assembly: AssemblyTitle("EnhancedPing")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace EnhancedPing { internal sealed class EnhancedPingController { private readonly Plugin _plugin; private readonly ModConfig _config; private readonly ManualLogSource _logger; private readonly PathPreview _preview = new PathPreview(); private readonly PingDistanceOverlay _distanceOverlay; private readonly GhostReticleOverlay _ghostReticle = new GhostReticleOverlay(); private static readonly MethodInfo? ReceivePointMethod = AccessTools.Method(typeof(PointPinger), "ReceivePoint_Rpc", (Type[])null, (Type[])null); private PatchCapabilities _capabilities; private PathDrawingSession? _drawing; private PointPinger? _drawingPinger; private PointPinger? _drawingCarrier; private Coroutine? _sequence; private PointPinger? _sequencePinger; public EnhancedPingController(Plugin plugin, ModConfig config, ManualLogSource logger) { _plugin = plugin; _config = config; _logger = logger; _distanceOverlay = new PingDistanceOverlay(config, logger); } public void SetCapabilities(PatchCapabilities capabilities) { _capabilities = capabilities; } public void TickLifecycle() { if (!_config.Enabled.Value) { Reset("feature disabled"); return; } if (_drawing != null && ((Object)(object)_drawingPinger == (Object)null || (Object)(object)_drawingCarrier == (Object)null || (Object)(object)Character.localCharacter == (Object)null || !InputAllowed())) { CancelDrawing("input or character lifecycle ended"); } if (_sequence != null && ((Object)(object)_sequencePinger == (Object)null || (Object)(object)Character.localCharacter == (Object)null)) { CancelSequence(); } } public bool BeforePointPingerUpdate(PointPinger pinger, ref float timeLastPinged) { if (!_capabilities.Input || !_config.Enabled.Value || !IsLocalPinger(pinger)) { return true; } InputAction action_ping = CharacterInput.action_ping; if (action_ping == null) { return true; } if (_drawing != null) { return ContinueDrawing(pinger, action_ping, ref timeLastPinged); } if (!_config.PathEnabled.Value) { if (!_config.GhostEnabled.Value || (Object)(object)pinger.character?.data == (Object)null || !pinger.character.data.dead) { return true; } if (action_ping.WasPressedThisFrame() && CanStartEnhancedPing(pinger, timeLastPinged) && InputAllowed() && TryResolvePingCarrier(pinger, out PointPinger carrier) && TryGetPingSample(out var sample)) { timeLastPinged = Time.time; SendVanillaPing(carrier, sample); } return false; } if (!action_ping.WasPressedThisFrame()) { return true; } CancelSequence(); if (CanStartEnhancedPing(pinger, timeLastPinged) && InputAllowed() && TryResolvePingCarrier(pinger, out PointPinger carrier2)) { BeginDrawing(pinger, carrier2); } return false; } public void ShowDistanceLabel(PointPinger pinger, GameObject previousMarker, GameObject currentMarker) { if (_capabilities.Distance && !((Object)(object)pinger.character == (Object)null) && !((Object)(object)currentMarker == (Object)null) && previousMarker != currentMarker) { _distanceOverlay.Show(pinger.character, currentMarker); } } public void InitializeDistanceOverlay(GUIManager gui) { if (_config.Enabled.Value && _config.DistanceEnabled.Value) { _distanceOverlay.Initialize(gui); } } public void DestroyDistanceOverlay() { _distanceOverlay.Dispose(); _ghostReticle.Dispose(); } public void UpdateDeadReticle(GUIManager gui) { Character localCharacter = Character.localCharacter; bool visible = _capabilities.DeadReticle && (Object)(object)localCharacter != (Object)null && (Object)(object)localCharacter.data != (Object)null && localCharacter.data.dead && _config.Enabled.Value && _config.GhostEnabled.Value && _config.ShowReticleWhenDead.Value && Time.timeScale > 0f && !GUIManager.InPauseMenu && !gui.windowBlockingInput && !gui.wheelActive; _ghostReticle.Update(gui, visible); } public void Reset(string reason) { bool num = _drawing != null || _sequence != null; CancelDrawing(reason); CancelSequence(); _distanceOverlay.Dispose(); _ghostReticle.Dispose(); if (num) { _logger.LogDebug((object)("Enhanced ping state reset: " + reason + ".")); } } private bool ContinueDrawing(PointPinger pinger, InputAction pingAction, ref float timeLastPinged) { PointPinger drawingCarrier = _drawingCarrier; if ((Object)(object)pinger != (Object)(object)_drawingPinger || (Object)(object)drawingCarrier == (Object)null || !IsLivingCarrier(drawingCarrier) || !_config.PathEnabled.Value || !InputAllowed()) { CancelDrawing("path drawing cancelled"); return false; } CaptureDrawingSample(); if (pingAction.IsPressed() && !pingAction.WasReleasedThisFrame()) { return false; } PathDrawingResult pathDrawingResult = _drawing.Finish(_config.SafeMaximumPingPoints); CancelDrawing("path drawing completed"); if (pathDrawingResult.Samples.Count == 0) { return false; } timeLastPinged = Time.time; if (pathDrawingResult.TotalAngleDegrees < _config.SafeMinimumPathAngle || pathDrawingResult.Samples.Count < 2) { SendVanillaPing(drawingCarrier, pathDrawingResult.Samples[pathDrawingResult.Samples.Count - 1]); return false; } _sequencePinger = drawingCarrier; _sequence = ((MonoBehaviour)_plugin).StartCoroutine(SendSequence(drawingCarrier, pathDrawingResult.Samples)); return false; } private void BeginDrawing(PointPinger pinger, PointPinger carrier) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) CancelDrawing("new path started"); _drawing = new PathDrawingSession(); _drawingPinger = pinger; _drawingCarrier = carrier; if (_config.ShowPathPreview.Value) { _preview.Initialize(GetPlayerColor(carrier.character)); } CaptureDrawingSample(); } private void CaptureDrawingSample() { if (_drawing != null && TryGetPingSample(out var sample)) { _drawing.Capture(sample, _config.SafeMinimumCaptureAngle); if (_config.ShowPathPreview.Value) { _preview.Update(_drawing.PreviewSamples); } } } private IEnumerator SendSequence(PointPinger pinger, IReadOnlyList samples) { float num = SequenceTiming.EffectivePointDuration(samples.Count, _config.SafePreferredPointDuration, _config.SafeMaximumSequenceDuration); WaitForSeconds wait = new WaitForSeconds(num); for (int i = 0; i < samples.Count; i++) { if ((Object)(object)pinger == (Object)null) { break; } if (!IsLivingCarrier(pinger)) { break; } if (!_config.Enabled.Value) { break; } SendVanillaPing(pinger, samples[i]); if (i < samples.Count - 1) { yield return wait; } } _sequence = null; _sequencePinger = null; } private void SendVanillaPing(PointPinger pinger, RuntimePingSample sample) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) PhotonView component = ((Component)pinger).GetComponent(); if (!((Object)(object)component == (Object)null)) { if (pinger.character == Character.localCharacter) { component.RPC("ReceivePoint_Rpc", (RpcTarget)0, new object[2] { sample.Point, sample.Normal }); } else { component.RPC("ReceivePoint_Rpc", (RpcTarget)1, new object[2] { sample.Point, sample.Normal }); ShowPingLocally(pinger, sample); } } } private void ShowPingLocally(PointPinger pinger, RuntimePingSample sample) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) Character localCharacter = Character.localCharacter; try { Character.localCharacter = pinger.character; ReceivePointMethod?.Invoke(pinger, new object[2] { sample.Point, sample.Normal }); } catch (Exception arg) { _logger.LogWarning((object)$"Could not show an enhanced ping locally: {arg}"); } finally { Character.localCharacter = localCharacter; } } private bool CanStartEnhancedPing(PointPinger pinger, float timeLastPinged) { if (Time.time - timeLastPinged < pinger.coolDown || (Object)(object)pinger.character?.data == (Object)null) { return false; } if (!pinger.character.data.fullyConscious) { if (_config.GhostEnabled.Value) { return pinger.character.data.dead; } return false; } return true; } private static bool TryResolvePingCarrier(PointPinger source, out PointPinger carrier) { carrier = null; if ((Object)(object)source.character?.data == (Object)null) { return false; } if (!source.character.data.dead) { carrier = source; return IsLivingCarrier(carrier); } Character specCharacter = MainCameraMovement.specCharacter; if ((Object)(object)specCharacter == (Object)null || specCharacter == source.character || (Object)(object)specCharacter.data == (Object)null || specCharacter.data.dead) { return false; } PointPinger component = ((Component)specCharacter).GetComponent(); if (!IsLivingCarrier(component)) { return false; } carrier = component; return true; } private static bool IsLivingCarrier(PointPinger pinger) { if ((Object)(object)pinger == (Object)null || (Object)(object)pinger.character?.data == (Object)null || pinger.character.data.dead) { return false; } PhotonView component = ((Component)pinger).GetComponent(); if ((Object)(object)component != (Object)null) { return component.ViewID > 0; } return false; } private static bool IsLocalPinger(PointPinger pinger) { if ((Object)(object)pinger.character == (Object)null || (Object)(object)pinger.character != (Object)(object)Character.localCharacter) { return false; } PhotonView component = ((Component)pinger).GetComponent(); if ((Object)(object)component != (Object)null) { return component.IsMine; } return false; } private static bool InputAllowed() { if (Time.timeScale <= 0f || GUIManager.InPauseMenu) { return false; } GUIManager instance = GUIManager.instance; if (!((Object)(object)instance == (Object)null)) { if (!instance.windowBlockingInput) { return !instance.wheelActive; } return false; } return true; } private static bool TryGetPingSample(out RuntimePingSample sample) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) sample = default(RuntimePingSample); Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return false; } Ray val = main.ScreenPointToRay(Input.mousePosition); int num = LayerMask.op_Implicit(HelperFunctions.GetMask((LayerType)1)); RaycastHit val2 = default(RaycastHit); if (num == 0 || !Physics.Raycast(val, ref val2, float.PositiveInfinity, num)) { return false; } if (!IsFinite(((Ray)(ref val)).direction) || !IsFinite(((RaycastHit)(ref val2)).point) || !IsFinite(((RaycastHit)(ref val2)).normal)) { return false; } Vector3 direction = ((Ray)(ref val)).direction; sample = new RuntimePingSample(((Vector3)(ref direction)).normalized, ((RaycastHit)(ref val2)).point, ((RaycastHit)(ref val2)).normal); return true; } private void CancelDrawing(string reason) { if (_drawing != null) { _logger.LogDebug((object)("Path drawing ended: " + reason + ".")); } _drawing = null; _drawingPinger = null; _drawingCarrier = null; _preview.Dispose(); } private void CancelSequence() { if (_sequence != null) { ((MonoBehaviour)_plugin).StopCoroutine(_sequence); } _sequence = null; _sequencePinger = null; } private static Color GetPlayerColor(Character character) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) try { return character.refs.customization.PlayerColor; } catch { return Color.white; } } private static bool IsFinite(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (IsFinite(value.x) && IsFinite(value.y)) { return IsFinite(value.z); } return false; } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } } internal sealed class GhostReticleOverlay { private Canvas? _canvas; private Image? _image; private GUIManager? _gui; public void Update(GUIManager gui, bool visible) { if (!visible) { if ((Object)(object)_canvas != (Object)null && ((Component)_canvas).gameObject.activeSelf) { ((Component)_canvas).gameObject.SetActive(false); } return; } if ((Object)(object)_canvas == (Object)null || (Object)(object)_image == (Object)null || (Object)(object)_gui != (Object)(object)gui) { Initialize(gui); } if ((Object)(object)_canvas != (Object)null && !((Component)_canvas).gameObject.activeSelf) { ((Component)_canvas).gameObject.SetActive(true); } } public void Dispose() { if ((Object)(object)_canvas != (Object)null) { Object.Destroy((Object)(object)((Component)_canvas).gameObject); } _canvas = null; _image = null; _gui = null; } private void Initialize(GUIManager gui) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Expected O, but got Unknown //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) Dispose(); Image reticleDefaultImage = gui.reticleDefaultImage; if (!((Object)(object)reticleDefaultImage == (Object)null) && !((Object)(object)reticleDefaultImage.sprite == (Object)null)) { GameObject val = new GameObject("Canvas_EnhancedPingGhostReticle", new Type[2] { typeof(Canvas), typeof(CanvasScaler) }); val.transform.SetParent(((Component)gui).transform, false); val.layer = LayerMask.NameToLayer("UI"); _canvas = val.GetComponent(); _canvas.renderMode = (RenderMode)0; _canvas.sortingOrder = 101; CanvasScaler component = val.GetComponent(); component.uiScaleMode = (ScaleMode)1; component.referenceResolution = new Vector2(1920f, 1080f); component.screenMatchMode = (ScreenMatchMode)1; GameObject val2 = new GameObject("EnhancedPingGhostReticle", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); val2.transform.SetParent(((Component)_canvas).transform, false); RectTransform rectTransform = ((Graphic)reticleDefaultImage).rectTransform; RectTransform component2 = val2.GetComponent(); component2.anchorMin = new Vector2(0.5f, 0.5f); component2.anchorMax = new Vector2(0.5f, 0.5f); component2.pivot = rectTransform.pivot; component2.anchoredPosition = Vector2.zero; Rect rect = rectTransform.rect; component2.sizeDelta = ((Rect)(ref rect)).size; ((Transform)component2).localScale = ((Transform)rectTransform).localScale; _image = val2.GetComponent(); _image.sprite = reticleDefaultImage.sprite; ((Graphic)_image).material = ((Graphic)reticleDefaultImage).material; ((Graphic)_image).color = ((Graphic)reticleDefaultImage).color; _image.type = reticleDefaultImage.type; _image.preserveAspect = reticleDefaultImage.preserveAspect; ((Graphic)_image).raycastTarget = false; _gui = gui; } } } internal sealed class ModConfig { private const int DefaultMaximumPingPoints = 10; private const float DefaultMinimumCaptureAngle = 0.5f; private const float DefaultMinimumPathAngle = 1.5f; private const float DefaultPreferredPointDuration = 0.2f; private const float DefaultMaximumSequenceDuration = 2f; private const int DefaultFontSize = 24; public ConfigEntry Enabled { get; } public ConfigEntry DistanceEnabled { get; } public ConfigEntry DistanceDecimalPlaces { get; } public ConfigEntry DistanceFontSize { get; } public ConfigEntry GhostEnabled { get; } public ConfigEntry ShowReticleWhenDead { get; } public ConfigEntry PathEnabled { get; } public ConfigEntry MaximumPingPoints { get; } public ConfigEntry MinimumCaptureAngleDegrees { get; } public ConfigEntry MinimumPathAngleDegrees { get; } public ConfigEntry PreferredPointDurationSeconds { get; } public ConfigEntry MaximumSequenceDurationSeconds { get; } public ConfigEntry ShowPathPreview { get; } public int SafeDistanceDecimals => Mathf.Clamp(DistanceDecimalPlaces.Value, 0, 1); public int SafeDistanceFontSize => Mathf.Clamp(DistanceFontSize.Value, 10, 72); public int SafeMaximumPingPoints => Mathf.Clamp(MaximumPingPoints.Value, 2, 20); public float SafeMinimumCaptureAngle => SafeRange(MinimumCaptureAngleDegrees.Value, 0.05f, 5f, 0.5f); public float SafeMinimumPathAngle => SafeRange(MinimumPathAngleDegrees.Value, 0.1f, 30f, 1.5f); public float SafePreferredPointDuration => SafeRange(PreferredPointDurationSeconds.Value, 0.05f, 1f, 0.2f); public float SafeMaximumSequenceDuration => SafeRange(MaximumSequenceDurationSeconds.Value, 1f, 10f, 2f); public ModConfig(ConfigFile config) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Expected O, but got Unknown //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Expected O, but got Unknown //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Expected O, but got Unknown //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Expected O, but got Unknown Enabled = config.Bind("General", "Enabled", true, "Enable EnhancedPing features."); DistanceEnabled = config.Bind("Distance", "Enabled", true, "Show distance on visible ping markers."); DistanceDecimalPlaces = config.Bind("Distance", "DecimalPlaces", 0, new ConfigDescription("Number of decimal places used by distance labels.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 1), Array.Empty())); DistanceFontSize = config.Bind("Distance", "FontSize", 24, new ConfigDescription("Distance label font size.", (AcceptableValueBase)(object)new AcceptableValueRange(10, 72), Array.Empty())); GhostEnabled = config.Bind("Ghost", "Enabled", true, "Allow ping input while the local scout is dead."); ShowReticleWhenDead = config.Bind("Ghost", "ShowReticleWhenDead", true, "Show PEAK's default center reticle while the local scout is dead."); PathEnabled = config.Bind("Path", "Enabled", true, "Hold the rebound Ping action and move the aim to draw a path."); MaximumPingPoints = config.Bind("Path", "MaximumPingPoints", 10, new ConfigDescription("Maximum transmitted points, including the start and end.", (AcceptableValueBase)(object)new AcceptableValueRange(2, 20), Array.Empty())); MinimumCaptureAngleDegrees = config.Bind("Path", "MinimumCaptureAngleDegrees", 0.5f, new ConfigDescription("Minimum angular movement between captured aim rays.", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 5f), Array.Empty())); MinimumPathAngleDegrees = config.Bind("Path", "MinimumPathAngleDegrees", 1.5f, new ConfigDescription("Minimum cumulative angular movement required to classify a held Ping as a path.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 30f), Array.Empty())); PreferredPointDurationSeconds = config.Bind("Path", "PreferredPointDurationSeconds", 0.2f, new ConfigDescription("Preferred lifetime of each intermediate ping before the next replaces it.", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 1f), Array.Empty())); MaximumSequenceDurationSeconds = config.Bind("Path", "MaximumSequenceDurationSeconds", 2f, new ConfigDescription("Maximum time from the first path ping until the endpoint appears.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 10f), Array.Empty())); ShowPathPreview = config.Bind("Path", "ShowPreview", true, "Show a local line while drawing a path."); } private static float SafeRange(float value, float minimum, float maximum, float fallback) { if (float.IsNaN(value) || float.IsInfinity(value)) { return fallback; } return Mathf.Clamp(value, minimum, maximum); } } internal readonly struct PatchCapabilities { public bool Input { get; } public bool Distance { get; } public bool DeadReticle { get; } public PatchCapabilities(bool input, bool distance, bool deadReticle) { Input = input; Distance = distance; DeadReticle = deadReticle; } } internal static class PatchInstaller { public static PatchCapabilities Install(Harmony harmony, ManualLogSource logger) { bool input = TryPatch(harmony, AccessTools.Method(typeof(PointPinger), "Update", (Type[])null, (Type[])null), AccessTools.Method(typeof(PatchCallbacks), "PointPingerUpdatePrefix", (Type[])null, (Type[])null), null, "PointPinger.Update", logger); bool distance = TryPatch(harmony, AccessTools.Method(typeof(PointPinger), "ReceivePoint_Rpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(PatchCallbacks), "ReceivePointPrefix", (Type[])null, (Type[])null), AccessTools.Method(typeof(PatchCallbacks), "ReceivePointPostfix", (Type[])null, (Type[])null), "PointPinger.ReceivePoint_Rpc", logger); TryPatch(harmony, AccessTools.Method(typeof(GUIManager), "Start", (Type[])null, (Type[])null), null, AccessTools.Method(typeof(PatchCallbacks), "GUIManagerStartPostfix", (Type[])null, (Type[])null), "GUIManager.Start", logger); TryPatch(harmony, AccessTools.Method(typeof(GUIManager), "OnDestroy", (Type[])null, (Type[])null), AccessTools.Method(typeof(PatchCallbacks), "GUIManagerOnDestroyPrefix", (Type[])null, (Type[])null), null, "GUIManager.OnDestroy", logger); bool deadReticle = TryPatch(harmony, AccessTools.Method(typeof(GUIManager), "LateUpdate", (Type[])null, (Type[])null), null, AccessTools.Method(typeof(PatchCallbacks), "GUIManagerLateUpdatePostfix", (Type[])null, (Type[])null), "GUIManager.LateUpdate", logger); return new PatchCapabilities(input, distance, deadReticle); } private static bool TryPatch(Harmony harmony, MethodInfo? original, MethodInfo? prefix = null, MethodInfo? postfix = null, string? description = null, ManualLogSource? logger = null) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (original == null || (prefix == null && postfix == null)) { if (logger != null) { logger.LogError((object)("Required patch target is missing: " + description + ".")); } return false; } try { harmony.Patch((MethodBase)original, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(prefix) { priority = 800 }, (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(postfix) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception arg) { if (logger != null) { logger.LogError((object)$"Failed to patch {description}: {arg}"); } return false; } } } internal static class PatchCallbacks { public static bool PointPingerUpdatePrefix(PointPinger __instance, ref float ____timeLastPinged) { return (Plugin.Instance?.Controller)?.BeforePointPingerUpdate(__instance, ref ____timeLastPinged) ?? true; } public static void ReceivePointPrefix(ref GameObject ___pingInstance, out GameObject __state) { __state = ___pingInstance; } public static void ReceivePointPostfix(PointPinger __instance, GameObject __state, ref GameObject ___pingInstance) { Plugin.Instance?.Controller.ShowDistanceLabel(__instance, __state, ___pingInstance); } public static void GUIManagerStartPostfix(GUIManager __instance) { Plugin.Instance?.Controller.InitializeDistanceOverlay(__instance); } public static void GUIManagerOnDestroyPrefix(GUIManager __instance) { Plugin.Instance?.Controller.DestroyDistanceOverlay(); } public static void GUIManagerLateUpdatePostfix(GUIManager __instance) { Plugin.Instance?.Controller.UpdateDeadReticle(__instance); } } internal readonly struct PathDrawingResult { public IReadOnlyList Samples { get; } public float TotalAngleDegrees { get; } public PathDrawingResult(IReadOnlyList samples, float totalAngleDegrees) { Samples = samples; TotalAngleDegrees = totalAngleDegrees; } } internal sealed class PathDrawingSession { private readonly List _accepted = new List(); private RuntimePingSample _latest; private bool _hasLatest; public IReadOnlyList PreviewSamples { get { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (!_hasLatest || _accepted.Count == 0) { return _accepted; } if (Angle(_accepted[_accepted.Count - 1].Direction, _latest.Direction) <= 0.0001f) { return _accepted; } List list = new List(_accepted.Count + 1); list.AddRange(_accepted); list.Add(_latest); return list; } } public void Capture(RuntimePingSample sample, float minimumAngleDegrees) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) _latest = sample; _hasLatest = true; if (_accepted.Count == 0 || Angle(_accepted[_accepted.Count - 1].Direction, sample.Direction) >= minimumAngleDegrees) { _accepted.Add(sample); } } public PathDrawingResult Finish(int maximumPoints) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (_hasLatest && (_accepted.Count == 0 || Angle(_accepted[_accepted.Count - 1].Direction, _latest.Direction) > 0.0001f)) { _accepted.Add(_latest); } List list = new List(_accepted.Count); foreach (RuntimePingSample item in _accepted) { list.Add(item.ToCore()); } float totalAngleDegrees; IReadOnlyList readOnlyList = SphericalPathSelector.Select(list, maximumPoints, out totalAngleDegrees); List list2 = new List(readOnlyList.Count); foreach (SphericalPathSample item2 in readOnlyList) { list2.Add(RuntimePingSample.FromCore(item2)); } return new PathDrawingResult(list2, totalAngleDegrees); } private static float Angle(Vector3 left, Vector3 right) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (((Vector3)(ref left)).sqrMagnitude <= 1E-08f || ((Vector3)(ref right)).sqrMagnitude <= 1E-08f) { return float.NaN; } return Vector3.Angle(left, right); } } internal sealed class PathPreview { private GameObject? _gameObject; private LineRenderer? _line; private Material? _material; public void Initialize(Color color) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) Dispose(); Shader val = Shader.Find("Sprites/Default"); if (!((Object)(object)val == (Object)null)) { _gameObject = new GameObject("EnhancedPing.PathPreview"); ((Object)_gameObject).hideFlags = (HideFlags)61; _line = _gameObject.AddComponent(); _material = new Material(val); ((Renderer)_line).material = _material; _line.useWorldSpace = true; _line.alignment = (LineAlignment)0; _line.widthMultiplier = 0.045f; _line.numCapVertices = 4; _line.numCornerVertices = 2; color.a = 0.9f; _line.startColor = color; _line.endColor = color; _line.positionCount = 0; } } public void Update(IReadOnlyList samples) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_line == (Object)null) { return; } _line.positionCount = samples.Count; for (int i = 0; i < samples.Count; i++) { Vector3 normal = samples[i].Normal; Vector3 val; if (!(((Vector3)(ref normal)).sqrMagnitude > 1E-08f)) { val = Vector3.up; } else { normal = samples[i].Normal; val = ((Vector3)(ref normal)).normalized; } Vector3 val2 = val; _line.SetPosition(i, samples[i].Point + val2 * 0.035f); } } public void Dispose() { if ((Object)(object)_gameObject != (Object)null) { Object.Destroy((Object)(object)_gameObject); } if ((Object)(object)_material != (Object)null) { Object.Destroy((Object)(object)_material); } _gameObject = null; _line = null; _material = null; } } internal sealed class PingDistanceOverlay { private readonly ModConfig _config; private readonly ManualLogSource _logger; private readonly Dictionary _instances = new Dictionary(); private Canvas? _canvas; private GameObject? _template; public PingDistanceOverlay(ModConfig config, ManualLogSource logger) { _config = config; _logger = logger; } public void Initialize(GUIManager gui) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Expected O, but got Unknown if ((Object)(object)_canvas != (Object)null && (Object)(object)_template != (Object)null && (Object)(object)((Component)_canvas).transform.parent == (Object)(object)((Component)gui).transform) { return; } Dispose(); TextMeshProUGUI interactNameText = gui.interactNameText; if ((Object)(object)interactNameText == (Object)null || (Object)(object)((TMP_Text)interactNameText).font == (Object)null || (Object)(object)((TMP_Text)interactNameText).fontSharedMaterial == (Object)null) { _logger.LogError((object)"Could not initialize distance labels from GUIManager.interactNameText."); return; } try { GameObject val = new GameObject("Canvas_EnhancedPingDistance", new Type[2] { typeof(Canvas), typeof(CanvasScaler) }); val.transform.SetParent(((Component)gui).transform, false); val.layer = LayerMask.NameToLayer("UI"); _canvas = val.GetComponent(); _canvas.renderMode = (RenderMode)0; _canvas.sortingOrder = 100; CanvasScaler component = val.GetComponent(); component.uiScaleMode = (ScaleMode)1; component.referenceResolution = new Vector2(1920f, 1080f); component.screenMatchMode = (ScreenMatchMode)1; _template = new GameObject("EnhancedPingDistanceIndicator", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(PingDistanceLabel) }); _template.transform.SetParent(((Component)_canvas).transform, false); CreateText(_template.transform, ((TMP_Text)interactNameText).font, ((TMP_Text)interactNameText).fontSharedMaterial); _template.SetActive(false); _logger.LogInfo((object)"Distance label overlay initialized from GUIManager.interactNameText."); } catch (Exception arg) { _logger.LogWarning((object)$"Could not initialize the ping distance overlay: {arg}"); Dispose(); } } public void Show(Character character, GameObject marker) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) if (!_config.Enabled.Value || !_config.DistanceEnabled.Value || (Object)(object)character == (Object)null || (Object)(object)marker == (Object)null) { return; } if ((Object)(object)_canvas == (Object)null || (Object)(object)_template == (Object)null) { GUIManager instance = GUIManager.instance; if ((Object)(object)instance != (Object)null) { Initialize(instance); } } if ((Object)(object)_canvas == (Object)null || (Object)(object)_template == (Object)null) { return; } if (_instances.TryGetValue(character, out GameObject value) && (Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } try { PointPing component = marker.GetComponent(); Vector3 normal = (((Object)(object)component != (Object)null && ((Vector3)(ref component.hitNormal)).sqrMagnitude > 1E-08f) ? ((Vector3)(ref component.hitNormal)).normalized : Vector3.up); GameObject val = Object.Instantiate(_template, ((Component)_canvas).transform); val.GetComponent().Initialize(marker, normal, GetPlayerColor(character), _config); val.SetActive(true); _instances[character] = val; } catch (Exception arg) { _logger.LogWarning((object)$"Could not create a ping distance label: {arg}"); } } public void Dispose() { if ((Object)(object)_canvas != (Object)null) { Object.Destroy((Object)(object)((Component)_canvas).gameObject); } _canvas = null; _template = null; _instances.Clear(); } private static void CreateText(Transform parent, TMP_FontAsset font, Material material) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("DistanceText", new Type[4] { typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI), typeof(Shadow) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent(); component.anchorMin = new Vector2(0.5f, 0.5f); component.anchorMax = new Vector2(0.5f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); component.sizeDelta = new Vector2(160f, 50f); TextMeshProUGUI component2 = val.GetComponent(); ((TMP_Text)component2).font = font; ((TMP_Text)component2).fontMaterial = material; ((Graphic)component2).color = Color.white; ((TMP_Text)component2).alignment = (TextAlignmentOptions)514; ((TMP_Text)component2).enableAutoSizing = false; ((Graphic)component2).raycastTarget = false; ((TMP_Text)component2).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)component2).outlineWidth = 0.1f; ((TMP_Text)component2).outlineColor = Color32.op_Implicit(Color.black); Shadow component3 = val.GetComponent(); component3.effectColor = new Color(0f, 0f, 0f, 0.95f); component3.effectDistance = new Vector2(2f, -2f); } private static Color GetPlayerColor(Character character) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) try { return character.refs.customization.PlayerColor; } catch { return Color.white; } } } internal sealed class PingDistanceLabel : MonoBehaviour { private GameObject? _marker; private Vector3 _normal; private Color _color; private ModConfig? _config; private TextMeshProUGUI? _text; public void Initialize(GameObject marker, Vector3 normal, Color color, ModConfig config) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) _marker = marker; _normal = normal; _color = color; _config = config; _text = ((Component)this).GetComponentInChildren(true); } private void LateUpdate() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_marker == (Object)null) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Camera main = Camera.main; if (_config == null || !_config.Enabled.Value || !_config.DistanceEnabled.Value || (Object)(object)main == (Object)null || (Object)(object)_text == (Object)null) { SetTextVisible(visible: false); return; } Vector3 position = _marker.transform.position; Vector3 val = main.WorldToScreenPoint(position + _normal); if (val.z <= 0f) { SetTextVisible(visible: false); return; } float num = Vector3.Distance(position, ((Component)main).transform.position); float num2 = Mathf.Clamp(20f / Mathf.Max(num, 0.01f), 0.5f, 1.5f); ((Component)this).transform.position = val; ((Component)this).transform.localScale = new Vector3(num2, num2, num2); int safeDistanceDecimals = _config.SafeDistanceDecimals; ((TMP_Text)_text).fontSize = _config.SafeDistanceFontSize; ((Graphic)_text).color = _color; ((TMP_Text)_text).text = num.ToString((safeDistanceDecimals == 0) ? "0" : "0.0", CultureInfo.InvariantCulture) + "m"; SetTextVisible(visible: true); } private void SetTextVisible(bool visible) { if ((Object)(object)_text != (Object)null && ((Component)_text).gameObject.activeSelf != visible) { ((Component)_text).gameObject.SetActive(visible); } } } [BepInPlugin("com.github.lllei.EnhancedPing", "EnhancedPing", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.github.lllei.EnhancedPing"; public const string PluginName = "EnhancedPing"; private Harmony? _harmony; internal static Plugin? Instance { get; private set; } internal EnhancedPingController Controller { get; private set; } private void Awake() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown Instance = this; ModConfig config = new ModConfig(((BaseUnityPlugin)this).Config); Controller = new EnhancedPingController(this, config, ((BaseUnityPlugin)this).Logger); _harmony = new Harmony("com.github.lllei.EnhancedPing"); PatchCapabilities capabilities = PatchInstaller.Install(_harmony, ((BaseUnityPlugin)this).Logger); Controller.SetCapabilities(capabilities); ((BaseUnityPlugin)this).Logger.LogInfo((object)("EnhancedPing 1.0.0 loaded for PEAK 2.0.a baseline. " + $"Input: {capabilities.Input}; distance: {capabilities.Distance}; " + $"dead reticle: {capabilities.DeadReticle}.")); } private void Update() { Controller?.TickLifecycle(); } private void OnDestroy() { Controller?.Reset("plugin destroyed"); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } Instance = null; } } internal readonly struct RuntimePingSample { public Vector3 Direction { get; } public Vector3 Point { get; } public Vector3 Normal { get; } public RuntimePingSample(Vector3 direction, Vector3 point, Vector3 normal) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) Direction = direction; Point = point; Normal = normal; } public SphericalPathSample ToCore() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) return new SphericalPathSample(ToCore(Direction), ToCore(Point), ToCore(Normal)); } public static RuntimePingSample FromCore(SphericalPathSample sample) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) return new RuntimePingSample(ToUnity(sample.Direction), ToUnity(sample.Point), ToUnity(sample.Normal)); } private static Vector3 ToCore(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) return new Vector3(value.x, value.y, value.z); } private static Vector3 ToUnity(Vector3 value) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Vector3(value.X, value.Y, value.Z); } } internal static class BuildInfo { internal const string Version = "1.0.0"; } } namespace EnhancedPing.Core { public static class SequenceTiming { public static float EffectivePointDuration(int pointCount, float preferredPointDurationSeconds, float maximumSequenceDurationSeconds) { if (pointCount <= 0) { throw new ArgumentOutOfRangeException("pointCount"); } if (!IsFinite(preferredPointDurationSeconds) || preferredPointDurationSeconds <= 0f) { throw new ArgumentOutOfRangeException("preferredPointDurationSeconds"); } if (!IsFinite(maximumSequenceDurationSeconds) || maximumSequenceDurationSeconds <= 0f) { throw new ArgumentOutOfRangeException("maximumSequenceDurationSeconds"); } return MathF.Min(preferredPointDurationSeconds, maximumSequenceDurationSeconds / (float)pointCount); } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } } public readonly struct SphericalPathSample { public Vector3 Direction { get; } public Vector3 Point { get; } public Vector3 Normal { get; } public SphericalPathSample(Vector3 direction, Vector3 point, Vector3 normal) { Direction = direction; Point = point; Normal = normal; } } public static class SphericalPathSelector { private const float DirectionEpsilonSquared = 1E-10f; private const float DuplicateAngleDegrees = 0.0001f; private const float RadToDeg = 180f / MathF.PI; public static float AngleDegrees(Vector3 left, Vector3 right) { if (!TryNormalize(left, out var normalized) || !TryNormalize(right, out var normalized2)) { return float.NaN; } return MathF.Acos(MathF.Max(-1f, MathF.Min(1f, Vector3.Dot(normalized, normalized2)))) * (180f / MathF.PI); } public static IReadOnlyList Select(IReadOnlyList samples, int maximumPoints) { float totalAngleDegrees; return Select(samples, maximumPoints, out totalAngleDegrees); } public static IReadOnlyList Select(IReadOnlyList samples, int maximumPoints, out float totalAngleDegrees) { if (samples == null) { throw new ArgumentNullException("samples"); } if (maximumPoints < 2) { throw new ArgumentOutOfRangeException("maximumPoints"); } List list = Sanitize(samples); float[] array = BuildCumulativeAngles(list); totalAngleDegrees = ((array.Length == 0) ? 0f : array[^1]); if (list.Count <= maximumPoints) { return list; } if (totalAngleDegrees <= 0.0001f) { return new SphericalPathSample[1] { list[0] }; } int num = Math.Min(list.Count, maximumPoints); List list2 = new List(num) { list[0] }; int num2 = 0; for (int i = 1; i < num - 1; i++) { float num3 = totalAngleDegrees * (float)i / (float)(num - 1); int num4 = num2 + 1; int num5 = list.Count - (num - i); int num6 = num4; float num7 = MathF.Abs(array[num6] - num3); for (int j = num4 + 1; j <= num5; j++) { float num8 = MathF.Abs(array[j] - num3); if (num8 < num7) { num7 = num8; num6 = j; } } list2.Add(list[num6]); num2 = num6; } list2.Add(list[list.Count - 1]); return list2; } private static float[] BuildCumulativeAngles(IReadOnlyList samples) { float[] array = new float[samples.Count]; for (int i = 1; i < samples.Count; i++) { float num = AngleDegrees(samples[i - 1].Direction, samples[i].Direction); array[i] = array[i - 1] + (IsFinite(num) ? num : 0f); } return array; } private static List Sanitize(IReadOnlyList samples) { List list = new List(samples.Count); foreach (SphericalPathSample sample in samples) { if (TryNormalize(sample.Direction, out var normalized) && IsFinite(sample.Point) && IsFinite(sample.Normal)) { Vector3 normalized2; Vector3 normal = (TryNormalize(sample.Normal, out normalized2) ? normalized2 : Vector3.UnitY); SphericalPathSample item = new SphericalPathSample(normalized, sample.Point, normal); if (list.Count <= 0 || !(AngleDegrees(list[list.Count - 1].Direction, normalized) <= 0.0001f)) { list.Add(item); } } } return list; } private static bool TryNormalize(Vector3 value, out Vector3 normalized) { float num = value.LengthSquared(); if (!IsFinite(num) || num <= 1E-10f) { normalized = Vector3.Zero; return false; } normalized = value / MathF.Sqrt(num); return IsFinite(normalized); } private static bool IsFinite(Vector3 value) { if (IsFinite(value.X) && IsFinite(value.Y)) { return IsFinite(value.Z); } return false; } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } } }