refactor: combined announceX methods in one, improved X message and selection for AI (part of #10330)

This commit is contained in:
Oleg Agafonov 2025-05-16 19:34:57 +04:00
parent 6af198836b
commit 66db821437
37 changed files with 90 additions and 158 deletions

View file

@ -2,7 +2,6 @@ package mage.player.ai;
import mage.MageObject;
import mage.abilities.*;
import mage.abilities.costs.VariableCost;
import mage.abilities.costs.mana.ManaCost;
import mage.cards.Card;
import mage.cards.Cards;
@ -250,20 +249,11 @@ public class ComputerPlayerControllableProxy extends ComputerPlayer7 {
}
@Override
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
public int announceX(int min, int max, String message, Game game, Ability source, boolean isManaPay) {
if (isUnderMe(game)) {
return super.announceXMana(min, max, message, game, ability);
return super.announceX(min, max, message, game, source, isManaPay);
} else {
return getControllingPlayer(game).announceXMana(min, max, message, game, ability);
}
}
@Override
public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost) {
if (isUnderMe(game)) {
return super.announceXCost(min, max, message, game, ability, variableCost);
} else {
return getControllingPlayer(game).announceXCost(min, max, message, game, ability, variableCost);
return getControllingPlayer(game).announceX(min, max, message, game, source, isManaPay);
}
}

View file

