Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of BuildPiecesCustomized v1.3.2
BuildPiecesCustomized.dll
Decompiled 2 months ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using ConditionalConfigSync; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using UnityEngine; using YamlDotNet.Serialization; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Build Pieces Customized")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Build Pieces Customized")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("448a53ac-6a9d-47e7-b934-d6ee2b13e7b2")] [assembly: AssemblyFileVersion("1.3.2")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.2.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace BuildPiecesCustomized { [BepInPlugin("shudnal.BuildPiecesCustomized", "Build Pieces Customized", "1.3.2")] [BepInDependency("_shudnal.ConditionalConfigSync", "1.0.5")] [BepInIncompatibility("aedenthorn.BuildPieceTweaks")] [BepInIncompatibility("TheSxW_EditMaterialProperties")] [BepInIncompatibility("lime.plugins.foreverbuild")] [BepInIncompatibility("bonesbro.val.floorsareroofs")] public class BuildPiecesCustomized : BaseUnityPlugin { internal class MaterialPropertiesConfig { public ConfigEntry<float> maxSupport; public ConfigEntry<float> minSupport; public ConfigEntry<float> verticalLoss; public ConfigEntry<float> horizontalLoss; } [HarmonyPatch(typeof(ZoneSystem), "OnDestroy")] private static class ZoneSystem_OnDestroy_PiecesWntCache { private static void Postfix() { piecesWntComponent.Clear(); } } [HarmonyPatch(typeof(Piece), "Awake")] private static class Piece_Awake_PiecesWntCache { private static void Prefix(Piece __instance) { if (modEnabled.Value) { piecesWntComponent[__instance] = ((Component)__instance).GetComponent<WearNTear>(); } } } [HarmonyPatch(typeof(Piece), "OnDestroy")] private static class Piece_OnDestroy_PiecesWntCache { private static void Prefix(Piece __instance) { piecesWntComponent.Remove(__instance); PiecePatches.GlobalPatches.ForgetPiece(__instance); } } [HarmonyPatch(typeof(ZoneSystem), "Start")] private static class ZoneSystem_Start_PiecesConfigWatcher { [HarmonyPriority(0)] private static void Postfix() { if (modEnabled.Value) { SetupConfigWatcher(); } } } [HarmonyPatch(typeof(WearNTear), "GetMaterialProperties")] private static class WearNTear_GetMaterialProperties_MaterialProperties { private static void Postfix(WearNTear __instance, ref float maxSupport, ref float minSupport, ref float horizontalLoss, ref float verticalLoss) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (modEnabled.Value && materialConfigs.TryGetValue(__instance.m_materialType, out var value)) { maxSupport *= value.maxSupport.Value; minSupport *= value.minSupport.Value; horizontalLoss /= value.horizontalLoss.Value; verticalLoss /= value.verticalLoss.Value; } } } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static EventHandler <>9__38_0; public static EventHandler <>9__38_1; public static EventHandler <>9__38_2; public static EventHandler <>9__38_3; public static EventHandler <>9__38_4; public static EventHandler <>9__38_5; public static EventHandler <>9__38_6; public static EventHandler <>9__38_7; public static EventHandler <>9__38_8; public static EventHandler <>9__38_9; public static EventHandler <>9__38_10; public static EventHandler <>9__38_11; public static ConsoleEvent <>9__41_0; public static Func<GameObject, string> <>9__41_6; public static ConsoleOptionsFetcher <>9__41_1; public static ConsoleEvent <>9__41_2; public static Func<GameObject, string> <>9__41_7; public static ConsoleOptionsFetcher <>9__41_3; public static ConsoleEvent <>9__41_4; public static Func<FileInfo, bool> <>9__45_0; public static Func<FileInfo, string> <>9__45_1; public static Func<FileInfo, string> <>9__45_2; internal void <ConfigInit>b__38_0(object s, EventArgs e) { if (!((Object)(object)ZNetScene.instance == (Object)null)) { if (modEnabled.Value) { SetupConfigWatcher(); } else { ConfigFileWatcher.Dispose(); } PiecePatches.UpdatePiecesProperties(); } } internal void <ConfigInit>b__38_1(object s, EventArgs e) { PiecePatches.UpdatePiecesProperties(); } internal void <ConfigInit>b__38_2(object s, EventArgs e) { PiecePatches.UpdatePiecesProperties(); } internal void <ConfigInit>b__38_3(object s, EventArgs e) { PiecePatches.UpdatePiecesProperties(); } internal void <ConfigInit>b__38_4(object s, EventArgs e) { PiecePatches.UpdatePiecesProperties(); } internal void <ConfigInit>b__38_5(object s, EventArgs e) { PiecePatches.UpdatePiecesProperties(); } internal void <ConfigInit>b__38_6(object s, EventArgs e) { PiecePatches.UpdatePiecesProperties(); } internal void <ConfigInit>b__38_7(object s, EventArgs e) { PiecePatches.UpdatePiecesProperties(); } internal void <ConfigInit>b__38_8(object s, EventArgs e) { PiecePatches.UpdatePiecesProperties(); } internal void <ConfigInit>b__38_9(object s, EventArgs e) { PiecePatches.UpdatePiecesProperties(); } internal void <ConfigInit>b__38_10(object s, EventArgs e) { PiecePatches.UpdatePiecesProperties(); } internal void <ConfigInit>b__38_11(object s, EventArgs e) { PiecePatches.UpdatePiecesProperties(); } internal void <InitCommands>b__41_0(ConsoleEventArgs args) { bool flag = args.Length >= 2; string value = (flag ? args.FullLine.Substring(args[0].Length + 1) : ""); Piece piece = default(Piece); foreach (GameObject buildPiece in CustomPieceData.GetBuildPieces()) { if (buildPiece.TryGetComponent<Piece>(ref piece) && (!flag || ((Object)buildPiece).name.IndexOf(value, StringComparison.Ordinal) != -1 || Regex.IsMatch(((Object)buildPiece).name, <InitCommands>g__WildCardToRegular|41_5(value)))) { new CustomPieceData(piece).SaveToDirectory(configDirectory.FullName); Terminal context = args.Context; if (context != null) { context.AddString("Saved " + ((Object)buildPiece).name + " file to config directory"); } } } } internal List<string> <InitCommands>b__41_1() { return (from piece in CustomPieceData.GetBuildPieces() select ((Object)piece).name).ToList(); } internal string <InitCommands>b__41_6(GameObject piece) { return ((Object)piece).name; } internal void <InitCommands>b__41_2(ConsoleEventArgs args) { if (args.Length < 2) { return; } string text = args.FullLine.Substring(args[0].Length + 1); CustomPieceData byPieceName = CustomPieceData.GetByPieceName(text); if (byPieceName == null) { Terminal context = args.Context; if (context != null) { context.AddString("Piece with name " + text + " was not found"); } return; } byPieceName.SaveToDirectory(configDirectory.FullName); Terminal context2 = args.Context; if (context2 != null) { context2.AddString("Saved " + text + " file to config directory"); } } internal List<string> <InitCommands>b__41_3() { return (from piece in CustomPieceData.GetBuildPieces() select ((Object)piece).name).ToList(); } internal string <InitCommands>b__41_7(GameObject piece) { return ((Object)piece).name; } internal void <InitCommands>b__41_4(ConsoleEventArgs args) { DocGen.GenerateDocumentationFile(); } internal bool <GetConfigFiles>b__45_0(FileInfo file) { return ConfigFileWatcher.IsConfigFile(file.FullName); } internal string <GetConfigFiles>b__45_1(FileInfo file) { return file.FullName; } internal string <GetConfigFiles>b__45_2(FileInfo file) { return file.FullName; } } public const string pluginID = "shudnal.BuildPiecesCustomized"; public const string pluginName = "Build Pieces Customized"; public const string pluginVersion = "1.3.2"; private readonly Harmony harmony = new Harmony("shudnal.BuildPiecesCustomized"); internal static readonly ConfigSync configSync = new ConfigSync("shudnal.BuildPiecesCustomized") { DisplayName = "Build Pieces Customized", CurrentVersion = "1.3.2", MinimumRequiredVersion = "1.3.2" }; internal static ConfigEntry<bool> modEnabled; internal static ConfigEntry<bool> configLocked; internal static ConfigEntry<bool> loggingEnabled; internal static ConfigEntry<bool> saveAsYAML; internal static ConfigEntry<string> toolsToPatchPieces; internal static ConfigEntry<string> prefabListClipEverything; internal static ConfigEntry<string> prefabListAllowedInDungeons; internal static ConfigEntry<string> prefabListCanBeRemoved; internal static ConfigEntry<string> prefabListIsRoof; internal static ConfigEntry<string> prefabListIsLeaky; internal static ConfigEntry<string> prefabListDisabled; internal static ConfigEntry<string> prefabListAshDamageImmune; internal static ConfigEntry<string> prefabListSnowDamageImmune; internal static ConfigEntry<string> prefabListNoRoofWear; internal static ConfigEntry<string> prefabListNoSupportWear; internal static readonly Dictionary<MaterialType, MaterialPropertiesConfig> materialConfigs = new Dictionary<MaterialType, MaterialPropertiesConfig>(); internal static BuildPiecesCustomized instance; internal static readonly CustomSyncedValue<Dictionary<string, string>> configsJSON = new CustomSyncedValue<Dictionary<string, string>>((ConditionalConfigSync)(object)configSync, "JSON configs", new Dictionary<string, string>(), 0, (IEqualityComparer<Dictionary<string, string>>)null); internal static readonly Dictionary<string, CustomPieceData> pieceData = new Dictionary<string, CustomPieceData>(StringComparer.OrdinalIgnoreCase); internal static readonly Dictionary<string, CraftingStation> craftingStations = new Dictionary<string, CraftingStation>(); internal static readonly Dictionary<string, CustomPieceData> defaultPieceData = new Dictionary<string, CustomPieceData>(); internal static DirectoryInfo pluginDirectory; internal static DirectoryInfo configDirectory; private static Dictionary<string, string> lastLocalConfigs; private static Dictionary<string, string> lastAppliedConfigs; internal static readonly IDeserializer YamlDeserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); internal static readonly ISerializer YamlSerializer = new SerializerBuilder().Build(); internal static readonly Dictionary<Piece, WearNTear> piecesWntComponent = new Dictionary<Piece, WearNTear>(); private void Awake() { instance = this; pluginDirectory = new DirectoryInfo(Assembly.GetExecutingAssembly().Location).Parent; configDirectory = new DirectoryInfo(Path.Combine(Paths.ConfigPath, "shudnal.BuildPiecesCustomized")); ConfigInit(); PiecePatches.GlobalPatches.UpdateProperties(); ((ConditionalConfigSync)configSync).AddLockingConfigEntry<bool>(configLocked); ((CustomSyncedValueBase)configsJSON).ValueChanged += LoadConfigs; harmony.PatchAll(); Game.isModded = true; } private void OnDestroy() { ConfigFileWatcher.Dispose(); PiecePatches.CancelPendingUpdate(); ((CustomSyncedValueBase)configsJSON).ValueChanged -= LoadConfigs; ((BaseUnityPlugin)this).Config.Save(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } instance = null; } public static void LogInfo(object data) { if (loggingEnabled.Value) { ((BaseUnityPlugin)instance).Logger.LogInfo(data); } } public static void LogWarning(object data) { ((BaseUnityPlugin)instance).Logger.LogWarning(data); } private ConfigDescription GetDescriptionSeparatedStrings(string description) { //IL_003f: 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_0045: Expected O, but got Unknown return Chainloader.PluginInfos.ContainsKey("_shudnal.ConfigurationManager") ? new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()) : new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { new CustomConfigs.ConfigurationManagerAttributes { CustomDrawer = CustomConfigs.DrawSeparatedStrings(",") } }); } public void ConfigInit() { //IL_0426: Unknown result type (might be due to invalid IL or missing references) //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_0432: Unknown result type (might be due to invalid IL or missing references) //IL_043f: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_0497: Unknown result type (might be due to invalid IL or missing references) //IL_04c3: Unknown result type (might be due to invalid IL or missing references) modEnabled = config("General", "Enabled", defaultValue: true, "Enable the mod."); modEnabled.SettingChanged += delegate { if (!((Object)(object)ZNetScene.instance == (Object)null)) { if (modEnabled.Value) { SetupConfigWatcher(); } else { ConfigFileWatcher.Dispose(); } PiecePatches.UpdatePiecesProperties(); } }; configLocked = config("General", "Lock Configuration", defaultValue: true, "Configuration is locked and can be changed by server admins only."); loggingEnabled = config("General", "Logging enabled", defaultValue: false, "Enable logging. [Not Synced with Server]", synchronizedSetting: false); saveAsYAML = config("General", "Save piece data as YAML", defaultValue: false, "Save piece data in YAML format. [Not Synced with Server]", synchronizedSetting: false); toolsToPatchPieces = config("General", "Tools list", "Hammer,Hoe,Cultivator", "Comma-separated list of tool prefab name to get build pieces from"); toolsToPatchPieces.SettingChanged += delegate { PiecePatches.UpdatePiecesProperties(); }; prefabListClipEverything = config("List - Global setting", "Clip everything", "", GetDescriptionSeparatedStrings("Comma-separated list of pieces that will clip through each other. Set \"AllPieces\" identifier to apply for all pieces.")); prefabListAllowedInDungeons = config("List - Global setting", "Allow in dungeons", "", GetDescriptionSeparatedStrings("Comma-separated list of pieces that will be allowed to build in dungeons. Set \"AllPieces\" identifier to apply for all pieces.")); prefabListCanBeRemoved = config("List - Global setting", "Can be removed", "", GetDescriptionSeparatedStrings("Comma-separated list of pieces that will be removeable. Set \"AllPieces\" identifier to apply for all pieces.")); prefabListDisabled = config("List - Global setting", "Disabled pieces", "", GetDescriptionSeparatedStrings("Comma-separated list of pieces that will be disabled.")); prefabListIsRoof = config("List - Global setting", "Is Roof", "", GetDescriptionSeparatedStrings("Comma-separated list of pieces that will work as a roof. Set \"AllPieces\" identifier to apply for all pieces.\nLeaky -> Roof transition will be applied immediately. \nRestart the game if you need Roof -> Leaky transition of updated pieces.")); prefabListIsLeaky = config("List - Global setting", "Is Leaky", "", GetDescriptionSeparatedStrings("Comma-separated list of pieces that will not work as a roof. Set \"AllPieces\" identifier to apply for all pieces.\nRoof -> Leaky transition will be applied immediately. \nRestart the game if you need Leaky -> Roof transition of updated pieces.")); prefabListClipEverything.SettingChanged += delegate { PiecePatches.UpdatePiecesProperties(); }; prefabListAllowedInDungeons.SettingChanged += delegate { PiecePatches.UpdatePiecesProperties(); }; prefabListCanBeRemoved.SettingChanged += delegate { PiecePatches.UpdatePiecesProperties(); }; prefabListDisabled.SettingChanged += delegate { PiecePatches.UpdatePiecesProperties(); }; prefabListIsRoof.SettingChanged += delegate { PiecePatches.UpdatePiecesProperties(); }; prefabListIsLeaky.SettingChanged += delegate { PiecePatches.UpdatePiecesProperties(); }; prefabListAshDamageImmune = config("List - Immune to", "Ash and lava", "", GetDescriptionSeparatedStrings("Comma-separated list of pieces that will be immune to ash and lava damage. Set \"AllPieces\" identifier to apply for all pieces.")); prefabListSnowDamageImmune = config("List - Immune to", "Heavy snow", "", GetDescriptionSeparatedStrings("Comma-separated list of pieces that will be immune to heavy snow damage. Set \"AllPieces\" identifier to apply for all pieces.")); prefabListNoRoofWear = config("List - Immune to", "Water damage", "", GetDescriptionSeparatedStrings("Comma-separated list of pieces that will be immune to water damage. Set \"AllPieces\" identifier to apply for all pieces.")); prefabListNoSupportWear = config("List - Immune to", "Structural integrity", "", GetDescriptionSeparatedStrings("Comma-separated list of pieces that will not be needed support. Set \"AllPieces\" identifier to apply for all pieces.")); prefabListAshDamageImmune.SettingChanged += delegate { PiecePatches.UpdatePiecesProperties(); }; prefabListSnowDamageImmune.SettingChanged += delegate { PiecePatches.UpdatePiecesProperties(); }; prefabListNoRoofWear.SettingChanged += delegate { PiecePatches.UpdatePiecesProperties(); }; prefabListNoSupportWear.SettingChanged += delegate { PiecePatches.UpdatePiecesProperties(); }; foreach (MaterialType value in Enum.GetValues(typeof(MaterialType))) { materialConfigs.Add(value, new MaterialPropertiesConfig { maxSupport = config($"Material - {(object)value}", "Max support multiplier", 1f, "Multiplier of maximum support value material can provide."), minSupport = config($"Material - {(object)value}", "Min support multiplier", 1f, "Multiplier of minimum support value material can provide."), verticalLoss = config($"Material - {(object)value}", "Vertical stability multiplier", 1f, "Multiplier of vertical support value. Increase to be able to build higher."), horizontalLoss = config($"Material - {(object)value}", "Horizontal stability multiplier", 1f, "Multiplier of horizontal support value. Increase to be able to build longer hanging beams.") }); } InitCommands(); } private ConfigEntry<T> config<T>(string group, string name, T defaultValue, ConfigDescription description, bool synchronizedSetting = true) { ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, defaultValue, description); ((ConditionalConfigSync)configSync).AddConfigEntry<T>(val, (ConfigSyncMode)1, synchronizedSetting); return val; } private ConfigEntry<T> config<T>(string group, string name, T defaultValue, string description, bool synchronizedSetting = true) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return config(group, name, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } public static void InitCommands() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0061: 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) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_0100: 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_00f6: Expected O, but got Unknown string text = "[Prefab partial name or wildcard *] - save data of ALL pieces into JSON files to config directory " + configDirectory.Name; object obj = <>c.<>9__41_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { bool flag = args.Length >= 2; string value = (flag ? args.FullLine.Substring(args[0].Length + 1) : ""); Piece piece = default(Piece); foreach (GameObject buildPiece in CustomPieceData.GetBuildPieces()) { if (buildPiece.TryGetComponent<Piece>(ref piece) && (!flag || ((Object)buildPiece).name.IndexOf(value, StringComparison.Ordinal) != -1 || Regex.IsMatch(((Object)buildPiece).name, WildCardToRegular(value)))) { new CustomPieceData(piece).SaveToDirectory(configDirectory.FullName); Terminal context = args.Context; if (context != null) { context.AddString("Saved " + ((Object)buildPiece).name + " file to config directory"); } } } }; <>c.<>9__41_0 = val; obj = (object)val; } object obj2 = <>c.<>9__41_1; if (obj2 == null) { ConsoleOptionsFetcher val2 = () => (from piece in CustomPieceData.GetBuildPieces() select ((Object)piece).name).ToList(); <>c.<>9__41_1 = val2; obj2 = (object)val2; } new ConsoleCommand("bpcsaveall", text, (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false); string text2 = "[Prefab name] - save piece data into JSON file to config directory " + configDirectory.Name; object obj3 = <>c.<>9__41_2; if (obj3 == null) { ConsoleEvent val3 = delegate(ConsoleEventArgs args) { if (args.Length >= 2) { string text3 = args.FullLine.Substring(args[0].Length + 1); CustomPieceData byPieceName = CustomPieceData.GetByPieceName(text3); if (byPieceName == null) { Terminal context = args.Context; if (context != null) { context.AddString("Piece with name " + text3 + " was not found"); } } else { byPieceName.SaveToDirectory(configDirectory.FullName); Terminal context2 = args.Context; if (context2 != null) { context2.AddString("Saved " + text3 + " file to config directory"); } } } }; <>c.<>9__41_2 = val3; obj3 = (object)val3; } object obj4 = <>c.<>9__41_3; if (obj4 == null) { ConsoleOptionsFetcher val4 = () => (from piece in CustomPieceData.GetBuildPieces() select ((Object)piece).name).ToList(); <>c.<>9__41_3 = val4; obj4 = (object)val4; } new ConsoleCommand("bpcsave", text2, (ConsoleEvent)obj3, false, false, false, false, false, false, (ConsoleOptionsFetcher)obj4, false, false, false); object obj5 = <>c.<>9__41_4; if (obj5 == null) { ConsoleEvent val5 = delegate { DocGen.GenerateDocumentationFile(); }; <>c.<>9__41_4 = val5; obj5 = (object)val5; } new ConsoleCommand("bpcdocs", "Save documentation file Pieces and properties.md to config directory", (ConsoleEvent)obj5, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); static string WildCardToRegular(string value) { return "^" + Regex.Escape(value).Replace("\\*", ".*") + "$"; } } public static void SetupConfigWatcher() { lastLocalConfigs = null; ConfigFileWatcher.Setup(); } internal static bool ReadConfigs(out string error) { try { ReadConfigSnapshot(); error = null; return true; } catch (Exception ex) { error = ex.Message; return false; } } private static void ReadConfigSnapshot() { Dictionary<string, CustomPieceData> dictionary = new Dictionary<string, CustomPieceData>(StringComparer.OrdinalIgnoreCase); PieceUsageTags.ConfigFile configFile = null; List<FileInfo> list = new List<FileInfo>(); pluginDirectory.Refresh(); configDirectory.Refresh(); if (pluginDirectory.Exists) { list.AddRange(GetConfigFiles(pluginDirectory)); } if (configDirectory.Exists) { list.AddRange(GetConfigFiles(configDirectory)); } foreach (FileInfo item in list) { if (!ConfigFileWatcher.IsConfigFile(item.FullName)) { continue; } if (loggingEnabled.Value) { LogInfo("Found " + item.FullName); } try { string text; using (FileStream stream = new FileStream(item.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) { using StreamReader streamReader = new StreamReader(stream); text = streamReader.ReadToEnd(); } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item.Name); bool flag = string.Equals(item.Extension, ".json", StringComparison.OrdinalIgnoreCase); if (string.Equals(fileNameWithoutExtension, "Piece usage tags", StringComparison.OrdinalIgnoreCase)) { if (configFile != null) { LogWarning("Multiple 'Piece usage tags' files found. Using the last file by configuration search priority: '" + item.FullName + "'."); } configFile = null; configFile = (flag ? JsonConvert.DeserializeObject<PieceUsageTags.ConfigFile>(text) : YamlDeserializer.Deserialize<PieceUsageTags.ConfigFile>(text)); if (configFile == null) { throw new InvalidDataException("The usage tag file is empty. Use an empty object to clear its overrides."); } } else { CustomPieceData customPieceData = (flag ? JsonConvert.DeserializeObject<CustomPieceData>(text) : YamlDeserializer.Deserialize<CustomPieceData>(text)); if (customPieceData == null) { throw new InvalidDataException("The piece file is empty. Delete it or use an empty object to clear its overrides."); } dictionary[fileNameWithoutExtension] = customPieceData; } } catch (Exception ex) { throw new InvalidDataException("Cannot read configuration '" + item.FullName + "': " + ex.Message, ex); } } ApplyBulkUsageTagReplacements(dictionary, configFile); ApplyBulkUsageTagAdditions(dictionary, configFile); Dictionary<string, string> dictionary2 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, CustomPieceData> item2 in dictionary) { dictionary2[item2.Key] = JsonConvert.SerializeObject((object)item2.Value); } ConfigFileWatcher.RememberFiles(list); if (!ConfigSnapshotsEqual(lastLocalConfigs, dictionary2)) { configsJSON.AssignLocalValue(dictionary2); lastLocalConfigs = dictionary2; } } private static IEnumerable<FileInfo> GetConfigFiles(DirectoryInfo directory) { return (from file in directory.EnumerateFiles("*", SearchOption.AllDirectories) where ConfigFileWatcher.IsConfigFile(file.FullName) select file).OrderBy<FileInfo, string>((FileInfo file) => file.FullName, StringComparer.OrdinalIgnoreCase).ThenBy<FileInfo, string>((FileInfo file) => file.FullName, StringComparer.Ordinal); } private static CustomPieceData GetOrCreatePieceConfig(Dictionary<string, CustomPieceData> configs, string pieceName) { if (!configs.TryGetValue(pieceName, out var value)) { value = (configs[pieceName] = new CustomPieceData { prefabName = pieceName }); } return value; } private static void ApplyBulkUsageTagReplacements(Dictionary<string, CustomPieceData> configs, PieceUsageTags.ConfigFile usageTagConfig) { if (usageTagConfig?.Pieces == null) { return; } foreach (KeyValuePair<string, List<string>> piece in usageTagConfig.Pieces) { string text = piece.Key?.Trim(); if (!string.IsNullOrWhiteSpace(text)) { UsageTagFlags result; List<string> canonicalNames; string invalidValue; if (piece.Value == null) { LogWarning("Ignoring null usage tag list for piece '" + text + "'. Use an empty list to clear all usage tags."); } else if (!PieceUsageTags.TryParseTags(piece.Value, out result, out canonicalNames, out invalidValue)) { LogWarning("Ignoring usage tag override for piece '" + text + "' because '" + invalidValue + "' is not a supported built-in usage tag."); } else { GetOrCreatePieceConfig(configs, text).usageTags = canonicalNames; } } } } private static void ApplyBulkUsageTagAdditions(Dictionary<string, CustomPieceData> configs, PieceUsageTags.ConfigFile usageTagConfig) { if (usageTagConfig?.Tags == null) { return; } foreach (KeyValuePair<string, List<string>> tag2 in usageTagConfig.Tags) { if (!PieceUsageTags.TryParseTag(tag2.Key, out var _, out var canonicalName)) { LogWarning("Ignoring unsupported built-in usage tag '" + tag2.Key + "' in 'Piece usage tags'."); continue; } if (tag2.Value == null) { LogWarning("Ignoring null piece list for usage tag '" + canonicalName + "'."); continue; } foreach (string item in tag2.Value) { string text = item?.Trim(); if (!string.IsNullOrWhiteSpace(text)) { CustomPieceData orCreatePieceConfig = GetOrCreatePieceConfig(configs, text); if (orCreatePieceConfig.usageTagsAdditions == null) { orCreatePieceConfig.usageTagsAdditions = new List<string>(); } if (!orCreatePieceConfig.usageTagsAdditions.Any((string a) => string.Equals(a, canonicalName, StringComparison.OrdinalIgnoreCase))) { orCreatePieceConfig.usageTagsAdditions.Add(canonicalName); } } } } } private static bool ConfigSnapshotsEqual(Dictionary<string, string> previous, Dictionary<string, string> current) { if (previous == null || current == null || previous.Count != current.Count) { return false; } foreach (KeyValuePair<string, string> item in current) { if (!previous.TryGetValue(item.Key, out var value) || !string.Equals(value, item.Value, StringComparison.Ordinal)) { return false; } } return true; } private static void LoadConfigs() { Dictionary<string, string> value = configsJSON.Value; if (value == null || ConfigSnapshotsEqual(lastAppliedConfigs, value)) { return; } Dictionary<string, CustomPieceData> dictionary = new Dictionary<string, CustomPieceData>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, string> item in value) { try { CustomPieceData customPieceData = JsonConvert.DeserializeObject<CustomPieceData>(item.Value); if (customPieceData == null || string.IsNullOrWhiteSpace(item.Key)) { throw new InvalidDataException("A synchronized piece entry is empty."); } if (dictionary.ContainsKey(item.Key)) { throw new InvalidDataException("Duplicate case-insensitive piece identifier."); } customPieceData.PrepareUsageTags(item.Key); dictionary.Add(item.Key, customPieceData); } catch (Exception ex) { LogWarning("Keeping the previous piece configuration: cannot parse '" + item.Key + "': " + ex.Message); return; } } pieceData.Clear(); foreach (KeyValuePair<string, CustomPieceData> item2 in dictionary) { pieceData[item2.Key] = item2.Value; } lastAppliedConfigs = new Dictionary<string, string>(value, StringComparer.OrdinalIgnoreCase); PiecePatches.UpdatePiecesProperties(); } internal static WearNTear GetWearNTearComponent(Piece piece) { if (piecesWntComponent.TryGetValue(piece, out var value)) { return value; } WearNTear component = ((Component)piece).GetComponent<WearNTear>(); piecesWntComponent[piece] = component; return component; } [CompilerGenerated] internal static string <InitCommands>g__WildCardToRegular|41_5(string value) { return "^" + Regex.Escape(value).Replace("\\*", ".*") + "$"; } } internal class CustomConfigs { internal class ConfigurationManagerAttributes { [UsedImplicitly] public Action<ConfigEntryBase>? CustomDrawer; } internal static object? configManager; internal static Type? configManagerStyles; internal static GUIStyle GetStyle(GUIStyle other) { //IL_0034: 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: Expected O, but got Unknown if (configManagerStyles == null) { return other; } FieldInfo fieldInfo = AccessTools.Field(configManagerStyles, "fontSize"); if (fieldInfo == null) { return other; } return new GUIStyle(other) { fontSize = (int)fieldInfo.GetValue(configManagerStyles) }; } internal static void Awake() { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "ConfigurationManager"); Type type = assembly?.GetType("ConfigurationManager.ConfigurationManager"); configManager = ((type == null) ? null : Chainloader.ManagerObject.GetComponent(type)); configManagerStyles = assembly?.GetType("ConfigurationManager.ConfigurationManagerStyles"); } internal static Action<ConfigEntryBase> DrawSeparatedStrings(string splitString) { return delegate(ConfigEntryBase cfg) { //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Expected O, but got Unknown //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Expected O, but got Unknown bool valueOrDefault = cfg.Description.Tags.Select((object a) => (a.GetType().Name == "ConfigurationManagerAttributes") ? ((bool?)a.GetType().GetField("ReadOnly")?.GetValue(a)) : ((bool?)null)).FirstOrDefault((bool? v) => v.HasValue) == true; bool flag = false; GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); List<string> list = new List<string>(); List<string> list2 = ((string)cfg.BoxedValue).Split(new string[1] { splitString }, StringSplitOptions.None).ToList(); for (int num = 0; num < list2.Count; num++) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); string text = list2[num]; string text2 = GUILayout.TextField(text, GetStyle(GUI.skin.textArea), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (text2 != text && !valueOrDefault) { flag = true; } if (GUILayout.Button("x", new GUIStyle(GetStyle(GUI.skin.button)) { fixedWidth = 21f }, Array.Empty<GUILayoutOption>()) && !valueOrDefault) { flag = true; } else { list.Add(text2); } if (GUILayout.Button("+", new GUIStyle(GetStyle(GUI.skin.button)) { fixedWidth = 21f }, Array.Empty<GUILayoutOption>()) && !valueOrDefault) { flag = true; list.Add(""); } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); if (flag) { cfg.BoxedValue = string.Join(splitString, list); } }; } internal static Action<ConfigEntryBase> DrawOrderedFixedStrings(string splitString) { return delegate(ConfigEntryBase cfg) { //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown bool valueOrDefault = cfg.Description.Tags.Select((object a) => (a.GetType().Name == "ConfigurationManagerAttributes") ? ((bool?)a.GetType().GetField("ReadOnly")?.GetValue(a)) : ((bool?)null)).FirstOrDefault((bool? v) => v.HasValue) == true; bool flag = false; GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); string[] array = ((string)cfg.BoxedValue).Split(new string[1] { splitString }, StringSplitOptions.None).ToArray(); for (int num = 0; num < array.Length; num++) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); string text = array[num]; GUILayout.Label(text, GetStyle(GUI.skin.textArea), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (GUILayout.Button("ʌ", new GUIStyle(GetStyle(GUI.skin.button)) { fixedWidth = 21f }, Array.Empty<GUILayoutOption>()) && !valueOrDefault && (flag = num > 0)) { ref string reference = ref array[num]; ref string reference2 = ref array[num - 1]; string text2 = array[num - 1]; string text3 = array[num]; reference = text2; reference2 = text3; } if (GUILayout.Button("v", new GUIStyle(GetStyle(GUI.skin.button)) { fixedWidth = 21f }, Array.Empty<GUILayoutOption>()) && !valueOrDefault && (flag = num < array.Length - 1)) { ref string reference = ref array[num]; ref string reference3 = ref array[num + 1]; string text3 = array[num + 1]; string text2 = array[num]; reference = text3; reference3 = text2; } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); if (flag) { cfg.BoxedValue = string.Join(splitString, array); } }; } } internal static class ConfigFileWatcher { private const float ReloadDelay = 0.25f; private const int MaxReadAttempts = 3; private static readonly StringComparer pathComparer = ((Path.DirectorySeparatorChar == '\\') ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal); private static readonly HashSet<string> configDirectories = new HashSet<string>(pathComparer); private static FileSystemWatcher pluginWatcher; private static FileSystemWatcher configWatcher; private static Coroutine pendingReload; private static float reloadAfter; private static bool forceApply; internal static void Setup() { Dispose(); try { Directory.CreateDirectory(BuildPiecesCustomized.configDirectory.FullName); pluginWatcher = CreateWatcher(BuildPiecesCustomized.pluginDirectory.FullName); configWatcher = CreateWatcher(BuildPiecesCustomized.configDirectory.FullName); } catch (Exception ex) { Dispose(); BuildPiecesCustomized.LogWarning("Cannot watch piece configuration directories: " + ex.Message); } QueueReload(reapply: true); } private static FileSystemWatcher CreateWatcher(string path) { FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(path, "*") { IncludeSubdirectories = true, NotifyFilter = (NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.Size | NotifyFilters.LastWrite), SynchronizingObject = ThreadingHelper.SynchronizingObject }; fileSystemWatcher.Changed += OnFileChanged; fileSystemWatcher.Created += OnFileChanged; fileSystemWatcher.Deleted += OnFileChanged; fileSystemWatcher.Renamed += OnFileChanged; fileSystemWatcher.Error += OnWatcherError; try { fileSystemWatcher.EnableRaisingEvents = true; return fileSystemWatcher; } catch { fileSystemWatcher.Dispose(); throw; } } internal static bool IsConfigFile(string path) { string extension = Path.GetExtension(path); if (!string.Equals(extension, ".json", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".yaml", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".yml", StringComparison.OrdinalIgnoreCase)) { return false; } return !string.Equals(Path.GetFileName(path), "manifest.json", StringComparison.OrdinalIgnoreCase) && !string.Equals(Path.GetFileNameWithoutExtension(path), "Piece categories", StringComparison.OrdinalIgnoreCase); } private static bool IsRelevantPath(string path, WatcherChangeTypes changeType) { if (configDirectories.Contains(path) || Directory.Exists(path)) { return changeType != WatcherChangeTypes.Changed; } return IsConfigFile(path); } private static void OnFileChanged(object sender, FileSystemEventArgs args) { if (!((Object)(object)BuildPiecesCustomized.instance == (Object)null) && BuildPiecesCustomized.modEnabled.Value && (sender == pluginWatcher || sender == configWatcher) && (IsRelevantPath(args.FullPath, args.ChangeType) || (args is RenamedEventArgs e && IsRelevantPath(e.OldFullPath, args.ChangeType)))) { QueueReload(); } } private static void OnWatcherError(object sender, ErrorEventArgs args) { if (!((Object)(object)BuildPiecesCustomized.instance == (Object)null) && BuildPiecesCustomized.modEnabled.Value && (sender == pluginWatcher || sender == configWatcher)) { BuildPiecesCustomized.LogWarning("Piece configuration watcher lost file notifications: " + args.GetException()?.Message); QueueReload(); } } private static void QueueReload(bool reapply = false) { if (!((Object)(object)BuildPiecesCustomized.instance == (Object)null)) { forceApply |= reapply; reloadAfter = Time.realtimeSinceStartup + 0.25f; if (pendingReload == null) { pendingReload = ((MonoBehaviour)BuildPiecesCustomized.instance).StartCoroutine(ReloadAfterChanges()); } } } private static IEnumerator ReloadAfterChanges() { WaitForSecondsRealtime delay = new WaitForSecondsRealtime(0.25f); try { string error = null; for (int attempt = 0; attempt < 3; attempt++) { do { yield return delay; } while (Time.realtimeSinceStartup < reloadAfter); if (BuildPiecesCustomized.ReadConfigs(out error)) { if (forceApply) { PiecePatches.UpdatePiecesProperties(); } yield break; } } BuildPiecesCustomized.LogWarning($"Keeping the previous piece configuration after {3} read attempts: {error}"); if (forceApply) { PiecePatches.UpdatePiecesProperties(); } } finally { pendingReload = null; forceApply = false; } } internal static void RememberFiles(IEnumerable<FileInfo> files) { configDirectories.Clear(); foreach (FileInfo file in files) { if (IsConfigFile(file.FullName)) { DirectoryInfo directoryInfo = file.Directory; while (directoryInfo != null && configDirectories.Add(directoryInfo.FullName)) { directoryInfo = directoryInfo.Parent; } } } } internal static void Dispose() { DisposeWatcher(ref pluginWatcher); DisposeWatcher(ref configWatcher); if (pendingReload != null && (Object)(object)BuildPiecesCustomized.instance != (Object)null) { ((MonoBehaviour)BuildPiecesCustomized.instance).StopCoroutine(pendingReload); } pendingReload = null; forceApply = false; configDirectories.Clear(); } private static void DisposeWatcher(ref FileSystemWatcher watcher) { if (watcher != null) { watcher.EnableRaisingEvents = false; watcher.Changed -= OnFileChanged; watcher.Created -= OnFileChanged; watcher.Deleted -= OnFileChanged; watcher.Renamed -= OnFileChanged; watcher.Error -= OnWatcherError; watcher.Dispose(); watcher = null; } } } internal sealed class BuildUiSnapshot { private sealed class Entry { private readonly string name; private readonly string description; private readonly UsageTagFlags usage; private readonly CraftingStation station; private readonly Requirement[] requirements; internal Entry(Piece piece) { //IL_0022: 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) name = piece.m_name; description = piece.m_description; usage = piece.m_usage; station = piece.m_craftingStation; requirements = PieceRequirements.Copy(piece.m_resources); } internal bool Matches(Piece piece) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) return name == piece.m_name && description == piece.m_description && usage == piece.m_usage && (Object)(object)station == (Object)(object)piece.m_craftingStation && PieceRequirements.Equal(requirements, piece.m_resources); } } private readonly PieceTable table; private readonly Dictionary<Piece, Entry> entries = new Dictionary<Piece, Entry>(); private readonly HashSet<Piece> available; private readonly HashSet<Piece> enabled; private BuildUiSnapshot(PieceTable table) { this.table = table; available = new HashSet<Piece>(table.m_availablePieces); enabled = new HashSet<Piece>(table.m_enabledPieces); foreach (Piece item in enabled) { if ((Object)(object)item != (Object)null) { entries[item] = new Entry(item); } } foreach (Piece item2 in available) { if ((Object)(object)item2 != (Object)null && !entries.ContainsKey(item2)) { entries[item2] = new Entry(item2); } } } internal static BuildUiSnapshot Capture() { Player localPlayer = Player.m_localPlayer; BuildUi val = (((Object)(object)Hud.instance != (Object)null) ? Hud.instance.m_buildUi : null); PieceTable val2 = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetBuildTool() : null); return ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy && (Object)(object)val2 != (Object)null && (Object)(object)val.m_currentBuildTool == (Object)(object)val2) ? new BuildUiSnapshot(val2) : null; } private bool Matches(PieceTable current) { if ((Object)(object)current != (Object)(object)table || !available.SetEquals(current.m_availablePieces) || !enabled.SetEquals(current.m_enabledPieces)) { return false; } foreach (KeyValuePair<Piece, Entry> entry in entries) { if ((Object)(object)entry.Key == (Object)null || !entry.Value.Matches(entry.Key)) { return false; } } return true; } internal static void Refresh(BuildUiSnapshot before) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { localPlayer.UpdateAvailablePiecesList(); BuildUi val = (((Object)(object)Hud.instance != (Object)null) ? Hud.instance.m_buildUi : null); PieceTable buildTool = localPlayer.GetBuildTool(); if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy && !((Object)(object)buildTool == (Object)null) && !((Object)(object)val.m_currentBuildTool != (Object)(object)buildTool) && (before == null || !before.Matches(buildTool))) { val.UpdateTagButtons(true); } } } } internal static class PieceRequirements { internal static Requirement[] Copy(Requirement[] source) { //IL_003d: 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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown if (source == null || source.Length == 0) { return Array.Empty<Requirement>(); } Requirement[] array = (Requirement[])(object)new Requirement[source.Length]; for (int i = 0; i < source.Length; i++) { Requirement val = source[i]; if (val != null) { array[i] = new Requirement { m_resItem = val.m_resItem, m_amount = val.m_amount, m_recover = val.m_recover, m_amountPerLevel = val.m_amountPerLevel, m_extraAmountOnlyOneIngredient = val.m_extraAmountOnlyOneIngredient, m_upgraderResource = val.m_upgraderResource }; } } return array; } internal static bool Equal(Requirement[] first, Requirement[] second) { if (first == second) { return true; } if (first == null || second == null || first.Length != second.Length) { return false; } for (int i = 0; i < first.Length; i++) { Requirement val = first[i]; Requirement val2 = second[i]; if (val != val2 && (val == null || val2 == null || (Object)(object)val.m_resItem != (Object)(object)val2.m_resItem || val.m_amount != val2.m_amount || val.m_recover != val2.m_recover || val.m_amountPerLevel != val2.m_amountPerLevel || val.m_extraAmountOnlyOneIngredient != val2.m_extraAmountOnlyOneIngredient || val.m_upgraderResource != val2.m_upgraderResource)) { return false; } } return true; } } [Serializable] internal class CustomPieceData { private readonly struct ResourceSpec { internal readonly string Name; internal readonly int Amount; internal readonly bool Recover; internal ResourceSpec(string name, int amount, bool recover) { Name = name; Amount = amount; Recover = recover; } } public string? prefabName; public string? name; public string? description; public bool? enabled; public List<string>? usageTags; public ComfortGroup? comfortGroup; public int? comfort; public bool? groundOnly; public bool? cultivatedGroundOnly; public bool? waterPiece; public bool? clipGround; public bool? clipEverything; public bool? noInWater; public bool? notOnWood; public bool? notOnTiltingSurface; public bool? inCeilingOnly; public bool? notOnFloor; public bool? noClipping; public bool? onlyInTeleportArea; public bool? allowedInDungeons; public bool? allowedInDeepSnow; public bool? requireDeepSnow; public float? spaceRequirement; public bool? canBeRemoved; public bool? allowRotatedOverlap; public bool? vegetationGroundOnly; public float? blockRadius; public int? extraPlacementDistance; public string? station; public Biome? onlyInBiome; public bool? targetNonPlayerBuilt; public bool? primaryTarget; public bool? randomTarget; public float? health; public bool? noRoofWear; public float? roofCheckOffset; public bool? noSupportWear; public bool? snowDamageImmune; public MaterialType? materialType; public bool? supports; public float? hitNoise; public float? destroyNoise; public bool? autoCreateFragments; public List<string>? damageModifiers; public bool? ashDamageImmune; public bool? ashDamageResist; public bool? burnable; public int? minToolTier; public bool? triggerPrivateArea; public string? requiredPersistentEvent; public bool? takeDamageIfInsideEvent; public float? eventDamage; public float? eventDamageDeviation; public Biome? requiredBiome; public float? outsideRequiredBiomeDamage; public List<string>? resources; [JsonProperty(/*Could not decode attribute arguments.*/)] [YamlIgnore] internal List<string>? usageTagsAdditions; [NonSerialized] [JsonIgnore] [YamlIgnore] private UsageTagFlags? capturedUsageTags; [NonSerialized] [JsonIgnore] [YamlIgnore] private UsageTagFlags? parsedUsageTags; [NonSerialized] [JsonIgnore] [YamlIgnore] private UsageTagFlags parsedUsageTagAdditions; [NonSerialized] [JsonIgnore] [YamlIgnore] internal Piece? sourcePrefab; [NonSerialized] [JsonIgnore] [YamlIgnore] private Requirement[]? capturedResources; [NonSerialized] [JsonIgnore] [YamlIgnore] private CraftingStation? capturedStation; [NonSerialized] [JsonIgnore] [YamlIgnore] private bool hasCapturedStation; [NonSerialized] [JsonIgnore] [YamlIgnore] private bool reportedMissingStation; [NonSerialized] [JsonIgnore] [YamlIgnore] private string? reportedMissingResource; [NonSerialized] [JsonIgnore] [YamlIgnore] private bool parsedResourcesValid = true; [NonSerialized] [JsonIgnore] private string[]? parsedResourceSource; [NonSerialized] [JsonIgnore] private ResourceSpec[] parsedResources = Array.Empty<ResourceSpec>(); [NonSerialized] [JsonIgnore] private string[]? parsedDamageSource; [NonSerialized] [JsonIgnore] private DamageModPair[] parsedDamageModifiers = Array.Empty<DamageModPair>(); private static bool Matches(List<string> source, string[]? cached) { if (cached == null || source.Count != cached.Length) { return false; } for (int i = 0; i < cached.Length; i++) { if (!string.Equals(source[i], cached[i], StringComparison.Ordinal)) { return false; } } return true; } private ResourceSpec[] GetResourceSpecs() { if (resources == null) { return Array.Empty<ResourceSpec>(); } if (Matches(resources, parsedResourceSource)) { return parsedResources; } List<ResourceSpec> list = new List<ResourceSpec>(resources.Count); parsedResourcesValid = true; foreach (string resource in resources) { string[] array = resource?.Split(new char[1] { ':' }) ?? Array.Empty<string>(); int result = 1; bool result2 = true; if (array.Length == 0 || array.Length > 3 || string.IsNullOrWhiteSpace(array[0]) || (array.Length > 1 && (!int.TryParse(array[1], out result) || result < 0)) || (array.Length > 2 && !bool.TryParse(array[2], out result2))) { BuildPiecesCustomized.LogWarning("Invalid resource entry for " + prefabName + ": " + resource + ". The complete resource override will be ignored."); parsedResourcesValid = false; } else { list.Add(new ResourceSpec(array[0].Trim(), result, result2)); } } parsedResources = list.ToArray(); parsedResourceSource = resources.ToArray(); return parsedResources; } private DamageModPair[] GetDamageModifierSpecs() { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: 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: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) if (damageModifiers == null) { return Array.Empty<DamageModPair>(); } if (Matches(damageModifiers, parsedDamageSource)) { return parsedDamageModifiers; } List<DamageModPair> list = new List<DamageModPair>(damageModifiers.Count); foreach (string damageModifier in damageModifiers) { string[] array = damageModifier?.Split(new char[1] { ':' }) ?? Array.Empty<string>(); if (array.Length == 2 && Enum.TryParse<DamageType>(array[0], out DamageType result) && Enum.TryParse<DamageModifier>(array[1], out DamageModifier result2)) { list.Add(new DamageModPair { m_type = result, m_modifier = result2 }); } } parsedDamageModifiers = list.ToArray(); parsedDamageSource = damageModifiers.ToArray(); return parsedDamageModifiers; } internal void PatchPiece(Piece piece, bool skipResources = false, bool skipStation = false) { //IL_006b: 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) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Invalid comparison between Unknown and I4 //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_04ff: Unknown result type (might be due to invalid IL or missing references) //IL_0504: Unknown result type (might be due to invalid IL or missing references) //IL_085a: Unknown result type (might be due to invalid IL or missing references) //IL_085f: Unknown result type (might be due to invalid IL or missing references) //IL_089e: Unknown result type (might be due to invalid IL or missing references) //IL_08a3: Unknown result type (might be due to invalid IL or missing references) //IL_08d0: Unknown result type (might be due to invalid IL or missing references) //IL_08d5: Unknown result type (might be due to invalid IL or missing references) //IL_08d8: Unknown result type (might be due to invalid IL or missing references) //IL_08da: Unknown result type (might be due to invalid IL or missing references) //IL_08df: Unknown result type (might be due to invalid IL or missing references) //IL_08e1: Unknown result type (might be due to invalid IL or missing references) //IL_08e3: Unknown result type (might be due to invalid IL or missing references) //IL_08e8: Unknown result type (might be due to invalid IL or missing references) //IL_08f2: Unknown result type (might be due to invalid IL or missing references) //IL_08f4: Unknown result type (might be due to invalid IL or missing references) //IL_08f6: Unknown result type (might be due to invalid IL or missing references) //IL_08f8: Unknown result type (might be due to invalid IL or missing references) //IL_08fa: Unknown result type (might be due to invalid IL or missing references) //IL_08fe: Invalid comparison between Unknown and I4 //IL_093d: Unknown result type (might be due to invalid IL or missing references) //IL_0944: Invalid comparison between Unknown and I4 //IL_0900: Unknown result type (might be due to invalid IL or missing references) //IL_0903: Invalid comparison between Unknown and I4 //IL_095c: Unknown result type (might be due to invalid IL or missing references) //IL_0963: Invalid comparison between Unknown and I4 //IL_0946: Unknown result type (might be due to invalid IL or missing references) //IL_094a: Invalid comparison between Unknown and I4 //IL_092a: Unknown result type (might be due to invalid IL or missing references) //IL_092e: Invalid comparison between Unknown and I4 //IL_0905: Unknown result type (might be due to invalid IL or missing references) //IL_0908: Unknown result type (might be due to invalid IL or missing references) //IL_091e: Expected I4, but got Unknown //IL_09cc: Unknown result type (might be due to invalid IL or missing references) //IL_09ce: Unknown result type (might be due to invalid IL or missing references) //IL_09b6: Unknown result type (might be due to invalid IL or missing references) //IL_09b8: Unknown result type (might be due to invalid IL or missing references) //IL_09a0: Unknown result type (might be due to invalid IL or missing references) //IL_09a2: Unknown result type (might be due to invalid IL or missing references) //IL_0974: Unknown result type (might be due to invalid IL or missing references) //IL_0976: Unknown result type (might be due to invalid IL or missing references) //IL_097f: Unknown result type (might be due to invalid IL or missing references) //IL_0981: Unknown result type (might be due to invalid IL or missing references) //IL_098a: Unknown result type (might be due to invalid IL or missing references) //IL_098c: Unknown result type (might be due to invalid IL or missing references) //IL_0967: Unknown result type (might be due to invalid IL or missing references) //IL_096e: Invalid comparison between Unknown and I4 //IL_094e: Unknown result type (might be due to invalid IL or missing references) //IL_0955: Invalid comparison between Unknown and I4 //IL_0932: Unknown result type (might be due to invalid IL or missing references) //IL_0936: Invalid comparison between Unknown and I4 //IL_0920: Unknown result type (might be due to invalid IL or missing references) //IL_0923: Invalid comparison between Unknown and I4 //IL_09d7: Unknown result type (might be due to invalid IL or missing references) //IL_09d9: Unknown result type (might be due to invalid IL or missing references) //IL_09c1: Unknown result type (might be due to invalid IL or missing references) //IL_09c3: Unknown result type (might be due to invalid IL or missing references) //IL_09ab: Unknown result type (might be due to invalid IL or missing references) //IL_09ad: Unknown result type (might be due to invalid IL or missing references) //IL_0995: Unknown result type (might be due to invalid IL or missing references) //IL_0997: Unknown result type (might be due to invalid IL or missing references) if (enabled.HasValue) { piece.m_enabled = enabled.Value; } if (name != null) { piece.m_name = name; } if (description != null) { piece.m_description = description; } if (capturedUsageTags.HasValue) { piece.m_usage = capturedUsageTags.Value; } else if (parsedUsageTags.HasValue) { piece.m_usage = parsedUsageTags.Value; } if ((int)parsedUsageTagAdditions > 0) { piece.m_usage |= parsedUsageTagAdditions; } if (comfort.HasValue) { piece.m_comfort = comfort.Value; } if (comfortGroup.HasValue) { piece.m_comfortGroup = comfortGroup.Value; } if (groundOnly.HasValue) { piece.m_groundOnly = groundOnly.Value; } if (cultivatedGroundOnly.HasValue) { piece.m_cultivatedGroundOnly = cultivatedGroundOnly.Value; } if (waterPiece.HasValue) { piece.m_waterPiece = waterPiece.Value; } if (clipGround.HasValue) { piece.m_clipGround = clipGround.Value; } if (clipEverything.HasValue) { piece.m_clipEverything = clipEverything.Value; } if (noInWater.HasValue) { piece.m_noInWater = noInWater.Value; } if (notOnWood.HasValue) { piece.m_notOnWood = notOnWood.Value; } if (notOnTiltingSurface.HasValue) { piece.m_notOnTiltingSurface = notOnTiltingSurface.Value; } if (inCeilingOnly.HasValue) { piece.m_inCeilingOnly = inCeilingOnly.Value; } if (notOnFloor.HasValue) { piece.m_notOnFloor = notOnFloor.Value; } if (noClipping.HasValue) { piece.m_noClipping = noClipping.Value; } if (onlyInTeleportArea.HasValue) { piece.m_onlyInTeleportArea = onlyInTeleportArea.Value; } if (allowedInDungeons.HasValue) { piece.m_allowedInDungeons = allowedInDungeons.Value; } if (allowedInDeepSnow.HasValue) { piece.m_allowedInDeepSnow = allowedInDeepSnow.Value; } if (requireDeepSnow.HasValue) { piece.m_requireDeepSnow = requireDeepSnow.Value; } if (spaceRequirement.HasValue) { piece.m_spaceRequirement = spaceRequirement.Value; } if (canBeRemoved.HasValue) { piece.m_canBeRemoved = canBeRemoved.Value; } if (allowRotatedOverlap.HasValue) { piece.m_allowRotatedOverlap = allowRotatedOverlap.Value; } if (vegetationGroundOnly.HasValue) { piece.m_vegetationGroundOnly = vegetationGroundOnly.Value; } if (blockRadius.HasValue) { piece.m_blockRadius = blockRadius.Value; } if (extraPlacementDistance.HasValue) { piece.m_extraPlacementDistance = extraPlacementDistance.Value; } if (!skipStation && hasCapturedStation) { piece.m_craftingStation = (CraftingStation)(((Object)(object)capturedStation != (Object)null || string.IsNullOrEmpty(station)) ? ((object)capturedStation) : ((object)GeneralExtensions.GetValueSafe<string, CraftingStation>(BuildPiecesCustomized.craftingStations, station))); } else if (!skipStation && station != null) { CraftingStation value; if (string.IsNullOrWhiteSpace(station)) { piece.m_craftingStation = null; } else if (BuildPiecesCustomized.craftingStations.TryGetValue(station, out value) && (Object)(object)value != (Object)null) { piece.m_craftingStation = value; reportedMissingStation = false; } else if (!reportedMissingStation) { reportedMissingStation = true; BuildPiecesCustomized.LogWarning("Unknown crafting station '" + station + "' for '" + (prefabName ?? ((Object)piece).name) + "'. Keeping the existing station requirement."); } } if (onlyInBiome.HasValue) { piece.m_onlyInBiome = onlyInBiome.Value; } if (targetNonPlayerBuilt.HasValue) { piece.m_targetNonPlayerBuilt = targetNonPlayerBuilt.Value; } if (primaryTarget.HasValue) { ((StaticTarget)piece).m_primaryTarget = primaryTarget.Value; } if (randomTarget.HasValue) { ((StaticTarget)piece).m_randomTarget = randomTarget.Value; } if (!skipResources) { if (capturedResources != null) { if (!PieceRequirements.Equal(piece.m_resources, capturedResources)) { piece.m_resources = PieceRequirements.Copy(capturedResources); } } else if (resources != null) { ApplyResourceRequirements(piece); } } WearNTear wearNTearComponent = BuildPiecesCustomized.GetWearNTearComponent(piece); if (wearNTearComponent == null) { return; } if (health.HasValue) { wearNTearComponent.m_health = health.Value; } if (noRoofWear.HasValue) { wearNTearComponent.m_noRoofWear = noRoofWear.Value; } if (roofCheckOffset.HasValue) { wearNTearComponent.m_roofCheckOffset = roofCheckOffset.Value; } if (noSupportWear.HasValue) { wearNTearComponent.m_noSupportWear = noSupportWear.Value; } if (snowDamageImmune.HasValue) { wearNTearComponent.m_snowDamageImmune = snowDamageImmune.Value; } if (supports.HasValue) { wearNTearComponent.m_supports = supports.Value; } if (hitNoise.HasValue) { wearNTearComponent.m_hitNoise = hitNoise.Value; } if (destroyNoise.HasValue) { wearNTearComponent.m_destroyNoise = destroyNoise.Value; } if (autoCreateFragments.HasValue) { wearNTearComponent.m_autoCreateFragments = autoCreateFragments.Value; } if (ashDamageImmune.HasValue) { wearNTearComponent.m_ashDamageImmune = ashDamageImmune.Value; } if (ashDamageResist.HasValue) { wearNTearComponent.m_ashDamageResist = ashDamageResist.Value; } if (burnable.HasValue) { wearNTearComponent.m_burnable = burnable.Value; } if (minToolTier.HasValue) { wearNTearComponent.m_minToolTier = minToolTier.Value; } if (triggerPrivateArea.HasValue) { wearNTearComponent.m_triggerPrivateArea = triggerPrivateArea.Value; } if (requiredPersistentEvent != null) { wearNTearComponent.m_requiredPersistentEvent = requiredPersistentEvent; } if (takeDamageIfInsideEvent.HasValue) { wearNTearComponent.m_takeDamageIfInsideEvent = takeDamageIfInsideEvent.Value; } if (eventDamage.HasValue) { wearNTearComponent.m_eventDamage = eventDamage.Value; } if (eventDamageDeviation.HasValue) { wearNTearComponent.m_eventDamageDeviation = eventDamageDeviation.Value; } if (requiredBiome.HasValue) { wearNTearComponent.m_requiredBiome = requiredBiome.Value; } if (outsideRequiredBiomeDamage.HasValue) { wearNTearComponent.m_outsideRequiredBiomeDamage = outsideRequiredBiomeDamage.Value; } if (materialType.HasValue) { wearNTearComponent.m_materialType = materialType.Value; } if (damageModifiers == null) { return; } DamageModPair[] damageModifierSpecs = GetDamageModifierSpecs(); foreach (DamageModPair val in damageModifierSpecs) { DamageType type = val.m_type; DamageModifier modifier = val.m_modifier; ref DamageModifiers damages = ref wearNTearComponent.m_damages; DamageType val2 = type; DamageType val3 = val2; if ((int)val3 <= 32) { if ((int)val3 <= 8) { switch (val3 - 1) { case 0: damages.m_blunt = modifier; continue; case 1: damages.m_slash = modifier; continue; case 3: damages.m_pierce = modifier; continue; case 2: continue; } if ((int)val3 == 8) { damages.m_chop = modifier; } } else if ((int)val3 != 16) { if ((int)val3 == 32) { damages.m_fire = modifier; } } else { damages.m_pickaxe = modifier; } } else if ((int)val3 <= 128) { if ((int)val3 != 64) { if ((int)val3 == 128) { damages.m_lightning = modifier; } } else { damages.m_frost = modifier; } } else if ((int)val3 != 256) { if ((int)val3 == 512) { damages.m_spirit = modifier; } } else { damages.m_poison = modifier; } } } private void ApplyResourceRequirements(Piece piece) { //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Expected O, but got Unknown ResourceSpec[] resourceSpecs = GetResourceSpecs(); if (!parsedResourcesValid) { return; } Requirement[] array = piece.m_resources; bool flag = array != null && array.Length == resourceSpecs.Length; for (int i = 0; i < resourceSpecs.Length; i++) { ResourceSpec resourceSpec = resourceSpecs[i]; object obj; if (!((Object)(object)ObjectDB.instance != (Object)null)) { obj = null; } else { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(resourceSpec.Name); obj = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null); } ItemDrop val = (ItemDrop)obj; if ((Object)(object)val == (Object)null) { if (reportedMissingResource != resourceSpec.Name) { reportedMissingResource = resourceSpec.Name; BuildPiecesCustomized.LogWarning("Unknown resource '" + resourceSpec.Name + "' for '" + (prefabName ?? ((Object)piece).name) + "'. Keeping the complete existing resource requirements."); } return; } if (flag) { Requirement val2 = array[i]; flag = val2 != null && (Object)(object)val2.m_resItem == (Object)(object)val && val2.m_amount == resourceSpec.Amount && val2.m_recover == resourceSpec.Recover; } } reportedMissingResource = null; if (!flag) { Requirement[] array2 = (Requirement[])(object)new Requirement[resourceSpecs.Length]; for (int j = 0; j < resourceSpecs.Length; j++) { ResourceSpec resourceSpec2 = resourceSpecs[j]; array2[j] = new Requirement { m_resItem = ObjectDB.instance.GetItemPrefab(resourceSpec2.Name).GetComponent<ItemDrop>(), m_amount = resourceSpec2.Amount, m_recover = resourceSpec2.Recover }; } piece.m_resources = array2; } } internal void PrepareUsageTags(string sourceName) { //IL_000f: 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_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) parsedUsageTags = null; parsedUsageTagAdditions = (UsageTagFlags)0; if (usageTags != null) { if (PieceUsageTags.TryParseTags(usageTags, out var result, out var canonicalNames, out var invalidValue)) { usageTags = canonicalNames; parsedUsageTags = result; } else { BuildPiecesCustomized.LogWarning("Invalid usage tag '" + invalidValue + "' in '" + sourceName + "'. The configured usageTags value will be ignored."); usageTags = null; } } if (usageTagsAdditions != null) { if (PieceUsageTags.TryParseTags(usageTagsAdditions, out var result2, out var canonicalNames2, out var invalidValue2)) { usageTagsAdditions = canonicalNames2; parsedUsageTagAdditions = result2; return; } BuildPiecesCustomized.LogWarning("Invalid usage tag '" + invalidValue2 + "' in the bulk usage tag override for '" + sourceName + "'. The tag additions will be ignored."); usageTagsAdditions = null; } } internal void SaveToDirectory(string directory) { Directory.CreateDirectory(directory); string path = Path.Combine(directory, prefabName + "." + (BuildPiecesCustomized.saveAsYAML.Value ? "yaml" : "json")); File.WriteAllText(path, BuildPiecesCustomized.saveAsYAML.Value ? BuildPiecesCustomized.YamlSerializer.Serialize((object)this) : JsonConvert.SerializeObject((object)this, (Formatting)1)); } internal CustomPieceData() { } internal CustomPieceData(Piece piece) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_0478: Unknown result type (might be due to invalid IL or missing references) //IL_04cc: Unknown result type (might be due to invalid IL or missing references) //IL_0512: Unknown result type (might be due to invalid IL or missing references) //IL_0558: Unknown result type (might be due to invalid IL or missing references) //IL_059e: Unknown result type (might be due to invalid IL or missing references) //IL_05e5: Unknown result type (might be due to invalid IL or missing references) //IL_062c: Unknown result type (might be due to invalid IL or missing references) //IL_0673: Unknown result type (might be due to invalid IL or missing references) //IL_06bd: Unknown result type (might be due to invalid IL or missing references) //IL_0707: Unknown result type (might be due to invalid IL or missing references) //IL_0751: Unknown result type (might be due to invalid IL or missing references) sourcePrefab = piece; prefabName = ((Object)((Component)piece).gameObject).name; enabled = piece.m_enabled; name = piece.m_name; description = piece.m_description; capturedUsageTags = piece.m_usage; usageTags = (PieceUsageTags.HasUnsupportedBits(piece.m_usage) ? null : PieceUsageTags.GetNames(piece.m_usage)); comfortGroup = piece.m_comfortGroup; comfort = piece.m_comfort; groundOnly = piece.m_groundOnly; cultivatedGroundOnly = piece.m_cultivatedGroundOnly; waterPiece = piece.m_waterPiece; clipGround = piece.m_clipGround; clipEverything = piece.m_clipEverything; noInWater = piece.m_noInWater; notOnWood = piece.m_notOnWood; notOnTiltingSurface = piece.m_notOnTiltingSurface; inCeilingOnly = piece.m_inCeilingOnly; notOnFloor = piece.m_notOnFloor; noClipping = piece.m_noClipping; onlyInTeleportArea = piece.m_onlyInTeleportArea; allowedInDungeons = piece.m_allowedInDungeons; allowedInDeepSnow = piece.m_allowedInDeepSnow; requireDeepSnow = piece.m_requireDeepSnow; spaceRequirement = piece.m_spaceRequirement; canBeRemoved = piece.m_canBeRemoved; capturedStation = piece.m_craftingStation; hasCapturedStation = true; station = (Object.op_Implicit((Object)(object)piece.m_craftingStation) ? piece.m_craftingStation.m_name : ""); onlyInBiome = piece.m_onlyInBiome; allowRotatedOverlap = piece.m_allowRotatedOverlap; vegetationGroundOnly = piece.m_vegetationGroundOnly; blockRadius = piece.m_blockRadius; extraPlacementDistance = piece.m_extraPlacementDistance; targetNonPlayerBuilt = piece.m_targetNonPlayerBuilt; primaryTarget = ((StaticTarget)piece).m_primaryTarget; randomTarget = ((StaticTarget)piece).m_randomTarget; capturedResources = PieceRequirements.Copy(piece.m_resources); resources = new List<string>(); Requirement[] array = capturedResources; foreach (Requirement val in array) { if (val != null && (Object)(object)val.m_resItem != (Object)null) { resources.Add($"{((Object)val.m_resItem).name}:{val.m_amount}:{val.m_recover}"); } } WearNTear wearNTearComponent = BuildPiecesCustomized.GetWearNTearComponent(piece); if ((Object)(object)wearNTearComponent != (Object)null) { health = wearNTearComponent.m_health; noRoofWear = wearNTearComponent.m_noRoofWear; roofCheckOffset = wearNTearComponent.m_roofCheckOffset; noSupportWear = wearNTearComponent.m_noSupportWear; snowDamageImmune = wearNTearComponent.m_snowDamageImmune; materialType = wearNTearComponent.m_materialType; supports = wearNTearComponent.m_supports; hitNoise = wearNTearComponent.m_hitNoise; destroyNoise = wearNTearComponent.m_destroyNoise; autoCreateFragments = wearNTearComponent.m_autoCreateFragments; ashDamageImmune = wearNTearComponent.m_ashDamageImmune; ashDamageResist = wearNTearComponent.m_ashDamageResist; burnable = wearNTearComponent.m_burnable; minToolTier = wearNTearComponent.m_minToolTier; triggerPrivateArea = wearNTearComponent.m_triggerPrivateArea; requiredPersistentEvent = wearNTearComponent.m_requiredPersistentEvent; takeDamageIfInsideEvent = wearNTearComponent.m_takeDamageIfInsideEvent; eventDamage = wearNTearComponent.m_eventDamage; eventDamageDeviation = wearNTearComponent.m_eventDamageDeviation; requiredBiome = wearNTearComponent.m_requiredBiome; outsideRequiredBiomeDamage = wearNTearComponent.m_outsideRequiredBiomeDamage; damageModifiers = new List<string> { Enum.GetName(typeof(DamageType), (object)(DamageType)1) + ":" + Enum.GetName(typeof(DamageModifier), wearNTearComponent.m_damages.m_blunt), Enum.GetName(typeof(DamageType), (object)(DamageType)2) + ":" + Enum.GetName(typeof(DamageModifier), wearNTearComponent.m_damages.m_slash), Enum.GetName(typeof(DamageType), (object)(DamageType)4) + ":" + Enum.GetName(typeof(DamageModifier), wearNTearComponent.m_damages.m_pierce), Enum.GetName(typeof(DamageType), (object)(DamageType)8) + ":" + Enum.GetName(typeof(DamageModifier), wearNTearComponent.m_damages.m_chop), Enum.GetName(typeof(DamageType), (object)(DamageType)16) + ":" + Enum.GetName(typeof(DamageModifier), wearNTearComponent.m_damages.m_pickaxe), Enum.GetName(typeof(DamageType), (object)(DamageType)32) + ":" + Enum.GetName(typeof(DamageModifier), wearNTearComponent.m_damages.m_fire), Enum.GetName(typeof(DamageType), (object)(DamageType)64) + ":" + Enum.GetName(typeof(DamageModifier), wearNTearComponent.m_damages.m_frost), Enum.GetName(typeof(DamageType), (object)(DamageType)128) + ":" + Enum.GetName(typeof(DamageModifier), wearNTearComponent.m_damages.m_lightning), Enum.GetName(typeof(DamageType), (object)(DamageType)256) + ":" + Enum.GetName(typeof(DamageModifier), wearNTearComponent.m_damages.m_poison), Enum.GetName(typeof(DamageType), (object)(DamageType)512) + ":" + Enum.GetName(typeof(DamageModifier), wearNTearComponent.m_damages.m_spirit) }; } } internal static CustomPieceData? GetByPieceName(string pieceName) { if (!Object.op_Implicit((Object)(object)ObjectDB.instance)) { return null; } GameObject val = ((IEnumerable<GameObject>)GetBuildPieces()).FirstOrDefault((Func<GameObject, bool>)((GameObject buildPiece) => ((Object)buildPiece).name == pieceName)); if ((Object)(object)val == (Object)null) { return null; } Piece piece = default(Piece); if (!val.TryGetComponent<Piece>(ref piece)) { return null; } return new CustomPieceData(piece); } internal static List<GameObject> GetBuildPieces() { List<GameObject> list = new List<GameObject>(); if ((Object)(object)ObjectDB.instance == (Object)null) { return list; } HashSet<GameObject> hashSet = new HashSet<GameObject>(); HashSet<string> hashSet2 = new HashSet<string>(from c in BuildPiecesCustomized.toolsToPatchPieces.Value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select c.Trim()); foreach (string item in hashSet2) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item); PieceTable val = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared?.m_buildPieces; if ((Object)(object)val == (Object)null || val.m_pieces == null) { continue; } foreach (GameObject piece in val.m_pieces) { if ((Object)(object)piece != (Object)null && hashSet.Add(piece)) { list.Add(piece); } } } return list; } } internal static class DocGen { [HarmonyPatch(typeof(ObjectDB), "Awake")] [HarmonyPriority(0)] private static class ObjectDB_Awake_DocGen { private static void Postfix() { if (BuildPiecesCustomized.modEnabled.Value) { GenerateDocumentationFile(); } } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] [HarmonyPriority(0)] private static class ObjectDB_CopyOtherDB_DocGen { private static void Postfix() { if (BuildPiecesCustomized.modEnabled.Value) { GenerateDocumentationFile(); } } } [HarmonyPatch(typeof(ZoneSystem), "Start")] [HarmonyPriority(0)] private static class ZoneSystem_Start_DocGen { private static void Postfix() { if (BuildPiecesCustomized.modEnabled.Value) { GenerateDocumentationFile(); } } } private static readonly StringBuilder sb = new StringBuilder(); internal const string filename = "Pieces and properties.md"; internal static void GenerateDocumentationFile() { string text = Path.Combine(BuildPiecesCustomized.configDirectory.FullName, "Pieces and properties.md"); try { Directory.CreateDirectory(BuildPiecesCustomized.configDirectory.FullName); string fileText = GetFileText(); if (!File.Exists(text) || !string.Equals(File.ReadAllText(text), fileText, StringComparison.Ordinal)) { File.WriteAllText(text, fileText); } } catch (Exception ex) { BuildPiecesCustomized.LogWarning("Error when writing file (" + text + ")! Error: " + ex.Message); } } private static void LogUsageTags() { sb.AppendLine("`usageTags` is an optional list of built-in Valheim Hammer usage tags."); sb.AppendLine("If the property is omitted, the original usage tags are preserved."); sb.AppendLine("If the property is present, the list fully replaces the current usage tags. An empty list clears them."); sb.AppendLine("Tag names are case-insensitive when loading, but generated files use the canonical names below."); sb.AppendLine("Numeric values and custom runtime tags added by other mods are not supported."); sb.AppendLine("Unsupported custom tags are preserved when usageTags is omitted, but a usageTags replacement contains built-in tags only."); sb.AppendLine(); sb.AppendLine("| Value | In-game name |"); sb.AppendLine("|---|---|"); foreach (PieceUsageTags.Definition definition in PieceUsageTags.Definitions) { sb.AppendLine("| " + definition.Name + " | " + PieceUsageTags.GetLocalizedDisplayName(definition) + " |"); } } private static void LogBulkUsageTagFormat() { sb.AppendLine("Use a file named `Piece usage tags.yaml`, `Piece usage tags.yml`, or `Piece usage tags.json` for centralized usage tag overrides."); sb.AppendLine("The bulk file has higher priority than individual piece files."); sb.AppendLine("The `pieces` section replaces the full tag list for a piece. The `tags` section adds one tag to every listed piece."); sb.AppendLine("Within the bulk file, `pieces` is applied first and `tags` is applied afterwards, so both directions can be mixed safely."); sb.AppendLine(); sb.AppendLine("```yaml"); sb.AppendLine("pieces:"); sb.AppendLine(" wood_door:"); sb.AppendLine(" - Building"); sb.AppendLine(" - Doors"); sb.AppendLine(" custom_piece: []"); sb.AppendLine(); sb.AppendLine("tags:"); sb.AppendLine(" Defense:"); sb.AppendLine(" - h_drawbridge01"); sb.AppendLine(" - hayzestake_01"); sb.AppendLine("```"); sb.AppendLine(); sb.AppendLine("```json"); sb.AppendLine("{"); sb.AppendLine(" \"pieces\": {"); sb.AppendLine(" \"wood_door\": [\"Building\", \"Doors\"],"); sb.AppendLine(" \"custom_piece\": []"); sb.AppendLine(" },"); sb.AppendLine(" \"tags\": {"); sb.AppendLine(" \"Defense\": [\"h_drawbridge01\", \"hayzestake_01\"]"); sb.AppendLine(" }"); sb.AppendLine("}"); sb.AppendLine("```"); } private static string GetFileText() { //IL_0565: Unknown result type (might be due to invalid IL or missing references) sb.Clear(); sb.AppendLine("This documentation is generated automatically. It contains all available pieces and identifiers used to configure pieces."); sb.AppendLine(); sb.AppendLine("# Properties and available values"); sb.AppendLine(); sb.AppendLine("## usageTags - Hammer categories"); LogUsageTags(); sb.AppendLine(); sb.AppendLine("## Piece usage tags bulk file"); LogBulkUsageTagFormat(); sb.AppendLine(); sb.AppendLine("## Deep North and environment properties"); sb.AppendLine("| Property | Description |"); sb.AppendLine("|---|---|"); sb.AppendLine("| `allowedInDeepSnow` | Allows placement in Deep North deep-snow areas that normally reject the piece. |"); sb.AppendLine("| `requireDeepSnow` | Requires Deep North deep snow for placement. |"); sb.AppendLine("| `roofCheckOffset` | Offset used by WearNTear roof detection. |"); sb.AppendLine("| `snowDamageImmune` | Immunity to heavy-snow damage. |"); sb.AppendLine("| `requiredPersistentEvent` | Persistent-event identifier used by WearNTear event damage logic. |"); sb.AppendLine("| `takeDamageIfInsideEvent` | Enables the configured persistent-event damage condition. |"); sb.AppendLine("| `eventDamage`, `eventDamageDeviation` | Base persistent-event damage and random deviation. |"); sb.AppendLine("| `requiredBiome`, `outsideRequiredBiomeDamage` | Biome requirement and damage applied outside that biome. |"); sb.AppendLine(); sb.AppendLine("## comfortGroup"); EnumToList(typeof(ComfortGroup)); sb.AppendLine(); sb.AppendLine("## biome values (`onlyInBiome`, `requiredBiome`)"); EnumToList(typeof(Biome)); sb.AppendLine(); sb.AppendLine("## materialType"); EnumToList(typeof(MaterialType)); sb.AppendLine(); sb.AppendLine("## damageModifiers"); sb.AppendLine(); sb.AppendLine("### type"); EnumToList(typeof(DamageType), noID: true); sb.AppendLine(); sb.AppendLine("### modifier"); EnumToList(typeof(DamageModifier), noID: true); if (Object.op_Implicit((Object)(object)ObjectDB.instance)) { sb.AppendLine(); sb.AppendLine("## station"); Dictionary<string, CraftingStation> dictionary = new Dictionary<string, CraftingStation>(); foreach (Recipe recipe in ObjectDB.instance.m_recipes) { if (!((Object)(object)recipe == (Object)null) && !((Object)(object)recipe.m_craftingStation == (Object)null)) { dictionary[((Object)recipe.m_craftingStation).name] = recipe.m_craftingStation; } } sb.AppendLine("| Prefab | Localized name |"); sb.AppendLine("|---|---|"); foreach (KeyValuePair<string, CraftingStation> item in dictionary) { sb.AppendLine("| " + MarkdownCell(item.Key) + " | " + MarkdownCell(Localization.instance.Localize(item.Value.m_name)) + " |"); } sb.AppendLine(); sb.AppendLine("# Piece prefab names and usage tags"); sb.AppendLine("Pieces are listed in the same order as their build tool."); Piece val = default(Piece); foreach (ItemDrop allItem in ObjectDB.instance.GetAllItems((ItemType)19, "")) { if ((Object)(object)allItem == (Object)null || (Object)(object)allItem.m_itemData.m_shared.m_buildPieces == (Object)null || allItem.m_itemData.m_shared.m_buildPieces.m_pieces.Count == 0) { continue; } sb.AppendLine(); sb.AppendLine("## " + ((Object)allItem).name + " - " + allItem.m_itemData.m_shared.m_name + " - " + Localization.instance.Localize(allItem.m_itemData.m_shared.m_name)); sb.AppendLine("| Prefab | Token | Localized name | usageTags |"); sb.AppendLine("|---|---|---|---|"); foreach (GameObject piece in allItem.m_itemData.m_shared.m_buildPieces.m_pieces) { if (!((Object)(object)piece == (Object)null) && piece.TryGetComponent<Piece>(ref val)) { sb.AppendLine("| " + MarkdownCell(((Object)val).name) + " | " + MarkdownCell(val.m_name) + " | " + MarkdownCell(Localization.instance.Localize(val.m_name)) + " | " + MarkdownCell(PieceUsageTags.FormatForDocumentation(val.m_usage)) + " |"); } } } } return sb.ToString(); } private static void EnumToList(Type enumType, bool noID = false) { if (noID) { sb.AppendLine("| Value |"); sb.AppendLine("|---|"); } else { sb.AppendLine("| ID | Value |"); sb.AppendLine("|---:|---|"); } foreach (object value in Enum.GetValues(enumType)) { sb.AppendLine(noID ? $"| {value} |" : $"| {(int)value} | {value} |"); } } private static string MarkdownCell(string value) { return (value ?? string.Empty).Replace("|", "\\|").Replace("\r", " ").Replace("\n", " "); } } public static class PiecePatches { [HarmonyPatch(typeof(ZNetScene), "OnDestroy")] private static class ZNetScene_OnDestroy_ClearFallbackPieces { private static void Postfix() { CancelPendingUpdate(); ConfigFileWatcher.Dispose(); GlobalPatches.ClearPieceCache(); fallbackPieces.Clear(); fallbackScene = null; } } [HarmonyPatch(typeof(Piece), "Awake")] private static class Piece_Awake_PatchPiece { private static void Postfix(Piece __instance) { if (BuildPiecesCustomized.modEnabled.Value) { PatchPiece(__instance); } } } [HarmonyPatch(typeof(PieceTable), "IsPieceAvailable")] private static class PieceTable_IsPieceAvailable_PieceDisabled { private static void Postfix(PieceTable __instance, Piece piece, ref bool __result) { if (BuildPiecesCustomized.modEnabled.Value && __result && GlobalPatches.HasDisabledPieces && GlobalPatches.IsPieceForceDisabled(piece)) { __result = false; } } } [HarmonyPatch(typeof(PieceTable), "UpdateAvailable")] private static class PieceTable_UpdateAvailable_PieceDisabled { [HarmonyPriority(0)] private static void Postfix(PieceTable __instance) { if (!BuildPiecesCustomized.modEnabled.Value || !GlobalPatches.HasDisabledPieces) { return; } int num = __instance.m_availablePieces.RemoveWhere(GlobalPatches.IsPieceForceDisabled); __instance.m_enabledPieces.RemoveWhere(GlobalPatches.IsPieceForceDisabled); foreach (List<Piece> item in __instance.m_availablePiecesByCategory) { item.RemoveAll(GlobalPatches.IsPieceForceDisabled); } if (num > 0 && BuildPiecesCustomized.loggingEnabled.Value) { BuildPiecesCustomized.LogInfo($"Hidden pieces {((Object)__instance).name}: {num}"); } } } public static class GlobalPatches { public const string allPiecesIdentifier = "AllPieces"; private static readonly string allPiecesListIdentifier = "AllPieces".ToLowerInvariant(); private static bool clipEverything; private static bool allowedInDungeons; private static bool canBeRemoved; private static bool isRoof; private static bool isLeaky; private static bool ashDamageImmune; private static bool snowDamageImmune; private static bool noRoofWear; private static bool noSupportWear; private static HashSet<string> listClipEverything; private static HashSet<string> listAllowedInDungeons; private static HashSet<string> listCanBeRemoved; private static HashSet<string> listIsRoof; private static HashSet<string> listIsLeaky; private static HashSet<string> listAshDamageImmune; private static HashSet<string> listSnowDamageImmune; private static HashSet<string> listNoRoofWear; private static HashSet<string> listNoSupportWear; private static HashSet<string> listDisabled; private static readonly Dictionary<Piece, bool> disabledPieceCache = new Dictionary<Piece, bool>(); private static readonly List<Collider> roofColliders = new List<Collider>(); public static bool HasDisabledPieces => listDisabled != null && listDisabled.Count > 0; internal static void ForgetPiece(Piece piece) { disabledPieceCache.Remove(piece); } internal static void ClearPieceCache() { disabledPieceCache.Clear(); } private static HashSet<string> ConfigToHashSet(string configString) { return new HashSet<string>(from p in configString.Split(new char[1] { ',' }) select p.Trim() into p where !string.IsNullOrWhiteSpace(p) select p, StringComparer.OrdinalIgnoreCase); } public static void UpdateProperties() { listClipEverything = ConfigToHashSet(BuildPiecesCustomized.prefabListClipEverything.Value); listAllowedInDungeons = ConfigToHashSet(BuildPiecesCustomized.prefabListAllowedInDungeons.Value); listCanBeRemoved = ConfigToHashSet(BuildPiecesCustomized.prefabListCanBeRemoved.Value); listIsRoof = ConfigToHashSet(BuildPiecesCustomized.prefabListIsRoof.Value); listIsLeaky = ConfigToHashSet(BuildPiecesCustomized.prefabListIsLeaky.Value); listAshDamageImmune = ConfigToHashSet(BuildPiecesCustomized.prefabListAshDamageImmune.Value); listSnowDamageImmune = ConfigToHashSet(BuildPiecesCustomized.prefabListSnowDamageImmune.Value); listNoRoofWear = ConfigToHashSet(BuildPiecesCustomized.prefabListNoRoofWear.Value); listNoSupportWear = ConfigToHashSet(BuildPiecesCustomized.prefabListNoSupportWear.Value); listDisabled = ConfigToHashSet(BuildPiecesCustomized.prefabListDisabled.Value); disabledPieceCache.Clear(); clipEverything = listClipEverything.Contains(allPiecesListIdentifier); allowedInDungeons = listAllowedInDungeons.Contains(allPiecesListIdentifier); canBeRemoved = listCanBeRemoved.Contains(allPiecesListIdentifier); isRoof = listIsRoof.Contains(allPiecesListIdentifier); isLeaky = listIsLeaky.Contains(allPiecesListIdentifier); ashDamageImmune = listAshDamageImmune.Contains(allPiecesListIdentifier); snowDamageImmune = listSnowDamageImmune.Contains(allPiecesListIdentifier); noRoofWear = listNoRoofWear.Contains(allPiecesListIdentifier); noSupportWear = listNoSupportWear.Contains(allPiecesListIdentifier); } public static bool IsPieceForceDisabled(Piece piece) { if (!HasDisabledPieces || (Object)(object)piece == (Object)null) { return false; } if (disabledPieceCache.TryGetValue(piece, out var value)) { return value; } value = listDisabled.Contains(Utils.GetPrefabName(((Component)piece).gameObject)); disabledPieceCache[piece] = value; return value; } public static bool IsPieceForceDisabled(GameObject gameObject) { Piece piece = default(Piece); return (Object)(object)gameObject != (Object)null && gameObject.TryGetComponent<Piece>(ref piece) && IsPieceForceDisabled(piece); } public static void PatchGlobalProperties(Piece piece, string pieceName) { if (clipEverything) { piece.m_clipEverything = true; } else if (listClipEverything.Contains(pieceName)) { if (BuildPiecesCustomized.loggingEnabled.Value) { BuildPiecesCustomized.LogInfo("Patching " + pieceName + " clip everything"); } piece.m_clipEverything = true; } if (allowedInDungeons) { piece.m_allowedInDungeons = true; } else if (listAllowedInDungeons.Contains(pieceName)) { if (BuildPiecesCustomized.loggingEnabled.Value) { BuildPiecesCustomized.LogInfo("Patching " + pieceName + " allowed in dungeons"); } piece.m_allowedInDungeons = true; } if (canBeRemoved) { piece.m_canBeRemoved = true; } else if (listCanBeRemoved.Contains(pieceName)) { if (BuildPiecesCustomized.loggingEnabled.Value) { BuildPiecesCustomized.LogInfo("Patching " + pieceName + " can be removed"); } piece.m_canBeRemoved = true; } if (listDisabled.Contains(pieceName)) { piece.m_enabled = false; } WearNTear wearNTearComponent = BuildPiecesCustomized.GetWearNTearComponent(piece); if (!((Object)(object)wearNTearComponent != (Object)null)) { return; } if (ashDamageImmune) { wearNTearComponent.m_ashDamageImmune = true; } else if (listAshDamageImmune.Contains(pieceName)) { if (BuildPiecesCustomized.loggingEnabled.Value) { BuildPiecesCustomized.LogInfo("Patching " + pieceName + " ash and lava immune"); } wearNTearComponent.m_ashDamageImmune = true; } if (snowDamageImmune) { wearNTearComponent.m_snowDamageImmune = true; } else if (listSnowDamageImmune.Contains(pieceName)) { if (BuildPiecesCustomized.loggingEnabled.Value) { BuildPiecesCustomized.LogInfo("Patching " + pieceName + " heavy snow immune"); } wearNTearComponent.m_snowDamageImmune = true; } if (noRoofWear) { wearNTearComponent.m_noRoofWear = false; } else if (listNoRoofWear.Contains(pieceName)) { if (BuildPiecesCustomized.loggingEnabled.Value) { BuildPiecesCustomized.LogInfo("Patching " + pieceName + " no water damage"); } wearNTearComponent.m_noRoofWear = false; } if (noSupportWear) { wearNTearComponent.m_noSupportWear = false; } else if (listNoSupportWear.Contains(pieceName)) { if (BuildPiecesCustomized.loggingEnabled.Value) { BuildPiecesCustomized.LogInfo("Patching " + pieceName + " no structural integrity"); } wearNTearComponent.m_noSupportWear = false; } bool flag = isRoof || listIsRoof.Contains(pieceName); bool flag2 = isLeaky || listIsLeaky.Contains(pieceName); if (!(flag || flag2)) { return; } ((Component)piece).GetComponentsInChildren<Collider>(true, roofColliders); foreach (Collider roofCollider in roofColliders)