mirror of
https://github.com/magefree/mage.git
synced 2025-12-19 18:20:13 -08:00
create base class for flashback-like abilities
This commit is contained in:
parent
2fbd7624b9
commit
c261facd61
3 changed files with 202 additions and 323 deletions
|
|
@ -0,0 +1,197 @@
|
||||||
|
package mage.abilities.keyword;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.SpellAbility;
|
||||||
|
import mage.abilities.costs.Cost;
|
||||||
|
import mage.abilities.costs.Costs;
|
||||||
|
import mage.abilities.effects.ContinuousEffect;
|
||||||
|
import mage.abilities.effects.ReplacementEffectImpl;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.cards.ModalDoubleFacedCard;
|
||||||
|
import mage.cards.SplitCard;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.events.GameEvent;
|
||||||
|
import mage.game.events.ZoneChangeEvent;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.targetpointer.FixedTarget;
|
||||||
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for Flashback and Harmonize and any future ability which works similarly
|
||||||
|
*
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public abstract class CastFromGraveyardAbility extends SpellAbility {
|
||||||
|
|
||||||
|
protected String abilityName;
|
||||||
|
private SpellAbility spellAbilityToResolve;
|
||||||
|
|
||||||
|
protected CastFromGraveyardAbility(Card card, Cost cost, SpellAbilityCastMode spellAbilityCastMode) {
|
||||||
|
super(null, "", Zone.GRAVEYARD, SpellAbilityType.BASE_ALTERNATE, spellAbilityCastMode);
|
||||||
|
this.setAdditionalCostsRuleVisible(false);
|
||||||
|
this.name = spellAbilityCastMode + " " + cost.getText();
|
||||||
|
this.addCost(cost);
|
||||||
|
this.timing = card.isSorcery() ? TimingRule.SORCERY : TimingRule.INSTANT;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CastFromGraveyardAbility(final CastFromGraveyardAbility ability) {
|
||||||
|
super(ability);
|
||||||
|
this.abilityName = ability.abilityName;
|
||||||
|
this.spellAbilityToResolve = ability.spellAbilityToResolve;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||||
|
// flashback ability dynamicly added to all card's parts (split cards)
|
||||||
|
if (!super.canActivate(playerId, game).canActivate()) {
|
||||||
|
return ActivationStatus.getFalse();
|
||||||
|
}
|
||||||
|
Card card = game.getCard(getSourceId());
|
||||||
|
if (card == null) {
|
||||||
|
return ActivationStatus.getFalse();
|
||||||
|
}
|
||||||
|
// Card must be in the graveyard zone
|
||||||
|
if (game.getState().getZone(card.getId()) != Zone.GRAVEYARD) {
|
||||||
|
return ActivationStatus.getFalse();
|
||||||
|
}
|
||||||
|
// Cards with no Mana Costs cant't be flashbacked (e.g. Ancestral Vision)
|
||||||
|
if (card.getManaCost().isEmpty()) {
|
||||||
|
return ActivationStatus.getFalse();
|
||||||
|
}
|
||||||
|
// CastFromGraveyard can never cast a split card by Fuse, because Fuse only works from hand
|
||||||
|
// https://tappedout.net/mtg-questions/snapcaster-mage-and-flashback-on-a-fuse-card-one-or-both-halves-legal-targets/
|
||||||
|
if (card instanceof SplitCard) {
|
||||||
|
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||||
|
return ((SplitCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||||
|
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||||
|
return ((SplitCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||||
|
}
|
||||||
|
} else if (card instanceof ModalDoubleFacedCard) {
|
||||||
|
if (((ModalDoubleFacedCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||||
|
return ((ModalDoubleFacedCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||||
|
} else if (((ModalDoubleFacedCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||||
|
return ((ModalDoubleFacedCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return card.getSpellAbility().canActivate(playerId, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SpellAbility getSpellAbilityToResolve(Game game) {
|
||||||
|
Card card = game.getCard(getSourceId());
|
||||||
|
if (card == null || spellAbilityToResolve != null) {
|
||||||
|
return spellAbilityToResolve;
|
||||||
|
}
|
||||||
|
SpellAbility spellAbilityCopy;
|
||||||
|
if (card instanceof SplitCard) {
|
||||||
|
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||||
|
spellAbilityCopy = ((SplitCard) card).getLeftHalfCard().getSpellAbility().copy();
|
||||||
|
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||||
|
spellAbilityCopy = ((SplitCard) card).getRightHalfCard().getSpellAbility().copy();
|
||||||
|
} else {
|
||||||
|
spellAbilityCopy = null;
|
||||||
|
}
|
||||||
|
} else if (card instanceof ModalDoubleFacedCard) {
|
||||||
|
if (((ModalDoubleFacedCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||||
|
spellAbilityCopy = ((ModalDoubleFacedCard) card).getLeftHalfCard().getSpellAbility().copy();
|
||||||
|
} else if (((ModalDoubleFacedCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||||
|
spellAbilityCopy = ((ModalDoubleFacedCard) card).getRightHalfCard().getSpellAbility().copy();
|
||||||
|
} else {
|
||||||
|
spellAbilityCopy = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spellAbilityCopy = card.getSpellAbility().copy();
|
||||||
|
}
|
||||||
|
if (spellAbilityCopy == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
spellAbilityCopy.setId(this.getId());
|
||||||
|
spellAbilityCopy.clearManaCosts();
|
||||||
|
spellAbilityCopy.clearManaCostsToPay();
|
||||||
|
spellAbilityCopy.addCost(this.getCosts().copy());
|
||||||
|
spellAbilityCopy.addCost(this.getManaCosts().copy());
|
||||||
|
spellAbilityCopy.setSpellAbilityCastMode(this.getSpellAbilityCastMode());
|
||||||
|
spellAbilityToResolve = spellAbilityCopy;
|
||||||
|
ContinuousEffect effect = new CastFromGraveyardReplacementEffect();
|
||||||
|
effect.setTargetPointer(new FixedTarget(getSourceId(), game.getState().getZoneChangeCounter(getSourceId())));
|
||||||
|
game.addEffect(effect, this);
|
||||||
|
return spellAbilityToResolve;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Costs<Cost> getCosts() {
|
||||||
|
if (spellAbilityToResolve == null) {
|
||||||
|
return super.getCosts();
|
||||||
|
}
|
||||||
|
return spellAbilityToResolve.getCosts();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRule(boolean all) {
|
||||||
|
return this.getRule();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for split card in PlayerImpl method:
|
||||||
|
* getOtherUseableActivatedAbilities
|
||||||
|
*
|
||||||
|
* @param abilityName
|
||||||
|
*/
|
||||||
|
public CastFromGraveyardAbility setAbilityName(String abilityName) {
|
||||||
|
this.abilityName = abilityName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CastFromGraveyardReplacementEffect extends ReplacementEffectImpl {
|
||||||
|
|
||||||
|
public CastFromGraveyardReplacementEffect() {
|
||||||
|
super(Duration.OneUse, Outcome.Exile);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CastFromGraveyardReplacementEffect(final CastFromGraveyardReplacementEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CastFromGraveyardReplacementEffect copy() {
|
||||||
|
return new CastFromGraveyardReplacementEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||||
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
|
if (controller == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Card card = game.getCard(event.getTargetId());
|
||||||
|
if (card == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
discard();
|
||||||
|
return controller.moveCards(
|
||||||
|
card, Zone.EXILED, source, game, false,
|
||||||
|
false, false, event.getAppliedEffects()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checksEventType(GameEvent event, Game game) {
|
||||||
|
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||||
|
UUID cardId = CardUtil.getMainCardId(game, source.getSourceId()); // for split cards
|
||||||
|
if (!cardId.equals(event.getTargetId())
|
||||||
|
|| ((ZoneChangeEvent) event).getFromZone() != Zone.STACK
|
||||||
|
|| ((ZoneChangeEvent) event).getToZone() == Zone.EXILED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int zcc = game.getState().getZoneChangeCounter(cardId);
|
||||||
|
return ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == zcc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,23 +1,8 @@
|
||||||
package mage.abilities.keyword;
|
package mage.abilities.keyword;
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
|
||||||
import mage.abilities.SpellAbility;
|
|
||||||
import mage.abilities.costs.Cost;
|
import mage.abilities.costs.Cost;
|
||||||
import mage.abilities.costs.Costs;
|
|
||||||
import mage.abilities.effects.ContinuousEffect;
|
|
||||||
import mage.abilities.effects.ReplacementEffectImpl;
|
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.ModalDoubleFacedCard;
|
import mage.constants.SpellAbilityCastMode;
|
||||||
import mage.cards.SplitCard;
|
|
||||||
import mage.constants.*;
|
|
||||||
import mage.game.Game;
|
|
||||||
import mage.game.events.GameEvent;
|
|
||||||
import mage.game.events.ZoneChangeEvent;
|
|
||||||
import mage.players.Player;
|
|
||||||
import mage.target.targetpointer.FixedTarget;
|
|
||||||
import mage.util.CardUtil;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 702.32. Flashback
|
* 702.32. Flashback
|
||||||
|
|
@ -33,106 +18,14 @@ import java.util.UUID;
|
||||||
*
|
*
|
||||||
* @author nantuko
|
* @author nantuko
|
||||||
*/
|
*/
|
||||||
public class FlashbackAbility extends SpellAbility {
|
public class FlashbackAbility extends CastFromGraveyardAbility {
|
||||||
|
|
||||||
private String abilityName;
|
|
||||||
private SpellAbility spellAbilityToResolve;
|
|
||||||
|
|
||||||
public FlashbackAbility(Card card, Cost cost) {
|
public FlashbackAbility(Card card, Cost cost) {
|
||||||
super(null, "", Zone.GRAVEYARD, SpellAbilityType.BASE_ALTERNATE, SpellAbilityCastMode.FLASHBACK);
|
super(card, cost, SpellAbilityCastMode.FLASHBACK);
|
||||||
this.setAdditionalCostsRuleVisible(false);
|
|
||||||
this.name = "Flashback " + cost.getText();
|
|
||||||
this.addCost(cost);
|
|
||||||
this.timing = card.isSorcery() ? TimingRule.SORCERY : TimingRule.INSTANT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlashbackAbility(final FlashbackAbility ability) {
|
protected FlashbackAbility(final FlashbackAbility ability) {
|
||||||
super(ability);
|
super(ability);
|
||||||
this.spellAbilityType = ability.spellAbilityType;
|
|
||||||
this.abilityName = ability.abilityName;
|
|
||||||
this.spellAbilityToResolve = ability.spellAbilityToResolve;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
|
||||||
// flashback ability dynamicly added to all card's parts (split cards)
|
|
||||||
if (super.canActivate(playerId, game).canActivate()) {
|
|
||||||
Card card = game.getCard(getSourceId());
|
|
||||||
if (card != null) {
|
|
||||||
// Card must be in the graveyard zone
|
|
||||||
if (game.getState().getZone(card.getId()) != Zone.GRAVEYARD) {
|
|
||||||
return ActivationStatus.getFalse();
|
|
||||||
}
|
|
||||||
// Cards with no Mana Costs cant't be flashbacked (e.g. Ancestral Vision)
|
|
||||||
if (card.getManaCost().isEmpty()) {
|
|
||||||
return ActivationStatus.getFalse();
|
|
||||||
}
|
|
||||||
// Flashback can never cast a split card by Fuse, because Fuse only works from hand
|
|
||||||
// https://tappedout.net/mtg-questions/snapcaster-mage-and-flashback-on-a-fuse-card-one-or-both-halves-legal-targets/
|
|
||||||
if (card instanceof SplitCard) {
|
|
||||||
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
|
||||||
return ((SplitCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
|
|
||||||
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
|
||||||
return ((SplitCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
|
|
||||||
}
|
|
||||||
} else if (card instanceof ModalDoubleFacedCard) {
|
|
||||||
if (((ModalDoubleFacedCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
|
||||||
return ((ModalDoubleFacedCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
|
|
||||||
} else if (((ModalDoubleFacedCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
|
||||||
return ((ModalDoubleFacedCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return card.getSpellAbility().canActivate(playerId, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ActivationStatus.getFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SpellAbility getSpellAbilityToResolve(Game game) {
|
|
||||||
Card card = game.getCard(getSourceId());
|
|
||||||
if (card != null) {
|
|
||||||
if (spellAbilityToResolve == null) {
|
|
||||||
SpellAbility spellAbilityCopy = null;
|
|
||||||
if (card instanceof SplitCard) {
|
|
||||||
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
|
||||||
spellAbilityCopy = ((SplitCard) card).getLeftHalfCard().getSpellAbility().copy();
|
|
||||||
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
|
||||||
spellAbilityCopy = ((SplitCard) card).getRightHalfCard().getSpellAbility().copy();
|
|
||||||
}
|
|
||||||
} else if (card instanceof ModalDoubleFacedCard) {
|
|
||||||
if (((ModalDoubleFacedCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
|
||||||
spellAbilityCopy = ((ModalDoubleFacedCard) card).getLeftHalfCard().getSpellAbility().copy();
|
|
||||||
} else if (((ModalDoubleFacedCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
|
||||||
spellAbilityCopy = ((ModalDoubleFacedCard) card).getRightHalfCard().getSpellAbility().copy();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
spellAbilityCopy = card.getSpellAbility().copy();
|
|
||||||
}
|
|
||||||
if (spellAbilityCopy == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
spellAbilityCopy.setId(this.getId());
|
|
||||||
spellAbilityCopy.clearManaCosts();
|
|
||||||
spellAbilityCopy.clearManaCostsToPay();
|
|
||||||
spellAbilityCopy.addCost(this.getCosts().copy());
|
|
||||||
spellAbilityCopy.addCost(this.getManaCosts().copy());
|
|
||||||
spellAbilityCopy.setSpellAbilityCastMode(this.getSpellAbilityCastMode());
|
|
||||||
spellAbilityToResolve = spellAbilityCopy;
|
|
||||||
ContinuousEffect effect = new FlashbackReplacementEffect();
|
|
||||||
effect.setTargetPointer(new FixedTarget(getSourceId(), game.getState().getZoneChangeCounter(getSourceId())));
|
|
||||||
game.addEffect(effect, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return spellAbilityToResolve;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Costs<Cost> getCosts() {
|
|
||||||
if (spellAbilityToResolve == null) {
|
|
||||||
return super.getCosts();
|
|
||||||
}
|
|
||||||
return spellAbilityToResolve.getCosts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -140,11 +33,6 @@ public class FlashbackAbility extends SpellAbility {
|
||||||
return new FlashbackAbility(this);
|
return new FlashbackAbility(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRule(boolean all) {
|
|
||||||
return this.getRule();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRule() {
|
public String getRule() {
|
||||||
StringBuilder sbRule = new StringBuilder("Flashback");
|
StringBuilder sbRule = new StringBuilder("Flashback");
|
||||||
|
|
@ -170,66 +58,4 @@ public class FlashbackAbility extends SpellAbility {
|
||||||
sbRule.append(" <i>(You may cast this card from your graveyard for its flashback cost. Then exile it.)</i>");
|
sbRule.append(" <i>(You may cast this card from your graveyard for its flashback cost. Then exile it.)</i>");
|
||||||
return sbRule.toString();
|
return sbRule.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for split card in PlayerImpl method:
|
|
||||||
* getOtherUseableActivatedAbilities
|
|
||||||
*
|
|
||||||
* @param abilityName
|
|
||||||
*/
|
|
||||||
public FlashbackAbility setAbilityName(String abilityName) {
|
|
||||||
this.abilityName = abilityName;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class FlashbackReplacementEffect extends ReplacementEffectImpl {
|
|
||||||
|
|
||||||
public FlashbackReplacementEffect() {
|
|
||||||
super(Duration.OneUse, Outcome.Exile);
|
|
||||||
staticText = "(If the flashback cost was paid, exile this card instead of putting it anywhere else any time it would leave the stack)";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected FlashbackReplacementEffect(final FlashbackReplacementEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FlashbackReplacementEffect copy() {
|
|
||||||
return new FlashbackReplacementEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
|
||||||
if (controller != null) {
|
|
||||||
Card card = game.getCard(event.getTargetId());
|
|
||||||
if (card != null) {
|
|
||||||
discard();
|
|
||||||
return controller.moveCards(
|
|
||||||
card, Zone.EXILED, source, game, false, false, false, event.getAppliedEffects());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean checksEventType(GameEvent event, Game game) {
|
|
||||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
|
||||||
UUID cardId = CardUtil.getMainCardId(game, source.getSourceId()); // for split cards
|
|
||||||
if (cardId.equals(event.getTargetId())
|
|
||||||
&& ((ZoneChangeEvent) event).getFromZone() == Zone.STACK
|
|
||||||
&& ((ZoneChangeEvent) event).getToZone() != Zone.EXILED) {
|
|
||||||
|
|
||||||
int zcc = game.getState().getZoneChangeCounter(cardId);
|
|
||||||
return ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == zcc;
|
|
||||||
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,29 +6,21 @@ import mage.abilities.Ability;
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.costs.Cost;
|
import mage.abilities.costs.Cost;
|
||||||
import mage.abilities.costs.Costs;
|
|
||||||
import mage.abilities.costs.VariableCostImpl;
|
import mage.abilities.costs.VariableCostImpl;
|
||||||
import mage.abilities.costs.VariableCostType;
|
import mage.abilities.costs.VariableCostType;
|
||||||
import mage.abilities.costs.common.TapTargetCost;
|
import mage.abilities.costs.common.TapTargetCost;
|
||||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
import mage.abilities.effects.ContinuousEffect;
|
|
||||||
import mage.abilities.effects.ReplacementEffectImpl;
|
|
||||||
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
|
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.ModalDoubleFacedCard;
|
|
||||||
import mage.cards.SplitCard;
|
|
||||||
import mage.constants.*;
|
import mage.constants.*;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.filter.common.FilterControlledPermanent;
|
import mage.filter.common.FilterControlledPermanent;
|
||||||
import mage.filter.predicate.permanent.PermanentIdPredicate;
|
import mage.filter.predicate.permanent.PermanentIdPredicate;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
|
||||||
import mage.game.events.ZoneChangeEvent;
|
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.target.common.TargetControlledPermanent;
|
import mage.target.common.TargetControlledPermanent;
|
||||||
import mage.target.targetpointer.FixedTarget;
|
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
@ -37,19 +29,15 @@ import java.util.UUID;
|
||||||
/**
|
/**
|
||||||
* @author TheElk801
|
* @author TheElk801
|
||||||
*/
|
*/
|
||||||
public class HarmonizeAbility extends SpellAbility {
|
public class HarmonizeAbility extends CastFromGraveyardAbility {
|
||||||
|
|
||||||
private String abilityName;
|
private String abilityName;
|
||||||
private SpellAbility spellAbilityToResolve;
|
private SpellAbility spellAbilityToResolve;
|
||||||
|
|
||||||
public HarmonizeAbility(Card card, String manaString) {
|
public HarmonizeAbility(Card card, String manaString) {
|
||||||
super(null, "", Zone.GRAVEYARD, SpellAbilityType.BASE_ALTERNATE, SpellAbilityCastMode.HARMONIZE);
|
super(card, new ManaCostsImpl<>(manaString), SpellAbilityCastMode.HARMONIZE);
|
||||||
this.setAdditionalCostsRuleVisible(false);
|
|
||||||
this.name = "Harmonize " + manaString;
|
|
||||||
this.addCost(new ManaCostsImpl<>(manaString));
|
|
||||||
this.addCost(new HarmonizeCost());
|
this.addCost(new HarmonizeCost());
|
||||||
this.addSubAbility(new SimpleStaticAbility(Zone.ALL, new HarmonizeCostReductionEffect()).setRuleVisible(false));
|
this.addSubAbility(new SimpleStaticAbility(Zone.ALL, new HarmonizeCostReductionEffect()).setRuleVisible(false));
|
||||||
this.timing = card.isSorcery() ? TimingRule.SORCERY : TimingRule.INSTANT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private HarmonizeAbility(final HarmonizeAbility ability) {
|
private HarmonizeAbility(final HarmonizeAbility ability) {
|
||||||
|
|
@ -61,94 +49,6 @@ public class HarmonizeAbility extends SpellAbility {
|
||||||
return new HarmonizeAbility(this);
|
return new HarmonizeAbility(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
|
||||||
// harmonize ability dynamicly added to all card's parts (split cards)
|
|
||||||
if (!super.canActivate(playerId, game).canActivate()) {
|
|
||||||
return ActivationStatus.getFalse();
|
|
||||||
}
|
|
||||||
Card card = game.getCard(getSourceId());
|
|
||||||
if (card == null) {
|
|
||||||
return ActivationStatus.getFalse();
|
|
||||||
}
|
|
||||||
// Card must be in the graveyard zone
|
|
||||||
if (game.getState().getZone(card.getId()) != Zone.GRAVEYARD) {
|
|
||||||
return ActivationStatus.getFalse();
|
|
||||||
}
|
|
||||||
// Cards with no Mana Costs cant't be harmonized (e.g. Ancestral Vision)
|
|
||||||
if (card.getManaCost().isEmpty()) {
|
|
||||||
return ActivationStatus.getFalse();
|
|
||||||
}
|
|
||||||
// Harmonize can never cast a split card by Fuse, because Fuse only works from hand
|
|
||||||
if (card instanceof SplitCard) {
|
|
||||||
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
|
||||||
return ((SplitCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
|
|
||||||
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
|
||||||
return ((SplitCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
|
|
||||||
}
|
|
||||||
} else if (card instanceof ModalDoubleFacedCard) {
|
|
||||||
if (((ModalDoubleFacedCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
|
||||||
return ((ModalDoubleFacedCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
|
|
||||||
} else if (((ModalDoubleFacedCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
|
||||||
return ((ModalDoubleFacedCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return card.getSpellAbility().canActivate(playerId, game);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SpellAbility getSpellAbilityToResolve(Game game) {
|
|
||||||
Card card = game.getCard(getSourceId());
|
|
||||||
if (card == null) {
|
|
||||||
return spellAbilityToResolve;
|
|
||||||
}
|
|
||||||
if (spellAbilityToResolve != null) {
|
|
||||||
return spellAbilityToResolve;
|
|
||||||
}
|
|
||||||
SpellAbility spellAbilityCopy;
|
|
||||||
if (card instanceof SplitCard) {
|
|
||||||
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
|
||||||
spellAbilityCopy = ((SplitCard) card).getLeftHalfCard().getSpellAbility().copy();
|
|
||||||
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
|
||||||
spellAbilityCopy = ((SplitCard) card).getRightHalfCard().getSpellAbility().copy();
|
|
||||||
} else {
|
|
||||||
spellAbilityCopy = null;
|
|
||||||
}
|
|
||||||
} else if (card instanceof ModalDoubleFacedCard) {
|
|
||||||
if (((ModalDoubleFacedCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
|
||||||
spellAbilityCopy = ((ModalDoubleFacedCard) card).getLeftHalfCard().getSpellAbility().copy();
|
|
||||||
} else if (((ModalDoubleFacedCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
|
||||||
spellAbilityCopy = ((ModalDoubleFacedCard) card).getRightHalfCard().getSpellAbility().copy();
|
|
||||||
} else {
|
|
||||||
spellAbilityCopy = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
spellAbilityCopy = card.getSpellAbility().copy();
|
|
||||||
}
|
|
||||||
if (spellAbilityCopy == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
spellAbilityCopy.setId(this.getId());
|
|
||||||
spellAbilityCopy.clearManaCosts();
|
|
||||||
spellAbilityCopy.clearManaCostsToPay();
|
|
||||||
spellAbilityCopy.addCost(this.getCosts().copy());
|
|
||||||
spellAbilityCopy.addCost(this.getManaCosts().copy());
|
|
||||||
spellAbilityCopy.setSpellAbilityCastMode(this.getSpellAbilityCastMode());
|
|
||||||
spellAbilityToResolve = spellAbilityCopy;
|
|
||||||
ContinuousEffect effect = new HarmonizeReplacementEffect();
|
|
||||||
effect.setTargetPointer(new FixedTarget(getSourceId(), game.getState().getZoneChangeCounter(getSourceId())));
|
|
||||||
game.addEffect(effect, this);
|
|
||||||
return spellAbilityToResolve;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Costs<Cost> getCosts() {
|
|
||||||
if (spellAbilityToResolve == null) {
|
|
||||||
return super.getCosts();
|
|
||||||
}
|
|
||||||
return spellAbilityToResolve.getCosts();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRule() {
|
public String getRule() {
|
||||||
return name + " <i>(You may cast this card from your graveyard for its harmonize cost. " +
|
return name + " <i>(You may cast this card from your graveyard for its harmonize cost. " +
|
||||||
|
|
@ -157,50 +57,6 @@ public class HarmonizeAbility extends SpellAbility {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class HarmonizeReplacementEffect extends ReplacementEffectImpl {
|
|
||||||
|
|
||||||
public HarmonizeReplacementEffect() {
|
|
||||||
super(Duration.OneUse, Outcome.Exile);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected HarmonizeReplacementEffect(final HarmonizeReplacementEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HarmonizeReplacementEffect copy() {
|
|
||||||
return new HarmonizeReplacementEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
|
||||||
Card card = game.getCard(event.getTargetId());
|
|
||||||
if (controller == null || card == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
discard();
|
|
||||||
return controller.moveCards(
|
|
||||||
card, Zone.EXILED, source, game, false,
|
|
||||||
false, false, event.getAppliedEffects()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean checksEventType(GameEvent event, Game game) {
|
|
||||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
|
||||||
UUID cardId = CardUtil.getMainCardId(game, source.getSourceId()); // for split cards
|
|
||||||
return event.getTargetId().equals(cardId)
|
|
||||||
&& ((ZoneChangeEvent) event).getFromZone().match(Zone.STACK)
|
|
||||||
&& !((ZoneChangeEvent) event).getToZone().match(Zone.EXILED)
|
|
||||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(cardId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HarmonizeCostReductionEffect extends CostModificationEffectImpl {
|
class HarmonizeCostReductionEffect extends CostModificationEffectImpl {
|
||||||
|
|
||||||
HarmonizeCostReductionEffect() {
|
HarmonizeCostReductionEffect() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue