diff --git a/Mage.Common/pom.xml b/Mage.Common/pom.xml index feb4b9b013..c786973c6d 100644 --- a/Mage.Common/pom.xml +++ b/Mage.Common/pom.xml @@ -67,8 +67,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/Mage.Sets/src/mage/cards/s/Spellskite.java b/Mage.Sets/src/mage/cards/s/Spellskite.java index 58ae3c060a..08340c9b3a 100644 --- a/Mage.Sets/src/mage/cards/s/Spellskite.java +++ b/Mage.Sets/src/mage/cards/s/Spellskite.java @@ -29,15 +29,25 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.ChangeATargetOfTargetSpellAbilityToSourceEffect; +import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.Zone; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.game.stack.StackAbility; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.Target; import mage.target.TargetStackObject; +import mage.target.Targets; /** * @@ -46,13 +56,13 @@ import mage.target.TargetStackObject; public class Spellskite extends CardImpl { public Spellskite(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); this.subtype.add("Horror"); this.power = new MageInt(0); this.toughness = new MageInt(4); // {UP}: Change a target of target spell or ability to Spellskite. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ChangeATargetOfTargetSpellAbilityToSourceEffect(), new ManaCostsImpl("{UP}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SpellskiteEffect(), new ManaCostsImpl("{UP}")); ability.addTarget(new TargetStackObject()); this.addAbility(ability); } @@ -66,3 +76,114 @@ public class Spellskite extends CardImpl { return new Spellskite(this); } } + +class SpellskiteEffect extends OneShotEffect { + + public SpellskiteEffect() { + super(Outcome.Neutral); + staticText = "Change a target of target spell or ability to {this}"; + } + + public SpellskiteEffect(final SpellskiteEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (stackObject != null && sourceObject != null) { + Targets targets = new Targets(); + Ability sourceAbility; + String oldTargetName = null; + if (stackObject instanceof Spell) { + Spell spell = (Spell) stackObject; + sourceAbility = spell.getSpellAbility(); + } else if (stackObject instanceof StackAbility) { + StackAbility stackAbility = (StackAbility) stackObject; + sourceAbility = stackAbility; + } else { + return false; + } + for (UUID modeId : sourceAbility.getModes().getSelectedModes()) { + Mode mode = sourceAbility.getModes().get(modeId); + targets.addAll(mode.getTargets()); + } + + boolean twoTimesTarget = false; + if (targets.size() == 1 && targets.get(0).getTargets().size() == 1) { + Target target = targets.get(0); + if (target.getFirstTarget().equals(source.getSourceId())) { + return true; // Target was already the same source, so no change / target event to create + } + if (target.canTarget(stackObject.getControllerId(), source.getSourceId(), sourceAbility, game)) { + oldTargetName = getTargetName(targets.getFirstTarget(), game); + target.clearChosen(); + // The source is still the spell on the stack + target.addTarget(source.getSourceId(), stackObject.getStackAbility(), game); + } + } else { //needed for targeted source's with multiple targets + Player controller = game.getPlayer(source.getControllerId()); + boolean validTargets = false; + do { + for (Target target : targets) { + for (UUID targetId : target.getTargets()) { + String name = getTargetName(targetId, game); + if (!targetId.equals(source.getSourceId()) && target.getTargets().contains(source.getSourceId())) { + // you can't change this target to source because the source is already another targetId of that target. + twoTimesTarget = true; + continue; + } + if (target.canTarget(stackObject.getControllerId(), source.getSourceId(), sourceAbility, game)) { + validTargets = true; + if (name != null + && controller.chooseUse(Outcome.Neutral, "Change target from " + name + " to " + sourceObject.getLogName() + '?', source, game)) { + oldTargetName = getTargetName(targetId, game); + int damageAmount = target.getTargetAmount(targetId); + target.remove(targetId); + // The source is still the spell on the stack + // add the source permanent id and set the damage if any + target.addTarget(source.getSourceId(), stackObject.getStackAbility(), game); + target.setTargetAmount(source.getSourceId(), damageAmount, game); + break; + } + } + } + if (oldTargetName != null) { + break; + } + } + if (oldTargetName == null) { + game.informPlayer(controller, "You have to select at least one target to change to " + sourceObject.getIdName() + '!'); + } + } while (validTargets && oldTargetName == null); + } + if (oldTargetName != null) { + game.informPlayers(sourceObject.getLogName() + ": Changed target of " + stackObject.getLogName() + " from " + oldTargetName + " to " + sourceObject.getLogName()); + } else if (twoTimesTarget) { + game.informPlayers(sourceObject.getLogName() + ": Target not changed to " + sourceObject.getLogName() + " because its not valid to target it twice for " + stackObject.getLogName()); + } else { + game.informPlayers(sourceObject.getLogName() + ": Target not changed to " + sourceObject.getLogName() + " because its no valid target for " + stackObject.getLogName()); + } + return true; + } + return false; + } + + @Override + public SpellskiteEffect copy() { + return new SpellskiteEffect(this); + } + + private String getTargetName(UUID objectId, Game game) { + MageObject object = game.getObject(objectId); + if (object != null) { + return object.getLogName(); + } + Player player = game.getPlayer(objectId); + if (player != null) { + return player.getLogName(); + } + return null; + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/AuratouchedMageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/AuratouchedMageTest.java index 6c69e73967..70259f1138 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/AuratouchedMageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/AuratouchedMageTest.java @@ -29,7 +29,6 @@ package org.mage.test.cards.abilities.other; import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -47,6 +46,8 @@ public class AuratouchedMageTest extends CardTestPlayerBase { * card and put it into your hand. Then shuffle your library. * */ + /* + @Ignore //If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice. @Test public void testAuratouchedMageEffectHasMadeIntoTypeArtifact() { //Any Aura card you find must be able to enchant Auratouched Mage as it currently exists, or as it most recently existed on the battlefield if it’s no @@ -69,7 +70,7 @@ public class AuratouchedMageTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Relic Ward", 1); } - + */ @Test public void testGainsLegalAura() { // Expected result: Brainwash gets placed on Auratouched Mage @@ -87,7 +88,7 @@ public class AuratouchedMageTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Brainwash", 1); } - + /* @Ignore //If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice. @Test @@ -112,6 +113,5 @@ public class AuratouchedMageTest extends CardTestPlayerBase { assertLibraryCount(playerA, "Animate Wall", 1); } -*/ - + */ } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeSourceEffect.java index 35ee7f03fd..83ac0c1e94 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeSourceEffect.java @@ -31,12 +31,7 @@ import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.ContinuousEffectImpl; -import mage.constants.CardType; -import mage.constants.DependencyType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; @@ -63,19 +58,13 @@ public class AddCardTypeSourceEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { super.init(source, game); - if (this.duration == Duration.Custom || this.duration.toString().startsWith("End")) { - affectedObjectList.add(new MageObjectReference(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()), game)); - if (affectedObjectList.isEmpty()) { - this.discard(); - } - } + affectedObjectList.add(new MageObjectReference(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()), game)); } @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null - && (affectedObjectList.isEmpty() || affectedObjectList.contains(new MageObjectReference(permanent, game)))) { + if (permanent != null && affectedObjectList.contains(new MageObjectReference(permanent, game))) { if (!permanent.getCardType().contains(addedCardType)) { permanent.getCardType().add(addedCardType); } @@ -97,7 +86,7 @@ public class AddCardTypeSourceEffect extends ContinuousEffectImpl { return staticText; } StringBuilder sb = new StringBuilder(); - sb.append("{this} becomes ").append(addedCardType.toString()).append(" in addition to its other types until end of turn"); + sb.append("{this} becomes ").append(addedCardType.toString()).append(" in addition to its other types " + this.getDuration().toString()); return sb.toString(); } } diff --git a/Mage/src/main/java/mage/target/Target.java b/Mage/src/main/java/mage/target/Target.java index 307c5a46d6..647c6281e7 100644 --- a/Mage/src/main/java/mage/target/Target.java +++ b/Mage/src/main/java/mage/target/Target.java @@ -53,7 +53,7 @@ public interface Target extends Serializable { boolean isNotTarget(); /** - * controlls if it will be checked, if the target can be targeted from + * controls if it will be checked, if the target can be targeted from * source * * @param notTarget true = do not check for protection, false = check for @@ -157,4 +157,8 @@ public interface Target extends Serializable { void setTargetTag(int tag); Target getOriginalTarget(); + + // used for cards like Spellskite + void setTargetAmount(UUID targetId, int amount, Game game); + } diff --git a/Mage/src/main/java/mage/target/TargetImpl.java b/Mage/src/main/java/mage/target/TargetImpl.java index 88c29215ac..546bafd5ad 100644 --- a/Mage/src/main/java/mage/target/TargetImpl.java +++ b/Mage/src/main/java/mage/target/TargetImpl.java @@ -544,7 +544,7 @@ public abstract class TargetImpl implements Target { } /** - * Is used to be able to check, that another target is slected within the + * Is used to be able to check, that another target is selected within the * group of targets of the ability with a target tag > 0. * * @param targetTag @@ -559,4 +559,10 @@ public abstract class TargetImpl implements Target { return this; } + @Override + public void setTargetAmount(UUID targetId, int amount, Game game) { + targets.put(targetId, amount); + rememberZoneChangeCounter(targetId, game); + } + }