Implement Mayhem mechanic (#13877)

This commit is contained in:
Evan Kranzler 2025-07-27 07:12:11 -04:00 committed by GitHub
parent e8cd6dbdad
commit 1700180455
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 137 additions and 15 deletions

View file

@ -4,15 +4,11 @@ import mage.cards.ExpansionSet;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.Arrays;
import java.util.List;
/**
* @author TheElk801
*/
public final class MarvelsSpiderMan extends ExpansionSet {
private static final List<String> unfinished = Arrays.asList("Electro's Bolt", "Spider-Islanders");
private static final MarvelsSpiderMan instance = new MarvelsSpiderMan();
public static MarvelsSpiderMan getInstance() {
@ -98,7 +94,5 @@ public final class MarvelsSpiderMan extends ExpansionSet {
cards.add(new SetCardInfo("Web-Warriors", 203, Rarity.UNCOMMON, mage.cards.w.WebWarriors.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Whoosh!", 48, Rarity.COMMON, mage.cards.w.Whoosh.class));
cards.add(new SetCardInfo("Wild Pack Squad", 23, Rarity.COMMON, mage.cards.w.WildPackSquad.class));
cards.removeIf(setCardInfo -> unfinished.contains(setCardInfo.getName()));
}
}

View file

@ -0,0 +1,89 @@
package org.mage.test.cards.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author TheElk801
*/
public class MayhemTest extends CardTestPlayerBase {
private static final String islanders = "Spider-Islanders";
@Test
public void testCastRegular() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
addCard(Zone.HAND, playerA, islanders);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, islanders);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, islanders, 1);
}
private static final String imp = "Putrid Imp";
@Test
public void testCastDiscarded() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.BATTLEFIELD, playerA, imp);
addCard(Zone.HAND, playerA, islanders);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
setChoice(playerA, islanders);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, islanders + " with Mayhem");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, islanders, 1);
}
@Test
public void testCantCastGraveyard() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.GRAVEYARD, playerA, islanders);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, islanders + " with Mayhem");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
try {
execute();
} catch (Throwable e) {
Assert.assertEquals(
"Should fail to be able to cast " + islanders + " with mayhem as it wasn't discarded this turn",
"Can't find ability to activate command: Cast " + islanders + " with Mayhem", e.getMessage()
);
}
}
@Test
public void testCantCastDiscardedPreviously() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.BATTLEFIELD, playerA, imp);
addCard(Zone.HAND, playerA, islanders);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
setChoice(playerA, islanders);
castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, islanders + " with Mayhem");
setStrictChooseMode(true);
setStopAt(3, PhaseStep.END_TURN);
try {
execute();
} catch (Throwable e) {
Assert.assertEquals(
"Should fail to be able to cast " + islanders + " with mayhem as it wasn't discarded this turn",
"Can't find ability to activate command: Cast " + islanders + " with Mayhem", e.getMessage()
);
}
}
}

View file

@ -1,16 +1,19 @@
package mage.abilities.keyword;
import mage.ApprovingObject;
import mage.MageIdentifier;
import mage.MageObjectReference;
import mage.abilities.SpellAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.cards.Card;
import mage.constants.SpellAbilityType;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.watchers.common.CastSpellLastTurnWatcher;
import mage.game.events.DiscardedCardsEvent;
import mage.game.events.GameEvent;
import mage.watchers.Watcher;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@ -33,9 +36,9 @@ public class MayhemAbility extends SpellAbility {
this.clearManaCosts();
this.clearManaCostsToPay();
this.addCost(new ManaCostsImpl<>(manaString));
this.addWatcher(new MayhemWatcher());
this.setRuleAtTheTop(true);
this.rule = "Surge " + manaString +
this.rule = "Mayhem " + manaString +
" <i>(You may cast this card from your graveyard for " + manaString +
" if you discarded it this turn. Timing rules still apply.)</i>";
}
@ -47,7 +50,10 @@ public class MayhemAbility extends SpellAbility {
@Override
public ActivationStatus canActivate(UUID playerId, Game game) {
// TODO: Implement this
if (!Zone.GRAVEYARD.match(game.getState().getZone(getSourceId()))
|| !MayhemWatcher.checkCard(getSourceId(), game)) {
return ActivationStatus.getFalse();
}
return super.canActivate(playerId, game);
}
@ -69,5 +75,38 @@ public class MayhemAbility extends SpellAbility {
public String getRule() {
return rule;
}
}
class MayhemWatcher extends Watcher {
private final Set<MageObjectReference> set = new HashSet<>();
MayhemWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() != GameEvent.EventType.DISCARDED_CARDS) {
return;
}
for (Card card : ((DiscardedCardsEvent) event).getDiscardedCards().getCards(game)) {
set.add(new MageObjectReference(card, game));
}
}
@Override
public void reset() {
super.reset();
set.clear();
}
static boolean checkCard(UUID cardId, Game game) {
return game
.getState()
.getWatcher(MayhemWatcher.class)
.set
.stream()
.anyMatch(mor -> mor.refersTo(cardId, game));
}
}