diff --git a/Mage.Sets/src/mage/cards/l/LieutenantsOfTheGuard.java b/Mage.Sets/src/mage/cards/l/LieutenantsOfTheGuard.java
index b9c327a7855..db5983ad7a4 100644
--- a/Mage.Sets/src/mage/cards/l/LieutenantsOfTheGuard.java
+++ b/Mage.Sets/src/mage/cards/l/LieutenantsOfTheGuard.java
@@ -1,4 +1,3 @@
-
package mage.cards.l;
import java.util.UUID;
@@ -11,8 +10,8 @@ import mage.abilities.effects.common.CreateTokenEffect;
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.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
@@ -27,13 +26,15 @@ public final class LieutenantsOfTheGuard extends CardImpl {
public LieutenantsOfTheGuard(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}");
-
+
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.SOLDIER);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
- // Council's dilemma — When Lieutenants of the Guard enters the battlefield, starting with you, each player votes for strength or numbers. Put a +1/+1 counter on Lieutenants of the Guard for each strength vote and put a 1/1 white Soldier creature token onto the battlefield for each numbers vote.
+ // Council's dilemma — When Lieutenants of the Guard enters the battlefield, starting with you,
+ // each player votes for strength or numbers. Put a +1/+1 counter on Lieutenants of the Guard for each
+ // strength vote and put a 1/1 white Soldier creature token onto the battlefield for each numbers vote.
this.addAbility(new EntersBattlefieldTriggeredAbility(new LieutenantsOfTheGuardDilemmaEffect(), false, "Council's dilemma — "));
}
@@ -48,6 +49,7 @@ public final class LieutenantsOfTheGuard extends CardImpl {
}
class LieutenantsOfTheGuardDilemmaEffect extends CouncilsDilemmaVoteEffect {
+
public LieutenantsOfTheGuardDilemmaEffect() {
super(Outcome.Benefit);
this.staticText = "starting with you, each player votes for strength or numbers. Put a +1/+1 counter on {this} for each strength vote and put a 1/1 white Soldier creature token onto the battlefield for each numbers vote.";
@@ -62,7 +64,9 @@ class LieutenantsOfTheGuardDilemmaEffect extends CouncilsDilemmaVoteEffect {
Player controller = game.getPlayer(source.getControllerId());
//If no controller, exit out here and do not vote.
- if (controller == null) return false;
+ if (controller == null) {
+ return false;
+ }
this.vote("strength", "numbers", controller, game, source);
@@ -70,8 +74,9 @@ class LieutenantsOfTheGuardDilemmaEffect extends CouncilsDilemmaVoteEffect {
//Strength Votes
//If strength received zero votes or the permanent is no longer on the battlefield, do not attempt to put P1P1 counters on it.
- if (voteOneCount > 0 && permanent != null)
+ if (voteOneCount > 0 && permanent != null) {
permanent.addCounters(CounterType.P1P1.createInstance(voteOneCount), source, game);
+ }
//Numbers Votes
if (voteTwoCount > 0) {
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/rules/WorldEnchantmentsRuleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/rules/WorldEnchantmentsRuleTest.java
index 6a5258b2126..e2866affcc4 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/rules/WorldEnchantmentsRuleTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/rules/WorldEnchantmentsRuleTest.java
@@ -1,4 +1,3 @@
-
package org.mage.test.cards.rules;
import mage.constants.PhaseStep;
@@ -10,15 +9,15 @@ import org.mage.test.serverside.base.CardTestMultiPlayerBase;
*
* @author LevelX2
*/
-
public class WorldEnchantmentsRuleTest extends CardTestMultiPlayerBase {
/**
- * 704.5m If two or more permanents have the supertype world, all except the one that has had
- * the world supertype for the shortest amount of time are put into their owners' graveyards.
- * In the event of a tie for the shortest amount of time, all are put into their owners' graveyards.
- * This is called the “world rule.
- *
+ * 704.5m If two or more permanents have the supertype world, all except the
+ * one that has had the world supertype for the shortest amount of time are
+ * put into their owners' graveyards. In the event of a tie for the shortest
+ * amount of time, all are put into their owners' graveyards. This is called
+ * the “world rule.
+ *
*/
@Test
public void TestTwoWorldEnchantments() {
@@ -26,14 +25,14 @@ public class WorldEnchantmentsRuleTest extends CardTestMultiPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
addCard(Zone.HAND, playerA, "Nether Void", 1);
addCard(Zone.HAND, playerA, "Silvercoat Lion", 1);
-
+
addCard(Zone.BATTLEFIELD, playerD, "Swamp", 7);
addCard(Zone.HAND, playerD, "Nether Void", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nether Void");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); // just needed to get different craete time to second Nether Void
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Nether Void");
-
+
setStopAt(2, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
@@ -64,4 +63,83 @@ public class WorldEnchantmentsRuleTest extends CardTestMultiPlayerBase {
assertPermanentCount(playerA, "Nether Void", 1);
assertPermanentCount(playerC, "Nether Void", 1);
}
-}
\ No newline at end of file
+
+ // 704.5 In the event of a tie for the shortest amount of time, all are put into their owners’ graveyards. This is called the “world rule.”
+ // In this example the execution order of the leaves the battlefield triggers of the two Oblivion Rings decide, which World Enchnatment may stay
+ // Player order: A -> D -> C -> B
+ @Test
+ public void TestTwoWorldEnchantmentsFromTriggers() {
+ setStrictChooseMode(true);
+ // When Oblivion Ring enters the battlefield, exile another target nonland permanent.
+ // When Oblivion Ring leaves the battlefield, return the exiled card to the battlefield under its owner's control.
+ addCard(Zone.HAND, playerA, "Oblivion Ring", 1);
+ // All creatures have haste.
+ addCard(Zone.HAND, playerA, "Concordant Crossroads", 1);
+ addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
+ addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
+
+ // When Oblivion Ring enters the battlefield, exile another target nonland permanent.
+ // When Oblivion Ring leaves the battlefield, return the exiled card to the battlefield under its owner's control.
+ addCard(Zone.HAND, playerD, "Oblivion Ring", 1); // Enchantment {2}{W}
+ // Destroy all white permanents.
+ addCard(Zone.HAND, playerD, "Anarchy", 1); // Sorcery {2}{R}{R}
+
+ // All creatures have haste.
+ addCard(Zone.BATTLEFIELD, playerD, "Concordant Crossroads", 1); // World Enchantment {G}
+ addCard(Zone.BATTLEFIELD, playerD, "Plains", 1);
+ addCard(Zone.BATTLEFIELD, playerD, "Mountain", 6);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Oblivion Ring");
+ addTarget(playerA, "Concordant Crossroads");
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Concordant Crossroads");
+ castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Oblivion Ring");
+ addTarget(playerD, "Concordant Crossroads");
+ castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Anarchy"); // Both World Enchantments return at the same time and go to grave
+
+ setStopAt(2, PhaseStep.BEGIN_COMBAT);
+ execute();
+ assertAllCommandsUsed();
+
+ assertPermanentCount(playerD, "Concordant Crossroads", 0);
+ assertPermanentCount(playerA, "Concordant Crossroads", 1);
+
+ assertGraveyardCount(playerA, "Oblivion Ring", 1);
+ assertGraveyardCount(playerA, "Concordant Crossroads", 0);
+ assertGraveyardCount(playerD, "Oblivion Ring", 1);
+ assertGraveyardCount(playerD, "Concordant Crossroads", 1);
+ assertGraveyardCount(playerD, "Anarchy", 1);
+ }
+
+ @Test
+ public void TestTwoWorldEnchantmentsWithSameOrder() {
+ setStrictChooseMode(true);
+ // When Charmed Griffin enters the battlefield, each other player may put an artifact or enchantment card onto the battlefield from their hand.
+ addCard(Zone.HAND, playerA, "Charmed Griffin", 1);
+ addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
+
+ // All creatures have haste.
+ addCard(Zone.HAND, playerD, "Concordant Crossroads", 1);
+ addCard(Zone.HAND, playerB, "Concordant Crossroads", 1);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Charmed Griffin");
+
+ setChoice(playerD, "Yes"); // Put an artifact or enchantment card from your hand onto the battlefield?
+ setChoice(playerD, "Concordant Crossroads");
+
+ setChoice(playerB, "Yes"); // Put an artifact or enchantment card from your hand onto the battlefield?
+ setChoice(playerB, "Concordant Crossroads");
+
+ concede(1, PhaseStep.PRECOMBAT_MAIN, playerC); // World Enchantments come into range
+
+ setStopAt(1, PhaseStep.BEGIN_COMBAT);
+ execute();
+ assertAllCommandsUsed();
+
+ assertPermanentCount(playerA, "Charmed Griffin", 1);
+ assertPermanentCount(playerD, "Concordant Crossroads", 0);
+ assertPermanentCount(playerB, "Concordant Crossroads", 0);
+
+ assertGraveyardCount(playerB, "Concordant Crossroads", 1);
+ assertGraveyardCount(playerD, "Concordant Crossroads", 1);
+ }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/AuraReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/AuraReplacementEffect.java
index 2369835049a..8ad5ee3acd0 100644
--- a/Mage/src/main/java/mage/abilities/effects/AuraReplacementEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/AuraReplacementEffect.java
@@ -1,5 +1,6 @@
package mage.abilities.effects;
+import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
@@ -17,8 +18,6 @@ import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetCardInGraveyard;
-import java.util.UUID;
-
/**
* Cards with the Aura subtype don't change the zone they are in, if there is no
* valid target on the battlefield. Also, when entering the battlefield and it
@@ -163,7 +162,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
PermanentCard permanent = new PermanentCard(card, (controllingPlayer == null ? card.getOwnerId() : controllingPlayer.getId()), game);
ZoneChangeEvent zoneChangeEvent = new ZoneChangeEvent(permanent, controllerId, fromZone, Zone.BATTLEFIELD);
permanent.updateZoneChangeCounter(game, zoneChangeEvent);
- game.getBattlefield().addPermanent(permanent);
+ game.addPermanent(permanent, 0);
card.setZone(Zone.BATTLEFIELD, game);
if (permanent.entersBattlefield(event.getSourceId(), game, fromZone, true)) {
if (targetCard != null) {
@@ -196,9 +195,9 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
return card != null && (card.isEnchantment() && card.hasSubtype(SubType.AURA, game)
|| // in case of transformable enchantments
(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId()) != null
- && card.getSecondCardFace() != null
- && card.getSecondCardFace().isEnchantment()
- && card.getSecondCardFace().hasSubtype(SubType.AURA, game)));
+ && card.getSecondCardFace() != null
+ && card.getSecondCardFace().isEnchantment()
+ && card.getSecondCardFace().hasSubtype(SubType.AURA, game)));
}
return false;
}
diff --git a/Mage/src/main/java/mage/game/Game.java b/Mage/src/main/java/mage/game/Game.java
index 106a68ece5c..8a3a983ea46 100644
--- a/Mage/src/main/java/mage/game/Game.java
+++ b/Mage/src/main/java/mage/game/Game.java
@@ -373,7 +373,16 @@ public interface Game extends MageItem, Serializable {
void addCommander(Commander commander);
- void addPermanent(Permanent permanent);
+ /**
+ * Adds a permanent to the battlefield
+ *
+ * @param permanent
+ * @param createOrder upcounting number from state about the create order of
+ * all permanents. Can equal for multiple permanents, if
+ * they go to battlefield at the same time. If the value
+ * is set to 0, a next number will be set automatically.
+ */
+ void addPermanent(Permanent permanent, int createOrder);
// priority method
void sendPlayerAction(PlayerAction playerAction, UUID playerId, Object data);
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index 05341b64b4a..48604e957e4 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -187,6 +187,7 @@ public abstract class GameImpl implements Game, Serializable {
this.startLife = game.startLife;
this.enterWithCounters.putAll(game.enterWithCounters);
this.startingSize = game.startingSize;
+ this.gameStopped = game.gameStopped;
}
@Override
@@ -1419,7 +1420,7 @@ public abstract class GameImpl implements Game, Serializable {
if (spell != null) {
if (spell.getCommandedBy() != null) {
UUID commandedBy = spell.getCommandedBy();
- UUID spellControllerId = null;
+ UUID spellControllerId;
if (commandedBy.equals(spell.getControllerId())) {
spellControllerId = spell.getSpellAbility().getFirstTarget(); // i.e. resolved spell is Word of Command
} else {
@@ -1605,9 +1606,12 @@ public abstract class GameImpl implements Game, Serializable {
}
@Override
- public void addPermanent(Permanent permanent) {
+ public void addPermanent(Permanent permanent, int createOrder) {
+ if (createOrder == 0) {
+ createOrder = getState().getNextPermanentOrderNumber();
+ }
+ permanent.setCreateOrder(createOrder);
getBattlefield().addPermanent(permanent);
- permanent.setCreateOrder(getState().getNextPermanentOrderNumber());
}
@Override
@@ -2254,30 +2258,38 @@ public abstract class GameImpl implements Game, Serializable {
}
}
}
- //704.5m - World Enchantments
+ //704.5k - World Enchantments
if (worldEnchantment.size() > 1) {
int newestCard = -1;
+ Set controllerIdOfNewest = new HashSet<>();
Permanent newestPermanent = null;
for (Permanent permanent : worldEnchantment) {
if (newestCard == -1) {
newestCard = permanent.getCreateOrder();
newestPermanent = permanent;
+ controllerIdOfNewest.clear();
+ controllerIdOfNewest.add(permanent.getControllerId());
} else if (newestCard < permanent.getCreateOrder()) {
newestCard = permanent.getCreateOrder();
newestPermanent = permanent;
+ controllerIdOfNewest.clear();
+ controllerIdOfNewest.add(permanent.getControllerId());
} else if (newestCard == permanent.getCreateOrder()) {
+ // In the event of a tie for the shortest amount of time, all are put into their owners’ graveyards. This is called the “world rule.”
newestPermanent = null;
+ controllerIdOfNewest.add(permanent.getControllerId());
}
}
+ for (UUID controllerId : controllerIdOfNewest) {
+ PlayerList newestPermanentControllerRange = state.getPlayersInRange(controllerId, this);
- PlayerList newestPermanentControllerRange = state.getPlayersInRange(newestPermanent.getControllerId(), this);
-
- // 801.12 The "world rule" applies to a permanent only if other world permanents are within its controller's range of influence.
- for (Permanent permanent : worldEnchantment) {
- if (newestPermanentControllerRange.contains(permanent.getControllerId())
- && !Objects.equals(newestPermanent, permanent)) {
- movePermanentToGraveyardWithInfo(permanent);
- somethingHappened = true;
+ // 801.12 The "world rule" applies to a permanent only if other world permanents are within its controller's range of influence.
+ for (Permanent permanent : worldEnchantment) {
+ if (newestPermanentControllerRange.contains(permanent.getControllerId())
+ && !Objects.equals(newestPermanent, permanent)) {
+ movePermanentToGraveyardWithInfo(permanent);
+ somethingHappened = true;
+ }
}
}
}
@@ -2788,7 +2800,7 @@ public abstract class GameImpl implements Game, Serializable {
}
if (amountToPrevent != Integer.MAX_VALUE) {
// set remaining amount
- result.setRemainingAmount(amountToPrevent -= result.getPreventedDamage());
+ result.setRemainingAmount(amountToPrevent - result.getPreventedDamage());
}
MageObject damageSource = game.getObject(damageEvent.getSourceId());
MageObject preventionSource = game.getObject(source.getSourceId());
@@ -3058,7 +3070,7 @@ public abstract class GameImpl implements Game, Serializable {
PermanentCard newPermanent = new PermanentCard(permanentCard.getCard(), ownerId, this);
getPermanentsEntering().put(newPermanent.getId(), newPermanent);
newPermanent.entersBattlefield(newPermanent.getId(), this, Zone.OUTSIDE, false);
- getBattlefield().addPermanent(newPermanent);
+ addPermanent(newPermanent, getState().getNextPermanentOrderNumber());
getPermanentsEntering().remove(newPermanent.getId());
newPermanent.removeSummoningSickness();
if (permanentCard.isTapped()) {
diff --git a/Mage/src/main/java/mage/game/ZonesHandler.java b/Mage/src/main/java/mage/game/ZonesHandler.java
index cb993563d32..9a2289ffa6b 100644
--- a/Mage/src/main/java/mage/game/ZonesHandler.java
+++ b/Mage/src/main/java/mage/game/ZonesHandler.java
@@ -1,5 +1,6 @@
package mage.game;
+import java.util.*;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
@@ -18,8 +19,6 @@ import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.TargetCard;
-import java.util.*;
-
/**
* Created by samuelsandeen on 9/6/16.
*/
@@ -27,7 +26,7 @@ public final class ZonesHandler {
public static boolean cast(ZoneChangeInfo info, Game game) {
if (maybeRemoveFromSourceZone(info, game)) {
- placeInDestinationZone(info, game);
+ placeInDestinationZone(info, game, 0);
// create a group zone change event if a card is moved to stack for casting (it's always only one card, but some effects check for group events (one or more xxx))
Set cards = new HashSet<>();
Set tokens = new HashSet<>();
@@ -53,7 +52,7 @@ public final class ZonesHandler {
public static List moveCards(List zoneChangeInfos, Game game) {
// Handle Unmelded Meld Cards
- for (ListIterator itr = zoneChangeInfos.listIterator(); itr.hasNext(); ) {
+ for (ListIterator itr = zoneChangeInfos.listIterator(); itr.hasNext();) {
ZoneChangeInfo info = itr.next();
MeldCard card = game.getMeldCard(info.event.getTargetId());
// Copies should be handled as normal cards.
@@ -67,8 +66,13 @@ public final class ZonesHandler {
}
}
zoneChangeInfos.removeIf(zoneChangeInfo -> !maybeRemoveFromSourceZone(zoneChangeInfo, game));
+ int createOrder = 0;
for (ZoneChangeInfo zoneChangeInfo : zoneChangeInfos) {
- placeInDestinationZone(zoneChangeInfo, game);
+ if (createOrder == 0 && Zone.BATTLEFIELD.equals(zoneChangeInfo.event.getToZone())) {
+ // All permanents go to battlefield at the same time (=create order)
+ createOrder = game.getState().getNextPermanentOrderNumber();
+ }
+ placeInDestinationZone(zoneChangeInfo, game, createOrder);
if (game.getPhase() != null) { // moving cards to zones before game started does not need events
game.addSimultaneousEvent(zoneChangeInfo.event);
}
@@ -76,14 +80,14 @@ public final class ZonesHandler {
return zoneChangeInfos;
}
- private static void placeInDestinationZone(ZoneChangeInfo info, Game game) {
+ private static void placeInDestinationZone(ZoneChangeInfo info, Game game, int createOrder) {
// Handle unmelded cards
if (info instanceof ZoneChangeInfo.Unmelded) {
ZoneChangeInfo.Unmelded unmelded = (ZoneChangeInfo.Unmelded) info;
Zone toZone = null;
for (ZoneChangeInfo subInfo : unmelded.subInfo) {
toZone = subInfo.event.getToZone();
- placeInDestinationZone(subInfo, game);
+ placeInDestinationZone(subInfo, game, createOrder);
}
// We arbitrarily prefer the bottom half card. This should never be relevant.
if (toZone != null) {
@@ -161,7 +165,7 @@ public final class ZonesHandler {
break;
case BATTLEFIELD:
Permanent permanent = event.getTarget();
- game.addPermanent(permanent);
+ game.addPermanent(permanent, createOrder);
game.getPermanentsEntering().remove(permanent.getId());
break;
default:
@@ -197,7 +201,7 @@ public final class ZonesHandler {
if (info instanceof ZoneChangeInfo.Unmelded) {
ZoneChangeInfo.Unmelded unmelded = (ZoneChangeInfo.Unmelded) info;
MeldCard meld = game.getMeldCard(info.event.getTargetId());
- for (Iterator itr = unmelded.subInfo.iterator(); itr.hasNext(); ) {
+ for (Iterator itr = unmelded.subInfo.iterator(); itr.hasNext();) {
ZoneChangeInfo subInfo = itr.next();
if (!maybeRemoveFromSourceZone(subInfo, game)) {
itr.remove();
diff --git a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
index 30919e478f3..0ba43809a5f 100644
--- a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
@@ -1,5 +1,9 @@
package mage.game.permanent.token;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.UUID;
import mage.MageObject;
import mage.MageObjectImpl;
import mage.abilities.Ability;
@@ -14,11 +18,6 @@ import mage.game.permanent.PermanentToken;
import mage.players.Player;
import mage.util.RandomUtil;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.UUID;
-
public abstract class TokenImpl extends MageObjectImpl implements Token {
protected String description;
@@ -202,8 +201,9 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
}
}
game.setScopeRelevant(false);
+ int createOrder = game.getState().getNextPermanentOrderNumber();
for (Permanent permanent : permanentsEntered) {
- game.addPermanent(permanent);
+ game.addPermanent(permanent, createOrder);
permanent.setZone(Zone.BATTLEFIELD, game);
game.getPermanentsEntering().remove(permanent.getId());
@@ -242,8 +242,8 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
}
/**
- * Set token index to search in card-pictures-tok.txt (if set have multiple tokens with same name)
- * Default is 1
+ * Set token index to search in card-pictures-tok.txt (if set have multiple
+ * tokens with same name) Default is 1
*/
@Override
public void setTokenType(int tokenType) {