Mountain-troll errata

- Overhauled how Discounts work for json cards to support all the existing DiscountEffects.
- Altered InZoneData to now be preserved when the data was set in the void/void-from-hand.
- Converted the original Mountain-troll to json
- Added unit tests for errata + original
This commit is contained in:
Christian 'ketura' McCarty
2022-08-19 19:28:26 -05:00
parent ee2839d7ec
commit c5a84e0338
18 changed files with 613 additions and 148 deletions

View File

@@ -0,0 +1,93 @@
{
15_112: {
cardInfo: {
//Either a full URL, or a subpath for the PC image server
imagePath: decipher/LOTR15112.jpg
//If this is true, then all gameplay-related info outside this cardInfo definition
// will be ignored and the java class loaded instead.
javaClass: false
//Versioning differentiates releases within a particular alt path, such as PC errata
// version 1 vs version 2. PC version 2 will not conflict with, say, Decipher version 2.
//Top-level cards should always be version 0.
version: 0
collInfo: 15R112
rarity: R
setNum: "15"
cardNum: 112
// Standard, Masterwork, Tengwar, FullArt, etc. Top-level cards are always Standard.
style: Standard
}
title: Mountain-troll
unique: false
side: Shadow
culture: Orc
twilight: 10
type: Minion
race: Troll
strength: 22
vitality: 6
site: 5
effects: [
{
type: discount
memorize: DiscountPaid
max: {
type: requires
requires: {
type: CanSpot
filter: culture(orc),minion
count: 5
}
true: 10
false: 0
}
discount: {
type: ifDiscardFromPlay
filter: culture(orc),minion
count: 5
}
}
{
type: modifier
modifier: {
type: addkeyword
keyword: fierce
filter: self
requires: {
#This is only set if the discount was paid for.
type: HasInZoneData
filter: self
}
}
}
{
type: activated
phase: shadow
cost: {
type: removeTwilight
amount: 3
}
effect: [
{
type: playCardFromDiscard
removedTwilight: 2
filter: choose(culture(orc), orc)
memorize: PlayedOrc
cost: -2
}
]
}
]
gametext: When you play this minion, you may discard 5 [orc] minions from play to make it strength +10 and <b>fierce</b> until the regroup phase. <br>Shadow: Remove (2) to play an [orc] Orc from your discard pile. It comes into play exhausted.
lore: “Suddenly on the last stroke the Gate of Gondor broke.”
promotext: ""
alts: {
//These are just CardInfo objects
promos: {
}
//These are full card definitions, with redundant info that is the same as the original card ommitted
errata: {
}
}
}
}

View File

