mirror of
https://github.com/correl/mage.git
synced 2024-12-24 11:50:45 +00:00
* FlashbackAbility - Fixed that for flashbacked spells additional optional costs did not work (e.g. Buyback, Replicate, Kicker).
This commit is contained in:
parent
bf260cd9c6
commit
b4dcddd0a9
7 changed files with 79 additions and 28 deletions
|
@ -41,6 +41,8 @@ import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||||
import mage.filter.predicate.permanent.ControllerPredicate;
|
import mage.filter.predicate.permanent.ControllerPredicate;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.stack.Spell;
|
import mage.game.stack.Spell;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.Target;
|
||||||
import mage.target.TargetSpell;
|
import mage.target.TargetSpell;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,7 +68,9 @@ public class IncreasingVengeance extends CardImpl<IncreasingVengeance> {
|
||||||
|
|
||||||
// Copy target instant or sorcery spell you control. If Increasing Vengeance was cast from a graveyard, copy that spell twice instead. You may choose new targets for the copies.
|
// Copy target instant or sorcery spell you control. If Increasing Vengeance was cast from a graveyard, copy that spell twice instead. You may choose new targets for the copies.
|
||||||
this.getSpellAbility().addEffect(new IncreasingVengeanceEffect());
|
this.getSpellAbility().addEffect(new IncreasingVengeanceEffect());
|
||||||
this.getSpellAbility().addTarget(new TargetSpell(filter));
|
Target target = new TargetSpell(filter);
|
||||||
|
target.setRequired(true);
|
||||||
|
this.getSpellAbility().addTarget(target);
|
||||||
|
|
||||||
// Flashback {3}{R}{R}
|
// Flashback {3}{R}{R}
|
||||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{3}{R}{R}"), TimingRule.INSTANT));
|
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{3}{R}{R}"), TimingRule.INSTANT));
|
||||||
|
@ -95,24 +99,29 @@ class IncreasingVengeanceEffect extends OneShotEffect<IncreasingVengeanceEffect>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
if (spell != null) {
|
if (controller != null) {
|
||||||
Spell copy = spell.copySpell();
|
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
|
||||||
copy.setControllerId(source.getControllerId());
|
if (spell != null) {
|
||||||
copy.setCopiedSpell(true);
|
Spell copy = spell.copySpell();
|
||||||
game.getStack().push(copy);
|
copy.setControllerId(source.getControllerId());
|
||||||
copy.chooseNewTargets(game, source.getControllerId());
|
copy.setCopiedSpell(true);
|
||||||
Spell sourceSpell = (Spell) game.getStack().getStackObject(source.getSourceId());
|
game.getStack().push(copy);
|
||||||
if (sourceSpell != null) {
|
copy.chooseNewTargets(game, source.getControllerId());
|
||||||
if (sourceSpell.getFromZone() == Zone.GRAVEYARD) {
|
game.informPlayers(new StringBuilder(controller.getName()).append(copy.getActivatedMessage(game)).toString());
|
||||||
copy = spell.copySpell();
|
Spell sourceSpell = (Spell) game.getStack().getStackObject(source.getSourceId());
|
||||||
copy.setControllerId(source.getControllerId());
|
if (sourceSpell != null) {
|
||||||
copy.setCopiedSpell(true);
|
if (sourceSpell.getFromZone() == Zone.GRAVEYARD) {
|
||||||
game.getStack().push(copy);
|
copy = spell.copySpell();
|
||||||
copy.chooseNewTargets(game, source.getControllerId());
|
copy.setControllerId(source.getControllerId());
|
||||||
|
copy.setCopiedSpell(true);
|
||||||
|
game.getStack().push(copy);
|
||||||
|
copy.chooseNewTargets(game, source.getControllerId());
|
||||||
|
game.informPlayers(new StringBuilder(controller.getName()).append(copy.getActivatedMessage(game)).toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,11 @@ public class IncreasingCardsTest extends CardTestPlayerBase {
|
||||||
assertExileCount("Increasing Ambition", 1);
|
assertExileCount("Increasing Ambition", 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// Increasing Confusion {X}{U}
|
||||||
|
// Sorcery
|
||||||
|
// Target player puts the top X cards of his or her library into his or her graveyard.
|
||||||
|
// If Increasing Confusion was cast from a graveyard, that player puts twice that many
|
||||||
|
// cards into his or her graveyard instead.
|
||||||
@Test
|
@Test
|
||||||
public void testIncreasingConfusion() {
|
public void testIncreasingConfusion() {
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
|
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
|
||||||
|
@ -102,6 +106,11 @@ public class IncreasingCardsTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Increasing Vengeance
|
||||||
|
// Instant
|
||||||
|
// Copy target instant or sorcery spell you control. If Increasing Vengeance was cast from a graveyard, copy that spell twice instead. You may choose new targets for the copies.
|
||||||
|
// Flashback {3}{R}{R} (You may cast this card from your graveyard for its flashback cost. Then exile it.)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIncreasingVengeance() {
|
public void testIncreasingVengeance() {
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
|
||||||
|
|
|
@ -49,6 +49,7 @@ import mage.abilities.effects.Effect;
|
||||||
import mage.abilities.effects.Effects;
|
import mage.abilities.effects.Effects;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.effects.PostResolveEffect;
|
import mage.abilities.effects.PostResolveEffect;
|
||||||
|
import mage.abilities.keyword.FlashbackAbility;
|
||||||
import mage.abilities.mana.ManaAbility;
|
import mage.abilities.mana.ManaAbility;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.choices.Choice;
|
import mage.choices.Choice;
|
||||||
|
@ -219,15 +220,25 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if ability can be cast for no mana, clear the mana costs now, because additional mana costs must be paid.
|
||||||
|
// For Flashback ability can be set X before, so the X costs have to be restored for the flashbacked ability
|
||||||
|
if (noMana) {
|
||||||
|
int xValue = this.getManaCostsToPay().getX();
|
||||||
|
this.getManaCostsToPay().clear();
|
||||||
|
VariableManaCost xCosts = new VariableManaCost();
|
||||||
|
xCosts.setAmount(xValue);
|
||||||
|
this.getManaCostsToPay().add(xCosts);
|
||||||
|
}
|
||||||
// 20130201 - 601.2b
|
// 20130201 - 601.2b
|
||||||
// If the spell has alternative or additional costs that will be paid as it's being cast such
|
// If the spell has alternative or additional costs that will be paid as it's being cast such
|
||||||
// as buyback, kicker, or convoke costs (see rules 117.8 and 117.9), the player announces his
|
// as buyback, kicker, or convoke costs (see rules 117.8 and 117.9), the player announces his
|
||||||
// or her intentions to pay any or all of those costs (see rule 601.2e).
|
// or her intentions to pay any or all of those costs (see rule 601.2e).
|
||||||
// A player can't apply two alternative methods of casting or two alternative costs to a single spell.
|
// A player can't apply two alternative methods of casting or two alternative costs to a single spell.
|
||||||
if (card != null) {
|
if (card != null && !(this instanceof FlashbackAbility)) {
|
||||||
boolean alternativeCostisUsed = false;
|
boolean alternativeCostisUsed = false;
|
||||||
for (Ability ability : card.getAbilities()) {
|
for (Ability ability : card.getAbilities()) {
|
||||||
if (ability instanceof AlternativeSourceCosts) {
|
// if cast for noMana no Alternative costs are allowed
|
||||||
|
if (!noMana && ability instanceof AlternativeSourceCosts) {
|
||||||
AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability;
|
AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability;
|
||||||
if (alternativeSpellCosts.isAvailable(this, game)) {
|
if (alternativeSpellCosts.isAvailable(this, game)) {
|
||||||
if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) {
|
if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) {
|
||||||
|
@ -241,7 +252,8 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
|
||||||
((OptionalAdditionalSourceCosts)ability).addOptionalAdditionalCosts(this, game);
|
((OptionalAdditionalSourceCosts)ability).addOptionalAdditionalCosts(this, game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!alternativeCostisUsed) {
|
// controller specific alternate spell costs
|
||||||
|
if (!noMana && !alternativeCostisUsed) {
|
||||||
if (this.getAbilityType().equals(AbilityType.SPELL)) {
|
if (this.getAbilityType().equals(AbilityType.SPELL)) {
|
||||||
for (AlternativeSourceCosts alternativeSourceCosts: controller.getAlternativeSourceCosts()) {
|
for (AlternativeSourceCosts alternativeSourceCosts: controller.getAlternativeSourceCosts()) {
|
||||||
if (alternativeSourceCosts.isAvailable(this, game)) {
|
if (alternativeSourceCosts.isAvailable(this, game)) {
|
||||||
|
@ -328,8 +340,8 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
|
||||||
|
|
||||||
if (!useAlternativeCost(game)) { // old way still used?
|
if (!useAlternativeCost(game)) { // old way still used?
|
||||||
|
|
||||||
//20100716 - 601.2f
|
//20100716 - 601.2f (noMana is not used here, because mana costs were cleared for this abaility before adding additional costs and applying cost modification effects)
|
||||||
if (!manaCostsToPay.pay(this, game, sourceId, activatorId, noMana)) {
|
if (!manaCostsToPay.pay(this, game, sourceId, activatorId, false)) {
|
||||||
logger.debug("activate failed - mana");
|
logger.debug("activate failed - mana");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -385,6 +397,7 @@ public abstract class AbilityImpl<T extends AbilityImpl<T>> implements Ability {
|
||||||
*
|
*
|
||||||
* @param game
|
* @param game
|
||||||
* @param noMana
|
* @param noMana
|
||||||
|
* @param controller
|
||||||
* @return variableManaCost for posting to log later
|
* @return variableManaCost for posting to log later
|
||||||
*/
|
*/
|
||||||
protected VariableManaCost handleManaXCosts(Game game, boolean noMana, Player controller) {
|
protected VariableManaCost handleManaXCosts(Game game, boolean noMana, Player controller) {
|
||||||
|
|
|
@ -116,7 +116,7 @@ public class SpellAbility extends ActivatedAbilityImpl<SpellAbility> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getGameLogMessage(Game game) {
|
public String getGameLogMessage(Game game) {
|
||||||
return new StringBuilder(" casts ").append(getMessageText(game)).toString();
|
return getMessageText(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -148,7 +148,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ManaCosts<T> getUnpaid() {
|
public ManaCosts<T> getUnpaid() {
|
||||||
ManaCosts<T> unpaid = new ManaCostsImpl<T>();
|
ManaCosts<T> unpaid = new ManaCostsImpl<>();
|
||||||
for (T cost : this) {
|
for (T cost : this) {
|
||||||
if (!(cost instanceof VariableManaCost) && !cost.isPaid()) {
|
if (!(cost instanceof VariableManaCost) && !cost.isPaid()) {
|
||||||
unpaid.add((T) cost.getUnpaid());
|
unpaid.add((T) cost.getUnpaid());
|
||||||
|
@ -159,7 +159,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ManaCosts<T> getUnpaidVariableCosts() {
|
public ManaCosts<T> getUnpaidVariableCosts() {
|
||||||
ManaCosts<T> unpaid = new ManaCostsImpl<T>();
|
ManaCosts<T> unpaid = new ManaCostsImpl<>();
|
||||||
for (ManaCost cost : this) {
|
for (ManaCost cost : this) {
|
||||||
if (cost instanceof VariableManaCost && !cost.isPaid()) {
|
if (cost instanceof VariableManaCost && !cost.isPaid()) {
|
||||||
unpaid.add((T) cost.getUnpaid());
|
unpaid.add((T) cost.getUnpaid());
|
||||||
|
@ -171,7 +171,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<VariableCost> getVariableCosts() {
|
public List<VariableCost> getVariableCosts() {
|
||||||
List<VariableCost> variableCosts = new ArrayList<VariableCost>();
|
List<VariableCost> variableCosts = new ArrayList<>();
|
||||||
for (ManaCost cost : this) {
|
for (ManaCost cost : this) {
|
||||||
if (cost instanceof VariableCost) {
|
if (cost instanceof VariableCost) {
|
||||||
variableCosts.add((VariableCost) cost);
|
variableCosts.add((VariableCost) cost);
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
*/
|
*/
|
||||||
package mage.abilities.keyword;
|
package mage.abilities.keyword;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.DelayedTriggeredAbility;
|
import mage.abilities.DelayedTriggeredAbility;
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
|
@ -73,6 +74,17 @@ public class FlashbackAbility extends SpellAbility {
|
||||||
this.abilityName = ability.abilityName;
|
this.abilityName = ability.abilityName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canActivate(UUID playerId, Game game) {
|
||||||
|
if (super.canActivate(playerId, game)) {
|
||||||
|
Card card = game.getCard(getSourceId());
|
||||||
|
if (card != null) {
|
||||||
|
return card.getSpellAbility().canActivate(playerId, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FlashbackAbility copy() {
|
public FlashbackAbility copy() {
|
||||||
return new FlashbackAbility(this);
|
return new FlashbackAbility(this);
|
||||||
|
@ -157,11 +169,13 @@ class FlashbackEffect extends OneShotEffect<FlashbackEffect> {
|
||||||
}
|
}
|
||||||
|
|
||||||
spellAbility.clear();
|
spellAbility.clear();
|
||||||
|
// used if flashbacked spell has a {X} cost
|
||||||
int amount = source.getManaCostsToPay().getX();
|
int amount = source.getManaCostsToPay().getX();
|
||||||
spellAbility.getManaCostsToPay().setX(amount);
|
spellAbility.getManaCostsToPay().setX(amount);
|
||||||
for (Target target : spellAbility.getTargets()) {
|
for (Target target : spellAbility.getTargets()) {
|
||||||
target.setRequired(true);
|
target.setRequired(true);
|
||||||
}
|
}
|
||||||
|
game.informPlayers(new StringBuilder(controller.getName()).append(" flashbacks ").append(card.getName()).toString());
|
||||||
return controller.cast(spellAbility, game, true);
|
return controller.cast(spellAbility, game, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,7 +146,13 @@ public class Spell<T extends Spell<T>> implements StackObject, Card {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getActivatedMessage(Game game) {
|
public String getActivatedMessage(Game game) {
|
||||||
return ability.getGameLogMessage(game);
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (isCopiedSpell()) {
|
||||||
|
sb.append(" copies ");
|
||||||
|
} else {
|
||||||
|
sb.append(" casts ");
|
||||||
|
}
|
||||||
|
return sb.append(ability.getGameLogMessage(game)).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in a new issue