* Non basic mana abilities - fixed rollback errors in AI games (#6300);

This commit is contained in:
Oleg Agafonov 2020-02-25 12:41:45 +04:00
parent 28169b5271
commit 169d9bf761
62 changed files with 692 additions and 509 deletions

View file

@ -11,10 +11,7 @@ import mage.constants.*;
import mage.game.Game;
import org.junit.Assert;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.*;
/**
* Adds condition to {@link ContinuousEffect}. Acts as decorator.
@ -34,7 +31,7 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
}
/**
* Only use this if both effects have the same layers
* Only use this if both effects have the same layers (TODO: add tests to search it?)
*
* @param effect
* @param otherwiseEffect
@ -178,4 +175,13 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
return super.isDependentTo(allEffectsInLayer);
}
/**
* Return all effects list, for tests only
*/
public List<ContinuousEffect> getAllEffects() {
List<ContinuousEffect> res = new ArrayList<>();
if (this.effect != null) res.add(this.effect);
if (this.otherwiseEffect != null) res.add(this.otherwiseEffect);
return res;
}
}

View file

@ -1,6 +1,5 @@
package mage.abilities.decorator;
import java.util.List;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
@ -10,8 +9,10 @@ import mage.choices.ChoiceColor;
import mage.game.Game;
import mage.players.Player;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author LevelX2
*/
public class ConditionalManaEffect extends ManaEffect {
@ -48,17 +49,23 @@ public class ConditionalManaEffect extends ManaEffect {
@Override
public List<Mana> getNetMana(Game game, Ability source) {
if (condition.apply(game, source)) {
return effect.getNetMana(game, source);
} else if (otherwiseEffect != null) {
return otherwiseEffect.getNetMana(game, source);
List<Mana> netMana = new ArrayList<>();
if (game != null) {
if (condition.apply(game, source)) {
return effect.getNetMana(game, source);
} else if (otherwiseEffect != null) {
return otherwiseEffect.getNetMana(game, source);
}
}
return null;
return netMana;
}
@Override
public Mana produceMana(Game game, Ability source) {
Mana mana = new Mana();
if (game == null) {
return mana;
}
if (condition.apply(game, source)) {
mana = effect.getManaTemplate().copy();
} else if (otherwiseEffect != null) {

View file

@ -58,7 +58,7 @@ public abstract class ManaEffect extends OneShotEffect {
* Returns the currently available max mana variations the effect can
* produce
*
* @param game
* @param game warning, can be NULL for card score calcs (AI games)
* @param source
* @return
*/
@ -78,7 +78,7 @@ public abstract class ManaEffect extends OneShotEffect {
* if you don't want it then overide getNetMana to return max possible mana values
* (if you have choose dialogs or extra effects like new counters in produceMana)
*
* @param game
* @param game warning, can be NULL for AI score calcs (game == null)
* @param source
* @return can return null or empty mana
*/

View file

@ -39,7 +39,11 @@ public class AddConditionalManaEffect extends ManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
return manaBuilder.setMana(mana, source, game).build();
if (game != null) {
return manaBuilder.setMana(mana, source, game).build();
} else {
return new Mana();
}
}
public Mana getMana() {

View file

@ -65,45 +65,45 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect {
@Override
public List<Mana> getNetMana(Game game, Ability source) {
List<Mana> netMana = new ArrayList<>();
int value = amount.calculate(game, source, this);
if (value > 0) {
netMana.add(Mana.AnyMana(value));
if (game != null) {
int value = amount.calculate(game, source, this);
if (value > 0) {
netMana.add(Mana.AnyMana(value));
}
}
return netMana;
}
@Override
public Mana produceMana(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return null;
}
ConditionalMana mana = null;
int value = amount.calculate(game, source, this);
ChoiceColor choice = new ChoiceColor(true);
for (int i = 0; i < value; i++) {
if (choice.getChoice() == null) {
controller.choose(outcome, choice, game);
}
if (choice.getChoice() == null) {
return null;
}
if (oneChoice) {
mana = new ConditionalMana(manaBuilder.setMana(choice.getMana(value), source, game).build());
break;
} else {
if (mana == null) {
mana = new ConditionalMana(manaBuilder.setMana(choice.getMana(1), source, game).build());
} else {
mana.add(choice.getMana(1));
if (game != null) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
ConditionalMana mana = null;
int value = amount.calculate(game, source, this);
ChoiceColor choice = new ChoiceColor(true);
for (int i = 0; i < value; i++) {
if (choice.getChoice() == null) {
controller.choose(outcome, choice, game);
}
if (choice.getChoice() == null) {
return null;
}
if (oneChoice) {
mana = new ConditionalMana(manaBuilder.setMana(choice.getMana(value), source, game).build());
break;
} else {
if (mana == null) {
mana = new ConditionalMana(manaBuilder.setMana(choice.getMana(1), source, game).build());
} else {
mana.add(choice.getMana(1));
}
choice.clearChoice();
}
}
choice.clearChoice();
return mana;
}
}
return mana;
return new Mana();
}
}

View file

@ -5,8 +5,6 @@
*/
package mage.abilities.effects.mana;
import java.util.ArrayList;
import java.util.List;
import mage.ConditionalMana;
import mage.Mana;
import mage.abilities.Ability;
@ -16,14 +14,16 @@ import mage.choices.ManaChoice;
import mage.game.Game;
import mage.players.Player;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author jeffwadsworth
*/
public class AddConditionalManaOfTwoDifferentColorsEffect extends ManaEffect {
private final ConditionalManaBuilder manaBuilder;
public AddConditionalManaOfTwoDifferentColorsEffect(ConditionalManaBuilder manaBuilder) {
super();
this.manaBuilder = manaBuilder;
@ -44,11 +44,14 @@ public class AddConditionalManaOfTwoDifferentColorsEffect extends ManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
Player player = getPlayer(game, source);
Mana mana = new ConditionalMana(manaBuilder.setMana(
ManaChoice.chooseTwoDifferentColors(
player, game), source, game).build());
return mana;
if (game != null) {
Player player = getPlayer(game, source);
Mana mana = new ConditionalMana(manaBuilder.setMana(
ManaChoice.chooseTwoDifferentColors(
player, game), source, game).build());
return mana;
}
return new Mana();
}
@Override

View file

@ -51,14 +51,16 @@ public class AddManaAnyColorAttachedControllerEffect extends ManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
Permanent enchantment = game.getPermanent(source.getSourceId());
if (enchantment != null) {
Permanent land = game.getPermanent(enchantment.getAttachedTo());
if (land != null) {
Player player = game.getPlayer(land.getControllerId());
ChoiceColor choice = new ChoiceColor();
if (player != null && player.choose(outcome, choice, game)) {
return choice.getMana(1);
if (game != null) {
Permanent enchantment = game.getPermanent(source.getSourceId());
if (enchantment != null) {
Permanent land = game.getPermanent(enchantment.getAttachedTo());
if (land != null) {
Player player = game.getPlayer(land.getControllerId());
ChoiceColor choice = new ChoiceColor();
if (player != null && player.choose(outcome, choice, game)) {
return choice.getMana(1);
}
}
}
}

View file

@ -28,12 +28,13 @@ public class AddManaChosenColorEffect extends ManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
ObjectColor color = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color");
if (color != null) {
return new Mana(ColoredManaSymbol.lookup(color.toString().charAt(0)));
} else {
return null;
if (game != null) {
ObjectColor color = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color");
if (color != null) {
return new Mana(ColoredManaSymbol.lookup(color.toString().charAt(0)));
}
}
return new Mana();
}
@Override

View file

@ -66,9 +66,11 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
@Override
public List<Mana> getNetMana(Game game, Ability source) {
List<Mana> netMana = new ArrayList<>();
int amountOfManaLeft = amount.calculate(game, source, this);
if (amountOfManaLeft > 0) {
netMana.add(Mana.AnyMana(amountOfManaLeft));
if (game != null) {
int amountOfManaLeft = amount.calculate(game, source, this);
if (amountOfManaLeft > 0) {
netMana.add(Mana.AnyMana(amountOfManaLeft));
}
}
return netMana;
}

View file

@ -54,21 +54,23 @@ public class AddManaOfAnyColorEffect extends BasicManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
String mes = String.format("Select color of %d mana to add it", this.amount);
if (mes != null) {
ChoiceColor choice = new ChoiceColor(true, mes, game.getObject(source.getSourceId()));
if (controller.choose(outcome, choice, game)) {
if (choice.getColor() != null) {
Mana mana = choice.getMana(amount);
mana.setFlag(setFlag);
return mana;
if (game != null) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
String mes = String.format("Select color of %d mana to add it", this.amount);
if (mes != null) {
ChoiceColor choice = new ChoiceColor(true, mes, game.getObject(source.getSourceId()));
if (controller.choose(outcome, choice, game)) {
if (choice.getColor() != null) {
Mana mana = choice.getMana(amount);
mana.setFlag(setFlag);
return mana;
}
}
}
}
}
return null;
return new Mana();
}
public int getAmount() {

View file

@ -47,71 +47,71 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (permanent != null) {
Player targetController = game.getPlayer(permanent.getControllerId());
if (targetController == null) {
return null;
}
Mana types = (Mana) this.getValue("mana");
if (types == null) {
return null;
}
Choice choice = new ChoiceColor(true);
choice.getChoices().clear();
choice.setMessage("Pick the type of mana to produce");
if (types.getBlack() > 0) {
choice.getChoices().add("Black");
}
if (types.getRed() > 0) {
choice.getChoices().add("Red");
}
if (types.getBlue() > 0) {
choice.getChoices().add("Blue");
}
if (types.getGreen() > 0) {
choice.getChoices().add("Green");
}
if (types.getWhite() > 0) {
choice.getChoices().add("White");
}
if (types.getColorless() > 0) {
choice.getChoices().add("Colorless");
}
Mana newMana = new Mana();
if (!choice.getChoices().isEmpty()) {
if (choice.getChoices().size() == 1) {
choice.setChoice(choice.getChoices().iterator().next());
} else {
if (!targetController.choose(outcome, choice, game)) {
return null;
}
Mana newMana = new Mana();
if (game != null) {
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (permanent != null) {
Player targetController = game.getPlayer(permanent.getControllerId());
Mana types = (Mana) this.getValue("mana");
if (targetController == null || types == null) {
return newMana;
}
switch (choice.getChoice()) {
case "Black":
newMana.setBlack(1);
break;
case "Blue":
newMana.setBlue(1);
break;
case "Red":
newMana.setRed(1);
break;
case "Green":
newMana.setGreen(1);
break;
case "White":
newMana.setWhite(1);
break;
case "Colorless":
newMana.setColorless(1);
break;
Choice choice = new ChoiceColor(true);
choice.getChoices().clear();
choice.setMessage("Pick the type of mana to produce");
if (types.getBlack() > 0) {
choice.getChoices().add("Black");
}
if (types.getRed() > 0) {
choice.getChoices().add("Red");
}
if (types.getBlue() > 0) {
choice.getChoices().add("Blue");
}
if (types.getGreen() > 0) {
choice.getChoices().add("Green");
}
if (types.getWhite() > 0) {
choice.getChoices().add("White");
}
if (types.getColorless() > 0) {
choice.getChoices().add("Colorless");
}
if (!choice.getChoices().isEmpty()) {
if (choice.getChoices().size() == 1) {
choice.setChoice(choice.getChoices().iterator().next());
} else {
if (!targetController.choose(outcome, choice, game)) {
return newMana;
}
}
switch (choice.getChoice()) {
case "Black":
newMana.setBlack(1);
break;
case "Blue":
newMana.setBlue(1);
break;
case "Red":
newMana.setRed(1);
break;
case "Green":
newMana.setGreen(1);
break;
case "White":
newMana.setWhite(1);
break;
case "Colorless":
newMana.setColorless(1);
break;
}
}
}
return newMana;
}
return null;
return newMana;
}
@Override

View file

@ -31,8 +31,12 @@ public class AddManaOfTwoDifferentColorsEffect extends ManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
Player player = getPlayer(game, source);
return ManaChoice.chooseTwoDifferentColors(player, game);
if (game != null) {
Player player = getPlayer(game, source);
return ManaChoice.chooseTwoDifferentColors(player, game);
} else {
return new Mana();
}
}
@Override

View file

@ -16,7 +16,6 @@ import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@ -49,20 +48,23 @@ public class DoUnlessAnyPlayerPaysManaEffect extends ManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
Player controller = getPlayer(game, source);
MageObject sourceObject = game.getObject(source.getSourceId());
String message = CardUtil.replaceSourceName(chooseUseText, sourceObject.getName());
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);
if (player != null && player.canRespond()
&& cost.canPay(source, source.getSourceId(), player.getId(), game)
&& player.chooseUse(Outcome.Detriment, message, source, game)) {
cost.clearPaid();
if (cost.pay(source, game, source.getSourceId(), player.getId(), false, null)) {
if (!game.isSimulation()) {
game.informPlayers(player.getLogName() + " pays the cost to prevent the effect");
Mana mana = new Mana();
if (game != null) {
Player controller = getPlayer(game, source);
MageObject sourceObject = game.getObject(source.getSourceId());
String message = CardUtil.replaceSourceName(chooseUseText, sourceObject.getName());
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);
if (player != null && player.canRespond()
&& cost.canPay(source, source.getSourceId(), player.getId(), game)
&& player.chooseUse(Outcome.Detriment, message, source, game)) {
cost.clearPaid();
if (cost.pay(source, game, source.getSourceId(), player.getId(), false, null)) {
if (!game.isSimulation()) {
game.informPlayers(player.getLogName() + " pays the cost to prevent the effect");
}
return mana;
}
return null;
}
}
}

View file

@ -82,6 +82,10 @@ public class DynamicManaEffect extends ManaEffect {
@Override
public List<Mana> getNetMana(Game game, Ability source) {
List<Mana> netMana = new ArrayList<>();
if (game == null) {
return netMana;
}
Mana computedMana = new Mana();
int count;
if (netAmount != null) {
@ -108,7 +112,6 @@ public class DynamicManaEffect extends ManaEffect {
} else {
computedMana.setGeneric(count);
}
List<Mana> netMana = new ArrayList<>();
netMana.add(computedMana);
return netMana;
}
@ -116,8 +119,10 @@ public class DynamicManaEffect extends ManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
Mana computedMana = new Mana();
if (game == null) {
return computedMana;
}
int count = amount.calculate(game, source, this);
if (baseMana.getBlack() > 0) {
computedMana.setBlack(count);
} else if (baseMana.getBlue() > 0) {

View file

@ -1,8 +1,5 @@
package mage.abilities.mana;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.Mana;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.costs.Cost;
@ -14,8 +11,11 @@ import mage.constants.TimingRule;
import mage.constants.Zone;
import mage.game.Game;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl implements ManaAbility {
@ -63,6 +63,8 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl
/**
* Used to check the possible mana production to determine which spells
* and/or abilities can be used. (player.getPlayable()).
* <p>
* Also used for deck's card mana cycle with game = null (score system, etc)
*
* @param game
* @return

View file

@ -115,6 +115,9 @@ class AnyColorLandsProduceManaEffect extends ManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
Mana mana = new Mana();
if (game == null) {
return mana;
}
Mana types = getManaTypes(game, source);
Choice choice = new ChoiceColor(true);
choice.getChoices().clear();

View file

@ -110,6 +110,9 @@ class AnyColorPermanentTypesManaEffect extends ManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
Mana mana = new Mana();
if (game == null) {
return mana;
}
Mana types = getManaTypes(game, source);
Choice choice = new ChoiceColor(true);
choice.getChoices().clear();

View file

@ -66,6 +66,9 @@ class CommanderIdentityManaEffect extends ManaEffect {
@Override
public List<Mana> getNetMana(Game game, Ability source) {
List<Mana> netMana = new ArrayList<>();
if (game == null) {
return netMana;
}
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
for (UUID commanderId : game.getCommandersIds(controller)) {
@ -96,6 +99,9 @@ class CommanderIdentityManaEffect extends ManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
Mana mana = new Mana();
if (game == null) {
return mana;
}
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Choice choice = new ChoiceImpl();

View file

@ -1,7 +1,5 @@
package mage.abilities.mana;
import java.util.ArrayList;
import java.util.List;
import mage.Mana;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
@ -10,6 +8,9 @@ import mage.constants.AbilityType;
import mage.constants.Zone;
import mage.game.Game;
import java.util.ArrayList;
import java.util.List;
/**
* see 20110715 - 605.1b
*
@ -53,7 +54,7 @@ public abstract class TriggeredManaAbility extends TriggeredAbilityImpl implemen
}
return newNetMana;
}
return new ArrayList<Mana>(netMana);
return new ArrayList<>(netMana);
}
/**

View file

@ -14,7 +14,7 @@ public class ManaChoice {
for (int i = 0; i < amount; i++) {
ChoiceColor choiceColor = new ChoiceColor();
if (amount > 1) {
choiceColor.setMessage("Choose color " + (i+1));
choiceColor.setMessage("Choose color " + (i + 1));
}
if (!player.choose(Outcome.Benefit, choiceColor, game)) {
return null;
@ -25,27 +25,28 @@ public class ManaChoice {
}
public static Mana chooseTwoDifferentColors(Player player, Game game) {
if (player == null) {
return null;
Mana mana = new Mana();
if (game == null || player == null) {
return mana;
}
ChoiceColor color1 = new ChoiceColor(true, "Choose color 1");
if (!player.choose(Outcome.PutManaInPool, color1, game) || color1.getColor() == null) {
return null;
return mana;
}
ChoiceColor color2 = new ChoiceColor(true, "Choose color 2");
color2.removeColorFromChoices(color1.getChoice());
if (!player.choose(Outcome.PutManaInPool, color2, game) || color2.getColor() == null) {
return null;
return mana;
}
if (color1.getColor().equals(color2.getColor())) {
game.informPlayers("Player " + player.getName() + " is cheating with mana choices.");
return null;
return mana;
}
Mana mana = new Mana();
mana.add(color1.getMana(1));
mana.add(color2.getMana(1));
return mana;