From dffa05c33f7c5923ff48fbb2dfcae561feb394b2 Mon Sep 17 00:00:00 2001 From: Christian 'ketura' McCarty Date: Thu, 4 Aug 2022 05:25:04 -0500 Subject: [PATCH 1/2] Fixed issue with Dammed Gate-stream permitting Response events to be selected --- .../src/main/web/cards/set11/set11-site.json | 24 ++++ .../lotro/cards/set11/site/Card11_235.java | 42 ------- .../cards/build/BuiltLotroCardBlueprint.java | 2 +- .../effect/appender/PlayCardFromDiscard.java | 4 +- .../effect/appender/PlayCardFromDrawDeck.java | 2 +- .../effect/appender/PlayCardFromStacked.java | 2 +- .../effect/modifier/CanPlayStackedCards.java | 2 +- .../com/gempukku/lotro/filters/Filters.java | 10 +- .../com/gempukku/lotro/logic/PlayUtils.java | 53 +++++++- .../lotro/logic/timing/ExtraFilters.java | 2 +- .../lotro/logic/timing/PlayConditions.java | 8 +- ...yerPlaysStartingFellowshipGameProcess.java | 2 +- .../timing/rules/PlayCardInPhaseRule.java | 18 +-- .../timing/rules/PlayResponseEventRule.java | 4 +- .../com/gempukku/lotro/at/AbstractAtTest.java | 6 +- .../lotro/cards/GenericCardTestHelper.java | 27 +++- .../official/set11/Card_11_235_Tests.java | 119 ++++++++++++++++++ 17 files changed, 244 insertions(+), 83 deletions(-) create mode 100644 gemp-lotr/gemp-lotr-async/src/main/web/cards/set11/set11-site.json delete mode 100644 gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/set11/site/Card11_235.java create mode 100644 gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set11/Card_11_235_Tests.java diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/cards/set11/set11-site.json b/gemp-lotr/gemp-lotr-async/src/main/web/cards/set11/set11-site.json new file mode 100644 index 000000000..a51547276 --- /dev/null +++ b/gemp-lotr/gemp-lotr-async/src/main/web/cards/set11/set11-site.json @@ -0,0 +1,24 @@ +{ + + "11_235": { + "title": "Dammed Gate-stream", + "type": "site", + "block": "Shadows", + "cost": 3, + "keyword": "Marsh", + "direction": "right", + "effects": { + "type": "trigger", + "trigger": { + "type": "startOfPhase", + "phase": "fellowship" + }, + "optional": true, + "effect": { + "type": "playCardFromDrawDeck", + "filter": "choose(culture(gollum),side(free peoples))" + } + } + }, + +} \ No newline at end of file diff --git a/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/set11/site/Card11_235.java b/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/set11/site/Card11_235.java deleted file mode 100644 index daccecd89..000000000 --- a/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/set11/site/Card11_235.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gempukku.lotro.cards.set11.site; - -import com.gempukku.lotro.logic.cardtype.AbstractShadowsSite; -import com.gempukku.lotro.logic.timing.TriggerConditions; -import com.gempukku.lotro.logic.effects.choose.ChooseAndPlayCardFromDeckEffect; -import com.gempukku.lotro.common.Culture; -import com.gempukku.lotro.common.Keyword; -import com.gempukku.lotro.common.Phase; -import com.gempukku.lotro.common.Side; -import com.gempukku.lotro.game.PhysicalCard; -import com.gempukku.lotro.game.state.LotroGame; -import com.gempukku.lotro.logic.actions.OptionalTriggerAction; -import com.gempukku.lotro.logic.timing.EffectResult; - -import java.util.Collections; -import java.util.List; - -/** - * Set: Shadows - * Twilight Cost: 3 - * Type: Site - * Game Text: Marsh. At the start of your fellowship phase, you may play a [GOLLUM] Free Peoples card from your draw - * deck. - */ -public class Card11_235 extends AbstractShadowsSite { - public Card11_235() { - super("Dammed Gate-stream", 3, Direction.RIGHT); - addKeyword(Keyword.MARSH); - } - - @Override - public List getOptionalAfterTriggers(String playerId, LotroGame game, EffectResult effectResult, PhysicalCard self) { - if (TriggerConditions.startOfPhase(game, effectResult, Phase.FELLOWSHIP) - && playerId.equals(game.getGameState().getCurrentPlayerId())) { - OptionalTriggerAction action = new OptionalTriggerAction(self); - action.appendEffect( - new ChooseAndPlayCardFromDeckEffect(playerId, Culture.GOLLUM, Side.FREE_PEOPLE)); - return Collections.singletonList(action); - } - return null; - } -} diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/BuiltLotroCardBlueprint.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/BuiltLotroCardBlueprint.java index 4367c5a7a..fd02958fc 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/BuiltLotroCardBlueprint.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/BuiltLotroCardBlueprint.java @@ -802,7 +802,7 @@ public class BuiltLotroCardBlueprint implements LotroCardBlueprint { for (Requirement playInOtherPhaseCondition : playInOtherPhaseConditions) { DefaultActionContext actionContext = new DefaultActionContext(playerId, game, self, null, null); if (playInOtherPhaseCondition.accepts(actionContext) - && PlayUtils.checkPlayRequirements(game, self, Filters.any, 0, 0, false, false)) + && PlayUtils.checkPlayRequirements(game, self, Filters.any, 0, 0, false, false, false)) playCardActions.add(PlayUtils.getPlayCardAction(game, self, 0, Filters.any, false)); } diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromDiscard.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromDiscard.java index dc130ff2b..f858c6cdb 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromDiscard.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromDiscard.java @@ -64,10 +64,10 @@ public class PlayCardFromDiscard implements EffectAppenderProducer { final int costModifier = costModifierSource.getEvaluator(actionContext).evaluateExpression(game, actionContext.getSource()); if (onFilterableSource != null) { final Filterable onFilterable = onFilterableSource.getFilterable(actionContext); - return Filters.and(Filters.playable(actionContext.getGame(), removedTwilight, costModifier, false, false), ExtraFilters.attachableTo(actionContext.getGame(), onFilterable)); + return Filters.and(Filters.playable(actionContext.getGame(), removedTwilight, costModifier, false, false, true), ExtraFilters.attachableTo(actionContext.getGame(), onFilterable)); } - return Filters.playable(actionContext.getGame(), removedTwilight, costModifier, false, false); + return Filters.playable(actionContext.getGame(), removedTwilight, costModifier, false, false, true); }, countSource, memorize, "you", "Choose card to play", environment)); result.addEffectAppender( diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromDrawDeck.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromDrawDeck.java index 7e0c8e7b1..5421191ae 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromDrawDeck.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromDrawDeck.java @@ -56,7 +56,7 @@ public class PlayCardFromDrawDeck implements EffectAppenderProducer { final Filterable onFilterable = onFilterableSource.getFilterable(actionContext); return Filters.and(Filters.playable(game, costModifier), ExtraFilters.attachableTo(game, onFilterable)); } - return Filters.playable(actionContext.getGame(), costModifier); + return Filters.playable(actionContext.getGame(), costModifier, false, false, true); }, countSource, memorize, "you", "you", "Choose card to play", environment)); result.addEffectAppender( diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromStacked.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromStacked.java index 8d48dbf52..cdb53d70f 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromStacked.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PlayCardFromStacked.java @@ -44,7 +44,7 @@ public class PlayCardFromStacked implements EffectAppenderProducer { result.addEffectAppender( CardResolver.resolveStackedCards(filter, actionContext -> Filters.playable(actionContext.getGame()), - actionContext -> Filters.playable(actionContext.getGame(), removedTwilight, 0, false, false), + actionContext -> Filters.playable(actionContext.getGame(), removedTwilight, 0, false, false, true), countSource, onFilterableSource, "_temp", "you", "Choose card to play", environment)); result.addEffectAppender( new DelayedAppender() { diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/modifier/CanPlayStackedCards.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/modifier/CanPlayStackedCards.java index 18e275481..1f09acb30 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/modifier/CanPlayStackedCards.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/modifier/CanPlayStackedCards.java @@ -36,7 +36,7 @@ public class CanPlayStackedCards implements ModifierSourceProducer { new RequirementCondition(requirements, actionContext), ModifierEffect.EXTRA_ACTION_MODIFIER) { @Override public List getExtraPhaseActionFromStacked(LotroGame game, PhysicalCard card) { - if (PlayUtils.checkPlayRequirements(game, card, Filters.any, 0, 0, false, false)) + if (PlayUtils.checkPlayRequirements(game, card, Filters.any, 0, 0, false, false, false)) return Collections.singletonList( PlayUtils.getPlayCardAction(game, card, 0, Filters.any, false)); return null; diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/filters/Filters.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/filters/Filters.java index 2a647e8f0..06445e0fd 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/filters/Filters.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/filters/Filters.java @@ -589,10 +589,14 @@ public class Filters { } public static Filter playable(final LotroGame game, final int twilightModifier, final boolean ignoreRoamingPenalty, final boolean ignoreCheckingDeadPile) { - return playable(game, 0, twilightModifier, ignoreRoamingPenalty, ignoreCheckingDeadPile); + return playable(game, 0, twilightModifier, ignoreRoamingPenalty, ignoreCheckingDeadPile, false); } - public static Filter playable(final LotroGame game, final int withTwilightRemoved, final int twilightModifier, final boolean ignoreRoamingPenalty, final boolean ignoreCheckingDeadPile) { + public static Filter playable(final LotroGame game, final int twilightModifier, final boolean ignoreRoamingPenalty, final boolean ignoreCheckingDeadPile, final boolean ignoreResponseEvents) { + return playable(game, 0, twilightModifier, ignoreRoamingPenalty, ignoreCheckingDeadPile, ignoreResponseEvents); + } + + public static Filter playable(final LotroGame game, final int withTwilightRemoved, final int twilightModifier, final boolean ignoreRoamingPenalty, final boolean ignoreCheckingDeadPile, final boolean ignoreResponseEvents) { return new Filter() { @Override public boolean accepts(LotroGame game, PhysicalCard physicalCard) { @@ -601,7 +605,7 @@ public class Filters { if (blueprint.getSide() != expectedSide) return false; - return PlayUtils.checkPlayRequirements(game, physicalCard, Filters.any, withTwilightRemoved, twilightModifier, ignoreRoamingPenalty, ignoreCheckingDeadPile); + return PlayUtils.checkPlayRequirements(game, physicalCard, Filters.any, withTwilightRemoved, twilightModifier, ignoreRoamingPenalty, ignoreCheckingDeadPile, ignoreResponseEvents); } }; } diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/PlayUtils.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/PlayUtils.java index 7f91e5c59..e1f2fe456 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/PlayUtils.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/PlayUtils.java @@ -1,20 +1,25 @@ package com.gempukku.lotro.logic; -import com.gempukku.lotro.common.CardType; -import com.gempukku.lotro.common.Filterable; -import com.gempukku.lotro.common.Side; -import com.gempukku.lotro.common.Zone; +import com.gempukku.lotro.common.*; import com.gempukku.lotro.filters.Filter; import com.gempukku.lotro.filters.Filters; import com.gempukku.lotro.game.LotroCardBlueprint; import com.gempukku.lotro.game.PhysicalCard; import com.gempukku.lotro.game.state.LotroGame; +import com.gempukku.lotro.game.state.actions.DefaultActionsEnvironment; import com.gempukku.lotro.logic.actions.AttachPermanentAction; import com.gempukku.lotro.logic.actions.CostToEffectAction; import com.gempukku.lotro.logic.actions.PlayEventAction; import com.gempukku.lotro.logic.actions.PlayPermanentAction; +import com.gempukku.lotro.logic.timing.Action; import com.gempukku.lotro.logic.timing.PlayConditions; import com.gempukku.lotro.logic.timing.RuleUtils; +import com.google.common.collect.ImmutableMap; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; public class PlayUtils { private static Zone getPlayToZone(PhysicalCard card) { @@ -29,6 +34,27 @@ public class PlayUtils { } } + public static Map PhaseKeywordMap = ImmutableMap.copyOf(new HashMap() {{ + put(Phase.FELLOWSHIP, Keyword.FELLOWSHIP); + put(Phase.SHADOW, Keyword.SHADOW); + put(Phase.MANEUVER, Keyword.MANEUVER); + put(Phase.ARCHERY, Keyword.ARCHERY); + put(Phase.ASSIGNMENT, Keyword.ASSIGNMENT); + put(Phase.SKIRMISH, Keyword.SKIRMISH); + put(Phase.REGROUP, Keyword.REGROUP); + }}); + +// static { +// phaseKeywordMap = new HashMap<>(); +// phaseKeywordMap.put(Phase.FELLOWSHIP, Keyword.FELLOWSHIP); +// phaseKeywordMap.put(Phase.SHADOW, Keyword.SHADOW); +// phaseKeywordMap.put(Phase.MANEUVER, Keyword.MANEUVER); +// phaseKeywordMap.put(Phase.ARCHERY, Keyword.ARCHERY); +// phaseKeywordMap.put(Phase.ASSIGNMENT, Keyword.ASSIGNMENT); +// phaseKeywordMap.put(Phase.SKIRMISH, Keyword.SKIRMISH); +// phaseKeywordMap.put(Phase.REGROUP, Keyword.REGROUP); +// } + private static Filter getFullAttachValidTargetFilter(final LotroGame game, final PhysicalCard card, int twilightModifier, int withTwilightRemoved) { return Filters.and(RuleUtils.getFullValidTargetFilter(card.getOwner(), game, card), @@ -84,6 +110,10 @@ public class PlayUtils { } public static boolean checkPlayRequirements(LotroGame game, PhysicalCard card, Filterable additionalAttachmentFilter, int withTwilightRemoved, int twilightModifier, boolean ignoreRoamingPenalty, boolean ignoreCheckingDeadPile) { + return checkPlayRequirements(game, card, additionalAttachmentFilter, withTwilightRemoved, twilightModifier, ignoreRoamingPenalty, ignoreCheckingDeadPile, false); + } + + public static boolean checkPlayRequirements(LotroGame game, PhysicalCard card, Filterable additionalAttachmentFilter, int withTwilightRemoved, int twilightModifier, boolean ignoreRoamingPenalty, boolean ignoreCheckingDeadPile, boolean ignoreResponseEvents) { final LotroCardBlueprint blueprint = card.getBlueprint(); // Check if card's own play requirements are met @@ -113,9 +143,22 @@ public class PlayUtils { return false; if (blueprint.getCardType() == CardType.COMPANION - && !(PlayConditions.checkRuleOfNine(game, card) && PlayConditions.checkPlayRingBearer(game, card))) + && !(PlayConditions.checkRuleOfNine(game, card) && PlayConditions.checkPlayRingBearer(game, card))) return false; + if(blueprint.getCardType() == CardType.EVENT) + { + if(game.getModifiersQuerying().hasKeyword(game, card, Keyword.RESPONSE)) { + if (ignoreResponseEvents) + return false; + } + else { + final Keyword phaseKeyword = PhaseKeywordMap.get(game.getGameState().getCurrentPhase()); + if (phaseKeyword != null && !game.getModifiersQuerying().hasKeyword(game, card, phaseKeyword)) + return false; + } + } + return (blueprint.getSide() != Side.SHADOW || PlayConditions.canPayForShadowCard(game, card, finalTargetFilter, withTwilightRemoved, twilightModifier, ignoreRoamingPenalty)); } } diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/ExtraFilters.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/ExtraFilters.java index 97b8e6a76..2f3a11d18 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/ExtraFilters.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/ExtraFilters.java @@ -19,7 +19,7 @@ public class ExtraFilters { public boolean accepts(LotroGame game, PhysicalCard physicalCard) { if (physicalCard.getBlueprint().getValidTargetFilter(physicalCard.getOwner(), game, physicalCard) == null) return false; - return PlayUtils.checkPlayRequirements(game, physicalCard, Filters.and(filters), 0, twilightModifier, false, false); + return PlayUtils.checkPlayRequirements(game, physicalCard, Filters.and(filters), 0, twilightModifier, false, false, true); } }); } diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/PlayConditions.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/PlayConditions.java index 0d226bb36..a194179db 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/PlayConditions.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/PlayConditions.java @@ -327,7 +327,7 @@ public class PlayConditions { } public static boolean canPlayFromHand(String playerId, LotroGame game, int withTwilightRemoved, int twilightModifier, boolean ignoreRoamingPenalty, boolean ignoreCheckingDeadPile, Filterable... filters) { - return Filters.filter(game.getGameState().getHand(playerId), game, Filters.and(filters, Filters.playable(game, withTwilightRemoved, twilightModifier, ignoreRoamingPenalty, ignoreCheckingDeadPile))).size() > 0; + return Filters.filter(game.getGameState().getHand(playerId), game, Filters.and(filters, Filters.playable(game, withTwilightRemoved, twilightModifier, ignoreRoamingPenalty, ignoreCheckingDeadPile, false))).size() > 0; } public static boolean canPlayFromDeadPile(String playerId, LotroGame game, Filterable... filters) { @@ -347,7 +347,7 @@ public class PlayConditions { public static boolean canPlayFromStacked(String playerId, LotroGame game, int withTwilightRemoved, Filterable stackedOn, Filterable... filters) { final Collection matchingStackedOn = Filters.filterActive(game, stackedOn); for (PhysicalCard stackedOnCard : matchingStackedOn) { - if (Filters.filter(game.getGameState().getStackedCards(stackedOnCard), game, Filters.and(filters, Filters.playable(game, withTwilightRemoved, 0, false, false))).size() > 0) + if (Filters.filter(game.getGameState().getStackedCards(stackedOnCard), game, Filters.and(filters, Filters.playable(game, withTwilightRemoved, 0, false, false, false))).size() > 0) return true; } @@ -357,7 +357,7 @@ public class PlayConditions { public static boolean canPlayFromStacked(String playerId, LotroGame game, int withTwilightRemoved, int twilightModifier, Filterable stackedOn, Filterable... filters) { final Collection matchingStackedOn = Filters.filterActive(game, stackedOn); for (PhysicalCard stackedOnCard : matchingStackedOn) { - if (Filters.filter(game.getGameState().getStackedCards(stackedOnCard), game, Filters.and(filters, Filters.playable(game, withTwilightRemoved, twilightModifier, false, false))).size() > 0) + if (Filters.filter(game.getGameState().getStackedCards(stackedOnCard), game, Filters.and(filters, Filters.playable(game, withTwilightRemoved, twilightModifier, false, false, false))).size() > 0) return true; } @@ -379,7 +379,7 @@ public class PlayConditions { public static boolean canPlayFromDiscard(String playerId, LotroGame game, int withTwilightRemoved, int modifier, Filterable... filters) { if (game.getModifiersQuerying().hasFlagActive(game, ModifierFlag.CANT_PLAY_FROM_DISCARD_OR_DECK)) return false; - return Filters.filter(game.getGameState().getDiscard(playerId), game, Filters.and(filters, Filters.playable(game, withTwilightRemoved, modifier, false, false))).size() > 0; + return Filters.filter(game.getGameState().getDiscard(playerId), game, Filters.and(filters, Filters.playable(game, withTwilightRemoved, modifier, false, false, false))).size() > 0; } public static boolean canDiscardFromPlay(final PhysicalCard source, LotroGame game, final PhysicalCard card) { diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/processes/pregame/PlayerPlaysStartingFellowshipGameProcess.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/processes/pregame/PlayerPlaysStartingFellowshipGameProcess.java index 12e592913..8ddd2c2c0 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/processes/pregame/PlayerPlaysStartingFellowshipGameProcess.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/processes/pregame/PlayerPlaysStartingFellowshipGameProcess.java @@ -46,7 +46,7 @@ public class PlayerPlaysStartingFellowshipGameProcess implements GameProcess { public boolean accepts(LotroGame game, PhysicalCard physicalCard) { int twilightCost = game.getModifiersQuerying().getTwilightCost(game, physicalCard, null, 0, false); return game.getGameState().getTwilightPool() + twilightCost <= 4 - && PlayUtils.checkPlayRequirements(game, physicalCard, Filters.any, 0, 0, false, false); + && PlayUtils.checkPlayRequirements(game, physicalCard, Filters.any, 0, 0, false, false, true); } }); } diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/rules/PlayCardInPhaseRule.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/rules/PlayCardInPhaseRule.java index 3ece27502..d6cff60f0 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/rules/PlayCardInPhaseRule.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/rules/PlayCardInPhaseRule.java @@ -19,19 +19,9 @@ import java.util.List; public class PlayCardInPhaseRule { private DefaultActionsEnvironment actionsEnvironment; - private final HashMap phaseKeywordMap; public PlayCardInPhaseRule(DefaultActionsEnvironment actionsEnvironment) { this.actionsEnvironment = actionsEnvironment; - - phaseKeywordMap = new HashMap<>(); - phaseKeywordMap.put(Phase.FELLOWSHIP, Keyword.FELLOWSHIP); - phaseKeywordMap.put(Phase.SHADOW, Keyword.SHADOW); - phaseKeywordMap.put(Phase.MANEUVER, Keyword.MANEUVER); - phaseKeywordMap.put(Phase.ARCHERY, Keyword.ARCHERY); - phaseKeywordMap.put(Phase.ASSIGNMENT, Keyword.ASSIGNMENT); - phaseKeywordMap.put(Phase.SKIRMISH, Keyword.SKIRMISH); - phaseKeywordMap.put(Phase.REGROUP, Keyword.REGROUP); } public void applyRule() { @@ -46,7 +36,7 @@ public class PlayCardInPhaseRule { List result = new LinkedList<>(); for (PhysicalCard card : Filters.filter(game.getGameState().getHand(playerId), game, side, Filters.or(Filters.and(CardType.EVENT, Keyword.FELLOWSHIP), Filters.not(CardType.EVENT)))) { - if (PlayUtils.checkPlayRequirements(game, card, Filters.any, 0, 0, false, false)) + if (PlayUtils.checkPlayRequirements(game, card, Filters.any, 0, 0, false, false, true)) result.add(PlayUtils.getPlayCardAction(game, card, 0, Filters.any, false)); } return result; @@ -56,18 +46,18 @@ public class PlayCardInPhaseRule { List result = new LinkedList<>(); for (PhysicalCard card : Filters.filter(game.getGameState().getHand(playerId), game, side, Filters.or(Filters.and(CardType.EVENT, Keyword.SHADOW), Filters.not(CardType.EVENT)))) { - if (PlayUtils.checkPlayRequirements(game, card, Filters.any, 0, 0, false, false)) + if (PlayUtils.checkPlayRequirements(game, card, Filters.any, 0, 0, false, false, true)) result.add(PlayUtils.getPlayCardAction(game, card, 0, Filters.any, false)); } return result; } } else { - final Keyword phaseKeyword = phaseKeywordMap.get(game.getGameState().getCurrentPhase()); + final Keyword phaseKeyword = PlayUtils.PhaseKeywordMap.get(game.getGameState().getCurrentPhase()); if (phaseKeyword != null) { List result = new LinkedList<>(); for (PhysicalCard card : Filters.filter(game.getGameState().getHand(playerId), game, side, Filters.and(CardType.EVENT, phaseKeyword))) { - if (PlayUtils.checkPlayRequirements(game, card, Filters.any, 0, 0, false, false)) + if (PlayUtils.checkPlayRequirements(game, card, Filters.any, 0, 0, false, false, true)) result.add(PlayUtils.getPlayCardAction(game, card, 0, Filters.any, false)); } return result; diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/rules/PlayResponseEventRule.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/rules/PlayResponseEventRule.java index 912e069a8..1b67ee2fb 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/rules/PlayResponseEventRule.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/rules/PlayResponseEventRule.java @@ -33,7 +33,7 @@ public class PlayResponseEventRule { List result = new LinkedList<>(); final Side side = GameUtils.getSide(game, playerId); for (PhysicalCard responseEvent : Filters.filter(game.getGameState().getHand(playerId), game, side, CardType.EVENT, Keyword.RESPONSE)) { - if (PlayUtils.checkPlayRequirements(game, responseEvent, Filters.any, 0, 0, false, false)) { + if (PlayUtils.checkPlayRequirements(game, responseEvent, Filters.any, 0, 0, false, false, false)) { final List actions = responseEvent.getBlueprint().getPlayResponseEventAfterActions(playerId, game, effectResult, responseEvent); if (actions != null) result.addAll(actions); @@ -47,7 +47,7 @@ public class PlayResponseEventRule { List result = new LinkedList<>(); final Side side = GameUtils.getSide(game, playerId); for (PhysicalCard responseEvent : Filters.filter(game.getGameState().getHand(playerId), game, side, CardType.EVENT, Keyword.RESPONSE)) { - if (PlayUtils.checkPlayRequirements(game, responseEvent, Filters.any, 0, 0, false, false)) { + if (PlayUtils.checkPlayRequirements(game, responseEvent, Filters.any, 0, 0, false, false, false)) { final List actions = responseEvent.getBlueprint().getPlayResponseEventBeforeActions(playerId, game, effect, responseEvent); if (actions != null) result.addAll(actions); 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 63a258bfe..8b77b7910 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 @@ -51,10 +51,14 @@ public abstract class AbstractAtTest { } protected void initializeGameWithDecks(Map decks) throws DecisionResultInvalidException { + initializeGameWithDecks(decks, "multipath"); + } + + protected void initializeGameWithDecks(Map decks, String formatName) throws DecisionResultInvalidException { _userFeedback = new DefaultUserFeedback(); LotroFormatLibrary formatLibrary = new LotroFormatLibrary(new DefaultAdventureLibrary(), _library); - LotroFormat format = formatLibrary.getFormat("multipath"); + LotroFormat format = formatLibrary.getFormat(formatName); _game = new DefaultLotroGame(format, decks, _userFeedback, _library); _userFeedback.setGame(_game); diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/GenericCardTestHelper.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/GenericCardTestHelper.java index 27e9ca86d..114493bb5 100644 --- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/GenericCardTestHelper.java +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/GenericCardTestHelper.java @@ -69,14 +69,16 @@ public class GenericCardTestHelper extends AbstractAtTest { public Map> Cards = new HashMap<>(); public GenericCardTestHelper(HashMap cardIDs) throws CardNotFoundException, DecisionResultInvalidException { - this(cardIDs, null, null, null); + this(cardIDs, null, null, null, "multipath"); } public GenericCardTestHelper(HashMap cardIDs, HashMap siteIDs, String ringBearerID, String ringID) throws CardNotFoundException, DecisionResultInvalidException { + this(cardIDs, siteIDs, ringBearerID, ringID, "multipath"); + } + + public GenericCardTestHelper(HashMap cardIDs, HashMap siteIDs, String ringBearerID, String ringID, String path) throws CardNotFoundException, DecisionResultInvalidException { super(); - - if(siteIDs == null || ringBearerID == null || ringID == null) { initializeSimplestGame(); } @@ -97,7 +99,7 @@ public class GenericCardTestHelper extends AbstractAtTest { decks.get(P1).setRing(ringID); decks.get(P2).setRing(ringID); - initializeGameWithDecks(decks); + initializeGameWithDecks(decks, path); } Cards.put(P1, new HashMap<>()); @@ -122,6 +124,15 @@ public class GenericCardTestHelper extends AbstractAtTest { skipMulligans(); } + public void SkipStartingFellowships() throws DecisionResultInvalidException { + if(FreepsDecisionAvailable("Starting fellowship")) { + FreepsChoose(""); + } + if(ShadowDecisionAvailable("Starting fellowship")) { + ShadowChoose(""); + } + } + public PhysicalCardImpl GetFreepsCard(String cardName) { return Cards.get(P1).get(cardName); } public PhysicalCardImpl GetShadowCard(String cardName) { return Cards.get(P2).get(cardName); } public PhysicalCardImpl GetFreepsCardByID(String id) { return GetCardByID(P1, Integer.parseInt(id)); } @@ -145,6 +156,14 @@ public class GenericCardTestHelper extends AbstractAtTest { List advDeck = (List)_game.getGameState().getAdventureDeck(playerID); return advDeck.stream().filter(x -> x.getBlueprint().getSiteNumber() == siteNum).findFirst().get(); } + public PhysicalCardImpl GetFreepsSite(String name) { return GetSiteByName(P1, name); } + public PhysicalCardImpl GetShadowSite(String name) { return GetSiteByName(P2, name); } + public PhysicalCardImpl GetSiteByName(String playerID, String name) + { + final String lowername = name.toLowerCase(); + List advDeck = (List)_game.getGameState().getAdventureDeck(playerID); + return advDeck.stream().filter(x -> x.getBlueprint().getTitle().toLowerCase().contains(lowername)).findFirst().get(); + } public List FreepsGetAvailableActions() { return GetAvailableActions(P1); } public List ShadowGetAvailableActions() { return GetAvailableActions(P2); } diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set11/Card_11_235_Tests.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set11/Card_11_235_Tests.java new file mode 100644 index 000000000..39af18070 --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/official/set11/Card_11_235_Tests.java @@ -0,0 +1,119 @@ + +package com.gempukku.lotro.cards.official.set11; + +import com.gempukku.lotro.cards.GenericCardTestHelper; +import com.gempukku.lotro.common.CardType; +import com.gempukku.lotro.common.Keyword; +import com.gempukku.lotro.common.Zone; +import com.gempukku.lotro.game.CardNotFoundException; +import com.gempukku.lotro.game.PhysicalCardImpl; +import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException; +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_11_235_Tests +{ + + protected GenericCardTestHelper GetScenario() throws CardNotFoundException, DecisionResultInvalidException { + return new GenericCardTestHelper( + new HashMap() + {{ + put("dontlook", "6_39"); + put("smeagol", "7_71"); + put("boat", "13_48"); + put("slippery", "6_43"); + put("clever", "7_54"); + }}, + new HashMap() {{ + put("site1", "11_235"); + put("site2", "18_138"); + put("site3", "18_138"); + put("site4", "18_138"); + put("site5", "18_138"); + put("site6", "18_138"); + put("site7", "18_138"); + put("site8", "18_138"); + put("site9", "18_138"); + }}, + GenericCardTestHelper.FOTRFrodo, + GenericCardTestHelper.FOTRRing, + "open" + ); + } + + @Test + public void DammedGateStreamStatsAndKeywordsAreCorrect() throws DecisionResultInvalidException, CardNotFoundException { + + /** + * Set: 11 + * Title: Dammed Gate-stream + * Side: Site + * Culture: Site + * Twilight Cost: 3 + * Type: Site + * Site Number: * + * Game Text: Marsh. At the start of your fellowship phase, you may play a [GOLLUM] Free Peoples card from your draw deck. + */ + + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + PhysicalCardImpl site1 = scn.GetFreepsSite("Dammed Gate-stream"); + + assertFalse(site1.getBlueprint().isUnique()); + //assertEquals(Side.FREE_PEOPLE, site1.getBlueprint().getSide()); + //assertEquals(Culture., site1.getBlueprint().getCulture()); + assertEquals(CardType.SITE, site1.getBlueprint().getCardType()); + //assertEquals(Race.CREATURE, site1.getBlueprint().getRace()); + assertTrue(scn.HasKeyword(site1, Keyword.MARSH)); // test for keywords as needed + assertEquals(3, site1.getBlueprint().getTwilightCost()); + //assertEquals(, site1.getBlueprint().getStrength()); + //assertEquals(, site1.getBlueprint().getVitality()); + //assertEquals(, site1.getBlueprint().getResistance()); + //assertEquals(Signet., site1.getBlueprint().getSignet()); + assertEquals(0, site1.getBlueprint().getSiteNumber()); // Change this to getAllyHomeSiteNumbers for allies + + } + + @Test + public void StartOfFellowshipPhaseActionAllowsPlayFromDrawDeck() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTestHelper scn = GetScenario(); + + PhysicalCardImpl frodo = scn.GetRingBearer(); + PhysicalCardImpl dontlook = scn.GetFreepsCard("dontlook"); + PhysicalCardImpl smeagol = scn.GetFreepsCard("smeagol"); + PhysicalCardImpl slippery = scn.GetFreepsCard("slippery"); + PhysicalCardImpl boat = scn.GetFreepsCard("boat"); + PhysicalCardImpl clever = scn.GetFreepsCard("clever"); + PhysicalCardImpl site1 = scn.GetFreepsSite("Dammed Gate-stream"); + + scn.FreepsChooseCardBPFromSelection(site1); + scn.SkipStartingFellowships(); + //Apparently it's drawing cards now. It doesn't do this in other formats. + scn.FreepsMoveCardsToBottomOfDeck(dontlook, smeagol, slippery, boat, clever); + scn.StartGame(); + + assertTrue(scn.FreepsHasOptionalTriggerAvailable()); + scn.FreepsAcceptOptionalTrigger(); + + //Not Listening is a response event, it shouldn't show up + //Clever Hobbits is a skirmish event, also shouldn't show + //Smeagol and Don't Look At Them are valid. Fishing Boat would theoretically be valid if Smeagol were in play. + assertEquals(2, scn.GetFreepsCardChoiceCount()); + + assertEquals(Zone.DECK, dontlook.getZone()); + assertEquals(Zone.DECK, smeagol.getZone()); + scn.FreepsChooseCardBPFromSelection(dontlook); + + assertEquals(Zone.SUPPORT, dontlook.getZone()); + assertEquals(Zone.DECK, smeagol.getZone()); + + } + +} From b8185fb60d93df209e41fc7efe0955dee592d0c7 Mon Sep 17 00:00:00 2001 From: Christian 'ketura' McCarty Date: Thu, 4 Aug 2022 05:27:26 -0500 Subject: [PATCH 2/2] Updating changelog --- .../gemp-lotr-async/src/main/web/includes/changeLog.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/includes/changeLog.html b/gemp-lotr/gemp-lotr-async/src/main/web/includes/changeLog.html index 33a4f44f3..6b6ac8cf8 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/web/includes/changeLog.html +++ b/gemp-lotr/gemp-lotr-async/src/main/web/includes/changeLog.html @@ -1,12 +1,18 @@
 Most recent update
 
+2022 August 04
+- New players now have all of the library decks in the main hall to use for matches, which persist until they make their first deck.
+- Adjusted the canonical ordering of formats and decks to keep the 3 most-used formats at the top, and the rest in chronological order
+- Fixed Dammed Gate-stream (11U235) causing a crash by selecting a Response event.
+- Due to the above, if you see any issues with playing Response events in general, please let us know ASAP
+
 2022 August 03
 - Added Library Decks, which are a shared set of decks that anyone can load (tho not save or edit)
 - Added all starter decks and as many Worlds championship 1/2 decks that we could find to the Library
 - Improved the format listings so that all formats are sorted according to chronological order, rather than alphabetical.
 - Added ability to rename decks while on the deck selection window
-- New players now have all of the library decks in the main hall, which persist until they make their first deck.
+
 
 2022 August 02
 - Fixed each card draw pausing between cards