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)delegate { Fish component = ((Component)nview).GetComponent(); 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()); } } 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() != (Object)null) { return true; } GameObject gameObject = ((Component)fish).gameObject; ItemDrop component = ((Component)fish).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } gameObject.AddComponent(); Rigidbody component2 = gameObject.GetComponent(); 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(); } if ((Object)(object)val == (Object)null) { val = ((Component)drop).gameObject.AddComponent(); } 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() != (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(); if ((Object)(object)component != (Object)null) { return component; } return val.GetComponentInParent(); } 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() == (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() : 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(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()); 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 EnableSpears; internal static ConfigEntry EnableHarpoons; internal static ConfigEntry EnableBows; internal static ConfigEntry EnableCrossbows; internal static ConfigEntry AutoPickupAll; internal static ConfigEntry RequireInWater; internal static ConfigEntry GrantFishingXp; internal static ConfigEntry FishingXpAmount; internal static ConfigEntry ShowMessages; internal static ConfigEntry HitEffects; internal static ConfigEntry 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("Weapons", "Enable Spears", true, "Catch fish with thrown spears."); EnableHarpoons = ((BaseUnityPlugin)this).Config.Bind("Weapons", "Enable Harpoons", true, "Catch fish with the Abyssal Harpoon (auto-pickup)."); EnableBows = ((BaseUnityPlugin)this).Config.Bind("Weapons", "Enable Bows", true, "Catch fish with bow arrows."); EnableCrossbows = ((BaseUnityPlugin)this).Config.Bind("Weapons", "Enable Crossbows", true, "Catch fish with crossbow bolts."); AutoPickupAll = ((BaseUnityPlugin)this).Config.Bind("Gameplay", "Auto Pickup All", false, "If true, all enabled weapons auto-add fish to inventory. Harpoons always auto-pickup."); RequireInWater = ((BaseUnityPlugin)this).Config.Bind("Gameplay", "Require In Water", true, "Only catch fish that are still swimming (not already stranded)."); GrantFishingXp = ((BaseUnityPlugin)this).Config.Bind("Gameplay", "Grant Fishing XP", true, "Grant a small amount of Fishing skill XP on a successful catch."); FishingXpAmount = ((BaseUnityPlugin)this).Config.Bind("Gameplay", "Fishing XP Amount", 0.5f, "Fishing skill XP granted per catch when Grant Fishing XP is enabled."); ShowMessages = ((BaseUnityPlugin)this).Config.Bind("Feedback", "Show Messages", true, "Show center HUD messages when catching fish."); HitEffects = ((BaseUnityPlugin)this).Config.Bind("Feedback", "Hit Effects", true, "Play hit / blood VFX when a fish is speared."); DebugLog = ((BaseUnityPlugin)this).Config.Bind("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) { } } }