Decompiled source of SummonerCards v1.8.0

SummonerCards.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnboundLib;
using UnboundLib.Cards;
using UnboundLib.GameModes;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 SummonerCards
{
	[HarmonyPatch(typeof(CardChoice), "SpawnUniqueCard")]
	internal static class CardGatePatch
	{
		[ThreadStatic]
		private static int depth;

		private static readonly MethodInfo SpawnUnique = AccessTools.Method(typeof(CardChoice), "SpawnUniqueCard", new Type[2]
		{
			typeof(Vector3),
			typeof(Quaternion)
		}, (Type[])null);

		private static readonly FieldInfo PickerTypeField = AccessTools.Field(typeof(CardChoice), "pickerType");

		private static void Postfix(CardChoice __instance, Vector3 pos, Quaternion rot, ref GameObject __result)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			try
			{
				if ((Object)(object)__result == (Object)null || depth > 40 || SpawnUnique == null)
				{
					return;
				}
				CardInfo component = __result.GetComponent<CardInfo>();
				Player picker = GetPicker(__instance);
				if ((Object)(object)component == (Object)null || (Object)(object)picker == (Object)null || UpgradeGate.IsAllowed(component, picker))
				{
					return;
				}
				depth++;
				try
				{
					Object.Destroy((Object)(object)__result);
					__result = (GameObject)SpawnUnique.Invoke(__instance, new object[2] { pos, rot });
				}
				finally
				{
					depth--;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("SummonerCards card gate: " + ex));
			}
		}

		private static Player GetPicker(CardChoice cc)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Invalid comparison between Unknown and I4
			int pickrID = cc.pickrID;
			if (pickrID < 0 || (Object)(object)PlayerManager.instance == (Object)null)
			{
				return null;
			}
			bool flag = false;
			if (PickerTypeField != null)
			{
				flag = PickerTypeField.GetValue(cc) is PickerType val && (int)val == 0;
			}
			if (flag)
			{
				Player[] playersInTeam = PlayerManager.instance.GetPlayersInTeam(pickrID);
				if (playersInTeam == null || playersInTeam.Length == 0)
				{
					return null;
				}
				return playersInTeam[0];
			}
			List<Player> players = PlayerManager.instance.players;
			if (pickrID >= players.Count)
			{
				return null;
			}
			return players[pickrID];
		}
	}
	public class CardDef
	{
		public string Title;

		public string Description;

		public Rarity Rarity = (Rarity)1;

		public CardThemeColorType Theme = (CardThemeColorType)3;

		public CardInfoStat[] Stats = Array.Empty<CardInfoStat>();

		public static CardInfoStat Stat(bool isGood, string label, string value)
		{
			//IL_0000: 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)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			return new CardInfoStat
			{
				positive = isGood,
				stat = label,
				amount = value
			};
		}
	}
	public static class CardText
	{
		public static readonly CardDef Doppelganger = new CardDef
		{
			Title = "Doppelganger",
			Description = "Blocking splits off a clone that fights for you",
			Rarity = (Rarity)2,
			Theme = (CardThemeColorType)2,
			Stats = (CardInfoStat[])(object)new CardInfoStat[2]
			{
				CardDef.Stat(isGood: false, "Damage", "-50%"),
				CardDef.Stat(isGood: false, "Health", "-50%")
			}
		};

		public static readonly CardDef Necromancer = new CardDef
		{
			Title = "Necromancer",
			Description = "Blocking raises everyone you've killed as allies",
			Rarity = (Rarity)2,
			Theme = (CardThemeColorType)4,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: false, "Undead stats", "-50%") }
		};

		public static readonly CardDef Sentry = new CardDef
		{
			Title = "Sentry",
			Description = "Block to deploy a stationary turret that fires your weapon",
			Rarity = (Rarity)1,
			Theme = (CardThemeColorType)3,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: false, "Turret damage", "-50%") }
		};

		public static readonly CardDef Hive = new CardDef
		{
			Title = "Hive",
			Description = "Block to deploy a bee hive and a swarm that fights for you",
			Rarity = (Rarity)2,
			Theme = (CardThemeColorType)1,
			Stats = (CardInfoStat[])(object)new CardInfoStat[3]
			{
				CardDef.Stat(isGood: true, "Bees", "+3"),
				CardDef.Stat(isGood: false, "Bee damage", "-65%"),
				CardDef.Stat(isGood: false, "Kill the hive", "swarm dies")
			}
		};

		public static readonly CardDef Revenant = new CardDef
		{
			Title = "Revenant",
			Description = "Rise as a full-power ghost when you die",
			Rarity = (Rarity)2,
			Theme = (CardThemeColorType)4,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: false, "Ghost lasts", "10 seconds") }
		};

		public static readonly CardDef BodyDouble = new CardDef
		{
			Title = "Body Double",
			Description = "Blocking swaps places with your clone",
			Rarity = (Rarity)1,
			Theme = (CardThemeColorType)2
		};

		public static readonly CardDef SplitSoul = new CardDef
		{
			Title = "Split Soul",
			Description = "You and your clone share one health pool",
			Rarity = (Rarity)1,
			Theme = (CardThemeColorType)2,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: true, "Split", "no health cost") }
		};

		public static readonly CardDef HallOfMirrors = new CardDef
		{
			Title = "Hall of Mirrors",
			Description = "Keep a second clone out at once",
			Rarity = (Rarity)2,
			Theme = (CardThemeColorType)2,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: true, "Clones", "+1") }
		};

		public static readonly CardDef SharperImage = new CardDef
		{
			Title = "Sharper Image",
			Description = "Your clone deals your full damage",
			Rarity = (Rarity)1,
			Theme = (CardThemeColorType)2,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: true, "Clone damage", "+100%") }
		};

		public static readonly CardDef TwinSentries = new CardDef
		{
			Title = "Twin Sentries",
			Description = "Deploy a second turret",
			Rarity = (Rarity)1,
			Theme = (CardThemeColorType)3,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: true, "Turrets", "+1") }
		};

		public static readonly CardDef Overclocked = new CardDef
		{
			Title = "Overclocked",
			Description = "Your turrets fire at full damage",
			Rarity = (Rarity)2,
			Theme = (CardThemeColorType)3,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: true, "Turret damage", "+100%") }
		};

		public static readonly CardDef Bunkered = new CardDef
		{
			Title = "Bunkered",
			Description = "Your turrets deploy with double health",
			Rarity = (Rarity)1,
			Theme = (CardThemeColorType)3,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: true, "Turret health", "+100%") }
		};

		public static readonly CardDef Bulwark = new CardDef
		{
			Title = "Bulwark",
			Description = "Your turrets block on their own",
			Rarity = (Rarity)1,
			Theme = (CardThemeColorType)3
		};

		public static readonly CardDef PlagueLord = new CardDef
		{
			Title = "Plague Lord",
			Description = "Also raise fallen summons — turrets, undead, bees and more",
			Rarity = (Rarity)2,
			Theme = (CardThemeColorType)4
		};

		public static readonly CardDef SoulHarvest = new CardDef
		{
			Title = "Soul Harvest",
			Description = "Your undead attack at full damage",
			Rarity = (Rarity)2,
			Theme = (CardThemeColorType)4,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: true, "Undead damage", "+100%") }
		};

		public static readonly CardDef UndyingHorde = new CardDef
		{
			Title = "Undying Horde",
			Description = "Every corpse rises as two undead",
			Rarity = (Rarity)1,
			Theme = (CardThemeColorType)4,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: true, "Undead count", "+1") }
		};

		public static readonly CardDef Swarm = new CardDef
		{
			Title = "Swarm",
			Description = "Your hive breeds a bigger swarm",
			Rarity = (Rarity)1,
			Theme = (CardThemeColorType)1,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: true, "Bees", "+2") }
		};

		public static readonly CardDef Tiny = new CardDef
		{
			Title = "Tiny Swarm",
			Description = "Your bees shrink down — smaller and harder to hit",
			Rarity = (Rarity)1,
			Theme = (CardThemeColorType)1,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: true, "Bee size", "smaller") }
		};

		public static readonly CardDef Lingering = new CardDef
		{
			Title = "Lingering Spirit",
			Description = "Your ghost lingers far longer before fading",
			Rarity = (Rarity)1,
			Theme = (CardThemeColorType)4,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: true, "Ghost lasts", "+5 seconds") }
		};

		public static readonly CardDef SecondWind = new CardDef
		{
			Title = "Second Wind",
			Description = "If your ghost lands a kill, you come back to life",
			Rarity = (Rarity)2,
			Theme = (CardThemeColorType)4,
			Stats = (CardInfoStat[])(object)new CardInfoStat[1] { CardDef.Stat(isGood: true, "Ghost kill", "you respawn") }
		};
	}
	public class CloneSummoner : MonoBehaviour
	{
		private static readonly List<CloneSummoner> Active = new List<CloneSummoner>();

		private const float HalfMult = 0.5f;

		public int cardCount;

		public int swapCount;

		public int shareCount;

		public int bonusClones;

		public int cloneFullDamageCount;

		private Player summoner;

		private Block block;

		private bool halved;

		private bool damageHalved;

		private float origDamage;

		private float origMaxHealth;

		public bool CanSwap => swapCount > 0;

		public bool SharesHealth => shareCount > 0;

		public bool CloneFullDamage => cloneFullDamageCount > 0;

		public int MaxClones => 1 + bonusClones;

		private void Start()
		{
			summoner = ((Component)this).GetComponent<Player>();
			block = ((Component)this).GetComponent<Block>();
			if ((Object)(object)block != (Object)null)
			{
				Block obj = block;
				obj.BlockAction = (Action<BlockTriggerType>)Delegate.Combine(obj.BlockAction, new Action<BlockTriggerType>(OnBlock));
			}
			if (!Active.Contains(this))
			{
				Active.Add(this);
			}
			Summons.EnsureHook();
		}

		private void OnDestroy()
		{
			if ((Object)(object)block != (Object)null)
			{
				Block obj = block;
				obj.BlockAction = (Action<BlockTriggerType>)Delegate.Remove(obj.BlockAction, new Action<BlockTriggerType>(OnBlock));
			}
			Active.Remove(this);
			RestoreStats();
		}

		private void OnBlock(BlockTriggerType triggerType)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (!PhotonNetwork.OfflineMode || (Object)(object)summoner == (Object)null || (Object)(object)summoner.data == (Object)null)
			{
				return;
			}
			int num = Summons.CountLiveSummons(summoner, BotKind.Clone);
			Plugin.Log.LogInfo((object)$"Doppelganger: block — {num}/{MaxClones} clone(s) live, you at {((Component)summoner).transform.position}.");
			if (num < MaxClones)
			{
				if (!((Object)(object)PlayerAssigner.instance == (Object)null) && !((Object)(object)PlayerAssigner.instance.playerPrefab == (Object)null))
				{
					try
					{
						Summon();
					}
					catch (Exception ex)
					{
						Plugin.Log.LogError((object)("Doppelganger: summon failed — " + ex));
					}
				}
			}
			else if (CanSwap)
			{
				try
				{
					SwapWithClone();
				}
				catch (Exception ex2)
				{
					Plugin.Log.LogError((object)("Doppelganger: swap failed — " + ex2));
				}
			}
		}

		private void Summon()
		{
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			Gun myGun = summoner.data.weaponHandler.gun;
			bool share = SharesHealth;
			if (!halved)
			{
				origDamage = myGun.damage;
				origMaxHealth = summoner.data.maxHealth;
				halved = true;
			}
			myGun.damage = origDamage * 0.5f;
			damageHalved = true;
			if (!share)
			{
				CharacterData data = summoner.data;
				data.maxHealth *= 0.5f;
				summoner.data.health = Mathf.Min(summoner.data.health, summoner.data.maxHealth);
			}
			Vector3 pos = ((Component)summoner).transform.position + Summons.FacingOffset(summoner, 1.5f);
			float cloneHealth = summoner.data.maxHealth;
			float fullDamage = origDamage;
			bool cloneFullDamage = CloneFullDamage;
			Summons.SpawnBot(summoner, pos, Summons.GetColorId(summoner), BotKind.Clone, delegate(GameObject cloneObj)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				CharacterData component = cloneObj.GetComponent<CharacterData>();
				Summons.AddAI(cloneObj, anchored: false, canShoot: true, canBlock: false, pos);
				Summons.ApplyFace(cloneObj, summoner.playerID);
				component.maxHealth = cloneHealth;
				component.health = component.maxHealth;
				NecroManager.CopyGun(myGun, component.weaponHandler.gun);
				NecroManager.CopyValueFields(summoner.data.stats, component.stats, NecroManager.StatFieldsToClone);
				if (cloneFullDamage)
				{
					component.weaponHandler.gun.damage = fullDamage;
				}
				if (share)
				{
					cloneObj.AddComponent<SharedHealthLink>().Init(summoner.data, component);
				}
			});
			Plugin.Log.LogInfo((object)($"Doppelganger: split player {summoner.playerID}" + (share ? " (shared health)." : ".")));
		}

		private void SwapWithClone()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: 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)
			GameObject liveSummon = Summons.GetLiveSummon(summoner, BotKind.Clone);
			if (!((Object)(object)liveSummon == (Object)null))
			{
				Vector3 position = ((Component)summoner).transform.position;
				Vector3 position2 = liveSummon.transform.position;
				Teleport(summoner.data, position2);
				Teleport(liveSummon.GetComponent<CharacterData>(), position);
				Plugin.Log.LogInfo((object)$"Doppelganger: player {summoner.playerID} swapped with clone.");
			}
		}

		private static void Teleport(CharacterData cd, Vector3 pos)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)cd == (Object)null))
			{
				((Component)cd).transform.position = pos;
				if ((Object)(object)cd.playerVel != (Object)null)
				{
					cd.playerVel.position = Vector2.op_Implicit(pos);
				}
			}
		}

		private void RestoreStats()
		{
			if (halved)
			{
				halved = false;
				damageHalved = false;
				if ((Object)(object)summoner != (Object)null && (Object)(object)summoner.data != (Object)null)
				{
					summoner.data.weaponHandler.gun.damage = origDamage;
					summoner.data.maxHealth = origMaxHealth;
				}
			}
		}

		private void OnCloneDied()
		{
			if (damageHalved && !((Object)(object)summoner == (Object)null) && !((Object)(object)summoner.data == (Object)null) && Summons.CountLiveSummons(summoner, BotKind.Clone) <= 0)
			{
				summoner.data.weaponHandler.gun.damage = origDamage;
				damageHalved = false;
			}
		}

		public static void NotifyPossibleCloneDeath(Player dead)
		{
			if ((Object)(object)dead == (Object)null)
			{
				return;
			}
			SummonedBotTag component = ((Component)dead).GetComponent<SummonedBotTag>();
			if ((Object)(object)component == (Object)null || component.kind != BotKind.Clone || (Object)(object)component.master == (Object)null)
			{
				return;
			}
			foreach (CloneSummoner item in Active)
			{
				if ((Object)(object)item.summoner == (Object)(object)component.master)
				{
					item.OnCloneDied();
					break;
				}
			}
		}

		public static void RestoreAll()
		{
			foreach (CloneSummoner item in Active)
			{
				item.RestoreStats();
			}
		}
	}
	public class DoppelgangerCard : SummonerCard
	{
		protected override CardDef Def => CardText.Doppelganger;

		public override void OnAddCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats)
		{
			CloneSummoner cloneSummoner = ((Component)player).gameObject.GetComponent<CloneSummoner>();
			if ((Object)(object)cloneSummoner == (Object)null)
			{
				cloneSummoner = ((Component)player).gameObject.AddComponent<CloneSummoner>();
			}
			cloneSummoner.cardCount++;
		}

		public override void OnRemoveCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats)
		{
			CloneSummoner component = ((Component)player).gameObject.GetComponent<CloneSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.cardCount--;
				if (component.cardCount <= 0)
				{
					Object.Destroy((Object)(object)component);
				}
			}
		}
	}
	public class DoppelgangerShareCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Clone;

		protected override CardDef Def => CardText.SplitSoul;

		protected override void Apply(Player player)
		{
			CloneSummoner component = ((Component)player).GetComponent<CloneSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.shareCount++;
			}
		}

		protected override void Remove(Player player)
		{
			CloneSummoner component = ((Component)player).GetComponent<CloneSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.shareCount--;
			}
		}
	}
	public class DoppelgangerSharpCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Clone;

		protected override CardDef Def => CardText.SharperImage;

		protected override void Apply(Player player)
		{
			CloneSummoner component = ((Component)player).GetComponent<CloneSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.cloneFullDamageCount++;
			}
		}

		protected override void Remove(Player player)
		{
			CloneSummoner component = ((Component)player).GetComponent<CloneSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.cloneFullDamageCount--;
			}
		}
	}
	public class DoppelgangerSwapCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Clone;

		protected override CardDef Def => CardText.BodyDouble;

		protected override void Apply(Player player)
		{
			CloneSummoner component = ((Component)player).GetComponent<CloneSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.swapCount++;
			}
		}

		protected override void Remove(Player player)
		{
			CloneSummoner component = ((Component)player).GetComponent<CloneSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.swapCount--;
			}
		}
	}
	public class DoppelgangerTwinCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Clone;

		protected override CardDef Def => CardText.HallOfMirrors;

		protected override void Apply(Player player)
		{
			CloneSummoner component = ((Component)player).GetComponent<CloneSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.bonusClones++;
			}
		}

		protected override void Remove(Player player)
		{
			CloneSummoner component = ((Component)player).GetComponent<CloneSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.bonusClones--;
			}
		}
	}
	public class DualSentryCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Turret;

		protected override CardDef Def => CardText.TwinSentries;

		protected override void Apply(Player player)
		{
			TurretSummoner component = ((Component)player).GetComponent<TurretSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.bonusTurrets++;
			}
		}

		protected override void Remove(Player player)
		{
			TurretSummoner component = ((Component)player).GetComponent<TurretSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.bonusTurrets--;
			}
		}
	}
	[HarmonyPatch(typeof(CharacterSelectionMenu), "PlayerJoined")]
	internal static class CharacterSelectionMenu_PlayerJoined_Patch
	{
		private static bool Prefix(Player joinedPlayer)
		{
			if (!((Object)(object)joinedPlayer == (Object)null))
			{
				return (Object)(object)((Component)joinedPlayer).GetComponent<SummonedBotTag>() == (Object)null;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(PlayerManager), "PlayerDied")]
	internal static class PlayerManager_PlayerDied_Patch
	{
		private static void Prefix(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			try
			{
				RevenantSummoner component = ((Component)player).GetComponent<RevenantSummoner>();
				if ((Object)(object)component != (Object)null)
				{
					component.OnOwnerDied();
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Revenant prefix: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(FollowPlayer), "LateUpdate")]
	internal static class FollowPlayer_LateUpdate_Patch
	{
		private static bool Prefix(FollowPlayer __instance)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			try
			{
				Player value = Traverse.Create((object)__instance).Field("ownPlayer").GetValue<Player>();
				Player val = (((int)__instance.target != 1) ? value : (((Object)(object)PlayerManager.instance != (Object)null) ? PlayerManager.instance.GetOtherPlayer(value) : null));
				if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).transform == (Object)null)
				{
					return false;
				}
			}
			catch
			{
			}
			return true;
		}
	}
	public class HiveCard : SummonerCard
	{
		protected override CardDef Def => CardText.Hive;

		public override void OnAddCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats)
		{
			HiveSummoner hiveSummoner = ((Component)player).gameObject.GetComponent<HiveSummoner>();
			if ((Object)(object)hiveSummoner == (Object)null)
			{
				hiveSummoner = ((Component)player).gameObject.AddComponent<HiveSummoner>();
			}
			hiveSummoner.cardCount++;
		}

		public override void OnRemoveCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats)
		{
			HiveSummoner component = ((Component)player).gameObject.GetComponent<HiveSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.cardCount--;
				if (component.cardCount <= 0)
				{
					Object.Destroy((Object)(object)component);
				}
			}
		}
	}
	public class HiveSummoner : MonoBehaviour
	{
		public int cardCount;

		public int bonusBees;

		public int sizeUpgradeCount;

		private const float BeeHealthMult = 0.25f;

		private const float BeeDamageMult = 0.35f;

		private Player owner;

		private Block block;

		public int BeeCount => 3 + bonusBees;

		public float BeeSize => Mathf.Max(0.2f, 0.5f - 0.15f * (float)sizeUpgradeCount);

		private void Start()
		{
			owner = ((Component)this).GetComponent<Player>();
			block = ((Component)this).GetComponent<Block>();
			if ((Object)(object)block != (Object)null)
			{
				Block obj = block;
				obj.BlockAction = (Action<BlockTriggerType>)Delegate.Combine(obj.BlockAction, new Action<BlockTriggerType>(OnBlock));
			}
			Summons.EnsureHook();
		}

		private void OnDestroy()
		{
			if ((Object)(object)block != (Object)null)
			{
				Block obj = block;
				obj.BlockAction = (Action<BlockTriggerType>)Delegate.Remove(obj.BlockAction, new Action<BlockTriggerType>(OnBlock));
			}
		}

		private void OnBlock(BlockTriggerType triggerType)
		{
			if (!PhotonNetwork.OfflineMode || (Object)(object)owner == (Object)null || (Object)(object)owner.data == (Object)null || (Object)(object)PlayerAssigner.instance == (Object)null || (Object)(object)PlayerAssigner.instance.playerPrefab == (Object)null || Summons.HasLiveSummon(owner, BotKind.Hive))
			{
				return;
			}
			try
			{
				SpawnOne();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Hive: spawn failed — " + ex));
			}
		}

		private void SpawnOne()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 hivePos = ((Component)owner).transform.position + Summons.FacingOffset(owner, 1.5f);
			float hiveHealth = owner.data.maxHealth;
			int colorId = Summons.GetColorId(owner);
			GameObject hive = Summons.SpawnBot(owner, hivePos, colorId, BotKind.Hive, delegate(GameObject obj)
			{
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				CharacterData component = obj.GetComponent<CharacterData>();
				Summons.AddAI(obj, anchored: true, canShoot: false, canBlock: false, hivePos);
				Summons.ClearFace(obj);
				component.maxHealth = hiveHealth;
				component.health = component.maxHealth;
			});
			Gun myGun = owner.data.weaponHandler.gun;
			float beeHealth = owner.data.maxHealth * 0.25f;
			float beeDamage = myGun.damage * 0.35f;
			float beeSize = BeeSize;
			for (int num = 0; num < BeeCount; num++)
			{
				float num2 = ((num % 2 == 0) ? 1f : (-1f)) * (1f + (float)num) * 0.8f;
				Vector3 beePos = hivePos + Vector3.right * num2 + Vector3.up * 0.5f;
				Summons.SpawnBot(owner, beePos, colorId, BotKind.Bee, delegate(GameObject bee)
				{
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					CharacterData component = bee.GetComponent<CharacterData>();
					Summons.AddAI(bee, anchored: false, canShoot: true, canBlock: false, beePos, canFly: true);
					Summons.ClearFace(bee);
					SummonedBotTag component2 = bee.GetComponent<SummonedBotTag>();
					if ((Object)(object)component2 != (Object)null)
					{
						component2.parentSummon = hive;
					}
					component.maxHealth = beeHealth;
					component.health = component.maxHealth;
					NecroManager.CopyGun(myGun, component.weaponHandler.gun);
					component.weaponHandler.gun.damage = beeDamage;
					NecroManager.CopyValueFields(owner.data.stats, component.stats, NecroManager.StatFieldsToClone);
					Summons.ApplySize(component, beeSize);
					component.jumps = 100;
					component.currentJumps = 100;
				});
			}
			Plugin.Log.LogInfo((object)$"Hive: player {owner.playerID} deployed a hive with {BeeCount} bees.");
		}
	}
	public class HiveSwarmCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Hive;

		protected override CardDef Def => CardText.Swarm;

		protected override void Apply(Player player)
		{
			HiveSummoner component = ((Component)player).GetComponent<HiveSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.bonusBees += 2;
			}
		}

		protected override void Remove(Player player)
		{
			HiveSummoner component = ((Component)player).GetComponent<HiveSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.bonusBees -= 2;
			}
		}
	}
	public class HiveTinyCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Hive;

		protected override CardDef Def => CardText.Tiny;

		protected override void Apply(Player player)
		{
			HiveSummoner component = ((Component)player).GetComponent<HiveSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.sizeUpgradeCount++;
			}
		}

		protected override void Remove(Player player)
		{
			HiveSummoner component = ((Component)player).GetComponent<HiveSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.sizeUpgradeCount--;
			}
		}
	}
	public static class NecroManager
	{
		public class Corpse
		{
			public int deadPlayerId;

			public int colorId;

			public int faceId;

			public int killerId;

			public Vector3 pos;

			public Player dead;
		}

		private static readonly List<Corpse> Corpses = new List<Corpse>();

		public static readonly string[] StatFieldsToClone = new string[5] { "movementSpeed", "jump", "sizeMultiplier", "gravity", "attackSpeedMultiplier" };

		public static void RecordCorpse(Player dead)
		{
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)dead == (Object)null)
			{
				return;
			}
			SummonedBotTag component = ((Component)dead).GetComponent<SummonedBotTag>();
			Player val = (((Object)(object)dead.data != (Object)null) ? dead.data.lastSourceOfDamage : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)dead)
			{
				Plugin.Log.LogInfo((object)$"Necromancer: {dead.playerID} died with no valid killer — no corpse.");
				return;
			}
			Player val2 = val;
			SummonedBotTag component2 = ((Component)val).GetComponent<SummonedBotTag>();
			if ((Object)(object)component2 != (Object)null && (Object)(object)component2.master != (Object)null)
			{
				val2 = component2.master;
			}
			if (!((Object)(object)val2 == (Object)null))
			{
				Reanimator component3 = ((Component)val2).GetComponent<Reanimator>();
				bool flag = (Object)(object)component3 != (Object)null && component3.EmpowersMinions;
				if (!((Object)(object)component != (Object)null) || component.kind == BotKind.Clone || flag)
				{
					int colorId = (((Object)(object)component != (Object)null && component.colorId >= 0) ? component.colorId : Summons.GetColorId(dead));
					int faceId = (((Object)(object)component != (Object)null && component.faceId >= 0) ? component.faceId : dead.playerID);
					Corpses.Add(new Corpse
					{
						deadPlayerId = dead.playerID,
						colorId = colorId,
						faceId = faceId,
						killerId = val2.playerID,
						pos = ((Component)dead).transform.position,
						dead = dead
					});
					Plugin.Log.LogInfo((object)$"Necromancer: recorded corpse {dead.playerID} for player {val2.playerID}.");
				}
			}
		}

		public static List<Corpse> TakeCorpsesFor(int killerId)
		{
			List<Corpse> list = new List<Corpse>();
			for (int num = Corpses.Count - 1; num >= 0; num--)
			{
				Corpse corpse = Corpses[num];
				if (corpse.killerId == killerId)
				{
					Corpses.RemoveAt(num);
					if (!((Object)(object)corpse.dead == (Object)null) && !((Object)(object)corpse.dead.data == (Object)null) && corpse.dead.data.dead)
					{
						list.Add(corpse);
					}
				}
			}
			return list;
		}

		public static void ClearCorpses()
		{
			Corpses.Clear();
		}

		public static void CopyGun(Gun src, Gun dst)
		{
			if (!((Object)(object)src == (Object)null) && !((Object)(object)dst == (Object)null))
			{
				CopyValueFields(src, dst, null);
				dst.objectsToSpawn = src.objectsToSpawn;
				dst.projectiles = src.projectiles;
			}
		}

		public static void CopyValueFields(object src, object dst, string[] whitelist)
		{
			if (src == null || dst == null)
			{
				return;
			}
			FieldInfo[] fields = src.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				if ((whitelist == null || Array.IndexOf(whitelist, fieldInfo.Name) >= 0) && (fieldInfo.FieldType.IsPrimitive || fieldInfo.FieldType.IsEnum))
				{
					try
					{
						fieldInfo.SetValue(dst, fieldInfo.GetValue(src));
					}
					catch
					{
					}
				}
			}
		}
	}
	public class NecromancerCard : SummonerCard
	{
		protected override CardDef Def => CardText.Necromancer;

		public override void OnAddCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats)
		{
			Reanimator reanimator = ((Component)player).gameObject.GetComponent<Reanimator>();
			if ((Object)(object)reanimator == (Object)null)
			{
				reanimator = ((Component)player).gameObject.AddComponent<Reanimator>();
			}
			reanimator.cardCount++;
		}

		public override void OnRemoveCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats)
		{
			Reanimator component = ((Component)player).gameObject.GetComponent<Reanimator>();
			if ((Object)(object)component != (Object)null)
			{
				component.cardCount--;
				if (component.cardCount <= 0)
				{
					Object.Destroy((Object)(object)component);
				}
			}
		}
	}
	public class NecromancerEmpowerCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Reanimated;

		protected override CardDef Def => CardText.PlagueLord;

		protected override void Apply(Player player)
		{
			Reanimator component = ((Component)player).GetComponent<Reanimator>();
			if ((Object)(object)component != (Object)null)
			{
				component.empowerCount++;
			}
		}

		protected override void Remove(Player player)
		{
			Reanimator component = ((Component)player).GetComponent<Reanimator>();
			if ((Object)(object)component != (Object)null)
			{
				component.empowerCount--;
			}
		}
	}
	public class NecromancerHarvestCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Reanimated;

		protected override CardDef Def => CardText.SoulHarvest;

		protected override void Apply(Player player)
		{
			Reanimator component = ((Component)player).GetComponent<Reanimator>();
			if ((Object)(object)component != (Object)null)
			{
				component.fullDamageCount++;
			}
		}

		protected override void Remove(Player player)
		{
			Reanimator component = ((Component)player).GetComponent<Reanimator>();
			if ((Object)(object)component != (Object)null)
			{
				component.fullDamageCount--;
			}
		}
	}
	public class NecromancerHordeCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Reanimated;

		protected override CardDef Def => CardText.UndyingHorde;

		protected override void Apply(Player player)
		{
			Reanimator component = ((Component)player).GetComponent<Reanimator>();
			if ((Object)(object)component != (Object)null)
			{
				component.extraRaiseCount++;
			}
		}

		protected override void Remove(Player player)
		{
			Reanimator component = ((Component)player).GetComponent<Reanimator>();
			if ((Object)(object)component != (Object)null)
			{
				component.extraRaiseCount--;
			}
		}
	}
	[BepInPlugin("com.blueflame327.rounds.summonercards", "SummonerCards", "1.8.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInProcess("Rounds.exe")]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModId = "com.blueflame327.rounds.summonercards";

		public const string ModName = "SummonerCards";

		public const string ModVersion = "1.8.0";

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			new Harmony("com.blueflame327.rounds.summonercards").PatchAll();
		}

		private void Start()
		{
			BuildBase<DoppelgangerCard>();
			BuildBase<NecromancerCard>();
			BuildBase<TurretCard>();
			BuildBase<HiveCard>();
			BuildBase<RevenantCard>();
			BuildUpgrade<DoppelgangerSwapCard>();
			BuildUpgrade<DoppelgangerShareCard>();
			BuildUpgrade<DoppelgangerTwinCard>();
			BuildUpgrade<DoppelgangerSharpCard>();
			BuildUpgrade<DualSentryCard>();
			BuildUpgrade<SentryOverclockCard>();
			BuildUpgrade<SentryFortifyCard>();
			BuildUpgrade<SentryBlockCard>();
			BuildUpgrade<NecromancerEmpowerCard>();
			BuildUpgrade<NecromancerHarvestCard>();
			BuildUpgrade<NecromancerHordeCard>();
			BuildUpgrade<HiveSwarmCard>();
			BuildUpgrade<HiveTinyCard>();
			BuildUpgrade<RevenantDurationCard>();
			BuildUpgrade<RevenantRespawnCard>();
			GameModeManager.AddHook("BattleStart", (Func<IGameModeHandler, IEnumerator>)((IGameModeHandler gm) => BattleStart()));
			GameModeManager.AddHook("PointEnd", (Func<IGameModeHandler, IEnumerator>)((IGameModeHandler gm) => Cleanup()));
			GameModeManager.AddHook("RoundEnd", (Func<IGameModeHandler, IEnumerator>)((IGameModeHandler gm) => Cleanup()));
			GameModeManager.AddHook("GameEnd", (Func<IGameModeHandler, IEnumerator>)((IGameModeHandler gm) => Cleanup()));
			Unbound.RegisterCredits("SummonerCards", new string[1] { "BlueFlame327" }, new string[1] { "GitHub" }, new string[1] { "https://github.com/BlueFlame327/SummonerCards" });
			Log.LogInfo((object)"SummonerCards v1.8.0 loaded.");
		}

		private static void BuildBase<T>() where T : CustomCard
		{
			CustomCard.BuildCard<T>((Action<CardInfo>)delegate(CardInfo cardInfo)
			{
				cardInfo.allowMultiple = false;
			});
		}

		private static void BuildUpgrade<T>() where T : UpgradeCard
		{
			CustomCard.BuildCard<T>((Action<CardInfo>)delegate(CardInfo cardInfo)
			{
				T component = ((Component)cardInfo).GetComponent<T>();
				if (!((Object)(object)component == (Object)null))
				{
					cardInfo.allowMultiple = component.AllowMultiple;
					UpgradeGate.Register(cardInfo.cardName, component.Family);
				}
			});
		}

		private static IEnumerator BattleStart()
		{
			Summons.PurgeStragglers();
			yield break;
		}

		private static IEnumerator Cleanup()
		{
			Summons.OnPointEnd();
			NecroManager.ClearCorpses();
			CloneSummoner.RestoreAll();
			yield break;
		}
	}
	public class Reanimator : MonoBehaviour
	{
		public int cardCount;

		public int empowerCount;

		public int fullDamageCount;

		public int extraRaiseCount;

		private const float HalfMult = 0.5f;

		private Player owner;

		private Block block;

		public bool EmpowersMinions => empowerCount > 0;

		public bool FullDamage => fullDamageCount > 0;

		public int RaisesPerCorpse => 1 + extraRaiseCount;

		private void Start()
		{
			owner = ((Component)this).GetComponent<Player>();
			block = ((Component)this).GetComponent<Block>();
			if ((Object)(object)block != (Object)null)
			{
				Block obj = block;
				obj.BlockAction = (Action<BlockTriggerType>)Delegate.Combine(obj.BlockAction, new Action<BlockTriggerType>(OnBlock));
			}
			Summons.EnsureHook();
		}

		private void OnDestroy()
		{
			if ((Object)(object)block != (Object)null)
			{
				Block obj = block;
				obj.BlockAction = (Action<BlockTriggerType>)Delegate.Remove(obj.BlockAction, new Action<BlockTriggerType>(OnBlock));
			}
		}

		private void OnBlock(BlockTriggerType triggerType)
		{
			if (!PhotonNetwork.OfflineMode || (Object)(object)owner == (Object)null)
			{
				return;
			}
			List<NecroManager.Corpse> list = NecroManager.TakeCorpsesFor(owner.playerID);
			Plugin.Log.LogInfo((object)$"Necromancer: player {owner.playerID} blocked — {list.Count} corpse(s) to raise.");
			foreach (NecroManager.Corpse item in list)
			{
				try
				{
					Reanimate(item);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Necromancer: reanimate failed — " + ex));
				}
			}
		}

		private void Reanimate(NecroManager.Corpse c)
		{
			int raisesPerCorpse = RaisesPerCorpse;
			for (int i = 0; i < raisesPerCorpse; i++)
			{
				RaiseOne(c, i);
			}
		}

		private void RaiseOne(NecroManager.Corpse c, int index)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			CharacterData data = c.dead.data;
			Gun deadGun = data.weaponHandler.gun;
			float num = ((index % 2 == 0) ? 1f : (-1f)) * (float)index * 1.2f;
			Vector3 pos = c.pos + Vector3.up * 1.5f + Vector3.right * num;
			float botHealth = data.maxHealth * 0.5f;
			float botDamage = deadGun.damage * (FullDamage ? 1f : 0.5f);
			int faceId = c.faceId;
			int colorId = c.colorId;
			Summons.SpawnBot(owner, pos, c.colorId, BotKind.Reanimated, delegate(GameObject botObj)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				CharacterData component = botObj.GetComponent<CharacterData>();
				Summons.AddAI(botObj, anchored: false, canShoot: true, canBlock: false, pos);
				Summons.ClearFace(botObj);
				SummonedBotTag component2 = botObj.GetComponent<SummonedBotTag>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.colorId = colorId;
					component2.faceId = faceId;
				}
				component.maxHealth = botHealth;
				component.health = component.maxHealth;
				NecroManager.CopyGun(deadGun, component.weaponHandler.gun);
				component.weaponHandler.gun.damage = botDamage;
			});
			Plugin.Log.LogInfo((object)$"Necromancer: player {owner.playerID} raised {c.deadPlayerId}.");
		}

		private static void ApplyFace(GameObject botObj, int deadPlayerId)
		{
			CharacterCreatorHandler instance = CharacterCreatorHandler.instance;
			if ((Object)(object)instance == (Object)null || deadPlayerId < 0 || deadPlayerId >= instance.selectedPlayerFaces.Length)
			{
				return;
			}
			PlayerFace val = instance.selectedPlayerFaces[deadPlayerId];
			if (val != null)
			{
				CharacterCreatorItemEquipper componentInChildren = botObj.GetComponentInChildren<CharacterCreatorItemEquipper>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.EquipFace(val);
				}
			}
		}
	}
	public class RevenantCard : SummonerCard
	{
		protected override CardDef Def => CardText.Revenant;

		public override void OnAddCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats)
		{
			RevenantSummoner revenantSummoner = ((Component)player).gameObject.GetComponent<RevenantSummoner>();
			if ((Object)(object)revenantSummoner == (Object)null)
			{
				revenantSummoner = ((Component)player).gameObject.AddComponent<RevenantSummoner>();
			}
			revenantSummoner.cardCount++;
		}

		public override void OnRemoveCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats)
		{
			RevenantSummoner component = ((Component)player).gameObject.GetComponent<RevenantSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.cardCount--;
				if (component.cardCount <= 0)
				{
					Object.Destroy((Object)(object)component);
				}
			}
		}
	}
	public class RevenantDurationCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Revenant;

		protected override CardDef Def => CardText.Lingering;

		protected override void Apply(Player player)
		{
			RevenantSummoner component = ((Component)player).GetComponent<RevenantSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.extraDurationCount++;
			}
		}

		protected override void Remove(Player player)
		{
			RevenantSummoner component = ((Component)player).GetComponent<RevenantSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.extraDurationCount--;
			}
		}
	}
	public class RevenantRespawnCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Revenant;

		protected override CardDef Def => CardText.SecondWind;

		protected override void Apply(Player player)
		{
			RevenantSummoner component = ((Component)player).GetComponent<RevenantSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.respawnOnKillCount++;
			}
		}

		protected override void Remove(Player player)
		{
			RevenantSummoner component = ((Component)player).GetComponent<RevenantSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.respawnOnKillCount--;
			}
		}
	}
	public class RevenantSummoner : MonoBehaviour
	{
		public int cardCount;

		public int extraDurationCount;

		public int respawnOnKillCount;

		private const float BaseSeconds = 10f;

		private const float BonusSeconds = 5f;

		private Player owner;

		private bool subscribed;

		public float Duration => 10f + (float)extraDurationCount * 5f;

		public bool RespawnsOnKill => respawnOnKillCount > 0;

		private void Start()
		{
			owner = ((Component)this).GetComponent<Player>();
			Summons.EnsureHook();
			if ((Object)(object)PlayerManager.instance != (Object)null)
			{
				PlayerManager.instance.AddPlayerDiedAction((Action<Player, int>)OnAnyDeath);
				subscribed = true;
			}
		}

		private void OnAnyDeath(Player dead, int aliveCount)
		{
			if (!subscribed || cardCount <= 0 || !PhotonNetwork.OfflineMode || (Object)(object)owner == (Object)null || (Object)(object)owner.data == (Object)null || (Object)(object)dead == (Object)null || !RespawnsOnKill || !owner.data.dead || !((Object)(object)dead != (Object)(object)owner) || !((Object)(object)dead.data != (Object)null))
			{
				return;
			}
			Player lastSourceOfDamage = dead.data.lastSourceOfDamage;
			SummonedBotTag summonedBotTag = (((Object)(object)lastSourceOfDamage != (Object)null) ? ((Component)lastSourceOfDamage).GetComponent<SummonedBotTag>() : null);
			if (!((Object)(object)summonedBotTag != (Object)null) || summonedBotTag.kind != BotKind.Revenant || !((Object)(object)summonedBotTag.master == (Object)(object)owner))
			{
				return;
			}
			try
			{
				RespawnOwner();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Revenant: respawn failed — " + ex));
			}
		}

		public void OnOwnerDied()
		{
			if (cardCount <= 0 || !PhotonNetwork.OfflineMode || (Object)(object)owner == (Object)null || (Object)(object)owner.data == (Object)null || Summons.HasLiveSummon(owner, BotKind.Revenant))
			{
				return;
			}
			try
			{
				SpawnRevenant();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Revenant: summon failed — " + ex));
			}
		}

		private void RespawnOwner()
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)owner.data.healthHandler != (Object)null)
			{
				owner.data.healthHandler.Revive(true);
			}
			Plugin.Log.LogInfo((object)$"Revenant: player {owner.playerID} respawned from a ghost kill.");
			GameObject liveSummon = Summons.GetLiveSummon(owner, BotKind.Revenant);
			if ((Object)(object)liveSummon != (Object)null)
			{
				CharacterData component = liveSummon.GetComponent<CharacterData>();
				if ((Object)(object)component != (Object)null && !component.dead && (Object)(object)component.healthHandler != (Object)null)
				{
					component.healthHandler.DoDamage(new Vector2(0f, -1f) * 100000f, Vector2.op_Implicit(((Component)component).transform.position), Color.white, (GameObject)null, (Player)null, false, true, true);
				}
			}
		}

		private void SpawnRevenant()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 pos = ((Component)owner).transform.position;
			Gun myGun = owner.data.weaponHandler.gun;
			float ghostHealth = owner.data.maxHealth;
			GameObject obj = Summons.SpawnBot(owner, pos, Summons.GetColorId(owner), BotKind.Revenant, delegate(GameObject o)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				CharacterData component2 = o.GetComponent<CharacterData>();
				Summons.AddAI(o, anchored: false, canShoot: true, canBlock: false, pos);
				Summons.ClearFace(o);
				component2.maxHealth = ghostHealth;
				component2.health = component2.maxHealth;
				NecroManager.CopyGun(myGun, component2.weaponHandler.gun);
				NecroManager.CopyValueFields(owner.data.stats, component2.stats, NecroManager.StatFieldsToClone);
			});
			SummonedBotTag component = obj.GetComponent<SummonedBotTag>();
			if ((Object)(object)component != (Object)null)
			{
				component.survivesMasterDeath = true;
			}
			Summons.KillAfter(obj, Duration);
			Plugin.Log.LogInfo((object)$"Revenant: player {owner.playerID} rose for a last stand.");
		}
	}
	public class SentryBlockCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Turret;

		protected override CardDef Def => CardText.Bulwark;

		protected override void Apply(Player player)
		{
			TurretSummoner component = ((Component)player).GetComponent<TurretSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.blockCount++;
			}
		}

		protected override void Remove(Player player)
		{
			TurretSummoner component = ((Component)player).GetComponent<TurretSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.blockCount--;
			}
		}
	}
	public class SentryFortifyCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Turret;

		protected override CardDef Def => CardText.Bunkered;

		protected override void Apply(Player player)
		{
			TurretSummoner component = ((Component)player).GetComponent<TurretSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.healthBonusCount++;
			}
		}

		protected override void Remove(Player player)
		{
			TurretSummoner component = ((Component)player).GetComponent<TurretSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.healthBonusCount--;
			}
		}
	}
	public class SentryOverclockCard : UpgradeCard
	{
		public override BotKind Family => BotKind.Turret;

		protected override CardDef Def => CardText.Overclocked;

		protected override void Apply(Player player)
		{
			TurretSummoner component = ((Component)player).GetComponent<TurretSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.fullDamageCount++;
			}
		}

		protected override void Remove(Player player)
		{
			TurretSummoner component = ((Component)player).GetComponent<TurretSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.fullDamageCount--;
			}
		}
	}
	public class SharedHealthLink : MonoBehaviour
	{
		private CharacterData a;

		private CharacterData b;

		private float pool;

		private float lastA;

		private float lastB;

		private bool started;

		public void Init(CharacterData summoner, CharacterData clone)
		{
			a = summoner;
			b = clone;
			pool = Mathf.Min(a.health, b.health);
			lastA = a.health;
			lastB = b.health;
			started = true;
		}

		private void FixedUpdate()
		{
			if (!started)
			{
				return;
			}
			if ((Object)(object)a == (Object)null || (Object)(object)b == (Object)null || (Object)(object)a.player == (Object)null || (Object)(object)b.player == (Object)null)
			{
				Done();
				return;
			}
			if (a.dead || b.dead)
			{
				Done();
				return;
			}
			pool += a.health - lastA + (b.health - lastB);
			float num = Mathf.Min(a.maxHealth, b.maxHealth);
			pool = Mathf.Clamp(pool, 0f, num);
			if (pool <= 0f)
			{
				Kill(a);
				Kill(b);
				Done();
			}
			else
			{
				a.health = pool;
				b.health = pool;
				lastA = pool;
				lastB = pool;
			}
		}

		private static void Kill(CharacterData cd)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)cd == (Object)null) && !cd.dead && !((Object)(object)cd.healthHandler == (Object)null))
			{
				cd.healthHandler.DoDamage(new Vector2(0f, -1f) * 100000f, Vector2.op_Implicit(((Component)cd).transform.position), Color.white, (GameObject)null, (Player)null, false, true, true);
			}
		}

		private void Done()
		{
			started = false;
			Object.Destroy((Object)(object)this);
		}
	}
	public class SummonAI : MonoBehaviour
	{
		public bool isAnchored;

		public bool canShoot = true;

		public bool canBlock;

		public bool canFly;

		public Vector3 spawnPos;

		public Sprite sprite;

		private const float IdleSpeed = 1f;

		private const float StuckAfter = 1.25f;

		private const float NudgeTime = 0.6f;

		private const float FarEnough = 6.5f;

		private static readonly FieldInfo KinematicField = typeof(PlayerVelocity).GetField("isKinematic", BindingFlags.Instance | BindingFlags.NonPublic);

		private CharacterData data;

		private GeneralInput input;

		private Block block;

		private bool anchored;

		private Vector3 anchorPos;

		private Vector3 lastPos;

		private float stuckTimer;

		private float nudgeTimer;

		private void Start()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			data = ((Component)this).GetComponentInParent<CharacterData>();
			if (!((Object)(object)data == (Object)null))
			{
				input = data.input;
				block = ((Component)data).GetComponent<Block>();
				if (isAnchored)
				{
					((MonoBehaviour)this).StartCoroutine(AnchorAfterSettle());
					return;
				}
				((Component)this).gameObject.AddComponent<PlayerAI>();
				lastPos = ((Component)data).transform.position;
			}
		}

		private void Update()
		{
			if (!((Object)(object)data == (Object)null) && !((Object)(object)input == (Object)null) && !((Object)(object)PlayerManager.instance == (Object)null))
			{
				if (isAnchored)
				{
					UpdateAnchored();
				}
				else
				{
					UpdateMobile();
				}
			}
		}

		private void LateUpdate()
		{
			if (!((Object)(object)data == (Object)null))
			{
				if (isAnchored)
				{
					AnchorLock();
				}
				else
				{
					UnstickNudge();
				}
			}
		}

		private IEnumerator AnchorAfterSettle()
		{
			yield return (object)new WaitForSeconds(0.35f);
			if ((Object)(object)data != (Object)null && (Object)(object)data.playerVel != (Object)null)
			{
				anchorPos = ((Component)data).transform.position;
				anchored = true;
				if (KinematicField != null)
				{
					KinematicField.SetValue(data.playerVel, true);
				}
			}
		}

		private void AnchorLock()
		{
			//IL_0022: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			if (!anchored || data.dead)
			{
				return;
			}
			((Component)data).transform.position = anchorPos;
			if ((Object)(object)data.playerVel != (Object)null)
			{
				data.playerVel.position = Vector2.op_Implicit(anchorPos);
				if (KinematicField != null)
				{
					KinematicField.SetValue(data.playerVel, true);
				}
			}
		}

		private void UpdateAnchored()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: 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)
			input.direction = Vector3.zero;
			if (!canShoot)
			{
				input.shootIsPressed = false;
				input.shootWasPressed = false;
				return;
			}
			Player otherPlayer = PlayerManager.instance.GetOtherPlayer(data.player);
			if ((Object)(object)otherPlayer == (Object)null)
			{
				input.shootIsPressed = false;
				input.shootWasPressed = false;
				return;
			}
			GeneralInput obj = input;
			Vector3 val = ((Component)otherPlayer).transform.position - ((Component)this).transform.position;
			obj.aimDirection = ((Vector3)(ref val)).normalized;
			if (canBlock && (Object)(object)block != (Object)null)
			{
				block.TryBlock();
			}
			bool canSee = PlayerManager.instance.CanSeePlayer(Vector2.op_Implicit(((Component)this).transform.position), otherPlayer).canSee;
			input.shootIsPressed = canSee;
			input.shootWasPressed = canSee;
		}

		private void UpdateMobile()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			Player otherPlayer = PlayerManager.instance.GetOtherPlayer(data.player);
			if (!((Object)(object)otherPlayer == (Object)null))
			{
				if (canBlock && (Object)(object)block != (Object)null)
				{
					block.TryBlock();
				}
				if (canFly && ((Component)otherPlayer).transform.position.y > ((Component)this).transform.position.y + 1f)
				{
					input.jumpWasPressed = true;
				}
			}
		}

		private void UnstickNudge()
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			if (data.dead || (Object)(object)input == (Object)null || (Object)(object)PlayerManager.instance == (Object)null)
			{
				return;
			}
			if (!canShoot)
			{
				input.shootIsPressed = false;
				input.shootWasPressed = false;
			}
			Player otherPlayer = PlayerManager.instance.GetOtherPlayer(data.player);
			if ((Object)(object)otherPlayer == (Object)null)
			{
				return;
			}
			Vector3 position = ((Component)data).transform.position;
			float deltaTime = Time.deltaTime;
			float num;
			if (!(deltaTime > 0f))
			{
				num = 0f;
			}
			else
			{
				Vector3 val = position - lastPos;
				num = ((Vector3)(ref val)).magnitude / deltaTime;
			}
			float num2 = num;
			lastPos = position;
			float num3 = Vector2.Distance(Vector2.op_Implicit(position), Vector2.op_Implicit(((Component)otherPlayer).transform.position));
			if (nudgeTimer > 0f)
			{
				nudgeTimer -= deltaTime;
				if (num3 <= 6.5f)
				{
					nudgeTimer = 0f;
					return;
				}
				float num4 = ((Component)otherPlayer).transform.position.x - position.x;
				input.direction = new Vector3((num4 >= 0f) ? 1f : (-1f), 0f, 0f);
			}
			else if (num3 > 6.5f && num2 < 1f)
			{
				stuckTimer += deltaTime;
				if (stuckTimer >= 1.25f)
				{
					stuckTimer = 0f;
					nudgeTimer = 0.6f;
				}
			}
			else
			{
				stuckTimer = 0f;
			}
		}
	}
	public enum BotKind
	{
		Clone,
		Reanimated,
		Turret,
		Hive,
		Bee,
		Revenant
	}
	public class SummonedBotTag : MonoBehaviour
	{
		public BotKind kind;

		public Player master;

		public int colorId = -1;

		public int faceId = -1;

		public GameObject parentSummon;

		public bool survivesMasterDeath;
	}
	public abstract class SummonerCard : CustomCard
	{
		protected abstract CardDef Def { get; }

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

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

		protected override CardInfoStat[] GetStats()
		{
			return Def.Stats;
		}

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

		protected override CardThemeColorType GetTheme()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return Def.Theme;
		}

		protected override GameObject GetCardArt()
		{
			return null;
		}

		public override string GetModName()
		{
			return "SummonerCards";
		}
	}
	public static class Summons
	{
		private class Runner : MonoBehaviour
		{
		}

		private static readonly List<GameObject> All = new List<GameObject>();

		private static bool hooked;

		private static Runner runner;

		private static readonly MethodInfo ConfigureMassAndSize = typeof(CharacterStatModifiers).GetMethod("ConfigureMassAndSize", BindingFlags.Instance | BindingFlags.NonPublic);

		private static Type rwfPlayerExt;

		private static bool rwfChecked;

		private static Runner TheRunner
		{
			get
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_001d: Expected O, but got Unknown
				if ((Object)(object)runner == (Object)null)
				{
					GameObject val = new GameObject("SummonsRunner");
					Object.DontDestroyOnLoad((Object)val);
					runner = val.AddComponent<Runner>();
				}
				return runner;
			}
		}

		public static void EnsureHook()
		{
			if (!hooked && !((Object)(object)PlayerManager.instance == (Object)null))
			{
				PlayerManager.instance.AddPlayerDiedAction((Action<Player, int>)OnPlayerDied);
				hooked = true;
			}
		}

		private static void OnPlayerDied(Player dead, int aliveCount)
		{
			if (!((Object)(object)dead == (Object)null))
			{
				NecroManager.RecordCorpse(dead);
				KillByMaster(dead);
				KillChildrenOf(((Component)dead).gameObject);
				CloneSummoner.NotifyPossibleCloneDeath(dead);
			}
		}

		public static GameObject SpawnBot(Player master, Vector3 pos, int colorPlayerId, BotKind kind, Action<GameObject> configure = null)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = PhotonNetwork.Instantiate(((Object)PlayerAssigner.instance.playerPrefab).name, pos, Quaternion.identity, (byte)0, (object[])null);
			CharacterData component = val.GetComponent<CharacterData>();
			Player player = component.player;
			player.playerID = colorPlayerId;
			player.teamID = master.teamID;
			int count = PlayerManager.instance.players.Count;
			PlayerManager.RegisterPlayer(player);
			SetTeamColor.TeamColorThis(val, PlayerSkinBank.GetPlayerSkinColors(colorPlayerId));
			component.input.controlledElseWhere = true;
			component.master = master;
			component.dead = false;
			PlayerAPI component2 = val.GetComponent<PlayerAPI>();
			if ((Object)(object)component2 != (Object)null)
			{
				((Behaviour)component2).enabled = false;
			}
			SummonedBotTag summonedBotTag = val.AddComponent<SummonedBotTag>();
			summonedBotTag.kind = kind;
			summonedBotTag.master = master;
			summonedBotTag.colorId = colorPlayerId;
			summonedBotTag.faceId = colorPlayerId;
			All.Add(val);
			SetPos(component, pos);
			((MonoBehaviour)TheRunner).StartCoroutine(Setup(component, count, pos, configure));
			return val;
		}

		private static void SetPos(CharacterData cd, Vector3 pos)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)cd == (Object)null))
			{
				((Component)cd).transform.position = pos;
				if ((Object)(object)cd.playerVel != (Object)null)
				{
					cd.playerVel.position = Vector2.op_Implicit(pos);
				}
			}
		}

		private static IEnumerator Setup(CharacterData cd, int uniqueId, Vector3 spawnPos, Action<GameObject> configure)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			yield return null;
			if ((Object)(object)cd == (Object)null || (Object)(object)cd.player == (Object)null)
			{
				yield break;
			}
			int guard = 0;
			while (guard++ < 60 && (Object)(object)cd != (Object)null && (Object)(object)cd.player != (Object)null && ((Object)(object)cd.weaponHandler == (Object)null || (Object)(object)cd.weaponHandler.gun == (Object)null || (Object)(object)cd.weaponHandler.gun.player == (Object)null))
			{
				yield return null;
			}
			if ((Object)(object)cd == (Object)null || (Object)(object)cd.player == (Object)null)
			{
				yield break;
			}
			cd.isPlaying = true;
			cd.healthHandler.DestroyOnDeath = false;
			try
			{
				cd.healthHandler.Revive(true);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Summons: revive failed — " + ex));
			}
			cd.player.playerID = uniqueId;
			try
			{
				configure?.Invoke(((Component)cd).gameObject);
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogError((object)("Summons: configure failed — " + ex2));
			}
			SetPos(cd, spawnPos);
			SummonedBotTag component = ((Component)cd).GetComponent<SummonedBotTag>();
			if ((Object)(object)component != (Object)null && !component.survivesMasterDeath && (Object)(object)component.master != (Object)null && (Object)(object)component.master.data != (Object)null && component.master.data.dead && !cd.dead && (Object)(object)cd.healthHandler != (Object)null)
			{
				cd.healthHandler.DoDamage(new Vector2(0f, -1f) * 100000f, Vector2.op_Implicit(((Component)cd).transform.position), Color.white, (GameObject)null, (Player)null, false, true, true);
				yield break;
			}
			for (int i = 0; i < 10; i++)
			{
				if (!((Object)(object)cd != (Object)null))
				{
					break;
				}
				if (cd.dead)
				{
					break;
				}
				cd.isPlaying = true;
				if ((Object)(object)cd.input != (Object)null)
				{
					cd.input.controlledElseWhere = true;
				}
				yield return null;
			}
		}

		public static void ApplySize(CharacterData cd, float sizeMult)
		{
			if ((Object)(object)cd == (Object)null || (Object)(object)cd.stats == (Object)null)
			{
				return;
			}
			cd.stats.sizeMultiplier = sizeMult;
			try
			{
				ConfigureMassAndSize?.Invoke(cd.stats, null);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Summons: size failed — " + ex));
			}
		}

		public static int GetColorId(Player p)
		{
			if ((Object)(object)p == (Object)null)
			{
				return 0;
			}
			if (!rwfChecked)
			{
				rwfChecked = true;
				rwfPlayerExt = Type.GetType("RWF.PlayerExtensions, RoundsWithFriends");
			}
			if (rwfPlayerExt != null)
			{
				try
				{
					object obj = rwfPlayerExt.GetMethod("GetAdditionalData")?.Invoke(null, new object[1] { p });
					object obj2 = obj?.GetType().GetField("character")?.GetValue(obj);
					object obj3 = obj2?.GetType().GetField("colorID")?.GetValue(obj2);
					if (obj3 is int)
					{
						return (int)obj3;
					}
				}
				catch
				{
				}
			}
			return p.playerID;
		}

		public static void ClearFace(GameObject bot)
		{
			if ((Object)(object)bot == (Object)null)
			{
				return;
			}
			CharacterCreatorItemEquipper componentInChildren = bot.GetComponentInChildren<CharacterCreatorItemEquipper>();
			if (!((Object)(object)componentInChildren == (Object)null) && !((Object)(object)componentInChildren.itemParent == (Object)null))
			{
				Transform transform = componentInChildren.itemParent.transform;
				for (int num = transform.childCount - 1; num >= 0; num--)
				{
					Object.Destroy((Object)(object)((Component)transform.GetChild(num)).gameObject);
				}
			}
		}

		public static void ApplyFace(GameObject bot, int faceId)
		{
			CharacterCreatorHandler instance = CharacterCreatorHandler.instance;
			if ((Object)(object)bot == (Object)null || (Object)(object)instance == (Object)null || faceId < 0 || faceId >= instance.selectedPlayerFaces.Length)
			{
				return;
			}
			PlayerFace val = instance.selectedPlayerFaces[faceId];
			if (val != null)
			{
				CharacterCreatorItemEquipper componentInChildren = bot.GetComponentInChildren<CharacterCreatorItemEquipper>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.EquipFace(val);
				}
			}
		}

		public static SummonAI AddAI(GameObject bot, bool anchored, bool canShoot, bool canBlock, Vector3 spawnPos, bool canFly = false)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)bot == (Object)null)
			{
				return null;
			}
			SummonAI summonAI = bot.AddComponent<SummonAI>();
			summonAI.isAnchored = anchored;
			summonAI.canShoot = canShoot;
			summonAI.canBlock = canBlock;
			summonAI.canFly = canFly;
			summonAI.spawnPos = spawnPos;
			return summonAI;
		}

		public static Vector3 EnemyOffset(Player owner, float dist)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)owner == (Object)null || (Object)(object)PlayerManager.instance == (Object)null)
			{
				return Vector3.zero;
			}
			Player otherPlayer = PlayerManager.instance.GetOtherPlayer(owner);
			if ((Object)(object)otherPlayer == (Object)null)
			{
				return Vector3.zero;
			}
			return new Vector3((((Component)otherPlayer).transform.position.x - ((Component)owner).transform.position.x >= 0f) ? dist : (0f - dist), 0f, 0f);
		}

		public static Vector3 FacingOffset(Player owner, float dist)
		{
			//IL_0017: 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)
			if ((Object)(object)owner == (Object)null || (Object)(object)owner.data == (Object)null)
			{
				return Vector3.zero;
			}
			return new Vector3(((((Object)(object)owner.data.input != (Object)null) ? owner.data.input.aimDirection.x : 0f) < 0f) ? (0f - dist) : dist, 0f, 0f);
		}

		public static bool HasLiveSummon(Player master, BotKind kind)
		{
			return CountLiveSummons(master, kind) > 0;
		}

		public static int CountLiveSummons(Player master, BotKind kind)
		{
			int num = 0;
			foreach (GameObject item in All)
			{
				if (!((Object)(object)item == (Object)null))
				{
					SummonedBotTag component = item.GetComponent<SummonedBotTag>();
					CharacterData component2 = item.GetComponent<CharacterData>();
					if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null && (Object)(object)component.master == (Object)(object)master && component.kind == kind && !component2.dead)
					{
						num++;
					}
				}
			}
			return num;
		}

		public static GameObject GetLiveSummon(Player master, BotKind kind)
		{
			foreach (GameObject item in All)
			{
				if (!((Object)(object)item == (Object)null))
				{
					SummonedBotTag component = item.GetComponent<SummonedBotTag>();
					CharacterData component2 = item.GetComponent<CharacterData>();
					if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null && (Object)(object)component.master == (Object)(object)master && component.kind == kind && !component2.dead)
					{
						return item;
					}
				}
			}
			return null;
		}

		public static void KillByMaster(Player master)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			GameObject[] array = All.ToArray();
			foreach (GameObject val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					SummonedBotTag component = val.GetComponent<SummonedBotTag>();
					CharacterData component2 = val.GetComponent<CharacterData>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component2 == (Object)null) && !component.survivesMasterDeath && (Object)(object)component.master == (Object)(object)master && !component2.dead)
					{
						component2.healthHandler.DoDamage(new Vector2(0f, -1f) * 100000f, Vector2.op_Implicit(((Component)component2).transform.position), Color.white, (GameObject)null, (Player)null, false, true, true);
					}
				}
			}
		}

		public static void KillChildrenOf(GameObject parent)
		{
			//IL_0073: 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)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)parent == (Object)null)
			{
				return;
			}
			GameObject[] array = All.ToArray();
			foreach (GameObject val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					SummonedBotTag component = val.GetComponent<SummonedBotTag>();
					CharacterData component2 = val.GetComponent<CharacterData>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component2 == (Object)null) && (Object)(object)component.parentSummon == (Object)(object)parent && !component2.dead)
					{
						component2.healthHandler.DoDamage(new Vector2(0f, -1f) * 100000f, Vector2.op_Implicit(((Component)component2).transform.position), Color.white, (GameObject)null, (Player)null, false, true, true);
					}
				}
			}
		}

		public static void KillAfter(GameObject bot, float seconds)
		{
			if (!((Object)(object)bot == (Object)null))
			{
				((MonoBehaviour)TheRunner).StartCoroutine(KillAfterRoutine(bot, seconds));
			}
		}

		private static IEnumerator KillAfterRoutine(GameObject bot, float seconds)
		{
			yield return (object)new WaitForSeconds(seconds);
			if (!((Object)(object)bot == (Object)null))
			{
				CharacterData component = bot.GetComponent<CharacterData>();
				if ((Object)(object)component != (Object)null && !component.dead && (Object)(object)component.healthHandler != (Object)null)
				{
					component.healthHandler.DoDamage(new Vector2(0f, -1f) * 100000f, Vector2.op_Implicit(((Component)component).transform.position), Color.white, (GameObject)null, (Player)null, false, true, true);
				}
			}
		}

		public static void OnPointEnd()
		{
			int count = All.Count;
			foreach (GameObject item in All)
			{
				DestroySummon(item);
			}
			All.Clear();
			int num = PurgeStragglers();
			Plugin.Log.LogInfo((object)$"Summons: cleanup destroyed {count} tracked, purged {num} straggler(s).");
		}

		private static void DestroySummon(GameObject o)
		{
			if ((Object)(object)o == (Object)null)
			{
				return;
			}
			try
			{
				CharacterData component = o.GetComponent<CharacterData>();
				if ((Object)(object)component != (Object)null && (Object)(object)component.player != (Object)null && (Object)(object)PlayerManager.instance != (Object)null)
				{
					PlayerManager.instance.players.Remove(component.player);
				}
				PhotonNetwork.Destroy(o);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Summons destroy: " + ex));
			}
			try
			{
				if ((Object)(object)o != (Object)null)
				{
					Object.Destroy((Object)(object)o);
				}
			}
			catch
			{
			}
		}

		public static int PurgeStragglers()
		{
			if ((Object)(object)PlayerManager.instance == (Object)null)
			{
				return 0;
			}
			int num = 0;
			List<Player> players = PlayerManager.instance.players;
			for (int num2 = players.Count - 1; num2 >= 0; num2--)
			{
				Player val = players[num2];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponent<SummonedBotTag>() == (Object)null))
				{
					players.RemoveAt(num2);
					All.Remove(((Component)val).gameObject);
					DestroySummon(((Component)val).gameObject);
					num++;
				}
			}
			return num;
		}
	}
	public class TurretCard : SummonerCard
	{
		protected override CardDef Def => CardText.Sentry;

		public override void OnAddCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats)
		{
			TurretSummoner turretSummoner = ((Component)player).gameObject.GetComponent<TurretSummoner>();
			if ((Object)(object)turretSummoner == (Object)null)
			{
				turretSummoner = ((Component)player).gameObject.AddComponent<TurretSummoner>();
			}
			turretSummoner.cardCount++;
		}

		public override void OnRemoveCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats)
		{
			TurretSummoner component = ((Component)player).gameObject.GetComponent<TurretSummoner>();
			if ((Object)(object)component != (Object)null)
			{
				component.cardCount--;
				if (component.cardCount <= 0)
				{
					Object.Destroy((Object)(object)component);
				}
			}
		}
	}
	public class TurretSummoner : MonoBehaviour
	{
		public int cardCount;

		public int bonusTurrets;

		public int fullDamageCount;

		public int healthBonusCount;

		public int blockCount;

		private const float TurretDamageMult = 0.5f;

		private Player owner;

		private Block block;

		public int MaxTurrets => 1 + bonusTurrets;

		public bool FullDamage => fullDamageCount > 0;

		public bool CanBlock => blockCount > 0;

		private void Start()
		{
			owner = ((Component)this).GetComponent<Player>();
			block = ((Component)this).GetComponent<Block>();
			if ((Object)(object)block != (Object)null)
			{
				Block obj = block;
				obj.BlockAction = (Action<BlockTriggerType>)Delegate.Combine(obj.BlockAction, new Action<BlockTriggerType>(OnBlock));
			}
			Summons.EnsureHook();
		}

		private void OnDestroy()
		{
			if ((Object)(object)block != (Object)null)
			{
				Block obj = block;
				obj.BlockAction = (Action<BlockTriggerType>)Delegate.Remove(obj.BlockAction, new Action<BlockTriggerType>(OnBlock));
			}
		}

		private void OnBlock(BlockTriggerType triggerType)
		{
			if (!PhotonNetwork.OfflineMode || (Object)(object)owner == (Object)null || (Object)(object)owner.data == (Object)null || (Object)(object)PlayerAssigner.instance == (Object)null || (Object)(object)PlayerAssigner.instance.playerPrefab == (Object)null || Summons.CountLiveSummons(owner, BotKind.Turret) >= MaxTurrets)
			{
				return;
			}
			try
			{
				SpawnOne();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Turret: spawn failed — " + ex));
			}
		}

		private void SpawnOne()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			Vector3 pos = ((Component)owner).transform.position + Summons.FacingOffset(owner, 1.5f);
			Gun myGun = owner.data.weaponHandler.gun;
			float turretHealth = owner.data.maxHealth * (1f + (float)healthBonusCount);
			float turretDamage = myGun.damage * (FullDamage ? 1f : 0.5f);
			bool canBlock = CanBlock;
			Summons.SpawnBot(owner, pos, Summons.GetColorId(owner), BotKind.Turret, delegate(GameObject obj)
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				CharacterData component = obj.GetComponent<CharacterData>();
				Summons.AddAI(obj, anchored: true, canShoot: true, canBlock, pos);
				Summons.ClearFace(obj);
				component.maxHealth = turretHealth;
				component.health = component.maxHealth;
				NecroManager.CopyGun(myGun, component.weaponHandler.gun);
				component.weaponHandler.gun.damage = turretDamage;
			});
			Plugin.Log.LogInfo((object)$"Turret: player {owner.playerID} deployed a turret.");
		}
	}
	public abstract class UpgradeCard : SummonerCard
	{
		public abstract BotKind Family { get; }

		public virtual bool AllowMultiple => false;

		protected abstract void Apply(Player player);

		protected abstract void Remove(Player player);

		public override void OnAddCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats)
		{
			try
			{
				Apply(player);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"{((CustomCard)this).GetTitle()}: apply failed — {arg}");
			}
		}

		public override void OnRemoveCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats)
		{
			try
			{
				Remove(player);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"{((CustomCard)this).GetTitle()}: remove failed — {arg}");
			}
		}
	}
	public static class UpgradeGate
	{
		private static readonly Dictionary<string, BotKind> Requirements = new Dictionary<string, BotKind>();

		public static void Register(string cardName, BotKind family)
		{
			if (!string.IsNullOrEmpty(cardName))
			{
				Requirements[cardName] = family;
			}
		}

		public static bool IsAllowed(CardInfo info, Player player)
		{
			if ((Object)(object)info == (Object)null || (Object)(object)player == (Object)null)
			{
				return true;
			}
			string cardName = info.cardName;
			if (string.IsNullOrEmpty(cardName) || !Requirements.TryGetValue(cardName, out var value))
			{
				return true;
			}
			return value switch
			{
				BotKind.Clone => (Object)(object)((Component)player).GetComponent<CloneSummoner>() != (Object)null, 
				BotKind.Reanimated => (Object)(object)((Component)player).GetComponent<Reanimator>() != (Object)null, 
				BotKind.Turret => (Object)(object)((Component)player).GetComponent<TurretSummoner>() != (Object)null, 
				BotKind.Hive => (Object)(object)((Component)player).GetComponent<HiveSummoner>() != (Object)null, 
				BotKind.Revenant => (Object)(object)((Component)player).GetComponent<RevenantSummoner>() != (Object)null, 
				_ => true, 
			};
		}
	}
}