Optional effect type overhaul

- So it turns out that Optional is frequently used to wrap a CostToEffect, but because of how that's set up the Optional does not ensure the "Cost" part is actually enforced.  Thus someone can choose "yes" and it will always treat both halves of a CostToEffect as optional.
- Added the standard "requires" and "cost" fields to Optional
- Refactored about 30 cards to no longer use CostToEffect nested within an Optional, instead opting to use the cost field of Optional itself
This commit is contained in:
Christian 'ketura' McCarty
2025-04-08 23:21:40 -05:00
parent 2fd2958fab
commit 75bd836c62
28 changed files with 373 additions and 299 deletions

View File

@@ -688,20 +688,17 @@
}
true: {
type: optional
text: Would you like to discard an ISENGARD minion revealed to draw 2 cards?
text: Would you like to discard an {ISENGARD} minion revealed to draw 2 cards?
cost: {
type: discardFromHand
hand: shadowPlayer
forced: true
player: you
select: choose(memory(revealedCards),culture(isengard),minion)
}
effect: {
type: costToEffect
cost: {
type: discardFromHand
hand: shadowPlayer
forced: true
player: you
select: choose(memory(revealedCards),culture(isengard),minion)
}
effect: {
type: drawCards
count: 2
}
type: drawCards
count: 2
}
}
}

View File

@@ -28,19 +28,16 @@
type: optional
player: fp
text: Would you like to discard 2 cards from hand to discard Dunlending Rampager?
cost: {
type: discardFromHand
forced: false
player: fp
hand: fp
count: 2
}
effect: {
type: costToEffect
cost: {
type: discardFromHand
forced: false
player: fp
hand: fp
count: 2
}
effect: {
type: discard
select: self
}
type: discard
select: self
}
}
}
@@ -84,19 +81,16 @@
type: optional
player: fp
text: Would you like to discard 2 cards from hand to discard Dunlending Rampager?
cost: {
type: discardFromHand
forced: false
player: fp
hand: fp
count: 2
}
effect: {
type: costToEffect
cost: {
type: discardFromHand
forced: false
player: fp
hand: fp
count: 2
}
effect: {
type: discard
select: self
}
type: discard
select: self
}
}
}
@@ -204,19 +198,16 @@
type: optional
player: fp
text: Would you like to discard 4 cards from hand to discard Wild Men of the Hills?
cost: {
type: discardFromHand
forced: false
player: fp
hand: fp
count: 4
}
effect: {
type: costToEffect
cost: {
type: discardFromHand
forced: false
player: fp
hand: fp
count: 4
}
effect: {
type: discard
select: self
}
type: discard
select: self
}
}
}

View File

@@ -240,20 +240,17 @@
{
type: optional
text: Would you like to exert Glorfindel to make a Nazgul he is skirmishing strength -X, where X is the twilight cost the the card revealed?
cost: {
type: exert
select: self
}
effect: {
type: costToEffect
cost: {
type: exert
select: self
}
effect: {
type: modifyStrength
select: choose(nazgul,inSkirmishAgainst(self))
amount: {
type: twilightCostInMemory
memory: revealedCard
multiplier: -1
}
type: modifyStrength
select: choose(nazgul,inSkirmishAgainst(self))
amount: {
type: twilightCostInMemory
memory: revealedCard
multiplier: -1
}
}
}

View File

@@ -168,17 +168,17 @@
type: optional
player: shadowPlayer
text: Would you like to discard 2 cards to draw 2 cards?
cost: {
type: discardFromHand
player: shadow
hand: shadow
forced: false
count: 2
}
effect: {
type: costToEffect
cost: {
type: discardFromHand
forced: false
count: 2
}
effect: {
type: drawCards
count: 2
}
type: drawCards
player: shadow
count: 2
}
}
}

View File

@@ -80,19 +80,16 @@
}
{
type: optional
text: Would you like exert the played ROHAN Man to play a possession on him or her?
text: Would you like exert the played {ROHAN} Man to play a possession on him or her?
cost: {
type: exert
select: memory(playedRohanMan)
ignoreCostCheckFailure: true
}
effect: {
type: costToEffect
cost: {
type: exert
select: memory(playedRohanMan)
ignoreCostCheckFailure: true
}
effect: {
type: PlayCardFromHand
select: choose(possession)
on: memory(playedRohanMan)
}
type: PlayCardFromHand
select: choose(possession)
on: memory(playedRohanMan)
}
}
{

View File

@@ -199,19 +199,16 @@
}
true: {
type: optional
text: Discard the card?
text: Discard the card to wound each minion in this skirmish?
cost: {
type: discardCardsFromDrawDeck
select: memory(revealedCard)
showAll: false
shuffle: false
}
effect: {
type: costToEffect
cost: {
type: discardCardsFromDrawDeck
select: memory(revealedCard)
showAll: false
shuffle: false
}
effect: {
type: wound
select: all(minion,inSkirmish)
}
type: wound
select: all(minion,inSkirmish)
}
}
}

