Decompiled source of Deproliferation v0.1.1

Deproliferation.dll

Decompiled a day ago
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CatAPI;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[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;
		}
	}
}
internal static class PluginInfo
{
	public const string PLUGIN_GUID = "com.yummi.dsp.deproliferation";

	public const string PLUGIN_NAME = "Deproliferation";

	public const string PLUGIN_VERSION = "0.1.1";
}
namespace Deproliferation
{
	[HarmonyPatch]
	internal static class AssemblerPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(AssemblerComponent), "InternalUpdate")]
		[Guard("470e8622e2ea8ccc")]
		public static void InternalUpdate_Prefix(ref AssemblerComponent __instance, float power)
		{
			if (power < 0.1f || !Chance.Enabled)
			{
				return;
			}
			RecipeExecuteData recipeExecuteData = __instance.recipeExecuteData;
			if (recipeExecuteData == null)
			{
				return;
			}
			if (__instance.extraTime >= recipeExecuteData.extraTimeSpend && Chance.Roll() != Outcome.Success)
			{
				__instance.extraTime -= recipeExecuteData.extraTimeSpend;
			}
			if (__instance.time >= recipeExecuteData.timeSpend && !OutputFull(ref __instance, recipeExecuteData.productCounts))
			{
				Outcome outcome = Chance.Roll();
				Feedback.Report(__instance.produced, outcome);
				switch (outcome)
				{
				case Outcome.Fail:
					__instance.time -= recipeExecuteData.timeSpend;
					__instance.replicating = true;
					break;
				case Outcome.Meltdown:
					__instance.replicating = false;
					__instance.extraSpeed = 0;
					__instance.speedOverride = __instance.speed;
					__instance.extraPowerRatio = 0;
					__instance.time -= recipeExecuteData.timeSpend;
					break;
				}
			}
		}

		private static bool OutputFull(ref AssemblerComponent a, int[] productCounts)
		{
			//IL_000c: 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)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			int[] produced = a.produced;
			for (int i = 0; i < productCounts.Length; i++)
			{
				ERecipeType recipeType = a.recipeType;
				if ((int)recipeType != 1)
				{
					if ((int)recipeType == 4)
					{
						if (produced[i] > productCounts[i] * 9)
						{
							return true;
						}
					}
					else if (produced[i] > productCounts[i] * 19)
					{
						return true;
					}
				}
				else if (produced[i] + productCounts[i] > 100)
				{
					return true;
				}
			}
			return false;
		}
	}
	internal enum Outcome
	{
		Success,
		Fail,
		Meltdown
	}
	internal static class Chance
	{
		private static int seed = Environment.TickCount;

		[ThreadStatic]
		private static Random rng;

		private static Random Rng => rng ?? (rng = new Random(Interlocked.Add(ref seed, 625341585)));

		internal static bool Enabled => Settings.FailPermille > 0;

		internal static Outcome Roll()
		{
			int failPermille = Settings.FailPermille;
			if (failPermille <= 0 || Rng.Next(1000) >= failPermille)
			{
				return Outcome.Success;
			}
			if (Rng.Next(1000) >= Settings.MeltdownPermille)
			{
				return Outcome.Fail;
			}
			return Outcome.Meltdown;
		}
	}
	internal static class Feedback
	{
		private const int MaxTipsPerFrame = 3;

		private const float HeadHeight = 3f;

		private static volatile int[] watchedProduced;

		private static volatile bool watchForge;

		private static readonly ConcurrentQueue<Outcome> pending = new ConcurrentQueue<Outcome>();

		internal static void Report(int[] produced, Outcome outcome)
		{
			if (produced != null && produced == watchedProduced)
			{
				pending.Enqueue(outcome);
			}
		}

		internal static void ReportForge(Outcome outcome)
		{
			if (watchForge)
			{
				pending.Enqueue(outcome);
			}
		}

		internal static void Tick()
		{
			UIGame val = ((!Settings.ShowTips || !GameMain.isRunning) ? null : UIRoot.instance?.uiGame);
			watchedProduced = (((Object)(object)val == (Object)null) ? null : FindWatched(val));
			watchForge = (Object)(object)val != (Object)null && (Object)(object)val.replicator != (Object)null && ((ManualBehaviour)val.replicator).active;
			int num = 0;
			Outcome result;
			while (pending.TryDequeue(out result))
			{
				if ((Object)(object)val != (Object)null && num < 3)
				{
					Show(result);
					num++;
				}
			}
		}

		private static int[] FindWatched(UIGame ui)
		{
			FactorySystem val = GameMain.localPlanet?.factory?.factorySystem;
			if (val == null)
			{
				return null;
			}
			UIAssemblerWindow assemblerWindow = ui.assemblerWindow;
			if ((Object)(object)assemblerWindow != (Object)null && ((ManualBehaviour)assemblerWindow).active)
			{
				int assemblerId = assemblerWindow.assemblerId;
				if (assemblerId > 0 && assemblerId < val.assemblerCursor && val.assemblerPool[assemblerId].id == assemblerId)
				{
					return val.assemblerPool[assemblerId].produced;
				}
			}
			UILabWindow labWindow = ui.labWindow;
			if ((Object)(object)labWindow != (Object)null && ((ManualBehaviour)labWindow).active)
			{
				int labId = labWindow.labId;
				if (labId > 0 && labId < val.labCursor && val.labPool[labId].id == labId && !val.labPool[labId].researchMode)
				{
					return val.labPool[labId].produced;
				}
			}
			return null;
		}

		private static void Show(Outcome outcome)
		{
			//IL_0018: 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_001f: 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_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)
			string text = Text(outcome);
			Player mainPlayer = GameMain.mainPlayer;
			if (mainPlayer != null && GameMain.localPlanet != null)
			{
				Vector3 position = mainPlayer.position;
				UIRealtimeTip.Popup(text, position + ((Vector3)(ref position)).normalized * 3f, false, 0);
			}
			else
			{
				UIRealtimeTip.Popup(text, false, 0);
			}
		}

		private static string Text(Outcome outcome)
		{
			bool isZHCN = Localization.isZHCN;
			switch (outcome)
			{
			case Outcome.Fail:
				if (!isZHCN)
				{
					return "Failed!";
				}
				return "失败!";
			case Outcome.Meltdown:
				if (!isZHCN)
				{
					return "Meltdown!";
				}
				return "融毁!";
			default:
				if (!isZHCN)
				{
					return "Success!";
				}
				return "成功!";
			}
		}
	}
	[HarmonyPatch]
	internal static class ForgePatch
	{
		internal static bool Ready;

		[ThreadStatic]
		private static bool inTick;

		[ThreadStatic]
		private static bool rolled;

		[ThreadStatic]
		private static Outcome outcome;

		[ThreadStatic]
		private static bool meltdownHappened;

		private static Outcome Current()
		{
			if (!rolled)
			{
				outcome = Chance.Roll();
				rolled = true;
			}
			return outcome;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(MechaForge), "GameTick")]
		[Guard("1b63527b1492f2a3")]
		public static void GameTick_Prefix(MechaForge __instance)
		{
			inTick = false;
			rolled = false;
			meltdownHappened = false;
			if (Ready && Chance.Enabled)
			{
				inTick = true;
				RefillOrCancel(__instance);
			}
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(MechaForge), "GameTick")]
		[Guard("1b63527b1492f2a3")]
		public static void GameTick_Finalizer(MechaForge __instance)
		{
			inTick = false;
			rolled = false;
			if (meltdownHappened && __instance.tasks != null)
			{
				__instance.CalculateExtra();
			}
			meltdownHappened = false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Mecha), "AddConsumptionStat")]
		[Guard("d43e4cb17b432c06")]
		public static bool AddConsumptionStat_Prefix()
		{
			if (inTick)
			{
				return Current() != Outcome.Fail;
			}
			return true;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Mecha), "AddProductionStat")]
		[Guard("b343fa55a107408d")]
		public static bool AddProductionStat_Prefix()
		{
			if (inTick)
			{
				return Current() == Outcome.Success;
			}
			return true;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ForgeTask), "Produce")]
		[Guard("76f145124b84fec0")]
		public static bool Produce_Prefix(ForgeTask __instance)
		{
			if (!inTick)
			{
				return true;
			}
			Outcome outcome = Current();
			rolled = false;
			Feedback.ReportForge(outcome);
			switch (outcome)
			{
			case Outcome.Fail:
				__instance.tick -= __instance.tickSpend;
				return false;
			case Outcome.Meltdown:
			{
				for (int i = 0; i < __instance.itemCounts.Length; i++)
				{
					__instance.served[i] -= __instance.itemCounts[i];
				}
				__instance.count--;
				__instance.tick -= __instance.tickSpend;
				meltdownHappened = true;
				return false;
			}
			default:
				return true;
			}
		}

		private static void RefillOrCancel(MechaForge forge)
		{
			if (forge.tasks == null || forge.tasks.Count == 0 || forge.player == null || !forge.player.isAlive)
			{
				return;
			}
			ForgeTask val = forge.tasks[0];
			if (val.itemEnough)
			{
				return;
			}
			StorageComponent package = forge.player.package;
			if (package != null)
			{
				int num4 = default(int);
				for (int i = 0; i < val.itemIds.Length; i++)
				{
					int num = val.itemCounts[i] * val.count - val.served[i];
					if (num > 0)
					{
						int num2 = val.itemIds[i];
						int num3 = num;
						package.TakeTailItems(ref num2, ref num3, ref num4, false);
						val.served[i] += num3;
					}
				}
			}
			if (!val.itemEnough)
			{
				forge.CancelTask(0);
				UIRealtimeTip.Popup(Localization.isZHCN ? "融毁损失了中间产物,背包里不够补,已取消该制造任务" : "A meltdown lost intermediate items and the inventory cannot cover it; the craft was cancelled", true, 0);
			}
			forge.CalculateExtra();
		}
	}
	[HarmonyPatch]
	internal static class LabPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(LabComponent), "InternalUpdateAssemble")]
		[Guard("614a888f1ca1f96c")]
		public static void InternalUpdateAssemble_Prefix(ref LabComponent __instance, float power)
		{
			if (power < 0.1f || !Chance.Enabled)
			{
				return;
			}
			RecipeExecuteData recipeExecuteData = __instance.recipeExecuteData;
			if (recipeExecuteData == null)
			{
				return;
			}
			if (__instance.extraTime >= recipeExecuteData.extraTimeSpend && Chance.Roll() != Outcome.Success)
			{
				__instance.extraTime -= recipeExecuteData.extraTimeSpend;
			}
			if (__instance.time >= recipeExecuteData.timeSpend && !OutputFull(ref __instance, recipeExecuteData.productCounts))
			{
				Outcome outcome = Chance.Roll();
				Feedback.Report(__instance.produced, outcome);
				switch (outcome)
				{
				case Outcome.Fail:
					__instance.time -= recipeExecuteData.timeSpend;
					__instance.replicating = true;
					break;
				case Outcome.Meltdown:
					__instance.replicating = false;
					__instance.extraSpeed = 0;
					__instance.speedOverride = __instance.speed;
					__instance.extraPowerRatio = 0;
					__instance.time -= recipeExecuteData.timeSpend;
					break;
				}
			}
		}

		private static bool OutputFull(ref LabComponent lab, int[] productCounts)
		{
			int num = 10 * ((lab.speedOverride + 9999) / 10000);
			for (int i = 0; i < productCounts.Length; i++)
			{
				if (lab.produced[i] + productCounts[i] > num)
				{
					return true;
				}
			}
			return false;
		}
	}
	[BepInPlugin("com.yummi.dsp.deproliferation", "Deproliferation", "0.1.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.yummi.dsp.deproliferation";

		public const string PluginName = "Deproliferation";

		public const string PluginVersion = "0.1.1";

		private static readonly Harmony harmony = new Harmony("com.yummi.dsp.deproliferation");

		private static readonly PatchOptions HarnessOptions = new PatchOptions
		{
			ModName = "Deproliferation"
		};

		internal static ManualLogSource Log;

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			Settings.Bind(((BaseUnityPlugin)this).Config);
			Harness.PatchAll(harmony, typeof(AssemblerPatch), HarnessOptions);
			Harness.PatchAll(harmony, typeof(LabPatch), HarnessOptions);
			Harness.PatchAll(harmony, typeof(ForgePatch), HarnessOptions);
			ForgePatch.Ready = IsPatched(AccessTools.Method(typeof(MechaForge), "GameTick", (Type[])null, (Type[])null)) && IsPatched(AccessTools.Method(typeof(ForgeTask), "Produce", (Type[])null, (Type[])null)) && IsPatched(AccessTools.Method(typeof(Mecha), "AddProductionStat", (Type[])null, (Type[])null)) && IsPatched(AccessTools.Method(typeof(Mecha), "AddConsumptionStat", (Type[])null, (Type[])null));
			if (!ForgePatch.Ready)
			{
				Log.LogWarning((object)"机甲制造台的补丁没有全部应用,手搓制造不会失败。");
			}
			Log.LogInfo((object)string.Format("{0} v{1} 已加载:失败率 {2}‰,失败中融毁占 {3}‰", "Deproliferation", "0.1.1", Settings.FailPermille, Settings.MeltdownPermille));
		}

		private void Update()
		{
			Feedback.Tick();
		}

		private static bool IsPatched(MethodBase method)
		{
			Patches val = ((method == null) ? null : Harmony.GetPatchInfo(method));
			if (val != null)
			{
				return val.Owners.Contains("com.yummi.dsp.deproliferation");
			}
			return false;
		}

		private void OnDestroy()
		{
			harmony.UnpatchSelf();
		}
	}
	internal static class Settings
	{
		internal const int PermilleMax = 1000;

		internal const int DefaultPermille = 500;

		internal const int ConfigSchemaVersion = 1;

		private static ConfigEntry<int> failPermille;

		private static ConfigEntry<int> meltdownPermille;

		private static ConfigEntry<bool> showTips;

		internal static volatile int FailPermille;

		internal static volatile int MeltdownPermille;

		internal static bool ShowTips => showTips.Value;

		private static string Desc(string en, string zh)
		{
			return en + "\n" + zh;
		}

		internal static void Bind(ConfigFile config)
		{
			//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_0018: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			Harness.ApplyConfigSchema(config, 1, new ConfigSchemaOptions
			{
				WipeIfBelow = 1
			});
			AcceptableValueRange<int> val = new AcceptableValueRange<int>(0, 1000);
			failPermille = config.Bind<int>("Chance", "FailPermille", 500, new ConfigDescription(Desc("Chance (per mille, integer 0-1000) that a finished craft fails. 500 = half.", "每次制造完成时失败的概率(千分比,整数 0–1000)。500 即一半。"), (AcceptableValueBase)(object)val, Array.Empty<object>()));
			meltdownPermille = config.Bind<int>("Chance", "MeltdownPermille", 500, new ConfigDescription(Desc("Of the failed crafts, the chance (per mille, integer 0-1000) that it is a meltdown. Failure: no product, ingredients stay and the craft retries. Meltdown: no product and the ingredients are lost.", "在已经失败的制造里,变成融毁的概率(千分比,整数 0–1000)。失败:没有产物,原料还在,自动重做。融毁:没有产物,原料也消失。"), (AcceptableValueBase)(object)val, Array.Empty<object>()));
			showTips = config.Bind<bool>("Feedback", "ShowTips", true, Desc("While a machine's window (or the replicator window) is open, pop Success! / Failed! / Meltdown! above Icarus on each craft.", "打开某台机器的面板(或机甲制造台面板)时,它每制造一次就在伊卡洛斯头顶弹出 成功!/ 失败!/ 融毁!"));
			Harness.PruneConfigKeys(config, (ConfigDefinition[])(object)new ConfigDefinition[4]
			{
				((ConfigEntryBase)failPermille).Definition,
				((ConfigEntryBase)meltdownPermille).Definition,
				((ConfigEntryBase)showTips).Definition,
				new ConfigDefinition("_ConfigSchema", "Version")
			});
			Refresh();
			failPermille.SettingChanged += delegate
			{
				Refresh();
			};
			meltdownPermille.SettingChanged += delegate
			{
				Refresh();
			};
		}

		private static void Refresh()
		{
			FailPermille = Clamp(failPermille.Value);
			MeltdownPermille = Clamp(meltdownPermille.Value);
		}

		private static int Clamp(int value)
		{
			if (value < 0)
			{
				return 0;
			}
			if (value <= 1000)
			{
				return value;
			}
			return 1000;
		}
	}
}