diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/rules/TokenLimitTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/rules/TokenLimitTest.java new file mode 100644 index 00000000000..af0f77589a8 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/rules/TokenLimitTest.java @@ -0,0 +1,54 @@ +package org.mage.test.cards.rules; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ + +public class TokenLimitTest extends CardTestPlayerBase { + private static final String secure = "Secure the Wastes"; + private static final String procession = "Anointed Procession"; + private static final String warrior = "Warrior"; + + @Test + public void testOnePlayerHitsLimit() { + addCard(Zone.BATTLEFIELD, playerA, procession, 4); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 34); + addCard(Zone.HAND, playerA, secure, 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, secure); + setChoice(playerA, "X=16"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, secure); + setChoice(playerA, "X=16"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, warrior, 500); + } + + @Test + public void testOnePlayerHitsLimitWithOtherPlayer() { + addCard(Zone.BATTLEFIELD, playerA, procession, 4); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 33); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 3); + addCard(Zone.HAND, playerA, secure); + addCard(Zone.HAND, playerB, secure); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, secure); + setChoice(playerA, "X=32"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, secure); + setChoice(playerB, "X=3"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, warrior, 500); + assertPermanentCount(playerB, warrior, 2); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/Battlefield.java b/Mage/src/main/java/mage/game/permanent/Battlefield.java index 24c53796f4a..4e63f574984 100644 --- a/Mage/src/main/java/mage/game/permanent/Battlefield.java +++ b/Mage/src/main/java/mage/game/permanent/Battlefield.java @@ -372,4 +372,14 @@ public class Battlefield implements Serializable { return controlChanged; } + public int countTokens(UUID controllerId) { + return field + .values() + .stream() + .filter(Objects::nonNull) + .filter(PermanentToken.class::isInstance) + .map(permanent -> permanent.isControlledBy(controllerId)) + .mapToInt(x -> x ? 1 : 0) + .sum(); + } } 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 bd42a16551f..f61dfc50f14 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java +++ b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java @@ -30,6 +30,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { private String tokenDescriptor; private boolean expansionSetCodeChecked; private Card copySourceCard; // the card the Token is a copy from + private static final int MAX_TOKENS_PER_GAME = 500; // list of set codes token images are available for protected List availableImageSetCodes = new ArrayList<>(); @@ -174,6 +175,15 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { CreateTokenEvent event = new CreateTokenEvent(source, controllerId, amount, this); if (!created || !game.replaceEvent(event)) { + int currentTokens = game.getBattlefield().countTokens(event.getPlayerId()); + int tokenSlots = Math.max(MAX_TOKENS_PER_GAME - currentTokens, 0); + if (event.getAmount() > tokenSlots) { + game.informPlayers( + "The token limit per player is " + MAX_TOKENS_PER_GAME + ", " + controller.getName() + + " will only create " + tokenSlots + " tokens." + ); + } + event.setAmount(Math.min(event.getAmount(), tokenSlots)); putOntoBattlefieldHelper(event, game, source, tapped, attacking, attackedPlayer, created); return true; }