Decompiled source of ValheimCuisine v2.2.7
ValheimCuisine.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; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Timers; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using CreatureManager; using HarmonyLib; using ItemManager; using JetBrains.Annotations; using LocalizationManager; using LocationManager; using Microsoft.CodeAnalysis; using PieceManager; using ServerSync; using SoftReferenceableAssets; using StatusEffectManager; using TMPro; using UnityEngine; using UnityEngine.UI; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("ValheimCuisine")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("XutzBR")] [assembly: AssemblyProduct("ValheimCuisine")] [assembly: AssemblyCopyright("Copyright © 2022")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("E0E2F92E-557C-4A05-9D89-AA92A0BD75C4")] [assembly: AssemblyFileVersion("2.2.7")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.2.7.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 ValheimCuisine { [BepInPlugin("XutzBR.ValheimCuisine", "ValheimCuisine", "2.2.7")] public class ValheimCuisinePlugin : BaseUnityPlugin { private class GrimpyBoxConversionData { public int RequiredAmount { get; set; } public int ProducedAmount { get; set; } public GrimpyBoxConversionData(int requiredAmount, int producedAmount) { RequiredAmount = requiredAmount; ProducedAmount = producedAmount; } } public class ConversionData { public string ToItem { get; set; } public float CookTime { get; set; } public int ProducedItems { get; set; } public ConversionData(string toItem, float cookTime = 0f, int producedItems = 0) { ToItem = toItem; CookTime = cookTime; ProducedItems = producedItems; } } public enum Toggle { On = 1, Off = 0 } [HarmonyPatch(typeof(ZNetScene), "Awake")] public static class ButcherTableCraftingStationPatch { private static bool hasRun; private static void Postfix(ZNetScene __instance) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Expected O, but got Unknown if (hasRun) { return; } hasRun = true; if ((Object)(object)__instance == (Object)null) { return; } GameObject prefab = __instance.GetPrefab("cauldron_ext3_butchertable"); if ((Object)(object)prefab == (Object)null) { ValheimCuisineLogger.LogWarning((object)"Could not find butcher table prefab 'cauldron_ext3_butchertable'"); return; } CraftingStation component = prefab.GetComponent<CraftingStation>(); if ((Object)(object)component != (Object)null) { ValheimCuisineLogger.LogDebug((object)"CraftingStation already exists on butcher table, skipping..."); return; } component = prefab.AddComponent<CraftingStation>(); component.m_name = "$piece_cauldron_ext3_butchertable"; component.m_discoverRange = 4f; component.m_rangeBuild = 20f; component.m_useDistance = 2f; component.m_useAnimation = 1; component.m_craftingSkill = (SkillType)105; component.m_craftRequireRoof = false; component.m_craftRequireFire = false; Piece component2 = prefab.GetComponent<Piece>(); if ((Object)(object)component2 != (Object)null && (Object)(object)component2.m_icon != (Object)null) { component.m_icon = component2.m_icon; } CraftingStation obj = component; EffectList val = new EffectList(); val.m_effectPrefabs = (EffectData[])(object)new EffectData[1] { new EffectData { m_prefab = GetEffectPrefab(__instance, "VC_sfx_ChefTableCraft"), m_enabled = true, m_variant = -1 } }; obj.m_craftItemEffects = val; CraftingStation obj2 = component; val = new EffectList(); val.m_effectPrefabs = (EffectData[])(object)new EffectData[1] { new EffectData { m_prefab = GetEffectPrefab(__instance, "VC_sfx_gui_craftitem_workbench_end"), m_enabled = true, m_variant = -1 } }; obj2.m_craftItemDoneEffects = val; ValheimCuisineLogger.LogInfo((object)"Successfully added CraftingStation component to butcher table"); } private static GameObject GetEffectPrefab(ZNetScene zNetScene, string effectName) { GameObject prefab = zNetScene.GetPrefab(effectName); if ((Object)(object)prefab == (Object)null) { ValheimCuisineLogger.LogWarning((object)("Effect prefab '" + effectName + "' not found in ZNetScene")); } return prefab; } } [HarmonyPatch(typeof(StationExtension), "GetHoverText")] public static class ButcherTableHoverTextPatch { private static void Postfix(StationExtension __instance, ref string __result) { string prefabName = Utils.GetPrefabName(((Component)__instance).gameObject); if (prefabName != "cauldron_ext3_butchertable" || (Object)(object)Player.m_localPlayer == (Object)null) { return; } CraftingStation component = ((Component)__instance).GetComponent<CraftingStation>(); if (!((Object)(object)component == (Object)null)) { if (!component.InUseDistance((Humanoid)(object)Player.m_localPlayer)) { __result = Localization.instance.Localize("<color=#888888>$piece_toofar</color>"); } else { __result = Localization.instance.Localize("$piece_cauldron_ext3_butchertable\n[<color=yellow><b>$KEY_Use</b></color>] $piece_use"); } } } } [HarmonyPatch(typeof(ZNetScene), "Awake")] private static class ConversionsZNetScene_AwakePost_Patch { private static bool hasRun; private static List<ItemConversion> vanillaCookingStationConversions; private static List<ItemConversion> vanillaCookingStationIronConversions; private static List<ItemConversion> vanillaOvenConversions; private static List<ItemConversion> vanillaFermenterConversions; private static void Postfix(ZNetScene __instance) { if (!hasRun) { hasRun = true; ReapplyConversions(__instance, updateWorldInstances: false); } } public static void ReapplyConversions(ZNetScene scene, bool updateWorldInstances = true) { if ((Object)(object)scene == (Object)null) { return; } List<GameObject> prefabs = scene.m_prefabs; if (prefabs == null || prefabs.Count <= 0) { return; } try { GameObject prefab = scene.GetPrefab("piece_cookingstation"); CookingStation val = ((prefab != null) ? prefab.GetComponent<CookingStation>() : null); GameObject prefab2 = scene.GetPrefab("piece_cookingstation_iron"); CookingStation val2 = ((prefab2 != null) ? prefab2.GetComponent<CookingStation>() : null); GameObject prefab3 = scene.GetPrefab("piece_oven"); CookingStation val3 = ((prefab3 != null) ? prefab3.GetComponent<CookingStation>() : null); GameObject prefab4 = scene.GetPrefab("fermenter"); Fermenter val4 = ((prefab4 != null) ? prefab4.GetComponent<Fermenter>() : null); if (vanillaCookingStationConversions == null && (Object)(object)val != (Object)null && val.m_conversion != null) { vanillaCookingStationConversions = new List<ItemConversion>(val.m_conversion); } if (vanillaCookingStationIronConversions == null && (Object)(object)val2 != (Object)null && val2.m_conversion != null) { vanillaCookingStationIronConversions = new List<ItemConversion>(val2.m_conversion); } if (vanillaOvenConversions == null && (Object)(object)val3 != (Object)null && val3.m_conversion != null) { vanillaOvenConversions = new List<ItemConversion>(val3.m_conversion); } if (vanillaFermenterConversions == null && (Object)(object)val4 != (Object)null && val4.m_conversion != null) { vanillaFermenterConversions = new List<ItemConversion>(val4.m_conversion); } GameObject prefab5 = scene.GetPrefab("VC_SmokehouseWood"); CookingStation val5 = ((prefab5 != null) ? prefab5.GetComponent<CookingStation>() : null); GameObject prefab6 = scene.GetPrefab("VC_SmokehouseStone"); CookingStation val6 = ((prefab6 != null) ? prefab6.GetComponent<CookingStation>() : null); GameObject prefab7 = scene.GetPrefab("VC_DryingHook"); CookingStation val7 = ((prefab7 != null) ? prefab7.GetComponent<CookingStation>() : null); GameObject prefab8 = scene.GetPrefab("VC_DryingRack"); CookingStation val8 = ((prefab8 != null) ? prefab8.GetComponent<CookingStation>() : null); GameObject prefab9 = scene.GetPrefab("VC_FoodBarrel1"); Fermenter val9 = ((prefab9 != null) ? prefab9.GetComponent<Fermenter>() : null); GameObject prefab10 = scene.GetPrefab("VC_FoodBarrel2"); Fermenter val10 = ((prefab10 != null) ? prefab10.GetComponent<Fermenter>() : null); GameObject prefab11 = scene.GetPrefab("VC_LegendFermenter"); Fermenter val11 = ((prefab11 != null) ? prefab11.GetComponent<Fermenter>() : null); GameObject prefab12 = scene.GetPrefab("VC_DvergrCauldron"); Fermenter val12 = ((prefab12 != null) ? prefab12.GetComponent<Fermenter>() : null); ClearAllConversions(val, val2, val3, val4, val5, val6, val7, val8, val9, val10, val11, val12); if ((Object)(object)val != (Object)null) { RestoreVanillaConversions(val, vanillaCookingStationConversions); ApplyCookingStationConversions(scene, val, CookingStationConversions.Value, "Cooking Station"); } if ((Object)(object)val2 != (Object)null) { RestoreVanillaConversions(val2, vanillaCookingStationIronConversions); ApplyCookingStationConversions(scene, val2, CookingStationIronConversions.Value, "Iron Cooking Station"); } if ((Object)(object)val3 != (Object)null) { RestoreVanillaConversions(val3, vanillaOvenConversions); ApplyCookingStationConversions(scene, val3, OvenConversions.Value, "Oven"); } if ((Object)(object)val4 != (Object)null) { RestoreVanillaConversions(val4, vanillaFermenterConversions); ApplyFermenterConversions(scene, val4, FermenterConversions.Value, "Fermenter"); } if ((Object)(object)val5 != (Object)null) { ApplyCookingStationConversions(scene, val5, SmokehouseWoodConversions.Value, "Wooden Smokehouse"); } if ((Object)(object)val6 != (Object)null) { ApplyCookingStationConversions(scene, val6, SmokehouseStoneConversions.Value, "Stone Smokehouse"); } if ((Object)(object)val7 != (Object)null) { ApplyCookingStationConversions(scene, val7, DryingRack1Conversions.Value, "Drying Hook"); } if ((Object)(object)val8 != (Object)null) { ApplyCookingStationConversions(scene, val8, DryingRack2Conversions.Value, "Drying Rack"); } if ((Object)(object)val9 != (Object)null) { ApplyFermenterConversions(scene, val9, FoodBarrel1Conversions.Value, "Food Fermenter"); } if ((Object)(object)val10 != (Object)null) { ApplyFermenterConversions(scene, val10, FoodBarrel2Conversions.Value, "Ashwood Food Fermenter"); } if ((Object)(object)val11 != (Object)null) { ApplyFermenterConversions(scene, val11, LegendFermenterConversions.Value, "Great Fermenter"); } if ((Object)(object)val12 != (Object)null) { ApplyFermenterConversions(scene, val12, DvergrCauldronConversions.Value, "Dvergr Cauldron"); } if (updateWorldInstances) { UpdateAllWorldInstances(scene, val, val2, val3, val4, val5, val6, val7, val8, val9, val10, val11, val12); } ValheimCuisineLogger.LogInfo((object)(updateWorldInstances ? "[Conversions] All conversions applied from config (prefabs and world instances updated)" : "[Conversions] All conversions applied from config (prefabs only, world instances will update on config reload)")); } catch (Exception arg) { ValheimCuisineLogger.LogError((object)$"Error applying conversions: {arg}"); } } private static void RestoreVanillaConversions(CookingStation station, List<ItemConversion> vanillaConversions) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown if ((Object)(object)station == (Object)null || vanillaConversions == null) { return; } foreach (ItemConversion vanillaConversion in vanillaConversions) { station.m_conversion.Add(new ItemConversion { m_from = vanillaConversion.m_from, m_to = vanillaConversion.m_to, m_cookTime = vanillaConversion.m_cookTime }); } } private static void RestoreVanillaConversions(Fermenter fermenter, List<ItemConversion> vanillaConversions) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown if ((Object)(object)fermenter == (Object)null || vanillaConversions == null) { return; } foreach (ItemConversion vanillaConversion in vanillaConversions) { fermenter.m_conversion.Add(new ItemConversion { m_from = vanillaConversion.m_from, m_to = vanillaConversion.m_to, m_producedItems = vanillaConversion.m_producedItems }); } } private static void UpdateAllWorldInstances(ZNetScene scene, CookingStation cookingStationPrefab, CookingStation cookingStationIronPrefab, CookingStation cookingStationOvenPrefab, Fermenter fermenterPrefab, CookingStation smokehouseWoodPrefab, CookingStation smokehouseStonePrefab, CookingStation dryingRack1Prefab, CookingStation dryingRack2Prefab, Fermenter foodBarrel1Prefab, Fermenter foodBarrel2Prefab, Fermenter legendFermenterPrefab, Fermenter dvergrCauldronPrefab) { if ((Object)(object)Player.m_localPlayer == (Object)null) { ValheimCuisineLogger.LogWarning((object)"[Conversions] Skipping world instance update - player not yet loaded"); return; } int num = 0; CookingStation[] array = Object.FindObjectsByType<CookingStation>((FindObjectsSortMode)0); foreach (CookingStation val in array) { if (!((Object)(object)val == (Object)null)) { string prefabName = Utils.GetPrefabName(((Component)val).gameObject); if (prefabName == "piece_cookingstation" && (Object)(object)cookingStationPrefab != (Object)null) { val.m_conversion = new List<ItemConversion>(cookingStationPrefab.m_conversion); num++; } else if (prefabName == "piece_cookingstation_iron" && (Object)(object)cookingStationIronPrefab != (Object)null) { val.m_conversion = new List<ItemConversion>(cookingStationIronPrefab.m_conversion); num++; } else if (prefabName == "piece_oven" && (Object)(object)cookingStationOvenPrefab != (Object)null) { val.m_conversion = new List<ItemConversion>(cookingStationOvenPrefab.m_conversion); num++; } else if (prefabName == "VC_SmokehouseWood" && (Object)(object)smokehouseWoodPrefab != (Object)null) { val.m_conversion = new List<ItemConversion>(smokehouseWoodPrefab.m_conversion); num++; } else if (prefabName == "VC_SmokehouseStone" && (Object)(object)smokehouseStonePrefab != (Object)null) { val.m_conversion = new List<ItemConversion>(smokehouseStonePrefab.m_conversion); num++; } else if (prefabName == "VC_DryingHook" && (Object)(object)dryingRack1Prefab != (Object)null) { val.m_conversion = new List<ItemConversion>(dryingRack1Prefab.m_conversion); num++; } else if (prefabName == "VC_DryingRack" && (Object)(object)dryingRack2Prefab != (Object)null) { val.m_conversion = new List<ItemConversion>(dryingRack2Prefab.m_conversion); num++; } } } Fermenter[] array2 = Object.FindObjectsByType<Fermenter>((FindObjectsSortMode)0); foreach (Fermenter val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { string prefabName2 = Utils.GetPrefabName(((Component)val2).gameObject); if (prefabName2 == "fermenter" && (Object)(object)fermenterPrefab != (Object)null) { val2.m_conversion = new List<ItemConversion>(fermenterPrefab.m_conversion); num++; } else if (prefabName2 == "VC_FoodBarrel1" && (Object)(object)foodBarrel1Prefab != (Object)null) { val2.m_conversion = new List<ItemConversion>(foodBarrel1Prefab.m_conversion); num++; } else if (prefabName2 == "VC_FoodBarrel2" && (Object)(object)foodBarrel2Prefab != (Object)null) { val2.m_conversion = new List<ItemConversion>(foodBarrel2Prefab.m_conversion); num++; } else if (prefabName2 == "VC_LegendFermenter" && (Object)(object)legendFermenterPrefab != (Object)null) { val2.m_conversion = new List<ItemConversion>(legendFermenterPrefab.m_conversion); num++; } else if (prefabName2 == "VC_DvergrCauldron" && (Object)(object)dvergrCauldronPrefab != (Object)null) { val2.m_conversion = new List<ItemConversion>(dvergrCauldronPrefab.m_conversion); num++; } } } if (num > 0) { ValheimCuisineLogger.LogInfo((object)$"[Conversions] Updated {num} existing station instances in the world"); } } private static void ClearAllConversions(params object[] stations) { foreach (object obj in stations) { if (obj != null) { CookingStation val = (CookingStation)((obj is CookingStation) ? obj : null); if (val != null) { val.m_conversion.Clear(); } else { ((Fermenter)(((obj is Fermenter) ? obj : null)?)).m_conversion.Clear(); } } } } private static void ApplyCookingStationConversions(ZNetScene scene, CookingStation station, string configValue, string stationName) { //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Expected O, but got Unknown if (string.IsNullOrWhiteSpace(configValue)) { return; } string[] array = configValue.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); int num = 0; string[] array2 = array; foreach (string text in array2) { string[] array3 = text.Split(new char[1] { ':' }); if (array3.Length != 3) { ValheimCuisineLogger.LogWarning((object)("[Conversions] Invalid format for " + stationName + ": " + text)); continue; } string text2 = array3[0].Trim(); string text3 = array3[1].Trim(); if (!float.TryParse(array3[2].Trim(), out var result)) { ValheimCuisineLogger.LogWarning((object)("[Conversions] Invalid cook time for " + stationName + ": " + array3[2])); continue; } GameObject prefab = scene.GetPrefab(text2); GameObject prefab2 = scene.GetPrefab(text3); if (!((Object)(object)prefab == (Object)null) && !((Object)(object)prefab2 == (Object)null)) { ItemDrop component = prefab.GetComponent<ItemDrop>(); ItemDrop component2 = prefab2.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component2 == (Object)null)) { station.m_conversion.Add(new ItemConversion { m_cookTime = result, m_from = component, m_to = component2 }); num++; } } } if (num > 0) { ValheimCuisineLogger.LogInfo((object)$"[Conversions] Added {num} conversions to {stationName}"); } } private static void ApplyFermenterConversions(ZNetScene scene, Fermenter fermenter, string configValue, string fermenterName) { //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Expected O, but got Unknown if (string.IsNullOrWhiteSpace(configValue)) { return; } string[] array = configValue.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); int num = 0; string[] array2 = array; foreach (string text in array2) { string[] array3 = text.Split(new char[1] { ':' }); if (array3.Length != 3) { ValheimCuisineLogger.LogWarning((object)("[Conversions] Invalid format for " + fermenterName + ": " + text)); continue; } string text2 = array3[0].Trim(); string text3 = array3[1].Trim(); if (!int.TryParse(array3[2].Trim(), out var result)) { ValheimCuisineLogger.LogWarning((object)("[Conversions] Invalid produced items for " + fermenterName + ": " + array3[2])); continue; } GameObject prefab = scene.GetPrefab(text2); GameObject prefab2 = scene.GetPrefab(text3); if (!((Object)(object)prefab == (Object)null) && !((Object)(object)prefab2 == (Object)null)) { ItemDrop component = prefab.GetComponent<ItemDrop>(); ItemDrop component2 = prefab2.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component2 == (Object)null)) { fermenter.m_conversion.Add(new ItemConversion { m_from = component, m_to = component2, m_producedItems = result }); num++; } } } if (num > 0) { ValheimCuisineLogger.LogInfo((object)$"[Conversions] Added {num} conversions to {fermenterName}"); } } } [HarmonyPatch(typeof(Fermenter), "GetStatus")] public static class Fermenter_OverrideDuration { [HarmonyPrefix] private static void Prefix(Fermenter __instance) { switch (((Object)((Component)__instance).gameObject).name.Replace("(Clone)", "").Trim()) { case "fermenter": __instance.m_fermentationDuration = VanillaFermenterTime.Value; break; case "VC_LegendFermenter": __instance.m_fermentationDuration = LegendFermenterTime.Value; break; case "VC_FoodBarrel1": __instance.m_fermentationDuration = FoodBarrel1Time.Value; break; case "VC_FoodBarrel2": __instance.m_fermentationDuration = FoodBarrel2Time.Value; break; } } } public class GrimpyBoxConverter : MonoBehaviour { private static readonly List<GrimpyBoxConverter> m_instances = new List<GrimpyBoxConverter>(); private Container m_container; private ZNetView m_nview; private void Awake() { m_container = ((Component)this).GetComponent<Container>(); m_nview = m_container?.m_nview; if (!((Object)(object)m_container == (Object)null) && !((Object)(object)m_nview == (Object)null) && m_nview.IsValid()) { ((MonoBehaviour)this).InvokeRepeating("UpdateConversion", 1f, GrimpyBoxConversionTime.Value); m_instances.Add(this); ValheimCuisineLogger.LogInfo((object)("[GrimpyBox] Converter initialized on " + ((Object)((Component)this).gameObject).name)); } } private void OnDestroy() { m_instances.Remove(this); } public static void UpdateConversionRate() { foreach (GrimpyBoxConverter instance in m_instances) { ((MonoBehaviour)instance).CancelInvoke("UpdateConversion"); ((MonoBehaviour)instance).InvokeRepeating("UpdateConversion", 0f, GrimpyBoxConversionTime.Value); } } private void UpdateConversion() { if ((Object)(object)m_container == (Object)null || (Object)(object)m_nview == (Object)null || !m_nview.IsValid() || !m_nview.IsOwner()) { return; } Inventory inventory = m_container.GetInventory(); if (inventory == null) { return; } List<ItemData> allItems = inventory.GetAllItems(); if (!GetConversion(allItems, out ItemData itemToConvert, out GrimpyBoxConversionData conversionData) || itemToConvert == null || conversionData == null) { return; } string itemPrefabName = GetItemPrefabName(itemToConvert); int num = allItems.Where((ItemData i) => GetItemPrefabName(i) == itemPrefabName).Sum((ItemData i) => i.m_stack); if (num < conversionData.RequiredAmount) { return; } int num2 = conversionData.RequiredAmount; foreach (ItemData item in allItems.ToList()) { if (!(GetItemPrefabName(item) != itemPrefabName)) { if (item.m_stack >= num2) { inventory.RemoveItem(item, num2); break; } num2 -= item.m_stack; inventory.RemoveItem(item, item.m_stack); } } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Ectoplasm"); if ((Object)(object)itemPrefab == (Object)null) { ValheimCuisineLogger.LogError((object)"[GrimpyBox] Ectoplasm prefab not found in ObjectDB!"); return; } ItemData val = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone(); val.m_stack = conversionData.ProducedAmount; if (inventory.AddItem(val)) { ValheimCuisineLogger.LogInfo((object)$"[GrimpyBox] Converted {conversionData.RequiredAmount}x {itemPrefabName} to {conversionData.ProducedAmount}x Ectoplasm"); } else { ValheimCuisineLogger.LogError((object)"[GrimpyBox] Failed to add Ectoplasm to inventory!"); } m_container.Save(); } private bool GetConversion(List<ItemData> items, out ItemData itemToConvert, out GrimpyBoxConversionData conversionData) { itemToConvert = null; conversionData = null; foreach (ItemData item in items) { string itemPrefabName = GetItemPrefabName(item); if (grimpyBoxConversionDict.TryGetValue(itemPrefabName, out GrimpyBoxConversionData value)) { int num = items.Where((ItemData i) => GetItemPrefabName(i) == itemPrefabName).Sum((ItemData i) => i.m_stack); if (num >= value.RequiredAmount) { itemToConvert = item; conversionData = value; return true; } } } return false; } } [HarmonyPatch(typeof(Container), "Awake")] public static class GrimpyBox_Container_Awake_Patch { [HarmonyPostfix] public static void Postfix(Container __instance) { if (!(__instance.m_name != "$piece_vc_volvacauldron_ext4") && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsValid() && (Object)(object)((Component)__instance).GetComponent<GrimpyBoxConverter>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<GrimpyBoxConverter>(); } } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] public static class GrimpyBox_Inventory_AddItem_Patch { [HarmonyPrefix] public static bool Prefix(Inventory __instance, ItemData item) { if (__instance.m_name != "$piece_vc_volvacauldron_ext4") { return true; } string itemPrefabName = GetItemPrefabName(item); if (itemPrefabName == "Ectoplasm" || item.m_shared.m_name == "$item_ectoplasm") { return true; } if (!IsGrimpyBoxConvertibleItem(itemPrefabName)) { ValheimCuisineLogger.LogWarning((object)("[GrimpyBox] BLOCKED: " + itemPrefabName + " is not convertible!")); if ((Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, Localization.instance.Localize("$vc_grimpybox_only_convertible"), 0, (Sprite)null); } return false; } return true; } } [HarmonyPatch(typeof(Container), "OnContainerChanged")] public static class GrimpyBox_Container_OnContainerChanged_Patch { [HarmonyPostfix] public static void Postfix(Container __instance) { if (!(__instance.m_name != "$piece_vc_volvacauldron_ext4") && Object.op_Implicit((Object)(object)__instance.m_nview) && __instance.m_nview.IsValid() && __instance.IsOwner() && !__instance.m_loading) { __instance.Save(); } } } [HarmonyPatch(typeof(Container), "Interact")] public static class GrimpyBox_Container_Interact_Patch { [HarmonyPostfix] public static void Postfix(Container __instance, bool __result) { if (__result && !(__instance.m_name != "$piece_vc_volvacauldron_ext4") && (Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid() && __instance.IsOwner()) { __instance.Save(); } } } [HarmonyPatch(typeof(InventoryGui), "Hide")] public static class GrimpyBox_InventoryGui_Hide_Patch { [HarmonyPrefix] public static void Prefix(InventoryGui __instance) { Container currentContainer = __instance.m_currentContainer; if ((Object)(object)currentContainer != (Object)null && currentContainer.m_name == "$piece_vc_volvacauldron_ext4" && (Object)(object)currentContainer.m_nview != (Object)null && currentContainer.m_nview.IsValid() && currentContainer.IsOwner()) { currentContainer.Save(); } } } [HarmonyPatch(typeof(Container), "Save")] public static class GrimpyBox_Container_Save_Patch { [HarmonyPostfix] public static void Postfix(Container __instance) { if (!(__instance.m_name != "$piece_vc_volvacauldron_ext4") && (Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid()) { ZDO zDO = __instance.m_nview.GetZDO(); zDO.IncreaseDataRevision(); } } } [HarmonyPatch(typeof(Container), "GetHoverText")] public static class GrimpyBox_Container_GetHoverText_Patch { [HarmonyPostfix] public static void Postfix(Container __instance, ref string __result) { if (__instance.m_name != "$piece_vc_volvacauldron_ext4") { return; } Inventory inventory = __instance.GetInventory(); if (inventory != null) { int num = (from i in inventory.GetAllItems() where GetItemPrefabName(i) == "Ectoplasm" || i.m_shared.m_name == "$item_ectoplasm" select i).Sum((ItemData i) => i.m_stack); if (num > 0) { __result = __result + "\n\n<color=yellow>" + Localization.instance.Localize("$vc_grimpybox_ectoplasm") + ":</color> " + num; } else if (inventory.GetAllItems().Count == 0) { __result = __result + "\n<color=yellow>" + Localization.instance.Localize("$vc_grimpybox_empty") + "</color>"; } __result = __result + "\n<color=yellow>" + Localization.instance.Localize("$vc_grimpybox_conversion_time") + ":</color> " + GrimpyBoxConversionTime.Value + "s"; } } } [Serializable] public class FreydisResource { public string itemName; public int minAmount = 1; public int maxAmount = 3; public float dropChance = 100f; } public class FreydisCollector : MonoBehaviour, Hoverable, Interactable { public string m_name = "$piece_vc_freydis"; public Transform m_spawnPoint; public GameObject m_visualObject; public GameObject m_ravenSpeak; public GameObject m_workingEffect; public GameObject m_notEmptyEffect; public float m_secPerUnit = 60f; public int m_maxLevel = 5; public List<FreydisResource> m_collectionRewards = new List<FreydisResource>(); public EffectList m_spawnEffect = new EffectList(); public float m_doubleDropChance = 50f; public float m_tripleDropChance = 25f; public string m_extractText = "$piece_vc_freydis_findings"; public string m_collectingText = "$piece_vc_freydis_collecting"; public string m_readyText = "$piece_vc_freydis_ready"; public string m_fullText = "$piece_vc_freydis_full"; private ZNetView m_nview; private bool m_configured = false; public void Awake() { m_nview = ((Component)this).GetComponent<ZNetView>(); if ((Object)(object)m_nview == (Object)null) { return; } if (!m_configured) { ConfigureFromConfig(); m_configured = true; } if ((Object)(object)m_spawnPoint == (Object)null) { m_spawnPoint = ((Component)this).transform; } if (m_nview.GetZDO() != null) { if (m_nview.IsOwner() && m_nview.GetZDO().GetLong(ZDOVars.s_lastTime, 0L) == 0) { m_nview.GetZDO().Set(ZDOVars.s_lastTime, ZNet.instance.GetTime().Ticks); } m_nview.Register("RPC_Extract", (Action<long>)RPC_Extract); m_nview.Register("RPC_UpdateEffects", (Action<long>)RPC_UpdateEffects); ((MonoBehaviour)this).InvokeRepeating("UpdateTick", 2f, 5f); } } private void ConfigureFromConfig() { m_secPerUnit = FreydisCooldownTime?.Value ?? 60f; m_maxLevel = FreydisMaxStoredCollections?.Value ?? 5; m_doubleDropChance = FreydisDoubleDropChance?.Value ?? 50f; m_tripleDropChance = FreydisTripleDropChance?.Value ?? 25f; m_collectionRewards.Clear(); string text = FreydisDropTable?.Value ?? ""; if (string.IsNullOrWhiteSpace(text)) { return; } string[] array = text.Split(new char[1] { ',' }); string[] array2 = array; foreach (string text2 in array2) { string[] array3 = text2.Trim().Split(new char[1] { ':' }); if (array3.Length == 4 && int.TryParse(array3[1], out var result) && int.TryParse(array3[2], out var result2) && float.TryParse(array3[3], out var result3)) { m_collectionRewards.Add(new FreydisResource { itemName = array3[0].Trim(), minAmount = result, maxAmount = result2, dropChance = result3 }); } } } public string GetHoverText() { int level = GetLevel(); string text = Localization.instance.Localize(m_name); if (level > 0) { string text2 = ((level == 1) ? "$piece_vc_freydis_singular" : "$piece_vc_freydis_plural"); string text3 = Localization.instance.Localize(text2); text3 = text3.Replace("{0}", level.ToString()); text = text + " (" + text3 + ")"; text = text + "\n[<color=yellow><b>$KEY_Use</b></color>] " + Localization.instance.Localize(m_extractText); } else { text = text + " ( " + Localization.instance.Localize(GetStatusText()) + " )"; } return Localization.instance.Localize(text); } public string GetHoverName() { return Localization.instance.Localize(m_name); } public bool Interact(Humanoid character, bool repeat, bool alt) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (repeat) { return false; } if (!PrivateArea.CheckAccess(((Component)this).transform.position, 0f, true, false)) { return true; } if (GetLevel() > 0) { Extract(); return true; } return false; } public string GetStatusText() { int level = GetLevel(); if (level >= m_maxLevel) { return m_fullText; } if (level > 0) { return m_readyText; } return m_collectingText; } public bool UseItem(Humanoid user, ItemData item) { return false; } public void Extract() { m_nview.InvokeRPC("RPC_Extract", Array.Empty<object>()); } public void RPC_Extract(long caller) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) int level = GetLevel(); if (level > 0 && m_collectionRewards.Count > 0) { m_spawnEffect.Create(m_spawnPoint.position, Quaternion.identity, (Transform)null, 1f, -1); for (int i = 0; i < level; i++) { PerformLootRoll(); } ResetLevel(); m_nview.InvokeRPC(ZNetView.Everybody, "RPC_UpdateEffects", Array.Empty<object>()); } } private void PerformLootRoll() { int num = 1; float num2 = Random.Range(0f, 100f); if (num2 <= m_tripleDropChance) { num = 3; } else if (num2 <= m_tripleDropChance + m_doubleDropChance) { num = 2; } List<(int, float)> list = new List<(int, float)>(); for (int i = 0; i < m_collectionRewards.Count; i++) { list.Add((i, m_collectionRewards[i].dropChance)); } for (int j = 0; j < num; j++) { if (list.Count <= 0) { break; } int index = SelectWeightedItem(list); FreydisResource resource = m_collectionRewards[list[index].Item1]; list.RemoveAt(index); SpawnItem(resource); } } private int SelectWeightedItem(List<(int index, float weight)> items) { float num = 0f; foreach (var item in items) { num += item.weight; } float num2 = Random.Range(0f, num); float num3 = 0f; for (int i = 0; i < items.Count; i++) { num3 += items[i].weight; if (num2 <= num3) { return i; } } return items.Count - 1; } private void SpawnItem(FreydisResource resource) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(resource.itemName); if (!((Object)(object)itemPrefab == (Object)null)) { int stack = Random.Range(resource.minAmount, resource.maxAmount + 1); Vector3 val = m_spawnPoint.position + Random.insideUnitSphere * 0.3f; GameObject val2 = Object.Instantiate<GameObject>(itemPrefab, val, Quaternion.identity); ItemDrop component = val2.GetComponent<ItemDrop>(); if (component?.m_itemData != null) { component.m_itemData.m_stack = stack; component.Save(); } } } public float GetTimeSinceLastUpdate() { ZNetView nview = m_nview; if (((nview != null) ? nview.GetZDO() : null) == null) { return 0f; } DateTime dateTime = new DateTime(m_nview.GetZDO().GetLong(ZDOVars.s_lastTime, ZNet.instance.GetTime().Ticks)); DateTime time = ZNet.instance.GetTime(); TimeSpan timeSpan = time - dateTime; m_nview.GetZDO().Set(ZDOVars.s_lastTime, time.Ticks); return (timeSpan.TotalSeconds < 0.0) ? 0f : ((float)timeSpan.TotalSeconds); } public void ResetLevel() { m_nview.GetZDO().Set(ZDOVars.s_level, 0, false); } public void IncreaseLevel(int amount) { int level = GetLevel(); int num = Mathf.Clamp(level + amount, 0, m_maxLevel); m_nview.GetZDO().Set(ZDOVars.s_level, num, false); } public int GetLevel() { ZNetView nview = m_nview; if (((nview != null) ? nview.GetZDO() : null) == null) { return 0; } return m_nview.GetZDO().GetInt(ZDOVars.s_level, 0); } public void UpdateTick() { ZNetView nview = m_nview; if (((nview != null) ? nview.GetZDO() : null) == null || !m_nview.IsValid()) { return; } if (!m_nview.IsOwner()) { m_nview.ClaimOwnership(); } if (m_nview.IsOwner()) { float timeSinceLastUpdate = GetTimeSinceLastUpdate(); int level = GetLevel(); if (level < m_maxLevel) { float @float = m_nview.GetZDO().GetFloat(ZDOVars.s_product, 0f); @float += timeSinceLastUpdate; if (@float >= m_secPerUnit) { int amount = (int)(@float / m_secPerUnit); IncreaseLevel(amount); @float = 0f; } m_nview.GetZDO().Set(ZDOVars.s_product, @float); } } UpdateEffects(); } public void RPC_UpdateEffects(long caller) { UpdateEffects(); } public void UpdateEffects() { int level = GetLevel(); if ((Object)(object)m_visualObject != (Object)null) { m_visualObject.SetActive(level > 0); } if ((Object)(object)m_ravenSpeak != (Object)null) { m_ravenSpeak.SetActive(level > 0); } if ((Object)(object)m_notEmptyEffect != (Object)null) { m_notEmptyEffect.SetActive(level >= m_maxLevel); } if ((Object)(object)m_workingEffect != (Object)null) { m_workingEffect.SetActive(level == 0); } } } [HarmonyPatch(typeof(ZNetScene), "Awake")] public static class FreydisPatch { private static void Postfix(ZNetScene __instance) { //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Expected O, but got Unknown //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Expected O, but got Unknown if (__instance?.m_prefabs == null) { return; } GameObject prefab = __instance.GetPrefab("VC_Freydis"); if (!((Object)(object)prefab == (Object)null)) { Piece componentInChildren = prefab.GetComponentInChildren<Piece>(); GameObject val = (((Object)(object)componentInChildren != (Object)null) ? ((Component)componentInChildren).gameObject : prefab); Component component = val.GetComponent("RavenInteraction"); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } FreydisCollector freydisCollector = val.GetComponent<FreydisCollector>(); if ((Object)(object)freydisCollector == (Object)null) { freydisCollector = val.AddComponent<FreydisCollector>(); } Transform val2 = FindChildRecursive(val.transform, "SpawnPoint"); freydisCollector.m_spawnPoint = (((Object)(object)val2 != (Object)null) ? val2 : val.transform); Transform val3 = FindChildRecursive(val.transform, "Raven"); if ((Object)(object)val3 != (Object)null) { freydisCollector.m_visualObject = ((Component)val3).gameObject; } Transform val4 = FindChildRecursive(val.transform, "RavenSpeak"); if ((Object)(object)val4 != (Object)null) { freydisCollector.m_ravenSpeak = ((Component)val4).gameObject; } Transform val5 = FindChildRecursive(val.transform, "WorkingEffect"); Transform val6 = FindChildRecursive(val.transform, "NotEmptyEffect"); if ((Object)(object)val5 != (Object)null) { freydisCollector.m_workingEffect = ((Component)val5).gameObject; } if ((Object)(object)val6 != (Object)null) { freydisCollector.m_notEmptyEffect = ((Component)val6).gameObject; } if ((Object)(object)freydisCollector.m_workingEffect == (Object)null) { freydisCollector.m_workingEffect = new GameObject("WorkingEffect"); freydisCollector.m_workingEffect.transform.SetParent(val.transform); freydisCollector.m_workingEffect.SetActive(false); } if ((Object)(object)freydisCollector.m_notEmptyEffect == (Object)null) { freydisCollector.m_notEmptyEffect = new GameObject("NotEmptyEffect"); freydisCollector.m_notEmptyEffect.transform.SetParent(val.transform); freydisCollector.m_notEmptyEffect.SetActive(false); } } } private static Transform FindChildRecursive(Transform parent, string name) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown Transform val = parent.Find(name); if ((Object)(object)val != (Object)null) { return val; } foreach (Transform item in parent) { Transform parent2 = item; val = FindChildRecursive(parent2, name); if ((Object)(object)val != (Object)null) { return val; } } return null; } } [HarmonyPatch(typeof(ZNetScene), "Awake")] public static class BirdMeatDropsPatch { private static bool hasRun = false; private static readonly string[] BirdPrefabs = new string[3] { "Crow", "Seagal", "AshCrow" }; [HarmonyPostfix] [HarmonyPriority(0)] public static void Postfix(ZNetScene __instance) { if (!hasRun) { hasRun = true; string[] birdPrefabs = BirdPrefabs; foreach (string creatureName in birdPrefabs) { AddBirdMeatDrop(__instance, creatureName); } } } private static void AddBirdMeatDrop(ZNetScene zNetScene, string creatureName) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) GameObject prefab = zNetScene.GetPrefab(creatureName); if ((Object)(object)prefab == (Object)null) { ValheimCuisineLogger.LogWarning((object)("[BirdMeatDrops] " + creatureName + " prefab not found.")); return; } DropOnDestroyed component = prefab.GetComponent<DropOnDestroyed>(); if ((Object)(object)component == (Object)null) { ValheimCuisineLogger.LogWarning((object)("[BirdMeatDrops] " + creatureName + " has no DropOnDestroyed component.")); return; } ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab("VC_BirdMeat") : null); if ((Object)(object)val == (Object)null) { ValheimCuisineLogger.LogWarning((object)"[BirdMeatDrops] VC_BirdMeat prefab not found in ObjectDB."); return; } DropTable dropWhenDestroyed = component.m_dropWhenDestroyed; dropWhenDestroyed.m_drops.RemoveAll((DropData drop) => (Object)(object)drop.m_item != (Object)null && ((Object)drop.m_item).name == "VC_BirdMeat"); for (int i = 0; i < dropWhenDestroyed.m_drops.Count; i++) { DropData val2 = dropWhenDestroyed.m_drops[i]; if ((Object)(object)val2.m_item != (Object)null && ((Object)val2.m_item).name == "Feathers") { val2.m_weight = 10000f; dropWhenDestroyed.m_drops[i] = val2; break; } } dropWhenDestroyed.m_drops.Add(new DropData { m_item = val, m_stackMin = 1, m_stackMax = 1, m_weight = 3f, m_dontScale = false }); dropWhenDestroyed.m_drops.Add(new DropData { m_item = val, m_stackMin = 2, m_stackMax = 2, m_weight = 1f, m_dontScale = false }); dropWhenDestroyed.m_dropMin = 2; dropWhenDestroyed.m_dropMax = 2; dropWhenDestroyed.m_dropChance = 1f; dropWhenDestroyed.m_oneOfEach = true; ValheimCuisineLogger.LogInfo((object)("[BirdMeatDrops] Successfully configured " + creatureName + " to drop Feathers + VC_BirdMeat (75% → 1 meat, 25% → 2 meats)")); } } [HarmonyPatch(typeof(Player), "Update")] public static class LoxMilkingUpdatePatch { private const string BUCKET_PREFAB_NAME = "VC_MilkinBucket"; private const string MILK_PREFAB_NAME = "VC_LoxMilk"; private const string COOLDOWN_SE_NAME = "SE_LoxMilkedCooldown"; private static float m_lastMilkAttempt; private static void Postfix(Player __instance) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) if (MilkingKey == null || !MilkingKey.Value.IsKeyDown() || Time.time - m_lastMilkAttempt < 0.5f) { return; } m_lastMilkAttempt = Time.time; if (!HasBucketEquipped(__instance)) { return; } GameObject hoverObject = ((Humanoid)__instance).GetHoverObject(); if ((Object)(object)hoverObject == (Object)null) { return; } Tameable componentInParent = hoverObject.GetComponentInParent<Tameable>(); if ((Object)(object)componentInParent == (Object)null) { return; } Character character = componentInParent.m_character; if (!((Object)(object)character == (Object)null) && ((Object)character).name.StartsWith("Lox") && componentInParent.IsTamed() && !(Vector3.Distance(((Component)__instance).transform.position, ((Component)character).transform.position) > 3f)) { if (RequireBreeding != null && RequireBreeding.Value && !HasLoxBred(componentInParent)) { ((Character)__instance).Message((MessageType)2, Localization.instance.Localize("$lox_milking_nobaby"), 0, (Sprite)null); } else if (HasMilkCooldown(character)) { ((Character)__instance).Message((MessageType)2, Localization.instance.Localize("$lox_milking_cooldown"), 0, (Sprite)null); } else { MilkLox(__instance, character, componentInParent); } } } private static bool HasBucketEquipped(Player player) { ItemData rightItem = ((Humanoid)player).GetRightItem(); if (rightItem == null) { return false; } return (Object)(object)rightItem.m_dropPrefab != (Object)null && ((Object)rightItem.m_dropPrefab).name == "VC_MilkinBucket"; } private static bool HasLoxBred(Tameable tameable) { ZNetView nview = tameable.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return false; } int @int = zDO.GetInt("LoxBirthCount", 0); if (@int > 0) { ValheimCuisineLogger.LogInfo((object)$"Lox has birth count: {@int}"); return true; } float @float = zDO.GetFloat("pregnant", 0f); if (@float > 0f) { ValheimCuisineLogger.LogInfo((object)$"Lox is currently pregnant: {@float}"); return true; } if (zDO.GetBool("LoxHasBeenPregnant", false)) { ValheimCuisineLogger.LogInfo((object)"Lox has been pregnant before"); return true; } ValheimCuisineLogger.LogInfo((object)"Lox has never bred"); return false; } private static bool HasMilkCooldown(Character character) { return character.GetSEMan().HaveStatusEffect(StringExtensionMethods.GetStableHashCode("SE_LoxMilkedCooldown")); } private static void MilkLox(Player player, Character lox, Tameable tameable) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) ApplyCooldown(lox); GiveMilk(player); PlayMilkingEffects(((Component)lox).transform.position); ((Character)player).Message((MessageType)2, Localization.instance.Localize("$lox_milking_success"), 0, (Sprite)null); } private static void ApplyCooldown(Character lox) { StatusEffect statusEffect = ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("SE_LoxMilkedCooldown")); float ttl = CooldownDuration?.Value ?? 1800f; if ((Object)(object)statusEffect != (Object)null) { statusEffect.m_ttl = ttl; lox.GetSEMan().AddStatusEffect(statusEffect, false, 0, 0f); return; } SE_Stats val = ScriptableObject.CreateInstance<SE_Stats>(); ((Object)val).name = "SE_LoxMilkedCooldown"; ((StatusEffect)val).m_name = "Recently Milked"; ((StatusEffect)val).m_ttl = ttl; ((StatusEffect)val).m_tooltip = "This Lox was recently milked"; lox.GetSEMan().AddStatusEffect((StatusEffect)(object)val, true, 0, 0f); } private static void GiveMilk(Player player) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("VC_LoxMilk"); if ((Object)(object)itemPrefab == (Object)null) { Debug.LogError((object)"Could not find milk prefab: VC_LoxMilk"); ((Character)player).Message((MessageType)2, Localization.instance.Localize("$lox_milking_error"), 0, (Sprite)null); return; } int num = MilkAmount?.Value ?? 5; ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null) { Debug.LogError((object)"Milk prefab has no ItemDrop component"); return; } ItemData val = component.m_itemData.Clone(); val.m_stack = num; if (!((Humanoid)player).GetInventory().AddItem(val)) { ItemDrop.DropItem(val, num, ((Component)player).transform.position + ((Component)player).transform.forward * 0.5f + Vector3.up * 0.5f, ((Component)player).transform.rotation); ((Character)player).Message((MessageType)2, Localization.instance.Localize("$lox_milking_inventory_full"), 0, (Sprite)null); } } private static void PlayMilkingEffects(Vector3 position) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) GameObject prefab = ZNetScene.instance.GetPrefab("VC_sfx_LoxMilking"); if ((Object)(object)prefab != (Object)null) { Object.Instantiate<GameObject>(prefab, position, Quaternion.identity); } GameObject prefab2 = ZNetScene.instance.GetPrefab("vfx_lootspawn"); if ((Object)(object)prefab2 != (Object)null) { Object.Instantiate<GameObject>(prefab2, position + Vector3.up * 1f, Quaternion.identity); } } } [HarmonyPatch(typeof(Tameable), "GetHoverText")] public static class LoxMilkingHoverTextPatch { private const string BUCKET_PREFAB_NAME = "VC_MilkinBucket"; private const string COOLDOWN_SE_NAME = "SE_LoxMilkedCooldown"; private static void Postfix(Tameable __instance, ref string __result) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) if (MilkingKey == null) { return; } Character character = __instance.m_character; if ((Object)(object)character == (Object)null || !((Object)character).name.StartsWith("Lox") || !__instance.IsTamed()) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } ItemData rightItem = ((Humanoid)localPlayer).GetRightItem(); if (rightItem == null) { return; } GameObject dropPrefab = rightItem.m_dropPrefab; if (((dropPrefab != null) ? ((Object)dropPrefab).name : null) != "VC_MilkinBucket") { return; } KeyboardShortcut value = MilkingKey.Value; KeyCode mainKey = ((KeyboardShortcut)(ref value)).MainKey; string text = ((object)(KeyCode)(ref mainKey)).ToString(); if (RequireBreeding != null && RequireBreeding.Value) { ZNetView nview = __instance.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid()) { ZDO zDO = nview.GetZDO(); if (zDO != null) { int @int = zDO.GetInt("LoxBirthCount", 0); float @float = zDO.GetFloat("pregnant", 0f); bool @bool = zDO.GetBool("LoxHasBeenPregnant", false); if (@int == 0 && @float == 0f && !@bool) { __result = __result + "\n<color=orange>" + Localization.instance.Localize("$lox_milking_hover_nobaby") + "</color>"; return; } } } } if (character.GetSEMan().HaveStatusEffect(StringExtensionMethods.GetStableHashCode("SE_LoxMilkedCooldown"))) { __result = __result + "\n<color=orange>" + Localization.instance.Localize("$lox_milking_hover_cooldown") + "</color>"; return; } __result = __result + "\n[<color=yellow><b>" + text + "</b></color>] " + Localization.instance.Localize("$lox_milking_hover_action"); } } [HarmonyPatch(typeof(Procreation), "MakePregnant")] public static class ProcreationPregnancyTracker { private static void Postfix(Procreation __instance) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) Character component = ((Component)__instance).GetComponent<Character>(); if ((Object)(object)component == (Object)null || !((Object)component).name.StartsWith("Lox")) { return; } ZNetView component2 = ((Component)__instance).GetComponent<ZNetView>(); if (!((Object)(object)component2 == (Object)null) && component2.IsValid()) { ZDO zDO = component2.GetZDO(); if (zDO != null) { zDO.Set("LoxHasBeenPregnant", true); ValheimCuisineLogger.LogInfo((object)$"Lox {component.GetZDOID()} became pregnant"); } } } } [HarmonyPatch(typeof(CookingStation))] public static class CookingStationPatches { private static HashSet<string> noOvercookList = new HashSet<string>(); private static HashSet<string> multiFuelList = new HashSet<string>(); private static HashSet<string> passiveCookingList = new HashSet<string>(); private static Dictionary<string, List<string>> prefabFuelMap = new Dictionary<string, List<string>>(); private const string ZDO_FUEL_TYPE = "VC_CurrentFuelType"; public static void Initialize() { SmokehouseNoOvercookPrefabs.SettingChanged += delegate { RefreshNoOvercookList(); }; SmokehouseMultiFuelPrefabs.SettingChanged += delegate { RefreshMultiFuelList(); }; SmokehouseAllowedFuels.SettingChanged += delegate { RefreshFuelMaps(); }; PassiveCookingPrefabs.SettingChanged += delegate { RefreshPassiveCookingList(); }; RefreshNoOvercookList(); RefreshMultiFuelList(); RefreshFuelMaps(); RefreshPassiveCookingList(); } private static void RefreshNoOvercookList() { noOvercookList.Clear(); string[] array = SmokehouseNoOvercookPrefabs.Value.Split(new char[1] { ',' }); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (!string.IsNullOrEmpty(text2)) { noOvercookList.Add(text2); } } } private static void RefreshMultiFuelList() { multiFuelList.Clear(); string[] array = SmokehouseMultiFuelPrefabs.Value.Split(new char[1] { ',' }); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (!string.IsNullOrEmpty(text2)) { multiFuelList.Add(text2); } } } private static void RefreshFuelMaps() { prefabFuelMap.Clear(); List<string> value = (from f in SmokehouseAllowedFuels.Value.Split(new char[1] { ',' }) select f.Trim() into f where !string.IsNullOrEmpty(f) select f).ToList(); foreach (string multiFuel in multiFuelList) { prefabFuelMap[multiFuel] = value; } } private static void RefreshPassiveCookingList() { passiveCookingList.Clear(); string[] array = PassiveCookingPrefabs.Value.Split(new char[1] { ',' }); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (!string.IsNullOrEmpty(text2)) { passiveCookingList.Add(text2); } } } private static bool IsNoOvercookStation(CookingStation station) { if ((Object)(object)station == (Object)null) { return false; } string prefabName = Utils.GetPrefabName(((Component)station).gameObject); return noOvercookList.Contains(prefabName); } private static bool IsMultiFuelStation(CookingStation station) { if ((Object)(object)station == (Object)null) { return false; } string prefabName = Utils.GetPrefabName(((Component)station).gameObject); return multiFuelList.Contains(prefabName); } private static bool IsPassiveCookingStation(CookingStation station) { if ((Object)(object)station == (Object)null) { return false; } string prefabName = Utils.GetPrefabName(((Component)station).gameObject); return passiveCookingList.Contains(prefabName); } private static bool IsModStation(CookingStation station) { return IsNoOvercookStation(station) || IsMultiFuelStation(station) || IsPassiveCookingStation(station); } private static List<string> GetAllowedFuels(CookingStation station) { if ((Object)(object)station == (Object)null) { return null; } string prefabName = Utils.GetPrefabName(((Component)station).gameObject); return prefabFuelMap.ContainsKey(prefabName) ? prefabFuelMap[prefabName] : null; } private static bool IsStationFullyInitialized(CookingStation station) { if ((Object)(object)station == (Object)null) { return false; } if ((Object)(object)station.m_nview == (Object)null) { return false; } if (!station.m_nview.IsValid()) { return false; } if (station.m_slots == null) { return false; } if (station.m_conversion == null) { return false; } return true; } [HarmonyPrefix] [HarmonyPatch("UpdateCooking")] [HarmonyPriority(800)] public static bool UpdateCooking_Complete_Prefix(CookingStation __instance) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Invalid comparison between Unknown and I4 //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) if (!IsModStation(__instance)) { return true; } if (!IsStationFullyInitialized(__instance)) { return false; } bool flag = IsPassiveCookingStation(__instance); bool flag2 = (__instance.m_requireFire && __instance.IsFireLit()) || (__instance.m_useFuel && __instance.GetFuel() > 0f); if (__instance.m_nview.IsOwner()) { float deltaTime = __instance.GetDeltaTime(); if (flag2 || flag) { __instance.UpdateFuel(deltaTime); string text = default(string); float num = default(float); Status val = default(Status); for (int i = 0; i < __instance.m_slots.Length; i++) { __instance.GetSlot(i, ref text, ref num, ref val); if (string.IsNullOrEmpty(text) || (int)val == 2) { continue; } ItemConversion itemConversion = __instance.GetItemConversion(text); if (itemConversion == null) { __instance.SetSlot(i, "", 0f, (Status)0); } else { if ((Object)(object)itemConversion.m_from == (Object)null || (Object)(object)itemConversion.m_to == (Object)null) { continue; } num += deltaTime; if (IsNoOvercookStation(__instance)) { if (num > itemConversion.m_cookTime && text == ((Object)itemConversion.m_from).name) { __instance.m_doneEffect.Create(__instance.m_slots[i].position, Quaternion.identity, (Transform)null, 1f, -1); __instance.SetSlot(i, ((Object)itemConversion.m_to).name, itemConversion.m_cookTime, (Status)1); } else { __instance.SetSlot(i, text, num, (Status)0); } } else if (num > itemConversion.m_cookTime * 2f) { if (__instance.m_overcookedEffect != null && (Object)(object)__instance.m_overCookedItem != (Object)null) { __instance.m_overcookedEffect.Create(__instance.m_slots[i].position, Quaternion.identity, (Transform)null, 1f, -1); __instance.SetSlot(i, ((Object)__instance.m_overCookedItem).name, num, (Status)2); } } else if (num > itemConversion.m_cookTime && text == ((Object)itemConversion.m_from).name) { __instance.m_doneEffect.Create(__instance.m_slots[i].position, Quaternion.identity, (Transform)null, 1f, -1); __instance.SetSlot(i, ((Object)itemConversion.m_to).name, num, (Status)1); } else { __instance.SetSlot(i, text, num, (Status)0); } } } } } __instance.UpdateVisual(flag2); return false; } [HarmonyPrefix] [HarmonyPatch("IsItemDone")] public static bool IsItemDone_Prefix(CookingStation __instance, string itemName, ref bool __result) { if ((Object)(object)__instance == (Object)null || string.IsNullOrEmpty(itemName)) { __result = false; return false; } if (__instance.m_conversion == null) { __result = false; return false; } if ((Object)(object)__instance.m_overCookedItem == (Object)null && IsNoOvercookStation(__instance)) { ItemConversion itemConversion = __instance.GetItemConversion(itemName); if (itemConversion == null) { __result = false; return false; } if ((Object)(object)itemConversion.m_to == (Object)null) { __result = false; return false; } if (itemName == ((Object)itemConversion.m_to).name) { __result = true; return false; } __result = false; return false; } return true; } [HarmonyPrefix] [HarmonyPatch("OnAddFuelSwitch")] public static bool OnAddFuelSwitch_Prefix(CookingStation __instance, Switch sw, Humanoid user, ItemData item, ref bool __result) { if (!IsMultiFuelStation(__instance)) { return true; } List<string> allowedFuels = GetAllowedFuels(__instance); if (allowedFuels == null || allowedFuels.Count == 0) { return true; } if (__instance.GetFuel() > (float)(__instance.m_maxFuel - 1)) { ((Character)user).Message((MessageType)2, "$msg_itsfull", 0, (Sprite)null); __result = false; return false; } if (item == null) { foreach (ItemData allItem in user.GetInventory().GetAllItems()) { if ((Object)(object)allItem.m_dropPrefab != (Object)null) { string name = ((Object)allItem.m_dropPrefab).name; if (allowedFuels.Contains(name)) { item = allItem; break; } } } if (item == null) { ((Character)user).Message((MessageType)2, "$msg_noprocessableitems", 0, (Sprite)null); __result = false; return false; } } string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : ""); if (!allowedFuels.Contains(text)) { ((Character)user).Message((MessageType)2, "$msg_wrongitem", 0, (Sprite)null); __result = false; return false; } if (!user.GetInventory().HaveItem(item.m_shared.m_name, true)) { ((Character)user).Message((MessageType)2, "$msg_donthaveany " + item.m_shared.m_name, 0, (Sprite)null); __result = false; return false; } ((Character)user).Message((MessageType)2, "$msg_added " + item.m_shared.m_name, 0, (Sprite)null); user.GetInventory().RemoveItem(item.m_shared.m_name, 1, -1, true); if ((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid()) { __instance.m_nview.GetZDO().Set("VC_CurrentFuelType", text); } __instance.m_nview.InvokeRPC("RPC_AddFuel", Array.Empty<object>()); __result = true; return false; } [HarmonyPostfix] [HarmonyPatch("OnHoverFuelSwitch")] public static void OnHoverFuelSwitch_Postfix(CookingStation __instance, ref string __result) { if (!IsMultiFuelStation(__instance)) { return; } List<string> allowedFuels = GetAllowedFuels(__instance); if (allowedFuels == null || allowedFuels.Count == 0) { return; } float fuel = __instance.GetFuel(); List<string> list = new List<string>(); foreach (string item in allowedFuels) { ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab(item) : null); if ((Object)(object)val != (Object)null) { ItemDrop component = val.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null) { list.Add(Localization.instance.Localize(component.m_itemData.m_shared.m_name)); } } } string text = string.Join(", ", list); __result = Localization.instance.Localize($"{__instance.m_name} ($piece_fire_fuel {Mathf.Ceil(fuel)}/{__instance.m_maxFuel})\n" + "[<color=yellow><b>$KEY_Use</b></color>] $piece_smelter_add " + text); } [HarmonyPostfix] [HarmonyPatch("TryGetItems")] public static void TryGetItems_Postfix(CookingStation __instance, Switch switchRef, ref List<string> items, ref bool __result) { if (__result && IsMultiFuelStation(__instance) && (Object)(object)switchRef == (Object)(object)__instance.m_addFuelSwitch) { List<string> allowedFuels = GetAllowedFuels(__instance); if (allowedFuels != null && allowedFuels.Count > 0) { items.Clear(); items.AddRange(allowedFuels); } } } [HarmonyPrefix] [HarmonyPatch(typeof(WearNTear), "Destroy")] public static void WearNTear_Destroy_Prefix(WearNTear __instance) { if ((Object)(object)__instance == (Object)null) { return; } CookingStation component = ((Component)__instance).GetComponent<CookingStation>(); if ((Object)(object)component == (Object)null || !IsMultiFuelStation(component)) { return; } float fuel = component.GetFuel(); if (fuel <= 0f || (Object)(object)component.m_nview == (Object)null || !component.m_nview.IsValid()) { return; } string @string = component.m_nview.GetZDO().GetString("VC_CurrentFuelType", ""); if (string.IsNullOrEmpty(@string)) { return; } ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab(@string) : null); if (!((Object)(object)val == (Object)null)) { ItemDrop component2 = val.GetComponent<ItemDrop>(); if ((Object)(object)component2 != (Object)null) { component.m_fuelItem = component2; } } } } [HarmonyPatch(typeof(Fermenter), "GetHoverText")] public static class Fermenter_GetHoverText_Patch { [HarmonyPostfix] public static void Postfix(Fermenter __instance, ref string __result) { if (IsDvergrCauldron(__instance)) { string oldValue = Localization.instance.Localize("$piece_fermenter_fermenting"); string newValue = Localization.instance.Localize("$piece_vc_dvergrcauldron_processing"); __result = __result.Replace(oldValue, newValue); } } private static bool IsDvergrCauldron(Fermenter fermenter) { if ((Object)(object)((fermenter != null) ? ((Component)fermenter).gameObject : null) == (Object)null) { return false; } string text = ((Object)((Component)fermenter).gameObject).name.Replace("(Clone)", "").Trim(); return text == "VC_DvergrCauldron"; } } [HarmonyPatch(typeof(Projectile), "SpawnOnHit")] public static class Projectile_CreatureBomb_SpawnOnHit_Patch { private enum BombType { None, Niflheimr, Bloodmoon, Horde, Custom } [HarmonyPrefix] public static bool Prefix(Projectile __instance, GameObject go, Collider collider, Vector3 normal) { //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<bool> enableCreatureBombs = EnableCreatureBombs; if (enableCreatureBombs == null || !enableCreatureBombs.Value) { return true; } BombType bombType = GetBombType(__instance); if (bombType == BombType.None) { return true; } ValheimCuisineLogger.LogInfo((object)$"[CreatureBomb] Detected {bombType} bomb, spawning creatures..."); if (__instance.m_groundHitOnly && ((Object)(object)go == (Object)null || (Object)(object)go.GetComponent<Heightmap>() == (Object)null)) { ValheimCuisineLogger.LogWarning((object)"[CreatureBomb] Ground hit only restriction failed"); return true; } if (__instance.m_staticHitOnly && (((Object)(object)collider != (Object)null && (Object)(object)collider.attachedRigidbody != (Object)null) || ((Object)(object)go != (Object)null && go.GetComponent<IDestructible>() != null))) { ValheimCuisineLogger.LogWarning((object)"[CreatureBomb] Static hit only restriction failed"); return true; } Vector3 val = ((Component)__instance).transform.position + ((Component)__instance).transform.TransformDirection(__instance.m_spawnOffset); ValheimCuisineLogger.LogInfo((object)$"[CreatureBomb] Spawn center: {val}"); SpawnCreaturesForBomb(bombType, val); EffectList spawnOnHitEffects = __instance.m_spawnOnHitEffects; if (spawnOnHitEffects != null) { spawnOnHitEffects.Create(val, Quaternion.identity, (Transform)null, 1f, -1); } ValheimCuisineLogger.LogInfo((object)$"[CreatureBomb] {bombType} bomb completed!"); return false; } private static BombType GetBombType(Projectile projectile) { if ((Object)(object)projectile == (Object)null) { return BombType.None; } string text = ((Object)((Component)projectile).gameObject).name.Replace("(Clone)", "").Trim(); if (text.Equals("VC_NiflheimrBomb_projectile", StringComparison.OrdinalIgnoreCase)) { return BombType.Niflheimr; } if (text.Equals("VC_BloodmoonBomb_projectile", StringComparison.OrdinalIgnoreCase)) { return BombType.Bloodmoon; } if (text.Equals("VC_HordeBomb_projectile", StringComparison.OrdinalIgnoreCase)) { return BombType.Horde; } if (text.Equals("VC_CustomBomb_projectile", StringComparison.OrdinalIgnoreCase)) { return BombType.Custom; } if ((Object)(object)projectile.m_nview != (Object)null && projectile.m_nview.IsValid()) { ZDO zDO = projectile.m_nview.GetZDO(); if (zDO != null) { int prefab = zDO.GetPrefab(); if (prefab == StringExtensionMethods.GetStableHashCode("VC_NiflheimrBomb_projectile")) { return BombType.Niflheimr; } if (prefab == StringExtensionMethods.GetStableHashCode("VC_BloodmoonBomb_projectile")) { return BombType.Bloodmoon; } if (prefab == StringExtensionMethods.GetStableHashCode("VC_HordeBomb_projectile")) { return BombType.Horde; } if (prefab == StringExtensionMethods.GetStableHashCode("VC_CustomBomb_projectile")) { return BombType.Custom; } } } return BombType.None; } private static void SpawnCreaturesForBomb(BombType bombType, Vector3 center) { //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: 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_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) if (1 == 0) { } string text = bombType switch { BombType.Niflheimr => NiflheimrBombCreatures?.Value, BombType.Bloodmoon => BloodmoonBombCreatures?.Value, BombType.Horde => HordeBombCreatures?.Value, BombType.Custom => CustomBombCreatures?.Value, _ => null, }; if (1 == 0) { } string text2 = text; if (1 == 0) { } bool flag = bombType switch { BombType.Niflheimr => NiflheimrBombRandomizeLevels?.Value ?? true, BombType.Bloodmoon => BloodmoonBombRandomizeLevels?.Value ?? true, BombType.Horde => HordeBombRandomizeLevels?.Value ?? true, BombType.Custom => CustomBombRandomizeLevels?.Value ?? true, _ => false, }; if (1 == 0) { } bool flag2 = flag; ValheimCuisineLogger.LogInfo((object)$"[CreatureBomb] Config: '{text2}', Randomize: {flag2}"); if (string.IsNullOrWhiteSpace(text2)) { ValheimCuisineLogger.LogWarning((object)$"[CreatureBomb] No creatures configured for {bombType}!"); return; } if ((Object)(object)ZNetScene.instance == (Object)null) { ValheimCuisineLogger.LogError((object)"[CreatureBomb] ZNetScene.instance is null!"); return; } List<(string, int, int)> list = ParseCreatureList(text2); float radius = Mathf.Clamp(BombSpawnRadius?.Value ?? 2.5f, 0f, 10f); bool flag3 = BombSpawnRandomRotation?.Value ?? true; int num = 0; foreach (var item5 in list) { int item = item5.Item2; num += item; } ValheimCuisineLogger.LogInfo((object)$"[CreatureBomb] Spawning {num} total creatures at {center}"); int num2 = 0; foreach (var item6 in list) { string item2 = item6.Item1; int item3 = item6.Item2; int item4 = item6.Item3; GameObject prefab = ZNetScene.instance.GetPrefab(item2); if ((Object)(object)prefab == (Object)null) { ValheimCuisineLogger.LogWarning((object)("[CreatureBomb] Prefab not found: " + item2)); continue; } for (int i = 0; i < item3; i++) { Vector3 val = CalculateSpawnPosition(center, radius, num2, num); Quaternion val2 = (flag3 ? Quaternion.Euler(0f, Random.Range(0f, 360f), 0f) : Quaternion.identity); int num3 = item4; if (flag2 && item4 > 1) { num3 = Random.Range(1, item4 + 1); } GameObject val3 = Object.Instantiate<GameObject>(prefab, val, val2); if ((Object)(object)val3 != (Object)null) { ZNetView component = val3.GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && num3 > 1) { ZDO zDO = component.GetZDO(); if (zDO != null) { zDO.Set(ZDOVars.s_level, num3, false); } } MonsterAI component2 = val3.GetComponent<MonsterAI>(); if ((Object)(object)component2 != (Object)null) { component2.m_sleeping = false; ValheimCuisineLogger.LogInfo((object)$"[CreatureBomb] Spawned {item2} AWAKE at {val} (Level {num3})"); } else { ValheimCuisineLogger.LogInfo((object)$"[CreatureBomb] Spawned {item2} at {val} (Level {num3})"); } } else { ValheimCuisineLogger.LogError((object)("[CreatureBomb] Failed to spawn " + item2 + "!")); } num2++; } } } private static List<(string prefabName, int count, int level)> ParseCreatureList(string configValue) { List<(string, int, int)> list = new List<(string, int, int)>(); if (string.IsNullOrWhiteSpace(configValue)) { ValheimCuisineLogger.LogWarning((object)"[CreatureBomb] Config value is null or empty"); return list; } ValheimCuisineLogger.LogInfo((object)("[CreatureBomb] Parsing creature list: " + configValue)); string[] array = configValue.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { string[] array2 = text.Trim().Split(new char[1] { ':' }); if (array2.Length >= 2) { string text2 = array2[0].Trim(); if (!int.TryParse(array2[1].Trim(), out var result) || result <= 0) { ValheimCuisineLogger.LogWarning((object)("[CreatureBomb] Invalid count in entry: '" + text + "'")); continue; } int num = 1; if (array2.Length >= 3 && int.TryParse(array2[2].Trim(), out var result2)) { num = Mathf.Clamp(result2, 1, 3); } list.Add((text2, result, num)); ValheimCuisineLogger.LogInfo((object)$"[CreatureBomb] Parsed: {text2} x {result} (Level {num})"); } else { ValheimCuisineLogger.LogWarning((object)("[CreatureBomb] Invalid entry: '" + text + "'")); } } return list; } private static Vector3 CalculateSpawnPosition(Vector3 center, float radius, int index, int total) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) Vector3 val = center; if (radius > 0f && total > 1) { float num = 360f / (float)total * (float)index; float num2 = num * ((float)Math.PI / 180f); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(Mathf.Cos(num2) * radius, 0f, Mathf.Sin(num2) * radius); val += val2; } return val; } } [HarmonyPatch(typeof(Character), "OnDeath")] public static class GroaElixir_Character_OnDeath_Patch { [HarmonyPrefix] private static void Prefix(Character __instance) { //IL_00ac: 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_00b9: Unknown result type (might be due to invalid IL or missing references) if (__instance.IsPlayer() || !__instance.m_localPlayerHasHit) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } StatusEffect statusEffect = ((Character)localPlayer).GetSEMan().GetStatusEffect(StringExtensionMethods.GetStableHashCode("VC_GroaElixirEffect")); if ((Object)(object)statusEffect == (Object)null) { return; } float value = GroaElixirHealthOnKill.Value; if (value > 0f) { float health = Mathf.Min(((Character)localPlayer).GetHealth() + value, ((Character)localPlayer).GetMaxHealth()); ((Character)localPlayer).SetHealth(health); if (value >= 1f) { Vector3 centerPoint = ((Character)localPlayer).GetCenterPoint(); DamageText.instance.ShowText((TextType)4, centerPoint, value, false); } } } } [HarmonyPatch(typeof(Player))] public static class BerserkRoar_ActivePower_Patch { private static readonly Dictionary<long, float> playerCooldowns = new Dictionary<long, float>(); [HarmonyPostfix] [HarmonyPatch("Update")] private static void Update_Postfix(Player __instance) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (((Character)__instance).IsOwner() && Input.GetKeyDown(BerserkRoarActivationKey.Value)) { TryTriggerBerserkRoar(__instance); } } private static void TryTriggerBerserkRoar(Player player) { bool flag = false; foreach (StatusEffect statusEffect in ((Character)player).GetSEMan().GetStatusEffects()) { if (statusEffect.m_name == "$item_vc_fulingberserkerelixir") { flag = true; break; } } if (!flag) { if (((Humanoid)player).GetInventory().HaveItem("$item_vc_fulingberserkerelixir", true)) { ((Character)player).Message((MessageType)2, Localization.instance.Localize("$vc_hamask_needpotion"), 0, (Sprite)null); } return; } long playerID = player.GetPlayerID(); float time = Time.time; if (playerCooldowns.TryGetValue(playerID, out var value)) { float num = time - value; float num2 = BerserkRoarCooldown.Value - num; if (num2 > 0f) { int num3 = Mathf.FloorToInt(num2 / 60f); int num4 = Mathf.FloorToInt(num2 % 60f); string text = Localization.instance.Localize("$vc_hamask_cooldown"); text = ((num3 <= 0) ? (text + $" {num4}s") : (text + $" {num3}m {num4}s")); ((Character)player).Message((MessageType)2, text, 0, (Sprite)null); return; } } playerCooldowns[playerID] = time; TriggerBerserkRoar(player); } private static void TriggerBerserkRoar(Player player) { string text = ((player.GetPlayerModel() == 0) ? "VC_sfx_BerserkergangMale" : "VC_sfx_BerserkergangFemale"); if (player.StartEmote("roar", false)) { ((Character)player).m_nview.InvokeRPC(ZNetView.Everybody, "VC_PlayBerserkRoar", new object[1] { text }); SpawnHamaskVFX(player); ApplyRoarEffects(player); } else { ApplyRoarEffects(player); ((Character)player).Message((MessageType)2, Localization.instance.Localize("$vc_hamask_noanimation"), 0, (Sprite)null); } } private static void SpawnHamaskVFX(Player player) { //IL_0035: 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 (!((Object)(object)ZNetScene.instance == (Object)null)) { GameObject prefab = ZNetScene.instance.GetPrefab("VC_vfx_Hamask"); if ((Object)(object)prefab != (Object)null) { Object.Instantiate<GameObject>(prefab, ((Component)player).transform.position, Quaternion.identity); } } } private static void ApplyRoarEffects(Player player) { if (!((Object)(object)player == (Object)null)) { ApplyDamageBuff(player); StaggerNearbyEnemies(player); TauntNearbyEnemies(player); } } private static void ApplyDamageBuff(Player player) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) bool flag = false; ObjectDB instance = ObjectDB.instance; StatusEffect val = ((instance != null) ? instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("VC_FulingBerserkerElixirDamageEffect")) : null); if ((Object)(object)val != (Object)null) { SE_Stats val2 = (SE_Stats)(object)((val is SE_Stats) ? val : null); if ((Object)(object)val2 != (Object)null) { ((StatusEffect)val2).m_ttl = BerserkRoarDamageBoostDuration.Value; val2.m_damageModifier = BerserkRoarDamageBoostPercent.Value / 100f; val2.m_modifyAttackSkill = (SkillType)999; ((Character)player).GetSEMan().AddStatusEffect((StatusEffect)(object)val2, false, 0, 0f); return; } } SE_Stats val3 = ScriptableObject.CreateInstance<SE_Stats>(); ((Object)val3).name = "VC_FulingBerserkerElixirDamageEffect"; string text = Localization.instance.Localize("$vc_fulinghamask_damage"); ((StatusEffect)val3).m_name = ((!text.Contains("[") && text != "$vc_fulinghamask_damage") ? text : "Hamask"); string text2 = Localization.instance.Localize("$vc_fulinghamask_damage_tooltip"); ((StatusEffect)val3).m_tooltip = ((!text2.Contains("[") && text2 != "$vc_fulinghamask_damage_tooltip") ? text2 : "Damage increased!"); ((StatusEffect)val3).m_ttl = BerserkRoarDamageBoostDuration.Value; val3.m_damageModifier = BerserkRoarDamageBoostPercent.Value / 100f; val3.m_modifyAttackSkill = (SkillType)999; StatusEffect statusEffect = ((Character)player).GetSEMan().GetStatusEffect(StringExtensionMethods.GetStableHashCode("GP_Eikthyr")); if ((Object)(object)statusEffect != (Object)null && (Object)(object)statusEffect.m_icon != (Object)null) { ((StatusEffect)val3).m_icon = statusEffect.m_icon; } else { StatusEffect statusEffect2 = ((Character)player).GetSEMan().GetStatusEffect(StringExtensionMethods.GetStableHashCode("Rested")); if ((Object)(object)statusEffect2 != (Object)null && (Object)(object)statusEffect2.m_icon != (Object)null) { ((StatusEffect)val3).m_icon = statusEffect2.m_icon; } } ((Character)player).GetSEMan().AddStatusEffect((StatusEffect)(object)val3, false, 0, 0f); } private static void StaggerNearbyEnemies(Player player) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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) float value = BerserkRoarStaggerRadius.Value; List<Character> list = new List<Character>(); Character.GetCharactersInRange(((Component)player).transform.position, value, list); foreach (Character item in list) { if (!((Object)(object)item == (Object)(object)player) && !item.IsDead() && BaseAI.IsEnemy((Character)(object)player, item)) { item.Stagger(((Component)player).transform.position - ((Component)item).transform.position); SE_Stats val = ScriptableObject.CreateInstance<SE_Stats>(); ((StatusEffect)val).m_name = "Berserk Stagger"; ((StatusEffect)val).m_ttl = BerserkRoarStaggerDuration.Value; val.m_staggerModifier = 2f; item.GetSEMan().AddStatusEffect((StatusEffect)(object)val, false, 0, 0f); } } } private static void TauntNearbyEnemies(Player player) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) float value = BerserkRoarTauntRadius.Value; List<Character> list = new List<Character>(); Character.GetCharactersInRange(((Component)player).transform.position, value, list); foreach (Character item in list) { if (!((Object)(object)item == (Object)(object)player) && !item.IsDead() && BaseAI.IsEnemy((Character)(object)player, item)) { MonsterAI component = ((Component)item).GetComponent<MonsterAI>(); if ((Object)(object)component != (Object)null) { component.SetTarget((Character)(object)player); ((BaseAI)component).SetAlerted(true); } } } } } [HarmonyPatch(typeof(Player))] public static class BerserkRoar_Player_RPC_Patch { [HarmonyPostfix] [HarmonyPatch("Awake")] private static void Awake_Postfix(Player __instance) { Player __instance2 = __instance; if ((Object)(object)((Character)__instance2).m_nview != (Object)null && ((Character)__instance2).m_nview.IsValid()) { ((Character)__instance2).m_nview.Register<string>("VC_PlayBerserkRoar", (Action<long, string>)delegate(long sender, string sfxName) { RPC_PlayBerserkRoar(__instance2, sender, sfxName); }); } } private static void RPC_PlayBerserkRoar(Player player, long sender, string sfxName) { //IL_0031: 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) if (!((Object)(object)ZNetScene.instance == (Object)null)) { GameObject prefab = ZNetScene.instance.GetPrefab(sfxName); if ((Object)(object)prefab != (Object)null) { Object.Instantiate<GameObject>(prefab, ((Component)player).transform.position, Quaternion.identity); } } } } [HarmonyPatch(typeof(Attack), "GetAttackEitr")] public static class StaffEitrReduction_Patch { [HarmonyPostfix] private static void Postfix(Attack __instance, ref float __result) { if ((Object)(object)__instance.m_character == (Object)null || !((Character)__instance.m_character).IsPlayer()) { return; } ItemData weapon = __instance.m_weapon; if (weapon == null || !IsStaff(weapon)) { return; } Humanoid character = __instance.m_character; Player val = (Player)(object)((character is Player) ? character : null); if (!((Object)(object)val == (Object)null)) { float num = 0f; string text = ""; if (((Character)val).GetSEMan().HaveStatusEffect(StringExtensionMethods.GetStableHashCode("VC_FreyjaDraughtEffect"))) { num = FreyjaStaffEitrReduction.Value / 100f; text = "Freyja's Draught"; } else if (((Character)val).GetSEMan().HaveStatusEffect(StringExtensionMethods.GetStableHashCode("VC_FulingGutfireBrewEffect"))) { num = FulingStaffEitrReduction.Value / 100f; text = "Fuling Gutfire Brew"; } if (num > 0f) { float num2 = __result; __result *= 1f - num; ValheimCuisineLogger.LogDebug((object)$"{text} active: Reducing staff eitr cost by {num * 100f}% (from {num2} to {__result})"); } } } private static bool IsStaff(ItemData weapon) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 return (int)weapon.m_shared.m_skillType == 10 || (int)weapon.m_shared.m_skillType == 9; } } [HarmonyPatch(typeof(Character), "ApplyDamage")] public static class VidarrDraughtBossDamage_Patch { [HarmonyPrefix] private static void ApplyDamage_Prefix(Character __instance, ref HitData hit) { Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if (val != null && !((Object)(object)__instance == (Object)null) && __instance.IsBoss() && ((Character)val).GetSEMan().HaveStatusEffect(StringExtensionMethods.GetStableHashCode("VC_VidarrDraughtEffect"))) { float value = VidarrBossDamageMultiplier.Value; ((DamageTypes)(ref hit.m_damage)).Modify(value); ValheimCuisineLogger.LogDebug((object)$"Víðarr Draught active: Applying {value}x damage to boss {__instance.m_name}"); } } } public static class SkadiDraughtTracker { public static Player LastKillingPlayer; } [HarmonyPatch(typeof(Character), "OnDeath")] public static class Character_OnDeath_TrackKiller { [HarmonyPrefix] private static void Prefix(Character __instance) { SkadiDraughtTracker.LastKillingPlayer = null; if (__instance.m_lastHit != null) { Character attacker = __instance.m_lastHit.GetAttacker(); if ((Object)(object)attacker != (Object)null && attacker.IsPlayer()) { SkadiDraughtTracker.LastKillingPlayer = (Player)(object)((attacker is Player) ? attacker : null); } } } } [HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")] public static class CharacterDrop