From ab2399cbe73b0dde20af94038beedc24283887ca Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 3 Sep 2016 15:26:17 +0200 Subject: [PATCH] * Fixed that equipments with restrictions (e.g. Gate Smasher) were not unequipped if the equipped permanent did no longer fulfill the restrictions(fixes #2212). --- .../championsofkamigawa/KondasBanner.java | 17 ++- .../mage/sets/dissension/SimicGuildmage.java | 2 +- .../sets/dragonsoftarkir/GateSmasher.java | 17 ++- .../mage/sets/fifthdawn/AuriokWindwalker.java | 4 +- .../mage/sets/judgment/NomadMythmaker.java | 2 +- .../sets/morningtide/ReinsOfTheVinesteed.java | 2 +- .../sets/saviorsofkamigawa/ONaginata.java | 15 +-- .../mage/sets/shadowmoor/GlamerSpinners.java | 2 +- .../sets/shadowsoverinnistrad/GryffsBoon.java | 2 +- .../equipped/EquipRestrictedTest.java | 122 ++++++++++++++++++ .../test/cards/copy/PhantasmalImageTest.java | 3 + .../common/AttachableToRestrictedAbility.java | 47 +++++++ .../mage/abilities/keyword/EquipAbility.java | 21 ++- .../permanent/CanBeEnchantedByPredicate.java | 2 +- Mage/src/main/java/mage/game/GameImpl.java | 23 +++- .../java/mage/game/permanent/Permanent.java | 2 +- .../mage/game/permanent/PermanentImpl.java | 10 +- 17 files changed, 234 insertions(+), 59 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/EquipRestrictedTest.java create mode 100644 Mage/src/main/java/mage/abilities/common/AttachableToRestrictedAbility.java diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java index 2a46977a01..6ba52f06c8 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java @@ -1,5 +1,5 @@ /* - * + * * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -25,15 +25,15 @@ * 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.championsofkamigawa; import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.common.AttachableToRestrictedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; @@ -47,6 +47,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.SupertypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; import mage.util.CardUtil; @@ -56,7 +57,7 @@ import mage.util.CardUtil; */ public class KondasBanner extends CardImpl { - private static final FilterControlledCreaturePermanent legendaryFilter = new FilterControlledCreaturePermanent("Legendary creatures"); + private static final FilterControlledCreaturePermanent legendaryFilter = new FilterControlledCreaturePermanent("legendary creatures"); static { legendaryFilter.add(new SupertypePredicate("Legendary")); @@ -68,8 +69,9 @@ public class KondasBanner extends CardImpl { this.supertype.add("Legendary"); this.subtype.add("Equipment"); + Target target = new TargetControlledCreaturePermanent(1, 1, legendaryFilter, false); // Konda's Banner can be attached only to a legendary creature. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("{this} can be attached only to a legendary creature"))); + this.addAbility(new AttachableToRestrictedAbility(target)); // Creatures that share a color with equipped creature get +1/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KondasBannerColorBoostEffect())); @@ -78,10 +80,7 @@ public class KondasBanner extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KondasBannerTypeBoostEffect())); // Equip {2} - this.addAbility(new EquipAbility( - Outcome.AddAbility, - new GenericManaCost(2), - new TargetControlledCreaturePermanent(1, 1, legendaryFilter, false))); + this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2), target)); } diff --git a/Mage.Sets/src/mage/sets/dissension/SimicGuildmage.java b/Mage.Sets/src/mage/sets/dissension/SimicGuildmage.java index 9cdc034680..742c1523ee 100644 --- a/Mage.Sets/src/mage/sets/dissension/SimicGuildmage.java +++ b/Mage.Sets/src/mage/sets/dissension/SimicGuildmage.java @@ -229,7 +229,7 @@ class MoveAuraEffect extends OneShotEffect { } // Check for protection MageObject auraObject = game.getObject(auraId); - if (permanentToAttachAuras.cantBeEnchantedBy(auraObject, game)) { + if (permanentToAttachAuras.cantBeAttachedBy(auraObject, game)) { passed = false; } } diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/GateSmasher.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/GateSmasher.java index 30fe6e9229..075bd31f32 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/GateSmasher.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/GateSmasher.java @@ -29,10 +29,10 @@ package mage.sets.dragonsoftarkir; import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.common.AttachableToRestrictedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EquipAbility; @@ -46,6 +46,7 @@ import mage.constants.Zone; import mage.filter.Filter; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ToughnessPredicate; +import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -65,9 +66,10 @@ public class GateSmasher extends CardImpl { this.expansionSetCode = "DTK"; this.subtype.add("Equipment"); + Target target = new TargetControlledCreaturePermanent(1, 1, filter, false); // Gate Smasher can be attached only to a creature with toughness 4 or greater. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("{this} can be attached only to a creature with toughness 4 or greater"))); - + this.addAbility(new AttachableToRestrictedAbility(target)); + // Equipped creature gets +3/+0 and has trample. Effect effect = new BoostEquippedEffect(3, 0); effect.setText("Equipped creature gets +3/+0"); @@ -76,13 +78,10 @@ public class GateSmasher extends CardImpl { effect.setText("and has trample"); ability.addEffect(effect); this.addAbility(ability); - + // Equip {3} - this.addAbility(new EquipAbility( - Outcome.AddAbility, - new GenericManaCost(3), - new TargetControlledCreaturePermanent(1,1, filter, false))); - + this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(3), target)); + } public GateSmasher(final GateSmasher card) { diff --git a/Mage.Sets/src/mage/sets/fifthdawn/AuriokWindwalker.java b/Mage.Sets/src/mage/sets/fifthdawn/AuriokWindwalker.java index d69f7e1fa3..1cb4da8d16 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/AuriokWindwalker.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/AuriokWindwalker.java @@ -53,6 +53,7 @@ import mage.target.common.TargetControlledPermanent; public class AuriokWindwalker extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledPermanent("Equipment you control"); + static { filter.add(new SubtypePredicate("Equipment")); } @@ -67,7 +68,7 @@ public class AuriokWindwalker extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // {tap}: Attach target Equipment you control to target creature you control. + // {T}: Attach target Equipment you control to target creature you control. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AttachTargetEquipmentEffect(), new TapSourceCost()); ability.addTarget(new TargetControlledPermanent(filter)); ability.addTarget(new TargetControlledCreaturePermanent()); @@ -84,7 +85,6 @@ public class AuriokWindwalker extends CardImpl { } } - class AttachTargetEquipmentEffect extends OneShotEffect { public AttachTargetEquipmentEffect() { diff --git a/Mage.Sets/src/mage/sets/judgment/NomadMythmaker.java b/Mage.Sets/src/mage/sets/judgment/NomadMythmaker.java index 1219e79422..1eb06dbd47 100644 --- a/Mage.Sets/src/mage/sets/judgment/NomadMythmaker.java +++ b/Mage.Sets/src/mage/sets/judgment/NomadMythmaker.java @@ -121,7 +121,7 @@ class NomadMythmakerEffect extends OneShotEffect { && controller.choose(Outcome.PutCardInPlay, target, source.getSourceId(), game)) { Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent != null - && !permanent.cantBeEnchantedBy(aura, game)) { + && !permanent.cantBeAttachedBy(aura, game)) { game.getState().setValue("attachTo:" + aura.getId(), permanent); controller.moveCards(aura, Zone.BATTLEFIELD, source, game); return permanent.addAttachment(aura.getId(), game); diff --git a/Mage.Sets/src/mage/sets/morningtide/ReinsOfTheVinesteed.java b/Mage.Sets/src/mage/sets/morningtide/ReinsOfTheVinesteed.java index f31ee05546..447367dfcc 100644 --- a/Mage.Sets/src/mage/sets/morningtide/ReinsOfTheVinesteed.java +++ b/Mage.Sets/src/mage/sets/morningtide/ReinsOfTheVinesteed.java @@ -127,7 +127,7 @@ class ReinsOfTheVinesteedEffect extends OneShotEffect { if (controller != null && controller.choose(Outcome.PutCardInPlay, target, source.getSourceId(), game)) { Permanent targetPermanent = game.getPermanent(target.getFirstTarget()); - if (!targetPermanent.cantBeEnchantedBy(aura, game)) { + if (!targetPermanent.cantBeAttachedBy(aura, game)) { game.getState().setValue("attachTo:" + aura.getId(), targetPermanent); aura.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), controller.getId()); return targetPermanent.addAttachment(aura.getId(), game); diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/ONaginata.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/ONaginata.java index 409d3ded75..2231dfc03d 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/ONaginata.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/ONaginata.java @@ -29,10 +29,10 @@ package mage.sets.saviorsofkamigawa; import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.common.AttachableToRestrictedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EquipAbility; @@ -46,6 +46,7 @@ import mage.constants.Zone; import mage.filter.Filter; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; +import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -65,9 +66,10 @@ public class ONaginata extends CardImpl { this.expansionSetCode = "SOK"; this.subtype.add("Equipment"); + Target target = new TargetControlledCreaturePermanent(1, 1, filter, false); // O-Naginata can be attached only to a creature with 3 or more power. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("{this} can be attached only to a creature with 3 or more power"))); - + this.addAbility(new AttachableToRestrictedAbility(target)); + // Equipped creature gets +3/+0 and has trample. Effect effect = new BoostEquippedEffect(3, 0); effect.setText("Equipped creature gets +3/+0"); @@ -76,12 +78,9 @@ public class ONaginata extends CardImpl { effect.setText("and has trample"); ability.addEffect(effect); this.addAbility(ability); - + // Equip {2} - this.addAbility(new EquipAbility( - Outcome.AddAbility, - new GenericManaCost(2), - new TargetControlledCreaturePermanent(1,1, filter, false))); + this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2), target)); } public ONaginata(final ONaginata card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/GlamerSpinners.java b/Mage.Sets/src/mage/sets/shadowmoor/GlamerSpinners.java index b9b73fdc71..dd7b370a0a 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/GlamerSpinners.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/GlamerSpinners.java @@ -143,7 +143,7 @@ class GlamerSpinnersEffect extends OneShotEffect { } // Check for protection MageObject auraObject = game.getObject(auraId); - if (permanentToAttachAuras.cantBeEnchantedBy(auraObject, game)) { + if (permanentToAttachAuras.cantBeAttachedBy(auraObject, game)) { passed = false; } } diff --git a/Mage.Sets/src/mage/sets/shadowsoverinnistrad/GryffsBoon.java b/Mage.Sets/src/mage/sets/shadowsoverinnistrad/GryffsBoon.java index 848941da59..bf41ce2400 100644 --- a/Mage.Sets/src/mage/sets/shadowsoverinnistrad/GryffsBoon.java +++ b/Mage.Sets/src/mage/sets/shadowsoverinnistrad/GryffsBoon.java @@ -110,7 +110,7 @@ class GryffsBoonEffect extends OneShotEffect { if (aura != null && game.getState().getZone(aura.getId()).equals(Zone.GRAVEYARD)) { Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (!targetPermanent.cantBeEnchantedBy(aura, game)) { + if (!targetPermanent.cantBeAttachedBy(aura, game)) { game.getState().setValue("attachTo:" + aura.getId(), targetPermanent); aura.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId()); return targetPermanent.addAttachment(aura.getId(), game); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/EquipRestrictedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/EquipRestrictedTest.java new file mode 100644 index 0000000000..facac264dd --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/EquipRestrictedTest.java @@ -0,0 +1,122 @@ +/* + * 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 org.mage.test.cards.abilities.equipped; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class EquipRestrictedTest extends CardTestPlayerBase { + + @Test + public void testEquipLeoninScimitarToNonLegendary() { + addCard(Zone.BATTLEFIELD, playerB, "Auriok Windwalker"); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Leonin Scimitar"); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Attach target Equipment you control to target creature you control.", "Leonin Scimitar"); + addTarget(playerB, "Silvercout Lion"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + Permanent equipment = getPermanent("Leonin Scimitar", playerB); + Assert.assertTrue("Leonin Scimitar has to be attached", equipment.getAttachedTo() != null); + } + + /** + * Konda's Banner, Gate Smasher, and O-Naginata can all be attached to + * creatures that they should not be able to be attached to. The filter is + * only applied to the EquipAbility and does not check other methods (such + * as Auriok Windwaker) + */ + @Test + public void testEquipKondasBannerToNonLegendary() { + addCard(Zone.BATTLEFIELD, playerB, "Auriok Windwalker"); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Konda's Banner"); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Attach target Equipment you control to target creature you control.", "Konda's Banner"); + addTarget(playerB, "Silvercout Lion"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + Permanent equipment = getPermanent("Konda's Banner", playerB); + Assert.assertTrue("Konda's Banner may not be attached", equipment.getAttachedTo() == null); + } + + @Test + public void testEquipGateSmasher() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + // {1}{W}: Kranioceros gets +0/+3 until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Kranioceros");// 5/2 + // Gate Smasher can be attached only to a creature with toughness 4 or greater. + // Equipped creature gets +3/+0 and has trample. + // Equip {3} + addCard(Zone.BATTLEFIELD, playerA, "Gate Smasher"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}:"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Equip", "Kranioceros"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPowerToughness(playerA, "Kranioceros", 8, 5); + Permanent equipment = getPermanent("Gate Smasher", playerA); + Assert.assertTrue("Gate Smasher may no longer be attached", equipment.getAttachedTo() != null); + } + + @Test + public void testEquipGateSmasherAndUnattached() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + // {1}{W}: Kranioceros gets +0/+3 until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Kranioceros");// 5/2 + // Gate Smasher can be attached only to a creature with toughness 4 or greater. + // Equipped creature gets +3/+0 and has trample. + // Equip {3} + addCard(Zone.BATTLEFIELD, playerA, "Gate Smasher"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}:"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Equip", "Kranioceros"); + + setStopAt(2, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, "Kranioceros", 5, 2); + Permanent equipment = getPermanent("Gate Smasher", playerA); + Assert.assertTrue("Gate Smasher may no longer be attached", equipment.getAttachedTo() == null); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java index d677381ae8..5e69551e6e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java @@ -11,6 +11,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** * @author noxx diff --git a/Mage/src/main/java/mage/abilities/common/AttachableToRestrictedAbility.java b/Mage/src/main/java/mage/abilities/common/AttachableToRestrictedAbility.java new file mode 100644 index 0000000000..3cc732e8c3 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/AttachableToRestrictedAbility.java @@ -0,0 +1,47 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.common; + +import mage.abilities.Ability; +import mage.abilities.effects.common.InfoEffect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.Target; + +/** + * + * @author LevelX2 + */ +public class AttachableToRestrictedAbility extends SimpleStaticAbility { + + public AttachableToRestrictedAbility(Target target) { + super(Zone.BATTLEFIELD, new InfoEffect("{this} can be attached only to a " + target.getTargetName())); + addTarget(target); + } + + private AttachableToRestrictedAbility(AttachableToRestrictedAbility ability) { + super(ability); + } + + public boolean canEquip(Permanent toEquip, Ability source, Game game) { + for (Target target : getTargets()) { + if (source == null) { + if (!target.canTarget(toEquip.getId(), game)) { + return false; + } + } else if (!target.canTarget(toEquip.getId(), source, game)) { + return false; + } + } + return true; + } + + @Override + public AttachableToRestrictedAbility copy() { + return new AttachableToRestrictedAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java b/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java index 256a493b77..c6fa5c0810 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,12 +20,11 @@ * 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.keyword; import java.util.UUID; @@ -57,17 +56,15 @@ public class EquipAbility extends ActivatedAbilityImpl { @Override public boolean canActivate(UUID playerId, Game game) { - if(super.canActivate(playerId, game)){ + if (super.canActivate(playerId, game)) { Permanent permanent = game.getPermanent(sourceId); - if(permanent != null && permanent.hasSubtype("Equipment", game)){ + if (permanent != null && permanent.hasSubtype("Equipment", game)) { return true; } } return false; } - - public EquipAbility(final EquipAbility ability) { super(ability); } @@ -79,9 +76,7 @@ public class EquipAbility extends ActivatedAbilityImpl { @Override public String getRule() { - StringBuilder sb = new StringBuilder("Equip ").append(costs.getText()).append(manaCosts.getText()); - sb.append(" (").append(manaCosts.getText()).append(": Attach to target creature you control. Equip only as a sorcery.)"); - return sb.toString(); + return "Equip " + costs.getText() + manaCosts.getText() + " (" + manaCosts.getText() + ": Attach to target creature you control. Equip only as a sorcery.)"; } } diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/CanBeEnchantedByPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/CanBeEnchantedByPredicate.java index 6d876299e2..f9441f84d0 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/CanBeEnchantedByPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/CanBeEnchantedByPredicate.java @@ -45,7 +45,7 @@ public class CanBeEnchantedByPredicate implements Predicate { @Override public boolean apply(Permanent input, Game game) { - return !input.cantBeEnchantedBy(auraEnchantment, game); + return !input.cantBeAttachedBy(auraEnchantment, game); } @Override diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 1a543b15f0..ee2365a731 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -51,6 +51,7 @@ import mage.abilities.ActivatedAbility; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbility; +import mage.abilities.common.AttachableToRestrictedAbility; import mage.abilities.common.ChancellorAbility; import mage.abilities.common.GemstoneCavernsAbility; import mage.abilities.effects.ContinuousEffect; @@ -1775,12 +1776,12 @@ public abstract class GameImpl implements Game, Serializable { Filter auraFilter = spellAbility.getTargets().get(0).getFilter(); if (auraFilter instanceof FilterControlledCreaturePermanent) { if (!((FilterControlledCreaturePermanent) auraFilter).match(attachedTo, perm.getId(), perm.getControllerId(), this) - || attachedTo.cantBeEnchantedBy(perm, this)) { + || attachedTo.cantBeAttachedBy(perm, this)) { if (movePermanentToGraveyardWithInfo(perm)) { somethingHappened = true; } } - } else if (!auraFilter.match(attachedTo, this) || attachedTo.cantBeEnchantedBy(perm, this)) { + } else if (!auraFilter.match(attachedTo, this) || attachedTo.cantBeAttachedBy(perm, this)) { // handle bestow unattachment Card card = this.getCard(perm.getId()); if (card != null && card.getCardType().contains(CardType.CREATURE)) { @@ -1816,13 +1817,23 @@ public abstract class GameImpl implements Game, Serializable { if (FILTER_EQUIPMENT.match(perm, this)) { //20091005 - 704.5p, 702.14d if (perm.getAttachedTo() != null) { - Permanent creature = getPermanent(perm.getAttachedTo()); - if (creature == null || !creature.getAttachments().contains(perm.getId())) { + Permanent attachedTo = getPermanent(perm.getAttachedTo()); + if (attachedTo != null) { + for (Ability ability : perm.getAbilities(this)) { + if (ability instanceof AttachableToRestrictedAbility) { + if (!((AttachableToRestrictedAbility) ability).canEquip(attachedTo, null, this)) { + attachedTo = null; + break; + } + } + } + } + if (attachedTo == null || !attachedTo.getAttachments().contains(perm.getId())) { UUID wasAttachedTo = perm.getAttachedTo(); perm.attachTo(null, this); fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, wasAttachedTo, perm.getId(), perm.getControllerId())); - } else if (!creature.getCardType().contains(CardType.CREATURE) || creature.hasProtectionFrom(perm, this)) { - if (creature.removeAttachment(perm.getId(), this)) { + } else if (!attachedTo.getCardType().contains(CardType.CREATURE) || attachedTo.hasProtectionFrom(perm, this)) { + if (attachedTo.removeAttachment(perm.getId(), this)) { somethingHappened = true; } } diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index 7319a51a11..1d20fa0516 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -121,7 +121,7 @@ public interface Permanent extends Card, Controllable { boolean hasProtectionFrom(MageObject source, Game game); - boolean cantBeEnchantedBy(MageObject source, Game game); + boolean cantBeAttachedBy(MageObject source, Game game); boolean wasControlledFromStartOfControllerTurn(); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 08462ac566..84aeff54b6 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -625,13 +625,13 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { @Override public boolean changeControllerId(UUID controllerId, Game game) { Player newController = game.getPlayer(controllerId); - + GameEvent loseControlEvent = GameEvent.getEvent(GameEvent.EventType.LOSE_CONTROL, this.getId(), null, controllerId); if (game.replaceEvent(loseControlEvent)) { return false; } - + if (newController != null && (!newController.hasLeft() || !newController.hasLost())) { this.controllerId = controllerId; return true; @@ -981,7 +981,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { @Override public boolean hasProtectionFrom(MageObject source, Game game) { - for (ProtectionAbility ability : abilities.getProtectionAbilities()) { + for (ProtectionAbility ability : this.getAbilities(game).getProtectionAbilities()) { if (!ability.canTarget(source, game)) { return true; } @@ -990,8 +990,8 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } @Override - public boolean cantBeEnchantedBy(MageObject source, Game game) { - for (ProtectionAbility ability : abilities.getProtectionAbilities()) { + public boolean cantBeAttachedBy(MageObject source, Game game) { + for (ProtectionAbility ability : this.getAbilities(game).getProtectionAbilities()) { if (!(source.getSubtype(game).contains("Aura") && !ability.removesAuras()) && !source.getId().equals(ability.getAuraIdNotToBeRemoved())