Decompiled source of Zips LoQ Patches v0.1.6

BepInEx/plugins/ZipsLOQ/ZipsLOQ.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using InControl;
using Photon.Pun;
using TMPro;
using UnboundLib;
using UnboundLib.Cards;
using UnboundLib.Utils.UI;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
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("ZipsLOQ")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ZipsLOQ")]
[assembly: AssemblyTitle("ZipsLOQ")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ZipsLOQ;

internal static class CombatPatchState
{
	internal sealed class RollingCounter
	{
		private readonly Queue<float> times = new Queue<float>();

		internal void Prune(float now, float window)
		{
			while (times.Count > 0 && now - times.Peek() >= window)
			{
				times.Dequeue();
			}
		}

		internal bool CanAdd(float now, int maximum, float window)
		{
			Prune(now, window);
			return times.Count < Mathf.Max(1, maximum);
		}

		internal void Add(float now)
		{
			times.Enqueue(now);
		}
	}

	internal sealed class BlockState
	{
		internal readonly RollingCounter Blocks = new RollingCounter();

		internal readonly RollingCounter Effects = new RollingCounter();
	}

	internal sealed class SilenceState
	{
		internal struct Slice
		{
			internal float End;

			internal float Duration;

			internal Slice(float end, float duration)
			{
				End = end;
				Duration = duration;
			}
		}

		internal readonly Queue<Slice> Silence = new Queue<Slice>();

		internal float Continuous;

		internal bool WasActive;

		internal void Begin(float now)
		{
			if (!WasActive)
			{
				WasActive = true;
				Continuous = 0f;
			}
		}

		internal void End()
		{
			WasActive = false;
			Continuous = 0f;
		}

		internal void AddTime(float now, float duration, float window)
		{
			if (!(duration <= 0f))
			{
				Silence.Enqueue(new Slice(now, duration));
				Prune(now, window);
			}
		}

		internal float UsedInWindow(float now, float window)
		{
			Prune(now, window);
			float num = now - window;
			float num2 = 0f;
			foreach (Slice item in Silence)
			{
				float num3 = item.End - item.Duration;
				if (item.End > num)
				{
					num2 += item.Duration - Mathf.Max(0f, num - num3);
				}
			}
			return Mathf.Max(0f, num2);
		}

		private void Prune(float now, float window)
		{
			float num = now - window;
			while (Silence.Count > 0 && Silence.Peek().End <= num)
			{
				Silence.Dequeue();
			}
		}
	}

	internal static readonly Dictionary<int, BlockState> Blocks = new Dictionary<int, BlockState>();

	internal static readonly Dictionary<int, SilenceState> Silences = new Dictionary<int, SilenceState>();

	internal static BlockState ForBlock(int playerId)
	{
		if (!Blocks.TryGetValue(playerId, out var value))
		{
			value = new BlockState();
			Blocks.Add(playerId, value);
		}
		return value;
	}

	internal static SilenceState ForSilence(int playerId)
	{
		if (!Silences.TryGetValue(playerId, out var value))
		{
			value = new SilenceState();
			Silences.Add(playerId, value);
		}
		return value;
	}

	internal static void Reset()
	{
		Blocks.Clear();
		Silences.Clear();
		KillTracking.Reset();
	}
}
internal static class KillTracking
{
	private sealed class DamageShare
	{
		internal float Damage;

		internal float MaxHealth;
	}

	internal struct DamageSnapshot
	{
		internal int VictimId;

		internal int AttackerId;

		internal float HealthBefore;

		internal float MaxHealthBefore;

		internal float DamageMagnitude;
	}

	private static readonly Dictionary<int, Dictionary<int, DamageShare>> DamageByVictim = new Dictionary<int, Dictionary<int, DamageShare>>();

	private static readonly Dictionary<int, int> KillsByPlayer = new Dictionary<int, int>();

	internal static bool TryCapture(HealthHandler health, Player attacker, Vector2 damage, out DamageSnapshot snapshot)
	{
		snapshot = default(DamageSnapshot);
		if (!Plugin.IsAuthority || !Plugin.HasEnoughPlayers || (Object)(object)health == (Object)null || (Object)(object)attacker == (Object)null)
		{
			return false;
		}
		Player component = ((Component)health).GetComponent<Player>();
		if ((Object)(object)component == (Object)null || (Object)(object)component.data == (Object)null || component.playerID == attacker.playerID || component.data.dead)
		{
			return false;
		}
		snapshot.VictimId = component.playerID;
		snapshot.AttackerId = attacker.playerID;
		snapshot.HealthBefore = component.data.health;
		snapshot.MaxHealthBefore = Mathf.Max(1f, component.data.maxHealth);
		snapshot.DamageMagnitude = ((Vector2)(ref damage)).magnitude;
		return true;
	}

	internal static void RecordDamage(DamageSnapshot snapshot, HealthHandler health)
	{
		if ((Object)(object)health == (Object)null || !Plugin.IsAuthority)
		{
			return;
		}
		Player component = ((Component)health).GetComponent<Player>();
		if ((Object)(object)component == (Object)null || component.playerID != snapshot.VictimId)
		{
			return;
		}
		float num = Mathf.Clamp(snapshot.HealthBefore - component.data.health, 0f, snapshot.DamageMagnitude);
		if (!(num <= 0f))
		{
			if (!DamageByVictim.TryGetValue(snapshot.VictimId, out var value))
			{
				value = new Dictionary<int, DamageShare>();
				DamageByVictim.Add(snapshot.VictimId, value);
			}
			if (!value.TryGetValue(snapshot.AttackerId, out var value2))
			{
				value2 = new DamageShare();
				value.Add(snapshot.AttackerId, value2);
			}
			value2.Damage += num;
			value2.MaxHealth = snapshot.MaxHealthBefore;
		}
	}

	internal static void OnDeath(HealthHandler health)
	{
		if (!Plugin.IsAuthority || !Plugin.HasEnoughPlayers || (Object)(object)health == (Object)null)
		{
			return;
		}
		Player component = ((Component)health).GetComponent<Player>();
		if ((Object)(object)component == (Object)null || (Object)(object)component.data == (Object)null || !DamageByVictim.TryGetValue(component.playerID, out var value))
		{
			return;
		}
		Player lastSourceOfDamage = component.data.lastSourceOfDamage;
		if ((Object)(object)lastSourceOfDamage == (Object)null || lastSourceOfDamage.playerID == component.playerID)
		{
			DamageByVictim.Remove(component.playerID);
			return;
		}
		if (value.TryGetValue(lastSourceOfDamage.playerID, out var value2))
		{
			float num = Mathf.Max(0f, LoqSettings.EffectiveDamageThresholdPercent) / 100f;
			if (value2.Damage > Mathf.Max(1f, component.data.maxHealth) * num)
			{
				KillsByPlayer.TryGetValue(lastSourceOfDamage.playerID, out var value3);
				value3++;
				KillsByPlayer[lastSourceOfDamage.playerID] = value3;
				int num2 = Mathf.Max(1, LoqSettings.EffectiveKillsPerReward);
				if (value3 % num2 == 0)
				{
					Plugin.AwardKillReward(lastSourceOfDamage.playerID);
				}
			}
		}
		DamageByVictim.Remove(component.playerID);
	}

	internal static void Reset()
	{
		DamageByVictim.Clear();
		KillsByPlayer.Clear();
	}
}
[HarmonyPatch(typeof(Block), "Cooldown")]
internal static class BlockCooldownPatch
{
	private static void Postfix(ref float __result)
	{
		if (LoqSettings.EffectiveAntiBlockEnabled)
		{
			__result = Mathf.Max(__result, 2.5f);
		}
	}
}
[HarmonyPatch(typeof(Block), "RPCA_DoBlock")]
internal static class BlockRateLimitPatch
{
	private static bool Prefix(Block __instance, bool firstBlock, bool dontSetCD, BlockTriggerType triggerType, bool onlyBlockEffects)
	{
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		if (!LoqSettings.EffectiveAntiBlockEnabled || (Object)(object)__instance == (Object)null)
		{
			return true;
		}
		Player component = ((Component)__instance).GetComponent<Player>();
		if ((Object)(object)component == (Object)null)
		{
			return true;
		}
		float unscaledTime = Time.unscaledTime;
		CombatPatchState.BlockState blockState = CombatPatchState.ForBlock(component.playerID);
		blockState.Blocks.Prune(unscaledTime, 10f);
		blockState.Effects.Prune(unscaledTime, 10f);
		if (firstBlock && !dontSetCD && (int)triggerType == 0 && !onlyBlockEffects)
		{
			blockState.Blocks.Add(unscaledTime);
			blockState.Effects.Add(unscaledTime);
			return true;
		}
		if (!blockState.Blocks.CanAdd(unscaledTime, 4, 10f) || !blockState.Effects.CanAdd(unscaledTime, 5, 10f))
		{
			return false;
		}
		blockState.Effects.Add(unscaledTime);
		blockState.Blocks.Add(unscaledTime);
		return true;
	}
}
[HarmonyPatch(typeof(SilenceHandler), "RPCA_AddSilence")]
internal static class SilenceApplyPatch
{
	private static bool Prefix(SilenceHandler __instance, ref float f)
	{
		if (!LoqSettings.EffectiveAntiSilenceEnabled || (Object)(object)__instance == (Object)null)
		{
			return true;
		}
		Player component = ((Component)__instance).GetComponent<Player>();
		if ((Object)(object)component == (Object)null || (Object)(object)component.data == (Object)null)
		{
			return true;
		}
		CombatPatchState.SilenceState silenceState = CombatPatchState.ForSilence(component.playerID);
		float unscaledTime = Time.unscaledTime;
		if (!component.data.isSilenced)
		{
			silenceState.Begin(unscaledTime);
		}
		float num = silenceState.UsedInWindow(unscaledTime, 10f);
		float num2 = Mathf.Max(0f, 2f - silenceState.Continuous);
		float num3 = Mathf.Max(0f, 4f - num);
		float num4 = Mathf.Min(num2, num3);
		if (num4 <= 0f)
		{
			if (component.data.isSilenced)
			{
				__instance.StopSilence();
			}
			return false;
		}
		f = Mathf.Min(f, num4);
		if (component.data.silenceTime > num4)
		{
			component.data.silenceTime = num4;
		}
		return true;
	}
}
[HarmonyPatch(typeof(SilenceHandler), "Update")]
internal static class SilenceDurationPatch
{
	private static void Postfix(SilenceHandler __instance)
	{
		if (!LoqSettings.EffectiveAntiSilenceEnabled || (Object)(object)__instance == (Object)null)
		{
			return;
		}
		Player component = ((Component)__instance).GetComponent<Player>();
		if ((Object)(object)component == (Object)null || (Object)(object)component.data == (Object)null)
		{
			return;
		}
		CombatPatchState.SilenceState silenceState = CombatPatchState.ForSilence(component.playerID);
		if (!component.data.isSilenced)
		{
			silenceState.End();
			return;
		}
		float num = Mathf.Max(0f, Time.unscaledDeltaTime);
		float unscaledTime = Time.unscaledTime;
		silenceState.Begin(unscaledTime);
		silenceState.Continuous += num;
		silenceState.AddTime(unscaledTime, num, 10f);
		float num2 = Mathf.Max(0f, 2f - silenceState.Continuous);
		float num3 = Mathf.Max(0f, 4f - silenceState.UsedInWindow(unscaledTime, 10f));
		float num4 = Mathf.Min(num2, num3);
		if (num4 <= 0f)
		{
			__instance.StopSilence();
		}
		else if (component.data.silenceTime > num4)
		{
			component.data.silenceTime = num4;
		}
	}
}
[HarmonyPatch(typeof(HealthHandler), "DoDamage", new Type[]
{
	typeof(Vector2),
	typeof(Vector2),
	typeof(Color),
	typeof(GameObject),
	typeof(Player),
	typeof(bool),
	typeof(bool),
	typeof(bool)
})]
internal static class DamageAttributionPatch
{
	private static void Prefix(HealthHandler __instance, ref Vector2 damage, Player damagingPlayer, out KillTracking.DamageSnapshot __state)
	{
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		__state = default(KillTracking.DamageSnapshot);
		if ((Object)(object)damagingPlayer != (Object)null && (Object)(object)__instance != (Object)null)
		{
			Player component = ((Component)__instance).GetComponent<Player>();
			if ((Object)(object)component != (Object)null && damagingPlayer.teamID == component.teamID)
			{
				float num = Mathf.Clamp(LoqSettings.EffectiveFriendlyDamageMultiplier, 0f, 2f);
				damage *= num;
			}
		}
		KillTracking.TryCapture(__instance, damagingPlayer, damage, out __state);
	}

	private static void Postfix(HealthHandler __instance, KillTracking.DamageSnapshot __state)
	{
		if (__state.MaxHealthBefore > 0f)
		{
			KillTracking.RecordDamage(__state, __instance);
		}
	}
}
[HarmonyPatch(typeof(HealthHandler), "RPCA_Die")]
internal static class KillRewardDeathPatch
{
	private static void Postfix(HealthHandler __instance)
	{
		KillTracking.OnDeath(__instance);
	}
}
[HarmonyPatch(typeof(HealthHandler), "RPCA_Die_Phoenix")]
internal static class KillRewardPhoenixPatch
{
	private static void Postfix(HealthHandler __instance)
	{
		KillTracking.OnDeath(__instance);
	}
}
internal sealed class ProjectileBounceCounter : MonoBehaviour
{
	internal int Count;
}
internal sealed class PendingBlockHit : MonoBehaviour
{
	internal bool Waiting;
}
[HarmonyPatch(typeof(ProjectileHit), "Hit")]
internal static class BlockLatencyFixPatch
{
	private static readonly HashSet<int> BypassDelay = new HashSet<int>();

	private static bool Prefix(ProjectileHit __instance, HitInfo hit, bool forceCall)
	{
		int num = Mathf.Clamp(LoqSettings.EffectiveBlockLatencyWindowMs, 0, 50);
		if ((Object)(object)__instance == (Object)null || hit == null || (Object)(object)hit.transform == (Object)null || num <= 0 || BypassDelay.Contains(((Object)__instance).GetInstanceID()))
		{
			return true;
		}
		if ((Object)(object)__instance.view == (Object)null || (!__instance.view.IsMine && !forceCall))
		{
			return true;
		}
		HealthHandler component = ((Component)hit.transform).GetComponent<HealthHandler>();
		if ((Object)(object)component == (Object)null || (Object)(object)((Component)component).GetComponent<Player>() == (Object)null)
		{
			return true;
		}
		Block component2 = ((Component)component).GetComponent<Block>();
		if ((Object)(object)component2 == (Object)null || component2.IsBlocking())
		{
			return true;
		}
		PendingBlockHit pendingBlockHit = ((Component)__instance).GetComponent<PendingBlockHit>();
		if ((Object)(object)pendingBlockHit == (Object)null)
		{
			pendingBlockHit = ((Component)__instance).gameObject.AddComponent<PendingBlockHit>();
		}
		if (pendingBlockHit.Waiting)
		{
			return false;
		}
		pendingBlockHit.Waiting = true;
		((MonoBehaviour)__instance).StartCoroutine(ResolveAfterBlockWindow(__instance, hit, forceCall, pendingBlockHit, num));
		return false;
	}

	private static IEnumerator ResolveAfterBlockWindow(ProjectileHit projectile, HitInfo hit, bool forceCall, PendingBlockHit pending, int delayMs)
	{
		MoveTransform movement = ((Component)projectile).GetComponent<MoveTransform>();
		bool movementWasEnabled = (Object)(object)movement != (Object)null && ((Behaviour)movement).enabled;
		if ((Object)(object)movement != (Object)null)
		{
			((Behaviour)movement).enabled = false;
		}
		yield return (object)new WaitForSecondsRealtime((float)delayMs / 1000f);
		if ((Object)(object)projectile == (Object)null)
		{
			yield break;
		}
		if ((Object)(object)movement != (Object)null)
		{
			((Behaviour)movement).enabled = movementWasEnabled;
		}
		if ((Object)(object)pending != (Object)null)
		{
			pending.Waiting = false;
		}
		int projectileId = ((Object)projectile).GetInstanceID();
		BypassDelay.Add(projectileId);
		try
		{
			((RayHit)projectile).Hit(hit, forceCall);
		}
		finally
		{
			BypassDelay.Remove(projectileId);
		}
	}
}
[HarmonyPatch(typeof(RayHitReflect), "DoHitEffect")]
internal static class BulletBounceLimitPatch
{
	private static bool Prefix(RayHitReflect __instance, ref HasToReturn __result)
	{
		if ((Object)(object)__instance == (Object)null)
		{
			return true;
		}
		int num = Mathf.Clamp(LoqSettings.EffectiveBulletBounceLimit, 0, 50);
		ProjectileBounceCounter component = ((Component)__instance).GetComponent<ProjectileBounceCounter>();
		if ((Object)(object)component != (Object)null && component.Count < num)
		{
			return true;
		}
		if ((Object)(object)component == (Object)null && num > 0)
		{
			return true;
		}
		__result = (HasToReturn)1;
		return false;
	}

	private static void Postfix(RayHitReflect __instance, HasToReturn __result)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Invalid comparison between Unknown and I4
		if ((Object)(object)__instance != (Object)null && (int)__result == 0)
		{
			ProjectileBounceCounter projectileBounceCounter = ((Component)__instance).GetComponent<ProjectileBounceCounter>();
			if ((Object)(object)projectileBounceCounter == (Object)null)
			{
				projectileBounceCounter = ((Component)__instance).gameObject.AddComponent<ProjectileBounceCounter>();
			}
			projectileBounceCounter.Count++;
		}
	}
}
[HarmonyPatch(typeof(ScreenEdgeBounce), "DoHit")]
internal static class VoidBounceLimitPatch
{
	private static bool Prefix(ScreenEdgeBounce __instance)
	{
		if ((Object)(object)__instance == (Object)null)
		{
			return true;
		}
		int num = Mathf.Clamp(LoqSettings.EffectiveBulletBounceLimit, 0, 50);
		ProjectileHit componentInParent = ((Component)__instance).GetComponentInParent<ProjectileHit>();
		ProjectileBounceCounter projectileBounceCounter = (((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).GetComponent<ProjectileBounceCounter>() : null);
		if (((Object)(object)projectileBounceCounter == (Object)null && num > 0) || ((Object)(object)projectileBounceCounter != (Object)null && projectileBounceCounter.Count < num))
		{
			return true;
		}
		if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.view != (Object)null)
		{
			if (componentInParent.view.IsMine)
			{
				PhotonNetwork.Destroy(((Component)componentInParent).gameObject);
			}
		}
		else if ((Object)(object)componentInParent != (Object)null)
		{
			Object.Destroy((Object)(object)((Component)componentInParent).gameObject);
		}
		return false;
	}
}
[HarmonyPatch(typeof(CardChoice), "DoPick")]
internal static class RewardCardPickPhasePatch
{
	private static void Postfix(ref IEnumerator __result)
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			__result = Plugin.Instance.ContinueCardPickWithPendingRewards(__result);
		}
	}
}
internal static class LoqSettings
{
	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static UnityAction <>9__45_0;

