diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-dwarven.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-dwarven.hjson index 7d8868c52..27ab6565c 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-dwarven.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-dwarven.hjson @@ -136,11 +136,24 @@ against: bearer memorize: losingMinion } - effect: { - type: discardTopCardsFromDeck - deck: ownerFromMemory(losingMinion) - forced: true - } + effect: [ + { + type: conditional + requires: { + type: perPhaseLimit + limit: 1 + } + effect: { + type: discardTopCardsFromDeck + deck: ownerFromMemory(losingMinion) + forced: true + } + } + { + type: incrementPerPhaseLimit + limit: 1 + } + ] } } 1_10: { diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Dwarven-errata.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Dwarven-errata.hjson index e443fd87d..9f3b2133f 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Dwarven-errata.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Dwarven-errata.hjson @@ -90,7 +90,7 @@ } effect: { type: choice - player: fp + player: OwnerFromMemory(losingMinion) texts: [ Exert a minion Discard the bottom card of your draw deck @@ -102,7 +102,7 @@ } { type: discardBottomCardsFromDeck - deck: ownerFromMemory(losingMinion) + deck: OwnerFromMemory(losingMinion) forced: false } ] diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Elven-errata.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Elven-errata.hjson index 995949932..0127f2026 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Elven-errata.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Elven-errata.hjson @@ -64,12 +64,13 @@ type: preventable player: ownerFromMemory(losingMinion) cost: { - type: discardCardAtRandomFromHand + type: DiscardFromHand + player: ownerFromMemory(losingMinion) hand: ownerFromMemory(losingMinion) - count: 1 + count: 2 forced: false } - text: Discard 1 card at random from hand to prevent healing {chosenElves} + text: Discard 2 cards from hand to prevent healing {chosenElves} effect: { type: heal filter: memory(chosenElves) diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_009_Tests.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_009_Tests.java new file mode 100644 index 000000000..cc10677bf --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_009_Tests.java @@ -0,0 +1,160 @@ +package com.gempukku.lotro.cards.official.set01; + +import com.gempukku.lotro.cards.GenericCardTestHelper; +import com.gempukku.lotro.common.*; +import com.gempukku.lotro.game.CardNotFoundException; +import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException; +import org.junit.Test; + +import java.util.HashMap; + +import static org.junit.Assert.*; + +public class Card_01_009_Tests +{ + + protected GenericCardTestHelper GetScenario() throws CardNotFoundException, DecisionResultInvalidException { + return new GenericCardTestHelper( + new HashMap() + {{ + put("axe", "1_9"); + put("gimli", "1_13"); + + put("runner", "1_178"); + put("runner2", "1_178"); + }}, + GenericCardTestHelper.FellowshipSites, + GenericCardTestHelper.FOTRFrodo, + GenericCardTestHelper.FOTRRing + ); + } + + @Test + public void DwarvenAxeStatsAndKeywordsAreCorrect() throws DecisionResultInvalidException, CardNotFoundException { + + /** + * Set: 1 + * Title: Dwarven Axe + * Unique: False + * Side: FREE_PEOPLE + * Culture: Dwarven + * Twilight Cost: 0 + * Type: possession + * Subtype: Hand Weapon + * Strength: 2 + * Game Text: Bearer must be a Dwarf.
Each time a minion loses a skirmish to bearer, that minion's owner must exert a minion or discard the bottom card of their draw deck. + */ + + //Pre-game setup + var scn = GetScenario(); + + var card = scn.GetFreepsCard("axe"); + + assertFalse(card.getBlueprint().isUnique()); + assertEquals(Side.FREE_PEOPLE, card.getBlueprint().getSide()); + assertEquals(Culture.DWARVEN, card.getBlueprint().getCulture()); + assertEquals(CardType.POSSESSION, card.getBlueprint().getCardType()); + assertTrue(card.getBlueprint().getPossessionClasses().contains(PossessionClass.HAND_WEAPON)); + assertEquals(0, card.getBlueprint().getTwilightCost()); + assertEquals(2, card.getBlueprint().getStrength()); + } + + @Test + public void AxeMustBeBorneByDwarf() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + var scn = GetScenario(); + + var axe = scn.GetFreepsCard("axe"); + var gimli = scn.GetFreepsCard("gimli"); + scn.FreepsMoveCardToHand(axe, gimli); + + scn.StartGame(); + assertFalse(scn.FreepsPlayAvailable(axe)); + scn.FreepsPlayCard(gimli); + assertTrue(scn.FreepsPlayAvailable(axe)); + } + + @Test + public void AxeCausesDeckDiscardOnSkirmishVictory() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + var scn = GetScenario(); + + var axe = scn.GetFreepsCard("axe"); + var gimli = scn.GetFreepsCard("gimli"); + scn.FreepsMoveCharToTable(gimli); + scn.AttachCardsTo(gimli, axe); + + var runner = scn.GetShadowCard("runner"); + scn.ShadowMoveCharToTable(runner); + + scn.StartGame(); + + scn.SkipToPhase(Phase.ASSIGNMENT); + + scn.PassCurrentPhaseActions(); + scn.FreepsAssignToMinions(gimli, runner); + + scn.FreepsResolveSkirmish(gimli); + scn.PassCurrentPhaseActions(); + + var topcard = scn.GetShadowTopOfDeck(); + assertEquals(Zone.DECK, topcard.getZone()); + assertEquals(3, scn.GetShadowDeckCount()); + assertEquals(0, scn.GetShadowDiscardCount()); + + scn.FreepsResolveActionOrder("Axe"); + assertEquals(Zone.DISCARD, topcard.getZone()); + assertEquals(2, scn.GetShadowDeckCount()); + assertEquals(2, scn.GetShadowDiscardCount());//runner, and a card discarded from the deck + + } + + @Test + public void AxeAffectsMaxOneMinionDuringSkirmish() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + var scn = GetScenario(); + + var axe = scn.GetFreepsCard("axe"); + var gimli = scn.GetFreepsCard("gimli"); + scn.FreepsMoveCharToTable(gimli); + scn.AttachCardsTo(gimli, axe); + + var runner = scn.GetShadowCard("runner"); + var runner2 = scn.GetShadowCard("runner2"); + scn.ShadowMoveCharToTable(runner, runner2); + + scn.StartGame(); + + scn.SkipToPhase(Phase.ASSIGNMENT); + + scn.PassCurrentPhaseActions(); + scn.FreepsDeclineAssignments(); + scn.ShadowAssignToMinions(gimli, runner, runner2); + + scn.FreepsResolveSkirmish(gimli); + scn.FreepsUseCardAction(gimli); + scn.ShadowPassCurrentPhaseAction(); + scn.FreepsUseCardAction(gimli); + scn.ShadowPassCurrentPhaseAction(); + scn.FreepsPassCurrentPhaseAction(); + + //Should only have "resolve skirmish wounds" and one instance of "dwarven axe required trigger". + // If there are two axe-triggers, then the card is violating the latest Decipher "clarification": + // + //"This card can trigger only once for each Shadow player with a minion in that skirmish, + // regardless of how many minions that player had." + assertEquals(3, scn.FreepsGetAvailableActions().size()); + + var topcard = scn.GetShadowTopOfDeck(); + assertEquals(Zone.DECK, topcard.getZone()); + assertEquals(2, scn.GetShadowDeckCount()); + assertEquals(0, scn.GetShadowDiscardCount()); + + //Ideally we shouldn't have both triggers, but at least we can squelch the results of the second + scn.FreepsResolveActionOrder("Axe"); + scn.FreepsResolveActionOrder("Axe"); + assertEquals(Zone.DISCARD, topcard.getZone()); + assertEquals(1, scn.GetShadowDeckCount()); + assertEquals(3, scn.GetShadowDiscardCount());//2 dead runners, and a card discarded from the deck + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set01/Card_01_009_ErrataTests.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set01/Card_01_009_ErrataTests.java new file mode 100644 index 000000000..4985f3688 --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set01/Card_01_009_ErrataTests.java @@ -0,0 +1,172 @@ +package com.gempukku.lotro.cards.unofficial.pc.errata.set01; + +import com.gempukku.lotro.cards.GenericCardTestHelper; +import com.gempukku.lotro.common.*; +import com.gempukku.lotro.game.CardNotFoundException; +import com.gempukku.lotro.game.PhysicalCardImpl; +import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException; +import com.gempukku.lotro.logic.modifiers.KeywordModifier; +import org.junit.Test; + +import java.util.HashMap; + +import static org.junit.Assert.*; + +public class Card_01_009_ErrataTests +{ + + protected GenericCardTestHelper GetScenario() throws CardNotFoundException, DecisionResultInvalidException { + return new GenericCardTestHelper( + new HashMap() + {{ + put("axe", "71_9"); + put("gimli", "1_13"); + + put("scout", "1_191"); + put("runner", "1_178"); + }}, + GenericCardTestHelper.FellowshipSites, + GenericCardTestHelper.FOTRFrodo, + GenericCardTestHelper.FOTRRing + ); + } + + @Test + public void DwarvenAxeStatsAndKeywordsAreCorrect() throws DecisionResultInvalidException, CardNotFoundException { + + /** + * Set: 1 + * Title: Dwarven Axe + * Unique: False + * Side: FREE_PEOPLE + * Culture: Dwarven + * Twilight Cost: 0 + * Type: possession + * Subtype: Hand Weapon + * Strength: 2 + * Game Text: Bearer must be a Dwarf.
Each time a minion loses a skirmish to bearer, that minion's owner must exert a minion or discard the bottom card of their draw deck. + */ + + //Pre-game setup + var scn = GetScenario(); + + var card = scn.GetFreepsCard("axe"); + + assertFalse(card.getBlueprint().isUnique()); + assertEquals(Side.FREE_PEOPLE, card.getBlueprint().getSide()); + assertEquals(Culture.DWARVEN, card.getBlueprint().getCulture()); + assertEquals(CardType.POSSESSION, card.getBlueprint().getCardType()); + assertTrue(card.getBlueprint().getPossessionClasses().contains(PossessionClass.HAND_WEAPON)); + assertEquals(0, card.getBlueprint().getTwilightCost()); + assertEquals(2, card.getBlueprint().getStrength()); + } + + @Test + public void AxeMustBeBorneByDwarf() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + var scn = GetScenario(); + + var axe = scn.GetFreepsCard("axe"); + var gimli = scn.GetFreepsCard("gimli"); + scn.FreepsMoveCardToHand(axe, gimli); + + scn.StartGame(); + assertFalse(scn.FreepsPlayAvailable(axe)); + scn.FreepsPlayCard(gimli); + assertTrue(scn.FreepsPlayAvailable(axe)); + } + + @Test + public void AxeCausesExertOrDeckDiscardChoice() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + var scn = GetScenario(); + + var axe = scn.GetFreepsCard("axe"); + var gimli = scn.GetFreepsCard("gimli"); + var frodo = scn.GetRingBearer(); + scn.FreepsMoveCharToTable(gimli); + scn.AttachCardsTo(gimli, axe); + + var scout = scn.GetShadowCard("scout"); + var runner = scn.GetShadowCard("runner"); + scn.ShadowMoveCharToTable(runner,scout); + + scn.ApplyAdHocModifier(new KeywordModifier(null, Race.ORC, Keyword.FIERCE)); + + scn.StartGame(); + + scn.SkipToPhase(Phase.ASSIGNMENT); + + scn.PassCurrentPhaseActions(); + scn.FreepsDeclineAssignments(); + scn.ShadowAssignToMinions(new PhysicalCardImpl[]{gimli, runner}, new PhysicalCardImpl[]{frodo,scout}); + + scn.FreepsResolveSkirmish(gimli); + scn.PassCurrentPhaseActions(); + scn.FreepsResolveActionOrder("Axe"); + assertTrue(scn.ShadowDecisionAvailable("choose")); + + assertEquals(0, scn.GetWoundsOn(scout)); + scn.ShadowChooseMultipleChoiceOption("exert"); + assertEquals(1, scn.GetWoundsOn(scout)); + + scn.FreepsResolveSkirmish(frodo); + scn.PassCurrentPhaseActions(); + scn.FreepsDeclineOptionalTrigger(); // ring + + //Fierce assignment + scn.PassCurrentPhaseActions(); + scn.FreepsAssignToMinions(gimli, scout); + scn.FreepsResolveSkirmish(gimli); + scn.PassCurrentPhaseActions(); + + var botcard = scn.GetShadowBottomOfDeck(); + assertEquals(Zone.DECK, botcard.getZone()); + assertEquals(2, scn.GetShadowDeckCount()); + assertEquals(1, scn.GetShadowDiscardCount()); + scn.FreepsResolveActionOrder("Axe"); + assertEquals(Zone.DISCARD, botcard.getZone()); + assertEquals(1, scn.GetShadowDeckCount()); + assertEquals(3, scn.GetShadowDiscardCount());//runner, scout, and a card discarded from the deck + + } + + @Test + public void AxeAffectsMultipleMinionsSeparately() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + var scn = GetScenario(); + + var axe = scn.GetFreepsCard("axe"); + var gimli = scn.GetFreepsCard("gimli"); + scn.FreepsMoveCharToTable(gimli); + scn.AttachCardsTo(gimli, axe); + + var scout = scn.GetShadowCard("scout"); + var runner = scn.GetShadowCard("runner"); + scn.ShadowMoveCharToTable(scout, runner); + + scn.StartGame(); + + scn.SkipToPhase(Phase.ASSIGNMENT); + + scn.PassCurrentPhaseActions(); + scn.FreepsDeclineAssignments(); + scn.ShadowAssignToMinions(gimli, scout, runner); + + scn.FreepsResolveSkirmish(gimli); + scn.FreepsUseCardAction(gimli); + scn.ShadowPassCurrentPhaseAction(); + scn.FreepsUseCardAction(gimli); + scn.ShadowPassCurrentPhaseAction(); + scn.FreepsPassCurrentPhaseAction(); + scn.FreepsResolveActionOrder("Axe"); + assertTrue(scn.ShadowDecisionAvailable("choose")); + scn.ShadowChooseMultipleChoiceOption("exert"); + + assertEquals(2, scn.GetShadowDeckCount()); + assertEquals(0, scn.GetShadowDiscardCount()); + scn.FreepsResolveActionOrder("Axe"); + assertEquals(1, scn.GetShadowDeckCount()); + assertEquals(3, scn.GetShadowDiscardCount());//runner, scout, and a card discarded from the deck + } +}