This commit is contained in:
jeffwadsworth 2019-12-31 15:40:13 -06:00
parent 06657e4980
commit 13cb86d69f
9 changed files with 152 additions and 78 deletions

View file

@ -1,4 +1,3 @@
package mage.abilities.effects.common;
import mage.MageObjectReference;
@ -35,8 +34,13 @@ public class PlayTargetWithoutPayingManaEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Card target = (Card) game.getObject(source.getFirstTarget());
if (controller != null && target != null) {
return controller.cast(target.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
if (controller != null
&& target != null) {
game.getState().setValue("PlayFromNotOwnHandZone" + target.getId(), Boolean.TRUE);
Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(target, game, true),
game, true, new MageObjectReference(source.getSourceObject(game), game));
game.getState().setValue("PlayFromNotOwnHandZone" + target.getId(), null);
return cardWasCast;
}
return false;
}

View file

@ -1,4 +1,3 @@
package mage.abilities.effects.common.cost;
import mage.MageObjectReference;
@ -40,7 +39,8 @@ public class CastWithoutPayingManaCostEffect extends OneShotEffect {
public CastWithoutPayingManaCostEffect(DynamicValue maxCost) {
super(Outcome.PlayForFree);
this.manaCost = maxCost;
this.staticText = "you may cast a card with converted mana cost " + maxCost + " or less from your hand without paying its mana cost";
this.staticText = "you may cast a card with converted mana cost "
+ maxCost + " or less from your hand without paying its mana cost";
}
public CastWithoutPayingManaCostEffect(final CastWithoutPayingManaCostEffect effect) {
@ -56,21 +56,24 @@ public class CastWithoutPayingManaCostEffect extends OneShotEffect {
return false;
}
int cmc = manaCost.calculate(game, source, this);
FilterCard filter = new FilterNonlandCard("card with converted mana cost " + cmc + " or less from your hand");
FilterCard filter = new FilterNonlandCard("card with converted mana cost "
+ cmc + " or less from your hand");
filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, cmc + 1));
Target target = new TargetCardInHand(filter);
if (target.canChoose(source.getSourceId(), controller.getId(), game)
&& controller.chooseUse(outcome, "Cast a card with converted mana cost " + cmc
+ " or less from your hand without paying its mana cost?", source, game)) {
&& controller.chooseUse(Outcome.PlayForFree, "Cast a card with converted mana cost " + cmc
+ " or less from your hand without paying its mana cost?", source, game)) {
Card cardToCast = null;
boolean cancel = false;
while (controller.canRespond() && !cancel) {
if (controller.chooseTarget(outcome, target, source, game)) {
while (controller.canRespond()
&& !cancel) {
if (controller.chooseTarget(Outcome.PlayForFree, target, source, game)) {
cardToCast = game.getCard(target.getFirstTarget());
if (cardToCast != null) {
if (cardToCast.getSpellAbility() == null) {
Logger.getLogger(CastWithoutPayingManaCostEffect.class).fatal("Card: " + cardToCast.getName() + " is no land and has no spell ability!");
Logger.getLogger(CastWithoutPayingManaCostEffect.class).fatal("Card: "
+ cardToCast.getName() + " is no land and has no spell ability!");
cancel = true;
}
if (cardToCast.getSpellAbility().canChooseTarget(game)) {
@ -82,7 +85,10 @@ public class CastWithoutPayingManaCostEffect extends OneShotEffect {
}
}
if (cardToCast != null) {
controller.cast(cardToCast.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), Boolean.TRUE);
controller.cast(controller.chooseAbilityForCast(cardToCast, game, true),
game, true, new MageObjectReference(source.getSourceObject(game), game));
game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), null);
}
}
return true;

View file

@ -20,8 +20,11 @@ import mage.players.Player;
public class CascadeAbility extends TriggeredAbilityImpl {
//20091005 - 702.82
private static final String REMINDERTEXT = " <i>(When you cast this spell, exile cards from the top of your library until you exile a nonland card that costs less."
+ " You may cast it without paying its mana cost. Put the exiled cards on the bottom in a random order.)</i>";
private static final String REMINDERTEXT = " <i>(When you cast this spell, "
+ "exile cards from the top of your library until you exile a "
+ "nonland card that costs less."
+ " You may cast it without paying its mana cost. "
+ "Put the exiled cards on the bottom in a random order.)</i>";
private boolean withReminder;
public CascadeAbility() {
@ -46,7 +49,8 @@ public class CascadeAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Spell spell = game.getStack().getSpell(event.getTargetId());
return spell != null && spell.getSourceId().equals(this.getSourceId());
return spell != null
&& spell.getSourceId().equals(this.getSourceId());
}
@Override
@ -82,7 +86,8 @@ class CascadeEffect extends OneShotEffect {
if (controller == null) {
return false;
}
ExileZone exile = game.getExile().createZone(source.getSourceId(), controller.getName() + " Cascade");
ExileZone exile = game.getExile().createZone(source.getSourceId(),
controller.getName() + " Cascade");
card = game.getCard(source.getSourceId());
if (card == null) {
return false;
@ -102,7 +107,10 @@ class CascadeEffect extends OneShotEffect {
if (card != null) {
if (controller.chooseUse(outcome, "Use cascade effect on " + card.getLogName() + '?', source, game)) {
controller.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE);
controller.cast(controller.chooseAbilityForCast(card, game, true),
game, true, new MageObjectReference(source.getSourceObject(game), game));
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null);
}
}
// Move the remaining cards to the buttom of the library in a random order

View file

@ -3,7 +3,6 @@ package mage.abilities.keyword;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.MyTurnCondition;
import mage.abilities.effects.OneShotEffect;
@ -64,7 +63,9 @@ class ReboundCastFromHandReplacementEffect extends ReplacementEffectImpl {
ReboundCastFromHandReplacementEffect() {
super(Duration.WhileOnStack, Outcome.Benefit);
this.staticText = "Rebound <i>(If you cast this spell from your hand, exile it as it resolves. At the beginning of your next upkeep, you may cast this card from exile without paying its mana cost.)</i>";
this.staticText = "Rebound <i>(If you cast this spell from your hand, "
+ "exile it as it resolves. At the beginning of your next upkeep, "
+ "you may cast this card from exile without paying its mana cost.)</i>";
}
ReboundCastFromHandReplacementEffect(ReboundCastFromHandReplacementEffect effect) {
@ -83,7 +84,8 @@ class ReboundCastFromHandReplacementEffect extends ReplacementEffectImpl {
&& event.getSourceId() != null
&& event.getSourceId().equals(source.getSourceId())) { // if countered the source.sourceId is different or null if it fizzles
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell != null && spell.getFromZone() == Zone.HAND) {
if (spell != null
&& spell.getFromZone() == Zone.HAND) {
return true;
}
}
@ -101,10 +103,12 @@ class ReboundCastFromHandReplacementEffect extends ReplacementEffectImpl {
Player player = game.getPlayer(sourceCard.getOwnerId());
if (player != null) {
// Add the delayed triggered effect
ReboundEffectCastFromExileDelayedTrigger trigger = new ReboundEffectCastFromExileDelayedTrigger(source.getSourceId(), source.getSourceId());
ReboundEffectCastFromExileDelayedTrigger trigger
= new ReboundEffectCastFromExileDelayedTrigger(source.getSourceId(), source.getSourceId());
game.addDelayedTriggeredAbility(trigger, source);
player.moveCardToExileWithInfo(sourceCard, sourceCard.getId(), player.getName() + " Rebound", source.getSourceId(), game, Zone.STACK, true);
player.moveCardToExileWithInfo(sourceCard, sourceCard.getId(),
player.getName() + " Rebound", source.getSourceId(), game, Zone.STACK, true);
return true;
}
}
@ -160,10 +164,11 @@ class ReboundEffectCastFromExileDelayedTrigger extends DelayedTriggeredAbility {
*/
class ReboundCastSpellFromExileEffect extends OneShotEffect {
private static String castFromExileText = "Rebound - You may cast {this} from exile without paying its mana cost";
private static String castFromExileText = "Rebound - You may cast {this} "
+ "from exile without paying its mana cost";
ReboundCastSpellFromExileEffect() {
super(Outcome.Benefit);
super(Outcome.PlayForFree);
staticText = castFromExileText;
}
@ -174,16 +179,19 @@ class ReboundCastSpellFromExileEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
ExileZone zone = game.getExile().getExileZone(source.getSourceId());
if (zone == null || zone.isEmpty()) {
if (zone == null
|| zone.isEmpty()) {
return false;
}
Card reboundCard = zone.get(source.getSourceId(), game);
Player player = game.getPlayer(source.getControllerId());
if (player != null && reboundCard != null) {
SpellAbility ability = reboundCard.getSpellAbility();
player.cast(ability, game, true, new MageObjectReference(source.getSourceObject(game), game));
zone.remove(reboundCard.getId());
return true;
if (player != null
&& reboundCard != null) {
game.getState().setValue("PlayFromNotOwnHandZone" + reboundCard.getId(), Boolean.TRUE);
Boolean cardWasCast = player.cast(player.chooseAbilityForCast(reboundCard, game, true),
game, true, new MageObjectReference(source.getSourceObject(game), game));
game.getState().setValue("PlayFromNotOwnHandZone" + reboundCard.getId(), null);
return cardWasCast;
}
return false;
}

View file

@ -1,4 +1,3 @@
package mage.abilities.keyword;
import java.util.ArrayList;
@ -97,10 +96,9 @@ import mage.target.targetpointer.FixedTarget;
* has enough mana in their mana pool to pay the cost, that player must do so.
* If the player can't possibly pay the cost, the card remains removed from the
* game. However, if the player has the means to produce enough mana to pay the
* cost, then they have a choice: The player may play the spell, produce
* mana, and pay the cost. Or the player may choose to play no mana abilities,
* thus making the card impossible to play because the additional mana can't be
* paid.
* cost, then they have a choice: The player may play the spell, produce mana,
* and pay the cost. Or the player may choose to play no mana abilities, thus
* making the card impossible to play because the additional mana can't be paid.
*
* A creature played via suspend comes into play with haste. It still has haste
* after the first turn it's in play as long as the same player controls it. As
@ -140,15 +138,19 @@ public class SuspendAbility extends SpecialAction {
}
StringBuilder sb = new StringBuilder("Suspend ");
if (cost != null) {
sb.append(suspend == Integer.MAX_VALUE ? "X" : suspend).append("&mdash;").append(cost.getText()).append(suspend == Integer.MAX_VALUE ? ". X can't be 0" : "");
sb.append(suspend == Integer.MAX_VALUE ? "X" : suspend).append("&mdash;").append(cost.getText()).append(suspend
== Integer.MAX_VALUE ? ". X can't be 0" : "");
if (!shortRule) {
sb.append(" <i>(Rather than cast this card from your hand, pay ")
.append(cost.getText())
.append(" and exile it with ")
.append((suspend == 1 ? "a time counter" : (suspend == Integer.MAX_VALUE ? "X time counters" : suspend + " time counters")))
.append((suspend == 1 ? "a time counter" : (suspend == Integer.MAX_VALUE
? "X time counters" : suspend + " time counters")))
.append(" on it.")
.append(" At the beginning of your upkeep, remove a time counter. When the last is removed, cast it without paying its mana cost.")
.append(card.isCreature() ? " If you play it this way and it's a creature, it gains haste until you lose control of it." : "")
.append(" At the beginning of your upkeep, remove a time counter. "
+ "When the last is removed, cast it without paying its mana cost.")
.append(card.isCreature() ? " If you play it this way and it's a creature, "
+ "it gains haste until you lose control of it." : "")
.append(")</i>");
}
if (card.getManaCost().isEmpty()) {
@ -174,7 +176,8 @@ public class SuspendAbility extends SpecialAction {
ability.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(card, ability);
SuspendBeginningOfUpkeepInterveningIfTriggeredAbility ability1 = new SuspendBeginningOfUpkeepInterveningIfTriggeredAbility();
SuspendBeginningOfUpkeepInterveningIfTriggeredAbility ability1 =
new SuspendBeginningOfUpkeepInterveningIfTriggeredAbility();
ability1.setSourceId(card.getId());
ability1.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(card, ability1);
@ -211,7 +214,8 @@ public class SuspendAbility extends SpecialAction {
MageObject object = game.getObject(sourceId);
return new ActivationStatus(object.isInstant()
|| object.hasAbility(FlashAbility.getInstance().getId(), game)
|| null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game)
|| null != game.getContinuousEffects().asThough(sourceId,
AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game)
|| game.canPlaySorcery(playerId), null);
}
@ -237,7 +241,8 @@ class SuspendExileEffect extends OneShotEffect {
public SuspendExileEffect(int suspend) {
super(Outcome.PutCardInPlay);
this.staticText = new StringBuilder("Suspend ").append(suspend == Integer.MAX_VALUE ? "X" : suspend).toString();
this.staticText = new StringBuilder("Suspend ").append(suspend
== Integer.MAX_VALUE ? "X" : suspend).toString();
this.suspend = suspend;
}
@ -257,7 +262,8 @@ class SuspendExileEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
if (card != null && controller != null) {
UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game);
if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " + controller.getName(), source.getSourceId(), game, Zone.HAND, true)) {
if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of "
+ controller.getName(), source.getSourceId(), game, Zone.HAND, true)) {
if (suspend == Integer.MAX_VALUE) {
suspend = source.getManaCostsToPay().getX();
}
@ -292,7 +298,8 @@ class SuspendPlayCardAbility extends TriggeredAbilityImpl {
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(getSourceId())) {
Card card = game.getCard(getSourceId());
if (card != null && game.getState().getZone(card.getId()) == Zone.EXILED
if (card != null
&& game.getState().getZone(card.getId()) == Zone.EXILED
&& card.getCounters(game).getCount(CounterType.TIME) == 0) {
return true;
}
@ -314,7 +321,7 @@ class SuspendPlayCardAbility extends TriggeredAbilityImpl {
class SuspendPlayCardEffect extends OneShotEffect {
public SuspendPlayCardEffect() {
super(Outcome.PutCardInPlay);
super(Outcome.PlayForFree);
this.staticText = "play it without paying its mana cost if able. If you can't, it remains removed from the game";
}
@ -343,7 +350,8 @@ class SuspendPlayCardEffect extends OneShotEffect {
}
if (!abilitiesToRemove.isEmpty()) {
for (Ability ability : card.getAbilities()) {
if (ability instanceof SuspendBeginningOfUpkeepInterveningIfTriggeredAbility || ability instanceof SuspendPlayCardAbility) {
if (ability instanceof SuspendBeginningOfUpkeepInterveningIfTriggeredAbility
|| ability instanceof SuspendPlayCardAbility) {
abilitiesToRemove.add(ability);
}
}
@ -351,7 +359,11 @@ class SuspendPlayCardEffect extends OneShotEffect {
card.getAbilities().removeAll(abilitiesToRemove);
}
// cast the card for free
if (player.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game))) {
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE);
Boolean cardWasCast = player.cast(player.chooseAbilityForCast(card, game, true),
game, true, new MageObjectReference(source.getSourceObject(game), game));
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null);
if (cardWasCast) {
if (card.isCreature()) {
ContinuousEffect effect = new GainHasteEffect();
effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game) + 1));
@ -397,7 +409,8 @@ class GainHasteEffect extends ContinuousEffectImpl {
}
return true;
}
if (game.getState().getZoneChangeCounter(((FixedTarget) getTargetPointer()).getTarget()) >= ((FixedTarget) getTargetPointer()).getZoneChangeCounter()) {
if (game.getState().getZoneChangeCounter(((FixedTarget) getTargetPointer()).getTarget())
>= ((FixedTarget) getTargetPointer()).getZoneChangeCounter()) {
this.discard();
}
return false;
@ -408,7 +421,8 @@ class GainHasteEffect extends ContinuousEffectImpl {
class SuspendBeginningOfUpkeepInterveningIfTriggeredAbility extends ConditionalInterveningIfTriggeredAbility {
public SuspendBeginningOfUpkeepInterveningIfTriggeredAbility() {
super(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()), TargetController.YOU, false),
super(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()),
TargetController.YOU, false),
SuspendedCondition.instance,
"At the beginning of your upkeep, if this card ({this}) is suspended, remove a time counter from it.");
this.setRuleVisible(false);