@@ -26,33 +26,61 @@
}
title: Mountain-troll
unique: false
side: Shadow
culture: Orc
twilight: 8
type: Minion
requires: {
type: canSpot
filter: orc
}
race: Troll
strength: 13
vitality: 4
site: 5
effects: [
{
type: modifier,
modifier: {
type: addKeyword
filter: dwarf
keyword: damage+1
}
},
{
type: trigger
optional: true,
trigger: {
type: winsSkirmish
filter: dwarf
type: played
filter: self
},
effect: {
type: wound
filter: choose(orc)
cost: {
type: discard
count: 5
filter: choose(culture(orc), minion, another)
}
effect: [
{
type: addKeyword
keyword: fierce
filter: self
until: endofturn
}
{
type: modifystrength
amount: 10
filter: self
until: endofturn
}
]
}
{
type: activated
phase: shadow
cost: {
type: removeTwilight
amount: 2
}
effect: [
{
type: playCardFromDiscard
removedTwilight: 2
filter: choose(culture(orc), orc)
memorize: PlayedOrc
}
{
type: exhaust
filter: memory(PlayedOrc)
}
]
}
]
gametext: When you play this minion, you may discard 5 [orc] minions from play to make it strength +10 and <b>fierce</b> until the regroup phase. <br>Shadow: Remove (2) to play an [orc] Orc from your discard pile. It comes into play exhausted.

View File

@@ -135,12 +135,12 @@
]
effects: {
type: discount
amount: {
max: {
type: forEachInHand
filter: culture(moria),goblin
}
discount: {
type: discardFromHand
type: perDiscardFromHand
filter: culture(moria),goblin
}
}

View File

@@ -1,75 +0,0 @@
package com.gempukku.lotro.cards.set15.orc;
import com.gempukku.lotro.common.*;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.actions.ActivateCardAction;
import com.gempukku.lotro.logic.actions.CostToEffectAction;
import com.gempukku.lotro.logic.cardtype.AbstractMinion;
import com.gempukku.lotro.logic.effects.AddUntilEndOfTurnModifierEffect;
import com.gempukku.lotro.logic.effects.RemoveTwilightEffect;
import com.gempukku.lotro.logic.effects.choose.ChooseAndPlayCardFromDiscardEffect;
import com.gempukku.lotro.logic.effects.discount.OptionalDiscardDiscountEffect;
import com.gempukku.lotro.logic.modifiers.KeywordModifier;
import com.gempukku.lotro.logic.timing.PlayConditions;
import com.gempukku.lotro.logic.timing.UnrespondableEffect;
import java.util.Collections;
import java.util.List;
/**
* Set: The Hunters
* Side: Shadow
* Culture: Orc
* Twilight Cost: 10
* Type: Minion • Troll
* Strength: 22
* Vitality: 6
* Site: 5
* Game Text: When you play this minion, you may discard 5 [ORC] minions from play to make it twilight cost -10
* and fierce. Shadow: Remove (3) to play an [ORC] Orc from your discard pile. Its twilight cost is -2.
*/
public class Card15_112 extends AbstractMinion {
public Card15_112() {
super(10, 22, 6, 5, Race.TROLL, Culture.ORC, "Mountain-troll");
}
@Override
public int getPotentialDiscount(LotroGame game, String playerId, PhysicalCard self) {
if (PlayConditions.canDiscardFromPlay(self, game, 5, Culture.ORC, CardType.MINION))
return 10;
return 0;
}
@Override
public void appendPotentialDiscountEffects(LotroGame game, final CostToEffectAction action, String playerId, final PhysicalCard self) {
action.appendPotentialDiscount(new OptionalDiscardDiscountEffect(action, 10, playerId, 5, Culture.ORC, CardType.MINION) {
@Override
protected void discountPaidCallback() {
action.appendEffect(
new UnrespondableEffect() {
@Override
protected void doPlayEffect(LotroGame game) {
action.appendEffect(
new AddUntilEndOfTurnModifierEffect(
new KeywordModifier(self, self, Keyword.FIERCE)));
}
});
}
});
}
@Override
public List<? extends ActivateCardAction> getPhaseActionsInPlay(String playerId, LotroGame game, PhysicalCard self) {
if (PlayConditions.canUseShadowCardDuringPhase(game, Phase.SHADOW, self, 3)
&& PlayConditions.canPlayFromDiscard(playerId, game, 3, -2, Culture.ORC, Race.ORC)) {
ActivateCardAction action = new ActivateCardAction(self);
action.appendCost(
new RemoveTwilightEffect(3));
action.appendEffect(
new ChooseAndPlayCardFromDiscardEffect(playerId, game, -2, Culture.ORC, Race.ORC));
return Collections.singletonList(action);
}
return null;
}
}

View File

@@ -7,38 +7,160 @@ import com.gempukku.lotro.cards.build.field.effect.appender.resolver.ValueResolv
import com.gempukku.lotro.common.Filterable;
import com.gempukku.lotro.logic.actions.CostToEffectAction;
import com.gempukku.lotro.logic.effects.DiscountEffect;
import com.gempukku.lotro.logic.effects.discount.DiscardCardFromHandDiscountEffect;
import com.gempukku.lotro.logic.effects.discount.*;
import org.json.simple.JSONObject;
public class PotentialDiscount implements EffectProcessor {
@Override
public void processEffect(JSONObject value, BuiltLotroCardBlueprint blueprint, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
FieldUtils.validateAllowedFields(value, "amount", "discount");
FieldUtils.validateAllowedFields(value, "max", "discount", "memorize");
final ValueSource amountSource = ValueResolver.resolveEvaluator(value.get("amount"), environment);
final ValueSource maxSource = ValueResolver.resolveEvaluator(value.get("max"), 1000, environment);
final JSONObject discount = (JSONObject) value.get("discount");
final String memory = FieldUtils.getString(value.get("memorize"), "memorize", "_temp");
final String discountType = FieldUtils.getString(discount.get("type"), "type");
if (discountType.equals("discardFromHand")) {
if (discountType.equalsIgnoreCase("perDiscardFromHand")) {
FieldUtils.validateAllowedFields(discount, "filter");
final String filter = FieldUtils.getString(discount.get("filter"), "filter", "any");
final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter, environment);
blueprint.appendDiscountSource(
new DiscountSource() {
@Override
public int getPotentialDiscount(ActionContext actionContext) {
return amountSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), actionContext.getSource());
}
new DiscountSource() {
@Override
public int getPotentialDiscount(ActionContext actionContext) {
return maxSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), actionContext.getSource());
}
@Override
public DiscountEffect getDiscountEffect(CostToEffectAction action, ActionContext actionContext) {
final Filterable filterable = filterableSource.getFilterable(actionContext);
return new DiscardCardFromHandDiscountEffect(action, actionContext.getPerformingPlayer(), filterable);
}
});
} else {
@Override
public DiscountEffect getDiscountEffect(CostToEffectAction action, ActionContext actionContext) {
final Filterable filterable = filterableSource.getFilterable(actionContext);
return new DiscardCardFromHandDiscountEffect(action, actionContext.getPerformingPlayer(), filterable) {
@Override
protected void discountPaidCallback(int paid) {
actionContext.setValueToMemory(memory, String.valueOf(paid));
actionContext.getSource().setWhileInZoneData(paid);
}
};
}
});
}
else if (discountType.equalsIgnoreCase("perExert")) {
FieldUtils.validateAllowedFields(discount, "multiplier", "filter");
final ValueSource multiplierSource = ValueResolver.resolveEvaluator(discount.get("multiplier"), environment);
final String filter = FieldUtils.getString(discount.get("filter"), "filter", "any");
final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter, environment);
blueprint.appendDiscountSource(
new DiscountSource() {
@Override
public int getPotentialDiscount(ActionContext actionContext) {
return maxSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), actionContext.getSource());
}
@Override
public DiscountEffect getDiscountEffect(CostToEffectAction action, ActionContext actionContext) {
final Filterable filterable = filterableSource.getFilterable(actionContext);
final int multiplier = multiplierSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), null);
return new ExertCharactersDiscountEffect(action, actionContext.getSource(),
actionContext.getPerformingPlayer(), multiplier, filterable) {
@Override
protected void discountPaidCallback(int paid) {
actionContext.setValueToMemory(memory, String.valueOf(paid));
actionContext.getSource().setWhileInZoneData(paid);
}
};
}
});
}
else if (discountType.equalsIgnoreCase("perThreatRemoved")) {
//FieldUtils.validateAllowedFields(discount, "multiplier");
blueprint.appendDiscountSource(
new DiscountSource() {
@Override
public int getPotentialDiscount(ActionContext actionContext) {
return maxSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), actionContext.getSource());
}
@Override
public DiscountEffect getDiscountEffect(CostToEffectAction action, ActionContext actionContext) {
//final int multiplier = multiplierSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), null);
return new RemoveThreatsToDiscountEffect(action) {
@Override
protected void discountPaidCallback(int paid) {
actionContext.setValueToMemory(memory, String.valueOf(paid));
actionContext.getSource().setWhileInZoneData(paid);
}
};
}
});
}
else if (discountType.equalsIgnoreCase("ifDiscardFromPlay")) {
FieldUtils.validateAllowedFields(discount, "count", "filter");
final ValueSource discardCountSource = ValueResolver.resolveEvaluator(discount.get("count"), environment);
final String filter = FieldUtils.getString(discount.get("filter"), "filter", "any");
final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter, environment);
blueprint.appendDiscountSource(
new DiscountSource() {
@Override
public int getPotentialDiscount(ActionContext actionContext) {
return maxSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), actionContext.getSource());
}
@Override
public DiscountEffect getDiscountEffect(CostToEffectAction action, ActionContext actionContext) {
final Filterable filterable = filterableSource.getFilterable(actionContext);
final int max = maxSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), actionContext.getSource());
final int discardCount = discardCountSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), actionContext.getSource());
actionContext.setValueToMemory(memory, "No");
return new OptionalDiscardDiscountEffect(action, max, actionContext.getPerformingPlayer(), discardCount, filterable) {
@Override
protected void discountPaidCallback(int paid) {
actionContext.setValueToMemory(memory, "Yes");
actionContext.getSource().setWhileInZoneData(memory);
}
};
}
});
}
else if (discountType.equalsIgnoreCase("ifRemoveFromDiscard")) {
FieldUtils.validateAllowedFields(discount, "count", "filter");
final ValueSource removeCountSource = ValueResolver.resolveEvaluator(value.get("count"), environment);
final String filter = FieldUtils.getString(discount.get("filter"), "filter", "any");
final FilterableSource filterableSource = environment.getFilterFactory().generateFilter(filter, environment);
blueprint.appendDiscountSource(
new DiscountSource() {
@Override
public int getPotentialDiscount(ActionContext actionContext) {
return maxSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), actionContext.getSource());
}
@Override
public DiscountEffect getDiscountEffect(CostToEffectAction action, ActionContext actionContext) {
final Filterable filterable = filterableSource.getFilterable(actionContext);
final int max = maxSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), actionContext.getSource());
final int removeCount = removeCountSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), actionContext.getSource());
actionContext.setValueToMemory(memory, "No");
return new RemoveCardsFromDiscardDiscountEffect(actionContext.getSource(), actionContext.getPerformingPlayer(), removeCount, max, filterable) {
@Override
protected void discountPaidCallback(int paid) {
actionContext.setValueToMemory(memory, "Yes");
actionContext.getSource().setWhileInZoneData(memory);
}
};
}
});
}
else {
throw new InvalidCardDefinitionException("Unknown type of discount: " + discountType);
}
}

