mirror of
https://github.com/correl/mage.git
synced 2025-02-18 03:00:15 +00:00
some changes to ExileSourceCost
This commit is contained in:
parent
448d0914dc
commit
8198322ba5
4 changed files with 113 additions and 117 deletions
|
@ -1,36 +1,29 @@
|
|||
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.DelayedTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
|
||||
import mage.abilities.costs.common.ExileSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreatureCard;
|
||||
import mage.constants.*;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class ShiftyDoppelganger extends CardImpl {
|
||||
|
@ -43,10 +36,9 @@ public final class ShiftyDoppelganger extends CardImpl {
|
|||
this.toughness = new MageInt(1);
|
||||
|
||||
// {3}{U}, Exile Shifty Doppelganger: You may put a creature card from your hand onto the battlefield. If you do, that creature gains haste until end of turn. At the beginning of the next end step, sacrifice that creature. If you do, return Shifty Doppelganger to the battlefield.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ShiftyDoppelgangerExileEffect(), new ManaCostsImpl("{3}{U}"));
|
||||
ability.addCost(new ExileSourceCost(true));
|
||||
Ability ability = new SimpleActivatedAbility(new ShiftyDoppelgangerExileEffect(), new ManaCostsImpl<>("{3}{U}"));
|
||||
ability.addCost(new ExileSourceCost());
|
||||
this.addAbility(ability);
|
||||
|
||||
}
|
||||
|
||||
private ShiftyDoppelganger(final ShiftyDoppelganger card) {
|
||||
|
@ -63,10 +55,12 @@ class ShiftyDoppelgangerExileEffect extends OneShotEffect {
|
|||
|
||||
public ShiftyDoppelgangerExileEffect() {
|
||||
super(Outcome.PutCreatureInPlay);
|
||||
this.staticText = "You may put a creature card from your hand onto the battlefield. If you do, that creature gains haste until end of turn. At the beginning of the next end step, sacrifice that creature. If you do, return {this} to the battlefield";
|
||||
this.staticText = "You may put a creature card from your hand onto the battlefield. If you do, " +
|
||||
"that creature gains haste until end of turn. At the beginning of the next end step, " +
|
||||
"sacrifice that creature. If you do, return {this} to the battlefield";
|
||||
}
|
||||
|
||||
public ShiftyDoppelgangerExileEffect(final ShiftyDoppelgangerExileEffect effect) {
|
||||
private ShiftyDoppelgangerExileEffect(final ShiftyDoppelgangerExileEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
@ -77,56 +71,52 @@ class ShiftyDoppelgangerExileEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
FilterCreatureCard filter = new FilterCreatureCard("a creature card");
|
||||
boolean putCreature = false;
|
||||
UUID creatureId = UUID.randomUUID();
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null && player.chooseUse(Outcome.PutCardInPlay, "Put " + filter.getMessage() + " from your hand onto the battlefield?", source, game)) {
|
||||
TargetCardInHand target = new TargetCardInHand(filter);
|
||||
if (player.choose(Outcome.PutCreatureInPlay, target, source.getSourceId(), game)) {
|
||||
Card card = game.getCard(target.getFirstTarget());
|
||||
if (card != null) {
|
||||
putCreature = player.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
if (putCreature) {
|
||||
creatureId = card.getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (player == null
|
||||
|| player.getHand().count(StaticFilters.FILTER_CARD_CREATURE, game) < 1
|
||||
|| !player.chooseUse(
|
||||
Outcome.PutCardInPlay, "Put a creature card " +
|
||||
"from your hand onto the battlefield?", source, game
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
if (putCreature) {
|
||||
Permanent creature = game.getPermanent(creatureId);
|
||||
if (creature != null) {
|
||||
ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn);
|
||||
hasteEffect.setTargetPointer(new FixedTarget(creature, game));
|
||||
game.addEffect(hasteEffect, source);
|
||||
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(
|
||||
new ShiftyDoppelgangerReturnEffect(creature.getId(), creature.getZoneChangeCounter(game), (int) game.getState().getValue(source.getSourceId().toString())));
|
||||
game.addDelayedTriggeredAbility(delayedAbility, source);
|
||||
}
|
||||
TargetCardInHand target = new TargetCardInHand(StaticFilters.FILTER_CARD_CREATURE);
|
||||
player.choose(Outcome.PutCreatureInPlay, player.getHand(), target, game);
|
||||
Card card = game.getCard(target.getFirstTarget());
|
||||
if (card == null) {
|
||||
return false;
|
||||
}
|
||||
player.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
Permanent creature = game.getPermanent(card.getId());
|
||||
if (creature == null) {
|
||||
return false;
|
||||
}
|
||||
game.addEffect(new GainAbilityTargetEffect(
|
||||
HasteAbility.getInstance(), Duration.EndOfTurn
|
||||
).setTargetPointer(new FixedTarget(creature, game)), source);
|
||||
game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(
|
||||
new ShiftyDoppelgangerReturnEffect(creature, source, game)
|
||||
), source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class ShiftyDoppelgangerReturnEffect extends OneShotEffect {
|
||||
|
||||
private final UUID creatureId;
|
||||
private final int creatureZoneCount;
|
||||
private final int sourceZoneCount;
|
||||
private final MageObjectReference creatureMor;
|
||||
private final MageObjectReference sourceMor;
|
||||
|
||||
ShiftyDoppelgangerReturnEffect(UUID creatureId, int creatureZoneCount, int sourceZoneCount) {
|
||||
ShiftyDoppelgangerReturnEffect(Permanent creature, Ability source, Game game) {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "sacrifice that creature. If you do, return {this} to the battlefield";
|
||||
this.creatureId = creatureId;
|
||||
this.creatureZoneCount = creatureZoneCount;
|
||||
this.sourceZoneCount = sourceZoneCount;
|
||||
this.creatureMor = new MageObjectReference(creature, game);
|
||||
this.sourceMor = new MageObjectReference(source, 1);
|
||||
}
|
||||
|
||||
ShiftyDoppelgangerReturnEffect(final ShiftyDoppelgangerReturnEffect effect) {
|
||||
private ShiftyDoppelgangerReturnEffect(final ShiftyDoppelgangerReturnEffect effect) {
|
||||
super(effect);
|
||||
this.creatureId = effect.creatureId;
|
||||
this.creatureZoneCount = effect.creatureZoneCount;
|
||||
this.sourceZoneCount = effect.sourceZoneCount;
|
||||
this.creatureMor = effect.creatureMor;
|
||||
this.sourceMor = effect.sourceMor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -136,14 +126,16 @@ class ShiftyDoppelgangerReturnEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent creature = game.getPermanent(creatureId);
|
||||
Permanent creature = creatureMor.getPermanent(game);
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
if (creature != null && creature.getZoneChangeCounter(game) == this.creatureZoneCount && creature.sacrifice(source, game)) {
|
||||
if (player != null && sourceObject != null && sourceObject.getZoneChangeCounter(game) == this.sourceZoneCount) {
|
||||
player.moveCards(game.getCard(source.getSourceId()), Zone.BATTLEFIELD, source, game);
|
||||
}
|
||||
if (creature == null || player == null
|
||||
|| !creature.sacrifice(source, game)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
Card sourceCard = sourceMor.getCard(game);
|
||||
if (sourceCard != null) {
|
||||
player.moveCards(sourceCard, Zone.BATTLEFIELD, source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.DelayedTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.common.ExileSourceCost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostEquippedEffect;
|
||||
import mage.abilities.keyword.EquipAbility;
|
||||
|
@ -17,12 +16,13 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.TatsumaDragonToken;
|
||||
import mage.game.permanent.token.Token;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
@ -36,11 +36,11 @@ public final class TatsumasaTheDragonsFang extends CardImpl {
|
|||
this.subtype.add(SubType.EQUIPMENT);
|
||||
|
||||
// Equipped creature gets +5/+5.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(5, 5)));
|
||||
this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(5, 5)));
|
||||
|
||||
// {6}, Exile Tatsumasa, the Dragon's Fang: Create a 5/5 blue Dragon Spirit creature token with flying. Return Tatsumasa to the battlefield under its owner's control when that token dies.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TatsumaTheDragonsFangEffect(), new GenericManaCost(6));
|
||||
ability.addCost(new ExileSourceCost(true));
|
||||
Ability ability = new SimpleActivatedAbility(new TatsumaTheDragonsFangEffect(), new GenericManaCost(6));
|
||||
ability.addCost(new ExileSourceCost());
|
||||
this.addAbility(ability);
|
||||
|
||||
// Equip {3}
|
||||
|
@ -61,7 +61,8 @@ class TatsumaTheDragonsFangEffect extends OneShotEffect {
|
|||
|
||||
public TatsumaTheDragonsFangEffect() {
|
||||
super(Outcome.PutCreatureInPlay);
|
||||
this.staticText = "Create a 5/5 blue Dragon Spirit creature token with flying. Return Tatsumasa to the battlefield under its owner's control when that token dies";
|
||||
this.staticText = "Create a 5/5 blue Dragon Spirit creature token with flying. " +
|
||||
"Return {this} to the battlefield under its owner's control when that token dies";
|
||||
}
|
||||
|
||||
public TatsumaTheDragonsFangEffect(final TatsumaTheDragonsFangEffect effect) {
|
||||
|
@ -75,35 +76,25 @@ class TatsumaTheDragonsFangEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
CreateTokenEffect effect = new CreateTokenEffect(new TatsumaDragonToken());
|
||||
effect.apply(game, source);
|
||||
for (UUID tokenId : effect.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield
|
||||
Permanent tokenPermanent = game.getPermanent(tokenId);
|
||||
if (tokenPermanent != null) {
|
||||
FixedTarget fixedTarget = new FixedTarget(tokenPermanent, game);
|
||||
Effect returnEffect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false);
|
||||
returnEffect.setTargetPointer(new FixedTarget(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId())));
|
||||
DelayedTriggeredAbility delayedAbility = new TatsumaTheDragonsFangTriggeredAbility(fixedTarget, returnEffect);
|
||||
game.addDelayedTriggeredAbility(delayedAbility, source);
|
||||
}
|
||||
}
|
||||
|
||||
Token token = new TatsumaDragonToken();
|
||||
token.putOntoBattlefield(1, game, source);
|
||||
game.addDelayedTriggeredAbility(new TatsumaTheDragonsFangTriggeredAbility(token, source, game), source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class TatsumaTheDragonsFangTriggeredAbility extends DelayedTriggeredAbility {
|
||||
|
||||
protected FixedTarget fixedTarget;
|
||||
private final Set<UUID> tokens = new HashSet<>();
|
||||
|
||||
public TatsumaTheDragonsFangTriggeredAbility(FixedTarget fixedTarget, Effect effect) {
|
||||
super(effect, Duration.OneUse);
|
||||
this.fixedTarget = fixedTarget;
|
||||
public TatsumaTheDragonsFangTriggeredAbility(Token token, Ability source, Game game) {
|
||||
super(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false).setTargetPointer(new FixedTarget(new MageObjectReference(source, 1))), Duration.Custom, false, false);
|
||||
tokens.addAll(token.getLastAddedTokenIds());
|
||||
}
|
||||
|
||||
public TatsumaTheDragonsFangTriggeredAbility(final TatsumaTheDragonsFangTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.fixedTarget = ability.fixedTarget;
|
||||
tokens.addAll(ability.tokens);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -118,12 +109,7 @@ class TatsumaTheDragonsFangTriggeredAbility extends DelayedTriggeredAbility {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (((ZoneChangeEvent) event).isDiesEvent()) {
|
||||
if (fixedTarget.getFirst(game, this).equals(event.getTargetId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return ((ZoneChangeEvent) event).isDiesEvent() && tokens.contains(event.getTargetId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package org.mage.test.cards.single.chk;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class TatsumasaTheDragonsFangTest extends CardTestPlayerBase {
|
||||
|
||||
private static final String tatsumasa = "Tatsumasa, the Dragon's Fang";
|
||||
private static final String murder = "Murder";
|
||||
|
||||
@Test
|
||||
public void testTatsumasa() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 9);
|
||||
addCard(Zone.BATTLEFIELD, playerA, tatsumasa);
|
||||
addCard(Zone.HAND, playerA, murder);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{6}");
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, murder, "Dragon Spirit");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, tatsumasa, 1);
|
||||
assertGraveyardCount(playerA, murder, 1);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.costs.common;
|
||||
|
||||
import mage.MageObject;
|
||||
|
@ -6,9 +5,9 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.CostImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -17,46 +16,32 @@ import java.util.UUID;
|
|||
*/
|
||||
public class ExileSourceCost extends CostImpl {
|
||||
|
||||
private final boolean toUniqueExileZone;
|
||||
|
||||
public ExileSourceCost() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param toUniqueExileZone moves the card to a source object dependant
|
||||
* unique exile zone, so another effect of the same source object (e.g.
|
||||
* Deadeye Navigator) can identify the card
|
||||
*/
|
||||
public ExileSourceCost(boolean toUniqueExileZone) {
|
||||
public ExileSourceCost() {
|
||||
this.text = "exile {this}";
|
||||
this.toUniqueExileZone = toUniqueExileZone;
|
||||
}
|
||||
|
||||
public ExileSourceCost(ExileSourceCost cost) {
|
||||
super(cost);
|
||||
this.toUniqueExileZone = cost.toUniqueExileZone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
|
||||
MageObject sourceObject = ability.getSourceObject(game);
|
||||
Player controller = game.getPlayer(controllerId);
|
||||
if (controller != null && sourceObject instanceof Card) {
|
||||
UUID exileZoneId = null;
|
||||
String exileZoneName = "";
|
||||
if (toUniqueExileZone) {
|
||||
exileZoneId = CardUtil.getExileZoneId(game, ability.getSourceId(), ability.getSourceObjectZoneChangeCounter());
|
||||
exileZoneName = sourceObject.getName();
|
||||
game.getState().setValue(sourceObject.getId().toString(), ability.getSourceObjectZoneChangeCounter());
|
||||
}
|
||||
controller.moveCardToExileWithInfo((Card) sourceObject, exileZoneId, exileZoneName, source, game, game.getState().getZone(sourceObject.getId()), true);
|
||||
// 117.11. The actions performed when paying a cost may be modified by effects.
|
||||
// Even if they are, meaning the actions that are performed don't match the actions
|
||||
// that are called for, the cost has still been paid.
|
||||
// so return state here is not important because the user indended to exile the target anyway
|
||||
paid = true;
|
||||
if (controller == null || !(sourceObject instanceof Card)) {
|
||||
return paid;
|
||||
}
|
||||
controller.moveCards((Card) sourceObject, Zone.EXILED, source, game);
|
||||
// 117.11. The actions performed when paying a cost may be modified by effects.
|
||||
// Even if they are, meaning the actions that are performed don't match the actions
|
||||
// that are called for, the cost has still been paid.
|
||||
// so return state here is not important because the user indended to exile the target anyway
|
||||
paid = true;
|
||||
return paid;
|
||||
}
|
||||
|
||||
|
@ -69,5 +54,4 @@ public class ExileSourceCost extends CostImpl {
|
|||
public ExileSourceCost copy() {
|
||||
return new ExileSourceCost(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue