96 lines
3.2 KiB
C#
96 lines
3.2 KiB
C#
|
|
using OECS;
|
||
|
|
|
||
|
|
namespace Game.CardWars;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// A card effect that can be registered with the effect dispatcher.
|
||
|
|
/// </summary>
|
||
|
|
public interface ICardEffect
|
||
|
|
{
|
||
|
|
CardEffect Effect { get; }
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Called when the card is played face-up or flipped face-up.
|
||
|
|
/// Return true if fully resolved. Return false if a pending component
|
||
|
|
/// was set and the effect needs a player command to continue.
|
||
|
|
/// </summary>
|
||
|
|
bool Resolve(World world, Entity card, Entity player, Entity owner);
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Called when the player provides a choice to continue a pending effect.
|
||
|
|
/// The effect handler is responsible for reading the correct pending
|
||
|
|
/// component type from the singleton and removing it when done.
|
||
|
|
/// </summary>
|
||
|
|
bool ResolveChoice(World world, Entity target);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Registry of card effects, ordered by registration.
|
||
|
|
/// </summary>
|
||
|
|
public static class CardEffectRegistry
|
||
|
|
{
|
||
|
|
private static readonly List<ICardEffect> _effects = new();
|
||
|
|
private static bool _frozen;
|
||
|
|
|
||
|
|
public static void Add<TEffect>() where TEffect : ICardEffect, new()
|
||
|
|
{
|
||
|
|
if (_frozen)
|
||
|
|
throw new InvalidOperationException("Cannot add effects after the registry is frozen.");
|
||
|
|
_effects.Add(new TEffect());
|
||
|
|
}
|
||
|
|
|
||
|
|
public static void Freeze() => _frozen = true;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Resolve a card effect. Returns true if fully resolved.
|
||
|
|
/// </summary>
|
||
|
|
public static bool Dispatch(World world, CardEffect effect, Entity card, Entity player, Entity owner)
|
||
|
|
{
|
||
|
|
foreach (var e in _effects)
|
||
|
|
{
|
||
|
|
if (e.Effect == effect)
|
||
|
|
return e.Resolve(world, card, player, owner);
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Look up the handler for the currently pending effect and resolve it.
|
||
|
|
/// </summary>
|
||
|
|
public static bool DispatchChoice(World world, Entity target)
|
||
|
|
{
|
||
|
|
// Find which pending component is active and dispatch to the matching handler.
|
||
|
|
if (world.HasSingleton<PendingMercenary>())
|
||
|
|
return DispatchByEffect(world, CardEffect.Mercenary, target);
|
||
|
|
if (world.HasSingleton<PendingDancer>())
|
||
|
|
return DispatchByEffect(world, CardEffect.Dancer, target);
|
||
|
|
if (world.HasSingleton<PendingPaladin>())
|
||
|
|
return DispatchByEffect(world, CardEffect.Paladin, target);
|
||
|
|
if (world.HasSingleton<PendingNun>())
|
||
|
|
return DispatchByEffect(world, CardEffect.Nun, target);
|
||
|
|
if (world.HasSingleton<PendingScout>())
|
||
|
|
return DispatchByEffect(world, CardEffect.Scout, target);
|
||
|
|
if (world.HasSingleton<PendingPegasus>())
|
||
|
|
return DispatchByEffect(world, CardEffect.Pegasus, target);
|
||
|
|
if (world.HasSingleton<PendingCurseMaster>())
|
||
|
|
return DispatchByEffect(world, CardEffect.CurseMaster, target);
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static bool DispatchByEffect(World world, CardEffect effect, Entity target)
|
||
|
|
{
|
||
|
|
foreach (var e in _effects)
|
||
|
|
{
|
||
|
|
if (e.Effect == effect)
|
||
|
|
return e.ResolveChoice(world, target);
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static void ClearForTests()
|
||
|
|
{
|
||
|
|
_effects.Clear();
|
||
|
|
_frozen = false;
|
||
|
|
}
|
||
|
|
}
|