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 CustomItemRegistry v0.6.2
CustomItemRegistry.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Jotunn; using Jotunn.Configs; using Jotunn.Entities; 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.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("BreakoutMods")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.6.2.0")] [assembly: AssemblyInformationalVersion("0.6.2")] [assembly: AssemblyProduct("CustomItemRegistry")] [assembly: AssemblyTitle("CustomItemRegistry")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/BreakoutMods/CIR-CustomItemRegistry")] [assembly: AssemblyVersion("0.6.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [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 ValheimCustomItemRegistry { public struct Ingredient { public string itemName; public int amount; public int amountPerLevel; public bool recover; internal string SourceModGuid; internal bool IsVanilla; internal string HelperValidationError; public Ingredient(string itemName, int amount, int amountPerLevel = 0, bool recover = true) { this.itemName = itemName; this.amount = amount; this.amountPerLevel = amountPerLevel; this.recover = recover; SourceModGuid = null; IsVanilla = false; HelperValidationError = null; } public static Ingredient From(VanillaItem item, int amount, int amountPerLevel = 0, bool recover = true) { return From(ItemRef.Vanilla(item), amount, amountPerLevel, recover); } public static Ingredient From(ItemRef item, int amount, int amountPerLevel = 0, bool recover = true) { Ingredient result = new Ingredient(item.PrefabName, amount, amountPerLevel, recover); result.SourceModGuid = item.SourceModGuid; result.IsVanilla = item.IsVanilla; result.HelperValidationError = item.ValidationError; return result; } } public struct CraftingRecipe { public List<Ingredient> ingredients; public string craftingStation; public string repairStation; public int amount; public int minStationLevel; public bool? enabled; public bool requireOnlyOneIngredient; public int qualityResultAmountMultiplier; internal string CraftingStationValidationError; internal string RepairStationValidationError; public CraftingRecipe(List<Ingredient> ingredients, string craftingStation, int amount = 1, string repairStation = null, int minStationLevel = 1, bool enabled = true, bool requireOnlyOneIngredient = false, int qualityResultAmountMultiplier = 1) { this.ingredients = ingredients; this.craftingStation = craftingStation; this.repairStation = repairStation; this.amount = amount; this.minStationLevel = minStationLevel; this.enabled = enabled; this.requireOnlyOneIngredient = requireOnlyOneIngredient; this.qualityResultAmountMultiplier = qualityResultAmountMultiplier; CraftingStationValidationError = null; RepairStationValidationError = null; } } public sealed class CustomItemDefinition { public DamageTypes Damages; public DamageTypes DamagesPerLevel; public string ItemName { get; set; } public string AssetBundlePath { get; set; } public AssetBundle AssetBundle { get; set; } public string PrefabName { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public string IconAssetName { get; set; } public Sprite Icon { get; set; } public PrefabPreparationOptions PrefabPreparation { get; set; } public CraftingRecipe Recipe { get; set; } public bool HasRecipe { get; set; } public ItemType? ItemType { get; set; } public float? Weight { get; set; } public int? StackSize { get; set; } public float? MaxDurability { get; set; } public float? DurabilityPerLevel { get; set; } public int? MaxQuality { get; set; } public int? ToolTier { get; set; } public float? Armor { get; set; } public float? ArmorPerLevel { get; set; } public float? BlockPower { get; set; } public float? BlockPowerPerLevel { get; set; } public float? DeflectionForce { get; set; } public float? DeflectionForcePerLevel { get; set; } public float? MovementModifier { get; set; } public bool? Teleportable { get; set; } public bool? CanBeRepaired { get; set; } public bool HasDamages { get; set; } public bool HasDamagesPerLevel { get; set; } public IList<Action<SharedData>> SharedDataConfigurators { get; private set; } internal string TemplateName { get; set; } internal bool TemplateRequiresDamage { get; set; } internal bool TemplateRequiresBlockPower { get; set; } internal bool TemplateRequiresArmor { get; set; } internal bool TemplateRequiresFoodStats { get; set; } internal bool TemplateHasFoodStats { get; set; } public CustomItemDefinition() { SharedDataConfigurators = new List<Action<SharedData>>(); PrefabPreparation = new PrefabPreparationOptions(); } public CustomItemDefinition(string itemName) : this() { ItemName = itemName; } } public sealed class CustomItemRegistrationException : Exception { public string ItemName { get; private set; } public string AssetBundlePath { get; private set; } public string PrefabName { get; private set; } public CustomItemRegistrationException(string message) : base(message) { } public CustomItemRegistrationException(string message, Exception innerException) : base(message, innerException) { } internal CustomItemRegistrationException(CustomItemDefinition definition, string message) : base(BuildMessage(definition, message)) { Capture(definition); } internal CustomItemRegistrationException(CustomItemDefinition definition, string message, Exception innerException) : base(BuildMessage(definition, message), innerException) { Capture(definition); } private void Capture(CustomItemDefinition definition) { if (definition != null) { ItemName = definition.ItemName; AssetBundlePath = definition.AssetBundlePath; PrefabName = definition.PrefabName; } } private static string BuildMessage(CustomItemDefinition definition, string message) { if (definition == null) { return message; } string text = definition.AssetBundlePath ?? (Object.op_Implicit((Object)(object)definition.AssetBundle) ? ((Object)definition.AssetBundle).name : "<null>"); return "Item '" + (definition.ItemName ?? "<null>") + "' from bundle '" + text + "' prefab '" + (definition.PrefabName ?? "<null>") + "': " + message; } } public static class CustomItemRegistry { private sealed class RegisteredItem { public readonly string ItemName; public readonly GameObject Prefab; public readonly CustomItem CustomItem; public RegisteredItem(string itemName, GameObject prefab, CustomItem customItem) { ItemName = itemName; Prefab = prefab; CustomItem = customItem; } } private sealed class PrefabPreparationReport { public readonly List<string> AddedComponents = new List<string>(); public readonly List<string> Warnings = new List<string>(); public bool CreatedItemDrop; } private static readonly Dictionary<string, RegisteredItem> RegisteredItems = new Dictionary<string, RegisteredItem>(); private static readonly Dictionary<string, AssetBundle> LoadedAssetBundles = new Dictionary<string, AssetBundle>(); private static ManualLogSource logger; public static CustomItemBuilder Item(string itemName) { return new CustomItemBuilder(itemName); } public static void RegisterItem(string itemName, string assetBundlePath, string prefabName, CraftingRecipe recipe) { if (RegisteredItems.ContainsKey(itemName)) { LogWarning("Item '" + itemName + "' is already registered"); return; } RegisterItem(new CustomItemDefinition(itemName) { AssetBundlePath = assetBundlePath, PrefabName = prefabName, Recipe = recipe, HasRecipe = true }); } public static ItemRegistrationResult RegisterItem(CustomItemDefinition definition) { //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown GameObject val = null; try { ValidateDefinition(definition); AssetBundle assetBundle = GetAssetBundle(definition); GameObject val2 = assetBundle.LoadAsset<GameObject>(definition.PrefabName); if (!Object.op_Implicit((Object)(object)val2)) { throw new CustomItemRegistrationException(definition, CreateMissingAssetMessage(definition, assetBundle, definition.PrefabName, "prefab")); } ItemDrop component = val2.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null) { bool flag = component.m_itemData == null; if (flag) { component.m_itemData = new ItemData(); } bool flag2 = component.m_itemData.m_shared == null; if (flag2) { component.m_itemData.m_shared = new SharedData(); } if (flag || flag2) { string text = (flag ? "m_itemData and m_itemData.m_shared" : "m_itemData.m_shared"); LogWarning("Prefab '" + definition.PrefabName + "' for item '" + definition.ItemName + "' has an ItemDrop component with uninitialized " + text + ". CIR initialized them to prevent a crash in ItemDrop.Awake(). Set up the ItemDrop component in Unity to remove this warning."); } } val = Object.Instantiate<GameObject>(val2); ((Object)val).name = definition.ItemName; val.SetActive(false); PrefabPreparationReport preparationReport = PrepareItemPrefab(definition, val); string iconSource = LoadIcon(definition, assetBundle); ItemConfig val3 = CreateItemConfig(definition); CustomItem val4 = new CustomItem(val, true, val3); ApplyGearMetadata(definition, val); ApplySharedDataConfigurators(definition, val); ValidatePreparedItem(definition, val, val4); WarnForMissingIngredients(definition); if (!ItemManager.Instance.AddItem(val4)) { throw new CustomItemRegistrationException(definition, "Jotunn rejected the custom item"); } RegisteredItems.Add(definition.ItemName, new RegisteredItem(definition.ItemName, val, val4)); FlushLiveRegistrations(); ItemRegistrationResult result = ItemRegistrationResult.Registered(definition, val, val4); LogRegistrationSuccess(definition, val, iconSource, preparationReport); return result; } catch (Exception ex) { if (Object.op_Implicit((Object)(object)val)) { Object.Destroy((Object)(object)val); } CustomItemRegistrationException obj = ((ex is CustomItemRegistrationException ex2 && !string.IsNullOrEmpty(ex2.ItemName)) ? ex2 : new CustomItemRegistrationException(definition, ex.Message, ex)); LogWarning(obj.Message); throw obj; } } public static bool TryRegisterItem(CustomItemDefinition definition, out ItemRegistrationResult result) { try { result = RegisterItem(definition); return true; } catch (Exception exception) { result = ItemRegistrationResult.Failed(definition, exception); return false; } } public static IReadOnlyList<ItemRegistrationResult> RegisterItems(IEnumerable<CustomItemDefinition> definitions) { if (definitions == null) { throw new ArgumentNullException("definitions"); } List<ItemRegistrationResult> list = new List<ItemRegistrationResult>(); foreach (CustomItemDefinition definition in definitions) { list.Add(RegisterItem(definition)); } return list; } public static ItemPackLoadResult LoadItemPacks() { return ItemPackLoader.LoadDefault(null); } public static ItemPackLoadResult LoadItemPacksFromDirectory(string directory, ItemPackLoadOptions options = null) { return ItemPackLoader.LoadDirectory(directory, options); } public static ItemPackLoadResult LoadItemPack(string filePath, ItemPackLoadOptions options = null) { return ItemPackLoader.LoadFile(filePath, options); } public static ItemPackParserStatus GetItemPackParserStatus() { return ItemPackLoader.GetParserStatus(); } internal static void SetLogger(ManualLogSource manualLogSource) { logger = manualLogSource; } internal static void FlushLiveRegistrations(ObjectDB objectDB = null, ZNetScene zNetScene = null) { if (RegisteredItems.Count == 0) { return; } ObjectDB val = (Object.op_Implicit((Object)(object)objectDB) ? objectDB : ObjectDB.instance); ZNetScene val2 = (Object.op_Implicit((Object)(object)zNetScene) ? zNetScene : ZNetScene.instance); foreach (RegisteredItem value in RegisteredItems.Values) { if (Object.op_Implicit((Object)(object)val2)) { PrefabManager.Instance.RegisterToZNetScene(value.Prefab); } if (Object.op_Implicit((Object)(object)val)) { RegisterPrefabInObjectDB(value); RegisterRecipeInObjectDB(val, value); } } } internal static bool PrepareItemPrefabForTest(CustomItemDefinition definition, GameObject itemPrefab, out IReadOnlyList<string> addedComponents, out IReadOnlyList<string> warnings, out string error) { addedComponents = Array.Empty<string>(); warnings = Array.Empty<string>(); error = null; try { PrefabPreparationReport prefabPreparationReport = PrepareItemPrefab(definition, itemPrefab); addedComponents = prefabPreparationReport.AddedComponents; warnings = prefabPreparationReport.Warnings; return true; } catch (Exception ex) { error = ex.Message; return false; } } internal static void UnloadAssetBundles() { foreach (AssetBundle value in LoadedAssetBundles.Values) { if (Object.op_Implicit((Object)(object)value)) { value.Unload(false); } } LoadedAssetBundles.Clear(); } private static void ValidateDefinition(CustomItemDefinition definition) { if (definition == null) { throw new CustomItemRegistrationException("Custom item definition is required"); } if (string.IsNullOrWhiteSpace(definition.ItemName)) { throw new CustomItemRegistrationException(definition, "Item name is required"); } if (!Object.op_Implicit((Object)(object)definition.AssetBundle) && string.IsNullOrWhiteSpace(definition.AssetBundlePath)) { throw new CustomItemRegistrationException(definition, "AssetBundle path is required"); } if (string.IsNullOrWhiteSpace(definition.PrefabName)) { throw new CustomItemRegistrationException(definition, "Prefab name is required"); } if (RegisteredItems.ContainsKey(definition.ItemName)) { throw new CustomItemRegistrationException(definition, "Item name is already registered"); } ValidateRecipe(definition); ValidateGear(definition); } private static void ValidateRecipe(CustomItemDefinition definition) { if (!definition.HasRecipe) { return; } CraftingRecipe recipe = definition.Recipe; if (!string.IsNullOrEmpty(recipe.CraftingStationValidationError)) { throw new CustomItemRegistrationException(definition, recipe.CraftingStationValidationError); } if (!string.IsNullOrEmpty(recipe.RepairStationValidationError)) { throw new CustomItemRegistrationException(definition, recipe.RepairStationValidationError); } if (recipe.amount <= 0) { throw new CustomItemRegistrationException(definition, "Recipe amount must be greater than zero"); } if (recipe.minStationLevel < 0) { throw new CustomItemRegistrationException(definition, "Recipe minimum station level cannot be negative"); } if (recipe.qualityResultAmountMultiplier < 0) { throw new CustomItemRegistrationException(definition, "Recipe quality result amount multiplier cannot be negative"); } if (recipe.ingredients == null) { return; } foreach (Ingredient ingredient in recipe.ingredients) { if (!string.IsNullOrEmpty(ingredient.HelperValidationError)) { throw new CustomItemRegistrationException(definition, ingredient.HelperValidationError); } if (string.IsNullOrWhiteSpace(ingredient.itemName)) { throw new CustomItemRegistrationException(definition, "Recipe contains an ingredient with an empty item name"); } if (ingredient.amount < 0) { throw new CustomItemRegistrationException(definition, "Recipe ingredient '" + ingredient.itemName + "' amount cannot be negative"); } if (ingredient.amountPerLevel < 0) { throw new CustomItemRegistrationException(definition, "Recipe ingredient '" + ingredient.itemName + "' amount per level cannot be negative"); } if (ingredient.amount == 0 && ingredient.amountPerLevel == 0) { throw new CustomItemRegistrationException(definition, "Recipe ingredient '" + ingredient.itemName + "' must have a craft amount or upgrade amount per level"); } } } private static void ValidateGear(CustomItemDefinition definition) { if (definition.Weight.HasValue && definition.Weight.Value < 0f) { throw new CustomItemRegistrationException(definition, "Weight cannot be negative"); } if (definition.StackSize.HasValue && definition.StackSize.Value < 1) { throw new CustomItemRegistrationException(definition, "Stack size must be greater than zero"); } if (definition.MaxDurability.HasValue && definition.MaxDurability.Value < 0f) { throw new CustomItemRegistrationException(definition, "Durability cannot be negative"); } if (definition.MaxQuality.HasValue && definition.MaxQuality.Value < 1) { throw new CustomItemRegistrationException(definition, "Max quality must be greater than zero"); } ValidateTemplate(definition); } private static void ValidateTemplate(CustomItemDefinition definition) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrEmpty(definition.TemplateName)) { string text = "Template '" + definition.TemplateName + "'"; if (definition.TemplateRequiresDamage && !HasAnyDamage(definition.Damages) && !HasAnyDamage(definition.DamagesPerLevel)) { throw new CustomItemRegistrationException(definition, text + " requires at least one damage value"); } if (definition.TemplateRequiresBlockPower && (!definition.BlockPower.HasValue || definition.BlockPower.Value <= 0f)) { throw new CustomItemRegistrationException(definition, text + " requires block power"); } if (definition.TemplateRequiresArmor && (!definition.Armor.HasValue || definition.Armor.Value <= 0f)) { throw new CustomItemRegistrationException(definition, text + " requires armor value"); } if (definition.TemplateRequiresFoodStats && !definition.TemplateHasFoodStats) { throw new CustomItemRegistrationException(definition, text + " requires health, stamina, or eitr food stats"); } } } private static bool HasAnyDamage(DamageTypes damages) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (!(damages.m_damage > 0f) && !(damages.m_blunt > 0f) && !(damages.m_slash > 0f) && !(damages.m_pierce > 0f) && !(damages.m_chop > 0f) && !(damages.m_pickaxe > 0f) && !(damages.m_fire > 0f) && !(damages.m_frost > 0f) && !(damages.m_lightning > 0f) && !(damages.m_poison > 0f)) { return damages.m_spirit > 0f; } return true; } private static AssetBundle LoadAssetBundle(string assetBundlePath) { string text = ResolveAssetBundlePath(assetBundlePath); if (!File.Exists(text)) { throw new FileNotFoundException("AssetBundle not found at '" + text + "'", text); } if (LoadedAssetBundles.TryGetValue(text, out var value) && Object.op_Implicit((Object)(object)value)) { return value; } AssetBundle val = AssetBundle.LoadFromFile(text); if (!Object.op_Implicit((Object)(object)val)) { throw new InvalidOperationException("Failed to load AssetBundle '" + text + "'"); } LoadedAssetBundles[text] = val; LogInfo("Loaded AssetBundle '" + text + "'"); return val; } private static AssetBundle GetAssetBundle(CustomItemDefinition definition) { if (Object.op_Implicit((Object)(object)definition.AssetBundle)) { return definition.AssetBundle; } return LoadAssetBundle(definition.AssetBundlePath); } private static string ResolveAssetBundlePath(string assetBundlePath) { if (Path.IsPathRooted(assetBundlePath)) { return Path.GetFullPath(assetBundlePath); } string path = Path.Combine(Paths.PluginPath, assetBundlePath); if (File.Exists(path)) { return Path.GetFullPath(path); } return Path.GetFullPath(assetBundlePath); } private static PrefabPreparationReport PrepareItemPrefab(CustomItemDefinition definition, GameObject itemPrefab) { //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Expected O, but got Unknown //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Expected O, but got Unknown //IL_0218: Unknown result type (might be due to invalid IL or missing references) PrefabPreparationOptions prefabPreparationOptions = definition.PrefabPreparation ?? new PrefabPreparationOptions(); PrefabPreparationReport prefabPreparationReport = new PrefabPreparationReport(); List<string> list = new List<string>(); ItemDrop val = itemPrefab.GetComponent<ItemDrop>(); if (!Object.op_Implicit((Object)(object)val)) { if (prefabPreparationOptions.RequireExistingItemDrop || !prefabPreparationOptions.AutoAddItemDrop) { list.Add("ItemDrop"); } else { val = itemPrefab.AddComponent<ItemDrop>(); prefabPreparationReport.AddedComponents.Add("ItemDrop"); prefabPreparationReport.CreatedItemDrop = true; } } Rigidbody component = itemPrefab.GetComponent<Rigidbody>(); ZNetView val2 = itemPrefab.GetComponent<ZNetView>(); ZSyncTransform component2 = itemPrefab.GetComponent<ZSyncTransform>(); bool flag = itemPrefab.GetComponentsInChildren<Collider>(true).Length != 0; if (!prefabPreparationOptions.AutoAddPhysics) { if (!Object.op_Implicit((Object)(object)component)) { list.Add("Rigidbody"); } if (!Object.op_Implicit((Object)(object)val2)) { list.Add("ZNetView"); } if (!Object.op_Implicit((Object)(object)component2)) { list.Add("ZSyncTransform"); } } if (!flag && !prefabPreparationOptions.WarnOnMissingCollider) { list.Add("Collider"); } if (list.Count > 0) { throw new CustomItemRegistrationException(definition, "Prefab '" + definition.PrefabName + "' is missing required components: " + string.Join(", ", list.ToArray()) + ". Add them in Unity, or opt into CIR auto-preparation with .PrefabPreparation(prep => prep.AutoAddItemDrop().AutoAddPhysics().WarnOnMissingCollider().AllowTextureIconFallback())."); } if (val.m_itemData == null) { val.m_itemData = new ItemData(); } if (val.m_itemData.m_shared == null) { val.m_itemData.m_shared = new SharedData(); } if (string.IsNullOrEmpty(val.m_itemData.m_shared.m_name)) { val.m_itemData.m_shared.m_name = ((!string.IsNullOrWhiteSpace(definition.DisplayName)) ? definition.DisplayName : ("$" + definition.ItemName.ToLowerInvariant())); } if (string.IsNullOrEmpty(val.m_itemData.m_shared.m_description) && !string.IsNullOrWhiteSpace(definition.Description)) { val.m_itemData.m_shared.m_description = definition.Description; } if (prefabPreparationReport.CreatedItemDrop) { if (!definition.ItemType.HasValue) { val.m_itemData.m_shared.m_itemType = (ItemType)1; } if (val.m_itemData.m_shared.m_maxStackSize < 1) { val.m_itemData.m_shared.m_maxStackSize = 1; } if (val.m_itemData.m_shared.m_weight <= 0f) { val.m_itemData.m_shared.m_weight = 1f; } val.m_itemData.m_shared.m_teleportable = true; } val.m_itemData.m_dropPrefab = itemPrefab; if (prefabPreparationOptions.AutoAddPhysics) { if (!Object.op_Implicit((Object)(object)component)) { component = itemPrefab.AddComponent<Rigidbody>(); prefabPreparationReport.AddedComponents.Add("Rigidbody"); } if (!Object.op_Implicit((Object)(object)val2)) { val2 = itemPrefab.AddComponent<ZNetView>(); prefabPreparationReport.AddedComponents.Add("ZNetView"); } val2.m_persistent = true; if (!Object.op_Implicit((Object)(object)component2)) { component2 = itemPrefab.AddComponent<ZSyncTransform>(); prefabPreparationReport.AddedComponents.Add("ZSyncTransform"); } } else if (Object.op_Implicit((Object)(object)val2)) { val2.m_persistent = true; } if (prefabPreparationOptions.WarnOnMissingCollider && !flag) { string text = "Item '" + definition.ItemName + "' prefab '" + definition.PrefabName + "' has no Collider. CIR will not add a guessed collider; add one in Unity for reliable pickup/drop physics."; prefabPreparationReport.Warnings.Add(text); LogWarning(text); } return prefabPreparationReport; } private static string LoadIcon(CustomItemDefinition definition, AssetBundle assetBundle) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)definition.Icon)) { return "direct Sprite"; } if (string.IsNullOrWhiteSpace(definition.IconAssetName)) { return "prefab/shared data"; } Sprite val = assetBundle.LoadAsset<Sprite>(definition.IconAssetName); if (Object.op_Implicit((Object)(object)val)) { definition.Icon = val; return "Sprite '" + definition.IconAssetName + "'"; } PrefabPreparationOptions prefabPreparationOptions = definition.PrefabPreparation ?? new PrefabPreparationOptions(); if (prefabPreparationOptions.AllowTextureIconFallback) { Texture2D val2 = assetBundle.LoadAsset<Texture2D>(definition.IconAssetName); if (Object.op_Implicit((Object)(object)val2)) { definition.Icon = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f)); ((Object)definition.Icon).name = definition.IconAssetName; return "Texture2D '" + definition.IconAssetName + "' converted to Sprite"; } } throw new CustomItemRegistrationException(definition, CreateMissingAssetMessage(definition, assetBundle, definition.IconAssetName, prefabPreparationOptions.AllowTextureIconFallback ? "icon Sprite or Texture2D" : "icon Sprite")); } private static ItemConfig CreateItemConfig(CustomItemDefinition definition) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Expected O, but got Unknown CraftingRecipe recipe = definition.Recipe; List<Ingredient> source = ((definition.HasRecipe && recipe.ingredients != null) ? recipe.ingredients : new List<Ingredient>()); return new ItemConfig { Name = definition.DisplayName, Description = definition.Description, Icon = definition.Icon, Amount = ((!definition.HasRecipe) ? 1 : recipe.amount), Enabled = (!definition.HasRecipe || recipe.enabled.GetValueOrDefault(true)), CraftingStation = (definition.HasRecipe ? recipe.craftingStation : null), RepairStation = (definition.HasRecipe ? recipe.repairStation : null), MinStationLevel = ((!definition.HasRecipe || recipe.minStationLevel <= 0) ? 1 : recipe.minStationLevel), RequireOnlyOneIngredient = (definition.HasRecipe && recipe.requireOnlyOneIngredient), QualityResultAmountMultiplier = ((!definition.HasRecipe || recipe.qualityResultAmountMultiplier <= 0) ? 1 : recipe.qualityResultAmountMultiplier), Weight = definition.Weight.GetValueOrDefault(-1f), StackSize = definition.StackSize.GetValueOrDefault(-1), Requirements = ((IEnumerable<Ingredient>)source).Select((Func<Ingredient, RequirementConfig>)((Ingredient ingredient) => new RequirementConfig(ingredient.itemName, ingredient.amount, ingredient.amountPerLevel, ingredient.recover))).ToArray() }; } private static void ApplyGearMetadata(CustomItemDefinition definition, GameObject itemPrefab) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) SharedData shared = itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared; if (definition.ItemType.HasValue) { shared.m_itemType = definition.ItemType.Value; } if (definition.Weight.HasValue) { shared.m_weight = definition.Weight.Value; } if (definition.StackSize.HasValue) { shared.m_maxStackSize = definition.StackSize.Value; } if (definition.MaxDurability.HasValue) { shared.m_maxDurability = definition.MaxDurability.Value; shared.m_useDurability = definition.MaxDurability.Value > 0f; } if (definition.DurabilityPerLevel.HasValue) { shared.m_durabilityPerLevel = definition.DurabilityPerLevel.Value; } if (definition.MaxQuality.HasValue) { shared.m_maxQuality = definition.MaxQuality.Value; } if (definition.ToolTier.HasValue) { shared.m_toolTier = definition.ToolTier.Value; } if (definition.Armor.HasValue) { shared.m_armor = definition.Armor.Value; } if (definition.ArmorPerLevel.HasValue) { shared.m_armorPerLevel = definition.ArmorPerLevel.Value; } if (definition.BlockPower.HasValue) { shared.m_blockPower = definition.BlockPower.Value; } if (definition.BlockPowerPerLevel.HasValue) { shared.m_blockPowerPerLevel = definition.BlockPowerPerLevel.Value; } if (definition.DeflectionForce.HasValue) { shared.m_deflectionForce = definition.DeflectionForce.Value; } if (definition.DeflectionForcePerLevel.HasValue) { shared.m_deflectionForcePerLevel = definition.DeflectionForcePerLevel.Value; } if (definition.MovementModifier.HasValue) { shared.m_movementModifier = definition.MovementModifier.Value; } if (definition.Teleportable.HasValue) { shared.m_teleportable = definition.Teleportable.Value; } if (definition.CanBeRepaired.HasValue) { shared.m_canBeReparied = definition.CanBeRepaired.Value; } if (definition.HasDamages) { shared.m_damages = definition.Damages; } if (definition.HasDamagesPerLevel) { shared.m_damagesPerLevel = definition.DamagesPerLevel; } } private static void ApplySharedDataConfigurators(CustomItemDefinition definition, GameObject itemPrefab) { if (definition.SharedDataConfigurators.Count == 0) { return; } SharedData shared = itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared; foreach (Action<SharedData> sharedDataConfigurator in definition.SharedDataConfigurators) { sharedDataConfigurator(shared); } } private static void ValidatePreparedItem(CustomItemDefinition definition, GameObject itemPrefab, CustomItem customItem) { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if (!Object.op_Implicit((Object)(object)component)) { throw new CustomItemRegistrationException(definition, "Prepared prefab is missing ItemDrop"); } if (CreatesRecipe(definition)) { Sprite[] array = component.m_itemData?.m_shared?.m_icons; if (array == null || array.Length == 0 || !Object.op_Implicit((Object)(object)array[0])) { throw new CustomItemRegistrationException(definition, "Craftable items must have an icon; set it in the prefab or call .Icon(...)"); } } if (customItem == null || (Object)(object)customItem.ItemPrefab == (Object)null) { throw new CustomItemRegistrationException(definition, "Jotunn custom item wrapper was not created"); } if ((definition.PrefabPreparation ?? new PrefabPreparationOptions()).ValidateWearableVisuals) { ValidateWearableVisuals(definition, itemPrefab, component.m_itemData?.m_shared); } } private static void ValidateWearableVisuals(CustomItemDefinition definition, GameObject itemPrefab, SharedData shared) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) if (shared == null || !IsWearableArmor(shared.m_itemType)) { return; } SkinnedMeshRenderer[] componentsInChildren = itemPrefab.GetComponentsInChildren<SkinnedMeshRenderer>(true); if (componentsInChildren == null || componentsInChildren.Length == 0) { throw new CustomItemRegistrationException(definition, $"Wearable item prefab '{definition.PrefabName}' is type '{shared.m_itemType}' but has no SkinnedMeshRenderer. Valheim wearable armor needs a skinned visual setup; a normal static mesh can register as a material, but will fail or crash when equipped in SetupVisEquipment."); } SkinnedMeshRenderer[] array = componentsInChildren; foreach (SkinnedMeshRenderer val in array) { string transformPath = GetTransformPath(((Component)val).transform, itemPrefab.transform); if ((Object)(object)val.sharedMesh == (Object)null) { throw new CustomItemRegistrationException(definition, "Wearable item prefab '" + definition.PrefabName + "' has SkinnedMeshRenderer '" + transformPath + "' without a mesh."); } if ((Object)(object)val.rootBone == (Object)null) { throw new CustomItemRegistrationException(definition, "Wearable item prefab '" + definition.PrefabName + "' has SkinnedMeshRenderer '" + transformPath + "' without rootBone."); } if (val.bones == null || val.bones.Length == 0) { throw new CustomItemRegistrationException(definition, "Wearable item prefab '" + definition.PrefabName + "' has SkinnedMeshRenderer '" + transformPath + "' without bones."); } if (val.bones.Any((Transform bone) => (Object)(object)bone == (Object)null)) { throw new CustomItemRegistrationException(definition, "Wearable item prefab '" + definition.PrefabName + "' has SkinnedMeshRenderer '" + transformPath + "' with one or more missing bone references."); } Material[] sharedMaterials = ((Renderer)val).sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0 || sharedMaterials.Any((Material material) => (Object)(object)material == (Object)null)) { LogWarning("Wearable item '" + definition.ItemName + "' prefab '" + definition.PrefabName + "' renderer '" + transformPath + "' has missing material references. The item may equip but render incorrectly."); } } if (RequiresAttachSkinHint(shared.m_itemType) && !HasAttachSkinChild(itemPrefab)) { LogWarning("Wearable item '" + definition.ItemName + "' prefab '" + definition.PrefabName + "' has skinned renderers but no 'attach_skin...' child. If the item crashes or appears wrong when equipped, base the hierarchy on a vanilla armor prefab and consider Jotunn BoneReorder.ApplyOnEquipmentChanged() for imported armor meshes."); } } private static bool IsWearableArmor(ItemType itemType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 if ((int)itemType != 7 && (int)itemType != 11 && (int)itemType != 6) { return (int)itemType == 17; } return true; } private static bool RequiresAttachSkinHint(ItemType itemType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 if ((int)itemType != 7 && (int)itemType != 11) { return (int)itemType == 17; } return true; } private static bool HasAttachSkinChild(GameObject itemPrefab) { return itemPrefab.GetComponentsInChildren<Transform>(true).Any((Transform child) => ((Object)child).name.StartsWith("attach_skin", StringComparison.OrdinalIgnoreCase)); } private static string GetTransformPath(Transform transform, Transform root) { if ((Object)(object)transform == (Object)null) { return "<missing transform>"; } Stack<string> stack = new Stack<string>(); Transform val = transform; while ((Object)(object)val != (Object)null) { stack.Push(((Object)val).name); if ((Object)(object)val == (Object)(object)root) { break; } val = val.parent; } return string.Join("/", stack.ToArray()); } private static bool CreatesRecipe(CustomItemDefinition definition) { if (definition.HasRecipe && definition.Recipe.ingredients != null) { return definition.Recipe.ingredients.Count > 0; } return false; } private static void WarnForMissingIngredients(CustomItemDefinition definition) { if (!CreatesRecipe(definition) || (!Object.op_Implicit((Object)(object)ObjectDB.instance) && !Object.op_Implicit((Object)(object)ZNetScene.instance))) { return; } foreach (Ingredient ingredient in definition.Recipe.ingredients) { if (!Object.op_Implicit((Object)(object)PrefabManager.Instance.GetPrefab(ingredient.itemName))) { if (!string.IsNullOrWhiteSpace(ingredient.SourceModGuid)) { LogWarning($"Item '{definition.ItemName}' recipe ingredient '{ingredient.itemName}' from mod '{ingredient.SourceModGuid}' was not found in loaded prefab databases (amount {ingredient.amount}, amount per level {ingredient.amountPerLevel}). Jotunn may still resolve it later if that mod registers it."); } else { LogWarning("Item '" + definition.ItemName + "' recipe ingredient '" + ingredient.itemName + "' was not found in loaded prefab databases. Jotunn may still resolve it later if another mod registers it."); } } } } private static void RegisterPrefabInObjectDB(RegisteredItem item) { if (Object.op_Implicit((Object)(object)ObjectDB.instance) && Object.op_Implicit((Object)(object)item.Prefab)) { ItemManager.Instance.RegisterItemInObjectDB(item.Prefab); } } private static void RegisterRecipeInObjectDB(ObjectDB objectDB, RegisteredItem item) { CustomRecipe recipe2 = item.CustomItem.Recipe; Recipe recipe = ((recipe2 != null) ? recipe2.Recipe : null); if (Object.op_Implicit((Object)(object)objectDB) && Object.op_Implicit((Object)(object)recipe) && !objectDB.m_recipes.Any((Recipe existing) => Object.op_Implicit((Object)(object)existing) && ((Object)existing).name == ((Object)recipe).name)) { if (recipe2.FixReference || recipe2.FixRequirementReferences) { PrefabExtension.FixReferences((object)recipe); recipe2.FixReference = false; recipe2.FixRequirementReferences = false; } objectDB.m_recipes.Add(recipe); LogInfo("Added recipe '" + ((Object)recipe).name + "' to ObjectDB"); } } private static string CreateMissingAssetMessage(CustomItemDefinition definition, AssetBundle assetBundle, string assetName, string assetKind) { string text = CreateAssetCandidateMessage(assetBundle, assetName); return "AssetBundle '" + GetBundleLabel(definition) + "' does not contain " + assetKind + " '" + assetName + "' for item '" + definition.ItemName + "' prefab '" + definition.PrefabName + "'. Asset names are case-sensitive." + text; } private static string CreateAssetCandidateMessage(AssetBundle assetBundle, string assetName) { string[] assetNames = GetAssetNames(assetBundle); if (assetNames.Length == 0) { return string.Empty; } string normalizedNeedle = assetName ?? string.Empty; List<string> list = (from name in (from name in assetNames.Select(PrettyAssetName) where !string.IsNullOrWhiteSpace(name) select name).Distinct<string>(StringComparer.OrdinalIgnoreCase) where name.IndexOf(normalizedNeedle, StringComparison.OrdinalIgnoreCase) >= 0 || normalizedNeedle.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0 || string.Equals(name, normalizedNeedle, StringComparison.OrdinalIgnoreCase) select name).Take(12).ToList(); if (list.Count == 0) { list = (from name in assetNames.Select(PrettyAssetName) where !string.IsNullOrWhiteSpace(name) select name).Distinct<string>(StringComparer.OrdinalIgnoreCase).Take(12).ToList(); } if (list.Count != 0) { return " Candidate assets include: " + string.Join(", ", list.ToArray()) + "."; } return string.Empty; } private static string[] GetAssetNames(AssetBundle assetBundle) { if (!Object.op_Implicit((Object)(object)assetBundle)) { return Array.Empty<string>(); } try { return assetBundle.GetAllAssetNames() ?? Array.Empty<string>(); } catch (Exception ex) { LogWarning("Could not inspect AssetBundle asset names: " + ex.Message); return Array.Empty<string>(); } } private static string PrettyAssetName(string assetPath) { if (string.IsNullOrWhiteSpace(assetPath)) { return string.Empty; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(assetPath); if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension)) { return fileNameWithoutExtension; } return assetPath; } private static string GetBundleLabel(CustomItemDefinition definition) { if (!string.IsNullOrWhiteSpace(definition.AssetBundlePath)) { return definition.AssetBundlePath; } if (!Object.op_Implicit((Object)(object)definition.AssetBundle)) { return "<unknown>"; } return ((Object)definition.AssetBundle).name; } private static void LogRegistrationSuccess(CustomItemDefinition definition, GameObject itemPrefab, string iconSource, PrefabPreparationReport preparationReport) { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); string text = ((Object.op_Implicit((Object)(object)component) && component.m_itemData != null && component.m_itemData.m_shared != null) ? ((object)(ItemType)(ref component.m_itemData.m_shared.m_itemType)).ToString() : "unknown"); string text2 = ((preparationReport.AddedComponents.Count == 0) ? "none" : string.Join(", ", preparationReport.AddedComponents.ToArray())); string text3 = ((!definition.HasRecipe) ? "none" : (string.IsNullOrWhiteSpace(definition.Recipe.craftingStation) ? "none" : definition.Recipe.craftingStation)); LogInfo($"Registered custom item '{definition.ItemName}' from bundle '{GetBundleLabel(definition)}' prefab '{definition.PrefabName}'. Type={text}; icon={iconSource}; recipeStation={text3}; addedComponents={text2}; warnings={preparationReport.Warnings.Count}"); } internal static void LogInfo(string message) { ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)message); } } internal static void LogWarning(string message) { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)message); } } } public sealed class ItemRegistrationResult { public bool Success { get; private set; } public string ItemName { get; private set; } public string AssetBundlePath { get; private set; } public string PrefabName { get; private set; } public GameObject Prefab { get; private set; } public CustomItem CustomItem { get; private set; } public Exception Exception { get; private set; } public string ErrorMessage => Exception?.Message; internal static ItemRegistrationResult Registered(CustomItemDefinition definition, GameObject prefab, CustomItem customItem) { return new ItemRegistrationResult { Success = true, ItemName = definition.ItemName, AssetBundlePath = definition.AssetBundlePath, PrefabName = definition.PrefabName, Prefab = prefab, CustomItem = customItem }; } internal static ItemRegistrationResult Failed(CustomItemDefinition definition, Exception exception) { return new ItemRegistrationResult { Success = false, ItemName = definition?.ItemName, AssetBundlePath = definition?.AssetBundlePath, PrefabName = definition?.PrefabName, Exception = exception }; } } public sealed class PrefabPreparationOptions { public bool RequireExistingItemDrop { get; set; } public bool AutoAddItemDrop { get; set; } public bool AutoAddPhysics { get; set; } public bool WarnOnMissingCollider { get; set; } public bool AllowTextureIconFallback { get; set; } public bool ValidateWearableVisuals { get; set; } public PrefabPreparationOptions() { RequireExistingItemDrop = true; AutoAddItemDrop = false; AutoAddPhysics = false; WarnOnMissingCollider = false; AllowTextureIconFallback = false; ValidateWearableVisuals = true; } } public sealed class CustomItemBuilder { private readonly CustomItemDefinition definition; internal CustomItemBuilder(string itemName) { definition = new CustomItemDefinition(itemName); } public CustomItemBuilder FromBundle(string assetBundlePath, string prefabName) { definition.AssetBundlePath = assetBundlePath; definition.AssetBundle = null; definition.PrefabName = prefabName; return this; } public CustomItemBuilder FromAssetBundle(AssetBundle assetBundle, string prefabName) { definition.AssetBundle = assetBundle; definition.AssetBundlePath = null; definition.PrefabName = prefabName; return this; } public CustomItemBuilder FromEmbeddedResource(string resourceName, Assembly assembly, string prefabName) { definition.AssetBundle = AssetUtils.LoadAssetBundleFromResources(resourceName, assembly); definition.AssetBundlePath = resourceName; definition.PrefabName = prefabName; return this; } public CustomItemBuilder DisplayName(string displayName) { definition.DisplayName = displayName; return this; } public CustomItemBuilder Description(string description) { definition.Description = description; return this; } public CustomItemBuilder Icon(string assetName) { definition.IconAssetName = assetName; return this; } public CustomItemBuilder Icon(Sprite sprite) { definition.Icon = sprite; return this; } public CustomItemBuilder PrefabPreparation(Action<PrefabPreparationBuilder> configure) { PrefabPreparationBuilder obj = new PrefabPreparationBuilder(definition.PrefabPreparation); configure?.Invoke(obj); return this; } public CustomItemBuilder Recipe(Action<RecipeBuilder> configure) { RecipeBuilder recipeBuilder = new RecipeBuilder(); configure?.Invoke(recipeBuilder); definition.Recipe = recipeBuilder.Build(); definition.HasRecipe = true; return this; } public CustomItemBuilder Gear(Action<GearBuilder> configure) { GearBuilder obj = new GearBuilder(definition); configure?.Invoke(obj); return this; } public CustomItemBuilder AsSword(Action<WeaponTemplateBuilder> configure) { WeaponTemplateBuilder obj = new WeaponTemplateBuilder(definition, "Sword", (ItemType)3); configure?.Invoke(obj); return this; } public CustomItemBuilder AsAxe(Action<WeaponTemplateBuilder> configure) { WeaponTemplateBuilder obj = new WeaponTemplateBuilder(definition, "Axe", (ItemType)3); configure?.Invoke(obj); return this; } public CustomItemBuilder AsMace(Action<WeaponTemplateBuilder> configure) { WeaponTemplateBuilder obj = new WeaponTemplateBuilder(definition, "Mace", (ItemType)3); configure?.Invoke(obj); return this; } public CustomItemBuilder AsSpear(Action<WeaponTemplateBuilder> configure) { WeaponTemplateBuilder obj = new WeaponTemplateBuilder(definition, "Spear", (ItemType)3); configure?.Invoke(obj); return this; } public CustomItemBuilder AsKnife(Action<WeaponTemplateBuilder> configure) { WeaponTemplateBuilder obj = new WeaponTemplateBuilder(definition, "Knife", (ItemType)3); configure?.Invoke(obj); return this; } public CustomItemBuilder AsAtgeir(Action<WeaponTemplateBuilder> configure) { WeaponTemplateBuilder obj = new WeaponTemplateBuilder(definition, "Atgeir", (ItemType)14); configure?.Invoke(obj); return this; } public CustomItemBuilder AsBow(Action<BowTemplateBuilder> configure) { BowTemplateBuilder obj = new BowTemplateBuilder(definition); configure?.Invoke(obj); return this; } public CustomItemBuilder AsArrow(Action<AmmoTemplateBuilder> configure) { AmmoTemplateBuilder obj = new AmmoTemplateBuilder(definition); configure?.Invoke(obj); return this; } public CustomItemBuilder AsShield(Action<ShieldTemplateBuilder> configure) { ShieldTemplateBuilder obj = new ShieldTemplateBuilder(definition); configure?.Invoke(obj); return this; } public CustomItemBuilder AsArmorChest(Action<ArmorTemplateBuilder> configure) { ArmorTemplateBuilder obj = new ArmorTemplateBuilder(definition, "ArmorChest", (ItemType)7); configure?.Invoke(obj); return this; } public CustomItemBuilder AsArmorLegs(Action<ArmorTemplateBuilder> configure) { ArmorTemplateBuilder obj = new ArmorTemplateBuilder(definition, "ArmorLegs", (ItemType)11); configure?.Invoke(obj); return this; } public CustomItemBuilder AsHelmet(Action<ArmorTemplateBuilder> configure) { ArmorTemplateBuilder obj = new ArmorTemplateBuilder(definition, "Helmet", (ItemType)6); configure?.Invoke(obj); return this; } public CustomItemBuilder AsCape(Action<ArmorTemplateBuilder> configure) { ArmorTemplateBuilder obj = new ArmorTemplateBuilder(definition, "Cape", (ItemType)17); configure?.Invoke(obj); return this; } public CustomItemBuilder AsTool(Action<ToolTemplateBuilder> configure) { ToolTemplateBuilder obj = new ToolTemplateBuilder(definition); configure?.Invoke(obj); return this; } public CustomItemBuilder AsFood(Action<FoodTemplateBuilder> configure) { FoodTemplateBuilder obj = new FoodTemplateBuilder(definition); configure?.Invoke(obj); return this; } public CustomItemBuilder AsMaterial(Action<MaterialTemplateBuilder> configure = null) { MaterialTemplateBuilder obj = new MaterialTemplateBuilder(definition); configure?.Invoke(obj); return this; } public CustomItemBuilder ConfigureSharedData(Action<SharedData> configure) { if (configure != null) { definition.SharedDataConfigurators.Add(configure); } return this; } public CustomItemDefinition Build() { return definition; } public ItemRegistrationResult Register() { return CustomItemRegistry.RegisterItem(definition); } } public sealed class GearBuilder { private readonly CustomItemDefinition definition; internal GearBuilder(CustomItemDefinition definition) { this.definition = definition; } public GearBuilder OneHandedWeapon() { return Type((ItemType)3); } public GearBuilder TwoHandedWeapon() { return Type((ItemType)14); } public GearBuilder TwoHandedWeaponLeft() { return Type((ItemType)22); } public GearBuilder Shield() { return Type((ItemType)5); } public GearBuilder Bow() { return Type((ItemType)4); } public GearBuilder Ammo() { return Type((ItemType)9); } public GearBuilder AmmoNonEquipable() { return Type((ItemType)23); } public GearBuilder Material() { return Type((ItemType)1); } public GearBuilder Consumable() { return Type((ItemType)2); } public GearBuilder Torch() { return Type((ItemType)15); } public GearBuilder Tool() { return Type((ItemType)19); } public GearBuilder Armor() { return Type((ItemType)7); } public GearBuilder Helmet() { return Type((ItemType)6); } public GearBuilder Chest() { return Type((ItemType)7); } public GearBuilder Legs() { return Type((ItemType)11); } public GearBuilder Shoulder() { return Type((ItemType)17); } public GearBuilder Utility() { return Type((ItemType)18); } public GearBuilder Trinket() { return Type((ItemType)24); } public GearBuilder Type(ItemType itemType) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) definition.ItemType = itemType; return this; } public GearBuilder Weight(float value) { definition.Weight = value; return this; } public GearBuilder StackSize(int value) { definition.StackSize = value; return this; } public GearBuilder Durability(float value) { definition.MaxDurability = value; return this; } public GearBuilder DurabilityPerLevel(float value) { definition.DurabilityPerLevel = value; return this; } public GearBuilder MaxQuality(int value) { definition.MaxQuality = value; return this; } public GearBuilder ToolTier(int value) { definition.ToolTier = value; return this; } public GearBuilder ArmorValue(float value) { definition.Armor = value; return this; } public GearBuilder ArmorPerLevel(float value) { definition.ArmorPerLevel = value; return this; } public GearBuilder BlockPower(float value) { definition.BlockPower = value; return this; } public GearBuilder BlockPowerPerLevel(float value) { definition.BlockPowerPerLevel = value; return this; } public GearBuilder BlockForce(float value) { definition.DeflectionForce = value; return this; } public GearBuilder BlockForcePerLevel(float value) { definition.DeflectionForcePerLevel = value; return this; } public GearBuilder Parry(float value) { definition.SharedDataConfigurators.Add(delegate(SharedData shared) { shared.m_timedBlockBonus = value; }); return this; } public GearBuilder AttackForce(float value) { definition.SharedDataConfigurators.Add(delegate(SharedData shared) { shared.m_attackForce = value; }); return this; } public GearBuilder MovementModifier(float value) { definition.MovementModifier = value; return this; } public GearBuilder Teleportable(bool value = true) { definition.Teleportable = value; return this; } public GearBuilder Repairable(bool value = true) { definition.CanBeRepaired = value; return this; } public GearBuilder BluntDamage(float value) { return Damage(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_blunt = value; return d; }); } public GearBuilder SlashDamage(float value) { return Damage(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_slash = value; return d; }); } public GearBuilder PierceDamage(float value) { return Damage(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_pierce = value; return d; }); } public GearBuilder FireDamage(float value) { return Damage(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_fire = value; return d; }); } public GearBuilder FrostDamage(float value) { return Damage(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_frost = value; return d; }); } public GearBuilder LightningDamage(float value) { return Damage(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_lightning = value; return d; }); } public GearBuilder PoisonDamage(float value) { return Damage(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_poison = value; return d; }); } public GearBuilder SpiritDamage(float value) { return Damage(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_spirit = value; return d; }); } public GearBuilder ChopDamage(float value) { return Damage(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_chop = value; return d; }); } public GearBuilder PickaxeDamage(float value) { return Damage(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_pickaxe = value; return d; }); } public GearBuilder BluntDamagePerLevel(float value) { return DamagePerLevel(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_blunt = value; return d; }); } public GearBuilder SlashDamagePerLevel(float value) { return DamagePerLevel(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_slash = value; return d; }); } public GearBuilder PierceDamagePerLevel(float value) { return DamagePerLevel(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_pierce = value; return d; }); } public GearBuilder FireDamagePerLevel(float value) { return DamagePerLevel(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_fire = value; return d; }); } public GearBuilder FrostDamagePerLevel(float value) { return DamagePerLevel(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_frost = value; return d; }); } public GearBuilder LightningDamagePerLevel(float value) { return DamagePerLevel(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_lightning = value; return d; }); } public GearBuilder PoisonDamagePerLevel(float value) { return DamagePerLevel(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_poison = value; return d; }); } public GearBuilder SpiritDamagePerLevel(float value) { return DamagePerLevel(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_spirit = value; return d; }); } public GearBuilder ChopDamagePerLevel(float value) { return DamagePerLevel(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_chop = value; return d; }); } public GearBuilder PickaxeDamagePerLevel(float value) { return DamagePerLevel(delegate(DamageTypes d) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) d.m_pickaxe = value; return d; }); } public GearBuilder DamageModifier(DamageType damageType, DamageModifier modifier) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) definition.SharedDataConfigurators.Add(delegate(SharedData shared) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if (shared.m_damageModifiers == null) { shared.m_damageModifiers = new List<DamageModPair>(); } shared.m_damageModifiers.RemoveAll((DamageModPair entry) => entry.m_type == damageType); shared.m_damageModifiers.Add(new DamageModPair { m_type = damageType, m_modifier = modifier }); }); return this; } public GearBuilder PrimaryAttackStamina(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_attackStamina = value; }); } public GearBuilder PrimaryAttackEitr(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_attackEitr = value; }); } public GearBuilder PrimaryAttackHealth(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_attackHealth = value; }); } public GearBuilder PrimaryAttackHealthPercentage(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_attackHealthPercentage = value; }); } public GearBuilder PrimaryAttackHealthReturnHit(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_attackHealthReturnHit = value; }); } public GearBuilder PrimaryAttackDamageMultiplierPerMissingHp(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_damageMultiplierPerMissingHP = value; }); } public GearBuilder PrimaryAttackForceMultiplier(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_forceMultiplier = value; }); } public GearBuilder PrimaryAttackProjectileCount(int value) { return PrimaryAttack(delegate(Attack attack) { attack.m_projectiles = value; }); } public GearBuilder ProjectileVelocity(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_projectileVel = value; }); } public GearBuilder ProjectileAccuracy(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_projectileAccuracy = value; }); } public GearBuilder DrawDuration(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_drawDurationMin = value; }); } public GearBuilder DrawStaminaDrain(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_drawStaminaDrain = value; }); } public GearBuilder ReloadTime(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_reloadTime = value; }); } public GearBuilder ReloadStaminaDrain(float value) { return PrimaryAttack(delegate(Attack attack) { attack.m_reloadStaminaDrain = value; }); } public GearBuilder SecondaryAttackStamina(float value) { return SecondaryAttack(delegate(Attack attack) { attack.m_attackStamina = value; }); } public GearBuilder SecondaryAttackEitr(float value) { return SecondaryAttack(delegate(Attack attack) { attack.m_attackEitr = value; }); } public GearBuilder SecondaryAttackHealth(float value) { return SecondaryAttack(delegate(Attack attack) { attack.m_attackHealth = value; }); } public GearBuilder SecondaryAttackHealthPercentage(float value) { return SecondaryAttack(delegate(Attack attack) { attack.m_attackHealthPercentage = value; }); } public GearBuilder SecondaryAttackForceMultiplier(float value) { return SecondaryAttack(delegate(Attack attack) { attack.m_forceMultiplier = value; }); } private GearBuilder Damage(Func<DamageTypes, DamageTypes> configure) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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) definition.Damages = configure(definition.Damages); definition.HasDamages = true; return this; } private GearBuilder DamagePerLevel(Func<DamageTypes, DamageTypes> configure) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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) definition.DamagesPerLevel = configure(definition.DamagesPerLevel); definition.HasDamagesPerLevel = true; return this; } private GearBuilder PrimaryAttack(Action<Attack> configure) { definition.SharedDataConfigurators.Add(delegate(SharedData shared) { ConfigureAttack(shared.m_attack, "primary", shared.m_name, configure); }); return this; } private GearBuilder SecondaryAttack(Action<Attack> configure) { definition.SharedDataConfigurators.Add(delegate(SharedData shared) { ConfigureAttack(shared.m_secondaryAttack, "secondary", shared.m_name, configure); }); return this; } private static void ConfigureAttack(Attack attack, string label, string itemName, Action<Attack> configure) { if (attack == null) { throw new CustomItemRegistrationException("Item '" + itemName + "' has no " + label + " attack to configure"); } configure(attack); } } public sealed class PrefabPreparationBuilder { private readonly PrefabPreparationOptions options; internal PrefabPreparationBuilder(PrefabPreparationOptions options) { this.options = options; } public PrefabPreparationBuilder RequireItemDrop() { options.RequireExistingItemDrop = true; options.AutoAddItemDrop = false; return this; } public PrefabPreparationBuilder AutoAddItemDrop(bool value = true) { options.AutoAddItemDrop = value; if (value) { options.RequireExistingItemDrop = false; } return this; } public PrefabPreparationBuilder AutoAddPhysics(bool value = true) { options.AutoAddPhysics = value; return this; } public PrefabPreparationBuilder WarnOnMissingCollider(bool value = true) { options.WarnOnMissingCollider = value; return this; } public PrefabPreparationBuilder AllowTextureIconFallback(bool value = true) { options.AllowTextureIconFallback = value; return this; } public PrefabPreparationBuilder ValidateWearableVisuals(bool value = true) { options.ValidateWearableVisuals = value; return this; } } public sealed class RecipeBuilder { private readonly List<Ingredient> ingredients = new List<Ingredient>(); private string craftingStation; private string repairStation; private int amount = 1; private int minStationLevel = 1; private bool enabled = true; private bool requireOnlyOneIngredient; private int qualityResultAmountMultiplier = 1; private string craftingStationValidationError; private string repairStationValidationError; public RecipeBuilder At(string station) { craftingStation = station; craftingStationValidationError = null; return this; } public RecipeBuilder At(CraftingStation station) { if (CraftingStationExtensions.TryToPrefabName(station, out var prefabName)) { craftingStation = prefabName; craftingStationValidationError = null; } else { craftingStation = null; craftingStationValidationError = $"Invalid CraftingStation value '{(int)station}' for crafting station"; } return this; } public RecipeBuilder RepairAt(string station) { repairStation = station; repairStationValidationError = null; return this; } public RecipeBuilder RepairAt(CraftingStation station) { if (CraftingStationExtensions.TryToPrefabName(station, out var prefabName)) { repairStation = prefabName; repairStationValidationError = null; } else { repairStation = null; repairStationValidationError = $"Invalid CraftingStation value '{(int)station}' for repair station"; } return this; } public RecipeBuilder StationLevel(int level) { minStationLevel = level; return this; } public RecipeBuilder Amount(int craftedAmount) { amount = craftedAmount; return this; } public RecipeBuilder Enabled(bool isEnabled = true) { enabled = isEnabled; return this; } public RecipeBuilder RequireOnlyOneIngredient(bool value = true) { requireOnlyOneIngredient = value; return this; } public RecipeBuilder QualityResultAmountMultiplier(int multiplier) { qualityResultAmountMultiplier = multiplier; return this; } public RecipeBuilder Requires(string itemName, int amount, int amountPerLevel = 0, bool recover = true) { ingredients.Add(new Ingredient(itemName, amount, amountPerLevel, recover)); return this; } public RecipeBuilder Requires(VanillaItem item, int amount, int amountPerLevel = 0, bool recover = true) { ingredients.Add(Ingredient.From(item, amount, amountPerLevel, recover)); return this; } public RecipeBuilder Requires(ItemRef item, int amount, int amountPerLevel = 0, bool recover = true) { ingredients.Add(Ingredient.From(item, amount, amountPerLevel, recover)); return this; } internal CraftingRecipe Build() { CraftingRecipe result = new CraftingRecipe(ingredients, craftingStation, amount, repairStation, minStationLevel, enabled, requireOnlyOneIngredient, qualityResultAmountMultiplier); result.CraftingStationValidationError = craftingStationValidationError; result.RepairStationValidationError = repairStationValidationError; return result; } } public enum CraftingStation { None, Workbench, Forge, Stonecutter, Cauldron, ArtisanTable, BlackForge, GaldrTable, EitrRefinery } public static class CraftingStationExtensions { public static string ToPrefabName(this CraftingStation station) { if (TryToPrefabName(station, out var prefabName)) { return prefabName; } throw new ArgumentOutOfRangeException("station", station, "Unknown CraftingStation value"); } internal static bool TryToPrefabName(CraftingStation station, out string prefabName) { switch (station) { case CraftingStation.None: prefabName = null; return true; case CraftingStation.Workbench: prefabName = "piece_workbench"; return true; case CraftingStation.Forge: prefabName = "forge"; return true; case CraftingStation.Stonecutter: prefabName = "piece_stonecutter"; return true; case CraftingStation.Cauldron: prefabName = "piece_cauldron"; return true; case CraftingStation.ArtisanTable: prefabName = "piece_artisanstation"; return true; case CraftingStation.BlackForge: prefabName = "blackforge"; return true; case CraftingStation.GaldrTable: prefabName = "piece_magetable"; return true; case CraftingStation.EitrRefinery: prefabName = "piece_eitrrefinery"; return true; default: prefabName = null; return false; } } } public readonly struct ItemRef { public string PrefabName { get; } public string SourceModGuid { get; } public bool IsVanilla { get; } internal string ValidationError { get; } private ItemRef(string prefabName, string sourceModGuid, bool isVanilla, string validationError) { PrefabName = prefabName; SourceModGuid = sourceModGuid; IsVanilla = isVanilla; ValidationError = validationError; } public static ItemRef Vanilla(VanillaItem item) { if (!VanillaItemExtensions.TryToPrefabName(item, out var prefabName)) { return new ItemRef(null, null, isVanilla: true, $"Invalid VanillaItem value '{(int)item}'"); } return new ItemRef(prefabName, null, isVanilla: true, null); } public static ItemRef Prefab(string prefabName) { if (!string.IsNullOrWhiteSpace(prefabName)) { return new ItemRef(prefabName, null, isVanilla: false, null); } return new ItemRef(prefabName, null, isVanilla: false, "ItemRef prefab name is required"); } public static ItemRef Modded(string sourceModGuid, string prefabName) { if (string.IsNullOrWhiteSpace(prefabName)) { return new ItemRef(prefabName, sourceModGuid, isVanilla: false, "Modded ItemRef prefab name is required"); } return new ItemRef(prefabName, sourceModGuid, isVanilla: false, null); } public static ItemRef FromRegisteredCIRItem(string itemName) { if (!string.IsNullOrWhiteSpace(itemName)) { return new ItemRef(itemName, "com.valheimcustomitemregistry.api", isVanilla: false, null); } return new ItemRef(itemName, "com.valheimcustomitemregistry.api", isVanilla: false, "Registered CIR item name is required"); } } public static class ItemRefs { public static ItemRef Vanilla(VanillaItem item) { return ItemRef.Vanilla(item); } public static ItemRef Prefab(string prefabName) { return ItemRef.Prefab(prefabName); } public static ItemRef Modded(string sourceModGuid, string prefabName) { return ItemRef.Modded(sourceModGuid, prefabName); } public static ItemRef RegisteredCIRItem(string itemName) { return ItemRef.FromRegisteredCIRItem(itemName); } } public enum VanillaItem { Wood, FineWood, RoundLog, ElderBark, YggdrasilWood, Stone, Flint, Coal, Resin, Amber, AmberPearl, Ruby, Crystal, Coins, SurtlingCore, BlackCore, CopperOre, Copper, TinOre, Tin, Bronze, IronScrap, Iron, SilverOre, Silver, BlackMetalScrap, BlackMetal, FlametalOre, Flametal, LeatherScraps, DeerHide, TrollHide, WolfPelt, LoxPelt, ScaleHide, Carapace, Feathers, BoneFragments, WitheredBone, HardAntler, DragonEgg, Wishbone, YmirRemains, QueenDrop, Mushroom, MushroomYellow, MushroomBlue, MushroomMagecap, MushroomJotunPuffs, Raspberry, Blueberries, Cloudberry, Honey, Carrot, Turnip, Onion, Barley, BarleyFlour, Flax, Sap, RoyalJelly, Dandelion, Thistle, Entrails, Bloodbag, Ooze, Guck, Tar, WolfFang, WolfClaw, Needle, Obsidian, Chitin, SerpentScale, BoarMeat, DeerMeat, WolfMeat, LoxMeat, SerpentMeat, HareMeat, ChickenMeat, BugMeat, FishRaw, ArrowWood, ArrowFire, ArrowFlint, ArrowIron, ArrowObsidian, ArrowPoison, ArrowSilver, ArrowNeedle, ArrowCarapace, BoltBone, BoltBlackmetal, BoltCarapace, TrophyBoar, TrophyDeer, TrophyEikthyr, TrophyTheElder, TrophyBonemass, TrophyDragonQueen, TrophyGoblinKing, TrophySeekerQueen } public static class VanillaItemExtensions { public static string ToPrefabName(this VanillaItem item) { if (TryToPrefabName(item, out var prefabName)) { return prefabName; } throw new ArgumentOutOfRangeException("item", item, "Unknown VanillaItem value"); } internal static bool TryToPrefabName(VanillaItem item, out string prefabName) { switch (item) { case VanillaItem.Wood: prefabName = "Wood"; return true; case VanillaItem.FineWood: prefabName = "FineWood"; return true; case VanillaItem.RoundLog: prefabName = "RoundLog"; return true; case VanillaItem.ElderBark: prefabName = "ElderBark"; return true; case VanillaItem.YggdrasilWood: prefabName = "YggdrasilWood"; return true; case VanillaItem.Stone: prefabName = "Stone"; return true; case VanillaItem.Flint: prefabName = "Flint"; return true; case VanillaItem.Coal: prefabName = "Coal"; return true; case VanillaItem.Resin: prefabName = "Resin"; return true; case VanillaItem.Amber: prefabName = "Amber"; return true; case VanillaItem.AmberPearl: prefabName = "AmberPearl"; return true; case VanillaItem.Ruby: prefabName = "Ruby"; return true; case VanillaItem.Crystal: prefabName = "Crystal"; return true; case VanillaItem.Coins: prefabName = "Coins"; return true; case VanillaItem.SurtlingCore: prefabName = "SurtlingCore"; return true; case VanillaItem.BlackCore: prefabName = "BlackCore"; return true; case VanillaItem.CopperOre: prefabName = "CopperOre"; return true; case VanillaItem.Copper: prefabName = "Copper"; return true; case VanillaItem.TinOre: prefabName = "TinOre"; return true; case VanillaItem.Tin: prefabName = "Tin"; return true; case VanillaItem.Bronze: prefabName = "Bronze"; return true; case VanillaItem.IronScrap: prefabName = "IronScrap"; return true; case VanillaItem.Iron: prefabName = "Iron"; return true; case VanillaItem.SilverOre: prefabName = "SilverOre"; return true; case VanillaItem.Silver: prefabName = "Silver"; return true; case VanillaItem.BlackMetalScrap: prefabName = "BlackMetalScrap"; return true; case VanillaItem.BlackMetal: prefabName = "BlackMetal"; return true; case VanillaItem.FlametalOre: prefabName = "FlametalOreNew"; return true; case VanillaItem.Flametal: prefabName = "FlametalNew"; return true; case VanillaItem.LeatherScraps: prefabName = "LeatherScraps"; return true; case VanillaItem.DeerHide: prefabName = "DeerHide"; return true; case VanillaItem.TrollHide: prefabName = "TrollHide"; return true; case VanillaItem.WolfPelt: prefabName = "WolfPelt"; return true; case VanillaItem.LoxPelt: prefabName = "LoxPelt"; return true; case VanillaItem.ScaleHide: prefabName = "ScaleHide"; return true; case VanillaItem.Carapace: prefabName = "Carapace"; return true; case VanillaItem.Feathers: prefabName = "Feathers"; return true; case VanillaItem.BoneFragments: prefabName = "BoneFragments"; return true; case VanillaItem.WitheredBone: prefabName = "WitheredBone"; return true; case VanillaItem.HardAntler: prefabName = "HardAntler"; return true; case VanillaItem.DragonEgg: prefabName = "DragonEgg"; return true; case VanillaItem.Wishbone: prefabName = "Wishbone"; return true; case VanillaItem.YmirRemains: prefabName = "YmirRemains"; return true; case VanillaItem.QueenDrop: prefabName = "QueenDrop"; return true; case VanillaItem.Mushroom: prefabName = "Mushroom"; return true; case VanillaItem.MushroomYellow: prefabName = "MushroomYellow"; return true; case VanillaItem.MushroomBlue: prefabName = "MushroomBlue"; return true; case VanillaItem.MushroomMagecap: prefabName = "MushroomMagecap"; return true; case VanillaItem.MushroomJotunPuffs: prefabName = "MushroomJotunPuffs"; return true; case VanillaItem.Raspberry: prefabName = "Raspberry"; return true; case VanillaItem.Blueberries: prefabName = "Blueberries"; return true; case VanillaItem.Cloudberry: prefabName = "Cloudberry"; return true; case VanillaItem.Honey: prefabName = "Honey"; return true; case VanillaItem.Carrot: prefabName = "Carrot"; return true; case VanillaItem.Turnip: prefabName = "Turnip"; return true; case VanillaItem.Onion: prefabName = "Onion"; return true; case VanillaItem.Barley: prefabName = "Barley"; return true; case VanillaItem.BarleyFlour: prefabName = "BarleyFlour"; return true; case VanillaItem.Flax: prefabName = "Flax"; return true; case VanillaItem.Sap: prefabName = "Sap"; return true; case VanillaItem.RoyalJelly: prefabName = "RoyalJelly"; return true; case VanillaItem.Dandelion: prefabName = "Dandelion"; return true; case VanillaItem.Thistle: prefabName = "Thistle"; return true; case VanillaItem.Entrails: prefabName = "Entrails"; return true; case VanillaItem.Bloodbag: prefabName = "Bloodbag"; return true; case VanillaItem.Ooze: prefabName = "Ooze"; return true; case VanillaItem.Guck: prefabName = "Guck"; return true; case VanillaItem.Tar: prefabName = "Tar"; return true; case VanillaItem.WolfFang: prefabName = "WolfFang"; return true; case VanillaItem.WolfClaw: prefabName = "WolfClaw"; return true; case VanillaItem.Needle: prefabName = "Needle"; return true; case VanillaItem.Obsidian: prefabName = "Obsidian"; return true; case VanillaItem.Chitin: prefabName = "Chitin"; return true; case VanillaItem.SerpentScale: prefabName = "SerpentScale"; return true; case VanillaItem.BoarMeat: prefabName = "RawMeat"; return true; case VanillaItem.DeerMeat: prefabName = "DeerMeat"; return true; case VanillaItem.WolfMeat: prefabName = "WolfMeat"; return true; case VanillaItem.LoxMeat: prefabName = "LoxMeat"; return true; case VanillaItem.SerpentMeat: prefabName = "SerpentMeat"; return true; case VanillaItem.HareMeat: prefabName = "HareMeat"; return true; case VanillaItem.ChickenMeat: prefabName = "ChickenMeat"; return true; case VanillaItem.BugMeat: prefabName = "BugMeat"; return true; case VanillaItem.FishRaw: prefabName = "FishRaw"; return true; case VanillaItem.ArrowWood: prefabName = "ArrowWood"; return true; case VanillaItem.ArrowFire: prefabName = "ArrowFire"; return true; case VanillaItem.ArrowFlint: prefabName = "ArrowFlint"; return true; case VanillaItem.ArrowIron: prefabName = "ArrowIron"; return true; case VanillaItem.ArrowObsidian: prefabName = "ArrowObsidian"; return true; case VanillaItem.ArrowPoison: prefabName = "ArrowPoison"; return true; case VanillaItem.ArrowSilver: prefabName = "ArrowSilver"; return true; case VanillaItem.ArrowNeedle: prefabName = "ArrowNeedle"; return true; case VanillaItem.ArrowCarapace: prefabName = "ArrowCarapace"; return true; case VanillaItem.BoltBone: prefabName = "BoltBone"; return true; case VanillaItem.BoltBlackmetal: prefabName = "BoltBlackmetal"; return true; case VanillaItem.BoltCarapace: prefabName = "BoltCarapace"; return true; case VanillaItem.TrophyBoar: prefabName = "TrophyBoar"; return true; case VanillaItem.TrophyDeer: prefabName = "TrophyDeer"; return true; case VanillaItem.TrophyEikthyr: prefabName = "TrophyEikthyr"; return true; case VanillaItem.TrophyTheElder: prefabName = "TrophyTheElder"; return true; case VanillaItem.TrophyBonemass: prefabName = "TrophyBonemass"; return true; case VanillaItem.TrophyDragonQueen: prefabName = "TrophyDragonQueen"; return true; case VanillaItem.TrophyGoblinKing: prefabName = "TrophyGoblinKing"; return true; case VanillaItem.TrophySeekerQueen: prefabName = "TrophySeekerQueen"; return true; default: prefabName = null; return false; } } } internal interface IItemPackParser { string FormatName { get; } string MissingDependencyMessage { get; } bool IsAvailable { get; } bool CanParse(string filePath); ItemPackDto Parse(string text); } internal sealed class ItemPackDto { public string Name { get; set; } public string Version { get; set; } public List<ItemPackItemDto> Items { get; set; } } internal sealed class ItemPackItemDto { public string ItemName { get; set; } public string AssetBundle { get; set; } public string AssetBundlePath { get; set; } public string PrefabName { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public string Icon { get; set; } public string ItemType { get; set; } public float? Weight { get; set; } public int? StackSize { get; set; } public bool? Teleportable { get; set; } public float? Durability { get; set; } public float? DurabilityPerLevel { get; set; } public int? MaxQuality { get; set; } public int? ToolTier { get; set; } public float? Armor { get; set; } public float? ArmorPerLevel { get; set; } public float? MovementModifier { get; set; } public ItemPackDamageDto Damages { get; set; } public ItemPackDamageDto DamagesPerLevel { get; set; } public ItemPackRecipeDto Recipe { get; set; } public ItemPackPrefabPreparationDto PrefabPreparation { get; set; } } internal sealed class ItemPackDamageDto { public float? Blunt { get; set; } public float? Slash { get; set; } public float? Pierce { get; set; } public float? Fire { get; set; } public float? Frost { get; set; } public float? Lightning { get; set; } public float? Poison { get; set; } public float? Spirit { get; set; } public float? Chop { get; set; } public float? Pickaxe { get; set; } } internal sealed class ItemPackRecipeDto { public bool? Enabled { get; set; } public string CraftingStation { get; set; } public string RepairStation { get; set; } public int? MinStationLevel { get; set; } public int? Amount { get; set; } public bool? RequireOnlyOneIngredient { get; set; } public int? QualityResultAmountMultiplier { get; set; } public List<ItemPackIngredientDto> Ingredients { get; set; } } internal sealed class ItemPackIngredientDto { public string Item { get; set; } public int? Amount { get; set; } public int? AmountPerLevel { get; set; } public bool? Recover { get; set; } public string SourceModGuid { get; set; } } internal sealed class ItemPackPrefabPreparationDto { public bool? AutoAddItemDrop { get; set; } public bool? AutoAddPhysics { get; set; } public bool? WarnOnMissingCollider { get; set; } public bool? AllowTextureIconFallback { get; set; } public bool? ValidateWearableVisuals { get; set; } } public sealed class ItemPackFileResult { public string FilePath { get; internal set; } public string Format { get; internal set; } public string PackName { get; internal set; } public string PackVersion { get; internal set; } public bool Skipped { get; internal set; } public string SkipReason { get; internal set; } public List<ItemRegistrationResult> Items { get; private set; } public List<ItemPackRegistrationError> Errors { get; private set; } public ItemPackFileResult() { Items = new List<ItemRegistrationResult>(); Errors = new List<ItemPackRegistrationError>(); } } internal static class ItemPackLoader { private static readonly IItemPackParser[] Parsers = new IItemPackParser[2] { new YamlItemPackParser(), new JsonItemPackParser() }; public static string DefaultPackDirectory => Path.Combine(Paths.ConfigPath, "CustomItemRegistry", "packs"); public static ItemPackParserStatus GetParserStatus() { return ItemPackParserStatus.Create(); } public static ItemPackLoadResult LoadDefault(ItemPackLoadOptions options) { return LoadDirectory(DefaultPackDirectory, options); } public static ItemPackLoadResult LoadDirectory(string directory, ItemPackLoadOptions options) { options = options ?? new ItemPackLoadOptions(); ItemPackLoadResult itemPackLoadResult = new ItemPackLoadResult { SourcePath = directory }; if (string.IsNullOrWhiteSpace(directory) || !Directory.Exists(directory)) { return itemPackLoadResult; } SearchOption searchOption = (options.Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); foreach (string item in Directory.EnumerateFiles(directory, "*.*", searchOption).Where(IsSupportedExtension).OrderBy<string, string>((string path) => path, StringComparer.OrdinalIgnoreCase)) { itemPackLoadResult.Add(LoadFile(item, options)); } LogSummary(itemPackLoadResult, options); return itemPackLoadResult; } public static ItemPackLoadResult LoadFile(string filePath, ItemPackLoadOptions options) { options = options ?? new ItemPackLoadOptions(); ItemPackLoadResult itemPackLoadResult = new ItemPackLoadResult { SourcePath = filePath }; ItemPackFileResult itemPackFileResult = new ItemPackFileResult { FilePath = filePath }; itemPackLoadResult.Files.Add(itemPackFileResult); IItemPackParser itemPackParser = Parsers.FirstOrDefault((IItemPackParser candidate) => candidate.CanParse(filePath)); if (itemPackParser == null) { itemPackFileResult.Skipped = true; itemPackFileResult.SkipReason = "Unsupported item-pack file extension"; return itemPackLoadResult; } itemPackFileResult.Format = itemPackParser.FormatName; if (!itemPackParser.IsAvailable) { itemPackFileResult.Skipped = true; itemPackFileResult.SkipReason = itemPackParser.MissingDependencyMessage; CustomItemRegistry.LogWarning(itemPackParser.MissingDependencyMessage + " Skipping '" + filePath + "'."); return itemPackLoadResult; } try { ItemPackDto itemPackDto = itemPackParser.Parse(File.ReadAllText(filePath)); itemPackFileResult.PackName = itemPackDto?.Name; itemPackFileResult.PackVersion = itemPackDto?.Version; if (itemPackDto?.Items == null || itemPackDto.Items.Count == 0) { itemPackFileResult.Errors.Add(new ItemPackRegistrationError(filePath, null, "Item pack contains no items")); return itemPackLoadResult; } foreach (ItemPackItemDto item in itemPackDto.Items) { RegisterItem(filePath, options, itemPackFileResult, item); } } catch (Exception exception) { string message = Unwrap(exception).Message; itemPackFileResult.Errors.Add(new ItemPackRegistrationError(filePath, null, message, exception)); CustomItemRegistry.LogWarning("Could not load item pack '" + filePath + "': " + message); } return itemPackLoadResult; } private static void RegisterItem(string filePath, ItemPackLoadOptions options, ItemPackFileResult fileResult, ItemPackItemDto item) { CustomItemDefinition customItemDefinition = null; try { customItemDefinition = ItemPackMapper.ToDefinition(item, filePath, options); if (options.RegisterItems) { CustomItemRegistry.TryRegisterItem(customItemDefinition, out var result); fileResult.Items.Add(result); if (!result.Success) { fileResult.Errors.Add(new ItemPackRegistrationError(filePath, customItemDefinition.ItemName, result.ErrorMessage, result.Exception)); } } } catch (Exception exception) { string text = customItemDefinition?.ItemName ?? item?.ItemName; string message = Unwrap(exception).Message; fileResult.Errors.Add(new ItemPackRegistrationError(filePath, text, message, exception)); CustomItemRegistry.LogWarning("Could not register item-pack item '" + (text ?? "<unknown>") + "' from '" + filePath + "': " + message); } } private static bool IsSupportedExtension(string filePath) { return Parsers.Any((IItemPackParser parser) => parser.CanParse(filePath)); } private static Exception Unwrap(Exception exception) { if (!(exception is TargetInvocationException ex) || ex.InnerException == null) { return exception; } return ex.InnerException; } private static void LogSummary(ItemPackLoadResult result, ItemPackLoadOptions options) { if (options.LogSummary && result.Files.Count != 0) { CustomItemRegistry.LogInfo($"Loaded CIR item packs from '{result.SourcePath}'. Files={result.Files.Count}; registered={result.RegisteredItemCount}; skipped={result.SkippedFileCount}; errors={result.FailedItemCount}."); } } } public sealed class ItemPackLoadOptions { public bool Recursive { get; set; } public bool RegisterItems { get; set; } public bool LogSummary { get; set; } public string AssetBundleBaseDirectory { get; set; } public ItemPackLoadOptions() { Recursive = true; RegisterItems = true; LogSummary = true; } } public sealed class ItemPackLoadResult { public string SourcePath { get; internal set; } public List<ItemPackFileResult> Files { get; private set; } public int RegisteredItemCount => Files.Sum((ItemPackFileResult file) => file.Items.Count((ItemRegistrationResult item) => item.Success)); public int FailedItemCount => Files.Sum((ItemPackFileResult file) => file.Items.Count((ItemRegistrationResult item) => !item.Success)) + Files.Sum((ItemPackFileResult file) => file.Errors.Count); public int SkippedFileCount => Files.Count((ItemPackFileResult file) => file.Skipped); public ItemPackLoadResult() { Files = new List<ItemPackFileResult>(); } internal void Add(ItemPackLoadResult other) { if (other != null) { Files.AddRange(other.Files); } } } internal static class ItemPackMapper { public static CustomItemDefinition ToDefinition(ItemPackItemDto item, string filePath, ItemPackLoadOptions options) { if (item == null) { throw new InvalidOperationException("Item entry is empty"); } CustomItemDefinition obj = new CustomItemDefinition(item.ItemName) { AssetBundlePath = ResolveAssetBundlePath(item.AssetBundle ?? item.AssetBundlePath, filePath, options), PrefabName = item.PrefabName, DisplayName = item.DisplayName, Description = item.Description, IconAssetName = item.Icon, Weight = item.Weight, StackSize = item.StackSize, Teleportable = item.Teleportable, MaxDurability = item.Durability, DurabilityPerLevel = item.DurabilityPerLevel, MaxQuality = item.MaxQuality, ToolTier = item.ToolTier, Armor = item.Armor, ArmorPerLevel = item.ArmorPerLevel, MovementModifier = item.MovementModifier }; ApplyItemType(obj, item.ItemType); ApplyDamages(obj, item.Damages, perLevel: false); ApplyDamages(obj, item.DamagesPerLevel, perLevel: true); ApplyRecipe(obj, item.Recipe); ApplyPrefabPreparation(obj, item.PrefabPreparation); return obj; } private static string ResolveAssetBundlePath(string assetBundle, string filePath, ItemPackLoadOptions options) { if (string.IsNullOrWhiteSpace(assetBundle)) { return assetBundle; } if (Path.IsPathRooted(assetBundle)) { return assetBundle; } string text = ((!string.IsNullOrWhiteSpace(options?.AssetBundleBaseDirectory)) ? options.AssetBundleBaseDirectory : Path.GetDirectoryName(filePath)); string text2 = (string.IsNullOrWhiteSpace(text) ? assetBundle : Path.GetFullPath(Path.Combine(text, assetBundle))); if (File.Exists(text2)) { return text2; } string fullPath = Path.GetFullPath(Path.Combine(Paths.PluginPath, assetBundle)); if (!File.Exists(fullPath)) { return text2; } return fullPath; } private static void ApplyItemType(CustomItemDefinition definition, string itemType) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrWhiteSpace(itemType)) { if (!Enum.TryParse<ItemType>(itemType, ignoreCase: true, out ItemType result)) { throw new InvalidOperationException("Invalid itemType '" + itemType + "'"); } definition.ItemType = result; } } private static void ApplyDamages(CustomItemDefinition definition, ItemPackDamageDto dto, bool perLevel) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) if (dto != null) { DamageTypes val = default(DamageTypes); val.m_blunt = dto.Blunt.GetValueOrDefault(); val.m_slash = dto.Slash.GetValueOrDefault(); val.m_pierce = dto.Pierce.GetValueOrDefault(); val.m_fire = dto.Fire.GetValueOrDefault(); val.m_frost = dto.Frost.GetValueOrDefault(); val.m_lightning = dto.Lightning.GetValueOrDefault(); val.m_poison = dto.Poison.GetValueOrDefault(); val.m_spirit = dto.Spirit.GetValueOrDefault(); val.m_chop = dto.Chop.GetValueOrDefault(); val.m_pickaxe = dto.Pickaxe.GetValueOrDefault(); DamageTypes val2 = val; if (perLevel) { definition.DamagesPerLevel = val2; definition.HasDamagesPerLevel = true; } else { definition.Damages = val2; definition.HasDamages = true; } } } private static void ApplyRecipe(CustomItemDefinition definition, ItemPackRecipeDto dto) { if (dto != null) { string validationError; string validationError2; CraftingRecipe recipe = new CraftingRecipe(BuildIngredients(dto.Ingredients), ResolveStation(dto.CraftingStation, out validationError), dto.Amount.GetValueOrDefault(1), ResolveStation(dto.RepairStation, out validationError2), dto.MinStationLevel.GetValueOrDefault(1), dto.Enabled.GetValueOrDefault(true), dto.RequireOnlyOneIngredient.GetValueOrDefault(), dto.QualityResultAmountMultiplier.GetValueOrDefault(1)); recipe.CraftingStationValidationError = validationError; recipe.RepairStationValidationError = validationError2; definition.Recipe = recipe; definition.HasRecipe = true; } } private static List<Ingredient> BuildIngredients(List<ItemPackIngredientDto> dtos) { List<Ingredient> list = new List<Ingredient>(); if (dtos == null) { return list; } foreach (ItemPackIngredientDto dto in dtos) { Ingredient ingredient = new Ingredient(dto?.Item, (dto?.Amount).GetValueOrDefault(), (dto?.AmountPerLevel).GetValueOrDefault(), (dto?.Recover).GetValueOrDefault(true)); ingredient.SourceModGuid = dto?.SourceModGuid; Ingredient item = ingredient; list.Add(item); } return list; } private static string ResolveStation(string station, out string validationError) { validationError = null; if (string.IsNullOrWhiteSpace(station)) { return station; } if (!Enum.TryParse<CraftingStation>(s