From 37cffc389517410bef80d6558d13949e366c2f79 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Thu, 4 Sep 2025 13:18:52 -0500 Subject: [PATCH] [SPM] implement Arachne, Psionic Weaver --- .../mage/cards/a/ArachnePsionicWeaver.java | 70 +++++++++++++++++++ Mage.Sets/src/mage/sets/MarvelsSpiderMan.java | 1 + .../single/spm/ArachnePsionicWeaverTest.java | 53 ++++++++++++++ .../mageobject/ChosenCardTypePredicate.java | 36 ++++++++++ 4 files changed, 160 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/a/ArachnePsionicWeaver.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ArachnePsionicWeaverTest.java create mode 100644 Mage/src/main/java/mage/filter/predicate/mageobject/ChosenCardTypePredicate.java diff --git a/Mage.Sets/src/mage/cards/a/ArachnePsionicWeaver.java b/Mage.Sets/src/mage/cards/a/ArachnePsionicWeaver.java new file mode 100644 index 00000000000..d80351d2164 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArachnePsionicWeaver.java @@ -0,0 +1,70 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.ChooseCardTypeEffect; +import mage.abilities.effects.common.LookAtTargetPlayerHandEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; +import mage.abilities.keyword.WebSlingingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ChosenCardTypePredicate; +import mage.target.common.TargetOpponent; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * + * @author Jmlundeen + */ +public final class ArachnePsionicWeaver extends CardImpl { + + private static final FilterCard filter = new FilterCard("spells of the chosen type"); + + static { + filter.add(ChosenCardTypePredicate.TRUE); + } + + public ArachnePsionicWeaver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Web-slinging {W} + this.addAbility(new WebSlingingAbility(this, "{W}")); + + // As Arachne enters, look at target opponent's hand, then choose a noncreature card type. + List types = Arrays.stream(CardType.values()).filter(cardType -> cardType != CardType.CREATURE) + .collect(Collectors.toList()); + Ability ability = new AsEntersBattlefieldAbility(new LookAtTargetPlayerHandEffect()); + ability.addEffect(new ChooseCardTypeEffect(Outcome.Benefit, types) + .setText("choose a noncreature card type") + .concatBy(", then")); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + + // Spells of the chosen type cost {1} more to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY))); + } + + private ArachnePsionicWeaver(final ArachnePsionicWeaver card) { + super(card); + } + + @Override + public ArachnePsionicWeaver copy() { + return new ArachnePsionicWeaver(this); + } +} diff --git a/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java b/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java index c0d9be25cb1..a224c6b4f2d 100644 --- a/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java @@ -30,6 +30,7 @@ public final class MarvelsSpiderMan extends ExpansionSet { cards.add(new SetCardInfo("Angry Rabble", 75, Rarity.COMMON, mage.cards.a.AngryRabble.class)); cards.add(new SetCardInfo("Anti-Venom, Horrifying Healer", 1, Rarity.MYTHIC, mage.cards.a.AntiVenomHorrifyingHealer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Anti-Venom, Horrifying Healer", 244, Rarity.MYTHIC, mage.cards.a.AntiVenomHorrifyingHealer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arachne, Psionic Weaver", 2, Rarity.RARE, mage.cards.a.ArachnePsionicWeaver.class)); cards.add(new SetCardInfo("Arana, Heart of the Spider", 123, Rarity.RARE, mage.cards.a.AranaHeartOfTheSpider.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arana, Heart of the Spider", 213, Rarity.RARE, mage.cards.a.AranaHeartOfTheSpider.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aunt May", 3, Rarity.UNCOMMON, mage.cards.a.AuntMay.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ArachnePsionicWeaverTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ArachnePsionicWeaverTest.java new file mode 100644 index 00000000000..a210814861c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ArachnePsionicWeaverTest.java @@ -0,0 +1,53 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class ArachnePsionicWeaverTest extends CardTestPlayerBase { + + /* + Arachne, Psionic Weaver + {2}{W} + Legendary Creature - Spider Human Hero + Web-slinging {W} + As Arachne enters, look at target opponent's hand, then choose a noncreature card type. + Spells of the chosen type cost {1} more to cast. + 3/3 + */ + private static final String arachnePsionicWeaver = "Arachne, Psionic Weaver"; + + /* + Tormod's Crypt + {0} + Artifact + {tap}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard. + */ + private static final String tormodsCrypt = "Tormod's Crypt"; + + @Test + public void testArachnePsionicWeaver() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, arachnePsionicWeaver); + addCard(Zone.HAND, playerA, tormodsCrypt); + addCard(Zone.HAND, playerB, tormodsCrypt); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerB, "Plains"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, arachnePsionicWeaver); + setChoice(playerA, CardType.ARTIFACT.toString()); + + checkPlayableAbility("Player A can't cast Tormod's", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast " + tormodsCrypt, false); + + checkPlayableAbility("Player B can cast Tormod's", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast " + tormodsCrypt, true); + setStopAt(2, PhaseStep.PRECOMBAT_MAIN); + execute(); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/ChosenCardTypePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/ChosenCardTypePredicate.java new file mode 100644 index 00000000000..ab1e4ab1ec1 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/ChosenCardTypePredicate.java @@ -0,0 +1,36 @@ +package mage.filter.predicate.mageobject; + +import mage.MageObject; +import mage.constants.CardType; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; + +/** + * + * @author jmlundeen + */ +public enum ChosenCardTypePredicate implements ObjectSourcePlayerPredicate { + TRUE(true), FALSE(false); + + private final boolean value; + + ChosenCardTypePredicate(boolean value) { + this.value = value; + } + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Object savedType = game.getState().getValue(input.getSourceId() + "_type"); + if (!(savedType instanceof String)) { + return false; + } + CardType cardType = CardType.fromString((String) savedType); + return input.getObject().getCardType(game).contains(cardType) == value; + } + + @Override + public String toString() { + return "Chosen card type"; + } +}