Decompiled source of SpearFishing v1.0.2

SpearFishing.dll

Decompiled 4 hours ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Valheim Overhaul")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Catch fish with spears, harpoons, bows and crossbows. Modern replacement for Korppis Spearfishing.")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2")]
[assembly: AssemblyProduct("SpearFishing")]
[assembly: AssemblyTitle("SpearFishing")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 SpearFishing
{
	internal static class FishCorpse
	{
		internal const string KillRpc = "SpearFishing_Kill";

		internal static readonly int DeadHash = StringExtensionMethods.GetStableHashCode("SpearFishing_Dead");

		internal static bool IsMarkedDead(ZNetView nview)
		{
			if ((Object)(object)nview != (Object)null && nview.IsValid())
			{
				return nview.GetZDO().GetBool(DeadHash, false);
			}
			return false;
		}

		internal static bool IsMarkedDead(Fish fish)
		{
			if ((Object)(object)fish != (Object)null)
			{
				return IsMarkedDead(fish.m_nview);
			}
			return false;
		}

		internal static void MarkDead(Fish fish)
		{
			if (!((Object)(object)fish?.m_nview == (Object)null) && fish.m_nview.IsValid())
			{
				if (!fish.m_nview.IsOwner())
				{
					fish.m_nview.ClaimOwnership();
				}
				fish.m_nview.GetZDO().Set(DeadHash, true);
			}
		}

		internal static void RegisterKillRpc(Fish fish)
		{
			if ((Object)(object)fish?.m_nview == (Object)null || !fish.m_nview.IsValid())
			{
				return;
			}
			ZNetView nview = fish.m_nview;
			nview.Register("SpearFishing_Kill", (Action<long>)delegate
			{
				Fish component = ((Component)nview).GetComponent<Fish>();
				if ((Object)(object)component != (Object)null)
				{
					ConvertToCorpse(component);
				}
			});
		}

		internal static void BroadcastKill(Fish fish)
		{
			if (!((Object)(object)fish?.m_nview == (Object)null) && fish.m_nview.IsValid())
			{
				fish.m_nview.InvokeRPC(ZNetView.Everybody, "SpearFishing_Kill", Array.Empty<object>());
			}
		}

		internal static bool ConvertToCorpse(Fish fish)
		{
			//IL_004c: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)fish == (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)fish).GetComponent<SpearFishCorpseMarker>() != (Object)null)
			{
				return true;
			}
			GameObject gameObject = ((Component)fish).gameObject;
			ItemDrop component = ((Component)fish).GetComponent<ItemDrop>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			gameObject.AddComponent<SpearFishCorpseMarker>();
			Rigidbody component2 = gameObject.GetComponent<Rigidbody>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.linearVelocity = Vector3.zero;
				component2.angularVelocity = Vector3.zero;
				component2.useGravity = true;
				component2.isKinematic = false;
			}
			gameObject.transform.Rotate(0f, 0f, (Random.value > 0.5f) ? 90f : (-90f));
			if (component.m_itemData != null && fish.m_pickupItemStackSize > 0)
			{
				component.m_itemData.m_stack = fish.m_pickupItemStackSize;
			}
			EnsureFloating(component, gameObject.transform.position);
			Object.Destroy((Object)(object)fish);
			return true;
		}

		internal static void EnsureFloating(ItemDrop drop, Vector3 position)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)drop == (Object)null))
			{
				Floating val = drop.m_floating;
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)drop).GetComponent<Floating>();
				}
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)drop).gameObject.AddComponent<Floating>();
				}
				val.m_waterLevelOffset = 0.65f;
				drop.m_floating = val;
				float liquidLevel = Floating.GetLiquidLevel(position, 1f, (LiquidType)0);
				val.SetLiquidLevel(liquidLevel, (LiquidType)0, (Component)null);
			}
		}
	}
	internal sealed class SpearFishCorpseMarker : MonoBehaviour
	{
	}
	[HarmonyPatch(typeof(Projectile), "OnHit")]
	internal static class ProjectileOnHitPatch
	{
		private static readonly int HarpoonedHash = StringExtensionMethods.GetStableHashCode("Harpooned");

		private static bool Prefix(Projectile __instance, Collider collider, Vector3 hitPoint, bool water)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || (Object)(object)collider == (Object)null)
			{
				return true;
			}
			if (!TryGetLocalOwner(__instance, out var owner))
			{
				return true;
			}
			Fish val = FindFish(collider);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			if (FishCorpse.IsMarkedDead(val) || (Object)(object)((Component)val).GetComponent<SpearFishCorpseMarker>() != (Object)null)
			{
				return true;
			}
			if (!IsCatchAllowed(val, __instance, out var harpoon))
			{
				return true;
			}
			Vector3 position = ((Component)val).transform.position;
			string name = val.m_name;
			if (Plugin.DebugLog.Value)
			{
				Plugin.Log.LogInfo((object)$"Spear hit fish '{name}' with skill={__instance.m_skill}, harpoon={harpoon}, waterHit={water}");
			}
			if (!((harpoon || Plugin.AutoPickupAll.Value) ? TryAutoPickup(val, owner, harpoon) : TryMakeCorpse(val, owner, name)))
			{
				return true;
			}
			PlayCatchEffects(position, __instance, hitPoint);
			FinishProjectile(__instance, hitPoint);
			return false;
		}

		private static bool TryGetLocalOwner(Projectile projectile, out Humanoid owner)
		{
			Character owner2 = projectile.m_owner;
			owner = (Humanoid)(object)((owner2 is Humanoid) ? owner2 : null);
			if ((Object)(object)owner == (Object)null)
			{
				return false;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)owner != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			return true;
		}

		private static Fish FindFish(Collider collider)
		{
			GameObject val = Projectile.FindHitObject(collider);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Fish component = val.GetComponent<Fish>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			return val.GetComponentInParent<Fish>();
		}

		private static bool IsCatchAllowed(Fish fish, Projectile projectile, out bool harpoon)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Invalid comparison between Unknown and I4
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Invalid comparison between Unknown and I4
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Invalid comparison between Unknown and I4
			harpoon = IsHarpoon(projectile);
			if (Plugin.RequireInWater.Value && fish.IsOutOfWater())
			{
				return false;
			}
			if (!Object.op_Implicit((Object)(object)fish.m_nview) || !fish.m_nview.IsValid())
			{
				return false;
			}
			if (harpoon)
			{
				return Plugin.EnableHarpoons.Value;
			}
			SkillType skill = projectile.m_skill;
			if ((int)skill != 5)
			{
				if ((int)skill != 8)
				{
					if ((int)skill == 14)
					{
						return Plugin.EnableCrossbows.Value;
					}
					return IsEnabledByProjectileName(((Object)projectile).name);
				}
				return Plugin.EnableBows.Value;
			}
			return Plugin.EnableSpears.Value;
		}

		private static bool IsHarpoon(Projectile projectile)
		{
			if (projectile.m_statusEffectHash == HarpoonedHash)
			{
				return true;
			}
			string name = ((Object)projectile).name;
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			name = name.ToLowerInvariant();
			if (!name.Contains("harpoon"))
			{
				return name.Contains("chitinharpoon");
			}
			return true;
		}

		private static bool IsEnabledByProjectileName(string projectileName)
		{
			if (string.IsNullOrEmpty(projectileName))
			{
				return false;
			}
			string text = projectileName.ToLowerInvariant();
			if (text.Contains("harpoon") || text.Contains("chitinharpoon"))
			{
				return Plugin.EnableHarpoons.Value;
			}
			if (text.Contains("spear") || text.Contains("splitner") || text.Contains("wolffang"))
			{
				return Plugin.EnableSpears.Value;
			}
			if (text.Contains("bow_projectile") || text.Contains("arrow"))
			{
				return Plugin.EnableBows.Value;
			}
			if (text.Contains("arbalest") || text.Contains("bolt"))
			{
				return Plugin.EnableCrossbows.Value;
			}
			return false;
		}

		private static bool TryAutoPickup(Fish fish, Humanoid owner, bool harpoon)
		{
			if (!fish.Pickup(owner))
			{
				return false;
			}
			if (Plugin.ShowMessages.Value)
			{
				string text = (harpoon ? " $msg_harpoon_harpooned" : " $msg_added");
				((Character)owner).Message((MessageType)2, fish.m_name + text, 0, (Sprite)null);
			}
			GrantXp(owner);
			return true;
		}

		private static bool TryMakeCorpse(Fish fish, Humanoid owner, string fishName)
		{
			if ((Object)(object)((Component)fish).GetComponent<ItemDrop>() == (Object)null)
			{
				return TryAutoPickup(fish, owner, harpoon: false);
			}
			ZNetView nview = fish.m_nview;
			FishCorpse.MarkDead(fish);
			FishCorpse.BroadcastKill(fish);
			Fish val = (((Object)(object)nview != (Object)null) ? ((Component)nview).GetComponent<Fish>() : null);
			if ((Object)(object)val != (Object)null && !FishCorpse.ConvertToCorpse(val))
			{
				return false;
			}
			if (Plugin.ShowMessages.Value)
			{
				((Character)owner).Message((MessageType)2, fishName, 0, (Sprite)null);
			}
			GrantXp(owner);
			return true;
		}

		private static void GrantXp(Humanoid owner)
		{
			if (Plugin.GrantFishingXp.Value && !((Object)(object)owner == (Object)null))
			{
				float value = Plugin.FishingXpAmount.Value;
				if (!(value <= 0f))
				{
					((Character)owner).RaiseSkill((SkillType)104, value);
				}
			}
		}

		private static void PlayCatchEffects(Vector3 fishPos, Projectile projectile, Vector3 hitPoint)
		{
			//IL_0024: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.HitEffects.Value)
			{
				return;
			}
			if ((Object)(object)projectile != (Object)null && projectile.m_hitEffects != null)
			{
				projectile.m_hitEffects.Create(hitPoint, Quaternion.identity, (Transform)null, 1f, -1);
			}
			if (!((Object)(object)ZNetScene.instance == (Object)null))
			{
				GameObject prefab = ZNetScene.instance.GetPrefab("fx_TickBloodHit");
				if ((Object)(object)prefab != (Object)null)
				{
					Object.Instantiate<GameObject>(prefab, fishPos, Quaternion.identity);
				}
			}
		}

		private static void FinishProjectile(Projectile projectile, Vector3 hitPoint)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)projectile == (Object)null)
			{
				return;
			}
			projectile.m_didHit = true;
			((Component)projectile).transform.position = hitPoint;
			if ((Object)(object)projectile.m_nview != (Object)null && projectile.m_nview.IsValid())
			{
				if (projectile.m_nview.IsOwner())
				{
					projectile.m_nview.InvokeRPC("RPC_OnHit", Array.Empty<object>());
					ZNetScene.instance.Destroy(((Component)projectile).gameObject);
				}
			}
			else
			{
				Object.Destroy((Object)(object)((Component)projectile).gameObject);
			}
		}
	}
	[HarmonyPatch(typeof(Fish), "Start")]
	internal static class FishStartPatch
	{
		private static void Postfix(Fish __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				FishCorpse.RegisterKillRpc(__instance);
				if (FishCorpse.IsMarkedDead(__instance))
				{
					FishCorpse.ConvertToCorpse(__instance);
				}
			}
		}
	}
	[BepInPlugin("valheimoverhaul.spearfishing", "SpearFishing", "1.0.2")]
	[BepInIncompatibility("org.bepinex.plugins.spearfishing")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "valheimoverhaul.spearfishing";

		public const string PluginName = "SpearFishing";

		public const string PluginVersion = "1.0.2";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> EnableSpears;

		internal static ConfigEntry<bool> EnableHarpoons;

		internal static ConfigEntry<bool> EnableBows;

		internal static ConfigEntry<bool> EnableCrossbows;

		internal static ConfigEntry<bool> AutoPickupAll;

		internal static ConfigEntry<bool> RequireInWater;

		internal static ConfigEntry<bool> GrantFishingXp;

		internal static ConfigEntry<float> FishingXpAmount;

		internal static ConfigEntry<bool> ShowMessages;

		internal static ConfigEntry<bool> HitEffects;

		internal static ConfigEntry<bool> DebugLog;

		private void Awake()
		{
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			EnableSpears = ((BaseUnityPlugin)this).Config.Bind<bool>("Weapons", "Enable Spears", true, "Catch fish with thrown spears.");
			EnableHarpoons = ((BaseUnityPlugin)this).Config.Bind<bool>("Weapons", "Enable Harpoons", true, "Catch fish with the Abyssal Harpoon (auto-pickup).");
			EnableBows = ((BaseUnityPlugin)this).Config.Bind<bool>("Weapons", "Enable Bows", true, "Catch fish with bow arrows.");
			EnableCrossbows = ((BaseUnityPlugin)this).Config.Bind<bool>("Weapons", "Enable Crossbows", true, "Catch fish with crossbow bolts.");
			AutoPickupAll = ((BaseUnityPlugin)this).Config.Bind<bool>("Gameplay", "Auto Pickup All", false, "If true, all enabled weapons auto-add fish to inventory. Harpoons always auto-pickup.");
			RequireInWater = ((BaseUnityPlugin)this).Config.Bind<bool>("Gameplay", "Require In Water", true, "Only catch fish that are still swimming (not already stranded).");
			GrantFishingXp = ((BaseUnityPlugin)this).Config.Bind<bool>("Gameplay", "Grant Fishing XP", true, "Grant a small amount of Fishing skill XP on a successful catch.");
			FishingXpAmount = ((BaseUnityPlugin)this).Config.Bind<float>("Gameplay", "Fishing XP Amount", 0.5f, "Fishing skill XP granted per catch when Grant Fishing XP is enabled.");
			ShowMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("Feedback", "Show Messages", true, "Show center HUD messages when catching fish.");
			HitEffects = ((BaseUnityPlugin)this).Config.Bind<bool>("Feedback", "Hit Effects", true, "Play hit / blood VFX when a fish is speared.");
			DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Debug Log", false, "Log catch attempts to the BepInEx log.");
			new Harmony("valheimoverhaul.spearfishing").PatchAll();
			Log.LogInfo((object)"SpearFishing 1.0.2 loaded.");
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}