Decompiled source of OreSpreadQoL v1.0.2

OreSpreadQoL.dll

Decompiled 12 hours ago
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ConditionalConfigSync;
using HarmonyLib;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace OreSpreadQoL;

[BepInPlugin("community.valheim.OreSpreadQoL", "Ore Spread QoL", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "community.valheim.OreSpreadQoL";

	public const string PluginName = "Ore Spread QoL";

	public const string PluginVersion = "1.0.0";

	internal static ConfigSync Sync;

	internal static ConfigEntry<KeyboardShortcut> BypassKey;

	internal static ConfigEntry<bool> DebugLogging;

	internal static SyncedConfigEntry<bool> EnableLevelMode;

	internal static SyncedConfigEntry<float> MinDistance;

	internal static SyncedConfigEntry<bool> CopperEnabled;

	internal static SyncedConfigEntry<float> CopperMaxDistance;

	internal static SyncedConfigEntry<bool> IronEnabled;

	internal static SyncedConfigEntry<float> IronMaxDistance;

	internal static SyncedConfigEntry<bool> SilverEnabled;

	internal static SyncedConfigEntry<float> SilverMaxDistance;

	internal static SyncedConfigEntry<bool> SoftTissueEnabled;

	internal static SyncedConfigEntry<float> SoftTissueMaxDistance;

	internal static SyncedConfigEntry<bool> BlackMarbleEnabled;

	internal static SyncedConfigEntry<float> BlackMarbleMaxDistance;

	internal static SyncedConfigEntry<bool> StoneEnabled;

	internal static SyncedConfigEntry<float> StoneMaxDistance;

	internal static SyncedConfigEntry<bool> FlametalEnabled;

	internal static SyncedConfigEntry<float> FlametalMaxDistance;

	internal static SyncedConfigEntry<bool> OtherEnabled;

	internal static SyncedConfigEntry<float> OtherMaxDistance;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Expected O, but got Unknown
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fc: Expected O, but got Unknown
		ConfigSync val = new ConfigSync("community.valheim.OreSpreadQoL");
		((ConditionalConfigSync)val).DisplayName = "Ore Spread QoL";
		((ConditionalConfigSync)val).CurrentVersion = "1.0.0";
		((ConditionalConfigSync)val).MinimumRequiredVersion = "1.0.0";
		((ConditionalConfigSync)val).ModRequired = true;
		Sync = val;
		BypassKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "BypassKey", new KeyboardShortcut((KeyCode)308, (KeyCode[])(object)new KeyCode[0]), "Hold this key while mining to use vanilla mining for that swing. This is client-local and is not synced.");
		DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Enable debug diagnostics in BepInEx\\LogOutput.log. Client-local and not synced. Leave this off for normal play.");
		EnableLevelMode = ServerBool("General", "EnableLevelMode", defaultValue: true, "Scale spread radius with Pickaxes skill, like PickaxeMaster. At skill 0 use MinDistance; at skill 100 use the resource MaxDistance.");
		MinDistance = ServerFloat("General", "MinDistance", 1f, 0f, 50f, "Minimum spread radius at Pickaxes skill 0 when EnableLevelMode is enabled.");
		CopperEnabled = ServerBool("Copper", "Enabled", defaultValue: true, "Enable damage spreading for segmented copper deposits.");
		CopperMaxDistance = ServerFloat("Copper", "MaxDistance", 15f, 0f, 50f, "Copper spread radius at Pickaxes skill 100, or fixed radius when Level Mode is disabled.");
		IronEnabled = ServerBool("Iron", "Enabled", defaultValue: true, "Enable damage spreading for muddy scrap piles.");
		IronMaxDistance = ServerFloat("Iron", "MaxDistance", 1.5f, 0f, 50f, "Muddy scrap pile spread radius at Pickaxes skill 100, or fixed radius when Level Mode is disabled.");
		SilverEnabled = ServerBool("Silver", "Enabled", defaultValue: true, "Enable damage spreading for segmented silver veins.");
		SilverMaxDistance = ServerFloat("Silver", "MaxDistance", 4f, 0f, 50f, "Silver spread radius at Pickaxes skill 100, or fixed radius when Level Mode is disabled.");
		SoftTissueEnabled = ServerBool("Soft Tissue", "Enabled", defaultValue: true, "Enable damage spreading for giant-brain soft tissue mining.");
		SoftTissueMaxDistance = ServerFloat("Soft Tissue", "MaxDistance", 3.5f, 0f, 50f, "Soft tissue spread radius at Pickaxes skill 100, or fixed radius when Level Mode is disabled.");
		BlackMarbleEnabled = ServerBool("Black Marble", "Enabled", defaultValue: true, "Enable damage spreading for petrified giant bone / black marble mining.");
		BlackMarbleMaxDistance = ServerFloat("Black Marble", "MaxDistance", 2.5f, 0f, 50f, "Black marble spread radius at Pickaxes skill 100, or fixed radius when Level Mode is disabled.");
		StoneEnabled = ServerBool("Stone", "Enabled", defaultValue: true, "Enable damage spreading for ordinary segmented rocks.");
		StoneMaxDistance = ServerFloat("Stone", "MaxDistance", 15f, 0f, 50f, "Large segmented stone-deposit spread radius at Pickaxes skill 100, or fixed radius when Level Mode is disabled. Small non-segmented rocks remain vanilla.");
		FlametalEnabled = ServerBool("Flametal", "Enabled", defaultValue: true, "Enable damage spreading for current Flametal ore veins (LeviathanLava / MineRock).");
		FlametalMaxDistance = ServerFloat("Flametal", "MaxDistance", 2f, 0f, 50f, "Current Flametal ore-vein spread radius at Pickaxes skill 100, or fixed radius when Level Mode is disabled.");
		OtherEnabled = ServerBool("Other", "Enabled", defaultValue: true, "Enable damage spreading for other segmented MineRock5 resources.");
		OtherMaxDistance = ServerFloat("Other", "MaxDistance", 1.5f, 0f, 50f, "Fallback spread radius at Pickaxes skill 100, or fixed radius when Level Mode is disabled.");
		_harmony = new Harmony("community.valheim.OreSpreadQoL");
		_harmony.PatchAll();
	}

	private SyncedConfigEntry<bool> ServerBool(string section, string name, bool defaultValue, string description)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected O, but got Unknown
		return ((ConditionalConfigSync)Sync).AddConfigEntry<bool>(((BaseUnityPlugin)this).Config, section, name, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, new object[0]), (ConfigSyncMode)0, true);
	}

	private SyncedConfigEntry<float> ServerFloat(string section, string name, float defaultValue, float min, float max, string description)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Expected O, but got Unknown
		return ((ConditionalConfigSync)Sync).AddConfigEntry<float>(((BaseUnityPlugin)this).Config, section, name, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[0]), (ConfigSyncMode)0, true);
	}

	private void OnDestroy()
	{
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
		}
	}
}
[HarmonyPatch(typeof(MineRock5), "Damage")]
internal static class MineRock5DamagePatch
{
	private static readonly FieldInfo NViewField = AccessTools.Field(typeof(MineRock5), "m_nview");

