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 ConfigurableCraftingRecipes v1.1.3
plugins\MGDev.ConfigurableCraftingRecipes.dll
Decompiled 18 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("MGDev.ConfigurableCraftingRecipes")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("MGDev.ConfigurableCraftingRecipes")] [assembly: AssemblyTitle("MGDev.ConfigurableCraftingRecipes")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace MGDev.ConfigurableCraftingRecipes { [BepInPlugin("mgdev.configurablecraftingrecipes", "MGDev Configurable Crafting Recipes", "1.1.3")] [BepInDependency("com.jotunn.jotunn", "2.20.0")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class ConfigurableCraftingRecipesPlugin : BaseUnityPlugin { private sealed class KnownRecipeConfig { public string ItemPrefab { get; } public ConfigEntry<bool> Enabled { get; } public ConfigEntry<int> Amount { get; } public ConfigEntry<string> Station { get; } public ConfigEntry<int> StationLevel { get; } public ConfigEntry<string> Requirements { get; } public KnownRecipeConfig(string itemPrefab, ConfigEntry<bool> enabled, ConfigEntry<int> amount, ConfigEntry<string> station, ConfigEntry<int> stationLevel, ConfigEntry<string> requirements) { ItemPrefab = itemPrefab; Enabled = enabled; Amount = amount; Station = station; StationLevel = stationLevel; Requirements = requirements; } } private sealed class TrophyRecipeConfig { public string DisplayName { get; } public string ItemPrefab { get; } public ConfigEntry<bool> Enabled { get; } public ConfigEntry<string> Station { get; } public ConfigEntry<string> Requirements { get; } public TrophyRecipeConfig(string displayName, string itemPrefab, ConfigEntry<bool> enabled, ConfigEntry<string> station, ConfigEntry<string> requirements) { DisplayName = displayName; ItemPrefab = itemPrefab; Enabled = enabled; Station = station; Requirements = requirements; } } private sealed class CustomRecipeConfig { public ConfigEntry<bool> Enabled { get; } public ConfigEntry<string> Item { get; } public ConfigEntry<int> Amount { get; } public ConfigEntry<string> Station { get; } public ConfigEntry<int> StationLevel { get; } public ConfigEntry<string> Requirements { get; } public CustomRecipeConfig(ConfigEntry<bool> enabled, ConfigEntry<string> item, ConfigEntry<int> amount, ConfigEntry<string> station, ConfigEntry<int> stationLevel, ConfigEntry<string> requirements) { Enabled = enabled; Item = item; Amount = amount; Station = station; StationLevel = stationLevel; Requirements = requirements; } } private sealed class RecipeDefinition { public string ItemPrefab { get; } public int OutputAmount { get; } public string Station { get; } public int StationLevel { get; } public List<IngredientDefinition> Ingredients { get; } public RecipeDefinition(string itemPrefab, int outputAmount, string station, int stationLevel, List<IngredientDefinition> ingredients) { ItemPrefab = itemPrefab; OutputAmount = outputAmount; Station = station; StationLevel = stationLevel; Ingredients = ingredients; } } private sealed class IngredientDefinition { public string ItemPrefab { get; } public int Amount { get; } public IngredientDefinition(string itemPrefab, int amount) { ItemPrefab = itemPrefab; Amount = amount; } } public const string ModGuid = "mgdev.configurablecraftingrecipes"; public const string ModName = "MGDev Configurable Crafting Recipes"; public const string ModVersion = "1.1.3"; private static readonly Dictionary<string, string> StationAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["None"] = "", ["NoStation"] = "", ["Workbench"] = "piece_workbench", ["WorkBench"] = "piece_workbench", ["Forge"] = "forge", ["Cauldron"] = "piece_cauldron", ["Stonecutter"] = "piece_stonecutter", ["StoneCutter"] = "piece_stonecutter", ["ArtisanTable"] = "piece_artisanstation", ["Artisan"] = "piece_artisanstation", ["BlackForge"] = "blackforge", ["GaldrTable"] = "piece_magetable", ["FoodPreparationTable"] = "piece_preptable", ["PrepTable"] = "piece_preptable" }; private static readonly List<KnownRecipeConfig> KnownRecipes = new List<KnownRecipeConfig>(); private static readonly List<TrophyRecipeConfig> TrophyRecipes = new List<TrophyRecipeConfig>(); private static readonly List<CustomRecipeConfig> CustomRecipes = new List<CustomRecipeConfig>(); private static ConfigEntry<bool> EnableTrophyRecipes; private static ManualLogSource Log; private static ObjectDB RegisteredObjectDB; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; BindConfig(); ItemManager.OnItemsRegistered += RegisterConfiguredRecipes; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded. Recipes will register when Jotunn finishes loading items."); } private void OnDestroy() { ItemManager.OnItemsRegistered -= RegisterConfiguredRecipes; } private void BindConfig() { KnownRecipes.Clear(); TrophyRecipes.Clear(); CustomRecipes.Clear(); AddKnownRecipe("1 - Custom Recipes", "Chain", "Chain", enabled: true, 1, "Forge", 3, "Iron:2"); AddKnownRecipe("1 - Custom Recipes", "Fishing Rod", "FishingRod", enabled: true, 1, "Workbench", 3, "FineWood:10,Iron:2"); AddKnownRecipe("1 - Custom Recipes", "Fishing Bait", "FishingBait", enabled: true, 20, "FoodPreparationTable", 1, "Entrails:5"); AddKnownRecipe("1 - Custom Recipes", "Thunderstone", "Thunderstone", enabled: true, 1, "Forge", 2, "SurtlingCore:10,Iron:1"); AddKnownRecipe("1 - Custom Recipes", "Barrel Rings", "BarrelRings", enabled: true, 1, "Forge", 1, "Iron:2"); AddKnownRecipe("1 - Custom Recipes", "Surtling Core", "SurtlingCore", enabled: true, 1, "Forge", 1, "Coal:25,Stone:10,Resin:5"); AddKnownRecipe("2 - Valuables Recipes", "Amber from Coins", "Amber", enabled: true, 1, "None", 1, "Coins:5"); AddKnownRecipe("2 - Valuables Recipes", "Amber Pearl from Coins", "AmberPearl", enabled: true, 1, "None", 1, "Coins:10"); AddKnownRecipe("2 - Valuables Recipes", "Ruby from Coins", "Ruby", enabled: true, 1, "None", 1, "Coins:20"); AddKnownRecipe("2 - Valuables Recipes", "Silver Necklace from Coins", "SilverNecklace", enabled: true, 1, "None", 1, "Coins:30"); AddKnownRecipe("2 - Valuables Recipes", "Coins from Amber", "Coins", enabled: true, 5, "None", 1, "Amber:1"); AddKnownRecipe("2 - Valuables Recipes", "Coins from Amber Pearl", "Coins", enabled: true, 10, "None", 1, "AmberPearl:1"); AddKnownRecipe("2 - Valuables Recipes", "Coins from Ruby", "Coins", enabled: true, 20, "None", 1, "Ruby:1"); AddKnownRecipe("2 - Valuables Recipes", "Coins from Silver Necklace", "Coins", enabled: true, 30, "None", 1, "SilverNecklace:1"); EnableTrophyRecipes = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Trophy Recipes", "Enable All Trophy Recipes", true, "Master toggle for all trophy crafting recipes."); AddTrophyRecipe("Boar", "TrophyBoar", enabled: true, "None", "LeatherScraps:10"); AddTrophyRecipe("Deer", "TrophyDeer", enabled: true, "None", "DeerHide:10"); AddTrophyRecipe("Neck", "TrophyNeck", enabled: true, "None", "NeckTail:10"); AddTrophyRecipe("Eikthyr", "TrophyEikthyr", enabled: true, "None", "HardAntler:10"); AddTrophyRecipe("Greydwarf", "TrophyGreydwarf", enabled: true, "None", "GreydwarfEye:10"); AddTrophyRecipe("Skeleton", "TrophySkeleton", enabled: true, "None", "BoneFragments:10"); AddTrophyRecipe("Troll", "TrophyFrostTroll", enabled: true, "None", "TrollHide:10"); AddTrophyRecipe("Elder", "TrophyTheElder", enabled: true, "None", "AncientSeed:10"); AddTrophyRecipe("Serpent", "TrophySerpent", enabled: true, "None", "SerpentScale:50"); AddTrophyRecipe("Leech", "TrophyLeech", enabled: true, "None", "Bloodbag:20"); AddTrophyRecipe("Surtling", "TrophySurtling", enabled: true, "None", "SurtlingCore:2"); AddTrophyRecipe("Abomination", "TrophyAbomination", enabled: true, "None", "Root:10"); AddTrophyRecipe("Bonemass", "TrophyBonemass", enabled: true, "None", "WitheredBone:20"); AddTrophyRecipe("Wolf", "TrophyWolf", enabled: true, "None", "WolfPelt:10"); AddTrophyRecipe("Drake", "TrophyHatchling", enabled: true, "None", "FreezeGland:3"); AddTrophyRecipe("Fenring", "TrophyFenring", enabled: true, "None", "WolfClaw:10"); AddTrophyRecipe("Cultist", "TrophyCultist", enabled: true, "None", "WolfHairBundle:10"); AddTrophyRecipe("Stone Golem", "TrophySGolem", enabled: true, "None", "Crystal:100"); AddTrophyRecipe("Moder", "TrophyDragonQueen", enabled: true, "None", "DragonTear:10"); AddTrophyRecipe("Deathsquito", "TrophyDeathsquito", enabled: true, "None", "Needle:50"); AddTrophyRecipe("Fuling Shaman", "TrophyGoblinShaman", enabled: true, "None", "GoblinTotem:2"); AddTrophyRecipe("Yagluth", "TrophyGoblinKing", enabled: true, "None", "TornSpirit:10"); AddTrophyRecipe("Growth", "TrophyGrowth", enabled: true, "None", "Tar:20"); AddTrophyRecipe("Lox", "TrophyLox", enabled: true, "None", "LoxPelt:10"); AddTrophyRecipe("Hare", "TrophyHare", enabled: true, "None", "ScaleHide:10"); AddTrophyRecipe("Tick", "TrophyTick", enabled: true, "None", "GiantBloodSack:10"); AddTrophyRecipe("Seeker", "TrophySeeker", enabled: true, "None", "Carapace:10"); AddTrophyRecipe("Gjall", "TrophyGjall", enabled: true, "None", "Bilebag:10"); AddTrophyRecipe("Queen", "TrophySeekerQueen", enabled: true, "None", "QueenDrop:10"); AddTrophyRecipe("Asksvin", "TrophyAsksvin", enabled: true, "None", "AskHide:10"); AddTrophyRecipe("Morgen", "TrophyMorgen", enabled: true, "None", "MorgenSinew:10"); AddTrophyRecipe("Fallen Valkyrie", "TrophyFallenValkyrie", enabled: true, "None", "CelestialFeather:10"); AddTrophyRecipe("Bonemaw Serpent", "TrophyBonemawSerpent", enabled: true, "None", "BonemawMeat:10"); AddTrophyRecipe("Fader", "TrophyFader", enabled: true, "None", "FaderDrop:10"); for (int i = 1; i <= 10; i++) { AddCustomRecipe(i); } } private void AddKnownRecipe(string section, string name, string item, bool enabled, int amount, string station, int stationLevel, string requirements) { KnownRecipes.Add(new KnownRecipeConfig(item, ((BaseUnityPlugin)this).Config.Bind<bool>(section, name + " - Enabled", enabled, "Enable the " + name + " recipe."), ((BaseUnityPlugin)this).Config.Bind<int>(section, name + " - Amount", amount, "How many " + item + " this recipe creates."), ((BaseUnityPlugin)this).Config.Bind<string>(section, name + " - Station", station, "Crafting station. Use None, Workbench, Forge, Cauldron, StoneCutter, ArtisanTable, BlackForge, GaldrTable, FoodPreparationTable, or an exact station prefab name."), ((BaseUnityPlugin)this).Config.Bind<int>(section, name + " - Station Level", stationLevel, "Required crafting station level. Use 1 for no upgrade requirement."), ((BaseUnityPlugin)this).Config.Bind<string>(section, name + " - Requirements", requirements, "Required items as ItemPrefab:Amount pairs separated by commas."))); } private void AddTrophyRecipe(string name, string item, bool enabled, string station, string requirements) { TrophyRecipes.Add(new TrophyRecipeConfig(name, item, ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Trophy Recipes", name + " - Enabled", enabled, "Enable crafting for " + name + " trophy."), ((BaseUnityPlugin)this).Config.Bind<string>("3 - Trophy Recipes", name + " - Station", station, "Crafting station. Use None, Workbench, Forge, Cauldron, StoneCutter, ArtisanTable, BlackForge, GaldrTable, FoodPreparationTable, or an exact station prefab name."), ((BaseUnityPlugin)this).Config.Bind<string>("3 - Trophy Recipes", name + " - Requirements", requirements, "Required items as ItemPrefab:Amount pairs separated by commas."))); } private void AddCustomRecipe(int index) { string text = $"Slot {index:00}"; CustomRecipes.Add(new CustomRecipeConfig(((BaseUnityPlugin)this).Config.Bind<bool>("4 - Extra Recipes", text + " - Enabled", false, "Enable this custom recipe slot."), ((BaseUnityPlugin)this).Config.Bind<string>("4 - Extra Recipes", text + " - Item", "", "Item prefab name to craft. Example: TrophyDeer, Chain, SurtlingCore"), ((BaseUnityPlugin)this).Config.Bind<int>("4 - Extra Recipes", text + " - Amount", 1, "How many items this recipe creates."), ((BaseUnityPlugin)this).Config.Bind<string>("4 - Extra Recipes", text + " - Station", "Workbench", "Crafting station. Use None, Workbench, Forge, Cauldron, StoneCutter, ArtisanTable, BlackForge, GaldrTable, FoodPreparationTable, or an exact station prefab name."), ((BaseUnityPlugin)this).Config.Bind<int>("4 - Extra Recipes", text + " - Station Level", 1, "Required crafting station level. Use 1 for no upgrade requirement."), ((BaseUnityPlugin)this).Config.Bind<string>("4 - Extra Recipes", text + " - Requirements", "", "Required items as ItemPrefab:Amount pairs separated by commas. Example: Wood:5,LeatherScraps:2"))); } private static void RegisterConfiguredRecipes() { if ((Object)(object)ObjectDB.instance == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"ObjectDB is not available yet. Recipes were not registered."); } } else { if ((Object)(object)RegisteredObjectDB == (Object)(object)ObjectDB.instance) { return; } RegisteredObjectDB = ObjectDB.instance; int num = 0; foreach (RecipeDefinition configuredRecipe in GetConfiguredRecipes()) { if (!ValidateRecipe(configuredRecipe)) { continue; } Recipe valheimRecipe = CreateValheimRecipe(configuredRecipe); if (!((Object)(object)valheimRecipe == (Object)null)) { ObjectDB.instance.m_recipes.RemoveAll((Recipe existing) => (Object)(object)existing != (Object)null && ((Object)existing).name == ((Object)valheimRecipe).name); ObjectDB.instance.m_recipes.Add(valheimRecipe); num++; ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)$"Registered recipe: {configuredRecipe.ItemPrefab} x{configuredRecipe.OutputAmount} at {configuredRecipe.Station}."); } } } ManualLogSource log3 = Log; if (log3 != null) { log3.LogInfo((object)$"Registered {num} configurable crafting recipe(s)."); } } } private static IEnumerable<RecipeDefinition> GetConfiguredRecipes() { foreach (KnownRecipeConfig knownRecipe in KnownRecipes) { if (knownRecipe.Enabled.Value) { yield return CreateRecipeDefinition(knownRecipe.ItemPrefab, knownRecipe.Amount.Value, knownRecipe.Station.Value, knownRecipe.StationLevel.Value, knownRecipe.Requirements.Value); } } if (EnableTrophyRecipes.Value) { foreach (TrophyRecipeConfig trophyRecipe in TrophyRecipes) { if (trophyRecipe.Enabled.Value) { yield return CreateRecipeDefinition(trophyRecipe.ItemPrefab, 1, trophyRecipe.Station.Value, 1, trophyRecipe.Requirements.Value); } } } foreach (CustomRecipeConfig customRecipe in CustomRecipes) { if (customRecipe.Enabled.Value) { yield return CreateRecipeDefinition(customRecipe.Item.Value.Trim(), customRecipe.Amount.Value, customRecipe.Station.Value, customRecipe.StationLevel.Value, customRecipe.Requirements.Value); } } } private static RecipeDefinition CreateRecipeDefinition(string itemPrefab, int amount, string station, int stationLevel, string requirements) { return new RecipeDefinition(itemPrefab, amount, station.Trim(), Math.Max(1, stationLevel), ParseIngredients(requirements, itemPrefab)); } private static List<IngredientDefinition> ParseIngredients(string value, string recipeText) { List<IngredientDefinition> list = new List<IngredientDefinition>(); string[] array = value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); string[] array2 = text.Split(new char[1] { ':' }); if (array2.Length != 2 || !int.TryParse(array2[1].Trim(), out var result) || result <= 0) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Ignored invalid ingredient '" + text + "' in recipe '" + recipeText + "'.")); } continue; } string text2 = array2[0].Trim(); if (text2.Length == 0) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("Ignored invalid ingredient '" + text + "' in recipe '" + recipeText + "'.")); } } else { list.Add(new IngredientDefinition(text2, result)); } } return list; } private static bool ValidateRecipe(RecipeDefinition recipe) { if (string.IsNullOrWhiteSpace(recipe.ItemPrefab)) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"Ignored recipe with empty item prefab."); } return false; } if (recipe.OutputAmount <= 0) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("Ignored recipe '" + recipe.ItemPrefab + "'. Amount must be positive.")); } return false; } if (recipe.Ingredients.Count == 0) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)("Ignored recipe '" + recipe.ItemPrefab + "'. At least one ingredient is required.")); } return false; } if (!ItemExists(recipe.ItemPrefab)) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogWarning((object)("Ignored recipe for missing item prefab: " + recipe.ItemPrefab)); } return false; } string text = ResolveStation(recipe.Station); if (text.Length > 0 && !StationExists(text)) { ManualLogSource log5 = Log; if (log5 != null) { log5.LogWarning((object)("Ignored recipe '" + recipe.ItemPrefab + "'. Crafting station not found: " + recipe.Station + " -> " + text)); } return false; } foreach (IngredientDefinition ingredient in recipe.Ingredients) { if (!ItemExists(ingredient.ItemPrefab)) { ManualLogSource log6 = Log; if (log6 != null) { log6.LogWarning((object)("Ignored recipe '" + recipe.ItemPrefab + "'. Ingredient prefab not found: " + ingredient.ItemPrefab)); } return false; } } return true; } private static Recipe CreateValheimRecipe(RecipeDefinition recipe) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(recipe.ItemPrefab); ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Ignored recipe '" + recipe.ItemPrefab + "'. Output item has no ItemDrop.")); } return null; } string text = ResolveStation(recipe.Station); CraftingStation val2 = null; if (text.Length > 0) { GameObject prefab = ZNetScene.instance.GetPrefab(text); val2 = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<CraftingStation>() : null); if ((Object)(object)val2 == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("Ignored recipe '" + recipe.ItemPrefab + "'. Crafting station has no CraftingStation component: " + recipe.Station + " -> " + text)); } return null; } } Recipe obj = ScriptableObject.CreateInstance<Recipe>(); ((Object)obj).name = string.Format("MGDev_{0}_{1}_Recipe", recipe.ItemPrefab, Math.Abs(string.Join("_", recipe.Ingredients.Select((IngredientDefinition ingredient) => ingredient.ItemPrefab)).GetHashCode())); obj.m_item = val; obj.m_amount = recipe.OutputAmount; obj.m_enabled = true; obj.m_craftingStation = val2; obj.m_minStationLevel = Math.Max(1, recipe.StationLevel); obj.m_resources = ((IEnumerable<IngredientDefinition>)recipe.Ingredients).Select((Func<IngredientDefinition, Requirement>)delegate(IngredientDefinition ingredient) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_003d: Expected O, but got Unknown GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab(ingredient.ItemPrefab); return new Requirement { m_resItem = itemPrefab2.GetComponent<ItemDrop>(), m_amount = ingredient.Amount, m_amountPerLevel = 0, m_recover = true }; }).ToArray(); return obj; } private static string ResolveStation(string station) { if (string.IsNullOrWhiteSpace(station)) { return ""; } string text = station.Trim(); if (!StationAliases.TryGetValue(text, out var value)) { return text; } return value; } private static bool ItemExists(string prefabName) { if ((Object)(object)ObjectDB.instance != (Object)null) { return (Object)(object)ObjectDB.instance.GetItemPrefab(prefabName) != (Object)null; } return false; } private static bool StationExists(string prefabName) { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefabName) : null); if ((Object)(object)val != (Object)null) { return (Object)(object)val.GetComponent<CraftingStation>() != (Object)null; } return false; } } }