From 1f44b8cde1d171f1cbd573c70c28cb4f3f5437eb Mon Sep 17 00:00:00 2001 From: wetterlicht Date: Mon, 16 May 2016 22:55:29 +0200 Subject: [PATCH 01/15] Implemented cards: Burden of Greed, Drill Skimmer, Drooling Ogre, Emissary of Despair, Psychic Overload, Pulse of the Tangle, Rebuking Ceremony, Tel-Jilad Wolf, Unforge, Vex --- .../mage/sets/darksteel/BurdenOfGreed.java | 101 ++++++++++++ .../src/mage/sets/darksteel/DrillSkimmer.java | 90 +++++++++++ .../src/mage/sets/darksteel/DroolingOgre.java | 145 ++++++++++++++++++ .../sets/darksteel/EmissaryOfDespair.java | 100 ++++++++++++ .../mage/sets/darksteel/PsychicOverload.java | 85 ++++++++++ .../mage/sets/darksteel/PulseOfTheTangle.java | 117 ++++++++++++++ .../mage/sets/darksteel/RebukingCeremony.java | 60 ++++++++ .../src/mage/sets/darksteel/TelJiladWolf.java | 74 +++++++++ .../src/mage/sets/darksteel/Unforge.java | 109 +++++++++++++ Mage.Sets/src/mage/sets/darksteel/Vex.java | 104 +++++++++++++ 10 files changed, 985 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/darksteel/BurdenOfGreed.java create mode 100644 Mage.Sets/src/mage/sets/darksteel/DrillSkimmer.java create mode 100644 Mage.Sets/src/mage/sets/darksteel/DroolingOgre.java create mode 100644 Mage.Sets/src/mage/sets/darksteel/EmissaryOfDespair.java create mode 100644 Mage.Sets/src/mage/sets/darksteel/PsychicOverload.java create mode 100644 Mage.Sets/src/mage/sets/darksteel/PulseOfTheTangle.java create mode 100644 Mage.Sets/src/mage/sets/darksteel/RebukingCeremony.java create mode 100644 Mage.Sets/src/mage/sets/darksteel/TelJiladWolf.java create mode 100644 Mage.Sets/src/mage/sets/darksteel/Unforge.java create mode 100644 Mage.Sets/src/mage/sets/darksteel/Vex.java diff --git a/Mage.Sets/src/mage/sets/darksteel/BurdenOfGreed.java b/Mage.Sets/src/mage/sets/darksteel/BurdenOfGreed.java new file mode 100644 index 0000000000..3bb212ffb6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/BurdenOfGreed.java @@ -0,0 +1,101 @@ +/* + * 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.darksteel; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.PermanentsTargetOpponentControlsCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.predicate.other.OwnerIdPredicate; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.target.TargetPlayer; + +/** + * + * @author wetterlicht + */ +public class BurdenOfGreed extends CardImpl { + + public BurdenOfGreed(UUID ownerId) { + super(ownerId, 38, "Burden of Greed", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{3}{B}"); + this.expansionSetCode = "DST"; + + // Target player loses 1 life for each tapped artifact he or she controls. + getSpellAbility().addTarget(new TargetPlayer()); + getSpellAbility().addEffect(new LoseLifeTargetEffect(new BurdenOfGreedCount())); + + } + + public BurdenOfGreed(final BurdenOfGreed card) { + super(card); + } + + @Override + public BurdenOfGreed copy() { + return new BurdenOfGreed(this); + } +} + +class BurdenOfGreedCount implements DynamicValue { + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + if (sourceAbility.getFirstTarget() == null) { + return 0; + } + FilterArtifactPermanent filter = new FilterArtifactPermanent(); + filter.add(new TappedPredicate()); + filter.add(new ControllerIdPredicate(sourceAbility.getFirstTarget())); + return game.getBattlefield().count(filter, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game); + } + + @Override + public DynamicValue copy() { + return new BurdenOfGreedCount(); + } + + @Override + public String toString() { + return "1"; + } + + @Override + public String getMessage() { + return "tapped artifact he or she controls"; + } + +} diff --git a/Mage.Sets/src/mage/sets/darksteel/DrillSkimmer.java b/Mage.Sets/src/mage/sets/darksteel/DrillSkimmer.java new file mode 100644 index 0000000000..ff33a8def1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/DrillSkimmer.java @@ -0,0 +1,90 @@ +/* + * 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.darksteel; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.ShroudAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPlaneswalkerPermanent; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.permanent.AnotherPredicate; + +/** + * + * @author wetterlicht + */ +public class DrillSkimmer extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("you control another artifact creature"); + + static { + filter.add(new AnotherPredicate()); + filter.add(new CardTypePredicate(CardType.ARTIFACT)); + } + + public DrillSkimmer(UUID ownerId) { + super(ownerId, 118, "Drill-Skimmer", Rarity.COMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + this.expansionSetCode = "DST"; + this.subtype.add("Thopter"); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + // Drill-Skimmer has shroud as long as you control another artifact creature. + Condition condition = new PermanentsOnTheBattlefieldCondition(filter, PermanentsOnTheBattlefieldCondition.CountType.MORE_THAN, 0); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(new GainAbilitySourceEffect(ShroudAbility.getInstance(), Duration.WhileOnBattlefield), + condition, "{this} has shroud as long as you control another artifact creature."))); + } + + public DrillSkimmer(final DrillSkimmer card) { + super(card); + } + + @Override + public DrillSkimmer copy() { + return new DrillSkimmer(this); + } +} diff --git a/Mage.Sets/src/mage/sets/darksteel/DroolingOgre.java b/Mage.Sets/src/mage/sets/darksteel/DroolingOgre.java new file mode 100644 index 0000000000..b2368462b4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/DroolingOgre.java @@ -0,0 +1,145 @@ +/* + * 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.darksteel; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +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.common.FilterArtifactSpell; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author wetterlicht + */ +public class DroolingOgre extends CardImpl { + + public DroolingOgre(UUID ownerId) { + super(ownerId, 58, "Drooling Ogre", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); + this.expansionSetCode = "DST"; + this.subtype.add("Ogre"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever a player casts an artifact spell, that player gains control of Drooling Ogre. + this.addAbility(new DroolingOgreTriggeredAbility()); + } + + public DroolingOgre(final DroolingOgre card) { + super(card); + } + + @Override + public DroolingOgre copy() { + return new DroolingOgre(this); + } + + private static class DroolingOgreEffect extends OneShotEffect { + + public DroolingOgreEffect() { + super(Outcome.GainControl); + this.staticText = "that player gains control of {this}"; + } + + private DroolingOgreEffect(DroolingOgreEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player newController = game.getPlayer(this.getTargetPointer().getFirst(game, source)); + if (newController != null && controller != null && !controller.equals(newController)) { + ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, newController.getId()); + effect.setTargetPointer(new FixedTarget(source.getSourceId())); + game.addEffect(effect, source); + return true; + } + return false; + } + + @Override + public Effect copy() { + return new DroolingOgreEffect(this); + } + + } + + class DroolingOgreTriggeredAbility extends TriggeredAbilityImpl { + + public DroolingOgreTriggeredAbility() { + super(Zone.BATTLEFIELD, new DroolingOgreEffect(), false); + } + + public DroolingOgreTriggeredAbility(final DroolingOgreTriggeredAbility ability) { + super(ability); + } + + @Override + public DroolingOgreTriggeredAbility copy() { + return new DroolingOgreTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null && spell.getCardType().contains(CardType.ARTIFACT)) { + this.getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever a player casts an artifact spell, that player gains control of {this}"; + } + } +} diff --git a/Mage.Sets/src/mage/sets/darksteel/EmissaryOfDespair.java b/Mage.Sets/src/mage/sets/darksteel/EmissaryOfDespair.java new file mode 100644 index 0000000000..70a8a54d27 --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/EmissaryOfDespair.java @@ -0,0 +1,100 @@ +/* + * 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.darksteel; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; + +/** + * + * @author wetterlicht + */ +public class EmissaryOfDespair extends CardImpl { + + public EmissaryOfDespair(UUID ownerId) { + super(ownerId, 42, "Emissary of Despair", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); + this.expansionSetCode = "DST"; + this.subtype.add("Spirit"); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + // Whenever Emissary of Despair deals combat damage to a player, that player loses 1 life for each artifact he or she controls. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new LoseLifeTargetEffect(new EmissaryOfDespairCount()), false, true)); + } + + public EmissaryOfDespair(final EmissaryOfDespair card) { + super(card); + } + + @Override + public EmissaryOfDespair copy() { + return new EmissaryOfDespair(this); + } +} + +class EmissaryOfDespairCount implements DynamicValue { + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + if (sourceAbility.getFirstTarget() == null) { + return 0; + } + FilterArtifactPermanent filter = new FilterArtifactPermanent(); + filter.add(new ControllerIdPredicate(sourceAbility.getFirstTarget())); + return game.getBattlefield().count(filter, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game); + } + + @Override + public DynamicValue copy() { + return new EmissaryOfDespairCount(); + } + + @Override + public String toString() { + return "1"; + } + + @Override + public String getMessage() { + return "artifact he or she controls"; + } +} diff --git a/Mage.Sets/src/mage/sets/darksteel/PsychicOverload.java b/Mage.Sets/src/mage/sets/darksteel/PsychicOverload.java new file mode 100644 index 0000000000..04e8c82dcc --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/PsychicOverload.java @@ -0,0 +1,85 @@ +/* + * 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.darksteel; + +import java.util.UUID; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.costs.common.DiscardXTargetCost; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DontUntapInControllersUntapStepEnchantedEffect; +import mage.abilities.effects.common.TapEnchantedEffect; +import mage.abilities.effects.common.UntapEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +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.common.FilterArtifactCard; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInHand; + +/** + * + * @author wetterlicht + */ +public class PsychicOverload extends CardImpl { + + public PsychicOverload(UUID ownerId) { + super(ownerId, 28, "Psychic Overload", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); + this.expansionSetCode = "DST"; + this.subtype.add("Aura"); + + // Enchant permanent + TargetPermanent auraTarget = new TargetPermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // When Psychic Overload enters the battlefield, tap enchanted permanent. + this.addAbility(new EntersBattlefieldTriggeredAbility(new TapEnchantedEffect())); + // Enchanted permanent doesn't untap during its controller's untap step. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect())); + // Enchanted permanent has "Discard two artifact cards: Untap this permanent." + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new UntapEnchantedEffect(), new DiscardTargetCost(new TargetCardInHand(2, new FilterArtifactCard("two artifact cards"))))); + } + + public PsychicOverload(final PsychicOverload card) { + super(card); + } + + @Override + public PsychicOverload copy() { + return new PsychicOverload(this); + } +} diff --git a/Mage.Sets/src/mage/sets/darksteel/PulseOfTheTangle.java b/Mage.Sets/src/mage/sets/darksteel/PulseOfTheTangle.java new file mode 100644 index 0000000000..029347f42c --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/PulseOfTheTangle.java @@ -0,0 +1,117 @@ +/* + * 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.darksteel; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +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.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.token.BeastToken; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.target.common.TargetOpponent; + +/** + * + * @author wetterlicht + */ +public class PulseOfTheTangle extends CardImpl { + + public PulseOfTheTangle(UUID ownerId) { + super(ownerId, 80, "Pulse of the Tangle", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{G}{G}"); + this.expansionSetCode = "DST"; + + // Put a 3/3 green Beast creature token onto the battlefield. Then if an opponent controls more creatures than you, return Pulse of the Tangle to its owner's hand. + this.getSpellAbility().addEffect(new CreateTokenEffect(new BeastToken())); + this.getSpellAbility().addEffect(new PulseOfTheTangleReturnToHandEffect()); + } + + public PulseOfTheTangle(final PulseOfTheTangle card) { + super(card); + } + + @Override + public PulseOfTheTangle copy() { + return new PulseOfTheTangle(this); + } +} + +class PulseOfTheTangleReturnToHandEffect extends OneShotEffect { + + PulseOfTheTangleReturnToHandEffect() { + super(Outcome.Benefit); + this.staticText = "Then if an opponent controls more creatures than you, return Pulse of the Tangle to its owner's hand"; + } + + PulseOfTheTangleReturnToHandEffect(final PulseOfTheTangleReturnToHandEffect effect) { + super(effect); + } + + @Override + public PulseOfTheTangleReturnToHandEffect copy() { + return new PulseOfTheTangleReturnToHandEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + FilterControlledCreaturePermanent controllerFilter = new FilterControlledCreaturePermanent(); + PermanentsOnBattlefieldCount controllerCount = new PermanentsOnBattlefieldCount(controllerFilter); + boolean check = false; + if (controller != null) { + for (UUID opponentID : game.getOpponents(controller.getId())) { + if (opponentID != null) { + FilterCreaturePermanent opponentFilter = new FilterCreaturePermanent(); + opponentFilter.add(new ControllerIdPredicate(opponentID)); + PermanentsOnBattlefieldCount opponentCreatureCount = new PermanentsOnBattlefieldCount(opponentFilter); + check = opponentCreatureCount.calculate(game, source, this) > controllerCount.calculate(game, source, this); + if (check) { + break; + } + } + } + if (check) { + Card card = game.getCard(source.getSourceId()); + controller.moveCards(card, Zone.HAND, source, game); + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/darksteel/RebukingCeremony.java b/Mage.Sets/src/mage/sets/darksteel/RebukingCeremony.java new file mode 100644 index 0000000000..8f800cd19d --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/RebukingCeremony.java @@ -0,0 +1,60 @@ +/* + * 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.darksteel; + +import java.util.UUID; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.target.common.TargetArtifactPermanent; + +/** + * + * @author wetterlicht + */ +public class RebukingCeremony extends CardImpl { + + public RebukingCeremony(UUID ownerId) { + super(ownerId, 82, "Rebuking Ceremony", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{3}{G}{G}"); + this.expansionSetCode = "DST"; + + // Put two target artifacts on top of their owners' libraries. + getSpellAbility().addEffect(new PutOnLibraryTargetEffect(true)); + getSpellAbility().addTarget(new TargetArtifactPermanent(2)); + } + + public RebukingCeremony(final RebukingCeremony card) { + super(card); + } + + @Override + public RebukingCeremony copy() { + return new RebukingCeremony(this); + } +} diff --git a/Mage.Sets/src/mage/sets/darksteel/TelJiladWolf.java b/Mage.Sets/src/mage/sets/darksteel/TelJiladWolf.java new file mode 100644 index 0000000000..ac25c3e509 --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/TelJiladWolf.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.darksteel; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BecomesBlockedAllTriggeredAbility; +import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility; +import mage.abilities.common.BecomesBlockedTriggeredAbility; +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.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.CardTypePredicate; + +/** + * + * @author wetterlicht + */ +public class TelJiladWolf extends CardImpl { + + private static FilterCreaturePermanent filter = new FilterCreaturePermanent("artifact creature"); + + static{ + filter.add(new CardTypePredicate(CardType.ARTIFACT)); + } + + public TelJiladWolf(UUID ownerId) { + super(ownerId, 88, "Tel-Jilad Wolf", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{G}"); + this.expansionSetCode = "DST"; + this.subtype.add("Wolf"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever Tel-Jilad Wolf becomes blocked by an artifact creature, Tel-Jilad Wolf gets +3/+3 until end of turn. + this.addAbility(new BecomesBlockedByCreatureTriggeredAbility(new BoostSourceEffect(3, 3, Duration.EndOfTurn), filter, false)); + } + + public TelJiladWolf(final TelJiladWolf card) { + super(card); + } + + @Override + public TelJiladWolf copy() { + return new TelJiladWolf(this); + } +} diff --git a/Mage.Sets/src/mage/sets/darksteel/Unforge.java b/Mage.Sets/src/mage/sets/darksteel/Unforge.java new file mode 100644 index 0000000000..023d1db70e --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/Unforge.java @@ -0,0 +1,109 @@ +/* + * 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.darksteel; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetArtifactPermanent; + +/** + * + * @author wetterlicht + */ +public class Unforge extends CardImpl { + + private static FilterArtifactPermanent filter = new FilterArtifactPermanent("equipment"); + + static{ + filter.add(new SubtypePredicate("Equipment")); + } + + public Unforge(UUID ownerId) { + super(ownerId, 71, "Unforge", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{R}"); + this.expansionSetCode = "DST"; + + // Destroy target Equipment. If that Equipment was attached to a creature, Unforge deals 2 damage to that creature. + getSpellAbility().addTarget(new TargetArtifactPermanent(filter)); + getSpellAbility().addEffect(new DestroyTargetEffect()); + getSpellAbility().addEffect(new UnforgeEffect()); + } + + public Unforge(final Unforge card) { + super(card); + } + + @Override + public Unforge copy() { + return new Unforge(this); + } + +} + +class UnforgeEffect extends OneShotEffect{ + + public UnforgeEffect(){ + super(Outcome.Damage); + staticText = "If that Equipment was attached to a creature, Unforge deals 2 damage to that creature."; + } + + public UnforgeEffect(final UnforgeEffect effect){ + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent equipment = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source)); + if(equipment != null){ + Permanent creature = game.getPermanent(equipment.getAttachedTo()); + if(creature != null){ + creature.damage(2, source.getId(), game, false, true); + return true; + } + } + + return false; + } + + @Override + public Effect copy() { + return new UnforgeEffect(this); + } + + +} diff --git a/Mage.Sets/src/mage/sets/darksteel/Vex.java b/Mage.Sets/src/mage/sets/darksteel/Vex.java new file mode 100644 index 0000000000..25b9675f3e --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/Vex.java @@ -0,0 +1,104 @@ +/* + * 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.darksteel; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetSpell; + +/** + * + * @author wetterlicht + */ +public class Vex extends CardImpl { + + public Vex(UUID ownerId) { + super(ownerId, 36, "Vex", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{U}"); + this.expansionSetCode = "DST"; + + // Counter target spell. That spell's controller may draw a card. + getSpellAbility().addTarget(new TargetSpell()); + getSpellAbility().addEffect(new VexEffect()); + } + + public Vex(final Vex card) { + super(card); + } + + @Override + public Vex copy() { + return new Vex(this); + } +} + +class VexEffect extends OneShotEffect { + + public VexEffect() { + super(Outcome.Neutral); + this.staticText = "That spell's controller may draw a card"; + } + + public VexEffect(final VexEffect effect) { + super(effect); + } + + @Override + public VexEffect copy() { + return new VexEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + UUID targetId = source.getFirstTarget(); + Player controller = null; + boolean countered = false; + if (targetId != null) { + controller = game.getPlayer(game.getControllerId(targetId)); + } + if (targetId != null + && game.getStack().counter(targetId, source.getSourceId(), game)) { + countered = true; + } + if (controller != null) { + controller.drawCards(1, game); + } + return countered; + } +} From 140d7e93761fb561479fba9d7d36dee16c2a8b11 Mon Sep 17 00:00:00 2001 From: wetterlicht Date: Mon, 16 May 2016 22:57:28 +0200 Subject: [PATCH 02/15] Added Filter to BecomesBlockedByCreatureTriggeredAbility --- ...omesBlockedByCreatureTriggeredAbility.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java index 641a4885ba..1386e11bcc 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java @@ -30,8 +30,10 @@ package mage.abilities.common; import mage.constants.Zone; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; /** @@ -40,10 +42,17 @@ import mage.target.targetpointer.FixedTarget; */ public class BecomesBlockedByCreatureTriggeredAbility extends TriggeredAbilityImpl { + private static FilterCreaturePermanent filter = new FilterCreaturePermanent(); + public BecomesBlockedByCreatureTriggeredAbility(Effect effect, boolean optional) { super(Zone.BATTLEFIELD, effect, optional); } + public BecomesBlockedByCreatureTriggeredAbility(Effect effect, FilterCreaturePermanent filter, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + this.filter = filter; + } + public BecomesBlockedByCreatureTriggeredAbility(final BecomesBlockedByCreatureTriggeredAbility ability) { super(ability); } @@ -56,17 +65,20 @@ public class BecomesBlockedByCreatureTriggeredAbility extends TriggeredAbilityIm @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getTargetId().equals(this.getSourceId())) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); + Permanent blocker = game.getPermanent(event.getSourceId()); + if (filter.match(blocker, game)) { + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getSourceId())); + } + return true; } - return true; } return false; } @Override public String getRule() { - return "Whenever {this} becomes blocked by a creature, " + super.getRule(); + return "Whenever {this} becomes blocked by a " + filter.getMessage() + ", " + super.getRule(); } @Override From 28902e5616e7e98766a1be824ac392444e2a834d Mon Sep 17 00:00:00 2001 From: wetterlicht Date: Mon, 16 May 2016 22:58:27 +0200 Subject: [PATCH 03/15] Implemented Murderous Spoils --- .../mage/sets/darksteel/MurderousSpoils.java | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/darksteel/MurderousSpoils.java diff --git a/Mage.Sets/src/mage/sets/darksteel/MurderousSpoils.java b/Mage.Sets/src/mage/sets/darksteel/MurderousSpoils.java new file mode 100644 index 0000000000..018c2b2c71 --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/MurderousSpoils.java @@ -0,0 +1,123 @@ +/* + * 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.darksteel; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author wetterlicht + */ +public class MurderousSpoils extends CardImpl { + + private static final FilterCreaturePermanent FILTER = new FilterCreaturePermanent("nonblack creature"); + + static { + FILTER.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); + } + + public MurderousSpoils(UUID ownerId) { + super(ownerId, 48, "Murderous Spoils", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{5}{B}"); + this.expansionSetCode = "DST"; + + // Destroy target nonblack creature. It can't be regenerated. You gain control of all Equipment that was attached to it. + this.getSpellAbility().addTarget(new TargetCreaturePermanent(FILTER)); + this.getSpellAbility().addEffect(new MurderousSpoilsEffect()); + + } + + public MurderousSpoils(final MurderousSpoils card) { + super(card); + } + + @Override + public MurderousSpoils copy() { + return new MurderousSpoils(this); + } +} + +class MurderousSpoilsEffect extends OneShotEffect { + + public MurderousSpoilsEffect() { + super(Outcome.DestroyPermanent); + staticText = "Destroy target nonblack creature. It can't be regenerated. You gain control of all Equipment that was attached to it."; + } + + public MurderousSpoilsEffect(final MurderousSpoilsEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent target = game.getPermanent(source.getFirstTarget()); + if (target != null) { + List attachments = new ArrayList(); + for (UUID uuid : target.getAttachments()) { + Permanent attached = game.getBattlefield().getPermanent(uuid); + if (attached.getSubtype().contains("Equipment")) { + attachments.add(attached); + } + } + for (Permanent p : attachments) { + ContinuousEffect gainControl = new GainControlTargetEffect(Duration.EndOfGame); + gainControl.setTargetPointer(new FixedTarget(p.getId())); + game.addEffect(gainControl, source); + } + target.destroy(source.getId(), game, true); + return true; + } + return false; + } + + @Override + public MurderousSpoilsEffect copy() { + return new MurderousSpoilsEffect(this); + } + +} From eea532b2a574d56e6b66f83d1f922f8e78ae9626 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 19 May 2016 12:52:57 +0200 Subject: [PATCH 04/15] * Invoke Prejudice - Some changes. --- .../mage/sets/legends/InvokePrejudice.java | 73 ++++++------------- 1 file changed, 23 insertions(+), 50 deletions(-) diff --git a/Mage.Sets/src/mage/sets/legends/InvokePrejudice.java b/Mage.Sets/src/mage/sets/legends/InvokePrejudice.java index 521ad011a7..7f2a670d3e 100644 --- a/Mage.Sets/src/mage/sets/legends/InvokePrejudice.java +++ b/Mage.Sets/src/mage/sets/legends/InvokePrejudice.java @@ -28,12 +28,12 @@ package mage.sets.legends; import java.util.UUID; +import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterUnlessPaysEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; @@ -92,54 +92,31 @@ class InvokePrejudiceTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - Card card = game.getCard(event.getSourceId()); - - if (card == null || !(card.getCardType().contains(CardType.CREATURE))) { - return false; - } - - // Get colors of the card - boolean castCreatureIsWhite = card.getColor(game).isWhite(); - boolean castCreatureIsBlue = card.getColor(game).isBlue(); - boolean castCreatureIsBlack = card.getColor(game).isBlack(); - boolean castCreatureIsRed = card.getColor(game).isRed(); - boolean castCreatureIsGreen = card.getColor(game).isGreen(); - - // Compare to colours of creatures of controller on bf - boolean hasToPay = true; - boolean gotACreature = false; - - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), getControllerId(), game)) { - gotACreature = true; - if (castCreatureIsWhite && permanent.getColor(game).isWhite()) { - hasToPay = false; - } - if (castCreatureIsBlue && permanent.getColor(game).isBlue()) { - hasToPay = false; - } - if (castCreatureIsBlack && permanent.getColor(game).isBlack()) { - hasToPay = false; - } - if (castCreatureIsRed && permanent.getColor(game).isRed()) { - hasToPay = false; - } - if (castCreatureIsGreen && permanent.getColor(game).isGreen()) { - hasToPay = false; + if (game.getOpponents(getControllerId()).contains(event.getPlayerId())) { + Spell spell = (Spell) game.getObject(event.getTargetId()); + if (spell != null && spell.getCardType().contains(CardType.CREATURE)) { + boolean creatureSharesAColor = false; + ObjectColor spellColor = spell.getColor(game); + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterControlledCreaturePermanent(), getControllerId(), game)) { + if (spellColor.shares(permanent.getColor(game))) { + creatureSharesAColor = true; + break; + } + } + if (!creatureSharesAColor) { + for (Effect effect : getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getTargetId())); + } + return true; + } } } - - if (hasToPay || !gotACreature) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(card.getId())); - } - } - - return hasToPay || !gotACreature; + return false; } @Override public String getRule() { - return "Whenever an opponent casts a creature spell that doesn't share a color with a creature you control, counter that spell unless that player pays {X}, where X is its converted mana cost."; + return "Whenever an opponent casts a creature spell that doesn't share a color with a creature you control, " + super.getRule(); } } @@ -163,14 +140,10 @@ class InvokePrejudiceEffect extends CounterUnlessPaysEffect { public boolean apply(Game game, Ability source) { boolean result = true; Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); - if (spell != null) { - Card card = spell.getCard(); - if (card != null) { - CounterUnlessPaysEffect effect = new CounterUnlessPaysEffect(new GenericManaCost(card.getConvertedManaCost())); - effect.setTargetPointer(new FixedTarget(spell.getId())); - result = effect.apply(game, source); - } + CounterUnlessPaysEffect effect = new CounterUnlessPaysEffect(new GenericManaCost(spell.getConvertedManaCost())); + effect.setTargetPointer(getTargetPointer()); + result = effect.apply(game, source); } return result; } From f81514842459a800c41fee6c63dba55f633ff023 Mon Sep 17 00:00:00 2001 From: Quercitron Date: Fri, 20 May 2016 03:25:03 +0300 Subject: [PATCH 05/15] * Runeflare Trap - Fix alternative cost condition (fixes #1943). --- .../journeyintonyx/KeranosGodOfStorms.java | 64 +---------- .../mage/sets/zendikar/ArchmageAscension.java | 51 ++------- .../src/mage/sets/zendikar/RuneflareTrap.java | 61 +--------- .../CardsAmountDrawnThisTurnWatcher.java | 104 ++++++++++++++++++ 4 files changed, 123 insertions(+), 157 deletions(-) create mode 100644 Mage/src/main/java/mage/watchers/common/CardsAmountDrawnThisTurnWatcher.java diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java b/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java index e43f6447ae..7695dff5a3 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java @@ -27,9 +27,6 @@ */ package mage.sets.journeyintonyx; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; @@ -47,7 +44,6 @@ import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; import mage.constants.Rarity; -import mage.constants.WatcherScope; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; @@ -55,7 +51,7 @@ import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreatureOrPlayer; -import mage.watchers.Watcher; +import mage.watchers.common.CardsAmountDrawnThisTurnWatcher; /** * @@ -83,7 +79,7 @@ public class KeranosGodOfStorms extends CardImpl { // Reveal the first card you draw on each of your turns. // Whenever you reveal a land card this way, draw a card. // Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player. - this.addAbility(new KeranosGodOfStormsTriggeredAbility(), new CardsDrawnDuringTurnWatcher()); + this.addAbility(new KeranosGodOfStormsTriggeredAbility(), new CardsAmountDrawnThisTurnWatcher()); } @@ -122,7 +118,8 @@ class KeranosGodOfStormsTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getPlayerId().equals(this.getControllerId())) { if (game.getActivePlayerId().equals(this.getControllerId())) { - CardsDrawnDuringTurnWatcher watcher = (CardsDrawnDuringTurnWatcher) game.getState().getWatchers().get("CardsDrawnDuringTurn"); + CardsAmountDrawnThisTurnWatcher watcher = + (CardsAmountDrawnThisTurnWatcher) game.getState().getWatchers().get(CardsAmountDrawnThisTurnWatcher.BASIC_KEY); if (watcher != null && watcher.getAmountCardsDrawn(event.getPlayerId()) != 1) { return false; } @@ -148,56 +145,7 @@ class KeranosGodOfStormsTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Reveal the first card you draw on each of your turns. Whenever you reveal a land card this way, draw a card. Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player."; + return "Reveal the first card you draw on each of your turns. Whenever you reveal a land card this way, draw a card. " + + "Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player."; } } - -class CardsDrawnDuringTurnWatcher extends Watcher { - - private final Map amountOfCardsDrawnThisTurn = new HashMap<>(); - - public CardsDrawnDuringTurnWatcher() { - super("CardsDrawnDuringTurn", WatcherScope.GAME); - } - - public CardsDrawnDuringTurnWatcher(final CardsDrawnDuringTurnWatcher watcher) { - super(watcher); - for (Entry entry : watcher.amountOfCardsDrawnThisTurn.entrySet()) { - amountOfCardsDrawnThisTurn.put(entry.getKey(), entry.getValue()); - } - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DREW_CARD) { - UUID playerId = event.getPlayerId(); - if (playerId != null) { - Integer amount = amountOfCardsDrawnThisTurn.get(playerId); - if (amount == null) { - amount = 1; - } else { - amount++; - } - amountOfCardsDrawnThisTurn.put(playerId, amount); - } - } - } - - public int getAmountCardsDrawn(UUID playerId) { - Integer amount = amountOfCardsDrawnThisTurn.get(playerId); - if (amount != null) { - return amount; - } - return 0; - } - - @Override - public void reset() { - amountOfCardsDrawnThisTurn.clear(); - } - - @Override - public CardsDrawnDuringTurnWatcher copy() { - return new CardsDrawnDuringTurnWatcher(this); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/zendikar/ArchmageAscension.java b/Mage.Sets/src/mage/sets/zendikar/ArchmageAscension.java index 81ea81a305..bf5da3cf61 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ArchmageAscension.java +++ b/Mage.Sets/src/mage/sets/zendikar/ArchmageAscension.java @@ -41,7 +41,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInLibrary; -import mage.watchers.Watcher; +import mage.watchers.common.CardsAmountDrawnThisTurnWatcher; import java.util.UUID; @@ -57,9 +57,10 @@ public class ArchmageAscension extends CardImpl { // At the beginning of each end step, if you drew two or more cards this turn, you may put a quest counter on Archmage Ascension. - this.addAbility(new ArchmageAscensionTriggeredAbility(), new CardsDrawnControllerWatcher()); + this.addAbility(new ArchmageAscensionTriggeredAbility(), new CardsAmountDrawnThisTurnWatcher()); - // As long as Archmage Ascension has six or more quest counters on it, if you would draw a card, you may instead search your library for a card, put that card into your hand, then shuffle your library. + // As long as Archmage Ascension has six or more quest counters on it, if you would draw a card, + // you may instead search your library for a card, put that card into your hand, then shuffle your library. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ArchmageAscensionReplacementEffect())); } @@ -97,8 +98,9 @@ class ArchmageAscensionTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent archmage = game.getPermanent(super.getSourceId()); - CardsDrawnControllerWatcher watcher = (CardsDrawnControllerWatcher) game.getState().getWatchers().get("CardsControllerDrawn"); - return archmage != null && watcher != null && watcher.conditionMet(); + CardsAmountDrawnThisTurnWatcher watcher = + (CardsAmountDrawnThisTurnWatcher) game.getState().getWatchers().get(CardsAmountDrawnThisTurnWatcher.BASIC_KEY); + return archmage != null && watcher != null && watcher.getAmountCardsDrawn(this.getControllerId()) >= 2; } @Override @@ -107,47 +109,12 @@ class ArchmageAscensionTriggeredAbility extends TriggeredAbilityImpl { } } -class CardsDrawnControllerWatcher extends Watcher { - - int cardsDrawn; - - public CardsDrawnControllerWatcher() { - super("CardsControllerDrawn", WatcherScope.GAME); - } - - public CardsDrawnControllerWatcher(final CardsDrawnControllerWatcher watcher) { - super(watcher); - this.cardsDrawn = watcher.cardsDrawn; - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DREW_CARD - && event.getPlayerId().equals(controllerId)) { - cardsDrawn += 1; - if (cardsDrawn >= 2) { - condition = true; - } - } - } - - @Override - public void reset() { - super.reset(); - cardsDrawn = 0; - } - - @Override - public CardsDrawnControllerWatcher copy() { - return new CardsDrawnControllerWatcher(this); - } -} - class ArchmageAscensionReplacementEffect extends ReplacementEffectImpl { public ArchmageAscensionReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "As long as {this} has six or more quest counters on it, if you would draw a card, you may instead search your library for a card, put that card into your hand, then shuffle your library"; + staticText = "As long as {this} has six or more quest counters on it, if you would draw a card, " + + "you may instead search your library for a card, put that card into your hand, then shuffle your library"; } public ArchmageAscensionReplacementEffect(final ArchmageAscensionReplacementEffect effect) { diff --git a/Mage.Sets/src/mage/sets/zendikar/RuneflareTrap.java b/Mage.Sets/src/mage/sets/zendikar/RuneflareTrap.java index 7c905403fd..b9d9629f0e 100644 --- a/Mage.Sets/src/mage/sets/zendikar/RuneflareTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/RuneflareTrap.java @@ -27,8 +27,6 @@ */ package mage.sets.zendikar; -import java.util.HashMap; -import java.util.Map; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.Condition; @@ -40,12 +38,10 @@ import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.WatcherScope; import mage.game.Game; -import mage.game.events.GameEvent; import mage.players.Player; import mage.target.TargetPlayer; -import mage.watchers.Watcher; +import mage.watchers.common.CardsAmountDrawnThisTurnWatcher; /** * @@ -59,7 +55,7 @@ public class RuneflareTrap extends CardImpl { this.subtype.add("Trap"); // If an opponent drew three or more cards this turn, you may pay {R} rather than pay Runeflare Trap's mana cost. - this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{R}"), RavenousTrapCondition.getInstance()), new CardsDrawnWatcher()); + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{R}"), RuneflareTrapCondition.getInstance()), new CardsAmountDrawnThisTurnWatcher()); // Runeflare Trap deals damage to target player equal to the number of cards in that player's hand. this.getSpellAbility().addEffect(new DamageTargetEffect(new TargetPlayerCardsInHandCount())); @@ -115,7 +111,8 @@ class RuneflareTrapCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - CardsDrawnWatcher watcher = (CardsDrawnWatcher) game.getState().getWatchers().get("CardsDrawnWatcher"); + CardsAmountDrawnThisTurnWatcher watcher = + (CardsAmountDrawnThisTurnWatcher) game.getState().getWatchers().get(CardsAmountDrawnThisTurnWatcher.BASIC_KEY); return watcher != null && watcher.opponentDrewXOrMoreCards(source.getControllerId(), 3, game); } @@ -124,53 +121,3 @@ class RuneflareTrapCondition implements Condition { return "If an opponent drew three or more cards this turn"; } } - -class CardsDrawnWatcher extends Watcher { - - private final Map playerCardsDrawn = new HashMap<>(); - - public CardsDrawnWatcher() { - super("CardsDrawnWatcher", WatcherScope.GAME); - } - - public CardsDrawnWatcher(final CardsDrawnWatcher watcher) { - super(watcher); - playerCardsDrawn.putAll(watcher.playerCardsDrawn); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DREW_CARD) { - if (playerCardsDrawn.containsKey(event.getPlayerId())) { - playerCardsDrawn.put(event.getPlayerId(), playerCardsDrawn.get(event.getPlayerId()) + 1); - } else { - playerCardsDrawn.put(event.getPlayerId(), 1); - } - } - } - - @Override - public void reset() { - super.reset(); - playerCardsDrawn.clear(); - } - - public boolean opponentDrewXOrMoreCards(UUID playerId, int x, Game game) { - Player player = game.getPlayer(playerId); - if (player != null) { - for (Map.Entry entry : playerCardsDrawn.entrySet()) { - if (game.isOpponent(player, playerId)) { - if (entry.getValue() >= x) { - return true; - } - } - } - } - return false; - } - - @Override - public CardsDrawnWatcher copy() { - return new CardsDrawnWatcher(this); - } -} diff --git a/Mage/src/main/java/mage/watchers/common/CardsAmountDrawnThisTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/CardsAmountDrawnThisTurnWatcher.java new file mode 100644 index 0000000000..b9f7d74154 --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/CardsAmountDrawnThisTurnWatcher.java @@ -0,0 +1,104 @@ +/* + * 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.watchers.common; + +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * + * @author Quercitron + * @author LevelX2 + * @author jeffwadsworth + */ +public class CardsAmountDrawnThisTurnWatcher extends Watcher { + + public final static String BASIC_KEY = "CardsAmountDrawnThisTurnWatcher"; + + private final Map amountOfCardsDrawnThisTurn = new HashMap<>(); + + public CardsAmountDrawnThisTurnWatcher() { + super(BASIC_KEY, WatcherScope.GAME); + } + + public CardsAmountDrawnThisTurnWatcher(final CardsAmountDrawnThisTurnWatcher watcher) { + super(watcher); + amountOfCardsDrawnThisTurn.putAll(watcher.amountOfCardsDrawnThisTurn); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DREW_CARD) { + if (amountOfCardsDrawnThisTurn.containsKey(event.getPlayerId())) { + amountOfCardsDrawnThisTurn.put(event.getPlayerId(), amountOfCardsDrawnThisTurn.get(event.getPlayerId()) + 1); + } else { + amountOfCardsDrawnThisTurn.put(event.getPlayerId(), 1); + } + } + } + + @Override + public void reset() { + super.reset(); + amountOfCardsDrawnThisTurn.clear(); + } + + public boolean opponentDrewXOrMoreCards(UUID playerId, int x, Game game) { + Player player = game.getPlayer(playerId); + if (player != null) { + for (Map.Entry entry : amountOfCardsDrawnThisTurn.entrySet()) { + if (game.isOpponent(player, entry.getKey())) { + if (entry.getValue() >= x) { + return true; + } + } + } + } + return false; + } + + public int getAmountCardsDrawn(UUID playerId) { + Integer amount = amountOfCardsDrawnThisTurn.get(playerId); + if (amount != null) { + return amount; + } + return 0; + } + + @Override + public CardsAmountDrawnThisTurnWatcher copy() { + return new CardsAmountDrawnThisTurnWatcher(this); + } +} From 2a2e8c3657255b3d7975e82ccb1d2d17327060d0 Mon Sep 17 00:00:00 2001 From: emerald000 Date: Fri, 20 May 2016 09:00:27 -0400 Subject: [PATCH 06/15] Added 8 new cards. Completed Morningtide and Venser vs. Koth! Arc Blade, Battletide Alchemist, Boldwyr Heavyweights, Coordinated Barrage, Hostile Realm, Stenchskipper, Torchling and Weight of Conscience. --- .../src/mage/sets/futuresight/ArcBlade.java | 76 +++++++ .../sets/morningtide/BattletideAlchemist.java | 134 +++++++++++ .../sets/morningtide/BoldwyrHeavyweights.java | 117 ++++++++++ .../sets/morningtide/CoordinatedBarrage.java | 109 +++++++++ .../mage/sets/morningtide/HostileRealm.java | 85 +++++++ .../mage/sets/morningtide/Stenchskipper.java | 79 +++++++ .../sets/morningtide/WeightOfConscience.java | 214 ++++++++++++++++++ .../src/mage/sets/planarchaos/Torchling.java | 52 +++++ .../src/mage/sets/venservskoth/Torchling.java | 138 +++++++++++ 9 files changed, 1004 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/futuresight/ArcBlade.java create mode 100644 Mage.Sets/src/mage/sets/morningtide/BattletideAlchemist.java create mode 100644 Mage.Sets/src/mage/sets/morningtide/BoldwyrHeavyweights.java create mode 100644 Mage.Sets/src/mage/sets/morningtide/CoordinatedBarrage.java create mode 100644 Mage.Sets/src/mage/sets/morningtide/HostileRealm.java create mode 100644 Mage.Sets/src/mage/sets/morningtide/Stenchskipper.java create mode 100644 Mage.Sets/src/mage/sets/morningtide/WeightOfConscience.java create mode 100644 Mage.Sets/src/mage/sets/planarchaos/Torchling.java create mode 100644 Mage.Sets/src/mage/sets/venservskoth/Torchling.java diff --git a/Mage.Sets/src/mage/sets/futuresight/ArcBlade.java b/Mage.Sets/src/mage/sets/futuresight/ArcBlade.java new file mode 100644 index 0000000000..dd51113b36 --- /dev/null +++ b/Mage.Sets/src/mage/sets/futuresight/ArcBlade.java @@ -0,0 +1,76 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.futuresight; + +import java.util.UUID; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.SuspendAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.counters.CounterType; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author emerald000 + */ +public class ArcBlade extends CardImpl { + + public ArcBlade(UUID ownerId) { + super(ownerId, 94, "Arc Blade", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{R}{R}"); + this.expansionSetCode = "FUT"; + + // Arc Blade deals 2 damage to target creature or player. + this.getSpellAbility().addEffect(new DamageTargetEffect(2)); + // Exile Arc Blade + this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); + // with three time counters on it. + Effect effect = new AddCountersSourceEffect(CounterType.TIME.createInstance(), new StaticValue(3), false, true); + effect.setText("with 3 time counters on it"); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); + + // Suspend 3-{2}{R} + this.addAbility(new SuspendAbility(3, new ManaCostsImpl<>("{2}{R}"), this)); + } + + public ArcBlade(final ArcBlade card) { + super(card); + } + + @Override + public ArcBlade copy() { + return new ArcBlade(this); + } +} diff --git a/Mage.Sets/src/mage/sets/morningtide/BattletideAlchemist.java b/Mage.Sets/src/mage/sets/morningtide/BattletideAlchemist.java new file mode 100644 index 0000000000..2d921f04e9 --- /dev/null +++ b/Mage.Sets/src/mage/sets/morningtide/BattletideAlchemist.java @@ -0,0 +1,134 @@ +/* + * 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.morningtide; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.PreventionEffectImpl; +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.common.FilterControlledCreaturePermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.players.Player; + +/** + * + * @author emerald000 + */ +public class BattletideAlchemist extends CardImpl { + + public BattletideAlchemist(UUID ownerId) { + super(ownerId, 2, "Battletide Alchemist", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); + this.expansionSetCode = "MOR"; + this.subtype.add("Kithkin"); + this.subtype.add("Cleric"); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // If a source would deal damage to a player, you may prevent X of that damage, where X is the number of Clerics you control. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BattletideAlchemistEffect())); + } + + public BattletideAlchemist(final BattletideAlchemist card) { + super(card); + } + + @Override + public BattletideAlchemist copy() { + return new BattletideAlchemist(this); + } +} + +class BattletideAlchemistEffect extends PreventionEffectImpl { + + BattletideAlchemistEffect() { + super(Duration.WhileOnBattlefield); + this.staticText = "If a source would deal damage to a player, you may prevent X of that damage, where X is the number of Clerics you control"; + } + + BattletideAlchemistEffect(final BattletideAlchemistEffect effect) { + super(effect); + } + + @Override + public BattletideAlchemistEffect copy() { + return new BattletideAlchemistEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + boolean result = false; + Player controller = game.getPlayer(source.getControllerId()); + Player targetPlayer = game.getPlayer(event.getTargetId()); + if (controller != null) { + int numberOfClericsControlled = new PermanentsOnBattlefieldCount(new FilterControlledCreaturePermanent("Cleric", "Clerics")).calculate(game, source, this); + int toPrevent = Math.min(numberOfClericsControlled, event.getAmount()); + if (toPrevent > 0 && controller.chooseUse(Outcome.PreventDamage, "Prevent " + toPrevent + " damage to " + targetPlayer.getName() + "?", source, game)) { + GameEvent preventEvent = new GameEvent(GameEvent.EventType.PREVENT_DAMAGE, targetPlayer.getId(), source.getSourceId(), source.getControllerId(), toPrevent, false); + if (!game.replaceEvent(preventEvent)) { + if (event.getAmount() >= toPrevent) { + event.setAmount(event.getAmount() - toPrevent); + } + else { + event.setAmount(0); + result = true; + } + if (toPrevent > 0) { + game.informPlayers("Battletide Alchemist prevented " + toPrevent + " damage to " + targetPlayer.getName()); + game.fireEvent(GameEvent.getEvent( + GameEvent.EventType.PREVENTED_DAMAGE, + targetPlayer.getId(), + source.getSourceId(), + source.getControllerId(), + toPrevent)); + } + } + } + } + return result; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.DAMAGE_PLAYER; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return super.applies(event, source, game); + } +} diff --git a/Mage.Sets/src/mage/sets/morningtide/BoldwyrHeavyweights.java b/Mage.Sets/src/mage/sets/morningtide/BoldwyrHeavyweights.java new file mode 100644 index 0000000000..bae6ee9a48 --- /dev/null +++ b/Mage.Sets/src/mage/sets/morningtide/BoldwyrHeavyweights.java @@ -0,0 +1,117 @@ +/* + * 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.morningtide; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.TrampleAbility; +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.common.FilterCreatureCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author emerald000 + */ +public class BoldwyrHeavyweights extends CardImpl { + + public BoldwyrHeavyweights(UUID ownerId) { + super(ownerId, 85, "Boldwyr Heavyweights", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + this.expansionSetCode = "MOR"; + this.subtype.add("Giant"); + this.subtype.add("Warrior"); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When Boldwyr Heavyweights enters the battlefield, each opponent may search his or her library for a creature card and put it onto the battlefield. Then each player who searched his or her library this way shuffles it. + this.addAbility(new EntersBattlefieldTriggeredAbility(new BoldwyrHeavyweightsEffect())); + } + + public BoldwyrHeavyweights(final BoldwyrHeavyweights card) { + super(card); + } + + @Override + public BoldwyrHeavyweights copy() { + return new BoldwyrHeavyweights(this); + } +} + +class BoldwyrHeavyweightsEffect extends OneShotEffect { + + BoldwyrHeavyweightsEffect() { + super(Outcome.Detriment); + this.staticText = "each opponent may search his or her library for a creature card and put it onto the battlefield. Then each player who searched his or her library this way shuffles it"; + } + + BoldwyrHeavyweightsEffect(final BoldwyrHeavyweightsEffect effect) { + super(effect); + } + + @Override + public BoldwyrHeavyweightsEffect copy() { + return new BoldwyrHeavyweightsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Set playersThatSearched = new HashSet<>(1); + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(opponentId); + if (opponent != null && opponent.chooseUse(Outcome.PutCreatureInPlay, "Search your library for a creature card and put it onto the battlefield?", source, game)) { + TargetCardInLibrary target = new TargetCardInLibrary(new FilterCreatureCard()); + if (opponent.searchLibrary(target, game)) { + Card targetCard = opponent.getLibrary().getCard(target.getFirstTarget(), game); + if (targetCard != null) { + opponent.moveCards(targetCard, Zone.BATTLEFIELD, source, game); + playersThatSearched.add(opponent); + } + } + } + } + for (Player opponent : playersThatSearched) { + opponent.shuffleLibrary(source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/morningtide/CoordinatedBarrage.java b/Mage.Sets/src/mage/sets/morningtide/CoordinatedBarrage.java new file mode 100644 index 0000000000..34f7fd93d3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/morningtide/CoordinatedBarrage.java @@ -0,0 +1,109 @@ +/* + * 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.morningtide; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.repository.CardRepository; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetAttackingOrBlockingCreature; + +/** + * + * @author emerald000 + */ +public class CoordinatedBarrage extends CardImpl { + + public CoordinatedBarrage(UUID ownerId) { + super(ownerId, 7, "Coordinated Barrage", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{W}"); + this.expansionSetCode = "MOR"; + + // Choose a creature type. Coordinated Barrage deals damage to target attacking or blocking creature equal to the number of permanents you control of the chosen type. + this.getSpellAbility().addEffect(new CoordinatedBarrageEffect()); + this.getSpellAbility().addTarget(new TargetAttackingOrBlockingCreature()); + } + + public CoordinatedBarrage(final CoordinatedBarrage card) { + super(card); + } + + @Override + public CoordinatedBarrage copy() { + return new CoordinatedBarrage(this); + } +} + +class CoordinatedBarrageEffect extends OneShotEffect { + + CoordinatedBarrageEffect() { + super(Outcome.Damage); + this.staticText = "Choose a creature type. Coordinated Barrage deals damage to target attacking or blocking creature equal to the number of permanents you control of the chosen type"; + } + + CoordinatedBarrageEffect(final CoordinatedBarrageEffect effect) { + super(effect); + } + + @Override + public CoordinatedBarrageEffect copy() { + return new CoordinatedBarrageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Choice choice = new ChoiceImpl(true); + choice.setMessage("Choose a creature type"); + choice.setChoices(CardRepository.instance.getCreatureTypes()); + if (controller.choose(Outcome.Damage, choice, game)) { + String chosenType = choice.getChoice(); + FilterControlledPermanent filter = new FilterControlledPermanent(); + filter.add(new SubtypePredicate(chosenType)); + int damageDealt = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); + Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); + if (permanent != null) { + permanent.damage(damageDealt, source.getSourceId(), game, false, true); + } + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/morningtide/HostileRealm.java b/Mage.Sets/src/mage/sets/morningtide/HostileRealm.java new file mode 100644 index 0000000000..5f093b2e74 --- /dev/null +++ b/Mage.Sets/src/mage/sets/morningtide/HostileRealm.java @@ -0,0 +1,85 @@ +/* + * 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.morningtide; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantBlockTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetLandPermanent; + +/** + * + * @author emerald000 + */ +public class HostileRealm extends CardImpl { + + public HostileRealm(UUID ownerId) { + super(ownerId, 91, "Hostile Realm", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + this.expansionSetCode = "MOR"; + this.subtype.add("Aura"); + + // Enchant land + TargetPermanent auraTarget = new TargetLandPermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted land has "{T}: Target creature can't block this turn." + Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBlockTargetEffect(Duration.EndOfTurn), new TapSourceCost()); + gainedAbility.addTarget(new TargetCreaturePermanent()); + Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA); + effect.setText("Enchanted land has \"{T}: Target creature can't block this turn.\""); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + } + + public HostileRealm(final HostileRealm card) { + super(card); + } + + @Override + public HostileRealm copy() { + return new HostileRealm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/morningtide/Stenchskipper.java b/Mage.Sets/src/mage/sets/morningtide/Stenchskipper.java new file mode 100644 index 0000000000..2874c65dbe --- /dev/null +++ b/Mage.Sets/src/mage/sets/morningtide/Stenchskipper.java @@ -0,0 +1,79 @@ +/* + * 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.morningtide; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; + +/** + * + * @author emerald000 + */ +public class Stenchskipper extends CardImpl { + + public Stenchskipper(UUID ownerId) { + super(ownerId, 79, "Stenchskipper", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{B}"); + this.expansionSetCode = "MOR"; + this.subtype.add("Elemental"); + this.power = new MageInt(6); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of the end step, if you control no Goblins, sacrifice Stenchskipper. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, + new SacrificeSourceEffect(), + TargetController.ANY, + new PermanentsOnTheBattlefieldCondition( + new FilterControlledCreaturePermanent("Goblin", "if you control no Goblins"), + PermanentsOnTheBattlefieldCondition.CountType.FEWER_THAN, + 1), + false)); + } + + public Stenchskipper(final Stenchskipper card) { + super(card); + } + + @Override + public Stenchskipper copy() { + return new Stenchskipper(this); + } +} diff --git a/Mage.Sets/src/mage/sets/morningtide/WeightOfConscience.java b/Mage.Sets/src/mage/sets/morningtide/WeightOfConscience.java new file mode 100644 index 0000000000..f0094a0335 --- /dev/null +++ b/Mage.Sets/src/mage/sets/morningtide/WeightOfConscience.java @@ -0,0 +1,214 @@ +/* + * 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.morningtide; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantAttackAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; + +/** + * + * @author emerald000 + */ +public class WeightOfConscience extends CardImpl { + + public WeightOfConscience(UUID ownerId) { + super(ownerId, 28, "Weight of Conscience", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + this.expansionSetCode = "MOR"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature can't attack. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackAttachedEffect(AttachmentType.AURA))); + + // Tap two untapped creatures you control that share a creature type: Exile enchanted creature. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new WeightOfConscienceEffect(), new TapTargetCost(new WeightOfConscienceTarget()))); + } + + public WeightOfConscience(final WeightOfConscience card) { + super(card); + } + + @Override + public WeightOfConscience copy() { + return new WeightOfConscience(this); + } +} + +class WeightOfConscienceEffect extends OneShotEffect { + + WeightOfConscienceEffect() { + super(Outcome.Exile); + staticText = "Exile enchanted creature"; + } + + WeightOfConscienceEffect(final WeightOfConscienceEffect effect) { + super(effect); + } + + @Override + public WeightOfConscienceEffect copy() { + return new WeightOfConscienceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (controller != null && enchantment != null && enchantment.getAttachedTo() != null) { + Permanent creature = game.getPermanent(enchantment.getAttachedTo()); + if (creature != null) { + controller.moveCardsToExile(creature, source, game, true, null, ""); + } + } + return false; + } +} + +class WeightOfConscienceTarget extends TargetControlledCreaturePermanent { + + private static final FilterControlledCreaturePermanent filterUntapped = new FilterControlledCreaturePermanent("untapped creatures you control that share a creature type"); + static { + filterUntapped.add(Predicates.not(new TappedPredicate())); + } + + WeightOfConscienceTarget() { + super(2, 2, filterUntapped, true); + } + + WeightOfConscienceTarget(final WeightOfConscienceTarget target) { + super(target); + } + + @Override + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + Player player = game.getPlayer(sourceControllerId); + Set possibleTargets = new HashSet<>(0); + if (player != null) { + // Choosing first target + if (this.getTargets().isEmpty()) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) { + for (String subtype : permanent.getSubtype()) { + if (!CardUtil.isNonCreatureSubtype(subtype)) { + if (game.getBattlefield().contains(new FilterControlledCreaturePermanent(subtype, subtype), sourceControllerId, game, 2)) { + possibleTargets.add(permanent.getId()); + } + } + } + } + } + // Choosing second target + else { + UUID firstTargetId = this.getTargets().get(0); + Permanent firstTargetCreature = game.getPermanent(firstTargetId); + if (firstTargetCreature != null) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) { + if (!permanent.getId().equals(firstTargetId) && CardUtil.shareSubtypes(firstTargetCreature, permanent)) { + possibleTargets.add(permanent.getId()); + } + } + } + } + } + return possibleTargets; + } + + @Override + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { + for (Permanent permanent1 : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) { + for (Permanent permanent2 : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) { + if (permanent1 != permanent2 && CardUtil.shareSubtypes(permanent1, permanent2)) { + return true; + } + } + } + return false; + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + if (super.canTarget(id, game)) { + Permanent targetPermanent = game.getPermanent(id); + if (targetPermanent != null) { + if (this.getTargets().isEmpty()) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, source.getControllerId(), game)) { + for (String subtype : permanent.getSubtype()) { + if (!CardUtil.isNonCreatureSubtype(subtype)) { + if (game.getBattlefield().contains(new FilterControlledCreaturePermanent(subtype, subtype), source.getControllerId(), game, 2)) { + return true; + } + } + } + } + } + else { + Permanent firstTarget = game.getPermanent(this.getTargets().get(0)); + if (firstTarget != null && CardUtil.shareSubtypes(firstTarget, targetPermanent)) { + return true; + } + } + } + } + return false; + } + + @Override + public WeightOfConscienceTarget copy() { + return new WeightOfConscienceTarget(this); + } +} diff --git a/Mage.Sets/src/mage/sets/planarchaos/Torchling.java b/Mage.Sets/src/mage/sets/planarchaos/Torchling.java new file mode 100644 index 0000000000..97f0d073c5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/planarchaos/Torchling.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.planarchaos; + +import java.util.UUID; + +/** + * + * @author emerald000 + */ +public class Torchling extends mage.sets.venservskoth.Torchling { + + public Torchling(UUID ownerId) { + super(ownerId); + this.cardNumber = 110; + this.expansionSetCode = "PLC"; + } + + public Torchling(final Torchling card) { + super(card); + } + + @Override + public Torchling copy() { + return new Torchling(this); + } +} diff --git a/Mage.Sets/src/mage/sets/venservskoth/Torchling.java b/Mage.Sets/src/mage/sets/venservskoth/Torchling.java new file mode 100644 index 0000000000..28f400a5f5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/venservskoth/Torchling.java @@ -0,0 +1,138 @@ +/* + * 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.venservskoth; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.ChooseNewTargetsTargetEffect; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.abilities.effects.common.combat.MustBeBlockedByTargetSourceEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.target.Target; +import mage.target.TargetSpell; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author emerald000 + */ +public class Torchling extends CardImpl { + + public Torchling(UUID ownerId) { + super(ownerId, 58, "Torchling", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); + this.expansionSetCode = "DDI"; + this.subtype.add("Shapeshifter"); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // {R}: Untap Torchling. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new UntapSourceEffect(), new ColoredManaCost(ColoredManaSymbol.R))); + + // {R}: Target creature blocks Torchling this turn if able. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MustBeBlockedByTargetSourceEffect(), new ColoredManaCost(ColoredManaSymbol.R)); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // {R}: Change the target of target spell that targets only Torchling. + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ChooseNewTargetsTargetEffect(true, true), new ColoredManaCost(ColoredManaSymbol.R)); + FilterSpell filter = new FilterSpell("spell that targets only " + this.getName()); + filter.add(new TorchlingTargetPredicate(this.getId())); + ability.addTarget(new TargetSpell(filter)); + this.addAbility(ability); + + // {1}: Torchling gets +1/-1 until end of turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, -1, Duration.EndOfTurn), new GenericManaCost(1))); + + // {1}: Torchling gets -1/+1 until end of turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(-1, 1, Duration.EndOfTurn), new GenericManaCost(1))); + } + + public Torchling(final Torchling card) { + super(card); + } + + @Override + public Torchling copy() { + return new Torchling(this); + } +} + +class TorchlingTargetPredicate implements Predicate { + + private final UUID sourceId; + + TorchlingTargetPredicate(UUID sourceId) { + this.sourceId = sourceId; + } + + @Override + public boolean apply(MageObject input, Game game) { + Spell spell = game.getStack().getSpell(input.getId()); + if (spell != null) { + int numberOfTargets = 0; + for (SpellAbility spellAbility : spell.getSpellAbilities()) { + for (Mode mode : spellAbility.getModes().getSelectedModes()) { + for (Target target : mode.getTargets()) { + for (UUID targetId : target.getTargets()) { + if (!targetId.equals(sourceId)) { + return false; + } + else { + numberOfTargets++; + } + } + } + } + } + return numberOfTargets > 0; + } + return false; + } + + @Override + public String toString() { + return "target spell that targets only {this}"; + } +} From 608cab21a989ae6f2994f99ee9ab673596724bdf Mon Sep 17 00:00:00 2001 From: Quercitron Date: Sat, 21 May 2016 01:55:29 +0300 Subject: [PATCH 07/15] Fix that incorrect UserStats endTime can be set when rating is updated. --- .../server/record/UserStatsRepository.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Mage.Server/src/main/java/mage/server/record/UserStatsRepository.java b/Mage.Server/src/main/java/mage/server/record/UserStatsRepository.java index 0784ee448e..0e6ad74892 100644 --- a/Mage.Server/src/main/java/mage/server/record/UserStatsRepository.java +++ b/Mage.Server/src/main/java/mage/server/record/UserStatsRepository.java @@ -231,8 +231,11 @@ public enum UserStatsRepository { outcome = 0.5; } - ResultProtos.UserStatsProto player1StatsProto = getUserStatsProto(player1.getName(), matchEndTimeMs); - ResultProtos.UserStatsProto player2StatsProto = getUserStatsProto(player2.getName(), matchEndTimeMs); + // get players stats + UserStats player1Stats = getOrCreateUserStats(player1.getName(), tableEndTimeMs); + ResultProtos.UserStatsProto player1StatsProto = player1Stats.getProto(); + UserStats player2Stats = getOrCreateUserStats(player2.getName(), tableEndTimeMs); + ResultProtos.UserStatsProto player2StatsProto = player2Stats.getProto(); ResultProtos.UserStatsProto.Builder player1StatsBuilder = ResultProtos.UserStatsProto.newBuilder(player1StatsProto); @@ -305,8 +308,8 @@ public enum UserStatsRepository { } - this.update(new UserStats(player1StatsBuilder.build(), matchEndTimeMs)); - this.update(new UserStats(player2StatsBuilder.build(), matchEndTimeMs)); + this.update(new UserStats(player1StatsBuilder.build(), player1Stats.getEndTimeMs())); + this.update(new UserStats(player2StatsBuilder.build(), player2Stats.getEndTimeMs())); } private void updateRating( @@ -351,16 +354,14 @@ public enum UserStatsRepository { .setLastGameTimeMs(tableEndTimeMs); } - private ResultProtos.UserStatsProto getUserStatsProto(String playerName, long endTimeMs) { - UserStats player1Stats = this.getUser(playerName); - ResultProtos.UserStatsProto player1StatsProto; - if (player1Stats != null) { - player1StatsProto = player1Stats.getProto(); - } else { - player1StatsProto = ResultProtos.UserStatsProto.newBuilder().setName(playerName).build(); - this.add(new UserStats(player1StatsProto, endTimeMs)); + private UserStats getOrCreateUserStats(String playerName, long endTimeMs) { + UserStats userStats = this.getUser(playerName); + if (userStats == null) { + ResultProtos.UserStatsProto userStatsProto = ResultProtos.UserStatsProto.newBuilder().setName(playerName).build(); + userStats = new UserStats(userStatsProto, endTimeMs); + this.add(userStats); } - return player1StatsProto; + return userStats; } public void closeDB() { From 6d622a8f00a11f8868114d6d83af90b6323b11c5 Mon Sep 17 00:00:00 2001 From: drmDev Date: Sat, 21 May 2016 05:34:57 -0400 Subject: [PATCH 08/15] LastStand fixes - loss of life and 2x --- Mage.Sets/src/mage/sets/planechase2012/LastStand.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/sets/planechase2012/LastStand.java b/Mage.Sets/src/mage/sets/planechase2012/LastStand.java index 4d6b42246b..17579324b3 100644 --- a/Mage.Sets/src/mage/sets/planechase2012/LastStand.java +++ b/Mage.Sets/src/mage/sets/planechase2012/LastStand.java @@ -28,13 +28,13 @@ package mage.sets.planechase2012; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.Game; @@ -54,7 +54,6 @@ public class LastStand extends CardImpl { super(ownerId, 100, "Last Stand", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{W}{U}{B}{R}{G}"); this.expansionSetCode = "PC2"; - // Target opponent loses 2 life for each Swamp you control. Last Stand deals damage equal to the number of Mountains you control to target creature. Put a 1/1 green Saproling creature token onto the battlefield for each Forest you control. You gain 2 life for each Plains you control. Draw a card for each Island you control, then discard that many cards. this.getSpellAbility().addEffect(new LastStandEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); @@ -108,7 +107,7 @@ class LastStandEffect extends OneShotEffect { Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); if (opponent != null) { int swamps = game.getBattlefield().count(filterSwamp, source.getSourceId(), source.getControllerId(), game); - opponent.damage(swamps, source.getSourceId(), game, false, true); + opponent.loseLife(swamps * 2, game); } // Last Stand deals damage equal to the number of Mountains you control to target creature. Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); @@ -125,7 +124,7 @@ class LastStandEffect extends OneShotEffect { } // You gain 2 life for each Plains you control. int plains = game.getBattlefield().count(filterPlains, source.getSourceId(), source.getControllerId(), game); - controller.gainLife(plains, game); + controller.gainLife(plains * 2, game); // Draw a card for each Island you control, then discard that many cards int islands = game.getBattlefield().count(filterIsland, source.getSourceId(), source.getControllerId(), game); if (islands > 0) { From f8452a43d374204adfdd8a4a6cd61176a25cd61c Mon Sep 17 00:00:00 2001 From: Fenhl Date: Sat, 21 May 2016 10:50:02 +0000 Subject: [PATCH 09/15] Update card counts in readme Morningtide is complete! --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index d68cb2fa83..dfe53ff007 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ [![Join the chat at https://gitter.im/magefree/mage](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/magefree/mage?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -XMage allows you to play Magic against one or more online players or computer opponents. It includes full rules enforcement for over **13,000** unique cards (26,000 counting all cards from different editions). Starting with Shadowmoor, all regular sets have nearly all the cards implemented ([detailed overview](http://ct-magefree.rhcloud.com/stats)). +XMage allows you to play Magic against one or more online players or computer opponents. It includes full rules enforcement for over **13,000** unique cards (over 26,000 counting all cards from different editions). Starting with Morningtide, all regular sets have nearly all the cards implemented ([detailed overview](http://ct-magefree.rhcloud.com/stats)). There are public servers where you can play XMage against other players. You can also host your own server to play against the AI and/or your friends. From 4a416c7fbb90c83f3d0dde249b683ed77af8872e Mon Sep 17 00:00:00 2001 From: Nathaniel Brandes Date: Sat, 21 May 2016 03:59:16 -0700 Subject: [PATCH 10/15] Fix ETB kicker on Dralnu's Pet. --- Mage.Sets/src/mage/sets/planeshift/DralnusPet.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mage.Sets/src/mage/sets/planeshift/DralnusPet.java b/Mage.Sets/src/mage/sets/planeshift/DralnusPet.java index d156e71615..732fc1eb44 100644 --- a/Mage.Sets/src/mage/sets/planeshift/DralnusPet.java +++ b/Mage.Sets/src/mage/sets/planeshift/DralnusPet.java @@ -45,6 +45,7 @@ import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; +import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; @@ -111,6 +112,9 @@ class DralnusPetEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null && source.getAbilityType().equals(AbilityType.STATIC)) { + permanent = game.getPermanentEntering(source.getSourceId()); + } if (controller != null && permanent != null) { SpellAbility spellAbility = (SpellAbility) getValue(EntersBattlefieldEffect.SOURCE_CAST_SPELL_ABILITY); if (spellAbility != null From 4aadc359c4ad0ba209030d88fb11d76664f203e3 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 22 May 2016 23:11:04 +1000 Subject: [PATCH 11/15] spjspj - Implement Portcullis (STH) --- .../src/mage/sets/stronghold/Portcullis.java | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/stronghold/Portcullis.java diff --git a/Mage.Sets/src/mage/sets/stronghold/Portcullis.java b/Mage.Sets/src/mage/sets/stronghold/Portcullis.java new file mode 100644 index 0000000000..ba6001b110 --- /dev/null +++ b/Mage.Sets/src/mage/sets/stronghold/Portcullis.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.stronghold; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnFromExileForSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author spjspj + */ +public class Portcullis extends CardImpl { + + private final static FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature"); + + public Portcullis(UUID ownerId) { + super(ownerId, 133, "Portcullis", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{4}"); + this.expansionSetCode = "STH"; + + // Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature. + String rule = "Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature"; + TriggeredAbility ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new PortcullisExileEffect(), filter, false, SetTargetPointer.PERMANENT, rule, true); + + MoreThanXCreaturesOnBFCondition condition = new MoreThanXCreaturesOnBFCondition(2); + this.addAbility(new ConditionalTriggeredAbility(ability, condition, rule)); + + // Return that card to the battlefield under its owner's control when Portcullis leaves the battlefield. + Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false); + this.addAbility(ability2); + } + + public Portcullis(final Portcullis card) { + super(card); + } + + @Override + public Portcullis copy() { + return new Portcullis(this); + } +} + +class MoreThanXCreaturesOnBFCondition implements Condition { + + protected final int value; + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures on field"); + + public MoreThanXCreaturesOnBFCondition(int value) { + this.value = value; + } + + @Override + public final boolean apply(Game game, Ability source) { + PermanentsOnBattlefieldCount amount = new PermanentsOnBattlefieldCount(filter); + int count = amount.calculate(game, source, null); + return count > value; + } +} + +class PortcullisExileEffect extends OneShotEffect { + + public PortcullisExileEffect() { + super(Outcome.Neutral); + this.staticText = "Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature."; + } + + public PortcullisExileEffect(final PortcullisExileEffect effect) { + super(effect); + } + + @Override + public PortcullisExileEffect copy() { + return new PortcullisExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + Permanent creature = game.getPermanent(targetPointer.getFirst(game, source)); + + if (permanent != null && creature != null) { + Player controller = game.getPlayer(creature.getControllerId()); + Zone currentZone = game.getState().getZone(creature.getId()); + if (currentZone.equals(Zone.BATTLEFIELD)) { + controller.moveCardsToExile(creature, source, game, true, CardUtil.getCardExileZoneId(game, source), permanent.getIdName()); + } + } + return false; + } +} From 691294982508400018b8f295dc9ee21dbbb88795 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 22 May 2016 23:16:44 +1000 Subject: [PATCH 12/15] spjspj - Implement Portcullis (STH) --- Mage.Sets/src/mage/sets/stronghold/Portcullis.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/stronghold/Portcullis.java b/Mage.Sets/src/mage/sets/stronghold/Portcullis.java index ba6001b110..b1b6c284e7 100644 --- a/Mage.Sets/src/mage/sets/stronghold/Portcullis.java +++ b/Mage.Sets/src/mage/sets/stronghold/Portcullis.java @@ -63,7 +63,7 @@ public class Portcullis extends CardImpl { // Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature. String rule = "Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature"; - TriggeredAbility ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new PortcullisExileEffect(), filter, false, SetTargetPointer.PERMANENT, rule, true); + TriggeredAbility ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new PortcullisExileEffect(), filter, false, SetTargetPointer.PERMANENT, rule); MoreThanXCreaturesOnBFCondition condition = new MoreThanXCreaturesOnBFCondition(2); this.addAbility(new ConditionalTriggeredAbility(ability, condition, rule)); From f44987aa376be3c3b9c1c641642472d0c51cfc3e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 22 May 2016 16:02:10 +0200 Subject: [PATCH 13/15] * Fixed that Suspend was handled as activated ability for increase/decrease of mana activation costs. --- Mage.Client/serverlist.txt | 1 + .../cards/abilities/keywords/SuspendTest.java | 22 +++++++++++++++++++ .../cards/abilities/keywords/UndyingTest.java | 3 ++- .../abilities/keyword/SuspendAbility.java | 8 ++++--- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Mage.Client/serverlist.txt b/Mage.Client/serverlist.txt index a4379a04bb..596b67fadc 100644 --- a/Mage.Client/serverlist.txt +++ b/Mage.Client/serverlist.txt @@ -2,5 +2,6 @@ XMage.de 1 (Europe/Germany) fast :xmage.de:17171 woogerworks (North America/USA) :xmage.woogerworks.info:17171 XMage Testserver (Europe/France) 1.4.8v0 :176.31.186.181:17171 XMage BR (South America/Brazil) :ec2-54-233-67-0.sa-east-1.compute.amazonaws.com:17171 +XMage.tahiti :xmage.tahiti.one:443 Seedds Server (Asia) :115.29.203.80:17171 localhost -> connect to your local server (must be started):localhost:17171 diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java index 2505b48ac9..b57d4b3c5a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java @@ -163,4 +163,26 @@ public class SuspendTest extends CardTestPlayerBase { assertHandCount(playerA, "Ancestral Vision", 1); } + + /** + * Suppression Field incorrectly makes suspend cards cost 2 more to suspend. + * It made my Rift Bolt cost 2R to suspend instead of R + * + */ + @Test + public void testCostManipulation() { + // Rift Bolt deals 3 damage to target creature or player. + // Suspend 1-{R} + addCard(Zone.HAND, playerA, "Rift Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Suppression Field", 1); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Suspend"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertHandCount(playerA, "Rift Bolt", 0); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java index 61333082f4..61b401429c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java @@ -251,7 +251,8 @@ public class UndyingTest extends CardTestPlayerBase { // Sacrifice a creature: Add {C}{C} to your mana pool. addCard(Zone.BATTLEFIELD, playerA, "Ashnod's Altar", 1); // Whenever a Human deals damage to you, destroy it. - // Other non-Human creatures you control get +1/+1 and have undying. + // Other non-Human creatures you control get +1/+1 and have undying + // (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.). addCard(Zone.BATTLEFIELD, playerA, "Mikaeus, the Unhallowed", 1); // Flying diff --git a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java index 449e65c4e3..b3dc8dd964 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java @@ -32,7 +32,7 @@ import java.util.List; import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.ActivatedAbilityImpl; +import mage.abilities.SpecialAction; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.common.SuspendedCondition; @@ -142,7 +142,7 @@ import mage.target.targetpointer.FixedTarget; * @author LevelX2 * */ -public class SuspendAbility extends ActivatedAbilityImpl { +public class SuspendAbility extends SpecialAction { private String ruleText; private boolean gainedTemporary; @@ -160,7 +160,9 @@ public class SuspendAbility extends ActivatedAbilityImpl { } public SuspendAbility(int suspend, ManaCost cost, Card card, boolean shortRule) { - super(Zone.HAND, new SuspendExileEffect(suspend), cost); + super(Zone.HAND); + this.addCost(cost); + this.addEffect(new SuspendExileEffect(suspend)); this.usesStack = false; if (suspend == Integer.MAX_VALUE) { VariableManaCost xCosts = new VariableManaCost(); From 949662de1678b1bf3567f5072ac433747be54ea9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 22 May 2016 16:24:45 +0200 Subject: [PATCH 14/15] * Coat of Arms - FIxed that the boost effect also worked with non creature types (e.g. Basic land types if some lands were animated). --- Mage.Sets/src/mage/sets/magic2010/CoatOfArms.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2010/CoatOfArms.java b/Mage.Sets/src/mage/sets/magic2010/CoatOfArms.java index d2d2687c8a..6641c9bb58 100644 --- a/Mage.Sets/src/mage/sets/magic2010/CoatOfArms.java +++ b/Mage.Sets/src/mage/sets/magic2010/CoatOfArms.java @@ -44,6 +44,7 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; /** * @@ -87,11 +88,6 @@ class CoatOfArmsEffect extends ContinuousEffectImpl { return new CoatOfArmsEffect(this); } - @Override - public void init(Ability source, Game game) { - super.init(source, game); - } - @Override public boolean apply(Game game, Ability source) { List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game); @@ -112,9 +108,11 @@ class CoatOfArmsEffect extends ContinuousEffectImpl { for (Permanent permanent : permanents) { if (!permanent.getId().equals(target.getId())) { for (String subtype : targetSubtype) { - if (permanent.hasSubtype(subtype)) { - amount++; - break; + if (!CardUtil.isNonCreatureSubtype(subtype)) { + if (permanent.hasSubtype(subtype)) { + amount++; + break; + } } } } From dc4168efaee35a1ea551627925e6a9e55b376bd7 Mon Sep 17 00:00:00 2001 From: wetterlicht Date: Sun, 22 May 2016 19:41:59 +0200 Subject: [PATCH 15/15] --- .../src/mage/sets/darksteel/DroolingOgre.java | 4 +--- .../mage/sets/darksteel/EmissaryOfDespair.java | 4 ++-- .../src/mage/sets/darksteel/MurderousSpoils.java | 4 ++-- .../src/mage/sets/darksteel/PsychicOverload.java | 15 ++++++++++----- .../src/mage/sets/darksteel/PulseOfTheTangle.java | 5 +++-- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Mage.Sets/src/mage/sets/darksteel/DroolingOgre.java b/Mage.Sets/src/mage/sets/darksteel/DroolingOgre.java index b2368462b4..9dba69254a 100644 --- a/Mage.Sets/src/mage/sets/darksteel/DroolingOgre.java +++ b/Mage.Sets/src/mage/sets/darksteel/DroolingOgre.java @@ -31,7 +31,6 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.SpellCastAllTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -42,7 +41,6 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.common.FilterArtifactSpell; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; @@ -139,7 +137,7 @@ public class DroolingOgre extends CardImpl { @Override public String getRule() { - return "Whenever a player casts an artifact spell, that player gains control of {this}"; + return "Whenever a player casts an artifact spell, that player gains control of {this}."; } } } diff --git a/Mage.Sets/src/mage/sets/darksteel/EmissaryOfDespair.java b/Mage.Sets/src/mage/sets/darksteel/EmissaryOfDespair.java index 70a8a54d27..7647695b6e 100644 --- a/Mage.Sets/src/mage/sets/darksteel/EmissaryOfDespair.java +++ b/Mage.Sets/src/mage/sets/darksteel/EmissaryOfDespair.java @@ -75,11 +75,11 @@ class EmissaryOfDespairCount implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - if (sourceAbility.getFirstTarget() == null) { + if (effect.getTargetPointer().getFirst(game, sourceAbility) == null) { return 0; } FilterArtifactPermanent filter = new FilterArtifactPermanent(); - filter.add(new ControllerIdPredicate(sourceAbility.getFirstTarget())); + filter.add(new ControllerIdPredicate(effect.getTargetPointer().getFirst(game, sourceAbility))); return game.getBattlefield().count(filter, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game); } diff --git a/Mage.Sets/src/mage/sets/darksteel/MurderousSpoils.java b/Mage.Sets/src/mage/sets/darksteel/MurderousSpoils.java index 018c2b2c71..80c5130db5 100644 --- a/Mage.Sets/src/mage/sets/darksteel/MurderousSpoils.java +++ b/Mage.Sets/src/mage/sets/darksteel/MurderousSpoils.java @@ -105,8 +105,8 @@ class MurderousSpoilsEffect extends OneShotEffect { } } for (Permanent p : attachments) { - ContinuousEffect gainControl = new GainControlTargetEffect(Duration.EndOfGame); - gainControl.setTargetPointer(new FixedTarget(p.getId())); + ContinuousEffect gainControl = new GainControlTargetEffect(Duration.Custom); + gainControl.setTargetPointer(new FixedTarget(p, game)); game.addEffect(gainControl, source); } target.destroy(source.getId(), game, true); diff --git a/Mage.Sets/src/mage/sets/darksteel/PsychicOverload.java b/Mage.Sets/src/mage/sets/darksteel/PsychicOverload.java index 04e8c82dcc..d4fe61d82d 100644 --- a/Mage.Sets/src/mage/sets/darksteel/PsychicOverload.java +++ b/Mage.Sets/src/mage/sets/darksteel/PsychicOverload.java @@ -28,23 +28,25 @@ package mage.sets.darksteel; import java.util.UUID; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.costs.common.DiscardTargetCost; -import mage.abilities.costs.common.DiscardXTargetCost; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.DontUntapInControllersUntapStepEnchantedEffect; import mage.abilities.effects.common.TapEnchantedEffect; -import mage.abilities.effects.common.UntapEnchantedEffect; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; +import mage.constants.AttachmentType; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.common.FilterArtifactCard; import mage.target.TargetPermanent; import mage.target.common.TargetCardInHand; @@ -70,8 +72,11 @@ public class PsychicOverload extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new TapEnchantedEffect())); // Enchanted permanent doesn't untap during its controller's untap step. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect())); + // Enchanted permanent has "Discard two artifact cards: Untap this permanent." - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new UntapEnchantedEffect(), new DiscardTargetCost(new TargetCardInHand(2, new FilterArtifactCard("two artifact cards"))))); + Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new UntapSourceEffect(), new DiscardTargetCost(new TargetCardInHand(2, new FilterArtifactCard("two artifact cards")))); + Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA, Duration.WhileOnBattlefield, "Enchanted permanent has \"Discard two artifact cards: Untap this permanent.\""); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } public PsychicOverload(final PsychicOverload card) { diff --git a/Mage.Sets/src/mage/sets/darksteel/PulseOfTheTangle.java b/Mage.Sets/src/mage/sets/darksteel/PulseOfTheTangle.java index 029347f42c..55e0a3d74f 100644 --- a/Mage.Sets/src/mage/sets/darksteel/PulseOfTheTangle.java +++ b/Mage.Sets/src/mage/sets/darksteel/PulseOfTheTangle.java @@ -76,7 +76,7 @@ class PulseOfTheTangleReturnToHandEffect extends OneShotEffect { PulseOfTheTangleReturnToHandEffect() { super(Outcome.Benefit); - this.staticText = "Then if an opponent controls more creatures than you, return Pulse of the Tangle to its owner's hand"; + this.staticText = "Then if an opponent controls more creatures than you, return {this} to its owner's hand"; } PulseOfTheTangleReturnToHandEffect(final PulseOfTheTangleReturnToHandEffect effect) { @@ -93,6 +93,7 @@ class PulseOfTheTangleReturnToHandEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); FilterControlledCreaturePermanent controllerFilter = new FilterControlledCreaturePermanent(); PermanentsOnBattlefieldCount controllerCount = new PermanentsOnBattlefieldCount(controllerFilter); + int controllerAmount = controllerCount.calculate(game, source, this); boolean check = false; if (controller != null) { for (UUID opponentID : game.getOpponents(controller.getId())) { @@ -100,7 +101,7 @@ class PulseOfTheTangleReturnToHandEffect extends OneShotEffect { FilterCreaturePermanent opponentFilter = new FilterCreaturePermanent(); opponentFilter.add(new ControllerIdPredicate(opponentID)); PermanentsOnBattlefieldCount opponentCreatureCount = new PermanentsOnBattlefieldCount(opponentFilter); - check = opponentCreatureCount.calculate(game, source, this) > controllerCount.calculate(game, source, this); + check = opponentCreatureCount.calculate(game, source, this) > controllerAmount; if (check) { break; }