From e5a5e74862c8becce066ec02135d705e7e685355 Mon Sep 17 00:00:00 2001 From: Christiaan Date: Thu, 1 Mar 2018 16:50:46 +0100 Subject: [PATCH 1/6] added MMQ UphillBattle added MercadianMasques card Uphill Battle --- Mage.Sets/src/mage/cards/u/UphillBattle.java | 110 ++++++++++++++++++ Mage.Sets/src/mage/sets/MercadianMasques.java | 1 + 2 files changed, 111 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/u/UphillBattle.java diff --git a/Mage.Sets/src/mage/cards/u/UphillBattle.java b/Mage.Sets/src/mage/cards/u/UphillBattle.java new file mode 100644 index 00000000000..66d343af99f --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UphillBattle.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.u; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.common.CastFromHandWatcher; + +/** + * + * @author fireshoes + */ +public class UphillBattle extends CardImpl { + + public UphillBattle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); + + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UphillBattleTapEffect()), new CastFromHandWatcher()); + } + + public UphillBattle(final UphillBattle card) { + super(card); + } + + @Override + public UphillBattle copy() { + return new UphillBattle(this); + } +} + +class UphillBattleTapEffect extends ReplacementEffectImpl { + + UphillBattleTapEffect() { + super(Duration.WhileOnBattlefield, Outcome.Tap); + staticText = "Creatures played by your opponents enter the battlefield tapped"; + } + + UphillBattleTapEffect(final UphillBattleTapEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); + + CastFromHandWatcher watcher = (CastFromHandWatcher) game.getState().getWatchers().get(CastFromHandWatcher.class.getSimpleName()); + + if (target != null && watcher != null && watcher.spellWasCastFromHand(source.getSourceId())) { + target.setTapped(true); + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { + Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); + if (permanent != null && (permanent.isCreature())) { + return true; + } + } + return false; + } + + @Override + public UphillBattleTapEffect copy() { + return new UphillBattleTapEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/MercadianMasques.java b/Mage.Sets/src/mage/sets/MercadianMasques.java index 5ca90702ace..233c8fc0ecf 100644 --- a/Mage.Sets/src/mage/sets/MercadianMasques.java +++ b/Mage.Sets/src/mage/sets/MercadianMasques.java @@ -357,6 +357,7 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Two-Headed Dragon", 221, Rarity.RARE, mage.cards.t.TwoHeadedDragon.class)); cards.add(new SetCardInfo("Undertaker", 167, Rarity.COMMON, mage.cards.u.Undertaker.class)); cards.add(new SetCardInfo("Unmask", 168, Rarity.RARE, mage.cards.u.Unmask.class)); + cards.add(new SetCardInfo("Uphill Battle", 222, Rarity.UNCOMMON, mage.cards.u.UphillBattle.class)); cards.add(new SetCardInfo("Vendetta", 170, Rarity.COMMON, mage.cards.v.Vendetta.class)); cards.add(new SetCardInfo("Venomous Dragonfly", 282, Rarity.COMMON, mage.cards.v.VenomousDragonfly.class)); cards.add(new SetCardInfo("Vernal Equinox", 283, Rarity.RARE, mage.cards.v.VernalEquinox.class)); From 07c76e0784b96181c5f6469b92c2886a686c958b Mon Sep 17 00:00:00 2001 From: Danny Plenge Date: Thu, 1 Mar 2018 17:11:52 +0100 Subject: [PATCH 2/6] Test file for Uphill Battle card. --- Mage.Client/test.init | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Mage.Client/test.init diff --git a/Mage.Client/test.init b/Mage.Client/test.init new file mode 100644 index 00000000000..86238c724ab --- /dev/null +++ b/Mage.Client/test.init @@ -0,0 +1,28 @@ +[init] +battlefield:Human:Forest:5 +battlefield:Human:Plains:5 +battlefield:Human:Mountain:5 +battlefield:Human:Swamp:5 +battlefield:Human:Island:5 +hand:Human:Lightning Bolt:2 + +[current test] +graveyard:Human:Bloodghast:1 +graveyard:Computer:Bloodghast:1 +hand:Human:Bloodghast:1 +hand:Computer:Bloodghast:1 + +[2x bear to me] +battlefield:Human:Kitesail Corsair:2 + +[2x bear to comp] +battlefield:Computer:Kitesail Corsair:2 +//battlefield:Computer:Grizzly Bears:1 + +// create any useful commands +[clone] +hand:Human:Clone:3 +[force attack] +hand:Human:Pit Fight:3 +[exile] +hand:Human:Angelic Edict:3 \ No newline at end of file From f2410b7ef1cedd4a1035c72a697981e765d0cad9 Mon Sep 17 00:00:00 2001 From: Christiaan Date: Fri, 2 Mar 2018 14:32:12 +0100 Subject: [PATCH 3/6] Fixed creatures not getting tapped by Uphill Battle The condition was checking if the Uphill Battle was cast from hand, instead of the actual card entering the battlefield --- Mage.Sets/src/mage/cards/u/UphillBattle.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/cards/u/UphillBattle.java b/Mage.Sets/src/mage/cards/u/UphillBattle.java index 66d343af99f..721027f2da4 100644 --- a/Mage.Sets/src/mage/cards/u/UphillBattle.java +++ b/Mage.Sets/src/mage/cards/u/UphillBattle.java @@ -28,11 +28,9 @@ package mage.cards.u; import java.util.UUID; -import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -44,13 +42,12 @@ import mage.watchers.common.CastFromHandWatcher; /** * - * @author fireshoes + * @author chrvanorle */ public class UphillBattle extends CardImpl { - + public UphillBattle(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UphillBattleTapEffect()), new CastFromHandWatcher()); } @@ -78,10 +75,9 @@ class UphillBattleTapEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - CastFromHandWatcher watcher = (CastFromHandWatcher) game.getState().getWatchers().get(CastFromHandWatcher.class.getSimpleName()); - if (target != null && watcher != null && watcher.spellWasCastFromHand(source.getSourceId())) { + if (target != null && watcher != null && watcher.spellWasCastFromHand(target.getId())) { target.setTapped(true); } return false; From a059909fd290eeb9cd4f2e542c50b140f3ae33e8 Mon Sep 17 00:00:00 2001 From: Danny Plenge Date: Tue, 6 Mar 2018 09:52:39 +0100 Subject: [PATCH 4/6] Removed unnecessary file which I accidently added. --- Mage.Client/test.init | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 Mage.Client/test.init diff --git a/Mage.Client/test.init b/Mage.Client/test.init deleted file mode 100644 index 86238c724ab..00000000000 --- a/Mage.Client/test.init +++ /dev/null @@ -1,28 +0,0 @@ -[init] -battlefield:Human:Forest:5 -battlefield:Human:Plains:5 -battlefield:Human:Mountain:5 -battlefield:Human:Swamp:5 -battlefield:Human:Island:5 -hand:Human:Lightning Bolt:2 - -[current test] -graveyard:Human:Bloodghast:1 -graveyard:Computer:Bloodghast:1 -hand:Human:Bloodghast:1 -hand:Computer:Bloodghast:1 - -[2x bear to me] -battlefield:Human:Kitesail Corsair:2 - -[2x bear to comp] -battlefield:Computer:Kitesail Corsair:2 -//battlefield:Computer:Grizzly Bears:1 - -// create any useful commands -[clone] -hand:Human:Clone:3 -[force attack] -hand:Human:Pit Fight:3 -[exile] -hand:Human:Angelic Edict:3 \ No newline at end of file From 0fd5c4fba1a3808d2179908252e728d13333dd42 Mon Sep 17 00:00:00 2001 From: Christiaan Date: Tue, 6 Mar 2018 16:48:15 +0100 Subject: [PATCH 5/6] check on play instead of cast, updated ability to check for creature lands --- Mage.Sets/src/mage/cards/u/UphillBattle.java | 73 ++++++++++++++++++-- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/u/UphillBattle.java b/Mage.Sets/src/mage/cards/u/UphillBattle.java index 721027f2da4..59729ff95b1 100644 --- a/Mage.Sets/src/mage/cards/u/UphillBattle.java +++ b/Mage.Sets/src/mage/cards/u/UphillBattle.java @@ -27,6 +27,8 @@ */ package mage.cards.u; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -38,7 +40,8 @@ import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.watchers.common.CastFromHandWatcher; +import mage.watchers.Watcher; +import mage.watchers.common.CreatureWasCastWatcher; /** * @@ -48,7 +51,10 @@ public class UphillBattle extends CardImpl { public UphillBattle(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UphillBattleTapEffect()), new CastFromHandWatcher()); + Ability tapAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, new UphillBattleTapEffect()); + tapAbility.addWatcher(new CreatureWasCastWatcher()); + tapAbility.addWatcher(new PlayCreatureLandWatcher()); + addAbility(tapAbility); } public UphillBattle(final UphillBattle card) { @@ -61,6 +67,58 @@ public class UphillBattle extends CardImpl { } } +class PlayCreatureLandWatcher extends Watcher { + + final Set playerPlayedLand = new HashSet<>(); // player that played land + final Set landPlayed = new HashSet<>(); // land played + + public PlayCreatureLandWatcher() { + super(PlayCreatureLandWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public PlayCreatureLandWatcher(final PlayCreatureLandWatcher watcher) { + super(watcher); + playerPlayedLand.addAll(watcher.playerPlayedLand); + landPlayed.addAll(watcher.landPlayed); + } + + @Override + public PlayCreatureLandWatcher copy() { + return new PlayCreatureLandWatcher(this); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.PLAY_LAND) { + + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent != null + && permanent.isLand() + && permanent.isCreature() + && !playerPlayedLand.contains(event.getPlayerId())) { + playerPlayedLand.add(event.getPlayerId()); + landPlayed.add(event.getTargetId()); + } + } + } + + @Override + public void reset() { + playerPlayedLand.clear(); + landPlayed.clear(); + super.reset(); + } + + public boolean landPlayed(UUID playerId) { + return playerPlayedLand.contains(playerId); + } + + public boolean wasLandPlayed(UUID landId) { + return landPlayed.contains(landId); + } +} + + class UphillBattleTapEffect extends ReplacementEffectImpl { UphillBattleTapEffect() { @@ -71,13 +129,16 @@ class UphillBattleTapEffect extends ReplacementEffectImpl { UphillBattleTapEffect(final UphillBattleTapEffect effect) { super(effect); } - + @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - CastFromHandWatcher watcher = (CastFromHandWatcher) game.getState().getWatchers().get(CastFromHandWatcher.class.getSimpleName()); + CreatureWasCastWatcher creatureSpellWatcher = (CreatureWasCastWatcher) game.getState().getWatchers().get(CreatureWasCastWatcher.class.getSimpleName()); + PlayCreatureLandWatcher landWatcher = (PlayCreatureLandWatcher) game.getState().getWatchers().get(PlayCreatureLandWatcher.class.getSimpleName()); - if (target != null && watcher != null && watcher.spellWasCastFromHand(target.getId())) { + if (target != null + && ((creatureSpellWatcher != null && creatureSpellWatcher.wasCreatureCastThisTurn(target.getId())) + || (landWatcher != null && landWatcher.wasLandPlayed(target.getId())))) { target.setTapped(true); } return false; @@ -92,7 +153,7 @@ class UphillBattleTapEffect extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null && (permanent.isCreature())) { + if (permanent != null && permanent.isCreature()) { return true; } } From 0b8e8c000747f51728ae52eaa2af92f215d37005 Mon Sep 17 00:00:00 2001 From: Christiaan Date: Thu, 8 Mar 2018 16:17:36 +0100 Subject: [PATCH 6/6] Fire PLAY_LAND event when a player plays a land, fixed UphillBattle PlayCreatureLandWatcher Fire a PLAY_LAND event when a player plays a land. This is necessary for the PlayCreatureLandWatcher to know if a land was played, or put into thte battlefield (make distinction between playing Dryad Arbor and using Sneak Attack to put it on the battlefield) --- Mage.Sets/src/mage/cards/u/UphillBattle.java | 10 +++++----- Mage/src/main/java/mage/players/PlayerImpl.java | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/u/UphillBattle.java b/Mage.Sets/src/mage/cards/u/UphillBattle.java index 59729ff95b1..35064c10758 100644 --- a/Mage.Sets/src/mage/cards/u/UphillBattle.java +++ b/Mage.Sets/src/mage/cards/u/UphillBattle.java @@ -33,6 +33,7 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -90,11 +91,10 @@ class PlayCreatureLandWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.PLAY_LAND) { - - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent != null - && permanent.isLand() - && permanent.isCreature() + Card card = game.getCard(event.getTargetId()); + if (card != null + && card.isLand() + && card.isCreature() && !playerPlayedLand.contains(event.getPlayerId())) { playerPlayedLand.add(event.getPlayerId()); landPlayed.add(event.getTargetId()); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index b47fab6df90..da939fea992 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1097,6 +1097,8 @@ public abstract class PlayerImpl implements Player, Serializable { if (!ignoreTiming && !playLandAbility.canActivate(this.playerId, game)) { return false; } + + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId)); //20091005 - 305.1 if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId))) { // int bookmark = game.bookmarkState(); @@ -1113,7 +1115,7 @@ public abstract class PlayerImpl implements Player, Serializable { // what makes no real sense. So it makes no sense to generally do a restorState here. // restoreState(bookmark, card.getName(), game); } - // if the to play the land is replaced (e.g. Kjeldoran Outpos and don't sacrificing a Plains) it's a valid satte so returning true here + // if the to play the land is replaced (e.g. Kjeldoran Outpos and don't sacrificing a Plains) it's a valid state so returning true here return true; }