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 ValheimBB v0.2.2
BepInEx\plugins\ValheimBB.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Globalization; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Splatform; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ValheimBB")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ValheimBB")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("1699c6b0-5753-49cf-903b-e6b635fbcab9")] [assembly: AssemblyFileVersion("0.2.2.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.2.2.0")] namespace ValheimBB { [BepInPlugin("com.valheimbb", "ValheimBB", "0.2.2")] public class ValheimBBPlugin : BaseUnityPlugin { internal enum WisplightOutsideMistlandsMode { VanillaOrbit, Off, LockedInPlace } [HarmonyPatch(typeof(BaseAI), "DoIdleSound")] public static class BaseAI_DoIdleSound_AttachedOriginSound_Patch { private static void Prefix(BaseAI __instance) { SetDeerAlertedIdleSoundState(__instance, (Object)(object)__instance != (Object)null && __instance.IsAlerted()); ForceAttachSoundEffects(__instance?.m_idleSound); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return ReplaceEffectListCreateNullParentWithSelfTransform(instructions); } } [HarmonyPatch(typeof(BaseAI), "SetAlerted")] public static class BaseAI_SetAlerted_AttachedOriginSound_Patch { private static void Prefix(BaseAI __instance) { ForceAttachSoundEffects(__instance?.m_alertedEffects); } private static void Postfix(BaseAI __instance, bool __0) { SetDeerAlertedIdleSoundState(__instance, __0); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return ReplaceEffectListCreateNullParentWithSelfTransform(instructions); } } [HarmonyPatch(typeof(BaseAI), "OnDestroy")] public static class BaseAI_OnDestroy_DeerAlertedScaredSfx_Patch { private static void Postfix(BaseAI __instance) { ClearDeerAlertedIdleSoundState(__instance); } } [HarmonyPatch(typeof(Attack), "Update")] public static class Attack_Update_AttachedOriginSound_Patch { private static void Prefix(Attack __instance) { NormalizeAttackStartSoundEffects(__instance); } } [HarmonyPatch(typeof(Attack), "DoMeleeAttack")] public static class Attack_DoMeleeAttack_AttachedOriginSound_Patch { private static void Prefix(Attack __instance) { NormalizeAttackTriggerSoundEffects(__instance); } } [HarmonyPatch(typeof(Attack), "DoAreaAttack")] public static class Attack_DoAreaAttack_AttachedOriginSound_Patch { private static void Prefix(Attack __instance) { NormalizeAttackTriggerSoundEffects(__instance); } } [HarmonyPatch(typeof(Attack), "ProjectileAttackTriggered")] public static class Attack_ProjectileAttackTriggered_AttachedOriginSound_Patch { private static void Prefix(Attack __instance) { NormalizeAttackTriggerSoundEffects(__instance); } } [HarmonyPatch(typeof(Attack), "OnTrailStart")] public static class Attack_OnTrailStart_AttachedOriginSound_Patch { private static void Prefix(Attack __instance) { NormalizeAttackTrailSoundEffects(__instance); } } [HarmonyPatch(typeof(Character), "ApplyDamage")] public static class Character_ApplyDamage_FallDamageGrunt_Patch { private static void Prefix(Character __instance, HitData hit, ref float __state) { __state = 0f; if (ShouldPlayFallDamageGrunt(__instance, hit)) { __state = __instance.GetHealth(); } } private static void Postfix(Character __instance, HitData hit, float __state) { if (!(__state <= 0f) && ShouldPlayFallDamageGrunt(__instance, hit)) { float health = __instance.GetHealth(); if (!(health <= 0f) && !(__state - health <= 0.1f)) { TryBroadcastPlayerDamageGrunt((Player)(object)((__instance is Player) ? __instance : null)); } } } private static bool ShouldPlayFallDamageGrunt(Character character, HitData hit) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Invalid comparison between Unknown and I4 Player val = (Player)(object)((character is Player) ? character : null); if ((Object)(object)val != (Object)null && (Object)(object)val == (Object)(object)Player.m_localPlayer && hit != null) { return (int)hit.m_hitType == 3; } return false; } } [HarmonyPatch(typeof(Character), "AddFireDamage")] public static class Character_AddFireDamage_FireDamageGrunt_Patch { private static void Postfix(Character __instance, float damage) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && !(damage <= 0f)) { TryBroadcastPlayerDamageGrunt(val); } } } [HarmonyPatch(typeof(Player), "Awake")] public static class Player_Awake_PlayerSfxRpc_Patch { private static void Postfix(Player __instance) { RegisterPlayerSfxRpc(__instance); RegisterPickaxeBedrockAudioRpc(__instance); } } [HarmonyPatch(typeof(Player), "OnDeath")] public static class Player_OnDeath_DeathSplat_Patch { private static void Postfix(Player __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { TryBroadcastPlayerDeathSplat(__instance); } } } [HarmonyPatch(typeof(Attack), "SpawnOnHitTerrain")] public static class Attack_SpawnOnHitTerrain_PickaxeBedrockTracking_Patch { private static void Prefix(Vector3 hitPoint, GameObject prefab, Character character, ItemData weapon) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) RegisterLocalPickaxeTerrainHit(hitPoint, prefab, character, weapon); } } [HarmonyPatch(typeof(TerrainOp), "OnPlaced")] public static class TerrainOp_OnPlaced_PickaxeBedrockAudio_Patch { private static void Prefix(TerrainOp __instance) { TryHandlePickaxeBedrockHit(__instance); } } [HarmonyPatch(typeof(Attack), "DoMeleeAttack")] private static class Attack_DoMeleeAttack_PickaxeVegetationAudioContext { private static void Prefix(Attack __instance, out bool __state) { __state = BeginLocalPickaxeVegetationAudio(__instance); } private static Exception Finalizer(bool __state, Exception __exception) { EndLocalPickaxeVegetationAudio(__state); return __exception; } } [HarmonyPatch(typeof(Destructible), "Damage")] private static class Destructible_Damage_PickaxeVegetationAudio_Patch { private static void Prefix(Destructible __instance, HitData hit) { try { TryReplayPickaxeVegetationHitEffect((Component)(object)__instance, __instance?.m_hitEffect, hit); } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error replaying pickaxe destructible vegetation hit effect: {arg}"); } } } } [HarmonyPatch(typeof(WearNTear), "Damage")] private static class WearNTear_Damage_PickaxeVegetationAudio_Patch { private static void Prefix(WearNTear __instance, HitData hit) { try { TryReplayPickaxeVegetationHitEffect((Component)(object)__instance, __instance?.m_hitEffect, hit); } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error replaying pickaxe wear-and-tear vegetation hit effect: {arg}"); } } } } [HarmonyPatch(typeof(Chat), "InputText")] public static class Chat_InputText_Patch { private const float DefaultRemovePinRadius = 10f; private static bool Prefix(ref Chat __instance) { string text = ((TMP_InputField)((Terminal)__instance).m_input).text; if (string.IsNullOrWhiteSpace(text)) { return true; } if (!text.StartsWith("/", StringComparison.Ordinal)) { return true; } if (text.StartsWith("/addpin", StringComparison.OrdinalIgnoreCase)) { HandleAddPin(__instance, text); return false; } if (text.StartsWith("/removepin", StringComparison.OrdinalIgnoreCase)) { HandleRemoveNearestPin(__instance, text); return false; } if (text.StartsWith("/cleardeaths", StringComparison.OrdinalIgnoreCase)) { HandleClearDeathPins(__instance); return false; } return true; } private static void HandleAddPin(Chat chat, string text) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: 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_00b3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Minimap.instance == (Object)null) { return; } string[] array = text.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length >= 2 && array[1].Equals("help", StringComparison.OrdinalIgnoreCase)) { ShowHelp(); ((TMP_InputField)((Terminal)chat).m_input).text = string.Empty; return; } PinType val = (PinType)3; int num = 3; if (array.Length >= 2) { string text2 = array[1]; if (text2.StartsWith("icon", StringComparison.OrdinalIgnoreCase)) { text2 = text2.Substring(4); } if (int.TryParse(text2, out var result) && result >= 0 && result <= 4 && Enum.TryParse<PinType>("Icon" + result, out PinType result2)) { val = result2; num = result; } } Vector3 position = ((Component)Player.m_localPlayer).transform.position; Minimap.instance.AddPin(position, val, "", true, false, 0L, default(PlatformUserID)); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"[ValheimBB] /addpin -> {val} at {position}"); } if ((Object)(object)Chat.instance != (Object)null) { ((Terminal)Chat.instance).AddString("ValheimBB", $"Pin {num} added.\n" + "Options: Pin 0 = Campfire, Pin 1 = House, Pin 2 = Anchor, Pin 3 = Dot, Pin 4 = Portal", (Type)1, false); } ((TMP_InputField)((Terminal)chat).m_input).text = string.Empty; } private static void HandleRemoveNearestPin(Chat chat, string text) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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) if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Minimap.instance == (Object)null)) { float num = 10f; string[] array = text.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length >= 2 && float.TryParse(array[1], out var result) && result > 0f) { num = result; } Vector3 position = ((Component)Player.m_localPlayer).transform.position; bool flag = Minimap.instance.RemovePin(position, num); if ((Object)(object)Chat.instance != (Object)null) { string text2 = (flag ? $"Removed nearest pin within {num:0.#}m." : $"No pin found within {num:0.#}m."); ((Terminal)Chat.instance).AddString("ValheimBB", text2, (Type)1, false); } ((TMP_InputField)((Terminal)chat).m_input).text = string.Empty; } } private static void HandleClearDeathPins(Chat chat) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Invalid comparison between Unknown and I4 if ((Object)(object)Minimap.instance == (Object)null) { return; } int num = 0; try { Minimap instance = Minimap.instance; FieldInfo field = typeof(Minimap).GetField("m_pins", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[ValheimBB] Could not find Minimap.m_pins via reflection."); } return; } if (!(field.GetValue(instance) is List<PinData> list)) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[ValheimBB] Minimap.m_pins is null or of unexpected type."); } return; } List<PinData> list2 = new List<PinData>(); foreach (PinData item in list) { if ((int)item.m_type == 4) { list2.Add(item); } } num = list2.Count; foreach (PinData item2 in list2) { instance.RemovePin(item2); } if ((Object)(object)Chat.instance != (Object)null) { string text = ((num > 0) ? $"Removed {num} death marker(s)." : "No death markers found."); ((Terminal)Chat.instance).AddString("ValheimBB", text, (Type)1, false); } } catch (Exception arg) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogError((object)$"[ValheimBB] Error clearing death pins: {arg}"); } } finally { ((TMP_InputField)((Terminal)chat).m_input).text = string.Empty; } } public static void ShowHelp() { if (!((Object)(object)Chat.instance == (Object)null)) { string text = "ValheimBB command help:\n/addpin [index] - Add a map pin at your current position.\n index: 0 = Campfire, 1 = House, 2 = Anchor, 3 = Dot, 4 = Portal (default: 3)\n/removepin [radius] - Remove the nearest pin within the given radius (meters).\n radius: optional, default is 10.\n/cleardeaths - Remove all death markers from the map."; ((Terminal)Chat.instance).AddString("ValheimBB", text, (Type)1, false); } } } private sealed class ValheimBBCompendiumEntry { private readonly Func<string> _textFactory; private string _text; public string Label { get; } public string Name { get; } public string Topic { get; } public string Text { get { if (_text == null) { _text = _textFactory() ?? string.Empty; } return _text; } } public ValheimBBCompendiumEntry(string name, string topic, string label, string text) : this(name, topic, label, () => text) { } public ValheimBBCompendiumEntry(string name, string topic, string label, Func<string> textFactory) { if (textFactory == null) { throw new ArgumentNullException("textFactory"); } Name = name; Topic = topic; Label = label; _textFactory = textFactory; } } [HarmonyPatch] private static class Tutorial_Awake_ValheimBBCompendium_Patch { private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("Tutorial"); if (!(type == null)) { return AccessTools.Method(type, "Awake", (Type[])null, (Type[])null); } return null; } private static void Postfix(object __instance) { EnsureValheimBBCompendiumEntries(__instance); } } [HarmonyPatch(typeof(Player), "GetKnownTexts")] private static class Player_GetKnownTexts_ValheimBBCompendium_Patch { private static void Postfix(ref List<KeyValuePair<string, string>> __result) { EnsureValheimBBKnownTexts(__result); } } private sealed class AmmoRecipeBatchTweakDefinition { public string RecipeName; public string OutputPrefabName; public HashSet<string> StaticRequirementPrefabNames; public Dictionary<string, int> VanillaRequirementAmounts; public bool TryGetTargetRequirementAmount(string requirementPrefabName, out int amount) { amount = 0; if (string.IsNullOrWhiteSpace(requirementPrefabName) || VanillaRequirementAmounts == null || !VanillaRequirementAmounts.TryGetValue(requirementPrefabName, out var value)) { return false; } amount = ((StaticRequirementPrefabNames != null && StaticRequirementPrefabNames.Contains(requirementPrefabName)) ? value : (value * 5)); return true; } } private struct AmmoRequirementBaseline { public string PrefabName; public int Amount; public AmmoRequirementBaseline(string prefabName, int amount) { PrefabName = prefabName; Amount = amount; } } [HarmonyPatch(typeof(ObjectDB), "Awake")] private static class ObjectDB_Awake_AmmoRecipeBatchTweaks_Patch { private static void Postfix(ObjectDB __instance) { ApplyAmmoRecipeBatchTweaks(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] private static class ObjectDB_CopyOtherDB_AmmoRecipeBatchTweaks_Patch { private static void Postfix(ObjectDB __instance) { ApplyAmmoRecipeBatchTweaks(__instance); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] private static class ObjectDB_Awake_BearHeaddressRecipe_Patch { private static void Postfix(ObjectDB __instance) { UpdateBearHeaddressRecipe(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] private static class ObjectDB_CopyOtherDB_BearHeaddressRecipe_Patch { private static void Postfix(ObjectDB __instance) { UpdateBearHeaddressRecipe(__instance); } } [HarmonyPatch(typeof(Character), "ApplyPushback", new Type[] { typeof(Vector3), typeof(float) })] private static class Character_ApplyPushback_CreatureMassScaling_Patch { private static void Prefix(Character __instance, ref float pushForce) { if (IsFeatureEnabled(EnableCreaturePushbackScaling) && !((Object)(object)__instance == (Object)null) && !__instance.IsPlayer() && pushForce != 0f && !Mathf.Approximately(0.5f, 1f)) { pushForce /= 0.5f; } } } private struct PreviousLoadedWeaponState { public ItemData Weapon; } [HarmonyPatch] private static class Player_UpdateWeaponLoading_PersistentCrossbowLoad_Patch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Player), "UpdateWeaponLoading", (Type[])null, (Type[])null); } private static bool Prefix(Player __instance, ItemData weapon, float dt) { if (!IsFeatureEnabled(EnableCrossbowLoadedPersistence)) { return true; } ClearInvalidLoadedWeapon(__instance); ItemData loadedWeapon = GetLoadedWeapon(__instance); if (!RequiresReload(weapon)) { return false; } if (HasPersistentLoadedFlag(weapon)) { if (loadedWeapon != weapon) { SetLoadedWeapon(__instance, weapon); } return false; } if (!IsReloadActionQueued(__instance) && ((Character)__instance).TryUseEitr(weapon.m_shared.m_attack.m_reloadEitrDrain)) { QueueReloadAction(__instance); } return false; } } [HarmonyPatch] private static class Player_SetWeaponLoaded_PersistentCrossbowLoad_Patch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Player), "SetWeaponLoaded", (Type[])null, (Type[])null); } private static void Prefix(Player __instance, out PreviousLoadedWeaponState __state) { __state = new PreviousLoadedWeaponState { Weapon = (IsFeatureEnabled(EnableCrossbowLoadedPersistence) ? GetLoadedWeapon(__instance) : null) }; } private static void Postfix(Player __instance, ItemData weapon, PreviousLoadedWeaponState __state) { if (IsFeatureEnabled(EnableCrossbowLoadedPersistence)) { if (weapon == null && __state.Weapon != null) { SetPersistentLoadedFlag(__state.Weapon, isLoaded: false); } if (RequiresReload(weapon)) { SetPersistentLoadedFlag(weapon, isLoaded: true); } } } } [HarmonyPatch] private static class Player_IsWeaponLoaded_PersistentCrossbowLoad_Patch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Player), "IsWeaponLoaded", (Type[])null, (Type[])null); } private static void Postfix(Player __instance, ref bool __result) { if (!IsFeatureEnabled(EnableCrossbowLoadedPersistence) || (Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } ItemData currentWeapon = ((Humanoid)__instance).GetCurrentWeapon(); if (!RequiresReload(currentWeapon)) { __result = false; } else if (HasPersistentLoadedFlag(currentWeapon)) { if (GetLoadedWeapon(__instance) != currentWeapon) { SetLoadedWeapon(__instance, currentWeapon); } __result = true; } else { if (GetLoadedWeapon(__instance) == currentWeapon) { ClearLoadedWeapon(__instance); } __result = false; } } } [HarmonyPatch(typeof(Humanoid), "UnequipItem")] private static class Humanoid_UnequipItem_PersistentCrossbowLoad_Patch { private static void Prefix(Humanoid __instance, ItemData item) { s_loadedWeaponPreservePlayer = null; s_loadedWeaponPreserveItem = null; if (!IsFeatureEnabled(EnableCrossbowLoadedPersistence)) { return; } Player val = (Player)(object)((__instance is Player) ? __instance : null); if (!((Object)(object)val == (Object)null) && item != null) { ItemData loadedWeapon = GetLoadedWeapon(val); if (CanPersistLoadedWeapon(val, loadedWeapon)) { s_loadedWeaponPreservePlayer = val; s_loadedWeaponPreserveItem = loadedWeapon; } } } private static void Finalizer() { s_loadedWeaponPreservePlayer = null; s_loadedWeaponPreserveItem = null; } } [HarmonyPatch(typeof(Player), "ResetLoadedWeapon")] private static class Player_ResetLoadedWeapon_PersistentCrossbowLoad_Patch { private static bool Prefix(Player __instance) { if (!IsFeatureEnabled(EnableCrossbowLoadedPersistence)) { return true; } if ((Object)(object)s_loadedWeaponPreservePlayer != (Object)(object)__instance || s_loadedWeaponPreserveItem == null) { return true; } if (!CanPersistLoadedWeapon(__instance, s_loadedWeaponPreserveItem)) { return true; } CancelReloadAction(__instance); return false; } } [HarmonyPatch(typeof(Attack), "Start", new Type[] { typeof(Humanoid), typeof(Rigidbody), typeof(ZSyncAnimation), typeof(CharacterAnimEvent), typeof(VisEquipment), typeof(ItemData), typeof(Attack), typeof(float), typeof(float) })] private static class Attack_Start_PersistentCrossbowLoad_Patch { private static bool Prefix(Humanoid character, ItemData weapon, ref bool __result) { if (!IsFeatureEnabled(EnableCrossbowLoadedPersistence)) { return true; } Player val = (Player)(object)((character is Player) ? character : null); if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer || !RequiresReload(weapon)) { return true; } if (HasPersistentLoadedFlag(weapon)) { if (GetLoadedWeapon(val) != weapon) { SetLoadedWeapon(val, weapon); } return true; } __result = false; return false; } } [HarmonyPatch(typeof(Fireplace), "Interact")] public static class Fireplace_Interact_AltFuel_Patch { private static void Prefix(Fireplace __instance, Humanoid user, bool hold, bool alt, ref ItemDrop __state) { __state = null; try { if (hold || alt || (Object)(object)user == (Object)null) { return; } Inventory inventory = user.GetInventory(); if (inventory == null || inventory.GetItem("Wood", -1, true) != null) { return; } ItemData val = inventory.GetItem("RoundLog", -1, true) ?? inventory.GetItem("ElderBark", -1, true); if (val != null && !((Object)(object)val.m_dropPrefab == (Object)null)) { ItemDrop component = val.m_dropPrefab.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null)) { __state = __instance.m_fuelItem; __instance.m_fuelItem = component; } } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error in Fireplace.Interact alt-fuel Prefix: {arg}"); } } } private static void Postfix(Fireplace __instance, ItemDrop __state) { try { if ((Object)(object)__state != (Object)null) { __instance.m_fuelItem = __state; } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error restoring fireplace fuel item: {arg}"); } } } } [HarmonyPatch(typeof(Player), "ActivateGuardianPower")] public static class Player_ActivateGuardianPower_ModerDuration_Patch { private static void Prefix(StatusEffect ___m_guardianSE) { try { if (!((Object)(object)___m_guardianSE == (Object)null) && !(((Object)___m_guardianSE).name != "GP_Moder")) { float ttl = ___m_guardianSE.m_ttl; ___m_guardianSE.m_ttl = 600f; ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"[ValheimBB] Moder buff duration changed from {ttl} to {___m_guardianSE.m_ttl} seconds."); } } } catch (Exception arg) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)$"[ValheimBB] Error while extending Moder power duration: {arg}"); } } } } [HarmonyPatch(typeof(EnvMan), "GetWindDir")] public static class EnvMan_GetWindDir_ModerSideWind_Patch { private static void Postfix(ref Vector3 __result) { //IL_00a2: 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_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: 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) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } SEMan sEMan = ((Character)Player.m_localPlayer).GetSEMan(); if (sEMan == null) { return; } bool flag = false; List<StatusEffect> statusEffects = sEMan.GetStatusEffects(); if (statusEffects == null) { return; } foreach (StatusEffect item in statusEffects) { if ((Object)(object)item != (Object)null && ((Object)item).name == "GP_Moder") { flag = true; break; } } if (!flag) { return; } Ship localShip = Ship.GetLocalShip(); if ((Object)(object)localShip == (Object)null) { return; } Vector3 forward = ((Component)localShip).transform.forward; forward.y = 0f; if (!(((Vector3)(ref forward)).sqrMagnitude < 0.0001f)) { ((Vector3)(ref forward)).Normalize(); Vector3 val = Vector3.Cross(Vector3.up, forward); if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f)) { ((Vector3)(ref val)).Normalize(); __result = val; } } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error in Moder side-wind GetWindDir patch: {arg}"); } } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] private static class ObjectDB_Awake_MuckshakeFoodTweaks_Patch { private static void Postfix(ObjectDB __instance) { ApplyMuckshakeFoodTweaks(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] private static class ObjectDB_CopyOtherDB_MuckshakeFoodTweaks_Patch { private static void Postfix(ObjectDB __instance) { ApplyMuckshakeFoodTweaks(__instance); } } [HarmonyPatch(typeof(Player), "OnSpawned")] private static class Player_OnSpawned_MuckshakeFoodRepair_Patch { private static void Postfix(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } try { RepairMuckshakeInventory(((Humanoid)__instance).GetInventory()); } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error while repairing Muckshake items on player spawn: {arg}"); } } } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] private static class Inventory_AddItem_MuckshakeFoodRepair_Patch { private static void Prefix(ItemData item) { NormalizeMuckshakeItem(item); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData), typeof(Vector2i) })] private static class Inventory_AddItemAtPos_MuckshakeFoodRepair_Patch { private static void Prefix(ItemData item) { NormalizeMuckshakeItem(item); } } [HarmonyPatch] private static class Inventory_AddItemToGrid_MuckshakeFoodRepair_Patch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Inventory), "AddItem", new Type[4] { typeof(ItemData), typeof(int), typeof(int), typeof(int) }, (Type[])null); } private static void Prefix(ItemData item) { NormalizeMuckshakeItem(item); } } private sealed class CraftRecipeSeenClickHandler : MonoBehaviour { public InventoryGui InventoryGui; public GameObject Element; public void HandleClick() { if (IsFeatureEnabled(EnableBuildAndRecipeUnlockMarkers)) { MarkCraftRecipeSeenByElement(InventoryGui, Element); } } } [HarmonyPatch(typeof(Hud), "UpdatePieceList")] private static class Hud_UpdatePieceList_NewBuildPieceMarkers_Patch { private static void Postfix(Hud __instance, Player player) { RefreshNewBuildPieceMarkers(__instance, player); } } [HarmonyPatch(typeof(Hud), "OnHoverPiece")] private static class Hud_OnHoverPiece_NewBuildPieceMarkers_Patch { private static void Postfix(Hud __instance, UIInputHandler ih) { if (!IsFeatureEnabled(EnableBuildAndRecipeUnlockMarkers)) { return; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)ih == (Object)null)) { Piece buildPieceForIcon = GetBuildPieceForIcon(__instance, localPlayer, ((Component)ih).gameObject); if (!((Object)(object)buildPieceForIcon == (Object)null) && MarkBuildPieceSeen(localPlayer, buildPieceForIcon)) { RefreshNewBuildPieceMarkers(__instance, localPlayer); } } } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")] private static class InventoryGui_UpdateRecipeList_NewRecipeMarkers_Patch { private static void Postfix(InventoryGui __instance) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && __instance.InCraftTab()) { if (IsFeatureEnabled(EnableBuildAndRecipeUnlockMarkers)) { WireCraftRecipeHoverHandlers(__instance); } RefreshCraftRecipeMarkers(__instance, localPlayer); } } } [HarmonyPatch(typeof(Minimap), "Update")] private static class Minimap_Update_NoMapBiomeLabel_Patch { private static void Postfix(Minimap __instance) { UpdateNoMapBiomeLabel(__instance); } } [HarmonyPatch(typeof(Minimap), "OnDestroy")] private static class Minimap_OnDestroy_NoMapBiomeLabel_Patch { private static void Prefix() { s_noMapBiomeLabel = null; } } [HarmonyPatch(typeof(Attack), "DoMeleeAttack")] private static class Attack_DoMeleeAttack_BedrockEffectContext { private static void Prefix(Attack __instance, ref bool __state) { __state = false; if (IsPickaxeAttack(__instance)) { if (s_activePickaxeAttackDepth == 0) { s_activePickaxeAttack = __instance; } s_activePickaxeAttackDepth++; __state = true; } } private static Exception Finalizer(bool __state, Exception __exception) { if (__state && s_activePickaxeAttackDepth > 0) { s_activePickaxeAttackDepth--; if (s_activePickaxeAttackDepth == 0) { s_activePickaxeAttack = null; } } return __exception; } } [HarmonyPatch(typeof(TerrainModifier), "OnPlaced")] private static class TerrainModifier_OnPlaced_BedrockEffectContext { private static void Prefix(TerrainModifier __instance, ref bool __state) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) __state = false; if (!((Object)(object)__instance == (Object)null)) { Vector3 checkPosition = ((Component)__instance).transform.position + Vector3.up * s_terrainModifierLevelOffsetRef.Invoke(__instance); __state = TryBeginSuppressedPlacementEffect(s_terrainModifierOnPlacedEffectRef.Invoke(__instance), checkPosition); } } private static Exception Finalizer(bool __state, Exception __exception) { EndSuppressedPlacementEffect(__state); return __exception; } } [HarmonyPatch(typeof(TerrainOp), "OnPlaced")] private static class TerrainOp_OnPlaced_BedrockEffectContext { private static void Prefix(TerrainOp __instance, ref bool __state) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_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) //IL_0048: Unknown result type (might be due to invalid IL or missing references) __state = false; if (!((Object)(object)__instance == (Object)null)) { Settings val = s_terrainOpSettingsRef.Invoke(__instance); Vector3 checkPosition = ((Component)__instance).transform.position + Vector3.up * val.m_levelOffset; __state = TryBeginSuppressedPlacementEffect(s_terrainOpOnPlacedEffectRef.Invoke(__instance), checkPosition); } } private static Exception Finalizer(bool __state, Exception __exception) { EndSuppressedPlacementEffect(__state); return __exception; } } [HarmonyPatch(typeof(EffectList), "Create", new Type[] { typeof(Vector3), typeof(Quaternion), typeof(Transform), typeof(float), typeof(int) })] private static class EffectList_Create_BedrockParticleSuppress { private static bool Prefix(EffectList __instance, Vector3 __0, ref GameObject[] __result) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (!IsSuppressedPlacementEffect(__instance) && !ShouldSuppressPickaxeTerrainEffect(__instance, __0)) { return true; } __result = Array.Empty<GameObject>(); return false; } } private struct ActivePickaxeSwingState { public bool Active; public Player Player; public float PaidStaminaCost; public bool HitDirt; public bool HitNonDirt; } [HarmonyPatch(typeof(Attack), "GetAttackStamina")] public static class Attack_GetAttackStamina_PickaxeCost_Patch { [HarmonyPostfix] private static void Postfix(Attack __instance, ref float __result) { try { if (IsFeatureEnabled(EnablePickaxeStaminaTweaks) && TryGetLocalPickaxeSwingPlayer(__instance, out var _) && TryGetOverridePickaxeStaminaCost(__instance, out var staminaCost)) { __result = staminaCost; } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error overriding pickaxe stamina: {arg}"); } } } } [HarmonyPatch(typeof(Attack), "DoMeleeAttack")] public static class Attack_DoMeleeAttack_PickaxeRefund_Patch { [HarmonyPrefix] private static void Prefix(Attack __instance, out bool __state) { __state = false; try { if (IsFeatureEnabled(EnablePickaxeDirtOnlyRefund) && TryGetLocalPickaxeSwingPlayer(__instance, out var player)) { float paidStaminaCost = Mathf.Max(0f, GetCurrentAttackStaminaCost(__instance)); BeginLocalPickaxeSwing(player, paidStaminaCost); __state = true; } } catch (Exception arg) { s_activePickaxeSwing = default(ActivePickaxeSwingState); ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error starting pickaxe refund tracking: {arg}"); } } } [HarmonyPostfix] private static void Postfix(bool __state) { if (!__state) { return; } try { FinishLocalPickaxeSwing(); } catch (Exception arg) { s_activePickaxeSwing = default(ActivePickaxeSwingState); ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error finishing pickaxe refund tracking: {arg}"); } } } [HarmonyFinalizer] private static Exception Finalizer(bool __state, Exception __exception) { if (__state) { s_activePickaxeSwing = default(ActivePickaxeSwingState); } return __exception; } } [HarmonyPatch(typeof(Attack), "SpawnOnHit")] public static class Attack_SpawnOnHit_PickaxeRefund_Patch { [HarmonyPrefix] private static void Prefix(GameObject target) { try { if (IsFeatureEnabled(EnablePickaxeDirtOnlyRefund) && (Object)(object)target != (Object)null) { RegisterLocalPickaxeNonDirtHit(); } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error registering pickaxe non-dirt hit: {arg}"); } } } } [HarmonyPatch(typeof(Attack), "SpawnOnHitTerrain")] public static class Attack_SpawnOnHitTerrain_PickaxeRefund_Patch { [HarmonyPrefix] private static void Prefix(Vector3 hitPoint) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) try { if (IsFeatureEnabled(EnablePickaxeDirtOnlyRefund)) { if (Heightmap.AtMaxLevelDepth(hitPoint)) { RegisterLocalPickaxeNonDirtHit(); } else { RegisterLocalPickaxeDirtHit(); } } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error registering pickaxe terrain hit: {arg}"); } } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] public static class ObjectDB_Awake_RestingDelay_Patch { private static void Postfix(ObjectDB __instance) { ApplyRestingDelayToTemplates(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] public static class ObjectDB_CopyOtherDB_RestingDelay_Patch { private static void Postfix(ObjectDB __instance) { ApplyRestingDelayToTemplates(__instance); } } [HarmonyPatch(typeof(SE_Cozy), "Setup")] public static class SE_Cozy_Setup_RestingDelay_Patch { private static void Postfix(SE_Cozy __instance) { ApplyRestingDelay(__instance); } } [HarmonyPatch(typeof(SE_Cozy), "UpdateStatusEffect")] public static class SE_Cozy_UpdateStatusEffect_RestingDelay_Patch { private static void Prefix(SE_Cozy __instance) { ApplyRestingDelay(__instance); } } [HarmonyPatch(typeof(Hud), "UpdateStatusEffects")] public static class Hud_UpdateStatusEffects_RestingCountdown_Patch { private static void Postfix(Hud __instance, List<StatusEffect> statusEffects) { if (!((Object)(object)__instance == (Object)null) && statusEffects != null && HudStatusEffectsField?.GetValue(__instance) is List<RectTransform> list) { int num = Mathf.Min(statusEffects.Count, list.Count); for (int i = 0; i < num; i++) { RectTransform statusEffectRoot = list[i]; StatusEffect obj = statusEffects[i]; UpdateRestingCountdown(statusEffectRoot, (SE_Cozy)(object)((obj is SE_Cozy) ? obj : null)); } } } private static void UpdateRestingCountdown(RectTransform statusEffectRoot, SE_Cozy cozyEffect) { if ((Object)(object)statusEffectRoot == (Object)null) { return; } if (!IsRestingEffect(cozyEffect)) { HideRestingCountdown(statusEffectRoot); return; } if (!IsFeatureEnabled(EnableRestingTweaks)) { ApplyRestingDelay(cozyEffect); HideRestingCountdown(statusEffectRoot); return; } ApplyRestingDelay(cozyEffect); TextMeshProUGUI val = EnsureRestingCountdownLabel(statusEffectRoot); if (!((Object)(object)val == (Object)null)) { PositionRestingCountdownLabel(statusEffectRoot, ((TMP_Text)val).rectTransform); int num = Mathf.CeilToInt(15f - ((StatusEffect)cozyEffect).GetDuration()); if (num > 0) { ((TMP_Text)val).text = num.ToString(); ((Component)val).gameObject.SetActive(true); } else { ((Component)val).gameObject.SetActive(false); } } } private static void HideRestingCountdown(RectTransform statusEffectRoot) { Transform val = ((Transform)statusEffectRoot).Find("BB_RestingCountdown"); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); } } private static TextMeshProUGUI EnsureRestingCountdownLabel(RectTransform statusEffectRoot) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: 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_00f1: 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_0181: Unknown result type (might be due to invalid IL or missing references) Transform val = ((Transform)statusEffectRoot).Find("BB_RestingCountdown"); if ((Object)(object)val != (Object)null) { return ((Component)val).GetComponent<TextMeshProUGUI>(); } Transform obj = ((Transform)statusEffectRoot).Find("Icon"); Transform obj2 = ((obj is RectTransform) ? obj : null); Transform obj3 = ((Transform)statusEffectRoot).Find("TimeText"); TMP_Text val2 = ((obj3 != null) ? ((Component)obj3).GetComponent<TMP_Text>() : null); if ((Object)(object)obj2 == (Object)null || (Object)(object)val2 == (Object)null) { return null; } GameObject val3 = new GameObject("BB_RestingCountdown", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }) { layer = ((Component)statusEffectRoot).gameObject.layer }; RectTransform component = val3.GetComponent<RectTransform>(); ((Transform)component).SetParent((Transform)(object)statusEffectRoot, false); ((Transform)component).SetAsLastSibling(); component.pivot = new Vector2(1f, 0f); component.anchorMin = new Vector2(0.5f, 0.5f); component.anchorMax = new Vector2(0.5f, 0.5f); component.sizeDelta = new Vector2(26f, 16f); PositionRestingCountdownLabel(statusEffectRoot, component); TextMeshProUGUI component2 = val3.GetComponent<TextMeshProUGUI>(); ((TMP_Text)component2).font = val2.font; ((TMP_Text)component2).fontSharedMaterial = val2.fontSharedMaterial; ((TMP_Text)component2).fontStyle = val2.fontStyle; ((TMP_Text)component2).fontSize = val2.fontSize; ((TMP_Text)component2).characterSpacing = val2.characterSpacing; ((TMP_Text)component2).wordSpacing = val2.wordSpacing; ((TMP_Text)component2).lineSpacing = val2.lineSpacing; ((TMP_Text)component2).enableAutoSizing = val2.enableAutoSizing; ((TMP_Text)component2).fontSizeMin = val2.fontSizeMin; ((TMP_Text)component2).fontSizeMax = val2.fontSizeMax; ((Graphic)component2).color = ((Graphic)val2).color; ((TMP_Text)component2).alignment = (TextAlignmentOptions)1028; ((TMP_Text)component2).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)component2).overflowMode = (TextOverflowModes)0; ((Graphic)component2).raycastTarget = false; ((Component)component2).gameObject.SetActive(false); return component2; } private static void PositionRestingCountdownLabel(RectTransform statusEffectRoot, RectTransform countdownRect) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) Transform obj = ((Transform)statusEffectRoot).Find("Icon"); RectTransform val = (RectTransform)(object)((obj is RectTransform) ? obj : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)countdownRect == (Object)null)) { Vector2 anchoredPosition = val.anchoredPosition; Rect rect = val.rect; float num = ((Rect)(ref rect)).width * (1f - val.pivot.x); rect = val.rect; Vector2 val2 = anchoredPosition + new Vector2(num, (0f - ((Rect)(ref rect)).height) * val.pivot.y); countdownRect.anchoredPosition = val2 + new Vector2(-2f, 4f); } } } private struct PendingThrownSpearContext { public bool Active; public string ThrowerIdentity; public string ExpectedItemName; } [HarmonyPatch(typeof(Projectile), "OnHit")] private static class Projectile_OnHit_ThrownSpearPickupRules_Patch { private static void Prefix(Projectile __instance, out PendingThrownSpearContext __state) { __state = s_pendingThrownSpearContext; if (TryCreatePendingThrownSpearContext(__instance, out var context)) { s_pendingThrownSpearContext = context; } } private static void Finalizer(PendingThrownSpearContext __state) { s_pendingThrownSpearContext = __state; } } [HarmonyPatch(typeof(ItemDrop), "DropItem", new Type[] { typeof(ItemData), typeof(int), typeof(Vector3), typeof(Quaternion) })] private static class ItemDrop_DropItem_ThrownSpearPickupRules_Patch { private static void Prefix(ItemData item) { TryTagPendingThrownSpearItem(item); } } [HarmonyPatch(typeof(Player), "AutoPickup")] private static class Player_AutoPickup_ThrownSpearPickupRules_Patch { private static void Prefix(out int __state) { __state = s_autoPickupContextDepth; s_autoPickupContextDepth = __state + 1; } private static void Finalizer(int __state) { s_autoPickupContextDepth = __state; } } [HarmonyPatch(typeof(ItemDrop), "CanPickup", new Type[] { typeof(bool) })] private static class ItemDrop_CanPickup_ThrownSpearPickupRules_Patch { private static void Postfix(ItemDrop __instance, ref bool __result) { if (IsFeatureEnabled(EnableThrownSpearPickupProtection) && __result && IsInAutoPickupContext() && __instance?.m_itemData != null && TryGetThrownSpearThrowerIdentity(__instance.m_itemData, out var throwerIdentity) && TryGetPlayerIdentity(Player.m_localPlayer, out var playerIdentity) && !string.Equals(playerIdentity, throwerIdentity, StringComparison.Ordinal)) { __result = false; } } } [HarmonyPatch(typeof(ItemDrop), "RequestOwn")] private static class ItemDrop_RequestOwn_ThrownSpearPickupRules_Patch { private static bool Prefix(ItemDrop __instance) { if (!IsFeatureEnabled(EnableThrownSpearPickupProtection)) { return true; } if (!IsInAutoPickupContext() || __instance?.m_itemData == null) { return true; } if (!TryGetThrownSpearThrowerIdentity(__instance.m_itemData, out var throwerIdentity)) { return true; } if (!TryGetPlayerIdentity(Player.m_localPlayer, out var playerIdentity)) { return true; } return string.Equals(playerIdentity, throwerIdentity, StringComparison.Ordinal); } } [HarmonyPatch(typeof(Humanoid), "Pickup", new Type[] { typeof(GameObject), typeof(bool), typeof(bool) })] private static class Humanoid_Pickup_ThrownSpearPickupRules_Patch { private static void Postfix(GameObject go, bool __result) { if (__result && !((Object)(object)go == (Object)null)) { ItemDrop component = go.GetComponent<ItemDrop>(); if (component?.m_itemData != null) { ClearThrownSpearThrowerIdentity(component.m_itemData); } } } } [HarmonyPatch(typeof(ProximityState), "OnTriggerEnter")] private static class ProximityState_OnTriggerEnter_StoneOvenDoorDelay_Patch { private static void Postfix(ProximityState __instance, Collider other) { if (ShouldDelayStoneOvenDoor(__instance) && IsAcceptedProximityCollider(__instance, other)) { CancelStoneOvenDoorClose(__instance); } } } [HarmonyPatch(typeof(ProximityState), "Start")] private static class ProximityState_Start_StoneOvenDoorRadius_Patch { private static void Postfix(ProximityState __instance) { if (ShouldDelayStoneOvenDoor(__instance)) { TryApplyStoneOvenDoorTriggerRadius(__instance, out var _, out var _); } } } [HarmonyPatch(typeof(ProximityState), "OnTriggerExit")] private static class ProximityState_OnTriggerExit_StoneOvenDoorDelay_Patch { private static bool Prefix(ProximityState __instance, Collider other) { if (!ShouldDelayStoneOvenDoor(__instance)) { return true; } List<Collider> list = s_proximityStateNearRef.Invoke(__instance); list?.Remove(other); if ((list == null || list.Count == 0) && (Object)(object)__instance.m_animator != (Object)null && __instance.m_animator.GetBool("near")) { ScheduleStoneOvenDoorClose(__instance); } return false; } } [HarmonyPatch] private static class Player_UpdateTeleport_TransitionFailsafe_Patch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Player), "UpdateTeleport", (Type[])null, (Type[])null); } private static void Postfix(Player __instance) { TrackLocalTeleportState(__instance); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] private static class ObjectDB_Awake_WeaponKnockbackTweaks_Patch { private static void Postfix(ObjectDB __instance) { ApplyWeaponKnockbackTweaks(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] private static class ObjectDB_CopyOtherDB_WeaponKnockbackTweaks_Patch { private static void Postfix(ObjectDB __instance) { ApplyWeaponKnockbackTweaks(__instance); } } [HarmonyPatch(typeof(SE_Demister), "Setup")] private static class SE_Demister_Setup_WisplightAutoRetract_Patch { private static void Prefix(SE_Demister __instance) { if (GetWisplightOutsideMistlandsBehavior() != 0 && (Object)(object)__instance != (Object)null) { ((StatusEffect)__instance).m_startMessage = string.Empty; } } } [HarmonyPatch(typeof(SE_Demister), "UpdateStatusEffect")] private static class SE_Demister_UpdateStatusEffect_WisplightAutoRetract_Patch { private static bool Prefix(SE_Demister __instance) { if (!ShouldOverrideWisplightBall(__instance)) { return true; } if (GetWisplightOutsideMistlandsBehavior() == WisplightOutsideMistlandsMode.Off) { HideWisplightBall(__instance); } else { PinWisplightBallToChest(__instance); } return false; } } [HarmonyPatch(typeof(ObjectDB), "Awake")] public static class ObjectDB_Awake_Patch { private static readonly MethodInfo s_updateRegistersMethod = AccessTools.Method(typeof(ObjectDB), "UpdateRegisters", (Type[])null, (Type[])null); private static void Postfix(ObjectDB __instance) { if (!IsFeatureEnabled(EnableCustomHoes)) { return; } try { EnsureCustomHoeRegistrations(__instance); } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error while registering custom hoe recipes: {arg}"); } } } private static void EnsureFlintHoeRegistered(ObjectDB objectDB) { EnsureCustomHoeRegistered(objectDB, FlintHoePrefab, "HoeFlint", "Recipe_HoeFlint", "Flint", "Flint Hoe"); } private static void EnsureBronzeHoeRegistered(ObjectDB objectDB) { EnsureCustomHoeRegistered(objectDB, BronzeHoePrefab, "HoeBronze", "Recipe_HoeBronze", "Bronze", "Bronze Hoe"); } internal static void EnsureCustomHoeRegistrations(ObjectDB objectDB) { if (IsFeatureEnabled(EnableCustomHoes)) { EnsureCustomHoePrefabsAvailable(objectDB); EnsureFlintHoeRegistered(objectDB); EnsureBronzeHoeRegistered(objectDB); } } internal static void EnsureCustomHoePrefabsAvailable(ObjectDB objectDB) { EnsureCustomHoePrefabAvailable(objectDB, ref FlintHoePrefab, "HoeFlint", "Flint Hoe", "A flint-edged hoe for wider roadwork and brush clearing.", ref FlintHoeBaseStaminaCost, ref FlintHoeExtraClearStaminaCost); EnsureCustomHoePrefabAvailable(objectDB, ref BronzeHoePrefab, "HoeBronze", "Bronze Hoe", "A bronze hoe that clears more brush with each swing. Can also clear dead tree logs and stumps.", ref BronzeHoeBaseStaminaCost, ref BronzeHoeExtraClearStaminaCost); } private static void EnsureCustomHoePrefabAvailable(ObjectDB objectDB, ref GameObject customHoePrefab, string customHoePrefabName, string displayName, string description, ref float baseStaminaCost, ref float extraClearStaminaCost) { if ((Object)(object)objectDB == (Object)null) { return; } GameObject itemPrefab = objectDB.GetItemPrefab("Hoe"); ItemDrop val = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null); if ((Object)(object)itemPrefab == (Object)null || (Object)(object)val == (Object)null) { return; } val.m_itemData.m_shared.m_name = "Stone Hoe"; if ((Object)(object)customHoePrefab == (Object)null) { customHoePrefab = CreateDetachedCustomHoePrefab(itemPrefab, customHoePrefabName); } ItemDrop component = customHoePrefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("[ValheimBB] ItemDrop missing on " + displayName + " prefab.")); } return; } component.m_itemData.m_dropPrefab = customHoePrefab; component.m_itemData.m_shared.m_name = displayName; component.m_itemData.m_shared.m_description = description; float num = val.m_itemData.m_shared.m_attack?.m_attackStamina ?? 5f; baseStaminaCost = num * 0.2f; extraClearStaminaCost = Mathf.Max(0f, num * 0.5f - baseStaminaCost); if (component.m_itemData.m_shared.m_attack != null) { component.m_itemData.m_shared.m_attack.m_attackStamina = baseStaminaCost; } EnsureDetachedClonedPieceTable(val, component, customHoePrefabName + "_PieceTable"); } private static GameObject CreateDetachedCustomHoePrefab(GameObject originalHoePrefab, string prefabName) { bool activeSelf = originalHoePrefab.activeSelf; originalHoePrefab.SetActive(false); GameObject obj = Object.Instantiate<GameObject>(originalHoePrefab); originalHoePrefab.SetActive(activeSelf); ((Object)obj).name = prefabName; obj.SetActive(false); Object.DontDestroyOnLoad((Object)(object)obj); return obj; } private static PieceTable EnsureDetachedClonedPieceTable(ItemDrop originalHoeItem, ItemDrop customHoeItem, string pieceTableName) { PieceTable val = customHoeItem.m_itemData?.m_shared?.m_buildPieces; if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)null && ((Object)((Component)val).gameObject).name == pieceTableName) { return val; } PieceTable val2 = originalHoeItem.m_itemData?.m_shared?.m_buildPieces; if ((Object)(object)val2 == (Object)null || (Object)(object)((Component)val2).gameObject == (Object)null) { return null; } GameObject obj = Object.Instantiate<GameObject>(((Component)val2).gameObject); ((Object)obj).name = pieceTableName; Object.DontDestroyOnLoad((Object)(object)obj); PieceTable component = obj.GetComponent<PieceTable>(); customHoeItem.m_itemData.m_shared.m_buildPieces = component; return component; } private static void EnsureCustomHoeRegistered(ObjectDB objectDB, GameObject customHoePrefab, string customHoePrefabName, string recipeName, string replacementRequirementPrefab, string displayName) { if ((Object)(object)objectDB == (Object)null || objectDB.m_items == null || objectDB.m_recipes == null || (Object)(object)customHoePrefab == (Object)null) { return; } GameObject itemPrefab = objectDB.GetItemPrefab("Hoe"); if ((Object)(object)itemPrefab != (Object)null) { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null) { component.m_itemData.m_shared.m_name = "Stone Hoe"; } } if (!objectDB.m_items.Contains(customHoePrefab)) { objectDB.m_items.Add(customHoePrefab); } ItemDrop component2 = customHoePrefab.GetComponent<ItemDrop>(); if ((Object)(object)component2 == (Object)null) { return; } Recipe val = null; Recipe val2 = null; foreach (Recipe recipe in objectDB.m_recipes) { if (!((Object)(object)recipe == (Object)null) && !((Object)(object)recipe.m_item == (Object)null)) { string text = CleanPrefabName(((Object)recipe.m_item).name); if (text == "Hoe") { val = recipe; } if (text == customHoePrefabName || string.Equals(((Object)recipe).name, recipeName, StringComparison.OrdinalIgnoreCase)) { val2 = recipe; } } } if ((Object)(object)val == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("[ValheimBB] Could not find the vanilla Hoe recipe to clone for " + displayName + ".")); } RefreshObjectDB(objectDB); return; } if ((Object)(object)val2 == (Object)null) { val2 = Object.Instantiate<Recipe>(val); objectDB.m_recipes.Add(val2); } ((Object)val2).name = recipeName; val2.m_item = component2; val2.m_enabled = true; val2.m_resources = BuildCustomHoeRequirements(objectDB, val.m_resources, replacementRequirementPrefab); RefreshObjectDB(objectDB); } private static Requirement[] BuildCustomHoeRequirements(ObjectDB objectDB, Requirement[] sourceRequirements, string replacementRequirementPrefab) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown if (sourceRequirements == null || sourceRequirements.Length == 0) { return Array.Empty<Requirement>(); } List<Requirement> list = new List<Requirement>(sourceRequirements.Length); foreach (Requirement val in sourceRequirements) { if (val == null || (Object)(object)val.m_resItem == (Object)null) { continue; } Requirement val2 = new Requirement { m_resItem = val.m_resItem, m_amount = val.m_amount, m_amountPerLevel = val.m_amountPerLevel, m_recover = val.m_recover }; if (string.Equals(CleanPrefabName(((Object)val.m_resItem).name), "Stone", StringComparison.OrdinalIgnoreCase)) { GameObject itemPrefab = objectDB.GetItemPrefab(replacementRequirementPrefab); ItemDrop val3 = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val3 != (Object)null) { val2.m_resItem = val3; } } list.Add(val2); } return list.ToArray(); } private static void RefreshObjectDB(ObjectDB objectDB) { s_updateRegistersMethod?.Invoke(objectDB, null); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] public static class ObjectDB_CopyOtherDB_CustomHoeRegistration_Patch { private static void Postfix(ObjectDB __instance) { if (!IsFeatureEnabled(EnableCustomHoes)) { return; } try { ObjectDB_Awake_Patch.EnsureCustomHoeRegistrations(__instance); } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error while re-registering custom hoes after ObjectDB copy: {arg}"); } } } } [HarmonyPatch(typeof(Player), "OnSpawned")] public static class Player_OnSpawned_CustomHoeRepair_Patch { private static void Postfix(Player __instance) { if (!IsFeatureEnabled(EnableCustomHoes) || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } try { RepairCustomItemInventory(((Humanoid)__instance).GetInventory()); } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error while repairing custom hoe inventory items: {arg}"); } } } } [HarmonyPatch(typeof(Inventory), "Save")] public static class Inventory_Save_CustomHoeRepair_Patch { private static void Prefix(Inventory __instance, out List<SavedCustomItemDropPrefabState> __state) { __state = null; if (!IsFeatureEnabled(EnableCustomHoes)) { return; } try { __state = PrepareCustomItemInventoryForSave(__instance); } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error while repairing custom hoes before inventory save: {arg}"); } } } private static void Postfix(List<SavedCustomItemDropPrefabState> __state) { RestoreCustomItemInventoryAfterSave(__state); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] public static class Inventory_AddItem_CustomHoeRepair_Patch { private static void Prefix(ItemData item) { if (IsFeatureEnabled(EnableCustomHoes)) { NormalizeCustomItem(item); } } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData), typeof(Vector2i) })] public static class Inventory_AddItemAtPos_CustomHoeRepair_Patch { private static void Prefix(ItemData item) { if (IsFeatureEnabled(EnableCustomHoes)) { NormalizeCustomItem(item); } } } [HarmonyPatch] public static class Inventory_AddItemToGrid_CustomHoeRepair_Patch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Inventory), "AddItem", new Type[4] { typeof(ItemData), typeof(int), typeof(int), typeof(int) }, (Type[])null); } private static void Prefix(ItemData item) { if (IsFeatureEnabled(EnableCustomHoes)) { NormalizeCustomItem(item); } } } [HarmonyPatch] public static class Inventory_AddSerializedItem_CustomHoeAlias_Patch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Inventory), "AddItem", new Type[12] { typeof(string), typeof(int), typeof(float), typeof(Vector2i), typeof(bool), typeof(int), typeof(int), typeof(long), typeof(string), typeof(Dictionary<string, string>), typeof(int), typeof(bool) }, (Type[])null); } private static void Prefix(ref string name, Dictionary<string, string> customData) { if (!IsFeatureEnabled(EnableCustomHoes)) { return; } string text = name; if (!TryResolveSerializedCustomItemRuntimePrefab(name, customData, out var resolvedName)) { return; } name = resolvedName; if (!string.Equals(CleanPrefabName(text), resolvedName, StringComparison.OrdinalIgnoreCase)) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("[ValheimBB] Rewrote serialized custom item save name '" + text + "' to '" + resolvedName + "'.")); } } } } [HarmonyPatch(typeof(Humanoid), "EquipItem")] public static class Humanoid_EquipItem_CustomHoeRepair_Patch { private static void Prefix(ItemData item) { if (IsFeatureEnabled(EnableCustomHoes) && IsCustomHoeItem(item)) { NormalizeCustomItem(item); } } } [HarmonyPatch(typeof(ObjectDB), "GetItemPrefab", new Type[] { typeof(string) })] public static class ObjectDB_GetItemPrefab_CustomHoeAlias_Patch { private static bool Prefix(ObjectDB __instance, string name, ref GameObject __result) { if (!IsFeatureEnabled(EnableCustomHoes)) { return true; } if (!TryResolveCustomItemPrefab(__instance, null, name, out var prefab)) { return true; } __result = prefab; return false; } } [HarmonyPatch] public static class ObjectDB_TryGetItemPrefab_CustomHoeAlias_Patch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(ObjectDB), "TryGetItemPrefab", new Type[2] { typeof(string), typeof(GameObject).MakeByRefType() }, (Type[])null); } private static bool Prefix(string name, ref bool __result, ref GameObject prefab) { if (!IsFeatureEnabled(EnableCustomHoes)) { return true; } if (!TryResolveCustomItemPrefab(ObjectDB.instance, null, name, out var prefab2)) { return true; } prefab = prefab2; __result = (Object)(object)prefab2 != (Object)null; return false; } } private struct TreeBaseDropSuppressState { public bool Active; public DropTable DropWhenDestroyed; } private struct TreeLogDropSuppressState { public bool Active; public DropTable DropWhenDestroyed; public GameObject SubLogPrefab; } private struct DestructibleDropSuppressState { public bool Active; public GameObject SpawnWhenDestroyed; } private struct HoeClearDropSuppressState { public bool Active; } [HarmonyPatch] private static class DamageText_ShowText_HoeClearSuppress_Patch { [CompilerGenerated] private sealed class <TargetMethods>d__0 : IEnumerable<MethodBase>, IEnumerable, IEnumerator<MethodBase>, IDisposable, IEnumerator { private int <>1__state; private MethodBase <>2__current; private int <>l__initialThreadId; private MethodInfo[] <>7__wrap1; private int <>7__wrap2; MethodBase IEnumerator<MethodBase>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <TargetMethods>d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_006e; } <>1__state = -1; <>7__wrap1 = typeof(DamageText).GetMethods(BindingFlags.Instance | BindingFlags.Public); <>7__wrap2 = 0; goto IL_007c; IL_006e: <>7__wrap2++; goto IL_007c; IL_007c: if (<>7__wrap2 < <>7__wrap1.Length) { MethodInfo methodInfo = <>7__wrap1[<>7__wrap2]; if (methodInfo.Name == "ShowText") { <>2__current = methodInfo; <>1__state = 1; return true; } goto IL_006e; } <>7__wrap1 = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<MethodBase> IEnumerable<MethodBase>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new <TargetMethods>d__0(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<MethodBase>)this).GetEnumerator(); } } [IteratorStateMachine(typeof(<TargetMethods>d__0))] private static IEnumerable<MethodBase> TargetMethods() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <TargetMethods>d__0(-2); } private static bool Prefix() { if (s_hoeClearDamageTextSuppressDepth <= 0) { return Time.realtimeSinceStartup >= s_hoeClearDamageTextSuppressUntil; } return false; } } [HarmonyPatch(typeof(TreeBase), "RPC_Damage")] private static class TreeBase_RPCDamage_HoeClearNoDrops_Patch { private static void Prefix(TreeBase __instance, HitData hit, out TreeBaseDropSuppressState __state) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown __state = default(TreeBaseDropSuppressState); if (!((Object)(object)__instance == (Object)null) && IsHoeClearNoDropsHit(hit)) { __state = new TreeBaseDropSuppressState { Active = true, DropWhenDestroyed = __instance.m_dropWhenDestroyed }; BeginHoeClearDropSuppress(); __instance.m_dropWhenDestroyed = new DropTable(); } } private static void Finalizer(TreeBase __instance, TreeBaseDropSuppressState __state) { if (__state.Active) { if ((Object)(object)__instance != (Object)null) { __instance.m_dropWhenDestroyed = __state.DropWhenDestroyed; } EndHoeClearDropSuppress(); } } } [HarmonyPatch(typeof(TreeBase), "SpawnLog")] private static class TreeBase_SpawnLog_HoeClearNoDrops_Patch { private static bool Prefix() { return !ShouldSuppressHoeClearDrops(); } } [HarmonyPatch(typeof(TreeLog), "Destroy")] private static class TreeLog_Destroy_HoeClearNoDrops_Patch { private static void Prefix(TreeLog __instance, HitData hitData, out TreeLogDropSuppressState __state) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown __state = default(TreeLogDropSuppressState); if (!((Object)(object)__instance == (Object)null) && IsHoeClearNoDropsHit(hitData)) { __state = new TreeLogDropSuppressState { Active = true, DropWhenDestroyed = __instance.m_dropWhenDestroyed, SubLogPrefab = __instance.m_subLogPrefab }; BeginHoeClearDropSuppress(); __instance.m_dropWhenDestroyed = new DropTable(); __instance.m_subLogPrefab = null; } } private static void Finalizer(TreeLog __instance, TreeLogDropSuppressState __state) { if (__state.Active) { if ((Object)(object)__instance != (Object)null) { __instance.m_dropWhenDestroyed = __state.DropWhenDestroyed; __instance.m_subLogPrefab = __state.SubLogPrefab; } EndHoeClearDropSuppress(); } } } [HarmonyPatch(typeof(Destructible), "Destroy", new Type[] { typeof(HitData) })] private static class Destructible_Destroy_HoeClearNoDrops_Patch { private static void Prefix(Destructible __instance, HitData hit, out DestructibleDropSuppressState __state) { __state = default(DestructibleDropSuppressState); if (!((Object)(object)__instance == (Object)null) && IsHoeClearNoDropsHit(hit)) { __state = new DestructibleDropSuppressState { Active = true, SpawnWhenDestroyed = __instance.m_spawnWhenDestroyed }; BeginHoeClearDropSuppress(); __instance.m_spawnWhenDestroyed = null; } } private static void Finalizer(Destructible __instance, DestructibleDropSuppressState __state) { if (__state.Active) { if ((Object)(object)__instance != (Object)null) { __instance.m_spawnWhenDestroyed = __state.SpawnWhenDestroyed; } EndHoeClearDropSuppress(); } } } [HarmonyPatch(typeof(WearNTear), "Destroy", new Type[] { typeof(HitData), typeof(bool) })] private static class WearNTear_Destroy_HoeClearNoDrops_Patch { private static void Prefix(HitData hitData, ref bool blockDrop, out HoeClearDropSuppressState __state) { __state = default(HoeClearDropSuppressState); if (IsHoeClearNoDropsHit(hitData)) { __state = new HoeClearDropSuppressState { Active = true }; BeginHoeClearDropSuppress(); blockDrop = true; } } private static void Finalizer(HoeClearDropSuppressState __state) { if (__state.Active) { EndHoeClearDropSuppress(); } } } [HarmonyPatch(typeof(DropOnDestroyed), "OnDestroyed")] private static class DropOnDestroyed_OnDestroyed_HoeClearNoDrops_Patch { private static bool Prefix() { return !ShouldSuppressHoeClearDrops(); } } [HarmonyPatch(typeof(Player), "PlacePiece")] internal static class PathenShrubClearPatch { private static readonly FieldInfo s_placementGhostField = AccessTools.Field(typeof(Player), "m_placementGhost"); private static readonly int s_shrubMask = LayerMask.GetMask(new string[5] { "Default", "Default_small", "piece", "piece_nonsolid", "static_solid" }); [HarmonyPostfix] private static void Postfix(Player __instance, Piece piece) { //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) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) if (!IsFeatureEnabled(EnableCustomHoes)) { ClearPendingClearState(); } else { if ((Object)(object)piece == (Object)null) { return; } if (!IsPathPiece(piece)) { ClearPendingClearState(); return; } object? value = s_placementGhostField.GetValue(__instance); GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val == (Object)null) { return; } ItemData equippedHoeItem = GetEquippedHoeItem(__instance); string customHoePrefabName = GetCustomHoePrefabName(equippedHoeItem); int customHoeClearTargetCap = GetCustomHoeClearTargetCap(equippedHoeItem); if (string.IsNullOrEmpty(customHoePrefabName) || customHoeClearTargetCap <= 0) { ClearPendingClearState(); return; } Vector3 position = val.transform.position; float terrainRadius = GetTerrainRadius(((Component)piece).gameObject); List<ClearCandidate> list = CollectClearCandidates(position, terrainRadius, s_shrubMask); if (list.Count == 0) { ClearPendingClearState(); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"[ValheimBB] Clear preview at {position}: no candidate roots found."); } return; } LogClearCatalog(position, terrainRadius, equippedHoeItem, list); List<ClearCandidate> allowedCandidatesOrdered = GetAllowedCandidatesOrdered(list, position, equippedHoeItem); int count = allowedCandidatesOrdered.Count; ClearCandidate clearCandidate = null; ClearCandidate clearCandidate2 = null; ClearCandidate clearCandidate3 = null; foreach (ClearCandidate item in allowedCandidatesOrdered) { if (clearCandidate3 == null) { clearCandidate3 = item; } if (clearCandidate == null && HasEffects(item.HitEffect)) { clearCandidate = item; } if (clearCandidate2 == null && HasEffects(item.DestroyEffect)) { clearCandidate2 = item; } } if (clearCandidate == null) { clearCandidate = clearCandidate2; } if (clearCandidate == null) { clearCandidate = clearCandidate3; } if (count == 0) { ClearPendingClearState(); return; } string pieceName = CleanPrefabName(((Object)piece).name); if (!IsConfirmedClearClick(pieceName, customHoePrefabName, position, terrainRadius)) { StorePendingClearState(pieceName, customHoePrefabName, position, terrainRadius); if (clearCandidate != null) { PlayCandidateEffect(clearCandidate, preferDestroyEffect: false, position); } return; } float customHoeExtraClearStaminaCost = GetCustomHoeExtraClearStaminaCost(equippedHoeItem); if (customHoeExtraClearStaminaCost > 0f && !((Character)__instance).HaveStamina(customHoeExtraClearStaminaCost)) { StorePendingClearState(pieceName, customHoePrefabName, position, terrainRadius); ((Character)__instance).Message((MessageType)2, "Not enough stamina to clear brush.", 0, (Sprite)null); if (clearCandidate != null) { PlayCandidateEffect(clearCandidate, preferDestroyEffect: false, position); } return; } int num = 0; int num2 = Mathf.Min(customHoeClearTargetCap, allowedCandidatesOrdered.Count); for (int i = 0; i < num2; i++) { ClearCandidate candidate = allowedCandidatesOrdered[i]; if (DestroyCandidate(__instance, equippedHoeItem, candidate)) { num++; } } if (allowedCandidatesOrdered.Count > num) { StorePendingClearState(pieceName, customHoePrefabName, position, terrainRadius); } else { ClearPendingClearState(); } if (num > 0) { ApplyExtraClearStamina(__instance, equippedHoeItem); ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)$"[ValheimBB] Cleared {num} brush object(s) at {position} (radius {terrainRadius:F1}) using {customHoePrefabName}."); } } } } private static bool IsPathPiece(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } string text = CleanPrefabName(((Object)piece).name); if (!text.EndsWith("_clear", StringComparison.OrdinalIgnoreCase)) { return false; } if (!text.StartsWith("path", StringComparison.OrdinalIgnoreCase)) { return text.StartsWith("mud_road", StringComparison.OrdinalIgnoreCase); } return true; } } private sealed class CustomItemDefinition { public string ItemId; public int PersistenceVersion; public string RuntimePrefabName; public string SurrogatePrefabName; public string DisplayName; public string LegacyPrefabMarkerValue; public string LegacyPieceTableName; public string[] SpawnAliases; public Func<GameObject> GetRuntimePrefab; public Action<ObjectDB, ZNetScene> EnsureRuntimePrefabAvailable; } private sealed class SavedCustomItemDropPrefabState { public ItemData Item; public GameObject OriginalDropPrefab; } [HarmonyPatch(typeof(ZNetScene), "GetPrefab", new Type[] { typeof(string) })] public static class ZNetScene_GetPrefab_CustomItemAlias_Patch { private static bool Prefix(ZNetScene __instance, string name, ref GameObject __result) { if (!TryResolveCustomItemPrefab(null, __instance, name, out var prefab)) { return true; } __result = prefab; return false; } } [HarmonyPatch(typeof(ZNetScene), "GetPrefabNames")] public static class ZNetScene_GetPrefabNames_CustomItemAlias_Patch { private static void Postfix(List<string> __result) { if (!IsFeatureEnabled(EnableCustomHoes) || __result == null) { return; } CustomItemDefinition[] s_customItemDefinitions = ValheimBBPlugin.s_customItemDefinitions; foreach (CustomItemDefinition customItemDefinition in s_customItemDefinitions) { if (customItemDefinition == null) { continue; } if (!__result.Contains(customItemDefinition.RuntimePrefabName)) { __result.Add(customItemDefinition.RuntimePrefabName); } if (customItemDefinition.SpawnAliases == null) { continue; } string[] spawnAliases = customItemDefinition.SpawnAliases; foreach (string item in spawnAliases) { if (!__result.Contains(item)) { __result.Add(item); } } } } } [HarmonyPatch(typeof(ItemDrop), "DropItem", new Type[] { typeof(ItemData), typeof(int), typeof(Vector3), typeof(Quaternion) })] public static class ItemDrop_DropItem_CustomItemSurrogate_Patch { private static void Prefix(ref ItemData item) { ItemData val = CreateSurrogateWorldDropItem(item); if (val != null) { item = val; } } } [HarmonyPatch] public static class ItemDrop_LoadFromZDO_CustomItemRepair_Patch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(ItemDrop), "LoadFromZDO", new Type[2] { typeof(ItemData), typeof(ZDO) }, (Type[])null); } private static void Postfix(ItemData itemData) { NormalizeCustomItem(itemData); } } [HarmonyPatch] public static class ItemDrop_LoadIndexedFromZDO_CustomItemRepair_Patch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(ItemDrop), "LoadFromZDO", new Type[3] { typeof(int), typeof(ItemData), typeof(ZDO) }, (Type[])null); } private static void Postfix(ItemData itemData) { NormalizeCustomItem(itemData); } } [HarmonyPatch(typeof(Plant), "GetHoverText")] public static class ValheimBB_PlantHoverTimerPatch { [HarmonyPostfix] private static void AddGrowthTimer(Plant __instance, ref string __result) { if (!EnablePlantGrowthTimer.Value) { return; } try { if (!((Object)(object)__instance == (Object)null)) { string plantGrowthLabel = GetPlantGrowthLabel(__instance); if (!string.IsNullOrEmpty(plantGrowthLabel)) { string text = "<color=orange>(" + plantGrowthLabel + ")</color>"; __result = (string.IsNullOrEmpty(__result) ? text : (__result + "\n" + text)); } } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error adding plant growth timer: {arg}"); } } } } [HarmonyPatch(typeof(Pickable), "GetHoverText")] public static class ValheimBB_PickableBerryBushHoverPatch { [HarmonyPostfix] private static void ShowBerryBushRegrowthState(Pickable __instance, ref string __result) { if (!EnablePlantGrowthTimer.Value || !string.IsNullOrEmpty(__result)) { return; } try { if (!((Object)(object)__instance == (Object)null) && IsRegrowingPickable(__instance) && !(s_pickablePickedField == null) && (bool)s_pickablePickedField.GetValue(__instance)) { string hoverName = __instance.GetHoverName(); if (!string.IsNullOrEmpty(hoverName)) { string text = hoverName + " <color=orange>(" + GetPickableRegrowthLabel(__instance) + ")</color>"; __result = LocalizeHoverText(text); } } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error adding berry bush hover text: {arg}"); } } } } [HarmonyPatch(typeof(ZNetScene), "RemoveObjects")] private static class ZNetScene_RemoveObjects_InvalidInstanceGuard_Patch { private static void Prefix(ZNetScene __instance) { SanitizeTrackedSceneInstances(__instance); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] public static class ZNetScene_Awake_Patch { private const string LegacyPathPrefabName = "path"; private const string LegacyLevelGroundPrefabName = "mud_road"; private const string PathPrefabName = "path_v2"; private const string LevelGroundPrefabName = "mud_road_v2"; private const string LegacyPathClearPrefabName = "path_clear"; private const string LegacyLevelGroundClearPrefabName = "mud_road_clear"; private const string PathClearPrefabName = "path_v2_clear"; private const string LevelGroundClearPrefabName = "mud_road_v2_clear"; private static readonly FieldInfo s_prefabsField = AccessTools.Field(typeof(ZNetScene), "m_prefabs"); private static readonly FieldInfo s_namedPrefabsField = AccessTools.Field(typeof(ZNetScene), "m_namedPrefabs"); private static readonly Dictionary<string, string> s_hoeIconFiles = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "path", "clear_path.png" }, { "path_v2", "clear_path.png" }, { "mud_road", "clear_ground.png" }, { "mud_road_v2", "clear_ground.png" } }; private const string HiddenPrefabContainerName = "_ValheimBB_HiddenPrefabs"; private static readonly Dictionary<string, Sprite> s_hoeIconCache = new Dictionary<string, Sprite>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, GameObject> s_clearPieceVariantPrefabs = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, GameObject> s_hoeTablePiecePrefabs = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, float> s_baseTorchSecPerFuel = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private static GameObject s_hiddenPrefabContainer; private static void Postfix(ZNetScene __instance) { try { ConfigureTorchFuelSettings(__instance); } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[ValheimBB] Error while tweaking torch fuel settings: {arg}"); } } try { ConfigureStoneOvenDoorPrefab(__instance); } catch (Exception arg2) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)$"[ValheimBB] Error while tweaking stone oven door behavior: {arg2}"); } } if (IsFeatureEnabled(EnableStandingTorchAndKilnTweaks)) { try { GameObject prefab = __instance.GetPrefab("piece_groundtorch_wood"); if ((Object)(object)prefab == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"[ValheimBB] Could not find 'piece_groundtorch_wood' in ZNetScene."); } } else { Piece component = prefab.GetComponent<Piece>(); if ((Object)(object)component == (Object)null) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogWarning((object)"[ValheimBB] Prefab 'piece_groundtorch_wood' has no Piece component."); } } else { if ((Object)(object)component.m_craftingStation != (Object)null) { ManualLogSource log5 = Log; if (log5 != null) { log5.LogInfo((object)("[ValheimBB] Standing Wood Torch originally required crafting station: " + component.m_craftingStation.m_name + ". Removing requirement.")); } } else { ManualLogSource log6 = Log; if (log6 != null) { log6.LogInfo((object)"[ValheimBB] Standing Wood Torch already had no crafting station set."); } } component.m_craftingStation = null; } } } catch (Exception arg3) { ManualLogSource log7 = Log; if (log7 != null) { log7.LogError((object)$"[ValheimBB] Error while tweaking torch build requirements: {arg3}"); } } } if (IsFeatureEnabled(EnableCustomHoes)) { try { if (!TryMatchTerrainRadius(__instance, "mud_road_v2", "path_v2")) { TryMatchTerrainRadius(__instance, "mud_road", "path"); } } catch (Exception arg4) { ManualLogSource log8 = Log; if (log8 != null) { log8.LogError((object)$"[ValheimBB] Error while widening path radius: {arg4}"); } } try { EnsureFlintHoePrefab(__instance); EnsureBronzeHoePrefab(__instance); ConfigureFlintHoePreviewEffect(__instance); ObjectDB_Awake_Patch.EnsureCustomHoeRegistrations(ObjectDB.instance); } catch (Exception arg5) { ManualLogSource log9 = Log; if (log9 != null) { log9.LogError((object)$"[ValheimBB] Error while creating custom hoe prefabs: {arg5}"); } } } if (!IsFeatureEnabled(EnableStandingTorchAndKilnTweaks)) { return; } try { GameObject prefab2 = __instance.GetPrefab("charcoal_kiln"); if ((Object)(object)prefab2 == (Object)null) { ManualLogSource log10 = Log; if (log10 != null) { log10.LogWarning((object)"[ValheimBB] charcoal_kiln prefab not found."); } return; } Smelter component2 = prefab2.GetComponent<Smelter>(); if ((Object)(object)component2 == (Object)null) { ManualLogSource log11 = Log; if (log11 != null) { log11.LogWarning((object)"[ValheimBB] Smelter component missing on charcoal_kiln prefab."); } return; } if (component2.m_conversion == null) { ManualLogSource log12 = Log; if (log12 != null) { log12.LogWarning((object)"[ValheimBB] charcoal_kiln Smelter has no conversion list."); } return; } int count = component2.m_conversion.Count; component2.m_conversion.RemoveAll(delegate(ItemConversion conv) { if (conv == null || (Object)(object)conv.m_from == (Object)null) { return false; } GameObject gameObject = ((Component)conv.m_from).gameObject; string obj = (((Object)(object)gameObject != (Object)null) ? ((Object)gameObject).name : string.Empty); string text = conv.m_from.m_itemData?.m_shared?.m_name ?? string.Empty; bool num2 = obj.Equals("FineWood", StringComparison.OrdinalIgnoreCase); bool flag = text.Equals("$item_finewood", StringComparison.OrdinalIgnoreCase); return num2 || flag; }); int count2 = component2.m_conversion.Count; int num = count - count2; ManualLogSource log13 = Log; if (log13 != null) { log13.LogInfo((object)$"[ValheimBB] charcoal_kiln conversion entries: {count} -> {count2} (removed {num} FineWood entries)."); } } catch (Exception arg6) { ManualLogSource log14 = Log; if (log14 != null) { log14.LogError((object)$"[ValheimBB] Error while tweaking charcoal_kiln conversions: {arg6}"); } } } private static void ConfigureTorchFuelSettings(ZNetScene scene) { if ((Object)(object)scene == (Object)null) { return; } if (!(s_prefabsField?.GetValue(scene) is List<GameObject> list) || list.Count == 0) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[ValheimBB] Could not read ZNetScene prefab list while syncing torch and brazier fuel settings."); } return; } int num = 0; foreach (GameObject item in list) { if (!TryGetTorchOrBrazierFireplace(item, out var fireplace)) { continue; } string text = CleanPrefabName(((Object)item).name); if (!s_baseTorchSecPerFuel.TryGetValue(text, out var value)) { value = fireplace.m_secPerFuel; s_baseTorchSecPerFuel[text] = value; } float secPerFuel = value * 2f; float secPerFuel2 = fireplace.m_secPerFuel; float maxFuel = fireplace.m_maxFuel; fireplace.m_secPerFuel = secPerFuel; if (string.Equals(text, "piece_groundtorch_wood", StringComparison.OrdinalIgnoreCase) && IsFeatureEnabled(EnableStandingTorchAndKilnTweaks)) { fireplace.m_maxFuel = 5f; } if (!Mathf.Approximately(secPerFuel2, fireplace.m_secPerFuel) || !Mathf.Approximately(maxFuel, fireplace.m_maxFuel)) { num++; ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)$"[ValheimBB] Fuel updated for '{text}': max fuel {maxFuel:0.#} -> {fireplace.m_maxFuel:0.#}, sec/fuel {secPerFuel2:0.#} -> {fireplace.m_secPerFuel:0.#}."); } } } ManualLogSource log3 = Log; if (log3 != null) { log3.LogInfo((object)$"[ValheimBB] Updated burn time for {num} torch/brazier prefabs."); } } private static bool TryGetTorchOrBrazierFireplace(GameObject prefab, out Fireplace fireplace) { fireplace = ((prefab != null) ? prefab.GetComponent<Fireplace>() : null); if ((Object)(object)fireplace == (Object)null || (Object)(object)prefab.GetComponent<Piece>() == (Object)null) { return false; } string text = CleanPrefabName(((Object)prefab).name); if (string.IsNullOrWhiteSpace(text)) { return false; } if (text.IndexOf("torch", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("brazier", StringComparison.OrdinalIgnoreCase) < 0) { return text.IndexOf("sconce", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } internal static void EnsureFlintHoePrefab(ZNetScene scene) { GameObject prefab = scene.GetPrefab("Hoe"); if ((Object)(object)prefab == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[ValheimBB] Hoe prefab not found."); } return; } ItemDrop component = prefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[ValheimBB] ItemDrop missing on Hoe prefab."); } return; } component.m_itemData.m_shared.m_name = "Stone Hoe"; if ((Object)(object)FlintHoePrefab == (Object)null) { FlintHoePrefab = CreateCustomHoePrefab(prefab, "HoeFlint"); } ParkCloneAsHiddenPrefab(FlintHoePrefab); RegisterPrefabWithScene(scene, FlintHoePrefab); ConfigureFlintHoePrefab(component, FlintHoePrefab, scene); } internal static void EnsureBronzeHoePrefab(ZNetScene scene) { GameObject prefab = scene.GetPrefab("Hoe"); if ((Object)(object)prefab == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[ValheimBB] Hoe prefab not found."); } return; } ItemDrop component = prefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[ValheimBB] ItemDrop missing on Hoe prefab."); } return; } component.m_itemData.m_shared.m_name = "Stone Hoe"; if ((Object)(object)BronzeHoePrefab == (Object)null) { BronzeHoePrefab = CreateCustomHoePrefab(prefab, "HoeBronze"); } ParkCloneAsHiddenPrefab(BronzeHoePrefab); RegisterPrefabWithScene(scene, BronzeHoePrefab); ConfigureBronzeHoePrefab(component, BronzeHoePrefab, scene); } private static GameObject CreateCustomHoePrefab(GameObject originalHoePrefab, string prefabName) { bool activeSelf = originalHoePrefab.activeSelf; originalHoePrefab.SetActive(false); GameObject obj = Object.Instantiate<GameObject>(originalHoePrefab); originalHoePrefab.SetActive(activeSelf); ((Object)obj).name = prefabName; obj.SetActive(false); Object.DontDestroyOnLoad((Object)(object)obj); return obj; } private static void ConfigureFlintHoePreviewEffect(ZNetScene scene) { if (!((Object)(object)scene == (Object)null) && !TrySetFlintHoePreviewEffect(scene, "Beech1") && !TrySetFlintHoePreviewEffect(scene, "FirTree_oldLog") && !TrySetFlintHoePreviewEffect(scene, "Beech_small1") && !TrySetFlintHoePreviewEffect(scene, "Beech_small2")) { FlintHoePreviewEffect = null; FlintHoePreviewEffectSource = null; ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[ValheimBB] Could not resolve a fixed Flint Hoe preview effect."); } } } private static bool TrySetFlintHoePreviewEffect(ZNetScene scene, string prefabName) { GameObject prefab = scene.GetPrefab(prefabName); if ((Object)(object)prefab == (Object)null) { return false; } EffectList val = null; TreeBase component = prefab.GetComponent<TreeBase>(); if ((Object)(object)component != (Object)null && HasEffects(component.m_hitEffect)) { val = component.m_hitEffect; } if (!HasEffects(val)) { TreeLog component2 = prefab.GetComponent<TreeLog>(); if ((Object)(object)component2 != (Object)null && HasEffects(component2.m_hitEffect)) { val = component2.m_hitEffect; } } if (!HasEffects(val)) { Destructible component3 = prefab.GetComponent<Destructible>(); if ((Object)(object)component3 != (Object)null && HasEffects(component3.m_hitEffect)) { val = component3.m_hitEffect; } } if (!HasEffects(val)) { WearNTear component4 = prefab.GetComponent<WearNTear>(); if ((Object)(object)component4 != (Object)null && HasEffects(component4.m_hitEffect)) { val = component4.m_hitEffect; } } if (!HasEffects(val)) { return false; } FlintHoePreviewEffect = val; FlintHoePreviewEffectSource = prefabName; ManualLogSource log = Log; if (log != null)