using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using MegaCrit.Sts2.Core.Combat;
using MegaCrit.Sts2.Core.Entities.Players;
using MegaCrit.Sts2.Core.Models;
namespace VulnerabilityIndicator.Scripts;
///
/// Core logic for detecting whether a player has a card in hand
/// that can inflict Vulnerability on an enemy.
///
public sealed class VulnerabilityChecker
{
private static VulnerabilityChecker? _instance;
public static VulnerabilityChecker? Instance => _instance;
private readonly Dictionary _playerHasVulnCard = new();
private CombatState? _currentCombat;
///
/// Gets or creates the singleton instance.
///
public static VulnerabilityChecker GetOrCreate()
{
if (_instance == null)
_instance = new VulnerabilityChecker();
return _instance;
}
private VulnerabilityChecker()
{
DiscoverVulnerabilityTypes();
}
// Cached types discovered via reflection
private Type? _vulnerabilityPowerType;
private Type? _applyStatusCmdType;
private Type? _damageCmdType;
private void DiscoverVulnerabilityTypes()
{
var assembly = Assembly.GetAssembly(typeof(CombatState));
if (assembly == null) return;
var types = assembly.GetTypes();
// Look for Vulnerability power type
_vulnerabilityPowerType = types.FirstOrDefault(t => t.Name.Contains("Vulnerability")
&& t.FullName != null && (t.FullName.Contains("Powers") || t.FullName.Contains("Power")));
_applyStatusCmdType = types.FirstOrDefault(t => t.Name.Contains("ApplyStatus")
&& t.Name.Contains("Cmd"));
_damageCmdType = types.FirstOrDefault(t => t.Name.Contains("Damage")
&& t.Name.Contains("Cmd"));
}
///
/// Initializes the checker for a new combat.
///
public void Initialize(CombatState combat)
{
_currentCombat = combat;
_playerHasVulnCard.Clear();
foreach (var player in GetAllPlayers())
{
_playerHasVulnCard[player] = false;
}
OnCardChanged();
}
///
/// Called when cards change in hand (draw, discard, play).
/// Re-evaluates all players.
///
public void OnCardChanged()
{
if (_currentCombat == null) return;
foreach (var player in GetAllPlayers())
{
if (!_playerHasVulnCard.ContainsKey(player)) continue;
bool hadVuln = _playerHasVulnCard.GetValueOrDefault(player, false);
bool nowHasVuln = PlayerHasVulnerabilityCard(player);
if (hadVuln != nowHasVuln)
{
_playerHasVulnCard[player] = nowHasVuln;
OnPlayerVulnerabilityChanged?.Invoke(player, nowHasVuln);
}
}
}
///
/// Fired when a player's vulnerability card status changes.
///
public event Action? OnPlayerVulnerabilityChanged;
///
/// Returns true if the given player has at least one card in hand
/// that can inflict Vulnerability.
///
public bool PlayerHasVulnerabilityCard(Player player)
{
var hand = GetHandForPlayer(player);
if (hand == null || hand.Count == 0) return false;
foreach (var card in hand)
{
if (CardCanApplyVulnerability(card))
return true;
}
return false;
}
///
/// Gets all players in the current combat.
///
public IEnumerable GetAllPlayers()
{
if (_currentCombat == null) return Array.Empty();
var combatType = _currentCombat.GetType();
List result = new();
// Try Players property
foreach (var propName in new[] { "Players", "CombatPlayers" })
{
var prop = combatType.GetProperty(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (prop != null)
{
try
{
var value = prop.GetValue(_currentCombat);
if (value is IEnumerable players)
{
foreach (var player in players)
result.Add(player);
return result;
}
if (value is IEnumerable