Decompiled source of Lone Company v1.2.1

Lone_Company.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyVersion("0.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 Lone_Company
{
	[BepInPlugin("lone.company.Lone_Company", "Lone_Company", "1.2.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "lone.company.Lone_Company";

		public const string Name = "Lone_Company";

		public const string Version = "1.2.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<string> Mode;

		internal static ConfigEntry<string> Scaling;

		internal static ConfigEntry<float> ManualMultiplier;

		internal static ConfigEntry<string> EnemyAI;

		internal static int SpawnRound;

		private void Awake()
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Expected O, but got Unknown
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Mode = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Mode", "Value", new ConfigDescription("Value = multiplies how much scrap is worth.\nAmount = multiplies how much scrap spawns.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Value", "Amount" }), Array.Empty<object>()));
			Scaling = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Scaling", "Solo", new ConfigDescription("Manual = always use ManualMultiplier.\nAdaptive = ManualMultiplier divided by player count (min x1). Example: x4 = 1 player x4, 2 players x2, 4 players x1.\nSolo = use ManualMultiplier only when playing alone.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "Manual", "Adaptive", "Solo" }), Array.Empty<object>()));
			ManualMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ManualMultiplier", 2f, new ConfigDescription("Your base multiplier. Adaptive spreads it across the player count.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 100f), Array.Empty<object>()));
			EnemyAI = ((BaseUnityPlugin)this).Config.Bind<string>("Enemy AI", "EnemyAI", "On", new ConfigDescription("On = enemy multiplayer stats and AI are retained (snareflea no retries/butler with more than 2 health)\nOff = enemy solo stats (nicer snareflea/2 hp butler)", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "On", "Off" }), Array.Empty<object>()));
			Harmony harmony = new Harmony("lone.company.Lone_Company");
			Try("scrap patch", delegate
			{
				harmony.PatchAll();
			});
			Try("SID patch", delegate
			{
				SidFix.Apply(harmony);
			});
			Try("enemy patch", delegate
			{
				EnemyAIPlayerCount.PatchAllEnemies(harmony);
			});
			Log.LogInfo((object)("Lone_Company 1.2.1 loaded. Mode=" + Mode.Value + ", Scaling=" + Scaling.Value + ", EnemyAI=" + EnemyAI.Value));
		}

		private static void Try(string what, Action a)
		{
			try
			{
				a();
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to apply {what}: {arg}");
			}
		}

		internal static float GetMultiplier()
		{
			int totalPlayers = GameAccess.TotalPlayers;
			string value = Scaling.Value;
			string text = value;
			if (!(text == "Manual"))
			{
				if (text == "Solo")
				{
					return (totalPlayers == 1) ? ManualMultiplier.Value : 1f;
				}
				return Math.Max(1f, ManualMultiplier.Value / (float)totalPlayers);
			}
			return ManualMultiplier.Value;
		}
	}
	[HarmonyPatch(typeof(RoundManager), "SpawnScrapInLevel")]
	internal static class SpawnScrapPatch
	{
		private static float savedValue;

		private static float savedAmount;

		private static bool applied;

		[HarmonyPrefix]
		private static void Prefix(RoundManager __instance)
		{
			applied = false;
			Plugin.SpawnRound++;
			try
			{
				if (!((NetworkBehaviour)__instance).IsHost)
				{
					return;
				}
				float multiplier = Plugin.GetMultiplier();
				if (!(Math.Abs(multiplier - 1f) < 0.001f))
				{
					savedValue = __instance.scrapValueMultiplier;
					savedAmount = __instance.scrapAmountMultiplier;
					if (Plugin.Mode.Value == "Value")
					{
						__instance.scrapValueMultiplier = savedValue * multiplier;
					}
					else
					{
						__instance.scrapAmountMultiplier = savedAmount * multiplier;
					}
					applied = true;
					Plugin.Log.LogInfo((object)$"{Plugin.Mode.Value} mode: x{multiplier:0.##} (value mult {savedValue:0.###} -> {__instance.scrapValueMultiplier:0.###}, amount mult {savedAmount:0.###} -> {__instance.scrapAmountMultiplier:0.###})");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("SpawnScrap prefix failed (level will load normally): " + ex));
			}
		}

		[HarmonyFinalizer]
		private static void Finalizer(RoundManager __instance)
		{
			try
			{
				if (applied)
				{
					__instance.scrapValueMultiplier = savedValue;
					__instance.scrapAmountMultiplier = savedAmount;
					applied = false;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("SpawnScrap finalizer failed: " + ex));
			}
		}
	}
	internal static class EnemyAIPlayerCount
	{
		private static int depth;

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

		internal static void Prefix(out bool __state)
		{
			__state = false;
			try
			{
				if (Plugin.EnemyAI.Value != "On")
				{
					return;
				}
				if (depth == 0)
				{
					if (GameAccess.ConnectedPlayers != 0)
					{
						return;
					}
					GameAccess.ConnectedPlayers = 1;
				}
				depth++;
				__state = true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Enemy AI prefix failed: " + ex.Message));
			}
		}

		internal static void Finalizer(bool __state, Exception __exception, MethodBase __originalMethod)
		{
			if (__exception != null && __originalMethod != null)
			{
				string text = __originalMethod.DeclaringType?.Name + "." + __originalMethod.Name;
				if (reported.Add(text))
				{
					Plugin.Log.LogWarning((object)("Enemy AI: exception inside " + text + " while multiplayer count was faked: " + __exception.GetType().Name + ": " + __exception.Message));
				}
			}
			if (!__state)
			{
				return;
			}
			try
			{
				depth--;
				if (depth == 0)
				{
					GameAccess.ConnectedPlayers = 0;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Enemy AI finalizer failed: " + ex.Message));
			}
		}

		internal static void PatchAllEnemies(Harmony harmony)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			HarmonyMethod val = new HarmonyMethod(typeof(EnemyAIPlayerCount).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic));
			HarmonyMethod val2 = new HarmonyMethod(typeof(EnemyAIPlayerCount).GetMethod("Finalizer", BindingFlags.Static | BindingFlags.NonPublic));
			int num = 0;
			int num2 = 0;
			Type[] types = typeof(EnemyAI).Assembly.GetTypes();
			foreach (Type type in types)
			{
				if ((type != typeof(EnemyAI) && !type.IsSubclassOf(typeof(EnemyAI))) || type.ContainsGenericParameters)
				{
					continue;
				}
				num++;
				MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (!methodInfo.IsAbstract && !methodInfo.IsGenericMethod && !methodInfo.IsSpecialName && methodInfo.GetMethodBody() != null)
					{
						try
						{
							harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, val2, (HarmonyMethod)null);
							num2++;
						}
						catch (Exception ex)
						{
							Plugin.Log.LogDebug((object)("Skipped " + type.Name + "." + methodInfo.Name + ": " + ex.Message));
						}
					}
				}
			}
			Plugin.Log.LogInfo((object)$"Enemy AI: hooked {num2} methods on {num} enemy types");
		}
	}
	internal static class GameAccess
	{
		private static readonly Type RoundType = typeof(StartOfRound);

		private static FieldInfo instField;

		private static FieldInfo countField;

		private static PropertyInfo instProp;

		private static PropertyInfo countProp;

		private static bool resolved;

		internal static object Round
		{
			get
			{
				Resolve();
				try
				{
					return (instProp != null) ? instProp.GetValue(null, null) : instField?.GetValue(null);
				}
				catch
				{
					return null;
				}
			}
		}

		internal static int ConnectedPlayers
		{
			get
			{
				Resolve();
				object round = Round;
				if (round == null)
				{
					return -1;
				}
				try
				{
					return (int)((countProp != null) ? countProp.GetValue(round, null) : countField.GetValue(round));
				}
				catch
				{
					return -1;
				}
			}
			set
			{
				Resolve();
				object round = Round;
				if (round == null)
				{
					return;
				}
				try
				{
					if (countProp != null)
					{
						countProp.SetValue(round, value, null);
					}
					else
					{
						countField?.SetValue(round, value);
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Failed to set player count: " + ex.Message));
				}
			}
		}

		internal static int TotalPlayers => (ConnectedPlayers < 0) ? 1 : (ConnectedPlayers + 1);

		private static void Resolve()
		{
			if (!resolved)
			{
				resolved = true;
				instProp = RoundType.GetProperty("Instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				instField = RoundType.GetField("Instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				countProp = RoundType.GetProperty("connectedPlayersAmount", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				countField = RoundType.GetField("connectedPlayersAmount", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if ((instProp == null && instField == null) || (countProp == null && countField == null))
				{
					Plugin.Log.LogWarning((object)"Could not find StartOfRound.Instance / connectedPlayersAmount; player-count features disabled.");
				}
			}
		}
	}
	internal static class SidFix
	{
		private static int lastRound = -1;

		internal static void Apply(Harmony harmony)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(RoundManager), "SyncScrapValuesClientRpc", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Log.LogWarning((object)"SID fix: SyncScrapValuesClientRpc not found, SID won't be multiplied.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(SidFix).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static void Prefix(RoundManager __instance, object[] __args)
		{
			try
			{
				if (!((NetworkBehaviour)__instance).IsHost || Plugin.Mode.Value != "Value" || lastRound == Plugin.SpawnRound)
				{
					return;
				}
				object obj = AccessTools.Field(((object)__instance).GetType(), "__rpc_exec_stage")?.GetValue(__instance);
				if (obj != null && obj.ToString() == "Client")
				{
					return;
				}
				float multiplier = Plugin.GetMultiplier();
				if (Math.Abs(multiplier - 1f) < 0.001f)
				{
					return;
				}
				Array array = __args[0] as Array;
				int[] array2 = __args[1] as int[];
				if (array == null || array2 == null || array.Length < 3)
				{
					return;
				}
				string text = null;
				foreach (object item in array)
				{
					string text2 = ItemName(item);
					if (text2 == null)
					{
						return;
					}
					if (text == null)
					{
						text = text2;
					}
					else if (text2 != text)
					{
						return;
					}
				}
				lastRound = Plugin.SpawnRound;
				for (int i = 0; i < array2.Length; i++)
				{
					array2[i] = (int)Math.Round((float)array2[i] * multiplier);
				}
				Plugin.Log.LogInfo((object)$"Single item day ({text}): {array2.Length} items x{multiplier:0.##}");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("SID fix failed (scrap will sync normally): " + ex.Message));
			}
		}

		private static string ItemName(object netObjectRef)
		{
			object[] array = new object[2];
			MethodInfo method = netObjectRef.GetType().GetMethod("TryGet");
			if (method == null || !(bool)method.Invoke(netObjectRef, array))
			{
				return null;
			}
			object obj = array[0];
			Component val = (Component)((obj is Component) ? obj : null);
			Component val2 = ((val != null) ? val.GetComponent(typeof(GrabbableObject)) : null);
			if (val2 == null)
			{
				return null;
			}
			object obj2 = AccessTools.Field(((object)val2).GetType(), "itemProperties")?.GetValue(val2);
			return (obj2 == null) ? null : (AccessTools.Field(obj2.GetType(), "itemName")?.GetValue(obj2) as string);
		}
	}
}