Decompiled source of BoneEngine v1.0.2

BoneEngine.dll

Decompiled 17 hours ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("BoneEngine")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2+3b3d77b03712271c1770b598c924c40243616104")]
[assembly: AssemblyProduct("BoneEngine")]
[assembly: AssemblyTitle("BoneEngine")]
[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 BoneEngine
{
	[HarmonyPatch]
	internal static class EnginePatches
	{
		private static readonly Failsafe s_failsafe = new Failsafe();

		public static bool Off => s_failsafe.Off;

		[HarmonyPatch(typeof(Ship), "CustomFixedUpdate")]
		[HarmonyPostfix]
		private static void CustomFixedUpdate(Ship __instance, float fixedDeltaTime, ZNetView ___m_nview, Rigidbody ___m_body)
		{
			if (s_failsafe.Off)
			{
				return;
			}
			try
			{
				if (!((Object)(object)___m_nview == (Object)null) && ___m_nview.IsValid() && !((Object)(object)___m_body == (Object)null))
				{
					EngineState engineState = ShipEngine.State(__instance);
					ShipEngine.MaybeClaim(__instance, ___m_nview, engineState, fixedDeltaTime);
					if (!___m_nview.IsOwner())
					{
						engineState.Pushing = false;
						engineState.Remaining = 0f;
						engineState.OwnedSeconds = 0f;
					}
					else
					{
						engineState.OwnedSeconds += fixedDeltaTime;
						ShipEngine.Tick(__instance, ___m_body, engineState, fixedDeltaTime);
					}
				}
			}
			catch (Exception arg)
			{
				if (s_failsafe.Trip())
				{
					Plugin.Log.LogError((object)$"The engine failed and is off for this session (boats are vanilla): {arg}");
				}
			}
		}
	}
	internal static class EngineRules
	{
		public const string FuelItem = "$item_bonefragments";

		public const float EnginePower = 1.2f;

		public const float BurnSeconds = 15f;

		public const float ReferenceSailFactor = 0.05f;

		public const float OwnerStableSeconds = 2f;

		public const float HoldClosedBeforeClaimSeconds = 2f;

		public const float OwnedBeforeLoadSeconds = 1.5f;

		public const float ClaimInterval = 1f;

		public static float GearFraction(Speed gear)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected I4, but got Unknown
			switch (gear - 1)
			{
			case 0:
			case 1:
				return 1f / 3f;
			case 2:
				return 2f / 3f;
			case 3:
				return 1f;
			default:
				return 0f;
			}
		}

		public static int Direction(Speed gear)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			if ((int)gear == 1)
			{
				return -1;
			}
			return ((int)gear > 0) ? 1 : 0;
		}

		public static float Impulse(float sailForceFactor, float fraction, float mass)
		{
			return 1.2f * sailForceFactor * fraction * mass;
		}

		public static bool RunsHere(bool localSteering, bool anyoneSteering)
		{
			if (!localSteering)
			{
				return !anyoneSteering;
			}
			return true;
		}

		public static float BoneSeconds(float sailForceFactor)
		{
			if (sailForceFactor <= 0f)
			{
				return 15f;
			}
			return 0.75f / sailForceFactor;
		}

		public static bool NeedsBone(float remaining)
		{
			return remaining <= 0f;
		}

		public static bool HasEngine(float sailForceFactor)
		{
			return sailForceFactor > 0f;
		}

		public static bool CanLoad(bool holdOpen, int bones, float ownedSeconds)
		{
			if (!holdOpen && bones > 0)
			{
				return ownedSeconds >= 1.5f;
			}
			return false;
		}

		public static float Drain(float remaining, float dt, float fraction)
		{
			if (fraction <= 0f || dt <= 0f)
			{
				return remaining;
			}
			float num = remaining - dt * fraction;
			if (!(num > 0f))
			{
				return 0f;
			}
			return num;
		}

		public static bool ShouldClaim(bool steering, bool owner, float holdClosedSeconds, float ownerStableSeconds)
		{
			if (steering && !owner && holdClosedSeconds >= 2f)
			{
				return ownerStableSeconds >= 2f;
			}
			return false;
		}

		public static string CountLine(string itemName, int count)
		{
			return itemName + ": " + count;
		}

		public static string PanelLine(string itemName, int count, bool running, string goldHex)
		{
			string text = (running ? ("<color=" + goldHex + ">active</color>") : "idle");
			return CountLine(itemName, count) + "\nEngine status: " + text;
		}
	}
	internal sealed class Failsafe
	{
		public bool Off { get; private set; }

		public bool Trip()
		{
			if (Off)
			{
				return false;
			}
			Off = true;
			return true;
		}
	}
	[HarmonyPatch]
	internal static class HoverPatches
	{
		private static readonly Failsafe s_failsafe = new Failsafe();

		[HarmonyPatch(typeof(ShipControlls), "GetHoverText")]
		[HarmonyPostfix]
		private static void GetHoverText(ShipControlls __instance, ref string __result)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (s_failsafe.Off || EnginePatches.Off)
			{
				return;
			}
			try
			{
				Ship ship = __instance.m_ship;
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)ship == (Object)null) && !((Object)(object)localPlayer == (Object)null) && !((Object)(object)__instance.m_attachPoint == (Object)null) && !(Vector3.Distance(((Component)localPlayer).transform.position, __instance.m_attachPoint.position) >= __instance.m_maxUseRange) && ShipEngine.HoldInventory(ship) != null && EngineRules.HasEngine(ship.m_sailForceFactor))
				{
					__result = __result + "\n" + EngineRules.CountLine(Localization.instance.Localize("$item_bonefragments"), ShipEngine.Bones(ship));
				}
			}
			catch (Exception arg)
			{
				if (s_failsafe.Trip())
				{
					Plugin.Log.LogWarning((object)$"The rudder's bone count failed and is off for this session (the rudder text is vanilla): {arg}");
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class HudPatches
	{
		private const float LineGap = 34f;

		private const float LineFontSize = 20f;

		private static Hud s_hud;

		private static TMP_Text s_text;

		private static int s_lastCount = -1;

		private static bool s_lastRunning;

		private static bool s_off;

		private static string s_goldHex;

		private const string FallbackGoldHex = "#FFD23C";

		[HarmonyPatch(typeof(Hud), "UpdateShipHud")]
		[HarmonyPostfix]
		private static void UpdateShipHud(Hud __instance, Player player)
		{
			if (s_off)
			{
				return;
			}
			try
			{
				Ship val = (((Object)(object)player != (Object)null) ? player.GetControlledShip() : null);
				GameObject shipHudRoot = __instance.m_shipHudRoot;
				if (EnginePatches.Off || !((Object)(object)val != (Object)null) || !((Object)(object)shipHudRoot != (Object)null) || !shipHudRoot.activeSelf || ShipEngine.HoldInventory(val) == null || !EngineRules.HasEngine(val.m_sailForceFactor))
				{
					if ((Object)(object)s_text != (Object)null && ((Component)s_text).gameObject.activeSelf)
					{
						((Component)s_text).gameObject.SetActive(false);
					}
					return;
				}
				if ((Object)(object)s_text == (Object)null || (Object)(object)s_hud != (Object)(object)__instance)
				{
					s_hud = __instance;
					s_text = MakeLine(__instance);
					s_lastCount = -1;
					if ((Object)(object)s_text == (Object)null)
					{
						TurnOff("The steering panel has no text to copy (a game update?): no bone count on the panel");
						return;
					}
				}
				int num = ShipEngine.Bones(val);
				bool pushing = ShipEngine.State(val).Pushing;
				if (num != s_lastCount || pushing != s_lastRunning)
				{
					s_lastCount = num;
					s_lastRunning = pushing;
					s_text.text = EngineRules.PanelLine(Localization.instance.Localize("$item_bonefragments"), num, pushing, GoldHex());
					FitToText(s_text);
				}
				if (!((Component)s_text).gameObject.activeSelf)
				{
					((Component)s_text).gameObject.SetActive(true);
				}
			}
			catch (Exception arg)
			{
				TurnOff($"The steering panel's bone count failed; the panel is vanilla from here: {arg}");
			}
		}

		private static string GoldHex()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			if (s_goldHex != null)
			{
				return s_goldHex;
			}
			InventoryGui instance = InventoryGui.instance;
			TMP_Text val = (((Object)(object)instance != (Object)null) ? instance.m_weight : null);
			s_goldHex = (((Object)(object)val != (Object)null) ? ("#" + ColorUtility.ToHtmlStringRGB(((Graphic)val).color)) : "#FFD23C");
			return s_goldHex;
		}

		private static TMP_Text MakeLine(Hud hud)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: 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_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: 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_00e4: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text healthText = hud.m_healthText;
			RectTransform shipWindIndicatorRoot = hud.m_shipWindIndicatorRoot;
			RectTransform val = (RectTransform)(((Object)(object)shipWindIndicatorRoot != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)healthText == (Object)null || (Object)(object)val == (Object)null)
			{
				return null;
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)healthText).gameObject, (Transform)(object)val);
			((Object)val2).name = "BoneEngineCount";
			TMP_Text component = val2.GetComponent<TMP_Text>();
			if ((Object)(object)component == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				return null;
			}
			RectTransform val3 = (RectTransform)val2.transform;
			val3.anchorMin = shipWindIndicatorRoot.anchorMin;
			val3.anchorMax = shipWindIndicatorRoot.anchorMax;
			val3.pivot = new Vector2(1f, 1f);
			float y = shipWindIndicatorRoot.anchoredPosition.y;
			Rect rect = shipWindIndicatorRoot.rect;
			float num = y - ((Rect)(ref rect)).height * (1f - shipWindIndicatorRoot.pivot.y);
			float x = shipWindIndicatorRoot.anchoredPosition.x;
			rect = shipWindIndicatorRoot.rect;
			float num2 = x + ((Rect)(ref rect)).width * (1f - shipWindIndicatorRoot.pivot.x);
			val3.anchoredPosition = new Vector2(num2, num - 34f);
			val3.sizeDelta = new Vector2(420f, 60f);
			((Transform)val3).localScale = Vector3.one;
			((Transform)val3).localRotation = Quaternion.identity;
			component.alignment = (TextAlignmentOptions)257;
			component.textWrappingMode = (TextWrappingModes)0;
			component.overflowMode = (TextOverflowModes)0;
			component.enableAutoSizing = false;
			component.fontSize = 20f;
			((Graphic)component).color = Color.white;
			component.text = "";
			return component;
		}

		private static void FitToText(TMP_Text text)
		{
			//IL_0001: 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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			Vector2 preferredValues = text.GetPreferredValues();
			((RectTransform)text.transform).sizeDelta = new Vector2(preferredValues.x + 2f, preferredValues.y + 2f);
		}

		private static void TurnOff(string message)
		{
			s_off = true;
			Plugin.Log.LogWarning((object)message);
			try
			{
				if ((Object)(object)s_text != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)s_text).gameObject);
				}
			}
			catch (Exception)
			{
			}
			s_text = null;
		}
	}
	[BepInPlugin("algo7.boneengine", "BoneEngine", "1.0.2")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "algo7.boneengine";

		public const string Name = "BoneEngine";

		public const string PluginVersion = "1.0.2";

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			if (Application.isBatchMode)
			{
				Log.LogInfo((object)"BoneEngine loaded (v1.0.2): dedicated server, nothing to do");
				return;
			}
			Harmony harmony = new Harmony("algo7.boneengine");
			if (!TryPatch(harmony, typeof(EnginePatches), "engine hook"))
			{
				Log.LogError((object)"The engine is off: boats are vanilla");
				return;
			}
			if (!TryPatch(harmony, typeof(HoverPatches), "rudder text hook"))
			{
				Log.LogWarning((object)"No bone count on the rudder (the engine still works)");
			}
			if (!TryPatch(harmony, typeof(HudPatches), "steering panel hook"))
			{
				Log.LogWarning((object)"No bone count on the steering panel (the engine still works)");
			}
			Log.LogInfo((object)"BoneEngine loaded (v1.0.2)");
		}

		private static bool TryPatch(Harmony harmony, Type patches, string what)
		{
			try
			{
				harmony.PatchAll(patches);
				return true;
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Could not install the {what}: {arg}");
				return false;
			}
		}
	}
	internal sealed class EngineState
	{
		public float Remaining;

		public bool Pushing;

		public WaterVolume Water;

		public Container Hold;

		public bool HoldLooked;

		public float ClaimTimer;

		public long LastOwner;

		public float OwnerStable;

		public float OwnedSeconds;

		public float HoldClosedSeconds;
	}
	internal static class ShipEngine
	{
		private static readonly ConditionalWeakTable<Ship, EngineState> s_states = new ConditionalWeakTable<Ship, EngineState>();

		public static EngineState State(Ship ship)
		{
			return s_states.GetValue(ship, (Ship _) => new EngineState());
		}

		public static Container Hold(Ship ship)
		{
			EngineState engineState = State(ship);
			if (!engineState.HoldLooked)
			{
				engineState.HoldLooked = true;
				ZNetView component = ((Component)ship).GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null)
				{
					Container[] componentsInChildren = ((Component)ship).GetComponentsInChildren<Container>(true);
					foreach (Container val in componentsInChildren)
					{
						if (!((Object)(object)val.m_rootObjectOverride != (Object)(object)component))
						{
							engineState.Hold = val;
							break;
						}
					}
				}
			}
			return engineState.Hold;
		}

		public static Inventory HoldInventory(Ship ship)
		{
			Container val = Hold(ship);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return val.GetInventory();
		}

		public static int Bones(Ship ship)
		{
			Inventory val = HoldInventory(ship);
			if (val == null)
			{
				return 0;
			}
			return val.CountItems("$item_bonefragments", -1, false);
		}

		public static bool HoldOpen(ZNetView nview)
		{
			return nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 1;
		}

		private static bool InWater(Ship ship, Rigidbody body, EngineState state)
		{
			//IL_0001: 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)
			Vector3 worldCenterOfMass = body.worldCenterOfMass;
			float waterLevel = Floating.GetWaterLevel(worldCenterOfMass, ref state.Water);
			return worldCenterOfMass.y - waterLevel - ship.m_waterLevelOffset <= ship.m_disableLevel;
		}

		public static void MaybeClaim(Ship ship, ZNetView nview, EngineState state, float dt)
		{
			long owner = nview.GetZDO().GetOwner();
			if (owner != state.LastOwner)
			{
				state.LastOwner = owner;
				state.OwnerStable = 0f;
			}
			else
			{
				state.OwnerStable += dt;
			}
			if (HoldOpen(nview))
			{
				state.HoldClosedSeconds = 0f;
			}
			else
			{
				state.HoldClosedSeconds += dt;
			}
			state.ClaimTimer += dt;
			if (!(state.ClaimTimer < 1f))
			{
				state.ClaimTimer = 0f;
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)ship.m_shipControlls == (Object)null) && EngineRules.ShouldClaim(ship.m_shipControlls.GetUser() == localPlayer.GetPlayerID(), nview.IsOwner(), state.HoldClosedSeconds, state.OwnerStable))
				{
					nview.ClaimOwnership();
					state.OwnerStable = 0f;
					Plugin.Log.LogDebug((object)$"Took over {((Object)ship).name} for the engine (previous owner {owner})");
				}
			}
		}

		public static void Tick(Ship ship, Rigidbody body, EngineState state, float dt)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: 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_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: 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)
			state.Pushing = false;
			if (!EngineRules.HasEngine(ship.m_sailForceFactor))
			{
				return;
			}
			Speed speedSetting = ship.GetSpeedSetting();
			float num = EngineRules.GearFraction(speedSetting);
			if (num <= 0f)
			{
				return;
			}
			ShipControlls shipControlls = ship.m_shipControlls;
			Player localPlayer = Player.m_localPlayer;
			bool localSteering = (Object)(object)shipControlls != (Object)null && (Object)(object)localPlayer != (Object)null && shipControlls.GetUser() == localPlayer.GetPlayerID();
			bool anyoneSteering = (Object)(object)shipControlls != (Object)null && shipControlls.HaveValidUser();
			if (!EngineRules.RunsHere(localSteering, anyoneSteering))
			{
				return;
			}
			Container val = Hold(ship);
			Inventory val2 = (((Object)(object)val != (Object)null) ? val.GetInventory() : null);
			if (val2 == null || !InWater(ship, body, state))
			{
				return;
			}
			if (EngineRules.NeedsBone(state.Remaining))
			{
				int bones = val2.CountItems("$item_bonefragments", -1, false);
				if (!EngineRules.CanLoad(val.IsInUse(), bones, state.OwnedSeconds))
				{
					return;
				}
				state.Remaining = EngineRules.BoneSeconds(ship.m_sailForceFactor);
				val2.RemoveItem("$item_bonefragments", 1, -1, false);
			}
			Vector3 val3 = ((Component)ship).transform.forward * (float)EngineRules.Direction(speedSetting);
			float num2 = EngineRules.Impulse(ship.m_sailForceFactor, num, body.mass);
			body.AddForceAtPosition(val3 * num2, body.worldCenterOfMass, (ForceMode)1);
			state.Remaining = EngineRules.Drain(state.Remaining, dt, num);
			state.Pushing = true;
		}
	}
	internal static class PluginInfo
	{
		public const string Version = "1.0.2";
	}
}