@ -5,7 +5,6 @@ import mage.ConditionalMana;
import mage.MageObject;
import mage.Mana;
import mage.abilities.*;
import mage.abilities.costs.VariableCost;
import mage.abilities.costs.mana.*;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DamageTargetEffect;
@ -2039,42 +2038,40 @@ public class ComputerPlayer extends PlayerImpl {
}
@Override
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
// current logic - use max possible mana
public int announceX(int min, int max, String message, Game game, Ability source, boolean isManaPay) {
// fast calc on nothing to choose
if (min >= max) {
return min;
}
// TODO: add good/bad effects support
// TODO: add simple game simulations like declare blocker?
// TODO: add simple game simulations like declare blocker (need to find only workable payment)?
// TODO: remove random logic or make it more stable (e.g. use same value in same game cycle)
int numAvailable = getAvailableManaProducers(game).size() - ability.getManaCosts().manaValue();
if (numAvailable < 0) {
numAvailable = 0;
// protection from too big values
int realMin = min;
int realMax = max;
if (max == Integer.MAX_VALUE) {
realMax = Math.max(realMin, 10); // AI don't need huge values for X, cause can't use infinite combos
}
int xValue;
if (isManaPay) {
// as X mana payment - due available mana
xValue = Math.max(0, getAvailableManaProducers(game).size() - source.getManaCostsToPay().getUnpaid().manaValue());
} else {
if (numAvailable < min) {
numAvailable = min;
}
if (numAvailable > max) {
numAvailable = max;
}
}
return numAvailable;
// as X actions
xValue = RandomUtil.nextInt(realMax + 1);
}
@Override
public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variablCost) {
// current logic - use random non-zero value
// TODO: add good/bad effects support
// TODO: remove random logic
int value = RandomUtil.nextInt(CardUtil.overflowInc(max, 1));
if (value < min) {
value = min;
if (xValue > realMax) {
xValue = realMax;
}
if (value < max) {
// do not use zero values
value++;
if (xValue < realMin) {
xValue = realMin;
}
return value;
return xValue;
}
@Override

View file

@ -1687,64 +1687,39 @@ public class HumanPlayer extends PlayerImpl {
}
/**
* Gets the amount of mana the player want to spent for a x spell
*
* @param min
* @param max
* @param message
* @param ability
* @param game
* @return
* Gets the amount of mana the player want to spend for an x spell
*/
@Override
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
public int announceX(int min, int max, String message, Game game, Ability source, boolean isManaPay) {
if (!canCallFeedback(game)) {
return min;
}
int xValue = 0;
while (canRespond()) {
prepareForResponse(game);
if (!isExecutingMacro()) {
game.fireGetAmountEvent(playerId, message + CardUtil.getSourceLogName(game, ability), min, max);
}
waitForResponse(game);
if (response.getInteger() != null) {
break;
}
// TODO: add response verify here
}
if (response.getInteger() != null) {
xValue = response.getInteger();
}
return xValue;
}
@Override
public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost) {
if (!canCallFeedback(game)) {
// fast calc on nothing to choose
if (min >= max) {
return min;
}
int xValue = 0;
int xValue = min;
while (canRespond()) {
prepareForResponse(game);
if (!isExecutingMacro()) {
game.fireGetAmountEvent(playerId, message, min, max);
game.fireGetAmountEvent(playerId, message + CardUtil.getSourceLogName(game, source), min, max);
}
waitForResponse(game);
if (response.getInteger() != null) {
break;
}
if (response.getInteger() == null) {
continue;
}
if (response.getInteger() != null) {
xValue = response.getInteger();
if (xValue < min || xValue > max) {
continue;
}
break;
}
return xValue;
}

View file

@ -163,7 +163,7 @@ class AzorTheLawbringerAttacksEffect extends OneShotEffect {
if (controller != null) {
ManaCosts cost = new ManaCostsImpl<>("{X}{W}{U}{U}");
if (controller.chooseUse(Outcome.Damage, "Pay " + cost.getText() + "? If you do, you gain X life and draw X cards.", source, game)) {
int costX = controller.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int costX = controller.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (gain life and draw cards)", game, source,true);
cost.add(new GenericManaCost(costX));
if (cost.pay(source, game, source, source.getControllerId(), false, null)) {
controller.resetStoredBookmark(game); // otherwise you can undo the payment

View file

@ -86,7 +86,7 @@ class DepalaPilotExemplarEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
ManaCosts<ManaCost> cost = new ManaCostsImpl<>("{X}");
int xValue = controller.announceXMana(0, Integer.MAX_VALUE, "Choose the amount of mana to pay", game, source);
int xValue = controller.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to reveal)", game, source, true);
cost.add(new GenericManaCost(xValue));
if (cost.pay(source, game, source, source.getControllerId(), false) && xValue > 0) {
new RevealLibraryPutIntoHandEffect(xValue, filter, Zone.LIBRARY, false).apply(game, source);

View file

@ -80,7 +80,7 @@ class ElendaAndAzorEffect extends OneShotEffect {
if (controller != null) {
ManaCosts cost = new ManaCostsImpl<>("{X}{W}{U}{B}");
if (controller.chooseUse(Outcome.Damage, "Pay " + cost.getText() + "? If you do, draw X cards.", source, game)) {
int costX = controller.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int costX = controller.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to draw)", game, source, true);
cost.add(new GenericManaCost(costX));
if (cost.pay(source, game, source, source.getControllerId(), false, null)) {
controller.resetStoredBookmark(game); // otherwise you can undo the payment

View file

@ -68,7 +68,7 @@ class FlameblastDragonEffect extends OneShotEffect {
ManaCosts cost = new ManaCostsImpl<>("{X}{R}");
if (player != null) {
if (player.chooseUse(Outcome.Damage, "Pay " + cost.getText() + "? If you do, Flameblast Dragon deals X damage to any target", source, game)) {
int costX = player.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int costX = player.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to damage)", game, source, true);
cost.add(new GenericManaCost(costX));
if (cost.pay(source, game, source, source.getControllerId(), false, null)) {
Permanent permanent = game.getPermanent(source.getFirstTarget());

View file

@ -78,7 +78,7 @@ class HaloForagerPayEffect extends OneShotEffect {
if (player == null || !player.chooseUse(outcome, "Pay " + cost.getText() + "?", source, game)) {
return false;
}
int costX = player.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int costX = player.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to free cast)", game, source, true);
cost.add(new GenericManaCost(costX));
if (!cost.pay(source, game, source, source.getControllerId(), false, null)) {
return false;

View file

@ -70,7 +70,7 @@ class HeroOfLeinaTowerEffect extends OneShotEffect {
Player you = game.getPlayer(source.getControllerId());
ManaCosts cost = new ManaCostsImpl<>("{X}");
if (you != null && you.chooseUse(Outcome.BoostCreature, "Do you want to to pay {X}?", source, game)) {
int costX = you.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int costX = you.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to add +1/+1 counters)", game, source, true);
cost.add(new GenericManaCost(costX));
if (cost.pay(source, game, source, source.getControllerId(), false, null)) {
Permanent sourcePermanent = game.getPermanent(source.getSourceId());

View file

@ -88,7 +88,7 @@ class IncineratorOfTheGuiltyEffect extends OneShotEffect {
return false;
}
int xValue = controller.announceXMana(0, Integer.MAX_VALUE, "Announce the value for X", game, source);
int xValue = controller.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (collect evidence)", game, source, false);
CollectEvidenceCost cost = new CollectEvidenceCost(xValue);
if (!cost.pay(source, game, source, source.getControllerId(), false, null)) {
return false;

View file

@ -85,7 +85,7 @@ class IsarethTheAwakenerCreateReflexiveTriggerEffect extends OneShotEffect {
|| !player.chooseUse(Outcome.BoostCreature, "Pay " + cost.getText() + "?", source, game)) {
return false;
}
int costX = player.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int costX = player.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to return due mana value)", game, source, true);
cost.add(new GenericManaCost(costX));
if (!cost.pay(source, game, source, source.getControllerId(), false, null)) {
return false;

View file

@ -111,10 +111,8 @@ class LeylineTyrantDamageEffect extends OneShotEffect {
if (player == null) {
return false;
}
int costX = player.announceXMana(
0, Integer.MAX_VALUE,
"Announce the value for {X}", game, source
);
// TODO: add some AI hints by min/max values
int costX = player.announceX(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source, true);
String manaString;
if (costX == 0) {
manaString = "{0}";

View file

@ -81,7 +81,8 @@ class NumaJoragaChieftainEffect extends OneShotEffect {
if (!player.chooseUse(Outcome.BoostCreature, "Pay " + cost.getText() + "?", source, game)) {
return false;
}
int costX = player.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
// TODO: add some AI hints by min/max values
int costX = player.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to distribute counters)", game, source,true);
cost.add(new GenericManaCost(2 * costX));
if (!cost.pay(source, game, source, source.getControllerId(), false, null)) {
return false;

View file

@ -80,7 +80,7 @@ class PowerLeakEffect extends OneShotEffect {
String message = "Pay {X} to prevent X damage from " + permanent.getLogName() + "?";
int xValue = 0;
if (player.chooseUse(Outcome.Neutral, message, source, game)) {
xValue = player.announceXMana(0, Integer.MAX_VALUE, "Choose the amount of mana to pay", game, source);
xValue = player.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to prevent damage)", game, source, true);
cost.add(new GenericManaCost(xValue));
if (cost.pay(source, game, source, player.getId(), false, null)) {
game.informPlayers(player.getLogName() + " paid {" + xValue + "} for " + permanent.getLogName());

View file

@ -116,7 +116,7 @@ class RemnantOfTheRisingStarEffect extends OneShotEffect {
)) {
return false;
}
int xValue = player.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int xValue = player.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to add counters)", game, source, true);
cost.add(new GenericManaCost(xValue));
if (!cost.pay(source, game, source, source.getControllerId(), false, null)) {
return false;

View file

@ -19,7 +19,6 @@ import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorPredicate;
@ -87,7 +86,7 @@ class RiseOfTheHobgoblinsEffect extends OneShotEffect {
Player you = game.getPlayer(source.getControllerId());
ManaCosts<ManaCost> cost = new ManaCostsImpl<>("{X}");
if (you != null && you.chooseUse(Outcome.Neutral, "Do you want to to pay {X}?", source, game)) {
int costX = you.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int costX = you.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to add counters)", game, source, true);
cost.add(new GenericManaCost(costX));
if (cost.pay(source, game, source, source.getControllerId(), false, null)) {
Token token = new GoblinSoldierToken();

View file

@ -80,7 +80,7 @@ class RoseRoomTreasurerEffect extends OneShotEffect {
if (!player.chooseUse(Outcome.BoostCreature, "Pay {X}?", source, game)) {
return false;
}
int costX = player.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int costX = player.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to damage)", game, source, true);
cost.add(new GenericManaCost(costX));
if (!cost.pay(source, game, source, source.getControllerId(), false)) {
return false;

View file

@ -62,7 +62,7 @@ class ChooseNumberEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
int numberChoice = controller.announceXMana(0, Integer.MAX_VALUE, "Choose a number.", game, source);
int numberChoice = controller.getAmount(0, Integer.MAX_VALUE, "Choose a number (mana cost to restrict)", game);
game.getState().setValue(source.getSourceId().toString(), numberChoice);
Permanent permanent = game.getPermanentEntering(source.getSourceId());

View file

@ -18,7 +18,6 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.constants.ColoredManaSymbol;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
@ -80,7 +79,7 @@ class SquealingDevilEffect extends OneShotEffect {
ManaCosts cost = new ManaCostsImpl<>("{X}");
if (player != null) {
if (player.chooseUse(Outcome.BoostCreature, "Pay " + cost.getText() + "?", source, game)) {
int costX = player.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int costX = player.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to boost)", game, source, true);
cost.add(new GenericManaCost(costX));
if (cost.pay(source, game, source, source.getControllerId(), false, null)) {
Permanent permanent = game.getPermanent(source.getFirstTarget());

View file

@ -53,7 +53,7 @@ class SqueesRevengeEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if(player != null) {
int number = player.announceXMana(0, Integer.MAX_VALUE, "Choose how many times to flip a coin", game, source);
int number = player.getAmount(0, Integer.MAX_VALUE, "Choose how many times to flip a coin", game);
game.informPlayers(player.getLogName() + " chooses " + number + '.');
for(int i = 0; i < number; i++) {
if(!player.flipCoin(source, game, true)) {

View file

@ -78,7 +78,7 @@ class TilonallisSummonerEffect extends OneShotEffect {
if (controller != null) {
ManaCosts cost = new ManaCostsImpl<>("{X}{R}");
if (controller.chooseUse(outcome, "Pay " + cost.getText() + "? If you do, you create X 1/1 red Elemental creature tokens that are tapped and attacking.", source, game)) {
int costX = controller.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int costX = controller.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay for tokens)", game, source, true);
cost.add(new GenericManaCost(costX));
if (cost.pay(source, game, source, source.getControllerId(), false, null)) {
controller.resetStoredBookmark(game); // otherwise you can undo the payment

View file

@ -53,7 +53,7 @@ class VigilForTheLostEffect extends OneShotEffect {
if (controller == null) {
return false;
}
int costX = controller.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int costX = controller.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay to gain life)", game, source, true);
if (new GenericManaCost(costX).pay(source, game, source, source.getControllerId(), false, null)) {
controller.gainLife(costX, game, source);
return true;

View file

@ -64,7 +64,7 @@ class VoidEffect extends OneShotEffect {
return false;
}
int number = controller.announceXMana(0, Integer.MAX_VALUE, this.staticText, game, source);
int number = controller.getAmount(0, Integer.MAX_VALUE, "Choose a number (mana cost to destroy)", game);
game.informPlayers(controller.getLogName() + " chooses " + number + '.');
for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) {

View file

@ -59,7 +59,7 @@ class WellOfLostDreamsEffect extends OneShotEffect {
if (controller != null) {
int amount = SavedGainedLifeValue.MANY.calculate(game, source, this);
if (amount > 0) {
int xValue = controller.announceXMana(0, amount, "Announce X Value", game, source);
int xValue = controller.announceX(0, amount, "Announce the value for {X} (pay for draw cards)", game, source, true);
if (xValue > 0) {
if (new GenericManaCost(xValue).pay(source, game, source, controller.getId(), false)) {
game.informPlayers(controller.getLogName() + " payed {" + xValue + '}');

View file

@ -94,7 +94,7 @@ class WildbornPreserverCreateReflexiveTriggerEffect extends OneShotEffect {
if (!player.chooseUse(outcome, "Pay " + cost.getText() + "?", source, game)) {
return false;
}
int costX = player.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
int costX = player.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (pay for counters)", game, source, true);
cost.add(new GenericManaCost(costX));
if (!cost.pay(source, game, source, source.getControllerId(), false, null)) {
return false;

View file

@ -23,7 +23,7 @@ public class ProteanHydraTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Protean Hydra");
setStrictChooseMode(false); // test AI use max for X
setStrictChooseMode(false); // TODO: good test for AI's announceX - duplicate it as AI test
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();

View file

@ -169,6 +169,7 @@ public class SoulBurnTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Burn", "Craw Wurm");
setStrictChooseMode(false); // TODO: good test for AI's announceX - duplicate it as AI test (few examples)
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerB, "Craw Wurm", 1);

View file

@ -56,6 +56,7 @@ public class IncreasingCardsTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Increasing Confusion");
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback {X}{U}");
setStrictChooseMode(false); // TODO: good test for AI's announceX - duplicate it as AI test
setStopAt(3, PhaseStep.BEGIN_COMBAT);
execute();

View file

@ -65,7 +65,7 @@ public class HELIOSOneTest extends CardTestPlayerBase {
} catch (AssertionError e) {
Assert.assertTrue(
"X=0 is not a valid choice. Error message:\n" + e.getMessage(),
e.getMessage().contains("Message: Announce the number of {E} to pay")
e.getMessage().contains("Message: Announce the value for {X}")
);
}
}

View file

@ -29,6 +29,7 @@ public class PostMortemLungeTest extends CardTestPlayerBase {
attack(1, playerA, "Elite Vanguard");
setStrictChooseMode(false); // TODO: good test for AI's announceX - duplicate it as AI test
setStopAt(1, PhaseStep.CLEANUP);
execute();

View file

@ -6,7 +6,6 @@ import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.AlternativeSourceCosts;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.VariableCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.effects.common.InfoEffect;
@ -2916,39 +2915,20 @@ public class TestPlayer implements Player {
}
@Override
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
assertAliasSupportInChoices(false);
if (!choices.isEmpty()) {
for (String choice : new ArrayList<>(choices)) {
if (choice.startsWith("X=")) {
int xValue = Integer.parseInt(choice.substring(2));
assertXMinMaxValue(game, ability, xValue, min, max);
choices.remove(choice);
return xValue;
}
}
}
this.chooseStrictModeFailed("choice", game, getInfo(ability, game)
+ "\nMessage: " + message + prepareXMaxInfo(min, max));
return computerPlayer.announceXMana(min, max, message, game, ability);
}
@Override
public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variablCost) {
public int announceX(int min, int max, String message, Game game, Ability source, boolean isManaPay) {
assertAliasSupportInChoices(false);
if (!choices.isEmpty()) {
if (choices.get(0).startsWith("X=")) {
int xValue = Integer.parseInt(choices.get(0).substring(2));
assertXMinMaxValue(game, ability, xValue, min, max);
assertXMinMaxValue(game, source, xValue, min, max);
choices.remove(0);
return xValue;
}
}
this.chooseStrictModeFailed("choice", game, getInfo(ability, game)
this.chooseStrictModeFailed("choice", game, getInfo(source, game)
+ "\nMessage: " + message + prepareXMaxInfo(min, max));
return computerPlayer.announceXCost(min, max, message, game, ability, null);
return computerPlayer.announceX(min, max, message, game, source, isManaPay);
}
private String prepareXMaxInfo(int min, int max) {

View file

@ -787,8 +787,8 @@ public abstract class AbilityImpl implements Ability {
xValue = variableManaCost.getAmount();
} else {
// announce by player
xValue = controller.announceXMana(variableManaCost.getMinX(), variableManaCost.getMaxX(),
"Announce the value for " + variableManaCost.getText(), game, this);
xValue = controller.announceX(variableManaCost.getMinX(), variableManaCost.getMaxX(),
"Announce the value for " + variableManaCost.getText(), game, this, true);
}
int amountMana = xValue * variableManaCost.getXInstancesCount();

View file

@ -158,8 +158,8 @@ public abstract class VariableCostImpl implements Cost, VariableCost {
if (controller != null
&& (source instanceof ManaAbility
|| stackObject != null)) {
xValue = controller.announceXCost(getMinValue(source, game), getMaxValue(source, game),
"Announce the number of " + actionText, game, source, this);
xValue = controller.announceX(getMinValue(source, game), getMaxValue(source, game),
"Announce the value for {X} (" + actionText + ")", game, source, false);
}
return xValue;
}

View file

@ -173,8 +173,8 @@ class AssistEffect extends OneShotEffect {
// AI can't assist other players, maybe for teammates only (but tests must work as normal)
int amountToPay = 0;
if (!targetPlayer.isComputer()) {
amountToPay = targetPlayer.announceXMana(0, unpaid.getMana().getGeneric(),
"How much mana to pay as assist for " + controller.getName() + "?", game, source);
amountToPay = targetPlayer.announceX(0, unpaid.getMana().getGeneric(),
"How much mana to pay as assist for " + controller.getName() + "?", game, source, true);
}
if (amountToPay > 0) {

View file

@ -5,7 +5,6 @@ import mage.abilities.*;
import mage.abilities.costs.AlternativeSourceCosts;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.VariableCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.mana.ManaOptions;
@ -748,14 +747,10 @@ public interface Player extends MageItem, Copyable<Player> {
boolean shuffleCardsToLibrary(Card card, Game game, Ability source);
/**
* Set the value for X mana spells and abilities
* Set the value for X in spells and abilities
* @param isManaPay helper param for better AI logic
*/
int announceXMana(int min, int max, String message, Game game, Ability ability);
/**
* Set the value for non mana X costs
*/
int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost);
int announceX(int min, int max, String message, Game game, Ability source, boolean isManaPay);
// TODO: rework to use pair's list of effect + ability instead string's map
int chooseReplacementEffect(Map<String, String> effectsMap, Map<String, MageObject> objectsMap, Game game);

View file

@ -156,13 +156,8 @@ public class StubPlayer extends PlayerImpl {
}
@Override
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
return 0;
}
@Override
public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost) {
return 0;
public int announceX(int min, int max, String message, Game game, Ability source, boolean isManaPay) {
return min;
}
@Override

View file

@ -710,7 +710,7 @@ public final class ManaUtil {
int bookmark = game.bookmarkState();
player.resetStoredBookmark(game);
wantToPay = player.announceXMana(0, maxValue, "Choose how much mana to pay", game, source);
wantToPay = player.announceX(0, maxValue, "Choose how much mana to pay", game, source, true);
if (wantToPay > 0) {
Cost cost = ManaUtil.createManaCost(wantToPay, payAsX);
payed = cost.pay(source, game, source, player.getId(), false, null);