This commit is contained in:
Balázs Kristóf 2025-12-16 15:37:14 -06:00 committed by GitHub
commit 435e66a294
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 423 additions and 0 deletions

View file

@ -0,0 +1,146 @@
package mage.cards.d;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ReturnToBattlefieldUnderYourControlTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.hint.ValueHint;
import mage.abilities.keyword.HasteAbility;
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterControlledPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.SandWarriorToken;
import mage.game.permanent.token.Token;
import mage.target.targetpointer.FixedTarget;
import mage.target.targetpointer.FixedTargets;
/**
* @author balazskristof
*/
public final class DesertWarfare extends CardImpl {
private static final FilterControlledPermanent filter = new FilterControlledPermanent(
SubType.DESERT, "you control five or more deserts"
);
public DesertWarfare(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}");
// Whenever you sacrifice a Desert and whenever a Desert card is put into your graveyard from your hand or library, put that card onto the battlefield under your control at the beginning of your next end step.
this.addAbility(new DesertWarfareTriggeredAbility());
// At the beginning of combat on your turn, if you control five or more Deserts, create that many 1/1 red, green, and white Sand Warrior creature tokens. They gain haste.
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
new BeginningOfCombatTriggeredAbility(new DesertWarfareTokenEffect()),
new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.OR_GREATER, 5),
"At the beginning of combat on your turn, if you control five or more Deserts, "
+ "create that many 1/1 red, green, and white Sand Warrior creature tokens. They gain haste."
).addHint(new ValueHint("Deserts you control", new PermanentsOnBattlefieldCount(filter))));
}
private DesertWarfare(final DesertWarfare card) {
super(card);
}
@Override
public DesertWarfare copy() {
return new DesertWarfare(this);
}
}
class DesertWarfareTriggeredAbility extends TriggeredAbilityImpl {
DesertWarfareTriggeredAbility() {
super(Zone.BATTLEFIELD, null, false);
setTriggerPhrase("Whenever you sacrifice a Desert and whenever a Desert card is put into your graveyard from your hand or library, "
+ "put that card onto the battlefield under your control at the beginning of your next end step.");
}
private DesertWarfareTriggeredAbility(final DesertWarfareTriggeredAbility ability) {
super(ability);
}
@Override
public DesertWarfareTriggeredAbility copy() {
return new DesertWarfareTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.SACRIFICED_PERMANENT
|| event.getType() == GameEvent.EventType.ZONE_CHANGE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event instanceof ZoneChangeEvent) {
ZoneChangeEvent zce = (ZoneChangeEvent) event;
Card card = game.getCard(event.getTargetId());
if (card == null
|| !card.isOwnedBy(getControllerId())
|| !card.hasSubtype(SubType.DESERT, game)
|| zce.getToZone() != Zone.GRAVEYARD
|| (zce.getFromZone() != Zone.HAND
&& zce.getFromZone() != Zone.LIBRARY)) {
return false;
}
} else {
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent == null
|| !permanent.isOwnedBy(getControllerId())
|| !permanent.hasSubtype(SubType.DESERT, game)) {
return false;
}
}
Effect effect = new ReturnToBattlefieldUnderYourControlTargetEffect();
effect.setTargetPointer(new FixedTarget(event.getTargetId(), game));
game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect, TargetController.YOU), this);
return true;
}
}
class DesertWarfareTokenEffect extends OneShotEffect {
private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.DESERT);
DesertWarfareTokenEffect() {
super(Outcome.Benefit);
staticText = "create that many 1/1 red, green, and white Sand Warrior creature tokens. "
+ " They gain haste.";
}
private DesertWarfareTokenEffect(final DesertWarfareTokenEffect effect) {
super(effect);
}
@Override
public DesertWarfareTokenEffect copy() {
return new DesertWarfareTokenEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Token token = new SandWarriorToken();
int count = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game).size();
token.putOntoBattlefield(count, game, source, source.getControllerId());
game.addEffect(new GainAbilityTargetEffect(
HasteAbility.getInstance(), Duration.WhileOnBattlefield
).setTargetPointer(new FixedTargets(token, game)), source);
return true;
}
}

