diff --git a/Mage.Sets/src/mage/cards/d/DetentionSphere.java b/Mage.Sets/src/mage/cards/d/DetentionSphere.java index c16dc102058..185e67bfffe 100644 --- a/Mage.Sets/src/mage/cards/d/DetentionSphere.java +++ b/Mage.Sets/src/mage/cards/d/DetentionSphere.java @@ -1,36 +1,37 @@ package mage.cards.d; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnFromExileForSourceEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.NamePredicate; -import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; import mage.util.CardUtil; -import org.apache.log4j.Logger; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; /** * @author LevelX2 */ public final class DetentionSphere extends CardImpl { - static final protected FilterPermanent filter = new FilterNonlandPermanent("nonland permanent not named Detention Sphere"); + private static final FilterPermanent filter = new FilterNonlandPermanent("nonland permanent not named Detention Sphere"); static { filter.add(Predicates.not(new NamePredicate("Detention Sphere"))); @@ -48,7 +49,7 @@ public final class DetentionSphere extends CardImpl { // When Detention Sphere leaves the battlefield, return the exiled // cards to the battlefield under their owner's control. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new DetentionSphereLeavesEffect(), false)); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false)); } private DetentionSphere(final DetentionSphere card) { @@ -65,7 +66,8 @@ class DetentionSphereEntersEffect extends OneShotEffect { DetentionSphereEntersEffect() { super(Outcome.Exile); - staticText = "you may exile target nonland permanent not named Detention Sphere and all other permanents with the same name as that permanent"; + staticText = "exile target nonland permanent not named Detention Sphere " + + "and all other permanents with the same name as that permanent"; } private DetentionSphereEntersEffect(final DetentionSphereEntersEffect effect) { @@ -74,25 +76,23 @@ class DetentionSphereEntersEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source); - if (sourceObject != null && exileId != null && targetPermanent != null && controller != null) { - - if (CardUtil.haveEmptyName(targetPermanent)) { // face down creature - controller.moveCardToExileWithInfo(targetPermanent, exileId, sourceObject.getIdName(), source, game, Zone.BATTLEFIELD, true); - } else { - String name = targetPermanent.getName(); - for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - if (permanent != null && CardUtil.haveSameNames(permanent, name, game)) { - controller.moveCardToExileWithInfo(permanent, exileId, sourceObject.getIdName(), source, game, Zone.BATTLEFIELD, true); - } - } - } - return true; + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (player == null || permanent == null) { + return false; } - return false; + Set permanents = game + .getBattlefield() + .getActivePermanents(StaticFilters.FILTER_PERMANENT, source.getControllerId(), source, game) + .stream() + .filter(p -> p.sharesName(permanent, game)) + .collect(Collectors.toSet()); + permanents.add(permanent); + return player.moveCardsToExile( + permanents, source, game, true, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); } @Override @@ -100,39 +100,3 @@ class DetentionSphereEntersEffect extends OneShotEffect { return new DetentionSphereEntersEffect(this); } } - -class DetentionSphereLeavesEffect extends OneShotEffect { - - DetentionSphereLeavesEffect() { - super(Outcome.Neutral); - staticText = "return the exiled cards to the battlefield under their owner's control"; - } - - private DetentionSphereLeavesEffect(final DetentionSphereLeavesEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (sourceObject != null && controller != null) { - Permanent permanentLeftBattlefield = (Permanent) getValue("permanentLeftBattlefield"); - if (permanentLeftBattlefield == null) { - Logger.getLogger(ReturnFromExileForSourceEffect.class).error("Permanent not found: " + sourceObject.getName()); - return false; - } - ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), permanentLeftBattlefield.getZoneChangeCounter(game))); - if (exile != null) { - controller.moveCards(exile.getCards(game), Zone.BATTLEFIELD, source, game, false, false, true, null); - } - return true; - } - return false; - } - - @Override - public DetentionSphereLeavesEffect copy() { - return new DetentionSphereLeavesEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/g/GrimoireThief.java b/Mage.Sets/src/mage/cards/g/GrimoireThief.java index 8f9da4e6f2a..8ccdb8868a9 100644 --- a/Mage.Sets/src/mage/cards/g/GrimoireThief.java +++ b/Mage.Sets/src/mage/cards/g/GrimoireThief.java @@ -200,18 +200,7 @@ class GrimoireThiefCounterspellEffect extends OneShotEffect { for (Iterator iterator = game.getStack().iterator(); iterator.hasNext(); ) { StackObject stackObject = iterator.next(); MageObject mageObject = game.getObject(card.getId()); - String name1; - String name2; - if (mageObject instanceof SplitCard) { - name1 = ((SplitCard) mageObject).getLeftHalfCard().getName(); - name2 = ((SplitCard) mageObject).getRightHalfCard().getName(); - } else { - // modal double faces cards, adventure cards -- all have one name in non stack/battlefield zone - name1 = mageObject.getName(); - name2 = name1; - } - - if (CardUtil.haveSameNames(stackObject, name1, game) || CardUtil.haveSameNames(stackObject, name2, game)) { + if (stackObject.sharesName(mageObject, game)) { Spell spell = (Spell) stackObject; game.getStack().counter(stackObject.getId(), source, game); game.informPlayers(sourceObject.getLogName() + ": spell " + spell.getIdName() + " was countered."); diff --git a/Mage.Sets/src/mage/cards/r/ReflectorMage.java b/Mage.Sets/src/mage/cards/r/ReflectorMage.java index df5dc5d9021..0469f428b44 100644 --- a/Mage.Sets/src/mage/cards/r/ReflectorMage.java +++ b/Mage.Sets/src/mage/cards/r/ReflectorMage.java @@ -16,9 +16,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; -import java.util.Objects; import java.util.UUID; /** @@ -59,7 +57,8 @@ class ReflectorMageEffect extends OneShotEffect { ReflectorMageEffect() { super(Outcome.Benefit); - this.staticText = "return target creature an opponent controls to its owner's hand. That creature's owner can't cast spells with the same name as that creature until your next turn"; + this.staticText = "return target creature an opponent controls to its owner's hand. " + + "That creature's owner can't cast spells with the same name as that creature until your next turn"; } private ReflectorMageEffect(final ReflectorMageEffect effect) { @@ -74,35 +73,31 @@ class ReflectorMageEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (targetCreature != null) { - controller.moveCards(targetCreature, Zone.HAND, source, game); - if (!CardUtil.haveEmptyName(targetCreature)) { // if the creature had no name, no restrict effect will be created - game.addEffect(new ExclusionRitualReplacementEffect(targetCreature.getName(), targetCreature.getOwnerId()), source); - } - } - return true; + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (controller == null || targetCreature == null) { + return false; } - return false; + controller.moveCards(targetCreature, Zone.HAND, source, game); + game.addEffect(new ReflectorMageReplacementEffect(targetCreature, targetCreature.getOwnerId()), source); + return true; } } -class ExclusionRitualReplacementEffect extends ContinuousRuleModifyingEffectImpl { +class ReflectorMageReplacementEffect extends ContinuousRuleModifyingEffectImpl { - private final String creatureName; + private final Permanent creature; private final UUID ownerId; - ExclusionRitualReplacementEffect(String creatureName, UUID ownerId) { + ReflectorMageReplacementEffect(Permanent creature, UUID ownerId) { super(Duration.UntilYourNextTurn, Outcome.Detriment); staticText = "That creature's owner can't cast spells with the same name as that creature until your next turn"; - this.creatureName = creatureName; + this.creature = creature.copy(); this.ownerId = ownerId; } - private ExclusionRitualReplacementEffect(final ExclusionRitualReplacementEffect effect) { + private ReflectorMageReplacementEffect(final ReflectorMageReplacementEffect effect) { super(effect); - this.creatureName = effect.creatureName; + this.creature = effect.creature.copy(); this.ownerId = effect.ownerId; } @@ -114,18 +109,12 @@ class ExclusionRitualReplacementEffect extends ContinuousRuleModifyingEffectImpl @Override public boolean applies(GameEvent event, Ability source, Game game) { SpellAbility spellAbility = SpellAbility.getSpellAbilityFromEvent(event, game); - if (spellAbility == null) { - return false; - } Card card = spellAbility.getCharacteristics(game); - if (card == null) { - return false; - } - return CardUtil.haveSameNames(card, creatureName, game) && Objects.equals(ownerId, card.getOwnerId()); + return spellAbility != null && card != null && card.isOwnedBy(ownerId) && card.sharesName(creature, game); } @Override - public ExclusionRitualReplacementEffect copy() { - return new ExclusionRitualReplacementEffect(this); + public ReflectorMageReplacementEffect copy() { + return new ReflectorMageReplacementEffect(this); } } diff --git a/Mage.Sets/src/mage/cards/s/SearchTheCity.java b/Mage.Sets/src/mage/cards/s/SearchTheCity.java index a0206a95a4c..9b1c5e03af3 100644 --- a/Mage.Sets/src/mage/cards/s/SearchTheCity.java +++ b/Mage.Sets/src/mage/cards/s/SearchTheCity.java @@ -1,28 +1,30 @@ package mage.cards.s; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.PlayCardTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.StaticFilters; import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.game.stack.Spell; import mage.game.turn.TurnMod; import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInExile; +import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; /** * @author LevelX2 @@ -37,7 +39,6 @@ public final class SearchTheCity extends CardImpl { // Whenever you play a card with the same name as one of the exiled cards, you may put one of those cards with that name into its owner's hand. Then if there are no cards exiled with Search the City, sacrifice it. If you do, take an extra turn after this one. this.addAbility(new SearchTheCityTriggeredAbility()); - } private SearchTheCity(final SearchTheCity card) { @@ -64,17 +65,10 @@ class SearchTheCityExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - // move cards from library to exile - for (int i = 0; i < 5; i++) { - if (player.getLibrary().hasCards()) { - Card topCard = player.getLibrary().getFromTop(game); - topCard.moveToExile(source.getSourceId(), "Cards exiled by Search the City", source, game); - } - } - return true; - } - return false; + return player != null && player.moveCardsToExile( + player.getLibrary().getTopCards(game, 5), source, game, true, + CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source) + ); } @Override @@ -86,8 +80,8 @@ class SearchTheCityExileEffect extends OneShotEffect { class SearchTheCityTriggeredAbility extends PlayCardTriggeredAbility { public SearchTheCityTriggeredAbility() { - super(TargetController.YOU, Zone.BATTLEFIELD, new SearchTheCityExiledCardToHandEffect(), true); - setTriggerPhrase("Whenever you play a card with the same name as one of the exiled cards, " ); + super(TargetController.YOU, Zone.BATTLEFIELD, new SearchTheCityExiledCardToHandEffect()); + setTriggerPhrase("Whenever you play a card with the same name as one of the exiled cards, "); } private SearchTheCityTriggeredAbility(final SearchTheCityTriggeredAbility ability) { @@ -99,30 +93,33 @@ class SearchTheCityTriggeredAbility extends PlayCardTriggeredAbility { if (!super.checkTrigger(event, game)) { return false; } - String cardName = ""; - if (event.getType() == GameEvent.EventType.SPELL_CAST) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null) { - cardName = spell.getName(); - } + MageObject object; + switch (event.getType()) { + case SPELL_CAST: + object = game.getStack().getSpell(event.getTargetId()); + break; + case LAND_PLAYED: + object = game.getCard(event.getTargetId()); + break; + default: + object = null; } - if (event.getType() == GameEvent.EventType.LAND_PLAYED) { - Card card = game.getCard(event.getTargetId()); - if (card != null) { - cardName = card.getName(); - } - } - if (cardName.isEmpty()) { + if (object == null) { return false; } - ExileZone searchTheCityExileZone = game.getExile().getExileZone(this.getSourceId()); - FilterCard filter = new FilterCard(); - filter.add(new NamePredicate(cardName)); - - if (searchTheCityExileZone.count(filter, game) == 0) { + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, this)); + if (exileZone == null || exileZone.isEmpty()) { return false; } - this.getEffects().get(0).setValue("cardName", cardName); + Set cards = exileZone + .getCards(game) + .stream() + .filter(card -> card.sharesName(object, game)) + .collect(Collectors.toSet()); + if (cards.isEmpty()) { + return false; + } + this.getEffects().setTargetPointer(new FixedTargets(cards, game)); return true; } @@ -136,7 +133,8 @@ class SearchTheCityExiledCardToHandEffect extends OneShotEffect { SearchTheCityExiledCardToHandEffect() { super(Outcome.DrawCard); - staticText = "you may put one of those cards with that name into its owner's hand. Then if there are no cards exiled with {this}, sacrifice it. If you do, take an extra turn after this one"; + staticText = "you may put one of those cards with that name into its owner's hand. Then if there are " + + "no cards exiled with {this}, sacrifice it. If you do, take an extra turn after this one"; } private SearchTheCityExiledCardToHandEffect(final SearchTheCityExiledCardToHandEffect effect) { @@ -145,33 +143,27 @@ class SearchTheCityExiledCardToHandEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - String cardName = (String) this.getValue("cardName"); Player controller = game.getPlayer(source.getControllerId()); if (controller == null) { return false; } - ExileZone searchTheCityExileZone = game.getExile().getExileZone(source.getSourceId()); - if (cardName != null - && searchTheCityExileZone != null) { - for (Card card : searchTheCityExileZone.getCards(game)) { - if (CardUtil.haveSameNames(card, cardName, game)) { - if (controller.moveCards(card, Zone.HAND, source, game)) { - game.informPlayers("Search the City: put " + card.getName() + " into owner's hand"); - } - searchTheCityExileZone.remove(card); - if (searchTheCityExileZone.isEmpty()) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - permanent.sacrifice(source, game); - // extra turn - game.getState().getTurnMods().add(new TurnMod(source.getControllerId()).withExtraTurn()); - } - } - return true; - } - } + Cards cards = new CardsImpl(getTargetPointer().getTargets(game, source)); + cards.retainZone(Zone.EXILED, game); + TargetCard target = new TargetCardInExile(0, 1, StaticFilters.FILTER_CARD); + controller.choose(outcome, cards, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + controller.moveCards(card, Zone.HAND, source, game); } - return false; + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (exileZone != null && !exileZone.isEmpty()) { + return true; + } + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (sourcePermanent != null && sourcePermanent.sacrifice(source, game)) { + game.getState().getTurnMods().add(new TurnMod(source.getControllerId()).withExtraTurn()); + } + return true; } @Override diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 1bb40313225..d688aad4054 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -776,6 +776,10 @@ public final class CardUtil { return object1 != null && object2 != null && haveSameNames(object1.getName(), object2.getName(), false); } + /** + * Replaced by hasName method, kept to reduce refactoring of old cards + */ + @Deprecated public static boolean haveSameNames(MageObject object, String needName, Game game) { return object.hasName(needName, game); }