Decompiled source of UltimateLethal CrewMorale v0.1.6

UltimateLethal.CrewMorale.dll

Decompiled 5 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("UltimateLethal.CrewMorale")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("UltimateLethal.CrewMorale")]
[assembly: AssemblyTitle("UltimateLethal.CrewMorale")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace UltimateLethal.CrewMorale
{
	[BepInPlugin("ultimatelethal.crewmorale", "Ultimate Lethal Crew Morale", "0.1.6")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class CrewMoralePlugin : BaseUnityPlugin
	{
		public const string Guid = "ultimatelethal.crewmorale";

		public const string Name = "Ultimate Lethal Crew Morale";

		public const string Version = "0.1.6";

		internal static CrewMoralePlugin Instance;

		internal static ConfigEntry<float> StartingMorale;

		internal static ConfigEntry<float> MinimumMorale;

		internal static ConfigEntry<float> MaximumMorale;

		internal static ConfigEntry<float> DecorationPriceScaleCredits;

		internal static ConfigEntry<float> DecorationPriceExponent;

		internal static ConfigEntry<float> DecorationMaxMoraleEach;

		internal static ConfigEntry<float> DecorationBonusCap;

		internal static ConfigEntry<float> DeathPenalty;

		internal static ConfigEntry<float> ReunionReward;

		internal static ConfigEntry<float> QuotaFulfilledReward;

		internal static ConfigEntry<float> FullCrewShipRecoveryPerSecond;

		internal static ConfigEntry<float> OrbitRecoveryPerSecond;

		internal static ConfigEntry<float> EmptyShipDecayPerSecond;

		internal static ConfigEntry<float> HighMoraleSanityGainMultiplier;

		internal static ConfigEntry<float> LowMoraleSanityGainMultiplier;

		internal static ConfigEntry<float> HighMoraleRecoveryMultiplier;

		internal static ConfigEntry<float> LowMoraleRecoveryMultiplier;

		internal static ConfigEntry<bool> ShowHud;

		internal static ConfigEntry<float> HudX;

		internal static ConfigEntry<float> HudY;

		internal static ConfigEntry<float> HudWidth;

		internal static ConfigEntry<float> HudHeight;

		internal static ConfigEntry<float> SanityHudRaisePixels;

		private Harmony _harmony;

		private float _nextStateTick;

		private bool _wasFullLivingCrewInShip;

		private bool _wasLanded;

		private bool _someoneLeftShipThisExpedition;

		private bool _expeditionHadDeath;

		private bool _reunionAwardedThisExpedition;

		private int _expeditionStartingCrewCount;

		private readonly HashSet<PlayerControllerB> _expeditionRoster = new HashSet<PlayerControllerB>();

		private readonly HashSet<PlayerControllerB> _countedDeadPlayers = new HashSet<PlayerControllerB>();

		private bool _quotaStateInitialized;

		private bool _quotaWasFulfilled;

		private float _previousDecorationBonus = -1f;

		private float _eventUntil;

		private string _eventTitle = "";

		private string _eventDetail = "";

		private GUIStyle _labelStyle;

		private GUIStyle _smallStyle;

		private GUIStyle _eventTitleStyle;

		private GUIStyle _eventDetailStyle;

		private Texture2D _pixel;

		internal static float Morale { get; private set; } = 55f;

		internal static float DecorationBaseline { get; private set; } = 0f;

		internal static int DecorationCount { get; private set; } = 0;

		private void Awake()
		{
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0368: Expected O, but got Unknown
			Instance = this;
			StartingMorale = ((BaseUnityPlugin)this).Config.Bind<float>("Morale", "StartingMorale", 55f, "Morale at the beginning of a session.");
			MinimumMorale = ((BaseUnityPlugin)this).Config.Bind<float>("Morale", "MinimumMorale", 0f, "Minimum morale.");
			MaximumMorale = ((BaseUnityPlugin)this).Config.Bind<float>("Morale", "MaximumMorale", 100f, "Maximum morale.");
			DecorationPriceScaleCredits = ((BaseUnityPlugin)this).Config.Bind<float>("Decorations", "PriceScaleCredits", 200f, "Price divisor used by the decoration morale curve.");
			DecorationPriceExponent = ((BaseUnityPlugin)this).Config.Bind<float>("Decorations", "PriceExponent", 1.15f, "Exponent used by the decoration morale curve. Values above 1 make expensive decorations more efficient.");
			DecorationMaxMoraleEach = ((BaseUnityPlugin)this).Config.Bind<float>("Decorations", "MaxMoralePerDecoration", 10f, "Maximum morale a single decoration can contribute regardless of price.");
			DecorationBonusCap = ((BaseUnityPlugin)this).Config.Bind<float>("Decorations", "DecorationBonusCap", 20f, "Maximum total baseline morale supplied by all decorations.");
			DeathPenalty = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "DeathPenalty", 18f, "Immediate morale loss per crew death.");
			ReunionReward = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "PerfectReunionReward", 10f, "Morale gained when everyone who started the expedition returns to the ship alive after someone has left it.");
			QuotaFulfilledReward = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "QuotaFulfilledReward", 15f, "Morale gained once when the current quota becomes fulfilled.");
			FullCrewShipRecoveryPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "FullCrewShipRecoveryPerSecond", 0.00833333f, "Passive morale recovery while the full living crew is together inside the ship (+0.5 morale/minute).");
			OrbitRecoveryPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "OrbitRecoveryPerSecond", 0.0166667f, "Additional passive morale recovery while the ship is safely in orbit (+1 morale/minute).");
			EmptyShipDecayPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "EmptyShipDecayPerSecond", 0.03f, "Morale decay while nobody living is in the ship.");
			MigrateLegacyBalanceValues();
			HighMoraleSanityGainMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Sanity", "HighMoraleSanityGainMultiplier", 0.6f, "At 100 morale, positive insanity gain is multiplied by this.");
			LowMoraleSanityGainMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Sanity", "LowMoraleSanityGainMultiplier", 1.3f, "At 0 morale, positive insanity gain is multiplied by this.");
			HighMoraleRecoveryMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Sanity", "HighMoraleRecoveryMultiplier", 1.45f, "At 100 morale, negative insanity change (recovery) is multiplied by this.");
			LowMoraleRecoveryMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Sanity", "LowMoraleRecoveryMultiplier", 0.65f, "At 0 morale, negative insanity change (recovery) is multiplied by this.");
			ShowHud = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowMoraleBar", true, "Show Crew Morale while the local player is in the ship.");
			HudX = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "X", 0.5f, "Horizontal screen anchor (0 to 1).");
			HudY = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Y", 0.105f, "Vertical screen anchor (0 to 1).");
			HudWidth = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Width", 260f, "Morale bar width in pixels.");
			HudHeight = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Height", 14f, "Morale bar height in pixels.");
			SanityHudRaisePixels = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "SanityHudRaisePixels", 360f, "Moves InsanityMod's bottom-right sanity ring upward by this many pixels.");
			Morale = Clamp(StartingMorale.Value);
			_harmony = new Harmony("ultimatelethal.crewmorale");
			_harmony.PatchAll();
			TryPatchInsanityMod();
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Starting morale: {2:0}", "Ultimate Lethal Crew Morale", "0.1.6", Morale));
		}

		private void MigrateLegacyBalanceValues()
		{
			if (Mathf.Approximately(DeathPenalty.Value, 12f))
			{
				DeathPenalty.Value = 18f;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated legacy DeathPenalty 12 -> 18.");
			}
			if (Mathf.Approximately(FullCrewShipRecoveryPerSecond.Value, 0.2f) || Mathf.Approximately(FullCrewShipRecoveryPerSecond.Value, 0.07f) || Mathf.Approximately(FullCrewShipRecoveryPerSecond.Value, 0.03f))
			{
				FullCrewShipRecoveryPerSecond.Value = 0.00833333f;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated legacy full-crew passive recovery -> +0.5 morale/minute.");
			}
			if (Mathf.Approximately(OrbitRecoveryPerSecond.Value, 0.35f) || Mathf.Approximately(OrbitRecoveryPerSecond.Value, 0.12f) || Mathf.Approximately(OrbitRecoveryPerSecond.Value, 0.05f))
			{
				OrbitRecoveryPerSecond.Value = 0.0166667f;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated legacy orbit passive recovery -> +1 morale/minute.");
			}
		}

		private void Start()
		{
			_expeditionRoster.Clear();
			_countedDeadPlayers.Clear();
		}

		private void Update()
		{
			if (!(Time.unscaledTime < _nextStateTick))
			{
				float num = 0.25f;
				_nextStateTick = Time.unscaledTime + num;
				AdjustInsanityHudPosition();
				if (!((Object)(object)StartOfRound.Instance == (Object)null))
				{
					RefreshDecorationBaseline();
					ApplyWorldState(num);
				}
			}
		}

		private static void AdjustInsanityHudPosition()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GameObject.Find("InsanityHud_Root");
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			RectTransform component = val.GetComponent<RectTransform>();
			if (!((Object)(object)component == (Object)null))
			{
				float num = 130f + Mathf.Max(0f, SanityHudRaisePixels?.Value ?? 360f);
				Vector2 anchoredPosition = component.anchoredPosition;
				if (Mathf.Abs(anchoredPosition.y - num) > 0.1f)
				{
					component.anchoredPosition = new Vector2(anchoredPosition.x, num);
				}
			}
		}

		private void ApplyWorldState(float dt)
		{
			StartOfRound instance = StartOfRound.Instance;
			bool flag = (Object)(object)instance != (Object)null && instance.shipHasLanded;
			if (flag && !_wasLanded)
			{
				CaptureExpeditionRoster();
				_expeditionStartingCrewCount = _expeditionRoster.Count;
				_someoneLeftShipThisExpedition = false;
				_expeditionHadDeath = false;
				_reunionAwardedThisExpedition = false;
			}
			int num = DetectNewExpeditionDeaths();
			if (num > 0)
			{
				_expeditionHadDeath = true;
				float num2 = DeathPenalty.Value * (float)num;
				ChangeMorale(0f - num2, "crew death");
				ShowEvent("CREW MEMBER LOST", $"-{num2:0.#} MORALE");
			}
			int num3 = CountLivingExpeditionPlayers();
			int num4 = CountLivingExpeditionPlayersInShip();
			bool flag2 = num3 > 0 && num4 == num3;
			if (flag && num3 > 0 && num4 < num3)
			{
				_someoneLeftShipThisExpedition = true;
			}
			if (flag && _someoneLeftShipThisExpedition && !_expeditionHadDeath && !_reunionAwardedThisExpedition && _expeditionStartingCrewCount > 0 && num3 == _expeditionStartingCrewCount && flag2)
			{
				ChangeMorale(ReunionReward.Value, "perfect crew reunion");
				ShowEvent("CREW REUNITED", $"+{ReunionReward.Value:0.#} MORALE");
				_reunionAwardedThisExpedition = true;
			}
			UpdateQuotaReward();
			bool num5 = IsInOrbit();
			float num6 = 0f;
			if (flag2)
			{
				num6 += FullCrewShipRecoveryPerSecond.Value;
			}
			if (num5)
			{
				num6 += OrbitRecoveryPerSecond.Value;
			}
			if (num6 > 0f)
			{
				MoveTowardMorale(DecorationBaseline, num6 * dt);
			}
			else if (num3 > 0 && num4 == 0 && EmptyShipDecayPerSecond.Value > 0f)
			{
				ChangeMorale((0f - EmptyShipDecayPerSecond.Value) * dt, "empty ship");
			}
			_wasFullLivingCrewInShip = flag2;
			_wasLanded = flag;
		}

		private void UpdateQuotaReward()
		{
			try
			{
				TimeOfDay val = Object.FindObjectOfType<TimeOfDay>();
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				FieldInfo fieldInfo = AccessTools.Field(typeof(TimeOfDay), "quotaFulfilled");
				FieldInfo fieldInfo2 = AccessTools.Field(typeof(TimeOfDay), "profitQuota");
				if (fieldInfo == null || fieldInfo2 == null)
				{
					return;
				}
				int num = Convert.ToInt32(fieldInfo.GetValue(val));
				int num2 = Mathf.Max(1, Convert.ToInt32(fieldInfo2.GetValue(val)));
				bool flag = num >= num2;
				if (!_quotaStateInitialized)
				{
					_quotaStateInitialized = true;
					_quotaWasFulfilled = flag;
					return;
				}
				if (flag && !_quotaWasFulfilled)
				{
					ChangeMorale(QuotaFulfilledReward.Value, "quota fulfilled");
					ShowEvent("QUOTA FULFILLED", $"+{QuotaFulfilledReward.Value:0.#} MORALE");
				}
				_quotaWasFulfilled = flag;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)("Quota morale check failed: " + ex.Message));
			}
		}

		private void ShowEvent(string title, string detail, float seconds = 4f)
		{
			_eventTitle = title ?? "";
			_eventDetail = detail ?? "";
			_eventUntil = Time.unscaledTime + seconds;
		}

		internal static void ChangeMorale(float amount, string reason = null)
		{
			float morale = Morale;
			Morale = Clamp(Morale + amount);
			if ((Object)(object)Instance != (Object)null && Mathf.Abs(Morale - morale) >= 0.01f && reason != null)
			{
				((BaseUnityPlugin)Instance).Logger.LogDebug((object)$"Morale {morale:0.0} -> {Morale:0.0} ({reason})");
			}
		}

		private static void MoveTowardMorale(float target, float maxDelta)
		{
			target = Clamp(target);
			Morale = Mathf.MoveTowards(Morale, target, Mathf.Max(0f, maxDelta));
		}

		private static float Clamp(float value)
		{
			return Mathf.Clamp(value, MinimumMorale?.Value ?? 0f, MaximumMorale?.Value ?? 100f);
		}

		private void RefreshDecorationBaseline()
		{
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if (instance?.unlockablesList?.unlockables == null)
				{
					return;
				}
				int num = 0;
				float num2 = 0f;
				foreach (UnlockableItem unlockable in instance.unlockablesList.unlockables)
				{
					if (unlockable != null && unlockable.unlockableType == 1 && !unlockable.alreadyUnlocked && unlockable.hasBeenUnlockedByPlayer && !unlockable.inStorage)
					{
						int num3 = 0;
						if ((Object)(object)unlockable.shopSelectionNode != (Object)null)
						{
							num3 = Mathf.Max(0, unlockable.shopSelectionNode.itemCost);
						}
						float num4 = Mathf.Max(1f, DecorationPriceScaleCredits.Value);
						float num5 = Mathf.Max(0.01f, DecorationPriceExponent.Value);
						float num6 = Mathf.Pow(Mathf.Max(0f, (float)num3) / num4, num5);
						num6 = Mathf.Clamp(num6, 0f, DecorationMaxMoraleEach.Value);
						num2 += num6;
						num++;
					}
				}
				DecorationCount = num;
				float num7 = Mathf.Min(DecorationBonusCap.Value, num2);
				DecorationBaseline = Clamp(StartingMorale.Value + num7);
				if (_previousDecorationBonus >= 0f && Mathf.Abs(num7 - _previousDecorationBonus) >= 0.1f)
				{
					float num8 = num7 - _previousDecorationBonus;
					if (num8 > 0f)
					{
						ShowEvent("SHIP DECOR IMPROVED", $"+{num8:0.##} MORALE  •  DECOR {num7:0.##}/{DecorationBonusCap.Value:0}");
					}
					else
					{
						ShowEvent("DECORATION REMOVED", $"{num8:0.##} MORALE  •  DECOR {num7:0.##}/{DecorationBonusCap.Value:0}");
					}
				}
				_previousDecorationBonus = num7;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)("Decoration scan failed: " + ex.Message));
			}
		}

		private void CaptureExpeditionRoster()
		{
			_expeditionRoster.Clear();
			_countedDeadPlayers.Clear();
			PlayerControllerB[] array = StartOfRound.Instance?.allPlayerScripts;
			if (array == null)
			{
				return;
			}
			PlayerControllerB[] array2 = array;
			foreach (PlayerControllerB val in array2)
			{
				if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead)
				{
					_expeditionRoster.Add(val);
				}
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Captured expedition roster: {_expeditionRoster.Count} crew member(s).");
		}

		private int DetectNewExpeditionDeaths()
		{
			int num = 0;
			foreach (PlayerControllerB item in _expeditionRoster)
			{
				if (!((Object)(object)item == (Object)null) && item.isPlayerDead && !_countedDeadPlayers.Contains(item))
				{
					_countedDeadPlayers.Add(item);
					num++;
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Crew death detected from expedition roster. Counted deaths: {_countedDeadPlayers.Count}/{_expeditionRoster.Count}");
				}
			}
			return num;
		}

		private int CountLivingExpeditionPlayers()
		{
			int num = 0;
			foreach (PlayerControllerB item in _expeditionRoster)
			{
				if ((Object)(object)item != (Object)null && !item.isPlayerDead)
				{
					num++;
				}
			}
			return num;
		}

		private int CountLivingExpeditionPlayersInShip()
		{
			int num = 0;
			foreach (PlayerControllerB item in _expeditionRoster)
			{
				if (!((Object)(object)item == (Object)null) && !item.isPlayerDead && IsPlayerInShip(item))
				{
					num++;
				}
			}
			return num;
		}

		private static int CountLivingPlayers()
		{
			PlayerControllerB[] array = StartOfRound.Instance?.allPlayerScripts;
			if (array == null)
			{
				return 0;
			}
			int num = 0;
			PlayerControllerB[] array2 = array;
			foreach (PlayerControllerB val in array2)
			{
				if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead)
				{
					num++;
				}
			}
			return num;
		}

		private static int CountDeadPlayers()
		{
			PlayerControllerB[] array = StartOfRound.Instance?.allPlayerScripts;
			if (array == null)
			{
				return 0;
			}
			int num = 0;
			PlayerControllerB[] array2 = array;
			foreach (PlayerControllerB val in array2)
			{
				if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && val.isPlayerDead)
				{
					num++;
				}
			}
			return num;
		}

		private static int CountLivingPlayersInShip()
		{
			PlayerControllerB[] array = StartOfRound.Instance?.allPlayerScripts;
			if (array == null)
			{
				return 0;
			}
			int num = 0;
			PlayerControllerB[] array2 = array;
			foreach (PlayerControllerB val in array2)
			{
				if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead && IsPlayerInShip(val))
				{
					num++;
				}
			}
			return num;
		}

		internal static bool IsPlayerInShip(PlayerControllerB player)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (player.isInHangarShipRoom)
			{
				return true;
			}
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			Vector3 position = ((Component)player).transform.position;
			Bounds bounds;
			if ((Object)(object)instance.shipInnerRoomBounds != (Object)null)
			{
				bounds = instance.shipInnerRoomBounds.bounds;
				if (((Bounds)(ref bounds)).Contains(position))
				{
					return true;
				}
			}
			if ((Object)(object)instance.shipBounds != (Object)null)
			{
				bounds = instance.shipBounds.bounds;
				if (((Bounds)(ref bounds)).Contains(position))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsInOrbit()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			if (instance.inShipPhase)
			{
				return !instance.shipHasLanded;
			}
			return false;
		}

		private void TryPatchInsanityMod()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Expected O, but got Unknown
			try
			{
				Type type = AccessTools.TypeByName("InsanityMod.InsanityCalculator");
				Type type2 = AccessTools.TypeByName("InsanityMod.Managers.InsanityModifiers");
				Type type3 = AccessTools.TypeByName("InsanityMod.Managers.InsanityManager");
				int num = 0;
				MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "TickDelta", (Type[])null, (Type[])null) : null);
				if (methodInfo != null)
				{
					_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(InsanityIntegration), "ScaleTickDelta", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				MethodInfo methodInfo2 = ((type2 != null) ? AccessTools.Method(type2, "ComputeBonusRate", (Type[])null, (Type[])null) : null);
				if (methodInfo2 != null)
				{
					_harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(InsanityIntegration), "ScaleBonusRate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				MethodInfo methodInfo3 = ((type3 != null) ? AccessTools.Method(type3, "AddInsanity", (Type[])null, (Type[])null) : null);
				if (methodInfo3 != null)
				{
					_harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(InsanityIntegration), "ScaleSpike", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)((num > 0) ? $"InsanityMod integration active ({num}/3 hooks)." : "InsanityMod not detected; morale will run without sanity integration."));
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Could not patch InsanityMod: {arg}");
			}
		}

		private void OnGUI()
		{
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: 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_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			if (!ShowHud.Value)
			{
				return;
			}
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			if (!((Object)(object)val == (Object)null) && IsPlayerInShip(val) && !((Object)(object)HUDManager.Instance == (Object)null))
			{
				EnsureGui();
				float num = Mathf.Max(280f, HudWidth.Value);
				float num2 = Mathf.Max(12f, HudHeight.Value);
				float num3 = (float)Screen.width * HudX.Value - num * 0.5f;
				float num4 = (float)Screen.height * HudY.Value;
				float num5 = Mathf.Max(0f, DecorationBaseline - StartingMorale.Value);
				string arg = MoraleStatus(Morale);
				float num6 = InsanityIntegration.GainMultiplier();
				float num7 = InsanityIntegration.RecoveryMultiplier();
				GUI.Label(new Rect(num3, num4 - 37f, num, 18f), $"CREW MORALE   {Morale:0.#} / 100   •   {arg}", _labelStyle);
				GUI.Label(new Rect(num3, num4 - 19f, num, 16f), $"DECOR +{num5:0.##}/{DecorationBonusCap.Value:0}   •   SANITY ×{num6:0.00} GAIN / ×{num7:0.00} RECOVERY", _smallStyle);
				Rect val2 = new Rect(num3, num4, num, num2);
				GUI.color = new Color(0f, 0f, 0f, 0.76f);
				GUI.DrawTexture(val2, (Texture)(object)_pixel);
				float num8 = Mathf.InverseLerp(MinimumMorale.Value, MaximumMorale.Value, Morale);
				Rect val3 = new Rect(num3 + 2f, num4 + 2f, Mathf.Max(0f, (num - 4f) * num8), Mathf.Max(1f, num2 - 4f));
				if (Morale >= 80f)
				{
					GUI.color = new Color(0.35f, 0.95f, 0.55f, 0.96f);
				}
				else if (Morale >= 60f)
				{
					GUI.color = new Color(0.25f, 0.88f, 0.72f, 0.96f);
				}
				else if (Morale >= 40f)
				{
					GUI.color = new Color(0.2f, 0.75f, 0.95f, 0.96f);
				}
				else if (Morale >= 20f)
				{
					GUI.color = new Color(0.95f, 0.58f, 0.2f, 0.96f);
				}
				else
				{
					GUI.color = new Color(0.95f, 0.25f, 0.2f, 0.96f);
				}
				GUI.DrawTexture(val3, (Texture)(object)_pixel);
				GUI.color = Color.white;
				if (Time.unscaledTime < _eventUntil)
				{
					float num9 = num4 + num2 + 10f;
					GUI.Label(new Rect(num3, num9, num, 22f), _eventTitle, _eventTitleStyle);
					GUI.Label(new Rect(num3, num9 + 21f, num, 18f), _eventDetail, _eventDetailStyle);
				}
			}
		}

		private static string MoraleStatus(float morale)
		{
			if (morale >= 85f)
			{
				return "THRIVING";
			}
			if (morale >= 70f)
			{
				return "CONFIDENT";
			}
			if (morale >= 50f)
			{
				return "STABLE";
			}
			if (morale >= 35f)
			{
				return "UNEASY";
			}
			if (morale >= 20f)
			{
				return "SHAKEN";
			}
			return "BROKEN";
		}

		private void EnsureGui()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0023: 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_0050: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			//IL_008e: 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_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Expected O, but got Unknown
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: 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_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Expected O, but got Unknown
			if ((Object)(object)_pixel == (Object)null)
			{
				_pixel = new Texture2D(1, 1);
				_pixel.SetPixel(0, 0, Color.white);
				_pixel.Apply();
			}
			if (_labelStyle == null)
			{
				GUIStyle val = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4,
					fontSize = 13,
					fontStyle = (FontStyle)1
				};
				val.normal.textColor = Color.white;
				_labelStyle = val;
			}
			if (_smallStyle == null)
			{
				GUIStyle val2 = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4,
					fontSize = 10,
					fontStyle = (FontStyle)0
				};
				val2.normal.textColor = new Color(0.88f, 0.92f, 0.95f, 1f);
				_smallStyle = val2;
			}
			if (_eventTitleStyle == null)
			{
				GUIStyle val3 = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4,
					fontSize = 16,
					fontStyle = (FontStyle)1
				};
				val3.normal.textColor = Color.white;
				_eventTitleStyle = val3;
			}
			if (_eventDetailStyle == null)
			{
				GUIStyle val4 = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4,
					fontSize = 12,
					fontStyle = (FontStyle)1
				};
				val4.normal.textColor = new Color(0.9f, 0.95f, 1f, 1f);
				_eventDetailStyle = val4;
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)_pixel != (Object)null)
			{
				Object.Destroy((Object)(object)_pixel);
			}
		}
	}
	internal static class InsanityIntegration
	{
		internal static float Morale01 => Mathf.Clamp01(CrewMoralePlugin.Morale / 100f);

		internal static float GainMultiplier()
		{
			return Mathf.Lerp(CrewMoralePlugin.LowMoraleSanityGainMultiplier.Value, CrewMoralePlugin.HighMoraleSanityGainMultiplier.Value, Morale01);
		}

		internal static float RecoveryMultiplier()
		{
			return Mathf.Lerp(CrewMoralePlugin.LowMoraleRecoveryMultiplier.Value, CrewMoralePlugin.HighMoraleRecoveryMultiplier.Value, Morale01);
		}

		public static void ScaleTickDelta(ref float __result)
		{
			__result *= ((__result >= 0f) ? GainMultiplier() : RecoveryMultiplier());
		}

		public static void ScaleBonusRate(ref float __result)
		{
			__result *= ((__result >= 0f) ? GainMultiplier() : RecoveryMultiplier());
		}

		public static void ScaleSpike(ref float amount)
		{
			if (amount > 0f)
			{
				amount *= GainMultiplier();
			}
			else if (amount < 0f)
			{
				amount *= RecoveryMultiplier();
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "Start")]
	internal static class RoundStartPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			CrewMoralePlugin.ChangeMorale(CrewMoralePlugin.StartingMorale.Value - CrewMoralePlugin.Morale, "round/session init");
		}
	}
}