From 43b0694ee3281ab29b9cf44df05a24a9af23dc8e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 8 Mar 2015 01:40:16 +0100 Subject: [PATCH] * Jhoira of the Ghitu and Epochrasite - Fixed the not working suspend handling. --- .../mage/sets/modernmasters/Epochrasite.java | 80 +++++++++++++++++-- .../sets/modernmasters/JhoiraOfTheGhitu.java | 72 ++++++++++------- .../cards/abilities/keywords/SuspendTest.java | 23 ++++++ Mage/src/mage/abilities/AbilityImpl.java | 12 ++- .../mage/abilities/TriggeredAbilities.java | 17 ++-- .../condition/common/SuspendedCondition.java | 8 ++ .../abilities/keyword/SuspendAbility.java | 59 ++++++++++---- Mage/src/mage/game/GameState.java | 10 +++ 8 files changed, 215 insertions(+), 66 deletions(-) diff --git a/Mage.Sets/src/mage/sets/modernmasters/Epochrasite.java b/Mage.Sets/src/mage/sets/modernmasters/Epochrasite.java index 1b08cb8bdd1..3bd59ded48a 100644 --- a/Mage.Sets/src/mage/sets/modernmasters/Epochrasite.java +++ b/Mage.Sets/src/mage/sets/modernmasters/Epochrasite.java @@ -32,18 +32,25 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.condition.common.CastFromHandCondition; import mage.abilities.condition.InvertCondition; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.common.ExileSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.condition.common.CastFromHandCondition; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.SourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.SuspendAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; import mage.constants.Rarity; +import mage.constants.SubLayer; +import mage.constants.Zone; import mage.counters.CounterType; +import mage.game.Game; +import mage.players.Player; import mage.watchers.common.CastFromHandWatcher; /** @@ -68,10 +75,7 @@ public class Epochrasite extends CardImpl { new CastFromHandWatcher()); // When Epochrasite dies, exile it with three time counters on it and it gains suspend. - Ability ability = new DiesTriggeredAbility(new ExileSourceEffect()); - ability.addEffect(new AddCountersSourceEffect(CounterType.TIME.createInstance(3), new StaticValue(0), false, true)); - ability.addEffect(new GainAbilitySourceEffect(new SuspendAbility(3, null, this), Duration.OneUse, true)); - this.addAbility(ability); + this.addAbility(new DiesTriggeredAbility(new EpochrasiteEffect())); } public Epochrasite(final Epochrasite card) { @@ -84,3 +88,63 @@ public class Epochrasite extends CardImpl { } } +class EpochrasiteEffect extends OneShotEffect { + + public EpochrasiteEffect() { + super(Outcome.Benefit); + this.staticText = "exile it with three time counters on it and it gains suspend"; + } + + public EpochrasiteEffect(final EpochrasiteEffect effect) { + super(effect); + } + + @Override + public EpochrasiteEffect copy() { + return new EpochrasiteEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getSourceId()); + if (controller != null && card != null) { + if (game.getState().getZone(card.getId()).equals(Zone.GRAVEYARD)) { + UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); + controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getName(), source.getSourceId(), game, Zone.GRAVEYARD); + card.addCounters(CounterType.TIME.createInstance(3), game); + game.addEffect(new GainSuspendEffect(), source); + } + return true; + } + return false; + } +} + +class GainSuspendEffect extends ContinuousEffectImpl implements SourceEffect { + + public GainSuspendEffect() { + super(Duration.Custom, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "{this} gains suspend"; + } + + public GainSuspendEffect(final GainSuspendEffect effect) { + super(effect); + } + + @Override + public GainSuspendEffect copy() { + return new GainSuspendEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(source.getSourceId()); + if (card != null && game.getState().getZone(card.getId()).equals(Zone.EXILED)) { + SuspendAbility.addSuspendTemporaryToCard(card, source, game); + } else { + discard(); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java b/Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java index 4670aafb5e6..e8ac5863bd5 100644 --- a/Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java +++ b/Mage.Sets/src/mage/sets/modernmasters/JhoiraOfTheGhitu.java @@ -31,19 +31,24 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; import mage.MageInt; -import mage.abilities.Abilities; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.ExileFromHandCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.SourceEffect; import mage.abilities.keyword.SuspendAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.Rarity; +import mage.constants.SubLayer; import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterNonlandCard; @@ -116,38 +121,13 @@ class JhoiraOfTheGhituSuspendEffect extends OneShotEffect { } if (cards != null && !cards.isEmpty()) { Card card = game.getCard(cards.get(0).getId()); - boolean hasSuspend = false; - for (Ability ability :card.getAbilities()) { - if (ability instanceof SuspendAbility) { - hasSuspend = true; - break; - } - } + boolean hasSuspend = card.getAbilities().containsClass(SuspendAbility.class); - UUID exileId = (UUID) game.getState().getValue("SuspendExileId" + source.getControllerId().toString()); - if (exileId == null) { - exileId = UUID.randomUUID(); - game.getState().setValue("SuspendExileId" + source.getControllerId().toString(), exileId); - } - if (card.moveToExile(exileId, new StringBuilder("Suspended cards of ").append(controller.getName()).toString() , source.getSourceId(), game)) { + UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); + if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getName(), source.getSourceId(), game, Zone.HAND)) { card.addCounters(CounterType.TIME.createInstance(4), game); if (!hasSuspend) { - // add suspend ability - // TODO: Find a better solution for giving suspend to a card. - // If the exiled card leaves exile by another way, the abilites won't be removed from the card - Abilities oldAbilities = card.getAbilities().copy(); - SuspendAbility suspendAbility = new SuspendAbility(4, null, card); - card.addAbility(suspendAbility); - - for (Ability ability :card.getAbilities()) { - if (!oldAbilities.contains(ability)) { - ability.setControllerId(source.getControllerId()); - ability.setSourceId(source.getSourceId()); - ability.setSourceObject(source.getSourceObject(game)); - game.getState().addAbility(ability, card); - } - } - + game.addEffect(new JhoiraGainSuspendEffect(new MageObjectReference(card)), source); } game.informPlayers(controller.getName() + " suspends 4 - " + card.getName()); return true; @@ -156,3 +136,35 @@ class JhoiraOfTheGhituSuspendEffect extends OneShotEffect { return false; } } + +class JhoiraGainSuspendEffect extends ContinuousEffectImpl implements SourceEffect { + + MageObjectReference mor; + + public JhoiraGainSuspendEffect(MageObjectReference mor) { + super(Duration.Custom, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + this.mor = mor; + staticText = "{this} gains suspend"; + } + + public JhoiraGainSuspendEffect(final JhoiraGainSuspendEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public JhoiraGainSuspendEffect copy() { + return new JhoiraGainSuspendEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(mor.getSourceId()); + if (card != null && mor.refersTo(card) && game.getState().getZone(card.getId()).equals(Zone.EXILED)) { + SuspendAbility.addSuspendTemporaryToCard(card, source, game); + } else { + discard(); + } + return true; + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java index 19cf8038145..7c9abed4356 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java @@ -62,5 +62,28 @@ public class SuspendTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Epochrasite", 4, 4); } + /** + * Tests Jhoira of the Ghitu works (give suspend to a exiled card) + * {2}, Exile a nonland card from your hand: Put four time counters on the exiled card. If it doesn't have suspend, it gains suspend. + * + */ + @Test + public void testJhoiraOfTheGhitu() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + addCard(Zone.HAND, playerA, "Silvercoat Lion",1); + addCard(Zone.BATTLEFIELD, playerA, "Jhoira of the Ghitu", 1); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{2},Exile a nonland card from your hand: Put four time counters on the exiled card. If it doesn't have suspend, it gains suspend (At the beginning of your upkeep, remove a time counter from that card. When the last is removed, cast it without paying its mana cost. If it's a creature, it has haste.)."); + setChoice(playerA, "Silvercoat Lion"); + + setStopAt(11, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Jhoira of the Ghitu", 1); + assertHandCount(playerA, "Silvercoat Lion", 0); + assertPermanentCount(playerA, "Silvercoat Lion", 1); + + } } diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 86778a06933..ae3f30b82f3 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -846,9 +846,15 @@ public abstract class AbilityImpl implements Ability { if (object != null && !object.getAbilities().contains(this)) { boolean found = false; // unfortunately we need to handle double faced cards separately and only this way - if (object instanceof PermanentCard && ((PermanentCard)object).canTransform()) { - PermanentCard permanent = (PermanentCard)object; - found = permanent.getSecondCardFace().getAbilities().contains(this) || permanent.getCard().getAbilities().contains(this); + if (object instanceof PermanentCard) { + if (((PermanentCard)object).canTransform()) { + PermanentCard permanent = (PermanentCard)object; + found = permanent.getSecondCardFace().getAbilities().contains(this) || permanent.getCard().getAbilities().contains(this); + } + } else { + // check if it's an ability that is temporary gained to a card + Abilities otherAbilities = game.getState().getAllOtherAbilities(this.getSourceId()); + found = otherAbilities != null && otherAbilities.contains(this); } if (!found) { return false; diff --git a/Mage/src/mage/abilities/TriggeredAbilities.java b/Mage/src/mage/abilities/TriggeredAbilities.java index bf49b4f0dec..f48a5745d64 100644 --- a/Mage/src/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/mage/abilities/TriggeredAbilities.java @@ -73,7 +73,6 @@ public class TriggeredAbilities extends ConcurrentHashMap 0) { diff --git a/Mage/src/mage/abilities/keyword/SuspendAbility.java b/Mage/src/mage/abilities/keyword/SuspendAbility.java index 7cff82d2a2b..56297784769 100644 --- a/Mage/src/mage/abilities/keyword/SuspendAbility.java +++ b/Mage/src/mage/abilities/keyword/SuspendAbility.java @@ -176,15 +176,48 @@ public class SuspendAbility extends ActivatedAbilityImpl { .append(card.getCardType().contains(CardType.CREATURE)? " If you play it this way and it's a creature, it gains haste until you lose control of it.":"") .append(")"); } - } else { - gainedTemporary = true; + if (card.getManaCost().isEmpty()) { + setRuleAtTheTop(true); + } + card.addAbility(new SuspendBeginningOfUpkeepTriggeredAbility()); + card.addAbility(new SuspendPlayCardAbility(card.getCardType().contains(CardType.CREATURE))); } ruleText = sb.toString(); - if (card.getManaCost().isEmpty()) { - setRuleAtTheTop(true); - } - card.addAbility(new SuspendBeginningOfUpkeepTriggeredAbility()); - card.addAbility(new SuspendPlayCardAbility(card.getCardType().contains(CardType.CREATURE))); + } + + /** + * Adds suspend to a card that does not have it regularly + * e.g. Epochrasite or added by Jhoira of the Ghitu + * @param card + * @param source + * @param game + */ + public static void addSuspendTemporaryToCard(Card card, Ability source, Game game) { + SuspendAbility ability = new SuspendAbility(0, null, card, false); + ability.setSourceId(card.getId()); + ability.setControllerId(card.getOwnerId()); + game.getState().addOtherAbility(card.getId(), ability); + + SuspendBeginningOfUpkeepTriggeredAbility ability1 = new SuspendBeginningOfUpkeepTriggeredAbility(); + ability1.setSourceId(card.getId()); + ability1.setControllerId(card.getOwnerId()); + game.getState().addOtherAbility(card.getId(), ability1); + game.getState().addAbility(ability1, source.getSourceId(), card); + + SuspendPlayCardAbility ability2 = new SuspendPlayCardAbility(card.getCardType().contains(CardType.CREATURE)); + ability2.setSourceId(card.getId()); + ability2.setControllerId(card.getOwnerId()); + game.getState().addOtherAbility(card.getId(), ability2); + game.getState().addAbility(ability2, source.getSourceId(), card); + } + + public static UUID getSuspendExileId(UUID controllerId, Game game) { + UUID exileId = (UUID) game.getState().getValue("SuspendExileId" + controllerId.toString()); + if (exileId == null) { + exileId = UUID.randomUUID(); + game.getState().setValue("SuspendExileId" + controllerId.toString(), exileId); + } + return exileId; } public SuspendAbility(SuspendAbility ability) { @@ -248,12 +281,8 @@ class SuspendExileEffect extends OneShotEffect { return false; } } - UUID exileId = (UUID) game.getState().getValue("SuspendExileId" + source.getControllerId().toString()); - if (exileId == null) { - exileId = UUID.randomUUID(); - game.getState().setValue("SuspendExileId" + source.getControllerId().toString(), exileId); - } - if (card.moveToExile(exileId, new StringBuilder("Suspended cards of ").append(controller.getName()).toString() , source.getSourceId(), game)) { + UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game); + if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getName(), source.getSourceId(), game, Zone.HAND)) { if (suspend == Integer.MAX_VALUE) { suspend = source.getManaCostsToPay().getX(); } @@ -312,7 +341,7 @@ class SuspendPlayCardEffect extends OneShotEffect { public SuspendPlayCardEffect(boolean isCreature) { super(Outcome.PutCardInPlay); - this.staticText = "play it without paying its mana cost if able. If you can't, it remains removed from the game"; + this.staticText = "play it without paying its mana cost if able. If you can't, it remains removed from the game"; } public SuspendPlayCardEffect(final SuspendPlayCardEffect effect) { @@ -380,7 +409,7 @@ class GainHasteEffect extends ContinuousEffectImpl { } Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { - if (suspendController.equals(source.getControllerId())) { + if (suspendController.equals(source.getControllerId())) { permanent.addAbility(HasteAbility.getInstance(), source.getSourceId(), game); return true; } else { diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index 3f19d3c2526..1924a9ec3f0 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -701,6 +701,16 @@ public class GameState implements Serializable, Copyable { } } + /** + * Adds the ability to continuous or triggered abilities + * @param ability + * @param card + */ + public void addOtherAbility(Ability ability, Card card) { + addOtherAbility(card.getId(), ability); + addAbility(ability, card.getId(), card); + } + private void resetOtherAbilities() { otherAbilities.clear(); }