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 RecipeManager v0.12.2
plugins/RecipeManager.dll
Decompiled 6 hours 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.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using RecipeManager.Common; using RecipeManager.Importer; using RecipeManager.Patches; using UnityEngine; using YamlDotNet.Core; using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("RecipeManager")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("RecipeManager")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("0.12.2")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.12.2.0")] namespace RecipeManager { internal class ConversionPrintCommand : ConsoleCommand { internal const string DumpFileName = "AllConversionsDebug.yaml"; public override string Name => "RM_Conversion_PrintAll"; public override string Help => "Prints all conversions."; public override bool IsCheat => true; public override void Run(string[] args) { if (ValConfig.EnableDebugMode.Value) { Logger.LogInfo("Starting to dump conversion list"); } string text = Path.Combine(Paths.ConfigPath, "RecipeManager", "AllConversionsDebug.yaml"); DataObjects.ConversionModificationCollection conversionModificationCollection = new DataObjects.ConversionModificationCollection(); List<Smelter> list = (from pc in Resources.FindObjectsOfTypeAll<Smelter>() where IsPrefab((Component)(object)pc) select pc).ToList(); List<Fermenter> list2 = (from pc in Resources.FindObjectsOfTypeAll<Fermenter>() where IsPrefab((Component)(object)pc) select pc).ToList(); List<CookingStation> list3 = (from pc in Resources.FindObjectsOfTypeAll<CookingStation>() where IsPrefab((Component)(object)pc) select pc).ToList(); List<Incinerator> list4 = (from pc in Resources.FindObjectsOfTypeAll<Incinerator>() where IsPrefab((Component)(object)pc) select pc).ToList(); foreach (Smelter item in list) { string text2 = PrefabName((Component)(object)item); if (ValConfig.EnableDebugMode.Value) { Logger.LogInfo("Building Smelter definition " + text2); } DataObjects.ConversionModification conversionModification = new DataObjects.ConversionModification { prefab = text2, action = DataObjects.ConversionAction.Modify, fuelPerProduct = item.m_fuelPerProduct, maxFuel = item.m_maxFuel, maxOres = item.m_maxOre, fuelItem = ItemName(item.m_fuelItem), conversionTime = item.m_secPerProduct, spawnStack = item.m_spawnStack, requiresRoof = item.m_requiresRoof, conversions = new List<DataObjects.ConversionDef>() }; foreach (ItemConversion item2 in item.m_conversion ?? new List<ItemConversion>()) { if (item2 != null) { int num = ConversionUpdater.SmelterAmount(item, item2); conversionModification.conversions.Add(new DataObjects.ConversionDef { fromPrefab = ItemName(item2.m_from), toPrefab = ItemName(item2.m_to), amount = ((num > 1) ? new int?(num) : ((int?)null)) }); } } AddDefinition(conversionModificationCollection, conversionModification); } foreach (Fermenter item3 in list2) { if (ValConfig.EnableDebugMode.Value) { Logger.LogInfo("Building Fermenter definition " + item3.m_name); } DataObjects.ConversionModification conversionModification2 = new DataObjects.ConversionModification { prefab = PrefabName((Component)(object)item3), action = DataObjects.ConversionAction.Modify, conversionTime = item3.m_fermentationDuration, conversions = new List<DataObjects.ConversionDef>() }; foreach (ItemConversion item4 in item3.m_conversion ?? new List<ItemConversion>()) { if (item4 != null) { conversionModification2.conversions.Add(new DataObjects.ConversionDef { fromPrefab = ItemName(item4.m_from), toPrefab = ItemName(item4.m_to), amount = item4.m_producedItems }); } } AddDefinition(conversionModificationCollection, conversionModification2); } foreach (CookingStation item5 in list3) { if (ValConfig.EnableDebugMode.Value) { Logger.LogInfo("Building Cooking Station definition " + item5.m_name); } DataObjects.ConversionModification conversionModification3 = new DataObjects.ConversionModification { prefab = PrefabName((Component)(object)item5), action = DataObjects.ConversionAction.Modify, fuelItem = ItemName(item5.m_fuelItem), overCookedItem = ItemName(item5.m_overCookedItem), secPerFuel = item5.m_secPerFuel, maxFuel = item5.m_maxFuel, requiresFire = item5.m_requireFire, conversions = new List<DataObjects.ConversionDef>() }; foreach (ItemConversion item6 in item5.m_conversion ?? new List<ItemConversion>()) { if (item6 != null) { conversionModification3.conversions.Add(new DataObjects.ConversionDef { fromPrefab = ItemName(item6.m_from), toPrefab = ItemName(item6.m_to), cookTime = item6.m_cookTime }); } } AddDefinition(conversionModificationCollection, conversionModification3); } foreach (Incinerator item7 in list4) { DataObjects.ConversionModification conversionModification4 = new DataObjects.ConversionModification { prefab = PrefabName((Component)(object)item7), action = DataObjects.ConversionAction.Modify, defaultResult = ItemName(item7.m_defaultResult), defaultCost = item7.m_defaultCost, incineratorConversions = new List<DataObjects.IncineratorConversionDef>() }; foreach (IncineratorConversion item8 in item7.m_conversions ?? new List<IncineratorConversion>()) { if (item8 != null) { conversionModification4.incineratorConversions.Add(new DataObjects.IncineratorConversionDef { result = ItemName(item8.m_result), resultAmount = item8.m_resultAmount, priority = item8.m_priority, anyOneIngredient = item8.m_requireOnlyOneIngredient, ingredients = (from requirement in item8.m_requirements ?? new List<Requirement>() where requirement != null && (Object)(object)requirement.m_resItem != (Object)null select new DataObjects.IncineratorIngredientDef { prefab = ItemName(requirement.m_resItem), amount = requirement.m_amount }).ToList() }); } } AddDefinition(conversionModificationCollection, conversionModification4); } if (ValConfig.EnableDebugMode.Value) { Logger.LogInfo("Serializing and printing conversions."); } string value = ValConfig.yamlserializer.Serialize((object)conversionModificationCollection); using (StreamWriter streamWriter = new StreamWriter(text)) { streamWriter.WriteLine("# Reference dump of every conversion station, as it currently is."); streamWriter.WriteLine("# This file is NOT loaded by the mod, copy the entries you want to change into your own Conversions.yaml."); streamWriter.WriteLine(); streamWriter.WriteLine(value); } ItemCommandUtils.Respond($"Dumped {conversionModificationCollection.ConversionModifications.Count} station(s) to {text}"); } private static void AddDefinition(DataObjects.ConversionModificationCollection collection, DataObjects.ConversionModification convMod) { if (collection.ConversionModifications.ContainsKey(convMod.prefab)) { Logger.LogDebug(convMod.prefab + " was already dumped, skipping its second station."); } else { collection.ConversionModifications.Add(convMod.prefab, convMod); } } private static string ItemName(ItemDrop item) { if (!((Object)(object)item != (Object)null)) { return null; } return ((Object)((Component)item).gameObject).name; } private static string PrefabName(Component station) { return ((Object)ConversionUpdater.StationRoot(station)).name; } private static bool IsPrefab(Component station) { string text = PrefabName(station); if (!text.EndsWith("(Clone)")) { return !Regex.IsMatch(text.Trim(), "\\(\\d+\\)"); } return false; } } internal class ConversionReloadCommand : ConsoleCommand { public override string Name => "RM_Conversion_Reload"; public override string Help => "Resynchronizes conversion modifications."; public override bool IsCheat => true; public override void Run(string[] args) { ValConfig.Conversions.ReloadFromDisk(); } } internal class ConversionUpdater { private class ConversionSnapshot { private GameObject gameObject; private bool hasSmelter; private List<ItemConversion> smelterConversions; private ItemDrop smelterFuelItem; private int smelterFuelPerProduct; private float smelterSecPerProduct; private int smelterMaxOre; private int smelterMaxFuel; private bool smelterSpawnStack; private bool smelterRequiresRoof; private bool hasFermenter; private List<ItemConversion> fermenterConversions; private float fermentationDuration; private bool hasCookingStation; private List<ItemConversion> cookingConversions; private ItemDrop cookingFuelItem; private ItemDrop cookingOverCookedItem; private int cookingMaxFuel; private bool cookingUseFuel; private bool cookingRequireFire; private int cookingSecPerFuel; private bool hasIncinerator; private List<IncineratorConversion> incineratorConversions; private ItemDrop incineratorDefaultResult; private int incineratorDefaultCost; public bool IsEmpty { get { if (!hasSmelter && !hasFermenter && !hasCookingStation) { return !hasIncinerator; } return false; } } public static ConversionSnapshot From(GameObject go) { ConversionSnapshot conversionSnapshot = new ConversionSnapshot { gameObject = go }; Smelter val = ConversionUpdater.FindStation<Smelter>(go); if ((Object)(object)val != (Object)null) { conversionSnapshot.hasSmelter = true; conversionSnapshot.smelterConversions = Copy(val.m_conversion); conversionSnapshot.smelterFuelItem = val.m_fuelItem; conversionSnapshot.smelterFuelPerProduct = val.m_fuelPerProduct; conversionSnapshot.smelterSecPerProduct = val.m_secPerProduct; conversionSnapshot.smelterMaxOre = val.m_maxOre; conversionSnapshot.smelterMaxFuel = val.m_maxFuel; conversionSnapshot.smelterSpawnStack = val.m_spawnStack; conversionSnapshot.smelterRequiresRoof = val.m_requiresRoof; } Fermenter val2 = ConversionUpdater.FindStation<Fermenter>(go); if ((Object)(object)val2 != (Object)null) { conversionSnapshot.hasFermenter = true; conversionSnapshot.fermenterConversions = Copy(val2.m_conversion); conversionSnapshot.fermentationDuration = val2.m_fermentationDuration; } CookingStation val3 = ConversionUpdater.FindStation<CookingStation>(go); if ((Object)(object)val3 != (Object)null) { conversionSnapshot.hasCookingStation = true; conversionSnapshot.cookingConversions = Copy(val3.m_conversion); conversionSnapshot.cookingFuelItem = val3.m_fuelItem; conversionSnapshot.cookingOverCookedItem = val3.m_overCookedItem; conversionSnapshot.cookingMaxFuel = val3.m_maxFuel; conversionSnapshot.cookingUseFuel = val3.m_useFuel; conversionSnapshot.cookingRequireFire = val3.m_requireFire; conversionSnapshot.cookingSecPerFuel = val3.m_secPerFuel; } Incinerator val4 = ConversionUpdater.FindStation<Incinerator>(go); if ((Object)(object)val4 != (Object)null) { conversionSnapshot.hasIncinerator = true; conversionSnapshot.incineratorConversions = Copy(val4.m_conversions); conversionSnapshot.incineratorDefaultResult = val4.m_defaultResult; conversionSnapshot.incineratorDefaultCost = val4.m_defaultCost; } return conversionSnapshot; } public ConversionSnapshot For(GameObject go) { ConversionSnapshot obj = (ConversionSnapshot)MemberwiseClone(); obj.gameObject = go; obj.smelterConversions = Copy(smelterConversions); obj.fermenterConversions = Copy(fermenterConversions); obj.cookingConversions = Copy(cookingConversions); obj.incineratorConversions = Copy(incineratorConversions); return obj; } public bool WriteBack() { if ((Object)(object)gameObject == (Object)null) { return false; } if (hasSmelter) { Smelter val = ConversionUpdater.FindStation<Smelter>(gameObject); if ((Object)(object)val != (Object)null) { val.m_conversion = Copy(smelterConversions); val.m_fuelItem = smelterFuelItem; val.m_fuelPerProduct = smelterFuelPerProduct; val.m_secPerProduct = smelterSecPerProduct; val.m_maxOre = smelterMaxOre; val.m_maxFuel = smelterMaxFuel; val.m_spawnStack = smelterSpawnStack; val.m_requiresRoof = smelterRequiresRoof; } } if (hasFermenter) { Fermenter val2 = ConversionUpdater.FindStation<Fermenter>(gameObject); if ((Object)(object)val2 != (Object)null) { val2.m_conversion = Copy(fermenterConversions); val2.m_fermentationDuration = fermentationDuration; } } if (hasCookingStation) { CookingStation val3 = ConversionUpdater.FindStation<CookingStation>(gameObject); if ((Object)(object)val3 != (Object)null) { val3.m_conversion = Copy(cookingConversions); val3.m_fuelItem = cookingFuelItem; val3.m_overCookedItem = cookingOverCookedItem; val3.m_maxFuel = cookingMaxFuel; val3.m_useFuel = cookingUseFuel; val3.m_requireFire = cookingRequireFire; val3.m_secPerFuel = cookingSecPerFuel; } } if (hasIncinerator) { Incinerator val4 = ConversionUpdater.FindStation<Incinerator>(gameObject); if ((Object)(object)val4 != (Object)null) { val4.m_conversions = ByPriority(gameObject, Copy(incineratorConversions)); val4.m_defaultResult = incineratorDefaultResult; val4.m_defaultCost = incineratorDefaultCost; } } return true; } private static List<T> Copy<T>(List<T> source) { if (source != null) { return new List<T>(source); } return null; } } internal const int DefaultFermenterAmount = 4; internal const float DefaultCookTime = 10f; public static Dictionary<string, DataObjects.ConversionModification> ConversionsToModify = new Dictionary<string, DataObjects.ConversionModification>(); public static List<DataObjects.TrackedConversion> TrackedConversions = new List<DataObjects.TrackedConversion>(); private static readonly Dictionary<int, ConversionSnapshot> OriginalConversions = new Dictionary<int, ConversionSnapshot>(); private static readonly Dictionary<string, ConversionSnapshot> OriginalByPrefab = new Dictionary<string, ConversionSnapshot>(); private static readonly Dictionary<string, Dictionary<string, int>> SmelterAmounts = new Dictionary<string, Dictionary<string, int>>(); private static bool warnedOverCookedTime; public static void InitialSychronization() { RevertConversionModifications(); BuildConversionTracker(); ConversionUpdateRunner(); } public static void Reapply() { RevertConversionModifications(); BuildConversionTracker(); ConversionUpdateRunner(); } public static void ConversionUpdateRunner() { Logger.LogInfo($"Applying {TrackedConversions.Count} conversion modifications"); if (TrackedConversions.Count == 0) { return; } Dictionary<string, List<GameObject>> stations = IndexStations(); foreach (DataObjects.TrackedConversion trackedConversion in TrackedConversions) { ApplyConversionModifications(trackedConversion, stations); } } private static void ApplyConversionModifications(DataObjects.TrackedConversion conv, Dictionary<string, List<GameObject>> stations) { if (conv == null || (Object)(object)conv.prefab == (Object)null) { Logger.LogWarning("A tracked conversion has no target prefab, this modification will be skipped."); return; } Logger.LogDebug($"Applying conversion ({((Object)conv.prefab).name}) modification action: {conv.action}"); if (!stations.TryGetValue(((Object)conv.prefab).name, out var value)) { Logger.LogWarning("No stations found for conversion " + ((Object)conv.prefab).name + ", skipping modification."); return; } foreach (GameObject item in value) { if (!((Object)(object)item == (Object)null)) { switch (conv.action) { case DataObjects.ConversionAction.Modify: ModifyConversion(conv, item); break; case DataObjects.ConversionAction.Add: AddConversion(conv, item); break; case DataObjects.ConversionAction.Remove: RemoveConversion(conv, item); break; } } } if (conv.convertType == DataObjects.ConversionType.Smelter) { UpdateSmelterAmounts(conv); } } private static void UpdateSmelterAmounts(DataObjects.TrackedConversion conv) { if (conv.conversions == null || (Object)(object)conv.smelterComponent == (Object)null) { return; } string name = ((Object)conv.prefab).name; Dictionary<string, int> value; Dictionary<string, int> dictionary = (SmelterAmounts.TryGetValue(name, out value) ? value : new Dictionary<string, int>()); Dictionary<string, int> dictionary2 = new Dictionary<string, int>(dictionary); if (conv.action == DataObjects.ConversionAction.Modify) { dictionary2.Clear(); foreach (DataObjects.ResolvedConversion conversion in conv.conversions) { string key = InputName(conversion.from); if (!dictionary2.ContainsKey(key)) { dictionary2[key] = conversion.amount ?? ((!dictionary.TryGetValue(key, out var value2)) ? 1 : value2); } } } else if (conv.action == DataObjects.ConversionAction.Add) { foreach (DataObjects.ResolvedConversion conversion2 in conv.conversions) { if (conversion2.amount.HasValue) { dictionary2[InputName(conversion2.from)] = conversion2.amount.Value; } } } List<ItemConversion> list = conv.smelterComponent.m_conversion ?? new List<ItemConversion>(); foreach (string input in dictionary2.Keys.ToList()) { if (dictionary2[input] <= 1 || !list.Exists((ItemConversion e) => e != null && InputName(e.m_from) == input)) { dictionary2.Remove(input); } } if (dictionary2.Count > 0) { SmelterAmounts[name] = dictionary2; } else { SmelterAmounts.Remove(name); } } internal static int SmelterAmount(Smelter station, ItemConversion conversion) { if (SmelterAmounts.Count == 0 || conversion == null) { return 1; } if (!SmelterAmounts.TryGetValue(StationRef.PrefabNameOf(StationRoot((Component)(object)station)), out var value)) { return 1; } if (!value.TryGetValue(InputName(conversion.m_from), out var value2)) { return 1; } return value2; } private static string InputName(ItemDrop input) { if (!((Object)(object)input != (Object)null)) { return ""; } return ((Object)((Component)input).gameObject).name; } internal static Dictionary<string, List<GameObject>> IndexStations() { Dictionary<string, List<GameObject>> dictionary = new Dictionary<string, List<GameObject>>(); HashSet<int> seen = new HashSet<int>(); AddStations<Smelter>(dictionary, seen); AddStations<Fermenter>(dictionary, seen); AddStations<CookingStation>(dictionary, seen); AddStations<Incinerator>(dictionary, seen); return dictionary; } private static void AddStations<T>(Dictionary<string, List<GameObject>> index, HashSet<int> seen) where T : Component { T[] array = Resources.FindObjectsOfTypeAll<T>(); foreach (T val in array) { if ((Object)(object)val == (Object)null) { continue; } GameObject val2 = StationRoot((Component)(object)val); if (seen.Add(((Object)val2).GetInstanceID())) { string key = StationRef.PrefabNameOf(val2); if (!index.TryGetValue(key, out var value)) { value = new List<GameObject>(); index.Add(key, value); } if (IsPlaced(val2)) { value.Add(val2); } else { value.Insert(0, val2); } } } } private static bool IsPlaced(GameObject go) { return ((Object)go).name.EndsWith("(Clone)", StringComparison.Ordinal); } internal static GameObject StationRoot(Component station) { ZNetView componentInParent = station.GetComponentInParent<ZNetView>(true); if (!((Object)(object)componentInParent != (Object)null)) { return station.gameObject; } return ((Component)componentInParent).gameObject; } public static void RevertConversionModifications() { SmelterAmounts.Clear(); if (OriginalConversions.Count == 0) { return; } Logger.LogInfo($"Reverting {OriginalConversions.Count} conversion modifications"); List<int> list = new List<int>(); int num = 0; foreach (KeyValuePair<int, ConversionSnapshot> originalConversion in OriginalConversions) { if (originalConversion.Value.WriteBack()) { num++; } else { list.Add(originalConversion.Key); } } foreach (int item in list) { OriginalConversions.Remove(item); } Logger.LogDebug($"Restored {num} conversion station(s), {list.Count} no longer exist."); } internal static T FindStation<T>(GameObject go) where T : Component { return go.GetComponentInChildren<T>(true); } private static void CaptureOriginal(GameObject go) { if ((Object)(object)go == (Object)null) { return; } int instanceID = ((Object)go).GetInstanceID(); if (OriginalConversions.ContainsKey(instanceID)) { return; } string key = StationRef.PrefabNameOf(go); bool flag = IsPlaced(go); ConversionSnapshot value; ConversionSnapshot conversionSnapshot = ((flag && OriginalByPrefab.TryGetValue(key, out value)) ? value.For(go) : ConversionSnapshot.From(go)); if (!conversionSnapshot.IsEmpty) { OriginalConversions.Add(instanceID, conversionSnapshot); if (!flag && !OriginalByPrefab.ContainsKey(key)) { OriginalByPrefab.Add(key, conversionSnapshot); } } } private static void ModifyConversion(DataObjects.TrackedConversion conversion, GameObject go) { CaptureOriginal(go); switch (conversion.convertType) { case DataObjects.ConversionType.Smelter: { Smelter val4 = ConversionUpdater.FindStation<Smelter>(go); if ((Object)(object)val4 == (Object)null) { Logger.LogInfo("Smelter component not found on " + ((Object)go).name + ", cannot apply conversion modifications."); break; } if (conversion.conversions != null) { val4.m_conversion = ((IEnumerable<DataObjects.ResolvedConversion>)conversion.conversions).Select((Func<DataObjects.ResolvedConversion, ItemConversion>)((DataObjects.ResolvedConversion c) => new ItemConversion { m_from = c.from, m_to = c.to })).ToList(); } if ((Object)(object)conversion.fuelItem != (Object)null) { val4.m_fuelItem = conversion.fuelItem; } if (conversion.fuelPerProduct.HasValue) { val4.m_fuelPerProduct = conversion.fuelPerProduct.Value; } if (conversion.conversionTime.HasValue) { val4.m_secPerProduct = conversion.conversionTime.Value; } if (conversion.maxOres.HasValue) { val4.m_maxOre = conversion.maxOres.Value; } if (conversion.maxFuel.HasValue) { val4.m_maxFuel = conversion.maxFuel.Value; } if (conversion.spawnStack.HasValue) { val4.m_spawnStack = conversion.spawnStack.Value; } if (conversion.requiresRoof.HasValue) { val4.m_requiresRoof = conversion.requiresRoof.Value; } break; } case DataObjects.ConversionType.Fermenter: { Fermenter val2 = ConversionUpdater.FindStation<Fermenter>(go); if ((Object)(object)val2 == (Object)null) { Logger.LogInfo("Fermenter component not found on " + ((Object)go).name + ", cannot apply conversion modifications."); break; } if (conversion.conversions != null) { List<ItemConversion> current = val2.m_conversion ?? new List<ItemConversion>(); val2.m_conversion = ((IEnumerable<DataObjects.ResolvedConversion>)conversion.conversions).Select((Func<DataObjects.ResolvedConversion, ItemConversion>)((DataObjects.ResolvedConversion c) => new ItemConversion { m_from = c.from, m_to = c.to, m_producedItems = (c.amount ?? current.Find((ItemConversion e) => e != null && SameItem(e.m_from, c.from))?.m_producedItems ?? 4) })).ToList(); } if (conversion.conversionTime.HasValue) { val2.m_fermentationDuration = conversion.conversionTime.Value; } break; } case DataObjects.ConversionType.CookingStation: { CookingStation val3 = ConversionUpdater.FindStation<CookingStation>(go); if ((Object)(object)val3 == (Object)null) { Logger.LogInfo("CookingStation component not found on " + ((Object)go).name + ", cannot apply conversion modifications."); break; } if (conversion.conversions != null) { List<ItemConversion> current2 = val3.m_conversion ?? new List<ItemConversion>(); val3.m_conversion = ((IEnumerable<DataObjects.ResolvedConversion>)conversion.conversions).Select((Func<DataObjects.ResolvedConversion, ItemConversion>)((DataObjects.ResolvedConversion c) => new ItemConversion { m_from = c.from, m_to = c.to, m_cookTime = (c.cookTime ?? current2.Find((ItemConversion e) => e != null && SameItem(e.m_from, c.from))?.m_cookTime ?? 10f) })).ToList(); } if (conversion.maxFuel.HasValue) { val3.m_maxFuel = conversion.maxFuel.Value; } if ((Object)(object)conversion.fuelItem != (Object)null) { val3.m_fuelItem = conversion.fuelItem; } if ((Object)(object)conversion.overCookedItem != (Object)null) { val3.m_overCookedItem = conversion.overCookedItem; } if (conversion.requiresFire.HasValue) { val3.m_requireFire = conversion.requiresFire.Value; } if (conversion.secPerFuel.HasValue) { val3.m_secPerFuel = (int)conversion.secPerFuel.Value; } break; } case DataObjects.ConversionType.Incinerator: { Incinerator val = ConversionUpdater.FindStation<Incinerator>(go); if ((Object)(object)val == (Object)null) { Logger.LogInfo("Incinerator component not found on " + ((Object)go).name + ", cannot apply conversion modifications."); break; } if (conversion.incineratorConversions != null) { val.m_conversions = ByPriority(go, conversion.incineratorConversions.Select(((IncineratorConversion built, DataObjects.IncineratorConversionDef def) c) => NewIncineratorConversion(c.built, null)).ToList()); } if ((Object)(object)conversion.defaultResult != (Object)null) { val.m_defaultResult = conversion.defaultResult; } if (conversion.defaultCost.HasValue) { val.m_defaultCost = conversion.defaultCost.Value; } break; } } } private static List<IncineratorConversion> ByPriority(GameObject go, List<IncineratorConversion> conversions) { if (conversions != null && (Object)(object)go != (Object)null && IsPlaced(go)) { conversions.Sort((IncineratorConversion a, IncineratorConversion b) => (b?.m_priority ?? 0).CompareTo(a?.m_priority ?? 0)); } return conversions; } private static IncineratorConversion NewIncineratorConversion(IncineratorConversion built, IncineratorConversion existing) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown return new IncineratorConversion { m_result = built.m_result, m_resultAmount = built.m_resultAmount, m_priority = built.m_priority, m_requireOnlyOneIngredient = built.m_requireOnlyOneIngredient, m_requirements = (built.m_requirements ?? ((existing?.m_requirements != null) ? new List<Requirement>(existing.m_requirements) : new List<Requirement>())) }; } private static bool SameItem(ItemDrop a, ItemDrop b) { if ((Object)(object)a == (Object)null || (Object)(object)b == (Object)null) { if ((Object)(object)a == (Object)null) { return (Object)(object)b == (Object)null; } return false; } return ((Object)((Component)a).gameObject).name == ((Object)((Component)b).gameObject).name; } public static void AddConversion(DataObjects.TrackedConversion conversion, GameObject go) { if (conversion.convertType == DataObjects.ConversionType.Incinerator) { if (conversion.incineratorConversions != null) { CaptureOriginal(go); AddIncineratorConversions(conversion, go); } } else { if (conversion.conversions == null) { return; } CaptureOriginal(go); switch (conversion.convertType) { case DataObjects.ConversionType.Smelter: { Smelter val2 = ConversionUpdater.FindStation<Smelter>(go); if ((Object)(object)val2 == (Object)null) { Logger.LogWarning("Smelter component not found on " + ((Object)go).name + ", no conversions can be added to it."); break; } val2.m_conversion = Upsert<ItemConversion>(val2.m_conversion, conversion.conversions, emptyOutputAllowed: true, (ItemConversion e) => e.m_from, (ItemConversion e, DataObjects.ResolvedConversion c) => new ItemConversion { m_from = e.m_from, m_to = (c.toGiven ? c.to : e.m_to) }, (DataObjects.ResolvedConversion c) => new ItemConversion { m_from = c.from, m_to = c.to }); break; } case DataObjects.ConversionType.Fermenter: { Fermenter val3 = ConversionUpdater.FindStation<Fermenter>(go); if ((Object)(object)val3 == (Object)null) { Logger.LogWarning("Fermenter component not found on " + ((Object)go).name + ", no conversions can be added to it."); break; } val3.m_conversion = Upsert<ItemConversion>(val3.m_conversion, conversion.conversions, emptyOutputAllowed: false, (ItemConversion e) => e.m_from, (ItemConversion e, DataObjects.ResolvedConversion c) => new ItemConversion { m_from = e.m_from, m_to = (c.toGiven ? c.to : e.m_to), m_producedItems = (c.amount ?? e.m_producedItems) }, (DataObjects.ResolvedConversion c) => new ItemConversion { m_from = c.from, m_to = c.to, m_producedItems = (c.amount ?? 4) }); break; } case DataObjects.ConversionType.CookingStation: { CookingStation val = ConversionUpdater.FindStation<CookingStation>(go); if ((Object)(object)val == (Object)null) { Logger.LogWarning("CookingStation component not found on " + ((Object)go).name + ", no conversions can be added to it."); break; } val.m_conversion = Upsert<ItemConversion>(val.m_conversion, conversion.conversions, emptyOutputAllowed: false, (ItemConversion e) => e.m_from, (ItemConversion e, DataObjects.ResolvedConversion c) => new ItemConversion { m_from = e.m_from, m_to = (c.toGiven ? c.to : e.m_to), m_cookTime = (c.cookTime ?? e.m_cookTime) }, (DataObjects.ResolvedConversion c) => new ItemConversion { m_from = c.from, m_to = c.to, m_cookTime = (c.cookTime ?? 10f) }); break; } } } } private static void AddIncineratorConversions(DataObjects.TrackedConversion conversion, GameObject go) { Incinerator val = ConversionUpdater.FindStation<Incinerator>(go); if ((Object)(object)val == (Object)null) { Logger.LogWarning("Incinerator component not found on " + ((Object)go).name + ", no conversions can be added to it."); return; } List<IncineratorConversion> list = ((val.m_conversions == null) ? new List<IncineratorConversion>() : new List<IncineratorConversion>(val.m_conversions)); foreach (var incineratorConversion in conversion.incineratorConversions) { IncineratorConversion built = incineratorConversion.built; DataObjects.IncineratorConversionDef item = incineratorConversion.def; int num = list.FindIndex((IncineratorConversion e) => e != null && SameItem(e.m_result, built.m_result)); if (num < 0) { list.Add(NewIncineratorConversion(built, null)); continue; } IncineratorConversion val2 = list[num]; IncineratorConversion val3 = NewIncineratorConversion(built, val2); if (!item.resultAmount.HasValue) { val3.m_resultAmount = val2.m_resultAmount; } if (!item.priority.HasValue) { val3.m_priority = val2.m_priority; } if (!item.anyOneIngredient.HasValue) { val3.m_requireOnlyOneIngredient = val2.m_requireOnlyOneIngredient; } list[num] = val3; } val.m_conversions = ByPriority(go, list); } private static List<T> Upsert<T>(List<T> existing, List<DataObjects.ResolvedConversion> entries, bool emptyOutputAllowed, Func<T, ItemDrop> fromOf, Func<T, DataObjects.ResolvedConversion, T> update, Func<DataObjects.ResolvedConversion, T> create) where T : class { List<T> list = ((existing == null) ? new List<T>() : new List<T>(existing)); foreach (DataObjects.ResolvedConversion entry in entries) { int num = list.FindIndex((T e) => e != null && SameItem(fromOf(e), entry.from)); if (num > -1) { list[num] = update(list[num], entry); } else if ((Object)(object)entry.to != (Object)null || emptyOutputAllowed) { list.Add(create(entry)); } } return list; } public static void RemoveConversion(DataObjects.TrackedConversion conversion, GameObject go) { if (conversion.convertType == DataObjects.ConversionType.Incinerator) { if (conversion.incineratorConversions == null) { return; } CaptureOriginal(go); Incinerator val = ConversionUpdater.FindStation<Incinerator>(go); if ((Object)(object)val == (Object)null || val.m_conversions == null) { Logger.LogWarning("Incinerator component not found on " + ((Object)go).name + ", no conversions can be removed from it."); return; } val.m_conversions = ByPriority(go, val.m_conversions.Where((IncineratorConversion e) => e == null || !conversion.incineratorConversions.Exists(((IncineratorConversion built, DataObjects.IncineratorConversionDef def) c) => SameItem(e.m_result, c.built.m_result))).ToList()); } else { if (conversion.conversions == null) { return; } CaptureOriginal(go); switch (conversion.convertType) { case DataObjects.ConversionType.Smelter: { Smelter val4 = ConversionUpdater.FindStation<Smelter>(go); if ((Object)(object)val4 == (Object)null || val4.m_conversion == null) { Logger.LogWarning("Smelter component not found on " + ((Object)go).name + ", no conversions can be removed from it."); break; } val4.m_conversion = Without(val4.m_conversion, conversion.conversions, (ItemConversion e) => e.m_from, (ItemConversion e) => e.m_to); break; } case DataObjects.ConversionType.Fermenter: { Fermenter val3 = ConversionUpdater.FindStation<Fermenter>(go); if ((Object)(object)val3 == (Object)null || val3.m_conversion == null) { Logger.LogWarning("Fermenter component not found on " + ((Object)go).name + ", no conversions can be removed from it."); break; } val3.m_conversion = Without(val3.m_conversion, conversion.conversions, (ItemConversion e) => e.m_from, (ItemConversion e) => e.m_to); break; } case DataObjects.ConversionType.CookingStation: { CookingStation val2 = ConversionUpdater.FindStation<CookingStation>(go); if ((Object)(object)val2 == (Object)null || val2.m_conversion == null) { Logger.LogWarning("CookingStation component not found on " + ((Object)go).name + ", no conversions can be removed from it."); break; } val2.m_conversion = Without(val2.m_conversion, conversion.conversions, (ItemConversion e) => e.m_from, (ItemConversion e) => e.m_to); break; } } } } private static List<T> Without<T>(List<T> existing, List<DataObjects.ResolvedConversion> entries, Func<T, ItemDrop> fromOf, Func<T, ItemDrop> toOf) { List<T> list = new List<T>(existing); foreach (DataObjects.ResolvedConversion entry in entries) { list.RemoveAll((T e) => e != null && SameItem(fromOf(e), entry.from) && (!entry.toGiven || SameItem(toOf(e), entry.to))); } return list; } public static void BuildConversionTracker() { TrackedConversions.Clear(); foreach (KeyValuePair<string, DataObjects.ConversionModification> item3 in ConversionsToModify) { if (item3.Key == null || item3.Value == null) { continue; } Logger.LogDebug("Constructing Conversion modifications for " + item3.Key); if (string.IsNullOrEmpty(item3.Value.prefab)) { Logger.LogWarning("Conversion modification '" + item3.Key + "' has no prefab set, it will be skipped. Define a prefab to modify to fix this."); continue; } DataObjects.TrackedConversion trackedConversion = new DataObjects.TrackedConversion { key = item3.Key, action = item3.Value.action }; GameObject prefab = PrefabManager.Instance.GetPrefab(item3.Value.prefab); if ((Object)(object)prefab == (Object)null) { Logger.LogWarning("Could not find piece with name: " + item3.Value.prefab + ", this modification will be skipped."); continue; } trackedConversion.prefab = prefab; Smelter val = ConversionUpdater.FindStation<Smelter>(prefab); if ((Object)(object)val != (Object)null) { trackedConversion.smelterComponent = val; trackedConversion.convertType = DataObjects.ConversionType.Smelter; } Fermenter val2 = ConversionUpdater.FindStation<Fermenter>(prefab); if ((Object)(object)val2 != (Object)null) { trackedConversion.fermenterComponent = val2; trackedConversion.convertType = DataObjects.ConversionType.Fermenter; } CookingStation val3 = ConversionUpdater.FindStation<CookingStation>(prefab); if ((Object)(object)val3 != (Object)null) { trackedConversion.cookingStationComponent = val3; trackedConversion.convertType = DataObjects.ConversionType.CookingStation; } Incinerator val4 = ConversionUpdater.FindStation<Incinerator>(prefab); if ((Object)(object)val4 != (Object)null) { trackedConversion.incineratorComponent = val4; trackedConversion.convertType = DataObjects.ConversionType.Incinerator; } if ((Object)(object)trackedConversion.cookingStationComponent == (Object)null && (Object)(object)trackedConversion.fermenterComponent == (Object)null && (Object)(object)trackedConversion.smelterComponent == (Object)null && (Object)(object)trackedConversion.incineratorComponent == (Object)null) { Logger.LogWarning(item3.Value.prefab + " did not have an attached Fermenter, Smelter, CookingStation or Incinerator. This modification will be skipped."); continue; } bool flag = trackedConversion.convertType == DataObjects.ConversionType.Incinerator; if (flag && item3.Value.conversions != null) { Logger.LogWarning("Conversion modification '" + item3.Key + "' lists conversions for " + item3.Value.prefab + ", an incinerator. Use incineratorConversions instead, they are skipped."); } if (!flag && (item3.Value.incineratorConversions != null || item3.Value.defaultResult != null || item3.Value.defaultCost.HasValue)) { Logger.LogWarning("Conversion modification '" + item3.Key + "' sets incinerator settings on " + item3.Value.prefab + ", which is not an incinerator. They are skipped."); } if (item3.Value.overCookedTime > 0f && !warnedOverCookedTime) { warnedOverCookedTime = true; Logger.LogWarning("Conversion modification '" + item3.Key + "' sets overCookedTime, which the game has no setting for. It is ignored, remove it from your conversion files."); } if (item3.Value.conversions != null && trackedConversion.convertType != DataObjects.ConversionType.Incinerator) { trackedConversion.conversions = new List<DataObjects.ResolvedConversion>(); foreach (DataObjects.ConversionDef conversion in item3.Value.conversions) { if (ResolveConversion(item3.Key, conversion, trackedConversion, out var resolved)) { trackedConversion.conversions.Add(resolved); } } } switch (trackedConversion.convertType) { case DataObjects.ConversionType.Fermenter: Logger.LogDebug("Setting Fermenter"); if (item3.Value.conversionTime.HasValue) { if (item3.Value.conversionTime.Value > 0f) { trackedConversion.conversionTime = item3.Value.conversionTime; } else { Logger.LogWarning($"{item3.Key} has a conversionTime of {item3.Value.conversionTime.Value}, it must be above 0. The fermentation time will not be changed."); } } break; case DataObjects.ConversionType.CookingStation: Logger.LogDebug("Setting CookingStation"); trackedConversion.maxFuel = NonNegative(item3.Key, "maxFuel", item3.Value.maxFuel); if (item3.Value.secPerFuel.HasValue) { if (item3.Value.secPerFuel.Value > 0f) { trackedConversion.secPerFuel = item3.Value.secPerFuel; } else { Logger.LogWarning($"{item3.Key} has a secPerFuel of {item3.Value.secPerFuel.Value}, it must be above 0 and will be ignored."); } } trackedConversion.fuelItem = ResolveNamedItem(item3.Key, "fuel item", item3.Value.fuelItem); trackedConversion.overCookedItem = ResolveNamedItem(item3.Key, "over cooked item", item3.Value.overCookedItem); trackedConversion.requiresFire = item3.Value.requiresFire; break; case DataObjects.ConversionType.Incinerator: trackedConversion.defaultResult = ResolveNamedItem(item3.Key, "default result", item3.Value.defaultResult); if (item3.Value.defaultCost.HasValue) { if (item3.Value.defaultCost.Value > 0) { trackedConversion.defaultCost = item3.Value.defaultCost; } else { Logger.LogWarning($"{item3.Key} has a defaultCost of {item3.Value.defaultCost.Value}, it must be above 0 and will be ignored."); } } if (item3.Value.incineratorConversions == null) { break; } trackedConversion.incineratorConversions = new List<(IncineratorConversion, DataObjects.IncineratorConversionDef)>(); foreach (DataObjects.IncineratorConversionDef incineratorConversion in item3.Value.incineratorConversions) { IncineratorConversion val5 = ResolveIncineratorConversion(item3.Key, incineratorConversion, trackedConversion.action, val4); if (val5 != null) { trackedConversion.incineratorConversions.Add((val5, incineratorConversion)); } } break; case DataObjects.ConversionType.Smelter: Logger.LogDebug("Setting Smelter"); trackedConversion.conversionTime = SmelterConversionTime(item3.Key, item3.Value.conversionTime); trackedConversion.fuelPerProduct = NonNegative(item3.Key, "fuelPerProduct", item3.Value.fuelPerProduct); trackedConversion.maxFuel = NonNegative(item3.Key, "maxFuel", item3.Value.maxFuel); trackedConversion.maxOres = NonNegative(item3.Key, "maxOres", item3.Value.maxOres); trackedConversion.fuelItem = ResolveNamedItem(item3.Key, "fuel item", item3.Value.fuelItem); trackedConversion.spawnStack = item3.Value.spawnStack; trackedConversion.requiresRoof = item3.Value.requiresRoof; if (trackedConversion.requiresRoof == true && (Object)(object)val.m_roofCheckPoint == (Object)null) { Logger.LogWarning(item3.Key + " makes " + item3.Value.prefab + " need a roof, but it has no point to check for one from. That setting is ignored."); trackedConversion.requiresRoof = null; } if (trackedConversion.action != DataObjects.ConversionAction.Remove) { CheckSmelterSetup(item3.Key, trackedConversion); } break; } TrackedConversions.Add(trackedConversion); } PatchManager.Refresh(); static void CheckSmelterSetup(string entry, DataObjects.TrackedConversion tconv) { Smelter smelterComponent = tconv.smelterComponent; string name = ((Object)tconv.prefab).name; bool flag2 = tconv.action == DataObjects.ConversionAction.Modify; int num = (flag2 ? tconv.maxOres : ((int?)null)) ?? smelterComponent.m_maxOre; int num2 = (flag2 ? tconv.maxFuel : ((int?)null)) ?? smelterComponent.m_maxFuel; if (num > 0 && (Object)(object)smelterComponent.m_addOreSwitch == (Object)null) { Logger.LogWarning($"{entry} leaves {name} with maxOres {num}, but it has nowhere to load items. It will never produce anything unless maxOres is 0."); } if (num2 > 0 && (Object)(object)smelterComponent.m_addWoodSwitch == (Object)null) { Logger.LogWarning($"{entry} leaves {name} with maxFuel {num2}, but it has nowhere to load fuel. It will never produce anything unless maxFuel is 0."); } List<DataObjects.ResolvedConversion> conversions = tconv.conversions; if (num > 0) { int num3 = conversions?.RemoveAll((DataObjects.ResolvedConversion resolvedConversion) => (Object)(object)resolvedConversion.from == (Object)null) ?? 0; if (num3 > 0) { Logger.LogWarning($"{entry} gives {name} {num3} conversion(s) with no fromPrefab, but it takes items (maxOres {num}). Those only work with maxOres 0 and have been skipped."); } } else { List<ItemDrop> list; if (flag2 && conversions != null) { list = conversions.Select((DataObjects.ResolvedConversion resolvedConversion) => resolvedConversion.from).ToList(); } else { list = (from val6 in smelterComponent.m_conversion ?? new List<ItemConversion>() where val6 != null select val6.m_from).ToList(); foreach (DataObjects.ResolvedConversion c in conversions ?? new List<DataObjects.ResolvedConversion>()) { if (!list.Exists((ItemDrop i) => SameItem(i, c.from))) { list.Add(c.from); } } } if (list.Any((ItemDrop i) => (Object)(object)i != (Object)null)) { Logger.LogWarning(entry + " leaves " + name + " with maxOres 0, so it takes no items and its conversions with a fromPrefab will never run."); } if (list.Count((ItemDrop i) => (Object)(object)i == (Object)null) > 1) { Logger.LogWarning(entry + " leaves " + name + " with more than one conversion without a fromPrefab, only the first of them is ever produced."); } } } static int? NonNegative(string entry, string field, int? value) { if (value.HasValue && value.Value < 0) { Logger.LogWarning($"{entry} has a {field} of {value.Value}, it can not be negative and will be ignored."); return null; } return value; } static bool ResolveConversion(string entry, DataObjects.ConversionDef conv, DataObjects.TrackedConversion tconv, out DataObjects.ResolvedConversion reference) { reference = null; if (conv == null) { return false; } Logger.LogDebug("Creating Conversion " + conv.fromPrefab + " to " + conv.toPrefab); bool flag2 = tconv.convertType == DataObjects.ConversionType.Smelter; bool flag3 = string.IsNullOrEmpty(conv.fromPrefab); bool flag4 = string.IsNullOrEmpty(conv.toPrefab); if (flag3 && flag4) { Logger.LogWarning(entry + " has a conversion with neither a fromPrefab nor a toPrefab, it will be skipped."); return false; } if (!flag2 && flag3) { Logger.LogWarning(entry + " has a conversion with no fromPrefab, only smelter type stations can leave it out. This conversion will be skipped."); return false; } if (!ResolveItem(entry, conv.fromPrefab, out var item) || !ResolveItem(entry, conv.toPrefab, out var item2)) { return false; } if (!flag2 && flag4 && tconv.action != DataObjects.ConversionAction.Remove && (tconv.action != DataObjects.ConversionAction.Add || !StationHasInput(tconv, item))) { Logger.LogWarning(entry + " has a conversion from " + conv.fromPrefab + " with no toPrefab, only smelter type stations can leave it out. This conversion will be skipped."); return false; } int? amount = conv.amount; if (amount.HasValue && tconv.action != DataObjects.ConversionAction.Remove) { if (tconv.convertType == DataObjects.ConversionType.CookingStation) { Logger.LogWarning(entry + " sets an amount on its conversion from " + conv.fromPrefab + ", but cooking stations always make one item. It is ignored."); } else if (flag2 && amount.Value < 1) { Logger.LogWarning($"{entry} has an amount of {amount.Value} on its conversion from {conv.fromPrefab} to {conv.toPrefab}, it must be at least 1 and will be ignored."); amount = null; } } reference = new DataObjects.ResolvedConversion { from = item, to = item2, toGiven = (!flag4 || (flag2 && tconv.action == DataObjects.ConversionAction.Modify)), amount = amount, cookTime = conv.cookTime }; return true; } static IncineratorConversion ResolveIncineratorConversion(string entry, DataObjects.IncineratorConversionDef def, DataObjects.ConversionAction action, Incinerator station) { //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown if (def == null) { return null; } ItemDrop result = ResolveNamedItem(entry, "incinerator result", def.result); if (!((Object)(object)result == (Object)null)) { List<Requirement> list = null; if (def.ingredients != null) { list = new List<Requirement>(); foreach (DataObjects.IncineratorIngredientDef ingredient in def.ingredients) { if (ingredient != null) { ItemDrop val6 = ResolveNamedItem(entry, "incinerator ingredient", ingredient.prefab); if (!((Object)(object)val6 == (Object)null)) { list.Add(new Requirement { m_resItem = val6, m_amount = Math.Max(1, ingredient.amount ?? 1) }); } } } if (list.Count == 0 && action != DataObjects.ConversionAction.Remove) { Logger.LogWarning(entry + " has an incinerator conversion making " + def.result + " with no ingredients that exist. It needs at least one, it will be skipped."); return null; } } else if (action == DataObjects.ConversionAction.Modify || (action == DataObjects.ConversionAction.Add && station?.m_conversions?.Exists((IncineratorConversion e) => e != null && SameItem(e.m_result, result)) != true)) { Logger.LogWarning(entry + " has an incinerator conversion making " + def.result + " with no ingredients. It needs at least one, it will be skipped."); return null; } return new IncineratorConversion { m_result = result, m_resultAmount = Math.Max(1, def.resultAmount ?? 1), m_priority = def.priority.GetValueOrDefault(), m_requireOnlyOneIngredient = (def.anyOneIngredient == true), m_requirements = list }; } if (string.IsNullOrEmpty(def.result)) { Logger.LogWarning(entry + " has an incinerator conversion with no result, it will be skipped."); } return null; } static bool ResolveItem(string entry, string prefabName, out ItemDrop item) { item = null; if (string.IsNullOrEmpty(prefabName)) { return true; } GameObject prefab2 = PrefabManager.Instance.GetPrefab(prefabName); item = (((Object)(object)prefab2 != (Object)null) ? prefab2.GetComponent<ItemDrop>() : null); if ((Object)(object)item == (Object)null) { Logger.LogWarning(entry + " has a conversion using " + prefabName + ", which " + (((Object)(object)prefab2 == (Object)null) ? "could not be found" : "is not an item") + ". This conversion will be skipped."); return false; } return true; } static ItemDrop ResolveNamedItem(string entry, string field, string itemName) { if (string.IsNullOrEmpty(itemName)) { return null; } GameObject prefab2 = PrefabManager.Instance.GetPrefab(itemName); ItemDrop val6 = (((Object)(object)prefab2 != (Object)null) ? prefab2.GetComponent<ItemDrop>() : null); if ((Object)(object)val6 == (Object)null) { Logger.LogWarning(entry + " could not find an item named " + itemName + ", the " + field + " will not be changed."); } return val6; } static float? SmelterConversionTime(string entry, float? conversionTime) { if (!conversionTime.HasValue) { return null; } if (conversionTime.Value <= 0f) { Logger.LogWarning($"{entry} has a conversionTime of {conversionTime.Value}, a smelter with no conversion time never produces anything. The conversion time will not be changed."); return null; } if (conversionTime.Value < 1f) { Logger.LogWarning($"{entry} has a conversionTime of {conversionTime.Value}, smelters can not produce more than one item per second and a shorter time only burns more fuel. Using 1 instead."); return 1f; } return conversionTime; } static bool StationHasInput(DataObjects.TrackedConversion tconv, ItemDrop from) { if ((Object)(object)tconv.fermenterComponent != (Object)null && tconv.fermenterComponent.m_conversion != null) { return tconv.fermenterComponent.m_conversion.Exists((ItemConversion e) => e != null && SameItem(e.m_from, from)); } if ((Object)(object)tconv.cookingStationComponent != (Object)null && tconv.cookingStationComponent.m_conversion != null) { return tconv.cookingStationComponent.m_conversion.Exists((ItemConversion e) => e != null && SameItem(e.m_from, from)); } return false; } } } internal class WackyImportCommand : ConsoleCommand { public override string Name => "RM_Import_WackyDB"; public override string Help => "Converts WackysDatabase files into RecipeManager files, written to BepInEx/config/RecipeManager/WackyImport with a report. Usage: RM_Import_WackyDB [folder], the folder defaults to BepInEx/config/wackysDatabase"; public override bool IsCheat => true; public override void Run(string[] args) { if ((Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { ItemCommandUtils.Respond("Load into a world first, the import checks stations and items against the game."); return; } string text = ((args != null && args.Length != 0) ? string.Join(" ", args).Trim(new char[1] { '"' }) : Path.Combine(Paths.ConfigPath, "wackysDatabase")); if (!Path.IsPathRooted(text)) { text = Path.Combine(Paths.ConfigPath, text); } if (!Directory.Exists(text)) { ItemCommandUtils.Respond("There is no folder " + text + ". Give the folder WackysDatabase's files are in, for example RM_Import_WackyDB wackysDatabase"); return; } try { WackyImporter wackyImporter = new WackyImporter(text); wackyImporter.Run(); int count = wackyImporter.Report.Files.Count; int num = wackyImporter.Report.Files.Count((ImportReport.FileResult file) => file.Targets.Count > 0); ItemCommandUtils.Respond($"Converted {num} of {count} WackysDatabase file(s): {wackyImporter.Recipes.Count} recipe(s), {wackyImporter.Items.Count} item(s), {wackyImporter.Pieces.Count} piece(s), {wackyImporter.Conversions.Count} conversion(s) and {wackyImporter.StatusEffects.Count} status effect(s)."); ItemCommandUtils.Respond("Written to " + wackyImporter.Output + ", nothing there is loaded until you move it. Read ImportReport.txt there first."); if (wackyImporter.WackyDBLoaded) { ItemCommandUtils.Respond("WackysDatabase is running, so the report could not tell which of its values the game already had. Run the import with it disabled for a more accurate report."); } } catch (Exception ex) { ItemCommandUtils.Respond("The import failed: " + ex.Message); Logger.LogError($"WackysDatabase import from {text} failed. Error: {ex}"); } } } internal class ItemReloadCommand : ConsoleCommand { public override string Name => "RM_Items_Reload"; public override string Help => "Reloads item additions and modifications. Stats, names, icons and textures are re-applied; new or removed items need a restart."; public override bool IsCheat => true; public override void Run(string[] args) { ValConfig.Items.ReloadFromDisk(); ItemCommandUtils.Respond($"Reloaded {ItemUpdater.ItemDefinitions.Count} item entry(s), {ItemUpdater.ModifiedItems.Count} of which modify an existing item."); } } internal class ItemPrintCommand : ConsoleCommand { internal const string DumpFileName = "ObjectDBItems.yaml"; public override string Name => "RM_Items_PrintAll"; public override string Help => "Prints items in the Object DB to a reference file, with their real stats. Usage: RM_Items_PrintAll [nameFilter]"; public override bool IsCheat => true; public override void Run(string[] args) { if ((Object)(object)ObjectDB.instance == (Object)null) { ItemCommandUtils.Respond("The object DB is not available yet, load into a world first."); return; } string text = ((args != null && args.Length != 0) ? args[0] : null); if (ValConfig.EnableDebugMode.Value) { Logger.LogInfo("Starting to dump items"); } string text2 = Path.Combine(Paths.ConfigPath, "RecipeManager", "ObjectDBItems.yaml"); DataObjects.ItemAdditionCollection itemAdditionCollection = new DataObjects.ItemAdditionCollection(); foreach (GameObject item in ObjectDB.instance.m_items.ToList()) { if ((Object)(object)item == (Object)null || (text != null && ((Object)item).name.IndexOf(text, StringComparison.OrdinalIgnoreCase) < 0)) { continue; } SharedData val = item.GetComponent<ItemDrop>()?.m_itemData?.m_shared; if (val == null) { continue; } if (itemAdditionCollection.ItemAdditions.ContainsKey(((Object)item).name)) { Logger.LogWarning(((Object)item).name + " was already added to the list of items and will be skipped."); continue; } if (ValConfig.EnableDebugMode.Value) { Logger.LogInfo("Building item definition " + ((Object)item).name); } itemAdditionCollection.ItemAdditions.Add(((Object)item).name, DescribeItem(((Object)item).name, val)); } if (ValConfig.EnableDebugMode.Value) { Logger.LogInfo("Serializing and printing items."); } using (StreamWriter streamWriter = new StreamWriter(text2)) { streamWriter.WriteLine("# Reference dump of the items in the object DB, with the stats they currently have."); streamWriter.WriteLine("# This file is NOT loaded by the mod, it is here so you can see what the numbers are."); streamWriter.WriteLine("#"); streamWriter.WriteLine("# To change one of these items, copy its entry into your own Items.yaml and delete"); streamWriter.WriteLine("# every line you do not want to change - anything left out keeps its current value."); streamWriter.WriteLine("# To create a new item from one instead, set 'action: Add', point 'clonedFrom' at the"); streamWriter.WriteLine("# prefab below and give 'prefab' a new unique name."); streamWriter.WriteLine(); streamWriter.WriteLine(ValConfig.yamlserializer.Serialize((object)itemAdditionCollection)); } ItemCommandUtils.Respond($"Dumped {itemAdditionCollection.ItemAdditions.Count} item(s) to {text2}"); } private static DataObjects.ItemAddition DescribeItem(string prefabName, SharedData shared) { //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Invalid comparison between Unknown and I4 //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Invalid comparison between Unknown and I4 //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Invalid comparison between Unknown and I4 //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Invalid comparison between Unknown and I4 //IL_07f3: Unknown result type (might be due to invalid IL or missing references) //IL_07f8: Unknown result type (might be due to invalid IL or missing references) //IL_0806: Unknown result type (might be due to invalid IL or missing references) //IL_0808: Unknown result type (might be due to invalid IL or missing references) //IL_0813: Unknown result type (might be due to invalid IL or missing references) //IL_0815: Unknown result type (might be due to invalid IL or missing references) DataObjects.ItemAddition itemAddition = new DataObjects.ItemAddition { action = DataObjects.ItemAction.Modify, prefab = prefabName, name = shared.m_name, description = shared.m_description, weight = shared.m_weight, maxStackSize = shared.m_maxStackSize, value = shared.m_value, teleportable = shared.m_teleportable, equipDuration = shared.m_equipDuration, questItem = (shared.m_questItem ? new bool?(true) : ((bool?)null)), appendToolTip = (((Object)(object)shared.m_appendToolTip != (Object)null) ? ((Object)((Component)shared.m_appendToolTip).gameObject).name : null), quality = new DataObjects.ItemQuality { maxLevel = shared.m_maxQuality, weightPerLevel = ((shared.m_scaleWeightByQuality != 0f) ? new float?(shared.m_scaleWeightByQuality) : ((float?)null)) } }; if (shared.m_maxQuality > 1) { itemAddition.quality.upgradeChance = shared.m_upgradeChance; itemAddition.quality.breakChance = shared.m_breakChance; itemAddition.quality.upgradeSteps = shared.m_successUpgradeSteps; itemAddition.quality.breakRefund = shared.m_breakReturnIngreientsAmount; } if (shared.m_useDurability) { itemAddition.durability = new DataObjects.ItemDurability { enabled = true, max = shared.m_maxDurability, perLevel = shared.m_durabilityPerLevel, drainPerUse = shared.m_useDurabilityDrain, drainPerSecond = shared.m_durabilityDrain, repairable = shared.m_canBeReparied, destroyWhenBroken = shared.m_destroyBroken }; } if (shared.m_food > 0f || shared.m_foodStamina > 0f || shared.m_foodEitr > 0f || (int)shared.m_itemType == 2) { DataObjects.FoodStats obj = new DataObjects.FoodStats { health = shared.m_food, stamina = shared.m_foodStamina, eitr = shared.m_foodEitr, duration = shared.m_foodBurnTime, regen = shared.m_foodRegen, eatAnimTime = shared.m_foodEatAnimTime, isDrink = shared.m_isDrink }; StatusEffect consumeStatusEffect = shared.m_consumeStatusEffect; obj.consumeStatusEffect = ((consumeStatusEffect != null) ? ((Object)consumeStatusEffect).name : null); itemAddition.food = obj; } bool flag = UsesAttacks(shared.m_itemType) || ((int)shared.m_itemType == 5 && shared.m_buildBlockCharges); bool flag2 = shared.m_blockPower > 0f && ((int)shared.m_itemType == 5 || (UsesAttacks(shared.m_itemType) && (int)shared.m_itemType != 9)); if (flag || ((DamageTypes)(ref shared.m_damages)).HaveDamage() || ((DamageTypes)(ref shared.m_damagesPerLevel)).HaveDamage() || shared.m_toolTier > 0) { itemAddition.weapon = new DataObjects.WeaponStats { damage = DescribeDamage(shared.m_damages), damagePerLevel = DescribeDamage(shared.m_damagesPerLevel), attackForce = shared.m_attackForce, backstabBonus = shared.m_backstabBonus, toolTier = shared.m_toolTier, skill = ((object)Unsafe.As<SkillType, SkillType>(ref shared.m_skillType)/*cast due to .constrained prefix*/).ToString(), ammoType = (string.IsNullOrEmpty(shared.m_ammoType) ? null : shared.m_ammoType), dodgeable = shared.m_dodgeable, blockable = shared.m_blockable, hitVariant = ((shared.m_hitVariant >= 0) ? new int?(shared.m_hitVariant) : ((int?)null)), spawnOnHit = (((Object)(object)shared.m_spawnOnHit != (Object)null) ? ((Object)shared.m_spawnOnHit).name : null), spawnOnHitTerrain = (((Object)(object)shared.m_spawnOnHitTerrain != (Object)null) ? ((Object)shared.m_spawnOnHitTerrain).name : null) }; if (flag) { itemAddition.weapon.primaryAttack = DescribeAttack(shared.m_attack); if (shared.m_secondaryAttack != null && !string.IsNullOrEmpty(shared.m_secondaryAttack.m_attackAnimation)) { itemAddition.weapon.secondaryAttack = DescribeAttack(shared.m_secondaryAttack); } } } if (shared.m_armor > 0f || shared.m_blockPower > 0f || shared.m_movementModifier != 0f || shared.m_eitrRegenModifier != 0f || shared.m_heatResistanceModifier != 0f) { itemAddition.armor = new DataObjects.ArmorStats { armor = shared.m_armor, armorPerLevel = shared.m_armorPerLevel, blockPower = shared.m_blockPower, blockPowerPerLevel = shared.m_blockPowerPerLevel, deflectionForce = shared.m_deflectionForce, deflectionForcePerLevel = shared.m_deflectionForcePerLevel, movementModifier = shared.m_movementModifier, eitrRegenModifier = shared.m_eitrRegenModifier, heatResistanceModifier = ((shared.m_heatResistanceModifier != 0f) ? new float?(shared.m_heatResistanceModifier) : ((float?)null)) }; if (flag2) { itemAddition.armor.timedBlockBonus = shared.m_timedBlockBonus; itemAddition.armor.perfectBlockStaminaRegen = shared.m_perfectBlockStaminaRegen; itemAddition.armor.buildBlockCharges = shared.m_buildBlockCharges; if (shared.m_buildBlockCharges) { itemAddition.armor.maxBlockCharges = shared.m_maxBlockCharges; itemAddition.armor.blockChargeDecayTime = shared.m_blockChargeDecayTime; } } } if (shared.m_homeItemsStaminaModifier != 0f || shared.m_jumpStaminaModifier != 0f || shared.m_attackStaminaModifier != 0f || shared.m_blockStaminaModifier != 0f || shared.m_dodgeStaminaModifier != 0f || shared.m_swimStaminaModifier != 0f || shared.m_sneakStaminaModifier != 0f || shared.m_runStaminaModifier != 0f) { itemAddition.staminaModifiers = new DataObjects.StaminaModifiers { homeItems = shared.m_homeItemsStaminaModifier, jump = shared.m_jumpStaminaModifier, attack = shared.m_attackStaminaModifier, block = shared.m_blockStaminaModifier, dodge = shared.m_dodgeStaminaModifier, swim = shared.m_swimStaminaModifier, sneak = shared.m_sneakStaminaModifier, run = shared.m_runStaminaModifier }; } if (shared.m_maxAdrenaline > 0f || flag2) { itemAddition.adrenaline = new DataObjects.AdrenalineStats { max = ((shared.m_maxAdrenaline > 0f) ? new float?(shared.m_maxAdrenaline) : ((float?)null)), block = (flag2 ? new float?(shared.m_blockAdrenaline) : ((float?)null)), perfectBlock = (flag2 ? new float?(shared.m_perfectBlockAdrenaline) : ((float?)null)) }; } if (shared.m_damageModifiers != null && shared.m_damageModifiers.Count > 0) { itemAddition.resistances = new List<DataObjects.DamageResistance>(); foreach (DamageModPair damageModifier in shared.m_damageModifiers) { itemAddition.resistances.Add(new DataObjects.DamageResistance { type = damageModifier.m_type, modifier = damageModifier.m_modifier }); } } if ((Object)(object)shared.m_equipStatusEffect != (Object)null || (Object)(object)shared.m_attackStatusEffect != (Object)null || (Object)(object)shared.m_perfectBlockStatusEffect != (Object)null || (Object)(object)shared.m_fullAdrenalineSE != (Object)null) { DataObjects.ItemStatusEffects itemStatusEffects = new DataObjects.ItemStatusEffects(); StatusEffect equipStatusEffect = shared.m_equipStatusEffect; itemStatusEffects.equipped = ((equipStatusEffect != null) ? ((Object)equipStatusEffect).name : null); StatusEffect attackStatusEffect = shared.m_attackStatusEffect; itemStatusEffects.onHit = ((attackStatusEffect != null) ? ((Object)attackStatusEffect).name : null); itemStatusEffects.onHitChance = (((Object)(object)shared.m_attackStatusEffect != (Object)null) ? new float?(shared.m_attackStatusEffectChance) : ((float?)null)); StatusEffect perfectBlockStatusEffect = shared.m_perfectBlockStatusEffect; itemStatusEffects.perfectBlock = ((perfectBlockStatusEffect != null) ? ((Object)perfectBlockStatusEffect).name : null); StatusEffect fullAdrenalineSE = shared.m_fullAdrenalineSE; itemStatusEffects.fullAdrenaline = ((fullAdrenalineSE != null) ? ((Object)fullAdrenalineSE).name : null); itemAddition.statusEffects = itemStatusEffects; } if (!string.IsNullOrEmpty(shared.m_setName)) { DataObjects.SetBonus obj2 = new DataObjects.SetBonus { name = shared.m_setName, size = shared.m_setSize }; StatusEffect setStatusEffect = shared.m_setStatusEffect; obj2.statusEffect = ((setStatusEffect != null) ? ((Object)setStatusEffect).name : null); itemAddition.setBonus = obj2; } return itemAddition; } private static DataObjects.DamageValues DescribeDamage(DamageTypes damages) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) return new DataObjects.DamageValues { damage = damages.m_damage, blunt = damages.m_blunt, slash = damages.m_slash, pierce = damages.m_pierce, chop = damages.m_chop, pickaxe = damages.m_pickaxe, fire = damages.m_fire, frost = damages.m_frost, lightning = damages.m_lightning, poison = damages.m_poison, spirit = damages.m_spirit, nonPlayer = ((damages.m_nonPlayer != 0f) ? new float?(damages.m_nonPlayer) : ((float?)null)) }; } private static bool UsesAttacks(ItemType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 if ((int)type <= 9) { if (type - 3 <= 1 || (int)type == 9) { goto IL_0023; } } else if (type - 14 <= 1 || (int)type == 19 || (int)type == 22) { goto IL_0023; } return false; IL_0023: return true; } private static DataObjects.AttackStats DescribeAttack(Attack attack) { //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Invalid comparison between Unknown and I4 if (attack == null) { return null; } DataObjects.AttackStats attackStats = new DataObjects.AttackStats { stamina = attack.m_attackStamina, eitr = attack.m_attackEitr, health = attack.m_attackHealth, healthPercentage = attack.m_attackHealthPercentage, adrenaline = attack.m_attackAdrenaline, useAdrenaline = attack.m_attackUseAdrenaline, speedFactor = attack.m_speedFactor, rotationSpeedFactor = attack.m_speedFactorRotation, damageMultiplier = attack.m_damageMultiplier, forceMultiplier = attack.m_forceMultiplier, staggerMultiplier = attack.m_staggerMultiplier, lastChainDamageMultiplier = ((attack.m_attackChainLevels > 1) ? new float?(attack.m_lastChainDamageMultiplier) : ((float?)null)), damageMultiplierPerMissingHealth = ((attack.m_damageMultiplierPerMissingHP != 0f) ? new float?(attack.m_damageMultiplierPerMissingHP) : ((float?)null)), damageMultiplierByHealthMissing = ((attack.m_damageMultiplierByTotalHealthMissing != 0f) ? new float?(attack.m_damageMultiplierByTotalHealthMissing) : ((float?)null)), staminaReturnPerMissingHealth = ((attack.m_staminaReturnPerMissingHP != 0f) ? new float?(attack.m_staminaReturnPerMissingHP) : ((float?)null)), range = attack.m_attackRange, height = attack.m_attackHeight, angle = attack.m_attackAngle, rayWidth = attack.m_attackRayWidth, multiHit = attack.m_multiHit, lowerDamagePerHit = attack.m_lowerDamagePerHit, hitTerrain = attack.m_hitTerrain, hitFriendly = attack.m_hitFriendly, hitThroughWalls = attack.m_hitThroughWalls, recoilPushback = attack.m_recoilPushback, selfDamage = attack.m_selfDamage, consumeItem = attack.m_consumeItem, raiseSkillAmount = attack.m_raiseSkillAmount, startNoise = attack.m_attackStartNoise, hitNoise = attack.m_attackHitNoise, spawnOnTrigger = (((Object)(object)attack.m_spawnOnTrigger != (Object)null) ? ((Object)attack.m_spawnOnTrigger).name : null), spawnOnHit = (((Object)(object)attack.m_spawnOnHit != (Object)null) ? ((Object)attack.m_spawnOnHit).name : null), spawnOnHitChance = (((Object)(object)attack.m_spawnOnHit != (Object)null) ? new float?(attack.m_spawnOnHitChance) : ((float?)null)) }; if ((Object)(object)attack.m_attackProjectile != (Object)null || (int)attack.m_attackType == 2) { attackStats.projectile = new DataObjects.ProjectileStats { prefab = (((Object)(object)attack.m_attackProjectile != (Object)null) ? ((Object)attack.m_attackProjectile).name : null), velocity = attack.m_projectileVel, velocityMin = attack.m_projectileVelMin, accuracy = attack.m_projectileAccuracy, accuracyMin = attack.m_projectileAccuracyMin, skillAccuracy = attack.m_skillAccuracy, count = attack.m_projectiles, bursts = attack.m_projectileBursts, burstInterval = attack.m_burstInterval, launchAngle = attack.m_launchAngle }; } if (attack.m_requiresReload) { attackStats.reload = new DataObjects.ReloadStats { time = attack.m_reloadTime, staminaDrain = attack.m_reloadStaminaDrain, eitrDrain = attack.m_reloadEitrDrain }; } if (attack.m_bowDraw) { attackStats.draw = new DataObjects.DrawStats { durationMin = attack.m_drawDurationMin, staminaDrain = attack.m_drawStaminaDrain, eitrDrain = attack.m_drawEitrDrain }; } return attackStats; } public override List<string> CommandOptionList() { List<string> list = new List<string>(); if (ObjectDB.instance?.m_items == null) { return list; } foreach (GameObject item in ObjectDB.instance.m_items) { if ((Object)(object)item != (Object)null) { list.Add(((Object)item).name); } } return list; } } internal class TextureDumpCommand : ConsoleCommand { public override string Name => "RM_Textures_Dump"; public override string Help => "Dumps a prefab's icons and material textures to disk as PNGs. Usage: RM_Textures_Dump <prefabName>"; public override bool IsCheat => true; public override void Run(string[] args) { if (args == null || args.Length < 1 || string.IsNullOrEmpty(args[0])) { ItemCommandUtils.Respond("Usage: " + ((ConsoleCommand)this).Name + " <prefabName>"); return; } string text = args[0]; GameObject prefab = PrefabManager.Instance.GetPrefab(text); if ((Object)(object)prefab == (Object)null) { ItemCommandUtils.Respond("No prefab named '" + text + "' could be found. Prefab names are case sensitive."); return; } try { string outputDirectory; int num = TextureTools.DumpPrefab(prefab, out outputDirectory); if (num == 0) { ItemCommandUtils.Respond("Found no textures to dump for '" + text + "'. See " + outputDirectory + "\\_dump_info.txt"); } else { ItemCommandUtils.Respond($"Dumped {num} texture(s) for '{text}' to {outputDirectory}"); } } catch (Exception ex) { ItemCommandUtils.Respond("Dumping textures for '" + text + "' failed: " + ex.Message); Logger.LogError($"Texture dump for '{text}' failed. Error: {ex}"); } } public override List<string> CommandOptionList() { List<string> list = new List<string>(); if (ObjectDB.instance?.m_items == null) { return list; } foreach (GameObject item in ObjectDB.instance.m_items) { if ((Object)(object)item != (Object)null) { list.Add(((Object)item).name); } } return list; } } internal static class ItemCommandUtils { internal static void Respond(string message) { Logger.LogInfo(message); if ((Object)(object)Console.instance != (Object)null) { Console.instance.Print(message); } } } internal class ItemUpdater { public static Dictionary<string, DataObjects.ItemAddition> ItemDefinitions = new Dictionary<string, DataObjects.ItemAddition>(); public static List<DataObjects.TrackedItem> TrackedItems = new List<DataObjects.TrackedItem>(); public static List<DataObjects.TrackedItem> ModifiedItems = new List<DataObjects.TrackedItem>(); private static readonly HashSet<string> CreatedPrefabs = new HashSet<string>(); private static readonly HashSet<string> ModifiedPrefabs = new HashSet<string>(); public static void CreateCustomItems() { TrackedItems.Clear(); if (ItemDefinitions.Count == 0) { return; } foreach (KeyValuePair<string, DataObjects.ItemAddition> itemDefinition in ItemDefinitions) { if (itemDefinition.Value == null) { Logger.LogError("Item entry '" + itemDefinition.Key + "' is empty, it needs at least an action and a prefab. Skipping."); } else if (itemDefinition.Value.action != DataObjects.ItemAction.Modify) { try { CreateCustomItem(itemDefinition.Key, itemDefinition.Value); } catch (Exception arg) { Logger.LogError($"Item addition '{itemDefinition.Key}' failed and will be skipped. Error: {arg}"); } } } if (TrackedItems.Count > 0) { Logger.LogInfo($"Registered {TrackedItems.Count} custom item(s)."); } } private static void CreateCustomItem(string modificationName, DataObjects.ItemAddition def) { //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Expected O, but got Unknown if (!ValidateDefinition(modificationName, def)) { return; } if (def.action == DataObjects.ItemAction.Remove) { if (CreatedPrefabs.Contains(def.prefab)) { Logger.LogWarning("Item addition '" + modificationName + "' is set to Remove, but " + def.prefab + " was already registered this session. Restart the game to fully remove it."); } return; } DataObjects.TrackedItem trackedItem = new DataObjects.TrackedItem { action = def.action, prefab = def.prefab, clonedFrom = def.clonedFrom, definition = def }; if (CreatedPrefabs.Contains(def.prefab)) { GameObject prefab = PrefabManager.Instance.GetPrefab(def.prefab); if ((Object)(object)prefab == (Object)null) { Logger.LogWarning(def.prefab + " was registered earlier but can no longer be resolved, skipping."); return; } ApplyStatsOnly(prefab, def); trackedItem.itemPrefab = prefab; TrackedItems.Add(trackedItem); return; } if ((Object)(object)PrefabManager.Instance.GetPrefab(def.prefab) != (Object)null) { Logger.LogError("Item addition '" + modificationName + "' wants to create the prefab '" + def.prefab + "', but a prefab with that name already exists. Prefab names must be globally unique, and changing an item that already exists is done with 'action: Modify'. This addition will be skipped."); return; } GameObject val = PrefabManager.Instance.CreateClonedPrefab(def.prefab, def.clonedFrom); if ((Object)(object)val == (Object)null) { Logger.LogError("Item addition '" + modificationName + "' could not clone '" + def.clonedFrom + "'. Check that the prefab name is spelled correctly. This addition will be skipped."); return; } if ((Object)(object)val.GetComponent<ItemDrop>() == (Object)null) { Logger.LogError("'" + def.clonedFrom + "' has no ItemDrop component, so it cannot be used as the source for an item. Item addition '" + modificationName + "' will be skipped."); return; } ItemAttributes.RestoreOriginal(def.clonedFrom, GetSharedData(val)); ApplyDefinition(val, def); CustomItem val2 = new CustomItem(val, false); if (!val2.IsValid()) { Logger.LogError("The item created by '" + modificationName + "' is not valid and will be skipped."); return; } if (!ItemManager.Instance.AddItem(val2)) { Logger.LogError("Jotunn rejected the item created by '" + modificationName + "'. This addition will be skipped."); return; } CreatedPrefabs.Add(def.prefab); trackedItem.itemPrefab = val; trackedItem.customItem = val2; if (def.recipe != null) { CustomRecipe val3 = BuildRecipe(modificationName, def); if (val3 != null && !ItemManager.Instance.AddRecipe(val3)) { Logger.LogWarning("Could not register the recipe for '" + modificationName + "'. The item exists but will not be craftable."); } } TrackedItems.Add(trackedItem); Logger.LogInfo("Created item " + def.prefab + " from " + def.clonedFrom + "."); } private static bool ValidateDefinition(string modificationName, DataObjects.ItemAddition def) { if (string.IsNullOrEmpty(def.prefab)) { Logger.LogError("Item addition '" + modificationName + "' has no 'prefab' set. This is the name of the new item and is required. Skipping."); return false; } if (def.action != DataObjects.ItemAction.Remove && string.IsNullOrEmpty(def.clonedFrom)) { Logger.LogError("Item addition '" + modificationName + "' has no 'clonedFrom' set. This is the existing prefab to copy and is required. If you meant to change an item that already exists, use 'action: Modify' instead. Skipping."); return false; } if (string.Equals(def.prefab, def.clonedFrom, StringComparison.Ordinal)) { Logger.LogError("Item addition '" + modificationName + "' has 'prefab' and 'clonedFrom' set to the same value (" + def.prefab + "). The new item needs its own unique name, and changing the original in place is done with 'action: Modify'. Skipping."); return false; } return true; } public static void ApplyDefinition(GameObject prefab, DataObjects.ItemAddition def) { if (ApplyStatsOnly(prefab, def)) { ApplyArt(prefab, def.prefab, warnWhenIconless: true); } } private static bool ApplyStatsOnly(GameObject prefab, DataObjects.ItemAddition def) { SharedData sharedData = GetSharedData(prefab); if (sharedData == null) { Logger.LogWarning(def.prefab + " has no usable ItemDrop data, nothing to apply."); return false; } ItemAttributes.CaptureOriginal(def.prefab, sharedData); ItemAttributes.RestoreOriginal(def.prefab, sharedData); ApplyStats(sharedData, def, referencesAvailable: false); ItemAttributes.ApplyDefaultHitVariant(sharedData, new DataObjects.ItemAddition[1] { def }); return true; } private static void ApplyStats(SharedData shared, DataObjects.ItemAddition def, bool referencesAvailable) { if (def.action == DataObjects.ItemAction.Modify) { if (!string.IsNullOrEmpty(def.name)) { shared.m_name = Tokens.Resolve(def.name, "rm_item_", def.prefab, string.Empty); } } else { shared.m_name = ResolveNameToken(def); } string text = Tokens.Resolve(def.description, "rm_item_", def.prefab, "_desc"); if (text != null) { shared.m_description = text; } ItemAttributes.Apply(shared, def, referencesAvailable); } private static void ApplyArt(GameObject prefab, string prefabName, bool warnWhenIconless) { SharedData sharedData = GetSharedData(prefab); if (sharedData != null) { Sprite[] array = TextureTools.LoadIcons(prefabName, sharedData.m_icons); if (array != null && array.Length != 0) { sharedData.m_icons = array; } else if (warnWhenIconless) { Logger.LogWarning(prefabName + " ended up with no icons and would not be pickupable. Check that the prefab it was cloned from has an icon."); } int num = TextureTools.ApplyTextureOverrides(prefab, prefabName); if (num > 0) { Logger.LogInfo($"Applied {num} texture override(s) to {prefabName}."); } } } public static void ApplyItemModifications() { ApplyItemModifications(withArt: true); } private static void ApplyItemModifications(bool withArt) { Dictionary<string, List<DataObjects.ItemAddition>> dictionary = GroupModificationsByPrefab(); RevertPrefabs(Except(ModifiedPrefabs, dictionary.Keys)); ModifiedItems.Clear(); int num = 0; foreach (KeyValuePair<string, List<DataObjects.ItemAddition>> item in dictionary) { try { if (ApplyModification(item.Key, item.Value, withArt)) { num++; } } catch (Exception arg) { Logger.LogError($"Could not modify {item.Key}. Error: {arg}"); } } if (num > 0) { Logger.LogInfo($"Applied modifications to {num} existing item(s)."); } } private static bool ApplyModification(string prefabName, List<DataObjects.ItemAddition> definitions, bool withArt) { GameObject val = ResolveItemPrefab(prefabName); if ((Object)(object)val == (Object)null) { Logger.LogWarning("No item named '" + prefabName + "' could be found, the modification targeting it will be skipped. Prefab names are case sensitive, run RM_Items_PrintAll to see the ones that exist."); return false; } SharedData sharedData = GetSharedData(val); if (sharedData == null) { Logger.LogWarning("'" + prefabName + "' has no usable ItemDrop data, the modification targeting it will be skipped."); return false; } ItemAttributes.CaptureOriginal(prefabName, sharedData); ItemAttributes.RestoreOriginal(prefabName, sharedData); foreach (DataObjects.ItemAddition definition in definitions) { ApplyStats(sharedData, definition, referencesAvailable: true); ModifiedItems.Add(new DataObjects.TrackedItem { action = DataObjects.ItemAction.Modify, prefab = prefabName, itemPrefab = val, definition = definition }); } ItemAttributes.ApplyDefaultHitVariant(sharedData, definitions); if (withArt) { ApplyArt(val, prefabName, warnWhenIconless: false); } ModifiedPrefabs.Add(prefabName); if (ValConfig.EnableDebugMode.Value) { Logger.LogInfo("Modified " + prefabName + "."); } return true; } private static Dictionary<string, List<DataObjects.ItemAddition>> GroupModificationsByPrefab() { Dictionary<string, List<DataObjects.ItemAddition>> dictionary = new Dictionary<string, List<DataObjects.ItemAddition>>(); foreach (KeyValuePair<string, DataObjects.ItemAddition> itemDefinition in ItemDefinitions) { DataObjects.ItemAddition value = itemDefinition.Value; if (value == null || value.action != DataObjects.ItemAction.Modify) { continue; } if (string.IsNullOrEmpty(value.prefab)) { Logger.LogError("Item modification '" + itemDefinition.Key + "' has no 'prefab' set. This is the item to change and is required. Skipping."); } else if (CreatedPrefabs.Contains(value.prefab)) { Logger.LogError("Item modification '" + itemDefinition.Key + "' targets " + value.prefab + ", which is an item this mod adds. Put the stats on that 'action: Add' entry instead of modifying it separately. Skipping."); } else { if (!dictionary.TryGetValue(value.prefab, out var value2)) { value2 = new List<DataObjects.ItemAddition>(); dictionary.Add(value.prefab, value2); } value2.Add(value); } } return dictionary; } public static void RevertItemModifications() { RevertPrefabs(new List<string>(ModifiedPrefabs)); ModifiedItems.Clear(); } private static void RevertPrefabs(List<string> prefabNames) { foreach (string prefabName in prefabNames) { SharedData sharedData = GetSharedData(ResolveItemPrefab(prefabName)); if (sharedData == null) { Logger.LogWarning(prefabName + " can no longer be resolved, its original stats could not be restored."); } else if (ItemAttributes.RestoreOriginal(prefabName, sharedData) && ValConfig.EnableDebugMode.Value) { Logger.LogInfo("Restored the original stats of " + prefabName + "."); } ModifiedPrefabs.Remove(prefabName); } } private static List<string> Except(IEnumerable<string> source, ICollection<string> exclude) { List<string> list = new List<string>(); foreach (string item in source) { if (!exclude.Contains(item)) { list.Add(item); } } return list; } private static GameObject ResolveItemPrefab(string prefabName) { GameObject val = null; if ((Object)(object)ObjectDB.instance != (Object)null) { val = ObjectDB.instance.GetItemPrefab(prefabName); } if ((Object)(object)val == (Object)null) { val = PrefabManager.Instance.GetPrefab(prefabName); } return val; } private static SharedData GetSharedData(GameObject prefab) { if ((Object)(object)prefab == (Object)null) { return null; } return prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared; } public static void ResolveDeferredReferences() { ResolveDeferredReferences(TrackedItems); ResolveDeferredReferences(ModifiedItems); } private static void ResolveDeferredReferences(List<DataObjects.TrackedItem> tracked) { foreach (DataObjects.TrackedItem item in tracked) { if (item.definition != null && !((Object)(object)item.itemPrefab == (Object)null)) { SharedData sharedData = GetSharedData(item.itemPrefab); if (sharedData != null) { ItemAttributes.ApplyReferences(sharedData, item.definition, warnOnMissing: true); } } } } private static string ResolveNameToken(DataObjects.ItemAddition def) { if (string.IsNullOrEmpty(def.name)) { Logger.LogWarning("Item addition for " + def.prefab + " has no 'name' set. Falling back to the prefab name - set 'name' to give it a proper display name."); return Tokens.Resolve(def.prefab, "rm_item_", def.prefab, string.Empty); } return Tokens.Resolve(def.name, "rm_item_", def.prefab, string.Empty); } private static CustomRecipe BuildRecipe(string modificationName, DataObjects.ItemAddition def) { //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Expected O, but got Unknown //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Expected O, but got Unknown DataObjects.ItemRecipe recipe = def.recipe; List<RequirementConfig> list = new List<RequirementConfig>(); List<DataObjects.Ingrediant> list2 = new List<DataObjects.Ingrediant>(); foreach (DataObjects.Ingrediant item in recipe.ingredients ?? new List<DataObjects.Ingrediant>()) { if (item == null || string.IsNullOrEmpty(item.prefab)) { Logger.LogWarning("Recipe for '" + modificationName + "' has an ingredient with no prefab set, it will be ignored."); continue; } RequirementConfig val = new RequirementConfig { Item = item.prefab, Amount = item.craftCost.GetValueOrDefault(), AmountPerLevel = item.upgradeCost.GetValueOrDefault() }; if (val.Amount == 0 && val.AmountPerLevel == 0 && item.upgradeCosts != null && item.upgradeCosts.Count > 0) { val.AmountPerLevel = 1; } if (val.IsValid()) { list.Add(val); list2.Add(item); } } string text = StationPrefabName(modificationName, recipe.craftedAt); CustomRecipe val2 = new CustomRecipe(new RecipeConfig { Name = "Recipe_" + def.prefab, Item = def.prefab, Amount = (recipe.craftAmount ?? 1), CraftingStation = text, RepairStation = (string.IsNullOrEmpty(recipe.repairAt) ? text : StationPrefabName(modificationName, recipe.repairAt)), MinStationLevel = (recipe.minStationLevel ?? 1), RequireOnlyOneIngredient = (recipe.anyOneResource == true), Enabled = true, Requirements = list.ToArray() }); if ((Object)(object)val2.Recipe != (Object)null) { val2.Recipe.m_resources = RecipeFields.ApplyRequirements(val2.Recipe.m_resources, list2, null, null); for (int i = 0; i < list2.Count && i < val2.Recipe.m_resources.Length; i++) { if (RecipeUpgradeCosts.Register(RecipeUpgradeCosts.FromItems, val2.Recipe.m_resources[i], list2[i], "The recipe for '" + modificationName + "'")) { RecipeUpgradeCosts.ItemUsers.Add(modificationName); } } if (RecipeStationLevelCap.Register(RecipeStationLevelCap.FromItems, val2.Recipe, recipe.maxStationLevel, "The recipe for '" + modificationName + "'")) { RecipeStationLevelCap.ItemUsers.Add(modificationName); } } return val2; } private static string StationPrefabName(string modificationName, string configured) { if (string.IsNullOrEmpty(configured) || StationRef.IsNone(configured)) { return null; } CraftingStation val = StationRef.Resolve(configured); if ((Object)(object)val != (Object)null) { return StationRef.NameOf(val); } if (configured.StartsWith("$", StringComparison.Ordinal)) { Logger.LogWarning("The recipe for '" + modificationName + "' names the station " + configured + ", which matches no crafting station. Use the station's prefab name, such as piece_workbench."); } return configured; } public static void ReapplyExistingItems() { HashSet<string> hashSet = new HashSet<string>(); List<DataObjects.TrackedItem> list = new List<DataObjects.TrackedItem>(); foreach (KeyValuePair<string, DataObjects.ItemAddition> itemDefinition in ItemDefinitions) { DataObjects.ItemAddition def = itemDefinition.Value; if (def == null || string.IsNullOrEmpty(def.prefab) || def.action == DataObjects.ItemAction.Remove || def.action == DataObjects.ItemAction.Modify) { continue; } hashSet.Add(def.prefab); if (!CreatedPrefabs.Contains(def.prefab)) { Logger.LogWarning("'" + itemDefinition.Key + "' defines a new item (" + def.prefab + ") which cannot be added while the game is running. Restart the game or return to the main menu to create it."); continue; } GameObject prefab = PrefabManager.Instance.GetPrefab(def.prefab); if ((Object)(object)prefab == (Object)null) { Logger.LogWarning(def.prefab + " was registered but can no longer be resolved, skipping."); continue; } try { ApplyDefinition(prefab, def); if (ValConfig.EnableDebugMode.Value) { Logger.LogInfo("Refreshed " + def.prefab + "."); } } catch (Exception arg) { Logger.LogError($"Could not refresh {def.prefab}. Error: {arg}"); } list.Add(new DataObjects.TrackedItem { action = def.action, prefab = def.prefab, clonedFrom = def.clonedFrom, itemPrefab = prefab, customItem = TrackedItems.Find((DataObjects.TrackedItem item) => item.prefab == def.prefab)?.customItem, definition = def }); } TrackedItems.Clear(); TrackedItems.AddRange(list); foreach (string createdPrefab in CreatedPrefabs) { if (!hashSet.Contains(createdPrefab)) { Logger.LogWarning(createdPrefab + " was removed from the configuration but is already registered. Restart the game to remove it."); } } ApplyItemModifications(); ResolveDeferredReferences(TrackedItems); } public static void RefreshStatusEffectReferences() { foreach (DataObjects.TrackedItem trackedItem in TrackedItems) { if (trackedItem.definition != null && !((Object)(object)trackedItem.itemPrefab == (Object)null)) { try { ApplyStatsOnly(trackedItem.itemPrefab, trackedItem.definition); } catch (Exception arg) { Logger.LogError($"Could not refresh {trackedItem.prefab}. Error: {arg}"); } } } ApplyItemModifications(withArt: false); ResolveDeferredReferences(TrackedItems); } } internal static class ItemPieces { private const string WallStand = "itemstand"; private const string FloorStand = "itemstandh"; internal static GameObject Build(string key, DataObjects.PieceModification def, out GameObject source) { //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing referenc