Improved interactions between pay X and other effects;

This commit is contained in:
Oleg Agafonov 2019-06-21 11:40:36 +04:00
parent c0ef473d6d
commit 04591a24f7
60 changed files with 685 additions and 813 deletions

View file

@ -3,9 +3,9 @@ package mage.abilities.costs.common;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.CostImpl;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.game.Game;
import mage.util.ManaUtil;
import java.util.UUID;
@ -25,15 +25,13 @@ public class DynamicValueGenericManaCost extends CostImpl {
@Override
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
int convertedCost = amount.calculate(game, ability, null);
Cost cost = new GenericManaCost(convertedCost);
Cost cost = ManaUtil.createManaCost(amount, game, ability, null);
return cost.canPay(ability, sourceId, controllerId, game);
}
@Override
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
int convertedCost = amount.calculate(game, ability, null);
Cost cost = new GenericManaCost(convertedCost);
Cost cost = ManaUtil.createManaCost(amount, game, ability, null);
paid = cost.pay(ability, game, sourceId, controllerId, noMana);
return paid;
}

View file

@ -11,6 +11,9 @@ public class GenericManaCost extends ManaCostImpl {
protected int mana;
/**
* warning, use ManaUtil.createManaCost to create generic cost
*/
public GenericManaCost(int mana) {
this.mana = mana;
this.cost = Mana.GenericMana(mana);

View file

@ -57,8 +57,8 @@ public class CounterUnlessPaysEffect extends OneShotEffect {
costToPay = cost.copy();
costValueMessage = costToPay.getText();
} else {
costValueMessage = "{" + genericMana.calculate(game, source, this) + "}";
costToPay = ManaUtil.createManaCost(genericMana, game, source, this);
costValueMessage = "{" + genericMana.calculate(game, source, this) + "}";
}
String message;
if (costToPay instanceof ManaCost) {

View file

@ -1,11 +1,9 @@
package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
@ -16,9 +14,9 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
import mage.util.ManaUtil;
/**
*
* @author MarcoMarin & L_J
*/
public class DoUnlessTargetPlayerOrTargetsControllerPaysEffect extends OneShotEffect {
@ -70,62 +68,65 @@ public class DoUnlessTargetPlayerOrTargetsControllerPaysEffect extends OneShotEf
@Override
public boolean apply(Game game, Ability source) {
Player targetController = game.getPlayer(this.getTargetPointer().getFirst(game, source));
Player player = game.getPlayer(this.getTargetPointer().getFirst(game, source));
Permanent targetPermanent = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source));
if (targetPermanent != null) {
targetController = game.getPlayer(targetPermanent.getControllerId());
player = game.getPlayer(targetPermanent.getControllerId());
}
if (targetController != null) {
MageObject sourceObject = game.getObject(source.getSourceId());
if (sourceObject != null) {
Cost costToPay;
if (cost != null) {
costToPay = cost.copy();
} else {
costToPay = new GenericManaCost(genericMana.calculate(game, source, this));
}
String message;
if (chooseUseText == null) {
String effectText = executingEffects.getText(source.getModes().getMode());
message = "Pay " + costToPay.getText() + " to prevent (" + effectText.substring(0, effectText.length() - 1) + ")?";
} else {
message = chooseUseText;
}
message = CardUtil.replaceSourceName(message, sourceObject.getName());
boolean result = true;
boolean doEffect = true;
// check if targetController is willing to pay
if (costToPay.canPay(source, source.getSourceId(), targetController.getId(), game) && targetController.chooseUse(Outcome.Detriment, message, source, game)) {
costToPay.clearPaid();
if (costToPay.pay(source, game, source.getSourceId(), targetController.getId(), false, null)) {
if (!game.isSimulation()) {
game.informPlayers(targetController.getLogName() + " pays the cost to prevent the effect");
}
doEffect = false;
}
}
// do the effects if not paid
if (doEffect) {
for (Effect effect : executingEffects) {
effect.setTargetPointer(this.targetPointer);
if (effect instanceof OneShotEffect) {
result &= effect.apply(game, source);
} else {
game.addEffect((ContinuousEffect) effect, source);
}
}
} else if (otherwiseEffect != null) {
otherwiseEffect.setTargetPointer(this.targetPointer);
if (otherwiseEffect instanceof OneShotEffect) {
result &= otherwiseEffect.apply(game, source);
} else {
game.addEffect((ContinuousEffect) otherwiseEffect, source);
}
}
return result;
MageObject sourceObject = game.getObject(source.getSourceId());
if (player != null && sourceObject != null) {
Cost costToPay;
String costValueMessage;
if (cost != null) {
costToPay = cost.copy();
costValueMessage = costToPay.getText();
} else {
costToPay = ManaUtil.createManaCost(genericMana, game, source, this);
costValueMessage = "{" + genericMana.calculate(game, source, this) + "}";
}
String message;
if (chooseUseText == null) {
String effectText = executingEffects.getText(source.getModes().getMode());
message = "Pay " + costValueMessage + " to prevent (" + effectText.substring(0, effectText.length() - 1) + ")?";
} else {
message = chooseUseText;
}
message = CardUtil.replaceSourceName(message, sourceObject.getName());
boolean result = true;
boolean doEffect = true;
// check if targetController is willing to pay
if (costToPay.canPay(source, source.getSourceId(), player.getId(), game)
&& player.chooseUse(Outcome.Detriment, message, source, game)) {
costToPay.clearPaid();
if (costToPay.pay(source, game, source.getSourceId(), player.getId(), false, null)) {
if (!game.isSimulation()) {
game.informPlayers(player.getLogName() + " pays the cost to prevent the effect");
}
doEffect = false;
}
}
// do the effects if not paid
if (doEffect) {
for (Effect effect : executingEffects) {
effect.setTargetPointer(this.targetPointer);
if (effect instanceof OneShotEffect) {
result &= effect.apply(game, source);
} else {
game.addEffect((ContinuousEffect) effect, source);
}
}
} else if (otherwiseEffect != null) {
otherwiseEffect.setTargetPointer(this.targetPointer);
if (otherwiseEffect instanceof OneShotEffect) {
result &= otherwiseEffect.apply(game, source);
} else {
game.addEffect((ContinuousEffect) otherwiseEffect, source);
}
}
return result;
}
return false;
}

View file

@ -1,10 +1,5 @@
package mage.abilities.effects.common;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.GenericManaCost;
@ -21,42 +16,46 @@ import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.util.CardUtil;
import mage.util.ManaUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
*
* @author Eirkei
*/
public class SacrificeOpponentsUnlessPayEffect extends OneShotEffect{
public class SacrificeOpponentsUnlessPayEffect extends OneShotEffect {
protected Cost cost;
protected DynamicValue dynamicGenericMana;
protected DynamicValue genericMana;
protected DynamicValue amount;
protected FilterPermanent filter;
public SacrificeOpponentsUnlessPayEffect(Cost cost) {
this(cost, new FilterPermanent(), 1);
}
public SacrificeOpponentsUnlessPayEffect(int genericManaCost) {
this(genericManaCost, new FilterPermanent(), 1);
}
public SacrificeOpponentsUnlessPayEffect(Cost cost, FilterPermanent filter) {
this(cost, filter, 1);
}
public SacrificeOpponentsUnlessPayEffect(int genericManaCost, FilterPermanent filter) {
this(genericManaCost, filter, 1);
}
public SacrificeOpponentsUnlessPayEffect(Cost cost, FilterPermanent filter, int amount) {
this(cost, filter, new StaticValue(amount));
}
public SacrificeOpponentsUnlessPayEffect(int genericManaCost, FilterPermanent filter, int amount) {
this(new GenericManaCost(genericManaCost), filter, new StaticValue(amount));
}
public SacrificeOpponentsUnlessPayEffect(Cost cost, FilterPermanent filter, DynamicValue amount) {
super(Outcome.Sacrifice);
this.cost = cost;
@ -64,71 +63,70 @@ public class SacrificeOpponentsUnlessPayEffect extends OneShotEffect{
this.filter = filter;
setText();
}
public SacrificeOpponentsUnlessPayEffect(DynamicValue dynamicGenericMana, FilterPermanent filter, DynamicValue amount) {
public SacrificeOpponentsUnlessPayEffect(DynamicValue genericMana, FilterPermanent filter, DynamicValue amount) {
super(Outcome.Sacrifice);
this.dynamicGenericMana = dynamicGenericMana;
this.genericMana = genericMana;
this.amount = amount;
this.filter = filter;
setText();
}
public SacrificeOpponentsUnlessPayEffect(final SacrificeOpponentsUnlessPayEffect effect) {
super(effect);
if (effect.cost != null) {
this.cost = effect.cost.copy();
}
if (effect.dynamicGenericMana != null){
this.dynamicGenericMana = effect.dynamicGenericMana.copy();
if (effect.genericMana != null) {
this.genericMana = effect.genericMana.copy();
}
if (effect.amount != null){
if (effect.amount != null) {
this.amount = effect.amount.copy();
}
if (effect.filter != null){
if (effect.filter != null) {
this.filter = effect.filter.copy();
}
}
@Override
public SacrificeOpponentsUnlessPayEffect copy() {
return new SacrificeOpponentsUnlessPayEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
List<UUID> permsToSacrifice = new ArrayList<>();
filter.add(new ControllerPredicate(TargetController.YOU));
for (UUID playerId : game.getOpponents(source.getControllerId())) {
Player player = game.getPlayer(playerId);
if (player != null) {
Cost costToPay;
String costValueMessage;
if (cost != null) {
costToPay = cost.copy();
costValueMessage = costToPay.getText();
} else {
costToPay = new GenericManaCost(dynamicGenericMana.calculate(game, source, this));
costToPay = ManaUtil.createManaCost(genericMana, game, source, this);
costValueMessage = "{" + genericMana.calculate(game, source, this) + "}";
}
String message;
if (costToPay instanceof ManaCost) {
message = "Would you like to pay " + costToPay.getText() + " to prevent sacrifice effect?";
message = "Would you like to pay " + costValueMessage + " to prevent sacrifice effect?";
} else {
message = costToPay.getText() + " to prevent sacrifice effect?";
message = costValueMessage + " to prevent sacrifice effect?";
}
costToPay.clearPaid();
if (!(player.chooseUse(Outcome.Benefit, message, source, game) && costToPay.pay(source, game, source.getSourceId(), player.getId(), false, null))) {
game.informPlayers(player.getLogName() + " chooses not to pay " + costToPay.getText() + " to prevent the sacrifice effect");
if (!(player.chooseUse(Outcome.Benefit, message, source, game)
&& costToPay.pay(source, game, source.getSourceId(), player.getId(), false, null))) {
game.informPlayers(player.getLogName() + " chooses not to pay " + costValueMessage + " to prevent the sacrifice effect");
int numTargets = Math.min(amount.calculate(game, source, this), game.getBattlefield().countAll(filter, player.getId(), game));
if (numTargets > 0) {
TargetPermanent target = new TargetPermanent(numTargets, numTargets, filter, true);
@ -138,26 +136,26 @@ public class SacrificeOpponentsUnlessPayEffect extends OneShotEffect{
}
}
} else {
game.informPlayers(player.getLogName() + " chooses to pay " + costToPay.getText() + " to prevent the sacrifice effect");
game.informPlayers(player.getLogName() + " chooses to pay " + costValueMessage + " to prevent the sacrifice effect");
}
}
}
for (UUID permID : permsToSacrifice) {
Permanent permanent = game.getPermanent(permID);
if (permanent != null) {
permanent.sacrifice(source.getSourceId(), game);
}
}
return true;
}
private void setText() {
StringBuilder sb = new StringBuilder();
sb.append("each opponent sacrifices ");
if (amount.toString().equals("X")) {
sb.append(amount.toString());
} else {
@ -169,23 +167,23 @@ public class SacrificeOpponentsUnlessPayEffect extends OneShotEffect{
sb.append(CardUtil.numberToText(amount.toString()));
}
}
sb.append(' ');
sb.append(filter.getMessage());
sb.append(" unless he or she pays ");
if (cost != null) {
sb.append(cost.getText());
} else {
sb.append("{X}");
}
if (dynamicGenericMana != null && !dynamicGenericMana.getMessage().isEmpty()) {
if (genericMana != null && !genericMana.getMessage().isEmpty()) {
sb.append(", where X is ");
sb.append(dynamicGenericMana.getMessage());
sb.append(genericMana.getMessage());
}
staticText = sb.toString();
}
}

View file

@ -1,10 +1,9 @@
package mage.abilities.effects.common;
import java.util.Locale;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
@ -12,7 +11,9 @@ import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
import mage.util.ManaUtil;
import java.util.Locale;
/**
* Created by IntelliJ IDEA. User: Loki Date: 21.12.10 Time: 9:21
@ -44,26 +45,34 @@ public class SacrificeSourceUnlessPaysEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
if (genericMana != null) {
cost = new GenericManaCost(genericMana.calculate(game, source, this));
}
Player controller = game.getPlayer(source.getControllerId());
Player player = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (controller != null && sourcePermanent != null) {
StringBuilder sb = new StringBuilder(cost.getText()).append('?');
if (!sb.toString().toLowerCase(Locale.ENGLISH).startsWith("exile ") && !sb.toString().toLowerCase(Locale.ENGLISH).startsWith("return ")) {
sb.insert(0, "Pay ");
if (player != null && sourcePermanent != null) {
Cost costToPay;
String costValueMessage;
if (cost != null) {
costToPay = cost.copy();
costValueMessage = costToPay.getText();
} else {
costToPay = ManaUtil.createManaCost(genericMana, game, source, this);
costValueMessage = "{" + genericMana.calculate(game, source, this) + "}";
}
String message = CardUtil.replaceSourceName(sb.toString(), sourcePermanent.getLogName());
message = Character.toUpperCase(message.charAt(0)) + message.substring(1);
if (cost.canPay(source, source.getSourceId(), source.getControllerId(), game)
&& controller.chooseUse(Outcome.Benefit, message, source, game)) {
cost.clearPaid();
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) {
game.informPlayers(controller.getLogName() + " pays " + cost.getText());
return true;
}
String message;
if (costToPay instanceof ManaCost) {
message = "Would you like to pay " + costValueMessage + " to prevent sacrifice effect?";
} else {
message = costValueMessage + " to prevent sacrifice effect?";
}
costToPay.clearPaid();
if (costToPay.canPay(source, source.getSourceId(), source.getControllerId(), game)
&& player.chooseUse(Outcome.Benefit, message, source, game)
&& costToPay.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) {
game.informPlayers(player.getLogName() + " chooses to pay " + costValueMessage + " to prevent sacrifice effect");
return true;
}
game.informPlayers(player.getLogName() + " chooses not to pay " + costValueMessage + " to prevent sacrifice effect");
if (source.getSourceObjectZoneChangeCounter() == game.getState().getZoneChangeCounter(source.getSourceId())
&& game.getState().getZone(source.getSourceId()) == Zone.BATTLEFIELD) {
sourcePermanent.sacrifice(source.getSourceId(), game);
@ -85,7 +94,8 @@ public class SacrificeSourceUnlessPaysEffect extends OneShotEffect {
}
StringBuilder sb = new StringBuilder("sacrifice {this} unless you ");
String costText = cost.getText();
String costText = cost != null ? cost.getText() : "{X}";
if (costText.toLowerCase(Locale.ENGLISH).startsWith("discard")
|| costText.toLowerCase(Locale.ENGLISH).startsWith("remove")
|| costText.toLowerCase(Locale.ENGLISH).startsWith("return")

View file

@ -3,11 +3,11 @@ package mage.abilities.effects.common.cost;
import mage.abilities.Ability;
import mage.abilities.common.CastCommanderAbility;
import mage.abilities.common.PlayLandAsCommanderAbility;
import mage.abilities.costs.mana.GenericManaCost;
import mage.constants.CostModificationType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.util.ManaUtil;
import mage.watchers.common.CommanderPlaysCountWatcher;
import java.util.UUID;
@ -39,7 +39,7 @@ public class CommanderCostModification extends CostModificationEffectImpl {
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
int castCount = watcher.getPlaysCount(commanderId);
if (castCount > 0) {
abilityToModify.getManaCostsToPay().add(new GenericManaCost(2 * castCount));
abilityToModify.getManaCostsToPay().add(ManaUtil.createManaCost(2 * castCount, false));
}
return true;
}

View file

@ -1,4 +1,3 @@
package mage.abilities.keyword;
import mage.Mana;
@ -7,14 +6,9 @@ import mage.abilities.SpecialAction;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.AlternateManaPaymentAbility;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.constants.AbilityType;
import mage.constants.ManaType;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.constants.*;
import mage.filter.FilterPlayer;
import mage.filter.predicate.other.PlayerPredicate;
import mage.game.Game;
@ -22,6 +16,7 @@ import mage.players.ManaPool;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPlayer;
import mage.util.ManaUtil;
/*
* @author emerald000
@ -117,12 +112,12 @@ class AssistEffect extends OneShotEffect {
if (controller != null && targetPlayer != null) {
int amountToPay = targetPlayer.announceXMana(0, unpaid.getMana().getGeneric(), "How much mana to pay?", game, source);
if (amountToPay > 0) {
Cost cost = new GenericManaCost(amountToPay);
Cost cost = ManaUtil.createManaCost(amountToPay, false);
if (cost.pay(source, game, source.getSourceId(), targetPlayer.getId(), false)) {
ManaPool manaPool = controller.getManaPool();
manaPool.addMana(Mana.ColorlessMana(amountToPay), game, source);
manaPool.unlockManaType(ManaType.COLORLESS);
game.informPlayers(targetPlayer.getLogName() + " paid " + amountToPay + " mana.");
game.informPlayers(targetPlayer.getLogName() + " paid {" + amountToPay + "}.");
game.getState().setValue(source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()), true);
}
}

View file

@ -128,7 +128,7 @@ public class GameEvent implements Serializable {
/* X_MANA_ANNOUNCE
mana x-costs announced by players (X value can be changed by replace events like Unbound Flourishing)
targetId id of the spell that's cast
playerId player that casts the spell
playerId player that casts the spell or ability
amount X multiplier to change X value, default 1
*/
CAST_SPELL,

View file

@ -4,6 +4,7 @@ import mage.MageObject;
import mage.Mana;
import mage.ManaSymbol;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.*;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
@ -14,6 +15,7 @@ import mage.choices.Choice;
import mage.constants.ColoredManaSymbol;
import mage.filter.FilterMana;
import mage.game.Game;
import mage.players.Player;
import java.util.*;
@ -496,20 +498,63 @@ public final class ManaUtil {
}
}
public static ManaCost createManaCost(int manaValue) {
return new GenericManaCost(manaValue);
}
public static ManaCost createManaCost(DynamicValue manaValue, Game game, Ability sourceAbility, Effect effect) {
int costValue = manaValue.calculate(game, sourceAbility, effect);
if (manaValue instanceof ManacostVariableValue) {
// variable (X must be final value after all events and effects)
/**
* all ability/effect code with "new GenericManaCost" must be replaced by createManaCost call
*/
public static ManaCost createManaCost(int genericManaCount, boolean payAsX) {
if (payAsX) {
VariableManaCost xCost = new VariableManaCost();
xCost.setAmount(costValue, costValue, false);
xCost.setAmount(genericManaCount, genericManaCount, false);
return xCost;
} else {
// static/generic
return new GenericManaCost(costValue);
return new GenericManaCost(genericManaCount);
}
}
public static ManaCost createManaCost(DynamicValue genericManaCount, Game game, Ability sourceAbility, Effect effect) {
int costValue = genericManaCount.calculate(game, sourceAbility, effect);
if (genericManaCount instanceof ManacostVariableValue) {
// variable (X must be final value after all events and effects)
return createManaCost(costValue, true);
} else {
// static/generic
return createManaCost(costValue, false);
}
}
public static int playerPaysXGenericMana(boolean payAsX, String restoreContextName, Player player, Ability source, Game game) {
// payAsX - if your cost is X value (some mana can be used for X cost only)
// false: "you may pay any amount of mana"
// true: "counter that spell unless that player pays {X}"
int wantToPay = 0;
boolean payed = false;
while (player.canRespond() && !payed) {
int bookmark = game.bookmarkState();
player.resetStoredBookmark(game);
wantToPay = player.announceXMana(0, Integer.MAX_VALUE, "How much mana will you pay?", game, source);
if (wantToPay > 0) {
Cost cost = ManaUtil.createManaCost(wantToPay, payAsX);
payed = cost.pay(source, game, source.getSourceId(), player.getId(), false, null);
} else {
payed = true;
}
if (!payed) {
game.restoreState(bookmark, restoreContextName);
game.fireUpdatePlayersEvent();
} else {
game.removeBookmark(bookmark);
}
}
if (payed) {
game.informPlayers(player.getLogName() + " pays {" + wantToPay + "}.");
return wantToPay;
} else {
return 0;
}
}
}