diff --git a/Mage.Sets/src/mage/cards/d/DualNature.java b/Mage.Sets/src/mage/cards/d/DualNature.java index f4bebc48649..5a74d6767fd 100644 --- a/Mage.Sets/src/mage/cards/d/DualNature.java +++ b/Mage.Sets/src/mage/cards/d/DualNature.java @@ -1,9 +1,6 @@ package mage.cards.d; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.common.LeavesBattlefieldAllTriggeredAbility; @@ -20,15 +17,18 @@ import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.predicate.mageobject.SharesNamePredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class DualNature extends CardImpl { @@ -129,7 +129,7 @@ class DualNatureCreatureLeavesEffect extends OneShotEffect { if (creature != null) { FilterPermanent filter = new FilterPermanent(); filter.add(TokenPredicate.TRUE); - filter.add(new NamePredicate(creature.getName())); + filter.add(new SharesNamePredicate(creature)); new ExileAllEffect(filter).apply(game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/e/EndlessAtlas.java b/Mage.Sets/src/mage/cards/e/EndlessAtlas.java index 913fad07686..e11a38ba077 100644 --- a/Mage.Sets/src/mage/cards/e/EndlessAtlas.java +++ b/Mage.Sets/src/mage/cards/e/EndlessAtlas.java @@ -1,8 +1,5 @@ package mage.cards.e; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.condition.Condition; @@ -16,9 +13,11 @@ import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.RandomUtil; + +import java.util.*; /** - * * @author TheElk801 */ public final class EndlessAtlas extends CardImpl { @@ -31,7 +30,7 @@ public final class EndlessAtlas extends CardImpl { Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new GenericManaCost(2), - new EndlessAtlasCondition() + EndlessAtlasCondition.instance ); ability.addCost(new TapSourceCost()); this.addAbility(ability); @@ -47,21 +46,27 @@ public final class EndlessAtlas extends CardImpl { } } -class EndlessAtlasCondition implements Condition { +enum EndlessAtlasCondition implements Condition { + instance; @Override public boolean apply(Game game, Ability source) { - Map landMap = new HashMap<>(); - for (Permanent land : game.getBattlefield().getActivePermanents( + Set lands = new HashSet<>(game.getBattlefield().getActivePermanents( StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, - source.getControllerId(), game - )) { - if (land != null) { - int landCount = landMap.getOrDefault(land.getName(), 0); - if (landCount > 1) { + source.getControllerId(), source, game + )); + while (lands.size() >= 3) { + Permanent land = RandomUtil.randomFromCollection(lands); + lands.remove(land); + int amount = 0; + for (Permanent permanent : lands) { + if (!permanent.sharesName(land, game)) { + continue; + } + amount++; + if (amount >= 3) { return true; } - landMap.put(land.getName(), landCount + 1); } } return false; diff --git a/Mage.Sets/src/mage/cards/e/EscapedShapeshifter.java b/Mage.Sets/src/mage/cards/e/EscapedShapeshifter.java index e8b27784556..02e92f52e14 100644 --- a/Mage.Sets/src/mage/cards/e/EscapedShapeshifter.java +++ b/Mage.Sets/src/mage/cards/e/EscapedShapeshifter.java @@ -65,16 +65,16 @@ class EscapedShapeshifterEffect extends ContinuousEffectImpl { if (sourcePermanent == null) { return false; } - game.getBattlefield() .getActivePermanents( StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, source.getControllerId(), source, game ).stream() .filter(Objects::nonNull) - .filter(permanent -> !permanent.getName().equals("Escaped Shapeshifter")) + .filter(permanent -> !permanent.hasName("Escaped Shapeshifter", game)) .map(Permanent::getAbilities) - .flatMap(Collection::stream).filter(EscapedShapeshifterEffect::checkAbility) + .flatMap(Collection::stream) + .filter(EscapedShapeshifterEffect::checkAbility) .forEach(ability -> sourcePermanent.addAbility(ability, source.getSourceId(), game)); return true; } @@ -97,4 +97,4 @@ class EscapedShapeshifterEffect extends ContinuousEffectImpl { public EscapedShapeshifterEffect copy() { return new EscapedShapeshifterEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/e/ExtraplanarLens.java b/Mage.Sets/src/mage/cards/e/ExtraplanarLens.java index 57b52b8beeb..c12f80f17ab 100644 --- a/Mage.Sets/src/mage/cards/e/ExtraplanarLens.java +++ b/Mage.Sets/src/mage/cards/e/ExtraplanarLens.java @@ -1,7 +1,5 @@ - package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.Effect; @@ -11,11 +9,7 @@ import mage.abilities.mana.TriggeredManaAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityWord; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterLandPermanent; import mage.game.Game; import mage.game.events.GameEvent; @@ -27,8 +21,9 @@ import mage.target.common.TargetLandPermanent; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class ExtraplanarLens extends CardImpl { @@ -123,8 +118,8 @@ class ExtraplanarLensTriggeredAbility extends TriggeredManaAbility { Card imprinted = game.getCard(extraplanarLens.getImprinted().get(0)); if (imprinted != null && game.getState().getZone(imprinted.getId()) == Zone.EXILED) { - if (landTappedForMana.getName().equals(imprinted.getName()) - && landTappedForMana.isLand(game)) { + if (landTappedForMana.isLand(game) + && landTappedForMana.sharesName(imprinted, game)) { ManaEvent mEvent = (ManaEvent) event; for (Effect effect : getEffects()) { effect.setValue("mana", mEvent.getMana()); diff --git a/Mage.Sets/src/mage/cards/e/EyeOfSingularity.java b/Mage.Sets/src/mage/cards/e/EyeOfSingularity.java index 57fa01e7785..06aec1a059e 100644 --- a/Mage.Sets/src/mage/cards/e/EyeOfSingularity.java +++ b/Mage.Sets/src/mage/cards/e/EyeOfSingularity.java @@ -1,8 +1,7 @@ - package mage.cards.e; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -10,25 +9,31 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; /** - * * @author spjspj */ public final class EyeOfSingularity extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("a permanent other than a basic land"); + + static { + filter.add(Predicates.not(Predicates.or( + SuperType.BASIC.getPredicate(), + CardType.LAND.getPredicate() + ))); + } + public EyeOfSingularity(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); @@ -38,7 +43,7 @@ public final class EyeOfSingularity extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new EyeOfSingularityETBEffect())); // Whenever a permanent other than a basic land enters the battlefield, destroy all other permanents with that name. They can't be regenerated. - this.addAbility(new EyeOfSingularityTriggeredAbility()); + this.addAbility(new EntersBattlefieldAllTriggeredAbility(new EyeOfSingularityTriggeredEffect(), filter)); } private EyeOfSingularity(final EyeOfSingularity card) { @@ -56,12 +61,16 @@ class EyeOfSingularityETBEffect extends OneShotEffect { private static final FilterPermanent filter = new FilterPermanent(); static { - filter.add(Predicates.not(SuperType.BASIC.getPredicate())); + filter.add(Predicates.not(Predicates.or( + SuperType.BASIC.getPredicate(), + CardType.LAND.getPredicate() + ))); } EyeOfSingularityETBEffect() { super(Outcome.Benefit); - this.staticText = "destroy each permanent with the same name as another permanent, except for basic lands. They can't be regenerated"; + this.staticText = "destroy each permanent with the same name as another permanent, " + + "except for basic lands. They can't be regenerated"; } private EyeOfSingularityETBEffect(final EyeOfSingularityETBEffect effect) { @@ -75,80 +84,23 @@ class EyeOfSingularityETBEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Map cardNames = new HashMap<>(); - Map toDestroy = new HashMap<>(); + Set permanents = CardUtil.streamAllPairwiseMatches( + game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game), + (p1, p2) -> p1.sharesName(p2, game) + ).collect(Collectors.toSet()); - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) { - String cardName = permanent.getName(); - if (cardNames.get(cardName) == null) { - cardNames.put(cardName, permanent.getId()); - } else { - toDestroy.put(cardNames.get(cardName), 1); - toDestroy.put(permanent.getId(), 1); - } - } - for (UUID id : toDestroy.keySet()) { - Permanent permanent = game.getPermanent(id); - if (permanent != null) { - permanent.destroy(source, game, false); - } + for (Permanent permanent : permanents) { + permanent.destroy(source, game, true); } return true; } } -class EyeOfSingularityTriggeredAbility extends TriggeredAbilityImpl { - - EyeOfSingularityTriggeredAbility() { - super(Zone.BATTLEFIELD, new EyeOfSingularityTriggeredEffect(), false); - } - - private EyeOfSingularityTriggeredAbility(final EyeOfSingularityTriggeredAbility ability) { - super(ability); - } - - @Override - public EyeOfSingularityTriggeredAbility copy() { - return new EyeOfSingularityTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - UUID targetId = event.getTargetId(); - Permanent permanent = game.getPermanent(targetId); - - if (event.getTargetId().equals(this.getSourceId())) { - return false; - } - - if (permanent != null && !permanent.isBasic(game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId())); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever a permanent other than a basic land enters the battlefield, destroy all other permanents with that name. They can't be regenerated."; - } -} - class EyeOfSingularityTriggeredEffect extends OneShotEffect { - private static final FilterPermanent filter = new FilterPermanent(); - - static { - filter.add(Predicates.not(SuperType.BASIC.getPredicate())); - } - EyeOfSingularityTriggeredEffect() { super(Outcome.DestroyPermanent); + staticText = "destroy all other permanents with that name. They can’t be regenerated"; } private EyeOfSingularityTriggeredEffect(final EyeOfSingularityTriggeredEffect effect) { @@ -157,28 +109,20 @@ class EyeOfSingularityTriggeredEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Map toDestroy = new HashMap<>(); - Permanent etbPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); - - if (etbPermanent == null) { + Permanent permanent = (Permanent) getValue("permanentEnteringBattlefield"); + if (permanent == null) { return false; } - String cn = etbPermanent.getName(); - - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) { - String cardName = permanent.getName(); - if (cardName.equals(cn) && !Objects.equals(permanent.getId(), etbPermanent.getId())) { - toDestroy.put(permanent.getId(), 1); - } + Set permanents = game + .getBattlefield() + .getActivePermanents(StaticFilters.FILTER_PERMANENT, source.getControllerId(), source, game) + .stream() + .filter(p -> !p.equals(permanent)) + .filter(p -> p.sharesName(permanent, game)) + .collect(Collectors.toSet()); + for (Permanent p : permanents) { + p.destroy(source, game, true); } - - for (UUID id : toDestroy.keySet()) { - Permanent permanent = game.getPermanent(id); - if (permanent != null) { - permanent.destroy(source, game, false); - } - } - return true; } diff --git a/Mage.Sets/src/mage/cards/f/FrostpyreArcanist.java b/Mage.Sets/src/mage/cards/f/FrostpyreArcanist.java index 0c40a3f676a..569c8185fd8 100644 --- a/Mage.Sets/src/mage/cards/f/FrostpyreArcanist.java +++ b/Mage.Sets/src/mage/cards/f/FrostpyreArcanist.java @@ -1,7 +1,6 @@ package mage.cards.f; import mage.MageInt; -import mage.MageObject; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; @@ -92,7 +91,6 @@ enum FrostpyreArcanistPredicate implements ObjectSourcePlayerPredicate { .getGraveyard() .getCards(game) .stream() - .map(MageObject::getName) - .anyMatch(input.getObject().getName()::equals); + .anyMatch(card -> card.sharesName(input.getObject(), game)); } } diff --git a/Mage.Sets/src/mage/cards/g/GoblinArtisans.java b/Mage.Sets/src/mage/cards/g/GoblinArtisans.java index e9f7d247e3a..ebbfcfe6210 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinArtisans.java +++ b/Mage.Sets/src/mage/cards/g/GoblinArtisans.java @@ -98,7 +98,7 @@ class GoblinArtisansTarget extends TargetSpell { if (permanent != null && !sourceRef.refersTo(permanent, game) && permanent.isCreature(game) - && "Goblin Artisans".equals(permanent.getName()) + && permanent.hasName("Goblin Artisans", game) && stackObject .getStackAbility() .getTargets() diff --git a/Mage.Sets/src/mage/cards/g/Godsend.java b/Mage.Sets/src/mage/cards/g/Godsend.java index a14a9993897..7e520d69d16 100644 --- a/Mage.Sets/src/mage/cards/g/Godsend.java +++ b/Mage.Sets/src/mage/cards/g/Godsend.java @@ -1,6 +1,5 @@ package mage.cards.g; -import java.util.*; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; @@ -28,8 +27,9 @@ import mage.target.targetpointer.FirstTargetPointer; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; +import java.util.*; + /** - * * @author LevelX2 */ public final class Godsend extends CardImpl { @@ -201,7 +201,7 @@ class GodsendRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl { ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); if ((exileZone != null)) { for (Card card : exileZone.getCards(game)) { - if ((card.getName().equals(object.getName()))) { + if ((card.sharesName(object, game))) { return true; } } diff --git a/Mage.Sets/src/mage/cards/g/GollumObsessedStalker.java b/Mage.Sets/src/mage/cards/g/GollumObsessedStalker.java index b6cdbf3809e..ba33067057b 100644 --- a/Mage.Sets/src/mage/cards/g/GollumObsessedStalker.java +++ b/Mage.Sets/src/mage/cards/g/GollumObsessedStalker.java @@ -1,7 +1,6 @@ package mage.cards.g; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; import mage.abilities.dynamicvalue.common.ControllerGainedLifeCount; @@ -40,15 +39,11 @@ public final class GollumObsessedStalker extends CardImpl { this.addAbility(new SkulkAbility()); // At the beginning of your end step, each opponent dealt combat damage this game by a creature named Gollum, Obsessed Stalker loses life equal to the amount of life you gained this turn. - Ability ability = new BeginningOfYourEndStepTriggeredAbility( - new GollumObsessedStalkerEffect(), - false - ); + Ability ability = new BeginningOfYourEndStepTriggeredAbility(new GollumObsessedStalkerEffect(), false); ability.addWatcher(new PlayerGainedLifeWatcher()); ability.addWatcher(new GollumObsessedStalkerWatcher()); ability.addHint(ControllerGainedLifeCount.getHint()); ability.addHint(GollumObsessedStalkerHint.instance); - this.addAbility(ability); } @@ -64,8 +59,8 @@ public final class GollumObsessedStalker extends CardImpl { class GollumObsessedStalkerWatcher extends Watcher { - // For each creature name, the players damaged by them during combat. - private final Map> playersPerName = new HashMap<>(); + // Players damaged by creatures named Gollum, Obsessed Stalker during combat. + private final Set players = new HashSet<>(); public GollumObsessedStalkerWatcher() { super(WatcherScope.GAME); @@ -78,22 +73,14 @@ class GollumObsessedStalkerWatcher extends Watcher { return; } Permanent creature = game.getPermanent(event.getSourceId()); - if (creature == null) { - return; + if (creature != null && creature.isCreature(game) + && creature.hasName("Gollum, Obsessed Stalker", game)) { + players.add(event.getPlayerId()); } - - String name = creature.getName(); - UUID playerId = event.getPlayerId(); - if (creature.getName().isEmpty() || playerId == null) { - return; - } - - playersPerName.computeIfAbsent(name, k -> new HashSet<>()); - playersPerName.get(name).add(playerId); } - public Set getPlayersDamagedByNamed(String name) { - return playersPerName.getOrDefault(name, new HashSet<>()); + public Set getPlayersDamagedByNamed() { + return players; } } @@ -101,8 +88,8 @@ class GollumObsessedStalkerEffect extends OneShotEffect { GollumObsessedStalkerEffect() { super(Outcome.LoseLife); - staticText = "each opponent dealt combat damage this game by a creature named " - + "{this} loses life equal to the amount of life you gained this turn."; + staticText = "each opponent dealt combat damage this game by a creature named " + + "Gollum, Obsessed Stalker loses life equal to the amount of life you gained this turn."; } private GollumObsessedStalkerEffect(final GollumObsessedStalkerEffect effect) { @@ -118,14 +105,12 @@ class GollumObsessedStalkerEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { GollumObsessedStalkerWatcher damageWatcher = game.getState().getWatcher(GollumObsessedStalkerWatcher.class); PlayerGainedLifeWatcher lifeWatcher = game.getState().getWatcher(PlayerGainedLifeWatcher.class); - Permanent gollum = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (damageWatcher == null || lifeWatcher == null || gollum == null) { + if (damageWatcher == null || lifeWatcher == null) { return false; } - String name = gollum.getName(); int amount = lifeWatcher.getLifeGained(source.getControllerId()); - Set playersDamaged = damageWatcher.getPlayersDamagedByNamed(name); + Set playersDamaged = damageWatcher.getPlayersDamagedByNamed(); if (amount == 0 || playersDamaged.isEmpty()) { return true; @@ -135,12 +120,10 @@ class GollumObsessedStalkerEffect extends OneShotEffect { if (!playersDamaged.contains(playerId)) { continue; } - Player player = game.getPlayer(playerId); if (player == null) { continue; } - player.loseLife(amount, game, source, false); } @@ -153,39 +136,19 @@ enum GollumObsessedStalkerHint implements Hint { @Override public String getText(Game game, Ability ability) { - GollumObsessedStalkerWatcher watcher = game.getState().getWatcher(GollumObsessedStalkerWatcher.class); - if (watcher == null) { - return ""; - } - - String name = null; - Permanent gollum = game.getPermanentOrLKIBattlefield(ability.getSourceId()); - if (gollum != null) { - // Gollum is or was in play, its name is using LKI. - name = gollum.getName(); - } else { - // if Gollum LKI not in play (like in hand or in command zone), - // find the object. - MageObject gollumObj = game.getObject(ability.getSourceId()); - if (gollumObj != null) { - name = gollumObj.getName(); - } - } - if (name == null || name.isEmpty()) { - return ""; - } - // Not filtering by opponent intentionally, just to provide full info everywhere. - List namesOfPlayersDealtDamage = - watcher.getPlayersDamagedByNamed(name) - .stream() - .map(game::getPlayer) - .filter(Objects::nonNull) - .map(Player::getName) - .filter(n -> !n.isEmpty()) - .collect(Collectors.toList()); + List namesOfPlayersDealtDamage = game + .getState() + .getWatcher(GollumObsessedStalkerWatcher.class) + .getPlayersDamagedByNamed() + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getName) + .filter(n -> !n.isEmpty()) + .collect(Collectors.toList()); - return "Players dealt combat damage by creatures named " + name + " this game: [" + return "Players dealt combat damage by creatures named Gollum, Obsessed Stalker this game: [" + String.join(", ", namesOfPlayersDealtDamage) + "]"; } diff --git a/Mage.Sets/src/mage/cards/h/HintOfInsanity.java b/Mage.Sets/src/mage/cards/h/HintOfInsanity.java index 5d8af4de02f..bfd0eccc3ca 100644 --- a/Mage.Sets/src/mage/cards/h/HintOfInsanity.java +++ b/Mage.Sets/src/mage/cards/h/HintOfInsanity.java @@ -1,11 +1,10 @@ package mage.cards.h; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -14,9 +13,7 @@ import mage.players.Player; import mage.target.TargetPlayer; import mage.util.CardUtil; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -62,26 +59,14 @@ class HintOfInsanityEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); if (player == null) { return false; } - Map nameCounts = new HashMap<>(); - player.getHand() - .getCards(game) - .stream() - .map(MageObject::getName) - .forEach(s -> nameCounts.compute(s, CardUtil::setOrIncrementValue)); - Cards cards = new CardsImpl( - player.getHand() - .getCards(game) - .stream() - .filter(Objects::nonNull) - .filter(card -> !card.isLand(game)) - .filter(card -> nameCounts.getOrDefault(card.getName(), 0) > 1) - .collect(Collectors.toSet()) - ); - player.discard(cards, false, source, game); - return true; + Set cards = CardUtil.streamAllPairwiseMatches( + player.getHand().getCards(game), + (p1, p2) -> p1.sharesName(p2, game) + ).collect(Collectors.toSet()); + return !cards.isEmpty() && !player.discard(new CardsImpl(cards), false, source, game).isEmpty(); } } diff --git a/Mage.Sets/src/mage/cards/h/HourOfGlory.java b/Mage.Sets/src/mage/cards/h/HourOfGlory.java index c74c9482dd1..0f0162d68da 100644 --- a/Mage.Sets/src/mage/cards/h/HourOfGlory.java +++ b/Mage.Sets/src/mage/cards/h/HourOfGlory.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -21,7 +19,6 @@ import java.util.Set; import java.util.UUID; /** - * * @author LevelX2 */ public final class HourOfGlory extends CardImpl { @@ -63,28 +60,27 @@ class HourOfGloryEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (targetCreature != null) { - controller.moveCards(targetCreature, Zone.EXILED, source, game); - if (targetCreature.hasSubtype(SubType.GOD, game)) { - game.processAction(); - Player targetController = game.getPlayer(targetCreature.getControllerId()); - if (targetController != null) { - targetController.revealCards(sourceObject.getIdName(), targetController.getHand(), game); - Set toExile = new HashSet<>(); - for (Card card : targetController.getHand().getCards(game)) { - if (card.getName().equals(targetCreature.getName())) { - toExile.add(card); - } - } - targetController.moveCards(toExile, Zone.EXILED, source, game); - } - } - } + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (controller == null || targetCreature == null) { + return false; + } + controller.moveCards(targetCreature, Zone.EXILED, source, game); + if (!targetCreature.hasSubtype(SubType.GOD, game)) { return true; } - return false; + game.processAction(); + Player targetController = game.getPlayer(targetCreature.getControllerId()); + if (targetController == null) { + return true; + } + targetController.revealCards(source, targetController.getHand(), game); + Set toExile = new HashSet<>(); + for (Card card : targetController.getHand().getCards(game)) { + if (card.sharesName(targetCreature, game)) { + toExile.add(card); + } + } + targetController.moveCards(toExile, Zone.EXILED, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/j/JourneyForTheElixir.java b/Mage.Sets/src/mage/cards/j/JourneyForTheElixir.java index e78ed4ef421..3751f7d6910 100644 --- a/Mage.Sets/src/mage/cards/j/JourneyForTheElixir.java +++ b/Mage.Sets/src/mage/cards/j/JourneyForTheElixir.java @@ -1,6 +1,5 @@ package mage.cards.j; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.*; @@ -131,12 +130,11 @@ class JourneyForTheElixirLibraryTarget extends TargetCardInLibrary { .anyMatch(c -> c.isLand(game))) { return false; } - if (name.equals(card.getName()) + if (card.hasName(name, game) && cards .getCards(game) .stream() - .map(MageObject::getName) - .anyMatch(name::equals)) { + .anyMatch(c -> c.hasName(name, game))) { return false; } return true; @@ -198,13 +196,12 @@ class JourneyForTheElixirGraveyardTarget extends TargetCardInYourGraveyard { .getCards(game) .stream() .filter(Objects::nonNull) - .map(MageObject::getName) - .anyMatch(name::equals); + .anyMatch(card -> card.hasName(name, game)); possibleTargets.removeIf(uuid -> { Card card = game.getCard(uuid); return card != null && hasYanggu - && name.equals(card.getName()); + && card.hasName(name, game); }); return possibleTargets; } diff --git a/Mage.Sets/src/mage/cards/k/KotoseTheSilentSpider.java b/Mage.Sets/src/mage/cards/k/KotoseTheSilentSpider.java index b02bc89141e..0e5b200b4da 100644 --- a/Mage.Sets/src/mage/cards/k/KotoseTheSilentSpider.java +++ b/Mage.Sets/src/mage/cards/k/KotoseTheSilentSpider.java @@ -10,7 +10,7 @@ import mage.cards.*; import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.predicate.mageobject.SharesNamePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; @@ -100,7 +100,7 @@ class KotoseTheSilentSpiderEffect extends OneShotEffect { controller.moveCardsToExile(card, source, game, true, exileId, exileName); Cards cards = new CardsImpl(); FilterCard filter = new FilterCard("cards named " + card.getName() + " from " + opponent.getName() + "'s graveyard"); - filter.add(new NamePredicate(card.getName())); + filter.add(new SharesNamePredicate(card)); TargetCardInGraveyard targetCardInGraveyard = new TargetCardInGraveyard(0, Integer.MAX_VALUE, filter); controller.choose(outcome, opponent.getGraveyard(), targetCardInGraveyard, source, game); diff --git a/Mage.Sets/src/mage/cards/l/LegionsEnd.java b/Mage.Sets/src/mage/cards/l/LegionsEnd.java index 6041696698d..49bf3c97879 100644 --- a/Mage.Sets/src/mage/cards/l/LegionsEnd.java +++ b/Mage.Sets/src/mage/cards/l/LegionsEnd.java @@ -71,7 +71,7 @@ class LegionsEndEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent == null) { return false; } @@ -79,32 +79,28 @@ class LegionsEndEffect extends OneShotEffect { if (player == null) { return false; } - String name = permanent.getName(); - if (name == null || name.equals("")) { - player.revealCards(source, player.getHand(), game); - return player.moveCards(permanent, Zone.EXILED, source, game); - } Cards cards = new CardsImpl(); game.getBattlefield() - .getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, player.getId(), game) + .getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURE, player.getId(), source, game) .stream() - .filter(perm -> name.equals(perm.getName())) + .filter(perm -> perm.sharesName(permanent, game)) .forEach(cards::add); + player.moveCards(cards, Zone.EXILED, source, game); + cards.clear(); player.revealCards(source, player.getHand(), game); - player.getHand() .getCards(game) .stream() - .filter(card -> name.equals(card.getName())) + .filter(card -> card.sharesName(permanent, game)) .forEach(cards::add); player.getGraveyard() .getCards(game) .stream() - .filter(card -> name.equals(card.getName())) + .filter(card -> card.sharesName(permanent, game)) .forEach(cards::add); return player.moveCards(cards, Zone.EXILED, source, game); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/l/LocketOfYesterdays.java b/Mage.Sets/src/mage/cards/l/LocketOfYesterdays.java index 24662243bc5..b5dacc835e9 100644 --- a/Mage.Sets/src/mage/cards/l/LocketOfYesterdays.java +++ b/Mage.Sets/src/mage/cards/l/LocketOfYesterdays.java @@ -1,21 +1,23 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; import mage.game.Game; +import mage.players.Player; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class LocketOfYesterdays extends CardImpl { @@ -24,7 +26,7 @@ public final class LocketOfYesterdays extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); // Spells you cast cost {1} less to cast for each card with the same name as that spell in your graveyard. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LocketOfYesterdaysCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(new LocketOfYesterdaysCostReductionEffect())); } private LocketOfYesterdays(final LocketOfYesterdays card) { @@ -50,36 +52,32 @@ class LocketOfYesterdaysCostReductionEffect extends CostModificationEffectImpl { @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { + Player player = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(abilityToModify.getSourceId()); - if (sourceObject != null) { - int amount = 0; - for (UUID cardId : game.getPlayer(source.getControllerId()).getGraveyard()) { - Card card = game.getCard(cardId); - if (card != null && card.getName().equals(sourceObject.getName())) { - amount++; - } - } - if (amount > 0) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, amount); - } - return true; + if (player == null || sourceObject == null) { + return false; } - return false; + int amount = player + .getGraveyard() + .getCards(game) + .stream() + .filter(card -> card.sharesName(sourceObject, game)) + .mapToInt(x -> 1) + .sum(); + if (amount > 0) { + CardUtil.adjustCost((SpellAbility) abilityToModify, amount); + } + return true; } @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify.isControlledBy(source.getControllerId()) - && (abilityToModify instanceof SpellAbility)) { - return true; - } - return false; + return abilityToModify.isControlledBy(source.getControllerId()) + && abilityToModify instanceof SpellAbility; } @Override public LocketOfYesterdaysCostReductionEffect copy() { return new LocketOfYesterdaysCostReductionEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/l/LutriTheSpellchaser.java b/Mage.Sets/src/mage/cards/l/LutriTheSpellchaser.java index 818845b3be4..ee82afe44b8 100644 --- a/Mage.Sets/src/mage/cards/l/LutriTheSpellchaser.java +++ b/Mage.Sets/src/mage/cards/l/LutriTheSpellchaser.java @@ -1,7 +1,6 @@ package mage.cards.l; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromEverywhereSourceCondition; @@ -20,11 +19,11 @@ import mage.constants.TargetController; import mage.filter.FilterSpell; import mage.filter.common.FilterInstantOrSorcerySpell; import mage.target.TargetSpell; +import mage.util.CardUtil; -import java.util.HashMap; -import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; /** * @author TheElk801 @@ -84,14 +83,10 @@ enum LutriTheSpellchaserCompanionCondition implements CompanionCondition { @Override public boolean isLegal(Set deck, int minimumDeckSize) { - Map cardMap = new HashMap<>(); - deck.stream() + Set cards = deck + .stream() .filter(card -> !card.hasCardTypeForDeckbuilding(CardType.LAND)) - .map(MageObject::getName) - .forEach(s -> { - cardMap.putIfAbsent(s, 0); - cardMap.compute(s, (str, i) -> i + 1); - }); - return cardMap.values().stream().noneMatch(i -> i > 1); + .collect(Collectors.toSet()); + return cards.size() == CardUtil.differentlyNamedAmongCollection(cards, null); } } diff --git a/Mage.Sets/src/mage/cards/m/MechanizedProduction.java b/Mage.Sets/src/mage/cards/m/MechanizedProduction.java index a0adaa6e826..866bacda07e 100644 --- a/Mage.Sets/src/mage/cards/m/MechanizedProduction.java +++ b/Mage.Sets/src/mage/cards/m/MechanizedProduction.java @@ -2,8 +2,12 @@ package mage.cards.m; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.WinGameSourceControllerEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -11,19 +15,16 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.TargetController; -import mage.filter.common.FilterArtifactPermanent; -import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.Token; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetControlledPermanent; -import mage.util.functions.CopyTokenFunction; +import mage.util.RandomUtil; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; import java.util.UUID; /** @@ -37,14 +38,20 @@ public final class MechanizedProduction extends CardImpl { this.subtype.add(SubType.AURA); // Enchant artifact you control - TargetPermanent auraTarget = new TargetControlledPermanent(new FilterControlledArtifactPermanent()); + TargetPermanent auraTarget = new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Copy)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // At the beginning of your upkeep, create a token that's a copy of enchanted artifact. Then if you control eight or more artifacts with the same name as one another, you win the game. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new MechanizedProductionEffect(), TargetController.YOU, false)); + Ability ability = new BeginningOfUpkeepTriggeredAbility( + new MechanizedProductionEffect(), TargetController.YOU, false + ); + ability.addEffect(new ConditionalOneShotEffect( + new WinGameSourceControllerEffect(), MechanizedProductionCondition.instance, + "Then if you control eight or more artifacts with the same name as one another, you win the game" + )); + this.addAbility(ability); } private MechanizedProduction(final MechanizedProduction card) { @@ -61,7 +68,7 @@ class MechanizedProductionEffect extends OneShotEffect { MechanizedProductionEffect() { super(Outcome.Benefit); - this.staticText = "create a token that's a copy of enchanted artifact. Then if you control eight or more artifacts with the same name as one another, you win the game"; + this.staticText = "create a token that's a copy of enchanted artifact"; } private MechanizedProductionEffect(final MechanizedProductionEffect effect) { @@ -75,30 +82,40 @@ class MechanizedProductionEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent sourceObject = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (sourceObject != null && sourceObject.getAttachedTo() != null) { - Permanent enchantedArtifact = game.getPermanentOrLKIBattlefield(sourceObject.getAttachedTo()); - if (enchantedArtifact != null) { - Token token = CopyTokenFunction.createTokenCopy(enchantedArtifact, game); - token.putOntoBattlefield(1, game, source, source.getControllerId()); - } - Map countNames = new HashMap<>(); - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterArtifactPermanent(), source.getControllerId(), game)) { - int counter = countNames.getOrDefault(permanent.getName(), 0); - countNames.put(permanent.getName(), counter + 1); - } - for (Entry entry : countNames.entrySet()) { - if (entry.getValue() > 7) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - game.informPlayers(controller.getLogName() + " controls eight or more artifacts with the same name as one another (" + entry.getKey() + ")."); - controller.won(game); - return true; - } + Permanent enchantedArtifact = Optional + .ofNullable(source.getSourcePermanentOrLKI(game)) + .map(Permanent::getAttachedTo) + .map(game::getPermanentOrLKIBattlefield) + .orElse(null); + return enchantedArtifact != null + && new CreateTokenCopyTargetEffect() + .setSavedPermanent(enchantedArtifact) + .apply(game, source); + } +} + +enum MechanizedProductionCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Set artifacts = new HashSet<>(game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, + source.getControllerId(), source, game + )); + while (artifacts.size() >= 8) { + Permanent artifact = RandomUtil.randomFromCollection(artifacts); + artifacts.remove(artifact); + int amount = 0; + for (Permanent permanent : artifacts) { + if (!permanent.sharesName(artifact, game)) { + continue; + } + amount++; + if (amount >= 8) { + return true; } } - return true; - } return false; } diff --git a/Mage.Sets/src/mage/cards/m/MedomaisProphecy.java b/Mage.Sets/src/mage/cards/m/MedomaisProphecy.java index 1b68bfba3fa..4575175b127 100644 --- a/Mage.Sets/src/mage/cards/m/MedomaisProphecy.java +++ b/Mage.Sets/src/mage/cards/m/MedomaisProphecy.java @@ -108,7 +108,7 @@ class MedomaisProphecyDelayedTriggeredAbility extends DelayedTriggeredAbility { return false; } Spell spell = game.getStack().getSpell(event.getTargetId()); - return spell != null && spellName.equals(spell.getName()); + return spell != null && spell.hasName(spellName, game); } @Override diff --git a/Mage.Sets/src/mage/cards/m/MirrorMadPhantasm.java b/Mage.Sets/src/mage/cards/m/MirrorMadPhantasm.java index a70b07a6856..6fc68e80e4d 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorMadPhantasm.java +++ b/Mage.Sets/src/mage/cards/m/MirrorMadPhantasm.java @@ -1,18 +1,13 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; @@ -21,8 +16,9 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class MirrorMadPhantasm extends CardImpl { @@ -74,7 +70,7 @@ class MirrorMadPhantasmEffect extends OneShotEffect { Card phantasmCard = null; for (Card card : owner.getLibrary().getCards(game)) { cards.add(card); - if (card.getName().equals("Mirror-Mad Phantasm")) { + if (card.hasName("Mirror-Mad Phantasm", game)) { phantasmCard = card; break; } diff --git a/Mage.Sets/src/mage/cards/p/PatternMatcher.java b/Mage.Sets/src/mage/cards/p/PatternMatcher.java index f5ec6f0019b..de788f71e54 100644 --- a/Mage.Sets/src/mage/cards/p/PatternMatcher.java +++ b/Mage.Sets/src/mage/cards/p/PatternMatcher.java @@ -1,34 +1,34 @@ package mage.cards.p; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCardInLibrary; -import java.util.List; -import java.util.Objects; import java.util.UUID; -import java.util.stream.Collectors; /** * @author TheElk801 */ public final class PatternMatcher extends CardImpl { + private static final FilterCard filter = new FilterCreatureCard("a creature card with the same name as another creature you control"); + + static { + filter.add(RegularExpression.instance); + } + public PatternMatcher(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); @@ -37,7 +37,9 @@ public final class PatternMatcher extends CardImpl { this.toughness = new MageInt(3); // When Pattern Matcher enters the battlefield, you may search your library for a creature card with the same name as another creature you control, reveal it, put it into your hand, then shuffle your library. - this.addAbility(new EntersBattlefieldTriggeredAbility(new RegularExpression(), true)); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true), true + )); } private PatternMatcher(final PatternMatcher card) { @@ -50,45 +52,19 @@ public final class PatternMatcher extends CardImpl { } } -class RegularExpression extends OneShotEffect { - - RegularExpression() { - super(Outcome.Benefit); - staticText = "search your library for a card with the same name as another creature you control, " + - "reveal it, put it into your hand, then shuffle."; - } - - private RegularExpression(final RegularExpression effect) { - super(effect); - } +enum RegularExpression implements ObjectSourcePlayerPredicate { + instance; @Override - public RegularExpression copy() { - return new RegularExpression(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return false; - } - List predicates = game + public boolean apply(ObjectSourcePlayer input, Game game) { + return game + .getState() .getBattlefield() .getActivePermanents( - StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, - source.getControllerId(), source, game - ).stream() - .map(Permanent::getName) - .filter(Objects::nonNull) - .filter(s -> !s.isEmpty()) - .map(NamePredicate::new) - .collect(Collectors.toList()); - FilterCard filter - = new FilterCard("a creature card with the same name as another creature you control"); - filter.add(Predicates.or(predicates)); - return new SearchLibraryPutInHandEffect( - new TargetCardInLibrary(filter), true - ).apply(game, source); + StaticFilters.FILTER_OTHER_CONTROLLED_CREATURES, + input.getPlayerId(), input.getSource(), game + ) + .stream() + .anyMatch(permanent -> permanent.sharesName(input.getObject(), game)); } } diff --git a/Mage.Sets/src/mage/cards/r/ReapIntellect.java b/Mage.Sets/src/mage/cards/r/ReapIntellect.java index 1e9f975dbfc..24e10525b57 100644 --- a/Mage.Sets/src/mage/cards/r/ReapIntellect.java +++ b/Mage.Sets/src/mage/cards/r/ReapIntellect.java @@ -1,26 +1,29 @@ package mage.cards.r; -import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.predicate.mageobject.SharesNamePredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCardInHand; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetOpponent; -import mage.util.CardUtil; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; /** * @author jeffwadsworth @@ -36,7 +39,6 @@ public final class ReapIntellect extends CardImpl { // same name as that card and exile them. Then that player shuffles their library. this.getSpellAbility().addEffect(new ReapIntellectEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); - } private ReapIntellect(final ReapIntellect card) { @@ -51,12 +53,6 @@ public final class ReapIntellect extends CardImpl { class ReapIntellectEffect extends OneShotEffect { - private static final FilterCard filterNonLands = new FilterCard("up to X nonland cards"); - - static { - filterNonLands.add(Predicates.not(CardType.LAND.getPredicate())); - } - public ReapIntellectEffect() { super(Outcome.Exile); staticText = "Target opponent reveals their hand. You choose up to X " @@ -72,82 +68,51 @@ class ReapIntellectEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player targetPlayer = game.getPlayer(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source); - if (targetPlayer != null && sourceObject != null && controller != null) { - - // reveal hand of target player - targetPlayer.revealCards(sourceObject.getName(), targetPlayer.getHand(), game); - - // Chose cards to exile from hand - Cards exiledCards = new CardsImpl(); - int xCost = Math.min(CardUtil.getSourceCostsTag(game, source, "X", 0), targetPlayer.getHand().size()); - TargetCard target = new TargetCard(0, xCost, Zone.HAND, filterNonLands); - target.withNotTarget(true); - controller.chooseTarget(Outcome.Benefit, targetPlayer.getHand(), target, source, game); - for (UUID cardId : target.getTargets()) { - Card chosenCard = game.getCard(cardId); - if (chosenCard != null) { - controller.moveCardToExileWithInfo(chosenCard, null, "", source, game, Zone.HAND, true); - exiledCards.add(chosenCard); - } - } - // Exile other cards with the same name - // 4/15/2013 If you don't exile any cards from the player's hand, you don't search that player's library - if (!exiledCards.isEmpty()) { - - // Building a card filter with all names - List names = new ArrayList<>(); - FilterCard filterNamedCards = new FilterCard(); - for (Card card : exiledCards.getCards(game)) { - String nameToSearch = CardUtil.getCardNameForSameNameSearch(card); - if (exiledCards.size() == 1) { - filterNamedCards.add(new NamePredicate(nameToSearch)); - } else { - names.add(new NamePredicate(nameToSearch)); - } - } - if (exiledCards.size() > 1) { - filterNamedCards.add(Predicates.or(names)); - } - - // search cards in graveyard - TargetCardInGraveyard targetCardsGraveyard = new TargetCardInGraveyard(0, Integer.MAX_VALUE, filterNamedCards); - controller.chooseTarget(outcome, targetPlayer.getGraveyard(), targetCardsGraveyard, source, game); - for (UUID cardId : targetCardsGraveyard.getTargets()) { - Card card = game.getCard(cardId); - if (card != null) { - controller.moveCardToExileWithInfo(card, null, "", source, game, Zone.GRAVEYARD, true); - } - } - - // search cards in hand - TargetCard targetCardsHand = new TargetCard(0, Integer.MAX_VALUE, Zone.HAND, filterNamedCards); - controller.chooseTarget(Outcome.Benefit, targetPlayer.getGraveyard(), targetCardsHand, source, game); - for (UUID cardId : targetCardsHand.getTargets()) { - Card card = game.getCard(cardId); - if (card != null) { - controller.moveCardToExileWithInfo(card, null, "", source, game, Zone.HAND, true); - } - } - - // search cards in Library - TargetCardInLibrary targetCardsLibrary = new TargetCardInLibrary(0, Integer.MAX_VALUE, filterNamedCards); - controller.searchLibrary(targetCardsLibrary, source, game, targetPlayer.getId()); - for (UUID cardId : targetCardsLibrary.getTargets()) { - Card card = game.getCard(cardId); - if (card != null) { - controller.moveCardToExileWithInfo(card, null, "", source, game, Zone.LIBRARY, true); - } - } - - } - + Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (controller == null || targetPlayer == null) { + return false; + } + targetPlayer.revealCards(source, targetPlayer.getHand(), game); + TargetCard target = new TargetCardInHand( + 0, + GetXValue.instance.calculate(game, source, this), + StaticFilters.FILTER_CARDS_NON_LAND + ); + controller.chooseTarget(Outcome.Benefit, targetPlayer.getHand(), target, source, game); + Cards exiledCards = new CardsImpl(target.getTargets()); + controller.moveCards(exiledCards, Zone.EXILED, source, game); + exiledCards.retainZone(Zone.EXILED, game); + if (exiledCards.isEmpty()) { targetPlayer.shuffleLibrary(source, game); return true; } - return false; + FilterCard filterCard = new FilterCard("cards with the same name"); + filterCard.add(Predicates.or( + exiledCards + .getCards(game) + .stream() + .map(SharesNamePredicate::new) + .collect(Collectors.toSet()) + )); + exiledCards.clear(); + + TargetCardInGraveyard targetCardInGraveyard = new TargetCardInGraveyard(0, Integer.MAX_VALUE, filterCard, true); + controller.choose(Outcome.Exile, targetPlayer.getGraveyard(), targetCardInGraveyard, source, game); + exiledCards.addAll(targetCardInGraveyard.getTargets()); + + TargetCardInHand targetCardInHand = new TargetCardInHand(0, Integer.MAX_VALUE, filterCard); + controller.choose(Outcome.Exile, targetPlayer.getHand(), targetCardInHand, source, game); + exiledCards.addAll(targetCardInHand.getTargets()); + + TargetCardInLibrary targetCardInLibrary = new TargetCardInLibrary(0, Integer.MAX_VALUE, filterCard); + controller.searchLibrary(targetCardInLibrary, source, game, targetPlayer.getId()); + for (UUID cardId : targetCardInLibrary.getTargets()) { + exiledCards.add(targetPlayer.getLibrary().getCard(cardId, game)); + } + + targetPlayer.shuffleLibrary(source, game); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java b/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java index fa0f4be0b4f..612f6ce348f 100644 --- a/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java +++ b/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java @@ -1,35 +1,35 @@ package mage.cards.s; -import java.util.ArrayList; import mage.MageInt; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.TapForManaAllTriggeredManaAbility; import mage.abilities.costs.common.RevealHandSourceControllerCost; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.FlipSourceEffect; import mage.abilities.effects.mana.ManaEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceColor; import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterLandCard; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.predicate.mageobject.SharesNamePredicate; import mage.filter.predicate.permanent.PermanentIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.TokenImpl; import mage.players.Player; +import java.util.ArrayList; import java.util.List; import java.util.UUID; -import mage.abilities.effects.Effect; -import mage.choices.Choice; -import mage.choices.ChoiceColor; /** * @author LevelX2 @@ -143,14 +143,14 @@ class SasayasEssenceManaEffect extends ManaEffect { if (controller != null && producedMana != null && permanent != null) { FilterPermanent filter = new FilterLandPermanent(); filter.add(Predicates.not(new PermanentIdPredicate(permanent.getId()))); - filter.add(new NamePredicate(permanent.getName())); + filter.add(new SharesNamePredicate(permanent)); int count = game.getBattlefield().countAll(filter, controller.getId(), game); if (count > 0) { - if (producedMana.getBlack() > 0) { - netMana.add(Mana.BlackMana(count)); + if (producedMana.getBlack() > 0) { + netMana.add(Mana.BlackMana(count)); } if (producedMana.getRed() > 0) { - netMana.add(Mana.RedMana(count)); + netMana.add(Mana.RedMana(count)); } if (producedMana.getBlue() > 0) { netMana.add(Mana.BlueMana(count)); @@ -163,7 +163,7 @@ class SasayasEssenceManaEffect extends ManaEffect { } if (producedMana.getColorless() > 0) { netMana.add(Mana.ColorlessMana(count)); - } + } } } return netMana; @@ -174,12 +174,12 @@ class SasayasEssenceManaEffect extends ManaEffect { * RULINGS 6/1/2005 If Sasaya’s Essence’s controller has four Forests and * taps one of them for Green, the Essence will add GreenGreenGreen to that * player’s mana pool for a total of GreenGreenGreenGreen. - * + *

* 6/1/2005 If Sasaya’s Essence’s controller has four Mossfire Valley and * taps one of them for RedGreen, the Essence will add three mana (one for * each other Mossfire Valley) of any combination of Red and/or Green to * that player’s mana pool. - * + *

* 6/1/2005 If Sasaya’s Essence’s controller has two Brushlands and taps one * of them for White, Sasaya’s Essence adds another White to that player’s * mana pool. It won’t produce Green or Colorless unless the land was tapped @@ -197,7 +197,7 @@ class SasayasEssenceManaEffect extends ManaEffect { if (controller != null && mana != null && permanent != null) { FilterPermanent filter = new FilterLandPermanent(); filter.add(Predicates.not(new PermanentIdPredicate(permanent.getId()))); - filter.add(new NamePredicate(permanent.getName())); + filter.add(new SharesNamePredicate(permanent)); int count = game.getBattlefield().countAll(filter, controller.getId(), game); if (count > 0) { Choice choice = new ChoiceColor(true); diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfTheChimes.java b/Mage.Sets/src/mage/cards/s/SphinxOfTheChimes.java index 3faa7800b31..dee6b632e2a 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxOfTheChimes.java +++ b/Mage.Sets/src/mage/cards/s/SphinxOfTheChimes.java @@ -18,9 +18,7 @@ import mage.util.CardUtil; import java.util.Set; import java.util.UUID; -import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * @author LevelX2 @@ -75,13 +73,10 @@ class SphinxOfTheChimesTarget extends TargetCardInHand { break; } Set set = CardUtil - .streamPairsWithMap( + .streamAllPairwiseMatches( possibleTargets, (u1, u2) -> game.getCard(u1).sharesName(game.getCard(u2), game) - ? Stream.of(u1, u2) - : Stream.empty() ) - .flatMap(Function.identity()) .collect(Collectors.toSet()); possibleTargets.clear(); possibleTargets.addAll(set); diff --git a/Mage.Sets/src/mage/cards/y/YidaroWanderingMonster.java b/Mage.Sets/src/mage/cards/y/YidaroWanderingMonster.java index 6706653e9a4..bec26df1d2e 100644 --- a/Mage.Sets/src/mage/cards/y/YidaroWanderingMonster.java +++ b/Mage.Sets/src/mage/cards/y/YidaroWanderingMonster.java @@ -155,7 +155,7 @@ class YidaroWanderingMonsterWatcher extends Watcher { return; } Card card = game.getCard(object.getSourceId()); - if (card != null && "Yidaro, Wandering Monster".equals(card.getName())) { + if (card != null && card.hasName("Yidaro, Wandering Monster", game)) { countMap.merge(object.getControllerId(), 1, Integer::sum); } } diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 8c122146d71..92a1f4d5093 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -37,7 +37,7 @@ import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.predicate.mageobject.SharesNamePredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.permanent.LegendRuleAppliesPredicate; import mage.game.combat.Combat; @@ -2869,7 +2869,7 @@ public abstract class GameImpl implements Game { for (Permanent legend : legendary) { FilterPermanent filterLegendName = new FilterPermanent(); filterLegendName.add(SuperType.LEGENDARY.getPredicate()); - filterLegendName.add(new NamePredicate(legend.getName())); + filterLegendName.add(new SharesNamePredicate(legend)); filterLegendName.add(new ControllerIdPredicate(legend.getControllerId())); filterLegendName.add(LegendRuleAppliesPredicate.instance); if (!getBattlefield().contains(filterLegendName, legend.getControllerId(), null, this, 2)) { diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 829ef95a1ec..bc67863096f 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -58,6 +58,7 @@ import java.text.SimpleDateFormat; import java.util.*; import java.util.function.BiFunction; import java.util.function.BiPredicate; +import java.util.function.Function; import java.util.function.ToIntFunction; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -2169,6 +2170,19 @@ public final class CardUtil { return stream.filter(clazz::isInstance).map(clazz::cast).filter(Objects::nonNull); } + public static boolean checkAnyPairs(Collection collection, BiPredicate predicate) { + return streamPairsWithMap(collection, (t1, t2) -> predicate.test(t1, t2)).anyMatch(x -> x); + } + + public static Stream streamAllPairwiseMatches(Collection collection, BiPredicate predicate) { + return streamPairsWithMap( + collection, + (t1, t2) -> predicate.test(t1, t2) + ? Stream.of(t1, t2) + : Stream.empty() + ).flatMap(Function.identity()).distinct(); + } + private static class IntPairIterator implements Iterator> { private final int amount; private int firstCounter = 0; @@ -2201,13 +2215,7 @@ public final class CardUtil { } } - public static boolean checkAnyPairs(Collection collection, BiPredicate predicate) { - return streamPairsWithMap(collection, (t1, t2) -> predicate.test(t1, t2)).anyMatch(x -> x); - } - - public static Stream streamPairsWithMap( - Collection collection, - BiFunction function) { + public static Stream streamPairsWithMap(Collection collection, BiFunction function) { if (collection.size() < 2) { return Stream.empty(); }