diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/cards/set21/set1-errata.json b/gemp-lotr/gemp-lotr-async/src/main/web/cards/set21/set1-errata.json index 1ac6cf340..cd562e191 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/web/cards/set21/set1-errata.json +++ b/gemp-lotr/gemp-lotr-async/src/main/web/cards/set21/set1-errata.json @@ -442,19 +442,23 @@ "effects": { "type": "activated", "phase": "assignment", + "condition": { + "type": "canSpot", + "filter": "bearer,notAssignedToSkirmish" + }, "cost": [ { "type": "exert", - "filter": "self" - }, + "filter": "bearer" + } + ], + "effect":[ { "type": "assignFpCharacterToSkirmish", "player": "fp", - "fpCharacter": "self", - "against": "minion" - } - ], - "effect":[ + "fpCharacter": "bearer", + "against": "choose(minion)" + }, { "type": "modifyStrength", "filter": "choose(ally,allyInCurrentRegion)", diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/filter/FilterFactory.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/filter/FilterFactory.java index 4af6a945d..851190fb0 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/filter/FilterFactory.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/cards/build/field/effect/filter/FilterFactory.java @@ -199,6 +199,17 @@ public class FilterFactory { int number = Integer.parseInt(parameterSplit[1]); return (actionContext) -> Filters.isAllyHome(number, sitesBlock); }); + parameterFilters.put("allyInRegion", + (parameter, environment) -> { + final String[] parameterSplit = parameter.split(","); + final SitesBlock sitesBlock = Enum.valueOf(SitesBlock.class, parameterSplit[0].toUpperCase().replace('_', ' ')); + int number = Integer.parseInt(parameterSplit[1]); + return (actionContext) -> Filters.isAllyInRegion(number, sitesBlock); + }); + parameterFilters.put("allyInCurrentRegion", + (parameter, environment) -> { + return (actionContext) -> Filters.isAllyInCurrentRegion(); + }); parameterFilters.put("siteBlock", (parameter, environment) -> { final SitesBlock sitesBlock = Enum.valueOf(SitesBlock.class, parameter.toUpperCase().replace(' ', '_')); 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 ac3ce177d..6513aa6cb 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 @@ -665,6 +665,28 @@ public class Filters { }); } + public static Filter isAllyInRegion(final int regionNumber, final SitesBlock siteBlock) { + return Filters.and( + CardType.ALLY, + new Filter() { + @Override + public boolean accepts(LotroGame game, PhysicalCard physicalCard) { + return RuleUtils.isAllyInRegion(physicalCard, regionNumber, siteBlock); + } + }); + } + + public static Filter isAllyInCurrentRegion() { + return Filters.and( + CardType.ALLY, + new Filter() { + @Override + public boolean accepts(LotroGame game, PhysicalCard physicalCard) { + return RuleUtils.isAllyInRegion(physicalCard, GameUtils.getRegion(game), game.getGameState().getCurrentSiteBlock()); + } + }); + } + public static final Filter allyAtHome = Filters.and( CardType.ALLY, new Filter() { @@ -709,6 +731,8 @@ public class Filters { } }; + + public static Filter siteNumber(final int siteNumber) { return siteNumberBetweenInclusive(siteNumber, siteNumber); } diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/RuleUtils.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/RuleUtils.java index 0803ac7ac..9b6184b1d 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/RuleUtils.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/logic/timing/RuleUtils.java @@ -7,8 +7,10 @@ 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.Skirmish; +import com.gempukku.lotro.logic.GameUtils; import com.gempukku.lotro.logic.modifiers.evaluator.Evaluator; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Set; @@ -186,4 +188,18 @@ public class RuleUtils { return true; return false; } + + public static boolean isAllyInRegion(PhysicalCard ally, int regionNumber, SitesBlock siteBlock) { + final SitesBlock allySiteBlock = ally.getBlueprint().getAllyHomeSiteBlock(); + final int[] allyHomeSites = ally.getBlueprint().getAllyHomeSiteNumbers(); + if (allySiteBlock != siteBlock) + return false; +// for (int number : allyHomeSites) +// if (regionNumber == GameUtils.getRegion(number)) +// return true; +// return false; + + return Arrays.stream(ally.getBlueprint().getAllyHomeSiteNumbers()).anyMatch(x -> regionNumber == GameUtils.getRegion(x)); + } + } 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 bccc23d87..3f3e6d318 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 @@ -53,7 +53,7 @@ public abstract class AbstractAtTest { _userFeedback = new DefaultUserFeedback(); LotroFormatLibrary formatLibrary = new LotroFormatLibrary(new DefaultAdventureLibrary(), _library); - LotroFormat format = formatLibrary.getFormat("movie"); + LotroFormat format = formatLibrary.getFormat("multipath"); _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/GenericCardTest.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/GenericCardTest.java index a635ecaf6..4e0140a16 100644 --- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/GenericCardTest.java +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/GenericCardTest.java @@ -8,8 +8,10 @@ import com.gempukku.lotro.common.Zone; import com.gempukku.lotro.game.CardNotFoundException; import com.gempukku.lotro.game.PhysicalCard; import com.gempukku.lotro.game.PhysicalCardImpl; +import com.gempukku.lotro.game.state.Assignment; import com.gempukku.lotro.logic.decisions.AwaitingDecision; import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException; +import com.gempukku.lotro.logic.vo.LotroDeck; import java.util.*; @@ -19,32 +21,68 @@ public class GenericCardTest extends AbstractAtTest { private int LastCardID = 100; - //TODO: refactor this to not have different freeps/shadows collections - public Map freepsCards = new HashMap<>(); - public Map shadowCards = new HashMap<>(); + public static final HashMap FellowshipSites = new HashMap() {{ + put("site1", "1_319"); + put("site2", "1_327"); + put("site3", "1_337"); + put("site4", "1_343"); + put("site5", "1_349"); + put("site6", "1_350"); + put("site7", "1_353"); + put("site8", "1_356"); + put("site9", "1_360"); + }}; - public GenericCardTest(Map freepsIDs, Map shadowIDs) throws CardNotFoundException, DecisionResultInvalidException { + public static final String FOTRFrodo = "1_290"; + public static final String FOTRRing = "1_2"; + + + // Player key, then name/card + public Map> Cards = new HashMap<>(); + + public GenericCardTest(Map cardIDs) throws CardNotFoundException, DecisionResultInvalidException { + this(cardIDs, null, null, null); + } + + public GenericCardTest(Map cardIDs, Map siteIDs, String ringBearerID, String ringID) throws CardNotFoundException, DecisionResultInvalidException { super(); - initializeSimplestGame(); + if(siteIDs == null || ringBearerID == null || ringID == null) { + initializeSimplestGame(); + } + else { + Map decks = new HashMap(); + decks.put(P1, new LotroDeck(P1)); + decks.put(P2, new LotroDeck(P2)); - if(freepsIDs != null) { - for(String name : freepsIDs.keySet()) { - String id = freepsIDs.get(name); - PhysicalCardImpl card = CreateCard(P1, id); - freepsCards.put(name, card); - FreepsMoveCardToDeck(card); - ShadowMoveCardToDeck(CreateCard(P2, id)); + for(String name : siteIDs.keySet()) { + String id = siteIDs.get(name); + decks.get(P1).addSite(id); + decks.get(P2).addSite(id); } + + decks.get(P1).setRingBearer(ringBearerID); + decks.get(P2).setRingBearer(ringBearerID); + + decks.get(P1).setRing(ringID); + decks.get(P2).setRing(ringID); + + initializeGameWithDecks(decks); } - if(shadowIDs != null) { - for(String name : shadowIDs.keySet()) { - String id = shadowIDs.get(name); - PhysicalCardImpl card = CreateCard(P2, id); - shadowCards.put(name, card); - ShadowMoveCardToDeck(card); - FreepsMoveCardToDeck(CreateCard(P1, id)); + Cards.put(P1, new HashMap<>()); + Cards.put(P2, new HashMap<>()); + + if(cardIDs != null) { + for(String name : cardIDs.keySet()) { + String id = cardIDs.get(name); + PhysicalCardImpl card = CreateCard(P1, id); + Cards.get(P1).put(name, card); + FreepsMoveCardToDeck(card); + + card = CreateCard(P2, id); + Cards.get(P2).put(name, card); + FreepsMoveCardToDeck(card); } } } @@ -57,8 +95,8 @@ public class GenericCardTest extends AbstractAtTest { skipMulligans(); } - public PhysicalCardImpl GetFreepsCard(String cardName) { return freepsCards.get(cardName); } - public PhysicalCardImpl GetShadowCard(String cardName) { return shadowCards.get(cardName); } + public PhysicalCardImpl GetFreepsCard(String cardName) { return Cards.get(P1).get(cardName); } + public PhysicalCardImpl GetShadowCard(String cardName) { return Cards.get(P2).get(cardName); } public List FreepsGetAvailableActions() { return GetAvailableActions(P1); } public List ShadowGetAvailableActions() { return GetAvailableActions(P2); } @@ -213,18 +251,38 @@ public class GenericCardTest extends AbstractAtTest { public void FreepsAssignToMinion(PhysicalCardImpl comp, PhysicalCardImpl minion) throws DecisionResultInvalidException { playerDecided(P1, comp.getCardId() + " " + minion.getCardId()); } + public void FreepsAssignToMinions(PhysicalCardImpl[]... groups) throws DecisionResultInvalidException { + String result = ""; + + for (PhysicalCardImpl[] group : groups) { + result += group[0].getCardId(); + for(int i = 1; i < group.length; i++) + { + result += " " + group[i].getCardId(); + } + result += ","; + } + + playerDecided(P1, result); + } public List FreepsGetAttachedCards(String name) { return GetAttachedCards(GetFreepsCard(name)); } - public List GetAttachedCards(PhysicalCardImpl card) - { + public List GetAttachedCards(PhysicalCardImpl card) { return (List)(List)_game.getGameState().getAttachedCards(card); } public void FreepsResolveSkirmish(String name) throws DecisionResultInvalidException { FreepsResolveSkirmish(GetFreepsCard(name)); } - public void FreepsResolveSkirmish(PhysicalCardImpl comp) throws DecisionResultInvalidException { - playerDecided(P1, String.valueOf(comp.getCardId())); + public void FreepsResolveSkirmish(PhysicalCardImpl comp) throws DecisionResultInvalidException { FreepsChooseCard(comp); } + public void FreepsChooseCard(PhysicalCardImpl card) throws DecisionResultInvalidException { + playerDecided(P1, String.valueOf(card.getCardId())); } + public boolean IsCharAssigned(PhysicalCardImpl card) { + List assigns = _game.getGameState().getAssignments(); + return assigns.stream().anyMatch(x -> x.getFellowshipCharacter() == card || x.getShadowCharacters().contains(card)); + } + + public int FreepsGetStrength(String name) { return GetStrength(GetFreepsCard(name)); } public int ShadowGetStrength(String name) { return GetStrength(GetShadowCard(name)); } public int GetStrength(PhysicalCardImpl card) diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/Elrond_LoRErrataTest.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/Elrond_LoRErrataTest.java index 342f2e0d4..e822974a5 100644 --- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/Elrond_LoRErrataTest.java +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/Elrond_LoRErrataTest.java @@ -22,8 +22,7 @@ public class Elrond_LoRErrataTest {{ put("elrond", "21_1040"); put("randomcard", "1_3"); - }}, - null + }} ); } @@ -34,8 +33,7 @@ public class Elrond_LoRErrataTest put("elrond", "21_1040"); put("gandalf", "1_72"); put("arwen", "1_30"); - }}, - null + }} ); } @@ -48,8 +46,7 @@ public class Elrond_LoRErrataTest put("allyHome3_2", "1_27"); put("allyHome6_1", "1_56"); put("allyHome6_2", "1_57"); - }}, - null + }} ); } diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/FlamingBrandErrataTest.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/FlamingBrandErrataTest.java index 9815a3b33..7a22cd4f1 100644 --- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/FlamingBrandErrataTest.java +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/FlamingBrandErrataTest.java @@ -25,9 +25,7 @@ public class FlamingBrandErrataTest put("arwen", "1_30"); put("boromir", "1_97"); put("aragorn", "1_89"); - }}, - new HashMap() - {{ + put("nazgul", "1_229"); put("runner", "1_178"); }} diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/Galadriel_LoLErrataTest.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/Galadriel_LoLErrataTest.java index 974d47b14..7d7490e16 100644 --- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/Galadriel_LoLErrataTest.java +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/Galadriel_LoLErrataTest.java @@ -18,8 +18,7 @@ public class Galadriel_LoLErrataTest {{ put("galadriel", "21_1045"); put("elrond", "1_40"); - }}, - null + }} ); } @@ -32,8 +31,7 @@ public class Galadriel_LoLErrataTest put("allyHome3_2", "1_27"); put("allyHome6_1", "1_56"); put("allyHome6_2", "1_57"); - }}, - null + }} ); } diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/HornOfBoromirErrataTest.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/HornOfBoromirErrataTest.java new file mode 100644 index 000000000..83887855f --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/HornOfBoromirErrataTest.java @@ -0,0 +1,149 @@ +package com.gempukku.lotro.cards.unofficial.pc.errata; + +import com.gempukku.lotro.cards.GenericCardTest; +import com.gempukku.lotro.common.Phase; +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 junit.framework.Assert; +import org.junit.Test; + +import java.util.HashMap; + +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.*; + +public class HornOfBoromirErrataTest +{ + protected GenericCardTest GetFOTRScenario() throws CardNotFoundException, DecisionResultInvalidException { + return new GenericCardTest( + new HashMap() + {{ + put("horn", "21_3042"); + put("elrond", "1_40"); + put("boromir", "1_97"); + put("aragorn", "1_89"); + + put("runner1", "1_178"); + put("runner2", "1_178"); + }}, + GenericCardTest.FellowshipSites, + GenericCardTest.FOTRFrodo, + GenericCardTest.FOTRRing + ); + } + + protected GenericCardTest GetMovieScenario() throws CardNotFoundException, DecisionResultInvalidException { + return new GenericCardTest( + new HashMap() + {{ + put("horn", "21_3042"); + put("elrond", "1_40"); + put("boromir", "1_97"); + put("aragorn", "1_89"); + + put("runner1", "1_178"); + put("runner2", "1_178"); + }} + ); + } + + @Test + public void CanBeBorneByBoromir() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTest scn = GetFOTRScenario(); + + PhysicalCardImpl elrond = scn.GetFreepsCard("elrond"); + PhysicalCardImpl boromir = scn.GetFreepsCard("boromir"); + PhysicalCardImpl aragorn = scn.GetFreepsCard("aragorn"); + PhysicalCardImpl horn = scn.GetFreepsCard("horn"); + + scn.FreepsMoveCharToTable(elrond); + + scn.FreepsMoveCharToTable(aragorn); + scn.FreepsMoveCardToHand(horn); + scn.FreepsMoveCardToHand(boromir); + + scn.StartGame(); + + assertFalse(scn.FreepsActionAvailable("Play Horn of Boromir")); + scn.FreepsUseAction("Play Boromir"); + assertTrue(scn.FreepsActionAvailable("Play Horn of Boromir")); + scn.FreepsUseAction("Play Horn of Boromir"); + + Assert.assertEquals(Zone.ATTACHED, horn.getZone()); + Assert.assertEquals(boromir, horn.getAttachedTo()); + } + + @Test + public void AbilityExertsAndAssignsBoromir() throws DecisionResultInvalidException, CardNotFoundException { + //Pre-game setup + GenericCardTest scn = GetFOTRScenario(); + + PhysicalCardImpl elrond = scn.GetFreepsCard("elrond"); + PhysicalCardImpl boromir = scn.GetFreepsCard("boromir"); + PhysicalCardImpl horn = scn.GetFreepsCard("horn"); + PhysicalCardImpl runner1 = scn.GetShadowCard("runner1"); + PhysicalCardImpl runner2 = scn.GetShadowCard("runner2"); + + scn.FreepsMoveCharToTable(elrond); + scn.FreepsMoveCharToTable(boromir); + scn.FreepsMoveCardToHand(horn); + + scn.ShadowMoveCharToTable(runner1); + scn.ShadowMoveCharToTable(runner2); + + scn.StartGame(); + + scn.FreepsUseAction("Play Horn of Boromir"); + + scn.SkipToPhase(Phase.ASSIGNMENT); + assertTrue(scn.FreepsActionAvailable("Use Horn of Boromir")); + scn.FreepsUseAction("Use Horn of Boromir"); + scn.FreepsChooseCard(runner1); + + assertEquals(1, scn.GetWoundsOn(boromir)); + assertEquals(11, scn.GetStrength(elrond)); + //Skip shadow player's action + scn.SkipCurrentPhaseActions(); + assertFalse(scn.FreepsActionAvailable("Use Horn of Boromir")); + scn.SkipCurrentPhaseActions(); + assertTrue(scn.IsCharAssigned(boromir)); + + scn.FreepsAssignToMinion(elrond, runner2); + assertTrue(scn.IsCharAssigned(elrond)); + } + + @Test + public void AbilityDoesNotPumpFarAwayAllies() throws DecisionResultInvalidException, CardNotFoundException { + GenericCardTest scn = GetMovieScenario(); + + PhysicalCardImpl elrond = scn.GetFreepsCard("elrond"); + PhysicalCardImpl boromir = scn.GetFreepsCard("boromir"); + PhysicalCardImpl horn = scn.GetFreepsCard("horn"); + PhysicalCardImpl runner1 = scn.GetShadowCard("runner1"); + PhysicalCardImpl runner2 = scn.GetShadowCard("runner2"); + + scn.FreepsMoveCharToTable(elrond); + scn.FreepsMoveCharToTable(boromir); + scn.FreepsMoveCardToHand(horn); + + scn.ShadowMoveCharToTable(runner1); + scn.ShadowMoveCharToTable(runner2); + + scn.StartGame(); + + scn.FreepsUseAction("Play Horn of Boromir"); + + scn.SkipToPhase(Phase.ASSIGNMENT); + scn.FreepsUseAction("Use Horn of Boromir"); + scn.FreepsChooseCard(runner1); + + assertEquals(1, scn.GetWoundsOn(boromir)); + assertEquals(8, scn.GetStrength(elrond)); + //Skip shadow player's action + scn.SkipCurrentPhaseActions(); + + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/Sam_SoHErrataTest.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/Sam_SoHErrataTest.java index 12406463d..7bd48f246 100644 --- a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/Sam_SoHErrataTest.java +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/cards/unofficial/pc/errata/Sam_SoHErrataTest.java @@ -17,9 +17,7 @@ public class Sam_SoHErrataTest new HashMap() {{ put("sam", "21_10311"); - }}, - new HashMap() - {{ + put("orc", "1_272"); }} );