diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 1fc642424d..5c960752f9 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -81,6 +81,7 @@ import java.lang.String; import java.util.*; import java.util.HashSet; import java.util.Map.Entry; +import mage.abilities.costs.VariableCost; import mage.cards.repository.ExpansionInfo; import mage.cards.repository.ExpansionRepository; @@ -1035,6 +1036,18 @@ public class ComputerPlayer> extends PlayerImpl i return numAvailable; } + @Override + public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variablCost) { + log.debug("announceXMana"); + //TODO: improve this + int value = new Random().nextInt(max+1); + if (value < max) { + max++; + } + return value; + } + + @Override public void abort() { abort = true; diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 570606c750..273d434d19 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -46,10 +46,10 @@ import mage.abilities.SpecialAction; import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbility; import mage.abilities.costs.Cost; +import mage.abilities.costs.VariableCost; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCost; -import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.PhyrexianManaCost; import mage.abilities.effects.RequirementEffect; @@ -534,6 +534,18 @@ public class HumanPlayer extends PlayerImpl { return xValue; } + @Override + public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost) { + int xValue = 0; + updateGameStatePriority("announceXCost", game); + game.fireGetAmountEvent(playerId, message, min, max); + waitForIntegerResponse(game); + if (response != null && response.getInteger() != null) { + xValue = response.getInteger().intValue(); + } + return xValue; + } + protected void playManaAbilities(ManaCost unpaid, Game game) { updateGameStatePriority("playManaAbilities", game); MageObject object = game.getObject(response.getUUID()); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/BurnAtTheStake.java b/Mage.Sets/src/mage/sets/avacynrestored/BurnAtTheStake.java index 836e151121..f4a7f7d00f 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/BurnAtTheStake.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/BurnAtTheStake.java @@ -28,24 +28,20 @@ package mage.sets.avacynrestored; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; import mage.abilities.Ability; -import mage.abilities.costs.CostImpl; -import mage.abilities.costs.VariableCost; +import mage.abilities.costs.common.TapVariableTargetCost; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; -import mage.filter.FilterMana; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreatureOrPlayer; /** @@ -54,6 +50,12 @@ import mage.target.common.TargetCreatureOrPlayer; */ public class BurnAtTheStake extends CardImpl { + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creatures you control"); + + static { + filter.add(Predicates.not(new TappedPredicate())); + } + public BurnAtTheStake(UUID ownerId) { super(ownerId, 130, "Burn at the Stake", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{2}{R}{R}{R}"); this.expansionSetCode = "AVR"; @@ -61,10 +63,10 @@ public class BurnAtTheStake extends CardImpl { this.color.setRed(true); // As an additional cost to cast Burn at the Stake, tap any number of untapped creatures you control. - this.getSpellAbility().addCost(new BurnAtTheStakeCost()); + this.getSpellAbility().addCost(new TapVariableTargetCost(filter, true, "any number of")); // Burn at the Stake deals damage to target creature or player equal to three times the number of creatures tapped this way. this.getSpellAbility().addEffect(new BurnAtTheStakeEffect()); - this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlayer(true)); } public BurnAtTheStake(final BurnAtTheStake card) { @@ -77,81 +79,6 @@ public class BurnAtTheStake extends CardImpl { } } -class BurnAtTheStakeCost extends CostImpl implements VariableCost { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creature you control"); - - static { - filter.add(Predicates.not(new TappedPredicate())); - } - private int amountPaid = 0; - - public BurnAtTheStakeCost() { - this.text = "As an additional cost to cast {this}, tap any number of untapped creatures you control"; - } - - public BurnAtTheStakeCost(final BurnAtTheStakeCost cost) { - super(cost); - this.amountPaid = cost.amountPaid; - } - - @Override - public BurnAtTheStakeCost copy() { - return new BurnAtTheStakeCost(this); - } - - @Override - public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - return true; - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - this.amountPaid = 0; - - Target target = new TargetControlledCreaturePermanent(1, 1, filter, false); - while (target.canChoose(controllerId, game) && target.choose(Outcome.Tap, controllerId, sourceId, game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - permanent.tap(game); - amountPaid++; - } - - target.clearChosen(); - } - - paid = true; - return true; - } - - @Override - public int getAmount() { - return this.amountPaid; - } - - /** - * Not Supported - * @param filter - */ - @Override - public void setFilter(FilterMana filter) { - } - - /** - * Not supported - * @return - */ - @Override - public FilterMana getFilter() { - return new FilterMana(); - } - - @Override - public void setAmount(int amount) { - this.amountPaid = amount; - } -} - class BurnAtTheStakeEffect extends OneShotEffect { public BurnAtTheStakeEffect() { diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/QuillmaneBaku.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/QuillmaneBaku.java index 2c3a0264e0..9f7b9c7a17 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/QuillmaneBaku.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/QuillmaneBaku.java @@ -29,10 +29,6 @@ package mage.sets.betrayersofkamigawa; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -43,8 +39,12 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.Card; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.Filter; import mage.filter.common.FilterCreaturePermanent; @@ -77,10 +77,27 @@ public class QuillmaneBaku extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new QuillmaneBakuReturnEffect(), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.KI.createInstance(1))); - ability.addTarget(new TargetCreaturePermanent()); + ability.addTarget(new TargetCreaturePermanent(true)); this.addAbility(ability); } + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability instanceof SimpleActivatedAbility) { + int maxConvManaCost = 0; + for (Cost cost : ability.getCosts()) { + if (cost instanceof RemoveVariableCountersSourceCost) { + maxConvManaCost = ((RemoveVariableCountersSourceCost)cost).getAmount(); + } + } + ability.getTargets().clear(); + FilterCreaturePermanent newFilter = new FilterCreaturePermanent("creature with converted mana cost " + maxConvManaCost + " or less"); + newFilter.add(new ConvertedManaCostPredicate(Filter.ComparisonType.LessThan, maxConvManaCost + 1)); + TargetCreaturePermanent target = new TargetCreaturePermanent(newFilter, true); + ability.getTargets().add(target); + } + } + public QuillmaneBaku(final QuillmaneBaku card) { super(card); } @@ -112,23 +129,12 @@ public class QuillmaneBaku extends CardImpl { if (player == null) { return false; } - - int maxConvManaCost = 0; - for (Cost cost : source.getCosts()) { - if (cost instanceof RemoveVariableCountersSourceCost) { - maxConvManaCost = ((RemoveVariableCountersSourceCost)cost).getAmount(); - } - } - - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with converted mana cost " + maxConvManaCost + " or less"); - filter.add(new ConvertedManaCostPredicate(Filter.ComparisonType.LessThan, maxConvManaCost + 1)); - TargetCreaturePermanent target = new TargetCreaturePermanent(filter); - - Permanent permanent = game.getPermanent(target.getFirstTarget()); + Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (permanent != null) { - return permanent.moveToZone(Zone.HAND, source.getId(), game, false); + player.moveCardToHandWithInfo((Card) permanent, source.getSourceId(), game, Zone.BATTLEFIELD); + return true; } return false; } } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/bornofthegods/ChampionOfStraySouls.java b/Mage.Sets/src/mage/sets/bornofthegods/ChampionOfStraySouls.java index 21e2513faa..0e235a486b 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/ChampionOfStraySouls.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/ChampionOfStraySouls.java @@ -27,33 +27,26 @@ */ package mage.sets.bornofthegods; -import java.util.List; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.CostImpl; -import mage.abilities.costs.VariableCost; +import mage.abilities.costs.common.SacrificeXTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.PutOnLibrarySourceEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.FilterMana; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreatureCard; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; -import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetCardInYourGraveyard; -import mage.target.common.TargetCreaturePermanent; /** * @@ -61,6 +54,12 @@ import mage.target.common.TargetCreaturePermanent; */ public class ChampionOfStraySouls extends CardImpl { + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("other creatures"); + + static { + filter.add(new AnotherPredicate()); + } + public ChampionOfStraySouls(UUID ownerId) { super(ownerId, 63, "Champion of Stray Souls", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); this.expansionSetCode = "BNG"; @@ -80,8 +79,8 @@ public class ChampionOfStraySouls extends CardImpl { effect.setText("Return X target creatures from your graveyard to the battlefield"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{3}{B}{B}")); ability.addCost(new TapSourceCost()); - ability.addCost(new ChampionOfStraySoulsSacrificeCost()); - ability.addTarget(new TargetCardInYourGraveyard(0,Integer.MAX_VALUE, new FilterCreatureCard())); + ability.addCost(new SacrificeXTargetCost(filter)); + ability.addTarget(new TargetCardInYourGraveyard(0,Integer.MAX_VALUE, new FilterCreatureCard("creature cards from your graveyard"))); this.addAbility(ability); // {5}{B}{B}: Put Champion of Stray Souls on top of your library from your graveyard. @@ -91,6 +90,19 @@ public class ChampionOfStraySouls extends CardImpl { } + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability instanceof SimpleActivatedAbility) { + for (Effect effect : ability.getEffects()) { + if (effect instanceof ReturnFromGraveyardToBattlefieldTargetEffect) { + int xValue = new GetXValue().calculate(game, ability); + ability.getTargets().clear(); + ability.addTarget(new TargetCardInYourGraveyard(xValue,xValue, new FilterCreatureCard("creature cards from your graveyard"))); + } + } + } + } + public ChampionOfStraySouls(final ChampionOfStraySouls card) { super(card); } @@ -100,74 +112,3 @@ public class ChampionOfStraySouls extends CardImpl { return new ChampionOfStraySouls(this); } } - -class ChampionOfStraySoulsSacrificeCost extends CostImpl implements VariableCost { - - protected int amountPaid = 0; - - public ChampionOfStraySoulsSacrificeCost() { - this.text = "Sacrifice X other creatures"; - } - - public ChampionOfStraySoulsSacrificeCost(final ChampionOfStraySoulsSacrificeCost cost) { - super(cost); - this.amountPaid = cost.amountPaid; - } - - @Override - public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - return true; - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - amountPaid = 0; - int creaturesToSacrifice = ability.getTargets().get(0).getTargets().size(); - this.text = new StringBuilder(creaturesToSacrifice).append(" other creatures you control").toString(); - - FilterCreaturePermanent filter = new FilterCreaturePermanent(this.text); - filter.add(new ControllerPredicate(TargetController.YOU)); - filter.add(new AnotherPredicate()); - TargetCreaturePermanent target = new TargetCreaturePermanent(creaturesToSacrifice, creaturesToSacrifice, filter, true); - - if (target.canChoose(controllerId, game) && target.choose(Outcome.Sacrifice, controllerId, sourceId, game)) { - for (UUID creatureId: (List) target.getTargets()) { - Permanent creature = game.getPermanent(creatureId); - if (creature != null) { - creature.sacrifice(sourceId, game); - amountPaid++; - } - - } - } else { - return false; - } - paid = true; - return true; - } - - @Override - public int getAmount() { - return amountPaid; - } - - @Override - public void setFilter(FilterMana filter) { - } - - @Override - public FilterMana getFilter() { - return new FilterMana(); - } - - @Override - public ChampionOfStraySoulsSacrificeCost copy() { - return new ChampionOfStraySoulsSacrificeCost(this); - } - - @Override - public void setAmount(int amount) { - amountPaid = amount; - } -} - diff --git a/Mage.Sets/src/mage/sets/commander2013/SpringjackPasture.java b/Mage.Sets/src/mage/sets/commander2013/SpringjackPasture.java index 06ec9be612..3d7062cfe4 100644 --- a/Mage.Sets/src/mage/sets/commander2013/SpringjackPasture.java +++ b/Mage.Sets/src/mage/sets/commander2013/SpringjackPasture.java @@ -1,4 +1,4 @@ -/* + /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,29 +33,25 @@ import mage.Mana; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.CostImpl; -import mage.abilities.costs.VariableCost; +import mage.abilities.costs.common.SacrificeXTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.choices.ChoiceColor; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.FilterMana; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.permanent.token.Token; import mage.players.Player; -import mage.target.common.TargetCreaturePermanent; /** * @@ -64,6 +60,12 @@ import mage.target.common.TargetCreaturePermanent; */ public class SpringjackPasture extends CardImpl { + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Goats"); + + static { + filter.add(new SubtypePredicate("Goat")); + } + public SpringjackPasture(UUID ownerId) { super(ownerId, 326, "Springjack Pasture", Rarity.RARE, new CardType[]{CardType.LAND}, ""); this.expansionSetCode = "C13"; @@ -77,10 +79,11 @@ public class SpringjackPasture extends CardImpl { this.addAbility(ability); // {tap}, Sacrifice X Goats: Add X mana of any one color to your mana pool. You gain X life. - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SpringjackPastureEffect(), new TapSourceCost()); - ability2.addChoice(new ChoiceColor()); - ability2.addCost(new SpringjackPastureCost()); - this.addAbility(ability2); + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SpringjackPastureEffect(), new TapSourceCost()); + ability.addChoice(new ChoiceColor()); + ability.addCost(new SacrificeXTargetCost(filter)); + ability.addEffect(new GainLifeEffect(new GetXValue())); + this.addAbility(ability); } @@ -98,7 +101,7 @@ class SpringjackPastureEffect extends OneShotEffect { public SpringjackPastureEffect() { super(Outcome.Benefit); - staticText = "Add X mana of any one color to your mana pool. You gain X life"; + staticText = "Add X mana of any one color to your mana pool"; } public SpringjackPastureEffect(final SpringjackPastureEffect effect) { @@ -110,22 +113,17 @@ class SpringjackPastureEffect extends OneShotEffect { Player you = game.getPlayer(source.getControllerId()); ChoiceColor choice = (ChoiceColor) source.getChoices().get(0); if (you != null && choice != null) { - int count = source.getManaCostsToPay().getX(); + int count = new GetXValue().calculate(game, source); if (choice.getColor().isBlack()) { you.getManaPool().addMana(new Mana(0, 0, 0, 0, count, 0, 0), game, source); - you.gainLife(count, game); } else if (choice.getColor().isBlue()) { you.getManaPool().addMana(new Mana(0, 0, count, 0, 0, 0, 0), game, source); - you.gainLife(count, game); } else if (choice.getColor().isRed()) { you.getManaPool().addMana(new Mana(count, 0, 0, 0, 0, 0, 0), game, source); - you.gainLife(count, game); } else if (choice.getColor().isGreen()) { you.getManaPool().addMana(new Mana(0, count, 0, 0, 0, 0, 0), game, source); - you.gainLife(count, game); } else if (choice.getColor().isWhite()) { you.getManaPool().addMana(new Mana(0, 0, 0, count, 0, 0, 0), game, source); - you.gainLife(count, game); } return true; @@ -139,80 +137,15 @@ class SpringjackPastureEffect extends OneShotEffect { } } -class SpringjackPastureCost extends CostImpl implements VariableCost { - - protected int amountPaid = 0; - - public SpringjackPastureCost() { - this.text = "sacrifice X Goats"; - } - - public SpringjackPastureCost(final SpringjackPastureCost cost) { - super(cost); - this.amountPaid = cost.amountPaid; - } - - @Override - public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - return true; - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - amountPaid = 0; - FilterCreaturePermanent filter = new FilterCreaturePermanent("X number of goats you control."); - filter.add(new SubtypePredicate("Goat")); - filter.add(new ControllerPredicate(TargetController.YOU)); - TargetCreaturePermanent target = new TargetCreaturePermanent(filter); - while (true) { - target.clearChosen(); - if (target.canChoose(controllerId, game) && target.choose(Outcome.Sacrifice, controllerId, sourceId, game)) { - UUID goat = target.getFirstTarget(); - if (goat != null) { - game.getPermanent(goat).sacrifice(sourceId, game); - amountPaid++; - } - } else { - break; - } - } - paid = true; - return true; - } - - @Override - public int getAmount() { - return amountPaid; - } - - @Override - public void setFilter(FilterMana filter) { - } - - @Override - public FilterMana getFilter() { - return new FilterMana(); - } - - @Override - public SpringjackPastureCost copy() { - return new SpringjackPastureCost(this); - } - - @Override - public void setAmount(int amount) { - amountPaid = amount; - } -} - class GoatToken extends Token { public GoatToken() { super("Goat", "0/1 white Goat creature token"); + setOriginalExpansionSetCode("EVE"); cardType.add(CardType.CREATURE); color = ObjectColor.WHITE; subtype.add("Goat"); power = new MageInt(0); toughness = new MageInt(1); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/commander2013/ToxicDeluge.java b/Mage.Sets/src/mage/sets/commander2013/ToxicDeluge.java index d14a85cad1..5097eaf516 100644 --- a/Mage.Sets/src/mage/sets/commander2013/ToxicDeluge.java +++ b/Mage.Sets/src/mage/sets/commander2013/ToxicDeluge.java @@ -51,7 +51,7 @@ public class ToxicDeluge extends CardImpl { this.color.setBlack(true); // As an additional cost to cast Toxic Deluge, pay X life. - this.getSpellAbility().addCost(new PayVariableLifeCost()); + this.getSpellAbility().addCost(new PayVariableLifeCost(true)); // All creatures get -X/-X until end of turn. DynamicValue xValue = new SignInversionDynamicValue(new GetXValue()); this.getSpellAbility().addEffect(new BoostAllEffect(xValue, xValue, Duration.EndOfTurn)); diff --git a/Mage.Sets/src/mage/sets/eventide/Quillspike.java b/Mage.Sets/src/mage/sets/eventide/Quillspike.java index 7708f3484d..363e86561e 100644 --- a/Mage.Sets/src/mage/sets/eventide/Quillspike.java +++ b/Mage.Sets/src/mage/sets/eventide/Quillspike.java @@ -62,7 +62,7 @@ public class Quillspike extends CardImpl { // {BG}, Remove a -1/-1 counter from a creature you control: Quillspike gets +3/+3 until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(3, 3, Duration.EndOfTurn), new ManaCostsImpl("{B/G}")); - TargetPermanent target = new TargetPermanent(1, 1, new FilterControlledCreaturePermanent(), true); + TargetPermanent target = new TargetPermanent(1, 1, new FilterControlledCreaturePermanent("creature you control"), true); ability.addCost(new RemoveCounterCost(target, CounterType.M1M1)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/gatecrash/OozeFlux.java b/Mage.Sets/src/mage/sets/gatecrash/OozeFlux.java index 0c5a43b194..b61778beea 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/OozeFlux.java +++ b/Mage.Sets/src/mage/sets/gatecrash/OozeFlux.java @@ -59,8 +59,7 @@ public class OozeFlux extends CardImpl { // {1}{G}, Remove one or more +1/+1 counters from among creatures you control: Put an X/X green Ooze creature token onto the battlefield, where X is the number of +1/+1 counters removed this way. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new OozeFluxCreateTokenEffect(new OozeToken()),new ManaCostsImpl("{1}{G}")); - ability.addCost(new RemoveVariableCountersTargetCost( - new TargetControlledCreaturePermanent(1,Integer.MAX_VALUE,new FilterControlledCreaturePermanent(), true), CounterType.P1P1)); + ability.addCost(new RemoveVariableCountersTargetCost(new FilterControlledCreaturePermanent("creatures you control"), CounterType.P1P1, "one or more", 1)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/innistrad/DelverOfSecrets.java b/Mage.Sets/src/mage/sets/innistrad/DelverOfSecrets.java index 634948812c..ad737f89af 100644 --- a/Mage.Sets/src/mage/sets/innistrad/DelverOfSecrets.java +++ b/Mage.Sets/src/mage/sets/innistrad/DelverOfSecrets.java @@ -81,7 +81,7 @@ public class DelverOfSecrets extends CardImpl { class DelverOfSecretsAbility extends TriggeredAbilityImpl { public DelverOfSecretsAbility() { - super(Zone.BATTLEFIELD, new TransformSourceEffect(true), false); + super(Zone.BATTLEFIELD, new TransformSourceEffect(true, false), false); } public DelverOfSecretsAbility(DelverOfSecretsAbility ability) { diff --git a/Mage.Sets/src/mage/sets/innistrad/HarvestPyre.java b/Mage.Sets/src/mage/sets/innistrad/HarvestPyre.java index 600b33192b..5523e5fba7 100644 --- a/Mage.Sets/src/mage/sets/innistrad/HarvestPyre.java +++ b/Mage.Sets/src/mage/sets/innistrad/HarvestPyre.java @@ -28,21 +28,13 @@ package mage.sets.innistrad; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.abilities.Ability; -import mage.abilities.costs.CostImpl; -import mage.abilities.costs.VariableCost; +import mage.abilities.costs.common.ExileXFromYourGraveCost; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.common.DamageTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; -import mage.filter.FilterMana; -import mage.game.Game; -import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetCardInYourGraveyard; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterCard; import mage.target.common.TargetCreaturePermanent; /** @@ -58,7 +50,7 @@ public class HarvestPyre extends CardImpl { this.color.setRed(true); // As an additional cost to cast Harvest Pyre, exile X cards from your graveyard. - this.getSpellAbility().addCost(new HarvestPyreCost()); + this.getSpellAbility().addCost(new ExileXFromYourGraveCost(new FilterCard("cards from your graveyard"))); // Harvest Pyre deals X damage to target creature. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); @@ -74,77 +66,3 @@ public class HarvestPyre extends CardImpl { return new HarvestPyre(this); } } - -class HarvestPyreCost extends CostImpl implements VariableCost { - - protected int amountPaid = 0; - - public HarvestPyreCost() { - this.text = "As an additional cost to cast Harvest Pyre, exile X cards from your graveyard"; - } - - public HarvestPyreCost(final HarvestPyreCost cost) { - super(cost); - this.amountPaid = cost.amountPaid; - } - - @Override - public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - return true; - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - amountPaid = 0; - Target target = new TargetCardInYourGraveyard(); - Player player = game.getPlayer(controllerId); - while (true) { - target.clearChosen(); - if (target.canChoose(controllerId, game) && target.choose(Outcome.Exile, controllerId, sourceId, game)) { - Card card = player.getGraveyard().get(target.getFirstTarget(), game); - if (card != null) { - player.getGraveyard().remove(card); - card.moveToExile(null, "", sourceId, game); - amountPaid++; - } - } - else - break; - } - paid = true; - return true; - } - - @Override - public int getAmount() { - return amountPaid; - } - - /** - * Not Supported - * @param filter - */ - @Override - public void setFilter(FilterMana filter) { - } - - /** - * Not supported - * @return - */ - @Override - public FilterMana getFilter() { - return new FilterMana(); - } - - @Override - public HarvestPyreCost copy() { - return new HarvestPyreCost(this); - } - - @Override - public void setAmount(int amount) { - amountPaid = amount; - } - -} diff --git a/Mage.Sets/src/mage/sets/limitedalpha/DrainLife.java b/Mage.Sets/src/mage/sets/limitedalpha/DrainLife.java index 1a4d98008f..599b7c1e8c 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/DrainLife.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/DrainLife.java @@ -30,6 +30,8 @@ package mage.sets.limitedalpha; import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.costs.VariableCost; +import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.constants.CardType; @@ -65,7 +67,10 @@ public class DrainLife extends CardImpl { // Drain Life deals X damage to target creature or player. You gain life equal to the damage dealt, but not more life than the player's life total before Drain Life dealt damage or the creature's toughness. this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().addEffect(new DrainLifeEffect()); - this.getSpellAbility().getManaCostsToPay().getVariableCosts().get(0).setFilter(filterBlack); + VariableCost variableCost = this.getSpellAbility().getManaCostsToPay().getVariableCosts().get(0); + if (variableCost instanceof VariableManaCost) { + ((VariableManaCost) variableCost).setFilter(filterBlack); + } } public DrainLife(final DrainLife card) { diff --git a/Mage.Sets/src/mage/sets/magic2010/ConsumeSpirit.java b/Mage.Sets/src/mage/sets/magic2010/ConsumeSpirit.java index 6b92bf3785..f15abffeca 100644 --- a/Mage.Sets/src/mage/sets/magic2010/ConsumeSpirit.java +++ b/Mage.Sets/src/mage/sets/magic2010/ConsumeSpirit.java @@ -32,6 +32,8 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.abilities.Ability; +import mage.abilities.costs.VariableCost; +import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.filter.FilterMana; @@ -63,7 +65,10 @@ public class ConsumeSpirit extends CardImpl { // Consume Spirit deals X damage to target creature or player and you gain X life. this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().addEffect(new ConsumeSpiritEffect()); - this.getSpellAbility().getManaCostsToPay().getVariableCosts().get(0).setFilter(filterBlack); + VariableCost variableCost = this.getSpellAbility().getManaCostsToPay().getVariableCosts().get(0); + if (variableCost instanceof VariableManaCost) { + ((VariableManaCost) variableCost).setFilter(filterBlack); + } } public ConsumeSpirit(final ConsumeSpirit card) { diff --git a/Mage.Sets/src/mage/sets/magic2014/DevoutInvocation.java b/Mage.Sets/src/mage/sets/magic2014/DevoutInvocation.java index d5d75b1e5e..52f7402272 100644 --- a/Mage.Sets/src/mage/sets/magic2014/DevoutInvocation.java +++ b/Mage.Sets/src/mage/sets/magic2014/DevoutInvocation.java @@ -27,6 +27,9 @@ */ package mage.sets.magic2014; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -39,6 +42,7 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.token.AngelToken; +import mage.players.Player; import mage.target.TargetPermanent; /** @@ -87,23 +91,35 @@ class DevoutInvocationEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - int tappedAmount = 0; - TargetPermanent target = new TargetPermanent(filter); - while (true) { - target.clearChosen(); - if (target.canChoose(source.getControllerId(), game) && target.choose(Outcome.Tap, source.getControllerId(), source.getId(), game)) { - UUID creature = target.getFirstTarget(); - if (creature != null) { - game.getPermanent(creature).tap(game); - tappedAmount++; + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int tappedAmount = 0; + TargetPermanent target = new TargetPermanent(0,1,filter, false); + while (true && controller.isInGame()) { + target.clearChosen(); + if (target.canChoose(source.getControllerId(), game)) { + Map options = new HashMap<>(); + options.put("UI.right.btn.text", "Tapping complete"); + controller.choose(outcome, target, source.getControllerId(), game, options); + if (target.getTargets().size() > 0) { + UUID creature = target.getFirstTarget(); + if (creature != null) { + game.getPermanent(creature).tap(game); + tappedAmount++; + } + } else { + break; + } + } + else { + break; } } - else - break; - } - if (tappedAmount > 0) { - AngelToken angelToken = new AngelToken(); - angelToken.putOntoBattlefield(tappedAmount, game, source.getId(), source.getControllerId()); + if (tappedAmount > 0) { + AngelToken angelToken = new AngelToken(); + angelToken.putOntoBattlefield(tappedAmount, game, source.getSourceId(), source.getControllerId()); + game.informPlayers(new StringBuilder(controller.getName()).append(" puts ").append(tappedAmount).append(" token").append(tappedAmount != 1 ?"s":"").append(" onto the battlefield").toString()); + } return true; } return false; diff --git a/Mage.Sets/src/mage/sets/newphyrexia/ParasiticImplant.java b/Mage.Sets/src/mage/sets/newphyrexia/ParasiticImplant.java index cf45552e83..e52c132178 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/ParasiticImplant.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/ParasiticImplant.java @@ -65,7 +65,7 @@ public class ParasiticImplant extends CardImpl { Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); ability = new BeginningOfUpkeepTriggeredAbility(new ParasiticImplantEffect(), TargetController.YOU, false); - ability.addEffect(new CreateTokenEffect(new MyrToken())); + ability.addEffect(new CreateTokenEffect(new MyrToken("NPH"))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/ShrineOfLoyalLegions.java b/Mage.Sets/src/mage/sets/newphyrexia/ShrineOfLoyalLegions.java index e22466eb45..dcf596c49e 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/ShrineOfLoyalLegions.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/ShrineOfLoyalLegions.java @@ -69,7 +69,7 @@ public class ShrineOfLoyalLegions extends CardImpl { this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), filter, false)); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new CreateTokenEffect(new MyrToken(), new CountersCount(CounterType.CHARGE)), + new CreateTokenEffect(new MyrToken("NPH"), new CountersCount(CounterType.CHARGE)), new GenericManaCost(3)); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); diff --git a/Mage.Sets/src/mage/sets/odyssey/SkeletalScrying.java b/Mage.Sets/src/mage/sets/odyssey/SkeletalScrying.java index c658124973..e929e3497b 100644 --- a/Mage.Sets/src/mage/sets/odyssey/SkeletalScrying.java +++ b/Mage.Sets/src/mage/sets/odyssey/SkeletalScrying.java @@ -28,23 +28,22 @@ package mage.sets.odyssey; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; -import mage.abilities.costs.CostImpl; -import mage.abilities.costs.VariableCost; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.ExileFromGraveCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.FilterMana; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; /** @@ -60,10 +59,11 @@ public class SkeletalScrying extends CardImpl { this.color.setBlack(true); // As an additional cost to cast Skeletal Scrying, exile X cards from your graveyard. - this.getSpellAbility().addCost(new ExileXFromGraveyardCost()); - + Ability ability = new SimpleStaticAbility(Zone.ALL,new SkeletalScryingRuleEffect()); + ability.setRuleAtTheTop(true); + this.addAbility(ability); // You draw X cards and you lose X life. - this.getSpellAbility().addEffect(new SkeletalScryingEffect(new GetXValue())); + this.getSpellAbility().addEffect(new SkeletalScryingEffect(new ManacostVariableValue())); } @@ -71,86 +71,41 @@ public class SkeletalScrying extends CardImpl { super(card); } + @Override + public void adjustCosts(Ability ability, Game game) { + int xValue = ability.getManaCostsToPay().getX(); + if (xValue > 0) { + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(xValue, xValue, new FilterCard("cards from your graveyard")))); + } + } + @Override public SkeletalScrying copy() { return new SkeletalScrying(this); } } -class ExileXFromGraveyardCost extends CostImpl implements VariableCost { +class SkeletalScryingRuleEffect extends OneShotEffect { - protected int amountPaid = 0; - - public ExileXFromGraveyardCost() { - this.text = "As an additional cost to cast Skeletal Scrying, exile X cards from your graveyard"; + public SkeletalScryingRuleEffect() { + super(Outcome.Benefit); + this.staticText = "As an additional cost to cast Skeletal Scrying, exile X cards from your graveyard"; } - public ExileXFromGraveyardCost(final ExileXFromGraveyardCost cost) { - super(cost); - this.amountPaid = cost.amountPaid; + public SkeletalScryingRuleEffect(final SkeletalScryingRuleEffect effect) { + super(effect); } @Override - public boolean canPay(UUID sourceId, UUID controllerId, Game game) { + public SkeletalScryingRuleEffect copy() { + return new SkeletalScryingRuleEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { return true; } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - amountPaid = 0; - Target target = new TargetCardInYourGraveyard(); - Player player = game.getPlayer(controllerId); - while (true) { - target.clearChosen(); - if (target.canChoose(controllerId, game) && target.choose(Outcome.Exile, controllerId, sourceId, game)) { - Card card = player.getGraveyard().get(target.getFirstTarget(), game); - if (card != null) { - player.getGraveyard().remove(card); - card.moveToExile(null, "", sourceId, game); - amountPaid++; - } - } - else - break; - } - paid = true; - return true; - } - - @Override - public int getAmount() { - return amountPaid; - } - - /** - * Not Supported - * @param filter - */ - @Override - public void setFilter(FilterMana filter) { - } - - /** - * Not supported - * @return - */ - @Override - public FilterMana getFilter() { - return new FilterMana(); - } - - @Override - public ExileXFromGraveyardCost copy() { - return new ExileXFromGraveyardCost(this); - } - - @Override - public void setAmount(int amount) { - amountPaid = amount; - } - } - class SkeletalScryingEffect extends OneShotEffect { protected DynamicValue amount; diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/DevastatingSummons.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/DevastatingSummons.java index 792aa312f0..0bae08dd6e 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/DevastatingSummons.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/DevastatingSummons.java @@ -28,22 +28,18 @@ package mage.sets.riseoftheeldrazi; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.costs.CostImpl; -import mage.abilities.costs.VariableCost; +import mage.abilities.costs.common.SacrificeXTargetCost; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.FilterMana; -import mage.filter.common.FilterLandPermanent; +import mage.constants.Rarity; +import mage.filter.common.FilterControlledLandPermanent; import mage.game.Game; import mage.game.permanent.token.Token; -import mage.target.common.TargetLandPermanent; /** * @@ -58,7 +54,7 @@ public class DevastatingSummons extends CardImpl { this.color.setRed(true); // As an additional cost to cast Devastating Summons, sacrifice X lands. - this.getSpellAbility().addCost(new DevastatingSummonsCost()); + this.getSpellAbility().addCost(new SacrificeXTargetCost(new FilterControlledLandPermanent("lands"), true)); // Put two X/X red Elemental creature tokens onto the battlefield. this.getSpellAbility().addEffect(new DevastatingSummonsEffect()); @@ -74,78 +70,6 @@ public class DevastatingSummons extends CardImpl { } } -class DevastatingSummonsCost extends CostImpl implements VariableCost { - - protected int amountPaid = 0; - - public DevastatingSummonsCost() { - this.text = "sacrifice X lands"; - } - - public DevastatingSummonsCost(final DevastatingSummonsCost cost) { - super(cost); - this.amountPaid = cost.amountPaid; - } - - @Override - public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - return true; - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - amountPaid = 0; - FilterLandPermanent filter = new FilterLandPermanent("X number of lands you control."); - TargetLandPermanent target = new TargetLandPermanent(filter); - while (true) { - target.clearChosen(); - if (target.canChoose(controllerId, game) && target.choose(Outcome.Sacrifice, controllerId, sourceId, game)) { - UUID land = target.getFirstTarget(); - if (land != null) { - game.getPermanent(land).sacrifice(sourceId, game); - amountPaid++; - } - } - else - break; - } - paid = true; - return true; - } - - @Override - public int getAmount() { - return amountPaid; - } - - /** - * Not Supported - * @param filter - */ - @Override - public void setFilter(FilterMana filter) { - } - - /** - * Not supported - * @return - */ - @Override - public FilterMana getFilter() { - return new FilterMana(); - } - - @Override - public DevastatingSummonsCost copy() { - return new DevastatingSummonsCost(this); - } - - @Override - public void setAmount(int amount) { - amountPaid = amount; - } -} - class DevastatingSummonsEffect extends OneShotEffect { public DevastatingSummonsEffect() { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/MyrBattlesphere.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/MyrBattlesphere.java index de846b7f85..94164b29a2 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/MyrBattlesphere.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/MyrBattlesphere.java @@ -28,33 +28,30 @@ package mage.sets.scarsofmirrodin; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.costs.common.TapVariableTargetCost; -import mage.abilities.dynamicvalue.common.GetXValue; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continious.BoostSourceEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.token.MyrToken; import mage.players.Player; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.TargetPermanent; /** * @@ -74,7 +71,7 @@ public class MyrBattlesphere extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new MyrToken(), 4), false)); // Whenever Myr Battlesphere attacks, you may tap X untapped Myr you control. If you do, Myr Battlesphere gets +X/+0 until end of turn and deals X damage to defending player. - this.addAbility(new MyrBattlesphereAbility()); + this.addAbility(new AttacksTriggeredAbility(new MyrBattlesphereEffect(), true)); } public MyrBattlesphere(final MyrBattlesphere card) { @@ -88,77 +85,63 @@ public class MyrBattlesphere extends CardImpl { } -class MyrBattlesphereAbility extends TriggeredAbilityImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped Myr"); - - static { - filter.add(new SubtypePredicate("Myr")); - filter.add(Predicates.not(new TappedPredicate())); - } - - public MyrBattlesphereAbility() { - super(Zone.BATTLEFIELD, new BoostSourceEffect(new GetXValue(), new StaticValue(0), Duration.EndOfTurn), true); - this.addEffect(new MyrBattlesphereEffect()); - this.addCost(new TapVariableTargetCost(new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE, filter, false))); - } - - public MyrBattlesphereAbility(final MyrBattlesphereAbility ability) { - super(ability); - } - - @Override - public boolean checkInterveningIfClause(Game game) { - if (costs.isPaid()) { - return true; - } - if (costs.canPay(this.getId(), this.getControllerId(), game)) { - return costs.pay(this, game, this.getId(), this.getControllerId(), false); - } - return false; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == EventType.ATTACKER_DECLARED && event.getSourceId().equals(this.getSourceId()) ) { - costs.clearPaid(); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} attacks, " + super.getRule(); - } - - @Override - public MyrBattlesphereAbility copy() { - return new MyrBattlesphereAbility(this); - } - -} - class MyrBattlesphereEffect extends OneShotEffect { - private GetXValue amount = new GetXValue(); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped Myr you control"); + + static { + filter.add(Predicates.not(new TappedPredicate())); + filter.add(new SubtypePredicate("Myr")); + } public MyrBattlesphereEffect() { super(Outcome.Damage); - staticText = "{source} deals X damage to defending player"; + staticText = "tap X untapped Myr you control. If you do, {source} gets +X/+0 until end of turn and deals X damage to defending player"; } public MyrBattlesphereEffect(final MyrBattlesphereEffect effect) { super(effect); - this.amount = effect.amount.copy(); } @Override public boolean apply(Game game, Ability source) { - UUID defenderId = game.getCombat().getDefenderId(source.getSourceId()); - Player defender = game.getPlayer(defenderId); - if (defender != null) { - defender.damage(amount.calculate(game, source), source.getSourceId(), game, false, false); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int tappedAmount = 0; + TargetPermanent target = new TargetPermanent(0,1,filter, false); + while (true && controller.isInGame()) { + target.clearChosen(); + if (target.canChoose(source.getControllerId(), game)) { + Map options = new HashMap<>(); + options.put("UI.right.btn.text", "Myr tapping complete"); + controller.choose(outcome, target, source.getControllerId(), game, options); + if (target.getTargets().size() > 0) { + UUID creature = target.getFirstTarget(); + if (creature != null) { + game.getPermanent(creature).tap(game); + tappedAmount++; + } + } else { + break; + } + } + else { + break; + } + } + if (tappedAmount > 0) { + game.informPlayers(new StringBuilder(controller.getName()).append(" taps ").append(tappedAmount).append(" Myrs").toString()); + // boost effect + game.addEffect(new BoostSourceEffect(tappedAmount, 0, Duration.EndOfTurn), source); + // damage to defender + UUID defenderId = game.getCombat().getDefenderId(source.getSourceId()); + Player defender = game.getPlayer(defenderId); + if (defender != null) { + defender.damage(tappedAmount, source.getSourceId(), game, false, true); + return true; + } + + } return true; } return false; diff --git a/Mage.Sets/src/mage/sets/seventhedition/CrimsonHellkite.java b/Mage.Sets/src/mage/sets/seventhedition/CrimsonHellkite.java index 06fbba4804..64e7552a75 100644 --- a/Mage.Sets/src/mage/sets/seventhedition/CrimsonHellkite.java +++ b/Mage.Sets/src/mage/sets/seventhedition/CrimsonHellkite.java @@ -31,8 +31,10 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.VariableCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.FlyingAbility; @@ -69,7 +71,10 @@ public class CrimsonHellkite extends CardImpl { // {X}, {tap}: Crimson Hellkite deals X damage to target creature. Spend only red mana this way. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new ManacostVariableValue()), new ManaCostsImpl("{X}")); ability.addCost(new TapSourceCost()); - ability.getManaCostsToPay().getVariableCosts().get(0).setFilter(filterRedMana); + VariableCost variableCost = ability.getManaCostsToPay().getVariableCosts().get(0); + if (variableCost instanceof VariableManaCost) { + ((VariableManaCost) variableCost).setFilter(filterRedMana); + } ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/visions/CryptRats.java b/Mage.Sets/src/mage/sets/visions/CryptRats.java index 1991149bbc..a7eb835d83 100644 --- a/Mage.Sets/src/mage/sets/visions/CryptRats.java +++ b/Mage.Sets/src/mage/sets/visions/CryptRats.java @@ -28,16 +28,19 @@ package mage.sets.visions; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageEverythingEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.filter.FilterMana; import mage.filter.FilterPermanent; @@ -48,6 +51,7 @@ import mage.filter.FilterPermanent; public class CryptRats extends CardImpl { public static final FilterMana filterBlack = new FilterMana(); + static { filterBlack.setBlack(true); } @@ -61,10 +65,14 @@ public class CryptRats extends CardImpl { this.toughness = new MageInt(1); // {X}: Crypt Rats deals X damage to each creature and each player. Spend only black mana this way. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageEverythingEffect(new ManacostVariableValue(), new FilterPermanent()), new ManaCostsImpl("{X}")); - ability.getManaCostsToPay().getVariableCosts().get(0).setFilter(filterBlack); + Effect effect = new DamageEverythingEffect(new ManacostVariableValue(), new FilterPermanent()); + effect.setText("{this} deals X damage to each creature and each player. Spend only black mana this way"); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect,new ManaCostsImpl("{X}")); + VariableCost variableCost = ability.getManaCostsToPay().getVariableCosts().get(0); + if (variableCost instanceof VariableManaCost) { + ((VariableManaCost) variableCost).setFilter(filterBlack); + } this.addAbility(ability); - this.addInfo("01", "Spend only black mana this way."); } public CryptRats(final CryptRats card) { diff --git a/Mage.Sets/src/mage/sets/weatherlight/Firestorm.java b/Mage.Sets/src/mage/sets/weatherlight/Firestorm.java index 6f912460aa..a70915ea4f 100644 --- a/Mage.Sets/src/mage/sets/weatherlight/Firestorm.java +++ b/Mage.Sets/src/mage/sets/weatherlight/Firestorm.java @@ -29,24 +29,19 @@ package mage.sets.weatherlight; import java.util.List; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.abilities.Ability; -import mage.abilities.costs.CostImpl; -import mage.abilities.costs.VariableCost; +import mage.abilities.costs.common.DiscardXTargetCost; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterMana; +import mage.constants.Rarity; +import mage.filter.FilterCard; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetCardInHand; import mage.target.common.TargetCreatureOrPlayer; /** @@ -62,16 +57,25 @@ public class Firestorm extends CardImpl { this.color.setRed(true); // As an additional cost to cast Firestorm, discard X cards. + this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), true)); // Firestorm deals X damage to each of X target creatures and/or players. this.getSpellAbility().addEffect(new FirestormEffect()); - this.getSpellAbility().addCost(new FirestormCost()); - } public Firestorm(final Firestorm card) { super(card); } + @Override + public void adjustTargets(Ability ability, Game game) { + int xValue = new GetXValue().calculate(game, ability); + if (xValue > 0) { + Target target = new TargetCreatureOrPlayer(xValue); + target.setRequired(true); + ability.addTarget(target); + } + } + @Override public Firestorm copy() { return new Firestorm(this); @@ -93,105 +97,30 @@ class FirestormEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player you = game.getPlayer(source.getControllerId()); int amount = (new GetXValue()).calculate(game, source); - TargetCreatureOrPlayer target = new TargetCreatureOrPlayer(amount); if (you != null) { - if (target.canChoose(source.getControllerId(), game) && target.choose(Outcome.Neutral, source.getControllerId(), source.getId(), game)) { - if (!target.getTargets().isEmpty()) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Permanent creature = game.getPermanent(targetId); - if (creature != null) { - creature.damage(amount, source.getSourceId(), game, true, false); - } else { - Player player = game.getPlayer(targetId); - if (player != null) { - player.damage(amount, source.getSourceId(), game, true, false); - } + if (source.getTargets().size() > 0) { + for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { + Permanent creature = game.getPermanent(targetId); + if (creature != null) { + creature.damage(amount, source.getSourceId(), game, true, false); + } else { + Player player = game.getPlayer(targetId); + if (player != null) { + player.damage(amount, source.getSourceId(), game, false, true); } } } - return true; } + return true; } return false; - } + + + @Override public FirestormEffect copy() { return new FirestormEffect(this); } } - -class FirestormCost extends CostImpl implements VariableCost { - - protected int amountPaid = 0; - - public FirestormCost() { - this.text = "discard X cards"; - } - - public FirestormCost(final FirestormCost cost) { - super(cost); - this.amountPaid = cost.amountPaid; - } - - @Override - public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - return true; - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - amountPaid = 0; - Target target = new TargetCardInHand(); - Player you = game.getPlayer(controllerId); - while (true) { - target.clearChosen(); - if (target.canChoose(controllerId, game) && target.choose(Outcome.Discard, controllerId, sourceId, game)) { - Card card = you.getHand().get(target.getFirstTarget(), game); - if (card != null) { - you.getHand().remove(card); - card.moveToZone(Zone.GRAVEYARD, sourceId, game, false); - amountPaid++; - } - } else { - break; - } - } - paid = true; - return true; - } - - @Override - public int getAmount() { - return amountPaid; - } - - /** - * Not Supported - * @param filter - */ - @Override - public void setFilter(FilterMana filter) { - } - - /** - * Not supported - * @return - */ - @Override - public FilterMana getFilter() { - return new FilterMana(); - } - - @Override - public FirestormCost copy() { - return new FirestormCost(this); - } - - @Override - public void setAmount(int amount) { - amountPaid = amount; - } -} diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 6e970016cd..49edbb1504 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -28,33 +28,43 @@ package mage.abilities; -import mage.constants.AbilityType; -import mage.constants.EffectType; -import mage.constants.Outcome; -import mage.constants.Zone; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; import mage.MageObject; -import mage.abilities.costs.*; +import mage.abilities.costs.AdjustingSourceCosts; +import mage.abilities.costs.AlternativeCost; +import mage.abilities.costs.AlternativeSourceCosts; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.OptionalAdditionalSourceCosts; +import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.VariableManaCost; -import mage.abilities.effects.*; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.Effects; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.PostResolveEffect; import mage.abilities.mana.ManaAbility; import mage.cards.Card; import mage.choices.Choice; import mage.choices.Choices; +import mage.constants.AbilityType; +import mage.constants.EffectType; +import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; +import mage.game.command.Emblem; import mage.game.permanent.PermanentCard; +import mage.players.Player; import mage.target.Target; import mage.target.Targets; import org.apache.log4j.Logger; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import mage.game.command.Emblem; - - /** * * @author BetaSteward_at_googlemail.com @@ -72,7 +82,7 @@ public abstract class AbilityImpl> implements Ability { protected ManaCosts manaCosts; protected ManaCosts manaCostsToPay; protected Costs costs; - protected ArrayList alternativeCosts = new ArrayList(); + protected ArrayList alternativeCosts = new ArrayList<>(); protected Costs optionalCosts; protected Modes modes; protected Zone zone; @@ -89,10 +99,10 @@ public abstract class AbilityImpl> implements Ability { this.originalId = id; this.abilityType = abilityType; this.zone = zone; - this.manaCosts = new ManaCostsImpl(); - this.manaCostsToPay = new ManaCostsImpl(); - this.costs = new CostsImpl(); - this.optionalCosts = new CostsImpl(); + this.manaCosts = new ManaCostsImpl<>(); + this.manaCostsToPay = new ManaCostsImpl<>(); + this.costs = new CostsImpl<>(); + this.optionalCosts = new CostsImpl<>(); this.modes = new Modes(); } @@ -222,7 +232,8 @@ public abstract class AbilityImpl> implements Ability { // 20121001 - 601.2b // If the spell has a variable cost that will be paid as it's being cast (such as an {X} in // its mana cost; see rule 107.3), the player announces the value of that variable. - VariableManaCost variableManaCost = handleXCosts(game, noMana); + VariableManaCost variableManaCost = handleManaXCosts(game, noMana); + String announceString = handleOtherXCosts(game); for (UUID modeId :this.getModes().getSelectedModes()) { this.getModes().setMode(this.getModes().get(modeId)); @@ -247,8 +258,11 @@ public abstract class AbilityImpl> implements Ability { card.adjustTargets(this, game); } if (getTargets().size() > 0 && getTargets().chooseTargets(getEffects().get(0).getOutcome(), this.controllerId, this, game) == false) { - if (variableManaCost != null) { - game.informPlayers(new StringBuilder(card != null ? card.getName(): "").append(": no valid targets with this value of X").toString()); + if (variableManaCost != null || announceString != null) { + Player controller = game.getPlayer(this.getControllerId()); + if (controller != null) { + game.informPlayer(controller, new StringBuilder(card != null ? card.getName(): "").append(": no valid targets with this value of X").toString()); + } } else { logger.debug("activate failed - target"); } @@ -305,21 +319,52 @@ public abstract class AbilityImpl> implements Ability { return false; } // inform about x costs now, so canceled announcements are not shown in the log + if (announceString != null) { + game.informPlayers(announceString); + } if (variableManaCost != null) { int xValue = getManaCostsToPay().getX(); - game.informPlayers(new StringBuilder(game.getPlayer(this.controllerId).getName()).append(" announced a value of ").append(xValue).append(" for ").append(variableManaCost.getText()).toString()); + game.informPlayers(new StringBuilder(game.getPlayer(this.controllerId).getName()).append(" announces a value of ").append(xValue).append(" for ").append(variableManaCost.getText()).toString()); } return true; } /** - * Handles the announcement of X mana costs and sets manaCostsToPay. + * Handles the setting of non mana X costs + * + * @param game + * @return announce message + * + */ + protected String handleOtherXCosts(Game game) { + String announceString = null; + for (VariableCost variableCost : this.costs.getVariableCosts()) { + if (!(variableCost instanceof VariableManaCost)) { + int xValue = variableCost.announceXValue(this, game); + costs.add(variableCost.getFixedCostsFromAnnouncedValue(xValue)); + // set the xcosts to paid + variableCost.setAmount(xValue); + ((Cost) variableCost).setPaid(); + String message = new StringBuilder(game.getPlayer(this.controllerId).getName()) + .append(" announces a value of ").append(xValue).append(" (").append(variableCost.getActionText()).append(")").toString(); + if (announceString == null) { + announceString = message; + } else { + announceString = new StringBuilder(announceString).append(" ").append(message).toString(); + } + } + } + return announceString; + } + + /** + * Handles X mana costs and sets manaCostsToPay. * * @param game * @param noMana - * @return variableManaCost for late check + * @return variableManaCost for posting to log later */ - protected VariableManaCost handleXCosts(Game game, boolean noMana) { + protected VariableManaCost handleManaXCosts(Game game, boolean noMana) { // 20121001 - 601.2b // If the spell has a variable cost that will be paid as it's being cast (such as an {X} in // its mana cost; see rule 107.3), the player announces the value of that variable. diff --git a/Mage/src/mage/abilities/ActivatedAbilityImpl.java b/Mage/src/mage/abilities/ActivatedAbilityImpl.java index f626dfe43f..64f7d23517 100644 --- a/Mage/src/mage/abilities/ActivatedAbilityImpl.java +++ b/Mage/src/mage/abilities/ActivatedAbilityImpl.java @@ -213,8 +213,8 @@ public abstract class ActivatedAbilityImpl> ex return ""; } MageObject object = game.getObject(this.sourceId); - return new StringBuilder(" activates ") - .append(object != null ? this.getRule(object.getName()) :this.getRule()) + return new StringBuilder(" activates: ") + .append(object != null ? this.formatRule(modes.getText(), object.getName()) :modes.getText()) .append(" from ") .append(getMessageText(game)).toString(); } diff --git a/Mage/src/mage/abilities/costs/VariableCost.java b/Mage/src/mage/abilities/costs/VariableCost.java index a82bcebc3b..73d2337992 100644 --- a/Mage/src/mage/abilities/costs/VariableCost.java +++ b/Mage/src/mage/abilities/costs/VariableCost.java @@ -28,16 +28,61 @@ package mage.abilities.costs; -import mage.filter.FilterMana; +import mage.abilities.Ability; +import mage.game.Game; /** * * @author BetaSteward_at_googlemail.com */ public interface VariableCost { - + /** + * Returns the variable amount if alreaady set + * + * @return + */ int getAmount(); + /** + * Sets the variable amount + * + * @param amount + */ void setAmount(int amount); - void setFilter(FilterMana filter); - FilterMana getFilter(); + + /** + * returns the action text (e.g. "creature cards to exile from your hand", "life to pay") + * + * @return + */ + String getActionText(); + /** + * Return a min value to announce + * + * @param source + * @param game + * @return + */ + int getMinValue(Ability source, Game game); + /** + * Returns a max value to announce + * + * @param source + * @param game + * @return + */ + int getMaxValue(Ability source, Game game); + /** + * Asks the controller to announce the variable value + * @param source + * @param game + * @return + */ + int announceXValue(Ability source, Game game); + /** + * Returns a fixed cost with the announced variabke value + * + * @param xValue + * @return + */ + Cost getFixedCostsFromAnnouncedValue(int xValue); } diff --git a/Mage/src/mage/abilities/costs/VariableCostImpl.java b/Mage/src/mage/abilities/costs/VariableCostImpl.java new file mode 100644 index 0000000000..fc980bbc17 --- /dev/null +++ b/Mage/src/mage/abilities/costs/VariableCostImpl.java @@ -0,0 +1,166 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.abilities.costs; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.game.Game; +import mage.players.Player; +import mage.target.Target; +import mage.target.Targets; + +/** + * + * @author LevelX2 + * @param variable cost type + */ + +public abstract class VariableCostImpl> implements Cost, VariableCost { + + protected UUID id; + protected String text; + protected boolean paid; + protected Targets targets; + protected int amountPaid; + protected String xText; + protected String actionText; + + @Override + public abstract T copy(); + + public VariableCostImpl(String actionText) { + this("X", actionText); + } + /** + * + * @param xText string for the defined value + * @param actionText what happens with the value (e.g. "to tap", "to exile from your graveyard") + */ + public VariableCostImpl(String xText, String actionText) { + id = UUID.randomUUID(); + paid = false; + targets = new Targets(); + amountPaid = 0; + this.xText = xText; + this.actionText = actionText; + } + + public VariableCostImpl(final VariableCostImpl cost) { + this.id = cost.id; + this.text = cost.text; + this.paid = cost.paid; + this.targets = cost.targets.copy(); + this.xText = cost.xText; + this.actionText = cost.actionText; + } + + @Override + public String getText() { + return text; + } + + @Override + public String getActionText() { + return actionText; + } + + public void addTarget(Target target) { + if (target != null) { + this.targets.add(target); + } + } + + @Override + public Targets getTargets() { + return this.targets; + } + + @Override + public boolean isPaid() { + return paid; + } + + @Override + public void clearPaid() { + paid = false; + amountPaid = 0; + } + + @Override + public void setPaid() { + paid = true; + } + + @Override + public UUID getId() { + return this.id; + } + + @Override + public boolean canPay(UUID sourceId, UUID controllerId, Game game) { + return true; /* not used */ + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { + return true; /* not used */ + } + + @Override + public int getAmount() { + return amountPaid; + } + + @Override + public void setAmount(int amount) { + amountPaid = amount; + } + + @Override + public int getMinValue(Ability source, Game game) { + return 0; + } + + @Override + public int getMaxValue(Ability source, Game game) { + return Integer.MAX_VALUE; + } + + @Override + public int announceXValue(Ability source, Game game) { + int xValue = 0; + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + xValue = controller.announceXCost(0, getMaxValue(source, game), + new StringBuilder("Announce the number of ").append(actionText).toString(), + game, source, this); + } + return xValue; + } +} diff --git a/Mage/src/mage/abilities/costs/common/DiscardXTargetCost.java b/Mage/src/mage/abilities/costs/common/DiscardXTargetCost.java new file mode 100644 index 0000000000..e0b84d55f5 --- /dev/null +++ b/Mage/src/mage/abilities/costs/common/DiscardXTargetCost.java @@ -0,0 +1,84 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.abilities.costs.common; + +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.VariableCostImpl; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInHand; + +/** + * + * @author LevelX2 + */ + +public class DiscardXTargetCost extends VariableCostImpl { + + protected FilterCard filter; + + public DiscardXTargetCost(FilterCard filter) { + this(filter, false); + } + + public DiscardXTargetCost(FilterCard filter, boolean additionalCostText) { + super(new StringBuilder(filter.getMessage()).append(" to discard").toString()); + this.text = new StringBuilder(additionalCostText ? "As an additional cost to cast {source}, discard ":"Discard ") + .append(xText).append(" ").append(filter.getMessage()).toString(); + this.filter = filter; + } + + public DiscardXTargetCost(final DiscardXTargetCost cost) { + super(cost); + this.filter = cost.filter; + } + + @Override + public DiscardXTargetCost copy() { + return new DiscardXTargetCost(this); + } + + @Override + public int getMaxValue(Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + return controller.getHand().count(filter, game); + } + return 0; + } + + @Override + public Cost getFixedCostsFromAnnouncedValue(int xValue) { + TargetCardInHand target = new TargetCardInHand(xValue, filter); + target.setRequired(true); + return new DiscardTargetCost(target); + } +} diff --git a/Mage/src/mage/abilities/costs/common/ExileFromGraveCost.java b/Mage/src/mage/abilities/costs/common/ExileFromGraveCost.java index 4916a2c1bb..69f6b6a56e 100644 --- a/Mage/src/mage/abilities/costs/common/ExileFromGraveCost.java +++ b/Mage/src/mage/abilities/costs/common/ExileFromGraveCost.java @@ -76,14 +76,18 @@ public class ExileFromGraveCost extends CostImpl { @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - if (targets.choose(Outcome.Exile, controllerId, sourceId, game)) { - for (UUID targetId: targets.get(0).getTargets()) { - Card card = game.getCard(targetId); - if (card == null || !game.getState().getZone(targetId).equals(Zone.GRAVEYARD)) { - return false; + Player controller = game.getPlayer(controllerId); + if (controller != null) { + if (targets.choose(Outcome.Exile, controllerId, sourceId, game)) { + for (UUID targetId: targets.get(0).getTargets()) { + Card card = game.getCard(targetId); + if (card == null || !game.getState().getZone(targetId).equals(Zone.GRAVEYARD)) { + return false; + } + paid |= controller.moveCardToExileWithInfo(card, null, null, sourceId, game, Zone.GRAVEYARD); } - paid |= card.moveToZone(Zone.EXILED, sourceId, game, false); } + } return paid; } diff --git a/Mage/src/mage/abilities/costs/common/ExileXFromYourGraveCost.java b/Mage/src/mage/abilities/costs/common/ExileXFromYourGraveCost.java new file mode 100644 index 0000000000..7b92635e0d --- /dev/null +++ b/Mage/src/mage/abilities/costs/common/ExileXFromYourGraveCost.java @@ -0,0 +1,86 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.abilities.costs.common; + +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.VariableCostImpl; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author LevelX2 + */ + +public class ExileXFromYourGraveCost extends VariableCostImpl { + + protected FilterCard filter; + + public ExileXFromYourGraveCost(FilterCard filter) { + this(filter, false); + } + + public ExileXFromYourGraveCost(FilterCard filter, boolean additionalCostText) { + super(new StringBuilder(filter.getMessage()).append(" to exile").toString()); + this.filter = filter; + this.text = new StringBuilder(additionalCostText ? "As an additional cost to cast {source}, exile ":"Exile ") + .append(xText).append(" ").append(filter.getMessage()).toString(); + } + + public ExileXFromYourGraveCost(final ExileXFromYourGraveCost cost) { + super(cost); + this.amountPaid = cost.amountPaid; + this.filter = cost.filter; + } + + @Override + public ExileXFromYourGraveCost copy() { + return new ExileXFromYourGraveCost(this); + } + + @Override + public int getMaxValue(Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + return controller.getGraveyard().count(filter, game); + } + return 0; + } + + @Override + public Cost getFixedCostsFromAnnouncedValue(int xValue) { + TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(xValue, filter); + target.setRequired(true); + return new ExileFromGraveCost(target); + } + +} diff --git a/Mage/src/mage/abilities/costs/common/PayVariableLifeCost.java b/Mage/src/mage/abilities/costs/common/PayVariableLifeCost.java index 3c52f111d7..1058cec9f4 100644 --- a/Mage/src/mage/abilities/costs/common/PayVariableLifeCost.java +++ b/Mage/src/mage/abilities/costs/common/PayVariableLifeCost.java @@ -28,11 +28,9 @@ package mage.abilities.costs.common; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.costs.CostImpl; -import mage.abilities.costs.VariableCost; -import mage.filter.FilterMana; +import mage.abilities.costs.Cost; +import mage.abilities.costs.VariableCostImpl; import mage.game.Game; import mage.players.Player; @@ -41,70 +39,20 @@ import mage.players.Player; * @author LevelX2 */ -public class PayVariableLifeCost extends CostImpl implements VariableCost { - - protected int amountPaid = 0; +public class PayVariableLifeCost extends VariableCostImpl { public PayVariableLifeCost() { - this.text = "pay X life"; + this(false); + } + + public PayVariableLifeCost(boolean additionalCostText) { + super("life to pay"); + this.text = new StringBuilder(additionalCostText ? "As an additional cost to cast {source}, pay ":"Pay ") + .append(xText).append(" ").append("life").toString(); } public PayVariableLifeCost(final PayVariableLifeCost cost) { super(cost); - this.amountPaid = cost.amountPaid; - } - - @Override - public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - Player controller = game.getPlayer(controllerId); - return controller != null && controller.getLife() > 0; - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - Player controller = game.getPlayer(controllerId); - if (controller != null) { - this.amountPaid = controller.getAmount(0, controller.getLife(), "Choose X (life to pay)", game); - if (this.amountPaid> 0) { - controller.loseLife(amountPaid, game); - } - game.informPlayers(new StringBuilder(controller.getName()).append(" pays ").append(amountPaid).append(" life.").toString()); - this.paid = true; - } - return paid; - } - - @Override - public void clearPaid() { - paid = false; - amountPaid = 0; - } - - @Override - public int getAmount() { - return amountPaid; - } - - @Override - public void setAmount(int amount) { - amountPaid = amount; - } - - /** - * Not Supported - * @param filter - */ - @Override - public void setFilter(FilterMana filter) { - } - - /** - * Not supported - * @return - */ - @Override - public FilterMana getFilter() { - return new FilterMana(); } @Override @@ -112,4 +60,19 @@ public class PayVariableLifeCost extends CostImpl implement return new PayVariableLifeCost(this); } + @Override + public Cost getFixedCostsFromAnnouncedValue(int xValue) { + return new PayLifeCost(xValue); + } + + @Override + public int getMaxValue(Ability source, Game game) { + int maxValue = 0; + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + maxValue = controller.getLife(); + } + return maxValue; + } + } diff --git a/Mage/src/mage/abilities/costs/common/PayVariableLoyaltyCost.java b/Mage/src/mage/abilities/costs/common/PayVariableLoyaltyCost.java index b16f144530..c74f3cc703 100644 --- a/Mage/src/mage/abilities/costs/common/PayVariableLoyaltyCost.java +++ b/Mage/src/mage/abilities/costs/common/PayVariableLoyaltyCost.java @@ -28,91 +28,46 @@ package mage.abilities.costs.common; -import java.util.UUID; - import mage.abilities.Ability; -import mage.abilities.costs.CostImpl; -import mage.abilities.costs.VariableCost; +import mage.abilities.costs.Cost; +import mage.abilities.costs.VariableCostImpl; import mage.counters.CounterType; -import mage.filter.FilterMana; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.players.Player; /** * * @author BetaSteward_at_googlemail.com */ -public class PayVariableLoyaltyCost extends CostImpl implements VariableCost { - - protected int amountPaid = 0; +public class PayVariableLoyaltyCost extends VariableCostImpl { public PayVariableLoyaltyCost() { + super("loyality counters to remove"); this.text = "-X"; } public PayVariableLoyaltyCost(final PayVariableLoyaltyCost cost) { super(cost); - this.amountPaid = cost.amountPaid; - } - - @Override - public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - Permanent planeswalker = game.getPermanent(sourceId); - return !planeswalker.isLoyaltyUsed(); - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - Permanent planeswalker = game.getPermanent(sourceId); - Player player = game.getPlayer(planeswalker.getControllerId()); - this.amountPaid = player.getAmount(0, planeswalker.getCounters().getCount(CounterType.LOYALTY), "Choose X", game); - if (this.amountPaid> 0) { - planeswalker.getCounters().removeCounter(CounterType.LOYALTY, this.amountPaid); - } else if (this.amountPaid < 0) { - planeswalker.getCounters().addCounter(CounterType.LOYALTY.createInstance(Math.abs(this.amountPaid))); - } - planeswalker.setLoyaltyUsed(true); - this.paid = true; - return paid; - } - - @Override - public void clearPaid() { - paid = false; - amountPaid = 0; - } - - @Override - public int getAmount() { - return amountPaid; - } - - @Override - public void setAmount(int amount) { - amountPaid = amount; - } - - /** - * Not Supported - * @param filter - */ - @Override - public void setFilter(FilterMana filter) { - } - - /** - * Not supported - * @return - */ - @Override - public FilterMana getFilter() { - return new FilterMana(); } @Override public PayVariableLoyaltyCost copy() { return new PayVariableLoyaltyCost(this); } + + @Override + public Cost getFixedCostsFromAnnouncedValue(int xValue) { + return new PayLoyaltyCost(xValue); + } + + @Override + public int getMaxValue(Ability source, Game game) { + int maxValue = 0; + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + maxValue = permanent.getCounters().getCount(CounterType.LOYALTY.getName()); + } + return maxValue; + } } diff --git a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java index 2f2a7d5653..1aa95cd7bf 100644 --- a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java +++ b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java @@ -32,17 +32,18 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.costs.CostImpl; import mage.choices.Choice; import mage.choices.ChoiceImpl; +import mage.constants.Outcome; import mage.counters.Counter; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; +import mage.util.CardUtil; /** * @@ -53,69 +54,96 @@ public class RemoveCounterCost extends CostImpl { private TargetPermanent target; private String name; private CounterType counterTypeToRemove; + private int countersToRemove; public RemoveCounterCost(TargetPermanent target) { this(target, null); } public RemoveCounterCost(TargetPermanent target, CounterType counterTypeToRemove) { + this(target, counterTypeToRemove, 1); + } + + public RemoveCounterCost(TargetPermanent target, CounterType counterTypeToRemove, int countersToRemove) { this.target = target; this.counterTypeToRemove = counterTypeToRemove; - text = setText(); + this.countersToRemove = countersToRemove; + + this.text = setText(); } public RemoveCounterCost(final RemoveCounterCost cost) { super(cost); this.target = cost.target.copy(); this.name = cost.name; + this.countersToRemove = cost.countersToRemove; } @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { paid = false; + int countersRemoved = 0; Player controller = game.getPlayer(controllerId); - if (target.choose(Outcome.UnboostCreature, controllerId, sourceId, game)) { - for (UUID targetId: (List)target.getTargets()) { - Permanent permanent = game.getPermanent(targetId); - if (permanent != null) { - if (permanent.getCounters().size() > 0 && (counterTypeToRemove == null || permanent.getCounters().containsKey(counterTypeToRemove))) { - String counterName = null; - if (counterTypeToRemove != null) { - counterName = counterTypeToRemove.getName(); - } else { - if (permanent.getCounters().size() > 1 && counterTypeToRemove == null) { - Choice choice = new ChoiceImpl(true); - Set choices = new HashSet(); - for (Counter counter : permanent.getCounters().values()) { - if (permanent.getCounters().getCount(counter.getName()) > 0) { - choices.add(counter.getName()); - } - } - choice.setChoices(choices); - choice.setMessage("Choose a counter to remove from " + permanent.getName()); - controller.choose(Outcome.UnboostCreature, choice, game); - counterName = choice.getChoice(); + if (controller != null) { + target.clearChosen(); + if (target.choose(Outcome.UnboostCreature, controllerId, sourceId, game)) { + for (UUID targetId: (List)target.getTargets()) { + Permanent permanent = game.getPermanent(targetId); + if (permanent != null) { + if (permanent.getCounters().size() > 0 && (counterTypeToRemove == null || permanent.getCounters().containsKey(counterTypeToRemove))) { + String counterName = null; + + if (counterTypeToRemove != null) { + counterName = counterTypeToRemove.getName(); } else { - for (Counter counter : permanent.getCounters().values()) { - if (counter.getCount() > 0) { - counterName = counter.getName(); + if (permanent.getCounters().size() > 1 && counterTypeToRemove == null) { + Choice choice = new ChoiceImpl(true); + Set choices = new HashSet<>(); + for (Counter counter : permanent.getCounters().values()) { + if (permanent.getCounters().getCount(counter.getName()) > 0) { + choices.add(counter.getName()); + } + } + choice.setChoices(choices); + choice.setMessage("Choose a counter to remove from " + permanent.getName()); + controller.choose(Outcome.UnboostCreature, choice, game); + counterName = choice.getChoice(); + } else { + for (Counter counter : permanent.getCounters().values()) { + if (counter.getCount() > 0) { + counterName = counter.getName(); + } } } } - } - if (counterName != null) { - permanent.removeCounters(counterName, 1, game); - if (permanent.getCounters().getCount(counterName) == 0 ){ - permanent.getCounters().removeCounter(counterName); + if (counterName != null) { + int countersLeft = countersToRemove - countersRemoved; + int countersOnPermanent = permanent.getCounters().getCount(counterName); + int numberOfCountersSelected = 1; + if (countersLeft > 1 && countersOnPermanent > 1) { + numberOfCountersSelected = controller.getAmount(1, Math.min(countersLeft, countersOnPermanent), + new StringBuilder("Remove how many counters from ").append(permanent.getName()).toString(), game); + } + permanent.removeCounters(counterName, numberOfCountersSelected, game); + if (permanent.getCounters().getCount(counterName) == 0 ){ + permanent.getCounters().removeCounter(counterName); + } + countersRemoved += numberOfCountersSelected; + game.informPlayers(new StringBuilder(controller.getName()) + .append(" removes ").append(numberOfCountersSelected == 1 ? "a":numberOfCountersSelected).append(" ") + .append(counterName).append(numberOfCountersSelected == 1 ? " counter from ":" counters from ") + .append(permanent.getName()).toString()); + if (countersRemoved == countersToRemove) { + this.paid = true; + break; + } } - this.paid = true; - game.informPlayers(new StringBuilder(controller.getName()).append(" removes a ").append(counterName).append(" counter from ").append(permanent.getName()).toString()); } } } } } - target.clearChosen(); + return paid; } @@ -125,11 +153,12 @@ public class RemoveCounterCost extends CostImpl { } private String setText() { - StringBuilder sb = new StringBuilder("Remove a "); + StringBuilder sb = new StringBuilder("Remove "); + sb.append(CardUtil.numberToText(countersToRemove, "a")).append(" "); if (counterTypeToRemove != null) { - sb.append(counterTypeToRemove.getName()).append(" "); + sb.append(counterTypeToRemove.getName()); } - sb.append("counter from a ").append(target.getTargetName()); + sb.append(countersToRemove == 1 ? " counter from ":" counters from ").append(target.getMaxNumberOfTargets() == 1 ? "a ":"").append(target.getTargetName()); return sb.toString(); } diff --git a/Mage/src/mage/abilities/costs/common/RemoveVariableCountersSourceCost.java b/Mage/src/mage/abilities/costs/common/RemoveVariableCountersSourceCost.java index c35a4beacd..21413528c8 100644 --- a/Mage/src/mage/abilities/costs/common/RemoveVariableCountersSourceCost.java +++ b/Mage/src/mage/abilities/costs/common/RemoveVariableCountersSourceCost.java @@ -28,30 +28,27 @@ package mage.abilities.costs.common; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.costs.CostImpl; -import mage.abilities.costs.VariableCost; +import mage.abilities.costs.Cost; +import mage.abilities.costs.VariableCostImpl; import mage.counters.Counter; -import mage.filter.FilterMana; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.players.Player; /** * * @author LevelX2 */ -public class RemoveVariableCountersSourceCost extends CostImpl implements VariableCost { +public class RemoveVariableCountersSourceCost extends VariableCostImpl { - protected int amountPaid = 0; protected int minimalCountersToPay = 0; - private String name; + private String counterName; public RemoveVariableCountersSourceCost(Counter counter, int minimalCountersToPay) { + super(new StringBuilder(counter.getName()).append(" counters to remove").toString()); this.minimalCountersToPay = minimalCountersToPay; - this.name = counter.getName(); - this.text = "Remove X " + name + " counters from {this}"; + this.counterName = counter.getName(); + this.text = new StringBuilder("Remove ").append(xText).append(" ").append(counterName).append(" counters from {this}").toString(); } public RemoveVariableCountersSourceCost(Counter counter) { @@ -60,65 +57,8 @@ public class RemoveVariableCountersSourceCost extends CostImpl= minimalCountersToPay; - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - Permanent permanent = game.getPermanent(sourceId); - if (permanent != null) { - Player player = game.getPlayer(permanent.getControllerId()); - if (player != null) { - this.amountPaid = player.getAmount(minimalCountersToPay, permanent.getCounters().getCount(name), "Choose X counters to remove", game); - if (this.amountPaid >= minimalCountersToPay) { - permanent.removeCounters(name, amountPaid, game); - this.paid = true; - } - game.informPlayers(new StringBuilder(player.getName()).append(" removes ").append(this.amountPaid).append(" ").append(name).append(" counter from ").append(permanent.getName()).toString()); - } - } - return paid; - } - - @Override - public void clearPaid() { - paid = false; - amountPaid = 0; - } - - @Override - public int getAmount() { - return amountPaid; - } - - @Override - public void setAmount(int amount) { - amountPaid = amount; - } - - /** - * Not Supported - * @param filter - */ - @Override - public void setFilter(FilterMana filter) { - } - - /** - * Not supported - * @return - */ - @Override - public FilterMana getFilter() { - return new FilterMana(); + this.counterName = cost.counterName; } @Override @@ -126,5 +66,25 @@ public class RemoveVariableCountersSourceCost extends CostImpl implements VariableCost { +public class RemoveVariableCountersTargetCost extends VariableCostImpl { - protected int amountPaid = 0; - protected TargetPermanent target; - protected String name; + protected FilterPermanent filter; protected CounterType counterTypeToRemove; + protected int minValue; - public RemoveVariableCountersTargetCost(TargetPermanent target) { - this(target, null); + public RemoveVariableCountersTargetCost(FilterPermanent filter) { + this(filter, null); } - public RemoveVariableCountersTargetCost(TargetPermanent target, CounterType counterTypeToRemove) { - this.target = target; + public RemoveVariableCountersTargetCost(FilterPermanent filter, CounterType counterTypeToRemove) { + this(filter, counterTypeToRemove, "X", 0); + } + + public RemoveVariableCountersTargetCost(FilterPermanent filter, CounterType counterTypeToRemove, String xText, int minValue) { + super(xText, new StringBuilder(counterTypeToRemove != null ? counterTypeToRemove.getName() + " ":"").append("counters to remove").toString()); + this.filter = filter; this.counterTypeToRemove = counterTypeToRemove; - text = setText(); + this.text = setText(); + this.minValue = minValue; } public RemoveVariableCountersTargetCost(final RemoveVariableCountersTargetCost cost) { super(cost); - this.target = cost.target.copy(); - this.name = cost.name; + this.filter = cost.filter; + this.minValue = cost.minValue; } @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - paid = false; - Player controller = game.getPlayer(controllerId); - if (target.choose(Outcome.UnboostCreature, controllerId, sourceId, game)) { - for (UUID targetId: (List)target.getTargets()) { - Permanent permanent = game.getPermanent(targetId); - if (permanent != null) { - if (permanent.getCounters().size() > 0 && (counterTypeToRemove == null || permanent.getCounters().containsKey(counterTypeToRemove))) { - String counterName = null; - if (counterTypeToRemove != null) { - counterName = counterTypeToRemove.getName(); - } else { - if (permanent.getCounters().size() > 1 && counterTypeToRemove == null) { - Choice choice = new ChoiceImpl(true); - Set choices = new HashSet(); - for (Counter counter : permanent.getCounters().values()) { - if (permanent.getCounters().getCount(counter.getName()) > 0) { - choices.add(counter.getName()); - } - } - choice.setChoices(choices); - choice.setMessage("Choose a counter to remove from " + permanent.getName()); - controller.choose(Outcome.UnboostCreature, choice, game); - counterName = choice.getChoice(); - } else { - for (Counter counter : permanent.getCounters().values()) { - if (counter.getCount() > 0) { - counterName = counter.getName(); - } - } - } - } - if (counterName != null) { - int countersToRemove = 1; - if (permanent.getCounters().getCount(counterName) > 1) { - countersToRemove = controller.getAmount(1, permanent.getCounters().getCount(counterName),"Remove how many counters from " + permanent.getName(), game); - } - permanent.removeCounters(counterName, countersToRemove, game); - if (permanent.getCounters().getCount(counterName) == 0 ){ - permanent.getCounters().removeCounter(counterName); - } - this.amountPaid += countersToRemove; - this.paid = true; - game.informPlayers(new StringBuilder(controller.getName()).append(" removes ").append(countersToRemove).append(" ").append(counterName).append(" counter from ").append(permanent.getName()).toString()); - } - } - } - } - } - target.clearChosen(); +// paid = false; +// Player controller = game.getPlayer(controllerId); +// if (target.choose(Outcome.UnboostCreature, controllerId, sourceId, game)) { +// for (UUID targetId: (List)target.getTargets()) { +// Permanent permanent = game.getPermanent(targetId); +// if (permanent != null) { +// if (permanent.getCounters().size() > 0 && (counterTypeToRemove == null || permanent.getCounters().containsKey(counterTypeToRemove))) { +// String counterName = null; +// if (counterTypeToRemove != null) { +// counterName = counterTypeToRemove.getName(); +// } else { +// if (permanent.getCounters().size() > 1 && counterTypeToRemove == null) { +// Choice choice = new ChoiceImpl(true); +// Set choices = new HashSet<>(); +// for (Counter counter : permanent.getCounters().values()) { +// if (permanent.getCounters().getCount(counter.getName()) > 0) { +// choices.add(counter.getName()); +// } +// } +// choice.setChoices(choices); +// choice.setMessage("Choose a counter to remove from " + permanent.getName()); +// controller.choose(Outcome.UnboostCreature, choice, game); +// counterName = choice.getChoice(); +// } else { +// for (Counter counter : permanent.getCounters().values()) { +// if (counter.getCount() > 0) { +// counterName = counter.getName(); +// } +// } +// } +// } +// if (counterName != null) { +// int countersToRemove = 1; +// if (permanent.getCounters().getCount(counterName) > 1) { +// countersToRemove = controller.getAmount(1, permanent.getCounters().getCount(counterName),"Remove how many counters from " + permanent.getName(), game); +// } +// permanent.removeCounters(counterName, countersToRemove, game); +// if (permanent.getCounters().getCount(counterName) == 0 ){ +// permanent.getCounters().removeCounter(counterName); +// } +// this.amountPaid += countersToRemove; +// this.paid = true; +// game.informPlayers(new StringBuilder(controller.getName()).append(" removes ").append(countersToRemove).append(" ").append(counterName).append(" counter from ").append(permanent.getName()).toString()); +// } +// } +// } +// } +// } +// target.clearChosen(); return paid; } - @Override - public int getAmount() { - return amountPaid; - } - - @Override - public void setAmount(int amount) { - amountPaid = amount; - } - - /** - * Not Supported - * @param filter - */ - @Override - public void setFilter(FilterMana filter) { - } - - /** - * Not supported - * @return - */ - @Override - public FilterMana getFilter() { - return new FilterMana(); - } - - @Override - public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - return target.canChoose(controllerId, game); - } - private String setText() { - // Remove one or more +1/+1 counters from among creatures you control - StringBuilder sb = new StringBuilder("Remove one or more "); + StringBuilder sb = new StringBuilder("Remove ").append(xText); if (counterTypeToRemove != null) { - sb.append(counterTypeToRemove.getName()).append(" "); + sb.append(" ").append(counterTypeToRemove.getName()); } - sb.append("counter from among ").append(target.getTargetName()); + sb.append(" counters from among ").append(filter.getMessage()); return sb.toString(); } @@ -173,4 +138,25 @@ public class RemoveVariableCountersTargetCost extends CostImpl { + + protected FilterControlledPermanent filter; + + public SacrificeXTargetCost(FilterControlledPermanent filter) { + this(filter, false); + } + + public SacrificeXTargetCost(FilterControlledPermanent filter, boolean additionalCostText) { + super(new StringBuilder(filter.getMessage()).append(" to sacrifice").toString()); + this.text = new StringBuilder(additionalCostText ? "As an additional cost to cast {source}, sacrifice ":"Sacrifice ").append(xText).append(" ").append(filter.getMessage()).toString(); + this.filter = filter; + } + + public SacrificeXTargetCost(final SacrificeXTargetCost cost) { + super(cost); + this.filter = cost.filter; + } + + @Override + public SacrificeXTargetCost copy() { + return new SacrificeXTargetCost(this); + } + + @Override + public int getMaxValue(Ability source, Game game) { + return game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); + } + + @Override + public Cost getFixedCostsFromAnnouncedValue(int xValue) { + TargetControlledPermanent target = new TargetControlledPermanent(xValue, xValue, filter, true); + target.setRequired(true); + return new SacrificeTargetCost(target); + } + +} diff --git a/Mage/src/mage/abilities/costs/common/TapVariableTargetCost.java b/Mage/src/mage/abilities/costs/common/TapVariableTargetCost.java index dc4aaaefec..551839113d 100644 --- a/Mage/src/mage/abilities/costs/common/TapVariableTargetCost.java +++ b/Mage/src/mage/abilities/costs/common/TapVariableTargetCost.java @@ -28,90 +28,35 @@ package mage.abilities.costs.common; -import java.util.Iterator; -import java.util.UUID; -import mage.constants.Outcome; import mage.abilities.Ability; -import mage.abilities.costs.CostImpl; -import mage.abilities.costs.VariableCost; -import mage.filter.FilterMana; +import mage.abilities.costs.Cost; +import mage.abilities.costs.VariableCostImpl; +import mage.filter.common.FilterControlledPermanent; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetControlledPermanent; /** * * @author BetaSteward_at_googlemail.com */ -public class TapVariableTargetCost extends CostImpl implements VariableCost { +public class TapVariableTargetCost extends VariableCostImpl { - protected int amountPaid = 0; - protected TargetControlledPermanent target; + protected FilterControlledPermanent filter; - public TapVariableTargetCost(TargetControlledPermanent target) { - this.target = target; - this.text = "tap X " + target.getTargetName() + " you control"; + public TapVariableTargetCost(FilterControlledPermanent filter) { + this(filter, false, "X"); + } + + public TapVariableTargetCost(FilterControlledPermanent filter, boolean additionalCostText, String xText) { + super(xText, new StringBuilder(filter.getMessage()).append(" to tap").toString()); + this.filter = filter; + this.text = new StringBuilder(additionalCostText ? "As an additional cost to cast {source}, tap ":"Tap ") + .append(this.xText).append(" ").append(filter.getMessage()).toString(); } public TapVariableTargetCost(final TapVariableTargetCost cost) { super(cost); - this.target = cost.target.copy(); - this.amountPaid = cost.amountPaid; - } - - @Override - public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - return target.canChoose(controllerId, game); - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { - amountPaid = 0; - target.clearChosen(); - if (target.canChoose(sourceId, controllerId, game) && target.choose(Outcome.Tap, controllerId, sourceId, game)) { - for (Iterator it = target.getTargets().iterator(); it.hasNext();) { - UUID uuid = (UUID) it.next(); - Permanent permanent = game.getPermanent(uuid); - if (permanent != null && permanent.tap(game)) { - amountPaid++; - } - } - } - paid = true; - return true; - } - - @Override - public void clearPaid() { - paid = false; - amountPaid = 0; - } - - @Override - public int getAmount() { - return amountPaid; - } - - @Override - public void setAmount(int amount) { - amountPaid = amount; - } - - /** - * Not Supported - * @param filter - */ - @Override - public void setFilter(FilterMana filter) { - } - - /** - * Not supported - * @return - */ - @Override - public FilterMana getFilter() { - return new FilterMana(); + this.filter = cost.filter.copy(); } @Override @@ -119,4 +64,14 @@ public class TapVariableTargetCost extends CostImpl imple return new TapVariableTargetCost(this); } + @Override + public int getMaxValue(Ability source, Game game) { + return game.getBattlefield().countAll(filter, source.getControllerId(), game); + } + + @Override + public Cost getFixedCostsFromAnnouncedValue(int xValue) { + return new TapTargetCost(new TargetControlledPermanent(xValue, xValue, filter, true)); + } + } diff --git a/Mage/src/mage/abilities/costs/mana/ManaCosts.java b/Mage/src/mage/abilities/costs/mana/ManaCosts.java index a16c099cb4..49e4989b36 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCosts.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCosts.java @@ -35,6 +35,7 @@ import mage.abilities.costs.VariableCost; /** * * @author BetaSteward_at_googlemail.com + * @param */ public interface ManaCosts extends List, ManaCost { diff --git a/Mage/src/mage/abilities/costs/mana/VariableManaCost.java b/Mage/src/mage/abilities/costs/mana/VariableManaCost.java index 511c54c79b..4d67b6c2b6 100644 --- a/Mage/src/mage/abilities/costs/mana/VariableManaCost.java +++ b/Mage/src/mage/abilities/costs/mana/VariableManaCost.java @@ -30,6 +30,7 @@ package mage.abilities.costs.mana; import mage.Mana; import mage.abilities.Ability; +import mage.abilities.costs.Cost; import mage.abilities.costs.VariableCost; import mage.constants.ColoredManaSymbol; import mage.filter.FilterMana; @@ -113,16 +114,6 @@ public class VariableManaCost extends ManaCostImpl implements return true; } - @Override - public void setFilter(FilterMana filter) { - this.filter = filter; - } - - @Override - public FilterMana getFilter() { - return filter; - } - @Override public VariableManaCost copy() { return new VariableManaCost(this); @@ -152,4 +143,37 @@ public class VariableManaCost extends ManaCostImpl implements public boolean containsColor(ColoredManaSymbol coloredManaSymbol) { return false; } + + @Override + public int announceXValue(Ability source, Game game) { + throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Cost getFixedCostsFromAnnouncedValue(int xValue) { + throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public String getActionText() { + throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public int getMinValue(Ability source, Game game) { + throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public int getMaxValue(Ability source, Game game) { + throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. + } + + public FilterMana getFilter() { + return filter; + } + + public void setFilter(FilterMana filter) { + this.filter = filter; + } } diff --git a/Mage/src/mage/abilities/effects/common/ReturnFromGraveyardToBattlefieldTargetEffect.java b/Mage/src/mage/abilities/effects/common/ReturnFromGraveyardToBattlefieldTargetEffect.java index 03e282bbed..dde5c8dd07 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnFromGraveyardToBattlefieldTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnFromGraveyardToBattlefieldTargetEffect.java @@ -86,6 +86,9 @@ public class ReturnFromGraveyardToBattlefieldTargetEffect extends OneShotEffect< @Override public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } StringBuilder sb = new StringBuilder(); Target target = mode.getTargets().get(0); sb.append("Return "); diff --git a/Mage/src/mage/abilities/effects/common/TransformSourceEffect.java b/Mage/src/mage/abilities/effects/common/TransformSourceEffect.java index 27cbeb4fdd..6728d919c0 100644 --- a/Mage/src/mage/abilities/effects/common/TransformSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/TransformSourceEffect.java @@ -50,7 +50,7 @@ public class TransformSourceEffect extends OneShotEffect this(fromDayToNight, false); } - private TransformSourceEffect(boolean fromDayToNight, boolean withoutTrigger) { + public TransformSourceEffect(boolean fromDayToNight, boolean withoutTrigger) { super(Outcome.Transform); this.withoutTrigger = withoutTrigger; this.fromDayToNight = fromDayToNight; diff --git a/Mage/src/mage/counters/Counter.java b/Mage/src/mage/counters/Counter.java index 098a3dd933..1d8346fb1b 100644 --- a/Mage/src/mage/counters/Counter.java +++ b/Mage/src/mage/counters/Counter.java @@ -33,6 +33,7 @@ import java.io.Serializable; /** * * @author BetaSteward_at_googlemail.com + * @param */ public class Counter> implements Serializable { diff --git a/Mage/src/mage/counters/Counters.java b/Mage/src/mage/counters/Counters.java index 7ce7a28f38..06319dee22 100644 --- a/Mage/src/mage/counters/Counters.java +++ b/Mage/src/mage/counters/Counters.java @@ -53,27 +53,31 @@ public class Counters extends HashMap implements Serializable { } public void addCounter(String name) { - if (!this.containsKey(name)) + if (!this.containsKey(name)) { this.put(name, new Counter(name)); + } this.get(name).add(); } public void addCounter(String name, int amount) { - if (!this.containsKey(name)) + if (!this.containsKey(name)) { this.put(name, new Counter(name)); + } this.get(name).add(amount); } public void addCounter(Counter counter) { - if (!this.containsKey(counter.name)) + if (!this.containsKey(counter.name)) { put(counter.name, counter); - else + } else { get(counter.name).add(counter.getCount()); + } } public void removeCounter(String name) { - if (this.containsKey(name)) + if (this.containsKey(name)) { this.get(name).remove(); + } } public void removeCounter(CounterType counterType, int amount) { @@ -83,13 +87,15 @@ public class Counters extends HashMap implements Serializable { } public void removeCounter(String name, int amount) { - if (this.containsKey(name)) + if (this.containsKey(name)) { this.get(name).remove(amount); + } } public int getCount(String name) { - if (this.containsKey(name)) + if (this.containsKey(name)) { return this.get(name).getCount(); + } return 0; } @@ -98,16 +104,18 @@ public class Counters extends HashMap implements Serializable { } public int getCount(CounterType type) { - if (this.containsKey(type.getName())) + if (this.containsKey(type.getName())) { return this.get(type.getName()).getCount(); + } return 0; } public List getBoostCounters() { - List boosters = new ArrayList(); + List boosters = new ArrayList<>(); for (Counter counter: this.values()) { - if (counter instanceof BoostCounter) + if (counter instanceof BoostCounter) { boosters.add((BoostCounter)counter); + } } return boosters; } diff --git a/Mage/src/mage/game/permanent/token/AngelToken.java b/Mage/src/mage/game/permanent/token/AngelToken.java index 1f5dccf276..410af8e085 100644 --- a/Mage/src/mage/game/permanent/token/AngelToken.java +++ b/Mage/src/mage/game/permanent/token/AngelToken.java @@ -6,13 +6,20 @@ import mage.abilities.keyword.FlyingAbility; import mage.constants.CardType; public class AngelToken extends Token { + public AngelToken() { + this("M14"); + } + + public AngelToken(String tokenImageSetCode) { super("Angel", "4/4 white Angel creature token with flying"); + this.setOriginalExpansionSetCode(tokenImageSetCode); cardType.add(CardType.CREATURE); color = ObjectColor.WHITE; subtype.add("Angel"); power = new MageInt(4); toughness = new MageInt(4); addAbility(FlyingAbility.getInstance()); + } } diff --git a/Mage/src/mage/game/permanent/token/MyrToken.java b/Mage/src/mage/game/permanent/token/MyrToken.java index bef737e5d7..0d26eb7c3b 100644 --- a/Mage/src/mage/game/permanent/token/MyrToken.java +++ b/Mage/src/mage/game/permanent/token/MyrToken.java @@ -4,8 +4,14 @@ import mage.MageInt; import mage.constants.CardType; public class MyrToken extends Token { + public MyrToken() { + this("SOM"); + } + + public MyrToken(String expansionSetCode) { super("Myr", "1/1 colorless Myr artifact creature token"); + this.setOriginalExpansionSetCode(expansionSetCode); cardType.add(CardType.CREATURE); cardType.add(CardType.ARTIFACT); subtype.add("Myr"); diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index d790b06c34..0f1ad4f34b 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -43,6 +43,7 @@ import mage.abilities.Mode; import mage.abilities.Modes; import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbility; +import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.ManaCost; import mage.cards.Card; import mage.cards.Cards; @@ -278,9 +279,12 @@ public interface Player extends MageItem, Copyable { */ boolean putCardsOnBottomOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder); - // set the value for X spells and abilities + // set the value for X mana spells and abilities int announceXMana(int min, int max, String message, Game game, Ability ability); + // set the value for non mana X costs + int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost); + int chooseEffect(List rEffects, Game game); TriggeredAbility chooseTriggeredAbility(List abilities, Game game); Mode chooseMode(Modes modes, Ability source, Game game); diff --git a/Mage/src/mage/target/TargetImpl.java b/Mage/src/mage/target/TargetImpl.java index 7f47edeb51..db8defb6cb 100644 --- a/Mage/src/mage/target/TargetImpl.java +++ b/Mage/src/mage/target/TargetImpl.java @@ -46,8 +46,8 @@ import java.util.*; */ public abstract class TargetImpl> implements Target { - protected Map targets = new LinkedHashMap(); - protected Map zoneChangeCounters = new HashMap(); + protected Map targets = new LinkedHashMap<>(); + protected Map zoneChangeCounters = new HashMap<>(); protected String targetName; protected Zone zone;