diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set03/set3-shire.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set03/set3-shire.hjson index 091d7b599..3cdf6d1fb 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set03/set3-shire.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set03/set3-shire.hjson @@ -162,9 +162,9 @@ type: trigger optional: true trigger: { - type: discardFromHandBy - by: side(shadow) - forced: true + type: discarded + source: side(shadow) + ignoreVoluntary: true } effect: { type: discard diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set07/set07-Sauron.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set07/set07-Sauron.hjson index a4ec06e84..66660a159 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set07/set07-Sauron.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/official/set07/set07-Sauron.hjson @@ -1374,7 +1374,7 @@ type: Response trigger: { type: beforeThreatWounds - killedFilter: companion + filter: companion deathCause: overwhelm } requires: [ @@ -1382,7 +1382,8 @@ type: canSpot filter: culture(sauron),orc,inSkirmish } - // TODO: Not sure why this is here, it was in the source code... maybe not needed + // 4bc90765940487de4959dfcee32ad8c130427016 + // "Mordor Assassin no longer crashes the game if the RB is overwhelmed" { type: canSpot filter: ringBearer diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set30/set30-fp.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set30/set30-fp.hjson index ba0593abe..6234a620b 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set30/set30-fp.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/hobbit/set30/set30-fp.hjson @@ -1152,8 +1152,9 @@ type: trigger optional: true trigger: { - type: discardFromHandBy - by: follower + type: discarded + fromZone: hand + source: follower } effect: { type: drawCards diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/setV01/V1_dwarven.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/setV01/V1_dwarven.hjson index a4473c526..40635f8b0 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/setV01/V1_dwarven.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/pc/setV01/V1_dwarven.hjson @@ -85,8 +85,9 @@ type: trigger optional: true trigger: { - type: discardfromdeck + type: discarded filter: your,culture(dwarven) + fromZone: deck memorize: topcard } cost:{ diff --git a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/second-edition/set40/set40-moria.hjson b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/second-edition/set40/set40-moria.hjson index 6ccaae84c..341f33a09 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/second-edition/set40/set40-moria.hjson +++ b/gemp-lotr/gemp-lotr-cards/src/main/resources/cards/unofficial/second-edition/set40/set40-moria.hjson @@ -670,7 +670,8 @@ { type: Response trigger: { - type: discardFromHand + type: discarded + fromZone: hand filter: goblin memorize: discardedGoblin } diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/BeforeThreatWounds.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/BeforeThreatWounds.java index f83454280..6ff71b3f8 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/BeforeThreatWounds.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/BeforeThreatWounds.java @@ -18,9 +18,9 @@ import java.util.Set; public class BeforeThreatWounds implements TriggerCheckerProducer { @Override public TriggerChecker getTriggerChecker(JSONObject value, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { - FieldUtils.validateAllowedFields(value, "killedFilter", "deathCause"); + FieldUtils.validateAllowedFields(value, "filter", "deathCause"); - String filter = FieldUtils.getString(value.get("killedFilter"), "killedFilter"); + String filter = FieldUtils.getString(value.get("filter"), "filter"); KillEffect.Cause deathCause = FieldUtils.getEnum(KillEffect.Cause.class, value.get("deathCause"), "deathCause"); final FilterableSource affectedFilter = environment.getFilterFactory().generateFilter(filter, environment); diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/DiscardFromDeck.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/DiscardFromDeck.java deleted file mode 100644 index 49de853d9..000000000 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/DiscardFromDeck.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gempukku.lotro.cards.build.field.effect.trigger; - -import com.gempukku.lotro.cards.build.*; -import com.gempukku.lotro.cards.build.field.FieldUtils; -import com.gempukku.lotro.logic.timing.TriggerConditions; -import com.gempukku.lotro.logic.timing.results.DiscardCardFromDeckResult; -import org.json.simple.JSONObject; - -public class DiscardFromDeck implements TriggerCheckerProducer { - @Override - public TriggerChecker getTriggerChecker(JSONObject value, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { - FieldUtils.validateAllowedFields(value, "filter", "memorize"); - - final String filter = FieldUtils.getString(value.get("filter"), "filter", "any"); - final String memorize = FieldUtils.getString(value.get("memorize"), "memorize"); - - final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter, environment); - - return new TriggerChecker() { - @Override - public boolean isBefore() { - return false; - } - - @Override - public boolean accepts(ActionContext actionContext) { - boolean result = TriggerConditions.forEachDiscardedFromDeck(actionContext.getGame(), actionContext.getEffectResult(), - filterableSource.getFilterable(actionContext)); - if (result && memorize != null) { - actionContext.setCardMemory(memorize, ((DiscardCardFromDeckResult) actionContext.getEffectResult()).getDiscardedCard()); - } - return result; - } - }; - } -} diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/DiscardFromHand.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/DiscardFromHand.java deleted file mode 100644 index 4191c1ae2..000000000 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/DiscardFromHand.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.gempukku.lotro.cards.build.field.effect.trigger; - -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.logic.timing.TriggerConditions; -import com.gempukku.lotro.logic.timing.results.DiscardCardFromHandResult; -import org.json.simple.JSONObject; - -public class DiscardFromHand implements TriggerCheckerProducer { - @Override - public TriggerChecker getTriggerChecker(JSONObject value, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { - FieldUtils.validateAllowedFields(value, "filter", "memorize", "player", "forced"); - - final String filter = FieldUtils.getString(value.get("filter"), "filter", "any"); - final String memorize = FieldUtils.getString(value.get("memorize"), "memorize"); - final String player = FieldUtils.getString(value.get("player"), "player"); - final boolean forced = FieldUtils.getBoolean(value.get("forced"), "forced", false); - - PlayerSource playerSource = (player != null) ? PlayerResolver.resolvePlayer(player, environment) : null; - final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter, environment); - - return new TriggerChecker() { - @Override - public boolean isBefore() { - return false; - } - - @Override - public boolean accepts(ActionContext actionContext) { - boolean result = TriggerConditions.forEachDiscardedFromHand(actionContext.getGame(), actionContext.getEffectResult(), - filterableSource.getFilterable(actionContext)); - if (result && forced) { - // Need to check if the discard was forced - if (!((DiscardCardFromHandResult) actionContext.getEffectResult()).isForced()) - result = false; - } - if (result && playerSource != null) { - // Need to check if it was that player discarding the card - final String performingPlayer = ((DiscardCardFromHandResult) actionContext.getEffectResult()).getSource().getOwner(); - if (performingPlayer == null || !performingPlayer.equals(playerSource.getPlayer(actionContext))) - result = false; - } - if (result && memorize != null) { - actionContext.setCardMemory(memorize, ((DiscardCardFromHandResult) actionContext.getEffectResult()).getDiscardedCard()); - } - return result; - } - }; - } -} diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/DiscardFromHandBy.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/DiscardFromHandBy.java deleted file mode 100644 index bc5a4c71a..000000000 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/DiscardFromHandBy.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.gempukku.lotro.cards.build.field.effect.trigger; - -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.logic.timing.TriggerConditions; -import com.gempukku.lotro.logic.timing.results.DiscardCardFromHandResult; -import org.json.simple.JSONObject; - -public class DiscardFromHandBy implements TriggerCheckerProducer { - @Override - public TriggerChecker getTriggerChecker(JSONObject value, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { - FieldUtils.validateAllowedFields(value, "filter", "memorize", "player", "by", "forced"); - - final String filter = FieldUtils.getString(value.get("filter"), "filter", "any"); - final String memorize = FieldUtils.getString(value.get("memorize"), "memorize"); - final String player = FieldUtils.getString(value.get("player"), "player", "you"); - final String byFilter = FieldUtils.getString(value.get("by"), "by"); - final boolean forced = FieldUtils.getBoolean(value.get("forced"), "forced", false); - - PlayerSource playerSource = (player != null) ? PlayerResolver.resolvePlayer(player, environment) : null; - final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter, environment); - final FilterableSource byFilterableSource = environment.getFilterFactory().generateFilter(byFilter, environment); - - return new TriggerChecker() { - @Override - public boolean isBefore() { - return false; - } - - @Override - public boolean accepts(ActionContext actionContext) { - boolean result = TriggerConditions.forEachDiscardedFromHandBy(actionContext.getGame(), actionContext.getEffectResult(), - byFilterableSource.getFilterable(actionContext), filterableSource.getFilterable(actionContext)); - if (result && forced) { - // Need to check if the discard was forced - if (!((DiscardCardFromHandResult) actionContext.getEffectResult()).isForced()) - result = false; - } - if (result && playerSource != null) { - // Need to check if it was that player discarding the card - final String performingPlayer = ((DiscardCardFromHandResult) actionContext.getEffectResult()).getSource().getOwner(); - if (performingPlayer == null || !performingPlayer.equals(playerSource.getPlayer(actionContext))) - result = false; - } - if (result && memorize != null) { - actionContext.setCardMemory(memorize, ((DiscardCardFromHandResult) actionContext.getEffectResult()).getDiscardedCard()); - } - return result; - } - }; - } -} diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/Discarded.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/Discarded.java index 86e1ca6d3..f7eb6ed75 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/Discarded.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/Discarded.java @@ -4,26 +4,40 @@ 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.common.Zone; import com.gempukku.lotro.filters.Filters; import com.gempukku.lotro.game.PhysicalCard; import com.gempukku.lotro.logic.timing.TriggerConditions; +import com.gempukku.lotro.logic.timing.results.DiscardCardFromDeckResult; +import com.gempukku.lotro.logic.timing.results.DiscardCardFromHandResult; import com.gempukku.lotro.logic.timing.results.DiscardCardsFromPlayResult; import org.json.simple.JSONObject; public class Discarded implements TriggerCheckerProducer { @Override public TriggerChecker getTriggerChecker(JSONObject value, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { - FieldUtils.validateAllowedFields(value, "filter", "memorize", "player", "source"); + FieldUtils.validateAllowedFields(value, "filter", "source", "player", "fromZone", "ignoreVoluntary", "memorize"); final String filter = FieldUtils.getString(value.get("filter"), "filter", "any"); + final String source = FieldUtils.getString(value.get("source"), "source", "any"); + final String discardingPlayer = FieldUtils.getString(value.get("player"), "player"); + Zone zone = FieldUtils.getEnum(Zone.class, value.get("fromZone"), "fromZone"); + final boolean ignoreVoluntary = FieldUtils.getBoolean(value.get("ignoreVoluntary"), "ignoreVoluntary", false); final String memorize = FieldUtils.getString(value.get("memorize"), "memorize"); - final String player = FieldUtils.getString(value.get("player"), "player"); - final String source = FieldUtils.getString(value.get("source"), "source"); - PlayerSource playerSource = (player != null) ? PlayerResolver.resolvePlayer(player, environment) : null; + final PlayerSource playerSource = (discardingPlayer != null) ? PlayerResolver.resolvePlayer(discardingPlayer, + environment) : null; final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter, environment); - final FilterableSource sourceSource = source != null ? environment.getFilterFactory().generateFilter(source, environment) : null; + final FilterableSource bySource = source != null ? environment.getFilterFactory().generateFilter(source, + environment) : null; + + //Squelch incoherent zone values + if (zone == Zone.FREE_CHARACTERS || zone == Zone.SHADOW_CHARACTERS || zone == Zone.STACKED || zone == Zone.ATTACHED + || zone == Zone.SUPPORT || zone == Zone.VOID || zone == Zone.VOID_FROM_HAND || zone == Zone.REMOVED) + { + throw new InvalidCardDefinitionException("'fromZone' using an unsupported zone in Discarded trigger (omit the value if wanting to trigger from play)."); + } return new TriggerChecker() { @Override @@ -34,21 +48,68 @@ public class Discarded implements TriggerCheckerProducer { @Override public boolean accepts(ActionContext actionContext) { final Filterable filterable = filterableSource.getFilterable(actionContext); - boolean result = TriggerConditions.forEachDiscardedFromPlay(actionContext.getGame(), actionContext.getEffectResult(), filterable); - DiscardCardsFromPlayResult discardResult = (DiscardCardsFromPlayResult) actionContext.getEffectResult(); - if (result && sourceSource != null) { - PhysicalCard discardSource = discardResult.getSource(); - if (discardSource == null || !Filters.accepts(actionContext.getGame(), sourceSource.getFilterable(actionContext), discardSource)) + + boolean result = false; + boolean forced = false; + PhysicalCard source = null; + String performingPlayer = null; + PhysicalCard discardedCard = null; + if(zone == null) { // From play + result = TriggerConditions.forEachDiscardedFromPlay(actionContext.getGame(), + actionContext.getEffectResult(), filterable); + + if(result) { + var discardResult = (DiscardCardsFromPlayResult) actionContext.getEffectResult(); + source = discardResult.getSource(); + //TODO: Add proper forcefulness detection to in-play discards + forced = false; + performingPlayer = discardResult.getPerformingPlayer(); + discardedCard = discardResult.getDiscardedCard(); + } + } + else if(zone == Zone.DECK) { + result = TriggerConditions.forEachDiscardedFromDeck(actionContext.getGame(), + actionContext.getEffectResult(), filterable); + + if(result) { + var discardResult = (DiscardCardFromDeckResult) actionContext.getEffectResult(); + source = discardResult.getSource(); + forced = discardResult.isForced(); + //TODO: split owner and performing player properly + performingPlayer = source.getOwner(); + discardedCard = discardResult.getDiscardedCard(); + } + } + else if(zone == Zone.HAND) { + result = TriggerConditions.forEachDiscardedFromHand(actionContext.getGame(), + actionContext.getEffectResult(), filterable); + + if(result) { + var discardResult = (DiscardCardFromHandResult) actionContext.getEffectResult(); + source = discardResult.getSource(); + forced = discardResult.isForced(); + //TODO: split owner and performing player properly + performingPlayer = source.getOwner(); + discardedCard = discardResult.getDiscardedCard(); + } + } + + if (result && bySource != null) { + if (discardedCard == null || !Filters.accepts(actionContext.getGame(), bySource.getFilterable(actionContext), source)) result = false; } + + if (result && ignoreVoluntary && !forced) { + result = false; + } + if (result && playerSource != null) { // Need to check if it was that player discarding the card - final String performingPlayer = discardResult.getPerformingPlayer(); if (performingPlayer == null || !performingPlayer.equals(playerSource.getPlayer(actionContext))) result = false; } + if (result && memorize != null) { - final PhysicalCard discardedCard = discardResult.getDiscardedCard(); actionContext.setCardMemory(memorize, discardedCard); } return result; diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/TriggerCheckerFactory.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/TriggerCheckerFactory.java index 1c280c332..e519483af 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/TriggerCheckerFactory.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/trigger/TriggerCheckerFactory.java @@ -12,6 +12,19 @@ public class TriggerCheckerFactory { private final Map triggerCheckers = new HashMap<>(); public TriggerCheckerFactory() { + + //Movement triggers + triggerCheckers.put("movesfrom", new MovesFrom()); + triggerCheckers.put("moves", new Moves()); + triggerCheckers.put("abouttomoveto", new AboutToMoveTo()); + triggerCheckers.put("movesto", new MovesTo()); + + //Timing Triggers + triggerCheckers.put("startofturn", new StartOfTurn()); + triggerCheckers.put("startofphase", new StartOfPhase()); + triggerCheckers.put("endofphase", new EndOfPhase()); + triggerCheckers.put("endofturn", new EndOfTurn()); + triggerCheckers.put("abouttoaddburden", new AboutToAddBurden()); triggerCheckers.put("abouttoaddtwilight", new AboutToAddTwilight()); triggerCheckers.put("abouttobekilled", new AboutToBeKilled()); @@ -20,7 +33,6 @@ public class TriggerCheckerFactory { triggerCheckers.put("abouttodrawcard", new AboutToDrawCard()); triggerCheckers.put("abouttoexert", new AboutToExert()); triggerCheckers.put("abouttoheal", new AboutToHeal()); - triggerCheckers.put("abouttomoveto", new AboutToMoveTo()); triggerCheckers.put("abouttotakecontrolofsite", new AboutToTakeControlOfSite()); triggerCheckers.put("abouttotakewound", new AboutToTakeWound()); triggerCheckers.put("addsburden", new AddsBurden()); @@ -33,11 +45,6 @@ public class TriggerCheckerFactory { triggerCheckers.put("cancelledskirmish", new CancelledSkirmish()); triggerCheckers.put("constantlycheck", new ConstantlyCheckTrigger()); triggerCheckers.put("discarded", new Discarded()); - triggerCheckers.put("discardfromdeck", new DiscardFromDeck()); - triggerCheckers.put("discardfromhand", new DiscardFromHand()); - triggerCheckers.put("discardfromhandby", new DiscardFromHandBy()); - triggerCheckers.put("endofphase", new EndOfPhase()); - triggerCheckers.put("endofturn", new EndOfTurn()); triggerCheckers.put("exertedby", new ExertedBy()); triggerCheckers.put("exerts", new Exerts()); triggerCheckers.put("exertsforspecialability", new ExertsForSpecialAbility()); @@ -52,9 +59,6 @@ public class TriggerCheckerFactory { triggerCheckers.put("killedinskirmish", new KilledInSkirmish()); triggerCheckers.put("losesinitiative", new LosesInitiative()); triggerCheckers.put("losesskirmish", new LosesSkirmish()); - triggerCheckers.put("moves", new Moves()); - triggerCheckers.put("movesfrom", new MovesFrom()); - triggerCheckers.put("movesto", new MovesTo()); triggerCheckers.put("played", new PlayedTriggerCheckerProducer()); triggerCheckers.put("playedfromstacked", new PlayedFromStacked()); triggerCheckers.put("playerdrawscard", new PlayerDrawsCard()); @@ -66,9 +70,7 @@ public class TriggerCheckerFactory { triggerCheckers.put("revealscardfromtopofdrawdeck", new RevealsCardFromTopOfDrawDeck()); triggerCheckers.put("revealedcardfromhand", new RevealedCardFromHand()); triggerCheckers.put("skirmishabouttoend", new SkirmishAboutToEnd()); - triggerCheckers.put("startofphase", new StartOfPhase()); triggerCheckers.put("startofskirmishinvolving", new StartOfSkirmishInvolving()); - triggerCheckers.put("startofturn", new StartOfTurn()); triggerCheckers.put("takencontrolofsite", new TakenControlOfSite()); triggerCheckers.put("takesoffring", new TakesOffRing()); triggerCheckers.put("takeswound", new TakesWound());