View File

@@ -16,7 +16,7 @@ public class MemoryIs implements RequirementProducer {
return (actionContext) -> {
String valueFromMemory = actionContext.getValueFromMemory(memory);
return valueFromMemory != null && valueFromMemory.equals(value);
return valueFromMemory != null && valueFromMemory.equalsIgnoreCase(value);
};
}
}

View File

@@ -455,7 +455,10 @@ public class GameState {
removeFromSkirmish(card, false);
removeAllTokens(card);
card.setWhileInZoneData(null);
//If this is reset, then there is no way for self-discounting effects (which are evaluated while in the void)
// to have any sort of permanent effect once the card is in play.
if(zone != Zone.VOID_FROM_HAND && zone != Zone.VOID)
card.setWhileInZoneData(null);
}
for (GameStateListener listener : getAllGameStateListeners())

View File

@@ -61,7 +61,7 @@ public abstract class ChooseActiveCardsEffect extends AbstractEffect {
@Override
public String getText(LotroGame game) {
return null;
return _choiceText;
}
@Override

View File

@@ -6,7 +6,7 @@ import com.gempukku.lotro.logic.timing.Effect;
public interface DiscountEffect extends Effect {
int getMaximumPossibleDiscount(LotroGame game);
public void setMinimalRequiredDiscount(int minimalDiscount);
void setMinimalRequiredDiscount(int minimalDiscount);
public int getDiscountPaidFor();
int getDiscountPaidFor();
}

View File

@@ -35,9 +35,12 @@ public class OptionalEffect extends AbstractSubActionEffect {
@Override
public void playEffect(final LotroGame game) {
if (_optionalEffect.isPlayableInFull(game))
if (_optionalEffect.isPlayableInFull(game)) {
String text = _optionalEffect.getText(game);
if(text != null)
text = text.toLowerCase();
game.getUserFeedback().sendAwaitingDecision(_playerId,
new MultipleChoiceAwaitingDecision(1, "Do you wish to " + _optionalEffect.getText(game) + "?", new String[]{"Yes", "No"}) {
new MultipleChoiceAwaitingDecision(1, "Do you wish to " + text + "?", new String[]{"Yes", "No"}) {
@Override
protected void validDecisionMade(int index, String result) {
if (index == 0) {
@@ -47,5 +50,6 @@ public class OptionalEffect extends AbstractSubActionEffect {
}
}
});
}
}
}

View File

@@ -64,9 +64,12 @@ public class DiscardCardFromHandDiscountEffect extends AbstractSubActionEffect i
@Override
protected void cardsBeingDiscardedCallback(Collection<PhysicalCard> cardsBeingDiscarded) {
_discardedCount = cardsBeingDiscarded.size();
discountPaidCallback(_discardedCount);
}
});
processSubAction(game, subAction);
}
}
protected void discountPaidCallback(int paid) { }
}

View File

@@ -10,6 +10,8 @@ import com.gempukku.lotro.logic.effects.choose.ChooseAndExertCharactersEffect;
import com.gempukku.lotro.logic.timing.AbstractSubActionEffect;
import com.gempukku.lotro.logic.timing.Action;
import java.util.Collection;
public class ExertCharactersDiscountEffect extends AbstractSubActionEffect implements DiscountEffect {
private final Action _action;
private final PhysicalCard _payingFor;
@@ -30,7 +32,7 @@ public class ExertCharactersDiscountEffect extends AbstractSubActionEffect imple
@Override
public String getText(LotroGame game) {
return null;
return "Exert characters to reduce twilight cost";
}
@Override
@@ -70,6 +72,11 @@ public class ExertCharactersDiscountEffect extends AbstractSubActionEffect imple
protected void forEachCardExertedCallback(PhysicalCard character) {
_exertedCount++;
}
@Override
protected void cardsToBeExertedCallback(Collection<PhysicalCard> characters) {
discountPaidCallback(_exertedCount);
}
});
processSubAction(game, subAction);
}
@@ -79,4 +86,6 @@ public class ExertCharactersDiscountEffect extends AbstractSubActionEffect imple
public int getDiscountPaidFor() {
return _exertedCount * _multiplier;
}
protected void discountPaidCallback(int paid) { }
}

View File

@@ -42,7 +42,7 @@ public class OptionalDiscardDiscountEffect extends AbstractSubActionEffect imple
@Override
public String getText(LotroGame game) {
return null;
return "Discard cards to get a twilight discount";
}
@Override
@@ -74,7 +74,7 @@ public class OptionalDiscardDiscountEffect extends AbstractSubActionEffect imple
protected void cardsToBeDiscardedCallback(Collection<PhysicalCard> cards) {
if (cards.size() == _discardCount) {
_paid = true;
discountPaidCallback();
discountPaidCallback(_discardCount);
}
}
}));
@@ -82,6 +82,5 @@ public class OptionalDiscardDiscountEffect extends AbstractSubActionEffect imple
}
}
protected void discountPaidCallback() {
}
protected void discountPaidCallback(int paid) { }
}

View File

@@ -104,10 +104,13 @@ public class RemoveCardsFromDiscardDiscountEffect implements DiscountEffect {
game.getGameState().addCardToZone(game, removedCard, Zone.REMOVED);
game.getGameState().sendMessage(_playerId + " removed " + GameUtils.getAppendedNames(removedCards) + " from discard using " + GameUtils.getCardLink(_source));
discountPaidCallback(removedCards.size());
}
@Override
public boolean wasCarriedOut() {
return !_required || _paid;
}
protected void discountPaidCallback(int paid) { }
}

View File

@@ -60,6 +60,7 @@ public class RemoveThreatsToDiscountEffect extends AbstractSubActionEffect imple
new RemoveThreatsEffect(_action.getActionSource(), threats));
processSubAction(game, subAction);
_threatsRemoved = threats;
discountPaidCallback(threats);
}
}
}
@@ -78,4 +79,6 @@ public class RemoveThreatsToDiscountEffect extends AbstractSubActionEffect imple
public int getDiscountPaidFor() {
return _threatsRemoved;
}
protected void discountPaidCallback(int paid) { }
}

View File

