diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-wraith.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-wraith.hjson index dd40f92ca..5014b4288 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-wraith.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set01/set1-wraith.hjson @@ -982,22 +982,61 @@ type: played filter: self } - effect: { - type: repeat - amount: { - type: forEachYouCanSpot - filter: companion - over: 4 + effect: [ + { + type: MemorizeValue + memory: choice + value: yes } - effect: { - type: optional - text: Would you like to play a minion from your discard? + { + type: conditional + requires: [ + { + type: CanSpot + filter: companion + count: 5 + } + { + type: PlayableFromDiscard + filter: minion + } + ] effect: { - type: playCardFromDiscard - filter: choose(minion) + type: Repeat + amount: { + type: forEachYouCanSpot + filter: companion + over: 4 + } + effect: { + type: conditional + requires: [ + { + type: MemoryIs + memory: choice + value: yes + } + { + type: HasCardInDiscard + filter: playable,minion + } + ] + effect: [ + { + type: chooseYesOrNo + player: you + text: Would you like to play a minion from your discard pile? + memorize: choice + } + { + type: playCardFromDiscard + filter: choose(minion) + } + ] + } } } - } + ] } } 1_235: { diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Ringwraith-errata.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Ringwraith-errata.hjson index bdec54784..3a83323e3 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Ringwraith-errata.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/errata/set01/set01-Ringwraith-errata.hjson @@ -40,24 +40,63 @@ type: played filter: self } - effect: { - type: repeat - amount: { - type: forEachYouCanSpot - filter: companion - over: 4 + effect: [ + { + type: MemorizeValue + memory: choice + value: yes } - effect: { - type: optional - text: Would you like to play a unique WRAITH minion from your discard? + { + type: conditional + requires: [ + { + type: CanSpot + filter: companion + count: 5 + } + { + type: PlayableFromDiscard + filter: unique,culture(wraith),minion + } + ] effect: { - type: playCardFromDiscard - filter: choose(unique,culture(wraith),minion) + type: Repeat + amount: { + type: forEachYouCanSpot + filter: companion + over: 4 + } + effect: { + type: conditional + requires: [ + { + type: MemoryIs + memory: choice + value: yes + } + { + type: HasCardInDiscard + filter: playable,unique,culture(wraith),minion + } + ] + effect: [ + { + type: chooseYesOrNo + player: you + text: Would you like to play a unique WRAITH minion from your discard? + memorize: choice + } + { + type: playCardFromDiscard + filter: choose(unique,culture(wraith),minion) + } + ] + } } } - } + ] } - gametext: When you play Ulaire Nertea, for each companion over 4, you may play a unique [Wraith] minion from your discard pile. + gametext: When you play Úlairë Nertëa, for each companion over 4, you may play a unique [Wraith] minion from your discard pile. lore: Some accepted Sauron's gift for glory and a notion of great deeds. promotext: "" alts: { diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/EffectAppenderFactory.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/EffectAppenderFactory.java index d2199df0b..717eef8cc 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/EffectAppenderFactory.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/EffectAppenderFactory.java @@ -79,6 +79,7 @@ public class EffectAppenderFactory { effectAppenderProducers.put("memorize", new MemorizeActive()); effectAppenderProducers.put("memorizenumber", new MemorizeNumber()); effectAppenderProducers.put("memorizestacked", new MemorizeStacked()); + effectAppenderProducers.put("memorizevalue", new MemorizeValue()); effectAppenderProducers.put("modifyarcherytotal", new ModifyArcheryTotal()); effectAppenderProducers.put("modifysitenumber", new ModifySiteNumber()); effectAppenderProducers.put("modifystrength", new ModifyStrength()); diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/MemorizeValue.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/MemorizeValue.java new file mode 100644 index 000000000..e705aaeea --- /dev/null +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/MemorizeValue.java @@ -0,0 +1,42 @@ +package com.gempukku.lotro.cards.build.field.effect.appender; + +import com.gempukku.lotro.cards.build.ActionContext; +import com.gempukku.lotro.cards.build.CardGenerationEnvironment; +import com.gempukku.lotro.cards.build.InvalidCardDefinitionException; +import com.gempukku.lotro.cards.build.ValueSource; +import com.gempukku.lotro.cards.build.field.FieldUtils; +import com.gempukku.lotro.cards.build.field.effect.EffectAppender; +import com.gempukku.lotro.cards.build.field.effect.EffectAppenderProducer; +import com.gempukku.lotro.cards.build.field.effect.appender.resolver.ValueResolver; +import com.gempukku.lotro.game.state.LotroGame; +import com.gempukku.lotro.logic.actions.CostToEffectAction; +import com.gempukku.lotro.logic.timing.Effect; +import com.gempukku.lotro.logic.timing.UnrespondableEffect; +import org.json.simple.JSONObject; + +public class MemorizeValue implements EffectAppenderProducer { + @Override + public EffectAppender createEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { + FieldUtils.validateAllowedFields(effectObject, "value", "memory"); + + final String value = FieldUtils.getString(effectObject.get("value"), "value"); + final String memory = FieldUtils.getString(effectObject.get("memory"), "memory"); + + if (memory == null) + throw new InvalidCardDefinitionException("Memory is required for a MemorizeValue effect."); + + + return new DelayedAppender() { + @Override + protected Effect createEffect(boolean cost, CostToEffectAction action, ActionContext actionContext) { + return new UnrespondableEffect() { + @Override + protected void doPlayEffect(LotroGame game) { + actionContext.setValueToMemory(memory, value); + } + }; + } + }; + } + +} diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/Repeat.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/Repeat.java index 99a94c7f6..2c10f19a0 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/Repeat.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/Repeat.java @@ -14,15 +14,17 @@ import com.gempukku.lotro.logic.effects.StackActionEffect; import com.gempukku.lotro.logic.timing.Effect; import org.json.simple.JSONObject; +import java.util.Arrays; + public class Repeat implements EffectAppenderProducer { @Override public EffectAppender createEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { FieldUtils.validateAllowedFields(effectObject, "amount", "effect"); final ValueSource amountSource = ValueResolver.resolveEvaluator(effectObject.get("amount"), environment); - final JSONObject effect = (JSONObject) effectObject.get("effect"); + final JSONObject[] effectArray = FieldUtils.getObjectArray(effectObject.get("effect"), "effect"); - final EffectAppender effectAppender = environment.getEffectAppenderFactory().getEffectAppender(effect, environment); + final EffectAppender[] effectAppenders = environment.getEffectAppenderFactory().getEffectAppenders(effectArray, environment); return new DelayedAppender() { @Override @@ -30,8 +32,10 @@ public class Repeat implements EffectAppenderProducer { final int count = amountSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), null); if (count > 0) { SubAction subAction = new SubAction(action); - for (int i = 0; i < count; i++) - effectAppender.appendEffect(cost, subAction, actionContext); + for (int i = 0; i < count; i++) { + for (EffectAppender effectAppender : effectAppenders) + effectAppender.appendEffect(false, subAction, actionContext); + } return new StackActionEffect(subAction); } else { return null; @@ -40,12 +44,18 @@ public class Repeat implements EffectAppenderProducer { @Override public boolean isPlayableInFull(ActionContext actionContext) { - return effectAppender.isPlayableInFull(actionContext); + for (EffectAppender effectAppender : effectAppenders) { + if (effectAppender.isPlayabilityCheckedForEffect() + && !effectAppender.isPlayableInFull(actionContext)) + return false; + } + + return true; } @Override public boolean isPlayabilityCheckedForEffect() { - return effectAppender.isPlayabilityCheckedForEffect(); + return Arrays.stream(effectAppenders).anyMatch(EffectAppender::isPlayabilityCheckedForEffect); } }; } diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/PlayableFromDiscard.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/PlayableFromDiscard.java new file mode 100644 index 000000000..219f63dbd --- /dev/null +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/PlayableFromDiscard.java @@ -0,0 +1,26 @@ +package com.gempukku.lotro.cards.build.field.effect.requirement; + +import com.gempukku.lotro.cards.build.*; +import com.gempukku.lotro.cards.build.field.FieldUtils; +import com.gempukku.lotro.cards.build.field.effect.appender.resolver.PlayerResolver; +import com.gempukku.lotro.common.Filterable; +import com.gempukku.lotro.logic.timing.PlayConditions; +import org.json.simple.JSONObject; + +public class PlayableFromDiscard implements RequirementProducer { + @Override + public Requirement getPlayRequirement(JSONObject object, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { + FieldUtils.validateAllowedFields(object, "player", "filter"); + + final String player = FieldUtils.getString(object.get("player"), "player", "you"); + final String filter = FieldUtils.getString(object.get("filter"), "filter", "any"); + + final PlayerSource playerSource = PlayerResolver.resolvePlayer(player, environment); + + final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter, environment); + return (actionContext) -> { + final Filterable filterable = filterableSource.getFilterable(actionContext); + return PlayConditions.canPlayFromDiscard(playerSource.getPlayer(actionContext), actionContext.getGame(), filterable); + }; + } +} diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/RequirementFactory.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/RequirementFactory.java index b21f9d947..29a9c7515 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/RequirementFactory.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/RequirementFactory.java @@ -50,6 +50,7 @@ public class RequirementFactory { requirementProducers.put("movecountminimum", new MoveCountMinimum()); requirementProducers.put("perphaselimit", new PerPhaseLimit()); requirementProducers.put("perturnlimit", new PerTurnLimit()); + requirementProducers.put("playablefromdiscard", new PlayableFromDiscard()); requirementProducers.put("phase", new PhaseRequirement()); requirementProducers.put("playedcardthisphase", new PlayedCardThisPhase()); requirementProducers.put("ringisactive", new RingIsActive()); diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/at/AbstractAtTest.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/at/AbstractAtTest.java index ca8d2d123..9e81a7b81 100644 --- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/at/AbstractAtTest.java +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/at/AbstractAtTest.java @@ -18,9 +18,9 @@ import java.util.*; import static org.junit.Assert.fail; public abstract class AbstractAtTest { - protected static LotroCardBlueprintLibrary _cardLibrary; - protected static LotroFormatLibrary _formatLibrary; - protected static ProductLibrary _productLibrary; + public static LotroCardBlueprintLibrary _cardLibrary; + public static LotroFormatLibrary _formatLibrary; + public static ProductLibrary _productLibrary; private final int cardId = 100; static { @@ -29,20 +29,20 @@ public abstract class AbstractAtTest { _productLibrary = new ProductLibrary(_cardLibrary); } - protected DefaultLotroGame _game; - protected DefaultUserFeedback _userFeedback; + public DefaultLotroGame _game; + public DefaultUserFeedback _userFeedback; public static final String P1 = "player1"; public static final String P2 = "player2"; - protected PhysicalCardImpl createCard(String owner, String blueprintId) throws CardNotFoundException { + public PhysicalCardImpl createCard(String owner, String blueprintId) throws CardNotFoundException { return (PhysicalCardImpl) _game.getGameState().createPhysicalCard(owner, _cardLibrary, blueprintId); } - protected void initializeSimplestGame() throws DecisionResultInvalidException { + public void initializeSimplestGame() throws DecisionResultInvalidException { this.initializeSimplestGame(null); } - protected void initializeSimplestGame(Map> additionalCardsInDeck) throws DecisionResultInvalidException { + public void initializeSimplestGame(Map> additionalCardsInDeck) throws DecisionResultInvalidException { Map decks = new HashMap<>(); addPlayerDeck(P1, decks, additionalCardsInDeck); addPlayerDeck(P2, decks, additionalCardsInDeck); @@ -50,11 +50,11 @@ public abstract class AbstractAtTest { initializeGameWithDecks(decks); } - protected void initializeGameWithDecks(Map decks) throws DecisionResultInvalidException { + public void initializeGameWithDecks(Map decks) throws DecisionResultInvalidException { initializeGameWithDecks(decks, "multipath"); } - protected void initializeGameWithDecks(Map decks, String formatName) throws DecisionResultInvalidException { + public void initializeGameWithDecks(Map decks, String formatName) throws DecisionResultInvalidException { _userFeedback = new DefaultUserFeedback(); LotroFormatLibrary formatLibrary = new LotroFormatLibrary(new DefaultAdventureLibrary(), _cardLibrary); @@ -72,13 +72,13 @@ public abstract class AbstractAtTest { playerDecided(P1, "0"); } - protected void skipMulligans() throws DecisionResultInvalidException { + public void skipMulligans() throws DecisionResultInvalidException { // Mulligans playerDecided(P1, "0"); playerDecided(P2, "0"); } - protected void validateContents(String[] array1, String[] array2) { + public void validateContents(String[] array1, String[] array2) { if (array1.length != array2.length) fail("Array sizes differ"); List values = new ArrayList<>(Arrays.asList(array1)); @@ -88,14 +88,14 @@ public abstract class AbstractAtTest { } } - protected String[] toCardIdArray(PhysicalCard... cards) { + public String[] toCardIdArray(PhysicalCard... cards) { String[] result = new String[cards.length]; for (int i = 0; i < cards.length; i++) result[i] = String.valueOf(cards[i].getCardId()); return result; } - protected String getArbitraryCardId(AwaitingDecision awaitingDecision, String blueprintId) { + public String getArbitraryCardId(AwaitingDecision awaitingDecision, String blueprintId) { String[] blueprints = (String[]) awaitingDecision.getDecisionParameters().get("blueprintId"); for (int i = 0; i < blueprints.length; i++) if (blueprints[i].equals(blueprintId)) @@ -103,7 +103,7 @@ public abstract class AbstractAtTest { return null; } - protected String getCardActionId(AwaitingDecision awaitingDecision, String actionTextStart) { + public String getCardActionId(AwaitingDecision awaitingDecision, String actionTextStart) { String[] actionTexts = (String[]) awaitingDecision.getDecisionParameters().get("actionText"); for (int i = 0; i < actionTexts.length; i++) if (actionTexts[i].startsWith(actionTextStart)) @@ -111,11 +111,11 @@ public abstract class AbstractAtTest { return null; } - protected String getCardActionId(String playerId, String actionTextStart) { + public String getCardActionId(String playerId, String actionTextStart) { return getCardActionId(_userFeedback.getAwaitingDecision(playerId), actionTextStart); } - protected String getCardActionIdContains(AwaitingDecision awaitingDecision, String actionTextContains) { + public String getCardActionIdContains(AwaitingDecision awaitingDecision, String actionTextContains) { String[] actionTexts = (String[]) awaitingDecision.getDecisionParameters().get("actionText"); for (int i = 0; i < actionTexts.length; i++) if (actionTexts[i].contains(actionTextContains)) @@ -123,7 +123,7 @@ public abstract class AbstractAtTest { return null; } - protected String getMultipleDecisionIndex(AwaitingDecision awaitingDecision, String result) { + public String getMultipleDecisionIndex(AwaitingDecision awaitingDecision, String result) { String[] actionTexts = (String[]) awaitingDecision.getDecisionParameters().get("results"); for (int i = 0; i < actionTexts.length; i++) if (actionTexts[i].equals(result)) @@ -131,7 +131,7 @@ public abstract class AbstractAtTest { return null; } - protected void addPlayerDeck(String player, Map decks, Map> additionalCardsInDeck) { + public void addPlayerDeck(String player, Map decks, Map> additionalCardsInDeck) { LotroDeck deck = createSimplestDeck(); if (additionalCardsInDeck != null) { Collection extraCards = additionalCardsInDeck.get(player); @@ -142,11 +142,11 @@ public abstract class AbstractAtTest { decks.put(player, deck); } - protected void moveCardToZone(PhysicalCardImpl card, Zone zone) { + public void moveCardToZone(PhysicalCardImpl card, Zone zone) { _game.getGameState().addCardToZone(_game, card, zone); } - protected void playerDecided(String player, String answer) throws DecisionResultInvalidException { + public void playerDecided(String player, String answer) throws DecisionResultInvalidException { AwaitingDecision decision = _userFeedback.getAwaitingDecision(player); _userFeedback.participantDecided(player); try { @@ -158,20 +158,20 @@ public abstract class AbstractAtTest { _game.carryOutPendingActionsUntilDecisionNeeded(); } - protected void carryOutEffectInPhaseActionByPlayer(String playerId, Effect effect) throws DecisionResultInvalidException { + public void carryOutEffectInPhaseActionByPlayer(String playerId, Effect effect) throws DecisionResultInvalidException { SystemQueueAction action = new SystemQueueAction(); action.appendEffect(effect); carryOutEffectInPhaseActionByPlayer(playerId, action); } - protected void carryOutEffectInPhaseActionByPlayer(String playerId, Action action) throws DecisionResultInvalidException { + public void carryOutEffectInPhaseActionByPlayer(String playerId, Action action) throws DecisionResultInvalidException { CardActionSelectionDecision awaitingDecision = (CardActionSelectionDecision) _userFeedback.getAwaitingDecision(playerId); awaitingDecision.addAction(action); playerDecided(playerId, "0"); } - protected LotroDeck createSimplestDeck() { + public LotroDeck createSimplestDeck() { LotroDeck lotroDeck = new LotroDeck("Some deck"); // 10_121,1_2 lotroDeck.setRingBearer("10_121"); diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/at/effects/PlayCardFromDiscardAtTest.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/at/effects/PlayCardFromDiscardAtTest.java index 0534006af..8aa33d23b 100644 --- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/at/effects/PlayCardFromDiscardAtTest.java +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/at/effects/PlayCardFromDiscardAtTest.java @@ -117,38 +117,39 @@ public class PlayCardFromDiscardAtTest extends AbstractAtTest { assertEquals(Zone.SHADOW_CHARACTERS, goblinSneakInDiscard.getZone()); } - @Test - public void ulaireNerteaCantPlayMinionsOnGreatRiver() throws Exception { - initializeSimplestGame(); - - for (int i=0; i<4; i++) { - final PhysicalCardImpl greatRiver = createCard(P1, "1_306"); - _game.getGameState().addCardToZone(_game, greatRiver, Zone.FREE_CHARACTERS); - } - - skipMulligans(); - - final PhysicalCardImpl greatRiver = createCard(P2, "3_118"); - greatRiver.setSiteNumber(2); - _game.getGameState().addCardToZone(_game, greatRiver, Zone.ADVENTURE_PATH); - - final PhysicalCardImpl ulaireNertea = createCard(P2, "1_234"); - _game.getGameState().addCardToZone(_game, ulaireNertea, Zone.HAND); - - final PhysicalCardImpl goblinRunner = createCard(P2, "1_178"); - _game.getGameState().addCardToZone(_game, goblinRunner, Zone.DISCARD); - - _game.getGameState().setTwilight(20); - - // Fellowship phase - playerDecided(P1, ""); - - assertEquals(greatRiver, _game.getGameState().getCurrentSite()); - - playerDecided(P2, getCardActionId(P2, "Play Úlairë Nertëa")); - - assertFalse(_userFeedback.getAwaitingDecision(P2).getDecisionType() == AwaitingDecisionType.MULTIPLE_CHOICE); - } + //Migrated to the Ulaire Nertea tests in cards/official/set01/Card_01_234_Tests +// @Test +// public void ulaireNerteaCantPlayMinionsOnGreatRiver() throws Exception { +// initializeSimplestGame(); +// +// for (int i=0; i<4; i++) { +// final PhysicalCardImpl greatRiver = createCard(P1, "1_306"); +// _game.getGameState().addCardToZone(_game, greatRiver, Zone.FREE_CHARACTERS); +// } +// +// skipMulligans(); +// +// final PhysicalCardImpl greatRiver = createCard(P2, "3_118"); +// greatRiver.setSiteNumber(2); +// _game.getGameState().addCardToZone(_game, greatRiver, Zone.ADVENTURE_PATH); +// +// final PhysicalCardImpl ulaireNertea = createCard(P2, "1_234"); +// _game.getGameState().addCardToZone(_game, ulaireNertea, Zone.HAND); +// +// final PhysicalCardImpl goblinRunner = createCard(P2, "1_178"); +// _game.getGameState().addCardToZone(_game, goblinRunner, Zone.DISCARD); +// +// _game.getGameState().setTwilight(20); +// +// // Fellowship phase +// playerDecided(P1, ""); +// +// assertEquals(greatRiver, _game.getGameState().getCurrentSite()); +// +// playerDecided(P2, getCardActionId(P2, "Play Úlairë Nertëa")); +// +// assertFalse(_userFeedback.getAwaitingDecision(P2).getDecisionType() == AwaitingDecisionType.MULTIPLE_CHOICE); +// } @Test public void azogCantPlayTrollFromDiscardInRivendell() throws Exception { diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_234_Tests.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_234_Tests.java new file mode 100644 index 000000000..46fc04934 --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set01/Card_01_234_Tests.java @@ -0,0 +1,238 @@ +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.game.PhysicalCardImpl; +import com.gempukku.lotro.logic.decisions.AwaitingDecisionType; +import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException; +import org.junit.Test; + +import java.util.HashMap; + +import static org.junit.Assert.*; + +public class Card_01_234_Tests +{ + + protected GenericCardTestHelper GetScenario() throws CardNotFoundException, DecisionResultInvalidException { + return new GenericCardTestHelper( + new HashMap<>() + {{ + put("comp2", "1_53"); + put("comp3", "1_53"); + put("comp4", "1_53"); + put("comp5", "1_53"); + put("comp6", "1_53"); + + put("nertea", "1_234"); + put("runner", "1_178"); + put("twk", "2_85"); + put("attea", "1_229"); + put("rit", "101_40"); + }}, + GenericCardTestHelper.FellowshipSites, + GenericCardTestHelper.FOTRFrodo, + GenericCardTestHelper.RulingRing + ); + } + + @Test + public void UlaireNerteaStatsAndKeywordsAreCorrect() throws DecisionResultInvalidException, CardNotFoundException { + + /** + * Set: 1 + * Title: *Ulaire Nertea, Messenger of Dol Guldur + * Side: Free Peoples + * Culture: Ringwraith + * Twilight Cost: 4 + * Type: minion + * Subtype: Nazgul + * Strength: 9 + * Vitality: 2 + * Site Number: 3 + * Game Text: When you play Úlairë Nertëa, for each companion over 4, you may play 1 minion from your discard pile. + */ + + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + PhysicalCardImpl nertea = scn.GetFreepsCard("nertea"); + + assertTrue(nertea.getBlueprint().isUnique()); + assertEquals(Side.SHADOW, nertea.getBlueprint().getSide()); + assertEquals(Culture.WRAITH, nertea.getBlueprint().getCulture()); + assertEquals(CardType.MINION, nertea.getBlueprint().getCardType()); + assertEquals(Race.NAZGUL, nertea.getBlueprint().getRace()); + assertEquals(4, nertea.getBlueprint().getTwilightCost()); + assertEquals(9, nertea.getBlueprint().getStrength()); + assertEquals(2, nertea.getBlueprint().getVitality()); + assertEquals(3, nertea.getBlueprint().getSiteNumber()); + + } + + @Test + public void NerteaDoesNotTriggerWith4Companions() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + scn.FreepsMoveCharToTable("comp2", "comp3", "comp4"); + + PhysicalCardImpl nertea = scn.GetShadowCard("nertea"); + scn.ShadowMoveCardToHand(nertea); + + scn.StartGame(); + scn.SetTwilight(20); + scn.FreepsPassCurrentPhaseAction(); + + scn.ShadowPlayCard(nertea); + assertFalse(scn.ShadowHasOptionalTriggerAvailable()); + } + + @Test + public void NerteaPlays1MinionIf5Companions() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + scn.FreepsMoveCharToTable("comp2", "comp3", "comp4", "comp5"); + + PhysicalCardImpl twk = scn.GetShadowCard("twk"); + PhysicalCardImpl attea = scn.GetShadowCard("attea"); + PhysicalCardImpl nertea = scn.GetShadowCard("nertea"); + scn.ShadowMoveCardToDiscard("runner", "rit", "twk", "attea"); + scn.ShadowMoveCardToDiscard(twk); + scn.ShadowMoveCardToHand(nertea); + + scn.StartGame(); + scn.SetTwilight(30); + scn.FreepsPassCurrentPhaseAction(); + + scn.ShadowPlayCard(nertea); + assertTrue(scn.ShadowDecisionAvailable("play a minion")); + scn.ShadowChooseYes(); + //twk, attea, rit, and runner + assertEquals(4, scn.GetShadowCardChoiceCount()); + assertEquals(Zone.DISCARD, twk.getZone()); + scn.ShadowChooseCardBPFromSelection(twk); + assertEquals(Zone.SHADOW_CHARACTERS, twk.getZone()); + + assertFalse(scn.ShadowDecisionAvailable("play a minion")); + } + + @Test + public void NerteaPlays2MinionsIf6Companions() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + scn.FreepsMoveCharToTable("comp2", "comp3", "comp4", "comp5", "comp6"); + + PhysicalCardImpl twk = scn.GetShadowCard("twk"); + PhysicalCardImpl attea = scn.GetShadowCard("attea"); + PhysicalCardImpl nertea = scn.GetShadowCard("nertea"); + PhysicalCardImpl runner = scn.GetShadowCard("runner"); + scn.ShadowMoveCardToDiscard("rit", "twk", "attea"); + scn.ShadowMoveCardToDiscard(twk, runner); + scn.ShadowMoveCardToHand(nertea); + + scn.StartGame(); + scn.SetTwilight(30); + scn.FreepsPassCurrentPhaseAction(); + + scn.ShadowPlayCard(nertea); + assertTrue(scn.ShadowDecisionAvailable("play a minion")); + scn.ShadowChooseYes(); + //twk, attea, rit, and runner + assertEquals(4, scn.GetShadowCardChoiceCount()); + assertEquals(Zone.DISCARD, twk.getZone()); + scn.ShadowChooseCardBPFromSelection(twk); + assertEquals(Zone.SHADOW_CHARACTERS, twk.getZone()); + + assertTrue(scn.ShadowDecisionAvailable("play a minion")); + scn.ShadowChooseYes(); + assertEquals(Zone.DISCARD, runner.getZone()); + scn.ShadowChooseCardBPFromSelection(runner); + assertEquals(Zone.SHADOW_CHARACTERS, runner.getZone()); + + assertFalse(scn.ShadowDecisionAvailable("play a minion")); + } + + @Test + public void CancelingNerteaAfterFirstMinionDoesntAskAgain() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + scn.FreepsMoveCharToTable("comp2", "comp3", "comp4", "comp5", "comp6"); + + PhysicalCardImpl twk = scn.GetShadowCard("twk"); + PhysicalCardImpl attea = scn.GetShadowCard("attea"); + PhysicalCardImpl nertea = scn.GetShadowCard("nertea"); + PhysicalCardImpl runner = scn.GetShadowCard("runner"); + scn.ShadowMoveCardToDiscard("rit", "twk", "attea"); + scn.ShadowMoveCardToDiscard(twk, runner); + scn.ShadowMoveCardToHand(nertea); + + scn.StartGame(); + scn.SetTwilight(30); + scn.FreepsPassCurrentPhaseAction(); + + scn.ShadowPlayCard(nertea); + assertTrue(scn.ShadowDecisionAvailable("play a minion")); + scn.ShadowChooseNo(); + + assertFalse(scn.ShadowDecisionAvailable("play a minion")); + } + + @Test + public void NerteaDoesNotPromptIfNoMinionsInDiscardPile() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + scn.FreepsMoveCharToTable("comp2", "comp3", "comp4", "comp5", "comp6"); + + PhysicalCardImpl nertea = scn.GetShadowCard("nertea"); + scn.ShadowMoveCardToHand("rit", "twk", "attea", "runner"); + scn.ShadowMoveCardToHand(nertea); + + scn.StartGame(); + scn.SetTwilight(30); + scn.FreepsPassCurrentPhaseAction(); + + scn.ShadowPlayCard(nertea); + assertFalse(scn.ShadowDecisionAvailable("play a minion")); + } + + //Imported from the at tests + @Test + public void ulaireNerteaCantPlayMinionsOnGreatRiver() throws Exception { + var scn = GetScenario(); + var _game = scn._game; + + for (int i=0; i<4; i++) { + final PhysicalCardImpl pippin = scn.createCard(scn.P1, "1_306"); + _game.getGameState().addCardToZone(_game, pippin, Zone.FREE_CHARACTERS); + } + + scn.skipMulligans(); + + final PhysicalCardImpl greatRiver = scn.createCard(scn.P2, "3_118"); + greatRiver.setSiteNumber(2); + _game.getGameState().addCardToZone(_game, greatRiver, Zone.ADVENTURE_PATH); + + final PhysicalCardImpl ulaireNertea = scn.createCard(scn.P2, "1_234"); + _game.getGameState().addCardToZone(_game, ulaireNertea, Zone.HAND); + + final PhysicalCardImpl goblinRunner = scn.createCard(scn.P2, "1_178"); + _game.getGameState().addCardToZone(_game, goblinRunner, Zone.DISCARD); + + _game.getGameState().setTwilight(20); + + // Fellowship phase + scn.playerDecided(scn.P1, ""); + + assertEquals(greatRiver, _game.getGameState().getCurrentSite()); + + scn.playerDecided(scn.P2, scn.getCardActionId(scn.P2, "Play Úlairë Nertëa")); + + assertFalse(scn._userFeedback.getAwaitingDecision(scn.P2).getDecisionType() == AwaitingDecisionType.MULTIPLE_CHOICE); + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set01/Card_01_234_ErrataTests.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set01/Card_01_234_ErrataTests.java index 7f6ee2fe3..4a929ac89 100644 --- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set01/Card_01_234_ErrataTests.java +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/set01/Card_01_234_ErrataTests.java @@ -4,6 +4,7 @@ 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.AwaitingDecisionType; import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException; import org.junit.Test; @@ -152,4 +153,91 @@ public class Card_01_234_ErrataTests assertFalse(scn.ShadowDecisionAvailable("play a unique WRAITH minion")); } + + @Test + public void CancelingNerteaAfterFirstMinionDoesntAskAgain() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + scn.FreepsMoveCharToTable("comp2", "comp3", "comp4", "comp5", "comp6"); + + PhysicalCardImpl twk = scn.GetShadowCard("twk"); + PhysicalCardImpl attea = scn.GetShadowCard("attea"); + PhysicalCardImpl nertea = scn.GetShadowCard("nertea"); + PhysicalCardImpl runner = scn.GetShadowCard("runner"); + scn.ShadowMoveCardToDiscard("rit", "twk", "attea"); + scn.ShadowMoveCardToDiscard(twk, runner); + scn.ShadowMoveCardToHand(nertea); + + scn.StartGame(); + scn.SetTwilight(30); + scn.FreepsPassCurrentPhaseAction(); + + scn.ShadowPlayCard(nertea); + assertTrue(scn.ShadowDecisionAvailable("play a unique WRAITH minion")); + scn.ShadowChooseNo(); + + assertFalse(scn.ShadowDecisionAvailable("play a unique WRAITH minion")); + } + + @Test + public void NerteaDoesNotPromptIfNoUniqueRingwraithMinionsInDiscardPile() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + scn.FreepsMoveCharToTable("comp2", "comp3", "comp4", "comp5", "comp6"); + + PhysicalCardImpl nertea = scn.GetShadowCard("nertea"); + scn.ShadowMoveCardToHand("twk", "attea"); + scn.ShadowMoveCardToDiscard("rit", "runner"); + scn.ShadowMoveCardToHand(nertea); + + scn.StartGame(); + scn.SetTwilight(30); + scn.FreepsPassCurrentPhaseAction(); + + scn.ShadowPlayCard(nertea); + assertFalse(scn.ShadowDecisionAvailable("play a unique WRAITH minion")); + } + + //Imported from the at tests + @Test + public void ulaireNerteaCantPlayMinionsOnGreatRiver() throws Exception { + var scn = GetScenario(); + var _game = scn._game; + + for (int i=0; i<4; i++) { + final PhysicalCardImpl pippin = scn.createCard(scn.P1, "1_306"); + _game.getGameState().addCardToZone(_game, pippin, Zone.FREE_CHARACTERS); + } + + scn.skipMulligans(); + + final PhysicalCardImpl greatRiver = scn.createCard(scn.P2, "3_118"); + greatRiver.setSiteNumber(2); + _game.getGameState().addCardToZone(_game, greatRiver, Zone.ADVENTURE_PATH); + + final PhysicalCardImpl ulaireNertea = scn.createCard(scn.P2, "1_234"); + _game.getGameState().addCardToZone(_game, ulaireNertea, Zone.HAND); + + final PhysicalCardImpl goblinRunner = scn.createCard(scn.P2, "1_178"); + _game.getGameState().addCardToZone(_game, goblinRunner, Zone.DISCARD); + + final PhysicalCardImpl ringwraithInTwilight = scn.createCard(scn.P2, "101_40"); + _game.getGameState().addCardToZone(_game, ringwraithInTwilight, Zone.DISCARD); + + final PhysicalCardImpl witchKing = scn.createCard(scn.P2, "2_85"); + _game.getGameState().addCardToZone(_game, witchKing, Zone.DISCARD); + + _game.getGameState().setTwilight(20); + + // Fellowship phase + scn.playerDecided(scn.P1, ""); + + assertEquals(greatRiver, _game.getGameState().getCurrentSite()); + + scn.playerDecided(scn.P2, scn.getCardActionId(scn.P2, "Play Úlairë Nertëa")); + + assertFalse(scn._userFeedback.getAwaitingDecision(scn.P2).getDecisionType() == AwaitingDecisionType.MULTIPLE_CHOICE); + } }