		public static Func<object, string> <>9__47_0;

		public static UnityAction<bool> <>9__49_0;

		public static UnityAction<bool> <>9__49_1;

		public static UnityAction<float> <>9__52_0;

		internal void <RegisterMenu>b__45_0()
		{
		}

		internal string <CreateSyncSignature>b__47_0(object value)
		{
			return Convert.ToString(value, CultureInfo.InvariantCulture);
		}

		internal void <BuildMenu>b__49_0(bool value)
		{
			SetIfHost(AntiBlockEnabled, value);
		}

		internal void <BuildMenu>b__49_1(bool value)
		{
			SetIfHost(AntiSilenceEnabled, value);
		}

		internal void <SliderFriendlyDamage>b__52_0(float value)
		{
			SetIfHost(FriendlyDamageMultiplier, Mathf.Clamp(Mathf.Round(value * 100f) / 100f, 0f, 2f));
		}
	}

	internal const int SyncValueCount = 7;

	internal const int RequiredPlayers = 3;

	private const int DefaultBulletBounceLimit = 5;

	private const float DefaultFriendlyDamageMultiplier = 1f;

	internal const float DamageBuff = 15f;

	internal const float SpeedBuff = 15f;

	internal const float HealthBuff = 20f;

	internal const float AttackSpeedBuff = 25f;

	internal const float ReloadSpeedBuff = 15f;

	internal const int AmmoBuff = 1;

	internal const float RegenBuff = 3f;

	internal const float BlockCooldownBuff = 0.35f;

	internal const float BulletSpeedBuff = 20f;

	internal static ConfigEntry<int> KillsPerReward;

	internal static ConfigEntry<float> DamageThresholdPercent;

	internal static ConfigEntry<bool> AntiBlockEnabled;

	internal static ConfigEntry<bool> AntiSilenceEnabled;

	internal static ConfigEntry<int> BulletBounceLimit;

	internal static ConfigEntry<float> FriendlyDamageMultiplier;

	internal static ConfigEntry<int> BlockLatencyWindowMs;

	private static bool hasHostSettings;

	private static int hostKillsPerReward = 4;

	private static float hostDamageThresholdPercent = 50f;

	private static bool hostAntiBlockEnabled = true;

	private static bool hostAntiSilenceEnabled = true;

	private static int hostBulletBounceLimit = 5;

	private static float hostFriendlyDamageMultiplier = 1f;

	private static int hostBlockLatencyWindowMs;

	private static bool UseHostSettings => PhotonNetwork.InRoom && !PhotonNetwork.OfflineMode && !PhotonNetwork.IsMasterClient;

	internal static int EffectiveKillsPerReward => (!UseHostSettings) ? Mathf.Clamp(KillsPerReward.Value, 1, 20) : (hasHostSettings ? hostKillsPerReward : 4);

	internal static float EffectiveDamageThresholdPercent => (!UseHostSettings) ? Mathf.Clamp(DamageThresholdPercent.Value, 1f, 100f) : (hasHostSettings ? hostDamageThresholdPercent : 50f);

	internal static bool EffectiveAntiBlockEnabled => (!UseHostSettings) ? AntiBlockEnabled.Value : (!hasHostSettings || hostAntiBlockEnabled);

	internal static bool EffectiveAntiSilenceEnabled => (!UseHostSettings) ? AntiSilenceEnabled.Value : (!hasHostSettings || hostAntiSilenceEnabled);

	internal static int EffectiveBulletBounceLimit => (!UseHostSettings) ? Mathf.Clamp(BulletBounceLimit.Value, 0, 50) : (hasHostSettings ? hostBulletBounceLimit : 5);

	internal static float EffectiveFriendlyDamageMultiplier => (!UseHostSettings) ? Mathf.Clamp(FriendlyDamageMultiplier.Value, 0f, 2f) : (hasHostSettings ? hostFriendlyDamageMultiplier : 1f);

	internal static int EffectiveBlockLatencyWindowMs => (!UseHostSettings) ? Mathf.Clamp(BlockLatencyWindowMs.Value, 0, 50) : (hasHostSettings ? hostBlockLatencyWindowMs : 0);

	internal static void Bind(ConfigFile config)
	{
		KillsPerReward = config.Bind<int>("Kill Incentivizer", "Kills per reward", 4, "Qualifying kills required for each reward.");
		DamageThresholdPercent = config.Bind<float>("Kill Incentivizer", "Damage required percent", 50f, "Damage required against a victim's max health. The killer must deal more than this amount.");
		AntiBlockEnabled = config.Bind<bool>("Anti Block Abuse", "Enabled", true, "Enforce the fixed block limits.");
		AntiSilenceEnabled = config.Bind<bool>("Anti Silence Abuse", "Enabled", true, "Enforce the fixed silence limits.");
		BulletBounceLimit = config.Bind<int>("Anti Bounce Abuse", "Maximum bullet bounces", 5, "Hard maximum number of times each bullet can bounce.");
		FriendlyDamageMultiplier = config.Bind<float>("Friendly Damage", "Damage multiplier", 1f, "Multiplier for damage dealt to yourself or a teammate.");
		BlockLatencyWindowMs = config.Bind<int>("Block Latency Fixer", "Block grace window (ms)", 0, "Wait this many milliseconds after a bullet hit before deciding whether the hit was blocked.");
	}

	internal static void RegisterMenu()
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Expected O, but got Unknown
		object obj = <>c.<>9__45_0;
		if (obj == null)
		{
			UnityAction val = delegate
			{
			};
			<>c.<>9__45_0 = val;
			obj = (object)val;
		}
		Unbound.RegisterMenu("Zip's LOQ Patches", (UnityAction)obj, (Action<GameObject>)BuildMenu, (GameObject)null, true);
	}

	internal static object[] CreateSyncPayload()
	{
		return new object[7] { EffectiveKillsPerReward, EffectiveDamageThresholdPercent, AntiBlockEnabled.Value, AntiSilenceEnabled.Value, EffectiveBulletBounceLimit, EffectiveFriendlyDamageMultiplier, EffectiveBlockLatencyWindowMs };
	}

	internal static string CreateSyncSignature()
	{
		return string.Join("|", from value in CreateSyncPayload()
			select Convert.ToString(value, CultureInfo.InvariantCulture));
	}

	internal static void ApplySyncPayload(object[] values)
	{
		KillsPerReward.Value = Mathf.Clamp(Convert.ToInt32(values[0]), 1, 20);
		DamageThresholdPercent.Value = Mathf.Clamp(Convert.ToSingle(values[1]), 1f, 100f);
		AntiBlockEnabled.Value = Convert.ToBoolean(values[2]);
		AntiSilenceEnabled.Value = Convert.ToBoolean(values[3]);
		BulletBounceLimit.Value = Mathf.Clamp(Convert.ToInt32(values[4]), 0, 50);
		FriendlyDamageMultiplier.Value = Mathf.Clamp(Convert.ToSingle(values[5]), 0f, 2f);
		BlockLatencyWindowMs.Value = Mathf.Clamp(Convert.ToInt32(values[6]), 0, 50);
		hostKillsPerReward = Mathf.Clamp(KillsPerReward.Value, 1, 20);
		hostDamageThresholdPercent = Mathf.Clamp(DamageThresholdPercent.Value, 1f, 100f);
		hostAntiBlockEnabled = AntiBlockEnabled.Value;
		hostAntiSilenceEnabled = AntiSilenceEnabled.Value;
		hostBulletBounceLimit = Mathf.Clamp(BulletBounceLimit.Value, 0, 50);
		hostFriendlyDamageMultiplier = Mathf.Clamp(FriendlyDamageMultiplier.Value, 0f, 2f);
		hostBlockLatencyWindowMs = Mathf.Clamp(BlockLatencyWindowMs.Value, 0, 50);
		hasHostSettings = true;
	}

	private static void BuildMenu(GameObject menu)
	{
		TextMeshProUGUI val = default(TextMeshProUGUI);
		MenuHandler.CreateText("Kill Incentivizer", menu, ref val, 36, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
		SliderInt("Kills needed per reward", menu, KillsPerReward, 1f, 20f);
		SliderFloat("Damage needed (% max health)", menu, DamageThresholdPercent, 1f, 100f, 1f);
		MenuHandler.CreateText("Abuse limits", menu, ref val, 36, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
		MenuHandler.CreateToggle(AntiBlockEnabled.Value, "Anti Block Abuse", menu, (UnityAction<bool>)delegate(bool value)
		{
			SetIfHost(AntiBlockEnabled, value);
		}, 25, false, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
		MenuHandler.CreateToggle(AntiSilenceEnabled.Value, "Anti Silence Abuse", menu, (UnityAction<bool>)delegate(bool value)
		{
			SetIfHost(AntiSilenceEnabled, value);
		}, 25, false, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
		MenuHandler.CreateText("Anti Bounce Abuse", menu, ref val, 36, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
		SliderInt("Maximum bullet bounces", menu, BulletBounceLimit, 0f, 50f);
		MenuHandler.CreateText("Friendly Damage", menu, ref val, 36, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
		SliderFriendlyDamage(menu);
		MenuHandler.CreateText("Block Latency Fixer", menu, ref val, 36, true, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
		SliderInt("Block grace window (ms)", menu, BlockLatencyWindowMs, 0f, 50f);
		MenuHandler.CreateText("Kill rewards require 3+ players. Anti-abuse patches also apply in 2-player matches. In Sandbox, press F6 to spawn a passive dummy.", menu, ref val, 24, false, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
	}

	private static void SliderInt(string label, GameObject menu, ConfigEntry<int> entry, float min, float max)
	{
		Slider val = default(Slider);
		MenuHandler.CreateSlider(label + ": " + entry.Value, menu, 22, min, max, (float)entry.Value, (UnityAction<float>)delegate(float value)
		{
			SetIfHost(entry, Mathf.Clamp(Mathf.RoundToInt(value), Mathf.RoundToInt(min), Mathf.RoundToInt(max)));
		}, ref val, true, (Color?)null, (Direction)0, false, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
	}

	private static void SliderFloat(string label, GameObject menu, ConfigEntry<float> entry, float min, float max, float step)
	{
		Slider val = default(Slider);
		MenuHandler.CreateSlider(label + ": " + entry.Value.ToString("0.##"), menu, 22, min, max, entry.Value, (UnityAction<float>)delegate(float value)
		{
			SetIfHost(entry, Mathf.Clamp(Mathf.Round(value / step) * step, min, max));
		}, ref val, false, (Color?)null, (Direction)0, false, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
	}

	private static void SliderFriendlyDamage(GameObject menu)
	{
		Slider val = default(Slider);
		MenuHandler.CreateSlider("Damage multiplier: " + FriendlyDamageMultiplier.Value.ToString("0.00") + "x", menu, 22, 0f, 2f, FriendlyDamageMultiplier.Value, (UnityAction<float>)delegate(float value)
		{
			SetIfHost(FriendlyDamageMultiplier, Mathf.Clamp(Mathf.Round(value * 100f) / 100f, 0f, 2f));
		}, ref val, false, (Color?)null, (Direction)0, false, (Color?)null, (TMP_FontAsset)null, (Material)null, (TextAlignmentOptions?)null);
	}

	private static void SetIfHost<T>(ConfigEntry<T> entry, T value)
	{
		if (Plugin.CanEditSettings)
		{
			entry.Value = value;
			Plugin.BroadcastSettingsAfterEdit();
		}
	}
}
[BepInPlugin("robin.zipsloqpatches", "Zip's LOQ Patches", "0.1.6")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInProcess("Rounds.exe")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string ModId = "robin.zipsloqpatches";

	public const string ModName = "Zip's LOQ Patches";

	public const string Version = "0.1.6";

	internal const string RewardEvent = "ZipsLOQ_BeginRewardPick";

	private const string SettingsEvent = "ZipsLOQ_SettingsSync";

	private static readonly Queue<int> PendingRewardPicks = new Queue<int>();

	private static bool rewardPickRunning;

	private static bool syncingSettings;

	private string lastSettingsSignature = "";

	private int lastRoomPlayerCount = -1;

	private bool dummySpawnInProgress;

	internal static Plugin Instance { get; private set; }

	internal static bool HasEnoughPlayers => (Object)(object)PlayerManager.instance != (Object)null && PlayerManager.instance.players.Count >= 3;

	internal static bool IsAuthority => PhotonNetwork.OfflineMode || PhotonNetwork.IsMasterClient;

	internal static bool CanEditSettings => PhotonNetwork.OfflineMode || !PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient;

	internal static bool IsSyncingSettings => syncingSettings;

	private void Awake()
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Expected O, but got Unknown
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Expected O, but got Unknown
		Instance = this;
		LoqSettings.Bind(((BaseUnityPlugin)this).Config);
		Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "robin.zipsloqpatches");
		NetworkingManager.RegisterEvent("ZipsLOQ_BeginRewardPick", new PhotonEvent(OnRewardPickRequested));
		NetworkingManager.RegisterEvent("ZipsLOQ_SettingsSync", new PhotonEvent(OnSettingsReceived));
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Zip's LOQ Patches loaded.");
	}

	private void Start()
	{
		SceneManager.sceneLoaded += OnSceneLoaded;
		LoqSettings.RegisterMenu();
		RewardCards.Register();
	}

	private void Update()
	{
		SyncHostSettingsIfNeeded();
		CheckDummyKey();
	}

	private void OnDestroy()
	{
		SceneManager.sceneLoaded -= OnSceneLoaded;
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		string text = ((Scene)(ref scene)).name.ToLowerInvariant();
		if (text.Contains("menu") || text.Contains("character"))
		{
			CombatPatchState.Reset();
			PendingRewardPicks.Clear();
			rewardPickRunning = false;
		}
	}

	private void SyncHostSettingsIfNeeded()
	{
		if (!PhotonNetwork.OfflineMode && PhotonNetwork.IsMasterClient && PhotonNetwork.InRoom)
		{
			int playerCount = PhotonNetwork.CurrentRoom.PlayerCount;
			string text = LoqSettings.CreateSyncSignature();
			if (playerCount != lastRoomPlayerCount || text != lastSettingsSignature)
			{
				lastRoomPlayerCount = playerCount;
				lastSettingsSignature = text;
				NetworkingManager.RaiseEventOthers("ZipsLOQ_SettingsSync", LoqSettings.CreateSyncPayload());
			}
		}
	}

	internal static void BroadcastSettings()
	{
		if (!((Object)(object)Instance == (Object)null) && !PhotonNetwork.OfflineMode && PhotonNetwork.IsMasterClient && PhotonNetwork.InRoom)
		{
			Instance.lastSettingsSignature = LoqSettings.CreateSyncSignature();
			Instance.lastRoomPlayerCount = PhotonNetwork.CurrentRoom.PlayerCount;
			NetworkingManager.RaiseEventOthers("ZipsLOQ_SettingsSync", LoqSettings.CreateSyncPayload());
		}
	}

	private static void OnSettingsReceived(object[] args)
	{
		if (args == null || args.Length != 7)
		{
			return;
		}
		syncingSettings = true;
		try
		{
			LoqSettings.ApplySyncPayload(args);
		}
		catch (Exception ex)
		{
			Debug.LogError("[Zip's LOQ Patches] Could not apply host settings: " + ex);
		}
		finally
		{
			syncingSettings = false;
		}
	}

	private void CheckDummyKey()
	{
		if (Input.GetKeyDown((KeyCode)287) && !((Object)(object)GM_Test.instance == (Object)null) && ((Component)GM_Test.instance).gameObject.activeInHierarchy && !GameManager.lockInput && !dummySpawnInProgress)
		{
			((MonoBehaviour)this).StartCoroutine(SpawnPassiveDummy());
		}
	}

	private IEnumerator SpawnPassiveDummy()
	{
		PlayerAssigner assigner = PlayerAssigner.instance;
		if ((Object)(object)assigner == (Object)null || (Object)(object)PlayerManager.instance == (Object)null || PlayerManager.instance.players.Count >= assigner.maxPlayers)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Sandbox dummy could not be spawned: the player limit has been reached.");
			Unbound.BuildInfoPopup("No free player slots for another dummy.");
			yield break;
		}
		dummySpawnInProgress = true;
		int playerCountBefore = PlayerManager.instance.players.Count;
		yield return ((MonoBehaviour)assigner).StartCoroutine(assigner.CreatePlayer((InputDevice)null, true));
		float wait = 0f;
		while (PlayerManager.instance.players.Count <= playerCountBefore && wait < 5f)
		{
			wait += Time.unscaledDeltaTime;
			yield return null;
		}
		if (PlayerManager.instance.players.Count > playerCountBefore)
		{
			Player spawned = PlayerManager.instance.players[PlayerManager.instance.players.Count - 1];
			PlayerAI[] aiControllers = ((Component)spawned).GetComponentsInChildren<PlayerAI>(true);
			for (int i = 0; i < aiControllers.Length; i++)
			{
				((Behaviour)aiControllers[i]).enabled = false;
			}
			spawned.data.input.ResetInput();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Spawned passive Sandbox dummy with player ID " + spawned.playerID + "."));
			Unbound.BuildInfoPopup("Passive dummy spawned.");
		}
		else
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Sandbox dummy spawn did not create a player.");
			Unbound.BuildInfoPopup("Dummy spawn failed. Check BepInEx/LogOutput.log.");
		}
		dummySpawnInProgress = false;
	}

	private static void OnRewardPickRequested(object[] args)
	{
		if (args != null && args.Length != 0 && args[0] is int)
		{
			PendingRewardPicks.Enqueue((int)args[0]);
		}
	}

	internal IEnumerator ContinueCardPickWithPendingRewards(IEnumerator normalPick)
	{
		while (normalPick?.MoveNext() ?? false)
		{
			yield return normalPick.Current;
		}
		GM_ArmsRace roundGame = GM_ArmsRace.instance;
		if (rewardPickRunning || PendingRewardPicks.Count == 0 || (Object)(object)roundGame == (Object)null || !((Component)roundGame).gameObject.activeInHierarchy || !roundGame.pickPhase || (Object)(object)GameManager.instance == (Object)null || GameManager.instance.battleOngoing)
		{
			yield break;
		}
		rewardPickRunning = true;
		while (PendingRewardPicks.Count > 0)
		{
			int playerId = PendingRewardPicks.Dequeue();
			float wait = 0f;
			while (!RewardCards.Ready && wait < 10f)
			{
				wait += Time.unscaledDeltaTime;
				yield return null;
			}
			wait = 0f;
			while (((Object)(object)CardChoice.instance == (Object)null || (Object)(object)CardChoiceVisuals.instance == (Object)null) && wait < 10f)
			{
				wait += Time.unscaledDeltaTime;
				yield return null;
			}
			if (RewardCards.Ready && (Object)(object)CardChoice.instance != (Object)null && (Object)(object)CardChoiceVisuals.instance != (Object)null)
			{
				yield return ((MonoBehaviour)Instance).StartCoroutine(RewardCards.ShowRewardChoice(playerId));
			}
		}
		rewardPickRunning = false;
	}

	internal static void AwardKillReward(int playerId)
	{
		if (IsAuthority && HasEnoughPlayers && RewardCards.Ready)
		{
			NetworkingManager.RaiseEvent("ZipsLOQ_BeginRewardPick", new object[1] { playerId });
		}
	}

	internal static void BroadcastSettingsAfterEdit()
	{
		if (!IsSyncingSettings)
		{
			BroadcastSettings();
		}
	}
}
internal enum RewardKind
{
	Damage,
	Speed,
	Health,
	AttackSpeed,
	ReloadSpeed,
	Ammo,
	Regeneration,
	BlockCooldown,
	BulletSpeed
}
internal static class RewardCards
{
	private static readonly Dictionary<RewardKind, CardInfo> Cards = new Dictionary<RewardKind, CardInfo>();

	private static readonly List<CardInfo> OrderedCards = new List<CardInfo>();

	private static int builtCount;

	internal static bool Ready => builtCount == 9 && OrderedCards.Count == 9;

	internal static void Register()
	{
		CustomCard.BuildCard<DamageRewardCard>((Action<CardInfo>)delegate(CardInfo info)
		{
			Add(RewardKind.Damage, info);
		});
		CustomCard.BuildCard<SpeedRewardCard>((Action<CardInfo>)delegate(CardInfo info)
		{
			Add(RewardKind.Speed, info);
		});
		CustomCard.BuildCard<HealthRewardCard>((Action<CardInfo>)delegate(CardInfo info)
		{
			Add(RewardKind.Health, info);
		});
		CustomCard.BuildCard<AttackSpeedRewardCard>((Action<CardInfo>)delegate(CardInfo info)
		{
			Add(RewardKind.AttackSpeed, info);
		});
		CustomCard.BuildCard<ReloadSpeedRewardCard>((Action<CardInfo>)delegate(CardInfo info)
		{
			Add(RewardKind.ReloadSpeed, info);
		});
		CustomCard.BuildCard<AmmoRewardCard>((Action<CardInfo>)delegate(CardInfo info)
		{
			Add(RewardKind.Ammo, info);
		});
		CustomCard.BuildCard<RegenerationRewardCard>((Action<CardInfo>)delegate(CardInfo info)
		{
			Add(RewardKind.Regeneration, info);
		});
		CustomCard.BuildCard<BlockCooldownRewardCard>((Action<CardInfo>)delegate(CardInfo info)
		{
			Add(RewardKind.BlockCooldown, info);
		});
		CustomCard.BuildCard<BulletSpeedRewardCard>((Action<CardInfo>)delegate(CardInfo info)
		{
			Add(RewardKind.BulletSpeed, info);
		});
	}

	private static void Add(RewardKind kind, CardInfo info)
	{
		if ((Object)(object)info == (Object)null)
		{
			Debug.LogError("[Zip's LOQ Patches] A reward card failed to register: " + kind);
			return;
		}
		Cards[kind] = info;
		if (!OrderedCards.Contains(info))
		{
			OrderedCards.Add(info);
			builtCount++;
		}
		RefreshCard(info, kind);
	}

	internal static string Title(RewardKind kind)
	{
		return kind switch
		{
			RewardKind.Damage => "LOQ: Damage", 
			RewardKind.Speed => "LOQ: Speed", 
			RewardKind.Health => "LOQ: Health", 
			RewardKind.AttackSpeed => "LOQ: Attack Speed", 
			RewardKind.ReloadSpeed => "LOQ: Quick Reload", 
			RewardKind.Ammo => "LOQ: Extra Ammo", 
			RewardKind.Regeneration => "LOQ: Regeneration", 
			RewardKind.BlockCooldown => "LOQ: Quick Block", 
			_ => "LOQ: Bullet Speed", 
		};
	}

	internal static string Description(RewardKind kind)
	{
		return kind switch
		{
			RewardKind.Damage => "Increase damage by " + 15f.ToString("0.#") + "%.", 
			RewardKind.Speed => "Increase movement speed by " + 15f.ToString("0.#") + "%.", 
			RewardKind.Health => "Increase maximum health by " + 20f.ToString("0.#") + "%.", 
			RewardKind.AttackSpeed => "Increase attack speed by " + 25f.ToString("0.#") + "%.", 
			RewardKind.ReloadSpeed => "Reload " + 15f.ToString("0.#") + "% faster.", 
			RewardKind.Ammo => "Gain " + 1 + " ammo.", 
			RewardKind.Regeneration => "Regenerate " + 3f.ToString("0.##") + " health per second.", 
			RewardKind.BlockCooldown => "Reduce block cooldown by " + 0.35f.ToString("0.##") + " seconds.", 
			_ => "Increase bullet speed by " + 20f.ToString("0.#") + "%.", 
		};
	}

	internal static IEnumerator ShowRewardChoice(int playerId)
	{
		if (Ready && !((Object)(object)CardChoice.instance == (Object)null) && !((Object)(object)CardChoiceVisuals.instance == (Object)null))
		{
			RefreshAllCards();
			CardInfo[] originalCards = CardChoice.instance.cards;
			CardChoice.instance.cards = OrderedCards.ToArray();
			CardChoiceVisuals.instance.Show(playerId, true);
			yield return CardChoice.instance.DoPick(1, playerId, (PickerType)1);
			CardChoice.instance.cards = originalCards;
		}
	}

	internal static void RefreshAllCards()
	{
		KeyValuePair<RewardKind, CardInfo>[] array = Cards.ToArray();
		for (int i = 0; i < array.Length; i++)
		{
			KeyValuePair<RewardKind, CardInfo> keyValuePair = array[i];
			RefreshCard(keyValuePair.Value, keyValuePair.Key);
		}
	}

	private static void RefreshCard(CardInfo info, RewardKind kind)
	{
		info.cardName = Title(kind);
		info.cardDestription = Description(kind);
		info.allowMultiple = true;
		Gun component = ((Component)info).GetComponent<Gun>();
		if ((Object)(object)component != (Object)null)
		{
			component.damage = 1f;
			component.attackSpeed = 1f;
			component.reloadTime = 1f;
			component.ammo = 0;
			component.projectileSpeed = 1f;
		}
		CharacterStatModifiers component2 = ((Component)info).GetComponent<CharacterStatModifiers>();
		if ((Object)(object)component2 != (Object)null)
		{
			component2.movementSpeed = 1f;
			component2.health = 1f;
			component2.regen = 0f;
		}
		Block componentInChildren = ((Component)info).GetComponentInChildren<Block>(true);
		if ((Object)(object)componentInChildren != (Object)null)
		{
			componentInChildren.cdAdd = 0f;
		}
		switch (kind)
		{
		case RewardKind.Damage:
			if ((Object)(object)component != (Object)null)
			{
				component.damage = 1f + Percent(15f);
			}
			break;
		case RewardKind.Speed:
			if ((Object)(object)component2 != (Object)null)
			{
				component2.movementSpeed = 1f + Percent(15f);
			}
			break;
		case RewardKind.Health:
			if ((Object)(object)component2 != (Object)null)
			{
				component2.health = 1f + Percent(20f);
			}
			break;
		case RewardKind.AttackSpeed:
			if ((Object)(object)component != (Object)null)
			{
				component.attackSpeed = 1f + Percent(25f);
			}
			break;
		case RewardKind.ReloadSpeed:
			if ((Object)(object)component != (Object)null)
			{
				component.reloadTime = 1f / (1f + Percent(15f));
			}
			break;
		case RewardKind.Ammo:
			if ((Object)(object)component != (Object)null)
			{
				component.ammo = Mathf.Max(0, 1);
			}
			break;
		case RewardKind.Regeneration:
			if ((Object)(object)component2 != (Object)null)
			{
				component2.regen = Mathf.Max(0f, 3f);
			}
			break;
		case RewardKind.BlockCooldown:
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.cdAdd = 0f - Mathf.Max(0f, 0.35f);
			}
			break;
		case RewardKind.BulletSpeed:
			if ((Object)(object)component != (Object)null)
			{
				component.projectileSpeed = 1f + Percent(20f);
			}
			break;
		}
	}

	private static float Percent(float value)
	{
		return Mathf.Max(0f, value) / 100f;
	}
}
internal abstract class LOQRewardCard : CustomCard
{
	protected abstract RewardKind Kind { get; }

	protected override string GetTitle()
	{
		return RewardCards.Title(Kind);
	}

	protected override string GetDescription()
	{
		return RewardCards.Description(Kind);
	}

	protected override CardInfoStat[] GetStats()
	{
		return (CardInfoStat[])(object)new CardInfoStat[0];
	}

	protected override Rarity GetRarity()
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		return (Rarity)0;
	}

	protected override GameObject GetCardArt()
	{
		return null;
	}

	protected override CardThemeColorType GetTheme()
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		return (CardThemeColorType)2;
	}

	public override string GetModName()
	{
		return "Zip's LOQ Patches";
	}

	public override bool GetEnabled()
	{
		return false;
	}

	public override void SetupCard(CardInfo cardInfo, Gun gun, ApplyCardStats cardStats, CharacterStatModifiers stats, Block block)
	{
		if ((Object)(object)gun != (Object)null)
		{
			gun.damage = 1f;
			gun.attackSpeed = 1f;
			gun.reloadTime = 1f;
			gun.ammo = 0;
			gun.projectileSpeed = 1f;
		}
		if ((Object)(object)stats != (Object)null)
		{
			stats.movementSpeed = 1f;
			stats.health = 1f;
			stats.regen = 0f;
		}
		if ((Object)(object)block != (Object)null)
		{
			block.cdAdd = 0f;
		}
	}

	public override void OnAddCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers stats)
	{
	}
}
internal sealed class DamageRewardCard : LOQRewardCard
{
	protected override RewardKind Kind => RewardKind.Damage;
}
internal sealed class SpeedRewardCard : LOQRewardCard
{
	protected override RewardKind Kind => RewardKind.Speed;
}
internal sealed class HealthRewardCard : LOQRewardCard
{
	protected override RewardKind Kind => RewardKind.Health;
}
internal sealed class AttackSpeedRewardCard : LOQRewardCard
{
	protected override RewardKind Kind => RewardKind.AttackSpeed;
}
internal sealed class ReloadSpeedRewardCard : LOQRewardCard
{
	protected override RewardKind Kind => RewardKind.ReloadSpeed;
}
internal sealed class AmmoRewardCard : LOQRewardCard
{
	protected override RewardKind Kind => RewardKind.Ammo;
}
internal sealed class RegenerationRewardCard : LOQRewardCard
{
	protected override RewardKind Kind => RewardKind.Regeneration;
}
internal sealed class BlockCooldownRewardCard : LOQRewardCard
{
	protected override RewardKind Kind => RewardKind.BlockCooldown;
}
internal sealed class BulletSpeedRewardCard : LOQRewardCard
{
	protected override RewardKind Kind => RewardKind.BulletSpeed;
}