Decompiled source of CustomCrosshairV2 v0.1.2

src/RoR2CustomCrosshair/bin/Debug/netstandard2.1/RoR2CustomCrosshair.dll

Decompiled 7 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using EntityStates;
using EntityStates.Bandit2.Weapon;
using EntityStates.Commando.CommandoWeapon;
using EntityStates.Toolbot;
using Microsoft.CodeAnalysis;
using On.RoR2.UI;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.Skills;
using RoR2.UI;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("RoR2CustomCrosshair")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+9a54713ad61bad5c325b1410f08b5cad2eda5494")]
[assembly: AssemblyProduct("RoR2CustomCrosshair")]
[assembly: AssemblyTitle("RoR2CustomCrosshair")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RoR2CustomCrosshair
{
	internal sealed class BanditSpreadSource : ISpreadSource
	{
		private sealed class Weapon
		{
			internal readonly string Name;

			internal readonly GenericBulletSpread Spread;

			internal readonly float FanDegrees;

			internal readonly bool HasFan;

			internal Weapon(string name, GenericBulletSpread spread, bool hasFan, float fanDegrees)
			{
				Name = name;
				Spread = spread;
				HasFan = hasFan;
				FanDegrees = fanDegrees;
			}

			internal float EnvelopeDegrees(float bloom)
			{
				float num = (HasFan ? (FanDegrees + bloom) : 0f);
				return Mathf.Max(num + Spread.HorizontalDegrees(bloom), Spread.VerticalDegrees(bloom));
			}
		}

		private const string BanditBodyName = "Bandit2Body";

		private BodyIndex _banditBodyIndex = (BodyIndex)(-1);

		private readonly Dictionary<Type, Weapon?> _weapons = new Dictionary<Type, Weapon>();

		private SkillDef? _lastPrimarySkill;

		private Weapon? _lastWeapon;

		private CharacterBody? _loggedBody;

		private Weapon? _loggedWeapon;

		public bool LogSamples => false;

		public bool CanHandle(CharacterBody body)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			if ((int)_banditBodyIndex == -1)
			{
				_banditBodyIndex = BodyCatalog.FindBodyIndex("Bandit2Body");
				if ((int)_banditBodyIndex == -1)
				{
					return false;
				}
			}
			return body.bodyIndex == _banditBodyIndex;
		}

		public bool TrySample(CharacterBody body, out SpreadSample sample)
		{
			sample = default(SpreadSample);
			Weapon weapon = CurrentPrimary(body);
			if (weapon == null || body.spreadBloomCurve == null)
			{
				return false;
			}
			float spreadBloomAngle = body.spreadBloomAngle;
			float bloom = body.spreadBloomCurve.Evaluate(0f);
			sample = new SpreadSample(weapon.EnvelopeDegrees(spreadBloomAngle), weapon.EnvelopeDegrees(bloom), spreadBloomAngle);
			LogWeaponOnce(body, weapon);
			return true;
		}

		private Weapon? CurrentPrimary(CharacterBody body)
		{
			GenericSkill val = (Object.op_Implicit((Object)(object)body.skillLocator) ? body.skillLocator.primary : null);
			SkillDef val2 = (Object.op_Implicit((Object)(object)val) ? val.skillDef : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return null;
			}
			if ((Object)(object)val2 != (Object)(object)_lastPrimarySkill)
			{
				_lastPrimarySkill = val2;
				Type stateType = ((SerializableEntityStateType)(ref val2.activationState)).stateType;
				_lastWeapon = ((stateType != null) ? WeaponFor(stateType) : null);
			}
			return _lastWeapon;
		}

		private Weapon? WeaponFor(Type stateType)
		{
			if (_weapons.TryGetValue(stateType, out Weapon value))
			{
				return value;
			}
			Weapon weapon = null;
			if (typeof(FireShotgun2).IsAssignableFrom(stateType))
			{
				FireShotgun2 val = GenericBulletSpread.ReadState<FireShotgun2>(stateType);
				if (val != null)
				{
					bool hasFan = ((GenericBulletBaseState)val).bulletCount > 1;
					float fanDegrees = Mathf.Max(val.minFixedSpreadYaw, val.maxFixedSpreadYaw);
					weapon = new Weapon(stateType.Name, GenericBulletSpread.From((GenericBulletBaseState)(object)val), hasFan, fanDegrees);
				}
			}
			else if (typeof(Bandit2FireRifle).IsAssignableFrom(stateType))
			{
				Bandit2FireRifle val2 = GenericBulletSpread.ReadState<Bandit2FireRifle>(stateType);
				if (val2 != null)
				{
					weapon = new Weapon(stateType.Name, GenericBulletSpread.From((GenericBulletBaseState)(object)val2), hasFan: false, 0f);
				}
			}
			_weapons[stateType] = weapon;
			return weapon;
		}

		private void LogWeaponOnce(CharacterBody body, Weapon weapon)
		{
			if (!((Object)(object)_loggedBody == (Object)(object)body) || _loggedWeapon != weapon)
			{
				_loggedBody = body;
				_loggedWeapon = weapon;
				AnimationCurve spreadBloomCurve = body.spreadBloomCurve;
				float num = spreadBloomCurve.Evaluate(0f);
				float num2 = spreadBloomCurve.Evaluate(1f);
				Plugin.Log.LogDebug((object)("[Spread] Bandit source active: primary '" + _lastPrimarySkill?.skillName + "' (" + weapon.Name + "), " + weapon.Spread.Describe() + ", fan " + (weapon.HasFan ? $"+/-{weapon.FanDegrees:0.###} deg + bloom" : "none") + "; " + $"bloom {num:0.###}..{num2:0.###} deg, decay {body.spreadBloomDecayTime:0.###} s " + $"(attack speed scales decay: {body.spreadBloomDecayWithAttackSpeed}); " + $"envelope {weapon.EnvelopeDegrees(num):0.###} deg at rest, {weapon.EnvelopeDegrees(num2):0.###} deg at full bloom."));
			}
		}
	}
	internal sealed class CommandoSpreadSource : ISpreadSource
	{
		private const string CommandoBodyName = "CommandoBody";

		private BodyIndex _commandoBodyIndex = (BodyIndex)(-1);

		private SkillDef? _lastPrimarySkill;

		private bool _lastPrimaryIsDoubleTap;

		private CharacterBody? _loggedBody;

		public bool LogSamples => false;

		public bool CanHandle(CharacterBody body)
		{
			return IsCommando(body);
		}

		public bool TrySample(CharacterBody body, out SpreadSample sample)
		{
			sample = default(SpreadSample);
			if (!IsCommando(body) || !PrimaryIsDoubleTap(body) || body.spreadBloomCurve == null)
			{
				return false;
			}
			sample = new SpreadSample(body.spreadBloomAngle, body.spreadBloomCurve.Evaluate(0f), body.spreadBloomAngle);
			LogBodyOnce(body);
			return true;
		}

		private bool IsCommando(CharacterBody body)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			if ((int)_commandoBodyIndex == -1)
			{
				_commandoBodyIndex = BodyCatalog.FindBodyIndex("CommandoBody");
				if ((int)_commandoBodyIndex == -1)
				{
					return false;
				}
			}
			return body.bodyIndex == _commandoBodyIndex;
		}

		private bool PrimaryIsDoubleTap(CharacterBody body)
		{
			GenericSkill val = (Object.op_Implicit((Object)(object)body.skillLocator) ? body.skillLocator.primary : null);
			SkillDef val2 = (Object.op_Implicit((Object)(object)val) ? val.skillDef : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			if ((Object)(object)val2 != (Object)(object)_lastPrimarySkill)
			{
				_lastPrimarySkill = val2;
				Type stateType = ((SerializableEntityStateType)(ref val2.activationState)).stateType;
				_lastPrimaryIsDoubleTap = stateType != null && typeof(FirePistol2).IsAssignableFrom(stateType);
			}
			return _lastPrimaryIsDoubleTap;
		}

		private void LogBodyOnce(CharacterBody body)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_loggedBody == (Object)(object)body)
			{
				return;
			}
			_loggedBody = body;
			string text = "none";
			GameObject defaultCrosshairPrefab = body.defaultCrosshairPrefab;
			CrosshairController val = default(CrosshairController);
			if (Object.op_Implicit((Object)(object)defaultCrosshairPrefab) && defaultCrosshairPrefab.TryGetComponent<CrosshairController>(ref val))
			{
				text = $"{((Object)defaultCrosshairPrefab).name} maxSpreadAngle {val.maxSpreadAngle:0.###}";
				SpritePosition[] spriteSpreadPositions = val.spriteSpreadPositions;
				if (spriteSpreadPositions != null && spriteSpreadPositions.Length > 0)
				{
					SpritePosition val2 = spriteSpreadPositions[0];
					text += string.Format(", sprite '{0}' moves {1} -> {2}", Object.op_Implicit((Object)(object)val2.target) ? ((Object)val2.target).name : "?", val2.zeroPosition, val2.onePosition);
				}
			}
			AnimationCurve spreadBloomCurve = body.spreadBloomCurve;
			Plugin.Log.LogDebug((object)("[Spread] Commando source active: primary '" + _lastPrimarySkill?.skillName + "', " + $"spread {spreadBloomCurve.Evaluate(0f):0.###} deg at rest, {spreadBloomCurve.Evaluate(1f):0.###} deg at full bloom, " + $"decay time {body.spreadBloomDecayTime:0.###} s (scales with attack speed: {body.spreadBloomDecayWithAttackSpeed}), " + "vanilla crosshair: " + text + "."));
		}
	}
	internal static class CrosshairConfig
	{
		private const int CurrentConfigVersion = 2;

		private const float SliderStep = 0.5f;

		internal static ConfigEntry<bool> Enabled;

		internal static BarsSettings InnerBars;

		internal static BarsSettings OuterBars;

		internal static CircleSettings Circle;

		internal static DotSettings CenterDot;

		internal static int Revision { get; private set; }

		internal static void Bind(ConfigFile config)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			Enabled = config.Bind<bool>("General", "Enabled", true, "Replace the ordinary crosshair of your own survivor with the custom crosshair.");
			InnerBars = new BarsSettings(config, "Inner Bars", new BarsDefaults
			{
				Enabled = true,
				Length = 8f,
				Thickness = 2f,
				Gap = 4f,
				GapMax = 64f,
				Fill = new Color32((byte)0, byte.MaxValue, (byte)0, byte.MaxValue)
			});
			OuterBars = new BarsSettings(config, "Outer Bars", new BarsDefaults
			{
				Enabled = false,
				Length = 10f,
				Thickness = 2f,
				Gap = 14f,
				GapMax = 128f,
				Fill = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue)
			});
			Circle = new CircleSettings(config, "Circle");
			CenterDot = new DotSettings(config, "Center Dot");
			MigrateFromVersion1(config);
			config.Save();
			config.SaveOnConfigSet = saveOnConfigSet;
			config.SettingChanged += delegate
			{
				Revision++;
			};
		}

		internal static void RegisterRiskOfOptions()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			ModSettingsManager.SetModDescription("Procedural crosshair with Inner Bars, Outer Bars, Circle and Center Dot layers. Sizes are pixels at 1920x1080 and scale with resolution.");
			Sprite val = ModIcon.Load();
			if ((Object)(object)val != (Object)null)
			{
				ModSettingsManager.SetModIcon(val, "com.example.ror2customcrosshair", "RoR2 Custom Crosshair");
			}
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(Enabled));
			InnerBars.Register();
			OuterBars.Register();
			Circle.Register();
			CenterDot.Register();
		}

		private static void MigrateFromVersion1(ConfigFile config)
		{
			ConfigEntry<int> val = config.Bind<int>("Internal", "Config Version", 0, "Settings layout version, used for migration. Do not edit.");
			if (val.Value < 2)
			{
				BarsSettings innerBars = InnerBars;
				Move<float>(config, "Crosshair", "Length", innerBars.Length);
				Move<float>(config, "Crosshair", "Thickness", innerBars.Thickness);
				Move<float>(config, "Crosshair", "Gap", innerBars.Gap);
				Move<int>(config, "Crosshair Color", "Opacity", innerBars.Fill.Opacity);
				Move<int>(config, "Crosshair Color", "R", innerBars.Fill.Red);
				Move<int>(config, "Crosshair Color", "G", innerBars.Fill.Green);
				Move<int>(config, "Crosshair Color", "B", innerBars.Fill.Blue);
				Move<bool>(config, "Outline", "Enabled", innerBars.Outline.Enabled);
				Move<float>(config, "Outline", "Thickness", innerBars.Outline.Thickness);
				Move<int>(config, "Outline Color", "Opacity", innerBars.Outline.Color.Opacity);
				Move<int>(config, "Outline Color", "R", innerBars.Outline.Color.Red);
				Move<int>(config, "Outline Color", "G", innerBars.Outline.Color.Green);
				Move<int>(config, "Outline Color", "B", innerBars.Outline.Color.Blue);
				val.Value = 2;
				Plugin.Log.LogInfo((object)"Migrated crosshair settings to the layered layout (old values now under 'Inner Bars').");
			}
		}

		private static void Move<T>(ConfigFile config, string oldSection, string oldKey, ConfigEntry<T> target)
		{
			ConfigEntry<T> val = config.Bind<T>(oldSection, oldKey, (T)((ConfigEntryBase)target).DefaultValue, (ConfigDescription)null);
			target.Value = val.Value;
			config.Remove(((ConfigEntryBase)val).Definition);
		}

		internal static ConfigEntry<float> BindFloat(ConfigFile config, string section, string key, float defaultValue, float min, float max, string description)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			return config.Bind<float>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()));
		}

		internal static ConfigEntry<int> BindByte(ConfigFile config, string section, string key, int defaultValue)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			return config.Bind<int>(section, key, defaultValue, new ConfigDescription(key + " value from 0 to 255.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 255), Array.Empty<object>()));
		}

		internal static void AddFloatOption(ConfigEntry<float> entry, float min, float max)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: 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_0031: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(entry, new StepSliderConfig
			{
				min = min,
				max = max,
				increment = 0.5f,
				FormatString = "{0:0.0}"
			}));
		}

		internal static void AddByteOption(ConfigEntry<int> entry)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(entry, new IntSliderConfig
			{
				min = 0,
				max = 255
			}));
		}
	}
	internal sealed class ColorSettings
	{
		internal readonly ConfigEntry<int> Red;

		internal readonly ConfigEntry<int> Green;

		internal readonly ConfigEntry<int> Blue;

		internal readonly ConfigEntry<int> Opacity;

		internal Color Value => new Color((float)Red.Value / 255f, (float)Green.Value / 255f, (float)Blue.Value / 255f, (float)Opacity.Value / 255f);

		internal ColorSettings(ConfigFile config, string section, string keyPrefix, Color32 defaults)
		{
			//IL_0016: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			Red = CrosshairConfig.BindByte(config, section, keyPrefix + "R", defaults.r);
			Green = CrosshairConfig.BindByte(config, section, keyPrefix + "G", defaults.g);
			Blue = CrosshairConfig.BindByte(config, section, keyPrefix + "B", defaults.b);
			Opacity = CrosshairConfig.BindByte(config, section, keyPrefix + "Opacity", defaults.a);
		}

		internal void Register()
		{
			CrosshairConfig.AddByteOption(Red);
			CrosshairConfig.AddByteOption(Green);
			CrosshairConfig.AddByteOption(Blue);
			CrosshairConfig.AddByteOption(Opacity);
		}
	}
	internal sealed class OutlineSettings
	{
		internal const float MaxThickness = 10f;

		internal readonly ConfigEntry<bool> Enabled;

		internal readonly ConfigEntry<float> Thickness;

		internal readonly ColorSettings Color;

		internal bool Visible => Enabled.Value && Thickness.Value > 0.001f;

		internal OutlineSettings(ConfigFile config, string section)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			Enabled = config.Bind<bool>(section, "Outline Enabled", true, "Draw a border around this layer.");
			Thickness = CrosshairConfig.BindFloat(config, section, "Outline Thickness", 1f, 0f, 10f, "Border width on each side, in pixels at 1920x1080.");
			Color = new ColorSettings(config, section, "Outline ", new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue));
		}

		internal void Register()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(Enabled));
			CrosshairConfig.AddFloatOption(Thickness, 0f, 10f);
			Color.Register();
		}
	}
	internal struct BarsDefaults
	{
		internal bool Enabled;

		internal float Length;

		internal float Thickness;

		internal float Gap;

		internal float GapMax;

		internal Color32 Fill;
	}
	internal sealed class BarsSettings
	{
		internal const float MaxLength = 64f;

		internal const float MaxThickness = 20f;

		private readonly float _gapMax;

		internal readonly ConfigEntry<bool> Enabled;

		internal readonly ConfigEntry<CrosshairMode> Mode;

		internal readonly ConfigEntry<float> Length;

		internal readonly ConfigEntry<float> Thickness;

		internal readonly ConfigEntry<float> Gap;

		internal readonly ColorSettings Fill;

		internal readonly OutlineSettings Outline;

		internal BarsSettings(ConfigFile config, string section, BarsDefaults defaults)
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			_gapMax = defaults.GapMax;
			Enabled = config.Bind<bool>(section, "Enabled", defaults.Enabled, "Show the " + section + ".");
			Mode = config.Bind<CrosshairMode>(section, "Mode", CrosshairMode.Static, "Static: fixed gap. Dynamic: gap will follow actual weapon spread (not implemented yet; currently same as Static).");
			Length = CrosshairConfig.BindFloat(config, section, "Length", defaults.Length, 0.5f, 64f, "Length of each arm, in pixels at 1920x1080.");
			Thickness = CrosshairConfig.BindFloat(config, section, "Thickness", defaults.Thickness, 0.5f, 20f, "Thickness of each arm, in pixels at 1920x1080.");
			Gap = CrosshairConfig.BindFloat(config, section, "Gap", defaults.Gap, 0f, _gapMax, "Distance from screen center to the inner edge of each arm, in pixels at 1920x1080.");
			Fill = new ColorSettings(config, section, "", defaults.Fill);
			Outline = new OutlineSettings(config, section);
		}

		internal void Register()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(Enabled));
			ModSettingsManager.AddOption((BaseOption)new ChoiceOption((ConfigEntryBase)(object)Mode));
			CrosshairConfig.AddFloatOption(Length, 0.5f, 64f);
			CrosshairConfig.AddFloatOption(Thickness, 0.5f, 20f);
			CrosshairConfig.AddFloatOption(Gap, 0f, _gapMax);
			Fill.Register();
			Outline.Register();
		}
	}
	internal sealed class CircleSettings
	{
		internal const float MaxRadius = 128f;

		internal const float MaxThickness = 20f;

		internal readonly ConfigEntry<bool> Enabled;

		internal readonly ConfigEntry<CrosshairMode> Mode;

		internal readonly ConfigEntry<float> Radius;

		internal readonly ConfigEntry<float> Thickness;

		internal readonly ColorSettings Fill;

		internal readonly OutlineSettings Outline;

		internal CircleSettings(ConfigFile config, string section)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			Enabled = config.Bind<bool>(section, "Enabled", false, "Show the circle.");
			Mode = config.Bind<CrosshairMode>(section, "Mode", CrosshairMode.Static, "Static: fixed radius. Dynamic: radius will follow actual weapon spread (not implemented yet; currently same as Static).");
			Radius = CrosshairConfig.BindFloat(config, section, "Radius", 16f, 0f, 128f, "Distance from screen center to the inner edge of the ring, in pixels at 1920x1080.");
			Thickness = CrosshairConfig.BindFloat(config, section, "Thickness", 2f, 0.5f, 20f, "Ring thickness, in pixels at 1920x1080.");
			Fill = new ColorSettings(config, section, "", new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue));
			Outline = new OutlineSettings(config, section);
		}

		internal void Register()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(Enabled));
			ModSettingsManager.AddOption((BaseOption)new ChoiceOption((ConfigEntryBase)(object)Mode));
			CrosshairConfig.AddFloatOption(Radius, 0f, 128f);
			CrosshairConfig.AddFloatOption(Thickness, 0.5f, 20f);
			Fill.Register();
			Outline.Register();
		}
	}
	internal sealed class DotSettings
	{
		internal const float MaxSize = 20f;

		internal readonly ConfigEntry<bool> Enabled;

		internal readonly ConfigEntry<float> Size;

		internal readonly ColorSettings Fill;

		internal readonly OutlineSettings Outline;

		internal DotSettings(ConfigFile config, string section)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			Enabled = config.Bind<bool>(section, "Enabled", false, "Show the center dot.");
			Size = CrosshairConfig.BindFloat(config, section, "Size", 2f, 0.5f, 20f, "Diameter of the dot, in pixels at 1920x1080.");
			Fill = new ColorSettings(config, section, "", new Color32((byte)0, byte.MaxValue, (byte)0, byte.MaxValue));
			Outline = new OutlineSettings(config, section);
		}

		internal void Register()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(Enabled));
			CrosshairConfig.AddFloatOption(Size, 0.5f, 20f);
			Fill.Register();
			Outline.Register();
		}
	}
	internal static class CrosshairDisplayPolicy
	{
		private const string StandardCrosshairAddress = "RoR2/Base/UI/StandardCrosshair.prefab";

		private const string GenericSprintCrosshairPath = "Prefabs/Crosshair/SprintingCrosshair";

		private static readonly string[] StockPreservingAddresses = new string[3] { "RoR2/Base/Bandit2/Bandit2Crosshair.prefab", "RoR2/Base/Toolbot/ToolbotGrenadeLauncherCrosshair.prefab", "RoR2/Base/Toolbot/ToolbotDualWieldCrosshair.prefab" };

		private static readonly (string Address, string? BodyName)[] CosmeticSkillCrosshairAddresses = new(string, string)[3]
		{
			("RoR2/Base/Bandit2/Bandit2CrosshairPrepRevolver.prefab", null),
			("RoR2/Base/Bandit2/Bandit2CrosshairPrepRevolverFire.prefab", null),
			("RoR2/Base/UI/BadCrosshair.prefab", "ToolbotBody")
		};

		private static Transform? _holder;

		private static GameObject? _standardTemplate;

		private static readonly Dictionary<GameObject, GameObject?> StockPreserving = new Dictionary<GameObject, GameObject>();

		private static readonly Dictionary<GameObject, string?> CosmeticSkillCrosshairs = new Dictionary<GameObject, string>();

		private static readonly Dictionary<string, BodyIndex> BodyIndices = new Dictionary<string, BodyIndex>();

		private static GameObject? _genericSprintCrosshair;

		private static bool _genericSprintCrosshairLoaded;

		private static int _pendingLoads;

		internal static bool IsReady => _pendingLoads == 0 && (Object)(object)_standardTemplate != (Object)null;

		internal static void Initialize(Transform holder)
		{
			_holder = holder;
			_pendingLoads = 1 + StockPreservingAddresses.Length + CosmeticSkillCrosshairAddresses.Length;
			CrosshairPrefabFactory.LoadAsync("RoR2/Base/UI/StandardCrosshair.prefab", delegate(GameObject? standard)
			{
				_standardTemplate = Build(standard, (GameObject s) => CrosshairPrefabFactory.BuildStandard(s, holder));
				if ((Object)(object)_standardTemplate != (Object)null)
				{
					Plugin.Log.LogInfo((object)"Crosshair template ready.");
				}
				else
				{
					Plugin.Log.LogError((object)"Crosshair template unavailable; vanilla crosshairs will be used.");
				}
				_pendingLoads--;
			});
			string[] stockPreservingAddresses = StockPreservingAddresses;
			foreach (string address in stockPreservingAddresses)
			{
				CrosshairPrefabFactory.LoadAsync(address, delegate(GameObject? vanilla)
				{
					if ((Object)(object)vanilla != (Object)null)
					{
						GameObject val = Build(vanilla, (GameObject v) => CrosshairPrefabFactory.BuildStockPreserving(v, holder));
						StockPreserving[vanilla] = val;
						if ((Object)(object)val == (Object)null)
						{
							Plugin.Log.LogWarning((object)(((Object)vanilla).name + ": ammo displays could not be preserved; its vanilla crosshair will be kept."));
						}
					}
					_pendingLoads--;
				});
			}
			(string, string)[] cosmeticSkillCrosshairAddresses = CosmeticSkillCrosshairAddresses;
			for (int num2 = 0; num2 < cosmeticSkillCrosshairAddresses.Length; num2++)
			{
				var (address2, bodyName) = cosmeticSkillCrosshairAddresses[num2];
				CrosshairPrefabFactory.LoadAsync(address2, delegate(GameObject? prefab)
				{
					if ((Object)(object)prefab != (Object)null)
					{
						CosmeticSkillCrosshairs[prefab] = bodyName;
					}
					_pendingLoads--;
				});
			}
		}

		internal static void Shutdown()
		{
			_standardTemplate = null;
			StockPreserving.Clear();
			CosmeticSkillCrosshairs.Clear();
			BodyIndices.Clear();
			_holder = null;
		}

		internal static GameObject? ChooseTemplate(CharacterBody body, GameObject picked)
		{
			if (!IsReady || !Object.op_Implicit((Object)(object)picked))
			{
				return null;
			}
			if (StockPreserving.ContainsKey(picked) || IsOrdinaryCrosshair(body, picked) || IsActiveWeaponCrosshair(body, picked))
			{
				return TemplateFor(picked);
			}
			if (IsGenericSprintCrosshair(body, picked) || IsCosmeticSkillCrosshair(body, picked))
			{
				return TemplateFor(ActiveWeaponCrosshair(body) ?? body.defaultCrosshairPrefab);
			}
			return null;
		}

		private static GameObject? TemplateFor(GameObject? vanilla)
		{
			if ((Object)(object)vanilla != (Object)null && StockPreserving.TryGetValue(vanilla, out GameObject value))
			{
				return value;
			}
			return _standardTemplate;
		}

		private static GameObject? ActiveWeaponCrosshair(CharacterBody body)
		{
			GenericSkill val = (Object.op_Implicit((Object)(object)body.skillLocator) ? body.skillLocator.primary : null);
			object result;
			if (Object.op_Implicit((Object)(object)val))
			{
				SkillDef skillDef = val.skillDef;
				ToolbotWeaponSkillDef val2 = (ToolbotWeaponSkillDef)(object)((skillDef is ToolbotWeaponSkillDef) ? skillDef : null);
				if (val2 != null && Object.op_Implicit((Object)(object)val2.crosshairPrefab))
				{
					result = val2.crosshairPrefab;
					goto IL_004a;
				}
			}
			result = null;
			goto IL_004a;
			IL_004a:
			return (GameObject?)result;
		}

		private static bool IsActiveWeaponCrosshair(CharacterBody body, GameObject picked)
		{
			GameObject val = ActiveWeaponCrosshair(body);
			return (Object)(object)val != (Object)null && (Object)(object)picked == (Object)(object)val;
		}

		private static bool IsCosmeticSkillCrosshair(CharacterBody body, GameObject picked)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Invalid comparison between Unknown and I4
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Invalid comparison between Unknown and I4
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (!CosmeticSkillCrosshairs.TryGetValue(picked, out string value))
			{
				return false;
			}
			if (value == null)
			{
				return true;
			}
			if (!BodyIndices.TryGetValue(value, out var value2) || (int)value2 == -1)
			{
				value2 = BodyCatalog.FindBodyIndex(value);
				BodyIndices[value] = value2;
			}
			return (int)value2 != -1 && body.bodyIndex == value2;
		}

		private static GameObject? Build(GameObject? source, Func<GameObject, GameObject?> build)
		{
			if ((Object)(object)source == (Object)null || (Object)(object)_holder == (Object)null)
			{
				return null;
			}
			try
			{
				return build(source);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Could not build crosshair template from {((Object)source).name}: {arg}");
				return null;
			}
		}

		private static bool IsOrdinaryCrosshair(CharacterBody body, GameObject picked)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Invalid comparison between Unknown and I4
			if ((Object)(object)picked == (Object)(object)body.defaultCrosshairPrefab)
			{
				return true;
			}
			SkillCrosshairController val = default(SkillCrosshairController);
			if (((Component)body).TryGetComponent<SkillCrosshairController>(ref val) && val.options != null)
			{
				SkillDefCrosshairOptions[] options = val.options;
				foreach (SkillDefCrosshairOptions val2 in options)
				{
					if (val2 != null && (int)val2.priority < 1 && (Object)(object)val2.crosshairPrefab == (Object)(object)picked)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool IsGenericSprintCrosshair(CharacterBody body, GameObject picked)
		{
			if (!body.isSprinting || !body.useSprintCrosshair || Object.op_Implicit((Object)(object)body.sprintCrosshairPrefabOverride))
			{
				return false;
			}
			GameObject genericSprintCrosshair = GetGenericSprintCrosshair();
			return (Object)(object)genericSprintCrosshair != (Object)null && (Object)(object)picked == (Object)(object)genericSprintCrosshair;
		}

		private static GameObject? GetGenericSprintCrosshair()
		{
			if (!_genericSprintCrosshairLoaded)
			{
				_genericSprintCrosshairLoaded = true;
				_genericSprintCrosshair = LegacyResourcesAPI.Load<GameObject>("Prefabs/Crosshair/SprintingCrosshair");
				if ((Object)(object)_genericSprintCrosshair == (Object)null)
				{
					Plugin.Log.LogWarning((object)"Could not load 'Prefabs/Crosshair/SprintingCrosshair'; the vanilla sprint crosshair will be shown.");
				}
			}
			return _genericSprintCrosshair;
		}
	}
	internal readonly struct PixelFrame
	{
		internal readonly float ResolutionScale;

		internal readonly float PixelsPerUnit;

		internal readonly float CenterFraction;

		internal readonly Vector2 Center;

		internal readonly Vector2 OriginOffset;

		internal PixelFrame(float resolutionScale, float pixelsPerUnit, float centerFraction, Vector2 anchorScreen, Vector2 rootScreen)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			ResolutionScale = resolutionScale;
			PixelsPerUnit = pixelsPerUnit;
			CenterFraction = centerFraction;
			Center = new Vector2(SnapToGrid(anchorScreen.x, centerFraction), SnapToGrid(anchorScreen.y, centerFraction));
			OriginOffset = Center - rootScreen;
		}

		internal static float FractionFor(float logicalSize, float resolutionScale)
		{
			return (Mathf.Max(1, RoundPx(logicalSize * resolutionScale)) % 2 == 0) ? 0f : 0.5f;
		}

		internal int Width(float logical)
		{
			return Mathf.Max(1, RoundPx(logical * ResolutionScale));
		}

		internal int CenteredSize(float logical)
		{
			float num = logical * ResolutionScale;
			if (CenterFraction == 0f)
			{
				return Mathf.Max(2, 2 * RoundPx(num * 0.5f));
			}
			return Mathf.Max(1, 2 * (int)Mathf.Floor(num * 0.5f) + 1);
		}

		internal float EdgeDistance(float logical, float extraPixels = 0f)
		{
			float num = Mathf.Max(0f, Mathf.Max(0f, logical) * ResolutionScale + extraPixels);
			return Mathf.Floor(CenterFraction + num + 0.5f) - CenterFraction;
		}

		internal float ToLocal(float pixels)
		{
			return pixels / PixelsPerUnit;
		}

		internal void Place(RectTransform rect, Vector4 pixelRect)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//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_003d: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor((pixelRect.x + pixelRect.z) * 0.5f, (pixelRect.y + pixelRect.w) * 0.5f);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(pixelRect.z - pixelRect.x, pixelRect.w - pixelRect.y);
			rect.anchoredPosition = (OriginOffset + val) / PixelsPerUnit;
			rect.sizeDelta = val2 / PixelsPerUnit;
		}

		internal static int RoundPx(float value)
		{
			return (int)Mathf.Floor(value + 0.5f);
		}

		private static float SnapToGrid(float value, float fraction)
		{
			return Mathf.Floor(value - fraction + 0.5f) + fraction;
		}
	}
	internal sealed class BorderedRect
	{
		private static readonly string[] SideNames = new string[4] { "Top", "Bottom", "Left", "Right" };

		private readonly Image _fill;

		private readonly Image[] _strips = (Image[])(object)new Image[4];

		private BorderedRect(Transform layer, string name)
		{
			_fill = Find(layer, "Fill" + name);
			for (int i = 0; i < 4; i++)
			{
				_strips[i] = Find(layer, "Outline" + name + SideNames[i]);
			}
		}

		internal static BorderedRect Bind(Transform layer, string name)
		{
			return new BorderedRect(layer, name);
		}

		internal static void BuildBorder(Transform layer, string name)
		{
			string[] sideNames = SideNames;
			foreach (string text in sideNames)
			{
				CrosshairPrefabFactory.CreateImage("Outline" + name + text, layer);
			}
		}

		internal static void BuildFill(Transform layer, string name)
		{
			CrosshairPrefabFactory.CreateImage("Fill" + name, layer);
		}

		internal void Apply(in PixelFrame frame, Vector4 r, Color fill, int border, Color borderColor)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_0091: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: 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_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			Show(_fill, in frame, r, fill);
			if (border <= 0)
			{
				Image[] strips = _strips;
				foreach (Image val in strips)
				{
					((Behaviour)val).enabled = false;
				}
			}
			else
			{
				float num = border;
				Show(_strips[0], in frame, new Vector4(r.x - num, r.w, r.z + num, r.w + num), borderColor);
				Show(_strips[1], in frame, new Vector4(r.x - num, r.y - num, r.z + num, r.y), borderColor);
				Show(_strips[2], in frame, new Vector4(r.x - num, r.y, r.x, r.w), borderColor);
				Show(_strips[3], in frame, new Vector4(r.z, r.y, r.z + num, r.w), borderColor);
			}
		}

		internal void Hide()
		{
			((Behaviour)_fill).enabled = false;
			Image[] strips = _strips;
			foreach (Image val in strips)
			{
				((Behaviour)val).enabled = false;
			}
		}

		private static void Show(Image image, in PixelFrame frame, Vector4 r, Color color)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			frame.Place(((Graphic)image).rectTransform, r);
			((Graphic)image).color = color;
			((Behaviour)image).enabled = true;
		}

		private static Image Find(Transform layer, string name)
		{
			return ((Component)layer.Find(name)).GetComponent<Image>();
		}
	}
	internal sealed class BarsLayer
	{
		private static readonly string[] ArmNames = new string[4] { "Left", "Right", "Top", "Bottom" };

		private readonly BarsSettings _settings;

		private readonly BorderedRect[] _arms = new BorderedRect[4];

		internal bool Enabled => _settings.Enabled.Value;

		internal float Thickness => _settings.Thickness.Value;

		internal BarsLayer(Transform root, string layerName, BarsSettings settings)
		{
			_settings = settings;
			Transform layer = root.Find(layerName);
			for (int i = 0; i < 4; i++)
			{
				_arms[i] = BorderedRect.Bind(layer, ArmNames[i]);
			}
		}

		internal static void Build(Transform root, string layerName)
		{
			Transform layer = (Transform)(object)CrosshairPrefabFactory.CreateRect(layerName, root);
			string[] armNames = ArmNames;
			foreach (string name in armNames)
			{
				BorderedRect.BuildBorder(layer, name);
			}
			string[] armNames2 = ArmNames;
			foreach (string name2 in armNames2)
			{
				BorderedRect.BuildFill(layer, name2);
			}
		}

		internal void Apply(in PixelFrame frame, float dynamicOffset)
		{
			//IL_00ba: 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_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: 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_011f: 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_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled)
			{
				BorderedRect[] arms = _arms;
				foreach (BorderedRect borderedRect in arms)
				{
					borderedRect.Hide();
				}
				return;
			}
			float extraPixels = ((_settings.Mode.Value == CrosshairMode.Dynamic) ? dynamicOffset : 0f);
			float num = (float)frame.CenteredSize(_settings.Thickness.Value) * 0.5f;
			float num2 = frame.EdgeDistance(_settings.Gap.Value, extraPixels);
			float num3 = num2 + (float)frame.Width(_settings.Length.Value);
			Color value = _settings.Fill.Value;
			Color value2 = _settings.Outline.Color.Value;
			int border = (_settings.Outline.Visible ? frame.Width(_settings.Outline.Thickness.Value) : 0);
			_arms[0].Apply(in frame, new Vector4(0f - num3, 0f - num, 0f - num2, num), value, border, value2);
			_arms[1].Apply(in frame, new Vector4(num2, 0f - num, num3, num), value, border, value2);
			_arms[2].Apply(in frame, new Vector4(0f - num, num2, num, num3), value, border, value2);
			_arms[3].Apply(in frame, new Vector4(0f - num, 0f - num3, num, 0f - num2), value, border, value2);
		}
	}
	internal sealed class RingPart
	{
		private const float SegmentPixels = 1.5f;

		private const int MinSegments = 24;

		private const int MaxSegments = 512;

		private readonly RingMesh _mesh;

		private readonly Image _image;

		private RingPart(Transform layer, string name)
		{
			_mesh = ((Component)layer.Find(name)).GetComponent<RingMesh>();
			_image = _mesh.Image;
		}

		internal static RingPart Bind(Transform layer, string name)
		{
			return new RingPart(layer, name);
		}

		internal static void Build(Transform layer, string name)
		{
			CrosshairPrefabFactory.CreateImage(name, layer);
			((Component)layer.Find(name)).gameObject.AddComponent<RingMesh>();
		}

		internal void Apply(in PixelFrame frame, float inner, float outer, int border, Color fill, Color borderColor)
		{
			//IL_0042: 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_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			float num = outer + (float)border + 1f;
			int segments = Mathf.Clamp(Mathf.CeilToInt(MathF.PI * 2f * (outer + (float)border) / 1.5f), 24, 512);
			frame.Place(((Graphic)_image).rectTransform, new Vector4(0f - num, 0f - num, num, num));
			((Graphic)_image).color = Color.white;
			_mesh.SetShape(frame.ToLocal(inner), frame.ToLocal(outer), frame.ToLocal(border), frame.ToLocal(1f), fill, borderColor, segments);
			((Behaviour)_image).enabled = true;
		}

		internal void Hide()
		{
			((Behaviour)_image).enabled = false;
		}
	}
	internal sealed class CenterDotLayer
	{
		private const string DotName = "Dot";

		private readonly DotSettings _settings;

		private readonly RingPart _dot;

		internal bool Enabled => _settings.Enabled.Value;

		internal float Size => _settings.Size.Value;

		internal CenterDotLayer(Transform root, string layerName, DotSettings settings)
		{
			_settings = settings;
			_dot = RingPart.Bind(root.Find(layerName), "Dot");
		}

		internal static void Build(Transform root, string layerName)
		{
			RingPart.Build((Transform)(object)CrosshairPrefabFactory.CreateRect(layerName, root), "Dot");
		}

		internal void Apply(in PixelFrame frame)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled)
			{
				_dot.Hide();
				return;
			}
			float outer = (float)frame.CenteredSize(_settings.Size.Value) * 0.5f;
			int border = (_settings.Outline.Visible ? frame.Width(_settings.Outline.Thickness.Value) : 0);
			_dot.Apply(in frame, 0f, outer, border, _settings.Fill.Value, _settings.Outline.Color.Value);
		}
	}
	internal sealed class CircleLayer
	{
		private const string RingName = "Ring";

		private readonly CircleSettings _settings;

		private readonly RingPart _ring;

		internal CircleLayer(Transform root, string layerName, CircleSettings settings)
		{
			_settings = settings;
			_ring = RingPart.Bind(root.Find(layerName), "Ring");
		}

		internal static void Build(Transform root, string layerName)
		{
			RingPart.Build((Transform)(object)CrosshairPrefabFactory.CreateRect(layerName, root), "Ring");
		}

		internal void Apply(in PixelFrame frame, float dynamicOffset)
		{
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			if (!_settings.Enabled.Value)
			{
				_ring.Hide();
				return;
			}
			float extraPixels = ((_settings.Mode.Value == CrosshairMode.Dynamic) ? dynamicOffset : 0f);
			float num = frame.EdgeDistance(_settings.Radius.Value, extraPixels);
			float outer = num + (float)frame.Width(_settings.Thickness.Value);
			int border = (_settings.Outline.Visible ? frame.Width(_settings.Outline.Thickness.Value) : 0);
			_ring.Apply(in frame, num, outer, border, _settings.Fill.Value, _settings.Outline.Color.Value);
		}
	}
	internal enum CrosshairMode
	{
		Static,
		Dynamic
	}
	internal static class CrosshairPrefabFactory
	{
		internal const string ProceduralRootName = "ProceduralCrosshair";

		internal const string CircleLayerName = "Circle";

		internal const string OuterBarsLayerName = "OuterBars";

		internal const string InnerBarsLayerName = "InnerBars";

		internal const string CenterDotLayerName = "CenterDot";

		internal static void LoadAsync(string address, Action<GameObject?> onLoaded)
		{
			//IL_001b: 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)
			AsyncOperationHandle<GameObject> val = Addressables.LoadAssetAsync<GameObject>((object)address);
			val.Completed += delegate(AsyncOperationHandle<GameObject> op)
			{
				//IL_0003: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Invalid comparison between Unknown and I4
				if ((int)op.Status != 1 || (Object)(object)op.Result == (Object)null)
				{
					Plugin.Log.LogError((object)$"Could not load '{address}': {op.OperationException}");
					onLoaded(null);
				}
				else
				{
					onLoaded(op.Result);
				}
			};
		}

		internal static GameObject BuildStandard(GameObject standardCrosshair, Transform holder)
		{
			CrosshairController controller;
			GameObject val = Clone(standardCrosshair, holder, "RoR2CustomCrosshair", out controller);
			Plugin.Log.LogDebug((object)("StandardCrosshair components: " + string.Join(", ", from c in val.GetComponents<Component>()
				select ((object)c).GetType().Name)));
			controller.spriteSpreadPositions = Array.Empty<SpritePosition>();
			controller.skillStockSpriteDisplays = Array.Empty<SkillStockSpriteDisplay>();
			controller.remapSprites = Array.Empty<RawImage>();
			HideVanillaVisuals(val, new HashSet<Transform>());
			AddProceduralCrosshair(val);
			return val;
		}

		internal static GameObject? BuildStockPreserving(GameObject vanillaCrosshair, Transform holder)
		{
			//IL_0043: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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_00e6: 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)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			CrosshairController controller;
			GameObject val = Clone(vanillaCrosshair, holder, "RoR2CustomCrosshair_" + ((Object)vanillaCrosshair).name, out controller);
			Transform transform = val.transform;
			List<SkillStockSpriteDisplay> list = new List<SkillStockSpriteDisplay>();
			SkillStockSpriteDisplay[] array = controller.skillStockSpriteDisplays ?? Array.Empty<SkillStockSpriteDisplay>();
			foreach (SkillStockSpriteDisplay val2 in array)
			{
				if (Object.op_Implicit((Object)(object)val2.target) && !IsAtCenter(transform, val2.target.transform))
				{
					list.Add(val2);
				}
			}
			if (list.Count == 0)
			{
				Object.Destroy((Object)(object)val);
				return null;
			}
			controller.spriteSpreadPositions = Array.Empty<SpritePosition>();
			controller.remapSprites = Array.Empty<RawImage>();
			controller.skillStockSpriteDisplays = list.ToArray();
			HashSet<Transform> hashSet = new HashSet<Transform>();
			foreach (SkillStockSpriteDisplay item in list)
			{
				Transform val3 = item.target.transform;
				while ((Object)(object)val3 != (Object)null && (Object)(object)val3 != (Object)(object)transform)
				{
					hashSet.Add(val3);
					val3 = val3.parent;
				}
			}
			HideVanillaVisuals(val, hashSet);
			AddProceduralCrosshair(val);
			Plugin.Log.LogDebug((object)(((Object)vanillaCrosshair).name + ": keeping vanilla stock displays " + string.Join(", ", list.Select((SkillStockSpriteDisplay d) => ((Object)d.target).name)) + "; spread artwork removed."));
			return val;
		}

		private static GameObject Clone(GameObject source, Transform holder, string name, out CrosshairController controller)
		{
			GameObject val = Object.Instantiate<GameObject>(source, holder, false);
			((Object)val).name = name;
			HudElement val2 = default(HudElement);
			if (!val.TryGetComponent<CrosshairController>(ref controller) || !val.TryGetComponent<HudElement>(ref val2))
			{
				Object.Destroy((Object)(object)val);
				throw new InvalidOperationException(((Object)source).name + " no longer has CrosshairController + HudElement.");
			}
			return val;
		}

		private static bool IsAtCenter(Transform root, Transform target)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = root.InverseTransformPoint(target.position);
			return ((Vector3)(ref val)).sqrMagnitude < 1f;
		}

		private static void HideVanillaVisuals(GameObject template, HashSet<Transform> keep)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			foreach (Transform item in template.transform)
			{
				Transform val = item;
				if (!keep.Contains(val))
				{
					((Component)val).gameObject.SetActive(false);
				}
			}
			Graphic[] components = template.GetComponents<Graphic>();
			foreach (Graphic val2 in components)
			{
				((Behaviour)val2).enabled = false;
			}
		}

		private static void AddProceduralCrosshair(GameObject template)
		{
			RectTransform root = CreateRect("ProceduralCrosshair", template.transform);
			CircleLayer.Build((Transform)(object)root, "Circle");
			BarsLayer.Build((Transform)(object)root, "OuterBars");
			BarsLayer.Build((Transform)(object)root, "InnerBars");
			CenterDotLayer.Build((Transform)(object)root, "CenterDot");
			template.AddComponent<ProceduralCrosshairView>();
		}

		internal static RectTransform CreateRect(string name, Transform parent)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_003b: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(parent, false);
			val2.anchorMin = new Vector2(0.5f, 0.5f);
			val2.anchorMax = new Vector2(0.5f, 0.5f);
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.anchoredPosition = Vector2.zero;
			val2.sizeDelta = Vector2.zero;
			return val2;
		}

		internal static void CreateImage(string name, Transform parent)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = CreateRect(name, parent);
			((Component)val).gameObject.AddComponent<CanvasRenderer>();
			Image val2 = ((Component)val).gameObject.AddComponent<Image>();
			((Graphic)val2).raycastTarget = false;
			((MaskableGraphic)val2).maskable = false;
			((Graphic)val2).color = Color.clear;
			((Behaviour)val2).enabled = false;
		}
	}
	internal readonly struct SpreadSample
	{
		internal readonly float CurrentDegrees;

		internal readonly float RestingDegrees;

		internal readonly float BloomDegrees;

		internal SpreadSample(float currentDegrees, float restingDegrees, float bloomDegrees)
		{
			CurrentDegrees = currentDegrees;
			RestingDegrees = restingDegrees;
			BloomDegrees = bloomDegrees;
		}
	}
	internal interface ISpreadSource
	{
		bool LogSamples { get; }

		bool CanHandle(CharacterBody body);

		bool TrySample(CharacterBody body, out SpreadSample sample);
	}
	internal sealed class SpreadSourceResolver
	{
		private readonly ISpreadSource[] _sources;

		private CharacterBody? _body;

		private ISpreadSource? _source;

		internal SpreadSourceResolver(params ISpreadSource[] sources)
		{
			_sources = sources;
		}

		internal bool TrySample(CharacterBody body, out SpreadSample sample, out ISpreadSource? source)
		{
			if ((Object)(object)body != (Object)(object)_body)
			{
				_body = body;
				_source = null;
				ISpreadSource[] sources = _sources;
				foreach (ISpreadSource spreadSource in sources)
				{
					if (spreadSource.CanHandle(body))
					{
						_source = spreadSource;
						break;
					}
				}
			}
			source = _source;
			sample = default(SpreadSample);
			return _source != null && _source.TrySample(body, out sample);
		}
	}
	internal static class SpreadProjection
	{
		internal static float HalfAngleToPixels(float halfAngleDegrees, Camera camera)
		{
			float num = camera.fieldOfView * 0.5f * (MathF.PI / 180f);
			if (num <= 0f || halfAngleDegrees <= 0f)
			{
				return 0f;
			}
			float num2 = Mathf.Min(halfAngleDegrees, 89f) * (MathF.PI / 180f);
			return Mathf.Tan(num2) / Mathf.Tan(num) * ((float)camera.pixelHeight * 0.5f);
		}
	}
	internal sealed class GenericBulletSpread
	{
		internal readonly int BulletCount;

		internal readonly float MaxSpread;

		internal readonly float YawScale;

		internal readonly float PitchScale;

		private GenericBulletSpread(GenericBulletBaseState state)
		{
			BulletCount = state.bulletCount;
			MaxSpread = state.maxSpread;
			YawScale = state.spreadYawScale;
			PitchScale = state.spreadPitchScale;
		}

		internal static T? ReadState<T>(Type stateType) where T : GenericBulletBaseState
		{
			try
			{
				EntityState obj = EntityStateCatalog.InstantiateState(stateType);
				return (T)(object)((obj is T) ? obj : null);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not read configuration of " + stateType.FullName + ": " + ex.Message));
				return default(T);
			}
		}

		internal static GenericBulletSpread From(GenericBulletBaseState state)
		{
			return new GenericBulletSpread(state);
		}

		internal float HorizontalDegrees(float bloomDegrees)
		{
			return (MaxSpread + bloomDegrees) * YawScale;
		}

		internal float VerticalDegrees(float bloomDegrees)
		{
			return (MaxSpread + bloomDegrees) * PitchScale;
		}

		internal string Describe()
		{
			return $"{BulletCount} bullet(s), maxSpread {MaxSpread:0.###}, yaw x{YawScale:0.###}, pitch x{PitchScale:0.###}";
		}
	}
	internal sealed class HudCrosshairDriver : MonoBehaviour
	{
		private static readonly ISpreadSource[] SpreadSources = new ISpreadSource[3]
		{
			new CommandoSpreadSource(),
			new BanditSpreadSource(),
			new ToolbotSpreadSource()
		};

		private const float OffsetStepPixels = 0.25f;

		private const float SpreadLogInterval = 1f;

		private float _nextSpreadLogTime;

		private readonly SpreadSourceResolver _spreadResolver = new SpreadSourceResolver(SpreadSources);

		private CrosshairManager? _crosshairManager;

		private bool _replacing;

		internal static void Attach(HUD hud)
		{
			if (!Object.op_Implicit((Object)(object)((Component)hud).GetComponent<HudCrosshairDriver>()))
			{
				((Component)hud).gameObject.AddComponent<HudCrosshairDriver>();
			}
		}

		private void Awake()
		{
			_crosshairManager = ((Component)this).GetComponent<CrosshairManager>();
			if (!Object.op_Implicit((Object)(object)_crosshairManager))
			{
				Plugin.Log.LogWarning((object)("HUD '" + ((Object)this).name + "' has no CrosshairManager; custom crosshair unavailable."));
			}
		}

		private void OnEnable()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			if (Object.op_Implicit((Object)(object)_crosshairManager))
			{
				_crosshairManager.pickCrosshairGlobal += new PickCrosshairDelegate(OnPickCrosshair);
			}
		}

		private void OnDisable()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			if (Object.op_Implicit((Object)(object)_crosshairManager))
			{
				_crosshairManager.pickCrosshairGlobal -= new PickCrosshairDelegate(OnPickCrosshair);
			}
			SetReplacing(replacing: false, null);
		}

		private void OnPickCrosshair(CrosshairManager manager, ref GameObject crosshairPrefab)
		{
			CharacterBody ownLiveBody = GetOwnLiveBody(manager);
			GameObject val = (((Object)(object)ownLiveBody != (Object)null && CrosshairConfig.Enabled.Value) ? CrosshairDisplayPolicy.ChooseTemplate(ownLiveBody, crosshairPrefab) : null);
			if ((Object)(object)val != (Object)null)
			{
				crosshairPrefab = val;
			}
			SetReplacing((Object)(object)val != (Object)null, ownLiveBody);
		}

		internal DynamicOffsets GetDynamicOffsets()
		{
			if (!_replacing || !Object.op_Implicit((Object)(object)_crosshairManager))
			{
				return default(DynamicOffsets);
			}
			CameraRigController cameraRigController = _crosshairManager.cameraRigController;
			CharacterBody ownLiveBody = GetOwnLiveBody(_crosshairManager);
			Camera val = (Object.op_Implicit((Object)(object)cameraRigController) ? cameraRigController.sceneCam : null);
			if ((Object)(object)ownLiveBody == (Object)null || (Object)(object)val == (Object)null || !_spreadResolver.TrySample(ownLiveBody, out SpreadSample sample, out ISpreadSource source))
			{
				return default(DynamicOffsets);
			}
			float num = SpreadProjection.HalfAngleToPixels(sample.CurrentDegrees, val);
			float num2 = SpreadProjection.HalfAngleToPixels(sample.RestingDegrees, val);
			float num3 = Mathf.Round(Mathf.Max(0f, num - num2) / 0.25f) * 0.25f;
			if (source.LogSamples)
			{
				LogSpreadSample(source, in sample, val, num, num3);
			}
			return new DynamicOffsets(num3, num3, num3);
		}

		private void LogSpreadSample(ISpreadSource source, in SpreadSample sample, Camera camera, float currentPixels, float offsetPixels)
		{
			if (!(offsetPixels <= 0f) && !(Time.unscaledTime < _nextSpreadLogTime))
			{
				_nextSpreadLogTime = Time.unscaledTime + 1f;
				Plugin.Log.LogDebug((object)($"[Spread] {source.GetType().Name}: bloom {sample.BloomDegrees:0.###} deg -> spread {sample.CurrentDegrees:0.###} deg " + $"(rest {sample.RestingDegrees:0.###}), vertical FOV {camera.fieldOfView:0.#}, camera {camera.pixelWidth}x{camera.pixelHeight} -> " + $"cone radius {currentPixels:0.#} px, dynamic offset +{offsetPixels:0.##} px."));
			}
		}

		private static CharacterBody? GetOwnLiveBody(CrosshairManager manager)
		{
			CameraRigController cameraRigController = manager.cameraRigController;
			if (!Object.op_Implicit((Object)(object)cameraRigController) || cameraRigController.hasOverride)
			{
				return null;
			}
			LocalUser localUserViewer = cameraRigController.localUserViewer;
			CharacterBody targetBody = cameraRigController.targetBody;
			if (localUserViewer == null || !Object.op_Implicit((Object)(object)targetBody) || (Object)(object)localUserViewer.cachedBodyObject != (Object)(object)((Component)targetBody).gameObject)
			{
				return null;
			}
			if (!Object.op_Implicit((Object)(object)targetBody.healthComponent) || !targetBody.healthComponent.alive)
			{
				return null;
			}
			return targetBody;
		}

		private void SetReplacing(bool replacing, CharacterBody? body)
		{
			if (replacing != _replacing)
			{
				_replacing = replacing;
				Plugin.Log.LogDebug((object)(replacing ? ("Custom crosshair active for " + body.GetDisplayName() + " (" + ((Object)body).name + ").") : "Custom crosshair released; vanilla crosshair logic in control."));
			}
		}
	}
	internal static class ModIcon
	{
		private const string ResourceName = "RoR2CustomCrosshair.icon.png";

		private static Sprite? _sprite;

		internal static Sprite? Load()
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_00b5: 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 ((Object)(object)_sprite != (Object)null)
			{
				return _sprite;
			}
			try
			{
				byte[] array = ReadResource();
				if (array == null)
				{
					Plugin.Log.LogWarning((object)"Embedded resource 'RoR2CustomCrosshair.icon.png' not found; RiskOfOptions will show its default icon.");
					return null;
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true)
				{
					name = "RoR2CustomCrosshairIcon",
					filterMode = (FilterMode)2,
					wrapMode = (TextureWrapMode)1
				};
				if (!ImageConversion.LoadImage(val, array, true))
				{
					Object.Destroy((Object)(object)val);
					Plugin.Log.LogWarning((object)"Could not decode the embedded icon; RiskOfOptions will show its default icon.");
					return null;
				}
				Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
				((Object)val2).name = ((Object)val).name;
				((Object)val).hideFlags = (HideFlags)32;
				((Object)val2).hideFlags = (HideFlags)32;
				_sprite = val2;
				return _sprite;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"Could not load the mod icon; RiskOfOptions will show its default icon. {arg}");
				return null;
			}
		}

		private static byte[]? ReadResource()
		{
			using Stream stream = typeof(ModIcon).Assembly.GetManifestResourceStream("RoR2CustomCrosshair.icon.png");
			if (stream == null)
			{
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}
	}
	[BepInPlugin("com.example.ror2customcrosshair", "RoR2 Custom Crosshair", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static hook_Awake <0>__HUD_Awake;
		}

		public const string PluginGuid = "com.example.ror2customcrosshair";

		public const string PluginName = "RoR2 Custom Crosshair";

		public const string PluginVersion = "0.1.0";

		private GameObject? _prefabHolder;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			CrosshairConfig.Bind(((BaseUnityPlugin)this).Config);
			CrosshairConfig.RegisterRiskOfOptions();
			_prefabHolder = new GameObject("RoR2CustomCrosshair_Templates");
			_prefabHolder.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)_prefabHolder);
			CrosshairDisplayPolicy.Initialize(_prefabHolder.transform);
			object obj = <>O.<0>__HUD_Awake;
			if (obj == null)
			{
				hook_Awake val = HUD_Awake;
				<>O.<0>__HUD_Awake = val;
				obj = (object)val;
			}
			HUD.Awake += (hook_Awake)obj;
			Log.LogInfo((object)"RoR2 Custom Crosshair 0.1.0 loaded.");
		}

		private static void HUD_Awake(orig_Awake orig, HUD self)
		{
			orig.Invoke(self);
			HudCrosshairDriver.Attach(self);
		}

		private void OnDestroy()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			object obj = <>O.<0>__HUD_Awake;
			if (obj == null)
			{
				hook_Awake val = HUD_Awake;
				<>O.<0>__HUD_Awake = val;
				obj = (object)val;
			}
			HUD.Awake -= (hook_Awake)obj;
			HudCrosshairDriver[] array = Object.FindObjectsOfType<HudCrosshairDriver>();
			foreach (HudCrosshairDriver hudCrosshairDriver in array)
			{
				Object.Destroy((Object)(object)hudCrosshairDriver);
			}
			CrosshairDisplayPolicy.Shutdown();
			if ((Object)(object)_prefabHolder != (Object)null)
			{
				Object.Destroy((Object)(object)_prefabHolder);
			}
		}
	}
	internal readonly struct DynamicOffsets : IEquatable<DynamicOffsets>
	{
		internal readonly float InnerBars;

		internal readonly float OuterBars;

		internal readonly float Circle;

		internal DynamicOffsets(float innerBars, float outerBars, float circle)
		{
			InnerBars = innerBars;
			OuterBars = outerBars;
			Circle = circle;
		}

		public bool Equals(DynamicOffsets other)
		{
			return InnerBars == other.InnerBars && OuterBars == other.OuterBars && Circle == other.Circle;
		}
	}
	internal sealed class ProceduralCrosshairView : MonoBehaviour
	{
		private const float ReferenceWidth = 1920f;

		private const float ReferenceHeight = 1080f;

		private static int _loggedScreenWidth = -1;

		private static int _loggedScreenHeight = -1;

		private static float _loggedPixelsPerUnit = -1f;

		private RectTransform _rect = null;

		private RectTransform _root = null;

		private CircleLayer _circle = null;

		private BarsLayer _outerBars = null;

		private BarsLayer _innerBars = null;

		private CenterDotLayer _centerDot = null;

		private Canvas? _canvas;

		private HudCrosshairDriver? _driver;

		private int _lastRevision = -1;

		private int _lastScreenWidth = -1;

		private int _lastScreenHeight = -1;

		private float _lastPixelsPerUnit = -1f;

		private Vector2 _lastRootScreen;

		private Vector2 _lastAnchorScreen;

		private DynamicOffsets _lastOffsets;

		private void Awake()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			_rect = (RectTransform)((Component)this).transform;
			_root = (RectTransform)((Component)this).transform.Find("ProceduralCrosshair");
			_circle = new CircleLayer((Transform)(object)_root, "Circle", CrosshairConfig.Circle);
			_outerBars = new BarsLayer((Transform)(object)_root, "OuterBars", CrosshairConfig.OuterBars);
			_innerBars = new BarsLayer((Transform)(object)_root, "InnerBars", CrosshairConfig.InnerBars);
			_centerDot = new CenterDotLayer((Transform)(object)_root, "CenterDot", CrosshairConfig.CenterDot);
			Refresh();
		}

		private void OnTransformParentChanged()
		{
			_canvas = null;
			_driver = null;
		}

		private void LateUpdate()
		{
			Refresh();
		}

		private void Refresh()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: 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_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_canvas == (Object)null)
			{
				_canvas = ((Component)this).GetComponentInParent<Canvas>();
				if ((Object)(object)_canvas == (Object)null)
				{
					return;
				}
			}
			Canvas rootCanvas = _canvas.rootCanvas;
			float pixelsPerUnit = GetPixelsPerUnit(rootCanvas);
			if (pixelsPerUnit <= 0f)
			{
				return;
			}
			Camera val = (((int)rootCanvas.renderMode == 0) ? null : rootCanvas.worldCamera);
			Vector2 val2 = RectTransformUtility.WorldToScreenPoint(val, ((Transform)_root).position);
			Vector2 val3 = RectTransformUtility.WorldToScreenPoint(val, GetIntendedCenterWorld());
			if ((Object)(object)_driver == (Object)null)
			{
				_driver = ((Component)this).GetComponentInParent<HudCrosshairDriver>();
			}
			DynamicOffsets dynamicOffsets = (((Object)(object)_driver != (Object)null) ? _driver.GetDynamicOffsets() : default(DynamicOffsets));
			if (_lastRevision == CrosshairConfig.Revision && _lastScreenWidth == Screen.width && _lastScreenHeight == Screen.height && Mathf.Approximately(_lastPixelsPerUnit, pixelsPerUnit))
			{
				Vector2 val4 = _lastRootScreen - val2;
				if (((Vector2)(ref val4)).sqrMagnitude < 1E-06f)
				{
					val4 = _lastAnchorScreen - val3;
					if (((Vector2)(ref val4)).sqrMagnitude < 1E-06f && _lastOffsets.Equals(dynamicOffsets))
					{
						return;
					}
				}
			}
			PixelFrame frame = CreateFrame(pixelsPerUnit, val3, val2);
			_circle.Apply(in frame, dynamicOffsets.Circle);
			_outerBars.Apply(in frame, dynamicOffsets.OuterBars);
			_innerBars.Apply(in frame, dynamicOffsets.InnerBars);
			_centerDot.Apply(in frame);
			LogLayoutOnce(rootCanvas, in frame, val2, val3);
			_lastRevision = CrosshairConfig.Revision;
			_lastScreenWidth = Screen.width;
			_lastScreenHeight = Screen.height;
			_lastPixelsPerUnit = pixelsPerUnit;
			_lastRootScreen = val2;
			_lastAnchorScreen = val3;
			_lastOffsets = dynamicOffsets;
		}

		private PixelFrame CreateFrame(float pixelsPerUnit, Vector2 anchorScreen, Vector2 rootScreen)
		{
			//IL_0088: 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)
			float resolutionScale = Mathf.Min((float)Screen.width / 1920f, (float)Screen.height / 1080f);
			float centerFraction = (_innerBars.Enabled ? PixelFrame.FractionFor(_innerBars.Thickness, resolutionScale) : (_outerBars.Enabled ? PixelFrame.FractionFor(_outerBars.Thickness, resolutionScale) : (_centerDot.Enabled ? PixelFrame.FractionFor(_centerDot.Size, resolutionScale) : 0f)));
			return new PixelFrame(resolutionScale, pixelsPerUnit, centerFraction, anchorScreen, rootScreen);
		}

		private float GetPixelsPerUnit(Canvas rootCanvas)
		{
			//IL_0007: 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)
			float x = ((Component)rootCanvas).transform.lossyScale.x;
			if (x <= 0f)
			{
				return 0f;
			}
			return rootCanvas.scaleFactor * (((Transform)_root).lossyScale.x / x);
		}

		private Vector3 GetIntendedCenterWorld()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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)
			Transform parent = ((Transform)_rect).parent;
			if ((Object)(object)parent == (Object)null)
			{
				return ((Transform)_root).position;
			}
			return ((Transform)_root).position - parent.TransformVector(Vector2.op_Implicit(_rect.anchoredPosition));
		}

		private void LogLayoutOnce(Canvas rootCanvas, in PixelFrame frame, Vector2 rootScreen, Vector2 anchorScreen)
		{
			//IL_007c: 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_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: 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 (_loggedScreenWidth != Screen.width || _loggedScreenHeight != Screen.height || !Mathf.Approximately(_loggedPixelsPerUnit, frame.PixelsPerUnit))
			{
				_loggedScreenWidth = Screen.width;
				_loggedScreenHeight = Screen.height;
				_loggedPixelsPerUnit = frame.PixelsPerUnit;
				Plugin.Log.LogDebug((object)($"Crosshair layout: screen {Screen.width}x{Screen.height}, canvas {rootCanvas.renderMode} " + $"scaleFactor {rootCanvas.scaleFactor:0.###} pixelPerfect {rootCanvas.pixelPerfect}, " + $"px/unit {frame.PixelsPerUnit:0.###}, crosshair anchoredPosition {_rect.anchoredPosition}, " + $"root at {rootScreen.x:0.###},{rootScreen.y:0.###} px, anchor {anchorScreen.x:0.###},{anchorScreen.y:0.###} px, " + $"snapped center {frame.Center.x},{frame.Center.y}."));
			}
		}
	}
	[RequireComponent(typeof(Image))]
	internal sealed class RingMesh : MonoBehaviour, IMeshModifier
	{
		private readonly struct Stop
		{
			internal readonly float Radius;

			internal readonly Color32 Color;

			internal Stop(float radius, Color32 color)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				Radius = radius;
				Color = color;
			}
		}

		private readonly struct Shape : IEquatable<Shape>
		{
			internal readonly float Inner;

			internal readonly float Outer;

			internal readonly float Border;

			internal readonly float Feather;

			internal readonly Color32 Fill;

			internal readonly Color32 BorderColor;

			internal readonly int Segments;

			internal Shape(float inner, float outer, float border, float feather, Color32 fill, Color32 borderColor, int segments)
			{
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: 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_0029: Unknown result type (might be due to invalid IL or missing references)
				Inner = inner;
				Outer = outer;
				Border = border;
				Feather = feather;
				Fill = fill;
				BorderColor = borderColor;
				Segments = segments;
			}

			public bool Equals(Shape o)
			{
				//IL_0047: 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_005a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0060: Unknown result type (might be due to invalid IL or missing references)
				return Inner == o.Inner && Outer == o.Outer && Border == o.Border && Feather == o.Feather && Segments == o.Segments && Same(Fill, o.Fill) && Same(BorderColor, o.BorderColor);
			}

			private static bool Same(Color32 a, Color32 b)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_002a: 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)
				return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
			}
		}

		private readonly List<Stop> _stops = new List<Stop>();

		private Shape _shape;

		private bool _hasShape;

		private Image? _image;

		internal Image Image => ((Object)(object)_image != (Object)null) ? _image : (_image = ((Component)this).GetComponent<Image>());

		internal void SetShape(float inner, float outer, float border, float feather, Color fill, Color borderColor, int segments)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			Shape shape = new Shape(inner, outer, border, feather, Color32.op_Implicit(fill), Color32.op_Implicit(borderColor), segments);
			if (!_hasShape || !shape.Equals(_shape))
			{
				_shape = shape;
				_hasShape = true;
				((Graphic)Image).SetVerticesDirty();
			}
		}

		public void ModifyMesh(Mesh mesh)
		{
		}

		public void ModifyMesh(VertexHelper vh)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			vh.Clear();
			if (!_hasShape)
			{
				return;
			}
			BuildStops(_shape);
			int segments = _shape.Segments;
			float num = MathF.PI * 2f / (float)segments;
			Vector3 val = default(Vector3);
			for (int i = 0; i < _stops.Count; i++)
			{
				Stop stop = _stops[i];
				for (int j = 0; j < segments; j++)
				{
					float num2 = (float)j * num;
					((Vector3)(ref val))..ctor(Mathf.Cos(num2) * stop.Radius, Mathf.Sin(num2) * stop.Radius);
					vh.AddVert(val, stop.Color, Vector4.zero);
				}
			}
			for (int k = 0; k + 1 < _stops.Count; k++)
			{
				int num3 = k * segments;
				int num4 = (k + 1) * segments;
				for (int l = 0; l < segments; l++)
				{
					int num5 = (l + 1) % segments;
					vh.AddTriangle(num3 + l, num4 + l, num4 + num5);
					vh.AddTriangle(num3 + l, num4 + num5, num3 + num5);
				}
			}
		}

		private void BuildStops(Shape shape)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: 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_00df: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			_stops.Clear();
			float num = shape.Feather * 0.5f;
			bool flag = shape.Border > 0f;
			List<float> list = new List<float>(4);
			List<Color32> list2 = new List<Color32>(3);
			if (flag && shape.Inner > 0f)
			{
				list.Add(Mathf.Max(0f, shape.Inner - shape.Border));
				list2.Add(shape.BorderColor);
			}
			list.Add(shape.Inner);
			list2.Add(shape.Fill);
			list.Add(shape.Outer);
			if (flag)
			{
				list2.Add(shape.BorderColor);
				list.Add(shape.Outer + shape.Border);
			}
			for (int i = 0; i < list.Count; i++)
			{
				Color32 color = ((i == 0) ? Transparent(list2[0]) : list2[i - 1]);
				Color32 color2 = ((i == list.Count - 1) ? Transparent(list2[i - 1]) : list2[i]);
				if (i == 0 && list[0] <= 0f)
				{
					AddStop(0f, color2);
					continue;
				}
				AddStop(list[i] - num, color);
				AddStop(list[i] + num, color2);
			}
		}

		private void AddStop(float radius, Color32 color)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			float num = ((_stops.Count > 0) ? _stops[_stops.Count - 1].Radius : 0f);
			_stops.Add(new Stop(Mathf.Max(radius, num), color));
		}

		private static Color32 Transparent(Color32 color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			return new Color32(color.r, color.g, color.b, (byte)0);
		}
	}
	internal sealed class ToolbotSpreadSource : ISpreadSource
	{
		private const string ToolbotBodyName = "ToolbotBody";

		private BodyIndex _toolbotBodyIndex = (BodyIndex)(-1);

		private readonly Dictionary<SkillDef, bool> _isNailgun = new Dictionary<SkillDef, bool>();

		private CharacterBody? _loggedBody;

		private SkillDef? _loggedPrimary;

		private SkillDef? _loggedOffhand;

		public bool LogSamples => true;

		public bool CanHandle(CharacterBody body)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			if ((int)_toolbotBodyIndex == -1)
			{
				_toolbotBodyIndex = BodyCatalog.FindBodyIndex("ToolbotBody");
				if ((int)_toolbotBodyIndex == -1)
				{
					return false;
				}
			}
			return body.bodyIndex == _toolbotBodyIndex;
		}

		public bool TrySample(CharacterBody body, out SpreadSample sample)
		{
			sample = default(SpreadSample);
			SkillLocator skillLocator = body.skillLocator;
			if (!Object.op_Implicit((Object)(object)skillLocator))
			{
				return false;
			}
			ToolbotWeaponSkillDef val = (ToolbotWeaponSkillDef)(Object.op_Implicit((Object)(object)skillLocator.primary) ? /*isinst with value type is only supported in some contexts*/: null);
			ToolbotWeaponSkillDef val2 = (ToolbotWeaponSkillDef)(Object.op_Implicit((Object)(object)skillLocator.secondary) ? /*isinst with value type is only supported in some contexts*/: null);
			LogWeaponsOnce(body, (SkillDef?)(object)val, (SkillDef?)(object)val2);
			if (!IsNailgun((SkillDef?)(object)val) && !IsNailgun((SkillDef?)(object)val2))
			{
				return false;
			}
			if (body.spreadBloomCurve == null)
			{
				return false;
			}
			float num = NailgunEnvelopeScale();
			float spreadBloomAngle = body.spreadBloomAngle;
			float num2 = body.spreadBloomCurve.Evaluate(0f);
			sample = new SpreadSample(spreadBloomAngle * num, num2 * num, spreadBloomAngle);
			return true;
		}

		private static float NailgunEnvelopeScale()
		{
			float spreadYawScale = BaseNailgunState.spreadYawScale;
			float spreadPitchScale = BaseNailgunState.spreadPitchScale;
			return Math.Max(1f, Math.Max(spreadYawScale * spreadYawScale, spreadPitchScale * spreadPitchScale));
		}

		private bool IsNailgun(SkillDef? skill)
		{
			if ((Object)(object)skill == (Object)null)
			{
				return false;
			}
			if (!_isNailgun.TryGetValue(skill, out var value))
			{
				Type stateType = ((SerializableEntityStateType)(ref skill.activationState)).stateType;
				value = stateType != null && typeof(FireNailgun).IsAssignableFrom(stateType);
				_isNailgun[skill] = value;
			}
			return value;
		}

		private void LogWeaponsOnce(CharacterBody body, SkillDef? primary, SkillDef? offhand)
		{
			if (!((Object)(object)_loggedBody == (Object)(object)body) || !((Object)(object)_loggedPrimary == (Object)(object)primary) || !((Object)(object)_loggedOffhand == (Object)(object)offhand))
			{
				_loggedBody = body;
				_loggedPrimary = primary;
				_loggedOffhand = offhand;
				string arg = ((body.spreadBloomCurve != null) ? $"{body.spreadBloomCurve.Evaluate(0f):0.###}..{body.spreadBloomCurve.Evaluate(1f):0.###} deg" : "none");
				Plugin.Log.LogDebug((object)("[Spread] MUL-T weapons: active " + Describe(primary) + (((Object)(object)offhand != (Object)null) ? (", Power Mode offhand " + Describe(offhand)) : "") + $"; body bloom curve {arg}, decay {body.spreadBloomDecayTime:0.###} s; " + $"nailgun envelope scale {NailgunEnvelopeScale():0.###}."));
			}
			string Describe(SkillDef? skill)
			{
				return ((Object)(object)skill == (Object)null) ? "none" : ("'" + skill.skillName + "' (" + ((SerializableEntityStateType)(ref skill.activationState)).stateType?.Name + ", " + (IsNailgun(skill) ? "dynamic spread" : "no spread growth") + ")");
			}
		}
	}
}

