Decompiled source of Selective Free Build v1.1.0

SelectiveFreeBuild.dll

Decompiled 14 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Managers;
using Jotunn.Utils;
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: AssemblyCompany("SelectiveFreeBuild")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+01a4c25f9bb8ca7ea72a6d92e88c66a0fdbafcc2")]
[assembly: AssemblyProduct("SelectiveFreeBuild")]
[assembly: AssemblyTitle("SelectiveFreeBuild")]
[assembly: AssemblyVersion("1.1.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 SelectiveFreeBuild
{
	[HarmonyPatch(typeof(Piece), "FreeBuildKey")]
	internal static class PieceFreeBuildKeyPatch
	{
		private static void Postfix(Piece __instance, ref GlobalKeys __result)
		{
			ZoneSystem instance = ZoneSystem.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				bool globalKey = instance.GetGlobalKey(__result);
				bool flag = !Rules.MustPay(__instance) && (globalKey || Rules.FreeWithoutModifier);
				if (flag != globalKey)
				{
					__result = (GlobalKeys)(flag ? (-9001) : (-9002));
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZoneSystem), "GetGlobalKey", new Type[] { typeof(GlobalKeys) })]
	internal static class ZoneSystemGetGlobalKeyPatch
	{
		private static bool Prefix(GlobalKeys key, ref bool __result)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			if ((int)key == -9002)
			{
				__result = false;
				return false;
			}
			if ((int)key == -9001)
			{
				__result = true;
				return false;
			}
			return true;
		}
	}
	internal static class Rules
	{
		internal const GlobalKeys AlwaysFree = (GlobalKeys)(-9001);

		internal const GlobalKeys NeverFree = (GlobalKeys)(-9002);

		private static readonly Dictionary<UsageTagFlags, string> MenuNames = new Dictionary<UsageTagFlags, string>
		{
			{
				(UsageTagFlags)8,
				"Flooring"
			},
			{
				(UsageTagFlags)16,
				"Walls"
			},
			{
				(UsageTagFlags)32,
				"Roofing"
			},
			{
				(UsageTagFlags)2048,
				"Transportation"
			}
		};

		private static readonly Dictionary<string, UsageTagFlags> Aliases = new Dictionary<string, UsageTagFlags>(StringComparer.OrdinalIgnoreCase)
		{
			{
				"Flooring",
				(UsageTagFlags)8
			},
			{
				"Floors",
				(UsageTagFlags)8
			},
			{
				"Walls",
				(UsageTagFlags)16
			},
			{
				"Roofing",
				(UsageTagFlags)32
			},
			{
				"Roofs",
				(UsageTagFlags)32
			},
			{
				"Transportation",
				(UsageTagFlags)2048
			},
			{
				"Mead",
				(UsageTagFlags)8192
			},
			{
				"Feast",
				(UsageTagFlags)16384
			}
		};

		private static ConfigEntry<string> paidTagsEntry;

		private static ConfigEntry<string> alwaysPayEntry;

		private static ConfigEntry<string> neverPayEntry;

		private static ConfigEntry<bool> cultivatorPaysEntry;

		private static ConfigEntry<bool> grantFreeBuildEntry;

		private static ConfigEntry<bool> logDecisionsEntry;

		private static ManualLogSource log;

		private static UsageTagFlags paidTags;

		private static readonly Dictionary<PieceCategory, string> paidCategories = new Dictionary<PieceCategory, string>();

		private static readonly List<string> pendingCategories = new List<string>();

		private static bool unknownReported;

		private static HashSet<string> alwaysPay = new HashSet<string>();

		private static HashSet<string> neverPay = new HashSet<string>();

		private static HashSet<string> cultivatorPieces;

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

		internal static bool FreeWithoutModifier { get; private set; }

		internal static string TagNames => Describe((UsageTagFlags)(-1));

		private unsafe static string MenuName(UsageTagFlags tag)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (!MenuNames.TryGetValue(tag, out var value))
			{
				return ((object)(*(UsageTagFlags*)(&tag))/*cast due to .constrained prefix*/).ToString();
			}
			return value;
		}

		private static string Describe(UsageTagFlags flags)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			return string.Join(", ", (from UsageTagFlags tag in Enum.GetValues(typeof(UsageTagFlags))
				where (flags & tag) > 0
				select tag).Select(MenuName));
		}

		internal static void Bind(ConfigEntry<string> paidTags, ConfigEntry<string> alwaysPay, ConfigEntry<string> neverPay, ConfigEntry<bool> cultivatorPays, ConfigEntry<bool> grantFreeBuild, ConfigEntry<bool> logDecisions, ManualLogSource logger)
		{
			paidTagsEntry = paidTags;
			alwaysPayEntry = alwaysPay;
			neverPayEntry = neverPay;
			cultivatorPaysEntry = cultivatorPays;
			grantFreeBuildEntry = grantFreeBuild;
			logDecisionsEntry = logDecisions;
			log = logger;
		}

		internal static void Reload()
		{
			//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)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			if (paidTagsEntry != null)
			{
				paidCategories.Clear();
				pendingCategories.Clear();
				unknownReported = false;
				paidTags = ParseTags(paidTagsEntry.Value);
				alwaysPay = ParseNames(alwaysPayEntry.Value);
				neverPay = ParseNames(neverPayEntry.Value);
				FreeWithoutModifier = grantFreeBuildEntry.Value;
				logged.Clear();
				log.LogInfo((object)(((int)paidTags == 0 && alwaysPay.Count == 0 && !cultivatorPaysEntry.Value && pendingCategories.Count == 0) ? "Nothing is set to cost materials - free building is left as the world has it." : ($"Paying categories: {paidTags}." + $" Always pay: {alwaysPay.Count}. Never pay: {neverPay.Count}." + ((pendingCategories.Count > 0) ? (" Awaiting other mods: " + string.Join(", ", pendingCategories.ToArray()) + ".") : string.Empty) + $" Cultivator always pays: {cultivatorPaysEntry.Value}." + $" Free building granted without the world modifier: {FreeWithoutModifier}.")));
			}
		}

		internal static bool MustPay(Piece piece)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Invalid comparison between Unknown and I4
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Invalid comparison between Unknown and I4
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)piece == (Object)null || logDecisionsEntry == null)
			{
				return false;
			}
			if (pendingCategories.Count > 0)
			{
				ResolveCategories();
			}
			bool flag = paidCategories.ContainsKey(piece.m_category);
			if (alwaysPay.Count == 0 && neverPay.Count == 0 && !cultivatorPaysEntry.Value && !logDecisionsEntry.Value)
			{
				if (!flag)
				{
					return (piece.m_usage & paidTags) > 0;
				}
				return true;
			}
			string prefabName = Utils.GetPrefabName(((Component)piece).gameObject);
			if (neverPay.Contains(prefabName))
			{
				return Decided(prefabName, pays: false, "NeverPay");
			}
			if (alwaysPay.Contains(prefabName))
			{
				return Decided(prefabName, pays: true, "AlwaysPay");
			}
			if (IsCultivatorPiece(prefabName))
			{
				return Decided(prefabName, pays: true, "CultivatorAlwaysPays");
			}
			if (flag)
			{
				return Decided(prefabName, pays: true, paidCategories[piece.m_category]);
			}
			UsageTagFlags val = (UsageTagFlags)(piece.m_usage & paidTags);
			return Decided(prefabName, (int)val > 0, ((int)val != 0) ? Describe(val) : "no paying category");
		}

		private static void ResolveCategories()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			for (int num = pendingCategories.Count - 1; num >= 0; num--)
			{
				string text = pendingCategories[num];
				PieceManager instance = PieceManager.Instance;
				PieceCategory? val = ((instance != null) ? instance.GetPieceCategory(text) : ((PieceCategory?)null));
				if (val.HasValue)
				{
					paidCategories[val.Value] = text;
					pendingCategories.RemoveAt(num);
					log.LogInfo((object)("'" + text + "' is a build category added by another mod. Its pieces pay."));
				}
			}
			if (!unknownReported && pendingCategories.Count > 0)
			{
				unknownReported = true;
				log.LogWarning((object)("Ignoring unknown categories: " + string.Join(", ", pendingCategories.ToArray()) + ". Valid: " + TagNames + " - or any category another mod adds, by the name it shows in the build menu."));
			}
		}

		private static bool IsCultivatorPiece(string name)
		{
			if (!cultivatorPaysEntry.Value)
			{
				return false;
			}
			if (cultivatorPieces == null)
			{
				ResolveCultivatorPieces();
			}
			if (cultivatorPieces != null)
			{
				return cultivatorPieces.Contains(name);
			}
			return false;
		}

		private static void ResolveCultivatorPieces()
		{
			PieceManager instance = PieceManager.Instance;
			PieceTable val = ((instance != null) ? instance.GetPieceTable(PieceTables.Cultivator) : null);
			if (val?.m_pieces == null || val.m_pieces.Count == 0)
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (GameObject piece in val.m_pieces)
			{
				if ((Object)(object)piece != (Object)null)
				{
					hashSet.Add(Utils.GetPrefabName(piece));
				}
			}
			cultivatorPieces = hashSet;
			log.LogInfo((object)$"Cultivator pieces that cost materials: {hashSet.Count}.");
		}

		private static bool Decided(string name, bool pays, string reason)
		{
			if (logDecisionsEntry.Value && logged.Add(name))
			{
				log.LogInfo((object)(name + ": " + (pays ? "costs materials" : "free") + " (" + reason + ")."));
			}
			return pays;
		}

		private static UsageTagFlags ParseTags(string value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			UsageTagFlags val = (UsageTagFlags)0;
			foreach (string item in Split(value))
			{
				UsageTagFlags result;
				if (Aliases.TryGetValue(item, out var value2))
				{
					val |= value2;
				}
				else if (Enum.TryParse<UsageTagFlags>(item, ignoreCase: true, out result) && Enum.IsDefined(typeof(UsageTagFlags), result))
				{
					val |= result;
				}
				else
				{
					pendingCategories.Add(item);
				}
			}
			return val;
		}

		private static HashSet<string> ParseNames(string value)
		{
			return new HashSet<string>(Split(value), StringComparer.OrdinalIgnoreCase);
		}

		private static IEnumerable<string> Split(string value)
		{
			return from token in (value ?? string.Empty).Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
				select token.Trim() into token
				where token.Length > 0
				select token;
		}
	}
	[BepInPlugin("elric.SelectiveFreeBuild", "Selective Free Build", "1.1.0")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	internal class SelectiveFreeBuild : BaseUnityPlugin
	{
		public const string PluginGUID = "elric.SelectiveFreeBuild";

		public const string PluginName = "Selective Free Build";

		public const string PluginVersion = "1.1.0";

		private ConfigEntry<string> paidTags;

		private ConfigEntry<string> alwaysPay;

		private ConfigEntry<string> neverPay;

		private ConfigEntry<bool> cultivatorPays;

		private ConfigEntry<bool> grantFreeBuild;

		private ConfigEntry<bool> logDecisions;

		private Coroutine pendingReload;

		private void Awake()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			CreateConfig();
			Rules.Reload();
			new Harmony("elric.SelectiveFreeBuild").PatchAll();
		}

		private void ScheduleReload()
		{
			if (pendingReload != null)
			{
				((MonoBehaviour)this).StopCoroutine(pendingReload);
			}
			pendingReload = ((MonoBehaviour)this).StartCoroutine(ReloadWhenIdle());
		}

		private IEnumerator ReloadWhenIdle()
		{
			yield return (object)new WaitForSecondsRealtime(0.5f);
			pendingReload = null;
			Rules.Reload();
		}

		private void CreateConfig()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Expected O, but got Unknown
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Expected O, but got Unknown
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Expected O, but got Unknown
			paidTags = ((BaseUnityPlugin)this).Config.Bind<string>("Build cost", "PaidTags", "Crafting, Transportation", new ConfigDescription("Build menu categories that keep their material cost when free building is on. Comma separated, in any order. Valid: " + Rules.TagNames + " - or any category another mod adds, by the name it shows in the build menu. A piece pays if it carries any one of them.", (AcceptableValueBase)null, new object[1] { adminOnly() }));
			alwaysPay = ((BaseUnityPlugin)this).Config.Bind<string>("Build cost", "AlwaysPay", "", new ConfigDescription("Individual pieces that always cost, whatever their categories say. Comma separated prefab names, e.g. piece_beehive, piece_workbench.", (AcceptableValueBase)null, new object[1] { adminOnly() }));
			neverPay = ((BaseUnityPlugin)this).Config.Bind<string>("Build cost", "NeverPay", "", new ConfigDescription("Individual pieces that are always free, whatever their categories say. Comma separated prefab names. Checked before AlwaysPay, so it wins.", (AcceptableValueBase)null, new object[1] { adminOnly() }));
			cultivatorPays = ((BaseUnityPlugin)this).Config.Bind<bool>("Build cost", "CultivatorAlwaysPays", true, new ConfigDescription("Everything placed with the cultivator costs materials, so planting still spends seeds. Read from the cultivator's own tool menu, so seeds and plants added by other mods are covered too. NeverPay still overrides it.", (AcceptableValueBase)null, new object[1] { adminOnly() }));
			grantFreeBuild = ((BaseUnityPlugin)this).Config.Bind<bool>("Build cost", "GrantFreeBuild", false, new ConfigDescription("Make everything outside PaidTags free without the No Build Cost world modifier. Off means this mod only ever adds cost back to a world that already has free building, and does nothing in a world that does not.", (AcceptableValueBase)null, new object[1] { adminOnly() }));
			logDecisions = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogDecisions", false, new ConfigDescription("Write one line to the log the first time each piece is priced, saying whether it costs materials and which category decided it.", (AcceptableValueBase)null, Array.Empty<object>()));
			Rules.Bind(paidTags, alwaysPay, neverPay, cultivatorPays, grantFreeBuild, logDecisions, ((BaseUnityPlugin)this).Logger);
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				ScheduleReload();
			};
			static ConfigurationManagerAttributes adminOnly()
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Expected O, but got Unknown
				return new ConfigurationManagerAttributes
				{
					IsAdminOnly = true
				};
			}
		}
	}
}