using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FistVR;
using HarmonyLib;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")]
[assembly: AssemblyCompany("SosigOptimizer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SosigOptimizer")]
[assembly: AssemblyTitle("SosigOptimizer")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace SosigOptimizer;
public class GibMarker : MonoBehaviour
{
public static readonly List<GibMarker> All = new List<GibMarker>();
private void Awake()
{
All.Add(this);
}
private void OnDestroy()
{
All.Remove(this);
}
}
public class SosigLODController : MonoBehaviour
{
internal Sosig _sosig;
internal bool _lodActive;
private float _nextTickTime;
private float _avoidanceTimer;
private bool _avoidanceOff;
private int[] _origSolverIter;
private int[] _origSolverVelIter;
private bool[] _origProjection;
private RigidbodyInterpolation[] _origInterp;
private float[] _origSleepThreshold;
private ObstacleAvoidanceType _origAvoidance;
private ShadowCastingMode[] _origShadows;
private static readonly FieldInfo _navTickField = typeof(Sosig).GetField("m_navAgentUpdateTick", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly bool _navTickFieldFound = (object)typeof(Sosig).GetField("m_navAgentUpdateTick", BindingFlags.Instance | BindingFlags.NonPublic) != null;
public static readonly List<SosigLODController> AllControllers = new List<SosigLODController>();
private void Start()
{
//IL_0120: 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)
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
//IL_00c2: Expected I4, but got Unknown
//IL_017c: Unknown result type (might be due to invalid IL or missing references)
//IL_0182: Expected I4, but got Unknown
_sosig = ((Component)this).GetComponent<Sosig>();
int count = _sosig.Links.Count;
_origSolverIter = new int[count];
_origSolverVelIter = new int[count];
_origProjection = new bool[count];
_origInterp = (RigidbodyInterpolation[])(object)new RigidbodyInterpolation[count];
_origSleepThreshold = new float[count];
for (int i = 0; i < count; i++)
{
SosigLink val = _sosig.Links[i];
if (!((Object)(object)val == (Object)null))
{
if ((Object)(object)val.R != (Object)null)
{
_origSolverIter[i] = val.R.solverIterations;
_origSolverVelIter[i] = val.R.solverVelocityIterations;
_origInterp[i] = (RigidbodyInterpolation)(int)val.R.interpolation;
_origSleepThreshold[i] = val.R.sleepThreshold;
}
if ((Object)(object)val.J != (Object)null)
{
_origProjection[i] = val.J.enableProjection;
}
}
}
if ((Object)(object)_sosig.Agent != (Object)null)
{
_origAvoidance = _sosig.Agent.obstacleAvoidanceType;
}
if (_sosig.Renderers != null)
{
_origShadows = (ShadowCastingMode[])(object)new ShadowCastingMode[_sosig.Renderers.Length];
for (int j = 0; j < _sosig.Renderers.Length; j++)
{
if ((Object)(object)_sosig.Renderers[j] != (Object)null)
{
_origShadows[j] = (ShadowCastingMode)(int)_sosig.Renderers[j].shadowCastingMode;
}
}
}
_nextTickTime = Time.time + Random.Range(0f, SosigOptimizerPlugin.CfgLODInterval.Value);
AllControllers.Add(this);
}
private void Update()
{
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
if (Time.time >= _nextTickTime)
{
_nextTickTime = Time.time + SosigOptimizerPlugin.CfgLODInterval.Value;
if (SosigOptimizerPlugin.CfgLODEnabled.Value)
{
Tick();
}
else if (_lodActive)
{
RestoreLOD();
}
}
if (_lodActive && _avoidanceTimer > 0f && SosigOptimizerPlugin.CfgLODPulseAvoidance.Value && (Object)(object)_sosig?.Agent != (Object)null)
{
_avoidanceTimer -= Time.deltaTime;
if (_avoidanceTimer <= 0f)
{
_avoidanceOff = !_avoidanceOff;
_sosig.Agent.obstacleAvoidanceType = (ObstacleAvoidanceType)((!_avoidanceOff) ? ((int)_origAvoidance) : 0);
_avoidanceTimer = (_avoidanceOff ? SosigOptimizerPlugin.CfgLODAvoidanceOffSecs.Value : SosigOptimizerPlugin.CfgLODAvoidanceOnSecs.Value);
}
}
}
private bool IsObservedByCamera()
{
//IL_00e9: 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_00fe: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_0107: Unknown result type (might be due to invalid IL or missing references)
//IL_010f: Unknown result type (might be due to invalid IL or missing references)
//IL_0114: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
try
{
Camera val = null;
Camera[] allCameras = Camera.allCameras;
foreach (Camera val2 in allCameras)
{
if ((Object)(object)val2 == (Object)null)
{
continue;
}
if (val2.stereoEnabled)
{
if ((Object)(object)val == (Object)null)
{
val = val2;
}
}
else if ((Object)(object)val2.targetTexture != (Object)null)
{
Bounds val3 = (Bounds)((_sosig.Renderers != null && _sosig.Renderers.Length != 0 && (Object)(object)_sosig.Renderers[0] != (Object)null) ? _sosig.Renderers[0].bounds : new Bounds(((Component)this).transform.position, Vector3.one * 2f));
if (GeometryUtility.TestPlanesAABB(GeometryUtility.CalculateFrustumPlanes(val2), val3))
{
return true;
}
}
}
if ((Object)(object)val != (Object)null && SosigOptimizerPlugin.IsScopeActive())
{
Vector3 val4 = ((Component)_sosig).transform.position - ((Component)val).transform.position;
Vector3 normalized = ((Vector3)(ref val4)).normalized;
if (Vector3.Angle(((Component)val).transform.forward, normalized) < 15f)
{
return true;
}
}
return false;
}
catch
{
return false;
}
}
private void Tick()
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Invalid comparison between Unknown and I4
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
if ((int)_sosig.BodyState == 3)
{
if (_lodActive)
{
RestoreLOD();
}
}
else
{
if ((Object)(object)GM.CurrentPlayerBody == (Object)null)
{
return;
}
if (IsObservedByCamera())
{
if (_lodActive)
{
SosigOptimizerPlugin.Log.LogInfo((object)("[SosigLOD] RESTORE (observed) " + ((Object)_sosig).name));
RestoreLOD();
}
return;
}
float num = Vector3.Distance(((Component)this).transform.position, ((Component)GM.CurrentPlayerBody).transform.position);
if (!_lodActive && num > SosigOptimizerPlugin.CfgLODFarDist.Value)
{
SosigOptimizerPlugin.Log.LogInfo((object)$"[SosigLOD] LOD ON {((Object)_sosig).name} @ {num:F0}m");
ApplyLOD();
}
else if (_lodActive && num < SosigOptimizerPlugin.CfgLODNearDist.Value)
{
SosigOptimizerPlugin.Log.LogInfo((object)$"[SosigLOD] LOD OFF {((Object)_sosig).name} @ {num:F0}m");
RestoreLOD();
}
else if (_lodActive)
{
BumpNavTick();
ClearSleepThresholdIfH3MP();
}
}
}
private void ApplyLOD()
{
int solverIterations = Mathf.Max(1, SosigOptimizerPlugin.CfgLODSolverIter.Value);
for (int i = 0; i < _sosig.Links.Count; i++)
{
SosigLink val = _sosig.Links[i];
if ((Object)(object)val == (Object)null)
{
continue;
}
if ((Object)(object)val.R != (Object)null)
{
if (SosigOptimizerPlugin.CfgLODReducePhysics.Value)
{
val.R.solverIterations = solverIterations;
val.R.solverVelocityIterations = 1;
}
if (SosigOptimizerPlugin.CfgLODReduceInterp.Value)
{
val.R.interpolation = (RigidbodyInterpolation)0;
}
if (SosigOptimizerPlugin.CfgLODRaiseSleepThreshold.Value && !SosigOptimizerPlugin.IsH3MPSessionActive())
{
val.R.sleepThreshold = SosigOptimizerPlugin.CfgLODSleepThreshold.Value;
}
}
if ((Object)(object)val.J != (Object)null && SosigOptimizerPlugin.CfgLODReducePhysics.Value)
{
val.J.enableProjection = false;
}
}
if (SosigOptimizerPlugin.CfgLODPulseAvoidance.Value && (Object)(object)_sosig.Agent != (Object)null)
{
_sosig.Agent.obstacleAvoidanceType = (ObstacleAvoidanceType)0;
_avoidanceOff = true;
_avoidanceTimer = SosigOptimizerPlugin.CfgLODAvoidanceOffSecs.Value;
}
BumpNavTick();
if (SosigOptimizerPlugin.CfgLODHideShadows.Value && _sosig.Renderers != null)
{
Renderer[] renderers = _sosig.Renderers;
foreach (Renderer val2 in renderers)
{
if ((Object)(object)val2 != (Object)null)
{
val2.shadowCastingMode = (ShadowCastingMode)0;
}
}
}
_lodActive = true;
}
private void RestoreLOD()
{
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
_avoidanceTimer = 0f;
for (int i = 0; i < _sosig.Links.Count; i++)
{
SosigLink val = _sosig.Links[i];
if (!((Object)(object)val == (Object)null))
{
if ((Object)(object)val.R != (Object)null && i < _origSolverIter.Length)
{
val.R.solverIterations = _origSolverIter[i];
val.R.solverVelocityIterations = _origSolverVelIter[i];
val.R.interpolation = _origInterp[i];
val.R.sleepThreshold = _origSleepThreshold[i];
val.R.WakeUp();
}
if ((Object)(object)val.J != (Object)null && i < _origProjection.Length)
{
val.J.enableProjection = _origProjection[i];
}
}
}
if ((Object)(object)_sosig.Agent != (Object)null)
{
_sosig.Agent.obstacleAvoidanceType = _origAvoidance;
}
if (_sosig.Renderers != null && _origShadows != null)
{
for (int j = 0; j < _sosig.Renderers.Length; j++)
{
if ((Object)(object)_sosig.Renderers[j] != (Object)null && j < _origShadows.Length)
{
_sosig.Renderers[j].shadowCastingMode = _origShadows[j];
}
}
}
_lodActive = false;
}
private void BumpNavTick()
{
if (SosigOptimizerPlugin.CfgLODReduceNavTick.Value && _navTickFieldFound)
{
_navTickField.SetValue(_sosig, SosigOptimizerPlugin.CfgLODNavTickSecs.Value);
}
}
private void ClearSleepThresholdIfH3MP()
{
if (!SosigOptimizerPlugin.CfgLODRaiseSleepThreshold.Value || !SosigOptimizerPlugin.IsH3MPSessionActive())
{
return;
}
for (int i = 0; i < _sosig.Links.Count && i < _origSleepThreshold.Length; i++)
{
SosigLink val = _sosig.Links[i];
if (!((Object)(object)val == (Object)null) && !((Object)(object)val.R == (Object)null) && val.R.sleepThreshold != _origSleepThreshold[i])
{
val.R.sleepThreshold = _origSleepThreshold[i];
val.R.WakeUp();
}
}
}
private void OnDestroy()
{
if (_lodActive && (Object)(object)_sosig != (Object)null)
{
RestoreLOD();
}
AllControllers.Remove(this);
}
}
[BepInPlugin("h3vr.invent60.sosigoptimizer", "SosigOptimizer", "1.0.3")]
public class SosigOptimizerPlugin : BaseUnityPlugin
{
[CompilerGenerated]
private sealed class <AttachRadar>d__40 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public SosigOptimizerPlugin <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <AttachRadar>d__40(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
int num = <>1__state;
SosigOptimizerPlugin sosigOptimizerPlugin = <>4__this;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = null;
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
((Component)sosigOptimizerPlugin).gameObject.AddComponent<SosigRadar>();
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <DestroyLinkAfterDelay>d__39 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public float delay;
public SosigLink link;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <DestroyLinkAfterDelay>d__39(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = (object)new WaitForSeconds(delay);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
if ((Object)(object)link == (Object)null)
{
return false;
}
if ((Object)(object)link.O != (Object)null && ((FVRInteractiveObject)link.O).IsHeld)
{
return false;
}
Object.Destroy((Object)(object)((Component)link).gameObject);
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <EnforceGibCapNextFrame>d__38 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <EnforceGibCapNextFrame>d__38(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = null;
<>1__state = 1;
return true;
case 1:
{
<>1__state = -1;
_gibCapPending = false;
int value = CfgGibMax.Value;
if (value <= 0)
{
return false;
}
GibMarker[] array = GibMarker.All.ToArray();
if (array.Length <= value)
{
return false;
}
int num = array.Length - value;
for (int i = 0; i < num; i++)
{
int num2 = Random.Range(i, array.Length);
GibMarker gibMarker = array[i];
array[i] = array[num2];
array[num2] = gibMarker;
}
for (int j = 0; j < num; j++)
{
GibMarker gibMarker2 = array[j];
if (!((Object)(object)gibMarker2 == (Object)null) && !((Object)(object)((Component)gibMarker2).GetComponentInParent<FVRViveHand>() != (Object)null) && !((Object)(object)((Component)gibMarker2).GetComponentInParent<FVRPlayerBody>() != (Object)null) && !((Object)(object)((Component)gibMarker2).GetComponentInParent<FVRPlayerHitbox>() != (Object)null))
{
FVRInteractiveObject componentInParent = ((Component)gibMarker2).GetComponentInParent<FVRInteractiveObject>();
if (!((Object)(object)componentInParent != (Object)null) || !((Object)(object)componentInParent.m_hand != (Object)null))
{
Object.Destroy((Object)(object)((Component)gibMarker2).gameObject);
}
}
}
Log.LogInfo((object)("[SosigOptimizer] Gib cap: removed " + num + " of " + array.Length + "."));
return false;
}
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
internal static ManualLogSource Log;
internal static SosigOptimizerPlugin Instance;
internal static ConfigEntry<bool> CfgLODEnabled;
internal static ConfigEntry<float> CfgLODInterval;
internal static ConfigEntry<float> CfgLODNearDist;
internal static ConfigEntry<float> CfgLODFarDist;
internal static ConfigEntry<bool> CfgLODReducePhysics;
internal static ConfigEntry<int> CfgLODSolverIter;
internal static ConfigEntry<bool> CfgLODReduceInterp;
internal static ConfigEntry<bool> CfgLODRaiseSleepThreshold;
internal static ConfigEntry<float> CfgLODSleepThreshold;
internal static ConfigEntry<bool> CfgLODReduceNavTick;
internal static ConfigEntry<float> CfgLODNavTickSecs;
internal static ConfigEntry<bool> CfgLODPulseAvoidance;
internal static ConfigEntry<float> CfgLODAvoidanceOffSecs;
internal static ConfigEntry<float> CfgLODAvoidanceOnSecs;
internal static ConfigEntry<bool> CfgLODHideShadows;
internal static ConfigEntry<bool> CfgDebugRadar;
internal static ConfigEntry<bool> CfgGibEnabled;
internal static ConfigEntry<float> CfgGibLinkLifetime;
internal static ConfigEntry<float> CfgGibLifetime;
internal static ConfigEntry<int> CfgGibMax;
internal static ConfigEntry<float> CfgGibTagRadius;
private static Type _h3mpGMType;
private static PropertyInfo _h3mpSingletonProp;
private static bool _h3mpInitDone;
private static Type _pipControllerType;
private static MethodInfo _getActiveScopeMethod;
private static Object _cachedController;
private static float _controllerRefreshTime;
private static bool _scopeReflectionDone;
private static bool _gibCapPending;
private static void EnsureH3MPInit()
{
if (_h3mpInitDone)
{
return;
}
_h3mpInitDone = true;
try
{
_h3mpGMType = AccessTools.TypeByName("H3MP.GameManager");
if ((object)_h3mpGMType != null)
{
_h3mpSingletonProp = _h3mpGMType.GetProperty("singleton", BindingFlags.Static | BindingFlags.Public);
}
}
catch
{
}
Log.LogInfo((object)("[SosigOptimizer] H3MP: " + (((object)_h3mpGMType != null) ? "detected" : "not present")));
}
internal static bool IsH3MPSessionActive()
{
EnsureH3MPInit();
if ((object)_h3mpGMType == null || (object)_h3mpSingletonProp == null)
{
return false;
}
try
{
return _h3mpSingletonProp.GetValue(null, null) != null;
}
catch
{
return false;
}
}
internal static void InitScopeReflection()
{
if (_scopeReflectionDone)
{
return;
}
_scopeReflectionDone = true;
try
{
_pipControllerType = AccessTools.TypeByName("PIPScope");
if ((object)_pipControllerType != null)
{
_getActiveScopeMethod = _pipControllerType.GetMethod("GetActiveScope", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
}
}
catch (Exception ex)
{
Log.LogError((object)("[SosigOptimizer] scope reflection error: " + ex.Message));
}
Log.LogInfo((object)("[SosigOptimizer] PIPScope: " + (((object)_pipControllerType != null) ? "found" : "not found")));
Log.LogInfo((object)("[SosigOptimizer] GetActiveScope: " + (((object)_getActiveScopeMethod != null) ? "found" : "not found")));
}
internal static bool IsScopeActive()
{
if ((object)_pipControllerType == null || (object)_getActiveScopeMethod == null)
{
return false;
}
try
{
object obj;
if (_getActiveScopeMethod.IsStatic)
{
obj = null;
}
else
{
if (Time.time > _controllerRefreshTime + 1f)
{
_cachedController = Object.FindObjectOfType(_pipControllerType);
_controllerRefreshTime = Time.time;
}
if (_cachedController == null)
{
return false;
}
obj = _cachedController;
}
return _getActiveScopeMethod.Invoke(obj, null) != null;
}
catch
{
return false;
}
}
private void Awake()
{
//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
Log = ((BaseUnityPlugin)this).Logger;
Instance = this;
CfgLODEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("LOD - General", "Enabled", true, "Enable distance-based LOD for sosigs. Reduces CPU/GPU cost for distant enemies.");
CfgLODInterval = ((BaseUnityPlugin)this).Config.Bind<float>("LOD - General", "Check interval seconds", 0.5f, "How often each sosig checks its distance from the player. (0.1 to 5)");
CfgLODNearDist = ((BaseUnityPlugin)this).Config.Bind<float>("LOD - General", "Near distance meters (LOD OFF)", 150f, "LOD deactivates when a sosig comes closer than this. Must be less than Far distance. (5 to 500)");
CfgLODFarDist = ((BaseUnityPlugin)this).Config.Bind<float>("LOD - General", "Far distance meters (LOD ON)", 150f, "LOD activates when a sosig exceeds this distance. (10 to 2000)");
CfgLODReducePhysics = ((BaseUnityPlugin)this).Config.Bind<bool>("LOD - Physics", "Reduce physics solver iterations", true, "PERF: HIGH. Lowers joint solver iterations and disables projection for distant sosig bodies.");
CfgLODSolverIter = ((BaseUnityPlugin)this).Config.Bind<int>("LOD - Physics", "Physics solver iterations (far)", 1, "Joint solver iterations for distant sosigs. Default game value: 6. (1 to 6)");
CfgLODReduceInterp = ((BaseUnityPlugin)this).Config.Bind<bool>("LOD - Physics", "Disable Rigidbody interpolation when far", true, "PERF: LOW. Turns off motion interpolation on distant sosig bodies.");
CfgLODRaiseSleepThreshold = ((BaseUnityPlugin)this).Config.Bind<bool>("LOD - Physics", "Raise sleep threshold when far", true, "PERF: HIGH. Distant sosig bodies sleep faster when idle, skipping physics simulation entirely. Restores on approach.");
CfgLODSleepThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("LOD - Physics", "Sleep threshold (far)", 5f, "Physics sleep threshold for distant sosig bodies. Default game value: 0.005. Higher = bodies sleep sooner. (0.1 to 20)");
CfgLODReduceNavTick = ((BaseUnityPlugin)this).Config.Bind<bool>("LOD - Navigation", "Slow NavMesh path recalculation when far", false, "PERF: MEDIUM. Distant sosigs recalculate paths less frequently. Off by default.");
CfgLODNavTickSecs = ((BaseUnityPlugin)this).Config.Bind<float>("LOD - Navigation", "NavMesh recalc interval seconds (far)", 8f, "Seconds between path recalculations for distant sosigs. Only applies if above is ON. (2 to 30)");
CfgLODPulseAvoidance = ((BaseUnityPlugin)this).Config.Bind<bool>("LOD - Navigation", "Pulse obstacle avoidance when far", false, "PERF: MEDIUM. Cycles avoidance OFF/ON instead of every frame. Off by default.");
CfgLODAvoidanceOffSecs = ((BaseUnityPlugin)this).Config.Bind<float>("LOD - Navigation", "Avoidance OFF duration seconds", 8f, "How long avoidance is disabled per pulse cycle. Only applies if above is ON. (1 to 60)");
CfgLODAvoidanceOnSecs = ((BaseUnityPlugin)this).Config.Bind<float>("LOD - Navigation", "Avoidance ON duration seconds", 2f, "How long avoidance is re-enabled per pulse cycle. (0.5 to 10)");
CfgLODHideShadows = ((BaseUnityPlugin)this).Config.Bind<bool>("LOD - Rendering", "Disable shadow casting when far", true, "PERF: MEDIUM (GPU). Stops distant sosigs from casting shadows.");
CfgDebugRadar = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Show sosig radar on right hand", false, "Debug radar on right wrist. 15cm disc = 1000m radius. Red = LOD active, Blue = full quality, Green = you.");
CfgGibEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Gib Cleaner", "Enabled", true, "Enable automatic cleanup of sosig ragdoll links and explosion gib chunks.");
CfgGibLinkLifetime = ((BaseUnityPlugin)this).Config.Bind<float>("Gib Cleaner", "Ragdoll link lifetime seconds", 12f, "How long severed sosig body parts persist before being destroyed. Held pieces are never destroyed. (1 to 120)");
CfgGibLifetime = ((BaseUnityPlugin)this).Config.Bind<float>("Gib Cleaner", "Gib chunk lifetime seconds", 6f, "How long small gib chunks from grenade/explosion deaths persist. Lower = less physics lag. (1 to 60)");
CfgGibMax = ((BaseUnityPlugin)this).Config.Bind<int>("Gib Cleaner", "Max simultaneous gibs", 60, "Hard cap on gib chunks. Oldest chunks destroyed first when exceeded. 0 = no cap. (0 to 500)");
CfgGibTagRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Gib Cleaner", "Gib detection radius meters", 1.5f, "Radius around a destroyed sosig link used to detect nearby gib chunks. Increase if some gibs are missed. (0.5 to 5)");
InitScopeReflection();
new Harmony("h3vr.invent60.sosigoptimizer").PatchAll(typeof(SosigOptimizerPatches));
((MonoBehaviour)this).StartCoroutine(AttachRadar());
Log.LogInfo((object)"[SosigOptimizer] v1.0.3 Loaded.");
}
internal static void ScheduleGibCap()
{
if (!_gibCapPending)
{
_gibCapPending = true;
((MonoBehaviour)Instance).StartCoroutine(Instance.EnforceGibCapNextFrame());
}
}
[IteratorStateMachine(typeof(<EnforceGibCapNextFrame>d__38))]
private IEnumerator EnforceGibCapNextFrame()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <EnforceGibCapNextFrame>d__38(0);
}
[IteratorStateMachine(typeof(<DestroyLinkAfterDelay>d__39))]
internal IEnumerator DestroyLinkAfterDelay(SosigLink link, float delay)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <DestroyLinkAfterDelay>d__39(0)
{
link = link,
delay = delay
};
}
[IteratorStateMachine(typeof(<AttachRadar>d__40))]
private IEnumerator AttachRadar()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <AttachRadar>d__40(0)
{
<>4__this = this
};
}
}
public class SosigRadar : MonoBehaviour
{
private const float RadarRadius = 0.15f;
private const float WorldRadius = 1000f;
private const float Scale = 0.00015f;
private const float DotSize = 0.008f;
private const int CircleSegs = 64;
private Mesh _circleMesh;
private Mesh _dotMesh;
private Material _mat;
private MaterialPropertyBlock _mpb;
private void Start()
{
//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_001d: Expected O, but got Unknown
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Expected O, but got Unknown
_mat = new Material(Shader.Find("Unlit/Color"))
{
hideFlags = (HideFlags)61
};
_mpb = new MaterialPropertyBlock();
_circleMesh = BuildCircleMesh();
_dotMesh = BuildDotMesh();
}
private void OnDestroy()
{
if ((Object)(object)_mat != (Object)null)
{
Object.Destroy((Object)(object)_mat);
}
if ((Object)(object)_circleMesh != (Object)null)
{
Object.Destroy((Object)(object)_circleMesh);
}
if ((Object)(object)_dotMesh != (Object)null)
{
Object.Destroy((Object)(object)_dotMesh);
}
}
private void LateUpdate()
{
//IL_004e: 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_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: 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_008d: 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_0098: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
//IL_011e: Unknown result type (might be due to invalid IL or missing references)
//IL_0128: Unknown result type (might be due to invalid IL or missing references)
//IL_0143: Unknown result type (might be due to invalid IL or missing references)
//IL_014d: Unknown result type (might be due to invalid IL or missing references)
//IL_0190: Unknown result type (might be due to invalid IL or missing references)
//IL_019a: Unknown result type (might be due to invalid IL or missing references)
//IL_019f: Unknown result type (might be due to invalid IL or missing references)
//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
if (!SosigOptimizerPlugin.CfgDebugRadar.Value || (Object)(object)_mat == (Object)null)
{
return;
}
Vector3 position;
try
{
FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody;
if ((Object)(object)currentPlayerBody == (Object)null || (Object)(object)currentPlayerBody.RightHand == (Object)null)
{
return;
}
position = ((Component)currentPlayerBody.RightHand).transform.position;
}
catch
{
return;
}
Quaternion val = Quaternion.Euler(-90f, 0f, 0f);
_mpb.SetColor("_Color", Color.white);
Graphics.DrawMesh(_circleMesh, Matrix4x4.TRS(position, val, Vector3.one * 0.15f), _mat, 0, (Camera)null, 0, _mpb);
DrawDot(position, val, Color.green);
Vector3 position2;
try
{
position2 = ((Component)GM.CurrentPlayerBody).transform.position;
}
catch
{
return;
}
List<SosigLODController> allControllers = SosigLODController.AllControllers;
for (int i = 0; i < allControllers.Count; i++)
{
SosigLODController sosigLODController = allControllers[i];
if (!((Object)(object)sosigLODController == (Object)null) && !((Object)(object)sosigLODController._sosig == (Object)null))
{
float num = (((Component)sosigLODController._sosig).transform.position.x - position2.x) * 0.00015f;
float num2 = (((Component)sosigLODController._sosig).transform.position.z - position2.z) * 0.00015f;
float num3 = Mathf.Sqrt(num * num + num2 * num2);
if (num3 > 0.15f)
{
float num4 = 0.15f / num3;
num *= num4;
num2 *= num4;
}
DrawDot(position + new Vector3(num, 0f, num2), val, sosigLODController._lodActive ? Color.red : Color.blue);
}
}
}
private void DrawDot(Vector3 pos, Quaternion rot, Color col)
{
//IL_000b: 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)
//IL_0018: 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_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
_mpb.SetColor("_Color", col);
Graphics.DrawMesh(_dotMesh, Matrix4x4.TRS(pos, rot, Vector3.one * 0.008f), _mat, 0, (Camera)null, 0, _mpb);
}
private static Mesh BuildCircleMesh()
{
//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_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
//IL_00f5: Expected O, but got Unknown
Vector3[] array = (Vector3[])(object)new Vector3[128];
int[] array2 = new int[384];
for (int i = 0; i < 64; i++)
{
float num = (float)i * 2f * (float)Math.PI / 64f;
float num2 = Mathf.Cos(num);
float num3 = Mathf.Sin(num);
array[i] = new Vector3(num2, num3, 0f);
array[i + 64] = new Vector3(num2 * 0.88f, num3 * 0.88f, 0f);
}
for (int j = 0; j < 64; j++)
{
int num4 = (j + 1) % 64;
int num5 = j * 6;
array2[num5] = j;
array2[num5 + 1] = num4;
array2[num5 + 2] = j + 64;
array2[num5 + 3] = num4;
array2[num5 + 4] = num4 + 64;
array2[num5 + 5] = j + 64;
}
Mesh val = new Mesh
{
hideFlags = (HideFlags)61,
vertices = array,
triangles = array2
};
val.RecalculateNormals();
return val;
}
private static Mesh BuildDotMesh()
{
//IL_0012: 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)
//IL_0056: 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_009e: Unknown result type (might be due to invalid IL or missing references)
//IL_00a3: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Expected O, but got Unknown
Vector3[] array = (Vector3[])(object)new Vector3[13];
int[] array2 = new int[36];
array[0] = Vector3.zero;
for (int i = 0; i < 12; i++)
{
float num = (float)i * 2f * (float)Math.PI / 12f;
array[i + 1] = new Vector3(Mathf.Cos(num) * 0.5f, Mathf.Sin(num) * 0.5f, 0f);
}
for (int j = 0; j < 12; j++)
{
array2[j * 3] = 0;
array2[j * 3 + 1] = j + 1;
array2[j * 3 + 2] = (j + 1) % 12 + 1;
}
Mesh val = new Mesh
{
hideFlags = (HideFlags)61,
vertices = array,
triangles = array2
};
val.RecalculateNormals();
return val;
}
}
internal static class SosigOptimizerPatches
{
[HarmonyPatch(typeof(Sosig), "Start")]
[HarmonyPostfix]
private static void OnSosigStart(Sosig __instance)
{
if (SosigOptimizerPlugin.CfgLODEnabled.Value)
{
((Component)__instance).gameObject.AddComponent<SosigLODController>();
}
}
[HarmonyPatch(typeof(Sosig), "SosigDies")]
[HarmonyPostfix]
private static void OnSosigDied(Sosig __instance)
{
if (!SosigOptimizerPlugin.CfgGibEnabled.Value)
{
return;
}
float value = SosigOptimizerPlugin.CfgGibLinkLifetime.Value;
foreach (SosigLink link in __instance.Links)
{
if ((Object)(object)link != (Object)null)
{
((MonoBehaviour)SosigOptimizerPlugin.Instance).StartCoroutine(SosigOptimizerPlugin.Instance.DestroyLinkAfterDelay(link, value));
}
}
}
[HarmonyPatch(typeof(Sosig), "DestroyLink")]
[HarmonyPostfix]
private static void OnLinkDestroyed(SosigLink link)
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//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)
//IL_0030: 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)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
if (!SosigOptimizerPlugin.CfgGibEnabled.Value || (Object)(object)link == (Object)null)
{
return;
}
Vector3 position = ((Component)link).transform.position;
if (float.IsNaN(position.x) || float.IsNaN(position.y) || float.IsNaN(position.z))
{
return;
}
float value = SosigOptimizerPlugin.CfgGibTagRadius.Value;
Collider[] array = Physics.OverlapSphere(position, value);
for (int i = 0; i < array.Length; i++)
{
GameObject gameObject = ((Component)array[i]).gameObject;
if (!((Object)(object)gameObject.GetComponent<Rigidbody>() == (Object)null) && !((Object)(object)gameObject.GetComponent<GibMarker>() != (Object)null) && !((Object)(object)gameObject.GetComponent<ParticleSystem>() != (Object)null))
{
Scene scene = gameObject.scene;
if (!(((Scene)(ref scene)).name == "DontDestroyOnLoad") && !((Object)(object)gameObject.GetComponentInParent<FVRPhysicalObject>() != (Object)null) && !((Object)(object)gameObject.GetComponentInParent<FVRViveHand>() != (Object)null) && !((Object)(object)gameObject.GetComponentInParent<FVRPlayerBody>() != (Object)null) && !((Object)(object)gameObject.GetComponentInParent<FVRPlayerHitbox>() != (Object)null) && !((Object)(object)gameObject.GetComponentInParent<SosigLink>() != (Object)null) && !((Object)(object)gameObject.GetComponentInParent<SosigWeapon>() != (Object)null) && !((Object)(object)gameObject.GetComponentInParent<SosigWearable>() != (Object)null) && !((Object)(object)gameObject.GetComponentInParent<ParticleSystem>() != (Object)null))
{
gameObject.AddComponent<GibMarker>();
Object.Destroy((Object)(object)gameObject, SosigOptimizerPlugin.CfgGibLifetime.Value);
}
}
}
SosigOptimizerPlugin.ScheduleGibCap();
}
}