diff --git a/Mage.Sets/src/mage/sets/coldsnap/BalduvianRage.java b/Mage.Sets/src/mage/sets/coldsnap/BalduvianRage.java new file mode 100644 index 0000000000..b4a414ed61 --- /dev/null +++ b/Mage.Sets/src/mage/sets/coldsnap/BalduvianRage.java @@ -0,0 +1,73 @@ +/* + * 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.sets.coldsnap; + +import java.util.UUID; + +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.continious.BoostTargetEffect; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; + + +/** + * @author duncancmt + */ +public class BalduvianRage extends CardImpl { + + public BalduvianRage(UUID ownerId) { + super(ownerId, 76, "Balduvian Rage", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{X}{R}"); + this.expansionSetCode = "CSP"; + this.color.setRed(true); + + // Target attacking creature gets +X/+0 until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect(new ManacostVariableValue(), new StaticValue(0), Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Draw a card at the beginning of the next turn's upkeep. + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(1)),false)); + } + + public BalduvianRage(final BalduvianRage card) { + super(card); + } + + @Override + public BalduvianRage copy() { + return new BalduvianRage(this); + } +} diff --git a/Mage.Sets/src/mage/sets/futuresight/VeilstoneAmulet.java b/Mage.Sets/src/mage/sets/futuresight/VeilstoneAmulet.java new file mode 100644 index 0000000000..5861cc4a01 --- /dev/null +++ b/Mage.Sets/src/mage/sets/futuresight/VeilstoneAmulet.java @@ -0,0 +1,114 @@ +/* + * 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.sets.futuresight; + +import java.util.UUID; + +import mage.cards.CardImpl; +import mage.constants.Rarity; +import mage.constants.CardType; + + +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.filter.FilterSpell; +import mage.abilities.effects.ContinuousRuleModifiyingEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.abilities.Ability; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.events.GameEvent.EventType; + +/** + * @author duncancmt + */ +public class VeilstoneAmulet extends CardImpl { + + public VeilstoneAmulet(UUID ownerId) { + super(ownerId, 166, "Veilstone Amulet", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "FUT"; + + // Whenever you cast a spell, creatures you control can't be the targets of spells or abilities your opponents control this turn. + this.addAbility(new SpellCastControllerTriggeredAbility(new VeilstoneAmuletEffect(), + new FilterSpell(), + false, + "Whenever you cast a spell, creatures you control can't be the targets of spells or abilities your opponents control this turn.")); + } + + public VeilstoneAmulet(final VeilstoneAmulet card) { + super(card); + } + + @Override + public VeilstoneAmulet copy() { + return new VeilstoneAmulet(this); + } +} + +// Veilstone Amulet's effect is strange. It effects all creatures you control, +// even if they entered the battlefield after the ability resolved. It modifies +// the rules of the game until end of turn. +class VeilstoneAmuletEffect extends ContinuousRuleModifiyingEffectImpl { + + public VeilstoneAmuletEffect() { + super(Duration.EndOfTurn, Outcome.Benefit); + } + + public VeilstoneAmuletEffect(final VeilstoneAmuletEffect effect) { + super(effect); + } + + @Override + public VeilstoneAmuletEffect copy() { + return new VeilstoneAmuletEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(GameEvent event, Ability ability, Game game) { + if (event.getType() == EventType.TARGET) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null) { + UUID permanentController = permanent.getControllerId(); + UUID abilityController = ability.getControllerId(); + UUID sourceController = event.getPlayerId(); + if (permanent.getCardType().contains(CardType.CREATURE) && + permanentController.equals(abilityController) && + game.getPlayer(abilityController).hasOpponent(sourceController, game)) { + return true; + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/guildpact/InkTreaderNephilim.java b/Mage.Sets/src/mage/sets/guildpact/InkTreaderNephilim.java new file mode 100644 index 0000000000..6751f1ddc9 --- /dev/null +++ b/Mage.Sets/src/mage/sets/guildpact/InkTreaderNephilim.java @@ -0,0 +1,241 @@ +/* + * 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.sets.guildpact; + +import java.util.Collection; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.UUID; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.MageInt; +import mage.cards.CardImpl; + +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import java.util.Map; +import java.util.List; +import mage.target.Target; +import mage.abilities.Modes; +import mage.filter.predicate.mageobject.FromSetPredicate; +import mage.target.TargetPermanent; +import mage.util.SpellTargetAddress; + +/** + * @author duncancmt + */ +public class InkTreaderNephilim extends CardImpl { + + public InkTreaderNephilim(UUID ownerId) { + super(ownerId, 117, "Ink-Treader Nephilim", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{R}{G}{W}{U}"); + this.expansionSetCode = "GPT"; + this.subtype.add("Nephilim"); + this.color.setRed(true); + this.color.setGreen(true); + this.color.setWhite(true); + this.color.setBlue(true); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever a player casts an instant or sorcery spell, if that spell targets only Ink-Treader Nephilim, copy the spell for each other creature that spell could target. Each copy targets a different one of those creatures. + this.addAbility(new InkTreaderNephilimTriggeredAbility()); + } + + public InkTreaderNephilim(final InkTreaderNephilim card) { + super(card); + } + + @Override + public InkTreaderNephilim copy() { + return new InkTreaderNephilim(this); + } +} + +class InkTreaderNephilimTriggeredAbility extends TriggeredAbilityImpl { + + private static final FilterSpell filter = new FilterSpell(); + + static { + filter.add(Predicates.or( + new CardTypePredicate(CardType.INSTANT), + new CardTypePredicate(CardType.SORCERY))); + } + + InkTreaderNephilimTriggeredAbility() { + super(Zone.BATTLEFIELD, new InkTreaderNephilimEffect(), false); + } + + InkTreaderNephilimTriggeredAbility(final InkTreaderNephilimTriggeredAbility ability) { + super(ability); + } + + @Override + public InkTreaderNephilimTriggeredAbility copy() { + return new InkTreaderNephilimTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.SPELL_CAST) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null && + spell.getCardType().contains(CardType.INSTANT) || spell.getCardType().contains(CardType.SORCERY)){ + for (Effect effect : getEffects()) { + effect.setValue("TriggeringSpell", spell); + } + return true; + } + } + return false; + } + + @Override + public boolean checkInterveningIfClause(Game game) { + Spell spell = (Spell) getEffects().get(0).getValue("TriggeringSpell"); + if (spell != null) { + boolean allTargetsInkTreaderNephilim = true; + boolean atLeastOneTargetsInkTreaderNephilim = false; + for (SpellAbility sa: spell.getSpellAbilities()){ + Modes modes = sa.getModes(); + for (UUID mode : modes.getSelectedModes()) { + for (Target targetInstance : modes.get(mode).getTargets()) { + for (UUID target : targetInstance.getTargets()) { + allTargetsInkTreaderNephilim &= target.equals(sourceId); + atLeastOneTargetsInkTreaderNephilim |= target.equals(sourceId); + } + } + } + } + if (allTargetsInkTreaderNephilim && atLeastOneTargetsInkTreaderNephilim) { + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever a player casts an instant or sorcery spell, if that spell targets only Ink-Treader Nephilim, copy the spell for each other creature that spell could target. Each copy targets a different one of those creatures."; + } +} + +class InkTreaderNephilimEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent(); + + static { + filter.add(new CardTypePredicate(CardType.CREATURE)); + } + + public InkTreaderNephilimEffect() { + super(Outcome.Copy); + } + + public InkTreaderNephilimEffect(final InkTreaderNephilimEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = (Spell) getValue("TriggeringSpell"); + if (spell != null) { + Map targetable = new HashMap<>(); + UUID controller = source.getControllerId(); + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, controller, source.getSourceId(), game)) { + Spell copy = spell.copySpell(); + copy.setControllerId(controller); + copy.setCopiedSpell(true); + if (permanent.getId().equals(source.getSourceId())) { + continue; // copy only for other creatures + } + boolean legal = true; + for (SpellTargetAddress addr : SpellTargetAddress.walk(copy)) { + Target targetInstance = addr.getTarget(copy); + legal &= targetInstance.canTarget(permanent.getId(), addr.getSpellAbility(copy), game); + } + if (legal) { + for (SpellTargetAddress addr : SpellTargetAddress.walk(copy)) { + Target targetInstance = addr.getTarget(copy); + int numTargets = targetInstance.getNumberOfTargets(); + targetInstance.clearChosen(); + while (numTargets > 0) { + targetInstance.add(permanent.getId(), game); + numTargets--; + } + } + targetable.put(permanent.getId(), copy); + } + } + while (targetable.size() > 0) { + FilterPermanent filter = new FilterPermanent("creature that spell could target ("+Integer.toString(targetable.size())+" remaining)"); + filter.add(new FromSetPredicate(targetable.keySet())); + TargetPermanent target = new TargetPermanent(0, 1, filter, true); + if (target.possibleTargets(controller, game).size() > 1 + && target.canChoose(source.getSourceId(), controller, game)) { + game.getPlayer(controller).choose(Outcome.Neutral, target, source.getId(), game); + } + Collection chosen = target.getTargets(); + if (chosen.size() == 0) { + chosen = targetable.keySet(); + } + List toDelete = new ArrayList<>(); + for (UUID chosenId : chosen) { + Spell chosenCopy = targetable.get(chosenId); + if (chosenCopy != null) { + game.getStack().push(chosenCopy); + toDelete.add(chosenId); + } + } + for (UUID id : toDelete) { + targetable.remove(id); + } + } + return true; + } + return false; + } + + @Override + public InkTreaderNephilimEffect copy() { + return new InkTreaderNephilimEffect(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/ravnika/BreathOfFury.java b/Mage.Sets/src/mage/sets/ravnika/BreathOfFury.java new file mode 100644 index 0000000000..5d493b9e82 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ravnika/BreathOfFury.java @@ -0,0 +1,209 @@ +/* + * 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.sets.ravnika; + +import java.util.UUID; + +import mage.cards.CardImpl; +import mage.constants.Rarity; +import mage.constants.CardType; + +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.abilities.effects.common.AttachEffect; +import mage.constants.Outcome; +import mage.abilities.Ability; +import mage.abilities.keyword.EnchantAbility; + +import mage.abilities.TriggeredAbilityImpl; +import mage.constants.Zone; +import mage.game.events.GameEvent; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.abilities.effects.OneShotEffect; +import mage.game.permanent.Permanent; +import mage.abilities.effects.Effect; +import mage.target.targetpointer.FixedTarget; +import mage.players.Player; +import mage.target.Target; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.game.turn.TurnMod; +import mage.constants.TurnPhase; +import mage.MageObject; +import mage.filter.predicate.Predicate; + +/** + * @author duncancmt + */ +public class BreathOfFury extends CardImpl { + public BreathOfFury(UUID ownerId) { + super(ownerId, 116, "Breath of Fury", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}{R}"); + this.expansionSetCode = "RAV"; + this.subtype.add("Aura"); + + this.color.setRed(true); + + // Enchant creature you control + TargetPermanent auraTarget = new TargetControlledCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Benefit)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // When enchanted creature deals combat damage to a player, sacrifice it and attach Breath of Fury to a creature you control. If you do, untap all creatures you control and after this phase, there is an additional combat phase. + this.addAbility(new BreathOfFuryAbility()); + } + + public BreathOfFury(final BreathOfFury card) { + super(card); + } + + @Override + public BreathOfFury copy() { + return new BreathOfFury(this); + } +} + +class BreathOfFuryAbility extends TriggeredAbilityImpl { + + public BreathOfFuryAbility() { + super(Zone.BATTLEFIELD, new BreathOfFuryEffect()); + } + + public BreathOfFuryAbility(final BreathOfFuryAbility ability) { + super(ability); + } + + @Override + public BreathOfFuryAbility copy() { + return new BreathOfFuryAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event instanceof DamagedPlayerEvent) { + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent)event; + Permanent p = game.getPermanent(event.getSourceId()); + if (damageEvent.isCombatDamage() && p != null && p.getAttachments().contains(this.getSourceId())) { + for (Effect effect : getEffects()) { + effect.setTargetPointer(new FixedTarget(p.getId())); + } + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "When enchanted creature deals combat damage to a player, sacrifice it and attach Breath of Fury to a creature you control. If you do, untap all creatures you control and after this phase, there is an additional combat phase."; + } +} + +class BreathOfFuryEffect extends OneShotEffect { + + public BreathOfFuryEffect() { + super(Outcome.Benefit); + staticText = "Sacrifice enchanted creature and attach Breath of Fury to a creature you control. If you do, untap all creatures you control and after this phase, there is an additional combat phase."; + } + + public BreathOfFuryEffect(final BreathOfFuryEffect effect) { + super(effect); + } + + @Override + public BreathOfFuryEffect copy() { + return new BreathOfFuryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source){ + Permanent enchantedCreature = game.getPermanent(targetPointer.getFirst(game, source)); + Permanent enchantment = game.getPermanent(source.getSourceId()); + Player controller = game.getPlayer(source.getControllerId()); + Target target = new TargetControlledCreaturePermanent(new FilterCanBeEnchantedControlledCreaturePermanent(enchantment)); + target.setNotTarget(true); + if (enchantedCreature != null && + enchantedCreature.sacrifice(source.getSourceId(), game) && + enchantment != null && + controller != null && + target.canChoose(source.getSourceId(), source.getControllerId(), game)) { + controller.choose(outcome, target, source.getId(), game); + Permanent newCreature = game.getPermanent(target.getFirstTarget()); + if (newCreature != null && + newCreature.addAttachment(enchantment.getId(), game)) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterControlledCreaturePermanent(), + controller.getId(), game)) { + permanent.untap(game); + } + game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), TurnPhase.COMBAT, null, false)); + return true; + } + } + return false; + } +} + + + +class FilterCanBeEnchantedControlledCreaturePermanent extends FilterControlledCreaturePermanent { + + public FilterCanBeEnchantedControlledCreaturePermanent(final FilterCanBeEnchantedControlledCreaturePermanent filter) { + super(filter); + } + + public FilterCanBeEnchantedControlledCreaturePermanent(MageObject auraEnchantment) { + super("creature you control that could be enchanted by " + auraEnchantment.getName()); + this.add(new CanBeEnchantedPredicate(auraEnchantment)); + } + + @Override + public FilterCanBeEnchantedControlledCreaturePermanent copy() { + return new FilterCanBeEnchantedControlledCreaturePermanent(this); + } +} + + +class CanBeEnchantedPredicate implements Predicate { + + private final MageObject auraEnchantment; + + public CanBeEnchantedPredicate(MageObject auraEnchantment){ + this.auraEnchantment = auraEnchantment; + } + + @Override + public boolean apply(Permanent input, Game game) { + return !input.cantBeEnchantedBy(auraEnchantment, game); + } + + @Override + public String toString() { + return "CanBeEnchanted(" + auraEnchantment.toString() + ")"; + } +} diff --git a/Mage.Sets/src/mage/sets/ravnika/RallyTheRighteous.java b/Mage.Sets/src/mage/sets/ravnika/RallyTheRighteous.java new file mode 100644 index 0000000000..b0c148e0f2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ravnika/RallyTheRighteous.java @@ -0,0 +1,143 @@ +/* + * 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.sets.ravnika; + +import java.util.UUID; + +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Outcome; +import mage.cards.CardImpl; +import mage.filter.common.FilterCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.SubLayer; +import mage.game.permanent.Permanent; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.game.Game; + +/** + * @author duncancmt + */ + +public class RallyTheRighteous extends CardImpl { + + public RallyTheRighteous(UUID ownerId) { + super(ownerId, 222, "Rally the Righteous", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{R}{W}"); + this.expansionSetCode = "RAV"; + this.color.setRed(true); + this.color.setWhite(true); + + // Radiance — Untap target creature and each other creature that shares a color with it. Those creatures get +2/+0 until end of turn. + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new RallyTheRighteousUntapEffect()); + this.getSpellAbility().addEffect(new RallyTheRighteousBoostEffect()); + } + + public RallyTheRighteous(final RallyTheRighteous card) { + super(card); + } + + @Override + public RallyTheRighteous copy() { + return new RallyTheRighteous(this); + } +} + + +class RallyTheRighteousUntapEffect extends OneShotEffect { + + public RallyTheRighteousUntapEffect() { + super(Outcome.Untap); + staticText = "Radiance — Untap target creature and each other creature that shares a color with it."; + } + + public RallyTheRighteousUntapEffect(final RallyTheRighteousUntapEffect effect) { + super(effect); + } + + @Override + public RallyTheRighteousUntapEffect copy() { + return new RallyTheRighteousUntapEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent target = game.getPermanent(targetPointer.getFirst(game, source)); + if (target != null) { + ObjectColor color = target.getColor(); + target.untap(game); + for (Permanent p : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), game)) { + if (p.getColor().shares(color) && !p.getId().equals(target.getId())) { + p.untap(game); + } + } + return true; + } + return false; + } +} + + +class RallyTheRighteousBoostEffect extends ContinuousEffectImpl { + + public RallyTheRighteousBoostEffect() { + super(Duration.EndOfTurn, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); + staticText = "Radiance — Target creature and each other creature that shares a color with it get +2/+0 until end of turn."; + } + + public RallyTheRighteousBoostEffect(final RallyTheRighteousBoostEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent target = game.getPermanent(targetPointer.getFirst(game, source)); + if (target != null) { + ObjectColor color = target.getColor(); + target.addPower(2); + for (Permanent p : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), game)) { + if (p.getColor().shares(color) && !p.getId().equals(target.getId())) { + p.addPower(2); + } + } + return true; + } + return false; + } + + @Override + public RallyTheRighteousBoostEffect copy() { + return new RallyTheRighteousBoostEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/PrecursorGolem.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/PrecursorGolem.java index 55da525676..73e543efb2 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/PrecursorGolem.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/PrecursorGolem.java @@ -27,6 +27,11 @@ */ package mage.sets.scarsofmirrodin; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.SpellAbility; @@ -53,6 +58,9 @@ import mage.target.Target; import mage.target.targetpointer.FixedTarget; import java.util.UUID; +import mage.filter.predicate.mageobject.FromSetPredicate; +import mage.target.TargetPermanent; +import mage.util.SpellTargetAddress; /** * @author nantuko @@ -122,27 +130,25 @@ class PrecursorGolemCopyTriggeredAbility extends TriggeredAbilityImpl { if (spell != null && (spell.getCardType().contains(CardType.INSTANT) || spell.getCardType().contains(CardType.SORCERY))) { UUID targetGolem = null; - SpellAbility sa = spell.getSpellAbility(); - for (Effect effect : sa.getEffects()) { - for (UUID target : effect.getTargetPointer().getTargets(game, sa)) { + for (SpellTargetAddress addr : SpellTargetAddress.walk(spell)) { + Target targetInstance = addr.getTarget(spell); + for (UUID target : targetInstance.getTargets()) { Permanent permanent = game.getPermanent(target); - if (permanent != null) { - if (!permanent.hasSubtype("Golem")) { + if (permanent == null || !permanent.hasSubtype("Golem")) { + return false; + } + if (targetGolem == null) { + targetGolem = target; + } else { + // If a spell has multiple targets, but it's targeting the same Golem with all of them, Precursor Golem's last ability will trigger + if (!targetGolem.equals(target)) { return false; } - if (targetGolem == null) { - targetGolem = target; - } else { - // If a spell has multiple targets, but it's targeting the same Golem with all of them, Precursor Golem's last ability will trigger - if (!targetGolem.equals(target)) { - return false; - } - } } } } if (targetGolem != null) { - getEffects().get(0).setTargetPointer(new FixedTarget(spell.getId())); + getEffects().get(0).setValue("triggeringSpell", spell); getEffects().get(0).setValue("targetedGolem", targetGolem); return true; } @@ -174,16 +180,17 @@ class PrecursorGolemCopySpellEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); + Spell spell = (Spell) getValue("triggeringSpell"); if (spell != null) { - SpellAbility sa = spell.getSpellAbility(); UUID targetedGolem = (UUID) getValue("targetedGolem"); + Map targetable = new HashMap<>(); for (Permanent permanent : game.getBattlefield().getActivePermanents(filterGolem, source.getControllerId(), source.getSourceId(), game)) { if (permanent.getId().equals(targetedGolem)) { continue; // copy only for other golems } boolean legal = true; - for (Target target : sa.getTargets()) { + for (SpellTargetAddress addr : SpellTargetAddress.walk(spell)) { + Target target = addr.getTarget(spell); if (!target.canTarget(permanent.getId(), game)) { legal = false; break; @@ -191,16 +198,40 @@ class PrecursorGolemCopySpellEffect extends OneShotEffect { } if (legal) { Spell copy = spell.copySpell(); - copy.setControllerId(spell.getControllerId()); copy.setCopiedSpell(true); - for (Effect effect : copy.getSpellAbility().getEffects()) { - effect.setTargetPointer(new FixedTarget(permanent.getId())); - } - for (Target target : copy.getSpellAbility().getTargets()) { + for (SpellTargetAddress addr : SpellTargetAddress.walk(copy)) { + Target target = addr.getTarget(copy); target.clearChosen(); target.add(permanent.getId(), game); } - game.getStack().push(copy); + targetable.put(permanent.getId(), copy); + } + } + UUID spellController = spell.getControllerId(); + while (targetable.size() > 0) { + FilterPermanent filter = new FilterPermanent("Golem", + "Golem that spell could target ("+Integer.toString(targetable.size())+" remaining)"); + filter.add(new FromSetPredicate(targetable.keySet())); + TargetPermanent target = new TargetPermanent(0, 1, filter, true); + + if (target.possibleTargets(spellController, game).size() > 1 + && target.canChoose(spell.getSourceId(), spellController, game)) { + game.getPlayer(spellController).choose(Outcome.Neutral, target, source.getId(), game); + } + Collection chosen = target.getTargets(); + if (chosen.size() == 0) { + chosen = targetable.keySet(); + } + List toDelete = new ArrayList<>(); + for (UUID chosenId : chosen) { + Spell chosenCopy = targetable.get(chosenId); + if (chosenCopy != null) { + game.getStack().push(chosenCopy); + toDelete.add(chosenId); + } + } + for (UUID id : toDelete) { + targetable.remove(id); } } return true; diff --git a/Mage.Sets/src/mage/sets/shadowmoor/MercyKilling.java b/Mage.Sets/src/mage/sets/shadowmoor/MercyKilling.java new file mode 100644 index 0000000000..5d31d3636c --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/MercyKilling.java @@ -0,0 +1,114 @@ +/* + * 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.sets.shadowmoor; + +import java.util.UUID; + +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.cards.CardImpl; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Zone; +import mage.game.Game; +import mage.abilities.Ability; +import mage.game.permanent.Permanent; +import mage.constants.Outcome; +import mage.game.permanent.token.Token; +import mage.MageInt; + +/** + * @author duncancmt + */ +public class MercyKilling extends CardImpl { + + public MercyKilling(UUID ownerId) { + super(ownerId, 231, "Mercy Killing", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{G/W}"); + this.expansionSetCode = "SHM"; + this.color.setGreen(true); + this.color.setWhite(true); + + // Target creature's controller sacrifices it, then puts X 1/1 green and white Elf Warrior creature tokens onto the battlefield, where X is that creature's power. + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new SacrificeTargetEffect()); + this.getSpellAbility().addEffect(new MercyKillingTokenEffect()); + } + + public MercyKilling(final MercyKilling card) { + super(card); + } + + @Override + public MercyKilling copy() { + return new MercyKilling(this); + } +} + +class MercyKillingTokenEffect extends OneShotEffect { + + public MercyKillingTokenEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "Its controller puts X 1/1 green and white Elf Warrior creature tokens onto the battlefield, where X is that creature's power."; + } + + public MercyKillingTokenEffect(final MercyKillingTokenEffect effect) { + super(effect); + } + + @Override + public MercyKillingTokenEffect copy() { + return new MercyKillingTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = (Permanent) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD); + if (permanent != null) { + int power = permanent.getPower().getValue(); + MercyKillingToken token = new MercyKillingToken(); + token.putOntoBattlefield(power, game, source.getSourceId(), permanent.getControllerId()); + } + return true; + } + +} + +class MercyKillingToken extends Token { + + public MercyKillingToken() { + super("Elf Warrior", "1/1 green and white Elf Warrior creature token"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + color.setWhite(true); + subtype.add("Elf"); + subtype.add("Warrior"); + power = new MageInt(1); + toughness = new MageInt(1); + } +} diff --git a/Mage/src/mage/filter/predicate/mageobject/FromSetPredicate.java b/Mage/src/mage/filter/predicate/mageobject/FromSetPredicate.java new file mode 100644 index 0000000000..0c0822e3d2 --- /dev/null +++ b/Mage/src/mage/filter/predicate/mageobject/FromSetPredicate.java @@ -0,0 +1,50 @@ +/* + * 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.filter.predicate.mageobject; + +import java.util.Set; +import java.util.UUID; +import mage.MageObject; +import mage.filter.predicate.ObjectPlayer; +import mage.filter.predicate.ObjectPlayerPredicate; +import mage.game.Game; + +/** + * @author duncancmt + */ +public class FromSetPredicate> implements ObjectPlayerPredicate { + protected Set set; + + public FromSetPredicate(Set set) { + this.set = set; + } + + public boolean apply(T input, Game game) { + return set.contains(input.getObject().getId()); + } +} diff --git a/Mage/src/mage/util/SpellTargetAddress.java b/Mage/src/mage/util/SpellTargetAddress.java new file mode 100644 index 0000000000..9220afab77 --- /dev/null +++ b/Mage/src/mage/util/SpellTargetAddress.java @@ -0,0 +1,155 @@ +/* + * 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.util; + +import java.util.Iterator; +import java.util.UUID; +import mage.abilities.Mode; +import mage.abilities.Modes; +import mage.abilities.SpellAbility; +import mage.game.stack.Spell; +import mage.target.Target; + + +/** + * @author duncancmt + */ +public class SpellTargetAddress { + protected int spellAbilityIndex; + protected UUID mode; + protected int targetIndex; + + public SpellTargetAddress(int spellAbilityIndex, UUID mode, int targetIndex) { + this.spellAbilityIndex = spellAbilityIndex; + this.mode = mode; + this.targetIndex = targetIndex; + } + + protected static class SpellTargetAddressIterable implements Iterable { + protected final Spell spell; + + public SpellTargetAddressIterable(Spell spell) { + this.spell = spell; + } + + public Iterator iterator() { + return new SpellTargetAddressIterator(spell); + } + } + + protected static class SpellTargetAddressIterator implements Iterator { + protected Iterator spellAbilityIterator; + protected Integer lastSpellAbilityIndex = null; + protected Iterator modeIterator = null; + protected Modes modes = null; + protected UUID lastMode = null; + protected Iterator targetIterator = null; + protected Integer lastTargetIndex = null; + + public SpellTargetAddressIterator(Spell spell) { + this.spellAbilityIterator = spell.getSpellAbilities().iterator(); + calcNext(); + } + + public boolean hasNext() { + return lastTargetIndex != null; + } + + public SpellTargetAddress next() { + SpellTargetAddress ret = new SpellTargetAddress(lastSpellAbilityIndex, + lastMode, + lastTargetIndex); + calcNext(); + return ret; + + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + protected void calcNext() { + if (targetIterator == null) { + if (modeIterator == null) { + if (spellAbilityIterator.hasNext()) { + if (lastSpellAbilityIndex == null) { + lastSpellAbilityIndex = 0; + } else { + lastSpellAbilityIndex++; + } + modes = spellAbilityIterator.next().getModes(); + modeIterator = modes.getSelectedModes().iterator(); + } else { + lastSpellAbilityIndex = null; + return; + } + } + + if (modeIterator != null && modeIterator.hasNext()) { + lastMode = modeIterator.next(); + targetIterator = modes.get(lastMode).getTargets().iterator(); + } else { + lastMode = null; + modes = null; + modeIterator = null; + calcNext(); + } + } + + if (targetIterator != null && targetIterator.hasNext()) { + if (lastTargetIndex == null) { + lastTargetIndex = 0; + } else { + lastTargetIndex++; + } + targetIterator.next(); + } else { + targetIterator = null; + lastTargetIndex = null; + calcNext(); + } + } + } + + + public static Iterable walk(Spell spell) { + return new SpellTargetAddressIterable(spell); + } + + public Target getTarget(Spell spell) { + return spell.getSpellAbilities().get(spellAbilityIndex).getModes().get(mode).getTargets().get(targetIndex); + } + + public Mode getMode(Spell spell) { + return spell.getSpellAbilities().get(spellAbilityIndex).getModes().get(mode); + } + + public SpellAbility getSpellAbility(Spell spell) { + return spell.getSpellAbilities().get(spellAbilityIndex); + } +}