Decompiled source of BUG DroneCoverage v1.0.2

BUG_DroneCoverage.dll

Decompiled 9 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.2.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;
		}
	}
}
namespace DroneCoverage
{
	[BepInPlugin("bug.dsp.dronecoverage", "BUG_DroneCoverage", "1.0.2")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "bug.dsp.dronecoverage";

		private static GameData appliedFor;

		private static int frame;

		private ConfigEntry<bool> reset;

		private DateTime cfgTime;

		private void Awake()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			Coverage.Log = ((BaseUnityPlugin)this).Logger;
			reset = ((BaseUnityPlugin)this).Config.Bind<bool>("Reset", "ResetResearch", false, "Set to true to reset the Drone Coverage upgrades of the running (or next loaded) game to stage 0, so they can be researched again. Spent cubes are not refunded. Turns itself back off.");
			cfgTime = CfgTime();
			new Harmony("bug.dsp.dronecoverage").PatchAll(typeof(Coverage));
		}

		private DateTime CfgTime()
		{
			try
			{
				return File.GetLastWriteTimeUtc(((BaseUnityPlugin)this).Config.ConfigFilePath);
			}
			catch
			{
				return default(DateTime);
			}
		}

		private void Update()
		{
			if (GameMain.isRunning && GameMain.data != null && (Coverage.Dirty || appliedFor != GameMain.data || ++frame % 60 == 0))
			{
				DateTime dateTime = CfgTime();
				if (dateTime != cfgTime)
				{
					cfgTime = dateTime;
					((BaseUnityPlugin)this).Config.Reload();
				}
				if (reset.Value && GameMain.history != null)
				{
					Coverage.ResetResearch(GameMain.history);
					reset.Value = false;
					cfgTime = CfgTime();
				}
				Coverage.Dirty = false;
				appliedFor = GameMain.data;
				Coverage.Apply();
			}
		}
	}
	public static class Coverage
	{
		public class Scaled
		{
			public float Base = -1f;

			public float Set = -1f;
		}

		public const int MaxStage = 10;

		public const int Base = 9350;

		public const float Step = 0.25f;

		public const float PlanetWide = 10000f;

		private const string Name = "Drone Coverage";

		private const string Desc = "Widens the area the construction and repair drones of the mecha and of battlefield analysis bases cover, by 25% of the original range per stage. Stage 10 lets them work across the whole planet.";

		private const string Conclusion = "Construction and repair drones cover a wider area.";

		private const string LastName = "Drone Coverage: Planet-wide";

		private const string LastConclusion = "Construction and repair drones now cover the whole planet.";

		internal static ManualLogSource Log;

		public static TechProto[] Tiers;

		public static volatile bool Dirty;

		private static readonly ConditionalWeakTable<object, Scaled> scaled = new ConditionalWeakTable<object, Scaled>();

		[ThreadStatic]
		private static int searching;

		public static int Stage
		{
			get
			{
				Dictionary<int, TechState> states = GameMain.history?.techStates;
				TechState value;
				return Done((int id) => (states == null || !states.TryGetValue(id, out value)) ? ((TechState?)null) : new TechState?(value));
			}
		}

		public static float Range(float baseRange, int stage)
		{
			if (baseRange <= 0f || stage <= 0)
			{
				return baseRange;
			}
			return Math.Max(baseRange, (stage >= 10) ? 10000f : (baseRange * (1f + 0.25f * (float)stage)));
		}

		public static int Cubes(int stage)
		{
			if (stage < 10)
			{
				if (stage != 9)
				{
					return (stage + 1) / 2;
				}
				return 5;
			}
			return 6;
		}

		public static List<int[]> TierRanges()
		{
			List<int[]> list = new List<int[]>();
			for (int i = 1; i <= 10; i++)
			{
				if (list.Count > 0 && Cubes(list[list.Count - 1][0]) == Cubes(i))
				{
					list[list.Count - 1][1] = i;
					continue;
				}
				list.Add(new int[2] { i, i });
			}
			return list;
		}

		public static int Done(Func<int, TechState?> state)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			if (Tiers == null)
			{
				return num;
			}
			TechProto[] tiers = Tiers;
			foreach (TechProto val in tiers)
			{
				TechState? val2 = state(((Proto)val).ID);
				if (val2.HasValue)
				{
					if (val2.Value.unlocked)
					{
						num = Math.Max(num, val.MaxLevel);
					}
					else if (val2.Value.curLevel > val.Level)
					{
						num = Math.Max(num, val2.Value.curLevel - 1);
					}
				}
			}
			return num;
		}

		public static float Raise(object owner, float current, int stage)
		{
			Scaled orCreateValue = scaled.GetOrCreateValue(owner);
			if (orCreateValue.Set < 0f || current != orCreateValue.Set)
			{
				orCreateValue.Base = current;
			}
			orCreateValue.Set = Range(orCreateValue.Base, stage);
			return orCreateValue.Set;
		}

		public static bool SavedBase(object owner, float current, out float value)
		{
			value = current;
			if (!scaled.TryGetValue(owner, out var value2) || value2.Set < 0f || current != value2.Set || value2.Base == current)
			{
				return false;
			}
			value = value2.Base;
			return true;
		}

		public static void ResetResearch(GameHistoryData history)
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			if (Tiers == null || Tiers.Length == 0)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)"Reset skipped: upgrade techs are not registered");
				}
				return;
			}
			int stage = Stage;
			for (int num = history.techQueue.Length - 1; num >= 0; num--)
			{
				int num2 = history.techQueue[num];
				if (num2 > 9350 && num2 <= 9350 + Tiers.Length)
				{
					history.RemoveTechInQueue(num);
				}
			}
			TechProto[] tiers = Tiers;
			foreach (TechProto val in tiers)
			{
				history.techStates[((Proto)val).ID] = new TechState(false, val.Level, val.MaxLevel, 0L, val.GetHashNeeded(val.Level), 0);
			}
			Dirty = true;
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogInfo((object)("Drone Coverage research reset: stage " + stage + " -> " + Stage));
			}
		}

		public static void Apply()
		{
			int stage = Stage;
			Player mainPlayer = GameMain.mainPlayer;
			Mecha val = ((mainPlayer != null) ? mainPlayer.mecha : null);
			if (val != null)
			{
				float num = Raise(val, val.buildArea, stage);
				if (num != val.buildArea)
				{
					val.buildArea = num;
					if (val.constructionModule != null)
					{
						val.constructionModule.prebuildDistribution = null;
					}
					val.buildLastSearchAstroId = 0;
					val.repairLastSearchAstroId = 0;
					val.autoReconstructLastSearchAstroId = 0;
				}
			}
			GameData data = GameMain.data;
			if (data?.factories == null)
			{
				return;
			}
			for (int i = 0; i < data.factoryCount; i++)
			{
				ObjectPool<ConstructionModuleComponent> val2 = (data.factories[i]?.constructionSystem)?.constructionModules;
				if (val2?.buffer == null)
				{
					continue;
				}
				for (int j = 1; j < val2.cursor; j++)
				{
					ConstructionModuleComponent val3 = val2.buffer[j];
					if (val3 != null && val3.id == j && val3.entityId > 0)
					{
						float num2 = Raise(val3, val3.baseBuildRange, stage);
						if (num2 != val3.baseBuildRange)
						{
							val3.baseBuildRange = num2;
							val3.prebuildDistribution = null;
						}
					}
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Mecha), "Export")]
		private static void MechaExportPrefix(Mecha __instance, out float __state)
		{
			__state = -1f;
			if (SavedBase(__instance, __instance.buildArea, out var value))
			{
				__state = __instance.buildArea;
				__instance.buildArea = value;
			}
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(Mecha), "Export")]
		private static void MechaExportRestore(Mecha __instance, float __state)
		{
			if (__state >= 0f)
			{
				__instance.buildArea = __state;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ConstructionModuleComponent), "Export")]
		private static void ModuleExportPrefix(ConstructionModuleComponent __instance, out float __state)
		{
			__state = -1f;
			if (SavedBase(__instance, __instance.baseBuildRange, out var value))
			{
				__state = __instance.baseBuildRange;
				__instance.baseBuildRange = value;
			}
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(ConstructionModuleComponent), "Export")]
		private static void ModuleExportRestore(ConstructionModuleComponent __instance, float __state)
		{
			if (__state >= 0f)
			{
				__instance.baseBuildRange = __state;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(GameHistoryData), "NotifyTechUnlock")]
		private static void Unlocked()
		{
			Dirty = true;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ConstructionModuleComponent), "SearchBuildTargets")]
		[HarmonyPatch(typeof(ConstructionModuleComponent), "SearchRepairTargets")]
		[HarmonyPatch(typeof(ConstructionModuleComponent), "SearchAutoReconstructTargets")]
		[HarmonyPatch(typeof(ConstructionModuleComponent), "AutoReconstructAllInstantly")]
		[HarmonyPatch(typeof(ConstructionModuleComponent), "PlaceItems")]
		private static void SearchEnter()
		{
			searching++;
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(ConstructionModuleComponent), "SearchBuildTargets")]
		[HarmonyPatch(typeof(ConstructionModuleComponent), "SearchRepairTargets")]
		[HarmonyPatch(typeof(ConstructionModuleComponent), "SearchAutoReconstructTargets")]
		[HarmonyPatch(typeof(ConstructionModuleComponent), "AutoReconstructAllInstantly")]
		[HarmonyPatch(typeof(ConstructionModuleComponent), "PlaceItems")]
		private static void SearchLeave()
		{
			searching--;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(HashSystem), "GetBucketIdxesInArea", new Type[]
		{
			typeof(Vector3),
			typeof(float)
		})]
		private static bool AllBuckets(HashSystem __instance, Vector3 center, float areaRadius)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if (searching == 0 || HashSystem.bucketMap == null)
			{
				return true;
			}
			if (!NeedsAll(center, areaRadius))
			{
				return true;
			}
			int bucketCount = HashSystem.bucketCount;
			int[] activeBuckets = __instance.activeBuckets;
			if (activeBuckets == null || activeBuckets.Length < bucketCount)
			{
				return true;
			}
			for (int i = 0; i < bucketCount; i++)
			{
				activeBuckets[i] = i;
			}
			__instance.activeBucketsCount = bucketCount;
			return false;
		}

		public static bool NeedsAll(Vector3 center, float areaRadius)
		{
			//IL_0006: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			float cellSize = HashSystem.cellSize;
			int num = Mathf.Clamp((int)((center.x + 270f) / cellSize), 0, 99);
			int num2 = Mathf.Clamp((int)((center.y + 270f) / cellSize), 0, 99);
			int num3 = Mathf.Clamp((int)((center.z + 270f) / cellSize), 0, 99) * 10000 + num2 * 100 + num << 5;
			return (float)HashSystem.bucketMap[num3 + 31].dist <= areaRadius;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(VFPreload), "InvokeOnLoadWorkEnded")]
		[HarmonyAfter(new string[] { "me.xiaoye97.plugin.Dyson.LDBTool", "dsp.common-api.CommonAPI", "org.LoShin.GenesisBook", "bug.dsp.deliveryitemchain" })]
		private static void Register()
		{
			if (Tiers != null)
			{
				return;
			}
			try
			{
				Build();
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogError((object)("Upgrade techs not added: " + ex));
				}
				Tiers = (TechProto[])(object)new TechProto[0];
			}
		}

		private static void Build()
		{
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Expected O, but got Unknown
			//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: Unknown result type (might be due to invalid IL or missing references)
			TechProtoSet techs = LDB.techs;
			List<int[]> list = TierRanges();
			for (int i = 0; i < list.Count; i++)
			{
				if (((ProtoSet<TechProto>)(object)techs).Exist(9350 + i + 1))
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)("Tech ids " + 9351 + "-" + (9350 + list.Count) + " are taken by another mod (" + (9350 + i + 1) + "), upgrades not added"));
					}
					Tiers = (TechProto[])(object)new TechProto[0];
					return;
				}
			}
			int num = MechaDroneTech();
			Layout(((ProtoSet<TechProto>)(object)techs).dataArray, out var x, out var dx, out var y);
			TechProto[] array = (TechProto[])(object)new TechProto[list.Count];
			for (int j = 0; j < list.Count; j++)
			{
				int num2 = Cubes(list[j][0]);
				int[] array2 = new int[num2];
				int[] array3 = new int[num2];
				for (int k = 0; k < num2; k++)
				{
					array2[k] = TechProto.matrixIds[k];
					array3[k] = 20;
				}
				bool flag = list[j][1] >= 10;
				int num3 = j;
				TechProto val = new TechProto();
				((Proto)val).ID = 9350 + j + 1;
				((Proto)val).Name = (flag ? "Drone Coverage: Planet-wide" : "Drone Coverage");
				val.Desc = "Widens the area the construction and repair drones of the mecha and of battlefield analysis bases cover, by 25% of the original range per stage. Stage 10 lets them work across the whole planet.";
				val.Conclusion = (flag ? "Construction and repair drones now cover the whole planet." : "Construction and repair drones cover a wider area.");
				val.Published = true;
				val.IsLabTech = true;
				val.IconPath = "";
				val.IconTag = "";
				val.PreTechs = ((j == 0) ? new int[0] : new int[1] { ((Proto)array[j - 1]).ID });
				val.PreTechsImplicit = ((j == 0 && num > 0) ? new int[1] { num } : new int[0]);
				val.PreItem = new int[0];
				val.Items = array2;
				val.ItemPoints = array3;
				val.HashNeeded = 0L;
				val.LevelCoef1 = (flag ? 240000 : 18000);
				val.LevelCoef2 = (flag ? 240000 : 18000);
				val.Level = list[j][0];
				val.MaxLevel = list[j][1];
				val.UnlockRecipes = new int[0];
				val.UnlockFunctions = new int[0];
				val.UnlockValues = new double[0];
				val.AddItems = new int[0];
				val.AddItemCounts = new int[0];
				val.PropertyOverrideItems = new int[0];
				val.PropertyItemCounts = new int[0];
				val.PropertyOverrideItemArray = (IDCNT[])(object)new IDCNT[0];
				val.Position = new Vector2(x + (float)j * dx, y);
				array[num3] = val;
			}
			TechProto[] dataArray = ((ProtoSet<TechProto>)(object)techs).dataArray;
			((ProtoTable)techs).Init(dataArray.Length + array.Length);
			Array.Copy(dataArray, ((ProtoSet<TechProto>)(object)techs).dataArray, dataArray.Length);
			for (int l = 0; l < array.Length; l++)
			{
				((ProtoSet<TechProto>)(object)techs).dataArray[dataArray.Length + l] = array[l];
			}
			Dictionary<int, int> dictionary = new Dictionary<int, int>();
			for (int m = 0; m < ((ProtoSet<TechProto>)(object)techs).dataArray.Length; m++)
			{
				dictionary[((Proto)((ProtoSet<TechProto>)(object)techs).dataArray[m]).ID] = m;
			}
			Traverse.Create((object)techs).Field("dataIndices").SetValue((object)dictionary);
			if (((ProtoSet<TechProto>)(object)techs).Select(((Proto)array[0]).ID) != array[0])
			{
				throw new InvalidOperationException("tech index not rebuilt");
			}
			Sprite val2 = LoadIcon("tech-coverage.png");
			TechProto[] array4 = array;
			for (int n = 0; n < array4.Length; n++)
			{
				array4[n].Preload();
			}
			array4 = array;
			foreach (TechProto obj in array4)
			{
				obj.Preload2();
				Traverse.Create((object)obj).Field("_iconSprite").SetValue((object)val2);
			}
			Tiers = array;
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogInfo((object)("Added " + array.Length + " drone coverage upgrade techs (ids " + ((Proto)array[0]).ID + "-" + ((Proto)array[^1]).ID + ")" + (((Object)(object)val2 == (Object)null) ? ", icon missing" : "")));
			}
		}

		private static int MechaDroneTech()
		{
			int num = 0;
			TechProto[] dataArray = ((ProtoSet<TechProto>)(object)LDB.techs).dataArray;
			foreach (TechProto val in dataArray)
			{
				if (val?.UnlockFunctions != null && Array.IndexOf(val.UnlockFunctions, 1) >= 0 && (num == 0 || ((Proto)val).ID < num))
				{
					num = ((Proto)val).ID;
				}
			}
			return num;
		}

		public static void Layout(TechProto[] techs, out float x0, out float dx, out float y0)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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_005e: 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_0074: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			float num = float.MaxValue;
			Dictionary<float, float> dictionary = new Dictionary<float, float>();
			SortedSet<float> sortedSet = new SortedSet<float>();
			SortedSet<float> sortedSet2 = new SortedSet<float>();
			foreach (TechProto val in techs)
			{
				if (val != null && val.page == 1)
				{
					Vector2 position = val.Position;
					num = Math.Min(num, position.y);
					sortedSet.Add(position.y);
					sortedSet2.Add(position.x);
					dictionary[position.y] = (dictionary.TryGetValue(position.y, out var value) ? Math.Min(value, position.x) : position.x);
				}
			}
			dx = CommonGap(sortedSet2, 4f);
			float num2 = CommonGap(sortedSet, 4f);
			x0 = Mode(dictionary.Values, 0f);
			y0 = ((num == float.MaxValue) ? 0f : num) - num2;
		}

		private static float CommonGap(SortedSet<float> values, float fallback)
		{
			List<float> list = new List<float>();
			float num = float.NaN;
			foreach (float value in values)
			{
				if (!float.IsNaN(num) && value - num > 0.5f)
				{
					list.Add(value - num);
				}
				num = value;
			}
			return Mode(list, fallback);
		}

		private static float Mode(IEnumerable<float> values, float fallback)
		{
			Dictionary<float, int> dictionary = new Dictionary<float, int>();
			float num = fallback;
			int num2 = 0;
			foreach (float value2 in values)
			{
				dictionary[value2] = ((!dictionary.TryGetValue(value2, out var value)) ? 1 : (value + 1));
				if (dictionary[value2] > num2 || (dictionary[value2] == num2 && value2 < num))
				{
					num2 = dictionary[value2];
					num = value2;
				}
			}
			return num;
		}

		private static Sprite LoadIcon(string name)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			using Stream stream = typeof(Coverage).Assembly.GetManifestResourceStream(name);
			if (stream == null)
			{
				return null;
			}
			byte[] array = new byte[stream.Length];
			stream.Read(array, 0, array.Length);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true);
			ImageConversion.LoadImage(val, array);
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
		}
	}
}