View File

@@ -381,15 +381,12 @@
}
{
type: optional
text: Do you want to return it to hand?
text: Do you want to add a burden to return {self} to hand?
cost: {
type: addBurdens
}
effect: {
type: costToEffect
cost: {
type: addBurdens
}
effect: {
type: putPlayedEventIntoHand
}
type: putPlayedEventIntoHand
}
}
]

View File

@@ -1733,16 +1733,13 @@
true: {
type: optional
text: Would you like to remove (3) to wound {woundedCharacter} again?
cost: {
type: removeTwilight
amount: 3
}
effect: {
type: costToEffect
cost: {
type: removeTwilight
amount: 3
}
effect: {
type: wound
select: memory(woundedCharacter)
}
type: wound
select: memory(woundedCharacter)
}
}
}

View File

@@ -349,17 +349,14 @@
}
true: {
type: optional
text: Would you like to discard 2 cards to return this event to your hand?
text: Would you like to discard 2 cards to return {self} to your hand?
cost: {
type: discardFromHand
forced: false
count: 2
}
effect: {
type: costToEffect
cost: {
type: discardFromHand
forced: false
count: 2
}
effect: {
type: putPlayedEventIntoHand
}
type: putPlayedEventIntoHand
}
}
}

View File

@@ -856,15 +856,12 @@
effect: {
type: optional
text: Would you like to heal the Ring-beaer to add a burden?
cost: {
type: heal
select: choose(ringBearer)
}
effect: {
type: costToEffect
cost: {
type: heal
select: choose(ringBearer)
}
effect: {
type: addBurdens
}
type: addBurdens
}
}
}

View File

@@ -963,18 +963,15 @@
type: event
cost: {
type: optional
text: Would you like to remove 2 burdens?
text: Would you like to remove 2 burdens to add +4 more strength?
cost: {
type: removeBurdens
amount: 2
}
effect: {
type: costToEffect
cost: {
type: removeBurdens
amount: 2
}
effect: {
type: MemorizeInfo
memory: removedBurdens
info: yes
}
type: MemorizeInfo
memory: removedBurdens
info: yes
}
}
effect: [

View File

@@ -740,10 +740,8 @@
}
true: {
type: optional
text: Would you like to exert Théoden?
effect: {
type: costToEffect
cost: {
text: Would you like to exert Théoden to add +3 strength and damage +1?
cost: {
type: exert
select: choose(name(Théoden))
}
@@ -752,7 +750,6 @@
memory: exertedTheoden
info: yes
}
}
}
}
{

View File

@@ -671,31 +671,28 @@
type: optional
player: free people
text: Would you like to add a burden or exert the Ring-bearer twice to discard Orkish Sneak?
cost: {
type: choice
player: free people
texts: [
Add a burden
Exert the Ring-bearer twice
]
effects: [
{
type: addBurdens
}
{
type: exert
select: choose(ringBearer)
times: 2
}
]
}
effect: {
type: costToEffect
cost: {
type: choice
player: free people
texts: [
Add a burden
Exert the Ring-bearer twice
]
effects: [
{
type: addBurdens
}
{
type: exert
select: choose(ringBearer)
times: 2
}
]
}
effect: {
type: discard
player: free people
select: self
}
type: discard
player: free people
select: self
}
}
]

View File

@@ -25,15 +25,12 @@
}
{
type: optional
text: Would you like to remove (1) to place this event on the bottom of your draw deck?
text: Would you like to remove (1) to place {self} on the bottom of your draw deck?
cost: {
type: removeTwilight
}
effect: {
type: costToEffect
cost: {
type: removeTwilight
}
effect: {
type: putPlayedEventOnBottomOfDrawDeck
}
type: putPlayedEventOnBottomOfDrawDeck
}
}
]

View File

