mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
[MIR] Implement Teferi's Imp (#11326)
This commit is contained in:
parent
69517f998f
commit
5996cfbce2
9 changed files with 186 additions and 4 deletions
54
Mage.Sets/src/mage/cards/t/TeferisImp.java
Normal file
54
Mage.Sets/src/mage/cards/t/TeferisImp.java
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SourcePhaseInTriggeredAbility;
|
||||
import mage.abilities.common.SourcePhaseOutTriggeredAbility;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.discard.DiscardControllerEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.PhasingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public final class TeferisImp extends CardImpl {
|
||||
|
||||
public TeferisImp(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
|
||||
|
||||
this.subtype.add(SubType.IMP);
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Phasing
|
||||
this.addAbility(PhasingAbility.getInstance());
|
||||
|
||||
// Whenever Teferi's Imp phases out, discard a card.
|
||||
this.addAbility(new SourcePhaseOutTriggeredAbility(
|
||||
new DiscardControllerEffect(1), false)
|
||||
);
|
||||
|
||||
// Whenever Teferi's Imp phases in, draw a card.
|
||||
this.addAbility(new SourcePhaseInTriggeredAbility(
|
||||
new DrawCardSourceControllerEffect(1), false)
|
||||
);
|
||||
}
|
||||
|
||||
private TeferisImp(final TeferisImp card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TeferisImp copy() {
|
||||
return new TeferisImp(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -309,6 +309,7 @@ public final class Mirage extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Teeka's Dragon", 320, Rarity.RARE, mage.cards.t.TeekasDragon.class));
|
||||
cards.add(new SetCardInfo("Teferi's Curse", 96, Rarity.COMMON, mage.cards.t.TeferisCurse.class));
|
||||
cards.add(new SetCardInfo("Teferi's Drake", 97, Rarity.COMMON, mage.cards.t.TeferisDrake.class));
|
||||
cards.add(new SetCardInfo("Teferi's Imp", 98, Rarity.RARE, mage.cards.t.TeferisImp.class));
|
||||
cards.add(new SetCardInfo("Teferi's Isle", 330, Rarity.RARE, mage.cards.t.TeferisIsle.class));
|
||||
cards.add(new SetCardInfo("Telim'Tor", 197, Rarity.RARE, mage.cards.t.TelimTor.class));
|
||||
cards.add(new SetCardInfo("Telim'Tor's Darts", 321, Rarity.UNCOMMON, mage.cards.t.TelimTorsDarts.class));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
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 TeferisImpTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.t.TeferisImp} <br>
|
||||
* Teferi's Imp {2}{U} <br>
|
||||
* Creature — Imp <br>
|
||||
* Flying <br>
|
||||
* Phasing <br>
|
||||
* Whenever Teferi’s Imp phases out, discard a card. <br>
|
||||
* Whenever Teferi’s Imp phases in, draw a card. <br>
|
||||
* 1/1
|
||||
*/
|
||||
private static final String imp = "Teferi's Imp";
|
||||
|
||||
@Test
|
||||
public void test_Phasing_triggers() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.HAND, playerA, imp);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||
addCard(Zone.HAND, playerA, "Grizzly Bears", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, imp);
|
||||
|
||||
checkHandCount("before discard", 2, PhaseStep.END_TURN, playerA, 2);
|
||||
// Beginning of turn 3 -- imp phases out. trigger to discard.
|
||||
setChoice(playerA, "Grizzly Bears"); // choose to discard one of the Bears
|
||||
waitStackResolved(3, PhaseStep.UPKEEP);
|
||||
checkHandCount("after discard", 3, PhaseStep.UPKEEP, playerA, 1);
|
||||
|
||||
checkHandCount("before draw", 4, PhaseStep.END_TURN, playerA, 2);
|
||||
// Beginning of turn 5 -- imp phases in. trigger to draw.
|
||||
waitStackResolved(5, PhaseStep.UPKEEP);
|
||||
checkHandCount("after draw", 5, PhaseStep.UPKEEP, playerA, 3);
|
||||
|
||||
setStopAt(5, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Grizzly Bears", 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -419,6 +419,22 @@ public interface Ability extends Controllable, Serializable {
|
|||
*/
|
||||
void setWorksFaceDown(boolean worksFaceDown);
|
||||
|
||||
/**
|
||||
* Returns true if this ability has to work also with phased out object.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean getWorksPhasedOut();
|
||||
|
||||
/**
|
||||
* Sets the value for the worksPhasedOut flag
|
||||
* <p>
|
||||
* true = the ability works also if the object is phased out
|
||||
*
|
||||
* @param worksPhasedOut
|
||||
*/
|
||||
void setWorksPhasedOut(boolean worksPhasedOut);
|
||||
|
||||
/**
|
||||
* Returns true if this ability's rule is visible on the card tooltip
|
||||
*
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
protected boolean ruleAdditionalCostsVisible = true;
|
||||
protected boolean activated = false;
|
||||
protected boolean worksFaceDown = false;
|
||||
protected boolean worksPhasedOut = false;
|
||||
protected int sourceObjectZoneChangeCounter;
|
||||
protected List<Watcher> watchers = new ArrayList<>(); // access to it by GetWatchers only (it can be overridden by some abilities)
|
||||
protected List<Ability> subAbilities = null;
|
||||
|
|
@ -121,6 +122,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
this.ruleVisible = ability.ruleVisible;
|
||||
this.ruleAdditionalCostsVisible = ability.ruleAdditionalCostsVisible;
|
||||
this.worksFaceDown = ability.worksFaceDown;
|
||||
this.worksPhasedOut = ability.worksPhasedOut;
|
||||
this.abilityWord = ability.abilityWord;
|
||||
this.flavorWord = ability.flavorWord;
|
||||
this.sourceObjectZoneChangeCounter = ability.sourceObjectZoneChangeCounter;
|
||||
|
|
@ -1050,7 +1052,9 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
if (object != null) {
|
||||
if (object instanceof Permanent) {
|
||||
return object.hasAbility(this, game) && ((Permanent) object).isPhasedIn();
|
||||
return object.hasAbility(this, game) && (
|
||||
((Permanent) object).isPhasedIn() || this.getWorksPhasedOut()
|
||||
);
|
||||
} else {
|
||||
// cards and other objects
|
||||
return object.hasAbility(this, game);
|
||||
|
|
@ -1270,6 +1274,16 @@ public abstract class AbilityImpl implements Ability {
|
|||
this.worksFaceDown = worksFaceDown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getWorksPhasedOut() {
|
||||
return worksPhasedOut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWorksPhasedOut(boolean worksPhasedOut) {
|
||||
this.worksPhasedOut = worksPhasedOut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MageObject getSourceObject(Game game) {
|
||||
return game.getObject(getSourceId());
|
||||
|
|
@ -1481,7 +1495,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
// to change the zone of any existing singleton abilities
|
||||
return this;
|
||||
}
|
||||
AbilityImpl copy = ((AbilityImpl)this.copy());
|
||||
AbilityImpl copy = ((AbilityImpl) this.copy());
|
||||
copy.zone = zone;
|
||||
copy.newId();
|
||||
return copy;
|
||||
|
|
|
|||
|
|
@ -315,7 +315,6 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
|||
source = game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD);
|
||||
}
|
||||
break;
|
||||
case PHASED_OUT:
|
||||
case PHASED_IN:
|
||||
if (isLeavesTheBattlefieldTrigger()) {
|
||||
source = game.getLastKnownInformation(getSourceId(), event.getZone());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class SourcePhaseOutTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public SourcePhaseOutTriggeredAbility(Effect effect, boolean optional) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
setTriggerPhrase("Whenever {this} phases out, ");
|
||||
setWorksPhasedOut(true);
|
||||
}
|
||||
|
||||
protected SourcePhaseOutTriggeredAbility(final SourcePhaseOutTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourcePhaseOutTriggeredAbility copy() {
|
||||
return new SourcePhaseOutTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.PHASED_OUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return getSourceId().equals(event.getTargetId());
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
package mage.constants;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public enum Zone {
|
||||
|
|
|
|||
|
|
@ -540,6 +540,16 @@ public class StackAbility extends StackObjectImpl implements Ability {
|
|||
this.ability.setWorksFaceDown(worksFaceDown);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getWorksPhasedOut() {
|
||||
return this.ability.getWorksPhasedOut();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWorksPhasedOut(boolean worksPhasedOut) {
|
||||
this.ability.setWorksPhasedOut(worksPhasedOut);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Watcher> getWatchers() {
|
||||
return this.ability.getWatchers();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue