using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using ModdingUtils.Utils; using UnityEngine; using UpgradeDraft.Models; using UpgradeDraft.Services; using UpgradeDraft.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("0.0.0.0")] [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 UpgradeDraft { [BepInPlugin("com.damian.rounds.upgradedraft", "Upgrade Draft", "1.0.3")] [BepInProcess("Rounds.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class UpgradeDraftPlugin : BaseUnityPlugin { public const string PluginGuid = "com.damian.rounds.upgradedraft"; public const string PluginName = "Upgrade Draft"; public const string PluginVersion = "1.0.3"; private static readonly int[] DefaultChances = new int[4] { 25, 50, 20, 5 }; private Harmony _harmony; private int _chanceZero; private int _chanceOne; private int _chanceTwo; private int _chanceThree; internal static UpgradeDraftPlugin Instance { get; private set; } internal static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger; internal static PlayerDeckService DeckService { get; private set; } internal static DraftRollService RollService { get; private set; } internal static CardSelectionService SelectionService { get; private set; } internal static UpgradePreviewService PreviewService { get; private set; } internal static UpgradeCardVisualService VisualService { get; private set; } internal static UpgradeDraftState State { get; private set; } internal static NetworkAuthorityService AuthorityService { get; private set; } internal static SkipRerollService SkipRerollService { get; private set; } internal ConfigEntry NormalCardCount { get; private set; } internal ConfigEntry ChanceZeroUpgrades { get; private set; } internal ConfigEntry ChanceOneUpgrade { get; private set; } internal ConfigEntry ChanceTwoUpgrades { get; private set; } internal ConfigEntry ChanceThreeUpgrades { get; private set; } internal ConfigEntry EnableUpgradeVisuals { get; private set; } internal ConfigEntry EnableStatPreview { get; private set; } internal ConfigEntry UpgradeBlacklist { get; private set; } internal ConfigEntry VerboseLogging { get; private set; } internal ConfigEntry EnableSkipForPoint { get; private set; } internal ConfigEntry SkipPointsForReroll { get; private set; } internal ConfigEntry EnableSkipRerollOverlay { get; private set; } internal ConfigEntry AllowMultipleRerollsPerPick { get; private set; } private void Awake() { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown Instance = this; BindConfig(); ValidateConfig(); State = new UpgradeDraftState(((BaseUnityPlugin)this).Logger); DeckService = new PlayerDeckService(((BaseUnityPlugin)this).Logger, this); RollService = new DraftRollService(((BaseUnityPlugin)this).Logger, this); SelectionService = new CardSelectionService(((BaseUnityPlugin)this).Logger, this, DeckService); PreviewService = new UpgradePreviewService(((BaseUnityPlugin)this).Logger, this); VisualService = new UpgradeCardVisualService(((BaseUnityPlugin)this).Logger, this, PreviewService); AuthorityService = new NetworkAuthorityService(((BaseUnityPlugin)this).Logger); SkipRerollService = new SkipRerollService(((BaseUnityPlugin)this).Logger, this); if ((Object)(object)((Component)this).GetComponent() == (Object)null) { ((Component)this).gameObject.AddComponent(); } _harmony = new Harmony("com.damian.rounds.upgradedraft"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Upgrade Draft 1.0.3 loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } internal int GetCardCount() { int num = Math.Max(1, NormalCardCount.Value); if (num != 4) { ((BaseUnityPlugin)this).Logger.LogWarning((object)$"NormalCardCount is set to {num}. Values other than 4 are experimental."); } return num; } internal (int Zero, int One, int Two, int Three) GetWeightedChances() { return (_chanceZero, _chanceOne, _chanceTwo, _chanceThree); } internal bool IsVisualEnabled() { return EnableUpgradeVisuals.Value; } internal bool IsStatPreviewEnabled() { return EnableStatPreview.Value; } internal bool IsVerboseLoggingEnabled() { return VerboseLogging.Value; } internal bool IsSkipForPointEnabled() { return EnableSkipForPoint.Value; } internal bool IsSkipRerollOverlayEnabled() { return EnableSkipRerollOverlay.Value; } internal int GetSkipPointsForReroll() { return Math.Max(1, SkipPointsForReroll.Value); } internal bool IsMultipleRerollsPerPickAllowed() { return AllowMultipleRerollsPerPick.Value; } internal HashSet GetBlacklist() { return new HashSet(from v in (UpgradeBlacklist.Value ?? string.Empty).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select v.Trim().ToLowerInvariant() into v where !string.IsNullOrWhiteSpace(v) select v); } private void BindConfig() { NormalCardCount = ((BaseUnityPlugin)this).Config.Bind("General", "NormalCardCount", 4, "Number of visible card choices per draft pick. Values other than 4 are experimental."); ChanceZeroUpgrades = ((BaseUnityPlugin)this).Config.Bind("Chances", "ChanceZeroUpgrades", 25, "Chance weight for showing 0 upgrade cards."); ChanceOneUpgrade = ((BaseUnityPlugin)this).Config.Bind("Chances", "ChanceOneUpgrade", 50, "Chance weight for showing 1 upgrade card."); ChanceTwoUpgrades = ((BaseUnityPlugin)this).Config.Bind("Chances", "ChanceTwoUpgrades", 20, "Chance weight for showing 2 upgrade cards."); ChanceThreeUpgrades = ((BaseUnityPlugin)this).Config.Bind("Chances", "ChanceThreeUpgrades", 5, "Chance weight for showing 3 upgrade cards."); EnableUpgradeVisuals = ((BaseUnityPlugin)this).Config.Bind("UI", "EnableUpgradeVisuals", true, "Adds UPGRADE visuals and ownership info to upgrade cards."); EnableStatPreview = ((BaseUnityPlugin)this).Config.Bind("UI", "EnableStatPreview", true, "Appends conservative stacked stat preview lines for upgrade cards."); EnableSkipRerollOverlay = ((BaseUnityPlugin)this).Config.Bind("UI", "EnableSkipRerollOverlay", true, "Show skip/reroll buttons during card pick."); UpgradeBlacklist = ((BaseUnityPlugin)this).Config.Bind("Rules", "UpgradeBlacklist", string.Empty, "Comma-separated card names that are not allowed as upgrades."); EnableSkipForPoint = ((BaseUnityPlugin)this).Config.Bind("Rules", "EnableSkipForPoint", true, "Allow players to skip card pick and gain one skip point."); SkipPointsForReroll = ((BaseUnityPlugin)this).Config.Bind("Rules", "SkipPointsForReroll", 2, "Skip points required (and consumed) to reroll cards."); AllowMultipleRerollsPerPick = ((BaseUnityPlugin)this).Config.Bind("Rules", "AllowMultipleRerollsPerPick", false, "Allow more than one reroll during a single card pick when player has enough points."); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind("Debug", "VerboseLogging", false, "Enable extra debug logging."); } private void ValidateConfig() { NormalCardCount.Value = Math.Max(1, NormalCardCount.Value); SkipPointsForReroll.Value = Math.Max(1, SkipPointsForReroll.Value); int num = Math.Max(0, ChanceZeroUpgrades.Value); int num2 = Math.Max(0, ChanceOneUpgrade.Value); int num3 = Math.Max(0, ChanceTwoUpgrades.Value); int num4 = Math.Max(0, ChanceThreeUpgrades.Value); int num5 = num + num2 + num3 + num4; if (num5 <= 0) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Upgrade chance weights are invalid (sum <= 0). Falling back to defaults: 25/50/20/5."); _chanceZero = DefaultChances[0]; _chanceOne = DefaultChances[1]; _chanceTwo = DefaultChances[2]; _chanceThree = DefaultChances[3]; return; } if (num5 == 100) { _chanceZero = num; _chanceOne = num2; _chanceTwo = num3; _chanceThree = num4; return; } double num6 = 100.0 / (double)num5; _chanceZero = (int)Math.Round((double)num * num6); _chanceOne = (int)Math.Round((double)num2 * num6); _chanceTwo = (int)Math.Round((double)num3 * num6); _chanceThree = (int)Math.Round((double)num4 * num6); int num7 = _chanceZero + _chanceOne + _chanceTwo + _chanceThree; int num8 = 100 - num7; _chanceOne += num8; ((BaseUnityPlugin)this).Logger.LogWarning((object)$"Upgrade chance weights sum to {num5} instead of 100. Normalized to {_chanceZero}/{_chanceOne}/{_chanceTwo}/{_chanceThree}."); } } } namespace UpgradeDraft.UI { public sealed class SkipRerollOverlay : MonoBehaviour { private GUIStyle _buttonStyle; private GUIStyle _labelStyle; private GUIStyle _titleStyle; private void Awake() { //IL_000b: 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_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown _buttonStyle = new GUIStyle(GUI.skin.button) { fontSize = 18, alignment = (TextAnchor)4 }; GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 16, alignment = (TextAnchor)4 }; val.normal.textColor = Color.white; _labelStyle = val; GUIStyle val2 = new GUIStyle(GUI.skin.label) { fontSize = 18, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; val2.normal.textColor = Color.yellow; _titleStyle = val2; } private void Update() { if (!IsReady()) { return; } CardChoice instance = CardChoice.instance; int pickrID = instance.pickrID; if (UpgradeDraftPlugin.SkipRerollService.IsCardChoiceOpen(instance) && !UpgradeDraftPlugin.SkipRerollService.IsCardChoiceBusy(instance) && UpgradeDraftPlugin.SkipRerollService.IsLocalClientControllingPick(instance)) { if (Input.GetKeyDown((KeyCode)115)) { UpgradeDraftPlugin.SkipRerollService.TrySkip(instance, pickrID); } if (Input.GetKeyDown((KeyCode)114)) { UpgradeDraftPlugin.SkipRerollService.TryReroll(instance, pickrID); } } } private void OnGUI() { //IL_0084: 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_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) if (!IsReady()) { return; } CardChoice instance = CardChoice.instance; if (instance == null) { return; } int pickrID = instance.pickrID; if (!UpgradeDraftPlugin.SkipRerollService.IsCardChoiceOpen(instance) || !UpgradeDraftPlugin.SkipRerollService.IsLocalClientControllingPick(instance)) { return; } int skipPoints = UpgradeDraftPlugin.State.GetSkipPoints(pickrID); int skipPointsForReroll = UpgradeDraftPlugin.Instance.GetSkipPointsForReroll(); float num = 430f; float num2 = 190f; float num3 = ((float)Screen.width - num) * 0.5f; float num4 = (float)Screen.height - num2 - 30f; GUI.Box(new Rect(num3, num4, num, num2), string.Empty); GUI.Label(new Rect(num3 + 8f, num4 + 8f, num - 16f, 28f), "Upgrade Draft: Skip & Reroll", _titleStyle); GUI.Label(new Rect(num3 + 8f, num4 + 38f, num - 16f, 24f), $"Skip Points: {skipPoints}", _labelStyle); GUI.Label(new Rect(num3 + 8f, num4 + 60f, num - 16f, 24f), $"Reroll Cost: {skipPointsForReroll} points", _labelStyle); Rect val = default(Rect); ((Rect)(ref val))..ctor(num3 + 18f, num4 + 92f, (num - 54f) / 2f, 42f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).xMax + 18f, num4 + 92f, (num - 54f) / 2f, 42f); bool flag = UpgradeDraftPlugin.SkipRerollService.IsCardChoiceBusy(instance); GUI.enabled = !flag && UpgradeDraftPlugin.Instance.IsSkipForPointEnabled(); if (GUI.Button(val, "Skip (+1 point) [S]", _buttonStyle)) { UpgradeDraftPlugin.SkipRerollService.TrySkip(instance, pickrID); } string reason; bool flag2 = UpgradeDraftPlugin.SkipRerollService.CanReroll(instance, pickrID, out reason); bool num5 = skipPoints >= skipPointsForReroll; if (num5) { GUI.enabled = !flag && flag2; if (GUI.Button(val2, "Reroll (-points) [R]", _buttonStyle)) { UpgradeDraftPlugin.SkipRerollService.TryReroll(instance, pickrID); } } GUI.enabled = true; if (!num5) { GUI.Label(new Rect(num3 + 8f, num4 + 140f, num - 16f, 24f), $"Need {skipPointsForReroll} skip points to reroll.", _labelStyle); } else if (!flag2) { GUI.Label(new Rect(num3 + 8f, num4 + 140f, num - 16f, 24f), "Reroll unavailable right now.", _labelStyle); } } private static bool IsReady() { if ((Object)(object)UpgradeDraftPlugin.Instance == (Object)null || UpgradeDraftPlugin.State == null || UpgradeDraftPlugin.SkipRerollService == null) { return false; } if (!UpgradeDraftPlugin.Instance.IsSkipRerollOverlayEnabled()) { return false; } return CardChoice.instance != null; } } public sealed class UpgradeCardMarker : MonoBehaviour { public bool IsUpgrade; public int OwnedBefore; public int OwnedAfter; public string SourceCardName; } public sealed class UpgradeCardVisualService { private readonly ManualLogSource _logger; private readonly UpgradeDraftPlugin _plugin; private readonly UpgradePreviewService _previewService; public UpgradeCardVisualService(ManualLogSource logger, UpgradeDraftPlugin plugin, UpgradePreviewService previewService) { _logger = logger; _plugin = plugin; _previewService = previewService; } public void ApplyUpgradeVisual(GameObject spawnedCard, DraftCardEntry entry) { if (!_plugin.IsVisualEnabled() || spawnedCard == null || entry == null) { return; } try { UpgradeCardMarker obj = spawnedCard.GetComponent() ?? spawnedCard.AddComponent(); obj.IsUpgrade = true; obj.OwnedBefore = entry.OwnedBefore; obj.OwnedAfter = entry.OwnedAfter; obj.SourceCardName = entry.Card.cardName; ApplyTextComponents(spawnedCard, entry); } catch (Exception ex) { _logger.LogWarning((object)("Failed to apply upgrade visuals on card object '" + ((Object)spawnedCard).name + "'. Error: " + ex.Message)); } } private void ApplyTextComponents(GameObject spawnedCard, DraftCardEntry entry) { string value = _previewService.BuildUpgradeTitle(entry); string value2 = BuildShortUpgradeDescription(entry); List list = (from c in spawnedCard.GetComponentsInChildren(true) where (Object)(object)c != (Object)null where HasStringTextProperty(((object)c).GetType()) select c).ToList(); if (list.Count == 0) { _logger.LogWarning((object)("Upgrade card '" + ((Object)spawnedCard).name + "' has no discovered text components to patch.")); return; } bool flag = false; foreach (Component item in list) { string text = ((Object)item.gameObject).name.ToLowerInvariant(); if (!string.IsNullOrWhiteSpace(GetTextProperty(item))) { if (text.Contains("title") || text.Contains("name")) { SetTextProperty(item, value); flag = true; } else if (text.Contains("desc") || text.Contains("description")) { SetTextProperty(item, value2); flag = true; } } } if (!flag) { _logger.LogWarning((object)("Upgrade card '" + ((Object)spawnedCard).name + "' did not expose recognizable title/description text components.")); } } private static string BuildShortUpgradeDescription(DraftCardEntry entry) { string text = entry.Card.cardDestription ?? string.Empty; text = text.Trim(); string text2 = $"Upgrade: grants another copy. Owned: {entry.OwnedBefore} -> {entry.OwnedAfter}"; if (string.IsNullOrWhiteSpace(text)) { return text2; } return text + "\n" + text2; } private static bool HasStringTextProperty(Type type) { PropertyInfo property = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(string) && property.CanRead) { return property.CanWrite; } return false; } private static string GetTextProperty(Component component) { return ((object)component).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(component, null) as string; } private static void SetTextProperty(Component component, string value) { ((object)component).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(component, value, null); } } } namespace UpgradeDraft.Services { public sealed class CardSelectionService { private readonly ManualLogSource _logger; private readonly UpgradeDraftPlugin _plugin; private readonly PlayerDeckService _deckService; public CardSelectionService(ManualLogSource logger, UpgradeDraftPlugin plugin, PlayerDeckService deckService) { _logger = logger; _plugin = plugin; _deckService = deckService; } public List BuildDraftEntries(CardChoice cardChoice, Player player, DraftPlan plan) { HashSet blacklist = _plugin.GetBlacklist(); List eligibleUpgradeCards = _deckService.GetEligibleUpgradeCards(player, blacklist); HashSet hashSet = new HashSet(StringComparer.Ordinal); List list = new List(); foreach (CardInfo item in PickRandomDistinct(eligibleUpgradeCards, plan.ActualUpgradeCount)) { int ownedCount = _deckService.GetOwnedCount(player, item); list.Add(new DraftCardEntry(item, isUpgrade: true, ownedCount)); hashSet.Add(_deckService.GetStableCardId(item)); } List list2 = SelectNewCards(cardChoice, player, plan.NewCardCount, hashSet, strictUnique: true, strictNotOwned: true); if (list2.Count < plan.NewCardCount) { _logger.LogWarning((object)$"Unable to fill all new-card slots with strict unique+not-owned rules. Needed {plan.NewCardCount}, got {list2.Count}. Trying duplicate-new fallback."); int wanted = plan.NewCardCount - list2.Count; list2.AddRange(SelectNewCards(cardChoice, player, wanted, hashSet, strictUnique: false, strictNotOwned: true)); } if (list2.Count < plan.NewCardCount) { _logger.LogWarning((object)$"Still missing new cards after duplicate-new fallback. Needed {plan.NewCardCount}, got {list2.Count}. Trying broad safety fallback."); int wanted2 = plan.NewCardCount - list2.Count; list2.AddRange(SelectNewCards(cardChoice, player, wanted2, hashSet, strictUnique: false, strictNotOwned: false)); } foreach (CardInfo item2 in list2) { int ownedCount2 = _deckService.GetOwnedCount(player, item2); list.Add(new DraftCardEntry(item2, isUpgrade: false, ownedCount2)); hashSet.Add(_deckService.GetStableCardId(item2)); } if (list.Count < plan.TotalCardCount) { _logger.LogWarning((object)$"Draft pool has {list.Count}/{plan.TotalCardCount} cards after all selection fallbacks. Letting vanilla SpawnUniqueCard fill remainder as safety."); } return list; } private List SelectNewCards(CardChoice cardChoice, Player player, int wanted, ISet existingCardIds, bool strictUnique, bool strictNotOwned) { List list = new List(); if (wanted <= 0) { return list; } int num = 0; int num2 = Math.Max(1000, wanted * 400); while (list.Count < wanted && num < num2) { num++; CardInfo randomCardWithCondition = Cards.instance.GetRandomCardWithCondition(player, (Gun)null, (GunAmmo)null, (CharacterData)null, (HealthHandler)null, (Gravity)null, (Block)null, (CharacterStatModifiers)null, BuildSelectionCondition(cardChoice, player, existingCardIds, list, strictUnique, strictNotOwned), 1000); if (randomCardWithCondition != null && (!strictNotOwned || !_deckService.PlayerOwnsCard(player, randomCardWithCondition)) && !_deckService.IsBlacklisted(randomCardWithCondition, _plugin.GetBlacklist())) { string stableCardId = _deckService.GetStableCardId(randomCardWithCondition); if (!strictUnique || !existingCardIds.Contains(stableCardId)) { list.Add(randomCardWithCondition); existingCardIds.Add(stableCardId); } } } return list; } private Func BuildSelectionCondition(CardChoice cardChoice, Player player, ISet selectedIds, IEnumerable localNewCards, bool strictUnique, bool strictNotOwned) { HashSet localIds = new HashSet(localNewCards.Where((CardInfo c) => c != null).Select(_deckService.GetStableCardId), StringComparer.Ordinal); Func baseCondition = BuildCardChoiceCompatibleCondition(cardChoice); return delegate(CardInfo card, Player p, Gun gun, GunAmmo gunAmmo, CharacterData data, HealthHandler health, Gravity gravity, Block block, CharacterStatModifiers stats) { if (card == null) { return false; } if (strictNotOwned && _deckService.PlayerOwnsCard(player, card)) { return false; } if (_deckService.IsBlacklisted(card, _plugin.GetBlacklist())) { return false; } if (!Cards.instance.PlayerIsAllowedCard(player, card)) { return false; } if (!baseCondition(card, player)) { return false; } if (strictUnique) { string stableCardId = _deckService.GetStableCardId(card); if (selectedIds.Contains(stableCardId) || localIds.Contains(stableCardId)) { return false; } } return true; }; } private Func BuildCardChoiceCompatibleCondition(CardChoice cardChoice) { return delegate(CardInfo card, Player player) { if (card == null) { return false; } try { if ((Object)(object)cardChoice != (Object)null && cardChoice.pickrID != -1) { object fieldOrPropertyValue = ReflectionUtils.GetFieldOrPropertyValue(((Component)player.data).GetComponent("Holding"), "holdable"); Component val = (Component)((fieldOrPropertyValue is Component) ? fieldOrPropertyValue : null); if (val != null) { Gun component = val.GetComponent(); Gun component2 = ((Component)card).GetComponent(); if (component != null && component2 != null && component2.lockGunToDefault && component.lockGunToDefault) { return false; } } } } catch (Exception ex) { if (_plugin.IsVerboseLoggingEnabled()) { _logger.LogWarning((object)("CardChoice-compatible condition failed to inspect lockGunToDefault: " + ex.Message)); } } return true; }; } private List PickRandomDistinct(List cards, int amount) { List list = new List(); if (cards == null || cards.Count == 0 || amount <= 0) { return list; } List list2 = cards.Where((CardInfo c) => c != null).ToList(); for (int i = 0; i < list2.Count; i++) { int index = Random.Range(i, list2.Count); CardInfo value = list2[i]; list2[i] = list2[index]; list2[index] = value; } for (int j = 0; j < Math.Min(amount, list2.Count); j++) { list.Add(list2[j]); } return list; } } public sealed class DraftRollService { private readonly ManualLogSource _logger; private readonly UpgradeDraftPlugin _plugin; public DraftRollService(ManualLogSource logger, UpgradeDraftPlugin plugin) { _logger = logger; _plugin = plugin; } public DraftPlan BuildPlan(int eligibleUpgradeCount) { int cardCount = _plugin.GetCardCount(); int rollValue; int num = RollDesiredUpgradeCount(out rollValue); int num2 = Math.Min(Math.Max(0, num), Math.Max(0, eligibleUpgradeCount)); DraftPlan draftPlan = new DraftPlan { DesiredUpgradeCount = num, ActualUpgradeCount = num2, NewCardCount = Math.Max(0, cardCount - num2), TotalCardCount = cardCount, RollValue = rollValue, FallbackReason = string.Empty }; if (num2 != num) { draftPlan.FallbackReason = $"Clamped upgrades from desired {num} to {num2} because only {eligibleUpgradeCount} eligible upgrade card(s) exist."; _logger.LogWarning((object)draftPlan.FallbackReason); } return draftPlan; } private int RollDesiredUpgradeCount(out int rollValue) { (int, int, int, int) weightedChances = _plugin.GetWeightedChances(); rollValue = Random.Range(0, 100); int item = weightedChances.Item1; int num = item + weightedChances.Item2; int num2 = num + weightedChances.Item3; if (rollValue < item) { return 0; } if (rollValue < num) { return 1; } if (rollValue < num2) { return 2; } return 3; } } internal sealed class NetworkAuthorityService { private readonly ManualLogSource _logger; public NetworkAuthorityService(ManualLogSource logger) { _logger = logger; } public bool IsAuthoritativeClient() { try { Type type = Type.GetType("Photon.Pun.PhotonNetwork, PhotonUnityNetworking"); if (type != null) { PropertyInfo property = type.GetProperty("IsMasterClient", BindingFlags.Static | BindingFlags.Public); if (property != null) { return (bool)property.GetValue(null, null); } } Type type2 = Type.GetType("PhotonNetwork, Assembly-CSharp"); if (type2 != null) { PropertyInfo property2 = type2.GetProperty("isMasterClient", BindingFlags.Static | BindingFlags.Public); if (property2 != null) { return (bool)property2.GetValue(null, null); } } } catch (Exception ex) { _logger.LogWarning((object)("Failed to query network authority. Defaulting to local-authoritative execution. Error: " + ex.Message)); } return true; } } public sealed class PlayerDeckService { private readonly ManualLogSource _logger; private readonly UpgradeDraftPlugin _plugin; public PlayerDeckService(ManualLogSource logger, UpgradeDraftPlugin plugin) { _logger = logger; _plugin = plugin; } public List GetOwnedCards(Player player) { if (player == null) { return new List(); } try { object fieldOrPropertyValue = ReflectionUtils.GetFieldOrPropertyValue(player, "data"); if (fieldOrPropertyValue == null) { return new List(); } List list = ReflectionUtils.ToTypedList(ReflectionUtils.GetFieldOrPropertyValue(fieldOrPropertyValue, "currentCards")); if (list.Count > 0) { return list; } list = ReflectionUtils.ToTypedList(ReflectionUtils.GetFieldOrPropertyValue(fieldOrPropertyValue, "cards")); if (list.Count > 0) { return list; } } catch (Exception ex) { _logger.LogWarning((object)$"Failed to read owned cards for player {player.playerID}: {ex.Message}"); } return new List(); } public int GetOwnedCount(Player player, CardInfo card) { if (player == null || card == null) { return 0; } string cardName = ((Object)((Component)card).gameObject).name; return GetOwnedCards(player).Count((CardInfo c) => c != null && string.Equals(((Object)((Component)c).gameObject).name, cardName, StringComparison.Ordinal)); } public bool PlayerOwnsCard(Player player, CardInfo card) { return GetOwnedCount(player, card) > 0; } public List GetEligibleUpgradeCards(Player player, ISet blacklistLower) { List ownedCards = GetOwnedCards(player); HashSet hashSet = new HashSet(StringComparer.Ordinal); List list = new List(); foreach (CardInfo item in ownedCards) { if (IsEligibleUpgradeCard(player, item, blacklistLower)) { string stableCardId = GetStableCardId(item); if (hashSet.Add(stableCardId)) { list.Add(item); } } } return list; } public bool IsEligibleUpgradeCard(Player player, CardInfo card, ISet blacklistLower) { if (player == null || card == null) { return false; } if (!PlayerOwnsCard(player, card)) { return false; } if (!card.allowMultiple) { return false; } if (IsBlacklisted(card, blacklistLower)) { return false; } if (!Cards.instance.PlayerIsAllowedCard(player, card)) { return false; } return true; } public bool IsBlacklisted(CardInfo card, ISet blacklistLower) { if (card == null || blacklistLower == null || blacklistLower.Count == 0) { return false; } string item = (card.cardName ?? string.Empty).Trim().ToLowerInvariant(); string item2 = (((Object)(object)((Component)card).gameObject != (Object)null) ? ((Object)((Component)card).gameObject).name : string.Empty).Replace("(Clone)", string.Empty).Trim().ToLowerInvariant(); if (!blacklistLower.Contains(item)) { return blacklistLower.Contains(item2); } return true; } public string GetStableCardId(CardInfo card) { if (card == null) { return "null"; } if (!string.IsNullOrWhiteSpace(card.cardName)) { return card.cardName.Trim().ToLowerInvariant(); } string text = (((Object)(object)((Component)card).gameObject != (Object)null) ? ((Object)((Component)card).gameObject).name : string.Empty); if (!string.IsNullOrWhiteSpace(text)) { return text.Replace("(Clone)", string.Empty).Trim().ToLowerInvariant(); } return ((Object)card).GetInstanceID().ToString(); } } internal static class ReflectionUtils { internal static object GetFieldOrPropertyValue(object instance, string name) { if (instance == null || string.IsNullOrWhiteSpace(name)) { return null; } Type type = instance.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(instance); } return type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance, null); } internal static bool TrySetProperty(object instance, string propertyName, object value) { if (instance == null || string.IsNullOrWhiteSpace(propertyName)) { return false; } PropertyInfo property = instance.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property == null || !property.CanWrite) { return false; } property.SetValue(instance, value, null); return true; } internal static List ToTypedList(object maybeList) where T : class { if (maybeList == null) { return new List(); } if (maybeList is IList source) { return source.Where((T item) => item != null).ToList(); } if (maybeList is IEnumerable enumerable) { List list = new List(); { foreach (object item3 in enumerable) { if (item3 is T item2) { list.Add(item2); } } return list; } } return new List(); } } public sealed class SkipRerollService { private readonly ManualLogSource _logger; private readonly UpgradeDraftPlugin _plugin; public SkipRerollService(ManualLogSource logger, UpgradeDraftPlugin plugin) { _logger = logger; _plugin = plugin; } public bool IsCardChoiceBusy(CardChoice cardChoice) { try { object value = Traverse.Create((object)cardChoice).Field("isPlaying").GetValue(); if (value is bool) { return (bool)value; } } catch (Exception) { } return false; } public bool IsCardChoiceOpen(CardChoice cardChoice) { if (cardChoice == null || cardChoice.pickrID < 0) { return false; } try { object value = Traverse.Create((object)cardChoice).Field("IsPicking").GetValue(); bool flag = default(bool); int num; if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return true; } } catch (Exception) { } try { if (Traverse.Create((object)cardChoice).Field("spawnedCards").GetValue() is ICollection collection) { return collection.Count > 0; } } catch (Exception) { } return false; } public bool IsLocalClientControllingPick(CardChoice cardChoice) { Player val = ResolvePickingPlayer(cardChoice); if (val == null) { return true; } try { CharacterData data = val.data; if (data == null) { return true; } object fieldOrPropertyValue = ReflectionUtils.GetFieldOrPropertyValue(data, "view"); if (fieldOrPropertyValue == null) { return true; } object fieldOrPropertyValue2 = ReflectionUtils.GetFieldOrPropertyValue(fieldOrPropertyValue, "IsMine"); if (fieldOrPropertyValue2 is bool) { return (bool)fieldOrPropertyValue2; } } catch (Exception ex) { if (_plugin.IsVerboseLoggingEnabled()) { _logger.LogWarning((object)("Unable to resolve local pick ownership. Defaulting to visible controls. Error: " + ex.Message)); } } return true; } public bool TrySkip(CardChoice cardChoice, int pickerId) { if (!_plugin.IsSkipForPointEnabled()) { return false; } if (cardChoice == null || pickerId < 0) { return false; } if (IsCardChoiceBusy(cardChoice)) { return false; } try { cardChoice.Pick((GameObject)null, true); cardChoice.pickrID = -1; int num = UpgradeDraftPlugin.State.AddSkipPoint(pickerId); UpgradeDraftPlugin.State.ClearDraftPlanOnly("Skip pressed."); _logger.LogInfo((object)$"Player {pickerId} skipped card pick and gained 1 skip point (total: {num})."); return true; } catch (Exception ex) { _logger.LogWarning((object)$"Failed to skip pick for player {pickerId}: {ex.Message}"); return false; } } public bool CanReroll(CardChoice cardChoice, int pickerId, out string reason) { reason = string.Empty; if (cardChoice == null || pickerId < 0) { reason = "invalid_card_choice"; return false; } if (IsCardChoiceBusy(cardChoice)) { reason = "card_choice_busy"; return false; } int skipPointsForReroll = _plugin.GetSkipPointsForReroll(); if (UpgradeDraftPlugin.State.GetSkipPoints(pickerId) < skipPointsForReroll) { reason = "not_enough_points"; return false; } if (!_plugin.IsMultipleRerollsPerPickAllowed() && UpgradeDraftPlugin.State.RerolledThisPick) { reason = "already_rerolled_this_pick"; return false; } if (AccessTools.Method(((object)cardChoice).GetType(), "ReplaceCards", (Type[])null, (Type[])null) == null) { reason = "replace_cards_method_missing"; return false; } return true; } private Player ResolvePickingPlayer(CardChoice cardChoice) { if (cardChoice == null || PlayerManager.instance == null) { return null; } try { if (Convert.ToInt32(Traverse.Create((object)cardChoice).Field("pickerType").GetValue()) == 0) { return PlayerManager.instance.GetPlayersInTeam(cardChoice.pickrID).FirstOrDefault(); } if (cardChoice.pickrID >= 0 && cardChoice.pickrID < PlayerManager.instance.players.Count()) { return PlayerManager.instance.players[cardChoice.pickrID]; } } catch { } return null; } public bool TryReroll(CardChoice cardChoice, int pickerId) { if (!CanReroll(cardChoice, pickerId, out var reason)) { if (_plugin.IsVerboseLoggingEnabled()) { _logger.LogInfo((object)$"Reroll denied for player {pickerId}. Reason={reason}"); } return false; } int skipPointsForReroll = _plugin.GetSkipPointsForReroll(); if (!UpgradeDraftPlugin.State.TryConsumeSkipPoints(pickerId, skipPointsForReroll)) { _logger.LogWarning((object)$"Failed to consume reroll points for player {pickerId}. This should not happen after CanReroll check."); return false; } try { UpgradeDraftPlugin.State.ClearDraftPlanOnly("Reroll requested."); Traverse.Create((object)cardChoice).Field("picks").SetValue((object)_plugin.GetCardCount()); if (AccessTools.Method(((object)cardChoice).GetType(), "ReplaceCards", (Type[])null, (Type[])null).Invoke(cardChoice, new object[2] { null, true }) is IEnumerator enumerator) { ((MonoBehaviour)cardChoice).StartCoroutine(enumerator); UpgradeDraftPlugin.State.MarkRerolledThisPick(); int skipPoints = UpgradeDraftPlugin.State.GetSkipPoints(pickerId); _logger.LogInfo((object)$"Player {pickerId} rerolled card choices. Spent {skipPointsForReroll} skip points (remaining: {skipPoints})."); return true; } _logger.LogWarning((object)"CardChoice.ReplaceCards did not return IEnumerator. Reroll request could not start coroutine."); return false; } catch (Exception ex) { _logger.LogWarning((object)$"Failed to reroll cards for player {pickerId}: {ex.Message}"); return false; } } } public sealed class UpgradeDraftState { private readonly ManualLogSource _logger; private readonly Queue _pendingEntries = new Queue(); private readonly Dictionary _spawnedCardMap = new Dictionary(); private readonly Dictionary _skipPointsByPlayerId = new Dictionary(); private int _currentPickerId = -999; private bool _planReady; private bool _rerolledThisPick; public bool HasPlanForCurrentPick => _planReady; public int CurrentPickerId => _currentPickerId; public bool RerolledThisPick => _rerolledThisPick; public UpgradeDraftState(ManualLogSource logger) { _logger = logger; } public void BeginPick(int pickerId) { if (_currentPickerId != pickerId) { Clear($"Picker changed from {_currentPickerId} to {pickerId}."); } _currentPickerId = pickerId; _rerolledThisPick = false; } public void SetPlannedEntries(IEnumerable entries) { _pendingEntries.Clear(); foreach (DraftCardEntry entry in entries) { _pendingEntries.Enqueue(entry); } _planReady = true; } public bool TryDequeueNext(out DraftCardEntry entry) { entry = null; if (_pendingEntries.Count == 0) { return false; } entry = _pendingEntries.Dequeue(); return true; } public void RegisterSpawnedCard(GameObject spawnedCard, DraftCardEntry entry) { if (spawnedCard != null && entry != null) { _spawnedCardMap[((Object)spawnedCard).GetInstanceID()] = entry; } } public bool TryGetSpawnedEntry(GameObject spawnedCard, out DraftCardEntry entry) { entry = null; if (spawnedCard == null) { return false; } return _spawnedCardMap.TryGetValue(((Object)spawnedCard).GetInstanceID(), out entry); } public void Clear(string reason = "") { if (!string.IsNullOrWhiteSpace(reason)) { _logger.LogDebug((object)("Clearing UpgradeDraft state: " + reason)); } _pendingEntries.Clear(); _spawnedCardMap.Clear(); _planReady = false; _currentPickerId = -999; _rerolledThisPick = false; } public void ClearDraftPlanOnly(string reason = "") { if (!string.IsNullOrWhiteSpace(reason)) { _logger.LogDebug((object)("Clearing draft plan only: " + reason)); } _pendingEntries.Clear(); _spawnedCardMap.Clear(); _planReady = false; } public int GetSkipPoints(int playerId) { if (!_skipPointsByPlayerId.TryGetValue(playerId, out var value)) { return 0; } return value; } public int AddSkipPoint(int playerId) { int num = GetSkipPoints(playerId) + 1; _skipPointsByPlayerId[playerId] = num; return num; } public bool TryConsumeSkipPoints(int playerId, int pointsToConsume) { if (pointsToConsume <= 0) { return true; } int skipPoints = GetSkipPoints(playerId); if (skipPoints < pointsToConsume) { return false; } _skipPointsByPlayerId[playerId] = skipPoints - pointsToConsume; return true; } public void MarkRerolledThisPick() { _rerolledThisPick = true; } } public sealed class UpgradePreviewService { private static readonly Regex NumericRegex = new Regex("([+-]?\\d+(\\.\\d+)?)", RegexOptions.Compiled); private readonly ManualLogSource _logger; private readonly UpgradeDraftPlugin _plugin; public UpgradePreviewService(ManualLogSource logger, UpgradeDraftPlugin plugin) { _logger = logger; _plugin = plugin; } public string BuildUpgradeTitle(DraftCardEntry entry) { return "UPGRADE: " + entry.Card.cardName; } public string BuildUpgradeDescription(DraftCardEntry entry) { StringBuilder stringBuilder = new StringBuilder(); string text = entry.Card.cardDestription ?? string.Empty; if (!string.IsNullOrWhiteSpace(text)) { stringBuilder.AppendLine(text.Trim()); } stringBuilder.AppendLine("Upgrade pick: selecting this card grants another copy of the same card."); stringBuilder.AppendLine($"Owned: {entry.OwnedBefore} -> {entry.OwnedAfter}"); if (_plugin.IsStatPreviewEnabled()) { string value = BuildStackedStatPreview(entry); if (!string.IsNullOrWhiteSpace(value)) { stringBuilder.AppendLine(); stringBuilder.AppendLine(value); } } return stringBuilder.ToString().TrimEnd(); } public string BuildOwnedLine(DraftCardEntry entry) { return $"Owned: {entry.OwnedBefore} -> {entry.OwnedAfter}"; } public string BuildStackedStatPreview(DraftCardEntry entry) { CardInfoStat[] cardStats = entry.Card.cardStats; if (cardStats == null || cardStats.Length == 0) { return "Stack preview: no card stat lines available."; } List list = new List(); CardInfoStat[] array = cardStats; foreach (CardInfoStat val in array) { if (TryBuildStackLine(val, entry.OwnedAfter, out var line)) { list.Add(line); } else { list.Add(val.stat + ": " + val.amount); } } return string.Join("\n", list); } private bool TryBuildStackLine(CardInfoStat stat, int ownedAfter, out string line) { line = null; if (string.IsNullOrWhiteSpace(stat.amount)) { return false; } Match match = NumericRegex.Match(stat.amount); if (!match.Success) { return false; } if (!float.TryParse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return false; } string text = stat.amount.Replace(match.Value, string.Empty).Trim(); float value = result * (float)ownedAfter; if (stat.amount.Contains("%")) { line = stat.stat + ": " + stat.amount + " (" + FormatSigned(value) + "% total from this card after pick)"; return true; } if (!string.IsNullOrWhiteSpace(text)) { line = stat.stat + ": " + stat.amount + " (" + FormatSigned(value) + " " + text + " total from this card after pick)"; return true; } line = stat.stat + ": " + stat.amount + " (" + FormatSigned(value) + " total from this card after pick)"; return true; } private static string FormatSigned(float value) { if (!(value >= 0f)) { return value.ToString("0.##", CultureInfo.InvariantCulture); } return $"+{value:0.##}"; } } } namespace UpgradeDraft.Patches { [HarmonyPatch(typeof(CardChoice), "StartPick")] internal static class CardChoiceStartPickPatch { private static void Prefix(ref int picksToSet, int pickerIDToSet) { UpgradeDraftPlugin instance = UpgradeDraftPlugin.Instance; if (!((Object)(object)instance == (Object)null) && UpgradeDraftPlugin.State != null) { picksToSet = instance.GetCardCount(); UpgradeDraftPlugin.State.Clear("Starting new StartPick flow."); UpgradeDraftPlugin.State.BeginPick(pickerIDToSet); } } } [HarmonyPatch(typeof(CardChoice), "Pick")] internal static class CardChoicePickPatch { private static void Prefix(CardChoice __instance) { if (UpgradeDraftPlugin.State != null) { UpgradeDraftPlugin.State.Clear("Starting new Pick flow."); UpgradeDraftPlugin.State.BeginPick(__instance.pickrID); } } } [HarmonyPatch(typeof(CardChoice), "DoPick")] internal static class CardChoiceDoPickPatch { private static void Prefix(CardChoice __instance, ref int picksToSet, int picketIDToSet) { UpgradeDraftPlugin instance = UpgradeDraftPlugin.Instance; if (!((Object)(object)instance == (Object)null) && UpgradeDraftPlugin.State != null) { picksToSet = instance.GetCardCount(); UpgradeDraftPlugin.State.Clear("Starting new DoPick flow."); UpgradeDraftPlugin.State.BeginPick(picketIDToSet); } } } [HarmonyPatch(typeof(CardChoice), "SpawnUniqueCard")] internal static class CardChoiceSpawnUniqueCardPatch { private static bool Prefix(ref GameObject __result, CardChoice __instance, Vector3 pos, Quaternion rot) { //IL_00c4: 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) UpgradeDraftPlugin instance = UpgradeDraftPlugin.Instance; if ((Object)(object)instance == (Object)null || UpgradeDraftPlugin.State == null) { return true; } try { if (!UpgradeDraftPlugin.AuthorityService.IsAuthoritativeClient()) { if (instance.IsVerboseLoggingEnabled()) { UpgradeDraftPlugin.Log.LogInfo((object)"Non-authoritative client detected; letting vanilla SpawnUniqueCard run."); } return true; } Player val = ResolvePickingPlayer(__instance); if (val == null) { UpgradeDraftPlugin.Log.LogWarning((object)"Could not resolve picking player. Falling back to vanilla SpawnUniqueCard."); return true; } UpgradeDraftPlugin.State.BeginPick(val.playerID); if (!UpgradeDraftPlugin.State.HasPlanForCurrentPick) { BuildPlanForCurrentPick(__instance, val); } if (!UpgradeDraftPlugin.State.TryDequeueNext(out var entry) || entry == null || entry.Card == null) { if (instance.IsVerboseLoggingEnabled()) { UpgradeDraftPlugin.Log.LogInfo((object)"No queued draft entry available; falling back to vanilla SpawnUniqueCard."); } return true; } GameObject val2 = SpawnCardObject(__instance, entry.Card, pos, rot); if (val2 == null) { UpgradeDraftPlugin.Log.LogWarning((object)"Custom spawn returned null. Falling back to vanilla SpawnUniqueCard."); return true; } if (entry.IsUpgrade) { UpgradeDraftPlugin.VisualService.ApplyUpgradeVisual(val2, entry); } UpgradeDraftPlugin.State.RegisterSpawnedCard(val2, entry); __result = val2; return false; } catch (Exception arg) { UpgradeDraftPlugin.Log.LogWarning((object)$"UpgradeDraft SpawnUniqueCard patch failed: {arg}"); return true; } } private static void BuildPlanForCurrentPick(CardChoice cardChoice, Player pickingPlayer) { int count = UpgradeDraftPlugin.DeckService.GetEligibleUpgradeCards(pickingPlayer, UpgradeDraftPlugin.Instance.GetBlacklist()).Count; DraftPlan draftPlan = UpgradeDraftPlugin.RollService.BuildPlan(count); List list = UpgradeDraftPlugin.SelectionService.BuildDraftEntries(cardChoice, pickingPlayer, draftPlan); UpgradeDraftPlugin.State.SetPlannedEntries(list); if (!string.IsNullOrWhiteSpace(draftPlan.FallbackReason)) { UpgradeDraftPlugin.Log.LogWarning((object)draftPlan.FallbackReason); } if (UpgradeDraftPlugin.Instance.IsVerboseLoggingEnabled()) { UpgradeDraftPlugin.Log.LogInfo((object)$"Built draft plan (roll={draftPlan.RollValue}): desiredUpgrades={draftPlan.DesiredUpgradeCount}, actualUpgrades={draftPlan.ActualUpgradeCount}, newCards={draftPlan.NewCardCount}, total={draftPlan.TotalCardCount}, generatedEntries={list.Count}."); } } private static Player ResolvePickingPlayer(CardChoice cardChoice) { try { if (Convert.ToInt32(Traverse.Create((object)cardChoice).Field("pickerType").GetValue()) == 0) { return PlayerManager.instance.GetPlayersInTeam(cardChoice.pickrID).FirstOrDefault(); } if (cardChoice.pickrID >= 0 && cardChoice.pickrID < PlayerManager.instance.players.Count()) { return PlayerManager.instance.players[cardChoice.pickrID]; } } catch (Exception ex) { UpgradeDraftPlugin.Log.LogWarning((object)("Failed to resolve picking player from CardChoice. Error: " + ex.Message)); } return null; } private static GameObject SpawnCardObject(CardChoice cardChoice, CardInfo sourceCard, Vector3 pos, Quaternion rot) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (sourceCard == null) { return null; } try { object? obj = typeof(CardChoice).InvokeMember("Spawn", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, cardChoice, new object[3] { ((Component)sourceCard).gameObject, pos, rot }); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if (val == null) { return null; } CardInfo component = val.GetComponent(); if (component != null) { component.sourceCard = sourceCard; } DisableChoiceCardCollider(val); return val; } catch (Exception ex) { UpgradeDraftPlugin.Log.LogWarning((object)("Failed to spawn custom card object for '" + sourceCard.cardName + "': " + ex.Message)); return null; } } private static void DisableChoiceCardCollider(GameObject spawned) { try { DamagableEvent componentInChildren = spawned.GetComponentInChildren(); if (componentInChildren != null) { Collider2D component = ((Component)componentInChildren).GetComponent(); if (component != null) { ((Behaviour)component).enabled = false; } } } catch (Exception) { } } } } namespace UpgradeDraft.Models { public sealed class DraftCardEntry { public CardInfo Card { get; } public bool IsUpgrade { get; } public int OwnedBefore { get; } public int OwnedAfter { get; } public DraftCardEntry(CardInfo card, bool isUpgrade, int ownedBefore) { Card = card ?? throw new ArgumentNullException("card"); IsUpgrade = isUpgrade; OwnedBefore = ((ownedBefore >= 0) ? ownedBefore : 0); OwnedAfter = (IsUpgrade ? (OwnedBefore + 1) : OwnedBefore); } } public sealed class DraftPlan { public int DesiredUpgradeCount { get; set; } public int ActualUpgradeCount { get; set; } public int NewCardCount { get; set; } public int TotalCardCount { get; set; } public int RollValue { get; set; } public string FallbackReason { get; set; } } }