src/RoR2CustomCrosshair/obj/Debug/netstandard2.1/RoR2CustomCrosshair.dll

Decompiled 7 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using EntityStates;
using EntityStates.Bandit2.Weapon;
using EntityStates.Commando.CommandoWeapon;
using EntityStates.Toolbot;
using Microsoft.CodeAnalysis;
using On.RoR2.UI;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.Skills;
using RoR2.UI;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("RoR2CustomCrosshair")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+9a54713ad61bad5c325b1410f08b5cad2eda5494")]
[assembly: AssemblyProduct("RoR2CustomCrosshair")]
[assembly: AssemblyTitle("RoR2CustomCrosshair")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RoR2CustomCrosshair
{
	internal sealed class BanditSpreadSource : ISpreadSource
	{
		private sealed class Weapon
		{
			internal readonly string Name;

			internal readonly GenericBulletSpread Spread;

			internal readonly float FanDegrees;

			internal readonly bool HasFan;

			internal Weapon(string name, GenericBulletSpread spread, bool hasFan, float fanDegrees)
			{
				Name = name;
				Spread = spread;
				HasFan = hasFan;
				FanDegrees = fanDegrees;
			}

			internal float EnvelopeDegrees(float bloom)
			{
				float num = (HasFan ? (FanDegrees + bloom) : 0f);
				return Mathf.Max(num + Spread.HorizontalDegrees(bloom), Spread.VerticalDegrees(bloom));
			}
		}

		private const string BanditBodyName = "Bandit2Body";

		private BodyIndex _banditBodyIndex = (BodyIndex)(-1);

		private readonly Dictionary<Type, Weapon?> _weapons = new Dictionary<Type, Weapon>();

		private SkillDef? _lastPrimarySkill;

		private Weapon? _lastWeapon;

		private CharacterBody? _loggedBody;

		private Weapon? _loggedWeapon;

		public bool LogSamples => false;

		public bool CanHandle(CharacterBody body)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			if ((int)_banditBodyIndex == -1)
			{
				_banditBodyIndex = BodyCatalog.FindBodyIndex("Bandit2Body");
				if ((int)_banditBodyIndex == -1)
				{
					return false;
				}
			}
			return body.bodyIndex == _banditBodyIndex;
		}

		public bool TrySample(CharacterBody body, out SpreadSample sample)
		{
			sample = default(SpreadSample);
			Weapon weapon = CurrentPrimary(body);
			if (weapon == null || body.spreadBloomCurve == null)
			{
				return false;
			}
			float spreadBloomAngle = body.spreadBloomAngle;
			float bloom = body.spreadBloomCurve.Evaluate(0f);
			sample = new SpreadSample(weapon.EnvelopeDegrees(spreadBloomAngle), weapon.EnvelopeDegrees(bloom), spreadBloomAngle);
			LogWeaponOnce(body, weapon);
			return true;
		}

		private Weapon? CurrentPrimary(CharacterBody body)
		{
			GenericSkill val = (Object.op_Implicit((Object)(object)body.skillLocator) ? body.skillLocator.primary : null);
			SkillDef val2 = (Object.op_Implicit((Object)(object)val) ? val.skillDef : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return null;
			}
			if ((Object)(object)val2 != (Object)(object)_lastPrimarySkill)
			{
				_lastPrimarySkill = val2;
				Type stateType = ((SerializableEntityStateType)(ref val2.activationState)).stateType;
				_lastWeapon = ((stateType != null) ? WeaponFor(stateType) : null);
			}
			return _lastWeapon;
		}

		private Weapon? WeaponFor(Type stateType)
		{
			if (_weapons.TryGetValue(stateType, out Weapon value))
			{
				return value;
			}
			Weapon weapon = null;
			if (typeof(FireShotgun2).IsAssignableFrom(stateType))
			{
				FireShotgun2 val = GenericBulletSpread.ReadState<FireShotgun2>(stateType);
				if (val != null)
				{
					bool hasFan = ((GenericBulletBaseState)val).bulletCount > 1;
					float fanDegrees = Mathf.Max(val.minFixedSpreadYaw, val.maxFixedSpreadYaw);
					weapon = new Weapon(stateType.Name, GenericBulletSpread.From((GenericBulletBaseState)(object)val), hasFan, fanDegrees);
				}
			}
			else if (typeof(Bandit2FireRifle).IsAssignableFrom(stateType))
			{
				Bandit2FireRifle val2 = GenericBulletSpread.ReadState<Bandit2FireRifle>(stateType);
				if (val2 != null)
				{
					weapon = new Weapon(stateType.Name, GenericBulletSpread.From((GenericBulletBaseState)(object)val2), hasFan: false, 0f);
				}
			}
			_weapons[stateType] = weapon;
			return weapon;
		}

		private void LogWeaponOnce(CharacterBody body, Weapon weapon)
		{
			if (!((Object)(object)_loggedBody == (Object)(object)body) || _loggedWeapon != weapon)
			{
				_loggedBody = body;
				_loggedWeapon = weapon;
				AnimationCurve spreadBloomCurve = body.spreadBloomCurve;
				float num = spreadBloomCurve.Evaluate(0f);
				float num2 = spreadBloomCurve.Evaluate(1f);
				Plugin.Log.LogDebug((object)("[Spread] Bandit source active: primary '" + _lastPrimarySkill?.skillName + "' (" + weapon.Name + "), " + weapon.Spread.Describe() + ", fan " + (weapon.HasFan ? $"+/-{weapon.FanDegrees:0.###} deg + bloom" : "none") + "; " + $"bloom {num:0.###}..{num2:0.###} deg, decay {body.spreadBloomDecayTime:0.###} s " + $"(attack speed scales decay: {body.spreadBloomDecayWithAttackSpeed}); " + $"envelope {weapon.EnvelopeDegrees(num):0.###} deg at rest, {weapon.EnvelopeDegrees(num2):0.###} deg at full bloom."));
			}
		}
	}
	internal sealed class CommandoSpreadSource : ISpreadSource
	{
		private const string CommandoBodyName = "CommandoBody";

		private BodyIndex _commandoBodyIndex = (BodyIndex)(-1);

		private SkillDef? _lastPrimarySkill;

		private bool _lastPrimaryIsDoubleTap;

		private CharacterBody? _loggedBody;

		public bool LogSamples => false;

		public bool CanHandle(CharacterBody body)
		{
			return IsCommando(body);
		}

		public bool TrySample(CharacterBody body, out SpreadSample sample)
		{
			sample = default(SpreadSample);
			if (!IsCommando(body) || !PrimaryIsDoubleTap(body) || body.spreadBloomCurve == null)
			{
				return false;
			}
			sample = new SpreadSample(body.spreadBloomAngle, body.spreadBloomCurve.Evaluate(0f), body.spreadBloomAngle);
			LogBodyOnce(body);
			return true;
		}

		private bool IsCommando(CharacterBody body)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			if ((int)_commandoBodyIndex == -1)
			{
				_commandoBodyIndex = BodyCatalog.FindBodyIndex("CommandoBody");
				if ((int)_commandoBodyIndex == -1)
				{
					return false;
				}
			}
			return body.bodyIndex == _commandoBodyIndex;
		}

		private bool PrimaryIsDoubleTap(CharacterBody body)
		{
			GenericSkill val = (Object.op_Implicit((Object)(object)body.skillLocator) ? body.skillLocator.primary : null);
			SkillDef val2 = (Object.op_Implicit((Object)(object)val) ? val.skillDef : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			if ((Object)(object)val2 != (Object)(object)_lastPrimarySkill)
			{
				_lastPrimarySkill = val2;
				Type stateType = ((SerializableEntityStateType)(ref val2.activationState)).stateType;
				_lastPrimaryIsDoubleTap = stateType != null && typeof(FirePistol2).IsAssignableFrom(stateType);
			}
			return _lastPrimaryIsDoubleTap;
		}

		private void LogBodyOnce(CharacterBody body)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_loggedBody == (Object)(object)body)
			{
				return;
			}
			_loggedBody = body;
			string text = "none";
			GameObject defaultCrosshairPrefab = body.defaultCrosshairPrefab;
			CrosshairController val = default(CrosshairController);
			if (Object.op_Implicit((Object)(object)defaultCrosshairPrefab) && defaultCrosshairPrefab.TryGetComponent<CrosshairController>(ref val))
			{
				text = $"{((Object)defaultCrosshairPrefab).name} maxSpreadAngle {val.maxSpreadAngle:0.###}";
				SpritePosition[] spriteSpreadPositions = val.spriteSpreadPositions;
				if (spriteSpreadPositions != null && spriteSpreadPositions.Length > 0)
				{
					SpritePosition val2 = spriteSpreadPositions[0];
					text += string.Format(", sprite '{0}' moves {1} -> {2}", Object.op_Implicit((Object)(object)val2.target) ? ((Object)val2.target).name : "?", val2.zeroPosition, val2.onePosition);
				}
			}
			AnimationCurve spreadBloomCurve = body.spreadBloomCurve;
			Plugin.Log.LogDebug((object)("[Spread] Commando source active: primary '" + _lastPrimarySkill?.skillName + "', " + $"spread {spreadBloomCurve.Evaluate(0f):0.###} deg at rest, {spreadBloomCurve.Evaluate(1f):0.###} deg at full bloom, " + $"decay time {body.spreadBloomDecayTime:0.###} s (scales with attack speed: {body.spreadBloomDecayWithAttackSpeed}), " + "vanilla crosshair: " + text + "."));
		}
	}
	internal static class CrosshairConfig
	{
		private const int CurrentConfigVersion = 2;

		private const float SliderStep = 0.5f;

		internal static ConfigEntry<bool> Enabled;

		internal static BarsSettings InnerBars;

		internal static BarsSettings OuterBars;

		internal static CircleSettings Circle;

		internal static DotSettings CenterDot;

		internal static int Revision { get; private set; }

		internal static void Bind(ConfigFile config)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			Enabled = config.Bind<bool>("General", "Enabled", true, "Replace the ordinary crosshair of your own survivor with the custom crosshair.");
			InnerBars = new BarsSettings(config, "Inner Bars", new BarsDefaults
			{
				Enabled = true,
				Length = 8f,
				Thickness = 2f,
				Gap = 4f,
				GapMax = 64f,
				Fill = new Color32((byte)0, byte.MaxValue, (byte)0, byte.MaxValue)
			});
			OuterBars = new BarsSettings(config, "Outer Bars", new BarsDefaults
			{
				Enabled = false,
				Length = 10f,
				Thickness = 2f,
				Gap = 14f,
				GapMax = 128f,
				Fill = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue)
			});
			Circle = new CircleSettings(config, "Circle");
			CenterDot = new DotSettings(config, "Center Dot");
			MigrateFromVersion1(config);
			config.Save();
			config.SaveOnConfigSet = saveOnConfigSet;
			config.SettingChanged += delegate
			{
				Revision++;
			};
		}

		internal static void RegisterRiskOfOptions()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			ModSettingsManager.SetModDescription("Procedural crosshair with Inner Bars, Outer Bars, Circle and Center Dot layers. Sizes are pixels at 1920x1080 and scale with resolution.");
			Sprite val = ModIcon.Load();
			if ((Object)(object)val != (Object)null)
			{
				ModSettingsManager.SetModIcon(val, "com.example.ror2customcrosshair", "RoR2 Custom Crosshair");
			}
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(Enabled));
			InnerBars.Register();
			OuterBars.Register();
			Circle.Register();
			CenterDot.Register();
		}

		private static void MigrateFromVersion1(ConfigFile config)
		{
			ConfigEntry<int> val = config.Bind<int>("Internal", "Config Version", 0, "Settings layout version, used for migration. Do not edit.");
			if (val.Value < 2)
			{
				BarsSettings innerBars = InnerBars;
				Move<float>(config, "Crosshair", "Length", innerBars.Length);
				Move<float>(config, "Crosshair", "Thickness", innerBars.Thickness);
				Move<float>(config, "Crosshair", "Gap", innerBars.Gap);
				Move<int>(config, "Crosshair Color", "Opacity", innerBars.Fill.Opacity);
				Move<int>(config, "Crosshair Color", "R", innerBars.Fill.Red);
				Move<int>(config, "Crosshair Color", "G", innerBars.Fill.Green);
				Move<int>(config, "Crosshair Color", "B", innerBars.Fill.Blue);
				Move<bool>(config, "Outline", "Enabled", innerBars.Outline.Enabled);
				Move<float>(config, "Outline", "Thickness", innerBars.Outline.Thickness);
				Move<int>(config, "Outline Color", "Opacity", innerBars.Outline.Color.Opacity);
				Move<int>(config, "Outline Color", "R", innerBars.Outline.Color.Red);
				Move<int>(config, "Outline Color", "G", innerBars.Outline.Color.Green);
				Move<int>(config, "Outline Color", "B", innerBars.Outline.Color.Blue);
				val.Value = 2;
				Plugin.Log.LogInfo((object)"Migrated crosshair settings to the layered layout (old values now under 'Inner Bars').");
			}
		}

		private static void Move<T>(ConfigFile config, string oldSection, string oldKey, ConfigEntry<T> target)
		{
			ConfigEntry<T> val = config.Bind<T>(oldSection, oldKey, (T)((ConfigEntryBase)target).DefaultValue, (ConfigDescription)null);
			target.Value = val.Value;
			config.Remove(((ConfigEntryBase)val).Definition);
		}

		internal static ConfigEntry<float> BindFloat(ConfigFile config, string section, string key, float defaultValue, float min, float max, string description)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			return config.Bind<float>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()));
		}

		internal static ConfigEntry<int> BindByte(ConfigFile config, string section, string key, int defaultValue)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			return config.Bind<int>(section, key, defaultValue, new ConfigDescription(key + " value from 0 to 255.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 255), Array.Empty<object>()));
		}

		internal static void AddFloatOption(ConfigEntry<float> entry, float min, float max)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: 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_0031: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(entry, new StepSliderConfig
			{
				min = min,
				max = max,
				increment = 0.5f,
				FormatString = "{0:0.0}"
			}));
		}

		internal static void AddByteOption(ConfigEntry<int> entry)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(entry, new IntSliderConfig
			{
				min = 0,
				max = 255
			}));
		}
	}
	internal sealed class ColorSettings
	{
		internal readonly ConfigEntry<int> Red;

		internal readonly ConfigEntry<int> Green;

		internal readonly ConfigEntry<int> Blue;

		internal readonly ConfigEntry<int> Opacity;

		internal Color Value => new Color((float)Red.Value / 255f, (float)Green.Value / 255f, (float)Blue.Value / 255f, (float)Opacity.Value / 255f);

		internal ColorSettings(ConfigFile config, string section, string keyPrefix, Color32 defaults)
		{
			//IL_0016: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			Red = CrosshairConfig.BindByte(config, section, keyPrefix + "R", defaults.r);
			Green = CrosshairConfig.BindByte(config, section, keyPrefix + "G", defaults.g);
			Blue = CrosshairConfig.BindByte(config, section, keyPrefix + "B", defaults.b);
			Opacity = CrosshairConfig.BindByte(config, section, keyPrefix + "Opacity", defaults.a);
		}

		internal void Register()
		{
			CrosshairConfig.AddByteOption(Red);
			CrosshairConfig.AddByteOption(Green);
			CrosshairConfig.AddByteOption(Blue);
			CrosshairConfig.AddByteOption(Opacity);
		}
	}
	internal sealed class OutlineSettings
	{
		internal const float MaxThickness = 10f;

		internal readonly ConfigEntry<bool> Enabled;

		internal readonly ConfigEntry<float> Thickness;

		internal readonly ColorSettings Color;

		internal bool Visible => Enabled.Value && Thickness.Value > 0.001f;

		internal OutlineSettings(ConfigFile config, string section)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			Enabled = config.Bind<bool>(section, "Outline Enabled", true, "Draw a border around this layer.");
			Thickness = CrosshairConfig.BindFloat(config, section, "Outline Thickness", 1f, 0f, 10f, "Border width on each side, in pixels at 1920x1080.");
			Color = new ColorSettings(config, section, "Outline ", new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue));
		}

		internal void Register()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(Enabled));
			CrosshairConfig.AddFloatOption(Thickness, 0f, 10f);
			Color.Register();
		}
	}
	internal struct BarsDefaults
	{
		internal bool Enabled;

		internal float Length;

		internal float Thickness;

		internal float Gap;

		internal float GapMax;

		internal Color32 Fill;
	}
	internal sealed class BarsSettings
	{
		internal const float MaxLength = 64f;

		internal const float MaxThickness = 20f;

		private readonly float _gapMax;

		internal readonly ConfigEntry<bool> Enabled;

		internal readonly ConfigEntry<CrosshairMode> Mode;

		internal readonly ConfigEntry<float> Length;

		internal readonly ConfigEntry<float> Thickness;

		internal readonly ConfigEntry<float> Gap;

		internal readonly ColorSettings Fill;

		internal readonly OutlineSettings Outline;

		internal BarsSettings(ConfigFile config, string section, BarsDefaults defaults)
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			_gapMax = defaults.GapMax;
			Enabled = config.Bind<bool>(section, "Enabled", defaults.Enabled, "Show the " + section + ".");
			Mode = config.Bind<CrosshairMode>(section, "Mode", CrosshairMode.Static, "Static: fixed gap. Dynamic: gap will follow actual weapon spread (not implemented yet; currently same as Static).");
			Length = CrosshairConfig.BindFloat(config, section, "Length", defaults.Length, 0.5f, 64f, "Length of each arm, in pixels at 1920x1080.");
			Thickness = CrosshairConfig.BindFloat(config, section, "Thickness", defaults.Thickness, 0.5f, 20f, "Thickness of each arm, in pixels at 1920x1080.");
			Gap = CrosshairConfig.BindFloat(config, section, "Gap", defaults.Gap, 0f, _gapMax, "Distance from screen center to the inner edge of each arm, in pixels at 1920x1080.");
			Fill = new ColorSettings(config, section, "", defaults.Fill);
			Outline = new OutlineSettings(config, section);
		}

		internal void Register()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(Enabled));
			ModSettingsManager.AddOption((BaseOption)new ChoiceOption((ConfigEntryBase)(object)Mode));
			CrosshairConfig.AddFloatOption(Length, 0.5f, 64f);
			CrosshairConfig.AddFloatOption(Thickness, 0.5f, 20f);
			CrosshairConfig.AddFloatOption(Gap, 0f, _gapMax);
			Fill.Register();
			Outline.Register();
		}
	}
	internal sealed class CircleSettings
	{
		internal const float MaxRadius = 128f;

		internal const float MaxThickness = 20f;

		internal readonly ConfigEntry<bool> Enabled;

		internal readonly ConfigEntry<CrosshairMode> Mode;

		internal readonly ConfigEntry<float> Radius;

		internal readonly ConfigEntry<float> Thickness;

		internal readonly ColorSettings Fill;

		internal readonly OutlineSettings Outline;

		internal CircleSettings(ConfigFile config, string section)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			Enabled = config.Bind<bool>(section, "Enabled", false, "Show the circle.");
			Mode = config.Bind<CrosshairMode>(section, "Mode", CrosshairMode.Static, "Static: fixed radius. Dynamic: radius will follow actual weapon spread (not implemented yet; currently same as Static).");
			Radius = CrosshairConfig.BindFloat(config, section, "Radius", 16f, 0f, 128f, "Distance from screen center to the inner edge of the ring, in pixels at 1920x1080.");
			Thickness = CrosshairConfig.BindFloat(config, section, "Thickness", 2f, 0.5f, 20f, "Ring thickness, in pixels at 1920x1080.");
			Fill = new ColorSettings(config, section, "", new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue));
			Outline = new OutlineSettings(config, section);
		}

		internal void Register()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(Enabled));
			ModSettingsManager.AddOption((BaseOption)new ChoiceOption((ConfigEntryBase)(object)Mode));
			CrosshairConfig.AddFloatOption(Radius, 0f, 128f);
			CrosshairConfig.AddFloatOption(Thickness, 0.5f, 20f);
			Fill.Register();
			Outline.Register();
		}
	}
	internal sealed class DotSettings
	{
		internal const float MaxSize = 20f;

		internal readonly ConfigEntry<bool> Enabled;

		internal readonly ConfigEntry<float> Size;

		internal readonly ColorSettings Fill;

		internal readonly OutlineSettings Outline;

		internal DotSettings(ConfigFile config, string section)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			Enabled = config.Bind<bool>(section, "Enabled", false, "Show the center dot.");
			Size = CrosshairConfig.BindFloat(config, section, "Size", 2f, 0.5f, 20f, "Diameter of the dot, in pixels at 1920x1080.");
			Fill = new ColorSettings(config, section, "", new Color32((byte)0, byte.MaxValue, (byte)0, byte.MaxValue));
			Outline = new OutlineSettings(config, section);
		}

		internal void Register()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(Enabled));
			CrosshairConfig.AddFloatOption(Size, 0.5f, 20f);
			Fill.Register();
			Outline.Register();
		}
	}
	internal static class CrosshairDisplayPolicy
	{
		private const string StandardCrosshairAddress = "RoR2/Base/UI/StandardCrosshair.prefab";

		private const string GenericSprintCrosshairPath = "Prefabs/Crosshair/SprintingCrosshair";

		private static readonly string[] StockPreservingAddresses = new string[3] { "RoR2/Base/Bandit2/Bandit2Crosshair.prefab", "RoR2/Base/Toolbot/ToolbotGrenadeLauncherCrosshair.prefab", "RoR2/Base/Toolbot/ToolbotDualWieldCrosshair.prefab" };

		private static readonly (string Address, string? BodyName)[] CosmeticSkillCrosshairAddresses = new(string, string)[3]
		{
			("RoR2/Base/Bandit2/Bandit2CrosshairPrepRevolver.prefab", null),
			("RoR2/Base/Bandit2/Bandit2CrosshairPrepRevolverFire.prefab", null),
			("RoR2/Base/UI/BadCrosshair.prefab", "ToolbotBody")
		};

		private static Transform? _holder;

		private static GameObject? _standardTemplate;

		private static readonly Dictionary<GameObject, GameObject?> StockPreserving = new Dictionary<GameObject, GameObject>();

		private static readonly Dictionary<GameObject, string?> CosmeticSkillCrosshairs = new Dictionary<GameObject, string>();

		private static readonly Dictionary<string, BodyIndex> BodyIndices = new Dictionary<string, BodyIndex>();

		private static GameObject? _genericSprintCrosshair;

		private static bool _genericSprintCrosshairLoaded;

		private static int _pendingLoads;

		internal static bool IsReady => _pendingLoads == 0 && (Object)(object)_standardTemplate != (Object)null;

		internal static void Initialize(Transform holder)
		{
			_holder = holder;
			_pendingLoads = 1 + StockPreservingAddresses.Length + CosmeticSkillCrosshairAddresses.Length;
			CrosshairPrefabFactory.LoadAsync("RoR2/Base/UI/StandardCrosshair.prefab", delegate(GameObject? standard)
			{
				_standardTemplate = Build(standard, (GameObject s) => CrosshairPrefabFactory.BuildStandard(s, holder));
				if ((Object)(object)_standardTemplate != (Object)null)
				{
					Plugin.Log.LogInfo((object)"Crosshair template ready.");
				}
				else
				{
					Plugin.Log.LogError((object)"Crosshair template unavailable; vanilla crosshairs will be used.");
				}
				_pendingLoads--;
			});
			string[] stockPreservingAddresses = StockPreservingAddresses;
			foreach (string address in stockPreservingAddresses)
			{
				CrosshairPrefabFactory.LoadAsync(address, delegate(GameObject? vanilla)
				{
					if ((Object)(object)vanilla != (Object)null)
					{
						GameObject val = Build(vanilla, (GameObject v) => CrosshairPrefabFactory.BuildStockPreserving(v, holder));
						StockPreserving[vanilla] = val;
						if ((Object)(object)val == (Object)null)
						{
							Plugin.Log.LogWarning((object)(((Object)vanilla).name + ": ammo displays could not be preserved; its vanilla crosshair will be kept."));
						}
					}
					_pendingLoads--;
				});
			}
			(string, string)[] cosmeticSkillCrosshairAddresses = CosmeticSkillCrosshairAddresses;
			for (int num2 = 0; num2 < cosmeticSkillCrosshairAddresses.Length; num2++)
			{
				var (address2, bodyName) = cosmeticSkillCrosshairAddresses[num2];
				CrosshairPrefabFactory.LoadAsync(address2, delegate(GameObject? prefab)
				{
					if ((Object)(object)prefab != (Object)null)
					{
						CosmeticSkillCrosshairs[prefab] = bodyName;
					}
					_pendingLoads--;
				});
			}
		}

		internal static void Shutdown()
		{
			_standardTemplate = null;
			StockPreserving.Clear();
			CosmeticSkillCrosshairs.Clear();
			BodyIndices.Clear();
			_holder = null;
		}

		internal static GameObject? ChooseTemplate(CharacterBody body, GameObject picked)
		{
			if (!IsReady || !Object.op_Implicit((Object)(object)picked))
			{
				return null;
			}
			if (StockPreserving.ContainsKey(picked) || IsOrdinaryCrosshair(body, picked) || IsActiveWeaponCrosshair(body, picked))
			{
				return TemplateFor(picked);
			}
			if (IsGenericSprintCrosshair(body, picked) || IsCosmeticSkillCrosshair(body, picked))
			{
				return TemplateFor(ActiveWeaponCrosshair(body) ?? body.defaultCrosshairPrefab);
			}
			return null;
		}

		private static GameObject? TemplateFor(GameObject? vanilla)
		{
			if ((Object)(object)vanilla != (Object)null && StockPreserving.TryGetValue(vanilla, out GameObject value))
			{
				return value;
			}
			return _standardTemplate;
		}

		private static GameObject? ActiveWeaponCrosshair(CharacterBody body)
		{
			GenericSkill val = (Object.op_Implicit((Object)(object)body.skillLocator) ? body.skillLocator.primary : null);
			object result;
			if (Object.op_Implicit((Object)(object)val))
			{
				SkillDef skillDef = val.skillDef;
				ToolbotWeaponSkillDef val2 = (ToolbotWeaponSkillDef)(object)((skillDef is ToolbotWeaponSkillDef) ? skillDef : null);
				if (val2 != null && Object.op_Implicit((Object)(object)val2.crosshairPrefab))
				{
					result = val2.crosshairPrefab;
					goto IL_004a;
				}
			}
			result = null;
			goto IL_004a;
			IL_004a:
			return (GameObject?)result;
		}

		private static bool IsActiveWeaponCrosshair(CharacterBody body, GameObject picked)
		{
			GameObject val = ActiveWeaponCrosshair(body);
			return (Object)(object)val != (Object)null && (Object)(object)picked == (Object)(object)val;
		}

		private static bool IsCosmeticSkillCrosshair(CharacterBody body, GameObject picked)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Invalid comparison between Unknown and I4
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Invalid comparison between Unknown and I4
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (!CosmeticSkillCrosshairs.TryGetValue(picked, out string value))
			{
				return false;
			}
			if (value == null)
			{
				return true;
			}
			if (!BodyIndices.TryGetValue(value, out var value2) || (int)value2 == -1)
			{
				value2 = BodyCatalog.FindBodyIndex(value);
				BodyIndices[value] = value2;
			}
			return (int)value2 != -1 && body.bodyIndex == value2;
		}

		private static GameObject? Build(GameObject? source, Func<GameObject, GameObject?> build)
		{
			if ((Object)(object)source == (Object)null || (Object)(object)_holder == (Object)null)
			{
				return null;
			}
			try
			{
				return build(source);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Could not build crosshair template from {((Object)source).name}: {arg}");
				return null;
			}
		}

		private static bool IsOrdinaryCrosshair(CharacterBody body, GameObject picked)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Invalid comparison between Unknown and I4
			if ((Object)(object)picked == (Object)(object)body.defaultCrosshairPrefab)
			{
				return true;
			}
			SkillCrosshairController val = default(SkillCrosshairController);
			if (((Component)body).TryGetComponent<SkillCrosshairController>(ref val) && val.options != null)
			{
				SkillDefCrosshairOptions[] options = val.options;
				foreach (SkillDefCrosshairOptions val2 in options)
				{
					if (val2 != null && (int)val2.priority < 1 && (Object)(object)val2.crosshairPrefab == (Object)(object)picked)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool IsGenericSprintCrosshair(CharacterBody body, GameObject picked)
		{
			if (!body.isSprinting || !body.useSprintCrosshair || Object.op_Implicit((Object)(object)body.sprintCrosshairPrefabOverride))
			{
				return false;
			}
			GameObject genericSprintCrosshair = GetGenericSprintCrosshair();
			return (Object)(object)genericSprintCrosshair != (Object)null && (Object)(object)picked == (Object)(object)genericSprintCrosshair;
		}

		private static GameObject? GetGenericSprintCrosshair()
		{
			if (!_genericSprintCrosshairLoaded)
			{
				_genericSprintCrosshairLoaded = true;
				_genericSprintCrosshair = LegacyResourcesAPI.Load<GameObject>("Prefabs/Crosshair/SprintingCrosshair");
				if ((Object)(object)_genericSprintCrosshair == (Object)null)
				{
					Plugin.Log.LogWarning((object)"Could not load 'Prefabs/Crosshair/SprintingCrosshair'; the vanilla sprint crosshair will be shown.");
				}
			}
			return _genericSprintCrosshair;
		}
	}
	internal readonly struct PixelFrame
	{
		internal readonly float ResolutionScale;

		internal readonly float PixelsPerUnit;

		internal readonly float CenterFraction;

		internal readonly Vector2 Center;

		internal readonly Vector2 OriginOffset;

		internal PixelFrame(float resolutionScale, float pixelsPerUnit, float centerFraction, Vector2 anchorScreen, Vector2 rootScreen)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			ResolutionScale = resolutionScale;
			PixelsPerUnit = pixelsPerUnit;
			CenterFraction = centerFraction;
			Center = new Vector2(SnapToGrid(anchorScreen.x, centerFraction), SnapToGrid(anchorScreen.y, centerFraction));
			OriginOffset = Center - rootScreen;
		}

		internal static float FractionFor(float logicalSize, float resolutionScale)
		{
			return (Mathf.Max(1, RoundPx(logicalSize * resolutionScale)) % 2 == 0) ? 0f : 0.5f;
		}

		internal int Width(float logical)
		{
			return Mathf.Max(1, RoundPx(logical * ResolutionScale));
		}

		internal int CenteredSize(float logical)
		{
			float num = logical * ResolutionScale;
			if (CenterFraction == 0f)
			{
				return Mathf.Max(2, 2 * RoundPx(num * 0.5f));
			}
			return Mathf.Max(1, 2 * (int)Mathf.Floor(num * 0.5f) + 1);
		}

		internal float EdgeDistance(float logical, float extraPixels = 0f)
		{
			float num = Mathf.Max(0f, Mathf.Max(0f, logical) * ResolutionScale + extraPixels);
			return Mathf.Floor(CenterFraction + num + 0.5f) - CenterFraction;
		}

		internal float ToLocal(float pixels)
		{
			return pixels / PixelsPerUnit;
		}

		internal void Place(RectTransform rect, Vector4 pixelRect)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//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_003d: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor((pixelRect.x + pixelRect.z) * 0.5f, (pixelRect.y + pixelRect.w) * 0.5f);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(pixelRect.z - pixelRect.x, pixelRect.w - pixelRect.y);
			rect.anchoredPosition = (OriginOffset + val) / PixelsPerUnit;
			rect.sizeDelta = val2 / PixelsPerUnit;
		}

		internal static int RoundPx(float value)
		{
			return (int)Mathf.Floor(value + 0.5f);
		}

		private static float SnapToGrid(float value, float fraction)
		{
			return Mathf.Floor(value - fraction + 0.5f) + fraction;
		}
	}
	internal sealed class BorderedRect
	{
		private static readonly string[] SideNames = new string[4] { "Top", "Bottom", "Left", "Right" };

		private readonly Image _fill;

		private readonly Image[] _strips = (Image[])(object)new Image[4];

		private BorderedRect(Transform layer, string name)
		{
			_fill = Find(layer, "Fill" + name);
			for (int i = 0; i < 4; i++)
			{
				_strips[i] = Find(layer, "Outline" + name + SideNames[i]);
			}
		}

		internal static BorderedRect Bind(Transform layer, string name)
		{
			return new BorderedRect(layer, name);
		}

		internal static void BuildBorder(Transform layer, string name)
		{
			string[] sideNames = SideNames;
			foreach (string text in sideNames)
			{
				CrosshairPrefabFactory.CreateImage("Outline" + name + text, layer);
			}
		}

		internal static void BuildFill(Transform layer, string name)
		{
			CrosshairPrefabFactory.CreateImage("Fill" + name, layer);
		}

		internal void Apply(in PixelFrame frame, Vector4 r, Color fill, int border, Color borderColor)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_0091: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: 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_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			Show(_fill, in frame, r, fill);
			if (border <= 0)
			{
				Image[] strips = _strips;
				foreach (Image val in strips)
				{
					((Behaviour)val).enabled = false;
				}
			}
			else
			{
				float num = border;
				Show(_strips[0], in frame, new Vector4(r.x - num, r.w, r.z + num, r.w + num), borderColor);
				Show(_strips[1], in frame, new Vector4(r.x - num, r.y - num, r.z + num, r.y), borderColor);
				Show(_strips[2], in frame, new Vector4(r.x - num, r.y, r.x, r.w), borderColor);
				Show(_strips[3], in frame, new Vector4(r.z, r.y, r.z + num, r.w), borderColor);
			}
		}

		internal void Hide()
		{
			((Behaviour)_fill).enabled = false;
			Image[] strips = _strips;
			foreach (Image val in strips)
			{
				((Behaviour)val).enabled = false;
			}
		}

		private static void Show(Image image, in PixelFrame frame, Vector4 r, Color color)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			frame.Place(((Graphic)image).rectTransform, r);
			((Graphic)image).color = color;
			((Behaviour)image).enabled = true;
		}

		private static Image Find(Transform layer, string name)
		{
			return ((Component)layer.Find(name)).GetComponent<Image>();
		}
	}
	internal sealed class BarsLayer
	{
		private static readonly string[] ArmNames = new string[4] { "Left", "Right", "Top", "Bottom" };

		private readonly BarsSettings _settings;

		private readonly BorderedRect[] _arms = new BorderedRect[4];

		internal bool Enabled => _settings.Enabled.Value;

		internal float Thickness => _settings.Thickness.Value;

		internal BarsLayer(Transform root, string layerName, BarsSettings settings)
		{
			_settings = settings;
			Transform layer = root.Find(layerName);
			for (int i = 0; i < 4; i++)
			{
				_arms[i] = BorderedRect.Bind(layer, ArmNames[i]);
			}
		}

		internal static void Build(Transform root, string layerName)
		{
			Transform layer = (Transform)(object)CrosshairPrefabFactory.CreateRect(layerName, root);
			string[] armNames = ArmNames;
			foreach (string name in armNames)
			{
				BorderedRect.BuildBorder(layer, name);
			}
			string[] armNames2 = ArmNames;
			foreach (string name2 in armNames2)
			{
				BorderedRect.BuildFill(layer, name2);
			}
		}

		internal void Apply(in PixelFrame frame, float dynamicOffset)
		{
			//IL_00ba: 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_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: 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_011f: 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_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled)
			{
				BorderedRect[] arms = _arms;
				foreach (BorderedRect borderedRect in arms)
				{
					borderedRect.Hide();
				}
				return;
			}
			float extraPixels = ((_settings.Mode.Value == CrosshairMode.Dynamic) ? dynamicOffset : 0f);
			float num = (float)frame.CenteredSize(_settings.Thickness.Value) * 0.5f;
			float num2 = frame.EdgeDistance(_settings.Gap.Value, extraPixels);
			float num3 = num2 + (float)frame.Width(_settings.Length.Value);
			Color value = _settings.Fill.Value;
			Color value2 = _settings.Outline.Color.Value;
			int border = (_settings.Outline.Visible ? frame.Width(_settings.Outline.Thickness.Value) : 0);
			_arms[0].Apply(in frame, new Vector4(0f - num3, 0f - num, 0f - num2, num), value, border, value2);
			_arms[1].Apply(in frame, new Vector4(num2, 0f - num, num3, num), value, border, value2);
			_arms[2].Apply(in frame, new Vector4(0f - num, num2, num, num3), value, border, value2);
			_arms[3].Apply(in frame, new Vector4(0f - num, 0f - num3, num, 0f - num2), value, border, value2);
		}
	}
	internal sealed class RingPart
	{
		private const float SegmentPixels = 1.5f;

		private const int MinSegments = 24;

		private const int MaxSegments = 512;

		private readonly RingMesh _mesh;

		private readonly Image _image;

		private RingPart(Transform layer, string name)
		{
			_mesh = ((Component)layer.Find(name)).GetComponent<RingMesh>();
			_image = _mesh.Image;
		}

		internal static RingPart Bind(Transform layer, string name)
		{
			return new RingPart(layer, name);
		}

		internal static void Build(Transform layer, string name)
		{
			CrosshairPrefabFactory.CreateImage(name, layer);
			((Component)layer.Find(name)).gameObject.AddComponent<RingMesh>();
		}

		internal void Apply(in PixelFrame frame, float inner, float outer, int border, Color fill, Color borderColor)
		{
			//IL_0042: 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_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			float num = outer + (float)border + 1f;
			int segments = Mathf.Clamp(Mathf.CeilToInt(MathF.PI * 2f * (outer + (float)border) / 1.5f), 24, 512);
			frame.Place(((Graphic)_image).rectTransform, new Vector4(0f - num, 0f - num, num, num));
			((Graphic)_image).color = Color.white;
			_mesh.SetShape(frame.ToLocal(inner), frame.ToLocal(outer), frame.ToLocal(border), frame.ToLocal(1f), fill, borderColor, segments);
			((Behaviour)_image).enabled = true;
		}

		internal void Hide()
		{
			((Behaviour)_image).enabled = false;
		}
	}
	internal sealed class CenterDotLayer
	{
		private const string DotName = "Dot";

		private readonly DotSettings _settings;

		private readonly RingPart _dot;

		internal bool Enabled => _settings.Enabled.Value;

		internal float Size => _settings.Size.Value;

		internal CenterDotLayer(Transform root, string layerName, DotSettings settings)
		{
			_settings = settings;
			_dot = RingPart.Bind(root.Find(layerName), "Dot");
		}

		internal static void Build(Transform root, string layerName)
		{
			RingPart.Build((Transform)(object)CrosshairPrefabFactory.CreateRect(layerName, root), "Dot");
		}

		internal void Apply(in PixelFrame frame)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled)
			{
				_dot.Hide();
				return;
			}
			float outer = (float)frame.CenteredSize(_settings.Size.Value) * 0.5f;
			int border = (_settings.Outline.Visible ? frame.Width(_settings.Outline.Thickness.Value) : 0);
			_dot.Apply(in frame, 0f, outer, border, _settings.Fill.Value, _settings.Outline.Color.Value);
		}
	}
	internal sealed class CircleLayer
	{
		private const string RingName = "Ring";

		private readonly CircleSettings _settings;

		private readonly RingPart _ring;

		internal CircleLayer(Transform root, string layerName, CircleSettings settings)
		{
			_settings = settings;
			_ring = RingPart.Bind(root.Find(layerName), "Ring");
		}

		internal static void Build(Transform root, string layerName)
		{
			RingPart.Build((Transform)(object)CrosshairPrefabFactory.CreateRect(layerName, root), "Ring");
		}

		internal void Apply(in PixelFrame frame, float dynamicOffset)
		{
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			if (!_settings.Enabled.Value)
			{
				_ring.Hide();
				return;
			}
			float extraPixels = ((_settings.Mode.Value == CrosshairMode.Dynamic) ? dynamicOffset : 0f);
			float num = frame.EdgeDistance(_settings.Radius.Value, extraPixels);
			float outer = num + (float)frame.Width(_settings.Thickness.Value);
			int border = (_settings.Outline.Visible ? frame.Width(_settings.Outline.Thickness.Value) : 0);
			_ring.Apply(in frame, num, outer, border, _settings.Fill.Value, _settings.Outline.Color.Value);
		}
	}
	internal enum CrosshairMode
	{
		Static,
		Dynamic
	}
	internal static class CrosshairPrefabFactory
	{
		internal const string ProceduralRootName = "ProceduralCrosshair";

		internal const string CircleLayerName = "Circle";

		internal const string OuterBarsLayerName = "OuterBars";

		internal const string InnerBarsLayerName = "InnerBars";

		internal const string CenterDotLayerName = "CenterDot";

		internal static void LoadAsync(string address, Action<GameObject?> onLoaded)
		{
			//IL_001b: 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)
			AsyncOperationHandle<GameObject> val = Addressables.LoadAssetAsync<GameObject>((object)address);
			val.Completed += delegate(AsyncOperationHandle<GameObject> op)
			{
				//IL_0003: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Invalid comparison between Unknown and I4
				if ((int)op.Status != 1 || (Object)(object)op.Result == (Object)null)
				{
					Plugin.Log.LogError((object)$"Could not load '{address}': {op.OperationException}");
					onLoaded(null);
				}
				else
				{
					onLoaded(op.Result);
				}
			};
		}

		internal static GameObject BuildStandard(GameObject standardCrosshair, Transform holder)
		{
			CrosshairController controller;
			GameObject val = Clone(standardCrosshair, holder, "RoR2CustomCrosshair", out controller);
			Plugin.Log.LogDebug((object)("StandardCrosshair components: " + string.Join(", ", from c in val.GetComponents<Component>()
				select ((object)c).GetType().Name)));
			controller.spriteSpreadPositions = Array.Empty<SpritePosition>();
			controller.skillStockSpriteDisplays = Array.Empty<SkillStockSpriteDisplay>();
			controller.remapSprites = Array.Empty<RawImage>();
			HideVanillaVisuals(val, new HashSet<Transform>());
			AddProceduralCrosshair(val);
			return val;
		}

		internal static GameObject? BuildStockPreserving(GameObject vanillaCrosshair, Transform holder)
		{
			//IL_0043: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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_00e6: 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)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			CrosshairController controller;
			GameObject val = Clone(vanillaCrosshair, holder, "RoR2CustomCrosshair_" + ((Object)vanillaCrosshair).name, out controller);
			Transform transform = val.transform;
			List<SkillStockSpriteDisplay> list = new List<SkillStockSpriteDisplay>();
			SkillStockSpriteDisplay[] array = controller.skillStockSpriteDisplays ?? Array.Empty<SkillStockSpriteDisplay>();
			foreach (SkillStockSpriteDisplay val2 in array)
			{
				if (Object.op_Implicit((Object)(object)val2.target) && !IsAtCenter(transform, val2.target.transform))
				{
					list.Add(val2);
				}
			}
			if (list.Count == 0)
			{
				Object.Destroy((Object)(object)val);
				return null;
			}
			controller.spriteSpreadPositions = Array.Empty<SpritePosition>();
			controller.remapSprites = Array.Empty<RawImage>();
			controller.skillStockSpriteDisplays = list.ToArray();
			HashSet<Transform> hashSet = new HashSet<Transform>();
			foreach (SkillStockSpriteDisplay item in list)
			{
				Transform val3 = item.target.transform;
				while ((Object)(object)val3 != (Object)null && (Object)(object)val3 != (Object)(object)transform)
				{
					hashSet.Add(val3);
					val3 = val3.parent;
				}
			}
			HideVanillaVisuals(val, hashSet);
			AddProceduralCrosshair(val);
			Plugin.Log.LogDebug((object)(((Object)vanillaCrosshair).name + ": keeping vanilla stock displays " + string.Join(", ", list.Select((SkillStockSpriteDisplay d) => ((Object)d.target).name)) + "; spread artwork removed."));
			return val;
		}

		private static GameObject Clone(GameObject source, Transform holder, string name, out CrosshairController controller)
		{
			GameObject val = Object.Instantiate<GameObject>(source, holder, false);
			((Object)val).name = name;
			HudElement val2 = default(HudElement);
			if (!val.TryGetComponent<CrosshairController>(ref controller) || !val.TryGetComponent<HudElement>(ref val2))
			{
				Object.Destroy((Object)(object)val);
				throw new InvalidOperationException(((Object)source).name + " no longer has CrosshairController + HudElement.");
			}
			return val;
		}

		private static bool IsAtCenter(Transform root, Transform target)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = root.InverseTransformPoint(target.position);
			return ((Vector3)(ref val)).sqrMagnitude < 1f;
		}

		private static void HideVanillaVisuals(GameObject template, HashSet<Transform> keep)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			foreach (Transform item in template.transform)
			{
				Transform val = item;
				if (!keep.Contains(val))
				{
					((Component)val).gameObject.SetActive(false);
				}
			}
			Graphic[] components = template.GetComponents<Graphic>();
			foreach (Graphic val2 in components)
			{
				((Behaviour)val2).enabled = false;
			}
		}

		private static void AddProceduralCrosshair(GameObject template)
		{
			RectTransform root = CreateRect("ProceduralCrosshair", template.transform);
			CircleLayer.Build((Transform)(object)root, "Circle");
			BarsLayer.Build((Transform)(object)root, "OuterBars");
			BarsLayer.Build((Transform)(object)root, "InnerBars");
			CenterDotLayer.Build((Transform)(object)root, "CenterDot");
			template.AddComponent<ProceduralCrosshairView>();
		}

		internal static RectTransform CreateRect(string name, Transform parent)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_003b: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(parent, false);
			val2.anchorMin = new Vector2(0.5f, 0.5f);
			val2.anchorMax = new Vector2(0.5f, 0.5f);
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.anchoredPosition = Vector2.zero;
			val2.sizeDelta = Vector2.zero;
			return val2;
		}

		internal static void CreateImage(string name, Transform parent)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = CreateRect(name, parent);
			((Component)val).gameObject.AddComponent<CanvasRenderer>();
			Image val2 = ((Component)val).gameObject.AddComponent<Image>();
			((Graphic)val2).raycastTarget = false;
			((MaskableGraphic)val2).maskable = false;
			((Graphic)val2).color = Color.clear;
			((Behaviour)val2).enabled = false;
		}
	}
	internal readonly struct SpreadSample
	{
		internal readonly float CurrentDegrees;

		internal readonly float RestingDegrees;

		internal readonly float BloomDegrees;

		internal SpreadSample(float currentDegrees, float restingDegrees, float bloomDegrees)
		{
			CurrentDegrees = currentDegrees;
			RestingDegrees = restingDegrees;
			BloomDegrees = bloomDegrees;
		}
	}
	internal interface ISpreadSource
	{
		bool LogSamples { get; }

		bool CanHandle(CharacterBody body);

		bool TrySample(CharacterBody body, out SpreadSample sample);
	}
	internal sealed class SpreadSourceResolver
	{
		private readonly ISpreadSource[] _sources;

		private CharacterBody? _body;

		private ISpreadSource? _source;

		internal SpreadSourceResolver(params ISpreadSource[] sources)
		{
			_sources = sources;
		}

		internal bool TrySample(CharacterBody body, out SpreadSample sample, out ISpreadSource? source)
		{
			if ((Object)(object)body != (Object)(object)_body)
			{
				_body = body;
				_source = null;
				ISpreadSource[] sources = _sources;
				foreach (ISpreadSource spreadSource in sources)
				{
					if (spreadSource.CanHandle(body))
					{
						_source = spreadSource;
						break;
					}
				}
			}
			source = _source;
			sample = default(SpreadSample);
			return _source != null && _source.TrySample(body, out sample);
		}
	}
	internal static class SpreadProjection
	{
		internal static float HalfAngleToPixels(float halfAngleDegrees, Camera camera)
		{
			float num = camera.fieldOfView * 0.5f * (MathF.PI / 180f);
			if (num <= 0f || halfAngleDegrees <= 0f)
			{
				return 0f;
			}
			float num2 = Mathf.Min(halfAngleDegrees, 89f) * (MathF.PI / 180f);
			return Mathf.Tan(num2) / Mathf.Tan(num) * ((float)camera.pixelHeight * 0.5f);
		}
	}
	internal sealed class GenericBulletSpread
	{
		internal readonly int BulletCount;

		internal readonly float MaxSpread;

		internal readonly float YawScale;

		internal readonly float PitchScale;

		private GenericBulletSpread(GenericBulletBaseState state)
		{
			BulletCount = state.bulletCount;
			MaxSpread = state.maxSpread;
			YawScale = state.spreadYawScale;
			PitchScale = state.spreadPitchScale;
		}

		internal static T? ReadState<T>(Type stateType) where T : GenericBulletBaseState
		{
			try
			{
				EntityState obj = EntityStateCatalog.InstantiateState(stateType);
				return (T)(object)((obj is T) ? obj : null);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not read configuration of " + stateType.FullName + ": " + ex.Message));
				return default(T);
			}
		}

		internal static GenericBulletSpread From(GenericBulletBaseState state)
		{
			return new GenericBulletSpread(state);
		}

		internal float HorizontalDegrees(float bloomDegrees)
		{
			return (MaxSpread + bloomDegrees) * YawScale;
		}

		internal float VerticalDegrees(float bloomDegrees)
		{
			return (MaxSpread + bloomDegrees) * PitchScale;
		}

		internal string Describe()
		{
			return $"{BulletCount} bullet(s), maxSpread {MaxSpread:0.###}, yaw x{YawScale:0.###}, pitch x{PitchScale:0.###}";
		}
	}
	internal sealed class HudCrosshairDriver : MonoBehaviour
	{
		private static readonly ISpreadSource[] SpreadSources = new ISpreadSource[3]
		{
			new CommandoSpreadSource(),
			new BanditSpreadSource(),
			new ToolbotSpreadSource()
		};

		private const float OffsetStepPixels = 0.25f;

		private const float SpreadLogInterval = 1f;

		private float _nextSpreadLogTime;

		private readonly SpreadSourceResolver _spreadResolver = new SpreadSourceResolver(SpreadSources);

		private CrosshairManager? _crosshairManager;

		private bool _replacing;

		internal static void Attach(HUD hud)
		{
			if (!Object.op_Implicit((Object)(object)((Component)hud).GetComponent<HudCrosshairDriver>()))
			{
				((Component)hud).gameObject.AddComponent<HudCrosshairDriver>();
			}
		}

		private void Awake()
		{
			_crosshairManager = ((Component)this).GetComponent<CrosshairManager>();
			if (!Object.op_Implicit((Object)(object)_crosshairManager))
			{
				Plugin.Log.LogWarning((object)("HUD '" + ((Object)this).name + "' has no CrosshairManager; custom crosshair unavailable."));
			}
		}

		private void OnEnable()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			if (Object.op_Implicit((Object)(object)_crosshairManager))
			{
				_crosshairManager.pickCrosshairGlobal += new PickCrosshairDelegate(OnPickCrosshair);
			}
		}

		private void OnDisable()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			if (Object.op_Implicit((Object)(object)_crosshairManager))
			{
				_crosshairManager.pickCrosshairGlobal -= new PickCrosshairDelegate(OnPickCrosshair);
			}
			SetReplacing(replacing: false, null);
		}

		private void OnPickCrosshair(CrosshairManager manager, ref GameObject crosshairPrefab)
		{
			CharacterBody ownLiveBody = GetOwnLiveBody(manager);
			GameObject val = (((Object)(object)ownLiveBody != (Object)null && CrosshairConfig.Enabled.Value) ? CrosshairDisplayPolicy.ChooseTemplate(ownLiveBody, crosshairPrefab) : null);
			if ((Object)(object)val != (Object)null)
			{
				crosshairPrefab = val;
			}
			SetReplacing((Object)(object)val != (Object)null, ownLiveBody);
		}

		internal DynamicOffsets GetDynamicOffsets()
		{
			if (!_replacing || !Object.op_Implicit((Object)(object)_crosshairManager))
			{
				return default(DynamicOffsets);
			}
			CameraRigController cameraRigController = _crosshairManager.cameraRigController;
			CharacterBody ownLiveBody = GetOwnLiveBody(_crosshairManager);
			Camera val = (Object.op_Implicit((Object)(object)cameraRigController) ? cameraRigController.sceneCam : null);
			if ((Object)(object)ownLiveBody == (Object)null || (Object)(object)val == (Object)null || !_spreadResolver.TrySample(ownLiveBody, out SpreadSample sample, out ISpreadSource source))
			{
				return default(DynamicOffsets);
			}
			float num = SpreadProjection.HalfAngleToPixels(sample.CurrentDegrees, val);
			float num2 = SpreadProjection.HalfAngleToPixels(sample.RestingDegrees, val);
			float num3 = Mathf.Round(Mathf.Max(0f, num - num2) / 0.25f) * 0.25f;
			if (source.LogSamples)
			{
				LogSpreadSample(source, in sample, val, num, num3);
			}
			return new DynamicOffsets(num3, num3, num3);
		}

		private void LogSpreadSample(ISpreadSource source, in SpreadSample sample, Camera camera, float currentPixels, float offsetPixels)
		{
			if (!(offsetPixels <= 0f) && !(Time.unscaledTime < _nextSpreadLogTime))
			{
				_nextSpreadLogTime = Time.unscaledTime + 1f;
				Plugin.Log.LogDebug((object)($"[Spread] {source.GetType().Name}: bloom {sample.BloomDegrees:0.###} deg -> spread {sample.CurrentDegrees:0.###} deg " + $"(rest {sample.RestingDegrees:0.###}), vertical FOV {camera.fieldOfView:0.#}, camera {camera.pixelWidth}x{camera.pixelHeight} -> " + $"cone radius {currentPixels:0.#} px, dynamic offset +{offsetPixels:0.##} px."));
			}
		}

		private static CharacterBody? GetOwnLiveBody(CrosshairManager manager)
		{
			CameraRigController cameraRigController = manager.cameraRigController;
			if (!Object.op_Implicit((Object)(object)cameraRigController) || cameraRigController.hasOverride)
			{
				return null;
			}
			LocalUser localUserViewer = cameraRigController.localUserViewer;
			CharacterBody targetBody = cameraRigController.targetBody;
			if (localUserViewer == null || !Object.op_Implicit((Object)(object)targetBody) || (Object)(object)localUserViewer.cachedBodyObject != (Object)(object)((Component)targetBody).gameObject)
			{
				return null;
			}
			if (!Object.op_Implicit((Object)(object)targetBody.healthComponent) || !targetBody.healthComponent.alive)
			{
				return null;
			}
			return targetBody;
		}

		private void SetReplacing(bool replacing, CharacterBody? body)
		{
			if (replacing != _replacing)
			{
				_replacing = replacing;
				Plugin.Log.LogDebug((object)(replacing ? ("Custom crosshair active for " + body.GetDisplayName() + " (" + ((Object)body).name + ").") : "Custom crosshair released; vanilla crosshair logic in control."));
			}
		}
	}
	internal static class ModIcon
	{
		private const string ResourceName = "RoR2CustomCrosshair.icon.png";

		private static Sprite? _sprite;

		internal static Sprite? Load()
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_00b5: 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 ((Object)(object)_sprite != (Object)null)
			{
				return _sprite;
			}
			try
			{
				byte[] array = ReadResource();
				if (array == null)
				{
					Plugin.Log.LogWarning((object)"Embedded resource 'RoR2CustomCrosshair.icon.png' not found; RiskOfOptions will show its default icon.");
					return null;
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true)
				{
					name = "RoR2CustomCrosshairIcon",
					filterMode = (FilterMode)2,
					wrapMode = (TextureWrapMode)1
				};
				if (!ImageConversion.LoadImage(val, array, true))
				{
					Object.Destroy((Object)(object)val);
					Plugin.Log.LogWarning((object)"Could not decode the embedded icon; RiskOfOptions will show its default icon.");
					return null;
				}
				Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
				((Object)val2).name = ((Object)val).name;
				((Object)val).hideFlags = (HideFlags)32;
				((Object)val2).hideFlags = (HideFlags)32;
				_sprite = val2;
				return _sprite;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"Could not load the mod icon; RiskOfOptions will show its default icon. {arg}");
				return null;
			}
		}

		private static byte[]? ReadResource()
		{
			using Stream stream = typeof(ModIcon).Assembly.GetManifestResourceStream("RoR2CustomCrosshair.icon.png");
			if (stream == null)
			{
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}
	}
	[BepInPlugin("com.example.ror2customcrosshair", "RoR2 Custom Crosshair", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static hook_Awake <0>__HUD_Awake;
		}

		public const string PluginGuid = "com.example.ror2customcrosshair";

		public const string PluginName = "RoR2 Custom Crosshair";

		public const string PluginVersion = "0.1.0";

		private GameObject? _prefabHolder;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			CrosshairConfig.Bind(((BaseUnityPlugin)this).Config);
			CrosshairConfig.RegisterRiskOfOptions();
			_prefabHolder = new GameObject("RoR2CustomCrosshair_Templates");
			_prefabHolder.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)_prefabHolder);
			CrosshairDisplayPolicy.Initialize(_prefabHolder.transform);
			object obj = <>O.<0>__HUD_Awake;
			if (obj == null)
			{
				hook_Awake val = HUD_Awake;
				<>O.<0>__HUD_Awake = val;
				obj = (object)val;
			}
			HUD.Awake += (hook_Awake)obj;
			Log.LogInfo((object)"RoR2 Custom Crosshair 0.1.0 loaded.");
		}

		private static void HUD_Awake(orig_Awake orig, HUD self)
		{
			orig.Invoke(self);
			HudCrosshairDriver.Attach(self);
		}

		private void OnDestroy()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			object obj = <>O.<0>__HUD_Awake;
			if (obj == null)
			{
				hook_Awake val = HUD_Awake;
				<>O.<0>__HUD_Awake = val;
				obj = (object)val;
			}
			HUD.Awake -= (hook_Awake)obj;
			HudCrosshairDriver[] array = Object.FindObjectsOfType<HudCrosshairDriver>();
			foreach (HudCrosshairDriver hudCrosshairDriver in array)
			{
				Object.Destroy((Object)(object)hudCrosshairDriver);
			}
			CrosshairDisplayPolicy.Shutdown();
			if ((Object)(object)_prefabHolder != (Object)null)
			{
				Object.Destroy((Object)(object)_prefabHolder);
			}
		}
	}
	internal readonly struct DynamicOffsets : IEquatable<DynamicOffsets>
	{
		internal readonly float InnerBars;

		internal readonly float OuterBars;

		internal readonly float Circle;

		internal DynamicOffsets(float innerBars, float outerBars, float circle)
		{
			InnerBars = innerBars;
			OuterBars = outerBars;
			Circle = circle;
		}

		public bool Equals(DynamicOffsets other)
		{
			return InnerBars == other.InnerBars && OuterBars == other.OuterBars && Circle == other.Circle;
		}
	}
	internal sealed class ProceduralCrosshairView : MonoBehaviour
	{
		private const float ReferenceWidth = 1920f;

		private const float ReferenceHeight = 1080f;

		private static int _loggedScreenWidth = -1;

		private static int _loggedScreenHeight = -1;

		private static float _loggedPixelsPerUnit = -1f;

		private RectTransform _rect = null;

		private RectTransform _root = null;

		private CircleLayer _circle = null;

		private BarsLayer _outerBars = null;

		private BarsLayer _innerBars = null;

		private CenterDotLayer _centerDot = null;

		private Canvas? _canvas;

		private HudCrosshairDriver? _driver;

		private int _lastRevision = -1;

		private int _lastScreenWidth = -1;

		private int _lastScreenHeight = -1;

		private float _lastPixelsPerUnit = -1f;

		private Vector2 _lastRootScreen;

		private Vector2 _lastAnchorScreen;

		private DynamicOffsets _lastOffsets;

		private void Awake()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			_rect = (RectTransform)((Component)this).transform;
			_root = (RectTransform)((Component)this).transform.Find("ProceduralCrosshair");
			_circle = new CircleLayer((Transform)(object)_root, "Circle", CrosshairConfig.Circle);
			_outerBars = new BarsLayer((Transform)(object)_root, "OuterBars", CrosshairConfig.OuterBars);
			_innerBars = new BarsLayer((Transform)(object)_root, "InnerBars", CrosshairConfig.InnerBars);
			_centerDot = new CenterDotLayer((Transform)(object)_root, "CenterDot", CrosshairConfig.CenterDot);
			Refresh();
		}

		private void OnTransformParentChanged()
		{
			_canvas = null;
			_driver = null;
		}

		private void LateUpdate()
		{
			Refresh();
		}

		private void Refresh()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: 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_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_canvas == (Object)null)
			{
				_canvas = ((Component)this).GetComponentInParent<Canvas>();
				if ((Object)(object)_canvas == (Object)null)
				{
					return;
				}
			}
			Canvas rootCanvas = _canvas.rootCanvas;
			float pixelsPerUnit = GetPixelsPerUnit(rootCanvas);
			if (pixelsPerUnit <= 0f)
			{
				return;
			}
			Camera val = (((int)rootCanvas.renderMode == 0) ? null : rootCanvas.worldCamera);
			Vector2 val2 = RectTransformUtility.WorldToScreenPoint(val, ((Transform)_root).position);
			Vector2 val3 = RectTransformUtility.WorldToScreenPoint(val, GetIntendedCenterWorld());
			if ((Object)(object)_driver == (Object)null)
			{
				_driver = ((Component)this).GetComponentInParent<HudCrosshairDriver>();
			}
			DynamicOffsets dynamicOffsets = (((Object)(object)_driver != (Object)null) ? _driver.GetDynamicOffsets() : default(DynamicOffsets));
			if (_lastRevision == CrosshairConfig.Revision && _lastScreenWidth == Screen.width && _lastScreenHeight == Screen.height && Mathf.Approximately(_lastPixelsPerUnit, pixelsPerUnit))
			{
				Vector2 val4 = _lastRootScreen - val2;
				if (((Vector2)(ref val4)).sqrMagnitude < 1E-06f)
				{
					val4 = _lastAnchorScreen - val3;
					if (((Vector2)(ref val4)).sqrMagnitude < 1E-06f && _lastOffsets.Equals(dynamicOffsets))
					{
						return;
					}
				}
			}
			PixelFrame frame = CreateFrame(pixelsPerUnit, val3, val2);
			_circle.Apply(in frame, dynamicOffsets.Circle);
			_outerBars.Apply(in frame, dynamicOffsets.OuterBars);
			_innerBars.Apply(in frame, dynamicOffsets.InnerBars);
			_centerDot.Apply(in frame);
			LogLayoutOnce(rootCanvas, in frame, val2, val3);
			_lastRevision = CrosshairConfig.Revision;
			_lastScreenWidth = Screen.width;
			_lastScreenHeight = Screen.height;
			_lastPixelsPerUnit = pixelsPerUnit;
			_lastRootScreen = val2;
			_lastAnchorScreen = val3;
			_lastOffsets = dynamicOffsets;
		}

		private PixelFrame CreateFrame(float pixelsPerUnit, Vector2 anchorScreen, Vector2 rootScreen)
		{
			//IL_0088: 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)
			float resolutionScale = Mathf.Min((float)Screen.width / 1920f, (float)Screen.height / 1080f);
			float centerFraction = (_innerBars.Enabled ? PixelFrame.FractionFor(_innerBars.Thickness, resolutionScale) : (_outerBars.Enabled ? PixelFrame.FractionFor(_outerBars.Thickness, resolutionScale) : (_centerDot.Enabled ? PixelFrame.FractionFor(_centerDot.Size, resolutionScale) : 0f)));
			return new PixelFrame(resolutionScale, pixelsPerUnit, centerFraction, anchorScreen, rootScreen);
		}

		private float GetPixelsPerUnit(Canvas rootCanvas)
		{
			//IL_0007: 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)
			float x = ((Component)rootCanvas).transform.lossyScale.x;
			if (x <= 0f)
			{
				return 0f;
			}
			return rootCanvas.scaleFactor * (((Transform)_root).lossyScale.x / x);
		}

		private Vector3 GetIntendedCenterWorld()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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)
			Transform parent = ((Transform)_rect).parent;
			if ((Object)(object)parent == (Object)null)
			{
				return ((Transform)_root).position;
			}
			return ((Transform)_root).position - parent.TransformVector(Vector2.op_Implicit(_rect.anchoredPosition));
		}

		private void LogLayoutOnce(Canvas rootCanvas, in PixelFrame frame, Vector2 rootScreen, Vector2 anchorScreen)
		{
			//IL_007c: 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_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: 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 (_loggedScreenWidth != Screen.width || _loggedScreenHeight != Screen.height || !Mathf.Approximately(_loggedPixelsPerUnit, frame.PixelsPerUnit))
			{
				_loggedScreenWidth = Screen.width;
				_loggedScreenHeight = Screen.height;
				_loggedPixelsPerUnit = frame.PixelsPerUnit;
				Plugin.Log.LogDebug((object)($"Crosshair layout: screen {Screen.width}x{Screen.height}, canvas {rootCanvas.renderMode} " + $"scaleFactor {rootCanvas.scaleFactor:0.###} pixelPerfect {rootCanvas.pixelPerfect}, " + $"px/unit {frame.PixelsPerUnit:0.###}, crosshair anchoredPosition {_rect.anchoredPosition}, " + $"root at {rootScreen.x:0.###},{rootScreen.y:0.###} px, anchor {anchorScreen.x:0.###},{anchorScreen.y:0.###} px, " + $"snapped center {frame.Center.x},{frame.Center.y}."));
			}
		}
	}
	[RequireComponent(typeof(Image))]
	internal sealed class RingMesh : MonoBehaviour, IMeshModifier
	{
		private readonly struct Stop
		{
			internal readonly float Radius;

			internal readonly Color32 Color;

			internal Stop(float radius, Color32 color)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				Radius = radius;
				Color = color;
			}
		}

		private readonly struct Shape : IEquatable<Shape>
		{
			internal readonly float Inner;

			internal readonly float Outer;

			internal readonly float Border;

			internal readonly float Feather;

			internal readonly Color32 Fill;

			internal readonly Color32 BorderColor;

			internal readonly int Segments;

			internal Shape(float inner, float outer, float border, float feather, Color32 fill, Color32 borderColor, int segments)
			{
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: 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_0029: Unknown result type (might be due to invalid IL or missing references)
				Inner = inner;
				Outer = outer;
				Border = border;
				Feather = feather;
				Fill = fill;
				BorderColor = borderColor;
				Segments = segments;
			}

			public bool Equals(Shape o)
			{
				//IL_0047: 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_005a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0060: Unknown result type (might be due to invalid IL or missing references)
				return Inner == o.Inner && Outer == o.Outer && Border == o.Border && Feather == o.Feather && Segments == o.Segments && Same(Fill, o.Fill) && Same(BorderColor, o.BorderColor);
			}

			private static bool Same(Color32 a, Color32 b)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_002a: 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)
				return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
			}
		}

		private readonly List<Stop> _stops = new List<Stop>();

		private Shape _shape;

		private bool _hasShape;

		private Image? _image;

		internal Image Image => ((Object)(object)_image != (Object)null) ? _image : (_image = ((Component)this).GetComponent<Image>());

		internal void SetShape(float inner, float outer, float border, float feather, Color fill, Color borderColor, int segments)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			Shape shape = new Shape(inner, outer, border, feather, Color32.op_Implicit(fill), Color32.op_Implicit(borderColor), segments);
			if (!_hasShape || !shape.Equals(_shape))
			{
				_shape = shape;
				_hasShape = true;
				((Graphic)Image).SetVerticesDirty();
			}
		}

		public void ModifyMesh(Mesh mesh)
		{
		}

		public void ModifyMesh(VertexHelper vh)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			vh.Clear();
			if (!_hasShape)
			{
				return;
			}
			BuildStops(_shape);
			int segments = _shape.Segments;
			float num = MathF.PI * 2f / (float)segments;
			Vector3 val = default(Vector3);
			for (int i = 0; i < _stops.Count; i++)
			{
				Stop stop = _stops[i];
				for (int j = 0; j < segments; j++)
				{
					float num2 = (float)j * num;
					((Vector3)(ref val))..ctor(Mathf.Cos(num2) * stop.Radius, Mathf.Sin(num2) * stop.Radius);
					vh.AddVert(val, stop.Color, Vector4.zero);
				}
			}
			for (int k = 0; k + 1 < _stops.Count; k++)
			{
				int num3 = k * segments;
				int num4 = (k + 1) * segments;
				for (int l = 0; l < segments; l++)
				{
					int num5 = (l + 1) % segments;
					vh.AddTriangle(num3 + l, num4 + l, num4 + num5);
					vh.AddTriangle(num3 + l, num4 + num5, num3 + num5);
				}
			}
		}

		private void BuildStops(Shape shape)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: 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_00df: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			_stops.Clear();
			float num = shape.Feather * 0.5f;
			bool flag = shape.Border > 0f;
			List<float> list = new List<float>(4);
			List<Color32> list2 = new List<Color32>(3);
			if (flag && shape.Inner > 0f)
			{
				list.Add(Mathf.Max(0f, shape.Inner - shape.Border));
				list2.Add(shape.BorderColor);
			}
			list.Add(shape.Inner);
			list2.Add(shape.Fill);
			list.Add(shape.Outer);
			if (flag)
			{
				list2.Add(shape.BorderColor);
				list.Add(shape.Outer + shape.Border);
			}
			for (int i = 0; i < list.Count; i++)
			{
				Color32 color = ((i == 0) ? Transparent(list2[0]) : list2[i - 1]);
				Color32 color2 = ((i == list.Count - 1) ? Transparent(list2[i - 1]) : list2[i]);
				if (i == 0 && list[0] <= 0f)
				{
					AddStop(0f, color2);
					continue;
				}
				AddStop(list[i] - num, color);
				AddStop(list[i] + num, color2);
			}
		}

		private void AddStop(float radius, Color32 color)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			float num = ((_stops.Count > 0) ? _stops[_stops.Count - 1].Radius : 0f);
			_stops.Add(new Stop(Mathf.Max(radius, num), color));
		}

		private static Color32 Transparent(Color32 color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			return new Color32(color.r, color.g, color.b, (byte)0);
		}
	}
	internal sealed class ToolbotSpreadSource : ISpreadSource
	{
		private const string ToolbotBodyName = "ToolbotBody";

		private BodyIndex _toolbotBodyIndex = (BodyIndex)(-1);

		private readonly Dictionary<SkillDef, bool> _isNailgun = new Dictionary<SkillDef, bool>();

		private CharacterBody? _loggedBody;

		private SkillDef? _loggedPrimary;

		private SkillDef? _loggedOffhand;

		public bool LogSamples => true;

		public bool CanHandle(CharacterBody body)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			if ((int)_toolbotBodyIndex == -1)
			{
				_toolbotBodyIndex = BodyCatalog.FindBodyIndex("ToolbotBody");
				if ((int)_toolbotBodyIndex == -1)
				{
					return false;
				}
			}
			return body.bodyIndex == _toolbotBodyIndex;
		}

		public bool TrySample(CharacterBody body, out SpreadSample sample)
		{
			sample = default(SpreadSample);
			SkillLocator skillLocator = body.skillLocator;
			if (!Object.op_Implicit((Object)(object)skillLocator))
			{
				return false;
			}
			ToolbotWeaponSkillDef val = (ToolbotWeaponSkillDef)(Object.op_Implicit((Object)(object)skillLocator.primary) ? /*isinst with value type is only supported in some contexts*/: null);
			ToolbotWeaponSkillDef val2 = (ToolbotWeaponSkillDef)(Object.op_Implicit((Object)(object)skillLocator.secondary) ? /*isinst with value type is only supported in some contexts*/: null);
			LogWeaponsOnce(body, (SkillDef?)(object)val, (SkillDef?)(object)val2);
			if (!IsNailgun((SkillDef?)(object)val) && !IsNailgun((SkillDef?)(object)val2))
			{
				return false;
			}
			if (body.spreadBloomCurve == null)
			{
				return false;
			}
			float num = NailgunEnvelopeScale();
			float spreadBloomAngle = body.spreadBloomAngle;
			float num2 = body.spreadBloomCurve.Evaluate(0f);
			sample = new SpreadSample(spreadBloomAngle * num, num2 * num, spreadBloomAngle);
			return true;
		}

		private static float NailgunEnvelopeScale()
		{
			float spreadYawScale = BaseNailgunState.spreadYawScale;
			float spreadPitchScale = BaseNailgunState.spreadPitchScale;
			return Math.Max(1f, Math.Max(spreadYawScale * spreadYawScale, spreadPitchScale * spreadPitchScale));
		}

		private bool IsNailgun(SkillDef? skill)
		{
			if ((Object)(object)skill == (Object)null)
			{
				return false;
			}
			if (!_isNailgun.TryGetValue(skill, out var value))
			{
				Type stateType = ((SerializableEntityStateType)(ref skill.activationState)).stateType;
				value = stateType != null && typeof(FireNailgun).IsAssignableFrom(stateType);
				_isNailgun[skill] = value;
			}
			return value;
		}

		private void LogWeaponsOnce(CharacterBody body, SkillDef? primary, SkillDef? offhand)
		{
			if (!((Object)(object)_loggedBody == (Object)(object)body) || !((Object)(object)_loggedPrimary == (Object)(object)primary) || !((Object)(object)_loggedOffhand == (Object)(object)offhand))
			{
				_loggedBody = body;
				_loggedPrimary = primary;
				_loggedOffhand = offhand;
				string arg = ((body.spreadBloomCurve != null) ? $"{body.spreadBloomCurve.Evaluate(0f):0.###}..{body.spreadBloomCurve.Evaluate(1f):0.###} deg" : "none");
				Plugin.Log.LogDebug((object)("[Spread] MUL-T weapons: active " + Describe(primary) + (((Object)(object)offhand != (Object)null) ? (", Power Mode offhand " + Describe(offhand)) : "") + $"; body bloom curve {arg}, decay {body.spreadBloomDecayTime:0.###} s; " + $"nailgun envelope scale {NailgunEnvelopeScale():0.###}."));
			}
			string Describe(SkillDef? skill)
			{
				return ((Object)(object)skill == (Object)null) ? "none" : ("'" + skill.skillName + "' (" + ((SerializableEntityStateType)(ref skill.activationState)).stateType?.Name + ", " + (IsNailgun(skill) ? "dynamic spread" : "no spread growth") + ")");
			}
		}
	}
}