Decompiled source of FishEffects v0.5.3

plugins/ArceDev.FishEffects.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
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 System.Text;
using BepInEx;
using BepInEx.Logging;
using FishNet;
using FishNet.Broadcast;
using FishNet.Connection;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Transporting;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using NativeStatusUI;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ArceDev.FishEffects")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.3.0")]
[assembly: AssemblyInformationalVersion("0.5.3")]
[assembly: AssemblyProduct("ArceDev.FishEffects")]
[assembly: AssemblyTitle("FishEffects")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.5.3.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	[Embedded]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	[Embedded]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace Microsoft.CodeAnalysis
{
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace FishEffects
{
	internal readonly record struct StatusBroadcast(byte Effect, float Duration) : IBroadcast;
	internal static class EffectNetwork
	{
		private readonly record struct StatusStyle(string Name, string Resource, Color Color);

		private static readonly Action<StatusBroadcast, Channel> Handler = OnStatus;

		private static readonly Dictionary<TimedEffect, StatusStyle> Styles = new Dictionary<TimedEffect, StatusStyle>
		{
			[TimedEffect.Regeneration] = new StatusStyle("Regeneration", "regeneration.png", new Color(0.28f, 0.95f, 0.42f)),
			[TimedEffect.Agility] = new StatusStyle("Agility", "agility.png", new Color(1f, 0.82f, 0.2f)),
			[TimedEffect.FastBite] = new StatusStyle("FastBite", "fast-bite.png", new Color(0.2f, 0.9f, 1f)),
			[TimedEffect.Resistance] = new StatusStyle("Resistance", "resistance.png", new Color(0.3f, 0.58f, 1f)),
			[TimedEffect.Slowness] = new StatusStyle("Slowness", "slowness.png", new Color(0.68f, 0.45f, 0.85f)),
			[TimedEffect.Luck] = new StatusStyle("Luck", "clover.png", new Color(0.35f, 0.9f, 0.4f))
		};

		private static bool _clientRegistered;

		internal static void Initialize()
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			GenericWriter<StatusBroadcast>.SetWrite((Action<Writer, StatusBroadcast>)delegate(Writer writer, StatusBroadcast message)
			{
				writer.WriteUInt8Unpacked(message.Effect);
				writer.WriteSingle(message.Duration);
			});
			GenericReader<StatusBroadcast>.SetRead((Func<Reader, StatusBroadcast>)((Reader reader) => new StatusBroadcast(reader.ReadUInt8Unpacked(), reader.ReadSingle())));
			foreach (var (effect, statusStyle2) in Styles)
			{
				NativeStatus.Register(StatusId(effect), LoadPng(statusStyle2.Resource), statusStyle2.Color);
			}
		}

		internal static void RegisterClient()
		{
			if (!_clientRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
			{
				InstanceFinder.ClientManager.RegisterBroadcast<StatusBroadcast>(Handler);
				_clientRegistered = true;
			}
		}

		internal static void Send(Player player, TimedEffect effect, float duration)
		{
			NetworkConnection owner = ((NetworkBehaviour)player).Owner;
			if (owner != null && owner.IsValid && Object.op_Implicit((Object)(object)InstanceFinder.ServerManager))
			{
				InstanceFinder.ServerManager.Broadcast<StatusBroadcast>(owner, new StatusBroadcast((byte)effect, duration), true, (Channel)0);
			}
		}

		internal static string ColorHex(TimedEffect effect)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			return ColorUtility.ToHtmlStringRGB(Styles[effect].Color);
		}

		private static byte[] LoadPng(string resource)
		{
			using Stream stream = typeof(EffectNetwork).Assembly.GetManifestResourceStream("FishEffects." + resource) ?? throw new InvalidOperationException("Embedded status icon '" + resource + "' was not found.");
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}

		private static void OnStatus(StatusBroadcast message, Channel channel)
		{
			TimedEffect effect = (TimedEffect)message.Effect;
			if (Styles.ContainsKey(effect) && !(message.Duration <= 0f))
			{
				NativeStatus.Activate(StatusId(effect), message.Duration);
				TimedEffects.ActivateLocal(effect, message.Duration);
			}
		}

		private static string StatusId(TimedEffect effect)
		{
			return "ArceDev.FishEffects." + Styles[effect].Name;
		}
	}
	internal static class LuckEffect
	{
		internal const float DurationSeconds = 60f;

		private const int RerollSeedOffset = 104729;

		private static readonly Dictionary<ulong, float> ExpiresAt = new Dictionary<ulong, float>();

		internal static void Activate(Player player)
		{
			ExpiresAt[player.SteamID] = Time.time + 60f;
			EffectNetwork.Send(player, TimedEffect.Luck, 60f);
			Plugin.Log.LogInfo((object)$"Luck activated for {player.SteamName} for {60f:0} seconds.");
		}

		internal static void ApplyLuckyWeight(Bait bait)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			Item serverItemOnBait = bait.ServerItemOnBait;
			Creature val = ((serverItemOnBait != null) ? serverItemOnBait.Creature : null);
			FishingRod fishingRod = bait.FishingRod;
			Player val2 = ((fishingRod != null) ? ((Item)fishingRod).Holder : null);
			if (Object.op_Implicit((Object)(object)val) && (int)val.BossType == 0 && !val._skipRandomizedWeight && IsActive(val2))
			{
				float value = ((Item)val)._syncedRandomWeight.Value;
				int num = ((NetworkBehaviour)val).ObjectId + 104729;
				float weightWithStandardDeviation = CreatureUtils.GetWeightWithStandardDeviation(num);
				((Item)val)._syncedRandomWeight.Value = BetterWeight(value, weightWithStandardDeviation);
				Plugin.Log.LogDebug((object)$"Luck weight roll for {val2.SteamName}: {value:0.###} vs {weightWithStandardDeviation:0.###}.");
			}
		}

		internal static void Validate()
		{
			if (BetterWeight(3f, 2f) != 3f || BetterWeight(3f, 4f) != 4f)
			{
				throw new InvalidOperationException("FishEffects Luck weight validation failed.");
			}
		}

		private static float BetterWeight(float currentWeight, float rerolledWeight)
		{
			return Mathf.Max(currentWeight, rerolledWeight);
		}

		private static bool IsActive(Player? player)
		{
			if (!Object.op_Implicit((Object)(object)player) || !ExpiresAt.TryGetValue(player.SteamID, out var value))
			{
				return false;
			}
			if (Time.time < value)
			{
				return true;
			}
			ExpiresAt.Remove(player.SteamID);
			return false;
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("ArceDev.FishEffects", "FishEffects", "0.5.3")]
	public class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(Server), "RpcLogic___FinishEatingCreature___1039939981")]
		private static class FinishEatingCreaturePatch
		{
			private static void Prefix(Creature __0, out ConsumedFood __state)
			{
				__state = ((Object.op_Implicit((Object)(object)__0) && !((NetworkBehaviour)__0).IsDeinitializing) ? new ConsumedFood(FoodEffectRules.Resolve(__0), __0.IsDrip) : default(ConsumedFood));
			}

			private static void Postfix(Player __1, ConsumedFood __state)
			{
				ConsumedFood consumedFood = __state;
				if (Object.op_Implicit((Object)(object)__1) && !((NetworkBehaviour)__1).IsDeinitializing)
				{
					if (consumedFood.Effect.HasEffect)
					{
						consumedFood.Effect.Apply(__1);
						Log.LogDebug((object)$"Applied {consumedFood.Effect} to {__1.SteamName}.");
					}
					if (consumedFood.GrantsLuck)
					{
						LuckEffect.Activate(__1);
					}
				}
			}
		}

		[HarmonyPatch(typeof(CreatureManager), "HookItem")]
		private static class HookItemPatch
		{
			private static void Postfix(Bait __1)
			{
				LuckEffect.ApplyLuckyWeight(__1);
			}
		}

		[HarmonyPatch(typeof(Bait), "IncreaseUnderwaterTime")]
		private static class IncreaseUnderwaterTimePatch
		{
			private static void Prefix(Bait __instance, out float __state)
			{
				__state = __instance.TimeUnderWater;
			}

			private static void Postfix(Bait __instance, float __state)
			{
				TimedEffects.ApplyFastBite(__instance, __instance.TimeUnderWater - __state);
			}
		}

		[HarmonyPatch(typeof(PlayerVitals), "TakeDamage")]
		private static class TakeDamagePatch
		{
			private static void Prefix(Player ____player, ref int amount)
			{
				amount = TimedEffects.ApplyResistance(____player, amount);
			}
		}

		[HarmonyPatch(typeof(PlayerMovement), "UpdateMoveSpeed")]
		private static class UpdateMoveSpeedPatch
		{
			private readonly record struct Speeds(float Walk, float Sprint);

			private static void Prefix(Player ____player, ref float ____walkSpeed, ref float ____sprintSpeed, out Speeds __state)
			{
				__state = new Speeds(____walkSpeed, ____sprintSpeed);
				float localMovementMultiplier = TimedEffects.GetLocalMovementMultiplier(____player);
				____walkSpeed *= localMovementMultiplier;
				____sprintSpeed *= localMovementMultiplier;
			}

			private static void Postfix(ref float ____walkSpeed, ref float ____sprintSpeed, Speeds __state)
			{
				Speeds speeds = __state;
				____walkSpeed = speeds.Walk;
				____sprintSpeed = speeds.Sprint;
			}
		}

		[HarmonyPatch(typeof(WeaponUI), "UpdateInspectedItemText")]
		private static class UpdateInspectedItemTextPatch
		{
			private static void Prefix(Item ____inspectedItem, ref string ____extraDescription)
			{
				Creature val = (Creature)(object)((____inspectedItem is Creature) ? ____inspectedItem : null);
				if (val != null)
				{
					____extraDescription = InspectionText.WithEffect(____extraDescription, FoodEffectRules.Resolve(val), val.IsDrip);
				}
			}
		}

		[HarmonyPatch(typeof(VitalsUI), "Awake")]
		private static class VitalsUiPatch
		{
			private static void Postfix()
			{
				EffectNetwork.RegisterClient();
			}
		}

		public const string Id = "ArceDev.FishEffects";

		internal static ManualLogSource Log { get; private set; }

		public static string Name => "FishEffects";

		public static string Version => "0.5.3";

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			FoodEffectRules.Validate();
			InspectionText.Validate();
			LuckEffect.Validate();
			EffectNetwork.Initialize();
			Harmony.CreateAndPatchAll(typeof(Plugin).Assembly, "ArceDev.FishEffects");
			Log.LogInfo((object)("Plugin " + Name + " is loaded!"));
		}

		private void Update()
		{
			TimedEffects.UpdateServer();
		}
	}
	internal enum TimedEffect : byte
	{
		None,
		Regeneration,
		Agility,
		FastBite,
		Resistance,
		Slowness,
		Luck
	}
	internal enum CookState
	{
		Raw,
		Cooked,
		Burnt
	}
	internal readonly record struct FoodEffect(TimedEffect Timed = TimedEffect.None, float Duration = 0f, bool Poison = false, bool Fire = false)
	{
		internal bool HasEffect
		{
			get
			{
				if (Timed == TimedEffect.None && !Poison)
				{
					return Fire;
				}
				return true;
			}
		}

		internal void Apply(Player player)
		{
			if (Poison)
			{
				player.Vitals.ApplyNewPoison();
			}
			if (Fire)
			{
				player.Vitals.ApplyNewFire();
			}
			if (Timed != TimedEffect.None)
			{
				TimedEffects.Activate(player, Timed, Duration);
			}
		}

		public override string ToString()
		{
			if (Poison)
			{
				return "Poison";
			}
			if (Fire)
			{
				return "Fire";
			}
			return $"{TimedEffects.DisplayName(Timed)} ({Duration:0}s)";
		}

		[CompilerGenerated]
		private bool PrintMembers(StringBuilder builder)
		{
			builder.Append("Timed = ");
			builder.Append(Timed.ToString());
			builder.Append(", Duration = ");
			builder.Append(Duration.ToString());
			builder.Append(", Poison = ");
			builder.Append(Poison.ToString());
			builder.Append(", Fire = ");
			builder.Append(Fire.ToString());
			return true;
		}
	}
	internal readonly record struct ConsumedFood(FoodEffect Effect, bool GrantsLuck);
	internal static class InspectionText
	{
		private const string Marker = "\n\nFish Effects: ";

		internal static string WithEffect(string description, FoodEffect effect, bool grantsLuck = false)
		{
			string text = "\n\nFish Effects: " + FormatEffect(effect);
			if (grantsLuck)
			{
				text += string.Format("\nSpecial Effect: {0} ({1:0}s)", Colorize("Luck", EffectNetwork.ColorHex(TimedEffect.Luck)), 60f);
			}
			if (description.EndsWith(text, StringComparison.Ordinal))
			{
				return description;
			}
			int num = description.IndexOf("\n\nFish Effects: ", StringComparison.Ordinal);
			string text2 = ((num >= 0) ? description.Substring(0, num) : description);
			return text2 + text;
		}

		private static string FormatEffect(FoodEffect effect)
		{
			if (effect.Poison)
			{
				return Colorize("Poison", "8F35BE");
			}
			if (effect.Fire)
			{
				return Colorize("Fire", "FF6A2A");
			}
			string text = TimedEffects.DisplayName(effect.Timed);
			return $"{Colorize(text, EffectNetwork.ColorHex(effect.Timed))} ({effect.Duration:0}s)";
		}

		private static string Colorize(string text, string hex)
		{
			return "<color=#" + hex + ">" + text + "</color>";
		}

		internal static void Validate()
		{
			string text = "Weight: 1\nKillscore: 1x";
			string text2 = WithEffect(text, new FoodEffect(TimedEffect.Regeneration, 40f), grantsLuck: true);
			string text3 = WithEffect(text2, new FoodEffect(TimedEffect.None, 0f, Poison: false, Fire: true));
			string text4 = WithEffect(text3, new FoodEffect(TimedEffect.None, 0f, Poison: true));
			if (text2 != text + "\n\nFish Effects: <color=#47F26B>Regeneration</color> (40s)\nSpecial Effect: <color=#59E666>Luck</color> (60s)" || text3 != text + "\n\nFish Effects: <color=#FF6A2A>Fire</color>" || text4 != text + "\n\nFish Effects: <color=#8F35BE>Poison</color>" || WithEffect(text3, new FoodEffect(TimedEffect.None, 0f, Poison: false, Fire: true)) != text3)
			{
				throw new InvalidOperationException("FishEffects inspection text validation failed.");
			}
		}
	}
	internal static class FoodEffectRules
	{
		private const float CookedAt = 0.75f;

		private const float BurntAt = 1.25f;

		private static readonly HashSet<string> Toxic = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Pufferfish", "SeaUrchin", "Stonefish", "YellowBoxFish" };

		private static readonly HashSet<string> Indigestible = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Blobfish", "Bowlfish", "Dripper", "RottenShrimp", "Voxelfish" };

		private static readonly Dictionary<string, TimedEffect> SpeciesEffects = new Dictionary<string, TimedEffect>(StringComparer.OrdinalIgnoreCase)
		{
			["AngelFish"] = TimedEffect.Regeneration,
			["Bluegill"] = TimedEffect.Regeneration,
			["Clownfish"] = TimedEffect.Regeneration,
			["Goldfish"] = TimedEffect.Regeneration,
			["Parrotfish"] = TimedEffect.Regeneration,
			["Seahorse"] = TimedEffect.Regeneration,
			["SuperdwarfFish"] = TimedEffect.Regeneration,
			["FlyingFish"] = TimedEffect.Agility,
			["Needlefish"] = TimedEffect.Agility,
			["Piranha"] = TimedEffect.Agility,
			["Tigerfish"] = TimedEffect.Agility,
			["Tuna"] = TimedEffect.Agility,
			["YellowBoxFish"] = TimedEffect.Agility,
			["Anglerfish"] = TimedEffect.FastBite,
			["BingBong"] = TimedEffect.FastBite,
			["Eel"] = TimedEffect.FastBite,
			["Gammelgäddan"] = TimedEffect.FastBite,
			["Goby"] = TimedEffect.FastBite,
			["RedSnapper"] = TimedEffect.FastBite,
			["Stonefish"] = TimedEffect.FastBite,
			["Bass"] = TimedEffect.Resistance,
			["Belodontichthys Truncatus"] = TimedEffect.Resistance,
			["BlueShark"] = TimedEffect.Resistance,
			["BowheadWhale"] = TimedEffect.Resistance,
			["Catfish"] = TimedEffect.Resistance,
			["GiantPiranha"] = TimedEffect.Resistance,
			["GoblinShark"] = TimedEffect.Resistance,
			["Halibut"] = TimedEffect.Resistance,
			["Lobster"] = TimedEffect.Resistance,
			["Oarfish"] = TimedEffect.Resistance,
			["Pufferfish"] = TimedEffect.Resistance,
			["RockCrab"] = TimedEffect.Resistance,
			["SeaUrchin"] = TimedEffect.Resistance,
			["Spidercrab"] = TimedEffect.Resistance,
			["Sunfish"] = TimedEffect.Resistance
		};

		internal static FoodEffect Resolve(Creature creature)
		{
			return Resolve(((Object)creature).name.Replace("(Clone)", string.Empty).Trim(), ((Item)creature).Cookness);
		}

		internal static void Validate()
		{
			FoodEffect[] array = new FoodEffect[7]
			{
				Resolve("Pufferfish", 0.749f),
				Resolve("Pufferfish", 0.75f),
				Resolve("AngelFish", 0.75f),
				Resolve("Blobfish", 0.75f),
				Resolve("Anglerfish", 1.25f),
				Resolve("Anglerfish", 1.251f),
				Resolve("ModdedFish", 0.75f)
			};
			FoodEffect[] array2 = new FoodEffect[7]
			{
				new FoodEffect(TimedEffect.None, 0f, Poison: true),
				new FoodEffect(TimedEffect.Resistance, 50f),
				new FoodEffect(TimedEffect.Regeneration, 40f),
				new FoodEffect(TimedEffect.Slowness, 30f),
				new FoodEffect(TimedEffect.FastBite, 60f),
				new FoodEffect(TimedEffect.None, 0f, Poison: false, Fire: true),
				new FoodEffect(TimedEffect.FastBite, 60f)
			};
			if (!((ReadOnlySpan<FoodEffect>)array).SequenceEqual((ReadOnlySpan<FoodEffect>)array2))
			{
				throw new InvalidOperationException("FishEffects rule validation failed.");
			}
		}

		private static FoodEffect Resolve(string species, float cookness)
		{
			CookState cookState = GetCookState(cookness);
			if (cookState == CookState.Burnt)
			{
				return new FoodEffect(TimedEffect.None, 0f, Poison: false, Fire: true);
			}
			if (TryResolveHazard(species, cookState, out var effect))
			{
				return effect;
			}
			TimedEffect value;
			TimedEffect timedEffect = (SpeciesEffects.TryGetValue(species, out value) ? value : TimedEffect.FastBite);
			return new FoodEffect(timedEffect, Duration(timedEffect, cookState));
		}

		private static bool TryResolveHazard(string species, CookState state, out FoodEffect effect)
		{
			if (Indigestible.Contains(species))
			{
				effect = ((state == CookState.Raw) ? new FoodEffect(TimedEffect.None, 0f, Poison: true) : new FoodEffect(TimedEffect.Slowness, 30f));
				return true;
			}
			if (state == CookState.Raw && Toxic.Contains(species))
			{
				effect = new FoodEffect(TimedEffect.None, 0f, Poison: true);
				return true;
			}
			effect = default(FoodEffect);
			return false;
		}

		private static CookState GetCookState(float cookness)
		{
			if (!(cookness > 1.25f))
			{
				if (!(cookness >= 0.75f))
				{
					return CookState.Raw;
				}
				return CookState.Cooked;
			}
			return CookState.Burnt;
		}

		private static float Duration(TimedEffect effect, CookState state)
		{
			switch (effect)
			{
			case TimedEffect.Regeneration:
				return (state == CookState.Raw) ? 20f : 40f;
			case TimedEffect.Resistance:
				return (state == CookState.Raw) ? 25f : 50f;
			case TimedEffect.Agility:
			case TimedEffect.FastBite:
				return (state == CookState.Raw) ? 30f : 60f;
			default:
				throw new ArgumentOutOfRangeException("effect");
			}
		}
	}
	internal static class TimedEffects
	{
		private readonly record struct EffectKey(ulong PlayerId, TimedEffect Effect);

		private readonly record struct ActiveEffect(float ExpiresAt, float NextTick);

		private const float RegenerationInterval = 2f;

		private const int RegenerationAmount = 2;

		private const float AgilityMultiplier = 1.15f;

		private const float SlownessMultiplier = 0.8f;

		private const float FastBiteBonus = 0.25f;

		private const float ResistanceMultiplier = 0.8f;

		private static readonly Dictionary<EffectKey, ActiveEffect> ServerEffects = new Dictionary<EffectKey, ActiveEffect>();

		private static readonly Dictionary<TimedEffect, float> LocalExpiresAt = new Dictionary<TimedEffect, float>();

		internal static void Activate(Player player, TimedEffect effect, float duration)
		{
			float time = Time.time;
			ServerEffects[new EffectKey(player.SteamID, effect)] = new ActiveEffect(time + duration, (effect == TimedEffect.Regeneration) ? (time + 2f) : 0f);
			EffectNetwork.Send(player, effect, duration);
		}

		internal static void ActivateLocal(TimedEffect effect, float duration)
		{
			LocalExpiresAt[effect] = Time.time + duration;
		}

		internal static void UpdateServer()
		{
			if (InstanceFinder.IsServerStarted && ServerEffects.Count != 0)
			{
				float time = Time.time;
				EffectKey[] array = ServerEffects.Keys.ToArray();
				foreach (EffectKey key in array)
				{
					UpdateEffect(key, time);
				}
			}
		}

		internal static int ApplyResistance(Player player, int amount)
		{
			if (amount <= 0 || !IsServerActive(player, TimedEffect.Resistance))
			{
				return amount;
			}
			return Mathf.Max(1, Mathf.RoundToInt((float)amount * 0.8f));
		}

		internal static void ApplyFastBite(Bait bait, float actualIncrement)
		{
			if (!(actualIncrement <= 0f) && InstanceFinder.IsServerStarted)
			{
				FishingRod fishingRod = bait.FishingRod;
				if (IsServerActive((fishingRod != null) ? ((Item)fishingRod).Holder : null, TimedEffect.FastBite))
				{
					bait.TimeUnderWater += actualIncrement * 0.25f;
				}
			}
		}

		internal static float GetLocalMovementMultiplier(Player player)
		{
			if (!Object.op_Implicit((Object)(object)player) || !((NetworkBehaviour)player).IsOwner)
			{
				return 1f;
			}
			float num = 1f;
			if (IsLocalActive(TimedEffect.Agility))
			{
				num *= 1.15f;
			}
			if (IsLocalActive(TimedEffect.Slowness))
			{
				num *= 0.8f;
			}
			return num;
		}

		internal static string DisplayName(TimedEffect effect)
		{
			return effect switch
			{
				TimedEffect.Regeneration => "Regeneration", 
				TimedEffect.Agility => "Agility", 
				TimedEffect.FastBite => "Fast Bite", 
				TimedEffect.Resistance => "Resistance", 
				TimedEffect.Slowness => "Slowness", 
				TimedEffect.Luck => "Luck", 
				_ => "None", 
			};
		}

		private static bool IsServerActive(Player? player, TimedEffect effect)
		{
			if (!Object.op_Implicit((Object)(object)player) || !ServerEffects.TryGetValue(new EffectKey(player.SteamID, effect), out var value))
			{
				return false;
			}
			if (Time.time < value.ExpiresAt)
			{
				return true;
			}
			ServerEffects.Remove(new EffectKey(player.SteamID, effect));
			return false;
		}

		private static void UpdateEffect(EffectKey key, float now)
		{
			ActiveEffect active = ServerEffects[key];
			Player player = FindPlayer(key.PlayerId);
			if (ShouldRemove(player, active, now))
			{
				ServerEffects.Remove(key);
			}
			else if (key.Effect == TimedEffect.Regeneration && now >= active.NextTick)
			{
				TickRegeneration(key, player, active, now);
			}
		}

		private static bool ShouldRemove(Player? player, ActiveEffect active, float now)
		{
			if (Object.op_Implicit((Object)(object)player) && !((NetworkBehaviour)player).IsDeinitializing)
			{
				return now >= active.ExpiresAt;
			}
			return true;
		}

		private static void TickRegeneration(EffectKey key, Player player, ActiveEffect active, float now)
		{
			if (!player.Dying.IsDead)
			{
				player.Vitals.Heal(2);
			}
			ServerEffects[key] = active with
			{
				NextTick = now + 2f
			};
		}

		private static bool IsLocalActive(TimedEffect effect)
		{
			if (!LocalExpiresAt.TryGetValue(effect, out var value))
			{
				return false;
			}
			if (Time.time < value)
			{
				return true;
			}
			LocalExpiresAt.Remove(effect);
			return false;
		}

		private static Player? FindPlayer(ulong playerId)
		{
			return ((IEnumerable<Player>)PlayerManager.Players).FirstOrDefault((Func<Player, bool>)((Player player) => Object.op_Implicit((Object)(object)player) && player.SteamID == playerId));
		}
	}
}
namespace System.Diagnostics.CodeAnalysis
{
	[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ConstantExpectedAttribute : Attribute
	{
		public object? Min { get; set; }

		public object? Max { get; set; }
	}
	[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ExperimentalAttribute : Attribute
	{
		public string DiagnosticId { get; }

		public string? UrlFormat { get; set; }

		public ExperimentalAttribute(string diagnosticId)
		{
			DiagnosticId = diagnosticId;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
	[ExcludeFromCodeCoverage]
	internal sealed class MemberNotNullAttribute : Attribute
	{
		public string[] Members { get; }

		public MemberNotNullAttribute(string member)
		{
			Members = new string[1] { member };
		}

		public MemberNotNullAttribute(params string[] members)
		{
			Members = members;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
	[ExcludeFromCodeCoverage]
	internal sealed class MemberNotNullWhenAttribute : Attribute
	{
		public bool ReturnValue { get; }

		public string[] Members { get; }

		public MemberNotNullWhenAttribute(bool returnValue, string member)
		{
			ReturnValue = returnValue;
			Members = new string[1] { member };
		}

		public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
		{
			ReturnValue = returnValue;
			Members = members;
		}
	}
	[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class SetsRequiredMembersAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class StringSyntaxAttribute : Attribute
	{
		public const string CompositeFormat = "CompositeFormat";

		public const string DateOnlyFormat = "DateOnlyFormat";

		public const string DateTimeFormat = "DateTimeFormat";

		public const string EnumFormat = "EnumFormat";

		public const string GuidFormat = "GuidFormat";

		public const string Json = "Json";

		public const string NumericFormat = "NumericFormat";

		public const string Regex = "Regex";

		public const string TimeOnlyFormat = "TimeOnlyFormat";

		public const string TimeSpanFormat = "TimeSpanFormat";

		public const string Uri = "Uri";

		public const string Xml = "Xml";

		public string Syntax { get; }

		public object?[] Arguments { get; }

		public StringSyntaxAttribute(string syntax)
		{
			Syntax = syntax;
			Arguments = new object[0];
		}

		public StringSyntaxAttribute(string syntax, params object?[] arguments)
		{
			Syntax = syntax;
			Arguments = arguments;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class UnscopedRefAttribute : Attribute
	{
	}
}
namespace System.Runtime.Versioning
{
	[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class RequiresPreviewFeaturesAttribute : Attribute
	{
		public string? Message { get; }

		public string? Url { get; set; }

		public RequiresPreviewFeaturesAttribute()
		{
		}

		public RequiresPreviewFeaturesAttribute(string? message)
		{
			Message = message;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
	[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class CallerArgumentExpressionAttribute : Attribute
	{
		public string ParameterName { get; }

		public CallerArgumentExpressionAttribute(string parameterName)
		{
			ParameterName = parameterName;
		}
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class CollectionBuilderAttribute : Attribute
	{
		public Type BuilderType { get; }

		public string MethodName { get; }

		public CollectionBuilderAttribute(Type builderType, string methodName)
		{
			BuilderType = builderType;
			MethodName = methodName;
		}
	}
	[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class CompilerFeatureRequiredAttribute : Attribute
	{
		public const string RefStructs = "RefStructs";

		public const string RequiredMembers = "RequiredMembers";

		public string FeatureName { get; }

		public bool IsOptional { get; set; }

		public CompilerFeatureRequiredAttribute(string featureName)
		{
			FeatureName = featureName;
		}
	}
	[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
	{
		public string[] Arguments { get; }

		public InterpolatedStringHandlerArgumentAttribute(string argument)
		{
			Arguments = new string[1] { argument };
		}

		public InterpolatedStringHandlerArgumentAttribute(params string[] arguments)
		{
			Arguments = arguments;
		}
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class InterpolatedStringHandlerAttribute : Attribute
	{
	}
	[EditorBrowsable(EditorBrowsableState.Never)]
	[ExcludeFromCodeCoverage]
	internal static class IsExternalInit
	{
	}
	[AttributeUsage(AttributeTargets.Method, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ModuleInitializerAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class OverloadResolutionPriorityAttribute : Attribute
	{
		public int Priority { get; }

		public OverloadResolutionPriorityAttribute(int priority)
		{
			Priority = priority;
		}
	}
	[AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ParamCollectionAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class RequiredMemberAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
	[EditorBrowsable(EditorBrowsableState.Never)]
	[ExcludeFromCodeCoverage]
	internal sealed class RequiresLocationAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class SkipLocalsInitAttribute : Attribute
	{
	}
}