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);
}
}
}
}