	private static bool _redistributing;

	private static int _debugHits;

	private static bool Prefix(MineRock5 __instance, HitData hit)
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Expected O, but got Unknown
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)__instance == (Object)null || hit == null || _redistributing)
		{
			return true;
		}
		if (Plugin.BypassKey != null)
		{
			KeyboardShortcut value = Plugin.BypassKey.Value;
			if (((KeyboardShortcut)(ref value)).IsPressed())
			{
				return true;
			}
		}
		Character attacker = hit.GetAttacker();
		Player val = (Player)(object)((attacker is Player) ? attacker : null);
		if ((Object)(object)val == (Object)null)
		{
			val = TryResolvePlayerFromAttackerId(hit);
		}
		float effectiveRadius = GetEffectiveRadius(__instance, val);
		if (effectiveRadius <= 0f || hit.m_damage.m_pickaxe <= 0f)
		{
			return true;
		}
		Collider[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<Collider>();
		if (componentsInChildren == null || componentsInChildren.Length == 0)
		{
			return true;
		}
		float radiusSq = effectiveRadius * effectiveRadius;
		Vector3 hitPoint = hit.m_point;
		List<Collider> list = componentsInChildren.Where(delegate(Collider c)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)c != (Object)null)
			{
				Vector3 val4 = hitPoint;
				Bounds bounds2 = c.bounds;
				Vector3 val5 = val4 - ((Bounds)(ref bounds2)).center;
				return ((Vector3)(ref val5)).sqrMagnitude <= radiusSq;
			}
			return false;
		}).ToList();
		DebugHit(__instance, val, effectiveRadius, componentsInChildren.Length, list.Count, hit);
		if (list.Count <= 1)
		{
			return true;
		}
		float pickaxe = hit.m_damage.m_pickaxe / (float)list.Count;
		_redistributing = true;
		try
		{
			foreach (Collider item in list)
			{
				HitData val2 = new HitData();
				val2.m_damage = new DamageTypes
				{
					m_pickaxe = pickaxe
				};
				Bounds bounds = item.bounds;
				val2.m_point = ((Bounds)(ref bounds)).center;
				val2.m_hitCollider = item;
				val2.m_dir = hit.m_dir;
				val2.m_attacker = hit.m_attacker;
				val2.m_pushForce = hit.m_pushForce;
				val2.m_toolTier = hit.m_toolTier;
				HitData val3 = val2;
				MineRock5 componentInParent = ((Component)item).GetComponentInParent<MineRock5>();
				if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent != (Object)(object)__instance)
				{
					componentInParent.Damage(val3);
				}
				else
				{
					__instance.Damage(val3);
				}
			}
		}
		finally
		{
			_redistributing = false;
		}
		return false;
	}

	private static float GetEffectiveRadius(MineRock5 rock, Player player)
	{
		GetResourceSettings(rock, out var enabled, out var maxDistance);
		if (!enabled || maxDistance <= 0f)
		{
			return 0f;
		}
		if (Plugin.EnableLevelMode == null || !Plugin.EnableLevelMode.Value)
		{
			return maxDistance;
		}
		float num = ((Plugin.MinDistance != null) ? Plugin.MinDistance.Value : 1f);
		num = Mathf.Clamp(num, 0f, maxDistance);
		float num2 = (((Object)(object)player != (Object)null) ? Mathf.Clamp01(((Character)player).GetSkillFactor((SkillType)12)) : 0f);
		return Mathf.Lerp(num, maxDistance, num2);
	}

	private static Player TryResolvePlayerFromAttackerId(HitData hit)
	{
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Type type = AccessTools.TypeByName("ZNetScene");
			if (type == null)
			{
				return null;
			}
			PropertyInfo propertyInfo = AccessTools.Property(type, "instance");
			object obj = ((propertyInfo != null) ? propertyInfo.GetValue(null, null) : null);
			if (obj == null)
			{
				return null;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "FindInstance", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return null;
			}
			object obj2 = methodInfo.Invoke(obj, new object[1] { hit.m_attacker });
			Component val = (Component)((obj2 is Component) ? obj2 : null);
			if ((Object)(object)val != (Object)null)
			{
				return val.GetComponent<Player>();
			}
			GameObject val2 = (GameObject)((obj2 is GameObject) ? obj2 : null);
			return ((Object)(object)val2 != (Object)null) ? val2.GetComponent<Player>() : null;
		}
		catch
		{
			return null;
		}
	}

	private static void DebugHit(MineRock5 rock, Player player, float radius, int totalColliders, int affectedColliders, HitData hit)
	{
		if (Plugin.DebugLogging != null && Plugin.DebugLogging.Value && _debugHits < 12)
		{
			_debugHits++;
			string text = (((Object)(object)rock != (Object)null && (Object)(object)((Component)rock).gameObject != (Object)null) ? ((Object)((Component)rock).gameObject).name : "<null>");
			string text2 = (((Object)(object)rock != (Object)null) ? rock.m_name : "<null>");
			string hierarchyIdentity = GetHierarchyIdentity(rock);
			string networkPrefabIdentity = GetNetworkPrefabIdentity(rock);
			string materialIdentity = GetMaterialIdentity(rock);
			string dropIdentity = GetDropIdentity(rock);
			GetDropStats(rock, out var dropMin, out var dropMax, out var dropChance, out var dropCount);
			Logger.CreateLogSource("Ore Spread QoL").LogInfo((object)("Mining debug #" + _debugHits + ": object='" + text + "' m_name='" + text2 + "' hierarchy='" + hierarchyIdentity + "' prefab='" + networkPrefabIdentity + "' materials='" + materialIdentity + "' drops='" + dropIdentity + "' health=" + (((Object)(object)rock != (Object)null) ? rock.m_health.ToString("0.##") : "?") + " minTier=" + (((Object)(object)rock != (Object)null) ? rock.m_minToolTier.ToString() : "?") + " dropChance=" + dropChance.ToString("0.###") + " dropMin=" + dropMin + " dropMax=" + dropMax + " dropCount=" + dropCount + " radius=" + radius.ToString("0.00") + " totalColliders=" + totalColliders + " affected=" + affectedColliders + " attackerPlayer=" + (((Object)(object)player != (Object)null) ? "yes" : "no") + " pickaxeDamage=" + hit.m_damage.m_pickaxe.ToString("0.00")));
		}
	}

	private static void GetResourceSettings(MineRock5 rock, out bool enabled, out float maxDistance)
	{
		string text = (((Object)(object)rock != (Object)null) ? (rock.m_name ?? string.Empty) : string.Empty);
		string text2 = (((Object)(object)rock != (Object)null && (Object)(object)((Component)rock).gameObject != (Object)null) ? (((Object)((Component)rock).gameObject).name ?? string.Empty) : string.Empty);
		string hierarchyIdentity = GetHierarchyIdentity(rock);
		string networkPrefabIdentity = GetNetworkPrefabIdentity(rock);
		string materialIdentity = GetMaterialIdentity(rock);
		string dropIdentity = GetDropIdentity(rock);
		GetDropStats(rock, out var dropMin, out var dropMax, out var dropChance, out var _);
		int num = (((Object)(object)rock != (Object)null) ? rock.m_minToolTier : (-999));
		string value = networkPrefabIdentity + " " + text + " " + text2 + " " + hierarchyIdentity + " " + materialIdentity + " " + dropIdentity;
		if (Contains(materialIdentity, "rock_copper_internal") || Contains(materialIdentity, "rock1_copper") || Contains(dropIdentity, "copperore") || Contains(value, "rock4_copper") || Contains(value, "copper deposit"))
		{
			enabled = Plugin.CopperEnabled.Value;
			maxDistance = Plugin.CopperMaxDistance.Value;
		}
		else if (Contains(materialIdentity, "mudpile_ironscrap") || Contains(materialIdentity, "mudpile") || Contains(dropIdentity, "ironscrap") || Contains(value, "mudpile") || Contains(value, "mud pile"))
		{
			enabled = Plugin.IronEnabled.Value;
			maxDistance = Plugin.IronMaxDistance.Value;
		}
		else if (Contains(materialIdentity, "rock_silver_internal") || Contains(dropIdentity, "silverore") || Contains(value, "silver"))
		{
			enabled = Plugin.SilverEnabled.Value;
			maxDistance = Plugin.SilverMaxDistance.Value;
		}
		else if (Contains(materialIdentity, "giant_brain") || Contains(dropIdentity, "softtissue") || Contains(value, "softtissue") || Contains(value, "soft_tissue") || Contains(value, "giant_brain") || Contains(value, "giantbrain"))
		{
			enabled = Plugin.SoftTissueEnabled.Value;
			maxDistance = Plugin.SoftTissueMaxDistance.Value;
		}
		else if (Contains(materialIdentity, "giant_skeleton_exterior") || Contains(materialIdentity, "giant_skeleton_interior") || Contains(dropIdentity, "blackmarble") || Contains(value, "blackmarble") || Contains(value, "black_marble") || Contains(value, "giant_ribs") || Contains(value, "giant_skull") || Contains(value, "giant_bone") || Contains(value, "giantbone"))
		{
			enabled = Plugin.BlackMarbleEnabled.Value;
			maxDistance = Plugin.BlackMarbleMaxDistance.Value;
		}
		else if (Contains(dropIdentity, "flametal") || Contains(value, "flametal"))
		{
			enabled = Plugin.FlametalEnabled.Value;
			maxDistance = Plugin.FlametalMaxDistance.Value;
		}
		else if (num == 0 && Nearly(dropChance, 1f) && dropMin == 2 && dropMax == 4)
		{
			enabled = Plugin.CopperEnabled.Value;
			maxDistance = Plugin.CopperMaxDistance.Value;
		}
		else if (num == 0 && dropChance > 0f && dropChance <= 0.35f && dropMin == 1 && dropMax == 1)
		{
			enabled = Plugin.IronEnabled.Value;
			maxDistance = Plugin.IronMaxDistance.Value;
		}
		else if (num == 2 && Nearly(dropChance, 1f) && dropMin == 2 && dropMax == 3)
		{
			enabled = Plugin.SilverEnabled.Value;
			maxDistance = Plugin.SilverMaxDistance.Value;
		}
		else if (num >= 3 && Nearly(dropChance, 0.5f) && dropMin == 1 && dropMax == 1)
		{
			enabled = Plugin.SoftTissueEnabled.Value;
			maxDistance = Plugin.SoftTissueMaxDistance.Value;
		}
		else if (num >= 3 && Nearly(dropChance, 1f) && dropMin == 2 && dropMax == 4)
		{
			enabled = Plugin.BlackMarbleEnabled.Value;
			maxDistance = Plugin.BlackMarbleMaxDistance.Value;
		}
		else if (num >= 3 && Nearly(dropChance, 1f) && dropMin == 4 && dropMax == 8)
		{
			enabled = Plugin.FlametalEnabled.Value;
			maxDistance = Plugin.FlametalMaxDistance.Value;
		}
		else if (Contains(materialIdentity, "rock_internal") || Contains(dropIdentity, "stone") || Contains(value, "rock") || (num < 3 && Nearly(dropChance, 1f) && dropMin == 4 && dropMax == 8))
		{
			enabled = Plugin.StoneEnabled.Value;
			maxDistance = Plugin.StoneMaxDistance.Value;
		}
		else
		{
			enabled = Plugin.OtherEnabled.Value;
			maxDistance = Plugin.OtherMaxDistance.Value;
		}
	}

	private static string GetNetworkPrefabIdentity(MineRock5 rock)
	{
		if ((Object)(object)rock == (Object)null || NViewField == null)
		{
			return string.Empty;
		}
		try
		{
			object value = NViewField.GetValue(rock);
			if (value == null)
			{
				return string.Empty;
			}
			MethodInfo methodInfo = AccessTools.Method(value.GetType(), "GetPrefabName", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return string.Empty;
			}
			object obj = methodInfo.Invoke(value, null);
			return (obj as string) ?? string.Empty;
		}
		catch
		{
			return string.Empty;
		}
	}

	private static string GetHierarchyIdentity(MineRock5 rock)
	{
		if ((Object)(object)rock == (Object)null || (Object)(object)((Component)rock).transform == (Object)null)
		{
			return string.Empty;
		}
		try
		{
			List<string> list = new List<string>();
			Transform val = ((Component)rock).transform;
			int num = 0;
			while ((Object)(object)val != (Object)null && num++ < 12)
			{
				if (!string.IsNullOrEmpty(((Object)val).name))
				{
					list.Add(((Object)val).name);
				}
				val = val.parent;
			}
			return string.Join("/", list.ToArray());
		}
		catch
		{
			return string.Empty;
		}
	}

	private static string GetMaterialIdentity(MineRock5 rock)
	{
		if ((Object)(object)rock == (Object)null)
		{
			return string.Empty;
		}
		try
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(MineRock5), "m_meshRenderer");
			Renderer val = (Renderer)((fieldInfo != null) ? /*isinst with value type is only supported in some contexts*/: null);
			List<string> list = new List<string>();
			if ((Object)(object)val != (Object)null)
			{
				Material[] sharedMaterials = val.sharedMaterials;
				if (sharedMaterials != null)
				{
					Material[] array = sharedMaterials;
					foreach (Material val2 in array)
					{
						if ((Object)(object)val2 != (Object)null && !string.IsNullOrEmpty(((Object)val2).name))
						{
							list.Add(((Object)val2).name);
						}
					}
				}
			}
			Renderer[] componentsInChildren = ((Component)rock).GetComponentsInChildren<Renderer>();
			if (componentsInChildren != null)
			{
				Renderer[] array2 = componentsInChildren;
				foreach (Renderer val3 in array2)
				{
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					Material[] sharedMaterials2 = val3.sharedMaterials;
					if (sharedMaterials2 == null)
					{
						continue;
					}
					Material[] array3 = sharedMaterials2;
					foreach (Material val4 in array3)
					{
						if ((Object)(object)val4 != (Object)null && !string.IsNullOrEmpty(((Object)val4).name) && !list.Contains(((Object)val4).name))
						{
							list.Add(((Object)val4).name);
						}
					}
				}
			}
			return string.Join(",", list.ToArray());
		}
		catch
		{
			return string.Empty;
		}
	}

	private static string GetDropIdentity(MineRock5 rock)
	{
		//IL_0043: 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)
		if ((Object)(object)rock == (Object)null)
		{
			return string.Empty;
		}
		try
		{
			DropTable dropItems = rock.m_dropItems;
			if (dropItems == null || dropItems.m_drops == null)
			{
				return string.Empty;
			}
			List<string> list = new List<string>();
			foreach (DropData drop in dropItems.m_drops)
			{
				if ((Object)(object)drop.m_item != (Object)null && !string.IsNullOrEmpty(((Object)drop.m_item).name))
				{
					list.Add(((Object)drop.m_item).name);
				}
			}
			return string.Join(",", list.ToArray());
		}
		catch
		{
			return string.Empty;
		}
	}

	private static void GetDropStats(MineRock5 rock, out int dropMin, out int dropMax, out float dropChance, out int dropCount)
	{
		dropMin = -1;
		dropMax = -1;
		dropChance = -1f;
		dropCount = 0;
		if ((Object)(object)rock == (Object)null || rock.m_dropItems == null)
		{
			return;
		}
		try
		{
			DropTable dropItems = rock.m_dropItems;
			dropMin = dropItems.m_dropMin;
			dropMax = dropItems.m_dropMax;
			dropChance = dropItems.m_dropChance;
			dropCount = ((dropItems.m_drops != null) ? dropItems.m_drops.Count : 0);
		}
		catch
		{
		}
	}

	private static bool Nearly(float a, float b)
	{
		return Mathf.Abs(a - b) <= 0.011f;
	}

	private static bool Contains(string value, string fragment)
	{
		if (!string.IsNullOrEmpty(value))
		{
			return value.IndexOf(fragment, StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return false;
	}
}
[HarmonyPatch(typeof(MineRock), "Damage")]
internal static class MineRockFlametalDamagePatch
{
	private static readonly FieldInfo NViewField = AccessTools.Field(typeof(MineRock), "m_nview");

	private static readonly FieldInfo HitAreasField = AccessTools.Field(typeof(MineRock), "m_hitAreas");

	private static readonly MethodInfo GetAreaIndexMethod = AccessTools.Method(typeof(MineRock), "GetAreaIndex", new Type[1] { typeof(Collider) }, (Type[])null);

	private static bool _redistributing;

	private static int _debugHits;

	private static bool Prefix(MineRock __instance, HitData hit)
	{
		//IL_0021: 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_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_0265: Unknown result type (might be due to invalid IL or missing references)
		//IL_026c: Expected O, but got Unknown
		//IL_0270: Unknown result type (might be due to invalid IL or missing references)
		//IL_027f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0281: Unknown result type (might be due to invalid IL or missing references)
		//IL_028a: Unknown result type (might be due to invalid IL or missing references)
		//IL_028f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0293: Unknown result type (might be due to invalid IL or missing references)
		//IL_0298: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ee: 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)
		if ((Object)(object)__instance == (Object)null || hit == null || _redistributing)
		{
			return true;
		}
		if (Plugin.BypassKey != null)
		{
			KeyboardShortcut value = Plugin.BypassKey.Value;
			if (((KeyboardShortcut)(ref value)).IsPressed())
			{
				return true;
			}
		}
		if (!IsModernFlametal(__instance))
		{
			return true;
		}
		if (Plugin.FlametalEnabled == null || !Plugin.FlametalEnabled.Value)
		{
			return true;
		}
		Character attacker = hit.GetAttacker();
		Player val = (Player)(object)((attacker is Player) ? attacker : null);
		if ((Object)(object)val == (Object)null)
		{
			val = TryResolvePlayerFromAttackerId(hit);
		}
		float effectiveRadius = GetEffectiveRadius(val);
		if (effectiveRadius <= 0f || hit.m_damage.m_pickaxe <= 0f)
		{
			return true;
		}
		Collider[] array = ((HitAreasField != null) ? (HitAreasField.GetValue(__instance) as Collider[]) : null);
		if (array == null || array.Length == 0)
		{
			return true;
		}
		float num = effectiveRadius * effectiveRadius;
		Vector3 point = hit.m_point;
		int num2 = -1;
		try
		{
			if ((Object)(object)hit.m_hitCollider != (Object)null && GetAreaIndexMethod != null)
			{
				object obj = GetAreaIndexMethod.Invoke(__instance, new object[1] { hit.m_hitCollider });
				if (obj is int)
				{
					num2 = (int)obj;
				}
			}
		}
		catch
		{
			num2 = -1;
		}
		if (num2 < 0 || num2 >= array.Length)
		{
			DebugHit(__instance, val, effectiveRadius, array.Length, 0, num2, -1f, hit);
			return true;
		}
		Dictionary<int, Collider> dictionary = new Dictionary<int, Collider>();
		float num3 = float.MaxValue;
		for (int i = 0; i < array.Length; i++)
		{
			Collider val2 = array[i];
			if (!((Object)(object)val2 == (Object)null) && val2.enabled && !((Object)(object)((Component)val2).gameObject == (Object)null) && ((Component)val2).gameObject.activeInHierarchy)
			{
				Bounds bounds = val2.bounds;
				float num4 = ((Bounds)(ref bounds)).SqrDistance(point);
				if (num4 < num3)
				{
					num3 = num4;
				}
				if (!(num4 > num) || i == num2)
				{
					dictionary[i] = val2;
				}
			}
		}
		if (!dictionary.ContainsKey(num2) && (Object)(object)array[num2] != (Object)null)
		{
			dictionary[num2] = array[num2];
		}
		float nearestBoundsDistance = ((num3 < float.MaxValue) ? Mathf.Sqrt(num3) : (-1f));
		DebugHit(__instance, val, effectiveRadius, array.Length, dictionary.Count, num2, nearestBoundsDistance, hit);
		if (dictionary.Count <= 1)
		{
			return true;
		}
		float pickaxe = hit.m_damage.m_pickaxe / (float)dictionary.Count;
		_redistributing = true;
		try
		{
			foreach (Collider value2 in dictionary.Values)
			{
				HitData val3 = new HitData();
				val3.m_damage = new DamageTypes
				{
					m_pickaxe = pickaxe
				};
				Bounds bounds2 = value2.bounds;
				val3.m_point = ((Bounds)(ref bounds2)).center;
				val3.m_hitCollider = value2;
				val3.m_radius = 0f;
				val3.m_dir = hit.m_dir;
				val3.m_attacker = hit.m_attacker;
				val3.m_pushForce = hit.m_pushForce;
				val3.m_toolTier = hit.m_toolTier;
				val3.m_hitType = hit.m_hitType;
				HitData val4 = val3;
				__instance.Damage(val4);
			}
		}
		finally
		{
			_redistributing = false;
		}
		return false;
	}

	private static float GetEffectiveRadius(Player player)
	{
		float num = ((Plugin.FlametalMaxDistance != null) ? Plugin.FlametalMaxDistance.Value : 2f);
		if (num <= 0f)
		{
			return 0f;
		}
		if (Plugin.EnableLevelMode == null || !Plugin.EnableLevelMode.Value)
		{
			return num;
		}
		float num2 = ((Plugin.MinDistance != null) ? Plugin.MinDistance.Value : 1f);
		num2 = Mathf.Clamp(num2, 0f, num);
		float num3 = (((Object)(object)player != (Object)null) ? Mathf.Clamp01(((Character)player).GetSkillFactor((SkillType)12)) : 0f);
		return Mathf.Lerp(num2, num, num3);
	}

	private static bool IsModernFlametal(MineRock rock)
	{
		if ((Object)(object)rock == (Object)null)
		{
			return false;
		}
		string text = rock.m_name ?? string.Empty;
		string text2 = (((Object)(object)((Component)rock).gameObject != (Object)null) ? (((Object)((Component)rock).gameObject).name ?? string.Empty) : string.Empty);
		string hierarchyIdentity = GetHierarchyIdentity(rock);
		string networkPrefabIdentity = GetNetworkPrefabIdentity(rock);
		string dropIdentity = GetDropIdentity(rock);
		string value = text + " " + text2 + " " + hierarchyIdentity + " " + networkPrefabIdentity + " " + dropIdentity;
		if (!Contains(value, "LeviathanLava") && !Contains(text, "flametalore"))
		{
			return Contains(dropIdentity, "FlametalOreNew");
		}
		return true;
	}

	private static Player TryResolvePlayerFromAttackerId(HitData hit)
	{
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Type type = AccessTools.TypeByName("ZNetScene");
			if (type == null)
			{
				return null;
			}
			PropertyInfo propertyInfo = AccessTools.Property(type, "instance");
			object obj = ((propertyInfo != null) ? propertyInfo.GetValue(null, null) : null);
			if (obj == null)
			{
				return null;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "FindInstance", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return null;
			}
			object obj2 = methodInfo.Invoke(obj, new object[1] { hit.m_attacker });
			Component val = (Component)((obj2 is Component) ? obj2 : null);
			if ((Object)(object)val != (Object)null)
			{
				return val.GetComponent<Player>();
			}
			GameObject val2 = (GameObject)((obj2 is GameObject) ? obj2 : null);
			return ((Object)(object)val2 != (Object)null) ? val2.GetComponent<Player>() : null;
		}
		catch
		{
			return null;
		}
	}

	private static string GetNetworkPrefabIdentity(MineRock rock)
	{
		if ((Object)(object)rock == (Object)null || NViewField == null)
		{
			return string.Empty;
		}
		try
		{
			object value = NViewField.GetValue(rock);
			if (value == null)
			{
				return string.Empty;
			}
			MethodInfo methodInfo = AccessTools.Method(value.GetType(), "GetPrefabName", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return string.Empty;
			}
			object obj = methodInfo.Invoke(value, null);
			return (obj as string) ?? string.Empty;
		}
		catch
		{
			return string.Empty;
		}
	}

	private static string GetHierarchyIdentity(MineRock rock)
	{
		if ((Object)(object)rock == (Object)null || (Object)(object)((Component)rock).transform == (Object)null)
		{
			return string.Empty;
		}
		try
		{
			List<string> list = new List<string>();
			Transform val = ((Component)rock).transform;
			int num = 0;
			while ((Object)(object)val != (Object)null && num++ < 12)
			{
				if (!string.IsNullOrEmpty(((Object)val).name))
				{
					list.Add(((Object)val).name);
				}
				val = val.parent;
			}
			return string.Join("/", list.ToArray());
		}
		catch
		{
			return string.Empty;
		}
	}

	private static string GetDropIdentity(MineRock rock)
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)rock == (Object)null || rock.m_dropItems == null || rock.m_dropItems.m_drops == null)
		{
			return string.Empty;
		}
		try
		{
			List<string> list = new List<string>();
			foreach (DropData drop in rock.m_dropItems.m_drops)
			{
				if ((Object)(object)drop.m_item != (Object)null && !string.IsNullOrEmpty(((Object)drop.m_item).name))
				{
					list.Add(((Object)drop.m_item).name);
				}
			}
			return string.Join(",", list.ToArray());
		}
		catch
		{
			return string.Empty;
		}
	}

	private static void DebugHit(MineRock rock, Player player, float radius, int totalColliders, int affectedAreas, int directAreaIndex, float nearestBoundsDistance, HitData hit)
	{
		if (Plugin.DebugLogging != null && Plugin.DebugLogging.Value && _debugHits < 12)
		{
			_debugHits++;
			string text = (((Object)(object)rock != (Object)null && (Object)(object)((Component)rock).gameObject != (Object)null) ? ((Object)((Component)rock).gameObject).name : "<null>");
			string text2 = (((Object)(object)rock != (Object)null) ? rock.m_name : "<null>");
			string hierarchyIdentity = GetHierarchyIdentity(rock);
			string networkPrefabIdentity = GetNetworkPrefabIdentity(rock);
			string dropIdentity = GetDropIdentity(rock);
			Logger.CreateLogSource("Ore Spread QoL").LogInfo((object)("Flametal debug #" + _debugHits + ": object='" + text + "' m_name='" + text2 + "' hierarchy='" + hierarchyIdentity + "' prefab='" + networkPrefabIdentity + "' drops='" + dropIdentity + "' radius=" + radius.ToString("0.00") + " totalHitAreas=" + totalColliders + " directArea=" + directAreaIndex + " nearestBoundsDistance=" + nearestBoundsDistance.ToString("0.00") + " affectedAreas=" + affectedAreas + " attackerPlayer=" + (((Object)(object)player != (Object)null) ? "yes" : "no") + " pickaxeDamage=" + hit.m_damage.m_pickaxe.ToString("0.00")));
		}
	}

	private static bool Contains(string value, string fragment)
	{
		if (!string.IsNullOrEmpty(value))
		{
			return value.IndexOf(fragment, StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return false;
	}
}