using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("DadsVeinmine")] [assembly: AssemblyDescription("Vein mining for Valheim 1.0.12")] [assembly: AssemblyCompany("Dad_Is_Bored")] [assembly: AssemblyProduct("DadsVeinmine")] [assembly: AssemblyCopyright("Copyright 2026 Dad_Is_Bored")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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] [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 DadsVeinmine { [BepInPlugin("com.dadisbored.dadsveinmine", "DadsVeinmine", "1.0.0")] [BepInProcess("valheim.exe")] public sealed class DadsVeinminePlugin : BaseUnityPlugin { internal enum ActivationModes { HoldKey, AlwaysOn, Off } public const string PluginGuid = "com.dadisbored.dadsveinmine"; public const string PluginName = "DadsVeinmine"; public const string PluginVersion = "1.0.0"; internal static DadsVeinminePlugin Instance = null; internal static ConfigEntry ActivationMode = null; internal static ConfigEntry ActivationKey = null; internal static ConfigEntry SectionsPerFrame = null; internal static ConfigEntry ExcludedPrefabFragments = null; internal static ConfigEntry ProgressiveMode = null; internal static ConfigEntry ProgressiveRadiusMultiplier = null; internal static ConfigEntry MinimumProgressiveRadius = null; internal static ConfigEntry AdditionalStaminaPerSection = null; internal static ConfigEntry ExtraSkillGainMultiplier = null; private readonly List _jobs = new List(); private readonly HashSet _activeTargets = new HashSet(); private Harmony? _harmony; private static readonly FieldInfo MineRockHitAreasField = AccessTools.Field(typeof(MineRock), "m_hitAreas"); private static readonly FieldInfo MineRockViewField = AccessTools.Field(typeof(MineRock), "m_nview"); private static readonly FieldInfo MineRock5HitAreasField = AccessTools.Field(typeof(MineRock5), "m_hitAreas"); private static readonly FieldInfo MineRock5ViewField = AccessTools.Field(typeof(MineRock5), "m_nview"); private static readonly MethodInfo MineRock5SetupCollidersMethod = AccessTools.Method(typeof(MineRock5), "SetupColliders", (Type[])null, (Type[])null); private static readonly MethodInfo MineRock5LoadHealthMethod = AccessTools.Method(typeof(MineRock5), "LoadHealth", (Type[])null, (Type[])null); private static readonly Type MineRock5HitAreaType = AccessTools.Inner(typeof(MineRock5), "HitArea"); private static readonly FieldInfo MineRock5AreaColliderField = AccessTools.Field(MineRock5HitAreaType, "m_collider"); private static readonly FieldInfo MineRock5AreaHealthField = AccessTools.Field(MineRock5HitAreaType, "m_health"); private void Awake() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Expected O, but got Unknown //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Expected O, but got Unknown //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Expected O, but got Unknown Instance = this; ActivationMode = ((BaseUnityPlugin)this).Config.Bind("1 - Activation", "Mode", ActivationModes.HoldKey, "HoldKey requires the configured key, AlwaysOn activates on every valid pickaxe hit, and Off disables vein mining."); ActivationKey = ((BaseUnityPlugin)this).Config.Bind("1 - Activation", "Hold Key", new KeyboardShortcut((KeyCode)308, Array.Empty()), "Key held while striking a mine rock or ore deposit in HoldKey mode."); SectionsPerFrame = ((BaseUnityPlugin)this).Config.Bind("2 - Mining", "Sections Per Frame", 8, new ConfigDescription("Maximum sections sent through Valheim's native mining handler per frame.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 64), Array.Empty())); ExcludedPrefabFragments = ((BaseUnityPlugin)this).Config.Bind("2 - Mining", "Excluded Prefab Fragments", string.Empty, "Comma-separated prefab-name fragments that DadsVeinmine must ignore. Matching is case-insensitive."); ProgressiveMode = ((BaseUnityPlugin)this).Config.Bind("3 - Progressive Mining", "Enabled", false, "Limit vein mining to a radius derived from the player's Pickaxes skill."); ProgressiveRadiusMultiplier = ((BaseUnityPlugin)this).Config.Bind("3 - Progressive Mining", "Skill Radius Multiplier", 0.1f, new ConfigDescription("Pickaxes skill is multiplied by this value to determine mining radius.", (AcceptableValueBase)(object)new AcceptableValueRange(0.01f, 10f), Array.Empty())); MinimumProgressiveRadius = ((BaseUnityPlugin)this).Config.Bind("3 - Progressive Mining", "Minimum Radius", 1f, new ConfigDescription("Minimum radius used while progressive mining is enabled.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 100f), Array.Empty())); AdditionalStaminaPerSection = ((BaseUnityPlugin)this).Config.Bind("4 - Costs", "Additional Stamina Per Section", 0f, new ConfigDescription("Stamina charged for every section after the first. Zero disables the additional stamina cost.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1000f), Array.Empty())); ExtraSkillGainMultiplier = ((BaseUnityPlugin)this).Config.Bind("4 - Costs", "Extra Pickaxes Skill Gain", 0.2f, new ConfigDescription("Pickaxes skill gain applied for every section after the first. Zero disables additional skill gain.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty())); _harmony = new Harmony("com.dadisbored.dadsveinmine"); _harmony.PatchAll(typeof(DadsVeinminePlugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"DadsVeinmine 1.0.0 loaded for Valheim 1.0.12."); } private void Update() { for (int num = _jobs.Count - 1; num >= 0; num--) { MiningJob miningJob = _jobs[num]; int num2 = Mathf.Clamp(SectionsPerFrame.Value, 1, 64); while (num2-- > 0 && miningJob.NextTarget < miningJob.Targets.Count) { if (!ProcessNextTarget(miningJob)) { miningJob.NextTarget = miningJob.Targets.Count; break; } } if (miningJob.NextTarget >= miningJob.Targets.Count) { _activeTargets.Remove(miningJob.TargetInstanceId); _jobs.RemoveAt(num); } } } private bool ProcessNextTarget(MiningJob job) { //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)job.View == (Object)null || !job.View.IsValid() || (Object)(object)job.Player == (Object)null || ((Character)job.Player).IsDead()) { return false; } MiningTarget miningTarget = job.Targets[job.NextTarget]; if (job.NextTarget > 0) { if (((Humanoid)job.Player).GetCurrentWeapon() != job.Weapon) { return false; } float num = Mathf.Max(0f, AdditionalStaminaPerSection.Value); if (num > 0f && !((Character)job.Player).HaveStamina(num)) { return false; } if (job.Weapon.m_shared.m_useDurability) { if (job.Weapon.m_durability <= 0f) { return false; } float num2 = Mathf.Max(0f, job.Weapon.m_shared.m_useDurabilityDrain * Game.m_durabilityRate); job.Weapon.m_durability = Mathf.Max(0f, job.Weapon.m_durability - num2); } if (num > 0f) { ((Character)job.Player).UseStamina(num); } float num3 = Mathf.Max(0f, ExtraSkillGainMultiplier.Value); if (num3 > 0f) { ((Character)job.Player).RaiseSkill((SkillType)12, num3); } } HitData val = job.SourceHit.Clone(); val.m_hitCollider = miningTarget.Collider; val.m_point = miningTarget.Point; val.m_damage.m_pickaxe = Mathf.Max(val.m_damage.m_pickaxe, 1000000f); job.View.InvokeRPC(job.RpcName, new object[2] { val, miningTarget.AreaIndex }); job.NextTarget++; return true; } internal bool QueueMineRock(MineRock rock, HitData hit) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: 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_00e4: Unknown result type (might be due to invalid IL or missing references) if (!TryGetMiningContext((Component)(object)rock, hit, rock.m_minToolTier, out Player player, out ItemData weapon)) { return false; } object? value = MineRockViewField.GetValue(rock); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); Collider[] array = MineRockHitAreasField.GetValue(rock) as Collider[]; if ((Object)(object)val == (Object)null || !val.IsValid() || val.GetZDO() == null || array == null) { return false; } int instanceID = ((Object)rock).GetInstanceID(); if (_activeTargets.Contains(instanceID)) { return true; } List list = new List(); float maximumDistance = GetMaximumDistance(player); for (int i = 0; i < array.Length; i++) { Collider val2 = array[i]; if (!((Object)(object)val2 == (Object)null) && !(val.GetZDO().GetFloat("Health" + i, rock.GetHealth()) <= 0f)) { Bounds bounds = val2.bounds; Vector3 center = ((Bounds)(ref bounds)).center; if (Vector3.Distance(hit.m_point, center) <= maximumDistance) { list.Add(new MiningTarget(i, val2, center)); } } } return QueueJob(instanceID, val, "Hit", hit, player, weapon, list); } internal bool QueueMineRock5(MineRock5 rock, HitData hit) { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: 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_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) if (!TryGetMiningContext((Component)(object)rock, hit, rock.m_minToolTier, out Player player, out ItemData weapon)) { return false; } MineRock5SetupCollidersMethod.Invoke(rock, null); MineRock5LoadHealthMethod.Invoke(rock, null); object? value = MineRock5ViewField.GetValue(rock); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); IList list = MineRock5HitAreasField.GetValue(rock) as IList; if ((Object)(object)val == (Object)null || !val.IsValid() || list == null) { return false; } int instanceID = ((Object)rock).GetInstanceID(); if (_activeTargets.Contains(instanceID)) { return true; } List list2 = new List(); float maximumDistance = GetMaximumDistance(player); for (int i = 0; i < list.Count; i++) { object obj = list[i]; if (obj == null) { continue; } object? value2 = MineRock5AreaColliderField.GetValue(obj); Collider val2 = (Collider)((value2 is Collider) ? value2 : null); float num = (float)MineRock5AreaHealthField.GetValue(obj); if (!((Object)(object)val2 == (Object)null) && !(num <= 0f)) { Bounds bounds = val2.bounds; Vector3 center = ((Bounds)(ref bounds)).center; if (Vector3.Distance(hit.m_point, center) <= maximumDistance) { list2.Add(new MiningTarget(i, val2, center)); } } } return QueueJob(instanceID, val, "RPC_Damage", hit, player, weapon, list2); } private bool QueueJob(int instanceId, ZNetView view, string rpcName, HitData hit, Player player, ItemData weapon, List targets) { if ((Object)(object)view == (Object)null || !view.IsValid() || targets.Count == 0) { return false; } List targets2 = targets.OrderBy(delegate(MiningTarget target) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 val = target.Point - hit.m_point; return ((Vector3)(ref val)).sqrMagnitude; }).ToList(); _activeTargets.Add(instanceId); _jobs.Add(new MiningJob(instanceId, view, rpcName, hit.Clone(), player, weapon, targets2)); return true; } private static bool TryGetMiningContext(Component target, HitData hit, int minimumToolTier, out Player player, out ItemData weapon) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) player = null; weapon = null; if (!ActivationHeld() || hit == null || (Object)(object)target == (Object)null || IsExcluded(target)) { return false; } Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer) { return false; } player = val; ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon(); if (currentWeapon == null) { return false; } weapon = currentWeapon; if (weapon.GetDamage().m_pickaxe > 0f && hit.m_damage.m_pickaxe > 0f) { return hit.m_toolTier >= minimumToolTier; } return false; } private static bool ActivationHeld() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) switch (ActivationMode.Value) { case ActivationModes.AlwaysOn: return true; case ActivationModes.Off: return false; default: { KeyboardShortcut value = ActivationKey.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey == 0 || !Input.GetKey(((KeyboardShortcut)(ref value)).MainKey)) { return false; } return ((KeyboardShortcut)(ref value)).Modifiers.All((Func)Input.GetKey); } } } private static bool IsExcluded(Component target) { string prefabName = Utils.GetPrefabName(target.gameObject); return (from fragment in ExcludedPrefabFragments.Value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select fragment.Trim()).Any((string fragment) => fragment.Length > 0 && prefabName.IndexOf(fragment, StringComparison.OrdinalIgnoreCase) >= 0); } private static float GetMaximumDistance(Player player) { if (!ProgressiveMode.Value) { return float.PositiveInfinity; } float skillLevel = ((Character)player).GetSkills().GetSkillLevel((SkillType)12); return Mathf.Max(Mathf.Max(0.1f, MinimumProgressiveRadius.Value), skillLevel * Mathf.Max(0.01f, ProgressiveRadiusMultiplier.Value)); } private void OnDestroy() { _jobs.Clear(); _activeTargets.Clear(); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal sealed class MiningJob { internal readonly int TargetInstanceId; internal readonly ZNetView View; internal readonly string RpcName; internal readonly HitData SourceHit; internal readonly Player Player; internal readonly ItemData Weapon; internal readonly List Targets; internal int NextTarget; internal MiningJob(int targetInstanceId, ZNetView view, string rpcName, HitData sourceHit, Player player, ItemData weapon, List targets) { TargetInstanceId = targetInstanceId; View = view; RpcName = rpcName; SourceHit = sourceHit; Player = player; Weapon = weapon; Targets = targets; } } internal readonly struct MiningTarget { internal readonly int AreaIndex; internal readonly Collider Collider; internal readonly Vector3 Point; internal MiningTarget(int areaIndex, Collider collider, Vector3 point) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) AreaIndex = areaIndex; Collider = collider; Point = point; } } [HarmonyPatch(typeof(MineRock), "Damage")] internal static class MineRockDamagePatch { private static bool Prefix(MineRock __instance, HitData hit) { return !DadsVeinminePlugin.Instance.QueueMineRock(__instance, hit); } } [HarmonyPatch(typeof(MineRock5), "Damage")] internal static class MineRock5DamagePatch { private static bool Prefix(MineRock5 __instance, HitData hit) { return !DadsVeinminePlugin.Instance.QueueMineRock5(__instance, hit); } } }