[OTC] Implement Cataclysmic Prospecting

This commit is contained in:
Susucre 2024-04-06 02:33:18 +02:00
parent 16b832198a
commit e217b3d0d8
4 changed files with 211 additions and 1 deletions

View file

@ -99,10 +99,11 @@ enum BatColonyValue implements DynamicValue {
/**
* Inspired by {@link mage.watchers.common.ManaPaidSourceWatcher}
* If more cards like Bat Colony care for mana spent by Caves in the future, best to refactor the tracking there.
* If more cards like Bat Colony care for mana produced by Caves in the future, best to refactor the tracking there.
* For now the assumption is that it is a 1of, so don't want to track it in any game.
*/
class BatColonyWatcher extends Watcher {
private static final class CaveManaPaidTracker implements Serializable, Copyable<CaveManaPaidTracker> {
private int caveMana = 0;

View file

@ -0,0 +1,150 @@
package mage.cards.c;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.DamageAllEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ManaPaidEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.token.TreasureToken;
import mage.util.Copyable;
import mage.watchers.Watcher;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @author Susucr
*/
public final class CataclysmicProspecting extends CardImpl {
private static final Hint hint = new ValueHint("Mana spent from a Desert", CataclysmicProspectingValue.instance);
public CataclysmicProspecting(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{R}{R}");
// Cataclysmic Prospecting deals X damage to each creature. For each mana from a Desert spent to cast this spell, create a tapped Treasure token.
this.getSpellAbility().addEffect(new DamageAllEffect(ManacostVariableValue.REGULAR, StaticFilters.FILTER_PERMANENT_CREATURE));
this.getSpellAbility().addEffect(new CreateTokenEffect(new TreasureToken(), CataclysmicProspectingValue.instance, true, false));
this.getSpellAbility().addWatcher(new CataclysmicProspectingWatcher());
this.getSpellAbility().addHint(hint);
}
private CataclysmicProspecting(final CataclysmicProspecting card) {
super(card);
}
@Override
public CataclysmicProspecting copy() {
return new CataclysmicProspecting(this);
}
}
enum CataclysmicProspectingValue implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return sourceAbility == null ? 0 : CataclysmicProspectingWatcher.getDesertsAmount(sourceAbility.getSourceId(), game);
}
@Override
public CataclysmicProspectingValue copy() {
return this;
}
@Override
public String toString() {
return "1";
}
@Override
public String getMessage() {
return "mana from a Desert spent to cast it";
}
}
/**
* Inspired by {@link mage.watchers.common.ManaPaidSourceWatcher}
* If more cards like Cataclysmic care for mana produced by Deserts in the future, best to refactor the tracking there.
* For now the assumption is that it is a 1of, so don't want to track it in any game.
*/
class CataclysmicProspectingWatcher extends Watcher {
private static final class DesertManaPaidTracker implements Serializable, Copyable<DesertManaPaidTracker> {
private int desertMana = 0;
private DesertManaPaidTracker() {
super();
}
private DesertManaPaidTracker(final DesertManaPaidTracker tracker) {
this.desertMana = tracker.desertMana;
}
@Override
public DesertManaPaidTracker copy() {
return new DesertManaPaidTracker(this);
}
private void increment(MageObject sourceObject, Game game) {
if (sourceObject != null && sourceObject.hasSubtype(SubType.DESERT, game)) {
desertMana++;
}
}
}
private static final DesertManaPaidTracker emptyTracker = new DesertManaPaidTracker();
private final Map<UUID, DesertManaPaidTracker> manaMap = new HashMap<>();
public CataclysmicProspectingWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
switch (event.getType()) {
case ZONE_CHANGE:
if (((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD
// Bug #9943 Memory Deluge cast from graveyard during the same turn
|| ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) {
manaMap.remove(event.getTargetId());
}
return;
case MANA_PAID:
ManaPaidEvent manaEvent = (ManaPaidEvent) event;
manaMap.computeIfAbsent(manaEvent.getTargetId(), x -> new DesertManaPaidTracker())
.increment(manaEvent.getSourceObject(), game);
manaMap.computeIfAbsent(manaEvent.getSourcePaidId(), x -> new DesertManaPaidTracker())
.increment(manaEvent.getSourceObject(), game);
}
}
@Override
public void reset() {
super.reset();
manaMap.clear();
}
public static int getDesertsAmount(UUID sourceId, Game game) {
CataclysmicProspectingWatcher watcher = game.getState().getWatcher(CataclysmicProspectingWatcher.class);
return watcher == null ? 0 : watcher.manaMap.getOrDefault(sourceId, emptyTracker).desertMana;
}
}

View file

@ -22,6 +22,7 @@ public final class OutlawsOfThunderJunctionCommander extends ExpansionSet {
cards.add(new SetCardInfo("Angel of Indemnity", 45, Rarity.RARE, mage.cards.a.AngelOfIndemnity.class));
cards.add(new SetCardInfo("Angelic Sell-Sword", 10, Rarity.RARE, mage.cards.a.AngelicSellSword.class));
cards.add(new SetCardInfo("Back in Town", 18, Rarity.RARE, mage.cards.b.BackInTown.class));
cards.add(new SetCardInfo("Cataclysmic Prospecting", 24, Rarity.RARE, mage.cards.c.CataclysmicProspecting.class));
cards.add(new SetCardInfo("Charred Graverobber", 19, Rarity.RARE, mage.cards.c.CharredGraverobber.class));
cards.add(new SetCardInfo("Elemental Eruption", 27, Rarity.RARE, mage.cards.e.ElementalEruption.class));
cards.add(new SetCardInfo("Embrace the Unknown", 64, Rarity.RARE, mage.cards.e.EmbraceTheUnknown.class));

View file

@ -0,0 +1,58 @@
package org.mage.test.cards.single.otc;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class CataclysmicProspectingTest extends CardTestPlayerBase {
/**
* {@link mage.cards.c.CataclysmicProspecting Cataclysmic Prospecting} {X}{R}{R}
* Sorcery
* Cataclysmic Prospecting deals X damage to each creature. For each mana from a Desert spent to cast this spell, create a tapped Treasure token.
*/
private static final String prospecting = "Cataclysmic Prospecting";
@Test
public void test_Two_Desert() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerB, "Indomitable Ancients"); // 2/10
addCard(Zone.BATTLEFIELD, playerA, "Hostile Desert", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.HAND, playerA, prospecting);
setChoice(playerA, "X=5");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, prospecting);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, prospecting, 1);
assertDamageReceived(playerB, "Indomitable Ancients", 5);
assertPermanentCount(playerA, "Treasure Token", 2);
}
@Test
public void test_Zero_Desert() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerB, "Indomitable Ancients"); // 2/10
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 7);
addCard(Zone.HAND, playerA, prospecting);
setChoice(playerA, "X=5");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, prospecting);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, prospecting, 1);
assertDamageReceived(playerB, "Indomitable Ancients", 5);
assertPermanentCount(playerA, "Treasure Token", 0);
}
}