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 Definitions = new Dictionary { { 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 _sessionPerkCache = new Dictionary(); private static readonly List _perkBag = new List(); private static int _perkBagCycle; private static readonly Dictionary _activeBuffs = new Dictionary(); private static readonly Dictionary _quickFixNextTick = new Dictionary(); 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("" + message + ""); } } 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("" + SafePlayerName(player) + "'s " + GetDisplayName(value.Item1) + " 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 item in new List>(_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(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._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 = $"{FishPerkManager.GetDisplayName(perk)} {FishPerkManager.GetRemainingBuffTime(localPlayer):0}s"; } } [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 Entries = new List(); } private static readonly Dictionary> _data = new Dictionary>(); 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(text); if (saveFile?.Entries == null) { return; } int num = 0; foreach (Entry entry in saveFile.Entries) { if (Enum.TryParse(entry.Perk, out var result)) { if (!_data.TryGetValue(entry.SteamId, out var value)) { value = new Dictionary(); _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(); _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 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> datum in _data) { foreach (KeyValuePair 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 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("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(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(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(__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(__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(__instance, "_anim"); string field2 = ReflectionUtil.GetField(__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(__instance, "_curFood"); Player field2 = ReflectionUtil.GetField(__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("" + val2.SteamName + " consumed " + FishPerkManager.GetDisplayName(orAssignPerk) + "."); 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 AnnouncedItems = new HashSet(); [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("" + ((Item)rod).Holder.SteamName + " has obtained " + FishPerkManager.GetDisplayName(orAssignPerk) + "."); } } } [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 __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 = "" + FishPerkManager.GetDisplayName(orAssignPerk) + "\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(__instance, "_player"); if (!((Object)(object)field == (Object)null) && FishPerkManager.HasActivePerk(field, FishPerkType.FizzyYummy)) { __state.WalkSpeed = ReflectionUtil.GetField(__instance, "_walkSpeed"); __state.SprintSpeed = ReflectionUtil.GetField(__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(__instance, "_player"); Rigidbody field2 = ReflectionUtil.GetField(__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(__instance, "_toLoadFrom"); Player field2 = ReflectionUtil.GetField(__instance, "_player"); if (field == null || (Object)(object)field2 == (Object)null) { return; } Dictionary field3 = ReflectionUtil.GetField>(__instance, "_items"); if (field3 != null) { foreach (KeyValuePair 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 slotToItem) { if ((Object)(object)player == (Object)null) { return; } Capture(player.SteamID, 0, heldItem); if (slotToItem == null) { return; } foreach (KeyValuePair 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(__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(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); } } } }