* Added a check if life of players is going down during infinite loop check. Is so no Draw check message is shown (fixes #4557).

This commit is contained in:
LevelX2 2018-02-28 17:24:42 +01:00
parent 6516e7eea4
commit 8c8b4ce019
4 changed files with 107 additions and 26 deletions

View file

@ -51,18 +51,23 @@ import mage.target.common.TargetCreatureOrPlayer;
public class NivMizzetTheFiremind extends CardImpl {
public NivMizzetTheFiremind(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{U}{R}{R}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}{R}{R}");
addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.DRAGON);
this.subtype.add(SubType.WIZARD);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever you draw a card, Niv-Mizzet, the Firemind deals 1 damage to target creature or player.
Ability ability = new DrawCardControllerTriggeredAbility(new DamageTargetEffect(1), false);
ability.addTarget(new TargetCreatureOrPlayer());
this.addAbility(ability);
// {T}: Draw a card.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new TapSourceCost()));
}

View file

@ -41,8 +41,8 @@ import mage.abilities.keyword.IndestructibleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
@ -56,7 +56,7 @@ import mage.players.Player;
public class StuffyDoll extends CardImpl {
public StuffyDoll(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}");
this.subtype.add(SubType.CONSTRUCT);
this.power = new MageInt(0);
this.toughness = new MageInt(1);
@ -67,7 +67,7 @@ public class StuffyDoll extends CardImpl {
this.addAbility(IndestructibleAbility.getInstance());
// Whenever Stuffy Doll is dealt damage, it deals that much damage to the chosen player.
this.addAbility(new StuffyDollTriggeredAbility());
// {tap}: Stuffy Doll deals 1 damage to itself.
// {T}: Stuffy Doll deals 1 damage to itself.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageSelfEffect(1), new TapSourceCost()));
}

View file

@ -740,7 +740,7 @@ public class TestPlayer implements Player {
@Override
public boolean choose(Outcome outcome, Choice choice, Game game) {
if (!choices.isEmpty()) {
if(choice.setChoiceByAnswers(choices, true)){
if (choice.setChoiceByAnswers(choices, true)) {
return true;
}
}
@ -2167,6 +2167,24 @@ public class TestPlayer implements Player {
@Override
public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
switch (ability.getSpellAbilityType()) {
case SPLIT:
case SPLIT_FUSED:
case SPLIT_AFTERMATH:
if (!choices.isEmpty()) {
MageObject object = game.getObject(ability.getSourceId());
if (object != null) {
LinkedHashMap<UUID, ActivatedAbility> useableAbilities = computerPlayer.getSpellAbilities(object, game.getState().getZone(object.getId()), game);
for (String choose : choices) {
for (ActivatedAbility actiavtedAbility : useableAbilities.values()) {
if (actiavtedAbility.getRule().startsWith(choose)) {
return (SpellAbility) actiavtedAbility;
}
}
}
}
}
}
return computerPlayer.chooseSpellAbilityForCast(ability, game, noMana);
}
@ -2176,13 +2194,17 @@ public class TestPlayer implements Player {
}
@Override
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) {
public boolean choose(Outcome outcome, Target target,
UUID sourceId, Game game
) {
// needed to call here the TestPlayer because it's overwitten
return choose(outcome, target, sourceId, game, null);
}
@Override
public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) {
public boolean choose(Outcome outcome, Cards cards,
TargetCard target, Game game
) {
if (!choices.isEmpty()) {
for (String choose2 : choices) {
// TODO: More targetting to fix
@ -2212,58 +2234,78 @@ public class TestPlayer implements Player {
}
@Override
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) {
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target,
Ability source, Game game
) {
return computerPlayer.chooseTargetAmount(outcome, target, source, game);
}
@Override
public boolean chooseMulligan(Game game) {
public boolean chooseMulligan(Game game
) {
return computerPlayer.chooseMulligan(game);
}
@Override
public boolean choosePile(Outcome outcome, String message, List<? extends Card> pile1, List<? extends Card> pile2, Game game) {
public boolean choosePile(Outcome outcome, String message,
List<? extends Card> pile1, List<? extends Card> pile2,
Game game
) {
return computerPlayer.choosePile(outcome, message, pile1, pile2, game);
}
@Override
public boolean playMana(Ability ability, ManaCost unpaid, String promptText, Game game) {
public boolean playMana(Ability ability, ManaCost unpaid,
String promptText, Game game
) {
groupsForTargetHandling = null;
return computerPlayer.playMana(ability, unpaid, promptText, game);
}
@Override
public UUID chooseAttackerOrder(List<Permanent> attacker, Game game) {
public UUID chooseAttackerOrder(List<Permanent> attacker, Game game
) {
return computerPlayer.chooseAttackerOrder(attacker, game);
}
@Override
public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup, List<UUID> blockerOrder, Game game) {
public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup,
List<UUID> blockerOrder, Game game
) {
return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
}
@Override
public void assignDamage(int damage, List<UUID> targets, String singleTargetName, UUID sourceId, Game game) {
public void assignDamage(int damage, List<UUID> targets,
String singleTargetName, UUID sourceId,
Game game
) {
computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game);
}
@Override
public void sideboard(Match match, Deck deck) {
public void sideboard(Match match, Deck deck
) {
computerPlayer.sideboard(match, deck);
}
@Override
public void construct(Tournament tournament, Deck deck) {
public void construct(Tournament tournament, Deck deck
) {
computerPlayer.construct(tournament, deck);
}
@Override
public void pickCard(List<Card> cards, Deck deck, Draft draft) {
public void pickCard(List<Card> cards, Deck deck,
Draft draft
) {
computerPlayer.pickCard(cards, deck, draft);
}
@Override
public boolean scry(int value, Ability source, Game game) {
public boolean scry(int value, Ability source,
Game game
) {
// Don't scry at the start of the game.
if (game.getTurnNum() == 1 && game.getStep() == null) {
return false;
@ -2272,37 +2314,51 @@ public class TestPlayer implements Player {
}
@Override
public boolean moveCards(Card card, Zone toZone, Ability source, Game game) {
public boolean moveCards(Card card, Zone toZone,
Ability source, Game game
) {
return computerPlayer.moveCards(card, toZone, source, game);
}
@Override
public boolean moveCards(Card card, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects) {
public boolean moveCards(Card card, Zone toZone,
Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
) {
return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
}
@Override
public boolean moveCards(Cards cards, Zone toZone, Ability source, Game game) {
public boolean moveCards(Cards cards, Zone toZone,
Ability source, Game game
) {
return computerPlayer.moveCards(cards, toZone, source, game);
}
@Override
public boolean moveCards(Set<Card> cards, Zone toZone, Ability source, Game game) {
public boolean moveCards(Set<Card> cards, Zone toZone,
Ability source, Game game
) {
return computerPlayer.moveCards(cards, toZone, source, game);
}
@Override
public boolean moveCards(Set<Card> cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects) {
public boolean moveCards(Set<Card> cards, Zone toZone,
Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
) {
return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
}
@Override
public boolean hasDesignation(DesignationType designationName) {
public boolean hasDesignation(DesignationType designationName
) {
return computerPlayer.hasDesignation(designationName);
}
@Override
public void addDesignation(Designation designation) {
public void addDesignation(Designation designation
) {
computerPlayer.addDesignation(designation);
}

View file

@ -156,9 +156,12 @@ public abstract class GameImpl implements Game, Serializable {
private final int startLife;
protected PlayerList playerList;
// infinite loop check (no copy of this attributes neccessary)
private int infiniteLoopCounter; // used to check if the game is in an infinite loop
private int lastNumberOfAbilitiesOnTheStack; // used to check how long no new ability was put to stack
private List<Integer> lastPlayersLifes = null; // if life is going down, it's no infinite loop
private final LinkedList<UUID> stackObjectsCheck = new LinkedList<>(); // used to check if different sources used the stack
// used to set the counters a permanent adds the battlefield (if no replacement effect is used e.g. Persist)
protected Map<UUID, Counters> enterWithCounters = new HashMap<>();
// used to proceed player conceding requests
@ -1418,6 +1421,23 @@ public abstract class GameImpl implements Game, Serializable {
protected void checkInfiniteLoop(UUID removedStackObjectSourceId) {
if (stackObjectsCheck.contains(removedStackObjectSourceId)
&& getStack().size() >= lastNumberOfAbilitiesOnTheStack) {
// Create a list of players life
List<Integer> newLastPlayersLifes = new ArrayList<>();
for (Player player : this.getPlayers().values()) {
newLastPlayersLifes.add(player.getLife());
}
// Check if a player is loosing life
if (lastPlayersLifes != null && lastPlayersLifes.size() == newLastPlayersLifes.size()) {
for (int i = 0; i < newLastPlayersLifes.size(); i++) {
if (newLastPlayersLifes.get(i) < lastPlayersLifes.get(i)) {
// player is loosing life
lastPlayersLifes = null;
infiniteLoopCounter = 0; // reset the infinite counter
}
}
} else {
lastPlayersLifes = newLastPlayersLifes;
}
infiniteLoopCounter++;
if (infiniteLoopCounter > 15) {
Player controller = getPlayer(getControllerId(removedStackObjectSourceId));