From f0740c6f099e3238c34d551d130d5157223dc895 Mon Sep 17 00:00:00 2001 From: ssk97 Date: Mon, 1 Apr 2024 21:43:35 -0700 Subject: [PATCH] implement [MKM] Agency Outfitter (#12034) * Fix move cards log messages * Sludge Titan: Only track milled cards in public zones --- .../src/mage/cards/a/AgencyOutfitter.java | 135 ++++++++++++++++++ Mage.Sets/src/mage/cards/s/SludgeTitan.java | 5 +- .../src/mage/sets/MurdersAtKarlovManor.java | 1 + .../main/java/mage/players/PlayerImpl.java | 1 + ...raveyard.java => TargetCardAndOrCard.java} | 21 ++- .../common/TargetCardAndOrCardInLibrary.java | 5 + 6 files changed, 159 insertions(+), 9 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/a/AgencyOutfitter.java rename Mage/src/main/java/mage/target/common/{TargetCardAndOrCardInGraveyard.java => TargetCardAndOrCard.java} (81%) diff --git a/Mage.Sets/src/mage/cards/a/AgencyOutfitter.java b/Mage.Sets/src/mage/cards/a/AgencyOutfitter.java new file mode 100644 index 00000000000..e6d1980d5b2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AgencyOutfitter.java @@ -0,0 +1,135 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardAndOrCard; +import mage.target.common.TargetCardAndOrCardInLibrary; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author notgreat + */ +public final class AgencyOutfitter extends CardImpl { + + public AgencyOutfitter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); + this.subtype.add(SubType.SPHINX); + this.subtype.add(SubType.DETECTIVE); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Agency Outfitter enters the battlefield, you may search your graveyard, hand, and/or library for a card named Magnifying Glass and/or a card named Thinking Cap and put them onto the battlefield. If you search your library this way, shuffle. + this.addAbility(new EntersBattlefieldTriggeredAbility(new AgencyOutfitterEffect(), true)); + } + + private AgencyOutfitter(final AgencyOutfitter card) { + super(card); + } + + @Override + public AgencyOutfitter copy() { + return new AgencyOutfitter(this); + } +} + +class AgencyOutfitterEffect extends OneShotEffect { + private static final String glassName = "Magnifying Glass"; + private static final String capName = "Thinking Cap"; + + AgencyOutfitterEffect() { + super(Outcome.UnboostCreature); + this.staticText = "you may search your graveyard, hand, and/or library for a card named Magnifying Glass and/or a card named Thinking Cap and put them onto the battlefield. If you search your library this way, shuffle."; + } + + private AgencyOutfitterEffect(final AgencyOutfitterEffect effect) { + super(effect); + } + + @Override + public AgencyOutfitterEffect copy() { + return new AgencyOutfitterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Card glassCard = null; + Card capCard = null; + if (controller.chooseUse(Outcome.Neutral, "Search your library?", source, game)) { + TargetCardAndOrCardInLibrary libraryTarget = new TargetCardAndOrCardInLibrary(glassName, capName); + if (controller.searchLibrary(libraryTarget, source, game)) { + for (UUID id : libraryTarget.getTargets()) { + Card card = game.getCard(id); + if (card != null) { + if (CardUtil.haveSameNames(card, glassName, game)) { + glassCard = card; + } else if (CardUtil.haveSameNames(card, capName, game)) { + capCard = card; + } + } + } + } + controller.shuffleLibrary(source, game); + } + + if (glassCard == null || capCard == null) { + FilterCard filter; + TargetCard target; + if (glassCard == null && capCard == null) { + target = new TargetCardAndOrCard(glassName, capName); + filter = target.getFilter(); + } else { + String name = (glassCard == null ? glassName : capName); + filter = new FilterCard(); + filter.add(new NamePredicate(name)); + target = new TargetCard(0, 1, Zone.ALL, filter); + } + target.withNotTarget(true); + Cards cards = new CardsImpl(); + cards.addAllCards(controller.getHand().getCards(filter, source.getControllerId(), source, game)); + cards.addAllCards(controller.getGraveyard().getCards(filter, source.getControllerId(), source, game)); + if (!cards.isEmpty()) { + controller.choose(outcome, cards, target, source, game); + for (UUID id : target.getTargets()) { + Card card = game.getCard(id); + if (card != null) { + if (CardUtil.haveSameNames(card, glassName, game)) { + glassCard = card; + } else if (CardUtil.haveSameNames(card, capName, game)) { + capCard = card; + } + } + } + } + } + + Cards foundCards = new CardsImpl(); + foundCards.add(glassCard); + foundCards.add(capCard); + if (!foundCards.isEmpty()) { + controller.moveCards(foundCards, Zone.BATTLEFIELD, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SludgeTitan.java b/Mage.Sets/src/mage/cards/s/SludgeTitan.java index 6ad9e7d196e..18e40158d75 100644 --- a/Mage.Sets/src/mage/cards/s/SludgeTitan.java +++ b/Mage.Sets/src/mage/cards/s/SludgeTitan.java @@ -16,7 +16,7 @@ import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; -import mage.target.common.TargetCardAndOrCardInGraveyard; +import mage.target.common.TargetCardAndOrCard; import java.util.UUID; @@ -75,8 +75,9 @@ class SludgeTitanEffect extends OneShotEffect { } Cards cards = controller.millCards(5, source, game); game.getState().processAction(game); + cards.removeIf(card -> !game.getState().getZone(card).isPublicZone()); if (!cards.isEmpty()) { - TargetCard target = new TargetCardAndOrCardInGraveyard(CardType.CREATURE, CardType.LAND); + TargetCard target = new TargetCardAndOrCard(CardType.CREATURE, CardType.LAND); controller.choose(Outcome.DrawCard, cards, target, source, game); Cards toHand = new CardsImpl(); toHand.addAll(target.getTargets()); diff --git a/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java b/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java index a220936d7e4..167b387aeaa 100644 --- a/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java +++ b/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java @@ -25,6 +25,7 @@ public final class MurdersAtKarlovManor extends ExpansionSet { cards.add(new SetCardInfo("Absolving Lammasu", 2, Rarity.UNCOMMON, mage.cards.a.AbsolvingLammasu.class)); cards.add(new SetCardInfo("Aftermath Analyst", 148, Rarity.UNCOMMON, mage.cards.a.AftermathAnalyst.class)); cards.add(new SetCardInfo("Agency Coroner", 75, Rarity.COMMON, mage.cards.a.AgencyCoroner.class)); + cards.add(new SetCardInfo("Agency Outfitter", 38, Rarity.UNCOMMON, mage.cards.a.AgencyOutfitter.class)); cards.add(new SetCardInfo("Agrus Kos, Spirit of Justice", 184, Rarity.MYTHIC, mage.cards.a.AgrusKosSpiritOfJustice.class)); cards.add(new SetCardInfo("Airtight Alibi", 149, Rarity.COMMON, mage.cards.a.AirtightAlibi.class)); cards.add(new SetCardInfo("Alley Assailant", 76, Rarity.COMMON, mage.cards.a.AlleyAssailant.class)); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index ea49f9e1350..967ddb6a9e6 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -4704,6 +4704,7 @@ public abstract class PlayerImpl implements Player, Serializable { successfulMovedCards.add(permanent); if (!game.isSimulation()) { Player eventPlayer = game.getPlayer(info.event.getPlayerId()); + fromZone = info.event.getFromZone(); if (eventPlayer != null && fromZone != null) { game.informPlayers(eventPlayer.getLogName() + " puts " + GameLog.getColoredObjectIdName(permanent) + " from " diff --git a/Mage/src/main/java/mage/target/common/TargetCardAndOrCardInGraveyard.java b/Mage/src/main/java/mage/target/common/TargetCardAndOrCard.java similarity index 81% rename from Mage/src/main/java/mage/target/common/TargetCardAndOrCardInGraveyard.java rename to Mage/src/main/java/mage/target/common/TargetCardAndOrCard.java index fee2478e044..ddbecf60876 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardAndOrCardInGraveyard.java +++ b/Mage/src/main/java/mage/target/common/TargetCardAndOrCard.java @@ -6,10 +6,13 @@ import mage.cards.Card; import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.CardType; +import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.NamePredicate; import mage.game.Game; +import mage.target.TargetCard; import mage.util.CardUtil; import java.util.HashSet; @@ -21,7 +24,7 @@ import java.util.UUID; *

* almost identical to {@link TargetCardAndOrCardInLibrary} */ -public class TargetCardAndOrCardInGraveyard extends TargetCardInGraveyard { +public class TargetCardAndOrCard extends TargetCard { private static FilterCard makeFilter(Predicate firstPredicate, Predicate secondPredicate, @@ -42,25 +45,29 @@ public class TargetCardAndOrCardInGraveyard extends TargetCardInGraveyard { /** * a [firstType] card and/or a [secondType] card */ - protected TargetCardAndOrCardInGraveyard(Predicate firstPredicate, Predicate secondPredicate, String filterText) { - super(0, 2, makeFilter(firstPredicate, secondPredicate, filterText)); + protected TargetCardAndOrCard(Predicate firstPredicate, Predicate secondPredicate, String filterText) { + super(0, 2, Zone.ALL, makeFilter(firstPredicate, secondPredicate, filterText)); this.assignment = new PredicateCardAssignment(firstPredicate, secondPredicate); } - public TargetCardAndOrCardInGraveyard(CardType firstType, CardType secondType) { + public TargetCardAndOrCard(String firstName, String secondName) { + this(new NamePredicate(firstName), new NamePredicate(secondName), "a card named " + firstName + " and/or a card named " + secondName); + } + + public TargetCardAndOrCard(CardType firstType, CardType secondType) { this(firstType.getPredicate(), secondType.getPredicate(), makeFilterText( CardUtil.getTextWithFirstCharLowerCase(firstType.toString()), CardUtil.getTextWithFirstCharLowerCase(secondType.toString()))); } - protected TargetCardAndOrCardInGraveyard(final TargetCardAndOrCardInGraveyard target) { + protected TargetCardAndOrCard(final TargetCardAndOrCard target) { super(target); this.assignment = target.assignment; } @Override - public TargetCardAndOrCardInGraveyard copy() { - return new TargetCardAndOrCardInGraveyard(this); + public TargetCardAndOrCard copy() { + return new TargetCardAndOrCard(this); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetCardAndOrCardInLibrary.java b/Mage/src/main/java/mage/target/common/TargetCardAndOrCardInLibrary.java index 58b1338e1ba..43ac05bde77 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardAndOrCardInLibrary.java +++ b/Mage/src/main/java/mage/target/common/TargetCardAndOrCardInLibrary.java @@ -10,6 +10,7 @@ import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.NamePredicate; import mage.game.Game; import mage.util.CardUtil; @@ -52,6 +53,10 @@ public class TargetCardAndOrCardInLibrary extends TargetCardInLibrary { CardUtil.getTextWithFirstCharLowerCase(secondType.toString()))); } + public TargetCardAndOrCardInLibrary(String firstName, String secondName) { + this(new NamePredicate(firstName), new NamePredicate(secondName), "a card named " + firstName + " and/or a card named " + secondName); + } + public TargetCardAndOrCardInLibrary(SubType firstType, SubType secondType) { this(firstType.getPredicate(), secondType.getPredicate(), makeFilterText(firstType.getDescription(), secondType.getDescription())); }