Decompiled source of ECHOX v3.8.0

GearVision.dll

Decompiled 15 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BoneLib;
using BoneLib.BoneMenu;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.AI;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.XR;
using WeaponInspector;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(WeaponInspectorMod), "ECHOX", "3.8.0", "Cr4shers_inc", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonOptionalDependencies(new string[] { "BoneLib" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("GearVision")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("GearVision")]
[assembly: AssemblyTitle("GearVision")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 WeaponInspector
{
	public class WeaponInspectorMod : MelonMod
	{
		private class ReplayTransform
		{
			public Vector3 Position;

			public Quaternion Rotation = Quaternion.identity;
		}

		private class ReplayFrame
		{
			public float Time;

			public ReplayTransform Head = new ReplayTransform();

			public ReplayTransform LeftHand = new ReplayTransform();

			public ReplayTransform RightHand = new ReplayTransform();

			public ReplayTransform Weapon = new ReplayTransform();

			public bool HasHead;

			public bool HasLeftHand;

			public bool HasRightHand;

			public bool HasWeapon;

			public string WeaponName;
		}

		private class NPCReplayFrame
		{
			public float Time;

			public Vector3 Position;

			public Quaternion Rotation = Quaternion.identity;
		}

		private enum GearTag
		{
			None,
			Sidearm,
			Primary,
			Melee,
			Utility,
			Heavy,
			Experimental
		}

		private enum WeaponKind
		{
			Firearm,
			Melee,
			Unknown
		}

		private class WeaponInfo
		{
			public GameObject Anchor;

			public string Name;

			public WeaponKind Kind;

			public float Mass;

			public float Speed;

			public float Impact;

			public List<string> ExtraStats = new List<string>();

			public List<string> Components = new List<string>();
		}

		private GameObject inspectorRoot;

		private TextMesh inspectorText;

		private TextMesh hologramGlowText;

		private GameObject currentTarget;

		private WeaponInfo currentWeaponInfo;

		private string currentWeaponName;

		private float showUntil;

		private float currentAlpha;

		private bool visible;

		private GameObject lastScannedWeapon;

		private Material weaponOutlineMaterial;

		private bool outlineActive;

		private float outlineStartTime;

		private float outlineEndTime;

		private readonly List<Renderer> outlineRenderers = new List<Renderer>();

		private readonly List<GameObject> outlineObjects = new List<GameObject>();

		private readonly Dictionary<Mesh, Mesh> invertedOutlineMeshCache = new Dictionary<Mesh, Mesh>();

		private bool manualInfoVisible;

		private bool supportSecondaryWasPressed;

		private GameObject trackedTwoHandWeapon;

		private bool primaryHandKnown;

		private bool primaryHandIsLeft;

		private bool quickLeftWasPressed;

		private bool quickRightWasPressed;

		private bool quickScanVisible;

		private GameObject quickScannedWeapon;

		private GameObject circleDeckRoot;

		private LineRenderer outerCircle;

		private LineRenderer innerCircle;

		private LineRenderer circlePointer;

		private Material hologramLineMaterial;

		private float circleRotation;

		private GameObject coneRoot;

		private MeshFilter coneMeshFilter;

		private MeshRenderer coneRenderer;

		private bool swooshActive;

		private float swooshStartTime;

		private const float SwooshDuration = 0.28f;

		private AudioSource scanAudioSource;

		private AudioClip scanAudioClip;

		private float hologramPulseTimer;

		private float flickerCountdown;

		private float flickerEndTime;

		private float flickerMultiplier = 1f;

		private Quaternion lockedLocalRotation;

		private bool hasLockedRotation;

		private string fullInspectorText = "";

		private bool scanActive;

		private float scanStartTime;

		private float nextScanUpdate;

		private int lastScanCharacterCount = -1;

		private const float ScanDuration = 0.7f;

		private const float ScanRefreshRate = 1f / 30f;

		private readonly List<ReplayFrame> replayBuffer = new List<ReplayFrame>();

		private readonly List<ReplayFrame> lastKillReplay = new List<ReplayFrame>();

		private float nextReplaySampleTime;

		private readonly Dictionary<int, List<NPCReplayFrame>> npcReplayBuffers = new Dictionary<int, List<NPCReplayFrame>>();

		private readonly List<NPCReplayFrame> lastKillNPCReplay = new List<NPCReplayFrame>();

		private readonly List<AIBrain> trackedNPCs = new List<AIBrain>();

		private float nextNPCRefreshTime;

		private int killedNPCInstanceId = -1;

		private bool combatReplayPlaying;

		private float combatReplayStartTime;

		private float combatReplayOriginalTimeScale = 1f;

		private bool replayIntroActive;

		private bool replayOutroActive;

		private float replayIntroStart;

		private float replayOutroStart;

		private const float ReplayIntroDuration = 1.1f;

		private const float ReplayOutroDuration = 1.3f;

		private bool replayKillPulseTriggered;

		private float replayKillPulse;

		private GameObject replayRoot;

		private GameObject replayHeadGhost;

		private GameObject replayLeftGhost;

		private GameObject replayRightGhost;

		private GameObject replayWeaponGhost;

		private GameObject replayNPCGhost;

		private GameObject replayNPCHeadGhost;

		private GameObject replayImpactMarker;

		private TextMesh replayStatsText;

		private TextMesh replayIntroText;

		private TextMesh replayOutroText;

		private LineRenderer replayHeadPath;

		private LineRenderer replayLeftPath;

		private LineRenderer replayRightPath;

		private LineRenderer replayWeaponPath;

		private LineRenderer replayBulletPath;

		private Material replayMaterial;

		private string replayWeaponName = "UNKNOWN";

		private float replayKillDistance;

		private float replayHitVelocity;

		private Vector3 replayImpactPoint;

		private bool replayHasImpactPoint;

		private Vector3 lastShotOrigin;

		private Vector3 lastShotDirection;

		private float lastShotTime;

		private bool hasLastShot;

		private MelonPreferences_Category settingsCategory;

		private MelonPreferences_Entry<bool> enabled;

		private MelonPreferences_Entry<float> displayDuration;

		private MelonPreferences_Entry<float> fadeDuration;

		private MelonPreferences_Entry<bool> fadeEnabled;

		private MelonPreferences_Entry<float> textSize;

		private MelonPreferences_Entry<float> offsetX;

		private MelonPreferences_Entry<float> offsetY;

		private MelonPreferences_Entry<float> offsetZ;

		private MelonPreferences_Entry<float> textRed;

		private MelonPreferences_Entry<float> textGreen;

		private MelonPreferences_Entry<float> textBlue;

		private MelonPreferences_Entry<bool> showSpeed;

		private MelonPreferences_Entry<bool> showImpact;

		private MelonPreferences_Entry<bool> showComponents;

		private MelonPreferences_Entry<float> fontIndex;

		private MelonPreferences_Entry<bool> panelLock;

		private MelonPreferences_Entry<bool> scanAnimation;

		private MelonPreferences_Entry<int> flickerStrength;

		private MelonPreferences_Entry<bool> circleDeckEnabled;

		private MelonPreferences_Entry<float> circleSpinSpeed;

		private MelonPreferences_Entry<bool> swooshAnimation;

		private MelonPreferences_Entry<bool> scanSound;

		private MelonPreferences_Entry<float> scanSoundVolume;

		private MelonPreferences_Entry<bool> twoHandInfoToggle;

		private MelonPreferences_Entry<bool> quickScanEnabled;

		private MelonPreferences_Entry<float> quickScanRange;

		private MelonPreferences_Entry<bool> weaponOutlineEnabled;

		private MelonPreferences_Entry<float> weaponOutlineThickness;

		private MelonPreferences_Entry<float> weaponOutlineDuration;

		private MelonPreferences_Entry<bool> showFavoriteMarker;

		private MelonPreferences_Entry<bool> showTags;

		private MelonPreferences_Entry<string> favoritesData;

		private MelonPreferences_Entry<string> tagsData;

		private MelonPreferences_Entry<bool> combatReplayEnabled;

		private MelonPreferences_Entry<float> combatReplayLength;

		private MelonPreferences_Entry<float> combatReplaySpeed;

		private MelonPreferences_Entry<bool> combatReplayFreezeWorld;

		private MelonPreferences_Entry<bool> combatReplaySlowMotion;

		private MelonPreferences_Entry<bool> replayShowHeadPath;

		private MelonPreferences_Entry<bool> replayShowHandPaths;

		private MelonPreferences_Entry<bool> replayShowWeaponPath;

		private MelonPreferences_Entry<bool> replayShowBulletPath;

		private MelonPreferences_Entry<bool> replayShowImpactMarker;

		private MelonPreferences_Entry<bool> replayShowKillStats;

		private MelonPreferences_Entry<bool> replayShowNPC;

		private MelonPreferences_Entry<bool> replayIntroEnabled;

		private MelonPreferences_Entry<bool> replayOutroEnabled;

		private MelonPreferences_Entry<bool> replayKillPulseEnabled;

		private MelonPreferences_Entry<int> replayHologramStyle;

		private Font currentFont;

		private int loadedFontIndex = -1;

		private readonly string[][] availableFonts = new string[5][]
		{
			new string[3] { "Arial", "Liberation Sans", "Roboto" },
			new string[3] { "Roboto", "Arial", "Liberation Sans" },
			new string[3] { "Times New Roman", "Liberation Serif", "serif" },
			new string[3] { "Courier New", "Liberation Mono", "monospace" },
			new string[3] { "Verdana", "Arial", "Roboto" }
		};

		private readonly HashSet<string> favoriteWeapons = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, GearTag> weaponTags = new Dictionary<string, GearTag>(StringComparer.OrdinalIgnoreCase);

		private readonly string[] firearmWords = new string[10] { "gun", "pistol", "rifle", "shotgun", "revolver", "smg", "carbine", "firearm", "blaster", "launcher" };

		private readonly string[] meleeWords = new string[14]
		{
			"knife", "blade", "sword", "katana", "axe", "hatchet", "machete", "bat", "club", "hammer",
			"crowbar", "spear", "dagger", "melee"
		};

		public override void OnInitializeMelon()
		{
			CreatePreferences();
			LoadFavorites();
			LoadTags();
			Hooking.OnGrabObject += OnGrabObject;
			Hooking.OnReleaseObject += OnReleaseObject;
			Hooking.OnUIRigCreated += OnUIRigCreated;
			Hooking.OnLevelLoaded += OnLevelLoaded;
			Hooking.OnNPCBrainDie += OnNPCBrainDie;
			Hooking.OnPostFireGun += OnPostFireGun;
			TryCreateBoneMenu();
			((MelonBase)this).LoggerInstance.Msg("ECHOX OS 0.9.2 loaded.");
		}

		public override void OnUpdate()
		{
			UpdateTwoHandInfoToggle();
			UpdateQuickScan();
			UpdateInspector();
			UpdateWeaponOutline();
			UpdateCombatReplayRecording();
			UpdateReplayIntroOutro();
			UpdateCombatReplayPlayback();
		}

		private void CreatePreferences()
		{
			settingsCategory = MelonPreferences.CreateCategory("ECHOX", "ECHOX");
			enabled = settingsCategory.CreateEntry<bool>("Enabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			displayDuration = settingsCategory.CreateEntry<float>("DisplayDuration", 3f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			fadeDuration = settingsCategory.CreateEntry<float>("FadeDuration", 0.45f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			fadeEnabled = settingsCategory.CreateEntry<bool>("FadeEnabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			textSize = settingsCategory.CreateEntry<float>("TextSize", 0.006f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			offsetX = settingsCategory.CreateEntry<float>("OffsetX", 0.12f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			offsetY = settingsCategory.CreateEntry<float>("OffsetY", 0.08f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			offsetZ = settingsCategory.CreateEntry<float>("OffsetZ", 0.04f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			textRed = settingsCategory.CreateEntry<float>("TextRed", 0.65f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			textGreen = settingsCategory.CreateEntry<float>("TextGreen", 0.2f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			textBlue = settingsCategory.CreateEntry<float>("TextBlue", 1f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			showSpeed = settingsCategory.CreateEntry<bool>("ShowSpeed", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			showImpact = settingsCategory.CreateEntry<bool>("ShowImpact", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			showComponents = settingsCategory.CreateEntry<bool>("ShowComponents", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			fontIndex = settingsCategory.CreateEntry<float>("FontIndex", 0f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			panelLock = settingsCategory.CreateEntry<bool>("PanelLock", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			scanAnimation = settingsCategory.CreateEntry<bool>("ScanAnimation", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			flickerStrength = settingsCategory.CreateEntry<int>("FlickerStrength", 1, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			circleDeckEnabled = settingsCategory.CreateEntry<bool>("CircleDeck", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			circleSpinSpeed = settingsCategory.CreateEntry<float>("CircleSpinSpeed", 90f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			swooshAnimation = settingsCategory.CreateEntry<bool>("SwooshAnimation", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			scanSound = settingsCategory.CreateEntry<bool>("ScanSound", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			scanSoundVolume = settingsCategory.CreateEntry<float>("ScanSoundVolume", 0.45f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			twoHandInfoToggle = settingsCategory.CreateEntry<bool>("TwoHandInfoToggle", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			quickScanEnabled = settingsCategory.CreateEntry<bool>("QuickScan", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			quickScanRange = settingsCategory.CreateEntry<float>("QuickScanRange", 8f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			weaponOutlineEnabled = settingsCategory.CreateEntry<bool>("WeaponOutline", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			weaponOutlineThickness = settingsCategory.CreateEntry<float>("OutlineThickness", 0.005f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			weaponOutlineDuration = settingsCategory.CreateEntry<float>("OutlineDuration", 0.85f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			showFavoriteMarker = settingsCategory.CreateEntry<bool>("ShowFavoriteMarker", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			showTags = settingsCategory.CreateEntry<bool>("ShowTags", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			favoritesData = settingsCategory.CreateEntry<string>("Favorites", "", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			tagsData = settingsCategory.CreateEntry<string>("Tags", "", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			combatReplayEnabled = settingsCategory.CreateEntry<bool>("CombatReplayEnabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			combatReplayLength = settingsCategory.CreateEntry<float>("CombatReplayLength", 5f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			combatReplaySpeed = settingsCategory.CreateEntry<float>("CombatReplaySpeed", 1f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			combatReplayFreezeWorld = settingsCategory.CreateEntry<bool>("CombatReplayFreezeWorld", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			combatReplaySlowMotion = settingsCategory.CreateEntry<bool>("CombatReplaySlowMotion", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			replayShowHeadPath = settingsCategory.CreateEntry<bool>("ReplayShowHeadPath", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			replayShowHandPaths = settingsCategory.CreateEntry<bool>("ReplayShowHandPaths", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			replayShowWeaponPath = settingsCategory.CreateEntry<bool>("ReplayShowWeaponPath", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			replayShowBulletPath = settingsCategory.CreateEntry<bool>("ReplayShowBulletPath", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			replayShowImpactMarker = settingsCategory.CreateEntry<bool>("ReplayShowImpactMarker", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			replayShowKillStats = settingsCategory.CreateEntry<bool>("ReplayShowKillStats", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			replayShowNPC = settingsCategory.CreateEntry<bool>("ReplayShowNPC", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			replayIntroEnabled = settingsCategory.CreateEntry<bool>("ReplayIntroEnabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			replayOutroEnabled = settingsCategory.CreateEntry<bool>("ReplayOutroEnabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			replayKillPulseEnabled = settingsCategory.CreateEntry<bool>("ReplayKillPulse", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			replayHologramStyle = settingsCategory.CreateEntry<int>("ReplayHologramStyle", 0, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		}

		private void OnUIRigCreated()
		{
			TryCreateBoneMenu();
		}

		private void OnLevelLoaded(LevelInfo levelInfo)
		{
			TryCreateBoneMenu();
			StopCombatReplay();
			ClearWeaponOutline();
			replayBuffer.Clear();
			lastKillReplay.Clear();
			npcReplayBuffers.Clear();
			lastKillNPCReplay.Clear();
			trackedNPCs.Clear();
			nextReplaySampleTime = 0f;
			nextNPCRefreshTime = 0f;
			hasLastShot = false;
			killedNPCInstanceId = -1;
			lastScannedWeapon = null;
			trackedTwoHandWeapon = null;
			primaryHandKnown = false;
		}

		private void TryCreateBoneMenu()
		{
			//IL_0042: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0308: Unknown result type (might be due to invalid IL or missing references)
			//IL_033b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0349: Unknown result type (might be due to invalid IL or missing references)
			//IL_036d: Unknown result type (might be due to invalid IL or missing references)
			//IL_039f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0405: Unknown result type (might be due to invalid IL or missing references)
			//IL_041e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0451: Unknown result type (might be due to invalid IL or missing references)
			//IL_0484: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0514: Unknown result type (might be due to invalid IL or missing references)
			//IL_0538: Unknown result type (might be due to invalid IL or missing references)
			//IL_055c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0580: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0610: Unknown result type (might be due to invalid IL or missing references)
			//IL_0633: Unknown result type (might be due to invalid IL or missing references)
			//IL_065a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0668: Unknown result type (might be due to invalid IL or missing references)
			//IL_0681: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_06fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0714: Unknown result type (might be due to invalid IL or missing references)
			//IL_072d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0746: Unknown result type (might be due to invalid IL or missing references)
			//IL_075f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0778: Unknown result type (might be due to invalid IL or missing references)
			//IL_0790: Unknown result type (might be due to invalid IL or missing references)
			//IL_07a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_07ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_081c: Unknown result type (might be due to invalid IL or missing references)
			//IL_084f: Unknown result type (might be due to invalid IL or missing references)
			//IL_085d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0890: Unknown result type (might be due to invalid IL or missing references)
			//IL_08c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_08f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0902: Unknown result type (might be due to invalid IL or missing references)
			//IL_0926: Unknown result type (might be due to invalid IL or missing references)
			//IL_0949: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Page.Root == null || Page.Root.GetChildPage("ECHOX") != null)
				{
					return;
				}
				Color val = default(Color);
				((Color)(ref val))..ctor(0.55f, 0.15f, 1f);
				Page obj = Page.Root.CreatePage("ECHOX", val, 9, true);
				obj.CreateBool("Enabled", val, enabled.Value, (Action<bool>)delegate(bool value)
				{
					enabled.Value = value;
					if (!value)
					{
						HideInspector(immediate: true);
						ClearWeaponOutline();
						StopCombatReplay();
						lastScannedWeapon = null;
					}
				});
				obj.CreateFloat("Display Time", val, displayDuration.Value, 0.25f, 0.5f, 10f, (Action<float>)delegate(float value)
				{
					displayDuration.Value = value;
				});
				obj.CreateBool("Fade Out", val, fadeEnabled.Value, (Action<bool>)delegate(bool value)
				{
					fadeEnabled.Value = value;
					if (!value && visible)
					{
						currentAlpha = 1f;
					}
				});
				obj.CreateFloat("Fade Time", val, fadeDuration.Value, 0.05f, 0.05f, 3f, (Action<float>)delegate(float value)
				{
					fadeDuration.Value = value;
				});
				obj.CreateFloat("Text Size", val, textSize.Value, 0.001f, 0.002f, 0.03f, (Action<float>)delegate(float value)
				{
					textSize.Value = value;
					UpdateHoloDeckLayout();
				});
				obj.CreateFloat("Font 0-4", val, fontIndex.Value, 1f, 0f, 4f, (Action<float>)delegate(float value)
				{
					fontIndex.Value = Mathf.Round(value);
					LoadSelectedFont();
				});
				Page obj2 = obj.CreatePage("Controls", val, 0, true);
				obj2.CreateBool("Two-Hand Info Toggle", val, twoHandInfoToggle.Value, (Action<bool>)delegate(bool value)
				{
					twoHandInfoToggle.Value = value;
					supportSecondaryWasPressed = false;
					if (!value)
					{
						manualInfoVisible = false;
						RestoreAutomaticInspector();
					}
				});
				obj2.CreateBool("Quick Scan", val, quickScanEnabled.Value, (Action<bool>)delegate(bool value)
				{
					quickScanEnabled.Value = value;
					quickLeftWasPressed = false;
					quickRightWasPressed = false;
					if (!value && quickScanVisible)
					{
						quickScanVisible = false;
						quickScannedWeapon = null;
						HideInspector(immediate: true);
					}
				});
				obj2.CreateFloat("Quick Scan Range", val, quickScanRange.Value, 0.5f, 1f, 20f, (Action<float>)delegate(float value)
				{
					quickScanRange.Value = value;
				});
				Page obj3 = obj.CreatePage("Hologram", val, 8, true);
				obj3.CreateBool("Panel Lock", val, panelLock.Value, (Action<bool>)delegate(bool value)
				{
					panelLock.Value = value;
					OnPanelLockChanged();
				});
				obj3.CreateBool("Scan Animation", val, scanAnimation.Value, (Action<bool>)delegate(bool value)
				{
					scanAnimation.Value = value;
				});
				obj3.CreateInt("Flicker 0-3", val, flickerStrength.Value, 1, 0, 3, (Action<int>)delegate(int value)
				{
					flickerStrength.Value = value;
					ResetFlicker();
				});
				obj3.CreateBool("Circle Deck", val, circleDeckEnabled.Value, (Action<bool>)delegate(bool value)
				{
					circleDeckEnabled.Value = value;
					UpdateCircleVisibility();
				});
				obj3.CreateFloat("Circle Spin Speed", val, circleSpinSpeed.Value, 15f, -360f, 360f, (Action<float>)delegate(float value)
				{
					circleSpinSpeed.Value = value;
				});
				obj3.CreateBool("Swoosh Animation", val, swooshAnimation.Value, (Action<bool>)delegate(bool value)
				{
					swooshAnimation.Value = value;
				});
				obj3.CreateBool("Scan Sound", val, scanSound.Value, (Action<bool>)delegate(bool value)
				{
					scanSound.Value = value;
				});
				obj3.CreateFloat("Scan Volume", val, scanSoundVolume.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float value)
				{
					scanSoundVolume.Value = value;
					if ((Object)(object)scanAudioSource != (Object)null)
					{
						scanAudioSource.volume = value;
					}
				});
				Page obj4 = obj.CreatePage("Weapon Outline", val, 0, true);
				obj4.CreateBool("Enabled", val, weaponOutlineEnabled.Value, (Action<bool>)delegate(bool value)
				{
					weaponOutlineEnabled.Value = value;
					if (!value)
					{
						ClearWeaponOutline();
					}
				});
				obj4.CreateFloat("Thickness", val, weaponOutlineThickness.Value, 0.001f, 0.001f, 0.015f, (Action<float>)delegate(float value)
				{
					weaponOutlineThickness.Value = value;
				});
				obj4.CreateFloat("Duration", val, weaponOutlineDuration.Value, 0.1f, 0.2f, 3f, (Action<float>)delegate(float value)
				{
					weaponOutlineDuration.Value = value;
				});
				Page obj5 = obj.CreatePage("Combat Replay", val, 12, true);
				obj5.CreateBool("Enabled", val, combatReplayEnabled.Value, (Action<bool>)delegate(bool value)
				{
					combatReplayEnabled.Value = value;
					if (!value)
					{
						StopCombatReplay();
						replayBuffer.Clear();
						lastKillReplay.Clear();
						npcReplayBuffers.Clear();
						lastKillNPCReplay.Clear();
					}
				});
				obj5.CreateFunction("Replay Last Kill", val, (Action)PlayLastCombatReplay);
				obj5.CreateFloat("Record Length", val, combatReplayLength.Value, 0.5f, 1f, 10f, (Action<float>)delegate(float value)
				{
					combatReplayLength.Value = value;
				});
				obj5.CreateFloat("Replay Speed", val, combatReplaySpeed.Value, 0.1f, 0.1f, 2f, (Action<float>)delegate(float value)
				{
					combatReplaySpeed.Value = value;
				});
				obj5.CreateBool("Freeze World", val, combatReplayFreezeWorld.Value, (Action<bool>)delegate(bool value)
				{
					combatReplayFreezeWorld.Value = value;
				});
				obj5.CreateBool("Slow Motion", val, combatReplaySlowMotion.Value, (Action<bool>)delegate(bool value)
				{
					combatReplaySlowMotion.Value = value;
				});
				obj5.CreateBool("Show NPC", val, replayShowNPC.Value, (Action<bool>)delegate(bool value)
				{
					replayShowNPC.Value = value;
				});
				obj5.CreateBool("Kill Stats", val, replayShowKillStats.Value, (Action<bool>)delegate(bool value)
				{
					replayShowKillStats.Value = value;
				});
				obj5.CreateBool("Intro", val, replayIntroEnabled.Value, (Action<bool>)delegate(bool value)
				{
					replayIntroEnabled.Value = value;
				});
				obj5.CreateBool("Outro", val, replayOutroEnabled.Value, (Action<bool>)delegate(bool value)
				{
					replayOutroEnabled.Value = value;
				});
				obj5.CreateBool("Kill Pulse", val, replayKillPulseEnabled.Value, (Action<bool>)delegate(bool value)
				{
					replayKillPulseEnabled.Value = value;
				});
				obj5.CreateBool("Head Path", val, replayShowHeadPath.Value, (Action<bool>)delegate(bool value)
				{
					replayShowHeadPath.Value = value;
				});
				obj5.CreateBool("Hand Paths", val, replayShowHandPaths.Value, (Action<bool>)delegate(bool value)
				{
					replayShowHandPaths.Value = value;
				});
				obj5.CreateBool("Weapon Path", val, replayShowWeaponPath.Value, (Action<bool>)delegate(bool value)
				{
					replayShowWeaponPath.Value = value;
				});
				obj5.CreateBool("Bullet Path", val, replayShowBulletPath.Value, (Action<bool>)delegate(bool value)
				{
					replayShowBulletPath.Value = value;
				});
				obj5.CreateBool("Impact Marker", val, replayShowImpactMarker.Value, (Action<bool>)delegate(bool value)
				{
					replayShowImpactMarker.Value = value;
				});
				obj5.CreateInt("Hologram Style 0-2", val, replayHologramStyle.Value, 1, 0, 2, (Action<int>)delegate(int value)
				{
					replayHologramStyle.Value = value;
					UpdateReplayMaterialColor();
				});
				Page obj6 = obj.CreatePage("Favorites & Tags", val, 7, true);
				obj6.CreateFunction("Toggle Favorite Held", val, (Action)ToggleFavoriteHeld);
				obj6.CreateBool("Show Favorite", val, showFavoriteMarker.Value, (Action<bool>)delegate(bool value)
				{
					showFavoriteMarker.Value = value;
					RefreshCurrentDisplay();
				});
				obj6.CreateBool("Show Tags", val, showTags.Value, (Action<bool>)delegate(bool value)
				{
					showTags.Value = value;
					RefreshCurrentDisplay();
				});
				obj6.CreateFunction("Tag: Sidearm", val, (Action)delegate
				{
					SetCurrentTag(GearTag.Sidearm);
				});
				obj6.CreateFunction("Tag: Primary", val, (Action)delegate
				{
					SetCurrentTag(GearTag.Primary);
				});
				obj6.CreateFunction("Tag: Melee", val, (Action)delegate
				{
					SetCurrentTag(GearTag.Melee);
				});
				obj6.CreateFunction("Tag: Utility", val, (Action)delegate
				{
					SetCurrentTag(GearTag.Utility);
				});
				obj6.CreateFunction("Tag: Heavy", val, (Action)delegate
				{
					SetCurrentTag(GearTag.Heavy);
				});
				obj6.CreateFunction("Tag: Experimental", val, (Action)delegate
				{
					SetCurrentTag(GearTag.Experimental);
				});
				obj6.CreateFunction("Clear Held Tag", val, (Action)delegate
				{
					SetCurrentTag(GearTag.None);
				});
				obj6.CreateFunction("Clear All Favorites", val, (Action)ClearAllFavorites);
				obj6.CreateFunction("Clear All Tags", val, (Action)ClearAllTags);
				Page obj7 = obj.CreatePage("Offset", val, 0, true);
				obj7.CreateFloat("X", val, offsetX.Value, 0.01f, -1f, 1f, (Action<float>)delegate(float value)
				{
					offsetX.Value = value;
				});
				obj7.CreateFloat("Y", val, offsetY.Value, 0.01f, -1f, 1f, (Action<float>)delegate(float value)
				{
					offsetY.Value = value;
				});
				obj7.CreateFloat("Z", val, offsetZ.Value, 0.01f, -1f, 1f, (Action<float>)delegate(float value)
				{
					offsetZ.Value = value;
				});
				Page obj8 = obj.CreatePage("Text Color", val, 0, true);
				obj8.CreateFloat("Red", val, textRed.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float value)
				{
					textRed.Value = value;
					ApplyInspectorVisuals();
					UpdateReplayMaterialColor();
				});
				obj8.CreateFloat("Green", val, textGreen.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float value)
				{
					textGreen.Value = value;
					ApplyInspectorVisuals();
					UpdateReplayMaterialColor();
				});
				obj8.CreateFloat("Blue", val, textBlue.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float value)
				{
					textBlue.Value = value;
					ApplyInspectorVisuals();
					UpdateReplayMaterialColor();
				});
				Page obj9 = obj.CreatePage("Information", val, 0, true);
				obj9.CreateBool("Show Speed", val, showSpeed.Value, (Action<bool>)delegate(bool value)
				{
					showSpeed.Value = value;
					RefreshCurrentDisplay();
				});
				obj9.CreateBool("Show Impact", val, showImpact.Value, (Action<bool>)delegate(bool value)
				{
					showImpact.Value = value;
					RefreshCurrentDisplay();
				});
				obj9.CreateBool("Show Components", val, showComponents.Value, (Action<bool>)delegate(bool value)
				{
					showComponents.Value = value;
					RefreshCurrentDisplay();
				});
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Warning("ECHOX BoneMenu setup failed: " + ex.Message);
			}
		}

		private void OnNPCBrainDie(AIBrain brain)
		{
			//IL_002a: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_00b7: 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_00c4: Unknown result type (might be due to invalid IL or missing references)
			if (!combatReplayEnabled.Value || (Object)(object)brain == (Object)null)
			{
				return;
			}
			try
			{
				Transform transform = ((Component)brain).transform;
				if ((Object)(object)transform != (Object)null)
				{
					replayImpactPoint = transform.position;
					replayHasImpactPoint = true;
					if ((Object)(object)Player.Head != (Object)null)
					{
						replayKillDistance = Vector3.Distance(Player.Head.position, replayImpactPoint);
					}
				}
				killedNPCInstanceId = ((Object)brain).GetInstanceID();
				lastKillNPCReplay.Clear();
				if (npcReplayBuffers.TryGetValue(killedNPCInstanceId, out var value))
				{
					for (int i = 0; i < value.Count; i++)
					{
						NPCReplayFrame nPCReplayFrame = value[i];
						NPCReplayFrame nPCReplayFrame2 = new NPCReplayFrame();
						nPCReplayFrame2.Time = nPCReplayFrame.Time;
						nPCReplayFrame2.Position = nPCReplayFrame.Position;
						nPCReplayFrame2.Rotation = nPCReplayFrame.Rotation;
						lastKillNPCReplay.Add(nPCReplayFrame2);
					}
				}
				replayHitVelocity = GetCurrentWeaponVelocity();
				CaptureCombatReplay();
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Warning("ECHOX combat capture failed: " + ex.Message);
			}
		}

		private void OnPostFireGun(Gun gun)
		{
			//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_0020: 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)
			if ((Object)(object)gun == (Object)null)
			{
				return;
			}
			try
			{
				Transform transform = ((Component)gun).transform;
				lastShotOrigin = transform.position;
				lastShotDirection = transform.forward;
				lastShotTime = Time.unscaledTime;
				hasLastShot = true;
			}
			catch
			{
			}
		}

		private void UpdateCombatReplayRecording()
		{
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: 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_0074: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			if (!combatReplayEnabled.Value || combatReplayPlaying || replayIntroActive || replayOutroActive)
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime < nextReplaySampleTime)
			{
				return;
			}
			nextReplaySampleTime = unscaledTime + 1f / 30f;
			ReplayFrame replayFrame = new ReplayFrame();
			replayFrame.Time = unscaledTime;
			if ((Object)(object)Player.Head != (Object)null)
			{
				replayFrame.HasHead = true;
				replayFrame.Head.Position = Player.Head.position;
				replayFrame.Head.Rotation = Player.Head.rotation;
			}
			try
			{
				Hand leftHand = Player.LeftHand;
				if ((Object)(object)leftHand != (Object)null)
				{
					Transform transform = ((Component)leftHand).transform;
					replayFrame.HasLeftHand = true;
					replayFrame.LeftHand.Position = transform.position;
					replayFrame.LeftHand.Rotation = transform.rotation;
				}
				Hand rightHand = Player.RightHand;
				if ((Object)(object)rightHand != (Object)null)
				{
					Transform transform2 = ((Component)rightHand).transform;
					replayFrame.HasRightHand = true;
					replayFrame.RightHand.Position = transform2.position;
					replayFrame.RightHand.Rotation = transform2.rotation;
				}
			}
			catch
			{
			}
			try
			{
				GameObject objectInHand = Player.GetObjectInHand(Player.RightHand);
				if ((Object)(object)objectInHand == (Object)null)
				{
					objectInHand = Player.GetObjectInHand(Player.LeftHand);
				}
				if ((Object)(object)objectInHand != (Object)null)
				{
					GameObject val = FindWeaponRoot(objectInHand);
					if ((Object)(object)val != (Object)null)
					{
						replayFrame.HasWeapon = true;
						replayFrame.Weapon.Position = val.transform.position;
						replayFrame.Weapon.Rotation = val.transform.rotation;
						replayFrame.WeaponName = CleanName(((Object)val).name);
					}
				}
			}
			catch
			{
			}
			replayBuffer.Add(replayFrame);
			float num = unscaledTime - Mathf.Clamp(combatReplayLength.Value, 1f, 10f);
			while (replayBuffer.Count > 0 && replayBuffer[0].Time < num)
			{
				replayBuffer.RemoveAt(0);
			}
			while (replayBuffer.Count > 320)
			{
				replayBuffer.RemoveAt(0);
			}
			RecordNearbyNPCs(unscaledTime);
		}

		private void RecordNearbyNPCs(float now)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: 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)
			if (now >= nextNPCRefreshTime)
			{
				nextNPCRefreshTime = now + 1f;
				RefreshTrackedNPCs();
			}
			if (trackedNPCs.Count == 0)
			{
				return;
			}
			Vector3 val = (((Object)(object)Player.Head != (Object)null) ? Player.Head.position : Vector3.zero);
			int num = 0;
			for (int num2 = trackedNPCs.Count - 1; num2 >= 0 && num < 8; num2--)
			{
				AIBrain val2 = trackedNPCs[num2];
				if ((Object)(object)val2 == (Object)null)
				{
					trackedNPCs.RemoveAt(num2);
					continue;
				}
				Transform transform;
				try
				{
					transform = ((Component)val2).transform;
				}
				catch
				{
					continue;
				}
				if (!((Object)(object)transform == (Object)null) && !(Vector3.Distance(val, transform.position) > 35f))
				{
					int instanceID = ((Object)val2).GetInstanceID();
					if (!npcReplayBuffers.TryGetValue(instanceID, out var value))
					{
						value = new List<NPCReplayFrame>();
						npcReplayBuffers[instanceID] = value;
					}
					NPCReplayFrame nPCReplayFrame = new NPCReplayFrame();
					nPCReplayFrame.Time = now;
					nPCReplayFrame.Position = transform.position;
					nPCReplayFrame.Rotation = transform.rotation;
					value.Add(nPCReplayFrame);
					float num3 = now - Mathf.Clamp(combatReplayLength.Value, 1f, 10f);
					while (value.Count > 0 && value[0].Time < num3)
					{
						value.RemoveAt(0);
					}
					while (value.Count > 320)
					{
						value.RemoveAt(0);
					}
					num++;
				}
			}
		}

		private void RefreshTrackedNPCs()
		{
			try
			{
				trackedNPCs.Clear();
				AIBrain[] array = Il2CppArrayBase<AIBrain>.op_Implicit(Object.FindObjectsOfType<AIBrain>());
				if (array == null)
				{
					return;
				}
				int num = Mathf.Min(array.Length, 16);
				for (int i = 0; i < num; i++)
				{
					if ((Object)(object)array[i] != (Object)null)
					{
						trackedNPCs.Add(array[i]);
					}
				}
			}
			catch
			{
			}
		}

		private void CaptureCombatReplay()
		{
			//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_009f: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: 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)
			if (!combatReplayEnabled.Value || replayBuffer.Count < 2)
			{
				return;
			}
			lastKillReplay.Clear();
			for (int i = 0; i < replayBuffer.Count; i++)
			{
				ReplayFrame replayFrame = replayBuffer[i];
				ReplayFrame replayFrame2 = new ReplayFrame();
				replayFrame2.Time = replayFrame.Time;
				replayFrame2.HasHead = replayFrame.HasHead;
				replayFrame2.HasLeftHand = replayFrame.HasLeftHand;
				replayFrame2.HasRightHand = replayFrame.HasRightHand;
				replayFrame2.HasWeapon = replayFrame.HasWeapon;
				replayFrame2.Head.Position = replayFrame.Head.Position;
				replayFrame2.Head.Rotation = replayFrame.Head.Rotation;
				replayFrame2.LeftHand.Position = replayFrame.LeftHand.Position;
				replayFrame2.LeftHand.Rotation = replayFrame.LeftHand.Rotation;
				replayFrame2.RightHand.Position = replayFrame.RightHand.Position;
				replayFrame2.RightHand.Rotation = replayFrame.RightHand.Rotation;
				replayFrame2.Weapon.Position = replayFrame.Weapon.Position;
				replayFrame2.Weapon.Rotation = replayFrame.Weapon.Rotation;
				replayFrame2.WeaponName = replayFrame.WeaponName;
				lastKillReplay.Add(replayFrame2);
			}
			replayWeaponName = "UNKNOWN";
			for (int num = lastKillReplay.Count - 1; num >= 0; num--)
			{
				if (lastKillReplay[num].HasWeapon && !string.IsNullOrWhiteSpace(lastKillReplay[num].WeaponName))
				{
					replayWeaponName = lastKillReplay[num].WeaponName;
					break;
				}
			}
			((MelonBase)this).LoggerInstance.Msg("ECHOX OS // COMBAT ECHO CAPTURED // " + lastKillReplay.Count + " frames // " + replayWeaponName);
		}

		private void PlayLastCombatReplay()
		{
			if (!combatReplayEnabled.Value || combatReplayPlaying || replayIntroActive || replayOutroActive || lastKillReplay.Count < 2)
			{
				return;
			}
			CreateReplayObjects();
			if (!((Object)(object)replayRoot == (Object)null))
			{
				combatReplayOriginalTimeScale = Time.timeScale;
				if (combatReplayFreezeWorld.Value)
				{
					Time.timeScale = 0f;
				}
				replayKillPulse = 0f;
				replayKillPulseTriggered = false;
				if (replayIntroEnabled.Value)
				{
					replayIntroActive = true;
					replayOutroActive = false;
					replayIntroStart = Time.unscaledTime;
					combatReplayPlaying = false;
					ShowReplayIntro();
				}
				else
				{
					BeginActualReplay();
				}
			}
		}

		private void BeginActualReplay()
		{
			replayIntroActive = false;
			combatReplayPlaying = true;
			combatReplayStartTime = Time.unscaledTime;
			if (replayShowKillStats.Value)
			{
				CreateReplayStatsPanel();
			}
		}

		private void ShowReplayIntro()
		{
			replayIntroText = CreateReplayText("ECHOX_ReplayIntro", "ECHOX OS\nRECONSTRUCTING COMBAT ECHO...", centered: true);
		}

		private void BeginReplayOutro()
		{
			if (!replayOutroActive)
			{
				combatReplayPlaying = false;
				if ((Object)(object)replayStatsText != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)replayStatsText).gameObject);
					replayStatsText = null;
				}
				if (!replayOutroEnabled.Value)
				{
					FinishCombatReplay();
					return;
				}
				replayOutroActive = true;
				replayOutroStart = Time.unscaledTime;
				replayOutroText = CreateReplayText("ECHOX_ReplayOutro", "ECHO COMPLETE\n" + GetLastReplayDuration().ToString("0.00") + " SEC ANALYZED", centered: true);
			}
		}

		private void UpdateReplayIntroOutro()
		{
			float unscaledTime = Time.unscaledTime;
			if (replayIntroActive)
			{
				FaceReplayTextToPlayer(replayIntroText);
				if (unscaledTime - replayIntroStart >= 1.1f)
				{
					replayIntroActive = false;
					if ((Object)(object)replayIntroText != (Object)null)
					{
						Object.Destroy((Object)(object)((Component)replayIntroText).gameObject);
						replayIntroText = null;
					}
					BeginActualReplay();
				}
			}
			else if (replayOutroActive)
			{
				FaceReplayTextToPlayer(replayOutroText);
				if (unscaledTime - replayOutroStart >= 1.3f)
				{
					replayOutroActive = false;
					FinishCombatReplay();
				}
			}
		}

		private float GetLastReplayDuration()
		{
			if (lastKillReplay.Count < 2)
			{
				return 0f;
			}
			return lastKillReplay[lastKillReplay.Count - 1].Time - lastKillReplay[0].Time;
		}

		private void UpdateCombatReplayPlayback()
		{
			if (!combatReplayPlaying || lastKillReplay.Count < 2)
			{
				return;
			}
			float num = Mathf.Max(0.1f, combatReplaySpeed.Value);
			if (combatReplaySlowMotion.Value)
			{
				num *= 0.35f;
			}
			float num2 = (Time.unscaledTime - combatReplayStartTime) * num;
			ReplayFrame replayFrame = lastKillReplay[0];
			float num3 = lastKillReplay[lastKillReplay.Count - 1].Time - replayFrame.Time;
			if (num3 <= 0.001f)
			{
				BeginReplayOutro();
				return;
			}
			if (num2 >= num3)
			{
				BeginReplayOutro();
				return;
			}
			float num4 = replayFrame.Time + num2;
			int num5 = FindReplayFrameIndex(num4);
			ReplayFrame replayFrame2 = lastKillReplay[num5];
			ReplayFrame replayFrame3 = lastKillReplay[Mathf.Min(num5 + 1, lastKillReplay.Count - 1)];
			float num6 = Mathf.Max(0.0001f, replayFrame3.Time - replayFrame2.Time);
			float t = Mathf.Clamp01((num4 - replayFrame2.Time) / num6);
			SetReplayObjectState(replayHeadGhost, replayFrame2.HasHead && replayFrame3.HasHead);
			if (replayFrame2.HasHead && replayFrame3.HasHead)
			{
				ApplyReplayTransform(replayHeadGhost, replayFrame2.Head, replayFrame3.Head, t);
			}
			SetReplayObjectState(replayLeftGhost, replayFrame2.HasLeftHand && replayFrame3.HasLeftHand);
			if (replayFrame2.HasLeftHand && replayFrame3.HasLeftHand)
			{
				ApplyReplayTransform(replayLeftGhost, replayFrame2.LeftHand, replayFrame3.LeftHand, t);
			}
			SetReplayObjectState(replayRightGhost, replayFrame2.HasRightHand && replayFrame3.HasRightHand);
			if (replayFrame2.HasRightHand && replayFrame3.HasRightHand)
			{
				ApplyReplayTransform(replayRightGhost, replayFrame2.RightHand, replayFrame3.RightHand, t);
			}
			SetReplayObjectState(replayWeaponGhost, replayFrame2.HasWeapon && replayFrame3.HasWeapon);
			if (replayFrame2.HasWeapon && replayFrame3.HasWeapon)
			{
				ApplyReplayTransform(replayWeaponGhost, replayFrame2.Weapon, replayFrame3.Weapon, t);
			}
			UpdateNPCReplay(num4);
			UpdateReplayStatsPanel(num2, num3);
			if (replayKillPulseEnabled.Value)
			{
				UpdateKillMomentPulse(num2, num3);
			}
			UpdateReplayMaterialColor();
		}

		private int FindReplayFrameIndex(float targetTime)
		{
			for (int i = 0; i < lastKillReplay.Count - 1; i++)
			{
				if (lastKillReplay[i + 1].Time >= targetTime)
				{
					return i;
				}
			}
			return Mathf.Max(0, lastKillReplay.Count - 2);
		}

		private void UpdateNPCReplay(float targetTime)
		{
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			if (!replayShowNPC.Value || (Object)(object)replayNPCGhost == (Object)null || lastKillNPCReplay.Count < 2)
			{
				SetReplayObjectState(replayNPCGhost, state: false);
				SetReplayObjectState(replayNPCHeadGhost, state: false);
				return;
			}
			SetReplayObjectState(replayNPCGhost, state: true);
			SetReplayObjectState(replayNPCHeadGhost, state: true);
			int num = 0;
			for (int i = 0; i < lastKillNPCReplay.Count - 1; i++)
			{
				if (lastKillNPCReplay[i + 1].Time >= targetTime)
				{
					num = i;
					break;
				}
			}
			NPCReplayFrame nPCReplayFrame = lastKillNPCReplay[num];
			NPCReplayFrame nPCReplayFrame2 = lastKillNPCReplay[Mathf.Min(num + 1, lastKillNPCReplay.Count - 1)];
			float num2 = Mathf.Max(0.0001f, nPCReplayFrame2.Time - nPCReplayFrame.Time);
			float num3 = Mathf.Clamp01((targetTime - nPCReplayFrame.Time) / num2);
			Vector3 val = Vector3.Lerp(nPCReplayFrame.Position, nPCReplayFrame2.Position, num3);
			Quaternion val2 = Quaternion.Slerp(nPCReplayFrame.Rotation, nPCReplayFrame2.Rotation, num3);
			replayNPCGhost.transform.position = val;
			replayNPCGhost.transform.rotation = val2;
			replayNPCHeadGhost.transform.position = val + val2 * new Vector3(0f, 0.9f, 0f);
			replayNPCHeadGhost.transform.rotation = val2;
		}

		private void CreateReplayStatsPanel()
		{
			string text = BuildReplayStatsText(0f, GetLastReplayDuration());
			replayStatsText = CreateReplayText("ECHOX_KillStats", text, centered: false);
			PositionReplayStatsPanel();
		}

		private string BuildReplayStatsText(float elapsed, float replayLength)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("ECHOX OS // KILL DATA");
			stringBuilder.AppendLine("--------------------");
			stringBuilder.Append("WEAPON: ");
			stringBuilder.AppendLine(replayWeaponName.ToUpperInvariant());
			stringBuilder.Append("DISTANCE: ");
			stringBuilder.Append(replayKillDistance.ToString("0.00"));
			stringBuilder.AppendLine(" M");
			stringBuilder.Append("HIT VELOCITY: ");
			stringBuilder.Append(replayHitVelocity.ToString("0.0"));
			stringBuilder.AppendLine(" M/S");
			stringBuilder.Append("REPLAY TIME: ");
			stringBuilder.Append(elapsed.ToString("0.00"));
			stringBuilder.Append(" / ");
			stringBuilder.Append(replayLength.ToString("0.00"));
			stringBuilder.AppendLine(" SEC");
			stringBuilder.Append("STATUS: ");
			if (replayLength - elapsed <= 0.15f)
			{
				stringBuilder.AppendLine("KILL FRAME");
			}
			else
			{
				stringBuilder.AppendLine("RECONSTRUCTING");
			}
			return stringBuilder.ToString().TrimEnd();
		}

		private void UpdateReplayStatsPanel(float elapsed, float replayLength)
		{
			if (!replayShowKillStats.Value)
			{
				if ((Object)(object)replayStatsText != (Object)null)
				{
					((Component)replayStatsText).gameObject.SetActive(false);
				}
				return;
			}
			if ((Object)(object)replayStatsText == (Object)null)
			{
				CreateReplayStatsPanel();
			}
			if (!((Object)(object)replayStatsText == (Object)null))
			{
				((Component)replayStatsText).gameObject.SetActive(true);
				replayStatsText.text = BuildReplayStatsText(elapsed, replayLength);
				PositionReplayStatsPanel();
			}
		}

		private void PositionReplayStatsPanel()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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_005e: 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)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)replayStatsText == (Object)null) && !((Object)(object)Player.Head == (Object)null))
			{
				Transform head = Player.Head;
				((Component)replayStatsText).transform.position = head.position + head.forward * 1.05f + head.right * 0.42f - head.up * 0.05f;
				FaceReplayTextToPlayer(replayStatsText);
			}
		}

		private TextMesh CreateReplayText(string name, string text, bool centered)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0075: 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_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			if ((Object)(object)replayRoot != (Object)null)
			{
				val.transform.SetParent(replayRoot.transform, true);
			}
			TextMesh val2 = val.AddComponent<TextMesh>();
			val2.text = text;
			val2.fontSize = 40;
			val2.characterSize = 0.006f;
			val2.richText = false;
			val2.anchor = (TextAnchor)(centered ? 4 : 0);
			val2.alignment = (TextAlignment)(centered ? 1 : 0);
			val2.color = GetReplayColor(0.9f);
			if ((Object)(object)currentFont != (Object)null)
			{
				val2.font = currentFont;
				Renderer component = ((Component)val2).GetComponent<Renderer>();
				if ((Object)(object)component != (Object)null && (Object)(object)currentFont.material != (Object)null)
				{
					component.sharedMaterial = currentFont.material;
				}
			}
			if ((Object)(object)Player.Head != (Object)null)
			{
				Transform head = Player.Head;
				val.transform.position = head.position + head.forward * 1.1f;
				FaceReplayTextToPlayer(val2);
			}
			return val2;
		}

		private void FaceReplayTextToPlayer(TextMesh text)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)text == (Object)null) && !((Object)(object)Player.Head == (Object)null))
			{
				Vector3 val = ((Component)text).transform.position - Player.Head.position;
				if (((Vector3)(ref val)).sqrMagnitude > 0.0001f)
				{
					((Component)text).transform.rotation = Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up);
				}
			}
		}

		private void UpdateKillMomentPulse(float elapsed, float replayLength)
		{
			float num = replayLength - elapsed;
			if (!replayKillPulseTriggered && num <= 0.15f)
			{
				replayKillPulseTriggered = true;
				replayKillPulse = 1f;
			}
			if (replayKillPulseTriggered)
			{
				replayKillPulse = Mathf.MoveTowards(replayKillPulse, 0f, Time.unscaledDeltaTime * 4.5f);
				ApplyReplayPulse();
			}
		}

		private void ApplyReplayPulse()
		{
			//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)
			//IL_002b: 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)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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)
			float num = Mathf.Clamp01(replayKillPulse);
			if ((Object)(object)replayMaterial != (Object)null)
			{
				Color replayColor = GetReplayColor(0.32f);
				Color val = default(Color);
				((Color)(ref val))..ctor(Mathf.Clamp01(replayColor.r + num * 0.35f), Mathf.Clamp01(replayColor.g + num * 0.35f), Mathf.Clamp01(replayColor.b + num * 0.35f), Mathf.Clamp01(replayColor.a + num * 0.55f));
				if (replayMaterial.HasProperty("_Color"))
				{
					replayMaterial.SetColor("_Color", val);
				}
				if (replayMaterial.HasProperty("_TintColor"))
				{
					replayMaterial.SetColor("_TintColor", val);
				}
			}
			if ((Object)(object)replayImpactMarker != (Object)null)
			{
				float num2 = Mathf.Lerp(0.06f, 0.14f, num);
				replayImpactMarker.transform.localScale = Vector3.one * num2;
			}
		}

		private void CreateReplayObjects()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_002d: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			ClearReplayObjects();
			replayRoot = new GameObject("ECHOX_CombatReplay");
			replayHeadGhost = CreateReplayMarker("Head", (PrimitiveType)0, new Vector3(0.17f, 0.17f, 0.17f));
			replayLeftGhost = CreateReplayMarker("LeftHand", (PrimitiveType)0, new Vector3(0.075f, 0.075f, 0.075f));
			replayRightGhost = CreateReplayMarker("RightHand", (PrimitiveType)0, new Vector3(0.075f, 0.075f, 0.075f));
			replayWeaponGhost = CreateReplayMarker("Weapon", (PrimitiveType)3, new Vector3(0.045f, 0.045f, 0.24f));
			replayNPCGhost = CreateReplayMarker("NPC_Body", (PrimitiveType)1, new Vector3(0.32f, 0.65f, 0.32f));
			replayNPCHeadGhost = CreateReplayMarker("NPC_Head", (PrimitiveType)0, new Vector3(0.18f, 0.18f, 0.18f));
			if (!replayShowNPC.Value)
			{
				replayNPCGhost.SetActive(false);
				replayNPCHeadGhost.SetActive(false);
			}
			CreateReplayPaths();
			CreateBulletTrajectory();
			CreateImpactMarker();
		}

		private GameObject CreateReplayMarker(string name, PrimitiveType primitive, Vector3 scale)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GameObject.CreatePrimitive(primitive);
			((Object)val).name = "ECHOX_Replay_" + name;
			if ((Object)(object)replayRoot != (Object)null)
			{
				val.transform.SetParent(replayRoot.transform, true);
			}
			val.transform.localScale = scale;
			Collider component = val.GetComponent<Collider>();
			if ((Object)(object)component != (Object)null)
			{
				Object.Destroy((Object)(object)component);
			}
			Renderer component2 = val.GetComponent<Renderer>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.shadowCastingMode = (ShadowCastingMode)0;
				component2.receiveShadows = false;
				component2.lightProbeUsage = (LightProbeUsage)0;
				component2.reflectionProbeUsage = (ReflectionProbeUsage)0;
				component2.sharedMaterial = GetReplayMaterial();
			}
			return val;
		}

		private void SetReplayObjectState(GameObject obj, bool state)
		{
			if ((Object)(object)obj != (Object)null && obj.activeSelf != state)
			{
				obj.SetActive(state);
			}
		}

		private void ApplyReplayTransform(GameObject obj, ReplayTransform a, ReplayTransform b, float t)
		{
			//IL_0011: 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_001e: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)obj == (Object)null))
			{
				obj.transform.position = Vector3.Lerp(a.Position, b.Position, t);
				obj.transform.rotation = Quaternion.Slerp(a.Rotation, b.Rotation, t);
			}
		}

		private void CreateReplayPaths()
		{
			if (replayShowHeadPath.Value)
			{
				replayHeadPath = CreateReplayPath("HeadPath", 0);
			}
			if (replayShowHandPaths.Value)
			{
				replayLeftPath = CreateReplayPath("LeftHandPath", 1);
				replayRightPath = CreateReplayPath("RightHandPath", 2);
			}
			if (replayShowWeaponPath.Value)
			{
				replayWeaponPath = CreateReplayPath("WeaponPath", 3);
			}
		}

		private LineRenderer CreateReplayPath(string name, int pathType)
		{
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: 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)
			List<Vector3> list = new List<Vector3>();
			for (int i = 0; i < lastKillReplay.Count; i++)
			{
				ReplayFrame replayFrame = lastKillReplay[i];
				if (pathType == 0 && replayFrame.HasHead)
				{
					list.Add(replayFrame.Head.Position);
				}
				else if (pathType == 1 && replayFrame.HasLeftHand)
				{
					list.Add(replayFrame.LeftHand.Position);
				}
				else if (pathType == 2 && replayFrame.HasRightHand)
				{
					list.Add(replayFrame.RightHand.Position);
				}
				else if (pathType == 3 && replayFrame.HasWeapon)
				{
					list.Add(replayFrame.Weapon.Position);
				}
			}
			if (list.Count < 2)
			{
				return null;
			}
			GameObject val = new GameObject("ECHOX_" + name);
			val.transform.SetParent(replayRoot.transform, true);
			LineRenderer val2 = val.AddComponent<LineRenderer>();
			val2.useWorldSpace = true;
			val2.loop = false;
			val2.positionCount = list.Count;
			val2.startWidth = 0.008f;
			val2.endWidth = 0.003f;
			((Renderer)val2).sharedMaterial = GetReplayMaterial();
			Color val3 = (val2.startColor = GetReplayColor(0.5f));
			val2.endColor = new Color(val3.r, val3.g, val3.b, 0.08f);
			for (int j = 0; j < list.Count; j++)
			{
				val2.SetPosition(j, list[j]);
			}
			return val2;
		}

		private void CreateBulletTrajectory()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_00d9: 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_00e5: 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: 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_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			if (replayShowBulletPath.Value && hasLastShot && replayHasImpactPoint && lastKillReplay.Count != 0 && !(Mathf.Abs(lastKillReplay[lastKillReplay.Count - 1].Time - lastShotTime) > 2f))
			{
				GameObject val = new GameObject("ECHOX_BulletTrajectory");
				val.transform.SetParent(replayRoot.transform, true);
				replayBulletPath = val.AddComponent<LineRenderer>();
				replayBulletPath.useWorldSpace = true;
				replayBulletPath.positionCount = 2;
				replayBulletPath.startWidth = 0.006f;
				replayBulletPath.endWidth = 0.002f;
				((Renderer)replayBulletPath).sharedMaterial = GetReplayMaterial();
				Color replayColor = GetReplayColor(0.8f);
				replayBulletPath.startColor = replayColor;
				replayBulletPath.endColor = new Color(replayColor.r, replayColor.g, replayColor.b, 0.15f);
				replayBulletPath.SetPosition(0, lastShotOrigin);
				replayBulletPath.SetPosition(1, replayImpactPoint);
			}
		}

		private void CreateImpactMarker()
		{
			//IL_002d: 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)
			if (replayShowImpactMarker.Value && replayHasImpactPoint)
			{
				replayImpactMarker = CreateReplayMarker("Impact", (PrimitiveType)0, new Vector3(0.06f, 0.06f, 0.06f));
				if ((Object)(object)replayImpactMarker != (Object)null)
				{
					replayImpactMarker.transform.position = replayImpactPoint;
				}
			}
		}

		private Material GetReplayMaterial()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			if ((Object)(object)replayMaterial != (Object)null)
			{
				UpdateReplayMaterialColor();
				return replayMaterial;
			}
			Shader val = Shader.Find("Sprites/Default");
			if ((Object)(object)val == (Object)null)
			{
				val = Shader.Find("Unlit/Color");
			}
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			replayMaterial = new Material(val);
			((Object)replayMaterial).name = "ECHOX_ReplayMaterial";
			if (replayMaterial.HasProperty("_ZWrite"))
			{
				replayMaterial.SetInt("_ZWrite", 0);
			}
			replayMaterial.renderQueue = 3000;
			UpdateReplayMaterialColor();
			return replayMaterial;
		}

		private Color GetReplayColor(float alpha)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			return (Color)(Mathf.Clamp(replayHologramStyle.Value, 0, 2) switch
			{
				1 => new Color(textRed.Value, textGreen.Value, textBlue.Value, Mathf.Clamp01(alpha * 1.25f)), 
				2 => new Color(Mathf.Clamp01(textRed.Value + 0.2f), Mathf.Clamp01(textGreen.Value + 0.2f), Mathf.Clamp01(textBlue.Value + 0.2f), Mathf.Clamp01(alpha * 0.55f)), 
				_ => new Color(textRed.Value, textGreen.Value, textBlue.Value, Mathf.Clamp01(alpha)), 
			});
		}

		private void UpdateReplayMaterialColor()
		{
			//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)
			//IL_0046: 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)
			if (!((Object)(object)replayMaterial == (Object)null) && !(replayKillPulse > 0.001f))
			{
				Color replayColor = GetReplayColor(0.32f);
				if (replayMaterial.HasProperty("_Color"))
				{
					replayMaterial.SetColor("_Color", replayColor);
				}
				if (replayMaterial.HasProperty("_TintColor"))
				{
					replayMaterial.SetColor("_TintColor", replayColor);
				}
			}
		}

		private void StopCombatReplay()
		{
			combatReplayPlaying = false;
			replayIntroActive = false;
			replayOutroActive = false;
			replayKillPulse = 0f;
			replayKillPulseTriggered = false;
			if (combatReplayFreezeWorld != null && combatReplayFreezeWorld.Value)
			{
				Time.timeScale = combatReplayOriginalTimeScale;
			}
			ClearReplayObjects();
		}

		private void FinishCombatReplay()
		{
			combatReplayPlaying = false;
			replayIntroActive = false;
			replayOutroActive = false;
			replayKillPulse = 0f;
			replayKillPulseTriggered = false;
			if (combatReplayFreezeWorld.Value)
			{
				Time.timeScale = combatReplayOriginalTimeScale;
			}
			ClearReplayObjects();
		}

		private void ClearReplayObjects()
		{
			if ((Object)(object)replayRoot != (Object)null)
			{
				Object.Destroy((Object)(object)replayRoot);
				replayRoot = null;
			}
			replayHeadGhost = null;
			replayLeftGhost = null;
			replayRightGhost = null;
			replayWeaponGhost = null;
			replayNPCGhost = null;
			replayNPCHeadGhost = null;
			replayImpactMarker = null;
			replayHeadPath = null;
			replayLeftPath = null;
			replayRightPath = null;
			replayWeaponPath = null;
			replayBulletPath = null;
			replayStatsText = null;
			replayIntroText = null;
			replayOutroText = null;
		}

		private float GetCurrentWeaponVelocity()
		{
			//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)
			try
			{
				GameObject objectInHand = Player.GetObjectInHand(Player.RightHand);
				if ((Object)(object)objectInHand == (Object)null)
				{
					objectInHand = Player.GetObjectInHand(Player.LeftHand);
				}
				if ((Object)(object)objectInHand == (Object)null)
				{
					return 0f;
				}
				GameObject val = FindWeaponRoot(objectInHand);
				if ((Object)(object)val == (Object)null)
				{
					return 0f;
				}
				Rigidbody[] array = Il2CppArrayBase<Rigidbody>.op_Implicit(val.GetComponentsInChildren<Rigidbody>(true));
				float num = 0f;
				foreach (Rigidbody val2 in array)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						float num2 = num;
						Vector3 velocity = val2.velocity;
						num = Mathf.Max(num2, ((Vector3)(ref velocity)).magnitude);
					}
				}
				return num;
			}
			catch
			{
				return 0f;
			}
		}

		private Mesh GetInvertedOutlineMesh(Mesh source)
		{
			if ((Object)(object)source == (Object)null)
			{
				return null;
			}
			if (invertedOutlineMeshCache.TryGetValue(source, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			try
			{
				Mesh val = Object.Instantiate<Mesh>(source);
				((Object)val).name = ((Object)source).name + "_ECHOX_Inverted";
				for (int i = 0; i < val.subMeshCount; i++)
				{
					int[] array = Il2CppArrayBase<int>.op_Implicit((Il2CppArrayBase<int>)(object)val.GetTriangles(i));
					for (int j = 0; j + 2 < array.Length; j += 3)
					{
						int num = array[j + 1];
						array[j + 1] = array[j + 2];
						array[j + 2] = num;
					}
					val.SetTriangles(Il2CppStructArray<int>.op_Implicit(array), i);
				}
				val.RecalculateBounds();
				invertedOutlineMeshCache[source] = val;
				return val;
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Warning("ECHOX inverted mesh failed: " + ex.Message);
				return source;
			}
		}

		private void ShowWeaponOutline(GameObject weapon)
		{
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Expected O, but got Unknown
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Expected O, but got Unknown
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			if (!weaponOutlineEnabled.Value || (Object)(object)weapon == (Object)null)
			{
				return;
			}
			ClearWeaponOutline();
			try
			{
				CreateOutlineMaterial();
				if ((Object)(object)weaponOutlineMaterial == (Object)null)
				{
					return;
				}
				float num = Mathf.Clamp(weaponOutlineThickness.Value, 0.001f, 0.015f);
				float num2 = 1f + num;
				MeshRenderer[] array = Il2CppArrayBase<MeshRenderer>.op_Implicit(weapon.GetComponentsInChildren<MeshRenderer>(true));
				foreach (MeshRenderer val in array)
				{
					if ((Object)(object)val == (Object)null || !((Renderer)val).enabled)
					{
						continue;
					}
					MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component.sharedMesh == (Object)null) && !ShouldSkipOutlineMesh(component.sharedMesh, ((Component)val).gameObject))
					{
						Mesh invertedOutlineMesh = GetInvertedOutlineMesh(component.sharedMesh);
						if (!((Object)(object)invertedOutlineMesh == (Object)null))
						{
							GameObject val2 = new GameObject("ECHOX_OutlineMesh");
							val2.transform.SetParent(((Component)val).transform, false);
							val2.transform.localPosition = Vector3.zero;
							val2.transform.localRotation = Quaternion.identity;
							val2.transform.localScale = new Vector3(num2, num2, num2);
							val2.AddComponent<MeshFilter>().sharedMesh = invertedOutlineMesh;
							MeshRenderer val3 = val2.AddComponent<MeshRenderer>();
							((Renderer)val3).sharedMaterial = weaponOutlineMaterial;
							((Renderer)val3).shadowCastingMode = (ShadowCastingMode)0;
							((Renderer)val3).receiveShadows = false;
							((Renderer)val3).lightProbeUsage = (LightProbeUsage)0;
							((Renderer)val3).reflectionProbeUsage = (ReflectionProbeUsage)0;
							outlineObjects.Add(val2);
							outlineRenderers.Add((Renderer)(object)val3);
						}
					}
				}
				SkinnedMeshRenderer[] array2 = Il2CppArrayBase<SkinnedMeshRenderer>.op_Implicit(weapon.GetComponentsInChildren<SkinnedMeshRenderer>(true));
				foreach (SkinnedMeshRenderer val4 in array2)
				{
					if (!((Object)(object)val4 == (Object)null) && ((Renderer)val4).enabled && !((Object)(object)val4.sharedMesh == (Object)null) && !ShouldSkipOutlineMesh(val4.sharedMesh, ((Component)val4).gameObject))
					{
						Mesh invertedOutlineMesh2 = GetInvertedOutlineMesh(val4.sharedMesh);
						if (!((Object)(object)invertedOutlineMesh2 == (Object)null))
						{
							GameObject val5 = new GameObject("ECHOX_OutlineSkinned");
							val5.transform.SetParent(((Component)val4).transform, false);
							val5.transform.localPosition = Vector3.zero;
							val5.transform.localRotation = Quaternion.identity;
							val5.transform.localScale = new Vector3(num2, num2, num2);
							SkinnedMeshRenderer val6 = val5.AddComponent<SkinnedMeshRenderer>();
							val6.sharedMesh = invertedOutlineMesh2;
							val6.bones = val4.bones;
							val6.rootBone = val4.rootBone;
							((Renderer)val6).localBounds = ((Renderer)val4).localBounds;
							val6.updateWhenOffscreen = false;
							((Renderer)val6).sharedMaterial = weaponOutlineMaterial;
							((Renderer)val6).shadowCastingMode = (ShadowCastingMode)0;
							((Renderer)val6).receiveShadows = false;
							((Renderer)val6).lightProbeUsage = (LightProbeUsage)0;
							((Renderer)val6).reflectionProbeUsage = (ReflectionProbeUsage)0;
							outlineObjects.Add(val5);
							outlineRenderers.Add((Renderer)(object)val6);
						}
					}
				}
				outlineStartTime = Time.unscaledTime;
				outlineEndTime = outlineStartTime + Mathf.Max(0.2f, weaponOutlineDuration.Value);
				outlineActive = outlineObjects.Count > 0;
				ApplyOutlineColor(0.7f);
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Warning("ECHOX outline failed: " + ex.Message);
				ClearWeaponOutline();
			}
		}

		private bool ShouldSkipOutlineMesh(Mesh mesh, GameObject sourceObject)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)mesh == (Object)null || (Object)(object)sourceObject == (Object)null)
			{
				return true;
			}
			string text = ((Object)sourceObject).name.ToLowerInvariant();
			if (text.Contains("echox_outline"))
			{
				return true;
			}
			Bounds bounds = mesh.bounds;
			if (((Bounds)(ref bounds)).size.x > 5f || ((Bounds)(ref bounds)).size.y > 5f || ((Bounds)(ref bounds)).size.z > 5f)
			{
				return true;
			}
			if (text.Contains("collider") || text.Contains("collision") || text.Contains("trigger") || text.Contains("bounds") || text.Contains("bounding") || text.Contains("helper") || text.Contains("proxy"))
			{
				return true;
			}
			return false;
		}

		private void CreateOutlineMaterial()
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			if ((Object)(object)weaponOutlineMaterial != (Object)null)
			{
				return;
			}
			Shader val = Shader.Find("Unlit/Color");
			if ((Object)(object)val == (Object)null)
			{
				val = Shader.Find("Sprites/Default");
			}
			if ((Object)(object)val == (Object)null)
			{
				((MelonBase)this).LoggerInstance.Warning("ECHOX could not find outline shader.");
				return;
			}
			weaponOutlineMaterial = new Material(val);
			((Object)weaponOutlineMaterial).name = "ECHOX_InvertedHullOutline";
			if (weaponOutlineMaterial.HasProperty("_Cull"))
			{
				weaponOutlineMaterial.SetInt("_Cull", 2);
			}
			if (weaponOutlineMaterial.HasProperty("_ZWrite"))
			{
				weaponOutlineMaterial.SetInt("_ZWrite", 1);
			}
			if (weaponOutlineMaterial.HasProperty("_ZTest"))
			{
				weaponOutlineMaterial.SetInt("_ZTest", 4);
			}
			weaponOutlineMaterial.renderQueue = 2001;
		}

		private void UpdateWeaponOutline()
		{
			if (!outlineActive)
			{
				return;
			}
			if (outlineObjects.Count == 0)
			{
				ClearWeaponOutline();
				return;
			}
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime >= outlineEndTime)
			{
				ClearWeaponOutline();
				return;
			}
			float num = Mathf.Max(0.2f, weaponOutlineDuration.Value);
			float num2 = outlineEndTime - unscaledTime;
			float num3 = Mathf.Min(0.35f, num * 0.45f);
			float num4 = 0.7f;
			if (num2 < num3)
			{
				num4 *= Mathf.Clamp01(num2 / num3);
			}
			float num5 = 0.88f + Mathf.Sin(unscaledTime * 16f) * 0.12f;
			ApplyOutlineColor(num4 * num5);
		}

		private void ApplyOutlineColor(float alpha)
		{
			//IL_005a: 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)
			if (!((Object)(object)weaponOutlineMaterial == (Object)null))
			{
				Color val = default(Color);
				((Color)(ref val))..ctor(textRed.Value, textGreen.Value, textBlue.Value, Mathf.Clamp01(alpha));
				if (weaponOutlineMaterial.HasProperty("_Color"))
				{
					weaponOutlineMaterial.SetColor("_Color", val);
				}
				if (weaponOutlineMaterial.HasProperty("_TintColor"))
				{
					weaponOutlineMaterial.SetColor("_TintColor", val);
				}
			}
		}

		private void ClearWeaponOutline()
		{
			outlineActive = false;
			for (int i = 0; i < outlineObjects.Count; i++)
			{
				GameObject val = outlineObjects[i];
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
			}
			outlineObjects.Clear();
			outlineRenderers.Clear();
		}

		private void RestoreAutomaticInspector()
		{
			try
			{
				GameObject objectInHand = Player.GetObjectInHand(Player.LeftHand);
				GameObject objectInHand2 = Player.GetObjectInHand(Player.RightHand);
				GameObject val = (((Object)(object)objectInHand2 != (Object)null) ? objectInHand2 : objectInHand);
				if ((Object)(object)val == (Object)null)
				{
					HideInspector(immediate: true);
					return;
				}
				WeaponInfo weaponInfo = InspectWeapon(val);
				if (weaponInfo.Kind == WeaponKind.Unknown && weaponInfo.Mass <= 0.01f)
				{
					HideInspector(immediate: true);
					return;
				}
				manualInfoVisible = false;
				quickScanVisible = false;
				quickScannedWeapon = null;
				ShowInspector(weaponInfo.Anchor, weaponInfo);
				showUntil = Time.unscaledTime + displayDuration.Value;
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Warning("ECHOX automatic restore failed: " + ex.Message);
			}
		}

		private void UpdateTwoHandInfoToggle()
		{
			if (!enabled.Value || !twoHandInfoToggle.Value)
			{
				supportSecondaryWasPressed = false;
				return;
			}
			GameObject objectInHand;
			GameObject objectInHand2;
			try
			{
				objectInHand = Player.GetObjectInHand(Player.LeftHand);
				objectInHand2 = Player.GetObjectInHand(Player.RightHand);
			}
			catch
			{
				return;
			}
			if ((Object)(object)objectInHand == (Object)null || (Object)(object)objectInHand2 == (Object)null)
			{
				supportSecondaryWasPressed = false;
				return;
			}
			GameObject val = FindWeaponRoot(objectInHand);
			GameObject val2 = FindWeaponRoot(objectInHand2);
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)val != (Object)(object)val2)
			{
				supportSecondaryWasPressed = false;
			}
			else
			{
				if (!primaryHandKnown)
				{
					return;
				}
				bool leftHand = !primaryHandIsLeft;
				bool secondaryFaceButton = GetSecondaryFaceButton(leftHand);
				if (secondaryFaceButton && !supportSecondaryWasPressed)
				{
					if (manualInfoVisible)
					{
						manualInfoVisible = false;
						HideInspector(immediate: true);
					}
					else
					{
						WeaponInfo weaponInfo = InspectWeapon(val);
						quickScanVisible = false;
						quickScannedWeapon = null;
						ShowInspector(weaponInfo.Anchor, weaponInfo);
						manualInfoVisible = true;
					}
				}
				supportSecondaryWasPressed = secondaryFaceButton;
			}
		}

		private void UpdateQuickScan()
		{
			if (!enabled.Value || !quickScanEnabled.Value)
			{
				quickLeftWasPressed = false;
				quickRightWasPressed = false;
				return;
			}
			bool secondaryFaceButton = GetSecondaryFaceButton(leftHand: true);
			bool secondaryFaceButton2 = GetSecondaryFaceButton(leftHand: false);
			if (twoHandInfoToggle.Value && IsHoldingSameWeaponTwoHanded())
			{
				quickLeftWasPressed = secondaryFaceButton;
				quickRightWasPressed = secondaryFaceButton2;
				return;
			}
			if (secondaryFaceButton && !quickLeftWasPressed)
			{
				TryQuickScan(leftHand: true);
			}
			if (secondaryFaceButton2 && !quickRightWasPressed)
			{
				TryQuickScan(leftHand: false);
			}
			quickLeftWasPressed = secondaryFaceButton;
			quickRightWasPressed = secondaryFaceButton2;
		}

		private bool IsHoldingSameWeaponTwoHanded()
		{
			try
			{
				GameObject objectInHand = Player.GetObjectInHand(Player.LeftHand);
				GameObject objectInHand2 = Player.GetObjectInHand(Player.RightHand);
				if ((Object)(object)objectInHand == (Object)null || (Object)(object)objectInHand2 == (Object)null)
				{
					return false;
				}
				GameObject val = FindWeaponRoot(objectInHand);
				GameObject val2 = FindWeaponRoot(objectInHand2);
				if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
				{
					return false;
				}
				return (Object)(object)val == (Object)(object)val2;
			}
			catch
			{
				return false;
			}
		}

		private void TryQuickScan(bool leftHand)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_0036: 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)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Hand val = (leftHand ? Player.LeftHand : Player.RightHand);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				Transform transform = ((Component)val).transform;
				Vector3 forward = transform.forward;
				RaycastHit val2 = default(RaycastHit);
				if (!Physics.Raycast(transform.position + forward * 0.05f, forward, ref val2, quickScanRange.Value, -5, (QueryTriggerInteraction)1) || (Object)(object)((RaycastHit)(ref val2)).collider == (Object)null)
				{
					return;
				}
				WeaponInfo weaponInfo = InspectWeapon(((Component)((RaycastHit)(ref val2)).collider).gameObject);
				if (weaponInfo.Kind != WeaponKind.Unknown || !(weaponInfo.Mass <= 0.01f))
				{
					if (quickScanVisible && (Object)(object)quickScannedWeapon == (Object)(object)weaponInfo.Anchor)
					{
						quickScanVisible = false;
						quickScannedWeapon = null;
						HideInspector(immediate: true);
						ClearWeaponOutline();
					}
					else
					{
						manualInfoVisible = false;
						quickScanVisible = true;
						quickScannedWeapon = weaponInfo.Anchor;
						ShowInspector(weaponInfo.Anchor, weaponInfo);
					}
				}
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Warning("ECHOX Quick Scan failed: " + ex.Message);
			}
		}

		private bool GetSecondaryFaceButton(bool leftHand)
		{
			//IL_0007: 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)
			try
			{
				InputDevice deviceAtXRNode = InputDevices.GetDeviceAtXRNode((XRNode)(leftHand ? 4 : 5));
				if (!((InputDevice)(ref deviceAtXRNode)).isValid)
				{
					return false;
				}
				bool result = default(bool);
				if (((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.secondaryButton, ref result))
				{
					return result;
				}
			}
			catch
			{
			}
			return false;
		}

		private void OnGrabObject(GameObject grabbedObject, Hand hand)
		{
			if (!enabled.Value || (Object)(object)grabbedObject == (Object)null)
			{
				return;
			}
			try
			{
				WeaponInfo weaponInfo = InspectWeapon(grabbedObject);
				if ((Object)(object)weaponInfo.Anchor == (Object)null || (weaponInfo.Kind == WeaponKind.Unknown && weaponInfo.Mass <= 0.01f))
				{
					return;
				}
				bool flag = hand == Player.LeftHand;
				if ((Object)(object)lastScannedWeapon != (Object)null && (Object)(object)lastScannedWeapon == (Object)(object)weaponInfo.Anchor)
				{
					if ((Object)(object)trackedTwoHandWeapon == (Object)null)
					{
						trackedTwoHandWeapon = weaponInfo.Anchor;
					}
					return;
				}
				if ((Object)(object)trackedTwoHandWeapon != (Object)(object)weaponInfo.Anchor)
				{
					trackedTwoHandWeapon = weaponInfo.Anchor;
					primaryHandIsLeft = flag;
					primaryHandKnown = true;
				}
				manualInfoVisible = false;
				quickScanVisible = false;
				quickScannedWeapon = null;
				ShowInspector(weaponInfo.Anchor, weaponInfo);
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Warning("ECHOX inspection failed: " + ex.Message);
			}
		}

		private void OnReleaseObject(Hand hand)
		{
			try
			{
				GameObject objectInHand = Player.GetObjectInHand(Player.LeftHand);
				GameObject objectInHand2 = Player.GetObjectInHand(Player.RightHand);
				GameObject val = (((Object)(object)objectInHand != (Object)null) ? FindWeaponRoot(objectInHand) : null);
				GameObject val2 = (((Object)(object)objectInHand2 != (Object)null) ? FindWeaponRoot(objectInHand2) : null);
				if ((Object)(object)lastScannedWeapon != (Object)null && (Object)(object)val != (Object)(object)lastScannedWeapon && (Object)(object)val2 != (Object)(object)lastScannedWeapon)
				{
					lastScannedWeapon = null;
				}
				if ((Object)(object)trackedTwoHandWeapon != (Object)null && (Object)(object)val != (Object)(object)trackedTwoHandWeapon && (Object)(object)val2 != (Object)(object)trackedTwoHandWeapon)
				{
					trackedTwoHandWeapon = null;
					primaryHandKnown = false;
					supportSecondaryWasPressed = false;
				}
				if ((Object)(object)objectInHand == (Object)null && (Object)(object)objectInHand2 == (Object)null && !quickScanVisible)
				{
					manualInfoVisible = false;
					HideInspector(immediate: false);
				}
			}
			catch
			{
			}
		}

		private WeaponInfo InspectWeapon(GameObject grabbedObject)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			WeaponInfo weaponInfo = new WeaponInfo();
			GameObject val = FindWeaponRoot(grabbedObject);
			if ((Object)(object)val == (Object)null)
			{
				val = grabbedObject;
			}
			weaponInfo.Anchor = val;
			weaponInfo.Name = CleanName(((Object)val).name);
			Rigidbody[] array = Il2CppArrayBase<Rigidbody>.op_Implicit(val.GetComponentsInChildren<Rigidbody>(true));
			float num = 0f;
			float num2 = 0f;
			if (array != null)
			{
				Rigidbody[] array2 = array;
				foreach (Rigidbody val2 in array2)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						num += Mathf.Max(0f, val2.mass);
						float num3 = num2;
						Vector3 velocity = val2.velocity;
						num2 = Mathf.Max(num3, ((Vector3)(ref velocity)).magnitude);
					}
				}
			}
			weaponInfo.Mass = num;
			weaponInfo.Speed = num2;
			weaponInfo.Impact = 0.5f * num * num2 * num2;
			bool flag = false;
			bool flag2 = ContainsWord(weaponInfo.Name, firearmWords);
			bool flag3 = ContainsWord(weaponInfo.Name, meleeWords);
			Component[] array3 = Il2CppArrayBase<Component>.op_Implicit(val.GetComponentsInChildren<Component>(true));
			HashSet<string> hashSet = new HashSet<string>();
			if (array3 != null)
			{
				int num4 = Mathf.Min(array3.Length, 48);
				for (int j = 0; j < num4; j++)
				{
					Component val3 = array3[j];
					if (!((Object)(object)val3 == (Object)null))
					{
						string name = ((object)val3).GetType().Name;
						if (val3 is Gun)
						{
							flag = true;
						}
						if (ContainsWord(name, firearmWords))
						{
							flag2 = true;
						}
						if (ContainsWord(name, meleeWords))
						{
							flag3 = true;
						}
						if (hashSet.Count < 8 && hashSet.Add(name))
						{
							weaponInfo.Components.Add(name);
						}
						if (weaponInfo.ExtraStats.Count < 5)
						{
							ExtractInterestingStats(val3, weaponInfo.ExtraStats);
						}
					}
				}
			}
			if (flag || flag2)
			{
				weaponInfo.Kind = WeaponKind.Firearm;
			}
			else if (flag3)
			{
				weaponInfo.Kind = WeaponKind.Melee;
			}
			else
			{
				weaponInfo.Kind = WeaponKind.Unknown;
			}
			return weaponInfo;
		}

		private GameObject FindWeaponRoot(GameObject grabbedObject)
		{
			if ((Object)(object)grabbedObject == (Object)null)
			{
				return null;
			}
			Transform val = grabbedObject.transform;
			Transform val2 = null;
			for (int i = 0; i < 10; i++)
			{
				if (!((Object)(object)val != (Object)null))
				{
					break;
				}
				if ((Object)(object)((Component)val).GetComponent<Gun>() != (Object)null)
				{
					return ((Component)val).gameObject;
				}
				if ((Object)(object)val2 == (Object)null && (Object)(object)((Component)val).GetComponent<Rigidbody>() != (Object)null)
				{
					val2 = val;
				}
				val = val.parent;
			}
			if ((Object)(object)val2 != (Object)null)
			{
				return ((Component)val2).gameObject;
			}
			return grabbedObject;
		}

		private void ExtractInterestingStats(Component component, List<string> output)
		{
			if ((Object)(object)component == (Object)null || output.Count >= 5)
			{
				return;
			}
			Type type = ((object)component).GetType();
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public;
			try
			{
				PropertyInfo[] properties = type.GetProperties(bindingAttr);
				int num = Math.Min(properties.Length, 40);
				for (int i = 0; i < num; i++)
				{
					if (output.Count >= 5)
					{
						break;
					}
					PropertyInfo propertyInfo = properties[i];
					if (!propertyInfo.CanRead || propertyInfo.GetIndexParameters().Length != 0)
					{
						continue;
					}
					string name = propertyInfo.Name.ToLowerInvariant();
					if (IsInterestingStat(name))
					{
						Type propertyType = propertyInfo.PropertyType;
						if (propertyType.IsPrimitive || !(propertyType != typeof(string)) || propertyType.IsEnum)
						{
							object value = propertyInfo.GetValue(component, null);
							AddStat(output, propertyInfo.Name, value);
						}
					}
				}
			}
			catch
			{
			}
			if (output.Count >= 5)
			{
				return;
			}
			try
			{
				FieldInfo[] fields = type.GetFields(bindingAttr);
				int num2 = Math.Min(fields.Length, 40);
				for (int j = 0; j < num2; j++)
				{
					if (output.Count >= 5)
					{
						break;
					}
					FieldInfo fieldInfo = fields[j];
					string name2 = fieldInfo.Name.ToLowerInvariant();
					if (IsInterestingStat(name2))
					{
						Type fieldType = fieldInfo.FieldType;
						if (fieldType.IsPrimitive || !(fieldType != typeof(string)) || fieldType.IsEnum)
						{
							AddStat(output, fieldInfo.Name, fieldInfo.GetValue(component));
						}
					}
				}
			}
			catch
			{
			}
		}

		private bool IsInterestingStat(string name)
		{
			if (!name.Contains("ammo") && !name.Contains("round") && !name.Contains("capacity") && !name.Contains("rpm") && !name.Contains("firerate") && !name.Contains("fire_rate") && !name.Contains("velocity") && !name.Contains("muzzle") && !name.Contains("firemode") && !name.Contains("fire_mode"))
			{
				return name.Contains("caliber");
			}
			return true;
		}

		private void AddStat(List<string> output, string name, object value)
		{
			if (value == null)
			{
				return;
			}
			string text = value.ToString();
			if (!string.IsNullOrWhiteSpace(text) && text.Length <= 24)
			{
				string item = name.Replace("_", " ") + ": " + text;
				if (!output.Contains(item))
				{
					output.Add(item);
				}
			}
		}

		private bool ContainsWord(string value, string[] words)
		{
			if (string.IsNullOrEmpty(value))
			{
				return false;
			}
			string text = value.ToLowerInvariant();
			foreach (string value2 in words)
			{
				if (text.Contains(value2))
				{
					return true;
				}
			}
			return false;
		}

		private string CleanName(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return "Unknown Item";
			}
			return name.Replace("(Clone)", "").Tr