mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
[PIP] Implement Glowing One & Ingesting Radroach
This commit is contained in:
parent
0da12c4bc2
commit
9a21f24f2c
8 changed files with 374 additions and 11 deletions
|
|
@ -3,7 +3,7 @@ package mage.cards.a;
|
|||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.condition.common.SourceInGraveyardCondition;
|
||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
|
|
@ -34,7 +34,7 @@ public final class ArdenAngel extends CardImpl {
|
|||
// At the beginning of your upkeep, if Arden Angel is in your graveyard, roll a four-sided die. If the result is 1, return Arden Angel from your graveyard to the battlefield.
|
||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
||||
new BeginningOfUpkeepTriggeredAbility(new ArdenAngelEffect(), TargetController.YOU, false),
|
||||
ArdenAngelCondition.instance, "At the beginning of your upkeep, if {this} is in your graveyard, " +
|
||||
SourceInGraveyardCondition.instance, "At the beginning of your upkeep, if {this} is in your graveyard, " +
|
||||
"roll a four-sided die. If the result is 1, return {this} from your graveyard to the battlefield."
|
||||
));
|
||||
}
|
||||
|
|
@ -49,15 +49,6 @@ public final class ArdenAngel extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
enum ArdenAngelCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD;
|
||||
}
|
||||
}
|
||||
|
||||
class ArdenAngelEffect extends OneShotEffect {
|
||||
|
||||
ArdenAngelEffect() {
|
||||
|
|
|
|||
59
Mage.Sets/src/mage/cards/g/GlowingOne.java
Normal file
59
Mage.Sets/src/mage/cards/g/GlowingOne.java
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package mage.cards.g;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
|
||||
import mage.abilities.common.MillTriggeredAbility;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
import mage.abilities.keyword.DeathtouchAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterNonlandCard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public final class GlowingOne extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterNonlandCard();
|
||||
|
||||
public GlowingOne(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
|
||||
|
||||
this.subtype.add(SubType.ZOMBIE);
|
||||
this.subtype.add(SubType.MUTANT);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Deathtouch
|
||||
this.addAbility(DeathtouchAbility.getInstance());
|
||||
|
||||
// Whenever Glowing One deals combat damage to a player, they get four rad counters.
|
||||
this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(
|
||||
new AddCountersTargetEffect(CounterType.RAD.createInstance(4)), false, true)
|
||||
);
|
||||
|
||||
// Whenever a player mills a nonland card, you gain 1 life.
|
||||
this.addAbility(new MillTriggeredAbility(
|
||||
Zone.BATTLEFIELD, new GainLifeEffect(1),
|
||||
TargetController.ANY, filter, false
|
||||
));
|
||||
}
|
||||
|
||||
private GlowingOne(final GlowingOne card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlowingOne copy() {
|
||||
return new GlowingOne(this);
|
||||
}
|
||||
}
|
||||
68
Mage.Sets/src/mage/cards/i/InfestingRadroach.java
Normal file
68
Mage.Sets/src/mage/cards/i/InfestingRadroach.java
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package mage.cards.i;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.CantBlockAbility;
|
||||
import mage.abilities.common.DealsDamageToAPlayerTriggeredAbility;
|
||||
import mage.abilities.common.MillTriggeredAbility;
|
||||
import mage.abilities.condition.common.SourceInGraveyardCondition;
|
||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||
import mage.abilities.dynamicvalue.common.SavedDamageValue;
|
||||
import mage.abilities.effects.common.ReturnToHandSourceEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterNonlandCard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public final class InfestingRadroach extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterNonlandCard();
|
||||
|
||||
public InfestingRadroach(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
|
||||
|
||||
this.subtype.add(SubType.INSECT);
|
||||
this.subtype.add(SubType.MUTANT);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Infesting Radroach can't block.
|
||||
this.addAbility(new CantBlockAbility());
|
||||
|
||||
// Whenever Infesting Radroach deals combat damage to a player, they get that many rad counters.
|
||||
this.addAbility(new DealsDamageToAPlayerTriggeredAbility(
|
||||
new AddCountersTargetEffect(CounterType.RAD.createInstance(), SavedDamageValue.MANY),
|
||||
false, true
|
||||
));
|
||||
|
||||
// Whenever an opponent mills a nonland card, if Infesting Radroach is in your graveyard, you may return it to your hand.
|
||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
||||
new MillTriggeredAbility(Zone.GRAVEYARD, new ReturnToHandSourceEffect(), TargetController.OPPONENT, filter, true),
|
||||
SourceInGraveyardCondition.instance, "Whenever an opponent mills a nonland card, "
|
||||
+ "if {this} is in your graveyard, you may return it to your hand"
|
||||
));
|
||||
}
|
||||
|
||||
private InfestingRadroach(final InfestingRadroach card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfestingRadroach copy() {
|
||||
return new InfestingRadroach(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -131,6 +131,7 @@ public final class Fallout extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("General's Enforcer", 217, Rarity.UNCOMMON, mage.cards.g.GeneralsEnforcer.class));
|
||||
cards.add(new SetCardInfo("Glacial Fortress", 266, Rarity.RARE, mage.cards.g.GlacialFortress.class));
|
||||
cards.add(new SetCardInfo("Glimmer of Genius", 176, Rarity.UNCOMMON, mage.cards.g.GlimmerOfGenius.class));
|
||||
cards.add(new SetCardInfo("Glowing One", 76, Rarity.UNCOMMON, mage.cards.g.GlowingOne.class));
|
||||
cards.add(new SetCardInfo("Grave Titan", 346, Rarity.MYTHIC, mage.cards.g.GraveTitan.class));
|
||||
cards.add(new SetCardInfo("Guardian Project", 199, Rarity.RARE, mage.cards.g.GuardianProject.class));
|
||||
cards.add(new SetCardInfo("Gunner Conscript", 77, Rarity.UNCOMMON, mage.cards.g.GunnerConscript.class));
|
||||
|
|
@ -146,6 +147,7 @@ public final class Fallout extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Ian the Reckless", 59, Rarity.UNCOMMON, mage.cards.i.IanTheReckless.class));
|
||||
cards.add(new SetCardInfo("Impassioned Orator", 162, Rarity.COMMON, mage.cards.i.ImpassionedOrator.class));
|
||||
cards.add(new SetCardInfo("Inexorable Tide", 177, Rarity.RARE, mage.cards.i.InexorableTide.class));
|
||||
cards.add(new SetCardInfo("Infesting Radroach", 46, Rarity.UNCOMMON, mage.cards.i.InfestingRadroach.class));
|
||||
cards.add(new SetCardInfo("Inspiring Call", 203, Rarity.UNCOMMON, mage.cards.i.InspiringCall.class));
|
||||
cards.add(new SetCardInfo("Intangible Virtue", 163, Rarity.UNCOMMON, mage.cards.i.IntangibleVirtue.class));
|
||||
cards.add(new SetCardInfo("Intelligence Bobblehead", 134, Rarity.UNCOMMON, mage.cards.i.IntelligenceBobblehead.class));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
package org.mage.test.cards.single.pip;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class GlowingOneTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.g.GlowingOne Glowing One} {2}{G}
|
||||
* Creature — Zombie Mutant
|
||||
* Deathtouch
|
||||
* Whenever Glowing One deals combat damage to a player, they get four rad counters.
|
||||
* Whenever a player mills a nonland card, you gain 1 life.
|
||||
* 2/2
|
||||
*/
|
||||
private static final String glowing = "Glowing One";
|
||||
|
||||
@Test
|
||||
public void test_MillSelf() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, glowing);
|
||||
addCard(Zone.HAND, playerA, "Stitcher's Supplier"); // etb, mill 3
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
|
||||
addCard(Zone.LIBRARY, playerA, "Goblin Piker", 2);
|
||||
addCard(Zone.LIBRARY, playerA, "Taiga", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stitcher's Supplier");
|
||||
setChoice(playerA, "Whenever"); // 2 triggers to stack.
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, 3);
|
||||
assertLife(playerA, 20 + 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MillOpponent() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, glowing);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp");
|
||||
addCard(Zone.LIBRARY, playerB, "Goblin Piker", 1);
|
||||
addCard(Zone.LIBRARY, playerB, "Taiga", 2);
|
||||
addCard(Zone.LIBRARY, playerB, "Stitcher's Supplier"); // etb, mill 3
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Stitcher's Supplier");
|
||||
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, 3);
|
||||
assertLife(playerA, 20 + 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package org.mage.test.cards.single.pip;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class InfestingRadroachTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.i.InfestingRadroach Infesting Radroach} {2}{B}
|
||||
* Creature — Insect Mutant
|
||||
* Flying
|
||||
* Infesting Radroach can’t block.
|
||||
* Whenever Infesting Radroach deals combat damage to a player, they get that many rad counters.
|
||||
* Whenever an opponent mills a nonland card, if Infesting Radroach is in your graveyard, you may return it to your hand.
|
||||
* 2/2
|
||||
*/
|
||||
private static final String radroach = "Infesting Radroach";
|
||||
|
||||
@Test
|
||||
public void test_MillSelf() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.GRAVEYARD, playerA, radroach);
|
||||
addCard(Zone.HAND, playerA, "Stitcher's Supplier"); // etb, mill 3
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
|
||||
addCard(Zone.LIBRARY, playerA, "Goblin Piker", 2);
|
||||
addCard(Zone.LIBRARY, playerA, "Taiga", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stitcher's Supplier");
|
||||
// no trigger
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MillOpponent() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.GRAVEYARD, playerA, radroach);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp");
|
||||
addCard(Zone.LIBRARY, playerB, "Goblin Piker", 2);
|
||||
addCard(Zone.LIBRARY, playerB, "Taiga", 1);
|
||||
addCard(Zone.LIBRARY, playerB, "Stitcher's Supplier"); // etb, mill 3
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Stitcher's Supplier");
|
||||
setChoice(playerA, "Whenever"); // 2 triggers
|
||||
setChoice(playerA, true); // yes to first, second trigger fizzles
|
||||
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, 3);
|
||||
assertHandCount(playerA, radroach, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Damage() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, radroach);
|
||||
|
||||
attack(1, playerA, radroach, playerB);
|
||||
|
||||
setStopAt(1, PhaseStep.END_COMBAT);
|
||||
execute();
|
||||
|
||||
assertCounterCount(playerB, CounterType.RAD, 2);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class MillTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private final TargetController targetController;
|
||||
private final FilterCard filter;
|
||||
|
||||
public MillTriggeredAbility(Zone zone, Effect effect, TargetController targetController, FilterCard filter, boolean optional) {
|
||||
super(zone, effect, optional);
|
||||
this.targetController = targetController;
|
||||
this.filter = filter;
|
||||
setTriggerPhrase(generateTriggerPhrase());
|
||||
}
|
||||
|
||||
private MillTriggeredAbility(final MillTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.targetController = ability.targetController;
|
||||
this.filter = ability.filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MillTriggeredAbility copy() {
|
||||
return new MillTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.MILLED_CARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
UUID playerId = event.getPlayerId();
|
||||
switch (targetController) {
|
||||
case ANY:
|
||||
// no check.
|
||||
break;
|
||||
case OPPONENT:
|
||||
if (!game.getOpponents(getControllerId()).contains(playerId)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Wrong code usage. targetController not yet supported: " + targetController);
|
||||
}
|
||||
Card card = game.getCard(event.getTargetId());
|
||||
return card != null && filter.match(card, getControllerId(), this, game);
|
||||
}
|
||||
|
||||
private String generateTriggerPhrase() {
|
||||
String text = "Whenever ";
|
||||
switch (targetController) {
|
||||
case ANY:
|
||||
text += "a player ";
|
||||
break;
|
||||
case OPPONENT:
|
||||
text += "an opponent ";
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Wrong code usage. targetController not yet supported: " + targetController);
|
||||
}
|
||||
return text + "mills a " + filter.getMessage() + ", ";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* If {this} is in your graveyard
|
||||
*
|
||||
* @author Susucr
|
||||
*/
|
||||
|
||||
public enum SourceInGraveyardCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue