mirror of
https://github.com/correl/mage.git
synced 2025-01-11 11:05:23 +00:00
* 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:
parent
6516e7eea4
commit
8c8b4ce019
4 changed files with 107 additions and 26 deletions
|
@ -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()));
|
||||
}
|
||||
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue