diff --git a/Mage.Sets/src/mage/cards/n/NivMizzetTheFiremind.java b/Mage.Sets/src/mage/cards/n/NivMizzetTheFiremind.java index e4b3cc805c8..486e8cfd29a 100644 --- a/Mage.Sets/src/mage/cards/n/NivMizzetTheFiremind.java +++ b/Mage.Sets/src/mage/cards/n/NivMizzetTheFiremind.java @@ -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())); } diff --git a/Mage.Sets/src/mage/cards/s/StuffyDoll.java b/Mage.Sets/src/mage/cards/s/StuffyDoll.java index 5a7a8986ac9..99f01cfe7ee 100644 --- a/Mage.Sets/src/mage/cards/s/StuffyDoll.java +++ b/Mage.Sets/src/mage/cards/s/StuffyDoll.java @@ -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())); } diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 417fc5a46d3..41d0c95c6de 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -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 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 pile1, List pile2, Game game) { + public boolean choosePile(Outcome outcome, String message, + List pile1, List 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 attacker, Game game) { + public UUID chooseAttackerOrder(List attacker, Game game + ) { return computerPlayer.chooseAttackerOrder(attacker, game); } @Override - public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { + public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, + List blockerOrder, Game game + ) { return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); } @Override - public void assignDamage(int damage, List targets, String singleTargetName, UUID sourceId, Game game) { + public void assignDamage(int damage, List 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 cards, Deck deck, Draft draft) { + public void pickCard(List 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 appliedEffects) { + public boolean moveCards(Card card, Zone toZone, + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List 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 cards, Zone toZone, Ability source, Game game) { + public boolean moveCards(Set cards, Zone toZone, + Ability source, Game game + ) { return computerPlayer.moveCards(cards, toZone, source, game); } @Override - public boolean moveCards(Set cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects) { + public boolean moveCards(Set cards, Zone toZone, + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List 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); } diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index efb8984d58c..3c9714d00e9 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -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 lastPlayersLifes = null; // if life is going down, it's no infinite loop private final LinkedList 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 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 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));