Added new flag to PlayCardFromX effects to bypass checking for the target's existence. This permits A Wizard is Never Late to play to no effect if necessary.
This commit is contained in:
@@ -555,7 +555,8 @@
|
||||
"type": "event",
|
||||
"effect": {
|
||||
"type": "playCardFromDrawDeck",
|
||||
"filter": "choose(culture(gandalf),character)"
|
||||
"filter": "choose(culture(gandalf),character)",
|
||||
"nocheck": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -24,13 +24,23 @@ import java.util.Collection;
|
||||
public class PlayCardFromDiscard implements EffectAppenderProducer {
|
||||
@Override
|
||||
public EffectAppender createEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
FieldUtils.validateAllowedFields(effectObject, "filter", "on", "cost", "removedTwilight", "memorize");
|
||||
FieldUtils.validateAllowedFields(effectObject, "filter", "on", "cost", "removedTwilight", "memorize", "nocheck");
|
||||
|
||||
final String filter = FieldUtils.getString(effectObject.get("filter"), "filter");
|
||||
final String onFilter = FieldUtils.getString(effectObject.get("on"), "on");
|
||||
final int removedTwilight = FieldUtils.getInteger(effectObject.get("removedTwilight"), "removedTwilight", 0);
|
||||
final ValueSource costModifierSource = ValueResolver.resolveEvaluator(effectObject.get("cost"), 0, environment);
|
||||
final String memorize = FieldUtils.getString(effectObject.get("memorize"), "memorize", "_temp");
|
||||
final boolean noCheck = FieldUtils.getBoolean(effectObject.get("nocheck"), "nocheck", false);
|
||||
|
||||
ValueSource countSource = new ConstantEvaluator(1);
|
||||
if(noCheck)
|
||||
{
|
||||
//This range will cause choice checks to succeed even if no valid choices are found (which is how draw deck
|
||||
// searching is supposed to work RAW). However we don't want this to be the default, else dual-choice cards
|
||||
// that play "from draw deck or discard pile" would allow empty sources to be chosen, which is NPE.
|
||||
countSource = ValueResolver.resolveEvaluator("0-1", 1, environment);
|
||||
}
|
||||
|
||||
final FilterableSource onFilterableSource = (onFilter != null) ? environment.getFilterFactory().generateFilter(onFilter, environment) : null;
|
||||
|
||||
@@ -59,7 +69,7 @@ public class PlayCardFromDiscard implements EffectAppenderProducer {
|
||||
|
||||
return Filters.playable(actionContext.getGame(), removedTwilight, costModifier, false, false);
|
||||
},
|
||||
new ConstantEvaluator(1), memorize, "you", "Choose card to play", environment));
|
||||
countSource, memorize, "you", "Choose card to play", environment));
|
||||
result.addEffectAppender(
|
||||
new DelayedAppender() {
|
||||
@Override
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.gempukku.lotro.logic.actions.CostToEffectAction;
|
||||
import com.gempukku.lotro.logic.effects.StackActionEffect;
|
||||
import com.gempukku.lotro.logic.modifiers.ModifierFlag;
|
||||
import com.gempukku.lotro.logic.modifiers.evaluator.ConstantEvaluator;
|
||||
import com.gempukku.lotro.logic.modifiers.evaluator.Evaluator;
|
||||
import com.gempukku.lotro.logic.timing.Effect;
|
||||
import com.gempukku.lotro.logic.timing.ExtraFilters;
|
||||
import org.json.simple.JSONObject;
|
||||
@@ -24,12 +25,22 @@ import java.util.Collection;
|
||||
public class PlayCardFromDrawDeck implements EffectAppenderProducer {
|
||||
@Override
|
||||
public EffectAppender createEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
FieldUtils.validateAllowedFields(effectObject, "filter", "on", "cost", "memorize");
|
||||
FieldUtils.validateAllowedFields(effectObject, "filter", "on", "cost", "memorize", "nocheck");
|
||||
|
||||
final String filter = FieldUtils.getString(effectObject.get("filter"), "filter");
|
||||
final String onFilter = FieldUtils.getString(effectObject.get("on"), "on");
|
||||
final ValueSource costModifierSource = ValueResolver.resolveEvaluator(effectObject.get("cost"), 0, environment);
|
||||
final String memorize = FieldUtils.getString(effectObject.get("memorize"), "memorize", "_temp");
|
||||
final boolean noCheck = FieldUtils.getBoolean(effectObject.get("nocheck"), "nocheck", false);
|
||||
|
||||
ValueSource countSource = new ConstantEvaluator(1);
|
||||
if(noCheck)
|
||||
{
|
||||
//This range will cause choice checks to succeed even if no valid choices are found (which is how draw deck
|
||||
// searching is supposed to work RAW). However we don't want this to be the default, else dual-choice cards
|
||||
// that play "from draw deck or discard pile" would allow empty sources to be chosen, which is NPE.
|
||||
countSource = ValueResolver.resolveEvaluator("0-1", 1, environment);
|
||||
}
|
||||
|
||||
final FilterableSource onFilterableSource = (onFilter != null) ? environment.getFilterFactory().generateFilter(onFilter, environment) : null;
|
||||
|
||||
@@ -47,7 +58,7 @@ public class PlayCardFromDrawDeck implements EffectAppenderProducer {
|
||||
}
|
||||
return Filters.playable(actionContext.getGame(), costModifier);
|
||||
},
|
||||
new ConstantEvaluator(1), memorize, "you", "you", "Choose card to play", environment));
|
||||
countSource, memorize, "you", "you", "Choose card to play", environment));
|
||||
result.addEffectAppender(
|
||||
new DelayedAppender() {
|
||||
@Override
|
||||
|
||||
@@ -23,13 +23,23 @@ import java.util.Collection;
|
||||
public class PlayCardFromHand implements EffectAppenderProducer {
|
||||
@Override
|
||||
public EffectAppender createEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
FieldUtils.validateAllowedFields(effectObject, "filter", "on", "cost", "ignoreInDeadPile", "memorize");
|
||||
FieldUtils.validateAllowedFields(effectObject, "filter", "on", "cost", "ignoreInDeadPile", "memorize", "nocheck");
|
||||
|
||||
final String filter = FieldUtils.getString(effectObject.get("filter"), "filter");
|
||||
final String onFilter = FieldUtils.getString(effectObject.get("on"), "on");
|
||||
final ValueSource costModifierSource = ValueResolver.resolveEvaluator(effectObject.get("cost"), 0, environment);
|
||||
final boolean ignoreInDeadPile = FieldUtils.getBoolean(effectObject.get("ignoreInDeadPile"), "ignoreInDeadPile", false);
|
||||
final String memorize = FieldUtils.getString(effectObject.get("memorize"), "memorize", "_temp");
|
||||
final boolean noCheck = FieldUtils.getBoolean(effectObject.get("nocheck"), "nocheck", false);
|
||||
|
||||
ValueSource countSource = new ConstantEvaluator(1);
|
||||
if(noCheck)
|
||||
{
|
||||
//This range will cause choice checks to succeed even if no valid choices are found (which is how draw deck
|
||||
// searching is supposed to work RAW). However we don't want this to be the default, else dual-choice cards
|
||||
// that play "from draw deck or discard pile" would allow empty sources to be chosen, which is NPE.
|
||||
countSource = ValueResolver.resolveEvaluator("0-1", 1, environment);
|
||||
}
|
||||
|
||||
final FilterableSource onFilterableSource = (onFilter != null) ? environment.getFilterFactory().generateFilter(onFilter, environment) : null;
|
||||
|
||||
@@ -47,7 +57,7 @@ public class PlayCardFromHand implements EffectAppenderProducer {
|
||||
}
|
||||
return Filters.playable(game, costModifier, false, ignoreInDeadPile);
|
||||
},
|
||||
new ConstantEvaluator(1), memorize, "you", "you", "Choose card to play from hand", environment));
|
||||
countSource, memorize, "you", "you", "Choose card to play from hand", environment));
|
||||
result.addEffectAppender(
|
||||
new DelayedAppender() {
|
||||
@Override
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
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.FilterableSource;
|
||||
import com.gempukku.lotro.cards.build.InvalidCardDefinitionException;
|
||||
import com.gempukku.lotro.cards.build.*;
|
||||
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.CardResolver;
|
||||
import com.gempukku.lotro.cards.build.field.effect.appender.resolver.ValueResolver;
|
||||
import com.gempukku.lotro.filters.Filters;
|
||||
import com.gempukku.lotro.game.PhysicalCard;
|
||||
import com.gempukku.lotro.logic.PlayUtils;
|
||||
@@ -22,11 +20,21 @@ import java.util.Collection;
|
||||
public class PlayCardFromStacked implements EffectAppenderProducer {
|
||||
@Override
|
||||
public EffectAppender createEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
FieldUtils.validateAllowedFields(effectObject, "filter", "on", "removedTwilight");
|
||||
FieldUtils.validateAllowedFields(effectObject, "filter", "on", "removedTwilight", "nocheck");
|
||||
|
||||
final String filter = FieldUtils.getString(effectObject.get("filter"), "filter", "choose(any)");
|
||||
final String onFilter = FieldUtils.getString(effectObject.get("on"), "on");
|
||||
final int removedTwilight = FieldUtils.getInteger(effectObject.get("removedTwilight"), "removedTwilight", 0);
|
||||
final boolean noCheck = FieldUtils.getBoolean(effectObject.get("nocheck"), "nocheck", false);
|
||||
|
||||
ValueSource countSource = new ConstantEvaluator(1);
|
||||
if(noCheck)
|
||||
{
|
||||
//This range will cause choice checks to succeed even if no valid choices are found (which is how draw deck
|
||||
// searching is supposed to work RAW). However we don't want this to be the default, else dual-choice cards
|
||||
// that play "from draw deck or discard pile" would allow empty sources to be chosen, which is NPE.
|
||||
countSource = ValueResolver.resolveEvaluator("0-1", 1, environment);
|
||||
}
|
||||
|
||||
final FilterableSource onFilterableSource = (onFilter != null) ? environment.getFilterFactory().generateFilter(onFilter, environment) : null;
|
||||
|
||||
@@ -37,7 +45,7 @@ public class PlayCardFromStacked implements EffectAppenderProducer {
|
||||
CardResolver.resolveStackedCards(filter,
|
||||
actionContext -> Filters.playable(actionContext.getGame()),
|
||||
actionContext -> Filters.playable(actionContext.getGame(), removedTwilight, 0, false, false),
|
||||
new ConstantEvaluator(1), onFilterableSource, "_temp", "you", "Choose card to play", environment));
|
||||
countSource, onFilterableSource, "_temp", "you", "Choose card to play", environment));
|
||||
result.addEffectAppender(
|
||||
new DelayedAppender() {
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
|
||||
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.DecisionResultInvalidException;
|
||||
import com.gempukku.lotro.logic.modifiers.MoveLimitModifier;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class Card_01_087_Tests
|
||||
{
|
||||
|
||||
protected GenericCardTestHelper GetScenario() throws CardNotFoundException, DecisionResultInvalidException {
|
||||
return new GenericCardTestHelper(
|
||||
new HashMap<String, String>()
|
||||
{{
|
||||
put("late", "1_87");
|
||||
put("gandalf", "1_72");
|
||||
put("bb", "1_70");
|
||||
}},
|
||||
GenericCardTestHelper.FellowshipSites,
|
||||
GenericCardTestHelper.FOTRFrodo,
|
||||
GenericCardTestHelper.FOTRRing
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void AWizardIsNeverLateStatsAndKeywordsAreCorrect() throws DecisionResultInvalidException, CardNotFoundException {
|
||||
|
||||
/**
|
||||
* Set: 1
|
||||
* Title: A Wizard Is Never Late
|
||||
* Side: Free Peoples
|
||||
* Culture: Gandalf
|
||||
* Twilight Cost: 1
|
||||
* Type: Event
|
||||
* Subtype: Fellowship
|
||||
* Game Text: Fellowship: Play a [GANDALF] character from your draw deck.
|
||||
*/
|
||||
|
||||
//Pre-game setup
|
||||
GenericCardTestHelper scn = GetScenario();
|
||||
|
||||
PhysicalCardImpl late = scn.GetFreepsCard("late");
|
||||
|
||||
assertFalse(late.getBlueprint().isUnique());
|
||||
assertEquals(Side.FREE_PEOPLE, late.getBlueprint().getSide());
|
||||
assertEquals(Culture.GANDALF, late.getBlueprint().getCulture());
|
||||
assertEquals(CardType.EVENT, late.getBlueprint().getCardType());
|
||||
assertTrue(scn.HasKeyword(late, Keyword.FELLOWSHIP)); // test for keywords as needed
|
||||
assertEquals(1, late.getBlueprint().getTwilightCost());
|
||||
// assertEquals(2, late.getBlueprint().getStrength());
|
||||
// assertEquals(3, late.getBlueprint().getVitality());
|
||||
//assertEquals(, late.getBlueprint().getResistance());
|
||||
//assertEquals(Signet., late.getBlueprint().getSignet());
|
||||
//assertEquals(3, late.getBlueprint().getAllyHomeSiteNumbers()[0]); // Change this to getAllyHomeSiteNumbers for allies
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void PlaysAGandalfCompanionFromDeck() throws DecisionResultInvalidException, CardNotFoundException {
|
||||
//Pre-game setup
|
||||
GenericCardTestHelper scn = GetScenario();
|
||||
|
||||
PhysicalCardImpl late = scn.GetFreepsCard("late");
|
||||
PhysicalCardImpl gandalf = scn.GetFreepsCard("gandalf");
|
||||
PhysicalCardImpl bb = scn.GetFreepsCard("bb");
|
||||
scn.FreepsMoveCardToHand(late);
|
||||
scn.FreepsMoveCardsToTopOfDeck(gandalf, bb);
|
||||
|
||||
scn.StartGame();
|
||||
|
||||
assertTrue(scn.FreepsCardPlayAvailable(late));
|
||||
assertEquals(Zone.DECK, gandalf.getZone());
|
||||
assertEquals(Zone.DECK, bb.getZone());
|
||||
|
||||
scn.FreepsPlayCard(late);
|
||||
assertTrue(scn.FreepsDecisionAvailable("Choose card from deck"));
|
||||
assertEquals(2, scn.GetFreepsCardChoiceCount());
|
||||
scn.FreepsChooseCardBPFromSelection(gandalf);
|
||||
|
||||
assertEquals(Zone.FREE_CHARACTERS, gandalf.getZone());
|
||||
assertEquals(Zone.DECK, bb.getZone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void PlaysAGandalfAllyFromDeck() throws DecisionResultInvalidException, CardNotFoundException {
|
||||
//Pre-game setup
|
||||
GenericCardTestHelper scn = GetScenario();
|
||||
|
||||
PhysicalCardImpl late = scn.GetFreepsCard("late");
|
||||
PhysicalCardImpl gandalf = scn.GetFreepsCard("gandalf");
|
||||
PhysicalCardImpl bb = scn.GetFreepsCard("bb");
|
||||
scn.FreepsMoveCardToHand(late);
|
||||
scn.FreepsMoveCardsToTopOfDeck(gandalf, bb);
|
||||
|
||||
scn.StartGame();
|
||||
|
||||
assertTrue(scn.FreepsCardPlayAvailable(late));
|
||||
assertEquals(Zone.DECK, gandalf.getZone());
|
||||
assertEquals(Zone.DECK, bb.getZone());
|
||||
|
||||
scn.FreepsPlayCard(late);
|
||||
assertTrue(scn.FreepsDecisionAvailable("Choose card from deck"));
|
||||
assertEquals(2, scn.GetFreepsCardChoiceCount());
|
||||
scn.FreepsChooseCardBPFromSelection(bb);
|
||||
|
||||
assertEquals(Zone.DECK, gandalf.getZone());
|
||||
assertEquals(Zone.SUPPORT, bb.getZone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void CanPlayWithNoGandalfCharactersInTheDeck() throws DecisionResultInvalidException, CardNotFoundException {
|
||||
//Pre-game setup
|
||||
GenericCardTestHelper scn = GetScenario();
|
||||
|
||||
PhysicalCardImpl late = scn.GetFreepsCard("late");
|
||||
PhysicalCardImpl gandalf = scn.GetFreepsCard("gandalf");
|
||||
PhysicalCardImpl bb = scn.GetFreepsCard("bb");
|
||||
scn.FreepsMoveCardToHand(late, gandalf, bb);
|
||||
|
||||
scn.StartGame();
|
||||
|
||||
assertTrue(scn.FreepsCardPlayAvailable(late));
|
||||
assertEquals(Zone.HAND, gandalf.getZone());
|
||||
assertEquals(Zone.HAND, bb.getZone());
|
||||
|
||||
scn.FreepsPlayCard(late);
|
||||
|
||||
assertEquals(Zone.DISCARD, late.getZone());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user