From 4ed9419cbfa300489ddb15e5d2dd452df50cba60 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 23 Apr 2017 23:47:43 +1000 Subject: [PATCH 01/16] Fill in missing str --- Mage.Client/src/main/java/mage/client/MageFrame.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 5b6204f1d8a..0f2fe732d2d 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -32,6 +32,7 @@ import java.awt.event.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; +import java.net.SocketException; import java.util.*; import java.util.List; import java.util.concurrent.Executors; @@ -726,6 +727,12 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { currentConnection.setPassword(password); currentConnection.setHost(server); currentConnection.setPort(port); + String allMAC = ""; + try { + allMAC = currentConnection.getMAC(); + } catch (SocketException ex) { + } + currentConnection.setUserIdStr(System.getProperty("user.name") + ":" + System.getProperty("os.name") + ":" + MagePreferences.getUserNames() + ":" + allMAC); currentConnection.setProxyType(proxyType); currentConnection.setProxyHost(proxyServer); currentConnection.setProxyPort(proxyPort); From 4e1572bc6291cb90f824d45200daa9b2a58b6d28 Mon Sep 17 00:00:00 2001 From: fireshoes Date: Sun, 23 Apr 2017 11:20:17 -0500 Subject: [PATCH 02/16] Added missing Amonkhet Invocations. --- Mage.Sets/src/mage/sets/MasterpieceSeriesAmonkhet.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/sets/MasterpieceSeriesAmonkhet.java b/Mage.Sets/src/mage/sets/MasterpieceSeriesAmonkhet.java index 18b783f0038..38829eac36b 100644 --- a/Mage.Sets/src/mage/sets/MasterpieceSeriesAmonkhet.java +++ b/Mage.Sets/src/mage/sets/MasterpieceSeriesAmonkhet.java @@ -56,6 +56,7 @@ public class MasterpieceSeriesAmonkhet extends ExpansionSet { cards.add(new SetCardInfo("Attrition", 19, Rarity.SPECIAL, mage.cards.a.Attrition.class)); cards.add(new SetCardInfo("Austere Command", 1, Rarity.SPECIAL, mage.cards.a.AustereCommand.class)); cards.add(new SetCardInfo("Aven Mindcensor", 2, Rarity.SPECIAL, mage.cards.a.AvenMindcensor.class)); + cards.add(new SetCardInfo("Bontu the Glorified", 20, Rarity.SPECIAL, mage.cards.b.BontuTheGlorified.class)); cards.add(new SetCardInfo("Chain Lightning", 26, Rarity.SPECIAL, mage.cards.c.ChainLightning.class)); cards.add(new SetCardInfo("Consecrated Sphinx", 8, Rarity.SPECIAL, mage.cards.c.ConsecratedSphinx.class)); cards.add(new SetCardInfo("Containment Priest", 3, Rarity.SPECIAL, mage.cards.c.ContainmentPriest.class)); @@ -75,6 +76,7 @@ public class MasterpieceSeriesAmonkhet extends ExpansionSet { cards.add(new SetCardInfo("Mind Twist", 24, Rarity.SPECIAL, mage.cards.m.MindTwist.class)); cards.add(new SetCardInfo("Oketra the True", 5, Rarity.SPECIAL, mage.cards.o.OketraTheTrue.class)); cards.add(new SetCardInfo("Pact of Negation", 16, Rarity.SPECIAL, mage.cards.p.PactOfNegation.class)); + cards.add(new SetCardInfo("Rhonas the Indomitable", 28, Rarity.SPECIAL, mage.cards.r.RhonasTheIndomitable.class)); cards.add(new SetCardInfo("Spell Pierce", 17, Rarity.SPECIAL, mage.cards.s.SpellPierce.class)); cards.add(new SetCardInfo("Stifle", 18, Rarity.SPECIAL, mage.cards.s.Stifle.class)); cards.add(new SetCardInfo("Vindicate", 30, Rarity.SPECIAL, mage.cards.v.Vindicate.class)); From 747c76519e96eb889bb57e693fc809f8bed9f3bd Mon Sep 17 00:00:00 2001 From: Achilles Date: Sun, 23 Apr 2017 14:03:12 -0500 Subject: [PATCH 03/16] - Fixed filter Commit/Memory --- Mage.Sets/src/mage/cards/c/CommitMemory.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CommitMemory.java b/Mage.Sets/src/mage/cards/c/CommitMemory.java index dcd63f6ed8e..1ce9ffe6faa 100644 --- a/Mage.Sets/src/mage/cards/c/CommitMemory.java +++ b/Mage.Sets/src/mage/cards/c/CommitMemory.java @@ -41,9 +41,8 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SpellAbilityType; import mage.constants.Zone; +import mage.filter.common.FilterNonlandPermanent; import mage.filter.common.FilterSpellOrPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -59,7 +58,7 @@ public class CommitMemory extends SplitCard { private static final FilterSpellOrPermanent filter = new FilterSpellOrPermanent("spell or nonland permanent"); static { - filter.add(Predicates.not(new CardTypePredicate(CardType.LAND))); + filter.setPermanentFilter(new FilterNonlandPermanent()); } public CommitMemory(UUID ownerId, CardSetInfo setInfo) { From ec6f19f41f2e8f4ee2cf915570e60c3a04b8b61a Mon Sep 17 00:00:00 2001 From: drmDev Date: Sun, 23 Apr 2017 19:48:04 -0400 Subject: [PATCH 04/16] fixes #3226 wirefly tokens have flying --- Mage.Sets/src/mage/cards/w/WireflyHive.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/w/WireflyHive.java b/Mage.Sets/src/mage/cards/w/WireflyHive.java index a57ec8e7668..e843b17b297 100644 --- a/Mage.Sets/src/mage/cards/w/WireflyHive.java +++ b/Mage.Sets/src/mage/cards/w/WireflyHive.java @@ -35,6 +35,7 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.FlipCoinEffect; +import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -74,12 +75,13 @@ public class WireflyHive extends CardImpl { class WireflyToken extends Token { WireflyToken() { - super("Wirefly", "2/2 colorless Insect artifact creature token named Wirefly"); + super("Wirefly", "2/2 colorless Insect artifact creature token with flying named Wirefly"); this.setOriginalExpansionSetCode("DST"); this.getPower().modifyBaseValue(2); this.getToughness().modifyBaseValue(2); this.getSubtype(null).add("Insect"); this.addCardType(CardType.ARTIFACT); this.addCardType(CardType.CREATURE); + this.addAbility(FlyingAbility.getInstance()); } } From 1607849c19cb65d9465778595611d80880d57f41 Mon Sep 17 00:00:00 2001 From: Achilles Date: Sun, 23 Apr 2017 20:07:40 -0500 Subject: [PATCH 05/16] - Fixed Bug #3225 --- Mage/src/main/java/mage/game/permanent/PermanentImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 998360ec0fc..e296f0648f4 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -971,6 +971,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } game.fireEvent(GameEvent.getEvent(EventType.SACRIFICED_PERMANENT, objectId, sourceId, controllerId)); game.checkStateAndTriggered(); + game.applyEffects(); return true; } return false; From 7479cd5668f604e094fd57e0579a75e5f38313a3 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 24 Apr 2017 09:25:27 -0500 Subject: [PATCH 06/16] - Fixed Orator of Ojutai. Intervening If clause. --- .../src/mage/cards/o/OratorOfOjutai.java | 49 +++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/cards/o/OratorOfOjutai.java b/Mage.Sets/src/mage/cards/o/OratorOfOjutai.java index d6bc7533166..eca4ad6bd01 100644 --- a/Mage.Sets/src/mage/cards/o/OratorOfOjutai.java +++ b/Mage.Sets/src/mage/cards/o/OratorOfOjutai.java @@ -31,9 +31,10 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.InfoEffect; import mage.abilities.keyword.DefenderAbility; @@ -47,6 +48,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.Game; +import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; @@ -65,7 +67,7 @@ public class OratorOfOjutai extends CardImpl { } public OratorOfOjutai(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.subtype.add("Bird"); this.subtype.add("Monk"); this.power = new MageInt(0); @@ -79,7 +81,7 @@ public class OratorOfOjutai extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("As an additional cost to cast {this}, you may reveal a Dragon card from your hand"))); // When Orator of Ojutai enters the battlefield, if you revealed a Dragon card or controlled a Dragon as you cast Orator of Ojutai, draw a card. - this.addAbility(new EntersBattlefieldTriggeredAbility(new OratorOfOjutaiEffect()), new DragonOnTheBattlefieldWhileSpellWasCastWatcher()); + this.addAbility(new OratorOfOjutaiTriggeredAbility(new OratorOfOjutaiEffect()), new DragonOnTheBattlefieldWhileSpellWasCastWatcher()); } @Override @@ -88,7 +90,7 @@ public class OratorOfOjutai extends CardImpl { Player controller = game.getPlayer(ability.getControllerId()); if (controller != null) { if (controller.getHand().count(filter, game) > 0) { - ability.addCost(new RevealTargetFromHandCost(new TargetCardInHand(0,1, filter))); + ability.addCost(new RevealTargetFromHandCost(new TargetCardInHand(0, 1, filter))); } } } @@ -104,6 +106,42 @@ public class OratorOfOjutai extends CardImpl { } } +class OratorOfOjutaiTriggeredAbility extends TriggeredAbilityImpl { + + public OratorOfOjutaiTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect, false); + } + + public OratorOfOjutaiTriggeredAbility(final OratorOfOjutaiTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + //Intervening if must be checked + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(getSourceId()); + DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher = (DragonOnTheBattlefieldWhileSpellWasCastWatcher) game.getState().getWatchers().get("DragonOnTheBattlefieldWhileSpellWasCastWatcher"); + return event.getTargetId().equals(getSourceId()) + && watcher != null + && watcher.castWithConditionTrue(sourcePermanent.getSpellAbility().getId()); + } + + @Override + public String getRule() { + return "When {this} enters the battlefield, " + super.getRule(); + } + + @Override + public OratorOfOjutaiTriggeredAbility copy() { + return new OratorOfOjutaiTriggeredAbility(this); + } +} + class OratorOfOjutaiEffect extends OneShotEffect { public OratorOfOjutaiEffect() { @@ -122,6 +160,7 @@ class OratorOfOjutaiEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { + //Intervening if is checked again on resolution Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); @@ -129,9 +168,9 @@ class OratorOfOjutaiEffect extends OneShotEffect { DragonOnTheBattlefieldWhileSpellWasCastWatcher watcher = (DragonOnTheBattlefieldWhileSpellWasCastWatcher) game.getState().getWatchers().get("DragonOnTheBattlefieldWhileSpellWasCastWatcher"); if (watcher != null && watcher.castWithConditionTrue(sourcePermanent.getSpellAbility().getId())) { controller.drawCards(1, game); + return true; } } - return true; } return false; } From 2c617ae6528fe35eec5ca8dfbf27bb063015fa8b Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 24 Apr 2017 10:37:17 -0500 Subject: [PATCH 07/16] - Updated UT for Auratouched Mage --- .../cards/abilities/other/AuratouchedMageTest.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/AuratouchedMageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/AuratouchedMageTest.java index 70259f11387..b5ade08e396 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/AuratouchedMageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/AuratouchedMageTest.java @@ -29,6 +29,7 @@ package org.mage.test.cards.abilities.other; import mage.constants.PhaseStep; import mage.constants.Zone; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -46,9 +47,9 @@ public class AuratouchedMageTest extends CardTestPlayerBase { * card and put it into your hand. Then shuffle your library. * */ - /* - @Ignore //If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice. - @Test + + //If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice. + @Ignore public void testAuratouchedMageEffectHasMadeIntoTypeArtifact() { //Any Aura card you find must be able to enchant Auratouched Mage as it currently exists, or as it most recently existed on the battlefield if it’s no //longer on the battlefield. If an effect has made the Mage an artifact, for example, you could search for an Aura with “enchant artifact.” @@ -70,7 +71,7 @@ public class AuratouchedMageTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Relic Ward", 1); } - */ + @Test public void testGainsLegalAura() { // Expected result: Brainwash gets placed on Auratouched Mage @@ -89,8 +90,7 @@ public class AuratouchedMageTest extends CardTestPlayerBase { } - /* - @Ignore //If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice. + //If someone knows the way to elegantly handle the test mechanism in regards to no valid targets, please modify. The test works fine in practice. @Test public void testAuratouchedMageNotOnBattlefield() { // Expected result: Auratouched Mage is exiled immediately after entering the battlefield, the legal aura (Brainwash) gets put into controller's hand @@ -113,5 +113,4 @@ public class AuratouchedMageTest extends CardTestPlayerBase { assertLibraryCount(playerA, "Animate Wall", 1); } - */ } From 32cd4cc57d2c3c46cf40a53512e83ac3ca23b212 Mon Sep 17 00:00:00 2001 From: Derek Monturo Date: Mon, 24 Apr 2017 16:27:45 -0400 Subject: [PATCH 08/16] test for #3195 combat celebrant. ut framework does not support testing this scenario --- .../cards/abilities/keywords/ExertTest.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ExertTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ExertTest.java index dccfcf70044..a051159326f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ExertTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ExertTest.java @@ -88,4 +88,31 @@ public class ExertTest extends CardTestPlayerBase { assertLife(playerB, 24); assertTapped(gbInitiate, false); // stolen creature exerted does untap during owner's untap step } + + @Test + public void combatCelebrantExertedCannotAttackDuringNextCombatPhase() { + /* + Combat Celebrant 2R + Creature - Human Warrior 4/1 + If Combat Celebrant hasn't been exerted this turn, you may exert it as it attacks. When you do, untap all other creatures you control and after this phase, there is an additional combat phase. + */ + String cCelebrant = "Combat Celebrant"; + String memnite = "Memnite"; // {0} 1/1 + + addCard(Zone.BATTLEFIELD, playerA, cCelebrant); + addCard(Zone.BATTLEFIELD, playerA, memnite); + + attack(1, playerA, cCelebrant); + attack(1, playerA, memnite); + setChoice(playerA, "Yes"); // exert for extra turn and untap all creatures + attack(1, playerA, cCelebrant); // should not be able to attack again due to "if has not been exerted this turn" + attack(1, playerA, memnite); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 14); // 4 + 1 + 1 (Celebrant once, Memnite twice) + assertTapped(cCelebrant, true); + assertTapped(memnite, true); + } } From 11af70f7141427b5e685dfc0509e1060abd0ad16 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 24 Apr 2017 17:03:23 -0500 Subject: [PATCH 09/16] - Updated UT for Layers. --- .../test/cards/continuous/LayerTests.java | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LayerTests.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LayerTests.java index 43accbb348a..8de5dce172a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LayerTests.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LayerTests.java @@ -60,7 +60,7 @@ public class LayerTests extends CardTestPlayerBase { } - @Ignore + @Ignore //Works fine in the game. Test fails, though. public void complexExampleFromLayersArticle() { /*In play there is a Grizzly Bears which has already been Giant Growthed, a Bog Wraith enchanted by a Lignify, and Figure of Destiny with its 3rd ability activated. @@ -79,6 +79,7 @@ public class LayerTests extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Giant Growth", "Grizzly Bears"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lignify", "Bog Wrath"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R/W}:"); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R/W}{R/W}{R/W}:"); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R/W}{R/W}{R/W}{R/W}{R/W}{R/W}:"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mirrorweave", "Figure of Destiny"); @@ -92,4 +93,56 @@ public class LayerTests extends CardTestPlayerBase { assertPowerToughness(playerA, "Figure of Destiny", 0, 4, Filter.ComparisonScope.All); } + + @Test + public void testUrborgWithAnimateLandAndOvinize() { + // Animate Land: target land is a 3/3 until end of turn and is still a land. + // Ovinize: target creature becomes 0/1 and loses all abilities until end of turn. + // Urborg, Tomb of Yawgmoth : Each land is a Swamp in addition to its other types. + // Expected behavior: Urborg loses all abilities and becomes a 0/1 creature. + addCard(Zone.HAND, playerA, "Animate Land", 1); + addCard(Zone.HAND, playerA, "Ovinize", 1); + addCard(Zone.BATTLEFIELD, playerA, "Urborg, Tomb of Yawgmoth", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Land", "Urborg, Tomb of Yawgmoth"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ovinize", "Urborg, Tomb of Yawgmoth"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertType("Urborg, Tomb of Yawgmoth", CardType.CREATURE, "Swamp"); // Urborg is a creature + assertPowerToughness(playerA, "Urborg, Tomb of Yawgmoth", 0, 1); // Urborg is a 0/1 creature + + } + + @Ignore //This works fine in the game. Test fails. + public void testFromAnArticle() { + /* + Aiden has a Battlegate Mimic on the battlefield. Nick controls two Wilderness Hypnotists. + Aiden casts a Scourge of the Nobilis, targeting the Mimic; after that resolves Nick activates + one of his Hypnotist's abilities, targeting the Mimic. Aiden attacks with the Mimic, and + casts Inside Out before the damage step. Once Inside Out resolves, Nick activates the ability + of his other Hypnotist. How much damage will the Mimic deal? + */ + addCard(Zone.HAND, playerA, "Scourge of the Nobilis", 1); + addCard(Zone.HAND, playerA, "Inside Out", 1); + addCard(Zone.BATTLEFIELD, playerA, "Battlegate Mimic", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8); + addCard(Zone.BATTLEFIELD, playerA, "Island", 8); + + addCard(Zone.BATTLEFIELD, playerB, "Wilderness Hypnotist", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scourge of the Nobilis", "Battlegate Mimic"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}:", "Battlegate Mimic"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Inside Out", "Battlegate Mimic"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}:", "Battlegate Mimic"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, "Battlegate Mimic", 4, 2); + + } + } From 1d46fec3ff41dabb2b3b183c7d14d6aad4c8495f Mon Sep 17 00:00:00 2001 From: spjspj Date: Tue, 25 Apr 2017 10:18:00 +1000 Subject: [PATCH 10/16] Add in /card command for chat to get back card's: cost, type, text. --- .../main/java/mage/server/ChatManager.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Mage.Server/src/main/java/mage/server/ChatManager.java b/Mage.Server/src/main/java/mage/server/ChatManager.java index e7f921de2b5..bc4bf425b03 100644 --- a/Mage.Server/src/main/java/mage/server/ChatManager.java +++ b/Mage.Server/src/main/java/mage/server/ChatManager.java @@ -181,11 +181,14 @@ public enum ChatManager { + "
\\me - shows the history of the current player" + "
\\list or \\l - Show a list of commands" + "
\\whisper or \\w [player name] [text] - whisper to the player with the given name" + + "
\\card Card Name - Print oracle text for card" + "
[Card Name] - Show a highlighted card name" + "
\\ignore - shows current ignore list on this server." + "
\\ignore [username] - add a username to your ignore list on this server." + "
\\unignore [username] - remove a username from your ignore list on this server."; + final Pattern getCardTextPattern = Pattern.compile("^.card *(.*)"); + private boolean performUserCommand(User user, String message, UUID chatId, boolean doError) { String command = message.substring(1).trim().toUpperCase(Locale.ENGLISH); if (doError) { @@ -205,6 +208,25 @@ public enum ChatManager { chatSessions.get(chatId).broadcastInfoToUser(user, message); return true; } + if (command.startsWith("CARD ")) { + Matcher matchPattern = getCardTextPattern.matcher(message.toLowerCase()); + if (matchPattern.find()) { + String cardName = matchPattern.group(1); + CardInfo cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(cardName, true); + if (cardInfo != null) { + cardInfo.getRules(); + message = "" + cardInfo.getName() + ": Cost:" + cardInfo.getManaCosts().toString() + ", Types:" + cardInfo.getTypes().toString() + ", "; + for (String rule : cardInfo.getRules()) { + message = message + rule; + } + } else { + message = "Couldn't find: " + cardName; + + } + } + chatSessions.get(chatId).broadcastInfoToUser(user, message); + return true; + } if (command.startsWith("W ") || command.startsWith("WHISPER ")) { String rest = message.substring(command.startsWith("W ") ? 3 : 9); int first = rest.indexOf(' '); From f794b54ddab20452fc17e8b5722e98c1ae8fa071 Mon Sep 17 00:00:00 2001 From: drmDev Date: Mon, 24 Apr 2017 23:42:31 -0400 Subject: [PATCH 11/16] updated banlist resolves #3232 --- .../Mage.Deck.Constructed/src/mage/deck/Commander.java | 5 ++--- .../Mage.Deck.Constructed/src/mage/deck/Legacy.java | 1 + .../Mage.Deck.Constructed/src/mage/deck/Vintage.java | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java index 23e6ee2ec1d..5d3bf90c67b 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java @@ -27,6 +27,7 @@ */ package mage.deck; +import java.util.*; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.CanBeYourCommanderAbility; @@ -41,8 +42,6 @@ import mage.constants.SetType; import mage.filter.FilterMana; import mage.util.CardUtil; -import java.util.*; - /** * * @author Plopman @@ -71,6 +70,7 @@ public class Commander extends Constructed { banned.add("Gifts Ungiven"); banned.add("Griselbrand"); banned.add("Karakas"); + banned.add("Leovold, Emissary of Trest"); banned.add("Library of Alexandria"); banned.add("Limited Resources"); banned.add("Mox Emerald"); @@ -82,7 +82,6 @@ public class Commander extends Constructed { banned.add("Panoptic Mirror"); banned.add("Primeval Titan"); banned.add("Prophet of Kruphix"); - banned.add("Protean Hulk"); banned.add("Recurring Nightmare"); banned.add("Rofellos, Llanowar Emissary"); banned.add("Sundering Titan"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java index 13fc4428630..04836823038 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java @@ -97,6 +97,7 @@ public class Legacy extends Constructed { banned.add("Rebirth"); banned.add("Secret Summoning"); banned.add("Secrets of Paradise"); + banned.add("Sensei's Divining Top"); banned.add("Sentinel Dispatch"); banned.add("Shahrazad"); banned.add("Skullclamp"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java index 1dcdb14614a..52c7a292453 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java @@ -82,6 +82,8 @@ public class Vintage extends Constructed { restricted.add("Dig Through Time"); restricted.add("Fastbond"); restricted.add("Flash"); + restricted.add("Gitaxian Probe"); + restricted.add("Gush"); restricted.add("Imperial Seal"); restricted.add("Library of Alexandria"); restricted.add("Lion’s Eye Diamond"); From d99fe5a70ebf08649d49a48b8cc4b85aaaad1ebd Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 25 Apr 2017 09:49:43 +0200 Subject: [PATCH 12/16] * Fixed Mimeoplasm allows choosing same creature twice (fixes #3230). --- Mage.Sets/src/mage/cards/t/TheMimeoplasm.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/t/TheMimeoplasm.java b/Mage.Sets/src/mage/cards/t/TheMimeoplasm.java index fc9d2d8a78e..e9d0598607e 100644 --- a/Mage.Sets/src/mage/cards/t/TheMimeoplasm.java +++ b/Mage.Sets/src/mage/cards/t/TheMimeoplasm.java @@ -27,6 +27,7 @@ */ package mage.cards.t; +import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; @@ -37,13 +38,13 @@ import mage.cards.*; import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; -import java.util.UUID; - /** * * @author emerald000 @@ -51,7 +52,7 @@ import java.util.UUID; public class TheMimeoplasm extends CardImpl { public TheMimeoplasm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{U}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}{B}"); addSuperType(SuperType.LEGENDARY); this.subtype.add("Ooze"); @@ -95,10 +96,12 @@ class TheMimeoplasmEffect extends OneShotEffect { if (new CardsInAllGraveyardsCount(new FilterCreatureCard()).calculate(game, source, this) >= 2) { if (controller.chooseUse(Outcome.Benefit, "Do you want to exile two creature cards from graveyards?", source, game)) { TargetCardInGraveyard targetCopy = new TargetCardInGraveyard(new FilterCreatureCard("creature card to become a copy of")); - TargetCardInGraveyard targetCounters = new TargetCardInGraveyard(new FilterCreatureCard("creature card to determine amount of additional +1/+1 counters")); if (controller.choose(Outcome.Copy, targetCopy, source.getSourceId(), game)) { Card cardToCopy = game.getCard(targetCopy.getFirstTarget()); if (cardToCopy != null) { + FilterCreatureCard filter = new FilterCreatureCard("creature card to determine amount of additional +1/+1 counters"); + filter.add(Predicates.not(new CardIdPredicate(cardToCopy.getId()))); + TargetCardInGraveyard targetCounters = new TargetCardInGraveyard(filter); if (controller.choose(Outcome.Copy, targetCounters, source.getSourceId(), game)) { Card cardForCounters = game.getCard(targetCounters.getFirstTarget()); if (cardForCounters != null) { From a4e7c2b4078c18e232889f13d1e07cc3fc3ef8f5 Mon Sep 17 00:00:00 2001 From: spjspj Date: Tue, 25 Apr 2017 18:17:28 +1000 Subject: [PATCH 13/16] Fix for the audio trigger of 'Player joined the table' not being fired. --- .../main/java/mage/client/dialog/TableWaitingDialog.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java index d6492a0a391..314df928cb1 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java @@ -437,6 +437,7 @@ class UpdateSeatsTask extends SwingWorker { AudioManager.playPlayerJoinedTable(); } else { MageTray.instance.displayMessage("A player left your game."); + AudioManager.playPlayerLeft(); } MageTray.instance.blink(); } @@ -450,8 +451,10 @@ class UpdateSeatsTask extends SwingWorker { int playerCount = 0; if (tableView != null) { for (SeatView seatView : tableView.getSeats()) { - if (seatView.getPlayerId() != null && seatView.getPlayerType().equals("Human")) { - playerCount++; + if (seatView.getPlayerId() != null) { + if (seatView.getPlayerType().toString().equals("Human")) { + playerCount++; + } } } } From 4c85d89b64a87d6375c3b8a3571bbaad1a5942e8 Mon Sep 17 00:00:00 2001 From: spjspj Date: Tue, 25 Apr 2017 20:12:21 +1000 Subject: [PATCH 14/16] Fix for the audio trigger of 'Player joined the table' not being fired. --- .../main/java/mage/client/dialog/TableWaitingDialog.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java index 314df928cb1..a981e896ed0 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java @@ -54,6 +54,7 @@ import mage.client.util.GUISizeHelper; import mage.client.util.audio.AudioManager; import mage.client.util.gui.TableUtil; import mage.client.util.gui.countryBox.CountryCellRenderer; +import mage.players.PlayerType; import mage.remote.Session; import mage.view.SeatView; import mage.view.TableView; @@ -451,10 +452,8 @@ class UpdateSeatsTask extends SwingWorker { int playerCount = 0; if (tableView != null) { for (SeatView seatView : tableView.getSeats()) { - if (seatView.getPlayerId() != null) { - if (seatView.getPlayerType().toString().equals("Human")) { - playerCount++; - } + if (seatView.getPlayerId() != null && seatView.getPlayerType() == PlayerType.HUMAN) { + playerCount++; } } } From 3708ea2a89cf0787331b32ef1f82d8c0666099a7 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 25 Apr 2017 15:47:21 +0200 Subject: [PATCH 15/16] * Added 3 cards from Ravinca City of Gold set. --- Mage.Sets/src/mage/cards/d/Dogpile.java | 71 +++++++++++++++ .../src/mage/cards/s/SabertoothAlleyCat.java | 81 +++++++++++++++++ .../src/mage/cards/s/SeedsOfStrength.java | 74 ++++++++++++++++ .../src/mage/sets/RavnicaCityOfGuilds.java | 3 + .../CantBeBlockedByAllTargetEffect.java | 28 +++++- Mage/src/main/java/mage/filter/Filter.java | 5 +- .../src/main/java/mage/filter/FilterImpl.java | 3 +- .../targetpointer/ThirdTargetPointer.java | 87 +++++++++++++++++++ 8 files changed, 345 insertions(+), 7 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/d/Dogpile.java create mode 100644 Mage.Sets/src/mage/cards/s/SabertoothAlleyCat.java create mode 100644 Mage.Sets/src/mage/cards/s/SeedsOfStrength.java create mode 100644 Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java diff --git a/Mage.Sets/src/mage/cards/d/Dogpile.java b/Mage.Sets/src/mage/cards/d/Dogpile.java new file mode 100644 index 00000000000..18c8329fa05 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/Dogpile.java @@ -0,0 +1,71 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.d; + +import java.util.UUID; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author LevelX2 + */ +public class Dogpile extends CardImpl { + + private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking creatures you control"); + + static { + filter.add(new ControllerPredicate(TargetController.YOU)); + } + + public Dogpile(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}"); + + // Dogpile deals damage to target creature or player equal to the number of attacking creatures you control. + this.getSpellAbility().addEffect(new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter)). + setText("{this} deals damage to target creature or player equal to the number of attacking creatures you control")); + this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); + + } + + public Dogpile(final Dogpile card) { + super(card); + } + + @Override + public Dogpile copy() { + return new Dogpile(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SabertoothAlleyCat.java b/Mage.Sets/src/mage/cards/s/SabertoothAlleyCat.java new file mode 100644 index 00000000000..3324ebeaae3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SabertoothAlleyCat.java @@ -0,0 +1,81 @@ +/* + * 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.s; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.AttacksEachTurnStaticAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; + +/** + * + * @author LevelX2 + */ +public class SabertoothAlleyCat extends CardImpl { + + public SabertoothAlleyCat(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); + + this.subtype.add("Cat"); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Sabertooth Alley Cat attacks each turn if able. + this.addAbility(new AttacksEachTurnStaticAbility()); + + // {1}{R}: Creatures without defender can't block Sabertooth Alley Cat this turn. + this.addAbility(new SimpleActivatedAbility( + Zone.BATTLEFIELD, + new CantBeBlockedByCreaturesSourceEffect( + (FilterCreaturePermanent) new FilterCreaturePermanent().add(Predicates.not(new AbilityPredicate(DefenderAbility.class))), + Duration.EndOfTurn + ) + .setText("Creatures without defender can't block {this} this turn"), + new ManaCostsImpl<>("{1}{R}"))); + } + + public SabertoothAlleyCat(final SabertoothAlleyCat card) { + super(card); + } + + @Override + public SabertoothAlleyCat copy() { + return new SabertoothAlleyCat(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SeedsOfStrength.java b/Mage.Sets/src/mage/cards/s/SeedsOfStrength.java new file mode 100644 index 00000000000..39eae023a4e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SeedsOfStrength.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.cards.s; + +import java.util.UUID; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.common.FilterCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.SecondTargetPointer; +import mage.target.targetpointer.ThirdTargetPointer; + +/** + * + * @author LevelX2 + */ +public class SeedsOfStrength extends CardImpl { + + public SeedsOfStrength(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}{W}"); + + // Target creature gets +1/+1 until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 1, Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (1st)"))); + // Target creature gets +1/+1 until end of turn. + Effect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn).setText("
Target creature gets +1/+1 until end of turn."); + effect.setTargetPointer(new SecondTargetPointer()); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (2nd)"))); + // Target creature gets +1/+1 until end of turn. + effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn).setText("
Target creature gets +1/+1 until end of turn."); + effect.setTargetPointer(new ThirdTargetPointer()); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (3rd)"))); + } + + public SeedsOfStrength(final SeedsOfStrength card) { + super(card); + } + + @Override + public SeedsOfStrength copy() { + return new SeedsOfStrength(this); + } +} diff --git a/Mage.Sets/src/mage/sets/RavnicaCityOfGuilds.java b/Mage.Sets/src/mage/sets/RavnicaCityOfGuilds.java index 928016cccaf..e9481be12f8 100644 --- a/Mage.Sets/src/mage/sets/RavnicaCityOfGuilds.java +++ b/Mage.Sets/src/mage/sets/RavnicaCityOfGuilds.java @@ -117,6 +117,7 @@ public class RavnicaCityOfGuilds extends ExpansionSet { cards.add(new SetCardInfo("Disembowel", 85, Rarity.COMMON, mage.cards.d.Disembowel.class)); cards.add(new SetCardInfo("Divebomber Griffin", 14, Rarity.UNCOMMON, mage.cards.d.DivebomberGriffin.class)); cards.add(new SetCardInfo("Dizzy Spell", 43, Rarity.COMMON, mage.cards.d.DizzySpell.class)); + cards.add(new SetCardInfo("Dogpile", 120, Rarity.COMMON, mage.cards.d.Dogpile.class)); cards.add(new SetCardInfo("Doubling Season", 158, Rarity.RARE, mage.cards.d.DoublingSeason.class)); cards.add(new SetCardInfo("Dowsing Shaman", 159, Rarity.UNCOMMON, mage.cards.d.DowsingShaman.class)); cards.add(new SetCardInfo("Drake Familiar", 44, Rarity.COMMON, mage.cards.d.DrakeFamiliar.class)); @@ -250,6 +251,7 @@ public class RavnicaCityOfGuilds extends ExpansionSet { cards.add(new SetCardInfo("Rolling Spoil", 179, Rarity.UNCOMMON, mage.cards.r.RollingSpoil.class)); cards.add(new SetCardInfo("Roofstalker Wight", 102, Rarity.COMMON, mage.cards.r.RoofstalkerWight.class)); cards.add(new SetCardInfo("Root-Kin Ally", 180, Rarity.UNCOMMON, mage.cards.r.RootKinAlly.class)); + cards.add(new SetCardInfo("Sabertooth Alley Cat", 140, Rarity.COMMON, mage.cards.s.SabertoothAlleyCat.class)); cards.add(new SetCardInfo("Sacred Foundry", 280, Rarity.RARE, mage.cards.s.SacredFoundry.class)); cards.add(new SetCardInfo("Sadistic Augermage", 103, Rarity.COMMON, mage.cards.s.SadisticAugermage.class)); cards.add(new SetCardInfo("Sandsower", 28, Rarity.UNCOMMON, mage.cards.s.Sandsower.class)); @@ -259,6 +261,7 @@ public class RavnicaCityOfGuilds extends ExpansionSet { cards.add(new SetCardInfo("Scion of the Wild", 182, Rarity.RARE, mage.cards.s.ScionOfTheWild.class)); cards.add(new SetCardInfo("Searing Meditation", 226, Rarity.RARE, mage.cards.s.SearingMeditation.class)); cards.add(new SetCardInfo("Seed Spark", 30, Rarity.UNCOMMON, mage.cards.s.SeedSpark.class)); + cards.add(new SetCardInfo("Seeds of Strength", 227, Rarity.COMMON, mage.cards.s.SeedsOfStrength.class)); cards.add(new SetCardInfo("Seismic Spike", 141, Rarity.COMMON, mage.cards.s.SeismicSpike.class)); cards.add(new SetCardInfo("Selesnya Evangel", 228, Rarity.COMMON, mage.cards.s.SelesnyaEvangel.class)); cards.add(new SetCardInfo("Selesnya Guildmage", 252, Rarity.UNCOMMON, mage.cards.s.SelesnyaGuildmage.class)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java index 4d1136e4b42..fdd2aefac52 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java @@ -1,7 +1,29 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. */ package mage.abilities.effects.common.combat; diff --git a/Mage/src/main/java/mage/filter/Filter.java b/Mage/src/main/java/mage/filter/Filter.java index 3f701fa69b6..ecd2d46dcbb 100644 --- a/Mage/src/main/java/mage/filter/Filter.java +++ b/Mage/src/main/java/mage/filter/Filter.java @@ -27,11 +27,10 @@ */ package mage.filter; +import java.io.Serializable; import mage.filter.predicate.Predicate; import mage.game.Game; -import java.io.Serializable; - /** * @param * @author BetaSteward_at_googlemail.com @@ -45,7 +44,7 @@ public interface Filter extends Serializable { boolean match(E o, Game game); - void add(Predicate predicate); + Filter add(Predicate predicate); boolean checkObjectClass(Object object); diff --git a/Mage/src/main/java/mage/filter/FilterImpl.java b/Mage/src/main/java/mage/filter/FilterImpl.java index b48a94901eb..50923383920 100644 --- a/Mage/src/main/java/mage/filter/FilterImpl.java +++ b/Mage/src/main/java/mage/filter/FilterImpl.java @@ -65,8 +65,9 @@ public abstract class FilterImpl implements Filter { } @Override - public final void add(Predicate predicate) { + public final Filter add(Predicate predicate) { predicates.add(predicate); + return this; } @Override diff --git a/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java b/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java new file mode 100644 index 00000000000..2fdbf05037f --- /dev/null +++ b/Mage/src/main/java/mage/target/targetpointer/ThirdTargetPointer.java @@ -0,0 +1,87 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.target.targetpointer; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import mage.abilities.Ability; +import mage.cards.Card; +import mage.game.Game; + +/** + * + * @author Ludwig.Hirth + */ +public class ThirdTargetPointer implements TargetPointer { + + private Map zoneChangeCounter = new HashMap<>(); + + public static ThirdTargetPointer getInstance() { + return new ThirdTargetPointer(); + } + + public ThirdTargetPointer() { + } + + public ThirdTargetPointer(ThirdTargetPointer targetPointer) { + this.zoneChangeCounter = new HashMap<>(); + for (Map.Entry entry : targetPointer.zoneChangeCounter.entrySet()) { + this.zoneChangeCounter.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void init(Game game, Ability source) { + if (source.getTargets().size() > 2) { + for (UUID target : source.getTargets().get(2).getTargets()) { + Card card = game.getCard(target); + if (card != null) { + this.zoneChangeCounter.put(target, card.getZoneChangeCounter(game)); + } + } + } + } + + @Override + public List getTargets(Game game, Ability source) { + ArrayList target = new ArrayList<>(); + if (source.getTargets().size() > 2) { + for (UUID targetId : source.getTargets().get(2).getTargets()) { + Card card = game.getCard(targetId); + if (card != null && zoneChangeCounter.containsKey(targetId) + && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { + continue; + } + target.add(targetId); + } + } + return target; + } + + @Override + public UUID getFirst(Game game, Ability source) { + if (source.getTargets().size() > 2) { + UUID targetId = source.getTargets().get(2).getFirstTarget(); + if (zoneChangeCounter.containsKey(targetId)) { + Card card = game.getCard(targetId); + if (card != null && zoneChangeCounter.containsKey(targetId) + && card.getZoneChangeCounter(game) != zoneChangeCounter.get(targetId)) { + return null; + } + } + return targetId; + } + return null; + } + + @Override + public TargetPointer copy() { + return new ThirdTargetPointer(this); + } +} From 60cc3a76229fad495a51c3735c6e38e6d4089301 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 25 Apr 2017 17:37:38 +0200 Subject: [PATCH 16/16] * Some minor fixed and log changes. --- .../main/java/mage/server/MageServerImpl.java | 19 ++++----- .../src/main/java/mage/server/Session.java | 20 +++++----- .../main/java/mage/server/SessionManager.java | 4 +- .../src/mage/cards/t/TorrentialGearhulk.java | 8 +++- .../abilities/other/EndTurnEffectTest.java | 33 +++++++++++++++ .../mage/abilities/common/LicidAbility.java | 5 ++- .../abilities/effects/ContinuousEffects.java | 5 ++- .../counter/RemoveCounterTargetEffect.java | 40 +++++++++---------- Mage/src/main/java/mage/game/turn/Turn.java | 2 +- 9 files changed, 90 insertions(+), 46 deletions(-) diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index 0d756821670..039a25a81a3 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -636,7 +636,8 @@ public class MageServerImpl implements MageServer { } }); } else { - logger.error("table not found : " + tableId); + // this can happen if a game ends and a player quits XMage or a match nearly at the same time as the game ends + logger.trace("table not found : " + tableId); } return true; } @@ -1119,12 +1120,12 @@ public class MageServerImpl implements MageServer { public void toggleActivation(final String sessionId, final String userName) throws MageException { execute("toggleActivation", sessionId, () -> UserManager.instance.getUserByName(userName).ifPresent(user - -> { - user.setActive(!user.isActive()); - if (!user.isActive() && user.isConnected()) { - SessionManager.instance.disconnectUser(sessionId, user.getSessionId()); - } - })); + -> { + user.setActive(!user.isActive()); + if (!user.isActive() && user.isConnected()) { + SessionManager.instance.disconnectUser(sessionId, user.getSessionId()); + } + })); } @Override @@ -1159,8 +1160,8 @@ public class MageServerImpl implements MageServer { if (title != null && message != null) { execute("sendFeedbackMessage", sessionId, () -> SessionManager.instance.getSession(sessionId).ifPresent( - session -> FeedbackServiceImpl.instance.feedback(username, title, type, message, email, session.getHost()) - )); + session -> FeedbackServiceImpl.instance.feedback(username, title, type, message, email, session.getHost()) + )); } } diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index bdc2744cace..e06a5613358 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -27,6 +27,11 @@ */ package mage.server; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import mage.MageException; import mage.constants.Constants; import mage.interfaces.callback.ClientCallback; @@ -44,12 +49,6 @@ import org.jboss.remoting.callback.Callback; import org.jboss.remoting.callback.HandleCallbackException; import org.jboss.remoting.callback.InvokerCallbackHandler; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * @author BetaSteward_at_googlemail.com */ @@ -218,8 +217,8 @@ public class Session { if (authorizedUser.lockedUntil.compareTo(Calendar.getInstance().getTime()) > 0) { return "Your profile is deactivated until " + SystemUtil.dateFormat.format(authorizedUser.lockedUntil); } else { - UserManager.instance.createUser(userName, host, authorizedUser).ifPresent(user -> - user.setLockedUntil(null) + UserManager.instance.createUser(userName, host, authorizedUser).ifPresent(user + -> user.setLockedUntil(null) ); } @@ -263,7 +262,6 @@ public class Session { ChatManager.instance.sendReconnectMessage(userId); } - return null; } @@ -337,7 +335,7 @@ public class Session { lockSet = true; logger.debug("SESSION LOCK SET sessionId: " + sessionId); } else { - logger.error("CAN'T GET LOCK - userId: " + userId + " hold count: " + lock.getHoldCount()); + logger.warn("CAN'T GET LOCK - userId: " + userId + " hold count: " + lock.getHoldCount()); } Optional _user = UserManager.instance.getUser(userId); if (!_user.isPresent()) { @@ -393,7 +391,7 @@ public class Session { call.setMessageId(messageId++); callbackHandler.handleCallbackOneway(new Callback(call)); } catch (HandleCallbackException ex) { - ex.printStackTrace(); + // ex.printStackTrace(); UserManager.instance.getUser(userId).ifPresent(user -> { logger.warn("SESSION CALLBACK EXCEPTION - " + user.getName() + " userId " + userId); logger.warn(" - method: " + call.getMethod()); diff --git a/Mage.Server/src/main/java/mage/server/SessionManager.java b/Mage.Server/src/main/java/mage/server/SessionManager.java index 5ddc333875e..1b66fce0c13 100644 --- a/Mage.Server/src/main/java/mage/server/SessionManager.java +++ b/Mage.Server/src/main/java/mage/server/SessionManager.java @@ -144,8 +144,10 @@ public enum SessionManager { case LostConnection: // user lost connection - session expires countdaoun starts session.userLostConnection(); break; + case ConnectingOtherInstance: + break; default: - logger.error("endSession: unexpected reason " + reason.toString() + " - sessionId: " + sessionId); + logger.trace("endSession: unexpected reason " + reason.toString() + " - sessionId: " + sessionId); } } else { sessions.remove(sessionId); diff --git a/Mage.Sets/src/mage/cards/t/TorrentialGearhulk.java b/Mage.Sets/src/mage/cards/t/TorrentialGearhulk.java index 0f5bcee9062..c0a86a5e3e0 100644 --- a/Mage.Sets/src/mage/cards/t/TorrentialGearhulk.java +++ b/Mage.Sets/src/mage/cards/t/TorrentialGearhulk.java @@ -50,6 +50,7 @@ import mage.game.events.ZoneChangeEvent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import org.apache.log4j.Logger; /** * @@ -64,7 +65,7 @@ public class TorrentialGearhulk extends CardImpl { } public TorrentialGearhulk(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}{U}{U}"); this.subtype.add("Construct"); this.power = new MageInt(5); this.toughness = new MageInt(6); @@ -111,7 +112,7 @@ class TorrentialGearhulkEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); - if (card != null) { + if (card != null && card.getSpellAbility() != null) { if (controller.chooseUse(outcome, "Cast " + card.getLogName() + '?', source, game)) { if (controller.cast(card.getSpellAbility(), game, true)) { ContinuousEffect effect = new TorrentialGearhulkReplacementEffect(card.getId()); @@ -119,6 +120,9 @@ class TorrentialGearhulkEffect extends OneShotEffect { game.addEffect(effect, source); } } + } else { + Logger.getLogger(TorrentialGearhulkEffect.class).error("Torrential Gearhulk - Instant card without spellAbility : " + card.getName()); + return false; } return true; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/EndTurnEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/EndTurnEffectTest.java index 1a0462e4ace..af4fad430fa 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/EndTurnEffectTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/EndTurnEffectTest.java @@ -108,4 +108,37 @@ public class EndTurnEffectTest extends CardTestPlayerBase { assertHandCount(playerB, 0); } + + /** + * Test to remove a Aftermath card from spell + */ + @Test + public void testSpellAftermath() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // Insult Sorcery {2}{R} + // Damage can't be prevented this turn. If a source you control would deal damage this turn, it deals double that damage instead. + // Injury Sorcery {2}{R} + // Aftermath (Cast this spell only from your graveyard. Then exile it.) + // Injury deals 2 damage to target creature and 2 damage to target player. + addCard(Zone.GRAVEYARD, playerA, "Insult // Injury"); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + // End the turn. + // At the beginning of your next end step, you lose the game. + addCard(Zone.HAND, playerB, "Glorious End"); //Instant {2}{R} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Injury", "Silvercoat Lion"); + addTarget(playerA, playerB); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Glorious End", NO_TARGET, "Injury"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertExileCount(playerA, "Insult // Injury", 1); + assertGraveyardCount(playerB, "Glorious End", 0); + assertHandCount(playerA, 0); + assertHandCount(playerB, 0); + + } } diff --git a/Mage/src/main/java/mage/abilities/common/LicidAbility.java b/Mage/src/main/java/mage/abilities/common/LicidAbility.java index f9db3f24397..545eb9313ab 100644 --- a/Mage/src/main/java/mage/abilities/common/LicidAbility.java +++ b/Mage/src/main/java/mage/abilities/common/LicidAbility.java @@ -27,6 +27,7 @@ */ package mage.abilities.common; +import java.util.ArrayList; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.ActivatedAbilityImpl; @@ -144,14 +145,16 @@ class LicidContinuousEffect extends ContinuousEffectImpl { licid.getSubtype(game).add("Aura"); break; case AbilityAddingRemovingEffects_6: + ArrayList toRemove = new ArrayList<>(); for (Ability ability : licid.getAbilities(game)) { for (Effect effect : ability.getEffects()) { if (effect instanceof LicidEffect) { - licid.getAbilities(game).remove(ability); + toRemove.add(ability); break; } } } + licid.getAbilities(game).removeAll(toRemove); Ability ability = new EnchantAbility("creature"); ability.setRuleAtTheTop(true); licid.addAbility(ability, source.getSourceId(), game); diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java index ea7067e0d21..5d5454c5e1e 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java @@ -1218,7 +1218,10 @@ public class ContinuousEffects implements Serializable { } } } else { - logger.error("Replacement effect without ability: " + entry.getKey().toString()); + if (!(entry.getKey() instanceof AuraReplacementEffect) + && !(entry.getKey() instanceof PlaneswalkerRedirectionEffect)) { + logger.error("Replacement effect without ability: " + entry.getKey().toString()); + } } } return texts; diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveCounterTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveCounterTargetEffect.java index 5820f6166dc..e99b328e2f0 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveCounterTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveCounterTargetEffect.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.abilities.effects.common.counter; import java.util.HashSet; @@ -47,8 +46,8 @@ import mage.util.CardUtil; * * @author LevelX2 */ - public class RemoveCounterTargetEffect extends OneShotEffect { + private final Counter counter; public RemoveCounterTargetEffect() { @@ -69,23 +68,25 @@ public class RemoveCounterTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent p = game.getPermanent(targetPointer.getFirst(game, source)); - if(p != null) { + if (p != null) { Counter toRemove = (counter == null ? selectCounterType(game, source, p) : counter); - if(toRemove != null && p.getCounters(game).getCount(toRemove.getName()) >= toRemove.getCount()) { + if (toRemove != null && p.getCounters(game).getCount(toRemove.getName()) >= toRemove.getCount()) { p.removeCounters(toRemove.getName(), toRemove.getCount(), game); - if(!game.isSimulation()) + if (!game.isSimulation()) { game.informPlayers("Removed " + toRemove.getCount() + ' ' + toRemove.getName() - + " counter from " + p.getName()); + + " counter from " + p.getName()); + } return true; } } Card c = game.getCard(targetPointer.getFirst(game, source)); if (c != null && counter != null && c.getCounters(game).getCount(counter.getName()) >= counter.getCount()) { c.removeCounters(counter.getName(), counter.getCount(), game); - if (!game.isSimulation()) + if (!game.isSimulation()) { game.informPlayers(new StringBuilder("Removed ").append(counter.getCount()).append(' ').append(counter.getName()) - .append(" counter from ").append(c.getName()) - .append(" (").append(c.getCounters(game).getCount(counter.getName())).append(" left)").toString()); + .append(" counter from ").append(c.getName()) + .append(" (").append(c.getCounters(game).getCount(counter.getName())).append(" left)").toString()); + } return true; } return false; @@ -93,12 +94,12 @@ public class RemoveCounterTargetEffect extends OneShotEffect { private Counter selectCounterType(Game game, Ability source, Permanent permanent) { Player controller = game.getPlayer(source.getControllerId()); - if(controller != null && !permanent.getCounters(game).isEmpty()) { + if (controller != null && !permanent.getCounters(game).isEmpty()) { String counterName = null; - if(permanent.getCounters(game).size() > 1) { + if (permanent.getCounters(game).size() > 1) { Choice choice = new ChoiceImpl(true); Set choices = new HashSet<>(); - for(Counter counter : permanent.getCounters(game).values()) { + for (Counter counter : permanent.getCounters(game).values()) { if (permanent.getCounters(game).getCount(counter.getName()) > 0) { choices.add(counter.getName()); } @@ -108,8 +109,8 @@ public class RemoveCounterTargetEffect extends OneShotEffect { controller.choose(Outcome.Detriment, choice, game); counterName = choice.getChoice(); } else { - for(Counter counter : permanent.getCounters(game).values()) { - if(counter.getCount() > 0) { + for (Counter counter : permanent.getCounters(game).values()) { + if (counter.getCount() > 0) { counterName = counter.getName(); } } @@ -131,14 +132,13 @@ public class RemoveCounterTargetEffect extends OneShotEffect { } String text = "remove "; - if(counter == null) { + if (counter == null) { text += "a counter"; + } else { + text += CardUtil.numberToText(counter.getCount(), "a") + ' ' + counter.getName(); + text += counter.getCount() > 1 ? " counters" : " counter"; } - else { - text += CardUtil.numberToText(counter.getCount(), "a") + ' ' + counter.getName(); - text += counter.getCount() > 1 ? " counters" : " counter"; - } - text += " from target " + mode.getTargets().get(0).getTargetName(); + text += " from target " + (mode.getTargets().isEmpty() ? " object" : mode.getTargets().get(0).getTargetName()); return text; } } diff --git a/Mage/src/main/java/mage/game/turn/Turn.java b/Mage/src/main/java/mage/game/turn/Turn.java index 1de664ff84d..dcf5dd8bfa7 100644 --- a/Mage/src/main/java/mage/game/turn/Turn.java +++ b/Mage/src/main/java/mage/game/turn/Turn.java @@ -276,7 +276,7 @@ public class Turn implements Serializable { setEndTurnRequested(true); - // 1) All spells and abilities on the stack are exiled. This includes Time Stop, though it will continue to resolve. + // 1) All spells and abilities on the stack are exiled. This includes (e.g.) Time Stop, though it will continue to resolve. // It also includes spells and abilities that can't be countered. while (!game.getStack().isEmpty()) { StackObject stackObject = game.getStack().peekFirst();