@@ -281,17 +281,14 @@
{
type: optional
player: fp
text: Would you like to add a burden to discard this minion?
text: Would you like to add a burden to discard {self}?
cost: {
type: addBurdens
amount: 1
}
effect: {
type: costToEffect
cost: {
type: addBurdens
amount: 1
}
effect: {
type: discard
select: self
}
type: discard
select: self
}
}
]

View File

@@ -1133,10 +1133,8 @@
}
{
type: optional
text: Would you like to discard 2 Orcs to take this event back into hand?
effect: {
type: costToEffect
cost: {
text: Would you like to discard 2 Orcs to take {self} back into hand?
cost: {
type: discard
select: choose(orc)
count: 2
@@ -1144,7 +1142,6 @@
effect: {
type: putPlayedEventIntoHand
}
}
}
]
}

View File

@@ -47,17 +47,14 @@
type: Optional
player: you
text: Would you like to exert another Elf ally to discard another condition?
cost: {
type: exert
select: choose(Elf,ally,not(memory(firstAlly)))
}
effect: {
type: CostToEffect
cost: {
type: exert
select: choose(Elf,ally,not(memory(firstAlly)))
}
effect: {
type: Discard
count: 1
select: choose(condition)
}
type: Discard
count: 1
select: choose(condition)
}
}
}

View File

@@ -152,20 +152,17 @@
{
type: optional
text: Would you like to exert Glorfindel to make a Nazgul he is skirmishing strength -X, where X is the twilight cost the the card revealed?
cost: {
type: exert
select: self
}
effect: {
type: costToEffect
cost: {
type: exert
select: self
}
effect: {
type: modifyStrength
select: choose(nazgul,inSkirmishAgainst(self))
amount: {
type: twilightCostInMemory
memory: revealedCard
multiplier: -1
}
type: modifyStrength
select: choose(nazgul,inSkirmishAgainst(self))
amount: {
type: twilightCostInMemory
memory: revealedCard
multiplier: -1
}
}
}

View File

@@ -19,7 +19,6 @@
twilight: 4
type: Companion
race: Wizard
#keyword: damage+1
strength: 7
vitality: 4
signet: Gandalf
@@ -42,17 +41,17 @@
type: optional
player: shadowPlayer
text: Would you like to discard 2 cards to draw 2 cards?
cost: {
type: discardFromHand
player: shadow
hand: shadow
forced: false
count: 2
}
effect: {
type: costToEffect
cost: {
type: discardFromHand
forced: false
count: 2
}
effect: {
type: drawCards
count: 2
}
type: drawCards
player: shadow
count: 2
}
}
}

View File

@@ -30,18 +30,15 @@
type: optional
player: shadow
text: Would you like to discard 2 cards from hand to add a burden?
cost: {
type: discardFromHand
hand: shadowPlayer
player: shadowPlayer
forced: false
count: 2
}
effect: {
type: costToEffect
cost: {
type: discardFromHand
hand: shadowPlayer
player: shadowPlayer
forced: false
count: 2
}
effect: {
type: addBurdens
}
type: addBurdens
}
}
}

View File

@@ -29,24 +29,21 @@
}
effect: {
type: optional
text: Would you like to pay this card's twilight cost to take it into hand?
text: Would you like to pay {chosenAlly} twilight cost to take it into hand?
cost: {
type: addtwilight
amount: {
type: twilightCostInMemory
memory: chosenAlly
}
}
effect: {
type: costToEffect
cost: {
type: addtwilight
amount: {
type: twilightCostInMemory
memory: chosenAlly
}
}
effect: {
type: putcardsfromdeckintohand
count: 1
select: memory(chosenAlly)
reveal: true
shuffle: false
showAll: false
}
type: putcardsfromdeckintohand
count: 1
select: memory(chosenAlly)
reveal: true
shuffle: false
showAll: false
}
}
}

View File

@@ -32,18 +32,15 @@
effect: {
type: Optional
text: Would you like to discard 2 cards to draw 2?
cost: {
type: DiscardFromHand
forced: false
select: choose(any)
count: 2
}
effect: {
type: CostToEffect
cost: {
type: DiscardFromHand
forced: false
select: choose(any)
count: 2
}
effect: {
type: DrawCards
count: 2
}
type: DrawCards
count: 2
}
}
}

View File

@@ -70,6 +70,16 @@ public class CostToEffect implements EffectAppenderProducer {
return true;
}
@Override
public boolean isPlayabilityCheckedForEffect() {
//This is done intentinally, even though one would think that you would want to
// check the "cost" part of "CostToEffect". The reason for this is because CostToEffect
// is frequently used as a wrapper in contexts where the Cost cannot actually be checked
// ahead of time, such as when memory values are crucial to the execution (which of course
// are not popuulated before the action itself is executed at time of checking).
return false;
}
};
}
}

