mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 10:40:06 -08:00
implement [MIR] Shadowbane
This commit is contained in:
parent
4e717abaeb
commit
d57b99d6e4
4 changed files with 276 additions and 4 deletions
96
Mage.Sets/src/mage/cards/s/Shadowbane.java
Normal file
96
Mage.Sets/src/mage/cards/s/Shadowbane.java
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package mage.cards.s;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.PreventionEffectData;
|
||||
import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetSource;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public final class Shadowbane extends CardImpl {
|
||||
|
||||
public Shadowbane(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}");
|
||||
|
||||
// The next time a source of your choice would deal damage to you and/or creatures you control this turn, prevent that damage. If damage from a black source is prevented this way, you gain that much life.
|
||||
this.getSpellAbility().addEffect(new ShadowbanePreventionEffect());
|
||||
}
|
||||
|
||||
private Shadowbane(final Shadowbane card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shadowbane copy() {
|
||||
return new Shadowbane(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ShadowbanePreventionEffect extends PreventNextDamageFromChosenSourceEffect {
|
||||
|
||||
ShadowbanePreventionEffect() {
|
||||
super(Duration.EndOfTurn, false, ShadowbanePreventionApplier.instance);
|
||||
}
|
||||
|
||||
private ShadowbanePreventionEffect(final ShadowbanePreventionEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreventNextDamageFromChosenSourceEffect copy() {
|
||||
return new ShadowbanePreventionEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (!super.applies(event, source, game)) {
|
||||
return false;
|
||||
}
|
||||
UUID controllerId = source.getControllerId();
|
||||
UUID targetId = event.getTargetId();
|
||||
if (targetId.equals(controllerId)) {
|
||||
return true; // damage to you
|
||||
}
|
||||
Permanent permanent = game.getPermanent(targetId);
|
||||
return StaticFilters.FILTER_CONTROLLED_CREATURE.match(permanent, controllerId, source, game); // damage to creatures you control
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum ShadowbanePreventionApplier implements PreventNextDamageFromChosenSourceEffect.ApplierOnPrevention {
|
||||
instance;
|
||||
|
||||
public String getText() {
|
||||
return "If damage from a black source is prevented this way, you gain that much life";
|
||||
}
|
||||
|
||||
public boolean apply(PreventionEffectData data, TargetSource targetSource, GameEvent event, Ability source, Game game) {
|
||||
if (data == null || data.getPreventedDamage() <= 0) {
|
||||
return false;
|
||||
}
|
||||
MageObject sourceObject = game.getObject(targetSource.getFirstTarget());
|
||||
if (!sourceObject.getColor(game).isBlack()) {
|
||||
return false;
|
||||
}
|
||||
int prevented = data.getPreventedDamage();
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
controller.gainLife(prevented, game, source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -295,7 +295,7 @@ public final class Mirage extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Serene Heart", 242, Rarity.COMMON, mage.cards.s.SereneHeart.class, RETRO_ART));
|
||||
cards.add(new SetCardInfo("Sewer Rats", 139, Rarity.COMMON, mage.cards.s.SewerRats.class, RETRO_ART));
|
||||
cards.add(new SetCardInfo("Shadow Guildmage", 140, Rarity.COMMON, mage.cards.s.ShadowGuildmage.class, RETRO_ART));
|
||||
// cards.add(new SetCardInfo("Shadowbane", 38, Rarity.UNCOMMON, mage.cards.s.Shadowbane.class, RETRO_ART));
|
||||
cards.add(new SetCardInfo("Shadowbane", 242, Rarity.UNCOMMON, mage.cards.s.Shadowbane.class));
|
||||
cards.add(new SetCardInfo("Shallow Grave", 141, Rarity.RARE, mage.cards.s.ShallowGrave.class, RETRO_ART));
|
||||
cards.add(new SetCardInfo("Shaper Guildmage", 91, Rarity.COMMON, mage.cards.s.ShaperGuildmage.class, RETRO_ART));
|
||||
cards.add(new SetCardInfo("Shauku's Minion", 283, Rarity.UNCOMMON, mage.cards.s.ShaukusMinion.class, RETRO_ART));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,177 @@
|
|||
package org.mage.test.cards.single.mir;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class ShadowbaneTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.s.Shadowbane Shadowbane} {1}{W}
|
||||
* The next time a source of your choice would deal damage to you and/or creatures you control this turn, prevent that damage. If damage from a black source is prevented this way, you gain that much life.
|
||||
*/
|
||||
private static final String shadowbane = "Shadowbane";
|
||||
|
||||
@Test
|
||||
public void test_DamageOnCreature_Prevent_SourceNotBlack() {
|
||||
addCard(Zone.HAND, playerA, shadowbane, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane);
|
||||
setChoice(playerA, "Goblin Piker"); // source to prevent from
|
||||
|
||||
attack(2, playerB, "Goblin Piker", playerA);
|
||||
block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 0);
|
||||
assertLife(playerA, 20); // no life gain
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DamageOnCreature_Prevent_SourceBlack() {
|
||||
addCard(Zone.HAND, playerA, shadowbane, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Cabal Evangel", 1); // 2/2 black
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane);
|
||||
setChoice(playerA, "Cabal Evangel"); // source to prevent from
|
||||
|
||||
attack(2, playerB, "Cabal Evangel", playerA);
|
||||
block(2, playerA, "Caelorna, Coral Tyrant", "Cabal Evangel");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 0);
|
||||
assertLife(playerA, 20 + 2); // life gain
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DamageOnYou_Prevent_SourceNotBlack() {
|
||||
addCard(Zone.HAND, playerA, shadowbane, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane);
|
||||
setChoice(playerA, "Goblin Piker"); // source to prevent from
|
||||
|
||||
attack(2, playerB, "Goblin Piker", playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DamageOnYou_Prevent_SourceBlack() {
|
||||
addCard(Zone.HAND, playerA, shadowbane, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Cabal Evangel", 1); // 2/2 black
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane);
|
||||
setChoice(playerA, "Cabal Evangel"); // source to prevent from
|
||||
|
||||
attack(2, playerB, "Cabal Evangel", playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 + 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SpellDamage_Prevent_SourceNotBlack() {
|
||||
addCard(Zone.HAND, playerA, shadowbane, 1);
|
||||
addCard(Zone.HAND, playerB, "Lightning Bolt", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane, null, "Lightning Bolt");
|
||||
setChoice(playerA, "Lightning Bolt"); // source to prevent from
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SpellDamage_Prevent_SourceBlack() {
|
||||
addCard(Zone.HAND, playerA, shadowbane, 1);
|
||||
addCard(Zone.HAND, playerB, "Agonizing Syphon", 1); // 3 damage to any target. gain 3 life
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 4);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Agonizing Syphon", playerA);
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane, null, "Agonizing Syphon");
|
||||
setChoice(playerA, "Agonizing Syphon"); // source to prevent from
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 + 3);
|
||||
assertLife(playerB, 20 + 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SpellDamage_Prevent_Famine_SourceBlack() {
|
||||
addCard(Zone.HAND, playerA, shadowbane, 1);
|
||||
addCard(Zone.HAND, playerB, "Famine", 1); // Famine deals 3 damage to each creature and each player.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 5);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Ageless Guardian"); // 1/4
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Dune Beetle"); // 1/4
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Famine");
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane, null, "Famine");
|
||||
setChoice(playerA, "Famine"); // source to prevent from
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertDamageReceived(playerA, "Ageless Guardian", 0); // prevented
|
||||
assertDamageReceived(playerB, "Dune Beetle", 3); // not prevented on opponent's creature
|
||||
assertLife(playerB, 20 - 3); // not prevented on opponent
|
||||
assertLife(playerA, 20 + 3 * 2); // 2 preventions of 3 damage
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SpellDamage_Prevent_FieryConfluence() {
|
||||
addCard(Zone.HAND, playerA, shadowbane, 1);
|
||||
addCard(Zone.HAND, playerB, "Fiery Confluence", 3); // three instances of "Fiery Confluence deals 2 damage to each opponent."
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 4);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Fiery Confluence");
|
||||
setModeChoice(playerB, "2"); // Fiery Confluence deals 2 damage to each opponent
|
||||
setModeChoice(playerB, "2"); // Fiery Confluence deals 2 damage to each opponent
|
||||
setModeChoice(playerB, "2"); // Fiery Confluence deals 2 damage to each opponent
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane, null, "Fiery Confluence");
|
||||
setChoice(playerA, "Fiery Confluence"); // source to prevent from
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 - 2 * 2); // only 1 of the 3 damage instance is prevented
|
||||
}
|
||||
}
|
||||
|
|
@ -96,7 +96,7 @@ public class PreventNextDamageFromChosenSourceEffect extends PreventionEffectImp
|
|||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
PreventionEffectData data = preventDamageAction(event, source, game);
|
||||
this.used = true;
|
||||
discard();
|
||||
if (onPrevention != null) {
|
||||
onPrevention.apply(data, targetSource, event, source, game);
|
||||
}
|
||||
|
|
@ -105,8 +105,7 @@ public class PreventNextDamageFromChosenSourceEffect extends PreventionEffectImp
|
|||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return !this.used
|
||||
&& super.applies(event, source, game)
|
||||
return super.applies(event, source, game)
|
||||
&& (!toYou || event.getTargetId().equals(source.getControllerId()))
|
||||
&& event.getSourceId().equals(targetSource.getFirstTarget());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue