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:
@@ -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: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -135,12 +135,12 @@
|
||||
]
|
||||
effects: {
|
||||
type: discount
|
||||
amount: {
|
||||
max: {
|
||||
type: forEachInHand
|
||||
filter: culture(moria),goblin
|
||||
}
|
||||
discount: {
|
||||
type: discardFromHand
|
||||
type: perDiscardFromHand
|
||||
filter: culture(moria),goblin
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -61,7 +61,7 @@ public abstract class ChooseActiveCardsEffect extends AbstractEffect {
|
||||
|
||||
@Override
|
||||
public String getText(LotroGame game) {
|
||||
return null;
|
||||
return _choiceText;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) { }
|
||||
}
|
||||
|
||||
@@ -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) { }
|
||||
}
|
||||
|
||||
@@ -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) { }
|
||||
}
|
||||
|
||||
@@ -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) { }
|
||||
}
|
||||
|
||||
@@ -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) { }
|
||||
}
|
||||
|
||||
@@ -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"); }
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user