Added Item filter and DiscardFromDeck trigger

Added item filter (which is just possession + artifact)
Added trigger which keys off of cards being discarded from the deck.  Added the bulk of Deepest Delvings as a result.
This commit is contained in:
Christian 'ketura' McCarty
2021-09-22 03:15:49 -05:00
parent bf5204edfb
commit 35a8ca86b8
12 changed files with 128 additions and 82 deletions

View File

@@ -17,18 +17,18 @@
"type": "location", "type": "location",
"filter": "siteBlock(fellowship),or(siteNumber(4),siteNumber(5),siteNumber(6),siteNumber(7),siteNumber(8))" "filter": "siteBlock(fellowship),or(siteNumber(4),siteNumber(5),siteNumber(6),siteNumber(7),siteNumber(8))"
}, },
"filter": "dwarf,hasAttachedCount(2,or(possession,artifact))" "filter": "dwarf,hasAttachedCount(2,or(item))"
} }
}, },
{ {
"type": "trigger", "type": "trigger",
"optional": true, "optional": true,
"trigger": { "trigger": {
"type": "moves", "type": "moves"
}, },
"condition": { "condition": {
"type": "canSpot", "type": "canSpot",
"filter": "dwarf,hasAttachedCount(2,or(possession,artifact))" "filter": "dwarf,hasAttachedCount(2,item)"
}, },
"effect": { "effect": {
"type": "drawCards", "type": "drawCards",
@@ -47,24 +47,22 @@
"support area" "support area"
], ],
"effects": [ "effects": [
{
"type": "extraCost",
"cost": {
"type": "exert",
"filter": "choose(dwarf)"
}
},
{ {
"type": "trigger", "type": "trigger",
"optional": true,
"trigger": { "trigger": {
"type": "played", "type": "discardfromdeck",
"filter": "orc", "filter": "your,culture(dwarven)",
"memorize": "playedOrc" "memorize": "topcard"
},
"cost":{
"type": "addTwilight",
"amount": 2
}, },
"effect": { "effect": {
"type": "discardTopCardsFromDeck", "type": "stackcardsfromdiscard",
"forced": true, "where": "self",
"deck": "ownerFromMemory(playedOrc)" "filter": "memory(topcard)"
} }
} }
] ]

View File

@@ -37,6 +37,7 @@ public class FilterFactory {
simpleFilters.put("character", (actionContext) -> Filters.character); simpleFilters.put("character", (actionContext) -> Filters.character);
simpleFilters.put("mounted", (actionContext) -> Filters.mounted); simpleFilters.put("mounted", (actionContext) -> Filters.mounted);
simpleFilters.put("weapon", (actionContext) -> Filters.weapon); simpleFilters.put("weapon", (actionContext) -> Filters.weapon);
simpleFilters.put("item", (actionContext) -> Filters.item);
simpleFilters.put("wounded", (actionContext) -> Filters.wounded); simpleFilters.put("wounded", (actionContext) -> Filters.wounded);
simpleFilters.put("unwounded", (actionContext) -> Filters.unwounded); simpleFilters.put("unwounded", (actionContext) -> Filters.unwounded);
simpleFilters.put("exhausted", (actionContext) -> Filters.exhausted); simpleFilters.put("exhausted", (actionContext) -> Filters.exhausted);

View File

@@ -0,0 +1,36 @@
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;
}
};
}
}

View File

