From 2a44f913f950509b7ab748ff96b3c0f0539275e6 Mon Sep 17 00:00:00 2001 From: Matthew Wilson Date: Mon, 29 Jan 2024 11:54:23 +0200 Subject: [PATCH 1/3] [MKM] Implement Case of the Pilfered Proof --- .../mage/cards/c/CaseOfThePilferedProof.java | 175 ++++++++++++++++++ .../src/mage/sets/MurdersAtKarlovManor.java | 1 + .../mage/abilities/common/CaseAbility.java | 4 +- 3 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/cards/c/CaseOfThePilferedProof.java diff --git a/Mage.Sets/src/mage/cards/c/CaseOfThePilferedProof.java b/Mage.Sets/src/mage/cards/c/CaseOfThePilferedProof.java new file mode 100644 index 00000000000..936dbbcd0d1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CaseOfThePilferedProof.java @@ -0,0 +1,175 @@ +package mage.cards.c; + +import java.util.Map; +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.CaseAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.condition.common.SolvedSourceCondition; +import mage.abilities.decorator.ConditionalReplacementEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.hint.common.CaseSolvedHint; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.events.CreateTokenEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.ClueArtifactToken; +import mage.game.permanent.token.Token; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author DominionSpy + */ +public final class CaseOfThePilferedProof extends CardImpl { + + public CaseOfThePilferedProof(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + + this.subtype.add(SubType.CASE); + + // Whenever a Detective enters the battlefield under your control and whenever a Detective you control is turned face up, put a +1/+1 counter on it. + Ability initialAbility = new CaseOfThePilferedProofTriggeredAbility(); + // To solve -- You control three or more Detectives. + Condition toSolveCondition = new PermanentsOnTheBattlefieldCondition( + new FilterCreaturePermanent(SubType.DETECTIVE, "You control three or more Detectives"), + ComparisonType.MORE_THAN, 2, true); + // Solved -- If one or more tokens would be created under your control, those tokens plus a Clue token are created instead. + Ability solvedAbility = new SimpleStaticAbility(new ConditionalReplacementEffect( + new CaseOfThePilferedProofReplacementEffect(), SolvedSourceCondition.SOLVED)); + + this.addAbility(new CaseAbility(initialAbility, toSolveCondition,solvedAbility) + .addHint(new CaseOfThePilferedProofHint(toSolveCondition))); + } + + private CaseOfThePilferedProof(final CaseOfThePilferedProof card) { + super(card); + } + + @Override + public CaseOfThePilferedProof copy() { + return new CaseOfThePilferedProof(this); + } +} + +class CaseOfThePilferedProofTriggeredAbility extends TriggeredAbilityImpl { + + CaseOfThePilferedProofTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.P1P1.createInstance()).setText("put a +1/+1 counter on it")); + this.setTriggerPhrase("Whenever a Detective enters the battlefield under your control and whenever a Detective you control is turned face up, "); + } + + private CaseOfThePilferedProofTriggeredAbility(final CaseOfThePilferedProofTriggeredAbility ability) { + super(ability); + } + + @Override + public CaseOfThePilferedProofTriggeredAbility copy() { + return new CaseOfThePilferedProofTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + switch (event.getType()) { + case ENTERS_THE_BATTLEFIELD: + case TURNED_FACE_UP: + return true; + } + return false; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD || + event.getType() == GameEvent.EventType.TURNED_FACE_UP) { + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent != null + && permanent.hasSubtype(SubType.DETECTIVE, game) + && permanent.isControlledBy(this.getControllerId())) { + for (Effect effect : getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); + } + return true; + } + } + return false; + } +} + +class CaseOfThePilferedProofReplacementEffect extends ReplacementEffectImpl { + + CaseOfThePilferedProofReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + this.staticText = "If one or more tokens would be created under your control, those tokens plus a Clue token are created instead"; + } + + private CaseOfThePilferedProofReplacementEffect(final CaseOfThePilferedProofReplacementEffect effect) { + super(effect); + } + + @Override + public CaseOfThePilferedProofReplacementEffect copy() { + return new CaseOfThePilferedProofReplacementEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CREATE_TOKEN; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + if (event instanceof CreateTokenEvent) { + CreateTokenEvent tokenEvent = (CreateTokenEvent) event; + Map tokens = tokenEvent.getTokens(); + tokens.put(new ClueArtifactToken(), 1); + } + return false; + } +} + +class CaseOfThePilferedProofHint extends CaseSolvedHint { + + CaseOfThePilferedProofHint(Condition condition) { + super(condition); + } + + private CaseOfThePilferedProofHint(final CaseOfThePilferedProofHint hint) { + super(hint); + } + + @Override + public CaseOfThePilferedProofHint copy() { + return new CaseOfThePilferedProofHint(this); + } + + @Override + public String getConditionText(Game game, Ability ability) { + int detectives = game.getBattlefield() + .count(new FilterControlledCreaturePermanent(SubType.DETECTIVE), + ability.getControllerId(), ability, game); + return "Detectives: " + detectives + " (need 3)."; + } +} diff --git a/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java b/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java index 87c602500bf..63444f323ae 100644 --- a/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java +++ b/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java @@ -51,6 +51,7 @@ public final class MurdersAtKarlovManor extends ExpansionSet { cards.add(new SetCardInfo("Case of the Crimson Pulse", 114, Rarity.RARE, mage.cards.c.CaseOfTheCrimsonPulse.class)); cards.add(new SetCardInfo("Case of the Filched Falcon", 44, Rarity.UNCOMMON, mage.cards.c.CaseOfTheFilchedFalcon.class)); cards.add(new SetCardInfo("Case of the Locked Hothouse", 155, Rarity.RARE, mage.cards.c.CaseOfTheLockedHothouse.class)); + cards.add(new SetCardInfo("Case of the Pilfered Proof", 9, Rarity.UNCOMMON, mage.cards.c.CaseOfThePilferedProof.class)); cards.add(new SetCardInfo("Caught Red-Handed", 115, Rarity.UNCOMMON, mage.cards.c.CaughtRedHanded.class)); cards.add(new SetCardInfo("Cease // Desist", 246, Rarity.UNCOMMON, mage.cards.c.CeaseDesist.class)); cards.add(new SetCardInfo("Cerebral Confiscation", 81, Rarity.COMMON, mage.cards.c.CerebralConfiscation.class)); diff --git a/Mage/src/main/java/mage/abilities/common/CaseAbility.java b/Mage/src/main/java/mage/abilities/common/CaseAbility.java index d47e32e4803..8ec1b5f3389 100644 --- a/Mage/src/main/java/mage/abilities/common/CaseAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CaseAbility.java @@ -7,6 +7,7 @@ import mage.abilities.condition.common.SolvedSourceCondition; import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.decorator.ConditionalAsThoughEffect; import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalReplacementEffect; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -85,7 +86,8 @@ public class CaseAbility extends SimpleStaticAbility { if (solvedAbility instanceof SimpleStaticAbility) { for (Effect effect : solvedAbility.getEffects()) { if (!(effect instanceof ConditionalContinuousEffect || - effect instanceof ConditionalAsThoughEffect)) { + effect instanceof ConditionalAsThoughEffect || + effect instanceof ConditionalReplacementEffect)) { throw new IllegalArgumentException("solvedAbility must be one of ConditionalActivatedAbility, " + "ConditionalTriggeredAbility, or StaticAbility with conditional effects."); } From e17f423c412e60a757bf53e5b9edb97f1af34616 Mon Sep 17 00:00:00 2001 From: Matthew Wilson Date: Tue, 30 Jan 2024 13:30:45 +0200 Subject: [PATCH 2/3] Address PR comments --- .../mage/cards/c/CaseOfThePilferedProof.java | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CaseOfThePilferedProof.java b/Mage.Sets/src/mage/cards/c/CaseOfThePilferedProof.java index 936dbbcd0d1..f8504b53ac2 100644 --- a/Mage.Sets/src/mage/cards/c/CaseOfThePilferedProof.java +++ b/Mage.Sets/src/mage/cards/c/CaseOfThePilferedProof.java @@ -11,7 +11,6 @@ import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.condition.common.SolvedSourceCondition; import mage.abilities.decorator.ConditionalReplacementEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.hint.common.CaseSolvedHint; @@ -97,17 +96,12 @@ class CaseOfThePilferedProofTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD || - event.getType() == GameEvent.EventType.TURNED_FACE_UP) { - Permanent permanent = game.getPermanent(event.getSourceId()); - if (permanent != null - && permanent.hasSubtype(SubType.DETECTIVE, game) - && permanent.isControlledBy(this.getControllerId())) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); - } - return true; - } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null + && permanent.hasSubtype(SubType.DETECTIVE, game) + && permanent.isControlledBy(this.getControllerId())) { + getEffects().setTargetPointer(new FixedTarget(permanent, game)); + return true; } return false; } From 430d50af636ff6cb5f64cc2b75ad1ab1a27228d0 Mon Sep 17 00:00:00 2001 From: Matthew Wilson Date: Thu, 1 Feb 2024 13:32:01 +0200 Subject: [PATCH 3/3] Address PR comments --- .../src/mage/cards/c/CaseOfThePilferedProof.java | 11 ++++++++++- .../main/java/mage/abilities/common/CaseAbility.java | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CaseOfThePilferedProof.java b/Mage.Sets/src/mage/cards/c/CaseOfThePilferedProof.java index f8504b53ac2..bbaf413f149 100644 --- a/Mage.Sets/src/mage/cards/c/CaseOfThePilferedProof.java +++ b/Mage.Sets/src/mage/cards/c/CaseOfThePilferedProof.java @@ -137,8 +137,17 @@ class CaseOfThePilferedProofReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { if (event instanceof CreateTokenEvent) { CreateTokenEvent tokenEvent = (CreateTokenEvent) event; + ClueArtifactToken clueToken = null; Map tokens = tokenEvent.getTokens(); - tokens.put(new ClueArtifactToken(), 1); + for (Map.Entry entry : tokens.entrySet()) { + if (entry.getKey() instanceof ClueArtifactToken) { + clueToken = (ClueArtifactToken) entry.getKey(); + } + } + if (clueToken == null) { + clueToken = new ClueArtifactToken(); + } + tokens.put(clueToken, tokens.getOrDefault(clueToken, 0) + 1); } return false; } diff --git a/Mage/src/main/java/mage/abilities/common/CaseAbility.java b/Mage/src/main/java/mage/abilities/common/CaseAbility.java index 8ec1b5f3389..929853b83ac 100644 --- a/Mage/src/main/java/mage/abilities/common/CaseAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CaseAbility.java @@ -88,12 +88,12 @@ public class CaseAbility extends SimpleStaticAbility { if (!(effect instanceof ConditionalContinuousEffect || effect instanceof ConditionalAsThoughEffect || effect instanceof ConditionalReplacementEffect)) { - throw new IllegalArgumentException("solvedAbility must be one of ConditionalActivatedAbility, " + + throw new IllegalArgumentException("Wrong code usage: solvedAbility must be one of ConditionalActivatedAbility, " + "ConditionalTriggeredAbility, or StaticAbility with conditional effects."); } } } else { - throw new IllegalArgumentException("solvedAbility must be one of ConditionalActivatedAbility, " + + throw new IllegalArgumentException("Wrong code usage: solvedAbility must be one of ConditionalActivatedAbility, " + "ConditionalTriggeredAbility, or StaticAbility with conditional effects."); } }