View file

@ -96,6 +96,7 @@ public final class ModernHorizons3Commander extends ExpansionSet {
cards.add(new SetCardInfo("Decoction Module", 288, Rarity.UNCOMMON, mage.cards.d.DecoctionModule.class));
cards.add(new SetCardInfo("Deepfathom Skulker", 180, Rarity.RARE, mage.cards.d.DeepfathomSkulker.class));
cards.add(new SetCardInfo("Demolition Field", 335, Rarity.UNCOMMON, mage.cards.d.DemolitionField.class));
cards.add(new SetCardInfo("Desert Warfare", 64, Rarity.RARE, mage.cards.d.DesertWarfare.class));
cards.add(new SetCardInfo("Desert of the Indomitable", 336, Rarity.COMMON, mage.cards.d.DesertOfTheIndomitable.class));
cards.add(new SetCardInfo("Desert of the Mindful", 337, Rarity.COMMON, mage.cards.d.DesertOfTheMindful.class));
cards.add(new SetCardInfo("Disa the Restless", 1, Rarity.MYTHIC, mage.cards.d.DisaTheRestless.class));

View file

@ -0,0 +1,276 @@
package org.mage.test.cards.single.m3c;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author balazskristof
*/
public class DesertWarfareTest extends CardTestPlayerBase {
@Test
public void TestTokens() {
addCard(Zone.BATTLEFIELD, playerA, "Desert Warfare");
addCard(Zone.BATTLEFIELD, playerA, "Desert", 4);
addCard(Zone.HAND, playerA, "Desert");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertTokenCount(playerA, "Sand Warrior Token", 0);
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertTokenCount(playerA, "Sand Warrior Token", 0);
playLand(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Desert");
setStopAt(3, PhaseStep.BEGIN_COMBAT);
execute();
assertTokenCount(playerA, "Sand Warrior Token", 5);
setStopAt(4, PhaseStep.BEGIN_COMBAT);
execute();
assertTokenCount(playerA, "Sand Warrior Token", 5);
setStopAt(5, PhaseStep.BEGIN_COMBAT);
execute();
assertTokenCount(playerA, "Sand Warrior Token", 10);
}
@Test
public void TestSacrifice() {
addCard(Zone.BATTLEFIELD, playerA, "Desert Warfare");
addCard(Zone.BATTLEFIELD, playerA, "Desert");
addCard(Zone.BATTLEFIELD, playerA, "Akki Avalanchers");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sacrifice");
setChoice(playerA, "Desert");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Desert", 1);
assertPermanentCount(playerA, "Desert", 0);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Desert", 0);
assertPermanentCount(playerA, "Desert", 1);
}
@Test
public void TestSacrificeOpponentsTurn() {
addCard(Zone.BATTLEFIELD, playerA, "Desert Warfare");
addCard(Zone.BATTLEFIELD, playerA, "Desert");
addCard(Zone.BATTLEFIELD, playerA, "Akki Avalanchers");
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Sacrifice");
setChoice(playerA, "Desert");
setStopAt(2, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Desert", 1);
assertPermanentCount(playerA, "Desert", 0);
setStopAt(3, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Desert", 0);
assertPermanentCount(playerA, "Desert", 1);
}
@Test
public void TestDestroy() {
addCard(Zone.BATTLEFIELD, playerA, "Desert Warfare");
addCard(Zone.BATTLEFIELD, playerA, "Desert");
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 4);
addCard(Zone.HAND, playerB, "Volcanic Upheaval");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Volcanic Upheaval");
addTarget(playerB, "Desert");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Desert", 1);
assertPermanentCount(playerA, "Desert", 0);
}
@Test
public void TestDiscard() {
addCard(Zone.BATTLEFIELD, playerA, "Desert Warfare");
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Island");
addCard(Zone.LIBRARY, playerA, "Desert");
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.HAND, playerA, "Mountain", 2);
addCard(Zone.HAND, playerA, "Faithless Looting");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Faithless Looting");
setChoice(playerA, "Island^Desert");
//setChoice(playerA, "Desert");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Desert", 0);
assertGraveyardCount(playerA, "Island", 1);
assertPermanentCount(playerA, "Desert", 1);
}
@Test
public void TestDiscardOpponentsTurn() {
addCard(Zone.BATTLEFIELD, playerA, "Desert Warfare");
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Island");
addCard(Zone.LIBRARY, playerA, "Desert");
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.HAND, playerA, "Mountain", 2);
addCard(Zone.HAND, playerA, "Faithless Looting");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Faithless Looting");
setChoice(playerA, "Island");
setChoice(playerA, "Desert");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Desert", 0);
assertGraveyardCount(playerA, "Island", 1);
assertPermanentCount(playerA, "Desert", 1);
}
@Test
public void TestCycling() {
addCard(Zone.BATTLEFIELD, playerA, "Desert Warfare");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.HAND, playerA, "Desert of the Fervent");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling {1}{R}");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Desert of the Fervent", 0);
assertPermanentCount(playerA, "Desert of the Fervent", 1);
}
@Test
public void TestCyclingOpponentsTurn() {
addCard(Zone.BATTLEFIELD, playerA, "Desert Warfare");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.HAND, playerA, "Desert of the Fervent");
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling {1}{R}");
setStopAt(2, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Desert of the Fervent", 1);
assertPermanentCount(playerA, "Desert of the Fervent", 0);
setStopAt(3, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Desert of the Fervent", 0);
assertPermanentCount(playerA, "Desert of the Fervent", 1);
}
@Test
public void TestMill() {
addCard(Zone.BATTLEFIELD, playerA, "Desert Warfare");
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Plains");
addCard(Zone.LIBRARY, playerA, "Island");
addCard(Zone.LIBRARY, playerA, "Desert");
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
addCard(Zone.HAND, playerA, "Stitcher's Supplier");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stitcher's Supplier");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Desert", 0);
assertPermanentCount(playerA, "Desert", 1);
assertGraveyardCount(playerA, "Plains", 1);
assertGraveyardCount(playerA, "Island", 1);
}
@Test
public void TestMillOpponentsTurn() {
addCard(Zone.BATTLEFIELD, playerA, "Desert Warfare");
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Plains");
addCard(Zone.LIBRARY, playerA, "Island");
addCard(Zone.LIBRARY, playerA, "Desert");
addCard(Zone.BATTLEFIELD, playerA, "Stitcher's Supplier");
addCard(Zone.BATTLEFIELD, playerB, "Mountain");
addCard(Zone.HAND, playerB, "Shock");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Shock");
addTarget(playerB, "Stitcher's Supplier");
setStopAt(2, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Desert", 0);
assertGraveyardCount(playerA, "Desert", 1);
assertGraveyardCount(playerA, "Plains", 1);
assertGraveyardCount(playerA, "Island", 1);
setStopAt(3, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Desert", 1);
assertGraveyardCount(playerA, "Desert", 0);
assertGraveyardCount(playerA, "Plains", 1);
assertGraveyardCount(playerA, "Island", 1);
}
@Test
public void TestDuneChanter() {
addCard(Zone.BATTLEFIELD, playerA, "Desert Warfare");
addCard(Zone.BATTLEFIELD, playerA, "Dune Chanter");
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Plains");
addCard(Zone.LIBRARY, playerA, "Island");
addCard(Zone.LIBRARY, playerA, "Desert");
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
addCard(Zone.HAND, playerA, "Stitcher's Supplier");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stitcher's Supplier");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Desert", 0);
assertGraveyardCount(playerA, "Plains", 0);
assertGraveyardCount(playerA, "Island", 0);
assertPermanentCount(playerA, "Desert", 1);
assertPermanentCount(playerA, "Plains", 1);
assertPermanentCount(playerA, "Island", 1);
}
}