using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using HarmonyLib; using InfoOverhaul.Delta; using Microsoft.CodeAnalysis; using Photon.Pun; using ShieldsMod.Cards; using ShieldsMod.Networking; using ShieldsMod.Poison; using ShieldsMod.Shield; using UnboundLib; using UnboundLib.Cards; using UnboundLib.GameModes; using UnboundLib.Networking; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.1", FrameworkDisplayName = ".NET Framework 4.7.1")] [assembly: AssemblyCompany("ShieldsMod")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+661a91535814621160b5d94d61679bade4fe1c2d")] [assembly: AssemblyProduct("Shields Mod for ROUNDS")] [assembly: AssemblyTitle("ShieldsMod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 ShieldsMod { [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("ShieldsMod", "Shields Mod for ROUNDS", "1.0.0")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Logger; private void Awake() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) Logger = ((BaseUnityPlugin)this).Logger; SLog.Section("Plugin Awake"); new Harmony("ShieldsMod").PatchAll(); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); ShieldCardRegistrar.RegisterAll(); PoisonCardRegistrar.RegisterAll(); SLog.Line("Plugin ShieldsMod loaded."); } } internal static class SLog { private const string Sep = "====================="; public static void Section(string title) { Plugin.Logger.LogInfo((object)"====================="); Plugin.Logger.LogInfo((object)("[ShieldsMod] " + title)); Plugin.Logger.LogInfo((object)"====================="); } public static void Line(string message) { Plugin.Logger.LogInfo((object)("[ShieldsMod] " + message)); } public static void Warn(string message) { Plugin.Logger.LogWarning((object)("[ShieldsMod] " + message)); } public static void Error(string message) { Plugin.Logger.LogError((object)("[ShieldsMod] " + message)); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "ShieldsMod"; public const string PLUGIN_NAME = "Shields Mod for ROUNDS"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace ShieldsMod.Shield { internal static class ShieldHandRebuild { private static Dictionary _multiplierByCardName; internal static void RegisterTier(CardInfo card, float multiplier) { if (!((Object)(object)card == (Object)null) && !string.IsNullOrEmpty(card.cardName)) { if (_multiplierByCardName == null) { _multiplierByCardName = new Dictionary(StringComparer.Ordinal); } _multiplierByCardName[card.cardName] = multiplier; } } internal static void RebuildForPlayer(int playerID) { if ((Object)(object)ShieldManager.instance == (Object)null || (Object)(object)PlayerManager.instance == (Object)null) { return; } Player val = PlayerManager.instance.players.Find((Player p) => p.playerID == playerID); if (val?.data?.currentCards == null) { return; } float num = 100f; int num2 = 0; if (_multiplierByCardName != null) { foreach (CardInfo currentCard in val.data.currentCards) { if (!((Object)(object)currentCard == (Object)null)) { string cardName = currentCard.cardName; if (string.IsNullOrEmpty(cardName) && (Object)(object)currentCard.sourceCard != (Object)null) { cardName = currentCard.sourceCard.cardName; } if (!string.IsNullOrEmpty(cardName) && _multiplierByCardName.TryGetValue(cardName, out var value)) { num *= value; num2++; } } } } ShieldState orCreateShield = ShieldManager.instance.GetOrCreateShield(playerID); float max = orCreateShield.Max; orCreateShield.Max = num; orCreateShield.Current = Mathf.Min(orCreateShield.Current, orCreateShield.Max); ShieldManager.instance.RefreshVisual(playerID); SLog.Line($"ShieldHandRebuild player={playerID} tiers={num2} max {max:F0} -> {orCreateShield.Max:F0} current={orCreateShield.Current:F0}"); } } public class ShieldManager : MonoBehaviour { [CompilerGenerated] private sealed class d__8 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__8(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { if (<>1__state != 0) { return false; } <>1__state = -1; instance?.ResetAllForBattle(); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__10 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__10(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { if (<>1__state != 0) { return false; } <>1__state = -1; instance?.CleanupOrphanedAttachments(); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__9 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__9(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { if (<>1__state != 0) { return false; } <>1__state = -1; instance?.CleanupOrphanedAttachments(); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public static ShieldManager instance; private readonly Dictionary _shields = new Dictionary(); private readonly Dictionary _visuals = new Dictionary(); private readonly Dictionary _parryTriggers = new Dictionary(); private readonly Dictionary _poisonDrainBlockedUntil = new Dictionary(); private const float PoisonDrainBlockDuration = 4f; private void Awake() { instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); SLog.Section("ShieldManager — Awake"); GameModeManager.AddHook("BattleStart", (Func)OnBattleStart); GameModeManager.AddHook("PointEnd", (Func)OnPointEnd); GameModeManager.AddHook("GameStart", (Func)OnGameStart); } private void OnDestroy() { if ((Object)(object)instance == (Object)(object)this) { instance = null; } } [IteratorStateMachine(typeof(d__8))] private static IEnumerator OnBattleStart(IGameModeHandler gm) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__8(0); } [IteratorStateMachine(typeof(d__9))] private static IEnumerator OnPointEnd(IGameModeHandler gm) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__9(0); } [IteratorStateMachine(typeof(d__10))] private static IEnumerator OnGameStart(IGameModeHandler gm) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__10(0); } public void ResetAllForBattle() { _poisonDrainBlockedUntil.Clear(); SLog.Section("ShieldManager — ResetAllForBattle"); if ((Object)(object)PlayerManager.instance == (Object)null) { SLog.Warn("PlayerManager.instance is null."); return; } foreach (Player player in PlayerManager.instance.players) { if (!((Object)(object)player == (Object)null)) { ShieldState orCreateShield = GetOrCreateShield(player.playerID); orCreateShield.ResetToFull(); EnsureAttachments(player, orCreateShield); RefreshVisual(player.playerID); SLog.Line($"player={player.playerID} shield={orCreateShield.Current}/{orCreateShield.Max}"); } } } public ShieldState GetOrCreateShield(int playerID) { if (!_shields.TryGetValue(playerID, out var value)) { value = new ShieldState(); _shields[playerID] = value; } return value; } public ShieldState GetShield(int playerID) { return GetOrCreateShield(playerID); } public bool TryAbsorbFull(int playerID, float damageAmount, out bool depleted) { depleted = false; ShieldState orCreateShield = GetOrCreateShield(playerID); if (!orCreateShield.IsActive || damageAmount <= 0f) { return false; } if (damageAmount >= orCreateShield.Current) { orCreateShield.Current = 0f; depleted = true; } else { orCreateShield.Current -= damageAmount; } RefreshVisual(playerID); return true; } public void MarkPoisonDepleted(int playerID) { _poisonDrainBlockedUntil[playerID] = Time.time + 4f; } public bool IsPoisonDrainBlocked(int playerID) { if (_poisonDrainBlockedUntil.TryGetValue(playerID, out var value)) { return Time.time < value; } return false; } public void RefreshVisual(int playerID) { if (_visuals.TryGetValue(playerID, out var value) && (Object)(object)value != (Object)null) { value.Refresh(); } if (_parryTriggers.TryGetValue(playerID, out var value2) && (Object)(object)value2 != (Object)null) { value2.UpdateEnabled(); } } private void EnsureAttachments(Player player, ShieldState state) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) int playerID = player.playerID; if (!_visuals.TryGetValue(playerID, out var value) || (Object)(object)value == (Object)null) { GameObject val = new GameObject($"ShieldVisual_P{playerID}"); val.transform.SetParent(((Component)player).transform, false); value = val.AddComponent(); value.Init(player, state); _visuals[playerID] = value; } else { value.SetState(state); value.Refresh(); } if (!_parryTriggers.TryGetValue(playerID, out var value2) || (Object)(object)value2 == (Object)null) { GameObject val2 = new GameObject($"ShieldParry_P{playerID}"); val2.transform.SetParent(((Component)player).transform, false); value2 = val2.AddComponent(); float radius = ShieldVisual.GetBodyRadius(player) * 1.44f; value2.Init(player, state, radius); _parryTriggers[playerID] = value2; } else { value2.SetState(state); value2.UpdateEnabled(); } } private void CleanupOrphanedAttachments() { List list = new List(); foreach (KeyValuePair visual in _visuals) { if ((Object)(object)visual.Value == (Object)null) { list.Add(visual.Key); } } foreach (int item in list) { _visuals.Remove(item); } List list2 = new List(); foreach (KeyValuePair parryTrigger in _parryTriggers) { if ((Object)(object)parryTrigger.Value == (Object)null) { list2.Add(parryTrigger.Key); } } foreach (int item2 in list2) { _parryTriggers.Remove(item2); } } } public class ShieldParryTrigger : MonoBehaviour { private Player _owner; private Block _block; private CircleCollider2D _trigger; private ShieldState _state; private readonly HashSet _recentlyDeflected = new HashSet(); public void Init(Player owner, ShieldState state, float radius) { _owner = owner; _state = state; _block = ((Component)owner).GetComponent(); _trigger = ((Component)this).gameObject.AddComponent(); ((Collider2D)_trigger).isTrigger = true; _trigger.radius = radius; Rigidbody2D obj = ((Component)this).gameObject.AddComponent(); obj.bodyType = (RigidbodyType2D)1; obj.simulated = true; UpdateEnabled(); } public void SetState(ShieldState state) { _state = state; } public void SetRadius(float worldRadius) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_owner == (Object)null) && !((Object)(object)_trigger == (Object)null)) { float num = Mathf.Max(((Component)_owner).transform.localScale.x, 0.01f); _trigger.radius = worldRadius / num; } } public void UpdateEnabled() { if ((Object)(object)_trigger != (Object)null) { ((Behaviour)_trigger).enabled = _state != null && _state.IsActive; } } private void LateUpdate() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_owner == (Object)null)) { ((Component)this).transform.position = ((Component)_owner).transform.position; float bodyRadius = ShieldVisual.GetBodyRadius(_owner); SetRadius(bodyRadius * 1.44f); UpdateEnabled(); } } private void OnTriggerEnter2D(Collider2D other) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_owner == (Object)null || (Object)(object)_block == (Object)null || _state == null || !_state.IsActive || !_block.IsBlocking()) { return; } ProjectileHit val = ((Component)other).GetComponent() ?? ((Component)other).GetComponentInParent(); if ((Object)(object)val == (Object)null) { return; } PhotonView component = ((Component)val).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsMine) { return; } int instanceID = ((Object)((Component)val).gameObject).GetInstanceID(); if (!_recentlyDeflected.Contains(instanceID) && IsEnemyProjectile(val)) { Vector3 position = ((Component)val).transform.position; Vector3 val2 = ((Component)val).transform.forward; if (((Vector3)(ref val2)).sqrMagnitude < 0.001f) { Vector3 val3 = position - ((Component)_owner).transform.position; val2 = ((Vector3)(ref val3)).normalized; } _recentlyDeflected.Add(instanceID); _block.DoBlock(((Component)val).gameObject, val2, position); SLog.Line($"Outer parry player={_owner.playerID} projectile={((Object)((Component)val).gameObject).name}"); } } private bool IsEnemyProjectile(ProjectileHit projHit) { Player val = projHit.ownPlayer; if ((Object)(object)val == (Object)null) { SpawnedAttack component = ((Component)projHit).GetComponent(); if ((Object)(object)component != (Object)null) { val = component.spawner; } } if ((Object)(object)val == (Object)null) { return true; } if (val.playerID != _owner.playerID) { return (Object)(object)((Component)val).transform.root != (Object)(object)((Component)_owner).transform.root; } return false; } private void OnTriggerExit2D(Collider2D other) { ProjectileHit val = ((Component)other).GetComponent() ?? ((Component)other).GetComponentInParent(); if ((Object)(object)val != (Object)null) { _recentlyDeflected.Remove(((Object)((Component)val).gameObject).GetInstanceID()); } } } public class ShieldState { public const float DefaultMax = 100f; public float Max = 100f; public float Current = 100f; public const float BubbleAlpha = 0.05f; public float Percent { get { if (!(Max > 0f)) { return 0f; } return Current / Max; } } public bool IsActive => Current > 0f; public void ResetToFull() { Current = Max; } public Color GetBubbleColor(float alpha = 0.05f) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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) float percent = Percent; Color val = default(Color); if (percent > 0.65f) { ((Color)(ref val))..ctor(0.2f, 0.9f, 0.25f); } else if (percent > 0.35f) { ((Color)(ref val))..ctor(0.95f, 0.85f, 0.15f); } else { ((Color)(ref val))..ctor(0.95f, 0.2f, 0.15f); } return new Color(val.r, val.g, val.b, alpha); } } public class ShieldVisual : MonoBehaviour { public const float SizeMultiplier = 1.44f; private const int CircleTexSize = 128; private Player _player; private SpriteRenderer _sprite; private ShieldState _state; public static Sprite SharedCircleSprite { get; private set; } public void Init(Player player, ShieldState state) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown _player = player; _state = state; if ((Object)(object)SharedCircleSprite == (Object)null) { SharedCircleSprite = CreateCircleSprite(128); } GameObject val = new GameObject("ShieldBubbleSprite"); val.transform.SetParent(((Component)this).transform, false); _sprite = val.AddComponent(); _sprite.sprite = SharedCircleSprite; ((Renderer)_sprite).sortingOrder = -1; int num = SortingLayer.NameToID("Player" + (player.playerID + 1)); if (num != 0) { ((Renderer)_sprite).sortingLayerID = num; } player.data.SetWobbleObjectChild(((Component)this).transform); Refresh(); } public void SetState(ShieldState state) { _state = state; } public void Refresh() { //IL_005c: 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_0091: 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_00b7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_sprite == (Object)null || (Object)(object)_player == (Object)null || _state == null) { return; } if (!_state.IsActive) { ((Renderer)_sprite).enabled = false; return; } ((Renderer)_sprite).enabled = true; _sprite.color = _state.GetBubbleColor(); float num = GetBodyRadius(_player) * 2f * 1.44f; Bounds bounds = _sprite.sprite.bounds; float x = ((Bounds)(ref bounds)).size.x; if (x > 0.0001f) { ((Component)_sprite).transform.localScale = Vector3.one * (num / x); } } public static float GetBodyRadius(Player player) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return 0.5f; } CircleCollider2D val = ((Component)player).GetComponent(); if ((Object)(object)val == (Object)null) { val = ((Component)player).GetComponentInChildren(); } return (((Object)(object)val != (Object)null) ? val.radius : 0.5f) * ((Component)player).transform.localScale.x; } private void LateUpdate() { if (_state != null && _state.IsActive) { Refresh(); } } private static Sprite CreateCircleSprite(int size) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_00ab: 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_0079: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(size, size, (TextureFormat)4, false); ((Texture)val).filterMode = (FilterMode)1; float num = (float)(size - 1) * 0.5f; float num2 = num - 1f; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { float num3 = (float)j - num; float num4 = (float)i - num; float num5 = Mathf.Sqrt(num3 * num3 + num4 * num4); float num6 = ((num5 <= num2) ? 1f : Mathf.Clamp01(1f - (num5 - num2))); val.SetPixel(j, i, new Color(1f, 1f, 1f, num6)); } } val.Apply(); return Sprite.Create(val, new Rect(0f, 0f, (float)size, (float)size), new Vector2(0.5f, 0.5f), (float)size); } } } namespace ShieldsMod.Poison { internal static class PoisonDamageDetector { public static bool IsPoisonDamage(Color blinkColor, GameObject damagingWeapon) { //IL_003e: 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_0051: 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) if ((Object)(object)damagingWeapon != (Object)null) { if ((Object)(object)damagingWeapon.GetComponentInChildren() != (Object)null) { return true; } Transform root = damagingWeapon.transform.root; if ((Object)(object)root != (Object)null && (Object)(object)((Component)root).GetComponentInChildren() != (Object)null) { return true; } } if (blinkColor.g > 0.35f && blinkColor.g >= blinkColor.r) { return blinkColor.g >= blinkColor.b; } return false; } } internal static class PoisonHandRebuild { private static Dictionary _reductionByCardName; internal static void RegisterTier(string cardName, float totalReduction) { if (!string.IsNullOrEmpty(cardName)) { if (_reductionByCardName == null) { _reductionByCardName = new Dictionary(StringComparer.Ordinal); } _reductionByCardName[cardName] = totalReduction; } } internal static float ComputeReductionFromHand(IEnumerable cards) { float num = 0f; if (_reductionByCardName == null || cards == null) { return num; } foreach (CardInfo card in cards) { if (!((Object)(object)card == (Object)null)) { string cardName = card.cardName; if (string.IsNullOrEmpty(cardName) && (Object)(object)card.sourceCard != (Object)null) { cardName = card.sourceCard.cardName; } if (!string.IsNullOrEmpty(cardName) && _reductionByCardName.TryGetValue(cardName, out var value)) { num = Math.Max(num, value); } } } return num; } internal static void RebuildForPlayer(int playerID) { PoisonResistanceManager.instance?.RebuildForPlayer(playerID); } } public class PoisonResistanceManager : MonoBehaviour { [CompilerGenerated] private sealed class d__3 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__3(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { if (<>1__state != 0) { return false; } <>1__state = -1; if ((Object)(object)PlayerManager.instance == (Object)null) { return false; } foreach (Player player in PlayerManager.instance.players) { if ((Object)(object)player != (Object)null) { instance?.RebuildForPlayer(player.playerID); } } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public static PoisonResistanceManager instance; private readonly Dictionary _reductionByPlayer = new Dictionary(); private void Awake() { instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); GameModeManager.AddHook("BattleStart", (Func)OnBattleStart); SLog.Section("PoisonResistanceManager — Awake"); } [IteratorStateMachine(typeof(d__3))] private static IEnumerator OnBattleStart(IGameModeHandler gm) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__3(0); } private void OnDestroy() { if ((Object)(object)instance == (Object)(object)this) { instance = null; } } public float GetReduction(int playerID) { if (!_reductionByPlayer.TryGetValue(playerID, out var value)) { return 0f; } return value; } public float GetReductionPercent(int playerID) { return GetReduction(playerID) * 100f; } public void RebuildForPlayer(int playerID) { if (!((Object)(object)PlayerManager.instance == (Object)null)) { Player val = PlayerManager.instance.players.Find((Player p) => p.playerID == playerID); if (val?.data?.currentCards != null) { float num = PoisonHandRebuild.ComputeReductionFromHand(val.data.currentCards); _reductionByPlayer[playerID] = num; SLog.Line($"PoisonResistance rebuild player={playerID} reduction={num * 100f:F0}%"); } } } public float ApplyResistance(int playerID, float rawDamage, out float resistedAmount) { float reduction = GetReduction(playerID); resistedAmount = rawDamage * reduction; return rawDamage - resistedAmount; } } } namespace ShieldsMod.Patches { [HarmonyPatch] internal static class CardDeleteRebuildPatch { private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("Rounds2Mod.CardDeleteManager"); if (!(type == null)) { return AccessTools.Method(type, "URPC_SyncDelete", (Type[])null, (Type[])null); } return null; } private static bool Prepare() { bool num = TargetMethod() != null; if (!num) { SLog.Line("CardDeleteRebuildPatch — Rounds2Mod.CardDeleteManager not found; delete hook skipped."); } return num; } private static void Postfix(int playerID) { ShieldHandRebuild.RebuildForPlayer(playerID); PoisonHandRebuild.RebuildForPlayer(playerID); } } [HarmonyPatch(typeof(HealthHandler), "DoDamage")] internal static class HealthHandlerDoDamagePatch { [HarmonyPrefix] private static bool Prefix(HealthHandler __instance, ref Vector2 damage, Vector2 position, Color blinkColor, GameObject damagingWeapon, Player damagingPlayer, bool healthRemoval, bool lethal, bool ignoreBlock) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) if (damage == Vector2.zero) { return true; } Player component = ((Component)__instance).GetComponent(); if ((Object)(object)component == (Object)null) { return true; } float magnitude = ((Vector2)(ref damage)).magnitude; float num = magnitude; bool flag = PoisonDamageDetector.IsPoisonDamage(blinkColor, damagingWeapon); if (flag && (Object)(object)PoisonResistanceManager.instance != (Object)null && PoisonResistanceManager.instance.GetReduction(component.playerID) > 0f) { num = PoisonResistanceManager.instance.ApplyResistance(component.playerID, magnitude, out var _); if (num > 0f) { damage = ((Vector2)(ref damage)).normalized * num; } else { damage = Vector2.zero; } } if (num <= 0f) { return false; } if ((Object)(object)ShieldManager.instance == (Object)null) { if (flag && magnitude > num) { LogPoisonPlayerDamage(component.playerID, magnitude, num); } return true; } if (!ShieldManager.instance.TryAbsorbFull(component.playerID, num, out var depleted)) { if (flag && ShieldManager.instance.IsPoisonDrainBlocked(component.playerID)) { SLog.Line($"[ShieldsMod] Poison tick for player {component.playerID} suppressed — shield already drained this DoT."); return false; } if (flag && magnitude > num) { LogPoisonPlayerDamage(component.playerID, magnitude, num); } return true; } if (flag && depleted) { ShieldManager.instance.MarkPoisonDepleted(component.playerID); } if (flag && magnitude > num) { LogPoisonShieldDamage(component.playerID, magnitude, num, depleted); } damage = Vector2.zero; if ((Object)(object)component.data?.view != (Object)null && component.data.view.IsMine) { ShieldNetworkSync.BroadcastShieldHealth(component.playerID, ShieldManager.instance.GetShield(component.playerID).Current); } return false; } private static void LogPoisonPlayerDamage(int playerID, float rawAmount, float actualAmount) { SLog.Section("Poison resistance — player"); SLog.Line($"Player {playerID} would've taken {rawAmount:F1} poison damage but took {actualAmount:F1} instead."); } private static void LogPoisonShieldDamage(int playerID, float rawAmount, float actualAmount, bool depleted) { SLog.Section("Poison resistance — shield"); SLog.Line($"Player {playerID}'s shield would've taken {rawAmount:F1} poison damage but took {actualAmount:F1} instead (depleted={depleted})."); } } } namespace ShieldsMod.Networking { public static class ShieldNetworkSync { public static void BroadcastShieldHealth(int playerID, float current) { SLog.Line($"BroadcastShieldHealth player={playerID} current={current:F1}"); NetworkingManager.RPC(typeof(ShieldNetworkSync), "URPC_SetShieldHealth", new object[2] { playerID, current }); } [UnboundRPC] public static void URPC_SetShieldHealth(int playerID, float current) { if (!((Object)(object)ShieldManager.instance == (Object)null)) { ShieldState orCreateShield = ShieldManager.instance.GetOrCreateShield(playerID); orCreateShield.Current = Mathf.Clamp(current, 0f, orCreateShield.Max); ShieldManager.instance.RefreshVisual(playerID); SLog.Line($"URPC_SetShieldHealth player={playerID} current={orCreateShield.Current:F1}"); } } } } namespace ShieldsMod.Cards { public static class CardArtLoader { private static readonly Dictionary Cache = new Dictionary(); public static string AssetsDirectory => Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "", "assets"); public static GameObject Load(string fileName) { //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) SLog.Section("CardArtLoader.Load — '" + fileName + "'"); SLog.Line("AssetsDirectory = " + AssetsDirectory); if (string.IsNullOrEmpty(fileName)) { SLog.Warn("Load called with empty fileName — returning null."); return null; } if (Cache.TryGetValue(fileName, out var value) && (Object)(object)value != (Object)null) { SLog.Line("Returning cached art object."); return value; } string text = Path.Combine(AssetsDirectory, fileName); SLog.Line("Full path = " + text); SLog.Line($"File.Exists = {File.Exists(text)}"); if (!File.Exists(text)) { SLog.Warn("Card art not found at: " + text); return null; } byte[] array = File.ReadAllBytes(text); SLog.Line($"Read {array.Length} bytes."); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); bool flag = ImageConversion.LoadImage(val, array); SLog.Line($"Texture.LoadImage = {flag}, size = {((Texture)val).width}x{((Texture)val).height}"); if (!flag) { SLog.Warn("Failed to decode card art: " + text); Object.Destroy((Object)(object)val); return null; } ((Texture)val).filterMode = (FilterMode)1; ((Texture)val).wrapMode = (TextureWrapMode)1; Sprite sprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f); GameObject val2 = new GameObject("ShieldsCardArt_" + fileName); Object.DontDestroyOnLoad((Object)(object)val2); RectTransform obj = val2.AddComponent(); obj.anchorMin = Vector2.zero; obj.anchorMax = Vector2.one; obj.offsetMin = Vector2.zero; obj.offsetMax = Vector2.zero; ((Transform)obj).localPosition = Vector3.up * 10000f; Image obj2 = val2.AddComponent(); obj2.sprite = sprite; obj2.preserveAspect = true; ((Graphic)obj2).raycastTarget = false; Cache[fileName] = val2; SLog.Line("Loaded card art '" + fileName + "' from " + text); return val2; } } internal static class PoisonCardDeltaRegistrar { internal static void RegisterAll() { SLog.Section("PoisonCardDeltaRegistrar — RegisterAll"); RegisterTier(PoisonCardRegistry.Lv1, 0.25f); RegisterTier(PoisonCardRegistry.Lv2, 0.5f); RegisterTier(PoisonCardRegistry.Lv3, 0.75f); } private static void RegisterTier(CardInfo card, float totalReduction) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown if (!((Object)(object)card == (Object)null)) { PoisonHandRebuild.RegisterTier(card.cardName, totalReduction); CardDeltaRegistry.RegisterSimple(card.cardName, (SimpleDeltaProvider)((Player player, CardInfo _) => ComputeDelta(player, totalReduction))); SLog.Line($"Registered poison delta preview for '{card.cardName}' ({totalReduction * 100f:F0}%)."); } } private static IReadOnlyList<(string label, string before, string after)> ComputeDelta(Player player, float tierReduction) { if ((Object)(object)player == (Object)null || (Object)(object)PoisonResistanceManager.instance == (Object)null) { return Array.Empty<(string, string, string)>(); } float reductionPercent = PoisonResistanceManager.instance.GetReductionPercent(player.playerID); float num = tierReduction * 100f; return new(string, string, string)[1] { ("Poison Resistance", $"{reductionPercent:F0}%", $"{num:F0}%") }; } } internal static class PoisonCardRegistrar { internal static void RegisterAll() { SLog.Section("PoisonCardRegistrar — RegisterAll"); CustomCard.BuildCard((Action)delegate(CardInfo ci) { PoisonCardRegistry.Lv3 = ci; }); CustomCard.BuildCard((Action)delegate(CardInfo ci) { PoisonCardRegistry.Lv2 = ci; }); CustomCard.BuildCard((Action)OnLv1Built); } private static void OnLv1Built(CardInfo ci) { PoisonCardRegistry.Lv1 = ci; SLog.Line("Registered Poison Resistance LV1: " + ci.cardName); PoisonCardRegistry.WireLevelChain(); RegisterPrerequisites(); PoisonCardDeltaRegistrar.RegisterAll(); } private static void RegisterPrerequisites() { SLog.Section("PoisonCardRegistrar — RegisterPrerequisites"); if (!RtgPrerequisiteBridge.IsAvailable) { SLog.Warn("RoundsTheGathering not available — poison cards will NOT be unlock-gated."); return; } TryRegister(PoisonCardRegistry.Lv2, PoisonCardRegistry.Lv1); TryRegister(PoisonCardRegistry.Lv3, PoisonCardRegistry.Lv2); } private static void TryRegister(CardInfo card, CardInfo required) { if (!((Object)(object)card == (Object)null) && !((Object)(object)required == (Object)null)) { bool flag = RtgPrerequisiteBridge.Register(card.cardName, required.cardName); SLog.Line($"Prereq: \"{card.cardName}\" requires \"{required.cardName}\" — registered={flag}"); } } } internal static class PoisonCardRegistry { internal static CardInfo Lv1; internal static CardInfo Lv2; internal static CardInfo Lv3; internal static void WireLevelChain() { SetNext(Lv1, Lv2); SetNext(Lv2, Lv3); SLog.Line("Poison card level chain wired (LV1 -> LV3)."); } private static void SetNext(CardInfo from, CardInfo to) where T : PoisonResistanceBase { if (!((Object)(object)from == (Object)null)) { T component = ((Component)from).GetComponent(); if ((Object)(object)component != (Object)null) { component.NextLevelCard = to; } } } } public abstract class PoisonResistanceBase : CustomCard { public CardInfo NextLevelCard; protected abstract int Level { get; } protected abstract float TotalReduction { get; } protected abstract string ArtFileName { get; } protected abstract Rarity CardRarity { get; } protected override string GetTitle() { return "Poison Resistance " + Roman(Level); } protected override string GetDescription() { return "Decrease the effects of Poison on both you and your shield by " + PercentLabel(TotalReduction) + "."; } protected override CardInfoStat[] GetStats() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown return (CardInfoStat[])(object)new CardInfoStat[1] { new CardInfoStat { positive = true, amount = PercentLabel(TotalReduction), stat = "Poison Resist", simepleAmount = (SimpleAmount)0 } }; } protected override Rarity GetRarity() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return CardRarity; } protected override GameObject GetCardArt() { return CardArtLoader.Load(ArtFileName); } protected override CardThemeColorType GetTheme() { return (CardThemeColorType)5; } public override string GetModName() { return "ShieldsMod"; } public override void SetupCard(CardInfo cardInfo, Gun gun, ApplyCardStats cardStats, CharacterStatModifiers statModifiers, Block block) { cardInfo.allowMultiple = false; } public override void OnAddCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats) { if (!((Object)(object)player == (Object)null)) { PoisonResistanceManager.instance?.RebuildForPlayer(player.playerID); SLog.Section($"PoisonResistance LV{Level} — OnAddCard"); SLog.Line($"player={player.playerID} totalReduction={TotalReduction * 100f:F0}%"); if ((Object)(object)NextLevelCard != (Object)null) { SLog.Line($"Next tier '{NextLevelCard.cardName}' becomes draftable now that LV{Level} is owned."); } } } private static string PercentLabel(float reduction) { return $"{Mathf.RoundToInt(reduction * 100f)}%"; } private static string Roman(int level) { return level switch { 1 => "I", 2 => "II", 3 => "III", _ => level.ToString(), }; } } public class PoisonResistanceLv1 : PoisonResistanceBase { protected override int Level => 1; protected override float TotalReduction => 0.25f; protected override string ArtFileName => "poison_resistance_lv1.png"; protected override Rarity CardRarity => (Rarity)0; } public class PoisonResistanceLv2 : PoisonResistanceBase { protected override int Level => 2; protected override float TotalReduction => 0.5f; protected override string ArtFileName => "poison_resistance_lv2.png"; protected override Rarity CardRarity => (Rarity)2; } public class PoisonResistanceLv3 : PoisonResistanceBase { protected override int Level => 3; protected override float TotalReduction => 0.75f; protected override string ArtFileName => "poison_resistance_lv3.png"; protected override Rarity CardRarity => (Rarity)2; } internal static class RtgPrerequisiteBridge { private static MethodInfo _registerMethod; private static bool _resolved; public static bool IsAvailable { get { Resolve(); return _registerMethod != null; } } private static void Resolve() { if (_resolved) { return; } _resolved = true; try { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "RoundsTheGathering"); if (assembly == null) { SLog.Warn("RoundsTheGathering assembly not found — shield unlock chain will not be registered."); return; } Type type = assembly.GetType("RoundsTheGathering.CardPrerequisiteRegistry"); if (type == null) { SLog.Warn("RoundsTheGathering.CardPrerequisiteRegistry type not found."); return; } _registerMethod = type.GetMethod("RegisterPrerequisite", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(string), typeof(string) }, null); if (_registerMethod == null) { SLog.Warn("RegisterPrerequisite(string, string) not found on CardPrerequisiteRegistry."); } } catch (Exception ex) { SLog.Error("Failed to resolve RoundsTheGathering registry: " + ex.Message); } } internal static bool Register(string cardName, string requiredCardName) { Resolve(); if (_registerMethod == null) { return false; } try { _registerMethod.Invoke(null, new object[2] { cardName, requiredCardName }); return true; } catch (Exception ex) { SLog.Error("RegisterPrerequisite invoke failed: " + ex.Message); return false; } } } internal static class ShieldCardDeltaRegistrar { internal static void RegisterAll() { SLog.Section("ShieldCardDeltaRegistrar — RegisterAll"); RegisterTier(ShieldCardRegistry.Lv1, 1.1f); RegisterTier(ShieldCardRegistry.Lv2, 1.25f); RegisterTier(ShieldCardRegistry.Lv3, 1.5f); RegisterTier(ShieldCardRegistry.Lv4, 2f); RegisterTier(ShieldCardRegistry.Lv5, 2.65f); } private static void RegisterTier(CardInfo card, float multiplier) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown if ((Object)(object)card == (Object)null) { SLog.Warn("ShieldCardDeltaRegistrar — skipped null card reference."); return; } ShieldHandRebuild.RegisterTier(card, multiplier); CardDeltaRegistry.RegisterSimple(card.cardName, (SimpleDeltaProvider)((Player player, CardInfo _) => ComputeShieldDelta(player, multiplier))); SLog.Line($"Registered stat-delta preview for '{card.cardName}' (x{multiplier:F2})."); } private static IReadOnlyList<(string label, string before, string after)> ComputeShieldDelta(Player player, float multiplier) { if ((Object)(object)player == (Object)null || (Object)(object)ShieldManager.instance == (Object)null) { return Array.Empty<(string, string, string)>(); } float max = ShieldManager.instance.GetShield(player.playerID).Max; float num = max * multiplier; return new(string, string, string)[1] { ("Shield Max", $"{max:F0}", $"{num:F0}") }; } } internal static class ShieldCardRegistrar { internal static void RegisterAll() { SLog.Section("ShieldCardRegistrar — RegisterAll"); CustomCard.BuildCard((Action)OnLv5Built); CustomCard.BuildCard((Action)OnLv4Built); CustomCard.BuildCard((Action)OnLv3Built); CustomCard.BuildCard((Action)OnLv2Built); CustomCard.BuildCard((Action)OnLv1Built); } private static void OnLv5Built(CardInfo ci) { ShieldCardRegistry.Lv5 = ci; SLog.Line("Registered LV5: " + ci.cardName); } private static void OnLv4Built(CardInfo ci) { ShieldCardRegistry.Lv4 = ci; SLog.Line("Registered LV4: " + ci.cardName); } private static void OnLv3Built(CardInfo ci) { ShieldCardRegistry.Lv3 = ci; SLog.Line("Registered LV3: " + ci.cardName); } private static void OnLv2Built(CardInfo ci) { ShieldCardRegistry.Lv2 = ci; SLog.Line("Registered LV2: " + ci.cardName); } private static void OnLv1Built(CardInfo ci) { ShieldCardRegistry.Lv1 = ci; SLog.Line("Registered LV1: " + ci.cardName); ShieldCardRegistry.WireLevelChain(); RegisterPrerequisites(); ShieldCardDeltaRegistrar.RegisterAll(); } private static void RegisterPrerequisites() { SLog.Section("ShieldCardRegistrar — RegisterPrerequisites"); if (!RtgPrerequisiteBridge.IsAvailable) { SLog.Warn("RoundsTheGathering not available — shield cards will NOT be unlock-gated."); return; } TryRegister(ShieldCardRegistry.Lv2, ShieldCardRegistry.Lv1); TryRegister(ShieldCardRegistry.Lv3, ShieldCardRegistry.Lv2); TryRegister(ShieldCardRegistry.Lv4, ShieldCardRegistry.Lv3); TryRegister(ShieldCardRegistry.Lv5, ShieldCardRegistry.Lv4); } private static void TryRegister(CardInfo card, CardInfo required) { if ((Object)(object)card == (Object)null || (Object)(object)required == (Object)null) { SLog.Warn("Skipping prerequisite registration — a card reference was null."); return; } bool flag = RtgPrerequisiteBridge.Register(card.cardName, required.cardName); SLog.Line($"Prereq: \"{card.cardName}\" requires \"{required.cardName}\" — registered={flag}"); } } internal static class ShieldCardRegistry { internal static CardInfo Lv1; internal static CardInfo Lv2; internal static CardInfo Lv3; internal static CardInfo Lv4; internal static CardInfo Lv5; internal static void WireLevelChain() { SetNext(Lv1, Lv2); SetNext(Lv2, Lv3); SetNext(Lv3, Lv4); SetNext(Lv4, Lv5); SLog.Line("Shield card level chain wired (LV1 -> LV5)."); } private static void SetNext(CardInfo from, CardInfo to) where T : UpgradeShieldHealthBase { if (!((Object)(object)from == (Object)null)) { T component = ((Component)from).GetComponent(); if ((Object)(object)component != (Object)null) { component.NextLevelCard = to; } } } } public abstract class UpgradeShieldHealthBase : CustomCard { public CardInfo NextLevelCard; protected abstract int Level { get; } protected abstract float Multiplier { get; } protected abstract string ArtFileName { get; } protected abstract Rarity CardRarity { get; } protected override string GetTitle() { return "Upgrade Shield Health " + Roman(Level); } protected override string GetDescription() { return "Increases max shield health by " + PercentLabel(Multiplier) + ". Multiplies with other shield upgrades."; } protected override CardInfoStat[] GetStats() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown return (CardInfoStat[])(object)new CardInfoStat[1] { new CardInfoStat { positive = true, amount = PercentLabel(Multiplier), stat = "Shield Max", simepleAmount = (SimpleAmount)0 } }; } protected override Rarity GetRarity() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return CardRarity; } protected override GameObject GetCardArt() { return CardArtLoader.Load(ArtFileName); } protected override CardThemeColorType GetTheme() { return (CardThemeColorType)2; } public override string GetModName() { return "ShieldsMod"; } public override void SetupCard(CardInfo cardInfo, Gun gun, ApplyCardStats cardStats, CharacterStatModifiers statModifiers, Block block) { cardInfo.allowMultiple = false; } public override void OnAddCard(Player player, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers characterStats) { if (!((Object)(object)player == (Object)null) && !((Object)(object)ShieldManager.instance == (Object)null)) { int playerID = player.playerID; ShieldState orCreateShield = ShieldManager.instance.GetOrCreateShield(playerID); float max = orCreateShield.Max; orCreateShield.Max *= Multiplier; orCreateShield.Current = Mathf.Min(orCreateShield.Current, orCreateShield.Max); ShieldManager.instance.RefreshVisual(playerID); SLog.Section($"UpgradeShieldHealth LV{Level} — OnAddCard"); SLog.Line($"player={playerID} multiplier={Multiplier:F2} max {max:F1} -> {orCreateShield.Max:F1}"); if ((Object)(object)NextLevelCard != (Object)null) { SLog.Line($"Next tier '{NextLevelCard.cardName}' becomes draftable now that LV{Level} is owned."); } else { SLog.Line("This is the final tier (no next level)."); } } } private static string PercentLabel(float multiplier) { return $"+{Mathf.RoundToInt((multiplier - 1f) * 100f)}%"; } private static string Roman(int level) { return level switch { 1 => "I", 2 => "II", 3 => "III", 4 => "IV", 5 => "V", _ => level.ToString(), }; } } public class UpgradeShieldLv1 : UpgradeShieldHealthBase { protected override int Level => 1; protected override float Multiplier => 1.1f; protected override string ArtFileName => "lv1_artwork.png"; protected override Rarity CardRarity => (Rarity)0; } public class UpgradeShieldLv2 : UpgradeShieldHealthBase { protected override int Level => 2; protected override float Multiplier => 1.25f; protected override string ArtFileName => "lv2_artwork.png"; protected override Rarity CardRarity => (Rarity)0; } public class UpgradeShieldLv3 : UpgradeShieldHealthBase { protected override int Level => 3; protected override float Multiplier => 1.5f; protected override string ArtFileName => "lv3_artwork.png"; protected override Rarity CardRarity => (Rarity)1; } public class UpgradeShieldLv4 : UpgradeShieldHealthBase { protected override int Level => 4; protected override float Multiplier => 2f; protected override string ArtFileName => "lv4_artwork.png"; protected override Rarity CardRarity => (Rarity)1; } public class UpgradeShieldLv5 : UpgradeShieldHealthBase { protected override int Level => 5; protected override float Multiplier => 2.65f; protected override string ArtFileName => "lv5_artwork.png"; protected override Rarity CardRarity => (Rarity)2; } }