402 lines
13 KiB
C#
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 { 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 { 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 { 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 { Target = target });
|
|
}
|
|
|
|
public static void AddSkull(World world, Entity target)
|
|
{
|
|
var skull = world.CreateEntity();
|
|
world.AddComponent(skull, new Skull());
|
|
world.AddComponent(skull, new PlacedOn { 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;
|
|
}
|
|
} |