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<int> NormalCardCount { get; private set; }
internal ConfigEntry<int> ChanceZeroUpgrades { get; private set; }
internal ConfigEntry<int> ChanceOneUpgrade { get; private set; }
internal ConfigEntry<int> ChanceTwoUpgrades { get; private set; }
internal ConfigEntry<int> ChanceThreeUpgrades { get; private set; }
internal ConfigEntry<bool> EnableUpgradeVisuals { get; private set; }
internal ConfigEntry<bool> EnableStatPreview { get; private set; }
internal ConfigEntry<string> UpgradeBlacklist { get; private set; }
internal ConfigEntry<bool> VerboseLogging { get; private set; }
internal ConfigEntry<bool> EnableSkipForPoint { get; private set; }
internal ConfigEntry<int> SkipPointsForReroll { get; private set; }
internal ConfigEntry<bool> EnableSkipRerollOverlay { get; private set; }
internal ConfigEntry<bool> 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<SkipRerollOverlay>() == (Object)null)
{
((Component)this).gameObject.AddComponent<SkipRerollOverlay>();
}
_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<string> GetBlacklist()
{
return new HashSet<string>(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<int>("General", "NormalCardCount", 4, "Number of visible card choices per draft pick. Values other than 4 are experimental.");
ChanceZeroUpgrades = ((BaseUnityPlugin)this).Config.Bind<int>("Chances", "ChanceZeroUpgrades", 25, "Chance weight for showing 0 upgrade cards.");
ChanceOneUpgrade = ((BaseUnityPlugin)this).Config.Bind<int>("Chances", "ChanceOneUpgrade", 50, "Chance weight for showing 1 upgrade card.");
ChanceTwoUpgrades = ((BaseUnityPlugin)this).Config.Bind<int>("Chances", "ChanceTwoUpgrades", 20, "Chance weight for showing 2 upgrade cards.");
ChanceThreeUpgrades = ((BaseUnityPlugin)this).Config.Bind<int>("Chances", "ChanceThreeUpgrades", 5, "Chance weight for showing 3 upgrade cards.");
EnableUpgradeVisuals = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "EnableUpgradeVisuals", true, "Adds UPGRADE visuals and ownership info to upgrade cards.");
EnableStatPreview = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "EnableStatPreview", true, "Appends conservative stacked stat preview lines for upgrade cards.");
EnableSkipRerollOverlay = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "EnableSkipRerollOverlay", true, "Show skip/reroll buttons during card pick.");
UpgradeBlacklist = ((BaseUnityPlugin)this).Config.Bind<string>("Rules", "UpgradeBlacklist", string.Empty, "Comma-separated card names that are not allowed as upgrades.");
EnableSkipForPoint = ((BaseUnityPlugin)this).Config.Bind<bool>("Rules", "EnableSkipForPoint", true, "Allow players to skip card pick and gain one skip point.");
SkipPointsForReroll = ((BaseUnityPlugin)this).Config.Bind<int>("Rules", "SkipPointsForReroll", 2, "Skip points required (and consumed) to reroll cards.");
AllowMultipleRerollsPerPick = ((BaseUnityPlugin)this).Config.Bind<bool>("Rules", "AllowMultipleRerollsPerPick", false, "Allow more than one reroll during a single card pick when player has enough points.");
VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("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<UpgradeCardMarker>() ?? spawnedCard.AddComponent<UpgradeCardMarker>();
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<Component> list = (from c in spawnedCard.GetComponentsInChildren<Component>(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<DraftCardEntry> BuildDraftEntries(CardChoice cardChoice, Player player, DraftPlan plan)
{
HashSet<string> blacklist = _plugin.GetBlacklist();
List<CardInfo> eligibleUpgradeCards = _deckService.GetEligibleUpgradeCards(player, blacklist);
HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
List<DraftCardEntry> list = new List<DraftCardEntry>();
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<CardInfo> 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<CardInfo> SelectNewCards(CardChoice cardChoice, Player player, int wanted, ISet<string> existingCardIds, bool strictUnique, bool strictNotOwned)
{
List<CardInfo> list = new List<CardInfo>();
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<CardInfo, Player, Gun, GunAmmo, CharacterData, HealthHandler, Gravity, Block, CharacterStatModifiers, bool> BuildSelectionCondition(CardChoice cardChoice, Player player, ISet<string> selectedIds, IEnumerable<CardInfo> localNewCards, bool strictUnique, bool strictNotOwned)
{
HashSet<string> localIds = new HashSet<string>(localNewCards.Where((CardInfo c) => c != null).Select(_deckService.GetStableCardId), StringComparer.Ordinal);
Func<CardInfo, Player, bool> 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<CardInfo, Player, bool> 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>();
Gun component2 = ((Component)card).GetComponent<Gun>();
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<CardInfo> PickRandomDistinct(List<CardInfo> cards, int amount)
{
List<CardInfo> list = new List<CardInfo>();
if (cards == null || cards.Count == 0 || amount <= 0)
{
return list;
}
List<CardInfo> 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<CardInfo> GetOwnedCards(Player player)
{
if (player == null)
{
return new List<CardInfo>();
}
try
{
object fieldOrPropertyValue = ReflectionUtils.GetFieldOrPropertyValue(player, "data");
if (fieldOrPropertyValue == null)
{
return new List<CardInfo>();
}
List<CardInfo> list = ReflectionUtils.ToTypedList<CardInfo>(ReflectionUtils.GetFieldOrPropertyValue(fieldOrPropertyValue, "currentCards"));
if (list.Count > 0)
{
return list;
}
list = ReflectionUtils.ToTypedList<CardInfo>(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<CardInfo>();
}
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<CardInfo> GetEligibleUpgradeCards(Player player, ISet<string> blacklistLower)
{
List<CardInfo> ownedCards = GetOwnedCards(player);
HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
List<CardInfo> list = new List<CardInfo>();
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<string> 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<string> 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<T> ToTypedList<T>(object maybeList) where T : class
{
if (maybeList == null)
{
return new List<T>();
}
if (maybeList is IList<T> source)
{
return source.Where((T item) => item != null).ToList();
}
if (maybeList is IEnumerable enumerable)
{
List<T> list = new List<T>();
{
foreach (object item3 in enumerable)
{
if (item3 is T item2)
{
list.Add(item2);
}
}
return list;
}
}
return new List<T>();
}
}
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<DraftCardEntry> _pendingEntries = new Queue<DraftCardEntry>();
private readonly Dictionary<int, DraftCardEntry> _spawnedCardMap = new Dictionary<int, DraftCardEntry>();
private readonly Dictionary<int, int> _skipPointsByPlayerId = new Dictionary<int, int>();
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<DraftCardEntry> 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<string> list = new List<string>();
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<DraftCardEntry> 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<CardInfo>();
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<DamagableEvent>();
if (componentInChildren != null)
{
Collider2D component = ((Component)componentInChildren).GetComponent<Collider2D>();
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; }
}
}