Decompiled source of Throw Anything v0.1.3

plugins/ThrowAnything.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using AzNean.SotfCommon;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Microsoft.CodeAnalysis;
using Sons.Inventory;
using TheForest.Items.Inventory;
using TheForest.Items.Special;
using TheForest.Items.Utils;
using TheForest.Utils;
using ThrowAnything.Logic;
using ThrowAnything.Patches;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("ThrowAnything")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.3.0")]
[assembly: AssemblyInformationalVersion("0.1.3+25cbfd528443c0ae893d9407fc052029f68b30ae")]
[assembly: AssemblyProduct("ThrowAnything")]
[assembly: AssemblyTitle("ThrowAnything")]
[assembly: AssemblyVersion("0.1.3.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 AzNean.SotfCommon
{
	internal static class ModLog
	{
		private static ManualLogSource _log;

		private static ConfigEntry<bool> _full;

		private static readonly HashSet<string> _once = new HashSet<string>();

		private static readonly Dictionary<(int, string), int> _state = new Dictionary<(int, string), int>();

		internal static bool Full => _full.Value;

		internal static void Init(ManualLogSource log, ConfigEntry<bool> full)
		{
			_log = log;
			_full = full;
		}

		internal static void Trace(string message)
		{
			if (_full.Value)
			{
				_log.LogInfo((object)message);
			}
		}

		internal static bool Changed(int id, string what, int state)
		{
			if (!_full.Value || (_state.TryGetValue((id, what), out var value) && value == state))
			{
				return false;
			}
			_state[(id, what)] = state;
			return true;
		}

		internal static void Once(string key, string message, LogLevel level = (LogLevel)16)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (_once.Add(key))
			{
				_log.Log(level, (object)message);
			}
		}
	}
}
namespace ThrowAnything
{
	[BepInPlugin("aznan.sotf.throwanything", "ThrowAnything", "0.1.3")]
	public class Plugin : BasePlugin
	{
		internal const string Version = "0.1.3";

		internal const float HeldSpawnWaitSeconds = 3f;

		internal static ManualLogSource Logger;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> WholeStack;

		internal static ConfigEntry<bool> EquipPrevious;

		internal static ConfigEntry<bool> FullLogging;

		internal static ConfigEntry<KeyCode> ThrowKey;

		internal static ConfigEntry<KeyCode> ModifierKey;

		internal static ConfigEntry<float> ThrowSpeed;

		internal static ConfigEntry<float> HeldForceMultiplier;

		internal static ConfigEntry<float> MessageSeconds;

		internal static ConfigEntry<string> ThrownMessage;

		internal static ConfigEntry<string> NothingMessage;

		public override void Load()
		{
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: 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_01cb: Expected O, but got Unknown
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Expected O, but got Unknown
			Logger = ((BasePlugin)this).Log;
			Texts.SelfCheck();
			Enabled = ((BasePlugin)this).Config.Bind<bool>("General", "Enabled", true, "True = the throw key throws what you hold. False = the key does nothing.");
			ThrowKey = ((BasePlugin)this).Config.Bind<KeyCode>("Throw", "ThrowKey", (KeyCode)103, "Key that throws what you hold in your hands. Default G (with Alt, see ModifierKey).");
			ModifierKey = ((BasePlugin)this).Config.Bind<KeyCode>("Throw", "ModifierKey", (KeyCode)308, "Key to hold together with ThrowKey. Default LeftAlt (Alt+G). None = ThrowKey alone.");
			ThrowSpeed = ((BasePlugin)this).Config.Bind<float>("Throw", "ThrowSpeed", 12f, "Speed of an item thrown from the right hand (axe, stone, bottle...), in metres per second, where you look. Default 12. Advised: 5 to 25. 0 = dropped at your feet, as the game drops it.");
			HeldForceMultiplier = ((BasePlugin)this).Config.Bind<float>("Throw", "CarriedForceMultiplier", 2f, "Logs, planks and stones carried in your arms: how hard they are thrown, times the game's own drop push. Default 2. Advised: 1 to 4. 1 or less than 0 = the game's drop.");
			WholeStack = ((BasePlugin)this).Config.Bind<bool>("Throw", "ThrowWholeCarriedStack", false, "True = one press throws everything carried in your arms (all the logs, planks or stones). False = one item per press. Default false. An item in the right hand is always thrown alone.");
			EquipPrevious = ((BasePlugin)this).Config.Bind<bool>("Throw", "EquipPreviousItem", true, "True = after throwing the item in your right hand, the item you held before comes back to your hand, as when the game drops it. False = empty hand. Default true.");
			ThrownMessage = ((BasePlugin)this).Config.Bind<string>("Texts", "ThrownMessage", "", "On-screen message after a throw; {0} = how many items. Empty = automatic, in the game's language.");
			NothingMessage = ((BasePlugin)this).Config.Bind<string>("Texts", "NothingToThrowMessage", "", "On-screen message when the key is pressed with nothing to throw. Empty = automatic, in the game's language.");
			MessageSeconds = ((BasePlugin)this).Config.Bind<float>("Texts", "MessageSeconds", 2f, "Seconds the on-screen message stays. Default 2. Advised: 1 to 6. 0 or less = no message.");
			FullLogging = ((BasePlugin)this).Config.Bind<bool>("Debug", "FullLogging", false, "True = the BepInEx log gets every step useful to diagnose a problem (what was held, the game's drop forces, the speed given to the item). False = only loading, version, one line per throw and errors.");
			ModLog.Init(Logger, FullLogging);
			new Harmony("aznan.sotf.throwanything").PatchAll(typeof(Thrower).Assembly);
			ClassInjector.RegisterTypeInIl2Cpp<KeyWatcher>();
			GameObject val = new GameObject("ThrowAnything");
			Object.DontDestroyOnLoad((Object)val);
			((Object)val).hideFlags = (HideFlags)61;
			val.AddComponent<KeyWatcher>();
			ManualLogSource logger = Logger;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(22, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("ThrowAnything ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("0.1.3");
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" loaded.");
			}
			logger.LogInfo(val2);
		}
	}
}
namespace ThrowAnything.Patches
{
	internal static class Thrower
	{
		[HarmonyPatch(typeof(ItemUtils), "CalculateDropPositionAndRotation")]
		private static class AimVelocity
		{
			private static void Postfix(ref Vector3 mpVelocity)
			{
				//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)
				if (_throwingHand)
				{
					mpVelocity = _velocity;
				}
			}
		}

		[HarmonyPatch(typeof(PlayerInventory), "DropItemInternal")]
		private static class LaunchSpawned
		{
			private static void Postfix(ref GameObject resultingItem)
			{
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				if (_throwingHand)
				{
					Rigidbody val = (((Object)(object)resultingItem == (Object)null) ? null : resultingItem.GetComponentInChildren<Rigidbody>());
					if ((Object)(object)val == (Object)null)
					{
						ModLog.Trace("Throw: no local pickup to push (guest: the host spawns it with the velocity sent).");
						return;
					}
					val.isKinematic = false;
					val.velocity = _velocity;
					ModLog.Trace($"Throw: pickup {((Object)resultingItem).name} pushed at {((Vector3)(ref _velocity)).magnitude:0.0} m/s.");
				}
			}
		}

		[HarmonyPatch(typeof(HeldOnlyItemController), "SpawnHeldItemDrop")]
		private static class StrongerHeldDrop
		{
			private static void Prefix(HeldOnlyItemController __instance, out (bool on, float force, float up) __state)
			{
				__state = default((bool, float, float));
				if (_pendingHeld <= 0)
				{
					return;
				}
				if (Time.time > _pendingUntil)
				{
					ModLog.Trace($"Throw: {_pendingHeld} carried item(s) never spawned by the game within {3f} s: this drop keeps the game's push.");
					_pendingHeld = 0;
					return;
				}
				_pendingHeld--;
				float value = Plugin.HeldForceMultiplier.Value;
				if (value <= 0f)
				{
					ModLog.Trace("Throw: carried item dropped with the game's push (CarriedForceMultiplier is 0 or less).");
					return;
				}
				__state = (on: true, force: __instance._logDropForce, up: __instance._logDropUpForce);
				__instance._logDropForce *= value;
				__instance._logDropUpForce *= value;
				ModLog.Trace($"Throw: carried item pushed x{value}: force {__state.force:0.0} -> {__instance._logDropForce:0.0}, up {__state.up:0.0} -> {__instance._logDropUpForce:0.0}.");
			}

			private static void Postfix(HeldOnlyItemController __instance, (bool on, float force, float up) __state)
			{
				if (__state.on)
				{
					__instance._logDropForce = __state.force;
					__instance._logDropUpForce = __state.up;
				}
			}
		}

		private static bool _throwingHand;

		private static int _pendingHeld;

		private static float _pendingUntil;

		private static Vector3 _velocity;

		internal static void Throw()
		{
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Expected O, but got Unknown
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			PlayerInventory inventory = LocalPlayer.Inventory;
			if ((Object)(object)inventory == (Object)null || !LocalPlayer.IsInWorld)
			{
				ModLog.Trace("Throw key: " + (((Object)(object)inventory == (Object)null) ? "not in a game" : "not in the plain world view (backpack, menu, sleep)") + ": nothing thrown.");
				return;
			}
			IHeldOnlyItemController heldOnlyItemController = inventory.HeldOnlyItemController;
			HeldOnlyItemController val = ((heldOnlyItemController != null) ? ((Il2CppObjectBase)heldOnlyItemController).TryCast<HeldOnlyItemController>() : null);
			int num = 0;
			string text = "";
			bool flag = default(bool);
			if ((Object)(object)val != (Object)null && val._heldCount > 0)
			{
				int num2 = (_pendingHeld = ((!Plugin.WholeStack.Value) ? 1 : val._heldCount));
				_pendingUntil = Time.time + 3f;
				ModLog.Trace($"Throw: {num2} of {val._heldCount} carried item(s) id {val._heldItemId}, game forces {val._logDropForce:0.0} / up {val._logDropUpForce:0.0}.");
				if (Plugin.WholeStack.Value)
				{
					val.DropAll();
				}
				else
				{
					val.PutDown(false, true, true, (GameObject)null, -1, -1f);
				}
				num = num2;
			}
			else if (!inventory.IsRightHandEmpty() && !inventory.BlockDrop)
			{
				ItemInstance rightHandItem = inventory.RightHandItem;
				_velocity = LocalPlayer.MainCamTr.forward * Plugin.ThrowSpeed.Value;
				_throwingHand = true;
				try
				{
					inventory.DropRightHandItem(Plugin.EquipPrevious.Value);
				}
				finally
				{
					_throwingHand = false;
				}
				num = ((inventory.RightHandItem != rightHandItem) ? 1 : 0);
				if (num == 0)
				{
					ManualLogSource logger = Plugin.Logger;
					BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(62, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Throw: the game kept item ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int?>((rightHandItem != null) ? new int?(rightHandItem._itemID) : ((int?)null));
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" in the hand (it cannot be dropped).");
					}
					logger.LogWarning(val2);
				}
			}
			else
			{
				text = (inventory.IsRightHandEmpty() ? ": nothing carried and nothing in the right hand" : ": the game blocks dropping right now");
			}
			ManualLogSource logger2 = Plugin.Logger;
			BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(23, 3, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Throw: ");
				((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(num);
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" item(s) thrown");
				((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(text);
				((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(BoltNetwork.isClient ? " (multiplayer guest: sent to the host as the game's own drop)" : "");
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(".");
			}
			logger2.LogInfo(val3);
			Show(num);
		}

		private static void Show(int thrown)
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			if (Plugin.MessageSeconds.Value <= 0f)
			{
				ModLog.Trace("Throw: no on-screen message (Texts/MessageSeconds is 0 or less).");
				return;
			}
			string text = ((thrown > 0) ? Plugin.ThrownMessage.Value : Plugin.NothingMessage.Value);
			if (string.IsNullOrEmpty(text))
			{
				Locale selectedLocale = LocalizationSettings.SelectedLocale;
				text = Texts.Get((selectedLocale != null) ? selectedLocale.Identifier.Code : null, (thrown > 0) ? "Thrown" : "NothingHeld");
			}
			HudGui instance = HudGui._instance;
			if ((Object)(object)instance == (Object)null)
			{
				ModLog.Once("nohud", "Throw: the game's message line (HudGui) was not found: no on-screen message.", (LogLevel)4);
				return;
			}
			try
			{
				instance.DisplayGeneralMessage(text.Replace("{0}", thrown.ToString()), Plugin.MessageSeconds.Value, (ItemUiData)null);
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(33, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Throw: game message line failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				logger.LogWarning(val);
			}
		}
	}
	public class KeyWatcher : MonoBehaviour
	{
		public KeyWatcher(IntPtr ptr)
			: base(ptr)
		{
		}

		private void Update()
		{
			//IL_0005: 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_00ca: Expected O, but got Unknown
			//IL_0017: 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_001d: 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_008d: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			if (!Input.GetKeyDown(Plugin.ThrowKey.Value))
			{
				return;
			}
			KeyCode value = Plugin.ModifierKey.Value;
			if ((int)value != 0 && !Input.GetKey(value))
			{
				ModLog.Trace($"{Plugin.ThrowKey.Value} pressed without {value}: not a throw.");
				return;
			}
			if (!Plugin.Enabled.Value)
			{
				ModLog.Trace($"{Plugin.ThrowKey.Value} pressed: mod turned off (General/Enabled), nothing thrown.");
				return;
			}
			try
			{
				Thrower.Throw();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(14, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Throw failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
			}
		}
	}
}
namespace ThrowAnything.Logic
{
	public static class Texts
	{
		public const string Thrown = "Thrown";

		public const string NothingHeld = "NothingHeld";

		private static readonly Dictionary<string, Dictionary<string, string>> ByLanguage = new Dictionary<string, Dictionary<string, string>>
		{
			["en"] = new Dictionary<string, string>
			{
				["Thrown"] = "{0} item(s) thrown",
				["NothingHeld"] = "Nothing in your hands to throw"
			},
			["fr"] = new Dictionary<string, string>
			{
				["Thrown"] = "{0} objet(s) jeté(s)",
				["NothingHeld"] = "Rien en main à jeter"
			},
			["zh"] = new Dictionary<string, string>
			{
				["Thrown"] = "已投掷 {0} 件物品",
				["NothingHeld"] = "手中没有可投掷的物品"
			},
			["zh-hant"] = new Dictionary<string, string>
			{
				["Thrown"] = "已投擲 {0} 件物品",
				["NothingHeld"] = "手中沒有可投擲的物品"
			},
			["cs"] = new Dictionary<string, string>
			{
				["Thrown"] = "Vyhozeno předmětů: {0}",
				["NothingHeld"] = "V rukou nemáte nic k hození"
			},
			["fi"] = new Dictionary<string, string>
			{
				["Thrown"] = "{0} esine(ttä) heitetty",
				["NothingHeld"] = "Käsissäsi ei ole mitään heitettävää"
			},
			["de"] = new Dictionary<string, string>
			{
				["Thrown"] = "{0} Gegenstand/Gegenstände geworfen",
				["NothingHeld"] = "Nichts zum Werfen in den Händen"
			},
			["it"] = new Dictionary<string, string>
			{
				["Thrown"] = "{0} oggetto/i lanciato/i",
				["NothingHeld"] = "Niente da lanciare tra le mani"
			},
			["ja"] = new Dictionary<string, string>
			{
				["Thrown"] = "アイテムを {0} 個投げた",
				["NothingHeld"] = "投げるものを手に持っていません"
			},
			["ko"] = new Dictionary<string, string>
			{
				["Thrown"] = "아이템 {0}개 투척됨",
				["NothingHeld"] = "손에 던질 물건이 없습니다"
			},
			["pl"] = new Dictionary<string, string>
			{
				["Thrown"] = "Rzucono przedmiotów: {0}",
				["NothingHeld"] = "Nie masz w dłoniach nic do rzucenia"
			},
			["pt"] = new Dictionary<string, string>
			{
				["Thrown"] = "{0} item(ns) arremessado(s)",
				["NothingHeld"] = "Nada para arremessar nas mãos"
			},
			["ru"] = new Dictionary<string, string>
			{
				["Thrown"] = "Брошено предметов: {0}",
				["NothingHeld"] = "В руках нет ничего подходящего для броска"
			},
			["es"] = new Dictionary<string, string>
			{
				["Thrown"] = "{0} objeto(s) lanzado(s)",
				["NothingHeld"] = "No tienes nada que lanzar en las manos"
			},
			["sv"] = new Dictionary<string, string>
			{
				["Thrown"] = "{0} föremål kastat/kastade",
				["NothingHeld"] = "Inget i händerna att kasta"
			},
			["tr"] = new Dictionary<string, string>
			{
				["Thrown"] = "{0} eşya fırlatıldı",
				["NothingHeld"] = "Ellerinde fırlatacak bir şey yok"
			}
		};

		public static string Get(string localeCode, string key)
		{
			string text = (localeCode ?? "en").ToLowerInvariant();
			string key2 = (ByLanguage.ContainsKey(text) ? text : text.Split('-')[0]);
			if (!ByLanguage.TryGetValue(key2, out var value) || !value.TryGetValue(key, out var value2))
			{
				return ByLanguage["en"][key];
			}
			return value2;
		}

		public static void SelfCheck()
		{
			if (Get("fr-FR", "Thrown") != ByLanguage["fr"]["Thrown"] || Get("xx", "NothingHeld") != ByLanguage["en"]["NothingHeld"] || Get(null, "Thrown") != ByLanguage["en"]["Thrown"])
			{
				throw new InvalidOperationException("Texts lookup is wrong.");
			}
		}
	}
}