Decompiled source of CODFishPerks v1.3.0

FishPerksMod.dll

Decompiled 12 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
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 FishNet.Object;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("FishPerksMod")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("FishPerksMod")]
[assembly: AssemblyTitle("FishPerksMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace FishPerksMod
{
	public class FishPerkDefinition
	{
		public FishPerkType Type;

		public string Name;
	}
	public static class FishPerkDatabase
	{
		public static readonly Dictionary<FishPerkType, FishPerkDefinition> Definitions = new Dictionary<FishPerkType, FishPerkDefinition>
		{
			{
				FishPerkType.StoppingPower,
				new FishPerkDefinition
				{
					Type = FishPerkType.StoppingPower,
					Name = "Stopping Power"
				}
			},
			{
				FishPerkType.Juggernaut,
				new FishPerkDefinition
				{
					Type = FishPerkType.Juggernaut,
					Name = "Juggernaut"
				}
			},
			{
				FishPerkType.SpeedCola,
				new FishPerkDefinition
				{
					Type = FishPerkType.SpeedCola,
					Name = "Speed Cola"
				}
			},
			{
				FishPerkType.DoubleTap,
				new FishPerkDefinition
				{
					Type = FishPerkType.DoubleTap,
					Name = "Double Tap"
				}
			},
			{
				FishPerkType.FizzyYummy,
				new FishPerkDefinition
				{
					Type = FishPerkType.FizzyYummy,
					Name = "Fizzy Yummy"
				}
			},
			{
				FishPerkType.QuickFix,
				new FishPerkDefinition
				{
					Type = FishPerkType.QuickFix,
					Name = "Quick Fix"
				}
			},
			{
				FishPerkType.Hardline,
				new FishPerkDefinition
				{
					Type = FishPerkType.Hardline,
					Name = "Hardline"
				}
			},
			{
				FishPerkType.QuickRevive,
				new FishPerkDefinition
				{
					Type = FishPerkType.QuickRevive,
					Name = "Quick Revive"
				}
			}
		};
	}
	public static class FishPerkManager
	{
		public const float StoppingPowerDamageMulti = 1.55f;

		public const float JuggernautDamageTakenMulti = 0.1f;

		public const float SpeedColaReloadMulti = 1.4f;

		public const float DoubleTapFireRateMulti = 1.4f;

		public const float FizzyYummySpeedMulti = 1.3f;

		public const int QuickFixRestorePercent = 5;

		public const float HardlineMultiplier = 1.35f;

		public const float BuffDurationSeconds = 120f;

		public static bool VerboseLogging = true;

		private static readonly Dictionary<Creature, FishPerkType> _sessionPerkCache = new Dictionary<Creature, FishPerkType>();

		private static readonly List<FishPerkType> _perkBag = new List<FishPerkType>();

		private static int _perkBagCycle;

		private static readonly Dictionary<Player, (FishPerkType perk, float expireTime)> _activeBuffs = new Dictionary<Player, (FishPerkType, float)>();

		private static readonly Dictionary<Player, float> _quickFixNextTick = new Dictionary<Player, float>();

		public static ulong CurrentlyLoadingSteamId;

		public static SavedPlayer CurrentlyLoadingSavedPlayer;

		internal static void Announce(string message)
		{
			if ((Object)(object)Server.Instance != (Object)null && ((NetworkBehaviour)Server.Instance).IsServerInitialized)
			{
				ChatManager.ChatMessage("<i>" + message + "</i>");
			}
		}

		public static void ClearSession()
		{
			_sessionPerkCache.Clear();
			_perkBag.Clear();
			_perkBagCycle = 0;
			_activeBuffs.Clear();
			_quickFixNextTick.Clear();
		}

		public static FishPerkType GetOrAssignPerk(Creature creature)
		{
			if ((Object)(object)creature == (Object)null)
			{
				return FishPerkType.StoppingPower;
			}
			if (_sessionPerkCache.TryGetValue(creature, out var value))
			{
				return value;
			}
			FishPerkType fishPerkType = TakeNextPerkFromBag();
			_sessionPerkCache[creature] = fishPerkType;
			if (VerboseLogging)
			{
				Plugin.Log.LogInfo((object)$"[FishPerksMod] Assigned perk '{fishPerkType}' to '{SafeGetName(creature)}' (shuffled bag).");
			}
			return fishPerkType;
		}

		public static void AssignSpecificPerk(Creature creature, FishPerkType perk)
		{
			if (!((Object)(object)creature == (Object)null))
			{
				_sessionPerkCache[creature] = perk;
				if (VerboseLogging)
				{
					Plugin.Log.LogInfo((object)$"[FishPerksMod] Restored persisted perk '{perk}' on '{SafeGetName(creature)}'.");
				}
			}
		}

		private static int GetStableSeed(Creature creature)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				int num = ((NetworkBehaviour)creature).ObjectId;
				string text = SafeGetName(creature);
				string text2 = text;
				foreach (char c in text2)
				{
					num = num * 31 + c;
				}
				Vector3 position = ((Component)creature).transform.position;
				num = num * 31 + ((int)(position.x * 1000f) & 0xFFFF);
				num = num * 31 + ((int)(position.y * 1000f) & 0xFFFF);
				return num * 31 + ((int)(position.z * 1000f) & 0xFFFF);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[FishPerksMod] Could not generate stable seed - falling back to GetInstanceID(). The assigned perk may NOT match across clients! (" + ex.Message + ")"));
				return ((Object)creature).GetInstanceID();
			}
		}

		private static FishPerkType TakeNextPerkFromBag()
		{
			FishPerkType[] array = (FishPerkType[])Enum.GetValues(typeof(FishPerkType));
			if (array.Length == 0)
			{
				return FishPerkType.StoppingPower;
			}
			if (_perkBag.Count == 0)
			{
				_perkBag.AddRange(array);
				Random random = new Random(1179210568 + _perkBagCycle);
				for (int num = _perkBag.Count - 1; num > 0; num--)
				{
					int index = random.Next(num + 1);
					FishPerkType value = _perkBag[num];
					_perkBag[num] = _perkBag[index];
					_perkBag[index] = value;
				}
				_perkBagCycle++;
			}
			int index2 = _perkBag.Count - 1;
			FishPerkType result = _perkBag[index2];
			_perkBag.RemoveAt(index2);
			return result;
		}

		private static string SafeGetName(Creature c)
		{
			try
			{
				return ((Item)c).GetName();
			}
			catch
			{
				return ((Object)c).name;
			}
		}

		public static string GetDisplayName(FishPerkType perk)
		{
			FishPerkDefinition value;
			return FishPerkDatabase.Definitions.TryGetValue(perk, out value) ? value.Name : perk.ToString();
		}

		public static string GetDescription(FishPerkType perk)
		{
			return perk switch
			{
				FishPerkType.StoppingPower => $"Increases bullet damage by {54.999996f:0}%.", 
				FishPerkType.Juggernaut => $"Reduces incoming damage by {90f:0}%.", 
				FishPerkType.SpeedCola => $"Increases reload speed by {39.999996f:0}%.", 
				FishPerkType.DoubleTap => $"Increases fire rate by {39.999996f:0}%.", 
				FishPerkType.FizzyYummy => $"Increases movement speed by {29.999996f:0}%.", 
				FishPerkType.QuickFix => $"Restores {5}% of health and hunger every second.", 
				FishPerkType.Hardline => $"Increases all trickshot score multipliers by {35.000004f:0}%.", 
				FishPerkType.QuickRevive => "Revives you during a boss fight while another player is still alive.", 
				_ => string.Empty, 
			};
		}

		public static void ApplyBuff(Player player, FishPerkType perk, float? durationOverrideSeconds = null)
		{
			if (!((Object)(object)player == (Object)null))
			{
				float num = durationOverrideSeconds ?? 120f;
				_activeBuffs[player] = (perk, Time.time + num);
				if (perk == FishPerkType.QuickFix)
				{
					_quickFixNextTick[player] = Time.time + 1f;
				}
				if (VerboseLogging)
				{
					Plugin.Log.LogInfo((object)$"[FishPerksMod] Applied buff '{perk}' to '{SafePlayerName(player)}' for {num:0}s.");
				}
			}
		}

		public static bool TryGetActiveBuff(Player player, out FishPerkType perk)
		{
			perk = FishPerkType.StoppingPower;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (!_activeBuffs.TryGetValue(player, out (FishPerkType, float) value))
			{
				return false;
			}
			if (Time.time >= value.Item2)
			{
				_activeBuffs.Remove(player);
				_quickFixNextTick.Remove(player);
				Announce("<b>" + SafePlayerName(player) + "</b>'s <b>" + GetDisplayName(value.Item1) + "</b> effect has expired.");
				if (VerboseLogging)
				{
					Plugin.Log.LogInfo((object)$"[FishPerksMod] Buff '{value.Item1}' expired for '{SafePlayerName(player)}'.");
				}
				return false;
			}
			(perk, _) = value;
			return true;
		}

		public static bool HasActivePerk(Player player, FishPerkType perk)
		{
			FishPerkType perk2;
			return TryGetActiveBuff(player, out perk2) && perk2 == perk;
		}

		public static float GetRemainingBuffTime(Player player)
		{
			if ((Object)(object)player == (Object)null || !_activeBuffs.TryGetValue(player, out (FishPerkType, float) value))
			{
				return 0f;
			}
			return Mathf.Max(0f, value.Item2 - Time.time);
		}

		public static void UpdatePeriodicEffects()
		{
			foreach (KeyValuePair<Player, (FishPerkType, float)> item in new List<KeyValuePair<Player, (FishPerkType, float)>>(_activeBuffs))
			{
				if (TryGetActiveBuff(item.Key, out var perk) && perk == FishPerkType.QuickFix && !item.Key.Dying.IsDead && (!_quickFixNextTick.TryGetValue(item.Key, out var value) || Time.time >= value))
				{
					item.Key.Vitals.Heal(5);
					item.Key.Vitals.RestoreFullness(5);
					_quickFixNextTick[item.Key] = Time.time + 1f;
				}
			}
		}

		private static string SafePlayerName(Player p)
		{
			try
			{
				return p.SteamName;
			}
			catch
			{
				return ((Object)p).name;
			}
		}
	}
	public enum FishPerkType
	{
		StoppingPower,
		Juggernaut,
		SpeedCola,
		DoubleTap,
		FizzyYummy,
		QuickFix,
		Hardline,
		QuickRevive
	}
	internal sealed class PerkHud : MonoBehaviour
	{
		private TextMeshProUGUI _text;

		public static void Create()
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: 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)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)PlayerUI.CanvasTextPrefab == (Object)null) && !((Object)(object)PlayerUI.FXCanvasTrans == (Object)null))
			{
				Transform val = PlayerUI.FXCanvasTrans.Find("FishPerksActivePerk");
				if (!((Object)(object)val != (Object)null))
				{
					TextMeshProUGUI val2 = Object.Instantiate<TextMeshProUGUI>(PlayerUI.CanvasTextPrefab, PlayerUI.FXCanvasTrans);
					((Object)val2).name = "FishPerksActivePerk";
					((Graphic)val2).raycastTarget = false;
					((TMP_Text)val2).alignment = (TextAlignmentOptions)257;
					((TMP_Text)val2).enableWordWrapping = false;
					((TMP_Text)val2).fontSize = ((TMP_Text)val2).fontSize * 0.72f;
					((Graphic)val2).color = Color32.op_Implicit(new Color32(byte.MaxValue, (byte)220, (byte)120, byte.MaxValue));
					RectTransform rectTransform = ((TMP_Text)val2).rectTransform;
					rectTransform.anchorMin = new Vector2(0f, 1f);
					rectTransform.anchorMax = new Vector2(0f, 1f);
					rectTransform.pivot = new Vector2(0f, 1f);
					rectTransform.anchoredPosition = new Vector2(20f, -20f);
					rectTransform.sizeDelta = new Vector2(320f, 32f);
					PerkHud perkHud = ((Component)val2).gameObject.AddComponent<PerkHud>();
					perkHud._text = val2;
					perkHud.UpdateText();
				}
			}
		}

		private void Update()
		{
			UpdateText();
		}

		private void UpdateText()
		{
			Player localPlayer = Player.LocalPlayer;
			if (Plugin.HudEnabled == null || !Plugin.HudEnabled.Value || (Object)(object)localPlayer == (Object)null || !FishPerkManager.TryGetActiveBuff(localPlayer, out var perk))
			{
				((Behaviour)_text).enabled = false;
				return;
			}
			((Behaviour)_text).enabled = true;
			((TMP_Text)_text).text = $"<b>{FishPerkManager.GetDisplayName(perk)}</b>  <color=#FFFFFF>{FishPerkManager.GetRemainingBuffTime(localPlayer):0}s</color>";
		}
	}
	[HarmonyPatch(typeof(PlayerUI), "InitializeLocal")]
	internal static class PerkHudInitializationPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			PerkHud.Create();
		}
	}
	internal static class PersistenceStore
	{
		[Serializable]
		private class Entry
		{
			public ulong SteamId;

			public byte Slot;

			public string Perk;
		}

		[Serializable]
		private class SaveFile
		{
			public List<Entry> Entries = new List<Entry>();
		}

		private static readonly Dictionary<ulong, Dictionary<byte, FishPerkType>> _data = new Dictionary<ulong, Dictionary<byte, FishPerkType>>();

		private static string _currentSaveName;

		private static string GetPath(string saveName)
		{
			string text = Path.Combine(Paths.ConfigPath, "FishPerksMod");
			Directory.CreateDirectory(text);
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			string text2 = string.Join("_", saveName.Split(invalidFileNameChars, StringSplitOptions.None));
			return Path.Combine(text, text2 + "_perks.json");
		}

		public static void Load(string saveName)
		{
			_currentSaveName = saveName;
			_data.Clear();
			try
			{
				string path = GetPath(saveName);
				if (!File.Exists(path))
				{
					Plugin.Log.LogInfo((object)("[FishPerksMod] No persisted fish perk data found for save '" + saveName + "' (this is normal the first time)."));
					return;
				}
				string text = File.ReadAllText(path);
				SaveFile saveFile = JsonUtility.FromJson<SaveFile>(text);
				if (saveFile?.Entries == null)
				{
					return;
				}
				int num = 0;
				foreach (Entry entry in saveFile.Entries)
				{
					if (Enum.TryParse<FishPerkType>(entry.Perk, out var result))
					{
						if (!_data.TryGetValue(entry.SteamId, out var value))
						{
							value = new Dictionary<byte, FishPerkType>();
							_data[entry.SteamId] = value;
						}
						value[entry.Slot] = result;
						num++;
					}
				}
				Plugin.Log.LogInfo((object)$"[FishPerksMod] Loaded {num} persisted fish perk(s) for save '{saveName}'.");
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"[FishPerksMod] Failed to load persisted fish perks: {arg}");
			}
		}

		public static void Set(ulong steamId, byte slot, FishPerkType perk)
		{
			if (!_data.TryGetValue(steamId, out var value))
			{
				value = new Dictionary<byte, FishPerkType>();
				_data[steamId] = value;
			}
			value[slot] = perk;
		}

		public static void Remove(ulong steamId, byte slot)
		{
			if (_data.TryGetValue(steamId, out var value))
			{
				value.Remove(slot);
			}
		}

		public static bool TryGet(ulong steamId, byte slot, out FishPerkType perk)
		{
			perk = FishPerkType.StoppingPower;
			Dictionary<byte, FishPerkType> value;
			return _data.TryGetValue(steamId, out value) && value.TryGetValue(slot, out perk);
		}

		public static void Save(string saveName = null)
		{
			saveName = saveName ?? _currentSaveName;
			if (string.IsNullOrEmpty(saveName))
			{
				return;
			}
			try
			{
				SaveFile saveFile = new SaveFile();
				foreach (KeyValuePair<ulong, Dictionary<byte, FishPerkType>> datum in _data)
				{
					foreach (KeyValuePair<byte, FishPerkType> item in datum.Value)
					{
						saveFile.Entries.Add(new Entry
						{
							SteamId = datum.Key,
							Slot = item.Key,
							Perk = item.Value.ToString()
						});
					}
				}
				string contents = JsonUtility.ToJson((object)saveFile, true);
				File.WriteAllText(GetPath(saveName), contents);
				if (FishPerkManager.VerboseLogging)
				{
					Plugin.Log.LogInfo((object)$"[FishPerksMod] Persisted {saveFile.Entries.Count} fish perk(s) for save '{saveName}'.");
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"[FishPerksMod] Failed to save persisted fish perks: {arg}");
			}
		}
	}
	[BepInPlugin("com.fishperksmod.fishperks", "CODFish Perks", "1.3.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.fishperksmod.fishperks";

		public const string PluginName = "CODFish Perks";

		public const string PluginVersion = "1.3.0";

		private Harmony _harmony;

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigEntry<bool> HudEnabled { get; private set; }

		private void Awake()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			HudEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "Show Active Perk HUD", true, "Show or hide the active perk and remaining time on screen.");
			_harmony = new Harmony("com.fishperksmod.fishperks");
			_harmony.PatchAll();
			Log.LogInfo((object)"CODFish Perks 1.3.0 loaded.");
		}

		private void Update()
		{
			FishPerkManager.UpdatePeriodicEffects();
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Instance = null;
		}
	}
	internal static class ReflectionUtil
	{
		private static readonly Dictionary<(Type, string), FieldInfo> _cache = new Dictionary<(Type, string), FieldInfo>();

		private static FieldInfo FindField(Type type, string name)
		{
			(Type, string) key = (type, name);
			if (_cache.TryGetValue(key, out var value))
			{
				return value;
			}
			FieldInfo fieldInfo = null;
			Type type2 = type;
			while (type2 != null)
			{
				fieldInfo = type2.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (fieldInfo != null)
				{
					break;
				}
				type2 = type2.BaseType;
			}
			_cache[key] = fieldInfo;
			if (fieldInfo == null)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[FishPerksMod] Reflection: could not find field '" + name + "' on type '" + type.FullName + "' or any of its base types. This usually means the field was renamed in a game update."));
				}
			}
			return fieldInfo;
		}

		public static T GetField<T>(object instance, string fieldName)
		{
			if (instance == null)
			{
				return default(T);
			}
			FieldInfo fieldInfo = FindField(instance.GetType(), fieldName);
			if (fieldInfo == null)
			{
				return default(T);
			}
			try
			{
				return (T)fieldInfo.GetValue(instance);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[FishPerksMod] Reflection: failed to read field '" + fieldName + "' on '" + instance.GetType().Name + "': " + ex.Message));
				}
				return default(T);
			}
		}

		public static void SetField<T>(object instance, string fieldName, T value)
		{
			if (instance == null)
			{
				return;
			}
			FieldInfo fieldInfo = FindField(instance.GetType(), fieldName);
			if (fieldInfo == null)
			{
				return;
			}
			try
			{
				fieldInfo.SetValue(instance, value);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[FishPerksMod] Reflection: failed to set field '" + fieldName + "' on '" + instance.GetType().Name + "': " + ex.Message));
				}
			}
		}
	}
}
namespace FishPerksMod.Patches
{
	[HarmonyPatch(typeof(Attachments), "get_Damage")]
	internal static class StoppingPowerPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Attachments __instance, ref int __result)
		{
			try
			{
				Weapon weapon = __instance.Weapon;
				Player val = (((Object)(object)weapon != (Object)null) ? ((Item)weapon).Holder : null);
				if (!((Object)(object)val == (Object)null) && FishPerkManager.HasActivePerk(val, FishPerkType.StoppingPower))
				{
					int num = Mathf.RoundToInt((float)__result * 1.55f);
					if (FishPerkManager.VerboseLogging)
					{
						Plugin.Log.LogInfo((object)$"[FishPerksMod] Stopping Power: {__result} -> {num} damage for '{((Object)val).name}'.");
					}
					__result = num;
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"[FishPerksMod] StoppingPowerPatch failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(PlayerVitals), "TakeDamage")]
	internal static class JuggernautPatch
	{
		[HarmonyPrefix]
		private static void Prefix(PlayerVitals __instance, ref int amount)
		{
			try
			{
				Player field = ReflectionUtil.GetField<Player>(__instance, "_player");
				if (!((Object)(object)field == (Object)null) && FishPerkManager.HasActivePerk(field, FishPerkType.Juggernaut))
				{
					int num = Mathf.Max(1, Mathf.RoundToInt((float)amount * 0.1f));
					if (FishPerkManager.VerboseLogging)
					{
						Plugin.Log.LogInfo((object)$"[FishPerksMod] Juggernaut: incoming damage {amount} -> {num} for '{((Object)field).name}'.");
					}
					amount = num;
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"[FishPerksMod] JuggernautPatch failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(Weapon), "Shoot")]
	internal static class DoubleTapPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Weapon __instance, ref float __state)
		{
			__state = ReflectionUtil.GetField<float>(__instance, "_timeBetweenShots");
			if ((Object)(object)((Item)__instance).Holder != (Object)null && FishPerkManager.HasActivePerk(((Item)__instance).Holder, FishPerkType.DoubleTap))
			{
				ReflectionUtil.SetField(__instance, "_timeBetweenShots", __state / 1.4f);
			}
		}

		[HarmonyPostfix]
		private static void Postfix(Weapon __instance, float __state)
		{
			ReflectionUtil.SetField(__instance, "_timeBetweenShots", __state);
		}
	}
	[HarmonyPatch(typeof(Weapon), "Reload")]
	internal static class SpeedColaPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Weapon __instance)
		{
			if (!((Object)(object)((Item)__instance).Holder == (Object)null))
			{
				Animation field = ReflectionUtil.GetField<Animation>(__instance, "_anim");
				string field2 = ReflectionUtil.GetField<string>(__instance, "_activeReloadAnimName");
				if ((Object)(object)field != (Object)null && !string.IsNullOrEmpty(field2) && (TrackedReference)(object)field[field2] != (TrackedReference)null)
				{
					field[field2].speed = (FishPerkManager.HasActivePerk(((Item)__instance).Holder, FishPerkType.SpeedCola) ? 1.4f : 1f);
				}
			}
		}
	}
	[HarmonyPatch(typeof(PlayerEating), "FinishEating")]
	internal static class ClientFinishEatingPatch
	{
		[HarmonyPostfix]
		private static void Postfix(PlayerEating __instance)
		{
			try
			{
				Creature field = ReflectionUtil.GetField<Creature>(__instance, "_curFood");
				Player field2 = ReflectionUtil.GetField<Player>(__instance, "_player");
				if (!((Object)(object)field == (Object)null) && !((Object)(object)field2 == (Object)null) && field.IsDrip)
				{
					FishPerkType orAssignPerk = FishPerkManager.GetOrAssignPerk(field);
					FishPerkManager.ApplyBuff(field2, orAssignPerk);
					if (FishPerkManager.VerboseLogging)
					{
						Plugin.Log.LogInfo((object)$"[FishPerksMod] (client) '{((Object)field2).name}' ate a drip fish and gained '{orAssignPerk}'.");
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"[FishPerksMod] ClientFinishEatingPatch failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(Server), "RpcLogic___FinishEatingCreature___1039939981")]
	internal static class ServerFinishEatingPatch
	{
		[HarmonyPostfix]
		private static void Postfix(object[] __args)
		{
			try
			{
				if (__args == null || __args.Length < 2)
				{
					return;
				}
				object obj = __args[0];
				Creature val = (Creature)((obj is Creature) ? obj : null);
				object obj2 = __args[1];
				Player val2 = (Player)((obj2 is Player) ? obj2 : null);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && val.IsDrip)
				{
					FishPerkType orAssignPerk = FishPerkManager.GetOrAssignPerk(val);
					FishPerkManager.ApplyBuff(val2, orAssignPerk);
					FishPerkManager.Announce("<b>" + val2.SteamName + "</b> consumed <b>" + FishPerkManager.GetDisplayName(orAssignPerk) + "</b>.");
					if (FishPerkManager.VerboseLogging)
					{
						Plugin.Log.LogInfo((object)$"[FishPerksMod] (server) Registered buff '{orAssignPerk}' for '{((Object)val2).name}'.");
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"[FishPerksMod] ServerFinishEatingPatch failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(Item), "InitializeBait")]
	internal static class DripFishCaughtAnnouncementPatch
	{
		private static readonly HashSet<Item> AnnouncedItems = new HashSet<Item>();

		[HarmonyPostfix]
		private static void Postfix(Item __instance, FishingRod rod)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)rod == (Object)null) && Object.op_Implicit((Object)(object)__instance.Creature) && __instance.Creature.IsDrip && !((Object)(object)((Item)rod).Holder == (Object)null) && !AnnouncedItems.Contains(__instance))
			{
				FishPerkType orAssignPerk = FishPerkManager.GetOrAssignPerk(__instance.Creature);
				AnnouncedItems.Add(__instance);
				FishPerkManager.Announce("<b>" + ((Item)rod).Holder.SteamName + "</b> has obtained <b>" + FishPerkManager.GetDisplayName(orAssignPerk) + "</b>.");
			}
		}
	}
	[HarmonyPatch(typeof(Creature), "SetDrip")]
	internal static class DripPerkAssignmentPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Creature __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && __instance.IsDrip)
			{
				FishPerkManager.GetOrAssignPerk(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Item), "SetAttachedRod")]
	internal static class HookedDripPerkAssignmentPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Item __instance)
		{
			Creature val = (((Object)(object)__instance != (Object)null) ? __instance.Creature : null);
			if ((Object)(object)val != (Object)null && val.IsDrip)
			{
				FishPerkManager.GetOrAssignPerk(val);
			}
		}
	}
	[HarmonyPatch(typeof(Item), "OnStartClient")]
	internal static class SyncedDripPerkAssignmentPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Item __instance)
		{
			Creature val = (((Object)(object)__instance != (Object)null) ? __instance.Creature : null);
			if ((Object)(object)val != (Object)null && val.IsDrip)
			{
				FishPerkManager.GetOrAssignPerk(val);
			}
		}
	}
	[HarmonyPatch(typeof(Creature), "OnDeath")]
	internal static class DefeatedDripPerkAssignmentPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Creature __instance)
		{
			if ((Object)(object)__instance != (Object)null && __instance.IsDrip)
			{
				FishPerkManager.GetOrAssignPerk(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(KillScoreCalculator), "GetRangedBonuses")]
	internal static class HardlinePatch
	{
		[HarmonyPostfix]
		private static void Postfix(ref List<Bonus> __result)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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)
			Player localPlayer = Player.LocalPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && FishPerkManager.HasActivePerk(localPlayer, FishPerkType.Hardline) && __result != null)
			{
				for (int i = 0; i < __result.Count; i++)
				{
					Bonus value = __result[i];
					value.Worth *= 1.35f;
					__result[i] = value;
				}
				if (FishPerkManager.VerboseLogging)
				{
					Plugin.Log.LogInfo((object)$"[FishPerksMod] Hardline increased {__result.Count} ranged score multipliers by {35.000004f:0}%.");
				}
			}
		}
	}
	[HarmonyPatch(typeof(WeaponUI), "ShowInspectText", new Type[]
	{
		typeof(Item),
		typeof(string)
	})]
	internal static class InspectItemPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Item item, ref string extraDescription)
		{
			try
			{
				Creature val = (((Object)(object)item != (Object)null) ? item.Creature : null);
				if (!((Object)(object)val == (Object)null) && val.IsDrip)
				{
					FishPerkType orAssignPerk = FishPerkManager.GetOrAssignPerk(val);
					string text = "<b>" + FishPerkManager.GetDisplayName(orAssignPerk) + "</b>\n" + FishPerkManager.GetDescription(orAssignPerk);
					extraDescription = (string.IsNullOrEmpty(extraDescription) ? text : (extraDescription + "\n\n" + text));
					if (FishPerkManager.VerboseLogging)
					{
						Plugin.Log.LogInfo((object)$"[FishPerksMod] Inspect panel updated for '{((Object)val).name}' with perk '{orAssignPerk}'.");
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"[FishPerksMod] InspectItemPatch failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(PlayerMovement), "Move")]
	internal static class FizzyYummyPatch
	{
		private struct MovementState
		{
			public float WalkSpeed;

			public float SprintSpeed;

			public bool Changed;
		}

		[HarmonyPrefix]
		private static void Prefix(PlayerMovement __instance, ref MovementState __state)
		{
			Player field = ReflectionUtil.GetField<Player>(__instance, "_player");
			if (!((Object)(object)field == (Object)null) && FishPerkManager.HasActivePerk(field, FishPerkType.FizzyYummy))
			{
				__state.WalkSpeed = ReflectionUtil.GetField<float>(__instance, "_walkSpeed");
				__state.SprintSpeed = ReflectionUtil.GetField<float>(__instance, "_sprintSpeed");
				float num = 1.3f;
				ReflectionUtil.SetField(__instance, "_walkSpeed", __state.WalkSpeed * num);
				ReflectionUtil.SetField(__instance, "_sprintSpeed", __state.SprintSpeed * num);
				__state.Changed = true;
			}
		}

		[HarmonyPostfix]
		private static void Postfix(PlayerMovement __instance, MovementState __state)
		{
			if (__state.Changed)
			{
				ReflectionUtil.SetField(__instance, "_walkSpeed", __state.WalkSpeed);
				ReflectionUtil.SetField(__instance, "_sprintSpeed", __state.SprintSpeed);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerMovement), "Move")]
	internal static class FizzyYummyVelocityPatch
	{
		[HarmonyPostfix]
		private static void Postfix(PlayerMovement __instance)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			Player field = ReflectionUtil.GetField<Player>(__instance, "_player");
			Rigidbody field2 = ReflectionUtil.GetField<Rigidbody>(__instance, "_rig");
			if (!((Object)(object)field == (Object)null) && !((Object)(object)field2 == (Object)null) && FishPerkManager.HasActivePerk(field, FishPerkType.FizzyYummy))
			{
				Vector3 linearVelocity = field2.linearVelocity;
				Vector3 val = new Vector3(linearVelocity.x, 0f, linearVelocity.z) * 1.3f;
				field2.linearVelocity = new Vector3(val.x, linearVelocity.y, val.z);
			}
		}
	}
	[HarmonyPatch(typeof(SaveManager), "OnServerLoaded")]
	internal static class LoadPerksPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			if (SaveManager.CurServerSave != null)
			{
				PersistenceStore.Load(SaveManager.CurServerSave.Name);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerInventory), "LoadFromSave")]
	internal static class RestorePerksPatch
	{
		[HarmonyPostfix]
		private static void Postfix(PlayerInventory __instance)
		{
			SavedPlayer field = ReflectionUtil.GetField<SavedPlayer>(__instance, "_toLoadFrom");
			Player field2 = ReflectionUtil.GetField<Player>(__instance, "_player");
			if (field == null || (Object)(object)field2 == (Object)null)
			{
				return;
			}
			Dictionary<byte, Item> field3 = ReflectionUtil.GetField<Dictionary<byte, Item>>(__instance, "_items");
			if (field3 != null)
			{
				foreach (KeyValuePair<byte, Item> item in field3)
				{
					if ((Object)(object)item.Value != (Object)null && Object.op_Implicit((Object)(object)item.Value.Creature) && item.Value.Creature.IsDrip && PersistenceStore.TryGet(field.SteamID, item.Key, out var perk))
					{
						FishPerkManager.AssignSpecificPerk(item.Value.Creature, perk);
					}
				}
			}
			Item uninitializedHeldItem = field2.Holding.UninitializedHeldItem;
			if ((Object)(object)uninitializedHeldItem != (Object)null && Object.op_Implicit((Object)(object)uninitializedHeldItem.Creature) && uninitializedHeldItem.Creature.IsDrip && PersistenceStore.TryGet(field.SteamID, 0, out var perk2))
			{
				FishPerkManager.AssignSpecificPerk(uninitializedHeldItem.Creature, perk2);
			}
		}
	}
	[HarmonyPatch(typeof(SaveManager), "SavePlayer")]
	internal static class CapturePerksPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Player player, Item heldItem, Dictionary<byte, Item> slotToItem)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			Capture(player.SteamID, 0, heldItem);
			if (slotToItem == null)
			{
				return;
			}
			foreach (KeyValuePair<byte, Item> item in slotToItem)
			{
				Capture(player.SteamID, item.Key, item.Value);
			}
		}

		private static void Capture(ulong steamId, byte slot, Item item)
		{
			if ((Object)(object)item != (Object)null && Object.op_Implicit((Object)(object)item.Creature) && item.Creature.IsDrip)
			{
				PersistenceStore.Set(steamId, slot, FishPerkManager.GetOrAssignPerk(item.Creature));
			}
			else
			{
				PersistenceStore.Remove(steamId, slot);
			}
		}
	}
	[HarmonyPatch(typeof(SaveManager), "SaveServer")]
	internal static class SavePerksPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			if (SaveManager.CurServerSave != null)
			{
				PersistenceStore.Save(SaveManager.CurServerSave.Name);
			}
		}
	}
	[HarmonyPatch(typeof(Server), "OnStopServer")]
	internal static class ClearSessionPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			FishPerkManager.ClearSession();
		}
	}
	[HarmonyPatch(typeof(PlayerDying), "ServerDie")]
	internal static class QuickRevivePatch
	{
		[HarmonyPrefix]
		private static bool Prefix(PlayerDying __instance)
		{
			try
			{
				Player player = ReflectionUtil.GetField<Player>(__instance, "_player");
				if ((Object)(object)player == (Object)null || !FishPerkManager.HasActivePerk(player, FishPerkType.QuickRevive))
				{
					return true;
				}
				Creature boss = BossManager.Boss;
				bool flag = (Object)(object)boss != (Object)null && !boss.IsDead;
				bool flag2 = PlayerManager.Players != null && PlayerManager.Players.Any((Player other) => (Object)(object)other != (Object)null && (Object)(object)other != (Object)(object)player && !other.Dying.IsDead && other.Vitals.Health > 0);
				if (!flag || !flag2)
				{
					return true;
				}
				player.Vitals.OnResurrect();
				if (FishPerkManager.VerboseLogging)
				{
					Plugin.Log.LogInfo((object)("[FishPerksMod] Quick Revive saved '" + ((Object)player).name + "' during a boss fight."));
				}
				return false;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"[FishPerksMod] Quick Revive patch failed: {arg}");
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(PlayerDying), "LocalResurrect")]
	internal static class QuickReviveLocalSignalPatch
	{
		[HarmonyPostfix]
		private static void Postfix(PlayerDying __instance)
		{
			QuickReviveSignals.Play(__instance);
		}
	}
	[HarmonyPatch(typeof(PlayerDying), "ResurrectEffect")]
	internal static class QuickReviveObserverSignalPatch
	{
		[HarmonyPostfix]
		private static void Postfix(PlayerDying __instance)
		{
			QuickReviveSignals.Play(__instance);
		}
	}
	internal static class QuickReviveSignals
	{
		internal static void Play(PlayerDying dying)
		{
			Player field = ReflectionUtil.GetField<Player>(dying, "_player");
			if (!((Object)(object)field == (Object)null) && FishPerkManager.HasActivePerk(field, FishPerkType.QuickRevive))
			{
				field.Effects.OnRestoredFullness();
				field.Effects.OnRestoredFullness();
				AudioManager.PlayRandomPlayerClip("ShinyCreatureCaught_0", 1, 3, field, false, (AudioDistance)2, 0.5f, 0.1f);
			}
		}
	}
}