@@ -37,6 +37,7 @@ public class TriggerCheckerFactory {
triggerCheckers.put("exerts", new Exerts()); triggerCheckers.put("exerts", new Exerts());
triggerCheckers.put("discarded", new Discarded()); triggerCheckers.put("discarded", new Discarded());
triggerCheckers.put("discardfromhand", new DiscardFromHand()); triggerCheckers.put("discardfromhand", new DiscardFromHand());
triggerCheckers.put("discardfromdeck", new DiscardFromDeck());
triggerCheckers.put("putsonring", new PutsOnRing()); triggerCheckers.put("putsonring", new PutsOnRing());
triggerCheckers.put("addsburden", new AddsBurden()); triggerCheckers.put("addsburden", new AddsBurden());
triggerCheckers.put("removesburden", new RemovesBurden()); triggerCheckers.put("removesburden", new RemovesBurden());

View File

@@ -410,6 +410,7 @@ public class Filters {
public static final Filter galadriel = Filters.name("Galadriel"); public static final Filter galadriel = Filters.name("Galadriel");
public static final Filter weapon = Filters.or(PossessionClass.HAND_WEAPON, PossessionClass.RANGED_WEAPON); public static final Filter weapon = Filters.or(PossessionClass.HAND_WEAPON, PossessionClass.RANGED_WEAPON);
public static final Filter item = Filters.or(CardType.ARTIFACT, CardType.POSSESSION);
public static final Filter character = Filters.or(CardType.ALLY, CardType.COMPANION, CardType.MINION); public static final Filter character = Filters.or(CardType.ALLY, CardType.COMPANION, CardType.MINION);
public static final Filter ringBearer = new Filter() { public static final Filter ringBearer = new Filter() {

View File

@@ -6,6 +6,8 @@ import com.gempukku.lotro.game.state.GameState;
import com.gempukku.lotro.game.state.LotroGame; import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.timing.AbstractEffect; import com.gempukku.lotro.logic.timing.AbstractEffect;
import com.gempukku.lotro.logic.timing.Effect; import com.gempukku.lotro.logic.timing.Effect;
import com.gempukku.lotro.logic.timing.results.DiscardCardFromDeckResult;
import com.gempukku.lotro.logic.timing.results.DiscardCardFromPlayResult;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
@@ -61,6 +63,9 @@ public class DiscardTopCardFromDeckEffect extends AbstractEffect {
cardsDiscardedCallback(cardsDiscarded); cardsDiscardedCallback(cardsDiscarded);
} }
for (PhysicalCard discardedCard : cardsDiscarded)//PhysicalCard source, PhysicalCard card, String handPlayerId, boolean forced
game.getActionsEnvironment().emitEffectResult(new DiscardCardFromDeckResult(_source, discardedCard, _forced));
return new FullEffectResult(_count == cardsDiscarded.size()); return new FullEffectResult(_count == cardsDiscarded.size());
} }
return new FullEffectResult(false); return new FullEffectResult(false);

View File

@@ -15,6 +15,7 @@ public abstract class EffectResult {
FOR_EACH_RETURNED_TO_HAND, FOR_EACH_RETURNED_TO_HAND,
FOR_EACH_DISCARDED_FROM_HAND, FOR_EACH_DISCARDED_FROM_HAND,
FOR_EACH_DISCARDED_FROM_DECK,
FREE_PEOPLE_PLAYER_STARTS_ASSIGNING, FREE_PEOPLE_PLAYER_STARTS_ASSIGNING,
SKIRMISH_ABOUT_TO_END, SKIRMISH_ABOUT_TO_END,

View File

@@ -197,6 +197,12 @@ public class TriggerConditions {
return false; return false;
} }
public static boolean forEachDiscardedFromDeck(LotroGame game, EffectResult effectResult, Filterable... filters) {
if (effectResult.getType() == EffectResult.Type.FOR_EACH_DISCARDED_FROM_DECK)
return Filters.and(filters).accepts(game, ((DiscardCardFromDeckResult) effectResult).getDiscardedCard());
return false;
}
public static boolean forEachWounded(LotroGame game, EffectResult effectResult, Filterable... filters) { public static boolean forEachWounded(LotroGame game, EffectResult effectResult, Filterable... filters) {
if (effectResult.getType() == EffectResult.Type.FOR_EACH_WOUNDED) if (effectResult.getType() == EffectResult.Type.FOR_EACH_WOUNDED)
return Filters.and(filters).accepts(game, ((WoundResult) effectResult).getWoundedCard()); return Filters.and(filters).accepts(game, ((WoundResult) effectResult).getWoundedCard());

View File

@@ -0,0 +1,30 @@
package com.gempukku.lotro.logic.timing.results;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.logic.timing.EffectResult;
public class DiscardCardFromDeckResult extends EffectResult {
private PhysicalCard _source;
private PhysicalCard _card;
private boolean _forced;
public DiscardCardFromDeckResult(PhysicalCard source, PhysicalCard card, boolean forced) {
super(Type.FOR_EACH_DISCARDED_FROM_DECK);
_source = source;
_card = card;
_forced = forced;
}
public PhysicalCard getSource() {
return _source;
}
public boolean isForced() {
return _forced;
}
public PhysicalCard getDiscardedCard() {
return _card;
}
}

View File

@@ -238,6 +238,9 @@ public class GenericCardTestHelper extends AbstractAtTest {
public void AttachCard(PhysicalCardImpl card, PhysicalCardImpl bearer) { _game.getGameState().attachCard(_game, card, bearer); } public void AttachCard(PhysicalCardImpl card, PhysicalCardImpl bearer) { _game.getGameState().attachCard(_game, card, bearer); }
public void AttachCardsTo(PhysicalCardImpl holder, PhysicalCardImpl...cards) {
Arrays.stream(cards).forEach(card -> _game.getGameState().attachCard(_game, card, holder));
}
public void FreepsMoveCardToDeck(String...cardNames) { public void FreepsMoveCardToDeck(String...cardNames) {
Arrays.stream(cardNames).forEach(cardName -> FreepsMoveCardToDeck(GetFreepsCard(cardName))); Arrays.stream(cardNames).forEach(cardName -> FreepsMoveCardToDeck(GetFreepsCard(cardName)));
@@ -270,15 +273,17 @@ public class GenericCardTestHelper extends AbstractAtTest {
Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.SUPPORT)); Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.SUPPORT));
} }
public void FreepsMoveCardToDiscard(String cardName) { FreepsMoveCardToSupportArea(GetFreepsCard(cardName)); } public void FreepsMoveCardToDiscard(String cardName) { FreepsMoveCardToDiscard(GetFreepsCard(cardName)); }
public void FreepsMoveCardToDiscard(PhysicalCardImpl...cards) { public void FreepsMoveCardToDiscard(PhysicalCardImpl...cards) {
Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.DISCARD)); Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.DISCARD));
} }
public void ShadowMoveCardToDiscard(String cardName) { ShadowMoveCardToSupportArea(GetShadowCard(cardName)); } public void ShadowMoveCardToDiscard(String cardName) { ShadowMoveCardToDiscard(GetShadowCard(cardName)); }
public void ShadowMoveCardToDiscard(PhysicalCardImpl...cards) { public void ShadowMoveCardToDiscard(PhysicalCardImpl...cards) {
Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.DISCARD)); Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.DISCARD));
} }
public void MoveCardToZone(String player, PhysicalCardImpl card, Zone zone) { public void MoveCardToZone(String player, PhysicalCardImpl card, Zone zone) {
if(card.getZone() != null) if(card.getZone() != null)
{ {
@@ -428,6 +433,7 @@ public class GenericCardTestHelper extends AbstractAtTest {
public void FreepsChoose(String choice) throws DecisionResultInvalidException { playerDecided(P1, choice); } public void FreepsChoose(String choice) throws DecisionResultInvalidException { playerDecided(P1, choice); }
public void ShadowChoose(String choice) throws DecisionResultInvalidException { playerDecided(P2, choice); } public void ShadowChoose(String choice) throws DecisionResultInvalidException { playerDecided(P2, choice); }
public void FreepsChooseToMove() throws DecisionResultInvalidException { playerDecided(P1, "0"); } public void FreepsChooseToMove() throws DecisionResultInvalidException { playerDecided(P1, "0"); }
public void FreepsChooseToStay() throws DecisionResultInvalidException { playerDecided(P1, "1"); } public void FreepsChooseToStay() throws DecisionResultInvalidException { playerDecided(P1, "1"); }

View File

@@ -41,7 +41,7 @@ public class Card_V1_001Tests
/** /**
* Set: VSet1, VPack1 * Set: VSet1, VPack1
* Title: Hospitality of the Dwarves * Title: *Hospitality of the Dwarves
* Side: Free Peoples * Side: Free Peoples
* Culture: Dwarven * Culture: Dwarven
* Twilight Cost: 1 * Twilight Cost: 1

View File

@@ -20,12 +20,15 @@ public class Card_V1_002Tests
return new GenericCardTestHelper( return new GenericCardTestHelper(
new HashMap<String, String>() new HashMap<String, String>()
{{ {{
put("aragorn", "1_89"); put("gimli", "1_13");
put("boromir", "1_97"); put("axe", "1_9");
put("whitecity", "101_2"); put("axe2", "1_9");
put("deep", "151_2");
put("beneath", "2_1");
put("runner", "1_178");
put("plunder", "1_193");
put("attea", "1_229");
put("cantea", "1_230");
}} }}
); );
} }
@@ -36,14 +39,14 @@ public class Card_V1_002Tests
/** /**
* Set: VSet1, VPack1 * Set: VSet1, VPack1
* Title: *I Will Not Let the White City Fall * Title: *Deepest Delvings
* Side: Free Peoples * Side: Free Peoples
* Culture: Gondor * Culture: Dwarven
* Twilight Cost: 2 * Twilight Cost: 1
* Type: Condition * Type: Condition
* Subtype: Support Area * Subtype: Support Area
* Game Text: Each time Boromir loses a skirmish, make Aragorn strength +2 until the regroup phase. * Game Text: Each time you discard a [dwarven] card from the top of your deck you may add (1) to stack that card here.
* Each time Aragorn loses a skirmish, make Boromir strength +2 until the regroup phase. * Maneuver: Exert a dwarf to take a card stacked here into hand.
*/ */
//Pre-game setup //Pre-game setup
@@ -57,68 +60,26 @@ public class Card_V1_002Tests
} }
@Test @Test
public void OneIsBuffedWhenOtherLosesSkirmish() throws DecisionResultInvalidException, CardNotFoundException { public void topdecktest() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup //Pre-game setup
GenericCardTestHelper scn = GetScenario(); GenericCardTestHelper scn = GetScenario();
PhysicalCardImpl aragorn = scn.GetFreepsCard("aragorn"); PhysicalCardImpl beneath = scn.GetFreepsCard("beneath");
PhysicalCardImpl boromir = scn.GetFreepsCard("boromir"); PhysicalCardImpl deep = scn.GetFreepsCard("deep");
PhysicalCardImpl whitecity = scn.GetFreepsCard("whitecity"); scn.FreepsMoveCardToSupportArea(beneath);
scn.FreepsMoveCardToSupportArea(deep);
scn.FreepsMoveCharToTable(aragorn); scn.FreepsMoveCharToTable("gimli");
scn.FreepsMoveCharToTable(boromir); scn.FreepsMoveCardToDiscard("axe");
scn.FreepsMoveCardToHand(whitecity);
PhysicalCardImpl cantea = scn.GetShadowCard("cantea");
PhysicalCardImpl attea = scn.GetShadowCard("attea");
scn.ShadowMoveCharToTable(cantea, attea);
scn.StartGame(); scn.StartGame();
scn.FreepsPlayCard(whitecity);
scn.FreepsUseCardAction(beneath);
scn.SkipToPhase(Phase.ASSIGNMENT); scn.SkipToPhase(Phase.ASSIGNMENT);
assertEquals(7, scn.GetStrength(boromir));
assertEquals(8, scn.GetStrength(aragorn));
scn.SkipCurrentPhaseActions(); scn.SkipCurrentPhaseActions();
//Standard assignment
scn.FreepsAssignToMinions(new PhysicalCardImpl[] { aragorn, cantea}, new PhysicalCardImpl[] { boromir, attea});
scn.FreepsResolveSkirmish(aragorn);
scn.SkipCurrentPhaseActions();
scn.FreepsAcceptOptionalTrigger(); //Have to resolve the skirmish and the condition trigger
// 14 > 8, Aragorn lost, Boromir should be buffed
assertEquals(9, scn.GetStrength(boromir));
assertEquals(8, scn.GetStrength(aragorn));
scn.FreepsResolveSkirmish(boromir);
scn.SkipCurrentPhaseActions();
scn.FreepsAcceptOptionalTrigger(); //Have to resolve the skirmish and the condition trigger
// 12 > 9, Boromir lost, Aragorn should be buffed
assertEquals(9, scn.GetStrength(boromir));
assertEquals(10, scn.GetStrength(aragorn));
//Fierce assignment
scn.FreepsSkipCurrentPhaseAction();
scn.ShadowSkipCurrentPhaseAction();
scn.FreepsAssignToMinions(new PhysicalCardImpl[] { aragorn, cantea}, new PhysicalCardImpl[] { boromir, attea});
scn.FreepsResolveSkirmish(boromir);
scn.SkipCurrentPhaseActions();
scn.FreepsAcceptOptionalTrigger(); //Have to resolve the skirmish and the condition trigger
// 10 > 9, Boromir lost again, Aragorn should be buffed again
assertEquals(9, scn.GetStrength(boromir));
assertEquals(12, scn.GetStrength(aragorn));
scn.FreepsResolveSkirmish(aragorn);
//Aragorn now wins his skirmish, so there's no loss trigger to evaluate
scn.SkipToPhase(Phase.REGROUP);
//strength should be back to normal
assertEquals(7, scn.GetStrength(boromir));
assertEquals(8, scn.GetStrength(aragorn));
} }