From 27d74e86331b108ddc5e87178e3ba11ad9e18807 Mon Sep 17 00:00:00 2001 From: BursegSardaukar <> Date: Wed, 16 Sep 2015 16:51:13 -0400 Subject: [PATCH 01/23] Added Fodder Launch. --- .../src/mage/sets/lorwyn/FodderLaunch.java | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java diff --git a/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java b/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java new file mode 100644 index 0000000000..0910230b9f --- /dev/null +++ b/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.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 mage.sets.lorwyn; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; + +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreatureOrPlayer; +import mage.target.common.TargetCreaturePermanent; + +/** + * @author BursegSardaukar + */ +public class FodderLaunch extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a Goblin"); + + static { + filter.add(new SubtypePredicate("Goblin")); + } + + public FodderLaunch(UUID ownerId) { + super(ownerId, 114, "Fodder Launch", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{B}"); + this.expansionSetCode = "LRW"; + this.subtype.add("Goblin"); + + //As an additional cost to cast Fodder Launch, sacrifice a Goblin. + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, false))); + + //Target creature gets -5/-5 until end of turn. Fodder Launch deals 5 damage to that creature's controller. + this.getSpellAbility().addEffect(new BoostTargetEffect(-5, -5, Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new FodderLaunchEffect()); + } + + public FodderLaunch(final FodderLaunch card) { + super(card); + } + + @Override + public FodderLaunch copy() { + return new FodderLaunch(this); + } + +} + +class FodderLaunchEffect extends OneShotEffect { + + public FodderLaunchEffect() { + super(Outcome.Damage); + this.staticText = "Target creature gets -5/-5 until end of turn. Fodder Launch deals 5 damage to that creature's controller."; + } + + public FodderLaunchEffect(final FodderLaunchEffect effect) { + super(effect); + } + + @Override + public FodderLaunchEffect copy() { + return new FodderLaunchEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null) { + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (targetCreature != null) { + Player controllerOfTargetCreature = game.getPlayer(targetCreature.getControllerId()); + controllerOfTargetCreature.damage(5, source.getSourceId(), game, false, true); + return true; + } + return false; + } + } +} \ No newline at end of file From be496d90f737dd2cc9be11375818e4734906dfff Mon Sep 17 00:00:00 2001 From: BursegSardaukar <> Date: Wed, 16 Sep 2015 16:53:55 -0400 Subject: [PATCH 02/23] Corrected default false return placement for FodderLaunchEffect. --- Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java b/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java index 0910230b9f..d8d8cbebbc 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java +++ b/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java @@ -36,12 +36,8 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.common.FilterControlledCreaturePermanent; @@ -50,7 +46,6 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.common.TargetCreatureOrPlayer; import mage.target.common.TargetCreaturePermanent; /** @@ -116,7 +111,7 @@ class FodderLaunchEffect extends OneShotEffect { controllerOfTargetCreature.damage(5, source.getSourceId(), game, false, true); return true; } - return false; } + return false; } } \ No newline at end of file From 2cd72e967fa65298dfb0ab7ebb9d54c6544bcc02 Mon Sep 17 00:00:00 2001 From: BursegSardaukar <> Date: Wed, 16 Sep 2015 17:17:37 -0400 Subject: [PATCH 03/23] Implemented Mogg Squad --- .../src/mage/sets/tempest/MoggSquad.java | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/tempest/MoggSquad.java diff --git a/Mage.Sets/src/mage/sets/tempest/MoggSquad.java b/Mage.Sets/src/mage/sets/tempest/MoggSquad.java new file mode 100644 index 0000000000..39317d743b --- /dev/null +++ b/Mage.Sets/src/mage/sets/tempest/MoggSquad.java @@ -0,0 +1,89 @@ +/* + * 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.tempest; + +import java.util.UUID; + +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.MultipliedValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Loki + */ +public class MoggSquad extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("each other creature on the battlefield"); + + static { + filter.add(new AnotherPredicate()); + } + + public MoggSquad(UUID ownerId) { + super(ownerId, 192, "Mogg Squad", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); + this.expansionSetCode = "TMP"; + this.subtype.add("Goblin"); + + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + DynamicValue amount = new PermanentsOnBattlefieldCount(filter); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(new MultipliedValue(amount, -1), new MultipliedValue(amount, -1), Duration.WhileOnBattlefield)); + this.addAbility(ability); + } + + public MoggSquad(final MoggSquad card) { + super(card); + } + + @Override + public MoggSquad copy() { + return new MoggSquad(this); + } +} From 5c99b529404d40deb33e5e0f969c0c2bcbc8ccc0 Mon Sep 17 00:00:00 2001 From: BursegSardaukar <> Date: Fri, 18 Sep 2015 16:52:21 -0400 Subject: [PATCH 04/23] Added Rabble-Rouser Added Goblin Snowman Added Firefright Mage Added Quill-Slinger Boggart Added Skirk Commando Added Boggart Sprite-Chaser Added Goblin Fire Fiend Corrected MoggSquad Author --- .../mage/sets/archenemy/SkirkCommando.java | 114 ++++++++++++++++++ .../src/mage/sets/guildpact/RabbleRouser.java | 82 +++++++++++++ .../src/mage/sets/iceage/GoblinSnowman.java | 82 +++++++++++++ .../mage/sets/lorwyn/BoggartSpriteChaser.java | 82 +++++++++++++ .../mage/sets/lorwyn/QuillSlingerBoggart.java | 77 ++++++++++++ .../mage/sets/planarchaos/FirefrightMage.java | 93 ++++++++++++++ .../mage/sets/ravnica/GoblinFireFiend.java | 82 +++++++++++++ .../src/mage/sets/tempest/MoggSquad.java | 11 +- .../mage/sets/timespiral/GoblinSnowman.java | 52 ++++++++ 9 files changed, 665 insertions(+), 10 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java create mode 100644 Mage.Sets/src/mage/sets/guildpact/RabbleRouser.java create mode 100644 Mage.Sets/src/mage/sets/iceage/GoblinSnowman.java create mode 100644 Mage.Sets/src/mage/sets/lorwyn/BoggartSpriteChaser.java create mode 100644 Mage.Sets/src/mage/sets/lorwyn/QuillSlingerBoggart.java create mode 100644 Mage.Sets/src/mage/sets/planarchaos/FirefrightMage.java create mode 100644 Mage.Sets/src/mage/sets/ravnica/GoblinFireFiend.java create mode 100644 Mage.Sets/src/mage/sets/timespiral/GoblinSnowman.java diff --git a/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java b/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java new file mode 100644 index 0000000000..155b9d17d4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.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.archenemy; + +import java.util.UUID; + +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.MorphAbility; +import mage.cards.CardImpl; +import mage.constants.Outcome; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author BursegSardaukar + */ +public class SkirkCommando extends CardImpl { + + public SkirkCommando(UUID ownerId) { + super(ownerId, 47, "Skirk Commando", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); + this.expansionSetCode = "ARC"; + this.subtype.add("Goblin"); + this.subtype.add("Shaman"); + + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + //Whenever Skirk Commando deals combat damage to a player, you may have it deal 2 damage to target creature that player controls. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new SkirkCommandoEffect(), true, true)); + + //Morph {2}{R} (You may cast this card face down as a 2/2 creature for 3. Turn it face up any time for its morph cost.) + this.addAbility(new MorphAbility(this, new ManaCostsImpl("{2}{R}"))); + + } + + public SkirkCommando(final SkirkCommando card) { + super(card); + } + + @Override + public SkirkCommando copy() { + return new SkirkCommando(this); + } +} + +class SkirkCommandoEffect extends OneShotEffect { + + public SkirkCommandoEffect() { + super(Outcome.Damage); + staticText = "it deals 2 damage to target creature that player controls"; + } + + public SkirkCommandoEffect(final SkirkCommandoEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(targetPointer.getFirst(game, source)); + if (player != null) { + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature " + player.getLogName() + " controls"); + filter.add(new ControllerIdPredicate(player.getId())); + TargetCreaturePermanent target = new TargetCreaturePermanent(filter); + if (target.canChoose(source.getControllerId(), game) && target.choose(Outcome.Damage, source.getControllerId(), source.getSourceId(), game)) { + UUID creature = target.getFirstTarget(); + if (creature != null) { + game.getPermanent(creature).damage(2, source.getSourceId(), game, false, true); + return true; + } + } + } + return false; + } + + @Override + public SkirkCommandoEffect copy() { + return new SkirkCommandoEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/guildpact/RabbleRouser.java b/Mage.Sets/src/mage/sets/guildpact/RabbleRouser.java new file mode 100644 index 0000000000..9754e4f350 --- /dev/null +++ b/Mage.Sets/src/mage/sets/guildpact/RabbleRouser.java @@ -0,0 +1,82 @@ +/* + * 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.UUID; + +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.keyword.BloodthirstAbility; +import mage.cards.CardImpl; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.common.FilterAttackingCreature; + +/** + * + * @author BursegSardaukar + */ +public class RabbleRouser extends CardImpl { + + private final static FilterAttackingCreature filter = new FilterAttackingCreature("attacking creatures"); + + public RabbleRouser(UUID ownerId) { + super(ownerId, 73, "Rabble-Rouser", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{R}"); + this.expansionSetCode = "GPT"; + this.subtype.add("Goblin"); + this.subtype.add("Shaman"); + + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + //Bloodthirst 1 (If an opponent was dealt damage this turn, this creature enters the battlefield with a +1/+1 counter on it.) + this.addAbility(new BloodthirstAbility(1)); + + //{R}, {T}: Attacking creatures get +X/+0 until end of turn, where X is Rabble-Rouser's power. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostAllEffect(new SourcePermanentPowerCount(), new StaticValue(0), Duration.EndOfTurn, filter, false), new ManaCostsImpl("{R}")); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + public RabbleRouser(final RabbleRouser card) { + super(card); + } + + @Override + public RabbleRouser copy() { + return new RabbleRouser(this); + } +} diff --git a/Mage.Sets/src/mage/sets/iceage/GoblinSnowman.java b/Mage.Sets/src/mage/sets/iceage/GoblinSnowman.java new file mode 100644 index 0000000000..9fb3b18c09 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/GoblinSnowman.java @@ -0,0 +1,82 @@ +/* + * 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.iceage; + +import java.util.UUID; + +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BlocksTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.PreventCombatDamageBySourceEffect; +import mage.abilities.effects.common.PreventCombatDamageToSourceEffect; +import mage.cards.CardImpl; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.common.FilterAttackingCreature; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author BursegSardaukar + */ +public class GoblinSnowman extends CardImpl { + + public GoblinSnowman(UUID ownerId) { + super(ownerId, 191, "Goblin Snowman", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{R}"); + this.expansionSetCode = "ICE"; + this.subtype.add("Goblin"); + + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + //Whenever Goblin Snowman blocks, prevent all combat damage that would be dealt to and dealt by it this turn. + this.addAbility(new BlocksTriggeredAbility(new PreventCombatDamageBySourceEffect(Duration.EndOfTurn), false)); + this.addAbility(new BlocksTriggeredAbility(new PreventCombatDamageToSourceEffect(Duration.EndOfTurn), false)); + + //{T}: Goblin Snowman deals 1 damage to target creature it's blocking. + FilterAttackingCreature filter = new FilterAttackingCreature("creature it's blocking"); + filter.add(new BlockingByPredicate(this.getId())); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability, new BlockedByWatcher()); + } + + public GoblinSnowman(final GoblinSnowman card) { + super(card); + } + + @Override + public GoblinSnowman copy() { + return new GoblinSnowman(this); + } +} diff --git a/Mage.Sets/src/mage/sets/lorwyn/BoggartSpriteChaser.java b/Mage.Sets/src/mage/sets/lorwyn/BoggartSpriteChaser.java new file mode 100644 index 0000000000..e68e62365a --- /dev/null +++ b/Mage.Sets/src/mage/sets/lorwyn/BoggartSpriteChaser.java @@ -0,0 +1,82 @@ +/* + * 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.lorwyn; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceWhileControlsEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; + +/** + * + * @author BursegSardaukar + */ +public class BoggartSpriteChaser extends CardImpl { + + final static private String rule = "{this} flying as long as you control a Faerie"; + + private static final FilterPermanent filter = new FilterPermanent("a Faerie"); + + static { + filter.add(new SubtypePredicate("Faerie")); + } + + public BoggartSpriteChaser(UUID ownerId) { + super(ownerId, 156, "Boggart Sprite-Chaser", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); + this.expansionSetCode = "LRW"; + this.subtype.add("Goblin"); + this.subtype.add("Warrior"); + + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // As long as you control a Faerie, Boggart Sprite-Chaser gets +1/+1 and has flying. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceWhileControlsEffect(filter, 1, 1))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield), new PermanentsOnTheBattlefieldCondition(filter), rule))); + } + + public BoggartSpriteChaser(final BoggartSpriteChaser card) { + super(card); + } + + @Override + public BoggartSpriteChaser copy() { + return new BoggartSpriteChaser(this); + } +} diff --git a/Mage.Sets/src/mage/sets/lorwyn/QuillSlingerBoggart.java b/Mage.Sets/src/mage/sets/lorwyn/QuillSlingerBoggart.java new file mode 100644 index 0000000000..1e0360fbb2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/lorwyn/QuillSlingerBoggart.java @@ -0,0 +1,77 @@ +/* + * 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.lorwyn; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.TargetPlayer; + +/** + * + * @author BursegSardaukar + */ +public class QuillSlingerBoggart extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("Kithkin spell"); + + static { + filter.add(new SubtypePredicate("Kithkin")); + } + + public QuillSlingerBoggart(UUID ownerId) { + super(ownerId, 137, "Quill-Slinger Boggart", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{3}{B}"); + this.expansionSetCode = "LRW"; + this.subtype.add("Goblin"); + this.subtype.add("Warrior"); + + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever a player casts a Kithkin spell, you may have target player lose 1 life. + Ability ability = new SpellCastAllTriggeredAbility(new LoseLifeTargetEffect(2), filter, true); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + } + + public QuillSlingerBoggart(final QuillSlingerBoggart card) { + super(card); + } + + @Override + public QuillSlingerBoggart copy() { + return new QuillSlingerBoggart(this); + } +} diff --git a/Mage.Sets/src/mage/sets/planarchaos/FirefrightMage.java b/Mage.Sets/src/mage/sets/planarchaos/FirefrightMage.java new file mode 100644 index 0000000000..c5edfd6b4b --- /dev/null +++ b/Mage.Sets/src/mage/sets/planarchaos/FirefrightMage.java @@ -0,0 +1,93 @@ +/* + * 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.planarchaos; + +import java.util.UUID; + +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; +import mage.cards.CardImpl; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author BursegSardaukar + */ +public class FirefrightMage extends CardImpl { + + private final static FilterCreaturePermanent notArtifactOrRed = new FilterCreaturePermanent("except by artifact creatures and/or white creatures"); + + static { + notArtifactOrRed.add(Predicates.not( + Predicates.or( + new CardTypePredicate(CardType.ARTIFACT), + new ColorPredicate(ObjectColor.RED) + ) + )); + } + + public FirefrightMage(UUID ownerId) { + super(ownerId, 99, "Firefright Mage", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{R}"); + this.expansionSetCode = "PLC"; + this.subtype.add("Goblin"); + this.subtype.add("Spellshaper"); + + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + //{1} {R}, {T}, Discard a card: Target creature can't be blocked this turn except by artifact creatures and/or red creatures. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedByCreaturesSourceEffect(notArtifactOrRed, Duration.EndOfTurn), new ManaCostsImpl("{1}{R}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new DiscardCardCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public FirefrightMage(final FirefrightMage card) { + super(card); + } + + @Override + public FirefrightMage copy() { + return new FirefrightMage(this); + } +} diff --git a/Mage.Sets/src/mage/sets/ravnica/GoblinFireFiend.java b/Mage.Sets/src/mage/sets/ravnica/GoblinFireFiend.java new file mode 100644 index 0000000000..60117deba7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ravnica/GoblinFireFiend.java @@ -0,0 +1,82 @@ +/* + * 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.ravnica; + +import java.util.UUID; + +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneSourceEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.constants.Duration; +import mage.constants.Zone; + +/** + * + * @author BursegSardaukar + */ +public class GoblinFireFiend extends CardImpl { + + public GoblinFireFiend(UUID ownerId) { + super(ownerId, 127, "Goblin Fire Fiend", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{3}{R}"); + this.expansionSetCode = "RAV"; + this.subtype.add("Goblin"); + this.subtype.add("Berserker"); + + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + + //Haste + this.addAbility(HasteAbility.getInstance()); + + //Goblin Fire Fiend must be blocked if able. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect())); + + //{R}: Goblin Fire Fiend gets +1/+0 until end of turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(new StaticValue(1), new StaticValue(0), Duration.EndOfTurn), new ManaCostsImpl("{R}")); + this.addAbility(ability); + } + + public GoblinFireFiend(final GoblinFireFiend card) { + super(card); + } + + @Override + public GoblinFireFiend copy() { + return new GoblinFireFiend(this); + } +} diff --git a/Mage.Sets/src/mage/sets/tempest/MoggSquad.java b/Mage.Sets/src/mage/sets/tempest/MoggSquad.java index 39317d743b..2a0f91347a 100644 --- a/Mage.Sets/src/mage/sets/tempest/MoggSquad.java +++ b/Mage.Sets/src/mage/sets/tempest/MoggSquad.java @@ -33,29 +33,20 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.MultipliedValue; -import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.permanent.AnotherPredicate; -import mage.target.common.TargetControlledPermanent; -import mage.target.common.TargetCreaturePermanent; /** * - * @author Loki + * @author BursegSardaukar */ public class MoggSquad extends CardImpl { diff --git a/Mage.Sets/src/mage/sets/timespiral/GoblinSnowman.java b/Mage.Sets/src/mage/sets/timespiral/GoblinSnowman.java new file mode 100644 index 0000000000..ef7ca06256 --- /dev/null +++ b/Mage.Sets/src/mage/sets/timespiral/GoblinSnowman.java @@ -0,0 +1,52 @@ +/* + * 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.timespiral; + +import java.util.UUID; + +/** + * + * @author BursegSardaukar + */ +public class GoblinSnowman extends mage.sets.iceage.GoblinSnowman { + + public GoblinSnowman(UUID ownerId) { + super(ownerId); + this.cardNumber = 64; + this.expansionSetCode = "ICE"; + } + + public GoblinSnowman(final GoblinSnowman card) { + super(card); + } + + @Override + public GoblinSnowman copy() { + return new GoblinSnowman(this); + } +} From a023d07c7ea20383f99964d405ba2d10e9cf4d09 Mon Sep 17 00:00:00 2001 From: BursegSardaukar <> Date: Sat, 26 Sep 2015 00:05:11 -0400 Subject: [PATCH 05/23] Added Utvara Scalper Added Mogg Jailer Added Goblin Masons Cleaned up a bunch of goblin cards. --- .../mage/sets/archenemy/SkirkCommando.java | 2 +- .../mage/sets/dissension/UtvaraScalper.java | 69 +++++++++ .../src/mage/sets/guildpact/RabbleRouser.java | 6 +- .../mage/sets/lorwyn/BoggartSpriteChaser.java | 4 +- .../mage/sets/lorwyn/QuillSlingerBoggart.java | 2 +- .../src/mage/sets/planeshift/MoggJailer.java | 133 ++++++++++++++++++ .../mage/sets/ravnica/GoblinFireFiend.java | 1 - .../src/mage/sets/tempest/MoggSquad.java | 6 +- .../mage/sets/urzasdestiny/GoblinMasons.java | 78 ++++++++++ 9 files changed, 291 insertions(+), 10 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/dissension/UtvaraScalper.java create mode 100644 Mage.Sets/src/mage/sets/planeshift/MoggJailer.java create mode 100644 Mage.Sets/src/mage/sets/urzasdestiny/GoblinMasons.java diff --git a/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java b/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java index 155b9d17d4..d995faa763 100644 --- a/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java +++ b/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java @@ -82,7 +82,7 @@ class SkirkCommandoEffect extends OneShotEffect { public SkirkCommandoEffect() { super(Outcome.Damage); - staticText = "it deals 2 damage to target creature that player controls"; + staticText = "have it deal 2 damage to target creature that player controls"; } public SkirkCommandoEffect(final SkirkCommandoEffect effect) { diff --git a/Mage.Sets/src/mage/sets/dissension/UtvaraScalper.java b/Mage.Sets/src/mage/sets/dissension/UtvaraScalper.java new file mode 100644 index 0000000000..ffc5aaf419 --- /dev/null +++ b/Mage.Sets/src/mage/sets/dissension/UtvaraScalper.java @@ -0,0 +1,69 @@ +/* + * 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.dissension; + +import java.util.UUID; + +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.MageInt; +import mage.abilities.common.AttacksEachTurnStaticAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; + +/** + * + * @author BursegSardaukar + */ +public class UtvaraScalper extends CardImpl { + + public UtvaraScalper(UUID ownerId) { + super(ownerId, 76, "Utvara Scalper", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); + this.expansionSetCode = "DIS"; + this.subtype.add("Goblin"); + this.subtype.add("Scout"); + + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + //Flying + this.addAbility(FlyingAbility.getInstance()); + + //Utvara Scalper attacks each turn if able + this.addAbility(new AttacksEachTurnStaticAbility()); + } + + public UtvaraScalper(final UtvaraScalper card) { + super(card); + } + + @Override + public UtvaraScalper copy() { + return new UtvaraScalper(this); + } +} diff --git a/Mage.Sets/src/mage/sets/guildpact/RabbleRouser.java b/Mage.Sets/src/mage/sets/guildpact/RabbleRouser.java index 9754e4f350..aafc1493d0 100644 --- a/Mage.Sets/src/mage/sets/guildpact/RabbleRouser.java +++ b/Mage.Sets/src/mage/sets/guildpact/RabbleRouser.java @@ -36,6 +36,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.continuous.BoostAllEffect; @@ -51,7 +52,7 @@ import mage.filter.common.FilterAttackingCreature; */ public class RabbleRouser extends CardImpl { - private final static FilterAttackingCreature filter = new FilterAttackingCreature("attacking creatures"); + final static private String rule = "Attacking creatures get +X/+0 until end of turn, where X is Rabble-Rouser's power."; public RabbleRouser(UUID ownerId) { super(ownerId, 73, "Rabble-Rouser", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{R}"); @@ -66,7 +67,8 @@ public class RabbleRouser extends CardImpl { this.addAbility(new BloodthirstAbility(1)); //{R}, {T}: Attacking creatures get +X/+0 until end of turn, where X is Rabble-Rouser's power. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostAllEffect(new SourcePermanentPowerCount(), new StaticValue(0), Duration.EndOfTurn, filter, false), new ManaCostsImpl("{R}")); + DynamicValue amount = new SourcePermanentPowerCount(); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostAllEffect(amount, new StaticValue(0), Duration.EndOfTurn, new FilterAttackingCreature(), false, rule), new ManaCostsImpl("{R}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/lorwyn/BoggartSpriteChaser.java b/Mage.Sets/src/mage/sets/lorwyn/BoggartSpriteChaser.java index e68e62365a..9d6b6e441b 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/BoggartSpriteChaser.java +++ b/Mage.Sets/src/mage/sets/lorwyn/BoggartSpriteChaser.java @@ -49,9 +49,9 @@ import mage.filter.predicate.mageobject.SubtypePredicate; */ public class BoggartSpriteChaser extends CardImpl { - final static private String rule = "{this} flying as long as you control a Faerie"; + final static private String rule = "{this} has flying as long as you control a Faerie"; - private static final FilterPermanent filter = new FilterPermanent("a Faerie"); + private static final FilterPermanent filter = new FilterPermanent("Faerie"); static { filter.add(new SubtypePredicate("Faerie")); diff --git a/Mage.Sets/src/mage/sets/lorwyn/QuillSlingerBoggart.java b/Mage.Sets/src/mage/sets/lorwyn/QuillSlingerBoggart.java index 1e0360fbb2..513bf353a4 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/QuillSlingerBoggart.java +++ b/Mage.Sets/src/mage/sets/lorwyn/QuillSlingerBoggart.java @@ -45,7 +45,7 @@ import mage.target.TargetPlayer; */ public class QuillSlingerBoggart extends CardImpl { - private static final FilterSpell filter = new FilterSpell("Kithkin spell"); + private static final FilterSpell filter = new FilterSpell("a Kithkin spell"); static { filter.add(new SubtypePredicate("Kithkin")); diff --git a/Mage.Sets/src/mage/sets/planeshift/MoggJailer.java b/Mage.Sets/src/mage/sets/planeshift/MoggJailer.java new file mode 100644 index 0000000000..d4c0a32314 --- /dev/null +++ b/Mage.Sets/src/mage/sets/planeshift/MoggJailer.java @@ -0,0 +1,133 @@ +/* + * 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.planeshift; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.RestrictionEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.Filter; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author BursegSardaukar + + */ +public class MoggJailer extends CardImpl { + + static final private FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creature with power 2 or less"); + + static { + filter.add(Predicates.and(new PowerPredicate(Filter.ComparisonType.LessThan, 2), Predicates.not(new TappedPredicate()))); + //filter.add(new PowerPredicate(Filter.ComparisonType.LessThan, 3)); + } + + public MoggJailer(UUID ownerId) { + super(ownerId, 68, "Mogg Jailer", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); + this.expansionSetCode = "PLS"; + this.subtype.add("Goblin"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Mogg Jailer can't attack if defending player controls an untapped creature with power 2 or less. + Effect effect = new CantAttackIfDefenderControllsPermanent(filter); + effect.setText("Mogg Jailer can't attack if defending player controls an untapped creature with power 2 or less."); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + } + + public MoggJailer(final MoggJailer card) { + super(card); + } + + @Override + public MoggJailer copy() { + return new MoggJailer(this); + } +} + +class CantAttackIfDefenderControllsPermanent extends RestrictionEffect { + + private final FilterPermanent filter; + + public CantAttackIfDefenderControllsPermanent(FilterPermanent filter) { + super(Duration.WhileOnBattlefield); + this.filter = filter; + staticText = new StringBuilder("{this} can't attack if defending player controls ").append(filter.getMessage()).toString(); + } + + public CantAttackIfDefenderControllsPermanent(final CantAttackIfDefenderControllsPermanent effect) { + super(effect); + this.filter = effect.filter; + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent.getId().equals(source.getSourceId()); + } + + @Override + public boolean canAttack(UUID defenderId, Ability source, Game game) { + UUID defendingPlayerId; + Player player = game.getPlayer(defenderId); + if (player == null) { + Permanent permanent = game.getPermanent(defenderId); + if (permanent != null) { + defendingPlayerId = permanent.getControllerId(); + } else { + return true; + } + } else { + defendingPlayerId = defenderId; + } + if (defendingPlayerId != null && game.getBattlefield().countAll(filter, defendingPlayerId, game) > 0) { + return false; + } + return true; + } + + @Override + public CantAttackIfDefenderControllsPermanent copy() { + return new CantAttackIfDefenderControllsPermanent(this); + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/ravnica/GoblinFireFiend.java b/Mage.Sets/src/mage/sets/ravnica/GoblinFireFiend.java index 60117deba7..053454589d 100644 --- a/Mage.Sets/src/mage/sets/ravnica/GoblinFireFiend.java +++ b/Mage.Sets/src/mage/sets/ravnica/GoblinFireFiend.java @@ -58,7 +58,6 @@ public class GoblinFireFiend extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - //Haste this.addAbility(HasteAbility.getInstance()); diff --git a/Mage.Sets/src/mage/sets/tempest/MoggSquad.java b/Mage.Sets/src/mage/sets/tempest/MoggSquad.java index 2a0f91347a..6188bc2083 100644 --- a/Mage.Sets/src/mage/sets/tempest/MoggSquad.java +++ b/Mage.Sets/src/mage/sets/tempest/MoggSquad.java @@ -35,8 +35,8 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.MultipliedValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.SignInversionDynamicValue; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.constants.Duration; @@ -64,8 +64,8 @@ public class MoggSquad extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - DynamicValue amount = new PermanentsOnBattlefieldCount(filter); - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(new MultipliedValue(amount, -1), new MultipliedValue(amount, -1), Duration.WhileOnBattlefield)); + DynamicValue amount = new SignInversionDynamicValue(new PermanentsOnBattlefieldCount(filter)); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(amount, amount, Duration.WhileOnBattlefield)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/GoblinMasons.java b/Mage.Sets/src/mage/sets/urzasdestiny/GoblinMasons.java new file mode 100644 index 0000000000..eb2854b039 --- /dev/null +++ b/Mage.Sets/src/mage/sets/urzasdestiny/GoblinMasons.java @@ -0,0 +1,78 @@ +/* + * 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.urzasdestiny; + +import java.util.UUID; + +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.MageInt; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.TargetPermanent; + +/** + * + * @author BursegSardaukar + */ +public class GoblinMasons extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("Wall"); + + static { + filter.add(new SubtypePredicate("Wall")); + } + + + public GoblinMasons(UUID ownerId) { + super(ownerId, 86, "Goblin Masons", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); + this.expansionSetCode = "UDS"; + this.subtype.add("Goblin"); + + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + //When Goblin Masons dies, destroy target Wall + DiesTriggeredAbility ability = new DiesTriggeredAbility(new DestroyTargetEffect(), false); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + } + + public GoblinMasons(final GoblinMasons card) { + super(card); + } + + @Override + public GoblinMasons copy() { + return new GoblinMasons(this); + } +} From 435eb49b5841ddbfe84394f8047cdc2bd048f12e Mon Sep 17 00:00:00 2001 From: emerald000 Date: Fri, 2 Oct 2015 23:38:11 -0400 Subject: [PATCH 06/23] Added missing Licids + tests (including 2 failing tests). --- .../src/mage/sets/exodus/DominatingLicid.java | 71 +++++++++++ .../sets/exodus/TransmogrifyingLicid.java | 78 ++++++++++++ .../mage/sets/stronghold/CalmingLicid.java | 71 +++++++++++ .../mage/sets/stronghold/ConvulsingLicid.java | 71 +++++++++++ .../mage/sets/stronghold/CorruptingLicid.java | 72 +++++++++++ .../mage/sets/stronghold/GlidingLicid.java | 72 +++++++++++ .../mage/sets/stronghold/TemptingLicid.java | 71 +++++++++++ .../src/mage/sets/tempest/LeechingLicid.java | 74 +++++++++++ .../src/mage/sets/tempest/NurturingLicid.java | 71 +++++++++++ .../mage/sets/tempest/QuickeningLicid.java | 73 +++++++++++ .../src/mage/sets/tempest/StingingLicid.java | 70 +++++++++++ .../abilities/activated/LicidAbilityTest.java | 118 ++++++++++++++++++ 12 files changed, 912 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/exodus/DominatingLicid.java create mode 100644 Mage.Sets/src/mage/sets/exodus/TransmogrifyingLicid.java create mode 100644 Mage.Sets/src/mage/sets/stronghold/CalmingLicid.java create mode 100644 Mage.Sets/src/mage/sets/stronghold/ConvulsingLicid.java create mode 100644 Mage.Sets/src/mage/sets/stronghold/CorruptingLicid.java create mode 100644 Mage.Sets/src/mage/sets/stronghold/GlidingLicid.java create mode 100644 Mage.Sets/src/mage/sets/stronghold/TemptingLicid.java create mode 100644 Mage.Sets/src/mage/sets/tempest/LeechingLicid.java create mode 100644 Mage.Sets/src/mage/sets/tempest/NurturingLicid.java create mode 100644 Mage.Sets/src/mage/sets/tempest/QuickeningLicid.java create mode 100644 Mage.Sets/src/mage/sets/tempest/StingingLicid.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/LicidAbilityTest.java diff --git a/Mage.Sets/src/mage/sets/exodus/DominatingLicid.java b/Mage.Sets/src/mage/sets/exodus/DominatingLicid.java new file mode 100644 index 0000000000..3dc6bcb736 --- /dev/null +++ b/Mage.Sets/src/mage/sets/exodus/DominatingLicid.java @@ -0,0 +1,71 @@ +/* + * 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.exodus; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.LicidAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.ControlEnchantedEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author emerald000 + */ +public class DominatingLicid extends CardImpl { + + public DominatingLicid(UUID ownerId) { + super(ownerId, 30, "Dominating Licid", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); + this.expansionSetCode = "EXO"; + this.subtype.add("Licid"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {1}{U}{U}, {tap}: Dominating Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {U} to end this effect. + this.addAbility(new LicidAbility(new ManaCostsImpl<>("{1}{U}{U}"), new ColoredManaCost(ColoredManaSymbol.U))); + + // You control enchanted creature. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ControlEnchantedEffect())); + } + + public DominatingLicid(final DominatingLicid card) { + super(card); + } + + @Override + public DominatingLicid copy() { + return new DominatingLicid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/exodus/TransmogrifyingLicid.java b/Mage.Sets/src/mage/sets/exodus/TransmogrifyingLicid.java new file mode 100644 index 0000000000..c98e0f48e4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/exodus/TransmogrifyingLicid.java @@ -0,0 +1,78 @@ +/* + * 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.exodus; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.LicidAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.AddCardTypeAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author emerald000 + */ +public class TransmogrifyingLicid extends CardImpl { + + public TransmogrifyingLicid(UUID ownerId) { + super(ownerId, 141, "Transmogrifying Licid", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + this.expansionSetCode = "EXO"; + this.subtype.add("Licid"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {1}, {tap}: Transmogrifying Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {1} to end this effect. + this.addAbility(new LicidAbility(new GenericManaCost(1), new GenericManaCost(1))); + + // Enchanted creature gets +1/+1 and is an artifact in addition to its other types. + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, 1)); + Effect effect = new AddCardTypeAttachedEffect(CardType.ARTIFACT, Duration.WhileOnBattlefield, AttachmentType.AURA); + effect.setText("and is an artifact in addition to its other types"); + ability.addEffect(effect); + this.addAbility(ability); + } + + public TransmogrifyingLicid(final TransmogrifyingLicid card) { + super(card); + } + + @Override + public TransmogrifyingLicid copy() { + return new TransmogrifyingLicid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/stronghold/CalmingLicid.java b/Mage.Sets/src/mage/sets/stronghold/CalmingLicid.java new file mode 100644 index 0000000000..2f3b09634e --- /dev/null +++ b/Mage.Sets/src/mage/sets/stronghold/CalmingLicid.java @@ -0,0 +1,71 @@ +/* + * 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.stronghold; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.LicidAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.effects.common.combat.CantAttackAttachedEffect; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author emerald000 + */ +public class CalmingLicid extends CardImpl { + + public CalmingLicid(UUID ownerId) { + super(ownerId, 102, "Calming Licid", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{W}"); + this.expansionSetCode = "STH"; + this.subtype.add("Licid"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {W}, {tap}: Calming Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {W} to end this effect. + this.addAbility(new LicidAbility(new ColoredManaCost(ColoredManaSymbol.W), new ColoredManaCost(ColoredManaSymbol.W))); + + // Enchanted creature can't attack. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackAttachedEffect(AttachmentType.AURA))); + } + + public CalmingLicid(final CalmingLicid card) { + super(card); + } + + @Override + public CalmingLicid copy() { + return new CalmingLicid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/stronghold/ConvulsingLicid.java b/Mage.Sets/src/mage/sets/stronghold/ConvulsingLicid.java new file mode 100644 index 0000000000..ebb349d188 --- /dev/null +++ b/Mage.Sets/src/mage/sets/stronghold/ConvulsingLicid.java @@ -0,0 +1,71 @@ +/* + * 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.stronghold; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.LicidAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.effects.common.combat.CantBlockAttachedEffect; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author emerald000 + */ +public class ConvulsingLicid extends CardImpl { + + public ConvulsingLicid(UUID ownerId) { + super(ownerId, 77, "Convulsing Licid", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{R}"); + this.expansionSetCode = "STH"; + this.subtype.add("Licid"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {R}, {tap}: Convulsing Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {R} to end this effect. + this.addAbility(new LicidAbility(new ColoredManaCost(ColoredManaSymbol.R), new ColoredManaCost(ColoredManaSymbol.R))); + + // Enchanted creature can't block. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBlockAttachedEffect(AttachmentType.AURA))); + } + + public ConvulsingLicid(final ConvulsingLicid card) { + super(card); + } + + @Override + public ConvulsingLicid copy() { + return new ConvulsingLicid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/stronghold/CorruptingLicid.java b/Mage.Sets/src/mage/sets/stronghold/CorruptingLicid.java new file mode 100644 index 0000000000..081b7a3a61 --- /dev/null +++ b/Mage.Sets/src/mage/sets/stronghold/CorruptingLicid.java @@ -0,0 +1,72 @@ +/* + * 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.stronghold; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.LicidAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.FearAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author emerald000 + */ +public class CorruptingLicid extends CardImpl { + + public CorruptingLicid(UUID ownerId) { + super(ownerId, 4, "Corrupting Licid", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{B}"); + this.expansionSetCode = "STH"; + this.subtype.add("Licid"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {B}, {tap}: Corrupting Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {B} to end this effect. + this.addAbility(new LicidAbility(new ColoredManaCost(ColoredManaSymbol.B), new ColoredManaCost(ColoredManaSymbol.B))); + + // Enchanted creature has fear. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FearAbility.getInstance(), AttachmentType.AURA))); + } + + public CorruptingLicid(final CorruptingLicid card) { + super(card); + } + + @Override + public CorruptingLicid copy() { + return new CorruptingLicid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/stronghold/GlidingLicid.java b/Mage.Sets/src/mage/sets/stronghold/GlidingLicid.java new file mode 100644 index 0000000000..6f9a8feffb --- /dev/null +++ b/Mage.Sets/src/mage/sets/stronghold/GlidingLicid.java @@ -0,0 +1,72 @@ +/* + * 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.stronghold; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.LicidAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author emerald000 + */ +public class GlidingLicid extends CardImpl { + + public GlidingLicid(UUID ownerId) { + super(ownerId, 31, "Gliding Licid", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{U}"); + this.expansionSetCode = "STH"; + this.subtype.add("Licid"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {U}, {tap}: Gliding Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {U} to end this effect. + this.addAbility(new LicidAbility(new ColoredManaCost(ColoredManaSymbol.U), new ColoredManaCost(ColoredManaSymbol.U))); + + // Enchanted creature has flying. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA))); + } + + public GlidingLicid(final GlidingLicid card) { + super(card); + } + + @Override + public GlidingLicid copy() { + return new GlidingLicid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/stronghold/TemptingLicid.java b/Mage.Sets/src/mage/sets/stronghold/TemptingLicid.java new file mode 100644 index 0000000000..7666b868b9 --- /dev/null +++ b/Mage.Sets/src/mage/sets/stronghold/TemptingLicid.java @@ -0,0 +1,71 @@ +/* + * 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.stronghold; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.LicidAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.effects.common.combat.MustBeBlockedByAllAttachedEffect; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author emerald000 + */ +public class TemptingLicid extends CardImpl { + + public TemptingLicid(UUID ownerId) { + super(ownerId, 72, "Tempting Licid", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{G}"); + this.expansionSetCode = "STH"; + this.subtype.add("Licid"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {G}, {tap}: Tempting Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {G} to end this effect. + this.addAbility(new LicidAbility(new ColoredManaCost(ColoredManaSymbol.G), new ColoredManaCost(ColoredManaSymbol.G))); + + // All creatures able to block enchanted creature do so. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAllAttachedEffect(AttachmentType.AURA))); + } + + public TemptingLicid(final TemptingLicid card) { + super(card); + } + + @Override + public TemptingLicid copy() { + return new TemptingLicid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/tempest/LeechingLicid.java b/Mage.Sets/src/mage/sets/tempest/LeechingLicid.java new file mode 100644 index 0000000000..cdd9dc5ce2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/tempest/LeechingLicid.java @@ -0,0 +1,74 @@ +/* + * 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.tempest; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.LicidAbility; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; + +/** + * + * @author emerald000 + */ +public class LeechingLicid extends CardImpl { + + public LeechingLicid(UUID ownerId) { + super(ownerId, 35, "Leeching Licid", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{B}"); + this.expansionSetCode = "TMP"; + this.subtype.add("Licid"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {B}, {tap}: Leeching Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {B} to end this effect. + this.addAbility(new LicidAbility(new ColoredManaCost(ColoredManaSymbol.B), new ColoredManaCost(ColoredManaSymbol.B))); + + // At the beginning of the upkeep of enchanted creature's controller, Leeching Licid deals 1 damage to that player. + Effect effect = new DamageTargetEffect(1); + effect.setText("{this} deals 1 damage to that player"); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.CONTROLLER_ATTACHED_TO, false, true)); + } + + public LeechingLicid(final LeechingLicid card) { + super(card); + } + + @Override + public LeechingLicid copy() { + return new LeechingLicid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/tempest/NurturingLicid.java b/Mage.Sets/src/mage/sets/tempest/NurturingLicid.java new file mode 100644 index 0000000000..e2dafd29a9 --- /dev/null +++ b/Mage.Sets/src/mage/sets/tempest/NurturingLicid.java @@ -0,0 +1,71 @@ +/* + * 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.tempest; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.LicidAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.effects.common.RegenerateAttachedEffect; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author emerald000 + */ +public class NurturingLicid extends CardImpl { + + public NurturingLicid(UUID ownerId) { + super(ownerId, 136, "Nurturing Licid", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{G}"); + this.expansionSetCode = "TMP"; + this.subtype.add("Licid"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {G}, {tap}: Nurturing Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {G} to end this effect. + this.addAbility(new LicidAbility(new ColoredManaCost(ColoredManaSymbol.G), new ColoredManaCost(ColoredManaSymbol.G))); + + // {G}: Regenerate enchanted creature. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateAttachedEffect(AttachmentType.AURA), new ColoredManaCost(ColoredManaSymbol.G))); + } + + public NurturingLicid(final NurturingLicid card) { + super(card); + } + + @Override + public NurturingLicid copy() { + return new NurturingLicid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/tempest/QuickeningLicid.java b/Mage.Sets/src/mage/sets/tempest/QuickeningLicid.java new file mode 100644 index 0000000000..1fa9cc30c0 --- /dev/null +++ b/Mage.Sets/src/mage/sets/tempest/QuickeningLicid.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.tempest; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.LicidAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author emerald000 + */ +public class QuickeningLicid extends CardImpl { + + public QuickeningLicid(UUID ownerId) { + super(ownerId, 248, "Quickening Licid", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{W}"); + this.expansionSetCode = "TMP"; + this.subtype.add("Licid"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {1}{W}, {tap}: Quickening Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {W} to end this effect. + this.addAbility(new LicidAbility(new ManaCostsImpl<>("{1}{W}"), new ColoredManaCost(ColoredManaSymbol.W))); + + // Enchanted creature has first strike. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.AURA))); + } + + public QuickeningLicid(final QuickeningLicid card) { + super(card); + } + + @Override + public QuickeningLicid copy() { + return new QuickeningLicid(this); + } +} diff --git a/Mage.Sets/src/mage/sets/tempest/StingingLicid.java b/Mage.Sets/src/mage/sets/tempest/StingingLicid.java new file mode 100644 index 0000000000..7703fd35bb --- /dev/null +++ b/Mage.Sets/src/mage/sets/tempest/StingingLicid.java @@ -0,0 +1,70 @@ +/* + * 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.tempest; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BecomesTappedAttachedTriggeredAbility; +import mage.abilities.common.LicidAbility; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DamageAttachedControllerEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; + +/** + * + * @author emerald000 + */ +public class StingingLicid extends CardImpl { + + public StingingLicid(UUID ownerId) { + super(ownerId, 91, "Stinging Licid", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{U}"); + this.expansionSetCode = "TMP"; + this.subtype.add("Licid"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {1}{U}, {tap}: Stinging Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {U} to end this effect. + this.addAbility(new LicidAbility(new ManaCostsImpl<>("{1}{U}"), new ColoredManaCost(ColoredManaSymbol.U))); + + // Whenever enchanted creature becomes tapped, Stinging Licid deals 2 damage to that creature's controller. + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DamageAttachedControllerEffect(2), "enchanted creature")); + } + + public StingingLicid(final StingingLicid card) { + super(card); + } + + @Override + public StingingLicid copy() { + return new StingingLicid(this); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/LicidAbilityTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/LicidAbilityTest.java new file mode 100644 index 0000000000..27907d2fed --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/LicidAbilityTest.java @@ -0,0 +1,118 @@ +/* + * 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.activated; + +import mage.abilities.common.LicidAbility; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.keyword.HasteAbility; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author emerald0000 + */ + +public class LicidAbilityTest extends CardTestPlayerBase { + + /** + * Activate on another creature + */ + @Test + public void BasicUsageTest() { + addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox"); + // {R}, {T}: Enraging Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {R} to end this effect. + // Enchanted creature has haste. + addCard(Zone.BATTLEFIELD, playerA, "Enraging Licid"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + + activateAbility(1, PhaseStep.UPKEEP, playerA, "{R},", "Pillarfield Ox"); + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + + execute(); + + assertAbility(playerA, "Pillarfield Ox", HasteAbility.getInstance(), true); + assertAbility(playerA, "Enraging Licid", new LicidAbility(new ColoredManaCost(ColoredManaSymbol.R), new ColoredManaCost(ColoredManaSymbol.R)), false); + assertType("Enraging Licid", CardType.ENCHANTMENT, true); + assertType("Enraging Licid", CardType.CREATURE, false); + } + + /** + * Use special action to remove the continuous effect + */ + @Test + @Ignore("Test player can't activate special actions yet") + public void SpecialActionTest() { + addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox"); + // {R}, {T}: Enraging Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {R} to end this effect. + // Enchanted creature has haste. + addCard(Zone.BATTLEFIELD, playerA, "Enraging Licid"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + activateAbility(1, PhaseStep.UPKEEP, playerA, "{R},", "Pillarfield Ox"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: End"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertActionCount(playerA, 0); + assertAbility(playerA, "Pillarfield Ox", HasteAbility.getInstance(), false); + assertAbility(playerA, "Enraging Licid", new LicidAbility(new ColoredManaCost(ColoredManaSymbol.R), new ColoredManaCost(ColoredManaSymbol.R)), true); + assertType("Enraging Licid", CardType.ENCHANTMENT, false); + assertType("Enraging Licid", CardType.CREATURE, true); + } + + /** + * Licid should die if enchanted creature dies + */ + @Test + @Ignore("Enraging Licid doesn't die when its enchanted creature dies due to similarity to Bestow") + public void EnchantedCreatureDiesTest() { + addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox"); + // {R}, {T}: Enraging Licid loses this ability and becomes an Aura enchantment with enchant creature. Attach it to target creature. You may pay {R} to end this effect. + // Enchanted creature has haste. + addCard(Zone.BATTLEFIELD, playerA, "Enraging Licid"); + // Destroy target nonblack creature. + addCard(Zone.HAND, playerB, "Doom Blade"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + + activateAbility(1, PhaseStep.UPKEEP, playerA, "{R},", "Pillarfield Ox"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Doom Blade", "Pillarfield Ox"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertGraveyardCount(playerA, 2); // Pillarfield Ox + Enraging Licid + } +} From a1fb65858c88e1ad097f78fa76373a6df1350e65 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 3 Oct 2015 08:56:56 +0200 Subject: [PATCH 07/23] * Fixed some minor bugs of the split cards Supply // Demand and Pure // Simple. --- .../src/mage/sets/dissension/PureSimple.java | 28 ++++++++----------- .../mage/sets/dissension/SupplyDemand.java | 13 ++++----- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/Mage.Sets/src/mage/sets/dissension/PureSimple.java b/Mage.Sets/src/mage/sets/dissension/PureSimple.java index 9a2b77b35f..ec456b064d 100644 --- a/Mage.Sets/src/mage/sets/dissension/PureSimple.java +++ b/Mage.Sets/src/mage/sets/dissension/PureSimple.java @@ -25,18 +25,18 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.dissension; import java.util.UUID; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Rarity; -import mage.cards.SplitCard; import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.MulticoloredPredicate; +import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.TargetPermanent; /** @@ -45,30 +45,26 @@ import mage.target.TargetPermanent; */ public class PureSimple extends SplitCard { - private static final FilterPermanent filterAura = new FilterPermanent("aura"); - private static final FilterPermanent filterEquipment = new FilterPermanent("equipment"); + private static final FilterPermanent filterDestroy = new FilterPermanent("Auras and Equipment"); private static final FilterPermanent filterMulticolor = new FilterPermanent("multicolor permanent"); - + static { - filterAura.add(new SubtypePredicate("Aura")); - filterEquipment.add(new SubtypePredicate("Equipment")); + filterDestroy.add(Predicates.or(new SubtypePredicate("Aura"), new SubtypePredicate("Equipment"))); filterMulticolor.add(new MulticoloredPredicate()); } - + public PureSimple(UUID ownerId) { super(ownerId, 154, "Pure", "Simple", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{1}{R}{G}", "{1}{G}{W}", true); this.expansionSetCode = "DIS"; // Pure - // Destroy all Auras - getLeftHalfCard().getSpellAbility().addEffect(new DestroyAllEffect(filterAura)); - // and Equipment. - getLeftHalfCard().getSpellAbility().addEffect(new DestroyAllEffect(filterEquipment)); + // Destroy target multicolored permanent. + getLeftHalfCard().getSpellAbility().addEffect(new DestroyTargetEffect()); + getLeftHalfCard().getSpellAbility().addTarget(new TargetPermanent(filterMulticolor)); // Simple - // Destroy target multicolored permanent. - getRightHalfCard().getSpellAbility().addEffect(new DestroyTargetEffect()); - getRightHalfCard().getSpellAbility().addTarget(new TargetPermanent(filterMulticolor)); + // Destroy all Auras and Equipment. + getRightHalfCard().getSpellAbility().addEffect(new DestroyAllEffect(filterDestroy)); } public PureSimple(final PureSimple card) { diff --git a/Mage.Sets/src/mage/sets/dissension/SupplyDemand.java b/Mage.Sets/src/mage/sets/dissension/SupplyDemand.java index 8eae6bf7da..9f57c434cb 100644 --- a/Mage.Sets/src/mage/sets/dissension/SupplyDemand.java +++ b/Mage.Sets/src/mage/sets/dissension/SupplyDemand.java @@ -39,7 +39,6 @@ import mage.filter.predicate.mageobject.MulticoloredPredicate; import mage.game.permanent.token.SaprolingToken; import mage.target.common.TargetCardInLibrary; - /** * * @author LevelX2 @@ -53,16 +52,16 @@ public class SupplyDemand extends SplitCard { } public SupplyDemand(UUID ownerId) { - super(ownerId, 157, "Supply", "Demand", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{1}{W}{U}", "{X}{G}{W}", false); + super(ownerId, 157, "Supply", "Demand", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{X}{G}{W}", "{1}{W}{U}", false); this.expansionSetCode = "DIS"; - // Demand - // Search your library for a multicolored card, reveal it, and put it into your hand. Then shuffle your library. - getLeftHalfCard().getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(1, filter), true)); - // Supply // Put X 1/1 green Saproling creature tokens onto the battlefield. - getRightHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), new ManacostVariableValue())); + getLeftHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), new ManacostVariableValue())); + + // Demand + // Search your library for a multicolored card, reveal it, and put it into your hand. Then shuffle your library. + getRightHalfCard().getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(1, filter), true)); } From 8e50c18d8c53486e29b51b5a99ab06ae2139fa64 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 3 Oct 2015 08:57:32 +0200 Subject: [PATCH 08/23] * Added Hit//Run, Trial//Error and Bound//Determined. --- .../mage/sets/dissension/BoundDetermined.java | 179 ++++++++++++++++++ .../src/mage/sets/dissension/HitRun.java | 156 +++++++++++++++ .../src/mage/sets/dissension/TrialError.java | 133 +++++++++++++ .../effects/common/ExileSourceEffect.java | 26 +-- .../effects/common/ExileSpellEffect.java | 57 +++--- 5 files changed, 509 insertions(+), 42 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/dissension/BoundDetermined.java create mode 100644 Mage.Sets/src/mage/sets/dissension/HitRun.java create mode 100644 Mage.Sets/src/mage/sets/dissension/TrialError.java diff --git a/Mage.Sets/src/mage/sets/dissension/BoundDetermined.java b/Mage.Sets/src/mage/sets/dissension/BoundDetermined.java new file mode 100644 index 0000000000..5fb91193cc --- /dev/null +++ b/Mage.Sets/src/mage/sets/dissension/BoundDetermined.java @@ -0,0 +1,179 @@ +/* + * 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.dissension; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.cards.CardsImpl; +import mage.cards.SplitCard; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.FilterSpell; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.MulticoloredPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author LevelX2 + */ +public class BoundDetermined extends SplitCard { + + private static final FilterSpell filter = new FilterSpell("multicolored spell"); + + static { + filter.add(new MulticoloredPredicate()); + } + + public BoundDetermined(UUID ownerId) { + super(ownerId, 149, "Bound", "Determined", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{3}{B}{G}", "{G}{U}", false); + this.expansionSetCode = "DIS"; + + // Bound + // Sacrifice a creature. Return up to X cards from your graveyard to your hand, where X is the number of colors that creature was. Exile this card. + getLeftHalfCard().getSpellAbility().addEffect(new BoundEffect()); + Effect effect = new ExileSourceEffect(); + effect.setText("Exile this card"); + getLeftHalfCard().getSpellAbility().addEffect(effect); + // Determined + // Other spells you control can't be countered by spells or abilities this turn. + // Draw a card. + getRightHalfCard().getSpellAbility().addEffect(new DeterminedEffect()); + getRightHalfCard().getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + + } + + public BoundDetermined(final BoundDetermined card) { + super(card); + } + + @Override + public BoundDetermined copy() { + return new BoundDetermined(this); + } +} + +class BoundEffect extends OneShotEffect { + + public BoundEffect() { + super(Outcome.ReturnToHand); + this.staticText = "Sacrifice a creature. Return up to X cards from your graveyard to your hand, where X is the number of colors that creature was"; + } + + public BoundEffect(final BoundEffect effect) { + super(effect); + } + + @Override + public BoundEffect copy() { + return new BoundEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + TargetControlledPermanent target = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent("a creature (to sacrifice)"), true); + if (target.canChoose(source.getSourceId(), controller.getId(), game)) { + if (controller.chooseTarget(outcome, target, source, game)) { + Permanent toSacrifice = game.getPermanent(target.getFirstTarget()); + if (toSacrifice != null) { + toSacrifice.sacrifice(source.getSourceId(), game); + int colors = toSacrifice.getColor(game).getColorCount(); + if (colors > 0) { + TargetCardInYourGraveyard targetCard = new TargetCardInYourGraveyard(0, colors, + new FilterCard("up to " + colors + " card" + (colors > 1 ? "s" : "") + " from your graveyard")); + controller.chooseTarget(outcome, targetCard, source, game); + controller.moveCards(new CardsImpl(targetCard.getTargets()), null, Zone.HAND, source, game); + } + } + } + } + return true; + } + return false; + } +} + +class DeterminedEffect extends ContinuousRuleModifyingEffectImpl { + + DeterminedEffect() { + super(Duration.EndOfTurn, Outcome.Benefit); + staticText = "Other spells you control can't be countered by spells or abilities this turn"; + } + + DeterminedEffect(final DeterminedEffect effect) { + super(effect); + } + + @Override + public DeterminedEffect copy() { + return new DeterminedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public String getInfoMessage(Ability source, GameEvent event, Game game) { + MageObject sourceObject = game.getObject(source.getSourceId()); + if (sourceObject != null) { + return "This spell can't be countered (" + sourceObject.getIdName() + ")."; + } + return null; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COUNTER; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + return spell != null && !spell.getSourceId().equals(source.getSourceId()) && spell.getControllerId().equals(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/sets/dissension/HitRun.java b/Mage.Sets/src/mage/sets/dissension/HitRun.java new file mode 100644 index 0000000000..a7452d7791 --- /dev/null +++ b/Mage.Sets/src/mage/sets/dissension/HitRun.java @@ -0,0 +1,156 @@ +/* + * 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.dissension; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.SplitCard; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.target.common.TargetControlledPermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author LevelX2 + */ +public class HitRun extends SplitCard { + + public HitRun(UUID ownerId) { + super(ownerId, 152, "Hit", "Run", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{B}{R}", "{3}{R}{G}", false); + this.expansionSetCode = "DIS"; + + // Hit + // Target player sacrifices an artifact or creature. Hit deals damage to that player equal to that permanent's converted mana cost. + getLeftHalfCard().getSpellAbility().addEffect(new HitEffect()); + getLeftHalfCard().getSpellAbility().addTarget(new TargetPlayer()); + + // Run + // Attacking creatures you control get +1/+0 until end of turn for each other attacking creature. + getRightHalfCard().getSpellAbility().addEffect(new RunEffect()); + + } + + public HitRun(final HitRun card) { + super(card); + } + + @Override + public HitRun copy() { + return new HitRun(this); + } +} + +class HitEffect extends OneShotEffect { + + public HitEffect() { + super(Outcome.DestroyPermanent); + this.staticText = "Target player sacrifices an artifact or creature. Hit deals damage to that player equal to that permanent's converted mana cost"; + } + + public HitEffect(final HitEffect effect) { + super(effect); + } + + @Override + public HitEffect copy() { + return new HitEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player targetPlayer = game.getPlayer(source.getTargets().getFirstTarget()); + if (targetPlayer != null) { + FilterControlledPermanent filter = new FilterControlledPermanent("artifact or creature"); + filter.add(Predicates.or( + new CardTypePredicate(CardType.ARTIFACT), + new CardTypePredicate(CardType.CREATURE))); + TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, true); + + if (target.canChoose(targetPlayer.getId(), game)) { + targetPlayer.choose(Outcome.Sacrifice, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + permanent.sacrifice(source.getSourceId(), game); + int damage = permanent.getManaCost().convertedManaCost(); + if (damage > 0) { + targetPlayer.damage(damage, source.getSourceId(), game, false, true); + } + } + } + } + return true; + } +} + +class RunEffect extends OneShotEffect { + + public RunEffect() { + super(Outcome.BoostCreature); + this.staticText = "Attacking creatures you control get +1/+0 until end of turn for each other attacking creature"; + } + + public RunEffect(final RunEffect effect) { + super(effect); + } + + @Override + public RunEffect copy() { + return new RunEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int attackingCreatures = game.getBattlefield().count(new FilterAttackingCreature(), controller.getId(), controller.getId(), game); + if (attackingCreatures > 1) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterAttackingCreature(), controller.getId(), game)) { + ContinuousEffect effect = new BoostTargetEffect(attackingCreatures - 1, 0, Duration.EndOfTurn); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/dissension/TrialError.java b/Mage.Sets/src/mage/sets/dissension/TrialError.java new file mode 100644 index 0000000000..46877c7937 --- /dev/null +++ b/Mage.Sets/src/mage/sets/dissension/TrialError.java @@ -0,0 +1,133 @@ +/* + * 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.dissension; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.cards.Card; +import mage.cards.SplitCard; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.MulticoloredPredicate; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetSpell; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LevelX2 + */ +public class TrialError extends SplitCard { + + private static final FilterSpell filter = new FilterSpell("multicolored spell"); + + static { + filter.add(new MulticoloredPredicate()); + } + + public TrialError(UUID ownerId) { + super(ownerId, 158, "Trial", "Error", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{W}{U}", "{U}{B}", false); + this.expansionSetCode = "DIS"; + + // Trial + // Return all creatures blocking or blocked by target creature to their owner's hand. + getLeftHalfCard().getSpellAbility().addEffect(new TrialEffect()); + getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Error + // Counter target multicolored spell. + getRightHalfCard().getSpellAbility().addEffect(new CounterTargetEffect()); + getRightHalfCard().getSpellAbility().addTarget(new TargetSpell(filter)); + + } + + public TrialError(final TrialError card) { + super(card); + } + + @Override + public TrialError copy() { + return new TrialError(this); + } +} + +class TrialEffect extends OneShotEffect { + + public TrialEffect() { + super(Outcome.ReturnToHand); + this.staticText = "Return all creatures blocking or blocked by target creature to their owner's hand"; + } + + public TrialEffect(final TrialEffect effect) { + super(effect); + } + + @Override + public TrialEffect copy() { + return new TrialEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (controller != null && creature != null) { + Set toHand = new HashSet<>(); + for (CombatGroup combatGroup : game.getCombat().getGroups()) { + if (combatGroup.getBlockers().contains(creature.getId())) { + for (UUID attackerId : combatGroup.getAttackers()) { + Permanent attacker = game.getPermanent(attackerId); + if (attacker != null) { + toHand.add(attacker); + } + } + } else if (combatGroup.getAttackers().contains(creature.getId())) { + for (UUID blockerId : combatGroup.getBlockers()) { + Permanent blocker = game.getPermanent(blockerId); + if (blocker != null) { + toHand.add(blocker); + } + } + } + } + controller.moveCards(toHand, null, Zone.HAND, source, game); + return true; + } + return false; + } +} diff --git a/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java b/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java index f60cf67ba1..60efb497e9 100644 --- a/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileSourceEffect.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.effects.common; import java.util.UUID; @@ -49,11 +48,12 @@ public class ExileSourceEffect extends OneShotEffect { public ExileSourceEffect() { this(false); } - + /** - * - * @param toUniqueExileZone moves the card to a source object dependant unique exile zone, so another - * effect of the same source object (e.g. Deadeye Navigator) can identify the card + * + * @param toUniqueExileZone moves the card to a source object dependant + * unique exile zone, so another effect of the same source object (e.g. + * Deadeye Navigator) can identify the card */ public ExileSourceEffect(boolean toUniqueExileZone) { super(Outcome.Exile); @@ -72,10 +72,10 @@ public class ExileSourceEffect extends OneShotEffect { } @Override - public boolean apply(Game game, Ability source) { + public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); if (sourceObject instanceof Card) { UUID exileZoneId = null; String exileZoneName = ""; @@ -84,7 +84,7 @@ public class ExileSourceEffect extends OneShotEffect { exileZoneName = sourceObject.getName(); } Card sourceCard = (Card) sourceObject; - return controller.moveCardToExileWithInfo(sourceCard, exileZoneId, exileZoneName, source.getSourceId(), game, game.getState().getZone(sourceCard.getId()), true); + return controller.moveCardsToExile(sourceCard, source, game, true, exileZoneId, exileZoneName); } return true; } diff --git a/Mage/src/mage/abilities/effects/common/ExileSpellEffect.java b/Mage/src/mage/abilities/effects/common/ExileSpellEffect.java index df19c8af57..2725630dc3 100644 --- a/Mage/src/mage/abilities/effects/common/ExileSpellEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileSpellEffect.java @@ -1,31 +1,30 @@ /* -* 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. -*/ - + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ package mage.abilities.effects.common; import mage.abilities.Ability; @@ -58,14 +57,14 @@ public class ExileSpellEffect extends OneShotEffect implements MageSingleton { public ExileSpellEffect copy() { return fINSTANCE; } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Card spellCard = game.getStack().getSpell(source.getSourceId()).getCard(); if (spellCard != null) { - controller.moveCardToExileWithInfo(spellCard, null, "", source.getSourceId(), game, Zone.STACK, true); + controller.moveCards(spellCard, null, Zone.EXILED, source, game); } return true; } From 7b6860447122bc7111f90339859b3a399eaecb95 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 3 Oct 2015 13:33:22 +0200 Subject: [PATCH 09/23] Some changes/fixes to conspire ability. --- .../src/mage/sets/shadowmoor/AEthertow.java | 2 +- .../sets/shadowmoor/BarkshellBlessing.java | 8 +- .../src/mage/sets/shadowmoor/BurnTrail.java | 4 +- .../mage/sets/shadowmoor/DisturbingPlot.java | 2 +- .../sets/shadowmoor/GhastlyDiscovery.java | 2 +- .../mage/sets/shadowmoor/Giantbaiting.java | 2 +- .../mage/sets/shadowmoor/GleefulSabotage.java | 4 +- .../mage/sets/shadowmoor/MemorySluice.java | 6 +- .../mage/sets/shadowmoor/MineExcavation.java | 8 +- .../mage/sets/shadowmoor/TraitorsRoar.java | 2 +- .../abilities/keywords/ConspireTest.java | 95 ++++++--- Mage/src/mage/abilities/AbilityImpl.java | 32 +-- .../abilities/keyword/ConspireAbility.java | 191 +++++++++++------- Mage/src/mage/game/stack/StackObjImpl.java | 4 +- 14 files changed, 234 insertions(+), 128 deletions(-) diff --git a/Mage.Sets/src/mage/sets/shadowmoor/AEthertow.java b/Mage.Sets/src/mage/sets/shadowmoor/AEthertow.java index 2e384acbeb..14e313f4a5 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/AEthertow.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/AEthertow.java @@ -58,7 +58,7 @@ public class AEthertow extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } public AEthertow(final AEthertow card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/BarkshellBlessing.java b/Mage.Sets/src/mage/sets/shadowmoor/BarkshellBlessing.java index 7916a18bfe..db328a7899 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/BarkshellBlessing.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/BarkshellBlessing.java @@ -41,7 +41,7 @@ import mage.target.common.TargetCreaturePermanent; * @author jeffwadsworth */ public class BarkshellBlessing extends CardImpl { - + public BarkshellBlessing(UUID ownerId) { super(ownerId, 224, "Barkshell Blessing", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G/W}"); this.expansionSetCode = "SHM"; @@ -51,13 +51,13 @@ public class BarkshellBlessing extends CardImpl { this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn)); // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } - + public BarkshellBlessing(final BarkshellBlessing card) { super(card); } - + @Override public BarkshellBlessing copy() { return new BarkshellBlessing(this); diff --git a/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java b/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java index 0e4a193204..1a0e264ca3 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java @@ -48,9 +48,9 @@ public class BurnTrail extends CardImpl { // Burn Trail deals 3 damage to target creature or player. this.getSpellAbility().addEffect(new DamageTargetEffect(3)); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); - + // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } public BurnTrail(final BurnTrail card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/DisturbingPlot.java b/Mage.Sets/src/mage/sets/shadowmoor/DisturbingPlot.java index 5e22bf9f40..9d7cf44593 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/DisturbingPlot.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/DisturbingPlot.java @@ -51,7 +51,7 @@ public class DisturbingPlot extends CardImpl { this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard"))); // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/GhastlyDiscovery.java b/Mage.Sets/src/mage/sets/shadowmoor/GhastlyDiscovery.java index 2b317197e3..2ba3dd8d91 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/GhastlyDiscovery.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/GhastlyDiscovery.java @@ -52,7 +52,7 @@ public class GhastlyDiscovery extends CardImpl { this.getSpellAbility().addEffect(new GhastlyDiscoveryEffect()); // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.NONE)); } public GhastlyDiscovery(final GhastlyDiscovery card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/Giantbaiting.java b/Mage.Sets/src/mage/sets/shadowmoor/Giantbaiting.java index 43ca95f1ff..146c80a3a3 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/Giantbaiting.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/Giantbaiting.java @@ -58,7 +58,7 @@ public class Giantbaiting extends CardImpl { this.getSpellAbility().addEffect(new GiantbaitingEffect()); // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.NONE)); } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java b/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java index 16869be838..faf0258405 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java @@ -49,9 +49,9 @@ public class GleefulSabotage extends CardImpl { // Destroy target artifact or enchantment. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); - + // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } public GleefulSabotage(final GleefulSabotage card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/MemorySluice.java b/Mage.Sets/src/mage/sets/shadowmoor/MemorySluice.java index 61329fd77a..80c47736dd 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/MemorySluice.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/MemorySluice.java @@ -48,10 +48,10 @@ public class MemorySluice extends CardImpl { // Target player puts the top four cards of his or her library into his or her graveyard. this.getSpellAbility().addEffect(new PutLibraryIntoGraveTargetEffect(4)); this.getSpellAbility().addTarget(new TargetPlayer()); - + // Conspire - this.addAbility(new ConspireAbility(this)); - + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + } public MemorySluice(final MemorySluice card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/MineExcavation.java b/Mage.Sets/src/mage/sets/shadowmoor/MineExcavation.java index b26e24c4b1..4b5484404e 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/MineExcavation.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/MineExcavation.java @@ -43,9 +43,9 @@ import mage.target.common.TargetCardInGraveyard; * @author jeffwadsworth */ public class MineExcavation extends CardImpl { - + private static final FilterCard filter = new FilterCard("artifact or enchantment card in a graveyard"); - + static { filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ENCHANTMENT))); @@ -58,9 +58,9 @@ public class MineExcavation extends CardImpl { // Return target artifact or enchantment card from a graveyard to its owner's hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInGraveyard(filter)); - + // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } public MineExcavation(final MineExcavation card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/TraitorsRoar.java b/Mage.Sets/src/mage/sets/shadowmoor/TraitorsRoar.java index 90021a3dbb..df7145b391 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/TraitorsRoar.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/TraitorsRoar.java @@ -64,7 +64,7 @@ public class TraitorsRoar extends CardImpl { this.getSpellAbility().addEffect(new TraitorsRoarEffect()); // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java index eda7266347..a9ef3d1e76 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java @@ -25,7 +25,6 @@ * 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.keywords; import mage.constants.PhaseStep; @@ -40,31 +39,31 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class ConspireTest extends CardTestPlayerBase { /** - * 702.77. Conspire - * 702.77a Conspire is a keyword that represents two abilities. The first is a static ability that functions - * while the spell with conspire is on the stack. The second is a triggered ability that functions - * while the spell with conspire is on the stack. “Conspire” means “As an additional cost to cast - * this spell, you may tap two untapped creatures you control that each share a color with it” and - * “When you cast this spell, if its conspire cost was paid, copy it. If the spell has any targets, you - * may choose new targets for the copy.” Paying a spell’s conspire cost follows the rules for - * paying additional costs in rules 601.2b and 601.2e–g. - * - * 702.77b If a spell has multiple instances of conspire, each is paid separately and triggers based on - * its own payment, not any other instance of conspire - * - */ - - /** - * Burn Trail - * Sorcery, 3R (4) - * Burn Trail deals 3 damage to target creature or player. - * - * Conspire (As you cast this spell, you may tap two untapped creatures you - * control that share a color with it. When you do, copy it and you may - * choose a new target for the copy.) + * 702.77. Conspire 702.77a Conspire is a keyword that represents two + * abilities. The first is a static ability that functions while the spell + * with conspire is on the stack. The second is a triggered ability that + * functions while the spell with conspire is on the stack. “Conspire” means + * “As an additional cost to cast this spell, you may tap two untapped + * creatures you control that each share a color with it” and “When you cast + * this spell, if its conspire cost was paid, copy it. If the spell has any + * targets, you may choose new targets for the copy.” Paying a spell’s + * conspire cost follows the rules for paying additional costs in rules + * 601.2b and 601.2e–g. + * + * 702.77b If a spell has multiple instances of conspire, each is paid + * separately and triggers based on its own payment, not any other instance + * of conspire + * + */ + /** + * Burn Trail Sorcery, 3R (4) Burn Trail deals 3 damage to target creature + * or player. + * + * Conspire (As you cast this spell, you may tap two untapped creatures you + * control that share a color with it. When you do, copy it and you may + * choose a new target for the copy.) * */ - @Test public void testConspire() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); @@ -72,7 +71,6 @@ public class ConspireTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin"); addCard(Zone.HAND, playerA, "Burn Trail"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Burn Trail", playerB); setChoice(playerA, "Yes"); @@ -93,7 +91,6 @@ public class ConspireTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin"); addCard(Zone.HAND, playerA, "Burn Trail"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Burn Trail", playerB); setChoice(playerA, "No"); @@ -107,4 +104,50 @@ public class ConspireTest extends CardTestPlayerBase { } + @Test + public void testWortTheRaidmother() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 7); + // When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield. + // Each red or green instant or sorcery spell you cast has conspire. + // (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.) + addCard(Zone.HAND, playerA, "Wort, the Raidmother"); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wort, the Raidmother");// {4}{R/G}{R/G} + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "Yes"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPermanentCount(playerA, "Wort, the Raidmother", 1); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertLife(playerB, 14); + + } + + @Test + public void testWortTheRaidmotherWithConspireSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10); + addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin", 2); + // When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield. + // Each red or green instant or sorcery spell you cast has conspire. + // (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.) + addCard(Zone.HAND, playerA, "Wort, the Raidmother"); + addCard(Zone.HAND, playerA, "Burn Trail"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wort, the Raidmother"); // {4}{R/G}{R/G} + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Burn Trail", playerB); + setChoice(playerA, "Yes"); // use Conspire from Burn Trail itself + setChoice(playerA, "Yes"); // use Conspire gained from Wort, the Raidmother + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPermanentCount(playerA, "Wort, the Raidmother", 1); + assertLife(playerB, 11); + assertLife(playerA, 20); + assertGraveyardCount(playerA, "Burn Trail", 1); + + } } diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 5074663605..150ddd0747 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -445,20 +445,28 @@ public abstract class AbilityImpl implements Ability { public boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game) { boolean alternativeCostisUsed = false; if (sourceObject != null && !(sourceObject instanceof Permanent) && !(this instanceof FlashbackAbility)) { - for (Ability ability : sourceObject.getAbilities()) { - // if cast for noMana no Alternative costs are allowed - if (!noMana && ability instanceof AlternativeSourceCosts) { - AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability; - if (alternativeSpellCosts.isAvailable(this, game)) { - if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) { - // only one alternative costs may be activated - alternativeCostisUsed = true; - break; + Abilities abilities = null; + if (sourceObject instanceof Card) { + abilities = ((Card) sourceObject).getAbilities(game); + } else { + sourceObject.getAbilities(); + } + if (abilities != null) { + for (Ability ability : abilities) { + // if cast for noMana no Alternative costs are allowed + if (!noMana && ability instanceof AlternativeSourceCosts) { + AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability; + if (alternativeSpellCosts.isAvailable(this, game)) { + if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) { + // only one alternative costs may be activated + alternativeCostisUsed = true; + break; + } } } - } - if (ability instanceof OptionalAdditionalSourceCosts) { - ((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game); + if (ability instanceof OptionalAdditionalSourceCosts) { + ((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game); + } } } // controller specific alternate spell costs diff --git a/Mage/src/mage/abilities/keyword/ConspireAbility.java b/Mage/src/mage/abilities/keyword/ConspireAbility.java index fe470c425f..7d48fe50e0 100644 --- a/Mage/src/mage/abilities/keyword/ConspireAbility.java +++ b/Mage/src/mage/abilities/keyword/ConspireAbility.java @@ -27,14 +27,16 @@ */ package mage.abilities.keyword; +import java.util.HashSet; import java.util.Iterator; +import java.util.Set; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.Cost; import mage.abilities.costs.Costs; -import mage.abilities.costs.OptionalAdditionalCost; import mage.abilities.costs.OptionalAdditionalCostImpl; import mage.abilities.costs.OptionalAdditionalSourceCosts; import mage.abilities.costs.common.TapTargetCost; @@ -50,48 +52,77 @@ import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.common.TargetControlledPermanent; -/** - * 702.77. Conspire 702.77a Conspire is a keyword that represents two abilities. - * The first is a static ability that functions while the spell with conspire is - * on the stack. The second is a triggered ability that functions while the - * spell with conspire is on the stack. "Conspire" means "As an additional cost - * to cast this spell, you may tap two untapped creatures you control that each - * share a color with it" and "When you cast this spell, if its conspire cost - * was paid, copy it. If the spell has any targets, you may choose new targets - * for the copy." Paying a spell’s conspire cost follows the rules for paying - * additional costs in rules 601.2b and 601.2e–g. 702.77b If a spell has - * multiple instances of conspire, each is paid separately and triggers based on - * its own payment, not any other instance of conspire. * +/* + * 702.77. Conspire + * 702.77a Conspire is a keyword that represents two abilities. + * The first is a static ability that functions while the spell with conspire is on the stack. + * The second is a triggered ability that functions while the spell with conspire is on the stack. + * "Conspire" means "As an additional cost to cast this spell, + * you may tap two untapped creatures you control that each share a color with it" + * and "When you cast this spell, if its conspire cost was paid, copy it. + * If the spell has any targets, you may choose new targets for the copy." + * Paying a spell’s conspire cost follows the rules for paying additional costs in rules 601.2b and 601.2e–g. + * 702.77b If a spell has multiple instances of conspire, each is paid separately and triggers + * based on its own payment, not any other instance of conspire. * * * @author jeffwadsworth heavily based off the replicate keyword by LevelX */ public class ConspireAbility extends StaticAbility implements OptionalAdditionalSourceCosts { private static final String keywordText = "Conspire"; - private static final String reminderTextCost = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.)"; - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("two untapped creatures you control that share a color with it"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creatures you control that share a color with it"); + protected static final String CONSPIRE_ACTIVATION_KEY = "ConspireActivation"; static { filter.add(Predicates.not(new TappedPredicate())); filter.add(new SharesColorWithSourcePredicate()); } - Cost costConspire = new TapTargetCost(new TargetControlledPermanent(2, 2, filter, true)); - OptionalAdditionalCost conspireCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderTextCost, costConspire); + public enum ConspireTargets { - public ConspireAbility(Card card) { + NONE, + ONE, + MORE + } + + private UUID conspireId; + private String reminderText; + private OptionalAdditionalCostImpl conspireCost; + + /** + * Unique Id for a ConspireAbility but may not change while a continuous + * effect gives Conspire + * + * @param conspireId + * @param conspireTargets controls the content of the reminder text + */ + public ConspireAbility(UUID conspireId, ConspireTargets conspireTargets) { super(Zone.STACK, null); - setRuleAtTheTop(false); - addSubAbility(new ConspireTriggeredAbility()); + this.conspireId = conspireId; + switch (conspireTargets) { + case NONE: + reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it.)"; + break; + case ONE: + reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.)"; + break; + case MORE: + reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new targets for the copy.)"; + break; + } + conspireCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderText, + new TapTargetCost(new TargetControlledPermanent(2, 2, filter, true))); + addSubAbility(new ConspireTriggeredAbility(conspireId)); } public ConspireAbility(final ConspireAbility ability) { super(ability); - conspireCost = ability.conspireCost; + this.conspireId = ability.conspireId; + this.conspireCost = ability.conspireCost.copy(); + this.reminderText = ability.reminderText; } @Override @@ -106,18 +137,21 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional } } - @Override - public boolean isActivated() { - if (conspireCost != null) { - return conspireCost.isActivated(); - } - return false; + public UUID getConspireId() { + return conspireId; } - public void resetConspire() { - if (conspireCost != null) { - conspireCost.reset(); + @Override + public boolean isActivated() { + throw new UnsupportedOperationException("Use ConspireAbility.isActivated(Ability ability, Game game) method instead!"); + } + + public boolean isActivated(Ability ability, Game game) { + Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); + if (activations != null) { + return activations.contains(getConspireId()); } + return false; } @Override @@ -125,9 +159,9 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional if (ability instanceof SpellAbility) { Player player = game.getPlayer(controllerId); if (player != null) { - this.resetConspire(); - if (player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(conspireCost.getText(false)).append(" ?").toString(), ability, game)) { - conspireCost.activate(); + resetConspire(ability, game); + if (player.chooseUse(Outcome.Benefit, "Pay " + conspireCost.getText(false) + " ?", ability, game)) { + activateConspire(ability, game); for (Iterator it = ((Costs) conspireCost).iterator(); it.hasNext();) { Cost cost = (Cost) it.next(); ability.getCosts().add(cost.copy()); @@ -137,6 +171,22 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional } } + private void activateConspire(Ability ability, Game game) { + Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); + if (activations == null) { + activations = new HashSet<>(); + game.getState().setValue(CONSPIRE_ACTIVATION_KEY + ability.getId(), activations); + } + activations.add(getConspireId()); + } + + private void resetConspire(Ability ability, Game game) { + Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); + if (activations != null) { + activations.remove(getConspireId()); + } + } + @Override public String getRule() { StringBuilder sb = new StringBuilder(); @@ -167,13 +217,17 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional class ConspireTriggeredAbility extends TriggeredAbilityImpl { - public ConspireTriggeredAbility() { + private UUID conspireId; + + public ConspireTriggeredAbility(UUID conspireId) { super(Zone.STACK, new ConspireEffect()); + this.conspireId = conspireId; this.setRuleVisible(false); } private ConspireTriggeredAbility(final ConspireTriggeredAbility ability) { super(ability); + this.conspireId = ability.conspireId; } @Override @@ -188,20 +242,18 @@ class ConspireTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.sourceId)) { - StackObject spell = game.getStack().getStackObject(this.sourceId); - if (spell instanceof Spell) { - Card card = game.getCard(spell.getSourceId()); - if (card != null) { - for (Ability ability : card.getAbilities()) { - if (ability instanceof ConspireAbility) { - if (((ConspireAbility) ability).isActivated()) { - for (Effect effect : this.getEffects()) { - effect.setValue("ConspireSpell", spell); - } - return true; + if (event.getSourceId().equals(getSourceId())) { + Spell spell = game.getStack().getSpell(event.getSourceId()); + for (Ability ability : spell.getAbilities(game)) { + if (ability instanceof ConspireAbility + && ((ConspireAbility) ability).getConspireId().equals(getConspireId())) { + if (((ConspireAbility) ability).isActivated(spell.getSpellAbility(), game)) { + for (Effect effect : this.getEffects()) { + if (effect instanceof ConspireEffect) { + ((ConspireEffect) effect).setConspiredSpell(spell); } } + return true; } } } @@ -209,52 +261,53 @@ class ConspireTriggeredAbility extends TriggeredAbilityImpl { return false; } + public UUID getConspireId() { + return conspireId; + } + @Override public String getRule() { - return "Conspire: As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.)"; + return "When you pay the conspire costs, copy it and you may choose a new target for the copy."; } } class ConspireEffect extends OneShotEffect { + private Spell conspiredSpell; + public ConspireEffect() { super(Outcome.Copy); } public ConspireEffect(final ConspireEffect effect) { super(effect); + this.conspiredSpell = effect.conspiredSpell; } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Spell spell = (Spell) this.getValue("ConspireSpell"); - if (spell != null) { - Card card = game.getCard(spell.getSourceId()); - if (card != null) { - for (Ability ability : card.getAbilities()) { - if (ability instanceof ConspireAbility) { - if (((ConspireAbility) ability).isActivated()) { - ((ConspireAbility) ability).resetConspire(); - } - } - } - Spell copy = spell.copySpell(); - copy.setControllerId(source.getControllerId()); - copy.setCopiedSpell(true); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); - if (!game.isSimulation()) { - game.informPlayers(new StringBuilder(controller.getLogName()).append(copy.getActivatedMessage(game)).toString()); - } - return true; + if (controller != null && conspiredSpell != null) { + Card card = game.getCard(conspiredSpell.getSourceId()); + if (card != null) { + Spell copy = conspiredSpell.copySpell(); + copy.setControllerId(source.getControllerId()); + copy.setCopiedSpell(true); + game.getStack().push(copy); + copy.chooseNewTargets(game, source.getControllerId()); + if (!game.isSimulation()) { + game.informPlayers(controller.getLogName() + copy.getActivatedMessage(game)); } + return true; } } return false; } + public void setConspiredSpell(Spell conspiredSpell) { + this.conspiredSpell = conspiredSpell; + } + @Override public ConspireEffect copy() { return new ConspireEffect(this); diff --git a/Mage/src/mage/game/stack/StackObjImpl.java b/Mage/src/mage/game/stack/StackObjImpl.java index 8ca3c7a6be..6cf33e65ea 100644 --- a/Mage/src/mage/game/stack/StackObjImpl.java +++ b/Mage/src/mage/game/stack/StackObjImpl.java @@ -106,6 +106,7 @@ public abstract class StackObjImpl implements StackObject { public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { Player targetController = game.getPlayer(targetControllerId); if (targetController != null) { + StringBuilder oldTargetDescription = new StringBuilder(); StringBuilder newTargetDescription = new StringBuilder(); // Fused split spells or spells where "Splice on Arcane" was used can have more than one ability Abilities objectAbilities = new AbilitiesImpl<>(); @@ -118,6 +119,7 @@ public abstract class StackObjImpl implements StackObject { // Some spells can have more than one mode for (UUID modeId : ability.getModes().getSelectedModes()) { Mode mode = ability.getModes().get(modeId); + oldTargetDescription.append(ability.getTargetDescription(mode.getTargets(), game)); for (Target target : mode.getTargets()) { Target newTarget = chooseNewTarget(targetController, ability, mode, target, forceChange, filterNewTarget, game); // clear the old target and copy all targets from new target @@ -131,7 +133,7 @@ public abstract class StackObjImpl implements StackObject { } } - if (newTargetDescription.length() > 0 && !game.isSimulation()) { + if (!newTargetDescription.toString().equals(oldTargetDescription.toString()) && !game.isSimulation()) { game.informPlayers(this.getLogName() + " is now " + newTargetDescription.toString()); } return true; From d10e63bea4e2078398a5bcf634dca5f86d766755 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 3 Oct 2015 13:33:40 +0200 Subject: [PATCH 10/23] Added Wort, the Raidmother. --- .../sets/shadowmoor/WortTheRaidmother.java | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/shadowmoor/WortTheRaidmother.java diff --git a/Mage.Sets/src/mage/sets/shadowmoor/WortTheRaidmother.java b/Mage.Sets/src/mage/sets/shadowmoor/WortTheRaidmother.java new file mode 100644 index 0000000000..d44ba05813 --- /dev/null +++ b/Mage.Sets/src/mage/sets/shadowmoor/WortTheRaidmother.java @@ -0,0 +1,139 @@ +/* + * 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.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.ConspireAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.permanent.token.Token; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; + +/** + * + * @author LevelX2 + */ +public class WortTheRaidmother extends CardImpl { + + public WortTheRaidmother(UUID ownerId) { + super(ownerId, 223, "Wort, the Raidmother", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{4}{R/G}{R/G}"); + this.expansionSetCode = "SHM"; + this.supertype.add("Legendary"); + this.subtype.add("Goblin"); + this.subtype.add("Shaman"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WortTheRaidmotherToken(), 2), false)); + + // Each red or green instant or sorcery spell you cast has conspire. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new WortGainConspireEffect())); + } + + public WortTheRaidmother(final WortTheRaidmother card) { + super(card); + } + + @Override + public WortTheRaidmother copy() { + return new WortTheRaidmother(this); + } +} + +class WortGainConspireEffect extends ContinuousEffectImpl { + + private final static FilterInstantOrSorcerySpell filter = new FilterInstantOrSorcerySpell(); + + static { + filter.add(Predicates.or(new ColorPredicate(ObjectColor.RED), new ColorPredicate(ObjectColor.GREEN))); + } + private final ConspireAbility conspireAbility; + + public WortGainConspireEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "Each red or green instant or sorcery spell you cast has conspire. (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.)"; + conspireAbility = new ConspireAbility(getId(), ConspireAbility.ConspireTargets.MORE); + } + + public WortGainConspireEffect(final WortGainConspireEffect effect) { + super(effect); + this.conspireAbility = new ConspireAbility(getId(), ConspireAbility.ConspireTargets.MORE); + } + + @Override + public WortGainConspireEffect copy() { + return new WortGainConspireEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (StackObject stackObject : game.getStack()) { + // only spells cast, so no copies of spells + if ((stackObject instanceof Spell) && !stackObject.isCopy() && stackObject.getControllerId().equals(source.getControllerId())) { + Spell spell = (Spell) stackObject; + if (filter.match(stackObject, game)) { + game.getState().addOtherAbility(spell.getCard(), conspireAbility); + } + } + } + return true; + } +} + +class WortTheRaidmotherToken extends Token { + + public WortTheRaidmotherToken() { + super("Goblin Warrior", "1/1 red and green Goblin Warrior creature token"); + cardType.add(CardType.CREATURE); + color.setRed(true); + color.setGreen(true); + subtype.add("Goblin"); + subtype.add("Warrior"); + power = new MageInt(1); + toughness = new MageInt(1); + } +} From f6ec543b1b8f1d342a57d404a75352c2d10d2911 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 3 Oct 2015 18:20:59 +0200 Subject: [PATCH 11/23] TestPlayer fixed that target events created by RestPlayer are reset back if ability activation failed. --- .../test/java/org/mage/test/player/TestPlayer.java | 11 ++++++++--- Mage/src/mage/players/PlayerImpl.java | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 3abe238a3b..f168cd1c3d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -370,6 +370,7 @@ public class TestPlayer implements Player { } for (Ability ability : computerPlayer.getPlayable(game, true)) { if (ability.toString().startsWith(groups[0])) { + int bookmark = game.bookmarkState(); Ability newAbility = ability.copy(); if (groups.length > 1 && !groups[1].equals("target=NO_TARGET")) { if (!addTargets(newAbility, groups, game)) { @@ -377,9 +378,13 @@ public class TestPlayer implements Player { break; } } - computerPlayer.activateAbility((ActivatedAbility) newAbility, game); - actions.remove(action); - return true; + if (computerPlayer.activateAbility((ActivatedAbility) newAbility, game)) { + actions.remove(action); + return true; + } else { + game.restoreState(bookmark, ability.getRule()); + } + } } } else if (action.getAction().startsWith("manaActivate:")) { diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 3ce3070c96..b6d6cb2405 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -1105,7 +1105,7 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } - private void restoreState(int bookmark, String text, Game game) { + protected void restoreState(int bookmark, String text, Game game) { game.restoreState(bookmark, text); if (storedBookmark >= bookmark) { resetStoredBookmark(game); From 85f0cc6bb3e42da4e57ca26760773a0a01196fc0 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 3 Oct 2015 18:22:14 +0200 Subject: [PATCH 12/23] Fixed variable remove counter costs to work also correctly if X=0 (e.g. Retribution of the Ancients and Willbreaker). --- .../test/cards/control/ItThatBetraysTest.java | 15 ++-- .../test/cards/control/WillbreakerTest.java | 73 +++++++++++++++++++ .../abilities/costs/VariableCostImpl.java | 3 +- .../costs/common/RemoveCounterCost.java | 3 + 4 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/control/WillbreakerTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/ItThatBetraysTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/ItThatBetraysTest.java index e48bd054ec..3dd7735cd8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/control/ItThatBetraysTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/ItThatBetraysTest.java @@ -27,7 +27,6 @@ */ package org.mage.test.cards.control; -import mage.abilities.keyword.HasteAbility; import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; @@ -37,15 +36,15 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author LevelX2 */ - public class ItThatBetraysTest extends CardTestPlayerBase { /** * https://github.com/magefree/mage/issues/796 * - * When an opponent sacrifices a fetchland and you have an It That Betrays in play, - * sacrificing the fetchland that comes under your control from its ability returns - * it to play under your control, allowing you to fetch infinite lands. + * When an opponent sacrifices a fetchland and you have an It That Betrays + * in play, sacrificing the fetchland that comes under your control from its + * ability returns it to play under your control, allowing you to fetch + * infinite lands. * */ @Test @@ -62,7 +61,7 @@ public class ItThatBetraysTest extends CardTestPlayerBase { assertLife(playerA, 19); assertLife(playerB, 19); - + // Going to graveyard if player B sacrifices it assertGraveyardCount(playerA, "Flooded Strand", 1); } @@ -71,7 +70,7 @@ public class ItThatBetraysTest extends CardTestPlayerBase { @Test public void testExileItThatBetraysEffect() { addCard(Zone.BATTLEFIELD, playerA, "Flooded Strand", 1); - + addCard(Zone.BATTLEFIELD, playerA, "Rest in Peace", 1); addCard(Zone.BATTLEFIELD, playerB, "It That Betrays"); @@ -83,7 +82,7 @@ public class ItThatBetraysTest extends CardTestPlayerBase { assertLife(playerA, 19); assertLife(playerB, 20); - + // Player B now controls a Flooded Strand, even though it went to exile assertPermanentCount(playerB, "Flooded Strand", 1); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/WillbreakerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/WillbreakerTest.java new file mode 100644 index 0000000000..92b78b12a0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/WillbreakerTest.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 org.mage.test.cards.control; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class WillbreakerTest extends CardTestPlayerBase { + + /** + * http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=17664&start=120#p186736 + * + * I tried to activate Retribution of the Ancients while I only controlled + * Nantuko Husk and Willbreaker without +1/+1 counters on them. The program + * didn't let me activate the RotA, even though the creature you choose + * doesn't need to have any counters on it (X can be 0, doesn't say + * otherwise on the card and it isn't a target requirement). It asked me to + * pay B, then choose a creature I controlled, which I did... and then + * nothing happened. + * + */ + @Test + public void testRetributionOfTheAncientsZeroCounter() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + // {B}, Remove X +1/+1 counters from among creatures you control: Target creature gets -X/-X until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Retribution of the Ancients", 1); // Enchantment {B} + // Whenever a creature an opponent controls becomes the target of a spell or ability you control, gain control of that creature for as long as you control Willbreaker. + addCard(Zone.BATTLEFIELD, playerA, "Willbreaker", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{B},Remove", "Silvercoat Lion"); + setChoice(playerA, "X=0"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Willbreaker", 1); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + } + +} diff --git a/Mage/src/mage/abilities/costs/VariableCostImpl.java b/Mage/src/mage/abilities/costs/VariableCostImpl.java index c020d7922b..68557546df 100644 --- a/Mage/src/mage/abilities/costs/VariableCostImpl.java +++ b/Mage/src/mage/abilities/costs/VariableCostImpl.java @@ -157,8 +157,7 @@ public abstract class VariableCostImpl implements Cost, VariableCost { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { xValue = controller.announceXCost(getMinValue(source, game), getMaxValue(source, game), - new StringBuilder("Announce the number of ").append(actionText).toString(), - game, source, this); + "Announce the number of " + actionText, game, source, this); } return xValue; } diff --git a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java index 85f7676eb4..0167f67807 100644 --- a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java +++ b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java @@ -84,6 +84,9 @@ public class RemoveCounterCost extends CostImpl { int countersRemoved = 0; Player controller = game.getPlayer(controllerId); if (controller != null) { + if (countersToRemove == 0) { // Can happen when used for X costs where X = 0; + return paid = true; + } target.clearChosen(); if (target.choose(Outcome.UnboostCreature, controllerId, sourceId, game)) { for (UUID targetId : target.getTargets()) { From f24a1b38986fe846c6de10134fd0dc0c8bcd6aa7 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 3 Oct 2015 19:23:58 +0200 Subject: [PATCH 13/23] * Fixed check of dies attched triggered abilities not always triggering if attachment and attached object went to graveyard at the same time. --- .../cards/enchantments/SkullclampTest.java | 88 +++++++++++++++++++ .../common/DiesAttachedTriggeredAbility.java | 23 +++-- Mage/src/mage/game/permanent/Permanent.java | 2 + .../mage/game/permanent/PermanentImpl.java | 8 ++ 4 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/enchantments/SkullclampTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/SkullclampTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/SkullclampTest.java new file mode 100644 index 0000000000..8f4e7415e6 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/SkullclampTest.java @@ -0,0 +1,88 @@ +/* + * 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.enchantments; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class SkullclampTest extends CardTestPlayerBase { + + /** + * Skullclamp and the creature it's attached to are destroyed by same + * Pernicious Deed activation. AFAIK Skullclamp should trigger, but it + * doesn't. + * + * 400.7e Abilities of Auras that trigger when the enchanted permanent + * leaves the battlefield can find the new object that Aura became in its + * owners graveyard if it was put into that graveyard at the same time the + * enchanted permanent left the battlefield. It can also find the new object + * that Aura became in its owners graveyard as a result of being put there + * as a state-based action for not being attached to a permanent. (See rule + * 704.5n.) + * + */ + @Test + public void testPerniciousDeed() { + // Equipped creature gets +1/-1. + // Whenever equipped creature dies, draw two cards. + // Equip {1} + addCard(Zone.BATTLEFIELD, playerA, "Skullclamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + // {X}, Sacrifice Pernicious Deed: Destroy each artifact, creature, and enchantment with converted mana cost X or less. + addCard(Zone.BATTLEFIELD, playerB, "Pernicious Deed"); // Enchantment + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Silvercoat Lion"); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "{X},Sacrifice"); + setChoice(playerB, "X=2"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Skullclamp", 1); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + + assertGraveyardCount(playerB, "Pernicious Deed", 1); + + assertPermanentCount(playerA, "Pillarfield Ox", 1); + + assertHandCount(playerA, 2); + } + +} diff --git a/Mage/src/mage/abilities/common/DiesAttachedTriggeredAbility.java b/Mage/src/mage/abilities/common/DiesAttachedTriggeredAbility.java index 78fc74d57c..83adbea699 100644 --- a/Mage/src/mage/abilities/common/DiesAttachedTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/DiesAttachedTriggeredAbility.java @@ -1,21 +1,23 @@ package mage.abilities.common; -import mage.constants.Zone; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; /** * "When enchanted/equipped creature dies" triggered ability + * * @author Loki */ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl { - + private String attachedDescription; private boolean diesRuleText; - + public DiesAttachedTriggeredAbility(Effect effect, String attachedDescription) { this(effect, attachedDescription, false); } @@ -30,7 +32,6 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl { this.diesRuleText = diesRuleText; } - public DiesAttachedTriggeredAbility(final DiesAttachedTriggeredAbility ability) { super(ability); this.attachedDescription = ability.attachedDescription; @@ -49,9 +50,21 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((ZoneChangeEvent)event).isDiesEvent()) { + if (((ZoneChangeEvent) event).isDiesEvent()) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + boolean triggered = false; if (zEvent.getTarget().getAttachments().contains(this.getSourceId())) { + triggered = true; + } else { + // If both (attachment and attached went to graveyard at the same time, the attachemnets can be already removed from the attached object.) + // So check here with the LKI of the enchantment + Permanent attachment = game.getPermanentOrLKIBattlefield(getSourceId()); + if (attachment != null && attachment.getAttachedTo().equals(zEvent.getTargetId()) + && attachment.getAttachedToZoneChangeCounter() == zEvent.getTarget().getZoneChangeCounter(game)) { + triggered = true; + } + } + if (triggered) { for (Effect effect : getEffects()) { effect.setValue("attachedTo", zEvent.getTarget()); } diff --git a/Mage/src/mage/game/permanent/Permanent.java b/Mage/src/mage/game/permanent/Permanent.java index 84ae5f3986..b7e8178a81 100644 --- a/Mage/src/mage/game/permanent/Permanent.java +++ b/Mage/src/mage/game/permanent/Permanent.java @@ -109,6 +109,8 @@ public interface Permanent extends Card, Controllable { UUID getAttachedTo(); + int getAttachedToZoneChangeCounter(); + void attachTo(UUID permanentId, Game game); boolean addAttachment(UUID permanentId, Game game); diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index 93b2f355be..f5be69ff35 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -115,6 +115,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { protected Map> connectedCards = new HashMap<>(); protected HashSet dealtDamageByThisTurn; protected UUID attachedTo; + protected int attachedToZoneChangeCounter; protected UUID pairedCard; protected Counters counters; protected List markedDamage; @@ -172,6 +173,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } this.counters = permanent.counters.copy(); this.attachedTo = permanent.attachedTo; + this.attachedToZoneChangeCounter = permanent.attachedToZoneChangeCounter; this.minBlockedBy = permanent.minBlockedBy; this.maxBlockedBy = permanent.maxBlockedBy; this.transformed = permanent.transformed; @@ -676,6 +678,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return attachedTo; } + @Override + public int getAttachedToZoneChangeCounter() { + return attachedToZoneChangeCounter; + } + @Override public void addConnectedCard(String key, UUID connectedCard) { if (this.connectedCards.containsKey(key)) { @@ -712,6 +719,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } } this.attachedTo = permanentId; + this.attachedToZoneChangeCounter = game.getState().getZoneChangeCounter(permanentId); for (Ability ability : this.getAbilities()) { for (Iterator ite = ability.getEffects(game, EffectType.CONTINUOUS).iterator(); ite.hasNext();) { ContinuousEffect effect = (ContinuousEffect) ite.next(); From a2eed534eece60e32b27d14d08de75bd20ffc074 Mon Sep 17 00:00:00 2001 From: BursegSardaukar <> Date: Sat, 3 Oct 2015 15:48:58 -0400 Subject: [PATCH 14/23] Merge origin/master Conflicts: Mage.Sets/src/mage/sets/dissension/UtvaraScalper.java --- .../mage/sets/planarchaos/FirefrightMage.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/sets/planarchaos/FirefrightMage.java b/Mage.Sets/src/mage/sets/planarchaos/FirefrightMage.java index c5edfd6b4b..741bfabb0a 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/FirefrightMage.java +++ b/Mage.Sets/src/mage/sets/planarchaos/FirefrightMage.java @@ -35,10 +35,12 @@ import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleEvasionAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.cards.CardImpl; import mage.constants.Duration; import mage.constants.Zone; @@ -54,17 +56,16 @@ import mage.target.common.TargetCreaturePermanent; */ public class FirefrightMage extends CardImpl { - private final static FilterCreaturePermanent notArtifactOrRed = new FilterCreaturePermanent("except by artifact creatures and/or white creatures"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("except by artifact creatures and/or red creatures"); static { - notArtifactOrRed.add(Predicates.not( + filter.add(Predicates.not( Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new ColorPredicate(ObjectColor.RED) - ) - )); + Predicates.and(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.CREATURE)), + Predicates.and(new CardTypePredicate(CardType.CREATURE), new ColorPredicate(ObjectColor.RED) + )))); } - + public FirefrightMage(UUID ownerId) { super(ownerId, 99, "Firefright Mage", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{R}"); this.expansionSetCode = "PLC"; @@ -75,7 +76,7 @@ public class FirefrightMage extends CardImpl { this.toughness = new MageInt(1); //{1} {R}, {T}, Discard a card: Target creature can't be blocked this turn except by artifact creatures and/or red creatures. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedByCreaturesSourceEffect(notArtifactOrRed, Duration.EndOfTurn), new ManaCostsImpl("{1}{R}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(new SimpleEvasionAbility(new CantBeBlockedByCreaturesSourceEffect(filter, Duration.EndOfTurn)), Duration.EndOfTurn),new ManaCostsImpl("{1}{R}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); ability.addTarget(new TargetCreaturePermanent()); From 208a890d1d244855eb5225f35c41942cc61f0d0d Mon Sep 17 00:00:00 2001 From: emerald000 Date: Sun, 4 Oct 2015 01:57:37 -0400 Subject: [PATCH 15/23] Added Chainer, Dementia Master; Concerted Effort; Spellweaver Helix; Thought Lash and Tithe. Fixed OpponentControlsMoreCondition spelling. --- .../src/mage/sets/alliances/ThoughtLash.java | 52 ++++ .../sets/commander2014/GiftOfEstates.java | 4 +- Mage.Sets/src/mage/sets/legends/LandTax.java | 4 +- .../sets/masterseditionii/ThoughtLash.java | 182 ++++++++++++++ .../mage/sets/mirrodin/SpellweaverHelix.java | 231 ++++++++++++++++++ .../sets/onslaught/WeatheredWayfarer.java | 4 +- .../mage/sets/ravnica/ConcertedEffort.java | 164 +++++++++++++ .../shardsofalara/KnightOfTheWhiteOrchid.java | 4 +- .../sets/torment/ChainerDementiaMaster.java | 144 +++++++++++ Mage.Sets/src/mage/sets/visions/Tithe.java | 98 ++++++++ ...ava => OpponentControlsMoreCondition.java} | 4 +- .../keyword/CumulativeUpkeepAbility.java | 46 ++-- Mage/src/mage/game/events/GameEvent.java | 2 +- 13 files changed, 903 insertions(+), 36 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/alliances/ThoughtLash.java create mode 100644 Mage.Sets/src/mage/sets/masterseditionii/ThoughtLash.java create mode 100644 Mage.Sets/src/mage/sets/mirrodin/SpellweaverHelix.java create mode 100644 Mage.Sets/src/mage/sets/ravnica/ConcertedEffort.java create mode 100644 Mage.Sets/src/mage/sets/torment/ChainerDementiaMaster.java create mode 100644 Mage.Sets/src/mage/sets/visions/Tithe.java rename Mage/src/mage/abilities/condition/common/{OpponentControllsMoreCondition.java => OpponentControlsMoreCondition.java} (92%) diff --git a/Mage.Sets/src/mage/sets/alliances/ThoughtLash.java b/Mage.Sets/src/mage/sets/alliances/ThoughtLash.java new file mode 100644 index 0000000000..6f9c9f2720 --- /dev/null +++ b/Mage.Sets/src/mage/sets/alliances/ThoughtLash.java @@ -0,0 +1,52 @@ +/* + * 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.alliances; + +import java.util.UUID; + +/** + * + * @author emerald000 + */ +public class ThoughtLash extends mage.sets.masterseditionii.ThoughtLash { + + public ThoughtLash(UUID ownerId) { + super(ownerId); + this.cardNumber = 58; + this.expansionSetCode = "ALL"; + } + + public ThoughtLash(final ThoughtLash card) { + super(card); + } + + @Override + public ThoughtLash copy() { + return new ThoughtLash(this); + } +} diff --git a/Mage.Sets/src/mage/sets/commander2014/GiftOfEstates.java b/Mage.Sets/src/mage/sets/commander2014/GiftOfEstates.java index c87bb2c1f4..e4c6731a64 100644 --- a/Mage.Sets/src/mage/sets/commander2014/GiftOfEstates.java +++ b/Mage.Sets/src/mage/sets/commander2014/GiftOfEstates.java @@ -28,7 +28,7 @@ package mage.sets.commander2014; import java.util.UUID; -import mage.abilities.condition.common.OpponentControllsMoreCondition; +import mage.abilities.condition.common.OpponentControlsMoreCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; @@ -60,7 +60,7 @@ public class GiftOfEstates extends CardImpl { // If an opponent controls more lands than you, search your library for up to three Plains cards, reveal them, and put them into your hand. Then shuffle your library. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 3, filter), true), - new OpponentControllsMoreCondition(new FilterLandPermanent("lands")))); + new OpponentControlsMoreCondition(new FilterLandPermanent("lands")))); } public GiftOfEstates(final GiftOfEstates card) { diff --git a/Mage.Sets/src/mage/sets/legends/LandTax.java b/Mage.Sets/src/mage/sets/legends/LandTax.java index ffd80349b0..823758648d 100644 --- a/Mage.Sets/src/mage/sets/legends/LandTax.java +++ b/Mage.Sets/src/mage/sets/legends/LandTax.java @@ -29,7 +29,7 @@ package mage.sets.legends; import java.util.UUID; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.OpponentControllsMoreCondition; +import mage.abilities.condition.common.OpponentControlsMoreCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; @@ -53,7 +53,7 @@ public class LandTax extends CardImpl { // At the beginning of your upkeep, if an opponent controls more lands than you, you may search your library for up to three basic land cards, reveal them, and put them into your hand. If you do, shuffle your library. this.addAbility(new ConditionalTriggeredAbility( new BeginningOfUpkeepTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 3, new FilterBasicLandCard()), true), TargetController.YOU, true), - new OpponentControllsMoreCondition(new FilterLandPermanent("lands")), + new OpponentControlsMoreCondition(new FilterLandPermanent("lands")), "At the beginning of your upkeep, if an opponent controls more lands than you, you may search your library for up to three basic land cards, reveal them, and put them into your hand. If you do, shuffle your library" )); diff --git a/Mage.Sets/src/mage/sets/masterseditionii/ThoughtLash.java b/Mage.Sets/src/mage/sets/masterseditionii/ThoughtLash.java new file mode 100644 index 0000000000..e8a28e8196 --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditionii/ThoughtLash.java @@ -0,0 +1,182 @@ +/* + * 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.masterseditionii; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileFromTopOfLibraryCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.keyword.CumulativeUpkeepAbility; +import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.players.Player; + +/** + * + * @author emerald000 + */ +public class ThoughtLash extends CardImpl { + + public ThoughtLash(UUID ownerId) { + super(ownerId, 70, "Thought Lash", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}"); + this.expansionSetCode = "ME2"; + + // Cumulative upkeep - Exile the top card of your library. + this.addAbility(new CumulativeUpkeepAbility(new ExileFromTopOfLibraryCost(1))); + + // When a player doesn't pay Thought Lash's cumulative upkeep, that player exiles all cards from his or her library. + this.addAbility(new ThoughtLashTriggeredAbility()); + + // Exile the top card of your library: Prevent the next 1 damage that would be dealt to you this turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ThoughtLashPreventionEffect(), new ExileFromTopOfLibraryCost(1))); + } + + public ThoughtLash(final ThoughtLash card) { + super(card); + } + + @Override + public ThoughtLash copy() { + return new ThoughtLash(this); + } +} + +class ThoughtLashTriggeredAbility extends TriggeredAbilityImpl { + + ThoughtLashTriggeredAbility() { + super(Zone.BATTLEFIELD, new ThoughtLashExileLibraryEffect(), false); + } + + ThoughtLashTriggeredAbility(final ThoughtLashTriggeredAbility ability) { + super(ability); + } + + @Override + public ThoughtLashTriggeredAbility copy() { + return new ThoughtLashTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DIDNT_PAY_CUMULATIVE_UPKEEP; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getSourceId() == this.getSourceId(); + } + + @Override + public String getRule() { + return "When a player doesn't pay {this}'s cumulative upkeep, that player exiles all cards from his or her library."; + } +} + +class ThoughtLashExileLibraryEffect extends OneShotEffect { + + ThoughtLashExileLibraryEffect() { + super(Outcome.Detriment); + this.staticText = "that player exiles all cards from his or her library"; + } + + ThoughtLashExileLibraryEffect(final ThoughtLashExileLibraryEffect effect) { + super(effect); + } + + @Override + public ThoughtLashExileLibraryEffect copy() { + return new ThoughtLashExileLibraryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Cards cards = new CardsImpl(); + cards.addAll(controller.getLibrary().getTopCards(game, controller.getLibrary().size())); + controller.moveCards(cards, Zone.LIBRARY, Zone.EXILED, source, game); + return true; + } + return false; + } +} + +class ThoughtLashPreventionEffect extends PreventionEffectImpl { + + ThoughtLashPreventionEffect() { + super(Duration.EndOfTurn); + this.staticText = "Prevent the next 1 damage that would be dealt to you this turn"; + } + + ThoughtLashPreventionEffect(final ThoughtLashPreventionEffect effect) { + super(effect); + } + + @Override + public ThoughtLashPreventionEffect copy() { + return new ThoughtLashPreventionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + GameEvent preventEvent = new GameEvent(GameEvent.EventType.PREVENT_DAMAGE, + source.getControllerId(), source.getSourceId(), source.getControllerId(), event.getAmount(), false); + if (!game.replaceEvent(preventEvent)) { + int damage = event.getAmount(); + if (damage > 0) { + event.setAmount(damage - 1); + this.used = true; + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, + source.getControllerId(), source.getSourceId(), source.getControllerId(), 1)); + } + } + return false; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return !this.used && super.applies(event, source, game) && event.getTargetId().equals(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/sets/mirrodin/SpellweaverHelix.java b/Mage.Sets/src/mage/sets/mirrodin/SpellweaverHelix.java new file mode 100644 index 0000000000..e83b0afe79 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mirrodin/SpellweaverHelix.java @@ -0,0 +1,231 @@ +/* + * 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.mirrodin; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterCard; +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 mage.players.Player; +import mage.target.common.TargetCardInASingleGraveyard; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * + * @author emerald000 + */ +public class SpellweaverHelix extends CardImpl { + + private static final FilterCard filter = new FilterCard("sorcery cards from a single graveyard"); + static { + filter.add(new CardTypePredicate(CardType.SORCERY)); + } + + public SpellweaverHelix(UUID ownerId) { + super(ownerId, 247, "Spellweaver Helix", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MRD"; + + // Imprint - When Spellweaver Helix enters the battlefield, you may exile two target sorcery cards from a single graveyard. + Ability ability = new EntersBattlefieldTriggeredAbility(new SpellweaverHelixImprintEffect(), true, "Imprint — "); + ability.addTarget(new TargetCardInASingleGraveyard(2, 2, filter)); + this.addAbility(ability); + + // Whenever a player casts a card, if it has the same name as one of the cards exiled with Spellweaver Helix, you may copy the other. If you do, you may cast the copy without paying its mana cost. + this.addAbility(new SpellweaverHelixTriggeredAbility()); + } + + public SpellweaverHelix(final SpellweaverHelix card) { + super(card); + } + + @java.lang.Override + public SpellweaverHelix copy() { + return new SpellweaverHelix(this); + } +} + +class SpellweaverHelixImprintEffect extends OneShotEffect { + + SpellweaverHelixImprintEffect() { + super(Outcome.Exile); + this.staticText = "you may exile two target sorcery cards from a single graveyard"; + } + + SpellweaverHelixImprintEffect(final SpellweaverHelixImprintEffect effect) { + super(effect); + } + + @java.lang.Override + public SpellweaverHelixImprintEffect copy() { + return new SpellweaverHelixImprintEffect(this); + } + + @java.lang.Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { + Card card = game.getCard(targetId); + if (card != null) { + controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), source.getSourceObject(game).getIdName()); + if (sourcePermanent != null) { + sourcePermanent.imprint(targetId, game); + } + } + } + return true; + } + return false; + } +} + +class SpellweaverHelixTriggeredAbility extends TriggeredAbilityImpl { + + SpellweaverHelixTriggeredAbility() { + super(Zone.BATTLEFIELD, new SpellweaverHelixCastEffect(), false); + } + + SpellweaverHelixTriggeredAbility(final SpellweaverHelixTriggeredAbility ability) { + super(ability); + } + + @java.lang.Override + public SpellweaverHelixTriggeredAbility copy() { + return new SpellweaverHelixTriggeredAbility(this); + } + + @java.lang.Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @java.lang.Override + public boolean checkTrigger(GameEvent event, Game game) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null && spell.getCard() != null && !spell.getCard().isCopy()) { + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(spell.getId())); + } + return true; + } + return false; + } + + @java.lang.Override + public boolean checkInterveningIfClause(Game game) { + Spell spell = game.getStack().getSpell(this.getEffects().get(0).getTargetPointer().getFirst(game, this)); + if (spell != null) { + String spellName = spell.getName(); + Permanent sourcePermanent = game.getPermanent(this.getSourceId()); + if (sourcePermanent != null) { + for (UUID imprintId : sourcePermanent.getImprinted()) { + Card card = game.getCard(imprintId); + if (card != null && card.getName().equals(spellName)) { + ((SpellweaverHelixCastEffect) this.getEffects().get(0)).setSpellName(spellName); + return true; + } + } + } + } + return false; + } + + @java.lang.Override + public String getRule() { + return "Whenever a player casts a card, if it has the same name as one of the cards exiled with Spellweaver Helix, you may copy the other. If you do, you may cast the copy without paying its mana cost."; + } +} + +class SpellweaverHelixCastEffect extends OneShotEffect { + + private String spellName = ""; + + SpellweaverHelixCastEffect() { + super(Outcome.Benefit); + this.staticText = "you may copy the other. If you do, you may cast the copy without paying its mana cost"; + } + + SpellweaverHelixCastEffect(final SpellweaverHelixCastEffect effect) { + super(effect); + this.spellName = effect.spellName; + } + + @java.lang.Override + public SpellweaverHelixCastEffect copy() { + return new SpellweaverHelixCastEffect(this); + } + + public void setSpellName(String spellName) { + this.spellName = spellName; + } + + @java.lang.Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (sourcePermanent != null) { + boolean foundSpellWithSameName = false; + for (UUID imprintId : sourcePermanent.getImprinted()) { + Card card = game.getCard(imprintId); + if (card != null) { + if (!foundSpellWithSameName && card.getName().equals(spellName)) { + foundSpellWithSameName = true; + } + else { + if (controller.chooseUse(Outcome.Copy, "Copy " + card.getIdName(), source, game)) { + Card copy = game.copyCard(card, source, source.getControllerId()); + if (controller.chooseUse(Outcome.PlayForFree, "Cast " + copy.getIdName() + " without paying its mana cost?", source, game)) { + controller.cast(copy.getSpellAbility(), game, true); + } + } + } + } + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/onslaught/WeatheredWayfarer.java b/Mage.Sets/src/mage/sets/onslaught/WeatheredWayfarer.java index 74c9474f8e..738cd6a0d1 100644 --- a/Mage.Sets/src/mage/sets/onslaught/WeatheredWayfarer.java +++ b/Mage.Sets/src/mage/sets/onslaught/WeatheredWayfarer.java @@ -35,7 +35,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.condition.Condition; -import mage.abilities.condition.common.OpponentControllsMoreCondition; +import mage.abilities.condition.common.OpponentControlsMoreCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; @@ -67,7 +67,7 @@ public class WeatheredWayfarer extends CardImpl { Zone.BATTLEFIELD, new SearchLibraryPutInHandEffect(new TargetCardInLibrary(new FilterLandCard()), true, true), new ManaCostsImpl("{W}"), - new OpponentControllsMoreCondition(new FilterLandPermanent("lands"))); + new OpponentControlsMoreCondition(new FilterLandPermanent("lands"))); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/ravnica/ConcertedEffort.java b/Mage.Sets/src/mage/sets/ravnica/ConcertedEffort.java new file mode 100644 index 0000000000..672cfe903e --- /dev/null +++ b/Mage.Sets/src/mage/sets/ravnica/ConcertedEffort.java @@ -0,0 +1,164 @@ +/* + * 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.ravnica; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.FearAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.LandwalkAbility; +import mage.abilities.keyword.ProtectionAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author emerald000 + */ +public class ConcertedEffort extends CardImpl { + + public ConcertedEffort(UUID ownerId) { + super(ownerId, 8, "Concerted Effort", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); + this.expansionSetCode = "RAV"; + + // At the beginning of each upkeep, creatures you control gain flying until end of turn if a creature you control has flying. The same is true for fear, first strike, double strike, landwalk, protection, trample, and vigilance. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ConcertedEffortEffect(), TargetController.ANY, false)); + } + + public ConcertedEffort(final ConcertedEffort card) { + super(card); + } + + @Override + public ConcertedEffort copy() { + return new ConcertedEffort(this); + } +} + +class ConcertedEffortEffect extends OneShotEffect { + + private static final FilterCreaturePermanent filterFlying = new FilterCreaturePermanent(); + private static final FilterCreaturePermanent filterFear = new FilterCreaturePermanent(); + private static final FilterCreaturePermanent filterFirstStrike = new FilterCreaturePermanent(); + private static final FilterCreaturePermanent filterDoubleStrike = new FilterCreaturePermanent(); + private static final FilterCreaturePermanent filterLandwalk = new FilterCreaturePermanent(); + private static final FilterCreaturePermanent filterProtection = new FilterCreaturePermanent(); + private static final FilterCreaturePermanent filterTrample = new FilterCreaturePermanent(); + private static final FilterCreaturePermanent filterVigilance = new FilterCreaturePermanent(); + static { + filterFlying.add(new AbilityPredicate(FlyingAbility.class)); + filterFear.add(new AbilityPredicate(FearAbility.class)); + filterFirstStrike.add(new AbilityPredicate(FirstStrikeAbility.class)); + filterDoubleStrike.add(new AbilityPredicate(DoubleStrikeAbility.class)); + filterLandwalk.add(new AbilityPredicate(LandwalkAbility.class)); + filterProtection.add(new AbilityPredicate(ProtectionAbility.class)); + filterTrample.add(new AbilityPredicate(TrampleAbility.class)); + filterVigilance.add(new AbilityPredicate(VigilanceAbility.class)); + } + + ConcertedEffortEffect() { + super(Outcome.BoostCreature); + this.staticText = "creatures you control gain flying until end of turn if a creature you control has flying. The same is true for fear, first strike, double strike, landwalk, protection, trample, and vigilance"; + } + + ConcertedEffortEffect(final ConcertedEffortEffect effect) { + super(effect); + } + + @Override + public ConcertedEffortEffect copy() { + return new ConcertedEffortEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + // Flying + if (game.getBattlefield().contains(filterFlying, source.getControllerId(), 1, game)) { + game.addEffect(new GainAbilityAllEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), source); + } + + // Fear + if (game.getBattlefield().contains(filterFear, source.getControllerId(), 1, game)) { + game.addEffect(new GainAbilityAllEffect(FearAbility.getInstance(), Duration.EndOfTurn), source); + } + + // First strike + if (game.getBattlefield().contains(filterFirstStrike, source.getControllerId(), 1, game)) { + game.addEffect(new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), source); + } + + // Double strike + if (game.getBattlefield().contains(filterDoubleStrike, source.getControllerId(), 1, game)) { + game.addEffect(new GainAbilityAllEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn), source); + } + + // Landwalk + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterLandwalk, source.getControllerId(), game)) { + for (Ability ability : permanent.getAbilities(game)) { + if (ability instanceof LandwalkAbility) { + game.addEffect(new GainAbilityAllEffect(ability, Duration.EndOfTurn), source); + } + } + } + + // Protection + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterProtection, source.getControllerId(), game)) { + for (Ability ability : permanent.getAbilities(game)) { + if (ability instanceof ProtectionAbility) { + game.addEffect(new GainAbilityAllEffect(ability, Duration.EndOfTurn), source); + } + } + } + + // Trample + if (game.getBattlefield().contains(filterTrample, source.getControllerId(), 1, game)) { + game.addEffect(new GainAbilityAllEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), source); + } + + // Vigilance + if (game.getBattlefield().contains(filterVigilance, source.getControllerId(), 1, game)) { + game.addEffect(new GainAbilityAllEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn), source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/shardsofalara/KnightOfTheWhiteOrchid.java b/Mage.Sets/src/mage/sets/shardsofalara/KnightOfTheWhiteOrchid.java index 92a3e4ead3..7cd19df83b 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/KnightOfTheWhiteOrchid.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/KnightOfTheWhiteOrchid.java @@ -33,7 +33,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.common.OpponentControllsMoreCondition; +import mage.abilities.condition.common.OpponentControlsMoreCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.abilities.keyword.FirstStrikeAbility; @@ -63,7 +63,7 @@ public class KnightOfTheWhiteOrchid extends CardImpl { // When Knight of the White Orchid enters the battlefield, if an opponent controls more lands than you, you may search your library for a Plains card, put it onto the battlefield, then shuffle your library. this.addAbility(new ConditionalTriggeredAbility( new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, new FilterBySubtypeCard("Plains")), false), true), - new OpponentControllsMoreCondition(new FilterLandPermanent("lands")), + new OpponentControlsMoreCondition(new FilterLandPermanent("lands")), "When {this} enters the battlefield, if an opponent controls more lands than you, you may search your library for a Plains card, put it onto the battlefield, then shuffle your library")); } diff --git a/Mage.Sets/src/mage/sets/torment/ChainerDementiaMaster.java b/Mage.Sets/src/mage/sets/torment/ChainerDementiaMaster.java new file mode 100644 index 0000000000..88929b04b2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/torment/ChainerDementiaMaster.java @@ -0,0 +1,144 @@ +/* + * 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.torment; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileAllEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.effects.common.continuous.BecomesColorTargetEffect; +import mage.abilities.effects.common.continuous.BecomesSubtypeTargetEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author emerald000 + */ +public class ChainerDementiaMaster extends CardImpl { + + private static final FilterCreaturePermanent filterCreature = new FilterCreaturePermanent("Nightmare creatures"); + private static final FilterPermanent filterPermanent = new FilterPermanent("Nightmares"); + static { + filterCreature.add(new SubtypePredicate("Nightmare")); + filterPermanent.add(new SubtypePredicate("Nightmare")); + } + + public ChainerDementiaMaster(UUID ownerId) { + super(ownerId, 56, "Chainer, Dementia Master", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + this.expansionSetCode = "TOR"; + this.supertype.add("Legendary"); + this.subtype.add("Human"); + this.subtype.add("Minion"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Nightmare creatures get +1/+1. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filterCreature, false))); + + // {B}{B}{B}, Pay 3 life: Put target creature card from a graveyard onto the battlefield under your control. That creature is black and is a Nightmare in addition to its other creature types. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ChainerDementiaMasterEffect(), new ManaCostsImpl<>("{B}{B}{B}")); + ability.addCost(new PayLifeCost(3)); + ability.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard"))); + this.addAbility(ability); + + // When Chainer, Dementia Master leaves the battlefield, exile all Nightmares. + this.addAbility(new LeavesBattlefieldTriggeredAbility(new ExileAllEffect(filterPermanent), false)); + } + + public ChainerDementiaMaster(final ChainerDementiaMaster card) { + super(card); + } + + @Override + public ChainerDementiaMaster copy() { + return new ChainerDementiaMaster(this); + } +} + +class ChainerDementiaMasterEffect extends OneShotEffect { + + ChainerDementiaMasterEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "Put target creature card from a graveyard onto the battlefield under your control. That creature is black and is a Nightmare in addition to its other creature types"; + } + + ChainerDementiaMasterEffect(final ChainerDementiaMasterEffect effect) { + super(effect); + } + + @Override + public ChainerDementiaMasterEffect copy() { + return new ChainerDementiaMasterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + UUID cardId = this.getTargetPointer().getFirst(game, source); + new ReturnFromGraveyardToBattlefieldTargetEffect().apply(game, source); + Permanent permanent = game.getPermanent(cardId); + if (permanent != null) { + ContinuousEffectImpl effect = new BecomesColorTargetEffect(ObjectColor.BLACK, Duration.WhileOnBattlefield); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + effect = new BecomesSubtypeTargetEffect(Duration.WhileOnBattlefield, new ArrayList<>(Arrays.asList("Nightmare")), false); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/visions/Tithe.java b/Mage.Sets/src/mage/sets/visions/Tithe.java new file mode 100644 index 0000000000..dd0563cc51 --- /dev/null +++ b/Mage.Sets/src/mage/sets/visions/Tithe.java @@ -0,0 +1,98 @@ +/* + * 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.visions; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.filter.FilterCard; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetOpponent; + +/** + * + * @author emerald000 + */ +public class Tithe extends CardImpl { + + public Tithe(UUID ownerId) { + super(ownerId, 84, "Tithe", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{W}"); + this.expansionSetCode = "VIS"; + + // Search your library for a Plains card. If target opponent controls more lands than you, you may search your library for an additional Plains card. Reveal those cards and put them into your hand. Then shuffle your library. + this.getSpellAbility().addEffect(new TitheEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + public Tithe(final Tithe card) { + super(card); + } + + @Override + public Tithe copy() { + return new Tithe(this); + } +} + +class TitheEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("Plains"); + static { + filter.add(new SubtypePredicate("Plains")); + } + + TitheEffect() { + super(Outcome.Benefit); + this.staticText = "Search your library for a Plains card. If target opponent controls more lands than you, you may search your library for an additional Plains card. Reveal those cards and put them into your hand. Then shuffle your library"; + } + + TitheEffect(final TitheEffect effect) { + super(effect); + } + + @Override + public TitheEffect copy() { + return new TitheEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int numYourLands = game.getBattlefield().countAll(new FilterLandPermanent(), source.getControllerId(), game); + int numOpponentLands = game.getBattlefield().countAll(new FilterLandPermanent(), this.getTargetPointer().getFirst(game, source), game); + new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, (numOpponentLands > numYourLands ? 2 : 1), filter), true).apply(game, source); + return true; + } +} diff --git a/Mage/src/mage/abilities/condition/common/OpponentControllsMoreCondition.java b/Mage/src/mage/abilities/condition/common/OpponentControlsMoreCondition.java similarity index 92% rename from Mage/src/mage/abilities/condition/common/OpponentControllsMoreCondition.java rename to Mage/src/mage/abilities/condition/common/OpponentControlsMoreCondition.java index 8a8183af34..f4a0ccc106 100644 --- a/Mage/src/mage/abilities/condition/common/OpponentControllsMoreCondition.java +++ b/Mage/src/mage/abilities/condition/common/OpponentControlsMoreCondition.java @@ -39,11 +39,11 @@ import mage.game.Game; * @author LevelX2 */ -public class OpponentControllsMoreCondition implements Condition { +public class OpponentControlsMoreCondition implements Condition { private final FilterPermanent filter; - public OpponentControllsMoreCondition(FilterPermanent filter) { + public OpponentControlsMoreCondition(FilterPermanent filter) { this.filter = filter; } diff --git a/Mage/src/mage/abilities/keyword/CumulativeUpkeepAbility.java b/Mage/src/mage/abilities/keyword/CumulativeUpkeepAbility.java index 199c853610..4077074fa7 100644 --- a/Mage/src/mage/abilities/keyword/CumulativeUpkeepAbility.java +++ b/Mage/src/mage/abilities/keyword/CumulativeUpkeepAbility.java @@ -25,8 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - - package mage.abilities.keyword; import mage.abilities.Ability; @@ -41,6 +39,8 @@ import mage.constants.Outcome; import mage.constants.TargetController; import mage.counters.CounterType; import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; @@ -48,12 +48,10 @@ import mage.players.Player; * * @author Plopman */ - - public class CumulativeUpkeepAbility extends BeginningOfUpkeepTriggeredAbility { - + private Cost cumulativeCost; - + public CumulativeUpkeepAbility(Cost cumulativeCost) { super(new AddCountersSourceEffect(CounterType.AGE.createInstance()), TargetController.YOU, false); this.addEffect(new CumulativeUpkeepEffect(cumulativeCost)); @@ -82,9 +80,9 @@ public class CumulativeUpkeepAbility extends BeginningOfUpkeepTriggeredAbility { } class CumulativeUpkeepEffect extends OneShotEffect { - - private Cost cumulativeCost; - + + private final Cost cumulativeCost; + CumulativeUpkeepEffect(Cost cumulativeCost) { super(Outcome.Sacrifice); this.cumulativeCost = cumulativeCost; @@ -95,46 +93,44 @@ class CumulativeUpkeepEffect extends OneShotEffect { this.cumulativeCost = effect.cumulativeCost.copy(); } - @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getSourceId()); - if (player != null && permanent != null) { + if (player != null && permanent != null) { int ageCounter = permanent.getCounters().getCount(CounterType.AGE); - if(cumulativeCost instanceof ManaCost){ - ManaCostsImpl totalCost = new ManaCostsImpl(); - for(int i = 0 ; i < ageCounter; i++){ + if (cumulativeCost instanceof ManaCost) { + ManaCostsImpl totalCost = new ManaCostsImpl<>(); + for (int i = 0; i < ageCounter; i++) { totalCost.add((ManaCost) cumulativeCost.copy()); } if (player.chooseUse(Outcome.Benefit, "Pay " + totalCost.getText() + "?", source, game)) { totalCost.clearPaid(); - if (totalCost.payOrRollback(source, game, source.getSourceId(), source.getControllerId())){ + if (totalCost.payOrRollback(source, game, source.getSourceId(), source.getControllerId())) { return true; } } + game.fireEvent(new GameEvent(EventType.DIDNT_PAY_CUMULATIVE_UPKEEP, permanent.getId(), permanent.getId(), player.getId(), ageCounter, false)); permanent.sacrifice(source.getSourceId(), game); - return true; - } - else{ - CostsImpl totalCost = new CostsImpl(); - for(int i = 0 ; i < ageCounter; i++){ + return true; + } else { + CostsImpl totalCost = new CostsImpl<>(); + for (int i = 0; i < ageCounter; i++) { totalCost.add(cumulativeCost.copy()); } if (player.chooseUse(Outcome.Benefit, totalCost.getText() + "?", source, game)) { totalCost.clearPaid(); int bookmark = game.bookmarkState(); - if (totalCost.pay(source, game, source.getSourceId(), source.getControllerId(), false)){ + if (totalCost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { return true; - } - else{ + } else { game.restoreState(bookmark, source.getRule()); } } + game.fireEvent(new GameEvent(EventType.DIDNT_PAY_CUMULATIVE_UPKEEP, permanent.getId(), permanent.getId(), player.getId(), ageCounter, false)); permanent.sacrifice(source.getSourceId(), game); - return true; + return true; } - } return false; } diff --git a/Mage/src/mage/game/events/GameEvent.java b/Mage/src/mage/game/events/GameEvent.java index 93d4cef024..a6909fe729 100644 --- a/Mage/src/mage/game/events/GameEvent.java +++ b/Mage/src/mage/game/events/GameEvent.java @@ -185,6 +185,7 @@ public class GameEvent implements Serializable { ENCHANT_PLAYER, ENCHANTED_PLAYER, CAN_TAKE_MULLIGAN, FLIP_COIN, COIN_FLIPPED, SCRY, FATESEAL, + DIDNT_PAY_CUMULATIVE_UPKEEP, //permanent events ENTERS_THE_BATTLEFIELD, TAP, TAPPED, TAPPED_FOR_MANA, @@ -241,7 +242,6 @@ public class GameEvent implements Serializable { //combat events COMBAT_DAMAGE_APPLIED, SELECTED_ATTACKER, SELECTED_BLOCKER; - } public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId) { From 87078057e0bf025c078818950adf17ca18c482fe Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 4 Oct 2015 09:45:22 +0200 Subject: [PATCH 16/23] * Some fixed to cards submitted by BursegSardaukar. --- .../mage/sets/archenemy/SkirkCommando.java | 55 ++++++++---------- .../src/mage/sets/guildpact/RabbleRouser.java | 14 ++--- .../src/mage/sets/iceage/GoblinSnowman.java | 24 +++++--- .../src/mage/sets/iceage/TinderWall.java | 36 +----------- .../mage/sets/lorwyn/BoggartSpriteChaser.java | 22 ++++---- .../src/mage/sets/lorwyn/FodderLaunch.java | 56 ++----------------- .../mage/sets/lorwyn/QuillSlingerBoggart.java | 2 +- .../mage/sets/planarchaos/FirefrightMage.java | 17 +++--- .../mage/sets/ravnica/GoblinFireFiend.java | 13 ++--- .../src/mage/sets/tempest/MoggSquad.java | 8 +-- .../CantBeBlockedByAllTargetEffect.java | 51 +++++++++++++++++ 11 files changed, 131 insertions(+), 167 deletions(-) create mode 100644 Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java diff --git a/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java b/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java index d995faa763..a97f8981cb 100644 --- a/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java +++ b/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java @@ -28,21 +28,18 @@ package mage.sets.archenemy; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.MorphAbility; import mage.cards.CardImpl; -import mage.constants.Outcome; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; -import mage.players.Player; +import mage.game.events.GameEvent; import mage.target.common.TargetCreaturePermanent; /** @@ -50,7 +47,7 @@ import mage.target.common.TargetCreaturePermanent; * @author BursegSardaukar */ public class SkirkCommando extends CardImpl { - + public SkirkCommando(UUID ownerId) { super(ownerId, 47, "Skirk Commando", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); this.expansionSetCode = "ARC"; @@ -59,13 +56,13 @@ public class SkirkCommando extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - + //Whenever Skirk Commando deals combat damage to a player, you may have it deal 2 damage to target creature that player controls. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new SkirkCommandoEffect(), true, true)); - + this.addAbility(new SkirkCommandoTriggeredAbility()); + //Morph {2}{R} (You may cast this card face down as a 2/2 creature for 3. Turn it face up any time for its morph cost.) this.addAbility(new MorphAbility(this, new ManaCostsImpl("{2}{R}"))); - + } public SkirkCommando(final SkirkCommando card) { @@ -78,37 +75,29 @@ public class SkirkCommando extends CardImpl { } } -class SkirkCommandoEffect extends OneShotEffect { +class SkirkCommandoTriggeredAbility extends DealsCombatDamageToAPlayerTriggeredAbility { + + public SkirkCommandoTriggeredAbility() { + super(new DamageTargetEffect(2), true, false); - public SkirkCommandoEffect() { - super(Outcome.Damage); - staticText = "have it deal 2 damage to target creature that player controls"; } - public SkirkCommandoEffect(final SkirkCommandoEffect effect) { - super(effect); + public SkirkCommandoTriggeredAbility(SkirkCommandoTriggeredAbility ability) { + super(ability); } @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature " + player.getLogName() + " controls"); - filter.add(new ControllerIdPredicate(player.getId())); - TargetCreaturePermanent target = new TargetCreaturePermanent(filter); - if (target.canChoose(source.getControllerId(), game) && target.choose(Outcome.Damage, source.getControllerId(), source.getSourceId(), game)) { - UUID creature = target.getFirstTarget(); - if (creature != null) { - game.getPermanent(creature).damage(2, source.getSourceId(), game, false, true); - return true; - } - } + public boolean checkTrigger(GameEvent event, Game game) { + if (super.checkTrigger(event, game)) { + FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that player controls"); + filter.add(new ControllerIdPredicate(event.getPlayerId())); + addTarget(new TargetCreaturePermanent(filter)); } return false; } @Override - public SkirkCommandoEffect copy() { - return new SkirkCommandoEffect(this); + public SkirkCommandoTriggeredAbility copy() { + return new SkirkCommandoTriggeredAbility(this); } } diff --git a/Mage.Sets/src/mage/sets/guildpact/RabbleRouser.java b/Mage.Sets/src/mage/sets/guildpact/RabbleRouser.java index aafc1493d0..fc935905f7 100644 --- a/Mage.Sets/src/mage/sets/guildpact/RabbleRouser.java +++ b/Mage.Sets/src/mage/sets/guildpact/RabbleRouser.java @@ -28,9 +28,6 @@ package mage.sets.guildpact; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -42,7 +39,9 @@ import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.keyword.BloodthirstAbility; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterAttackingCreature; @@ -51,9 +50,7 @@ import mage.filter.common.FilterAttackingCreature; * @author BursegSardaukar */ public class RabbleRouser extends CardImpl { - - final static private String rule = "Attacking creatures get +X/+0 until end of turn, where X is Rabble-Rouser's power."; - + public RabbleRouser(UUID ownerId) { super(ownerId, 73, "Rabble-Rouser", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.expansionSetCode = "GPT"; @@ -68,7 +65,10 @@ public class RabbleRouser extends CardImpl { //{R}, {T}: Attacking creatures get +X/+0 until end of turn, where X is Rabble-Rouser's power. DynamicValue amount = new SourcePermanentPowerCount(); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostAllEffect(amount, new StaticValue(0), Duration.EndOfTurn, new FilterAttackingCreature(), false, rule), new ManaCostsImpl("{R}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + new BoostAllEffect(amount, new StaticValue(0), Duration.EndOfTurn, new FilterAttackingCreature(), false, + "Attacking creatures get +X/+0 until end of turn, where X is {this}'s power"), + new ManaCostsImpl("{R}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/iceage/GoblinSnowman.java b/Mage.Sets/src/mage/sets/iceage/GoblinSnowman.java index 9fb3b18c09..775512bd9a 100644 --- a/Mage.Sets/src/mage/sets/iceage/GoblinSnowman.java +++ b/Mage.Sets/src/mage/sets/iceage/GoblinSnowman.java @@ -28,21 +28,22 @@ package mage.sets.iceage; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BlocksTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.PreventCombatDamageBySourceEffect; import mage.abilities.effects.common.PreventCombatDamageToSourceEffect; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.permanent.BlockedByIdPredicate; import mage.target.common.TargetCreaturePermanent; /** @@ -60,15 +61,20 @@ public class GoblinSnowman extends CardImpl { this.toughness = new MageInt(1); //Whenever Goblin Snowman blocks, prevent all combat damage that would be dealt to and dealt by it this turn. - this.addAbility(new BlocksTriggeredAbility(new PreventCombatDamageBySourceEffect(Duration.EndOfTurn), false)); - this.addAbility(new BlocksTriggeredAbility(new PreventCombatDamageToSourceEffect(Duration.EndOfTurn), false)); - + Effect effect = new PreventCombatDamageBySourceEffect(Duration.EndOfTurn); + effect.setText("prevent all combat damage that would be dealt to"); + Ability ability = new BlocksTriggeredAbility(effect, false); + effect = new PreventCombatDamageToSourceEffect(Duration.EndOfTurn); + effect.setText("and dealt by it this turn"); + ability.addEffect(effect); + this.addAbility(ability); + //{T}: Goblin Snowman deals 1 damage to target creature it's blocking. FilterAttackingCreature filter = new FilterAttackingCreature("creature it's blocking"); - filter.add(new BlockingByPredicate(this.getId())); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new TapSourceCost()); + filter.add(new BlockedByIdPredicate(this.getId())); + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(filter)); - this.addAbility(ability, new BlockedByWatcher()); + this.addAbility(ability); } public GoblinSnowman(final GoblinSnowman card) { diff --git a/Mage.Sets/src/mage/sets/iceage/TinderWall.java b/Mage.Sets/src/mage/sets/iceage/TinderWall.java index 5ee8dc535c..048387e294 100644 --- a/Mage.Sets/src/mage/sets/iceage/TinderWall.java +++ b/Mage.Sets/src/mage/sets/iceage/TinderWall.java @@ -43,16 +43,15 @@ import mage.cards.CardImpl; import mage.constants.WatcherScope; import mage.constants.Zone; import mage.filter.common.FilterAttackingCreature; -import mage.filter.predicate.Predicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; import mage.watchers.Watcher; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import mage.filter.predicate.permanent.BlockedByIdPredicate; /** * @@ -75,7 +74,7 @@ public class TinderWall extends CardImpl { this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new BasicManaEffect(Mana.RedMana(2)), new SacrificeSourceCost())); // {R}, Sacrifice Tinder Wall: Tinder Wall deals 2 damage to target creature it's blocking. FilterAttackingCreature filter = new FilterAttackingCreature("creature it's blocking"); - filter.add(new BlockingByPredicate(this.getId())); + filter.add(new BlockedByIdPredicate(this.getId())); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(2), new ManaCostsImpl("{R}")); ability.addTarget(new TargetCreaturePermanent(filter)); ability.addCost(new SacrificeSourceCost()); @@ -92,10 +91,9 @@ public class TinderWall extends CardImpl { } } - class BlockedByWatcher extends Watcher { - public List blockedByWatcher = new ArrayList(); + public List blockedByWatcher = new ArrayList<>(); public BlockedByWatcher() { super("BlockedByWatcher", WatcherScope.CARD); @@ -127,31 +125,3 @@ class BlockedByWatcher extends Watcher { } } - -class BlockingByPredicate implements Predicate { - - private UUID source; - - public BlockingByPredicate(UUID source) { - this.source = source; - } - - @Override - public boolean apply(Permanent input, Game game) { - BlockedByWatcher watcher = (BlockedByWatcher) game.getState().getWatchers().get("BlockedByWatcher", source); - - for(UUID uuid :watcher.blockedByWatcher){ - Permanent creature = game.getPermanent(uuid); - if(creature != null && creature.getId().equals(input.getId())){ - return true; - } - } - return false; - - } - - @Override - public String toString() { - return "creature it's blocking"; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/lorwyn/BoggartSpriteChaser.java b/Mage.Sets/src/mage/sets/lorwyn/BoggartSpriteChaser.java index 9d6b6e441b..22c0d5a9e6 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/BoggartSpriteChaser.java +++ b/Mage.Sets/src/mage/sets/lorwyn/BoggartSpriteChaser.java @@ -29,9 +29,11 @@ package mage.sets.lorwyn; import java.util.UUID; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceWhileControlsEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.FlyingAbility; @@ -41,7 +43,6 @@ import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.SubtypePredicate; /** * @@ -49,14 +50,6 @@ import mage.filter.predicate.mageobject.SubtypePredicate; */ public class BoggartSpriteChaser extends CardImpl { - final static private String rule = "{this} has flying as long as you control a Faerie"; - - private static final FilterPermanent filter = new FilterPermanent("Faerie"); - - static { - filter.add(new SubtypePredicate("Faerie")); - } - public BoggartSpriteChaser(UUID ownerId) { super(ownerId, 156, "Boggart Sprite-Chaser", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.expansionSetCode = "LRW"; @@ -67,8 +60,15 @@ public class BoggartSpriteChaser extends CardImpl { this.toughness = new MageInt(2); // As long as you control a Faerie, Boggart Sprite-Chaser gets +1/+1 and has flying. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceWhileControlsEffect(filter, 1, 1))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield), new PermanentsOnTheBattlefieldCondition(filter), rule))); + FilterPermanent filter = new FilterPermanent("Faerie", "Faerie"); + Effect effect = new BoostSourceWhileControlsEffect(filter, 1, 1); + effect.setText("As long as you control a Faerie, {this} gets +1/+1"); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + ability.addEffect(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield), + new PermanentsOnTheBattlefieldCondition(filter), "and has flying")); + this.addAbility(ability); + } public BoggartSpriteChaser(final BoggartSpriteChaser card) { diff --git a/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java b/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java index d8d8cbebbc..5fbf233ad1 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java +++ b/Mage.Sets/src/mage/sets/lorwyn/FodderLaunch.java @@ -25,26 +25,18 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.lorwyn; import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetControllerEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.constants.Duration; -import mage.constants.Outcome; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.SubtypePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -53,24 +45,18 @@ import mage.target.common.TargetCreaturePermanent; */ public class FodderLaunch extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a Goblin"); - - static { - filter.add(new SubtypePredicate("Goblin")); - } - public FodderLaunch(UUID ownerId) { super(ownerId, 114, "Fodder Launch", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{B}"); this.expansionSetCode = "LRW"; this.subtype.add("Goblin"); - + //As an additional cost to cast Fodder Launch, sacrifice a Goblin. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, false))); - + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent("Goblin", "a Goblin"), true))); + //Target creature gets -5/-5 until end of turn. Fodder Launch deals 5 damage to that creature's controller. this.getSpellAbility().addEffect(new BoostTargetEffect(-5, -5, Duration.EndOfTurn)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new FodderLaunchEffect()); + this.getSpellAbility().addEffect(new DamageTargetControllerEffect(5)); } public FodderLaunch(final FodderLaunch card) { @@ -83,35 +69,3 @@ public class FodderLaunch extends CardImpl { } } - -class FodderLaunchEffect extends OneShotEffect { - - public FodderLaunchEffect() { - super(Outcome.Damage); - this.staticText = "Target creature gets -5/-5 until end of turn. Fodder Launch deals 5 damage to that creature's controller."; - } - - public FodderLaunchEffect(final FodderLaunchEffect effect) { - super(effect); - } - - @Override - public FodderLaunchEffect copy() { - return new FodderLaunchEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && sourceObject != null) { - Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (targetCreature != null) { - Player controllerOfTargetCreature = game.getPlayer(targetCreature.getControllerId()); - controllerOfTargetCreature.damage(5, source.getSourceId(), game, false, true); - return true; - } - } - return false; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/lorwyn/QuillSlingerBoggart.java b/Mage.Sets/src/mage/sets/lorwyn/QuillSlingerBoggart.java index 513bf353a4..cdb736215b 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/QuillSlingerBoggart.java +++ b/Mage.Sets/src/mage/sets/lorwyn/QuillSlingerBoggart.java @@ -61,7 +61,7 @@ public class QuillSlingerBoggart extends CardImpl { this.toughness = new MageInt(2); // Whenever a player casts a Kithkin spell, you may have target player lose 1 life. - Ability ability = new SpellCastAllTriggeredAbility(new LoseLifeTargetEffect(2), filter, true); + Ability ability = new SpellCastAllTriggeredAbility(new LoseLifeTargetEffect(1), filter, true); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/planarchaos/FirefrightMage.java b/Mage.Sets/src/mage/sets/planarchaos/FirefrightMage.java index 741bfabb0a..729e237799 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/FirefrightMage.java +++ b/Mage.Sets/src/mage/sets/planarchaos/FirefrightMage.java @@ -28,21 +28,18 @@ package mage.sets.planarchaos; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleEvasionAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByAllTargetEffect; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; @@ -55,7 +52,7 @@ import mage.target.common.TargetCreaturePermanent; * @author BursegSardaukar */ public class FirefrightMage extends CardImpl { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("except by artifact creatures and/or red creatures"); static { @@ -65,7 +62,7 @@ public class FirefrightMage extends CardImpl { Predicates.and(new CardTypePredicate(CardType.CREATURE), new ColorPredicate(ObjectColor.RED) )))); } - + public FirefrightMage(UUID ownerId) { super(ownerId, 99, "Firefright Mage", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{R}"); this.expansionSetCode = "PLC"; @@ -75,8 +72,8 @@ public class FirefrightMage extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - //{1} {R}, {T}, Discard a card: Target creature can't be blocked this turn except by artifact creatures and/or red creatures. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(new SimpleEvasionAbility(new CantBeBlockedByCreaturesSourceEffect(filter, Duration.EndOfTurn)), Duration.EndOfTurn),new ManaCostsImpl("{1}{R}")); + //{1}{R}, {T}, Discard a card: Target creature can't be blocked this turn except by artifact creatures and/or red creatures. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedByAllTargetEffect(filter, Duration.EndOfTurn), new ManaCostsImpl("{1}{R}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/sets/ravnica/GoblinFireFiend.java b/Mage.Sets/src/mage/sets/ravnica/GoblinFireFiend.java index 053454589d..1c7d12327e 100644 --- a/Mage.Sets/src/mage/sets/ravnica/GoblinFireFiend.java +++ b/Mage.Sets/src/mage/sets/ravnica/GoblinFireFiend.java @@ -32,11 +32,9 @@ import java.util.UUID; import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.HasteAbility; @@ -49,7 +47,7 @@ import mage.constants.Zone; * @author BursegSardaukar */ public class GoblinFireFiend extends CardImpl { - + public GoblinFireFiend(UUID ownerId) { super(ownerId, 127, "Goblin Fire Fiend", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.expansionSetCode = "RAV"; @@ -58,16 +56,15 @@ public class GoblinFireFiend extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - + //Haste this.addAbility(HasteAbility.getInstance()); - + //Goblin Fire Fiend must be blocked if able. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect())); - + //{R}: Goblin Fire Fiend gets +1/+0 until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(new StaticValue(1), new StaticValue(0), Duration.EndOfTurn), new ManaCostsImpl("{R}")); - this.addAbility(ability); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}"))); } public GoblinFireFiend(final GoblinFireFiend card) { diff --git a/Mage.Sets/src/mage/sets/tempest/MoggSquad.java b/Mage.Sets/src/mage/sets/tempest/MoggSquad.java index 6188bc2083..1bc3a474e7 100644 --- a/Mage.Sets/src/mage/sets/tempest/MoggSquad.java +++ b/Mage.Sets/src/mage/sets/tempest/MoggSquad.java @@ -28,9 +28,6 @@ package mage.sets.tempest; import java.util.UUID; - -import mage.constants.CardType; -import mage.constants.Rarity; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -39,7 +36,9 @@ import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.SignInversionDynamicValue; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; +import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; @@ -63,7 +62,8 @@ public class MoggSquad extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - + + // Mogg Squad gets -1/-1 for each other creature on the battlefield. DynamicValue amount = new SignInversionDynamicValue(new PermanentsOnBattlefieldCount(filter)); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(amount, amount, Duration.WhileOnBattlefield)); this.addAbility(ability); diff --git a/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java b/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java new file mode 100644 index 0000000000..ec697e3ba8 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java @@ -0,0 +1,51 @@ +/* + * 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.effects.common.combat; + +import mage.abilities.Ability; +import mage.abilities.effects.RestrictionEffect; +import mage.constants.Duration; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class CantBeBlockedByAllTargetEffect extends RestrictionEffect { + + private final FilterCreaturePermanent filterBlockedBy; + + public CantBeBlockedByAllTargetEffect(FilterCreaturePermanent filterBlockedBy, Duration duration) { + super(Duration.WhileOnBattlefield); + this.filterBlockedBy = filterBlockedBy; + staticText = "Target creature" + + " can't be blocked " + + (filterBlockedBy.getMessage().startsWith("except by") ? "" : "by ") + + filterBlockedBy.getMessage(); + } + + public CantBeBlockedByAllTargetEffect(final CantBeBlockedByAllTargetEffect effect) { + super(effect); + this.filterBlockedBy = effect.filterBlockedBy; + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return getTargetPointer().getTargets(game, source).contains(permanent.getId()); + } + + @Override + public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game) { + return !filterBlockedBy.match(blocker, source.getSourceId(), source.getControllerId(), game); + } + + @Override + public CantBeBlockedByAllTargetEffect copy() { + return new CantBeBlockedByAllTargetEffect(this); + } +} From 8c7dc7b2da3630b6dfec1390854fa2be11631c79 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 4 Oct 2015 09:56:47 +0200 Subject: [PATCH 17/23] * Fixed rule text problem of Skirk Commando. --- Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java b/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java index a97f8981cb..3dd3c0fc6d 100644 --- a/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java +++ b/Mage.Sets/src/mage/sets/archenemy/SkirkCommando.java @@ -79,7 +79,6 @@ class SkirkCommandoTriggeredAbility extends DealsCombatDamageToAPlayerTriggeredA public SkirkCommandoTriggeredAbility() { super(new DamageTargetEffect(2), true, false); - } public SkirkCommandoTriggeredAbility(SkirkCommandoTriggeredAbility ability) { @@ -100,4 +99,9 @@ class SkirkCommandoTriggeredAbility extends DealsCombatDamageToAPlayerTriggeredA public SkirkCommandoTriggeredAbility copy() { return new SkirkCommandoTriggeredAbility(this); } + + @Override + public String getRule() { + return "Whenever {this} deals combat damage to a player, you may have it deal 2 damage to target creature that player controls."; + } } From 3e27d07d0d1611620411706d05de18c744460a4e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 4 Oct 2015 10:33:33 +0200 Subject: [PATCH 18/23] Xmage 1.4.4v8 --- Mage.Common/src/mage/utils/MageVersion.java | 2 +- Mage/src/mage/cards/repository/CardRepository.java | 2 +- Utils/release/getting_implemented_cards.txt | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Mage.Common/src/mage/utils/MageVersion.java b/Mage.Common/src/mage/utils/MageVersion.java index c28ce7ebc7..5e79ba1e3e 100644 --- a/Mage.Common/src/mage/utils/MageVersion.java +++ b/Mage.Common/src/mage/utils/MageVersion.java @@ -41,7 +41,7 @@ public class MageVersion implements Serializable, Comparable { public final static int MAGE_VERSION_MAJOR = 1; public final static int MAGE_VERSION_MINOR = 4; public final static int MAGE_VERSION_PATCH = 4; - public final static String MAGE_VERSION_MINOR_PATCH = "v7"; + public final static String MAGE_VERSION_MINOR_PATCH = "v8"; public final static String MAGE_VERSION_INFO = ""; private final int major; diff --git a/Mage/src/mage/cards/repository/CardRepository.java b/Mage/src/mage/cards/repository/CardRepository.java index 8a709d9310..0605160f49 100644 --- a/Mage/src/mage/cards/repository/CardRepository.java +++ b/Mage/src/mage/cards/repository/CardRepository.java @@ -63,7 +63,7 @@ public enum CardRepository { // raise this if db structure was changed private static final long CARD_DB_VERSION = 41; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 39; + private static final long CARD_CONTENT_VERSION = 40; private final Random random = new Random(); private Dao cardDao; diff --git a/Utils/release/getting_implemented_cards.txt b/Utils/release/getting_implemented_cards.txt index 3a42aba487..f922aab6fb 100644 --- a/Utils/release/getting_implemented_cards.txt +++ b/Utils/release/getting_implemented_cards.txt @@ -33,6 +33,9 @@ git log 513a574ae98aff3d7820e5411a8e5f2a6506e69c..head --diff-filter=A --name-st since 1.4.4.v6 git log 7650f53dee0b4d480d2a63befed72b6c8197e752..head --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt +since 1.4.4.v8 +git log 8c7dc7b2da3630b6dfec1390854fa2be11631c79..head --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt + 3. Copy added_cards.txt to trunk\Utils folder 4. Run script: > perl extract_in_wiki_format.perl From fca3582420bb80c5bd4a9efacac1cb0bf42ab31e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 4 Oct 2015 20:08:19 +0200 Subject: [PATCH 19/23] * Rattleblaze Scarecrow - Fixed that the haze ability was gained from controlled black instead of red creature. --- .../mage/sets/shadowmoor/RattleblazeScarecrow.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/sets/shadowmoor/RattleblazeScarecrow.java b/Mage.Sets/src/mage/sets/shadowmoor/RattleblazeScarecrow.java index 41bcbcbe97..3bbba8d7fe 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/RattleblazeScarecrow.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/RattleblazeScarecrow.java @@ -50,9 +50,10 @@ import mage.filter.predicate.mageobject.ColorPredicate; * @author jeffwadsworth */ public class RattleblazeScarecrow extends CardImpl { - + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a black creature"); private static final FilterControlledCreaturePermanent filter2 = new FilterControlledCreaturePermanent("a red creature"); + static { filter.add(new ColorPredicate(ObjectColor.BLACK)); filter2.add(new ColorPredicate(ObjectColor.RED)); @@ -60,7 +61,7 @@ public class RattleblazeScarecrow extends CardImpl { private static final String rule = "{this} has persist as long as you control a black creature"; private static final String rule2 = "{this} has haste as long as you control a red creature"; - + public RattleblazeScarecrow(UUID ownerId) { super(ownerId, 259, "Rattleblaze Scarecrow", Rarity.COMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{6}"); this.expansionSetCode = "SHM"; @@ -71,11 +72,11 @@ public class RattleblazeScarecrow extends CardImpl { // Rattleblaze Scarecrow has persist as long as you control a black creature. ContinuousEffect effect = new GainAbilitySourceEffect(new PersistAbility(), Duration.WhileOnBattlefield); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect, new PermanentsOnTheBattlefieldCondition(filter), rule))); - + // Rattleblaze Scarecrow has haste as long as you control a red creature. ContinuousEffect effect2 = new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect2, new PermanentsOnTheBattlefieldCondition(filter), rule2))); - + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect2, new PermanentsOnTheBattlefieldCondition(filter2), rule2))); + } public RattleblazeScarecrow(final RattleblazeScarecrow card) { From 3bb7de006a6b3d4fed23c85d8e8946071df635bf Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 4 Oct 2015 20:41:59 +0200 Subject: [PATCH 20/23] * Goblin Snowman - Fixed set code and source file location. --- .../mage/sets/{timespiral => timeshifted}/GoblinSnowman.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename Mage.Sets/src/mage/sets/{timespiral => timeshifted}/GoblinSnowman.java (94%) diff --git a/Mage.Sets/src/mage/sets/timespiral/GoblinSnowman.java b/Mage.Sets/src/mage/sets/timeshifted/GoblinSnowman.java similarity index 94% rename from Mage.Sets/src/mage/sets/timespiral/GoblinSnowman.java rename to Mage.Sets/src/mage/sets/timeshifted/GoblinSnowman.java index ef7ca06256..bb2b7516b7 100644 --- a/Mage.Sets/src/mage/sets/timespiral/GoblinSnowman.java +++ b/Mage.Sets/src/mage/sets/timeshifted/GoblinSnowman.java @@ -25,7 +25,7 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ -package mage.sets.timespiral; +package mage.sets.timeshifted; import java.util.UUID; @@ -38,7 +38,7 @@ public class GoblinSnowman extends mage.sets.iceage.GoblinSnowman { public GoblinSnowman(UUID ownerId) { super(ownerId); this.cardNumber = 64; - this.expansionSetCode = "ICE"; + this.expansionSetCode = "TSB"; } public GoblinSnowman(final GoblinSnowman card) { From b3cadac4eaca1c3206db3c0af046d32c9a9c211a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 4 Oct 2015 22:58:09 +0200 Subject: [PATCH 21/23] * Image download - Fixed a bug of mythicspoiler source, that preveneted download of EXP images. --- .../mage/plugins/card/dl/sources/MythicspoilerComSource.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java index ef80a0785a..78995593cd 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java @@ -100,7 +100,10 @@ public class MythicspoilerComSource implements CardImageSource { Map setLinks = new HashMap<>(); try { String setNames = setsAliases.get(cardSet.toLowerCase()); - Set aliasesStart = cardNameAliasesStart.get(cardSet); + Set aliasesStart = new HashSet<>(); + if (cardNameAliasesStart.containsKey(cardSet)) { + aliasesStart.addAll(cardNameAliasesStart.get(cardSet)); + } if (setNames == null) { setNames = cardSet.toLowerCase(); } From 6c2e2cad5f3978b681e65800d1786b95f8cdc218 Mon Sep 17 00:00:00 2001 From: emerald000 Date: Sun, 4 Oct 2015 17:48:51 -0400 Subject: [PATCH 22/23] Added Blatant Thievery, Drafna's Restoration and Naked Singularity. --- .../sets/antiquities/DrafnasRestoration.java | 166 +++++++++++++++++ .../mage/sets/iceage/NakedSingularity.java | 168 ++++++++++++++++++ .../masterseditioniv/NakedSingularity.java | 52 ++++++ .../mage/sets/onslaught/BlatantThievery.java | 84 +++++++++ 4 files changed, 470 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/antiquities/DrafnasRestoration.java create mode 100644 Mage.Sets/src/mage/sets/iceage/NakedSingularity.java create mode 100644 Mage.Sets/src/mage/sets/masterseditioniv/NakedSingularity.java create mode 100644 Mage.Sets/src/mage/sets/onslaught/BlatantThievery.java diff --git a/Mage.Sets/src/mage/sets/antiquities/DrafnasRestoration.java b/Mage.Sets/src/mage/sets/antiquities/DrafnasRestoration.java new file mode 100644 index 0000000000..55f990e15d --- /dev/null +++ b/Mage.Sets/src/mage/sets/antiquities/DrafnasRestoration.java @@ -0,0 +1,166 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.antiquities; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterArtifactCard; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInGraveyard; + +/** + * + * @author emerald000 + */ +public class DrafnasRestoration extends CardImpl { + + public DrafnasRestoration(UUID ownerId) { + super(ownerId, 52, "Drafna's Restoration", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{U}"); + this.expansionSetCode = "ATQ"; + + // Return any number of target artifact cards from target player's graveyard to the top of his or her library in any order. + this.getSpellAbility().addEffect(new DrafnasRestorationEffect()); + this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addTarget(new DrafnasRestorationTarget()); + } + + public DrafnasRestoration(final DrafnasRestoration card) { + super(card); + } + + @Override + public DrafnasRestoration copy() { + return new DrafnasRestoration(this); + } +} + +class DrafnasRestorationTarget extends TargetCardInGraveyard { + + DrafnasRestorationTarget() { + super(0, Integer.MAX_VALUE, new FilterArtifactCard("any number of artifact cards from that player's graveyard")); + } + + DrafnasRestorationTarget(final DrafnasRestorationTarget target) { + super(target); + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + return targetPlayer != null && targetPlayer.getGraveyard().contains(id) && super.canTarget(id, source, game); + } + + @Override + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + Set possibleTargets = new HashSet<>(); + MageObject object = game.getObject(sourceId); + if (object != null && object instanceof StackObject) { + Player targetPlayer = game.getPlayer(((StackObject) object).getStackAbility().getFirstTarget()); + if (targetPlayer != null) { + for (Card card : targetPlayer.getGraveyard().getCards(filter, sourceId, sourceControllerId, game)) { + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.TARGET, card.getId(), sourceId, sourceControllerId))) { + possibleTargets.add(card.getId()); + } + } + } + } + return possibleTargets; + } + + @Override + public DrafnasRestorationTarget copy() { + return new DrafnasRestorationTarget(this); + } +} + +class DrafnasRestorationEffect extends OneShotEffect { + + DrafnasRestorationEffect() { + super(Outcome.Benefit); + this.staticText = "Return any number of target artifact cards from target player's graveyard to the top of his or her library in any order"; + } + + DrafnasRestorationEffect(final DrafnasRestorationEffect effect) { + super(effect); + } + + @Override + public DrafnasRestorationEffect copy() { + return new DrafnasRestorationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if (controller != null && targetPlayer != null) { + Cards cards = new CardsImpl(source.getTargets().get(1).getTargets()); // prevent possible ConcurrentModificationException + cards.addAll(source.getTargets().get(1).getTargets()); + if (!cards.isEmpty()) { + TargetCard target = new TargetCard(Zone.PICK, new FilterCard("card to put on the top of the library (last one chosen will be topmost)")); + target.setRequired(true); + while (controller.isInGame() && cards.size() > 1) { + controller.choose(Outcome.Neutral, cards, target, game); + UUID targetId = target.getFirstTarget(); + cards.remove(targetId); + target.clearChosen(); + Card card = targetPlayer.getGraveyard().get(targetId, game); + if (card != null) { + controller.moveCards(card, Zone.GRAVEYARD, Zone.LIBRARY, source, game); + } + } + if (cards.size() == 1) { + Card card = targetPlayer.getGraveyard().get(cards.iterator().next(), game); + if (card != null) { + controller.moveCards(card, Zone.GRAVEYARD, Zone.LIBRARY, source, game); + } + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/iceage/NakedSingularity.java b/Mage.Sets/src/mage/sets/iceage/NakedSingularity.java new file mode 100644 index 0000000000..5ef0077ec2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/NakedSingularity.java @@ -0,0 +1,168 @@ +/* + * 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.iceage; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.keyword.CumulativeUpkeepAbility; +import mage.cards.CardImpl; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ManaEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author emerald000 + */ +public class NakedSingularity extends CardImpl { + + public NakedSingularity(UUID ownerId) { + super(ownerId, 305, "Naked Singularity", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{5}"); + this.expansionSetCode = "ICE"; + + // Cumulative upkeep {3} + this.addAbility(new CumulativeUpkeepAbility(new GenericManaCost(3))); + + // If tapped for mana, Plains produce {R}, Islands produce {G}, Swamps produce {W}, Mountains produce {U}, and Forests produce {B} instead of any other type. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NakedSingularityEffect())); + } + + public NakedSingularity(final NakedSingularity card) { + super(card); + } + + @Override + public NakedSingularity copy() { + return new NakedSingularity(this); + } +} + +class NakedSingularityEffect extends ReplacementEffectImpl { + + NakedSingularityEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral); + staticText = "If tapped for mana, Plains produce {R}, Islands produce {G}, Swamps produce {W}, Mountains produce {U}, and Forests produce {B} instead of any other type"; + } + + NakedSingularityEffect(final NakedSingularityEffect effect) { + super(effect); + } + + @Override + public NakedSingularityEffect copy() { + return new NakedSingularityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent permanent = game.getPermanent(event.getSourceId()); + Choice choice = new ChoiceImpl(true); + choice.setMessage("Pick a color to produce"); + if (permanent.hasSubtype("Plains")) { + choice.getChoices().add("Red"); + } + if (permanent.hasSubtype("Island")) { + choice.getChoices().add("Green"); + } + if (permanent.hasSubtype("Swamp")) { + choice.getChoices().add("White"); + } + if (permanent.hasSubtype("Mountain")) { + choice.getChoices().add("Blue"); + } + if (permanent.hasSubtype("Forest")) { + choice.getChoices().add("Black"); + } + String chosenColor; + if (choice.getChoices().size() == 1) { + chosenColor = choice.getChoices().iterator().next(); + } + else { + controller.choose(Outcome.PutManaInPool, choice, game); + chosenColor = choice.getChoice(); + } + ManaEvent manaEvent = (ManaEvent) event; + Mana mana = manaEvent.getMana(); + int amount = mana.count(); + switch (chosenColor) { + case "White": + mana.setToMana(Mana.WhiteMana(amount)); + break; + case "Blue": + mana.setToMana(Mana.BlueMana(amount)); + break; + case "Black": + mana.setToMana(Mana.BlackMana(amount)); + break; + case "Red": + mana.setToMana(Mana.RedMana(amount)); + break; + case "Green": + mana.setToMana(Mana.GreenMana(amount)); + break; + } + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TAPPED_FOR_MANA; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent permanent = game.getPermanent(event.getSourceId()); + return permanent != null + && (permanent.hasSubtype("Plains") + || permanent.hasSubtype("Island") + || permanent.hasSubtype("Swamp") + || permanent.hasSubtype("Mountain") + || permanent.hasSubtype("Forest")); + } +} diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/NakedSingularity.java b/Mage.Sets/src/mage/sets/masterseditioniv/NakedSingularity.java new file mode 100644 index 0000000000..355d9ad917 --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditioniv/NakedSingularity.java @@ -0,0 +1,52 @@ +/* + * 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.masterseditioniv; + +import java.util.UUID; + +/** + * + * @author emerald000 + */ +public class NakedSingularity extends mage.sets.iceage.NakedSingularity { + + public NakedSingularity(UUID ownerId) { + super(ownerId); + this.cardNumber = 216; + this.expansionSetCode = "ME4"; + } + + public NakedSingularity(final NakedSingularity card) { + super(card); + } + + @Override + public NakedSingularity copy() { + return new NakedSingularity(this); + } +} diff --git a/Mage.Sets/src/mage/sets/onslaught/BlatantThievery.java b/Mage.Sets/src/mage/sets/onslaught/BlatantThievery.java new file mode 100644 index 0000000000..512927844e --- /dev/null +++ b/Mage.Sets/src/mage/sets/onslaught/BlatantThievery.java @@ -0,0 +1,84 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.onslaught; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPermanent; + +/** + * + * @author emerald000 + */ +public class BlatantThievery extends CardImpl { + + public BlatantThievery(UUID ownerId) { + super(ownerId, 71, "Blatant Thievery", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{4}{U}{U}{U}"); + this.expansionSetCode = "ONS"; + + // For each opponent, gain control of target permanent that player controls. + Effect effect = new GainControlTargetEffect(Duration.EndOfGame); + effect.setText("For each opponent, gain control of target permanent that player controls"); + this.getSpellAbility().addEffect(effect); + } + + public BlatantThievery(final BlatantThievery card) { + super(card); + } + + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability instanceof SpellAbility) { + ability.getTargets().clear(); + for (UUID opponentId : game.getOpponents(ability.getControllerId())) { + Player opponent = game.getPlayer(opponentId); + if (opponent != null) { + FilterPermanent filter = new FilterPermanent("permanent controlled by " + opponent.getName()); + filter.add(new ControllerIdPredicate(opponentId)); + ability.addTarget(new TargetPermanent(filter)); + } + } + } + } + + @Override + public BlatantThievery copy() { + return new BlatantThievery(this); + } +} From 87f3054f5df854c5d8bef6cc900bc587484500e7 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Mon, 5 Oct 2015 09:51:54 +0300 Subject: [PATCH 23/23] Fix Tithe's card number --- Mage.Sets/src/mage/sets/visions/Tithe.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/sets/visions/Tithe.java b/Mage.Sets/src/mage/sets/visions/Tithe.java index dd0563cc51..d36db987a0 100644 --- a/Mage.Sets/src/mage/sets/visions/Tithe.java +++ b/Mage.Sets/src/mage/sets/visions/Tithe.java @@ -49,7 +49,7 @@ import mage.target.common.TargetOpponent; public class Tithe extends CardImpl { public Tithe(UUID ownerId) { - super(ownerId, 84, "Tithe", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{W}"); + super(ownerId, 123, "Tithe", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{W}"); this.expansionSetCode = "VIS"; // Search your library for a Plains card. If target opponent controls more lands than you, you may search your library for an additional Plains card. Reveal those cards and put them into your hand. Then shuffle your library. @@ -68,26 +68,26 @@ public class Tithe extends CardImpl { } class TitheEffect extends OneShotEffect { - + private static final FilterCard filter = new FilterCard("Plains"); static { filter.add(new SubtypePredicate("Plains")); } - + TitheEffect() { super(Outcome.Benefit); this.staticText = "Search your library for a Plains card. If target opponent controls more lands than you, you may search your library for an additional Plains card. Reveal those cards and put them into your hand. Then shuffle your library"; } - + TitheEffect(final TitheEffect effect) { super(effect); } - + @Override public TitheEffect copy() { return new TitheEffect(this); } - + @Override public boolean apply(Game game, Ability source) { int numYourLands = game.getBattlefield().countAll(new FilterLandPermanent(), source.getControllerId(), game);