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(); 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 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(); 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 Active = new List(); 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(); block = ((Component)this).GetComponent(); if ((Object)(object)block != (Object)null) { Block obj = block; obj.BlockAction = (Action)Delegate.Combine(obj.BlockAction, new Action(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)Delegate.Remove(obj.BlockAction, new Action(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(); 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().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(), 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(); 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(); if ((Object)(object)cloneSummoner == (Object)null) { cloneSummoner = ((Component)player).gameObject.AddComponent(); } 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(); 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(); if ((Object)(object)component != (Object)null) { component.shareCount++; } } protected override void Remove(Player player) { CloneSummoner component = ((Component)player).GetComponent(); 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(); if ((Object)(object)component != (Object)null) { component.cloneFullDamageCount++; } } protected override void Remove(Player player) { CloneSummoner component = ((Component)player).GetComponent(); 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(); if ((Object)(object)component != (Object)null) { component.swapCount++; } } protected override void Remove(Player player) { CloneSummoner component = ((Component)player).GetComponent(); 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(); if ((Object)(object)component != (Object)null) { component.bonusClones++; } } protected override void Remove(Player player) { CloneSummoner component = ((Component)player).GetComponent(); 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(); if ((Object)(object)component != (Object)null) { component.bonusTurrets++; } } protected override void Remove(Player player) { TurretSummoner component = ((Component)player).GetComponent(); 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() == (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(); 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 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(); if ((Object)(object)hiveSummoner == (Object)null) { hiveSummoner = ((Component)player).gameObject.AddComponent(); } 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(); 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(); block = ((Component)this).GetComponent(); if ((Object)(object)block != (Object)null) { Block obj = block; obj.BlockAction = (Action)Delegate.Combine(obj.BlockAction, new Action(OnBlock)); } Summons.EnsureHook(); } private void OnDestroy() { if ((Object)(object)block != (Object)null) { Block obj = block; obj.BlockAction = (Action)Delegate.Remove(obj.BlockAction, new Action(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(); 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(); Summons.AddAI(bee, anchored: false, canShoot: true, canBlock: false, beePos, canFly: true); Summons.ClearFace(bee); SummonedBotTag component2 = bee.GetComponent(); 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(); if ((Object)(object)component != (Object)null) { component.bonusBees += 2; } } protected override void Remove(Player player) { HiveSummoner component = ((Component)player).GetComponent(); 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(); if ((Object)(object)component != (Object)null) { component.sizeUpgradeCount++; } } protected override void Remove(Player player) { HiveSummoner component = ((Component)player).GetComponent(); 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 Corpses = new List(); 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(); 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(); 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(); 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 TakeCorpsesFor(int killerId) { List list = new List(); 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(); if ((Object)(object)reanimator == (Object)null) { reanimator = ((Component)player).gameObject.AddComponent(); } 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(); 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(); if ((Object)(object)component != (Object)null) { component.empowerCount++; } } protected override void Remove(Player player) { Reanimator component = ((Component)player).GetComponent(); 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(); if ((Object)(object)component != (Object)null) { component.fullDamageCount++; } } protected override void Remove(Player player) { Reanimator component = ((Component)player).GetComponent(); 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(); if ((Object)(object)component != (Object)null) { component.extraRaiseCount++; } } protected override void Remove(Player player) { Reanimator component = ((Component)player).GetComponent(); 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(); BuildBase(); BuildBase(); BuildBase(); BuildBase(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); BuildUpgrade(); GameModeManager.AddHook("BattleStart", (Func)((IGameModeHandler gm) => BattleStart())); GameModeManager.AddHook("PointEnd", (Func)((IGameModeHandler gm) => Cleanup())); GameModeManager.AddHook("RoundEnd", (Func)((IGameModeHandler gm) => Cleanup())); GameModeManager.AddHook("GameEnd", (Func)((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() where T : CustomCard { CustomCard.BuildCard((Action)delegate(CardInfo cardInfo) { cardInfo.allowMultiple = false; }); } private static void BuildUpgrade() where T : UpgradeCard { CustomCard.BuildCard((Action)delegate(CardInfo cardInfo) { T component = ((Component)cardInfo).GetComponent(); 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(); block = ((Component)this).GetComponent(); if ((Object)(object)block != (Object)null) { Block obj = block; obj.BlockAction = (Action)Delegate.Combine(obj.BlockAction, new Action(OnBlock)); } Summons.EnsureHook(); } private void OnDestroy() { if ((Object)(object)block != (Object)null) { Block obj = block; obj.BlockAction = (Action)Delegate.Remove(obj.BlockAction, new Action(OnBlock)); } } private void OnBlock(BlockTriggerType triggerType) { if (!PhotonNetwork.OfflineMode || (Object)(object)owner == (Object)null) { return; } List 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(); Summons.AddAI(botObj, anchored: false, canShoot: true, canBlock: false, pos); Summons.ClearFace(botObj); SummonedBotTag component2 = botObj.GetComponent(); 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(); 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(); if ((Object)(object)revenantSummoner == (Object)null) { revenantSummoner = ((Component)player).gameObject.AddComponent(); } 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(); 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(); if ((Object)(object)component != (Object)null) { component.extraDurationCount++; } } protected override void Remove(Player player) { RevenantSummoner component = ((Component)player).GetComponent(); 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(); if ((Object)(object)component != (Object)null) { component.respawnOnKillCount++; } } protected override void Remove(Player player) { RevenantSummoner component = ((Component)player).GetComponent(); 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(); Summons.EnsureHook(); if ((Object)(object)PlayerManager.instance != (Object)null) { PlayerManager.instance.AddPlayerDiedAction((Action)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() : 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(); 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(); 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(); 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(); if ((Object)(object)component != (Object)null) { component.blockCount++; } } protected override void Remove(Player player) { TurretSummoner component = ((Component)player).GetComponent(); 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(); if ((Object)(object)component != (Object)null) { component.healthBonusCount++; } } protected override void Remove(Player player) { TurretSummoner component = ((Component)player).GetComponent(); 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(); if ((Object)(object)component != (Object)null) { component.fullDamageCount++; } } protected override void Remove(Player player) { TurretSummoner component = ((Component)player).GetComponent(); 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(); if (!((Object)(object)data == (Object)null)) { input = data.input; block = ((Component)data).GetComponent(); if (isAnchored) { ((MonoBehaviour)this).StartCoroutine(AnchorAfterSettle()); return; } ((Component)this).gameObject.AddComponent(); 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 All = new List(); 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(); } return runner; } } public static void EnsureHook() { if (!hooked && !((Object)(object)PlayerManager.instance == (Object)null)) { PlayerManager.instance.AddPlayerDiedAction((Action)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 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(); 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(); if ((Object)(object)component2 != (Object)null) { ((Behaviour)component2).enabled = false; } SummonedBotTag summonedBotTag = val.AddComponent(); 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 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(); 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(); 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(); 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.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(); CharacterData component2 = item.GetComponent(); 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(); CharacterData component2 = item.GetComponent(); 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(); CharacterData component2 = val.GetComponent(); 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(); CharacterData component2 = val.GetComponent(); 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(); 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(); 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 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() == (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(); if ((Object)(object)turretSummoner == (Object)null) { turretSummoner = ((Component)player).gameObject.AddComponent(); } 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(); 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(); block = ((Component)this).GetComponent(); if ((Object)(object)block != (Object)null) { Block obj = block; obj.BlockAction = (Action)Delegate.Combine(obj.BlockAction, new Action(OnBlock)); } Summons.EnsureHook(); } private void OnDestroy() { if ((Object)(object)block != (Object)null) { Block obj = block; obj.BlockAction = (Action)Delegate.Remove(obj.BlockAction, new Action(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(); 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 Requirements = new Dictionary(); 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() != (Object)null, BotKind.Reanimated => (Object)(object)((Component)player).GetComponent() != (Object)null, BotKind.Turret => (Object)(object)((Component)player).GetComponent() != (Object)null, BotKind.Hive => (Object)(object)((Component)player).GetComponent() != (Object)null, BotKind.Revenant => (Object)(object)((Component)player).GetComponent() != (Object)null, _ => true, }; } } }