@@ -804,7 +804,7 @@ public class GenericCardTestHelper extends AbstractAtTest {
public void ShadowAcceptOptionalTrigger() throws DecisionResultInvalidException { playerDecided(P2, "0"); }
public void ShadowDeclineOptionalTrigger() throws DecisionResultInvalidException { playerDecided(P2, ""); }
public void ShadowDeclineReconciliation() throws DecisionResultInvalidException { playerDecided(P2, ""); }
public void ShadowDeclineReconciliation() throws DecisionResultInvalidException { ShadowPassCurrentPhaseAction(); }
public void FreepsChooseYes() throws DecisionResultInvalidException { ChooseMultipleChoiceOption(P1, "Yes"); }
public void ShadowChooseYes() throws DecisionResultInvalidException { ChooseMultipleChoiceOption(P2, "Yes"); }

View File

@@ -0,0 +1,175 @@
package com.gempukku.lotro.cards.official.set15;
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;
import java.util.HashMap;
import static org.junit.Assert.*;
public class Card_15_112_Tests
{
protected GenericCardTestHelper GetScenario() throws CardNotFoundException, DecisionResultInvalidException {
return new GenericCardTestHelper(
new HashMap<String, String>()
{{
put("troll", "15_112");
put("orc1", "13_118");
put("orc2", "13_118");
put("orc3", "13_118");
put("orc4", "13_118");
put("orc5", "13_118");
}},
GenericCardTestHelper.FellowshipSites,
GenericCardTestHelper.FOTRFrodo,
GenericCardTestHelper.FOTRRing
);
}
@Test
public void MountaintrollStatsAndKeywordsAreCorrect() throws DecisionResultInvalidException, CardNotFoundException {
/**
* Set: 15
* Title: Mountain-troll
* Side: Free Peoples
* Culture: Orc
* Twilight Cost: 10
* Type: minion
* Subtype: Troll
* Strength: 13
* Vitality: 4
* Site Number: 5
* Game Text: When you play this minion, you may discard 5 [orc] minions from play to make it twilight cost -10 and
* <b>fierce</b>.
* Shadow: Remove (2) to play an [orc] Orc from your discard pile. It comes into play exhausted.
*/
//Pre-game setup
GenericCardTestHelper scn = GetScenario();
PhysicalCardImpl troll = scn.GetFreepsCard("troll");
assertFalse(troll.getBlueprint().isUnique());
assertEquals(Side.SHADOW, troll.getBlueprint().getSide());
assertEquals(Culture.ORC, troll.getBlueprint().getCulture());
assertEquals(CardType.MINION, troll.getBlueprint().getCardType());
assertEquals(Race.TROLL, troll.getBlueprint().getRace());
assertEquals(10, troll.getBlueprint().getTwilightCost());
assertEquals(22, troll.getBlueprint().getStrength());
assertEquals(6, troll.getBlueprint().getVitality());
//assertEquals(, troll.getBlueprint().getResistance());
//assertEquals(Signet., troll.getBlueprint().getSignet());
assertEquals(5, troll.getBlueprint().getSiteNumber()); // Change this to getAllyHomeSiteNumbers for allies
}
@Test
public void OnPlayTrollDoesNothingIfLessThan5OrcMinionsInPlay() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
GenericCardTestHelper scn = GetScenario();
var troll = scn.GetShadowCard("troll");
var orc1 = scn.GetShadowCard("orc1");
var orc2 = scn.GetShadowCard("orc2");
var orc3 = scn.GetShadowCard("orc3");
var orc4 = scn.GetShadowCard("orc4");
var orc5 = scn.GetShadowCard("orc5");
scn.ShadowMoveCardToHand(troll);
scn.ShadowMoveCharToTable(orc1, orc2, orc3, orc4);
scn.StartGame();
scn.SetTwilight(20);
scn.FreepsPassCurrentPhaseAction();
scn.ShadowPlayCard(troll);
assertFalse(scn.ShadowHasOptionalTriggerAvailable());
}
@Test
public void OnPlayTrollOptionallDiscards5OrcMinionsForMinus10TwilightAndFierceUntilEndOfTurn() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
GenericCardTestHelper scn = GetScenario();
var troll = scn.GetShadowCard("troll");
var orc1 = scn.GetShadowCard("orc1");
var orc2 = scn.GetShadowCard("orc2");
var orc3 = scn.GetShadowCard("orc3");
var orc4 = scn.GetShadowCard("orc4");
var orc5 = scn.GetShadowCard("orc5");
scn.ShadowMoveCardToHand(troll);
scn.ShadowMoveCharToTable(orc1, orc2, orc3, orc4, orc5);
scn.StartGame();
scn.SetTwilight(17);
scn.FreepsPassCurrentPhaseAction();
assertEquals(20, scn.GetTwilight());
scn.ShadowPlayCard(troll);
assertTrue(scn.ShadowDecisionAvailable("choose cards to discard"));
assertFalse(scn.HasKeyword(troll, Keyword.FIERCE));
scn.ShadowChooseYes();
assertTrue(scn.HasKeyword(troll, Keyword.FIERCE));
assertEquals(Zone.DISCARD, orc1.getZone());
assertEquals(Zone.DISCARD, orc2.getZone());
assertEquals(Zone.DISCARD, orc3.getZone());
assertEquals(Zone.DISCARD, orc4.getZone());
assertEquals(Zone.DISCARD, orc5.getZone());
assertEquals(18, scn.GetTwilight()); //20 initial -2 for roaming, troll was otherwise free
scn.SkipToPhase(Phase.ASSIGNMENT);
assertTrue(scn.HasKeyword(troll, Keyword.FIERCE));
scn.SkipToPhase(Phase.REGROUP);
assertTrue(scn.HasKeyword(troll, Keyword.FIERCE));
scn.PassCurrentPhaseActions();
scn.ShadowDeclineReconciliation();
scn.FreepsChooseToMove();
assertTrue(scn.HasKeyword(troll, Keyword.FIERCE));
}
@Test
public void ShadowAbilityRemoves3AndPlaysDiscountedOrcFromDiscard() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
GenericCardTestHelper scn = GetScenario();
var troll = scn.GetShadowCard("troll");
var orc1 = scn.GetShadowCard("orc1");
var orc2 = scn.GetShadowCard("orc2");
var orc3 = scn.GetShadowCard("orc3");
var orc4 = scn.GetShadowCard("orc4");
var orc5 = scn.GetShadowCard("orc5");
scn.ShadowMoveCharToTable(troll);
scn.ShadowMoveCardToDiscard(orc1, orc2, orc3, orc4, orc5);
scn.StartGame();
scn.SetTwilight(17);
scn.FreepsPassCurrentPhaseAction();
assertEquals(20, scn.GetTwilight());
assertTrue(scn.ShadowActionAvailable(troll));
assertEquals(Zone.DISCARD, orc1.getZone());
scn.ShadowUseCardAction(troll);
assertEquals(5, scn.GetShadowCardChoiceCount());
scn.ShadowChooseCardBPFromSelection(orc1);
// -3 for ability, -3 for orc, +2 for discount, -2 for roaming
assertEquals(14, scn.GetTwilight());
assertEquals(Zone.SHADOW_CHARACTERS, orc1.getZone());
assertEquals(0, scn.GetWoundsOn(orc1));
}
}

