From 9e941914088393783a25fc6e8251c8cb7bb2c53e Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 17:11:45 +0000 Subject: [PATCH 01/19] Implemented Vodalian War Machine --- .../src/mage/cards/v/VodalianWarMachine.java | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/v/VodalianWarMachine.java diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java new file mode 100644 index 00000000000..09e17dcfd92 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -0,0 +1,208 @@ +/* + * 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.cards.v; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.StackAbility; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.watchers.Watcher; + +/** + * + * @author L_J + */ +public class VodalianWarMachine extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped Merfolk you control"); + + static { + filter.add(Predicates.not(new TappedPredicate())); + filter.add(new SubtypePredicate(SubType.MERFOLK)); + } + + public VodalianWarMachine(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{U}"); + this.subtype.add(SubType.WALL); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // Tap an untapped Merfolk you control: Vodalian War Machine can attack this turn as though it didn't have defender. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true))); + this.addAbility(ability); + + // Tap an untapped Merfolk you control: Vodalian War Machine gets +2/+1 until end of turn. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2, 1, Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true)))); + + // When Vodalian War Machine dies, destroy all Merfolk tapped this turn to pay for its abilities. + this.addAbility(new DiesTriggeredAbility(new VodalianWarMachineEffect(), false), new VodalianWarMachineWatcher()); + } + + public VodalianWarMachine(final VodalianWarMachine card) { + super(card); + } + + @Override + public VodalianWarMachine copy() { + return new VodalianWarMachine(this); + } +} + +class VodalianWarMachineEffect extends OneShotEffect { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Merfolk tapped this turn to pay for its abilities"); + + static { + filter.add(new SubtypePredicate(SubType.MERFOLK)); + } + + public VodalianWarMachineEffect() { + super(Outcome.Detriment); + staticText = "destroy all " + filter.getMessage(); + } + + public VodalianWarMachineEffect(final VodalianWarMachineEffect effect) { + super(effect); + } + + @Override + public VodalianWarMachineEffect copy() { + return new VodalianWarMachineEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (sourcePermanent != null) { + VodalianWarMachineWatcher watcher = (VodalianWarMachineWatcher) game.getState().getWatchers().get(VodalianWarMachineWatcher.class.getSimpleName()); + if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId()) != null) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + if (watcher.getTappedMerfolkIds(sourcePermanent.getId()).contains(permanent.getId())) { + permanent.destroy(source.getSourceId(), game, false); + } + } + return true; + } + } + return false; + } + +} + +class VodalianWarMachineWatcher extends Watcher { + + public Map> tappedMerfolkIds = new HashMap<>(); + + public VodalianWarMachineWatcher() { + super(VodalianWarMachineWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public VodalianWarMachineWatcher(final VodalianWarMachineWatcher watcher) { + super(watcher); + this.tappedMerfolkIds = watcher.tappedMerfolkIds; + } + + @Override + public VodalianWarMachineWatcher copy() { + return new VodalianWarMachineWatcher(this); + } + + public Set getTappedMerfolkIds(UUID sourceId) { + return tappedMerfolkIds.get(sourceId); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.ACTIVATED_ABILITY) { + if (event.getSourceId() != null) { + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility != null) { + Ability ability = stackAbility.getStackAbility(); + if (ability != null) { + for (Cost cost : ability.getCosts()) { + if (cost instanceof TapTargetCost && cost.isPaid()) { + TapTargetCost tapCost = (TapTargetCost) cost; + if (tapCost.getTarget().isChosen()) { + Set toAdd; + if (tappedMerfolkIds.get(event.getSourceId()) == null) { + toAdd = new HashSet<>(); + } else { + toAdd = tappedMerfolkIds.get(event.getSourceId()); + } + for (UUID targetId : tapCost.getTarget().getTargets()) { + toAdd.add(targetId); + } + tappedMerfolkIds.put(event.getSourceId(), toAdd); + break; + } + } + } + } + } + } + } + } + + @Override + public void reset() { + super.reset(); + tappedMerfolkIds.clear(); + } +} From 93280d1835bb0d287bba1bbfb4eb44f312e53463 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 17:13:38 +0000 Subject: [PATCH 02/19] Implemented Vodalian War Machine --- .../main/java/mage/abilities/costs/common/TapTargetCost.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java index ef095d353cc..82ed1c371fa 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/TapTargetCost.java @@ -78,6 +78,10 @@ public class TapTargetCost extends CostImpl { public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { return target.canChoose(sourceId, controllerId, game); } + + public TargetControlledPermanent getTarget() { + return target; + } @Override public TapTargetCost copy() { From 7b42c36644a310aa1605a944fed8c30315648957 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 17:14:50 +0000 Subject: [PATCH 03/19] Implemented Vodalian War Machine (Fallen Empires compete) --- Mage.Sets/src/mage/sets/FallenEmpires.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/FallenEmpires.java b/Mage.Sets/src/mage/sets/FallenEmpires.java index 224a0628318..a6a5870493a 100644 --- a/Mage.Sets/src/mage/sets/FallenEmpires.java +++ b/Mage.Sets/src/mage/sets/FallenEmpires.java @@ -268,6 +268,7 @@ public class FallenEmpires extends ExpansionSet { cards.add(new SetCardInfo("Vodalian Soldiers", 63, Rarity.COMMON, VodalianSoldiers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vodalian Soldiers", 64, Rarity.COMMON, VodalianSoldiers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vodalian Soldiers", 65, Rarity.COMMON, VodalianSoldiers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vodalian War Machine", 66, Rarity.RARE, mage.cards.v.VodalianWarMachine.class)); cards.add(new SetCardInfo("Zelyon Sword", 176, Rarity.RARE, mage.cards.z.ZelyonSword.class)); } } From 3a2fe879dcdb1fdcaf6a4adc2542c0b7f5addb4c Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 20:42:00 +0000 Subject: [PATCH 04/19] Implemented Glyph of Doom --- Mage.Sets/src/mage/cards/g/GlyphOfDoom.java | 153 ++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/g/GlyphOfDoom.java diff --git a/Mage.Sets/src/mage/cards/g/GlyphOfDoom.java b/Mage.Sets/src/mage/cards/g/GlyphOfDoom.java new file mode 100644 index 00000000000..748661c0a08 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GlyphOfDoom.java @@ -0,0 +1,153 @@ +/* + * 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.cards.g; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.BlockedAttackerWatcher; + +/** + * + * @author LevelX2 & L_J + */ +public class GlyphOfDoom extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Wall creature"); + + static { + filter.add(new SubtypePredicate(SubType.WALL)); + } + + public GlyphOfDoom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + + // Choose target Wall creature. At this turn's next end of combat, destroy all creatures that were blocked by that creature this turn. + this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addEffect(new InfoEffect("Choose target Wall creature")); + this.getSpellAbility().addEffect(new GlyphOfDoomCreateDelayedTriggeredAbilityEffect()); + this.getSpellAbility().addWatcher(new BlockedAttackerWatcher()); + } + + public GlyphOfDoom(final GlyphOfDoom card) { + super(card); + } + + @Override + public GlyphOfDoom copy() { + return new GlyphOfDoom(this); + } +} + +class GlyphOfDoomCreateDelayedTriggeredAbilityEffect extends OneShotEffect { + + public GlyphOfDoomCreateDelayedTriggeredAbilityEffect() { + super(Outcome.Benefit); + this.staticText = "At this turn's next end of combat, destroy all creatures that were blocked by that creature this turn"; + } + + public GlyphOfDoomCreateDelayedTriggeredAbilityEffect(final GlyphOfDoomCreateDelayedTriggeredAbilityEffect effect) { + super(effect); + } + + @Override + public GlyphOfDoomCreateDelayedTriggeredAbilityEffect copy() { + return new GlyphOfDoomCreateDelayedTriggeredAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!source.getTargets().isEmpty() && source.getFirstTarget() != null) { + MageObjectReference mor = new MageObjectReference(source.getFirstTarget(), game); + AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new GlyphOfDoomEffect(mor)); + game.addDelayedTriggeredAbility(delayedAbility, source); + return true; + } + return false; + } +} + +class GlyphOfDoomEffect extends OneShotEffect { + + MageObjectReference targetCreature; + + public GlyphOfDoomEffect(MageObjectReference targetCreature) { + super(Outcome.DestroyPermanent); + this.targetCreature = targetCreature; + } + + public GlyphOfDoomEffect(final GlyphOfDoomEffect effect) { + super(effect); + targetCreature = effect.targetCreature; + } + + @Override + public GlyphOfDoomEffect copy() { + return new GlyphOfDoomEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null && targetCreature != null) { + BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get(BlockedAttackerWatcher.class.getSimpleName()); + if (watcher != null) { + List toDestroy = new ArrayList<>(); + for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game)) { + if (!creature.getId().equals(targetCreature.getSourceId())) { + if (watcher.creatureHasBlockedAttacker(new MageObjectReference(creature, game), targetCreature, game)) { + toDestroy.add(creature); + } + } + } + for (Permanent creature : toDestroy) { + creature.destroy(source.getSourceId(), game, false); + } + return true; + } + } + return false; + } +} From a61fd3ecd5711ec6ba437bb1161cc14b6ccab0d8 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 20:42:50 +0000 Subject: [PATCH 05/19] Implemented Wood Elemental --- Mage.Sets/src/mage/cards/w/WoodElemental.java | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WoodElemental.java diff --git a/Mage.Sets/src/mage/cards/w/WoodElemental.java b/Mage.Sets/src/mage/cards/w/WoodElemental.java new file mode 100644 index 00000000000..492ce5485d7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WoodElemental.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.cards.w; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author L_J + */ +public class WoodElemental extends CardImpl { + + public WoodElemental(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // As Wood Elemental enters the battlefield, sacrifice any number of untapped Forests. + this.addAbility(new AsEntersBattlefieldAbility(new WoodElementalEffect())); + + // Wood Elemental's power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield"))); + } + + public WoodElemental(final WoodElemental card) { + super(card); + } + + @Override + public WoodElemental copy() { + return new WoodElemental(this); + } +} + +class WoodElementalEffect extends OneShotEffect { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("untapped Forests you control"); + + static { + filter.add(Predicates.not(new TappedPredicate())); + filter.add(new SubtypePredicate(SubType.FOREST)); + } + + public WoodElementalEffect() { + super(Outcome.Sacrifice); + staticText = "sacrifice any number of untapped Forests"; + } + + public WoodElementalEffect(final WoodElementalEffect effect) { + super(effect); + } + + @Override + public WoodElementalEffect copy() { + return new WoodElementalEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card sourceCard = game.getCard(source.getSourceId()); + if (controller != null && sourceCard != null) { + Target target = new TargetControlledPermanent(0, Integer.MAX_VALUE, filter, true); + if (target.canChoose(source.getSourceId(), source.getControllerId(), game) + && controller.chooseTarget(Outcome.Detriment, target, source, game)) { + if (!target.getTargets().isEmpty()) { + int sacrificedForests = target.getTargets().size(); + game.informPlayers(controller.getLogName() + " sacrifices " + sacrificedForests + " untapped Forests for " + sourceCard.getLogName()); + for (UUID targetId : target.getTargets()) { + Permanent targetPermanent = game.getPermanent(targetId); + if (targetPermanent != null) { + targetPermanent.sacrifice(source.getSourceId(), game); + } + } + game.addEffect(new SetPowerToughnessSourceEffect(sacrificedForests, sacrificedForests, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + return true; + } + } + } + return false; + } +} From 4c6e13d2839a1982b42c9f01c75ade036f947673 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 20:44:13 +0000 Subject: [PATCH 06/19] Implemented Glyph of Doom and Wood Elemental --- Mage.Sets/src/mage/sets/Legends.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/sets/Legends.java b/Mage.Sets/src/mage/sets/Legends.java index ffd05358570..779015f3cdf 100644 --- a/Mage.Sets/src/mage/sets/Legends.java +++ b/Mage.Sets/src/mage/sets/Legends.java @@ -133,6 +133,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Giant Strength", 147, Rarity.COMMON, mage.cards.g.GiantStrength.class)); cards.add(new SetCardInfo("Giant Turtle", 102, Rarity.COMMON, mage.cards.g.GiantTurtle.class)); cards.add(new SetCardInfo("Glyph of Destruction", 148, Rarity.COMMON, mage.cards.g.GlyphOfDestruction.class)); + cards.add(new SetCardInfo("Glyph of Doom", 14, Rarity.COMMON, mage.cards.g.GlyphOfDoom.class)); cards.add(new SetCardInfo("Glyph of Life", 184, Rarity.COMMON, mage.cards.g.GlyphOfLife.class)); cards.add(new SetCardInfo("Gravity Sphere", 149, Rarity.RARE, mage.cards.g.GravitySphere.class)); cards.add(new SetCardInfo("Great Defender", 185, Rarity.UNCOMMON, mage.cards.g.GreatDefender.class)); @@ -299,6 +300,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Winds of Change", 169, Rarity.UNCOMMON, mage.cards.w.WindsOfChange.class)); cards.add(new SetCardInfo("Winter Blast", 127, Rarity.RARE, mage.cards.w.WinterBlast.class)); cards.add(new SetCardInfo("Wolverine Pack", 128, Rarity.COMMON, mage.cards.w.WolverinePack.class)); + cards.add(new SetCardInfo("Wood Elemental", 129, Rarity.RARE, mage.cards.w.WoodElemental.class)); cards.add(new SetCardInfo("Xira Arien", 310, Rarity.RARE, mage.cards.x.XiraArien.class)); cards.add(new SetCardInfo("Zephyr Falcon", 86, Rarity.COMMON, mage.cards.z.ZephyrFalcon.class)); } From 8b6d443fd385825698eb68bbeafd75f603d1afd0 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 20:44:51 +0000 Subject: [PATCH 07/19] Implemented Wood Elemental --- Mage.Sets/src/mage/sets/MastersEditionIV.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index 03ff7d240ac..c60ecead216 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -316,6 +316,7 @@ public class MastersEditionIV extends ExpansionSet { cards.add(new SetCardInfo("Wild Aesthir", 34, Rarity.COMMON, mage.cards.w.WildAesthir.class)); cards.add(new SetCardInfo("Wild Griffin", 35, Rarity.COMMON, mage.cards.w.WildGriffin.class)); cards.add(new SetCardInfo("Wild Ox", 174, Rarity.UNCOMMON, mage.cards.w.WildOx.class)); + cards.add(new SetCardInfo("Wood Elemental", 175, Rarity.RARE, mage.cards.w.WoodElemental.class)); cards.add(new SetCardInfo("Xenic Poltergeist", 104, Rarity.UNCOMMON, mage.cards.x.XenicPoltergeist.class)); cards.add(new SetCardInfo("Yotian Soldier", 240, Rarity.COMMON, mage.cards.y.YotianSoldier.class)); cards.add(new SetCardInfo("Zombie Master", 105, Rarity.UNCOMMON, mage.cards.z.ZombieMaster.class)); From f86cc8a04ad2fb1ec8ae16656533753f7c26ea8e Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 22:07:34 +0000 Subject: [PATCH 08/19] Implemented Orim's Prayer --- Mage.Sets/src/mage/cards/o/OrimsPrayer.java | 94 +++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/o/OrimsPrayer.java diff --git a/Mage.Sets/src/mage/cards/o/OrimsPrayer.java b/Mage.Sets/src/mage/cards/o/OrimsPrayer.java new file mode 100644 index 00000000000..4ad6e297a70 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OrimsPrayer.java @@ -0,0 +1,94 @@ +/* + * 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.cards.o; + +import java.util.UUID; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.dynamicvalue.common.AttackingCreatureCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; + +/** + * + * @author L_J + */ +public class OrimsPrayer extends CardImpl { + + public OrimsPrayer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}"); + + // Whenever one or more creatures attack you, you gain 1 life for each attacking creature. + this.addAbility(new OrimsPrayerTriggeredAbility()); + } + + public OrimsPrayer(final OrimsPrayer card) { + super(card); + } + + @Override + public OrimsPrayer copy() { + return new OrimsPrayer(this); + } +} + +class OrimsPrayerTriggeredAbility extends TriggeredAbilityImpl { + + public OrimsPrayerTriggeredAbility() { + super(Zone.BATTLEFIELD, new GainLifeEffect(new AttackingCreatureCount())); + } + + public OrimsPrayerTriggeredAbility(final OrimsPrayerTriggeredAbility ability) { + super(ability); + } + + @Override + public OrimsPrayerTriggeredAbility copy() { + return new OrimsPrayerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return game.getCombat().getDefenders().contains(getControllerId()); + } + + @Override + public String getRule() { + return "Whenever one or more creatures attack you, " + super.getRule(); + } +} From 35a065cb326a64a7b2ee3b56a265ab94b2f06643 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 21 Feb 2018 22:08:25 +0000 Subject: [PATCH 09/19] Implemented Orim's Prayer --- Mage.Sets/src/mage/sets/Tempest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/Tempest.java b/Mage.Sets/src/mage/sets/Tempest.java index e764804b94d..26a956063fb 100644 --- a/Mage.Sets/src/mage/sets/Tempest.java +++ b/Mage.Sets/src/mage/sets/Tempest.java @@ -219,6 +219,7 @@ public class Tempest extends ExpansionSet { cards.add(new SetCardInfo("Opportunist", 194, Rarity.UNCOMMON, mage.cards.o.Opportunist.class)); cards.add(new SetCardInfo("Oracle en-Vec", 243, Rarity.RARE, mage.cards.o.OracleEnVec.class)); cards.add(new SetCardInfo("Orim, Samite Healer", 244, Rarity.RARE, mage.cards.o.OrimSamiteHealer.class)); + cards.add(new SetCardInfo("Orim's Prayer", 245, Rarity.UNCOMMON, mage.cards.o.OrimsPrayer.class)); cards.add(new SetCardInfo("Overrun", 137, Rarity.UNCOMMON, mage.cards.o.Overrun.class)); cards.add(new SetCardInfo("Pacifism", 246, Rarity.COMMON, mage.cards.p.Pacifism.class)); cards.add(new SetCardInfo("Pallimud", 195, Rarity.RARE, mage.cards.p.Pallimud.class)); From 7bbe01b25aa00409c2afe198917d23f4c1bda55a Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 06:35:13 +0000 Subject: [PATCH 10/19] Updated Vodalian War Machine watcher with MageObjectReference --- .../src/mage/cards/v/VodalianWarMachine.java | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java index 09e17dcfd92..7a972a8726e 100644 --- a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -132,9 +133,9 @@ class VodalianWarMachineEffect extends OneShotEffect { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourcePermanent != null) { VodalianWarMachineWatcher watcher = (VodalianWarMachineWatcher) game.getState().getWatchers().get(VodalianWarMachineWatcher.class.getSimpleName()); - if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId()) != null) { + if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId(), game) != null) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (watcher.getTappedMerfolkIds(sourcePermanent.getId()).contains(permanent.getId())) { + if (watcher.getTappedMerfolkIds(sourcePermanent.getId(), game).contains(permanent.getId())) { permanent.destroy(source.getSourceId(), game, false); } } @@ -148,7 +149,7 @@ class VodalianWarMachineEffect extends OneShotEffect { class VodalianWarMachineWatcher extends Watcher { - public Map> tappedMerfolkIds = new HashMap<>(); + public Map> tappedMerfolkIds = new HashMap<>(); public VodalianWarMachineWatcher() { super(VodalianWarMachineWatcher.class.getSimpleName(), WatcherScope.GAME); @@ -164,33 +165,38 @@ class VodalianWarMachineWatcher extends Watcher { return new VodalianWarMachineWatcher(this); } - public Set getTappedMerfolkIds(UUID sourceId) { - return tappedMerfolkIds.get(sourceId); + public Set getTappedMerfolkIds(UUID sourceId, Game game) { + MageObjectReference mor = new MageObjectReference(sourceId, game); + return tappedMerfolkIds.get(mor); } @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ACTIVATED_ABILITY) { if (event.getSourceId() != null) { - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); - if (stackAbility != null) { - Ability ability = stackAbility.getStackAbility(); - if (ability != null) { - for (Cost cost : ability.getCosts()) { - if (cost instanceof TapTargetCost && cost.isPaid()) { - TapTargetCost tapCost = (TapTargetCost) cost; - if (tapCost.getTarget().isChosen()) { - Set toAdd; - if (tappedMerfolkIds.get(event.getSourceId()) == null) { - toAdd = new HashSet<>(); - } else { - toAdd = tappedMerfolkIds.get(event.getSourceId()); + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (sourcePermanent != null) { + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); + if (stackAbility != null) { + Ability ability = stackAbility.getStackAbility(); + if (ability != null) { + for (Cost cost : ability.getCosts()) { + if (cost instanceof TapTargetCost && cost.isPaid()) { + TapTargetCost tapCost = (TapTargetCost) cost; + if (tapCost.getTarget().isChosen()) { + MageObjectReference mor = new MageObjectReference(sourcePermanent.getId(), sourcePermanent.getZoneChangeCounter(game) + 1, game); + Set toAdd; + if (tappedMerfolkIds.get(mor) == null) { + toAdd = new HashSet<>(); + } else { + toAdd = tappedMerfolkIds.get(mor); + } + for (UUID targetId : tapCost.getTarget().getTargets()) { + toAdd.add(targetId); + } + tappedMerfolkIds.put(mor, toAdd); + break; } - for (UUID targetId : tapCost.getTarget().getTargets()) { - toAdd.add(targetId); - } - tappedMerfolkIds.put(event.getSourceId(), toAdd); - break; } } } From 5cb1d75a1d3eab8acffb674ddeeccaed480558ca Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 07:10:19 +0000 Subject: [PATCH 11/19] Some more changes Now it catches even instances of being exiled from owner's GY while the ability is still on the stack --- .../src/mage/cards/v/VodalianWarMachine.java | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java index 7a972a8726e..75c3b50aab7 100644 --- a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -39,6 +39,7 @@ import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -58,6 +59,7 @@ import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; import mage.target.common.TargetControlledCreaturePermanent; @@ -93,7 +95,7 @@ public class VodalianWarMachine extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2, 1, Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true)))); // When Vodalian War Machine dies, destroy all Merfolk tapped this turn to pay for its abilities. - this.addAbility(new DiesTriggeredAbility(new VodalianWarMachineEffect(), false), new VodalianWarMachineWatcher()); + this.addAbility(new VodalianWarMachineTriggeredAbility(), new VodalianWarMachineWatcher()); } public VodalianWarMachine(final VodalianWarMachine card) { @@ -106,7 +108,51 @@ public class VodalianWarMachine extends CardImpl { } } +class VodalianWarMachineTriggeredAbility extends DiesTriggeredAbility { + + public VodalianWarMachineTriggeredAbility() { + super(new VodalianWarMachineEffect(), false); + } + + public VodalianWarMachineTriggeredAbility(VodalianWarMachineTriggeredAbility ability) { + super(ability); + } + + @Override + public VodalianWarMachineTriggeredAbility copy() { + return new VodalianWarMachineTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent before = ((ZoneChangeEvent) event).getTarget(); + if (before == null) { + return false; + } + if (super.checkTrigger(event, game)) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getTarget().isTransformable()) { + if (!zEvent.getTarget().getAbilities().contains(this)) { + return false; + } + } + for (Effect effect : getEffects()) { + effect.setValue("permanentLeftBattlefield", zEvent.getTarget()); + if (effect instanceof VodalianWarMachineEffect) { + VodalianWarMachineEffect effectToSet = (VodalianWarMachineEffect) effect; + effectToSet.counter = before.getZoneChangeCounter(game); + } + } + return true; + } + return false; + } + +} + class VodalianWarMachineEffect extends OneShotEffect { + + protected int counter; private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Merfolk tapped this turn to pay for its abilities"); @@ -121,6 +167,7 @@ class VodalianWarMachineEffect extends OneShotEffect { public VodalianWarMachineEffect(final VodalianWarMachineEffect effect) { super(effect); + counter = effect.counter; } @Override @@ -133,9 +180,9 @@ class VodalianWarMachineEffect extends OneShotEffect { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (sourcePermanent != null) { VodalianWarMachineWatcher watcher = (VodalianWarMachineWatcher) game.getState().getWatchers().get(VodalianWarMachineWatcher.class.getSimpleName()); - if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId(), game) != null) { + if (watcher != null && watcher.getTappedMerfolkIds(sourcePermanent.getId(), counter, game) != null) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (watcher.getTappedMerfolkIds(sourcePermanent.getId(), game).contains(permanent.getId())) { + if (watcher.getTappedMerfolkIds(sourcePermanent.getId(), counter, game).contains(permanent.getId())) { permanent.destroy(source.getSourceId(), game, false); } } @@ -165,8 +212,8 @@ class VodalianWarMachineWatcher extends Watcher { return new VodalianWarMachineWatcher(this); } - public Set getTappedMerfolkIds(UUID sourceId, Game game) { - MageObjectReference mor = new MageObjectReference(sourceId, game); + public Set getTappedMerfolkIds(UUID sourceId, int counter, Game game) { + MageObjectReference mor = new MageObjectReference(sourceId, counter, game); return tappedMerfolkIds.get(mor); } @@ -184,7 +231,7 @@ class VodalianWarMachineWatcher extends Watcher { if (cost instanceof TapTargetCost && cost.isPaid()) { TapTargetCost tapCost = (TapTargetCost) cost; if (tapCost.getTarget().isChosen()) { - MageObjectReference mor = new MageObjectReference(sourcePermanent.getId(), sourcePermanent.getZoneChangeCounter(game) + 1, game); + MageObjectReference mor = new MageObjectReference(sourcePermanent.getId(), sourcePermanent.getZoneChangeCounter(game), game); Set toAdd; if (tappedMerfolkIds.get(mor) == null) { toAdd = new HashSet<>(); From 03454d326196d33411ab3b989e6ead643c1f83c4 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 08:40:12 +0000 Subject: [PATCH 12/19] Wood Elemental edit --- Mage.Sets/src/mage/cards/w/WoodElemental.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/w/WoodElemental.java b/Mage.Sets/src/mage/cards/w/WoodElemental.java index 492ce5485d7..ac6bc424c7e 100644 --- a/Mage.Sets/src/mage/cards/w/WoodElemental.java +++ b/Mage.Sets/src/mage/cards/w/WoodElemental.java @@ -70,7 +70,7 @@ public class WoodElemental extends CardImpl { this.addAbility(new AsEntersBattlefieldAbility(new WoodElementalEffect())); // Wood Elemental's power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield"))); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("{this}'s power and toughness are each equal to the number of Forests sacrificed as it entered the battlefield"))); } public WoodElemental(final WoodElemental card) { @@ -123,7 +123,7 @@ class WoodElementalEffect extends OneShotEffect { targetPermanent.sacrifice(source.getSourceId(), game); } } - game.addEffect(new SetPowerToughnessSourceEffect(sacrificedForests, sacrificedForests, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + game.addEffect(new SetPowerToughnessSourceEffect(sacrificedForests, sacrificedForests, Duration.Custom, SubLayer.SetPT_7b), source); return true; } } From 38930c707948156c6265af82bc22237b250b2860 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 08:41:10 +0000 Subject: [PATCH 13/19] Minion of the Wastes --- Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java b/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java index 35064a7591b..2714adcea44 100644 --- a/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java +++ b/Mage.Sets/src/mage/cards/m/MinionOfTheWastes.java @@ -105,7 +105,7 @@ class MinionOfTheWastesEffect extends OneShotEffect { controller.loseLife(payAmount, game, false); game.informPlayers(new StringBuilder(sourceCard.getLogName()).append(": ").append(controller.getLogName()) .append(" pays ").append(payAmount).append(" life").toString()); - game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.Custom, SubLayer.SetPT_7b), source); return true; } return false; From 2a26ef24a536cc5d80dd22586b3fb4c327560db3 Mon Sep 17 00:00:00 2001 From: L_J Date: Fri, 23 Feb 2018 08:41:27 +0000 Subject: [PATCH 14/19] Nameless Race edit --- Mage.Sets/src/mage/cards/n/NamelessRace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/n/NamelessRace.java b/Mage.Sets/src/mage/cards/n/NamelessRace.java index 0f413268ebf..615f1a48fc4 100644 --- a/Mage.Sets/src/mage/cards/n/NamelessRace.java +++ b/Mage.Sets/src/mage/cards/n/NamelessRace.java @@ -128,7 +128,7 @@ class NamelessRaceEffect extends OneShotEffect { controller.loseLife(payAmount, game, false); game.informPlayers(new StringBuilder(sourceCard.getLogName()).append(": ").append(controller.getLogName()) .append(" pays ").append(payAmount).append(" life").toString()); - game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source); + game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.Custom, SubLayer.SetPT_7b), source); return true; } return false; From 735a7668a1f2ff7d6d603ed1ae7ff096d4ce76f2 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:26:32 +0000 Subject: [PATCH 15/19] Implemented Honorable Passage --- .../src/mage/cards/h/HonorablePassage.java | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/h/HonorablePassage.java diff --git a/Mage.Sets/src/mage/cards/h/HonorablePassage.java b/Mage.Sets/src/mage/cards/h/HonorablePassage.java new file mode 100644 index 00000000000..585d6256e09 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HonorablePassage.java @@ -0,0 +1,108 @@ +/* + * 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.cards.h; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.PreventionEffectData; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author L_J + */ +public class HonorablePassage extends CardImpl { + + public HonorablePassage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); + + // The next time a source of your choice would deal damage to target creature or player this turn, prevent that damage. If damage from a red source is prevented this way, Honorable Passage deals that much damage to the source's controller. + this.getSpellAbility().addEffect(new HonorablePassageEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); + } + + public HonorablePassage(final HonorablePassage card) { + super(card); + } + + @Override + public HonorablePassage copy() { + return new HonorablePassage(this); + } +} + +class HonorablePassageEffect extends PreventNextDamageFromChosenSourceToTargetEffect { + + public HonorablePassageEffect() { + super(Duration.EndOfTurn); + } + + public HonorablePassageEffect(final HonorablePassageEffect effect) { + super(effect); + } + + @Override + public HonorablePassageEffect copy() { + return new HonorablePassageEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + int damage = event.getAmount(); + PreventionEffectData preventEffectData = preventDamageAction(event, source, game); + if (preventEffectData.getPreventedDamage() > 0) { + MageObject sourceObject = game.getObject(event.getSourceId()); + if (sourceObject != null && sourceObject.getColor(game).isRed()) { + UUID sourceControllerId = game.getControllerId(event.getSourceId()); + if (sourceControllerId != null) { + Player sourceController = game.getPlayer(sourceControllerId); + if (sourceController != null) { + sourceController.damage(damage, source.getSourceId(), game, false, true); + } + } + } + this.used = true; + } + return false; + } + + @Override + public String getText(Mode mode) { + return "The next time a source of your choice would deal damage to target creature or player this turn, prevent that damage. If damage from a red source is prevented this way, {this} deals that much damage to the source's controller"; + } +} From 41e156ed320d2d90af651ca9dfd63e3bc9f0cf6a Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:27:44 +0000 Subject: [PATCH 16/19] Implemented Wind Shear --- Mage.Sets/src/mage/cards/w/WindShear.java | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WindShear.java diff --git a/Mage.Sets/src/mage/cards/w/WindShear.java b/Mage.Sets/src/mage/cards/w/WindShear.java new file mode 100644 index 00000000000..4d70f93c00f --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WindShear.java @@ -0,0 +1,69 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.w; + +import java.util.UUID; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.effects.common.continuous.LoseAbilityAllEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.mageobject.AbilityPredicate; + +/** + * + * @author L_J + */ +public class WindShear extends CardImpl { + + private static final FilterAttackingCreature filter = new FilterAttackingCreature("Attacking creatures with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public WindShear(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{G}"); + + // Attacking creatures with flying get -2/-2 and lose flying until end of turn. + this.getSpellAbility().addEffect(new BoostAllEffect(-2, -2, Duration.EndOfTurn, filter, false).setText("Attacking creatures with flying get -2/-2")); + this.getSpellAbility().addEffect(new LoseAbilityAllEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, filter).setText("and lose flying until end of turn")); + } + + public WindShear(final WindShear card) { + super(card); + } + + @Override + public WindShear copy() { + return new WindShear(this); + } +} From 3c2acc407c3651c9d92c1ece1e1cb831b6eea661 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:28:49 +0000 Subject: [PATCH 17/19] Implemented Foreshadow --- Mage.Sets/src/mage/cards/f/Foreshadow.java | 110 +++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/f/Foreshadow.java diff --git a/Mage.Sets/src/mage/cards/f/Foreshadow.java b/Mage.Sets/src/mage/cards/f/Foreshadow.java new file mode 100644 index 00000000000..54db6503045 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/Foreshadow.java @@ -0,0 +1,110 @@ +/* + * 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.cards.f; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.NameACardEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +/** + * + * @author Quercitron & L_J + */ +public class Foreshadow extends CardImpl { + + public Foreshadow(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); + + // Choose a card name, then target opponent puts the top card of his or her library into his or her graveyard. If that card has the chosen name, you draw a card. + this.getSpellAbility().addEffect(new NameACardEffect(NameACardEffect.TypeOfName.ALL)); + this.getSpellAbility().addEffect(new ForeshadowEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + + // Draw a card at the beginning of the next turn's upkeep. + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(1)), false)); + } + + public Foreshadow(final Foreshadow card) { + super(card); + } + + @Override + public Foreshadow copy() { + return new Foreshadow(this); + } +} + +class ForeshadowEffect extends OneShotEffect { + + public ForeshadowEffect() { + super(Outcome.DrawCard); + this.staticText = ", then target opponent puts the top card of his or her library into his or her graveyard. If that card has the chosen name, you draw a card"; + } + + public ForeshadowEffect(final ForeshadowEffect effect) { + super(effect); + } + + @Override + public ForeshadowEffect copy() { + return new ForeshadowEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY); + if (controller != null && targetPlayer != null && cardName != null && !cardName.isEmpty()) { + Card card = targetPlayer.getLibrary().getFromTop(game); + if (card != null) { + controller.moveCards(card, Zone.GRAVEYARD, source, game); + if (card.getName().equals(cardName)) { + controller.drawCards(1, game); + } + } + return true; + } + return false; + } + +} From 22a49caf56dd8de4c1d3cfb45ed02107debfb841 Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:29:43 +0000 Subject: [PATCH 18/19] Implemented Honorable Passage --- Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java b/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java index 119cb767c9c..d124325bccc 100644 --- a/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java +++ b/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java @@ -96,6 +96,7 @@ public class TimeSpiralTimeshifted extends ExpansionSet { cards.add(new SetCardInfo("Goblin Snowman", 64, Rarity.UNCOMMON, mage.cards.g.GoblinSnowman.class)); cards.add(new SetCardInfo("Grinning Totem", 110, Rarity.SPECIAL, mage.cards.g.GrinningTotem.class)); cards.add(new SetCardInfo("Hail Storm", 79, Rarity.SPECIAL, mage.cards.h.HailStorm.class)); + cards.add(new SetCardInfo("Honorable Passage", 9, Rarity.SPECIAL, mage.cards.h.HonorablePassage.class)); cards.add(new SetCardInfo("Hunting Moa", 80, Rarity.COMMON, mage.cards.h.HuntingMoa.class)); cards.add(new SetCardInfo("Icatian Javelineers", 10, Rarity.SPECIAL, IcatianJavelineers.class)); cards.add(new SetCardInfo("Jasmine Boreal", 93, Rarity.COMMON, mage.cards.j.JasmineBoreal.class)); From fa783708feee4724061824d3f1c6daf7641996df Mon Sep 17 00:00:00 2001 From: L_J Date: Sat, 24 Feb 2018 11:30:16 +0000 Subject: [PATCH 19/19] Implemented cards --- Mage.Sets/src/mage/sets/Visions.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/Visions.java b/Mage.Sets/src/mage/sets/Visions.java index b537d3b33dd..e1458607a94 100644 --- a/Mage.Sets/src/mage/sets/Visions.java +++ b/Mage.Sets/src/mage/sets/Visions.java @@ -63,7 +63,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Brass-Talon Chimera", 142, Rarity.UNCOMMON, mage.cards.b.BrassTalonChimera.class)); cards.add(new SetCardInfo("Breathstealer's Crypt", 127, Rarity.RARE, mage.cards.b.BreathstealersCrypt.class)); cards.add(new SetCardInfo("Breezekeeper", 27, Rarity.COMMON, mage.cards.b.Breezekeeper.class)); - cards.add(new SetCardInfo("Brood of Cockroaches", 3, Rarity.UNCOMMON, mage.cards.b.BroodOfCockroaches.class)); + cards.add(new SetCardInfo("Brood of Cockroaches", 3, Rarity.UNCOMMON, mage.cards.b.BroodOfCockroaches.class)); cards.add(new SetCardInfo("Bull Elephant", 51, Rarity.COMMON, mage.cards.b.BullElephant.class)); cards.add(new SetCardInfo("Chronatog", 28, Rarity.RARE, mage.cards.c.Chronatog.class)); cards.add(new SetCardInfo("City of Solitude", 52, Rarity.RARE, mage.cards.c.CityOfSolitude.class)); @@ -96,6 +96,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Fireblast", 79, Rarity.COMMON, mage.cards.f.Fireblast.class)); cards.add(new SetCardInfo("Firestorm Hellkite", 130, Rarity.RARE, mage.cards.f.FirestormHellkite.class)); cards.add(new SetCardInfo("Flooded Shoreline", 32, Rarity.RARE, mage.cards.f.FloodedShoreline.class)); + cards.add(new SetCardInfo("Foreshadow", 33, Rarity.UNCOMMON, mage.cards.f.Foreshadow.class)); cards.add(new SetCardInfo("Freewind Falcon", 105, Rarity.COMMON, mage.cards.f.FreewindFalcon.class)); cards.add(new SetCardInfo("Funeral Charm", 11, Rarity.COMMON, mage.cards.f.FuneralCharm.class)); cards.add(new SetCardInfo("Giant Caterpillar", 58, Rarity.COMMON, mage.cards.g.GiantCaterpillar.class)); @@ -105,6 +106,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Griffin Canyon", 163, Rarity.RARE, mage.cards.g.GriffinCanyon.class)); cards.add(new SetCardInfo("Hearth Charm", 82, Rarity.COMMON, mage.cards.h.HearthCharm.class)); cards.add(new SetCardInfo("Helm of Awakening", 145, Rarity.UNCOMMON, mage.cards.h.HelmOfAwakening.class)); + cards.add(new SetCardInfo("Honorable Passage", 107, Rarity.UNCOMMON, mage.cards.h.HonorablePassage.class)); cards.add(new SetCardInfo("Hope Charm", 108, Rarity.COMMON, mage.cards.h.HopeCharm.class)); cards.add(new SetCardInfo("Hulking Cyclops", 84, Rarity.UNCOMMON, mage.cards.h.HulkingCyclops.class)); cards.add(new SetCardInfo("Impulse", 34, Rarity.COMMON, mage.cards.i.Impulse.class)); @@ -199,6 +201,7 @@ public class Visions extends ExpansionSet { cards.add(new SetCardInfo("Warthog", 74, Rarity.COMMON, mage.cards.w.Warthog.class)); cards.add(new SetCardInfo("Waterspout Djinn", 50, Rarity.UNCOMMON, mage.cards.w.WaterspoutDjinn.class)); cards.add(new SetCardInfo("Wicked Reward", 25, Rarity.COMMON, mage.cards.w.WickedReward.class)); + cards.add(new SetCardInfo("Wind Shear", 75, Rarity.UNCOMMON, mage.cards.w.WindShear.class)); cards.add(new SetCardInfo("Zhalfirin Crusader", 125, Rarity.RARE, mage.cards.z.ZhalfirinCrusader.class)); } }