Site Control trigger reworks
- Added SiteLiberated trigger with associated helper classes - Renamed trigger TakenControlOfSite -> SiteControlled - Renamed result TakeControlOfSiteResult -> SiteControlledResult - Renamed trigger enum TAKE_CONTROL_OF_SITE -> CONTROL_SITE - Implemented V2_1 Driven Into The Hills, which uses a liberate trigger, with associated tests - Fixed Rapt Hillman liberation not being optional
This commit is contained in:
@@ -1164,41 +1164,46 @@
|
||||
vitality: 2
|
||||
site: 4
|
||||
effects: [
|
||||
{
|
||||
type: trigger
|
||||
trigger: {
|
||||
type: startOfPhase
|
||||
phase: regroup
|
||||
}
|
||||
effect: {
|
||||
type: preventable
|
||||
player: shadow
|
||||
text: Would you like to spot 3 Men to prevent liberating site?
|
||||
cost: {
|
||||
type: spot
|
||||
filter: man
|
||||
count: 3
|
||||
}
|
||||
effect: {
|
||||
type: liberateSite
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
type: activated
|
||||
phase: maneuver
|
||||
requires: {
|
||||
type: canSpot
|
||||
filter: another,culture(men),man
|
||||
}
|
||||
cost: {
|
||||
type: exert
|
||||
select: self
|
||||
}
|
||||
effect: {
|
||||
type: takeControlOfSite
|
||||
}
|
||||
}
|
||||
{
|
||||
type: trigger
|
||||
trigger: {
|
||||
type: startOfPhase
|
||||
phase: regroup
|
||||
}
|
||||
effect: {
|
||||
type: Optional
|
||||
player: freeps
|
||||
text: Would you like to liberate a site?
|
||||
effect: {
|
||||
type: preventable
|
||||
player: shadow
|
||||
text: Would you like to spot 3 Men to prevent liberating site?
|
||||
cost: {
|
||||
type: spot
|
||||
filter: man
|
||||
count: 3
|
||||
}
|
||||
effect: {
|
||||
type: liberateSite
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
type: activated
|
||||
phase: maneuver
|
||||
requires: {
|
||||
type: canSpot
|
||||
filter: another,culture(men),man
|
||||
}
|
||||
cost: {
|
||||
type: exert
|
||||
select: self
|
||||
}
|
||||
effect: {
|
||||
type: takeControlOfSite
|
||||
}
|
||||
}
|
||||
]
|
||||
gametext: At the start of the regroup phase, the Free People's player may liberate a site. You may spot 3 Men to prevent this.<br><b>Maneuver:</b> Spot another [men] Man and exert this minion to take control of a site.
|
||||
lore: Dunlendings take delight in destruction.
|
||||
|
||||
@@ -1493,7 +1493,7 @@
|
||||
type: trigger
|
||||
optional: true
|
||||
trigger: {
|
||||
type: takenControlOfSite
|
||||
type: SiteControlled
|
||||
}
|
||||
requires: {
|
||||
type: canSpot
|
||||
|
||||
@@ -22,18 +22,39 @@
|
||||
twilight: 1
|
||||
type: Condition
|
||||
keywords: Support Area
|
||||
/*requires: {
|
||||
|
||||
}
|
||||
|
||||
effects: [
|
||||
{
|
||||
|
||||
type: ToPlay
|
||||
requires: {
|
||||
type: CanSpot
|
||||
filter: culture(Dunland), minion
|
||||
}
|
||||
}
|
||||
{
|
||||
|
||||
type: Modifier
|
||||
modifier: {
|
||||
type: SkipPhase
|
||||
phase: Archery
|
||||
requires: {
|
||||
type: HaveInitiative
|
||||
side: Shadow
|
||||
}
|
||||
}
|
||||
}
|
||||
]*/
|
||||
gametext: To play, spot a [Dunland] minion.<br>While the shadow has initiative, skip the archery phase.<br>If the Free-Peoples player liberates a site, discard this condition.
|
||||
{
|
||||
type: trigger
|
||||
optional: false
|
||||
trigger: {
|
||||
type: SiteLiberated
|
||||
}
|
||||
effect: {
|
||||
type: Discard
|
||||
select: self
|
||||
}
|
||||
}
|
||||
]
|
||||
gametext: To play, spot a [Dunland] minion.<br>While the shadow has initiative, skip the archery phase.<br>When the Free-Peoples player liberates a site, discard this condition.
|
||||
lore: "'Not in half a thousand years have they forgotten their grievance that the lords of Gondor gave the Mark to Eorl the Young and made alliance with him.'"
|
||||
promotext: ""
|
||||
alts: {
|
||||
|
||||
@@ -7,10 +7,10 @@ import com.gempukku.lotro.cards.build.PlayerSource;
|
||||
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.EffectResult;
|
||||
import com.gempukku.lotro.logic.timing.results.TakeControlOfSiteResult;
|
||||
import com.gempukku.lotro.logic.timing.results.SiteControlledResult;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
public class TakenControlOfSite implements TriggerCheckerProducer {
|
||||
public class SiteControlled implements TriggerCheckerProducer {
|
||||
@Override
|
||||
public TriggerChecker getTriggerChecker(JSONObject value, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
FieldUtils.validateAllowedFields(value, "player");
|
||||
@@ -23,8 +23,8 @@ public class TakenControlOfSite implements TriggerCheckerProducer {
|
||||
public boolean accepts(ActionContext actionContext) {
|
||||
String playerId = playerSource.getPlayer(actionContext);
|
||||
EffectResult effectResult = actionContext.getEffectResult();
|
||||
if (effectResult.getType() == EffectResult.Type.TAKE_CONTROL_OF_SITE) {
|
||||
TakeControlOfSiteResult takeResult = (TakeControlOfSiteResult) effectResult;
|
||||
if (effectResult.getType() == EffectResult.Type.CONTROL_SITE) {
|
||||
SiteControlledResult takeResult = (SiteControlledResult) effectResult;
|
||||
return takeResult.getPlayerId().equals(playerId);
|
||||
}
|
||||
return false;
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.gempukku.lotro.cards.build.field.effect.trigger;
|
||||
|
||||
import com.gempukku.lotro.cards.build.ActionContext;
|
||||
import com.gempukku.lotro.cards.build.CardGenerationEnvironment;
|
||||
import com.gempukku.lotro.cards.build.InvalidCardDefinitionException;
|
||||
import com.gempukku.lotro.cards.build.PlayerSource;
|
||||
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.EffectResult;
|
||||
import com.gempukku.lotro.logic.timing.results.SiteControlledResult;
|
||||
import com.gempukku.lotro.logic.timing.results.SiteLiberatedResult;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
public class SiteLiberated implements TriggerCheckerProducer {
|
||||
@Override
|
||||
public TriggerChecker getTriggerChecker(JSONObject value, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
FieldUtils.validateAllowedFields(value);
|
||||
|
||||
return new TriggerChecker() {
|
||||
@Override
|
||||
public boolean accepts(ActionContext actionContext) {
|
||||
EffectResult effectResult = actionContext.getEffectResult();
|
||||
return effectResult.getType() == EffectResult.Type.LIBERATE_SITE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBefore() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -65,9 +65,10 @@ public class TriggerCheckerFactory {
|
||||
triggerCheckers.put("replacessite", new ReplacesSite());
|
||||
triggerCheckers.put("revealscardfromtopofdrawdeck", new RevealsCardFromTopOfDrawDeck());
|
||||
triggerCheckers.put("revealedcardfromhand", new RevealedCardFromHand());
|
||||
triggerCheckers.put("siteliberated", new SiteLiberated());
|
||||
triggerCheckers.put("sitecontrolled", new SiteControlled());
|
||||
triggerCheckers.put("skirmishabouttoend", new SkirmishAboutToEnd());
|
||||
triggerCheckers.put("startofskirmishinvolving", new StartOfSkirmishInvolving());
|
||||
triggerCheckers.put("takencontrolofsite", new TakenControlOfSite());
|
||||
triggerCheckers.put("takesoffring", new TakesOffRing());
|
||||
triggerCheckers.put("takeswound", new TakesWound());
|
||||
triggerCheckers.put("transferred", new Transferred());
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.gempukku.lotro.logic.timing.AbstractEffect;
|
||||
import com.gempukku.lotro.logic.timing.Effect;
|
||||
import com.gempukku.lotro.logic.timing.PlayConditions;
|
||||
import com.gempukku.lotro.logic.timing.results.DiscardCardsFromPlayResult;
|
||||
import com.gempukku.lotro.logic.timing.results.SiteLiberatedResult;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -85,6 +86,7 @@ public class LiberateASiteEffect extends AbstractEffect {
|
||||
|
||||
game.getGameState().sendMessage(_liberatingPlayer + " liberated a " + GameUtils.getCardLink(siteToLiberate) + " using " + GameUtils.getCardLink(_source));
|
||||
|
||||
game.getActionsEnvironment().emitEffectResult(new SiteLiberatedResult(_liberatingPlayer));
|
||||
liberatedSiteCallback(siteToLiberate);
|
||||
|
||||
return new FullEffectResult(true);
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.gempukku.lotro.logic.GameUtils;
|
||||
import com.gempukku.lotro.logic.timing.AbstractEffect;
|
||||
import com.gempukku.lotro.logic.timing.Effect;
|
||||
import com.gempukku.lotro.logic.timing.Preventable;
|
||||
import com.gempukku.lotro.logic.timing.results.TakeControlOfSiteResult;
|
||||
import com.gempukku.lotro.logic.timing.results.SiteControlledResult;
|
||||
|
||||
public class TakeControlOfASiteEffect extends AbstractEffect implements Preventable {
|
||||
private final PhysicalCard _source;
|
||||
@@ -69,7 +69,7 @@ public class TakeControlOfASiteEffect extends AbstractEffect implements Preventa
|
||||
if (site != null && !_prevented) {
|
||||
game.getGameState().takeControlOfCard(_playerId, game, site, Zone.SUPPORT);
|
||||
game.getGameState().sendMessage(_playerId + " took control of " + GameUtils.getCardLink(site));
|
||||
game.getActionsEnvironment().emitEffectResult(new TakeControlOfSiteResult(_playerId));
|
||||
game.getActionsEnvironment().emitEffectResult(new SiteControlledResult(_playerId));
|
||||
return new FullEffectResult(true);
|
||||
}
|
||||
return new FullEffectResult(false);
|
||||
|
||||
@@ -56,7 +56,7 @@ public abstract class EffectResult {
|
||||
|
||||
ASSIGNED_AGAINST, ASSIGNED_TO_SKIRMISH, CARD_TRANSFERRED,
|
||||
|
||||
REPLACE_SITE, TAKE_CONTROL_OF_SITE,
|
||||
REPLACE_SITE, CONTROL_SITE, LIBERATE_SITE,
|
||||
|
||||
FINISHED_PLAYING_FELLOWSHIP,
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ package com.gempukku.lotro.logic.timing.results;
|
||||
|
||||
import com.gempukku.lotro.logic.timing.EffectResult;
|
||||
|
||||
public class TakeControlOfSiteResult extends EffectResult {
|
||||
public class SiteControlledResult extends EffectResult {
|
||||
private final String _playerId;
|
||||
|
||||
public TakeControlOfSiteResult(String playerId) {
|
||||
super(Type.TAKE_CONTROL_OF_SITE);
|
||||
public SiteControlledResult(String playerId) {
|
||||
super(Type.CONTROL_SITE);
|
||||
_playerId = playerId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.gempukku.lotro.logic.timing.results;
|
||||
|
||||
import com.gempukku.lotro.logic.timing.EffectResult;
|
||||
|
||||
public class SiteLiberatedResult extends EffectResult {
|
||||
private final String _playerId;
|
||||
|
||||
public SiteLiberatedResult(String playerId) {
|
||||
super(Type.LIBERATE_SITE);
|
||||
_playerId = playerId;
|
||||
}
|
||||
|
||||
public String getPlayerId() {
|
||||
return _playerId;
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public class WinConditionRule {
|
||||
game.playerWon(game.getGameState().getCurrentPlayerId(), "Surviving to Regroup phase on site 9");
|
||||
}
|
||||
} else if (game.getFormat().winOnControlling5Sites()
|
||||
&& effectResults.getType() == EffectResult.Type.TAKE_CONTROL_OF_SITE) {
|
||||
&& effectResults.getType() == EffectResult.Type.CONTROL_SITE) {
|
||||
for (String opponent : GameUtils.getShadowPlayers(game)) {
|
||||
if (Filters.countActive(game, CardType.SITE, Filters.siteControlled(opponent)) >= 5)
|
||||
game.playerWon(opponent, "Controls 5 sites");
|
||||
|
||||
@@ -956,6 +956,10 @@ public class GenericCardTestHelper extends AbstractAtTest {
|
||||
}
|
||||
|
||||
|
||||
public Boolean IsSiteControlled(PhysicalCardImpl card) {
|
||||
return card.getCardController() != null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void ApplyAdHocModifier(Modifier mod)
|
||||
|
||||
@@ -2,9 +2,12 @@ package com.gempukku.lotro.cards.unofficial.pc.vsets.set_v02;
|
||||
|
||||
import com.gempukku.lotro.cards.GenericCardTestHelper;
|
||||
import com.gempukku.lotro.common.*;
|
||||
import com.gempukku.lotro.filters.Filters;
|
||||
import com.gempukku.lotro.game.CardNotFoundException;
|
||||
import com.gempukku.lotro.game.PhysicalCardImpl;
|
||||
import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException;
|
||||
import com.gempukku.lotro.logic.modifiers.AddKeywordModifier;
|
||||
import com.gempukku.lotro.logic.modifiers.HasInitiativeModifier;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -18,8 +21,12 @@ public class Card_V2_001_Tests
|
||||
return new GenericCardTestHelper(
|
||||
new HashMap<>()
|
||||
{{
|
||||
put("card", "102_1");
|
||||
// put other cards in here as needed for the test case
|
||||
put("driven", "102_1");
|
||||
put("madman", "4_12");
|
||||
put("freca", "9_2");
|
||||
put("hillman", "15_90");
|
||||
|
||||
put("runner", "1_178");
|
||||
}},
|
||||
GenericCardTestHelper.FellowshipSites,
|
||||
GenericCardTestHelper.FOTRFrodo,
|
||||
@@ -46,7 +53,7 @@ public class Card_V2_001_Tests
|
||||
|
||||
var scn = GetScenario();
|
||||
|
||||
var card = scn.GetFreepsCard("card");
|
||||
var card = scn.GetFreepsCard("driven");
|
||||
|
||||
assertEquals("Driven into the Hills", card.getBlueprint().getTitle());
|
||||
assertNull(card.getBlueprint().getSubtitle());
|
||||
@@ -58,18 +65,120 @@ public class Card_V2_001_Tests
|
||||
assertEquals(1, card.getBlueprint().getTwilightCost());
|
||||
}
|
||||
|
||||
// Uncomment any @Test markers below once this is ready to be used
|
||||
//@Test
|
||||
public void DrivenintotheHillsTest1() throws DecisionResultInvalidException, CardNotFoundException {
|
||||
@Test
|
||||
public void DrivenRequiresDunlandMinionToPlay() throws DecisionResultInvalidException, CardNotFoundException {
|
||||
//Pre-game setup
|
||||
var scn = GetScenario();
|
||||
|
||||
var card = scn.GetFreepsCard("card");
|
||||
scn.FreepsMoveCardToHand(card);
|
||||
var driven = scn.GetShadowCard("driven");
|
||||
var runner = scn.GetShadowCard("runner");
|
||||
var madman = scn.GetShadowCard("madman");
|
||||
scn.ShadowMoveCardToHand(driven, runner, madman);
|
||||
|
||||
scn.StartGame();
|
||||
scn.FreepsPlayCard(card);
|
||||
|
||||
assertEquals(1, scn.GetTwilight());
|
||||
scn.SetTwilight(50);
|
||||
scn.FreepsPassCurrentPhaseAction();
|
||||
|
||||
assertFalse(scn.ShadowPlayAvailable(driven));
|
||||
scn.ShadowPlayCard(runner);
|
||||
assertFalse(scn.ShadowPlayAvailable(driven));
|
||||
scn.ShadowPlayCard(madman);
|
||||
assertTrue(scn.ShadowPlayAvailable(driven));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void DrivenSkipsArcheryPhaseIfShadowHasInitiative() throws DecisionResultInvalidException, CardNotFoundException {
|
||||
//Pre-game setup
|
||||
var scn = GetScenario();
|
||||
|
||||
var driven = scn.GetShadowCard("driven");
|
||||
var madman = scn.GetShadowCard("madman");
|
||||
scn.ShadowMoveCardToSupportArea(driven);
|
||||
scn.ShadowMoveCharToTable(madman);
|
||||
|
||||
scn.StartGame();
|
||||
|
||||
//cheating to give Shadow initiative
|
||||
scn.ApplyAdHocModifier(new HasInitiativeModifier(null, null, Side.SHADOW));
|
||||
|
||||
scn.SkipToPhase(Phase.MANEUVER);
|
||||
|
||||
assertTrue(scn.ShadowHasInitiative());
|
||||
assertEquals(Phase.MANEUVER, scn.GetCurrentPhase());
|
||||
|
||||
scn.PassCurrentPhaseActions();
|
||||
|
||||
//Skipped archery entirely
|
||||
assertEquals(Phase.ASSIGNMENT, scn.GetCurrentPhase());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void DrivenDoesNotSkipsArcheryPhaseIfFreepsHasInitiative() throws DecisionResultInvalidException, CardNotFoundException {
|
||||
//Pre-game setup
|
||||
var scn = GetScenario();
|
||||
|
||||
var driven = scn.GetShadowCard("driven");
|
||||
var madman = scn.GetShadowCard("madman");
|
||||
scn.ShadowMoveCardToSupportArea(driven);
|
||||
scn.ShadowMoveCharToTable(madman);
|
||||
|
||||
scn.StartGame();
|
||||
|
||||
//cheating to give Freeps initiative, since there are too few cards in hand
|
||||
scn.ApplyAdHocModifier(new HasInitiativeModifier(null, null, Side.FREE_PEOPLE));
|
||||
|
||||
scn.SkipToPhase(Phase.MANEUVER);
|
||||
|
||||
assertFalse(scn.ShadowHasInitiative());
|
||||
assertEquals(Phase.MANEUVER, scn.GetCurrentPhase());
|
||||
|
||||
scn.PassCurrentPhaseActions();
|
||||
|
||||
//Did not skip archery
|
||||
assertEquals(Phase.ARCHERY, scn.GetCurrentPhase());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void DrivenSelfDiscardsWhenASiteIsLiberated() throws DecisionResultInvalidException, CardNotFoundException {
|
||||
//Pre-game setup
|
||||
var scn = GetScenario();
|
||||
|
||||
var driven = scn.GetShadowCard("driven");
|
||||
var madman = scn.GetShadowCard("madman");
|
||||
var hillman = scn.GetShadowCard("hillman");
|
||||
var freca = scn.GetShadowCard("freca");
|
||||
scn.ShadowMoveCardToSupportArea(driven);
|
||||
|
||||
var site1 = scn.GetFreepsSite(1);
|
||||
|
||||
scn.StartGame();
|
||||
|
||||
//Need to have sites controlled to liberate
|
||||
scn.SkipToSite(3);
|
||||
|
||||
scn.ShadowMoveCharToTable(hillman, madman);
|
||||
scn.ShadowMoveCardToHand(freca);
|
||||
scn.FreepsPassCurrentPhaseAction();
|
||||
|
||||
scn.ShadowPlayCard(freca);
|
||||
scn.ShadowAcceptOptionalTrigger();
|
||||
|
||||
assertTrue(scn.IsSiteControlled(site1));
|
||||
|
||||
scn.SkipToPhase(Phase.MANEUVER);
|
||||
|
||||
assertTrue(scn.IsSiteControlled(site1));
|
||||
assertEquals(Zone.SUPPORT, driven.getZone());
|
||||
|
||||
scn.SkipToPhase(Phase.REGROUP);
|
||||
|
||||
//Rapt Hillman gives Freeps the opportunity to liberate a site,
|
||||
// and shadow can stop it (but we choose not to)
|
||||
scn.FreepsAcceptOptionalTrigger();
|
||||
scn.ShadowChooseNo();
|
||||
|
||||
assertFalse(scn.IsSiteControlled(site1));
|
||||
assertEquals(Zone.DISCARD, driven.getZone());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user