oecs-sharp/Game.CardWars/CardEffects.cs

402 lines
13 KiB
C#

using OECS;
namespace Game.CardWars;
// ────────────────────────────────────────────────────────────
// Simple effects that resolve immediately.
// ────────────────────────────────────────────────────────────
public class WarriorEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Warrior;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
if (HasOtherOnField(world, owner, card, CardEffect.Warrior))
CardEffectHelpers.AddHorn(world, card);
return true;
}
public bool ResolveChoice(World world, Entity target) => true;
private static bool HasOtherOnField(World world, Entity player, Entity self, CardEffect effect)
{
return GameUtil.GetFieldCards(world, player)
.Any(c => c != self && world.HasComponent<CardDef>(c)
&& world.ReadComponent<CardDef>(c).Effect == effect);
}
}
public class ArcherEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Archer;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
if (HasOtherOnField(world, owner, card, CardEffect.Archer))
{
var banner = CardEffectHelpers.GetBanner(world, owner);
if (banner != Entity.Null) CardEffectHelpers.AddHorn(world, banner);
}
return true;
}
public bool ResolveChoice(World world, Entity target) => true;
private static bool HasOtherOnField(World world, Entity player, Entity self, CardEffect effect)
{
return GameUtil.GetFieldCards(world, player)
.Any(c => c != self && world.HasComponent<CardDef>(c)
&& world.ReadComponent<CardDef>(c).Effect == effect);
}
}
public class MercenaryEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Mercenary;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
if (!HasOtherOnField(world, owner, card, CardEffect.Mercenary))
return true;
world.SetSingleton(new PendingMercenary { CardEntity = card, Player = player });
return false;
}
public bool ResolveChoice(World world, Entity target)
{
var pending = world.ReadSingleton<PendingMercenary>();
if (target != Entity.Null)
CardEffectHelpers.AddSkull(world, target);
world.RemoveSingleton<PendingMercenary>();
return true;
}
private static bool HasOtherOnField(World world, Entity player, Entity self, CardEffect effect)
{
return GameUtil.GetFieldCards(world, player)
.Any(c => c != self && world.HasComponent<CardDef>(c)
&& world.ReadComponent<CardDef>(c).Effect == effect);
}
}
public class MerchantEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Merchant;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
world.Commands.Enqueue(new DrawCardCommand { Player = player, Kind = null });
world.Commands.Enqueue(new ExtraActionCommand());
return true;
}
public bool ResolveChoice(World world, Entity target) => true;
}
public class DancerEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Dancer;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
var powered = GameUtil.GetFieldCards(world, owner)
.Where(c => world.HasComponent<Card>(c) && world.ReadComponent<Card>(c).Rank > 0)
.ToList();
if (powered.Count == 0) return true;
if (powered.Count <= 2)
{
foreach (var c in powered)
CardEffectHelpers.AddHorn(world, c);
return true;
}
world.SetSingleton(new PendingDancer { CardEntity = card, Player = player, Step = 0 });
return false;
}
public bool ResolveChoice(World world, Entity target)
{
var pending = world.ReadSingleton<PendingDancer>();
if (target != Entity.Null)
CardEffectHelpers.AddHorn(world, target);
if (pending.Step == 0)
{
world.SetSingleton(new PendingDancer { CardEntity = pending.CardEntity, Player = pending.Player, Step = 1 });
return false;
}
world.RemoveSingleton<PendingDancer>();
return true;
}
}
public class PaladinEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Paladin;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
world.SetSingleton(new PendingPaladin { CardEntity = card, Player = player });
return false;
}
public bool ResolveChoice(World world, Entity target)
{
if (target != Entity.Null)
CardEffectHelpers.AddHorn(world, target);
world.RemoveSingleton<PendingPaladin>();
return true;
}
}
public class PrincessEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Princess;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
world.Commands.Enqueue(new ReplaceCastleCommand());
return true;
}
public bool ResolveChoice(World world, Entity target) => true;
}
public class DuelistEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Duelist;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
world.Commands.Enqueue(new EndPlayPhaseCommand());
return true;
}
public bool ResolveChoice(World world, Entity target) => true;
}
public class NunEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Nun;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
var fieldCards = GameUtil.GetFieldCards(world, owner);
if (fieldCards.Count == 0)
{
world.Commands.Enqueue(new DrawCardCommand { Player = player, Kind = null });
return true;
}
world.SetSingleton(new PendingNun { CardEntity = card, Player = player });
return false;
}
public bool ResolveChoice(World world, Entity target)
{
var pending = world.ReadSingleton<PendingNun>();
if (target != Entity.Null)
{
var discardDeck = CardEffectHelpers.GetDiscardDeck(world, pending.Player);
world.RemoveComponent<OnField>(target);
if (world.HasComponent<Card>(target))
world.RemoveComponent<Card>(target);
world.AddComponent(target, new InDeck { Source = target, Target = discardDeck });
}
world.Commands.Enqueue(new DrawCardCommand { Player = pending.Player, Kind = null });
world.RemoveSingleton<PendingNun>();
return true;
}
}
public class ScoutEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Scout;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
world.SetSingleton(new PendingScout { CardEntity = card, Player = player });
return false;
}
public bool ResolveChoice(World world, Entity target)
{
var pending = world.ReadSingleton<PendingScout>();
if (target != Entity.Null && world.HasComponent<Player>(target))
{
foreach (var c in GameUtil.GetFieldCards(world, target))
{
if (world.TryGetComponent<Card>(c, out var cd) && cd.FaceDown)
{
ref var cardRef = ref world.GetComponent<Card>(c);
cardRef.FaceDown = false;
world.MarkModified<Card>(c);
}
}
}
world.RemoveSingleton<PendingScout>();
return true;
}
}
public class WitchEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Witch;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
var banner = CardEffectHelpers.GetBanner(world, owner);
if (banner != Entity.Null)
CardEffectHelpers.AddSkull(world, banner);
return true;
}
public bool ResolveChoice(World world, Entity target) => true;
}
public class ThiefEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Thief;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
return true;
}
public bool ResolveChoice(World world, Entity target) => true;
}
// ────────────────────────────────────────────────────────────
// Face-down flip effects
// ────────────────────────────────────────────────────────────
public class PegasusEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Pegasus;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
world.SetSingleton(new PendingPegasus { CardEntity = card, Player = player });
return false;
}
public bool ResolveChoice(World world, Entity target)
{
var pending = world.ReadSingleton<PendingPegasus>();
if (target != Entity.Null && target != pending.CardEntity)
{
world.RemoveComponent<OnField>(target);
if (world.HasComponent<Card>(target))
world.RemoveComponent<Card>(target);
world.AddComponent(target, new HeldBy { Source = target, Target = pending.Player });
}
world.RemoveSingleton<PendingPegasus>();
return true;
}
}
public class CurseMasterEffect : ICardEffect
{
public CardEffect Effect => CardEffect.CurseMaster;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
world.SetSingleton(new PendingCurseMaster { CardEntity = card, Player = player });
return false;
}
public bool ResolveChoice(World world, Entity target)
{
var pending = world.ReadSingleton<PendingCurseMaster>();
if (target != Entity.Null && world.HasComponent<Horn>(target))
{
var placedTargets = world.GetSources<PlacedOn>(target).ToList();
world.DestroyEntity(target);
foreach (var pt in placedTargets)
{
var skull = world.CreateEntity();
world.AddComponent(skull, new Skull());
world.AddComponent(skull, new PlacedOn { Source = skull, Target = pt });
}
}
world.RemoveSingleton<PendingCurseMaster>();
return true;
}
}
public class MageEffect : ICardEffect
{
public CardEffect Effect => CardEffect.Mage;
public bool Resolve(World world, Entity card, Entity player, Entity owner)
{
Entity best = Entity.Null;
int bestRank = int.MinValue;
foreach (var p in GameUtil.FindAllEntities<Player>(world))
{
foreach (var c in GameUtil.GetFieldCards(world, p))
{
if (world.TryGetComponent<Card>(c, out var cd) && !cd.FaceDown && cd.Rank > bestRank)
{
bestRank = cd.Rank;
best = c;
}
}
}
if (best != Entity.Null)
CardEffectHelpers.AddSkull(world, best);
return true;
}
public bool ResolveChoice(World world, Entity target) => true;
}
// ────────────────────────────────────────────────────────────
// Shared helpers
// ────────────────────────────────────────────────────────────
public static class CardEffectHelpers
{
public static void AddHorn(World world, Entity target)
{
var horn = world.CreateEntity();
world.AddComponent(horn, new Horn());
world.AddComponent(horn, new PlacedOn { Source = horn, Target = target });
}
public static void AddSkull(World world, Entity target)
{
var skull = world.CreateEntity();
world.AddComponent(skull, new Skull());
world.AddComponent(skull, new PlacedOn { Source = skull, Target = target });
}
public static Entity GetBanner(World world, Entity player)
{
using var iter = world.Select<Banner>();
while (iter.MoveNext())
{
if (iter.CurrentEntity == World.SingletonEntity) continue;
var onField = world.GetSources<OnField>(player);
if (onField.Contains(iter.CurrentEntity))
return iter.CurrentEntity;
}
return Entity.Null;
}
public static Entity GetDiscardDeck(World world, Entity player)
{
using var iter = world.Select<FactionDeck>();
while (iter.MoveNext())
{
if (iter.CurrentEntity != World.SingletonEntity)
return iter.CurrentEntity;
}
return Entity.Null;
}
}