Fixing Power and Terror not being playable if the twilight pool does not contain at least enough to pay for an undiscounted Balrog (even if the discount would make it playable)
This commit is contained in:
@@ -1046,6 +1046,15 @@
|
||||
memory: revealedCards
|
||||
multiplier: -2
|
||||
}
|
||||
//As a play-from-hand effect, this has to be checked for playability before
|
||||
// the event resolves, even tho it's an effect and not a cost. Thus we have to
|
||||
// provide the maximum theoretical discount so we can play this card even if
|
||||
// there is less twilight in the pool than the Balrog costs.
|
||||
maxDiscount: {
|
||||
type: ForEachInHand
|
||||
filter: culture(moria),orc
|
||||
multiplier: -2
|
||||
}
|
||||
}
|
||||
}
|
||||
gametext: <b>Shadow:</b> Reveal any number of [moria] Orcs from your hand to play The Balrog. Its twilight cost is -2 for each Orc revealed.
|
||||
|
||||
@@ -24,11 +24,12 @@ import java.util.Collection;
|
||||
public class PlayCardFromHand implements EffectAppenderProducer {
|
||||
@Override
|
||||
public EffectAppender createEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
FieldUtils.validateAllowedFields(effectObject, "select", "on", "discount", "removedTwilight", "ignoreInDeadPile", "ignoreRoamingPenalty", "memorize");
|
||||
FieldUtils.validateAllowedFields(effectObject, "select", "on", "discount", "maxDiscount", "removedTwilight", "ignoreInDeadPile", "ignoreRoamingPenalty", "memorize");
|
||||
|
||||
final String select = FieldUtils.getString(effectObject.get("select"), "select");
|
||||
final String onFilter = FieldUtils.getString(effectObject.get("on"), "on");
|
||||
final ValueSource costModifierSource = ValueResolver.resolveEvaluator(effectObject.get("discount"), 0, environment);
|
||||
final ValueSource maxDiscountSource = effectObject.get("maxDiscount") == null ? costModifierSource : ValueResolver.resolveEvaluator(effectObject.get("maxDiscount"), 0, environment);
|
||||
final int removedTwilight = FieldUtils.getInteger(effectObject.get("removedTwilight"), "removedTwilight", 0);
|
||||
final boolean ignoreInDeadPile = FieldUtils.getBoolean(effectObject.get("ignoreInDeadPile"), "ignoreInDeadPile", false);
|
||||
final boolean ignoreRoamingPenalty = FieldUtils.getBoolean(effectObject.get("ignoreRoamingPenalty"), "ignoreRoamingPenalty", false);
|
||||
@@ -50,6 +51,15 @@ public class PlayCardFromHand implements EffectAppenderProducer {
|
||||
}
|
||||
return Filters.playable(game, removedTwilight, costModifier, ignoreRoamingPenalty, ignoreInDeadPile, true);
|
||||
},
|
||||
(actionContext) -> {
|
||||
final LotroGame game = actionContext.getGame();
|
||||
final int maxDiscountModifier = maxDiscountSource.getEvaluator(actionContext).evaluateExpression(game, actionContext.getSource());
|
||||
if (onFilterableSource != null) {
|
||||
final Filterable onFilterable = onFilterableSource.getFilterable(actionContext);
|
||||
return Filters.and(Filters.playable(game, maxDiscountModifier, ignoreRoamingPenalty, ignoreInDeadPile), ExtraFilters.attachableTo(game, maxDiscountModifier, onFilterable));
|
||||
}
|
||||
return Filters.playable(game, removedTwilight, maxDiscountModifier, ignoreRoamingPenalty, ignoreInDeadPile, true);
|
||||
},
|
||||
actionContext -> new ConstantEvaluator(1), memorize, "you", "you", "Choose card to play from hand", false, environment));
|
||||
result.addEffectAppender(
|
||||
new DelayedAppender() {
|
||||
|
||||
@@ -111,14 +111,18 @@ public class CardResolver {
|
||||
}
|
||||
|
||||
public static EffectAppender resolveCardsInHand(String type, ValueSource countSource, String memory, String choicePlayer, String handPlayer, String choiceText, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
return resolveCardsInHand(type, null, countSource, memory, choicePlayer, handPlayer, choiceText, false, environment);
|
||||
return resolveCardsInHand(type, null, null, countSource, memory, choicePlayer, handPlayer, choiceText, false, environment);
|
||||
}
|
||||
|
||||
public static EffectAppender resolveCardsInHand(String type, ValueSource countSource, String memory, String choicePlayer, String handPlayer, String choiceText, boolean showMatchingOnly, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
return resolveCardsInHand(type, null, countSource, memory, choicePlayer, handPlayer, choiceText, showMatchingOnly, environment);
|
||||
return resolveCardsInHand(type, null, null, countSource, memory, choicePlayer, handPlayer, choiceText, showMatchingOnly, environment);
|
||||
}
|
||||
|
||||
public static EffectAppender resolveCardsInHand(String type, FilterableSource additionalFilter, ValueSource countSource, String memory, String choicePlayer, String handPlayer, String choiceText, boolean showMatchingOnly, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
public static EffectAppender resolveCardsInHand(String type, FilterableSource choiceFilter, ValueSource countSource, String memory, String choicePlayer, String handPlayer, String choiceText, boolean showMatchingOnly, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
return resolveCardsInHand(type, choiceFilter, choiceFilter, countSource, memory, choicePlayer, handPlayer, choiceText, showMatchingOnly, environment);
|
||||
}
|
||||
|
||||
public static EffectAppender resolveCardsInHand(String type, FilterableSource choiceFilter, FilterableSource playabilityFilter, ValueSource countSource, String memory, String choicePlayer, String handPlayer, String choiceText, boolean showMatchingOnly, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
final PlayerSource handSource = PlayerResolver.resolvePlayer(handPlayer);
|
||||
Function<ActionContext, Iterable<? extends PhysicalCard>> cardSource = actionContext -> {
|
||||
String handPlayer1 = handSource.getPlayer(actionContext);
|
||||
@@ -148,11 +152,11 @@ public class CardResolver {
|
||||
}
|
||||
};
|
||||
} else if (type.equals("self")) {
|
||||
return resolveSelf(additionalFilter, additionalFilter, countSource, memory, cardSource);
|
||||
return resolveSelf(choiceFilter, playabilityFilter, countSource, memory, cardSource);
|
||||
} else if (type.startsWith("memory(") && type.endsWith(")")) {
|
||||
return resolveMemoryCards(type, additionalFilter, additionalFilter, countSource, memory, cardSource);
|
||||
return resolveMemoryCards(type, choiceFilter, playabilityFilter, countSource, memory, cardSource);
|
||||
} else if (type.startsWith("all(") && type.endsWith(")")) {
|
||||
return resolveAllCards(type, additionalFilter, memory, environment, cardSource);
|
||||
return resolveAllCards(type, choiceFilter, memory, environment, cardSource);
|
||||
} else if (type.startsWith("choose(") && type.endsWith(")")) {
|
||||
final PlayerSource playerSource = PlayerResolver.resolvePlayer(choicePlayer);
|
||||
ChoiceEffectSource effectSource = (possibleCards, action, actionContext, min, max) -> {
|
||||
@@ -181,7 +185,7 @@ public class CardResolver {
|
||||
}
|
||||
};
|
||||
|
||||
return resolveChoiceCards(type, additionalFilter, additionalFilter, countSource, environment, cardSource, effectSource);
|
||||
return resolveChoiceCards(type, choiceFilter, playabilityFilter, countSource, environment, cardSource, effectSource);
|
||||
}
|
||||
throw new InvalidCardDefinitionException("Unable to resolve card resolver of type: " + type);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package com.gempukku.lotro.cards.official.set02;
|
||||
|
||||
import com.gempukku.lotro.cards.GenericCardTestHelper;
|
||||
import com.gempukku.lotro.common.CardType;
|
||||
import com.gempukku.lotro.common.Culture;
|
||||
import com.gempukku.lotro.common.Side;
|
||||
import com.gempukku.lotro.common.Timeword;
|
||||
import com.gempukku.lotro.common.*;
|
||||
import com.gempukku.lotro.game.CardNotFoundException;
|
||||
import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException;
|
||||
import org.junit.Test;
|
||||
@@ -20,8 +17,12 @@ public class Card_02_070_Tests
|
||||
return new GenericCardTestHelper(
|
||||
new HashMap<>()
|
||||
{{
|
||||
put("card", "2_70");
|
||||
// put other cards in here as needed for the test case
|
||||
put("power", "2_70");
|
||||
put("balrog", "6_76");
|
||||
|
||||
put("orc1", "1_178");
|
||||
put("orc2", "1_179");
|
||||
put("orc3", "1_181");
|
||||
}},
|
||||
GenericCardTestHelper.FellowshipSites,
|
||||
GenericCardTestHelper.FOTRFrodo,
|
||||
@@ -46,7 +47,7 @@ public class Card_02_070_Tests
|
||||
|
||||
var scn = GetScenario();
|
||||
|
||||
var card = scn.GetFreepsCard("card");
|
||||
var card = scn.GetFreepsCard("power");
|
||||
|
||||
assertEquals("Power and Terror", card.getBlueprint().getTitle());
|
||||
assertNull(card.getBlueprint().getSubtitle());
|
||||
@@ -58,18 +59,36 @@ public class Card_02_070_Tests
|
||||
assertEquals(0, card.getBlueprint().getTwilightCost());
|
||||
}
|
||||
|
||||
// Uncomment any @Test markers below once this is ready to be used
|
||||
//@Test
|
||||
public void PowerandTerrorTest1() throws DecisionResultInvalidException, CardNotFoundException {
|
||||
@Test
|
||||
public void PowerandTerrorPlaysTheBalrogEvenWhenOnlyPlayableViaDisount() throws DecisionResultInvalidException, CardNotFoundException {
|
||||
//Pre-game setup
|
||||
var scn = GetScenario();
|
||||
|
||||
var card = scn.GetFreepsCard("card");
|
||||
scn.FreepsMoveCardToHand(card);
|
||||
var power = scn.GetShadowCard("power");
|
||||
var balrog = scn.GetShadowCard("balrog");
|
||||
var orc1 = scn.GetShadowCard("orc1");
|
||||
var orc2 = scn.GetShadowCard("orc2");
|
||||
var orc3 = scn.GetShadowCard("orc3");
|
||||
scn.ShadowMoveCardToHand(power, balrog, orc1, orc2, orc3);
|
||||
|
||||
scn.StartGame();
|
||||
scn.FreepsPlayCard(card);
|
||||
scn.SetTwilight(7);
|
||||
|
||||
assertEquals(0, scn.GetTwilight());
|
||||
scn.FreepsPassCurrentPhaseAction();
|
||||
|
||||
// Starting 7 + 1 companion + 2 from site = 10
|
||||
assertEquals(10, scn.GetTwilight());
|
||||
assertEquals(14, balrog.getBlueprint().getTwilightCost());
|
||||
|
||||
// With 3 moria orcs in hand, the maximum discount that Power and Terror can give is -6.
|
||||
// 14 - 6 + 2 (roaming) = 10, which is exactly what's on the table. P&T should thus permit the play.
|
||||
assertTrue(scn.ShadowPlayAvailable(power));
|
||||
|
||||
assertEquals(Zone.HAND, balrog.getZone());
|
||||
scn.ShadowPlayCard(power);
|
||||
assertEquals(3, scn.GetShadowCardChoiceCount());
|
||||
scn.ShadowChooseCards(orc1, orc2, orc3);
|
||||
scn.FreepsDismissRevealedCards();
|
||||
assertEquals(Zone.SHADOW_CHARACTERS, balrog.getZone());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user