View File

@@ -17,31 +17,43 @@ import org.json.simple.JSONObject;
public class Optional implements EffectAppenderProducer {
@Override
public EffectAppender createEffectAppender(boolean cost, JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
FieldUtils.validateAllowedFields(effectObject, "player", "text", "effect");
FieldUtils.validateAllowedFields(effectObject, "player", "text", "requires", "cost", "effect");
final String player = FieldUtils.getString(effectObject.get("player"), "player", "you");
final String text = FieldUtils.getString(effectObject.get("text"), "text");
final JSONObject[] costArray = FieldUtils.getObjectArray(effectObject.get("cost"), "cost");
final JSONObject[] effectArray = FieldUtils.getObjectArray(effectObject.get("effect"), "effect");
final JSONObject[] requiresArray = FieldUtils.getObjectArray(effectObject.get("requires"), "requires");
if (text == null)
throw new InvalidCardDefinitionException("There is a text required for optional effects");
final PlayerSource playerSource = PlayerResolver.resolvePlayer(player);
final EffectAppender[] costAppenders = environment.getEffectAppenderFactory().getEffectAppenders(true, costArray, environment);
final EffectAppender[] effectAppenders = environment.getEffectAppenderFactory().getEffectAppenders(cost, effectArray, environment);
final Requirement[] requirements = environment.getRequirementFactory().getRequirements(requiresArray, environment);
return new DelayedAppender() {
@Override
protected Effect createEffect(boolean cost, CostToEffectAction action, ActionContext actionContext) {
final String choosingPlayer = playerSource.getPlayer(actionContext);
ActionContext delegate = new DelegateActionContext(actionContext,
choosingPlayer, actionContext.getGame(), actionContext.getSource(),
actionContext.getEffectResult(), actionContext.getEffect());
if(!isPlayableInFull(actionContext))
return null;
SubAction subAction = new SubAction(action);
subAction.appendCost(
new PlayoutDecisionEffect(choosingPlayer,
new YesNoDecision(GameUtils.substituteText(text, actionContext)) {
@Override
protected void yes() {
ActionContext delegate = new DelegateActionContext(actionContext,
choosingPlayer, actionContext.getGame(), actionContext.getSource(),
actionContext.getEffectResult(), actionContext.getEffect());
for (EffectAppender costAppender : costAppenders)
costAppender.appendEffect(true, subAction, actionContext);
for (EffectAppender effectAppender : effectAppenders) {
effectAppender.appendEffect(cost, subAction, delegate);
}
@@ -56,8 +68,18 @@ public class Optional implements EffectAppenderProducer {
ActionContext delegate = new DelegateActionContext(actionContext,
choosingPlayer, actionContext.getGame(), actionContext.getSource(),
actionContext.getEffectResult(), actionContext.getEffect());
if(!checkRequirements(delegate))
return false;
for (EffectAppender costAppender : costAppenders) {
if (!costAppender.isPlayableInFull(delegate))
return false;
}
for (EffectAppender effectAppender : effectAppenders) {
if (!effectAppender.isPlayableInFull(delegate))
if (effectAppender.isPlayabilityCheckedForEffect()
&& !effectAppender.isPlayableInFull(delegate))
return false;
}
@@ -72,6 +94,14 @@ public class Optional implements EffectAppenderProducer {
}
return false;
}
private boolean checkRequirements(ActionContext actionContext) {
for (Requirement req : requirements) {
if (!req.accepts(actionContext))
return false;
}
return true;
}
};
}
}

View File

@@ -55,7 +55,7 @@ public class ExertAtTest extends AbstractAtTest{
// Pass on viewing revealed card
playerDecided(P2, "");
// Can't exert (already exhausted)
playerDecided(P1, "0");
//playerDecided(P1, "0");
assertEquals(10, _game.getModifiersQuerying().getStrength(_game, cantea));
}
}

View File

@@ -3,7 +3,6 @@ package com.gempukku.lotro.cards.official.set05;
import com.gempukku.lotro.cards.GenericCardTestHelper;
import com.gempukku.lotro.common.*;
import com.gempukku.lotro.game.CardNotFoundException;
import com.gempukku.lotro.game.PhysicalCardImpl;
import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException;
import org.junit.Test;
@@ -18,7 +17,7 @@ public class Card_05_001_Tests
return new GenericCardTestHelper(
new HashMap<>()
{{
put("card", "5_1");
put("rampager", "5_1");
// put other cards in here as needed for the test case
}},
GenericCardTestHelper.FellowshipSites,
@@ -47,7 +46,7 @@ public class Card_05_001_Tests
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
var card = scn.GetFreepsCard("rampager");
assertEquals("Dunlending Rampager", card.getBlueprint().getTitle());
assertNull(card.getBlueprint().getSubtitle());
@@ -62,18 +61,21 @@ public class Card_05_001_Tests
assertEquals(3, card.getBlueprint().getSiteNumber());
}
// Uncomment any @Test markers below once this is ready to be used
//@Test
public void DunlendingRampagerTest1() throws DecisionResultInvalidException, CardNotFoundException {
@Test
public void DunlendingRampagerDoesNotOfferChoiceWhenNotEnoughCardsInFreepsHand() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
scn.FreepsMoveCardToHand(card);
var rampager = scn.GetShadowCard("rampager");
scn.ShadowMoveCardToHand(rampager);
scn.StartGame();
scn.FreepsPlayCard(card);
scn.FreepsPassCurrentPhaseAction();
assertTrue(scn.ShadowPlayAvailable(rampager));
scn.ShadowPlayCard(rampager);
assertEquals(1, scn.GetTwilight());
assertEquals(Zone.SHADOW_CHARACTERS, rampager.getZone());
assertEquals(0, scn.GetFreepsHandCount());
assertFalse(scn.FreepsDecisionAvailable("Would you like to discard 2 cards from hand to discard Dunlending Rampager?"));
}
}

View File

@@ -1,12 +1,11 @@
package com.gempukku.lotro.cards.official.set11;
import com.gempukku.lotro.cards.GenericCardTestHelper;
import com.gempukku.lotro.common.CardType;
import com.gempukku.lotro.common.Culture;
import com.gempukku.lotro.common.Side;
import com.gempukku.lotro.common.Timeword;
import com.gempukku.lotro.common.*;
import com.gempukku.lotro.filters.Filters;
import com.gempukku.lotro.game.CardNotFoundException;
import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException;
import com.gempukku.lotro.logic.modifiers.AddKeywordModifier;
import org.junit.Test;
import java.util.HashMap;
@@ -20,8 +19,8 @@ public class Card_11_104_Tests
return new GenericCardTestHelper(
new HashMap<>()
{{
put("card", "11_104");
// put other cards in here as needed for the test case
put("death", "11_104");
put("mouth", "12_73");
}},
GenericCardTestHelper.FellowshipSites,
GenericCardTestHelper.FOTRFrodo,
@@ -41,12 +40,13 @@ public class Card_11_104_Tests
* Twilight Cost: 3
* Type: Event
* Subtype: Skirmish
* Game Text: Wound a character skirmishing a [men] minion. If the fellowship is at a battleground site, you may remove (3) to wound that character again.
* Game Text: Wound a character skirmishing a [men] minion.
* If the fellowship is at a battleground site, you may remove (3) to wound that character again.
*/
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
var card = scn.GetFreepsCard("death");
assertEquals("Whistling Death", card.getBlueprint().getTitle());
assertNull(card.getBlueprint().getSubtitle());
@@ -58,18 +58,117 @@ public class Card_11_104_Tests
assertEquals(3, card.getBlueprint().getTwilightCost());
}
// Uncomment any @Test markers below once this is ready to be used
//@Test
public void WhistlingDeathTest1() throws DecisionResultInvalidException, CardNotFoundException {
@Test
public void WhistlingDeathWoundsThenRemoves3MoreToWoundAgain() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
scn.FreepsMoveCardToHand(card);
var death = scn.GetShadowCard("death");
var mouth = scn.GetShadowCard("mouth");
scn.ShadowMoveCharToTable(mouth);
scn.ShadowMoveCardToHand(death);
var frodo = scn.GetRingBearer();
//cheating to ensure site 2 qualifies
scn.ApplyAdHocModifier(new AddKeywordModifier(null, Filters.siteNumber(2), null, Keyword.BATTLEGROUND));
scn.StartGame();
scn.FreepsPlayCard(card);
assertEquals(3, scn.GetTwilight());
scn.SetTwilight(7);
scn.FreepsPassCurrentPhaseAction();
scn.SkipToAssignments();
scn.FreepsAssignToMinions(frodo, mouth);
scn.FreepsResolveSkirmish(frodo);
scn.FreepsPassCurrentPhaseAction();
assertEquals(0, scn.GetWoundsOn(frodo));
assertEquals(10, scn.GetTwilight());
assertTrue(scn.hasKeyword(scn.GetCurrentSite(), Keyword.BATTLEGROUND));
assertTrue(scn.ShadowPlayAvailable(death));
scn.ShadowPlayCard(death);
scn.FreepsDeclineOptionalTrigger(); //the one ring
assertEquals(1, scn.GetWoundsOn(frodo));
assertEquals(7, scn.GetTwilight());
assertTrue(scn.ShadowDecisionAvailable("Would you like to remove (3) to wound"));
scn.ShadowChooseYes();
scn.FreepsDeclineOptionalTrigger(); //the one ring
assertEquals(2, scn.GetWoundsOn(frodo));
assertEquals(4, scn.GetTwilight());
}
@Test
public void WhistlingDeathSecondWoundIsNotOfferedAtNonBattleground() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var death = scn.GetShadowCard("death");
var mouth = scn.GetShadowCard("mouth");
scn.ShadowMoveCharToTable(mouth);
scn.ShadowMoveCardToHand(death);
var frodo = scn.GetRingBearer();
scn.StartGame();
scn.SetTwilight(7);
scn.FreepsPassCurrentPhaseAction();
scn.SkipToAssignments();
scn.FreepsAssignToMinions(frodo, mouth);
scn.FreepsResolveSkirmish(frodo);
scn.FreepsPassCurrentPhaseAction();
assertEquals(0, scn.GetWoundsOn(frodo));
assertEquals(10, scn.GetTwilight());
assertFalse(scn.hasKeyword(scn.GetCurrentSite(), Keyword.BATTLEGROUND));
assertTrue(scn.ShadowPlayAvailable(death));
scn.ShadowPlayCard(death);
scn.FreepsDeclineOptionalTrigger(); //the one ring
assertEquals(1, scn.GetWoundsOn(frodo));
assertEquals(7, scn.GetTwilight());
assertFalse(scn.ShadowDecisionAvailable("Would you like to remove (3) to wound"));
assertTrue(scn.FreepsAnyDecisionsAvailable());
}
@Test
public void WhistlingDeathSecondWoundIsNotOfferedWhenLessThan3TwilightAvailable() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var death = scn.GetShadowCard("death");
var mouth = scn.GetShadowCard("mouth");
scn.ShadowMoveCharToTable(mouth);
scn.ShadowMoveCardToHand(death);
var frodo = scn.GetRingBearer();
//cheating to ensure site 2 qualifies
scn.ApplyAdHocModifier(new AddKeywordModifier(null, Filters.siteNumber(2), null, Keyword.BATTLEGROUND));
scn.StartGame();
scn.SetTwilight(2);
scn.FreepsPassCurrentPhaseAction();
scn.SkipToAssignments();
scn.FreepsAssignToMinions(frodo, mouth);
scn.FreepsResolveSkirmish(frodo);
scn.FreepsPassCurrentPhaseAction();
assertEquals(0, scn.GetWoundsOn(frodo));
assertEquals(5, scn.GetTwilight());
assertTrue(scn.hasKeyword(scn.GetCurrentSite(), Keyword.BATTLEGROUND));
assertTrue(scn.ShadowPlayAvailable(death));
scn.ShadowPlayCard(death);
scn.FreepsDeclineOptionalTrigger(); //the one ring
assertEquals(1, scn.GetWoundsOn(frodo));
assertEquals(2, scn.GetTwilight());
assertFalse(scn.ShadowDecisionAvailable("Would you like to remove (3) to wound"));
assertTrue(scn.FreepsAnyDecisionsAvailable());
}
}

View File

@@ -137,7 +137,7 @@ public class Card_18_134_ErrataTests
assertTrue(scn.FreepsPlayAvailable(onegoodturn));
scn.FreepsPlayCard(onegoodturn);
//This ensures that we have skipped over choosing a site and instead OGT has moved on to its other clause.
assertTrue(scn.FreepsDecisionAvailable("Do you want to return"));
assertTrue(scn.FreepsDecisionAvailable("Do you want to add a burden"));
assertNotEquals(scn.GetSite(3), freepsSite3);
assertEquals(scn.GetSite(3), doorway);