139 lines
4.7 KiB
C#
139 lines
4.7 KiB
C#
|
|
using FluentAssertions;
|
||
|
|
using Game.CardWars;
|
||
|
|
using OECS;
|
||
|
|
using Xunit;
|
||
|
|
|
||
|
|
namespace Game.CardWars.Tests;
|
||
|
|
|
||
|
|
public class CardEffectTests
|
||
|
|
{
|
||
|
|
private static (World World, SystemGroup Group) Setup()
|
||
|
|
{
|
||
|
|
CardEffectRegistry.ClearForTests();
|
||
|
|
return GameFactory.Create(playerCount: 2, seed: 42);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public void WarriorEffect_AddsHornWhenAnotherWarriorExists()
|
||
|
|
{
|
||
|
|
var (world, group) = Setup();
|
||
|
|
|
||
|
|
var state = world.ReadSingleton<GameState>();
|
||
|
|
var player = PlayCardCommand.FindPlayerByIndex(world, state.CurrentPlayerIndex);
|
||
|
|
|
||
|
|
// Play two warriors.
|
||
|
|
var hand = GameUtil.GetHandCards(world, player);
|
||
|
|
var warrior1 = FindCardWithEffect(world, hand, CardEffect.Warrior);
|
||
|
|
var warrior2 = FindSecondCardWithEffect(world, hand, CardEffect.Warrior);
|
||
|
|
|
||
|
|
if (warrior1 == Entity.Null || warrior2 == Entity.Null) return; // Not enough warriors in deck.
|
||
|
|
|
||
|
|
var def = world.ReadComponent<CardDef>(warrior1);
|
||
|
|
|
||
|
|
world.Commands.Enqueue(new PlayCardCommand
|
||
|
|
{
|
||
|
|
CardEntity = warrior1,
|
||
|
|
Rank = def.Ranks[0],
|
||
|
|
FaceDown = false,
|
||
|
|
TargetPlayer = null
|
||
|
|
});
|
||
|
|
group.RunLogical();
|
||
|
|
|
||
|
|
// First warrior: no other warrior, no horn.
|
||
|
|
GameUtil.GetFieldCards(world, player).Should().Contain(warrior1);
|
||
|
|
world.HasComponent<Horn>(warrior1).Should().BeFalse(because: "no other warrior on field");
|
||
|
|
|
||
|
|
// Play second warrior.
|
||
|
|
var def2 = world.ReadComponent<CardDef>(warrior2);
|
||
|
|
world.Commands.Enqueue(new PlayCardCommand
|
||
|
|
{
|
||
|
|
CardEntity = warrior2,
|
||
|
|
Rank = def2.Ranks[0],
|
||
|
|
FaceDown = false,
|
||
|
|
TargetPlayer = null
|
||
|
|
});
|
||
|
|
group.RunLogical();
|
||
|
|
|
||
|
|
// Second warrior has another warrior, should get horn.
|
||
|
|
// But Wait — the effect checks "other" warriors on field. The first warrior
|
||
|
|
// is on field, so the second warrior should get a horn.
|
||
|
|
world.HasComponent<Horn>(warrior2).Should().BeTrue(because: "another warrior exists on field");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public void MercenaryEffect_SetsPendingChoice()
|
||
|
|
{
|
||
|
|
var (world, group) = Setup();
|
||
|
|
|
||
|
|
var state = world.ReadSingleton<GameState>();
|
||
|
|
var player = PlayCardCommand.FindPlayerByIndex(world, state.CurrentPlayerIndex);
|
||
|
|
|
||
|
|
// Play first mercenary (no effect).
|
||
|
|
var hand = GameUtil.GetHandCards(world, player);
|
||
|
|
var merc = FindCardWithEffect(world, hand, CardEffect.Mercenary);
|
||
|
|
if (merc == Entity.Null) return;
|
||
|
|
|
||
|
|
var def = world.ReadComponent<CardDef>(merc);
|
||
|
|
world.Commands.Enqueue(new PlayCardCommand
|
||
|
|
{
|
||
|
|
CardEntity = merc,
|
||
|
|
Rank = def.Ranks[0],
|
||
|
|
FaceDown = false,
|
||
|
|
TargetPlayer = null
|
||
|
|
});
|
||
|
|
group.RunLogical();
|
||
|
|
|
||
|
|
// Play second mercenary. We need another one from a drawn card.
|
||
|
|
var publicDeck = GameUtil.FindEntity<PublicDeck>(world);
|
||
|
|
GameUtil.DrawCard(world, publicDeck, player);
|
||
|
|
var newHand = GameUtil.GetHandCards(world, player);
|
||
|
|
var merc2 = FindCardWithEffect(world, newHand, CardEffect.Mercenary);
|
||
|
|
if (merc2 == Entity.Null) return;
|
||
|
|
|
||
|
|
var def2 = world.ReadComponent<CardDef>(merc2);
|
||
|
|
world.Commands.Enqueue(new PlayCardCommand
|
||
|
|
{
|
||
|
|
CardEntity = merc2,
|
||
|
|
Rank = def2.Ranks[0],
|
||
|
|
FaceDown = false,
|
||
|
|
TargetPlayer = null
|
||
|
|
});
|
||
|
|
group.RunLogical();
|
||
|
|
|
||
|
|
// Should have a pending Mercenary choice.
|
||
|
|
world.HasSingleton<PendingMercenary>().Should().BeTrue();
|
||
|
|
var pending = world.ReadSingleton<PendingMercenary>();
|
||
|
|
pending.CardEntity.Should().Be(merc2);
|
||
|
|
|
||
|
|
// Resolve the choice (pick a target).
|
||
|
|
world.Commands.Enqueue(new ResolveChoiceCommand { Target = merc });
|
||
|
|
group.RunLogical();
|
||
|
|
|
||
|
|
// The pending should be cleared.
|
||
|
|
world.HasSingleton<PendingMercenary>().Should().BeFalse();
|
||
|
|
}
|
||
|
|
|
||
|
|
private static Entity FindCardWithEffect(World world, List<Entity> cards, CardEffect effect)
|
||
|
|
{
|
||
|
|
foreach (var c in cards)
|
||
|
|
{
|
||
|
|
if (world.HasComponent<CardDef>(c) && world.ReadComponent<CardDef>(c).Effect == effect)
|
||
|
|
return c;
|
||
|
|
}
|
||
|
|
return Entity.Null;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static Entity FindSecondCardWithEffect(World world, List<Entity> cards, CardEffect effect)
|
||
|
|
{
|
||
|
|
Entity first = Entity.Null;
|
||
|
|
foreach (var c in cards)
|
||
|
|
{
|
||
|
|
if (world.HasComponent<CardDef>(c) && world.ReadComponent<CardDef>(c).Effect == effect)
|
||
|
|
{
|
||
|
|
if (first == Entity.Null) first = c;
|
||
|
|
else return c;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return Entity.Null;
|
||
|
|
}
|
||
|
|
}
|