diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/cards/set1.json b/gemp-lotr/gemp-lotr-async/src/main/web/cards/set1.json index d1854fb75..e4742b4af 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/web/cards/set1.json +++ b/gemp-lotr/gemp-lotr-async/src/main/web/cards/set1.json @@ -1,4 +1,19 @@ { + "1_7": { + "title": "Dwarf Guard", + "side": "free people", + "culture": "dwarven", + "cost": 0, + "type": "companion", + "race": "dwarf", + "strength": 4, + "vitality": 2, + "resistance": 6, + "requirement": { + "type": "canSpot", + "filter": "dwarf" + } + }, "1_9": { "title": "Dwarven Axe", "side": "free people", @@ -21,6 +36,61 @@ } } }, + "1_11": { + "title": "Farin", + "subtitle": "Dwarven Emissary", + "unique": true, + "side": "free people", + "culture": "dwarven", + "cost": 2, + "type": "companion", + "race": "dwarf", + "strength": 5, + "vitality": 3, + "resistance": 6, + "requirement": { + "type": "canSpot", + "filter": "dwarf" + }, + "effects": { + "type": "modifyStrength", + "filter": "self,inSkirmishAgainst(orc)", + "amount": 2 + } + }, + "1_12": { + "title": "Gimli", + "subtitle": "Dwarf of Erebor", + "unique": true, + "side": "free people", + "culture": "dwarven", + "cost": 2, + "type": "companion", + "race": "dwarf", + "strength": 6, + "vitality": 3, + "resistance": 6, + "signet": "aragorn", + "keyword": "damage+1", + "effects": { + "type": "activated", + "phase": "fellowship", + "requirement": { + "type": "twilightPoolLessThan", + "amount": 2 + }, + "cost": { + "type": "addTwilight", + "amount": 2 + }, + "effect": [ + { + "type": "putCardFromHandOnBottomOfDeck", + "count": 1 + } + ] + } + }, "1_13": { "title": "Gimli", "subtitle": "Son of Gloin", 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 d99094f2e..1018aca0f 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 @@ -30,6 +30,7 @@ public class EffectAppenderFactory { effectAppenderProducers.put("canttakemorewoundsthan", new CantTakeMoreWoundsThan()); effectAppenderProducers.put("choice", new Choice()); effectAppenderProducers.put("assigntoskirmishagainstminion", new AssignToSkirmishAgainstMinion()); + effectAppenderProducers.put("putcardfromhandonbottomofdeck", new PutCardFromHandOnBottomOfDeck()); } public EffectAppender getEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PutCardFromHandOnBottomOfDeck.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PutCardFromHandOnBottomOfDeck.java new file mode 100644 index 000000000..d8d56c1f4 --- /dev/null +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/PutCardFromHandOnBottomOfDeck.java @@ -0,0 +1,66 @@ +package com.gempukku.lotro.cards.build.field.effect.appender; + +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.CountResolver; +import com.gempukku.lotro.cards.build.field.effect.appender.resolver.PlayerResolver; +import com.gempukku.lotro.common.Filterable; +import com.gempukku.lotro.filters.Filters; +import com.gempukku.lotro.game.PhysicalCard; +import com.gempukku.lotro.game.state.LotroGame; +import com.gempukku.lotro.logic.actions.CostToEffectAction; +import com.gempukku.lotro.logic.effects.PutCardFromHandOnBottomOfDeckEffect; +import com.gempukku.lotro.logic.timing.Effect; +import com.gempukku.lotro.logic.timing.EffectResult; +import org.json.simple.JSONObject; + +import java.util.Collection; + +public class PutCardFromHandOnBottomOfDeck implements EffectAppenderProducer { + @Override + public EffectAppender createEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { + FieldUtils.validateAllowedFields(effectObject, "player", "count", "filter"); + + final String player = FieldUtils.getString(effectObject.get("player"), "player", "owner"); + final CountResolver.Count count = CountResolver.resolveCount(effectObject.get("count"), 1); + final String filter = FieldUtils.getString(effectObject.get("filter"), "filter", "choose(any)"); + + MultiEffectAppender result = new MultiEffectAppender(); + + result.addEffectAppender( + CardResolver.resolveCardsInHand(filter, count.getMin(), count.getMax(), "_temp", player, "Choose cards from hand", environment)); + result.addEffectAppender( + new DelayedAppender() { + @Override + protected Effect createEffect(CostToEffectAction action, String playerId, LotroGame game, PhysicalCard self, EffectResult effectResult, Effect effect) { + final Collection cards = action.getCardsFromMemory("_temp"); + return new PutCardFromHandOnBottomOfDeckEffect(cards.toArray(new PhysicalCard[0])); + } + }); + + + return null; + } + + @Override + public Requirement createCostRequirement(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { + FieldUtils.validateAllowedFields(effectObject, "count", "player", "filter"); + + final String player = FieldUtils.getString(effectObject.get("player"), "player", "owner"); + final CountResolver.Count count = CountResolver.resolveCount(effectObject.get("count"), 1); + final String filter = FieldUtils.getString(effectObject.get("filter"), "filter"); + + final PlayerSource playerSource = PlayerResolver.resolvePlayer(player, environment); + final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter); + + return (playerId, game, self, effectResult, effect) -> { + final String playerHand = playerSource.getPlayer(playerId, game, self, effectResult, effect); + final Filterable filterable = filterableSource.getFilterable(playerId, game, self, effectResult, effect); + + return Filters.filter(game.getGameState().getHand(playerHand), game, filterable).size() >= count.getMin(); + }; + } +} diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/resolver/CardResolver.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/resolver/CardResolver.java index 4a0c5f4f8..42f2647f6 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/resolver/CardResolver.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/appender/resolver/CardResolver.java @@ -12,6 +12,7 @@ import com.gempukku.lotro.game.PhysicalCard; import com.gempukku.lotro.game.state.LotroGame; import com.gempukku.lotro.logic.actions.CostToEffectAction; import com.gempukku.lotro.logic.effects.ChooseActiveCardsEffect; +import com.gempukku.lotro.logic.effects.choose.ChooseCardsFromHandEffect; import com.gempukku.lotro.logic.timing.Effect; import com.gempukku.lotro.logic.timing.EffectResult; import com.gempukku.lotro.logic.timing.PlayConditions; @@ -20,6 +21,53 @@ import com.gempukku.lotro.logic.timing.UnrespondableEffect; import java.util.Collection; public class CardResolver { + public static EffectAppender resolveCardsInHand(String type, int min, int max, String memory, String choicePlayer, String choiceText, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { + if (type.startsWith("memory(") && type.endsWith(")")) { + String sourceMemory = type.substring(type.indexOf("(") + 1, type.lastIndexOf(")")); + return new AbstractEffectAppender() { + @Override + public boolean isPlayableInFull(String playerId, LotroGame game, PhysicalCard self, EffectResult effectResult, Effect effect) { + return true; + } + + @Override + protected Effect createEffect(CostToEffectAction action, String playerId, LotroGame game, PhysicalCard self, EffectResult effectResult, Effect effect) { + return new UnrespondableEffect() { + @Override + protected void doPlayEffect(LotroGame game) { + action.setCardMemory(memory, action.getCardFromMemory(sourceMemory)); + } + }; + } + }; + } else if (type.startsWith("choose(") && type.endsWith(")")) { + final String filter = type.substring(type.indexOf("(") + 1, type.lastIndexOf(")")); + final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter); + final PlayerSource playerSource = PlayerResolver.resolvePlayer(choicePlayer, environment); + return new AbstractEffectAppender() { + @Override + public boolean isPlayableInFull(String playerId, LotroGame game, PhysicalCard self, EffectResult effectResult, Effect effect) { + final Filterable filterable = filterableSource.getFilterable(playerId, game, self, effectResult, effect); + String choicePlayerId = playerSource.getPlayer(playerId, game, self, effectResult, effect); + return Filters.filter(game.getGameState().getHand(choicePlayerId), game, filterable).size() >= min; + } + + @Override + protected Effect createEffect(CostToEffectAction action, String playerId, LotroGame game, PhysicalCard self, EffectResult effectResult, Effect effect) { + final Filterable filterable = filterableSource.getFilterable(playerId, game, self, effectResult, effect); + String choicePlayerId = playerSource.getPlayer(playerId, game, self, effectResult, effect); + return new ChooseCardsFromHandEffect(choicePlayerId, min, max, filterable) { + @Override + protected void cardsSelected(LotroGame game, Collection cards) { + action.setCardMemory(memory, cards); + } + }; + } + }; + } + throw new RuntimeException("Unable to resolve card resolver of type: " + type); + } + public static EffectAppender resolveCard(String type, FilterableSource additionalFilter, String memory, String choicePlayer, String choiceText, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { return resolveCards(type, additionalFilter, 1, 1, memory, choicePlayer, choiceText, environment); } 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 27a5fdf05..318cef4e2 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 @@ -16,6 +16,7 @@ public class RequirementFactory { requirementProducers.put("canspot", new CanSpot()); requirementProducers.put("canspotburdens", new CanSpotBurdens()); requirementProducers.put("controlssite", new ControlsSite()); + requirementProducers.put("twilightpoollessthan", new TwilightPoolLessThan()); } public Requirement getRequirement(JSONObject object, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/TwilightPoolLessThan.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/TwilightPoolLessThan.java new file mode 100644 index 000000000..7ac8c2201 --- /dev/null +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/requirement/TwilightPoolLessThan.java @@ -0,0 +1,17 @@ +package com.gempukku.lotro.cards.build.field.effect.requirement; + +import com.gempukku.lotro.cards.build.CardGenerationEnvironment; +import com.gempukku.lotro.cards.build.InvalidCardDefinitionException; +import com.gempukku.lotro.cards.build.Requirement; +import com.gempukku.lotro.cards.build.field.FieldUtils; +import org.json.simple.JSONObject; + +public class TwilightPoolLessThan implements RequirementProducer { + @Override + public Requirement getPlayRequirement(JSONObject object, CardGenerationEnvironment environment) throws InvalidCardDefinitionException { + FieldUtils.validateAllowedFields(object, "amount"); + + final int count = FieldUtils.getInteger(object.get("amount"), "amount"); + return (playerId, game, self, effectResult, effect) -> game.getGameState().getTwilightPool() < count; + } +} diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/effects/PutCardFromHandOnBottomOfDeckEffect.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/effects/PutCardFromHandOnBottomOfDeckEffect.java index 26656726c..3b115ee2c 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/effects/PutCardFromHandOnBottomOfDeckEffect.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/effects/PutCardFromHandOnBottomOfDeckEffect.java @@ -7,18 +7,23 @@ import com.gempukku.lotro.game.state.LotroGame; import com.gempukku.lotro.logic.timing.AbstractEffect; import com.gempukku.lotro.logic.timing.Effect; -import java.util.Collections; +import java.util.Arrays; public class PutCardFromHandOnBottomOfDeckEffect extends AbstractEffect { - private PhysicalCard _physicalCard; + private PhysicalCard[] physicalCards; - public PutCardFromHandOnBottomOfDeckEffect(PhysicalCard physicalCard) { - _physicalCard = physicalCard; + public PutCardFromHandOnBottomOfDeckEffect(PhysicalCard... physicalCard) { + physicalCards = physicalCard; } @Override public boolean isPlayableInFull(LotroGame game) { - return _physicalCard.getZone() == Zone.HAND; + for (PhysicalCard card : physicalCards) { + if (card.getZone() != Zone.HAND) + return false; + } + + return true; } @Override @@ -35,9 +40,11 @@ public class PutCardFromHandOnBottomOfDeckEffect extends AbstractEffect { protected FullEffectResult playEffectReturningResult(LotroGame game) { if (isPlayableInFull(game)) { GameState gameState = game.getGameState(); - gameState.sendMessage(_physicalCard.getOwner() + " puts a card from hand on bottom of his or her deck"); - gameState.removeCardsFromZone(_physicalCard.getOwner(), Collections.singleton(_physicalCard)); - gameState.putCardOnBottomOfDeck(_physicalCard); + gameState.removeCardsFromZone(physicalCards[0].getOwner(), Arrays.asList(physicalCards)); + for (PhysicalCard physicalCard : physicalCards) { + gameState.sendMessage(physicalCard.getOwner() + " puts a card from hand on bottom of his or her deck"); + gameState.putCardOnBottomOfDeck(physicalCard); + } return new FullEffectResult(true); }