From d3dba583582f2e70c73802d4064680d48ab3d217 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 10 Apr 2015 00:39:06 +0200 Subject: [PATCH] * Fixed that continuous effects of copied cards with limited duration stop to work as the copied card stops to exist. --- .../util/layout/CardLayoutStrategy.java | 2 +- .../layout/impl/OldCardLayoutStrategy.java | 24 +++++++------- .../mage/sets/magic2012/AngelicDestiny.java | 29 ++++++++++++----- .../mage/sets/mirrodin/IsochronScepter.java | 2 +- .../mage/sets/returntoravnica/SoulTithe.java | 10 +++--- .../test/cards/copy/IsochronScepterTest.java | 31 +++++++++++++++++++ ...PutIntoGraveFromAnywhereSourceAbility.java | 6 ++-- .../abilities/effects/ContinuousEffects.java | 6 ++-- .../effects/ContinuousEffectsList.java | 9 ++++-- Mage/src/mage/game/GameImpl.java | 29 +++++++++++++++++ .../mage/game/permanent/PermanentCard.java | 3 +- 11 files changed, 113 insertions(+), 38 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/util/layout/CardLayoutStrategy.java b/Mage.Client/src/main/java/mage/client/util/layout/CardLayoutStrategy.java index 42fbd09e2b2..9e3615f6f69 100644 --- a/Mage.Client/src/main/java/mage/client/util/layout/CardLayoutStrategy.java +++ b/Mage.Client/src/main/java/mage/client/util/layout/CardLayoutStrategy.java @@ -1,6 +1,6 @@ package mage.client.util.layout; -import javax.swing.*; +import javax.swing.JLayeredPane; /** * Interface for operations that modify cards' layout diff --git a/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java b/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java index bcfef896a4d..47cd0d30e87 100644 --- a/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java +++ b/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java @@ -1,16 +1,16 @@ package mage.client.util.layout.impl; +import java.awt.Dimension; +import java.awt.Rectangle; +import java.util.Map; +import java.util.UUID; +import javax.swing.JLayeredPane; import mage.cards.MagePermanent; import mage.client.game.BattlefieldPanel; import mage.client.plugins.impl.Plugins; import mage.client.util.layout.CardLayoutStrategy; import mage.view.PermanentView; -import javax.swing.*; -import java.awt.*; -import java.util.Map; -import java.util.UUID; - /** * Card layout for client version 1.3.0 and earlier. * @@ -54,14 +54,14 @@ public class OldCardLayoutStrategy implements CardLayoutStrategy { } int position = jLayeredPane.getPosition(perm); perm.getLinks().clear(); - Rectangle r = perm.getBounds(); + Rectangle rectangleBaseCard = perm.getBounds(); if (!Plugins.getInstance().isCardPluginLoaded()) { for (UUID attachmentId: permanent.getAttachments()) { MagePermanent link = permanents.get(attachmentId); if (link != null) { perm.getLinks().add(link); - r.translate(20, 20); - link.setBounds(r); + rectangleBaseCard.translate(20, 20); + link.setBounds(rectangleBaseCard); jLayeredPane.setPosition(link, ++position); } } @@ -70,14 +70,14 @@ public class OldCardLayoutStrategy implements CardLayoutStrategy { for (UUID attachmentId: permanent.getAttachments()) { MagePermanent link = permanents.get(attachmentId); if (link != null) { - link.setBounds(r); + link.setBounds(rectangleBaseCard); perm.getLinks().add(link); if (index == 1) { - r.translate(ATTACHMENTS_DX_OFFSET, ATTACHMENT_DY_OFFSET); // do it once + rectangleBaseCard.translate(ATTACHMENTS_DX_OFFSET, ATTACHMENT_DY_OFFSET); // do it once } else { - r.translate(ATTACHMENT_DX_OFFSET, ATTACHMENT_DY_OFFSET); + rectangleBaseCard.translate(ATTACHMENT_DX_OFFSET, ATTACHMENT_DY_OFFSET); } - perm.setBounds(r); + perm.setBounds(rectangleBaseCard); jLayeredPane.moveToFront(link); jLayeredPane.moveToFront(perm); jPanel.setComponentZOrder(link, index); diff --git a/Mage.Sets/src/mage/sets/magic2012/AngelicDestiny.java b/Mage.Sets/src/mage/sets/magic2012/AngelicDestiny.java index dd6e5e7e4aa..f9604ea6f25 100644 --- a/Mage.Sets/src/mage/sets/magic2012/AngelicDestiny.java +++ b/Mage.Sets/src/mage/sets/magic2012/AngelicDestiny.java @@ -28,11 +28,10 @@ package mage.sets.magic2012; import java.util.UUID; - -import mage.constants.*; import mage.abilities.Ability; import mage.abilities.common.DiesAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; @@ -42,6 +41,12 @@ import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -56,19 +61,27 @@ public class AngelicDestiny extends CardImpl { this.expansionSetCode = "M12"; this.subtype.add("Aura"); - this.color.setWhite(true); - + // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(4, 4, Duration.WhileOnBattlefield))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.AURA))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AddCardSubtypeAttachedEffect("Angel", Duration.WhileOnBattlefield, AttachmentType.AURA))); + // Enchanted creature gets +4/+4, has flying and first strike, and is an Angel in addition to its other types. + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(4, 4, Duration.WhileOnBattlefield)); + Effect effect = new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA); + effect.setText(", has flying"); + ability.addEffect(effect); + effect = new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.AURA); + effect.setText("and first strike"); + ability.addEffect(effect); + effect = new AddCardSubtypeAttachedEffect("Angel", Duration.WhileOnBattlefield, AttachmentType.AURA); + effect.setText(", and is an Angel in addition to its other types"); + ability.addEffect(effect); + this.addAbility(ability); + // When enchanted creature dies, return Angelic Destiny to its owner's hand. this.addAbility(new DiesAttachedTriggeredAbility(new ReturnToHandSourceEffect(), "enchanted creature")); } diff --git a/Mage.Sets/src/mage/sets/mirrodin/IsochronScepter.java b/Mage.Sets/src/mage/sets/mirrodin/IsochronScepter.java index b798db5fdf4..9ba91ef89fe 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/IsochronScepter.java +++ b/Mage.Sets/src/mage/sets/mirrodin/IsochronScepter.java @@ -62,7 +62,7 @@ public class IsochronScepter extends CardImpl { this.expansionSetCode = "MRD"; // Imprint - When Isochron Scepter enters the battlefield, you may exile an instant card with converted mana cost 2 or less from your hand. - this.addAbility(new EntersBattlefieldTriggeredAbility(new IsochronScepterImprintEffect(), true, "Imprint - ")); + this.addAbility(new EntersBattlefieldTriggeredAbility(new IsochronScepterImprintEffect(), true, "Imprint — ")); // {2}, {tap}: You may copy the exiled card. If you do, you may cast the copy without paying its mana cost. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new IsochronScepterCopyEffect(), new GenericManaCost(2)); diff --git a/Mage.Sets/src/mage/sets/returntoravnica/SoulTithe.java b/Mage.Sets/src/mage/sets/returntoravnica/SoulTithe.java index aaefaae0b33..3c429990008 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/SoulTithe.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/SoulTithe.java @@ -29,6 +29,7 @@ package mage.sets.returntoravnica; import java.util.UUID; +import mage.MageObject; import mage.constants.AttachmentType; import mage.constants.CardType; import mage.constants.Duration; @@ -64,7 +65,6 @@ public class SoulTithe extends CardImpl { super(ownerId, 23, "Soul Tithe", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); this.expansionSetCode = "RTR"; this.subtype.add("Aura"); - this.color.setWhite(true); // Enchant nonland permanent TargetPermanent auraTarget = new TargetNonlandPermanent(); @@ -106,12 +106,12 @@ class SoulTitheSacrificeSourceUnlessPaysEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getSourceId()); - if (player != null && permanent != null) { + MageObject sourceObject = source.getSourceObject(game); + if (player != null && permanent != null && sourceObject != null) { int cmc = permanent.getManaCost().convertedManaCost(); - if (player.chooseUse(Outcome.Benefit, "Pay {" + cmc + "} for " + permanent.getName() + "?", game)) { + if (player.chooseUse(Outcome.Benefit, "Pay {" + cmc + "} for " + permanent.getName() + "? (otherwise you sacrifice it)", game)) { Cost cost = new GenericManaCost(cmc); - if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) - { + if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { return true; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java index 8ca4ed754fa..a5b1a67587b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java @@ -111,4 +111,35 @@ public class IsochronScepterTest extends CardTestPlayerBase { } + @Test + public void testAngelsGrace() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.HAND, playerA, "Isochron Scepter"); + addCard(Zone.HAND, playerA, "Angel's Grace"); + + addCard(Zone.BATTLEFIELD, playerB, "Dross Crocodile", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Isochron Scepter"); + addTarget(playerA, "Angel's Grace"); + + attack(2, playerB, "Dross Crocodile"); + attack(2, playerB, "Dross Crocodile"); + attack(2, playerB, "Dross Crocodile"); + attack(2, playerB, "Dross Crocodile"); + + activateAbility(2, PhaseStep.DECLARE_BLOCKERS, playerA, "{2},{T}:"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Yes"); + + setStopAt(2, PhaseStep.END_COMBAT); + execute(); + + assertPermanentCount(playerA, "Isochron Scepter", 1); + assertExileCount("Angel's Grace", 1); + assertGraveyardCount(playerA, "Angel's Grace", 0); + + assertLife(playerA, 1); + assertLife(playerB, 20); + + } } diff --git a/Mage/src/mage/abilities/common/PutIntoGraveFromAnywhereSourceAbility.java b/Mage/src/mage/abilities/common/PutIntoGraveFromAnywhereSourceAbility.java index 7571abf6262..f0f56848cb8 100644 --- a/Mage/src/mage/abilities/common/PutIntoGraveFromAnywhereSourceAbility.java +++ b/Mage/src/mage/abilities/common/PutIntoGraveFromAnywhereSourceAbility.java @@ -127,10 +127,8 @@ class PutIntoGraveFromAnywhereEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { if (((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD - && event.getTargetId().equals(source.getSourceId())) - { - if (condition == null || condition.apply(game, source)) - { + && event.getTargetId().equals(source.getSourceId())) { + if (condition == null || condition.apply(game, source)) { return true; } } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 4407e7b854a..ef6367aace6 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -351,10 +351,8 @@ public class ContinuousEffects implements Serializable { if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, event)) { if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { if (!game.getScopeRelevant() || effect.hasSelfScope() || !event.getTargetId().equals(ability.getSourceId())) { - if (checkAbilityStillExists(ability, effect, event, game)) { // TODO: This is really needed??? - if (effect.applies(event, ability, game)) { - applicableAbilities.add(ability); - } + if (effect.applies(event, ability, game)) { + applicableAbilities.add(ability); } } } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffectsList.java b/Mage/src/mage/abilities/effects/ContinuousEffectsList.java index f56ce23d94f..55e1a4e62d9 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffectsList.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffectsList.java @@ -117,10 +117,15 @@ public class ContinuousEffectsList extends ArrayList return false; } else if (effect.isDiscarded()) { it.remove(); - } else if (ability.getSourceId() != null && game.getObject(ability.getSourceId()) == null) { // Commander effects have no sourceId - it.remove(); // if the related source object does no longer exist the effect has to be removed } else { switch(effect.getDuration()) { + case WhileOnBattlefield: + case WhileInGraveyard: + case WhileOnStack: + if (ability.getSourceId() != null && game.getObject(ability.getSourceId()) == null) { // Commander effects have no sourceId + it.remove(); // if the related source object does no longer exist in game - the effect has to be removed + } + break; case OneUse: if (effect.isUsed()) { it.remove(); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index e532957cef5..997c93f8554 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -1424,6 +1424,35 @@ public abstract class GameImpl implements Game, Serializable { Card card = copiedCards.next(); Zone zone = state.getZone(card.getId()); if (zone != Zone.BATTLEFIELD && zone != Zone.STACK) { + switch (zone) { + case GRAVEYARD: + for(Player player: getPlayers().values()) { + if (player.getGraveyard().contains(card.getId())) { + player.getGraveyard().remove(card); + break; + } + } + break; + case HAND: + for(Player player: getPlayers().values()) { + if (player.getHand().contains(card.getId())) { + player.getHand().remove(card); + break; + } + } + break; + case LIBRARY: + for(Player player: getPlayers().values()) { + if (player.getLibrary().getCard(card.getId(), this) != null) { + player.getLibrary().remove(card.getId(), this); + break; + } + } + break; + case EXILED: + getExile().removeCard(card, this); + break; + } copiedCards.remove(); } } diff --git a/Mage/src/mage/game/permanent/PermanentCard.java b/Mage/src/mage/game/permanent/PermanentCard.java index 0975fb681ff..220905b133e 100644 --- a/Mage/src/mage/game/permanent/PermanentCard.java +++ b/Mage/src/mage/game/permanent/PermanentCard.java @@ -136,9 +136,10 @@ public class PermanentCard extends PermanentImpl { public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, ArrayList appliedEffects) { Zone fromZone = game.getState().getZone(objectId); Player controller = game.getPlayer(controllerId); - if (controller != null && controller.removeFromBattlefield(this, game)) { + if (controller != null) { ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, controllerId, fromZone, toZone, appliedEffects); if (!game.replaceEvent(event)) { + controller.removeFromBattlefield(this, game); Player owner = game.getPlayer(ownerId); game.rememberLKI(objectId, Zone.BATTLEFIELD, this); if (owner != null) {