View File

@@ -6,6 +6,7 @@ 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.MoveLimitModifier;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
@@ -21,8 +22,12 @@ public class Card_15_112_ErrataTests
return new GenericCardTestHelper(
new HashMap<String, String>()
{{
put("card", "85_112");
// put other cards in here as needed for the test case
put("troll", "85_112");
put("orc1", "13_118");
put("orc2", "13_118");
put("orc3", "13_118");
put("orc4", "13_118");
put("orc5", "13_118");
}},
GenericCardTestHelper.FellowshipSites,
GenericCardTestHelper.FOTRFrodo,
@@ -30,9 +35,7 @@ public class Card_15_112_ErrataTests
);
}
// Uncomment both @Test markers below once this is ready to be used
//@Test
@Test
public void MountaintrollStatsAndKeywordsAreCorrect() throws DecisionResultInvalidException, CardNotFoundException {
/**
@@ -46,40 +49,135 @@ public class Card_15_112_ErrataTests
* Strength: 13
* Vitality: 4
* Site Number: 5
* Game Text: When you play this minion, you may discard 5 [orc] minions from play to make it strength +10 and <b>fierce</b> until the regroup phase. <br>Shadow: Remove (2) to play an [orc] Orc from your discard pile. It comes into play exhausted.
* Game Text: When you play this minion, you may discard 5 [orc] minions from play to make it strength +10 and
* <b>fierce</b> until the end of the turn.
* Shadow: Remove (2) to play an [orc] Orc from your discard pile. It comes into play exhausted.
*/
//Pre-game setup
GenericCardTestHelper scn = GetScenario();
PhysicalCardImpl card = scn.GetFreepsCard("card");
PhysicalCardImpl troll = scn.GetFreepsCard("troll");
assertFalse(card.getBlueprint().isUnique());
assertEquals(Side.SHADOW, card.getBlueprint().getSide());
assertEquals(Culture.ORC, card.getBlueprint().getCulture());
assertEquals(CardType.MINION, card.getBlueprint().getCardType());
assertEquals(Race.CREATURE, card.getBlueprint().getRace());
assertTrue(scn.HasKeyword(card, Keyword.SUPPORT_AREA));
assertEquals(8, card.getBlueprint().getTwilightCost());
assertEquals(13, card.getBlueprint().getStrength());
assertEquals(4, card.getBlueprint().getVitality());
//assertEquals(, card.getBlueprint().getResistance());
//assertEquals(Signet., card.getBlueprint().getSignet());
assertEquals(5, card.getBlueprint().getSiteNumber()); // Change this to getAllyHomeSiteNumbers for allies
assertFalse(troll.getBlueprint().isUnique());
assertEquals(Side.SHADOW, troll.getBlueprint().getSide());
assertEquals(Culture.ORC, troll.getBlueprint().getCulture());
assertEquals(CardType.MINION, troll.getBlueprint().getCardType());
assertEquals(Race.TROLL, troll.getBlueprint().getRace());
assertEquals(8, troll.getBlueprint().getTwilightCost());
assertEquals(13, troll.getBlueprint().getStrength());
assertEquals(4, troll.getBlueprint().getVitality());
//assertEquals(, troll.getBlueprint().getResistance());
//assertEquals(Signet., troll.getBlueprint().getSignet());
assertEquals(5, troll.getBlueprint().getSiteNumber()); // Change this to getAllyHomeSiteNumbers for allies
}
//@Test
public void MountaintrollTest1() throws DecisionResultInvalidException, CardNotFoundException {
@Test
public void OnPlayTrollDoesNothingIfLessThan5OrcMinionsInPlay() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
GenericCardTestHelper scn = GetScenario();
PhysicalCardImpl card = scn.GetFreepsCard("card");
scn.FreepsMoveCardToHand(card);
var troll = scn.GetShadowCard("troll");
var orc1 = scn.GetShadowCard("orc1");
var orc2 = scn.GetShadowCard("orc2");
var orc3 = scn.GetShadowCard("orc3");
var orc4 = scn.GetShadowCard("orc4");
var orc5 = scn.GetShadowCard("orc5");
scn.ShadowMoveCardToHand(troll);
scn.ShadowMoveCharToTable(orc1, orc2, orc3, orc4);
scn.StartGame();
scn.FreepsPlayCard(card);
assertEquals(8, scn.GetTwilight());
scn.SetTwilight(20);
scn.FreepsPassCurrentPhaseAction();
scn.ShadowPlayCard(troll);
assertFalse(scn.ShadowHasOptionalTriggerAvailable());
}
@Test
public void OnPlayTrollOptionallDiscards5OrcMinionsForFierceAndStrengthPluse10UntilEndOfTurn() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
GenericCardTestHelper scn = GetScenario();
var troll = scn.GetShadowCard("troll");
var orc1 = scn.GetShadowCard("orc1");
var orc2 = scn.GetShadowCard("orc2");
var orc3 = scn.GetShadowCard("orc3");
var orc4 = scn.GetShadowCard("orc4");
var orc5 = scn.GetShadowCard("orc5");
scn.ShadowMoveCardToHand(troll);
scn.ShadowMoveCharToTable(orc1, orc2, orc3, orc4, orc5);
scn.StartGame();
scn.SetTwilight(17);
scn.FreepsPassCurrentPhaseAction();
assertEquals(20, scn.GetTwilight());
scn.ShadowPlayCard(troll);
assertTrue(scn.ShadowHasOptionalTriggerAvailable());
assertEquals(13, scn.GetStrength(troll));
assertFalse(scn.HasKeyword(troll, Keyword.FIERCE));
scn.ShadowAcceptOptionalTrigger();
assertEquals(23, scn.GetStrength(troll));
assertTrue(scn.HasKeyword(troll, Keyword.FIERCE));
assertEquals(Zone.DISCARD, orc1.getZone());
assertEquals(Zone.DISCARD, orc2.getZone());
assertEquals(Zone.DISCARD, orc3.getZone());
assertEquals(Zone.DISCARD, orc4.getZone());
assertEquals(Zone.DISCARD, orc5.getZone());
assertEquals(10, scn.GetTwilight()); //20 initial -8 for troll -2 for roaming
scn.SkipToPhase(Phase.ASSIGNMENT);
assertEquals(23, scn.GetStrength(troll));
assertTrue(scn.HasKeyword(troll, Keyword.FIERCE));
scn.SkipToPhase(Phase.REGROUP);
assertEquals(23, scn.GetStrength(troll));
assertTrue(scn.HasKeyword(troll, Keyword.FIERCE));
scn.PassCurrentPhaseActions();
scn.ShadowDeclineReconciliation();
scn.FreepsChooseToMove();
assertEquals(23, scn.GetStrength(troll));
assertTrue(scn.HasKeyword(troll, Keyword.FIERCE));
}
@Test
public void ShadowAbilityRemoves2AndPlaysExhaustedOrcFromDiscard() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
GenericCardTestHelper scn = GetScenario();
var troll = scn.GetShadowCard("troll");
var orc1 = scn.GetShadowCard("orc1");
var orc2 = scn.GetShadowCard("orc2");
var orc3 = scn.GetShadowCard("orc3");
var orc4 = scn.GetShadowCard("orc4");
var orc5 = scn.GetShadowCard("orc5");
scn.ShadowMoveCharToTable(troll);
scn.ShadowMoveCardToDiscard(orc1, orc2, orc3, orc4, orc5);
scn.StartGame();
scn.SetTwilight(17);
scn.FreepsPassCurrentPhaseAction();
assertEquals(20, scn.GetTwilight());
assertTrue(scn.ShadowActionAvailable(troll));
assertEquals(Zone.DISCARD, orc1.getZone());
scn.ShadowUseCardAction(troll);
assertEquals(5, scn.GetShadowCardChoiceCount());
scn.ShadowChooseCardBPFromSelection(orc1);
assertEquals(13, scn.GetTwilight()); // -2 for ability, -3 for orc, -2 for roaming
assertEquals(Zone.SHADOW_CHARACTERS, orc1.getZone());
assertEquals(1, scn.GetWoundsOn(orc1));
}
}