From e976920e2d900991eb65bb283285f661a3291bd8 Mon Sep 17 00:00:00 2001 From: jimga150 Date: Wed, 14 Aug 2024 21:13:09 -0400 Subject: [PATCH] [BLB] Implement Season of Gathering and Pawprints mechanic (#12617) * Add skeleton * Implement Pawprints modal functionality * Implement Seasons of Gathering * remove unused imports * Add Pawprints test * use withPawPRintValue() instead of setter * use 0 for non-pawprint mode and modes classes and move mode validation to addMode * Use GreatestPowerAmongControlledCreaturesValue * Fix pawprints check * calcualte sleected pawprint count based on selected modes * move max pawprints check to getAvailableModes * fix max pawprints checks --- .../src/mage/player/human/HumanPlayer.java | 26 ++- .../src/mage/cards/s/SeasonOfGathering.java | 162 +++++++++++++ Mage.Sets/src/mage/sets/Bloomburrow.java | 1 + .../mage/test/cards/modal/PawPrintsTest.java | 218 ++++++++++++++++++ Mage/src/main/java/mage/abilities/Mode.java | 11 + Mage/src/main/java/mage/abilities/Modes.java | 39 +++- 6 files changed, 449 insertions(+), 8 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/s/SeasonOfGathering.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/modal/PawPrintsTest.java diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 2cf01521aa7..24acd8c575f 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -2578,20 +2578,38 @@ public class HumanPlayer extends PlayerImpl { if (!modeText.isEmpty()) { modeText = Character.toUpperCase(modeText.charAt(0)) + modeText.substring(1); } - modeMap.put(mode.getId(), modeIndex + ". " + modeText); + StringBuilder sb = new StringBuilder(); + if (mode.getPawPrintValue() > 0){ + for (int i = 0; i < mode.getPawPrintValue(); ++i){ + sb.append("{P}"); + } + sb.append(": "); + } else { + sb.append(modeIndex).append(". "); + } + modeMap.put(mode.getId(), sb.append(modeText).toString()); } } // done button for "for up" choices only - boolean canEndChoice = modes.getSelectedModes().size() >= modes.getMinModes() || modes.isMayChooseNone(); + boolean canEndChoice = (modes.getSelectedModes().size() >= modes.getMinModes() && modes.getMaxPawPrints() == 0) || + (modes.getSelectedPawPrints() >= modes.getMaxPawPrints() && modes.getMaxPawPrints() > 0) || + modes.isMayChooseNone(); if (canEndChoice) { modeMap.put(Modes.CHOOSE_OPTION_DONE_ID, "Done"); } modeMap.put(Modes.CHOOSE_OPTION_CANCEL_ID, "Cancel"); // prepare dialog - String message = "Choose mode (selected " + modes.getSelectedModes().size() + " of " + modes.getMaxModes(game, source) - + ", min " + modes.getMinModes() + ")"; + String message; + if (modes.getMaxPawPrints() == 0){ + message = "Choose mode (selected " + modes.getSelectedModes().size() + " of " + modes.getMaxModes(game, source) + + ", min " + modes.getMinModes() + ")"; + } else { + message = "Choose mode (selected " + modes.getSelectedPawPrints() + " of " + modes.getMaxPawPrints() + + " {P})"; + } + if (obj != null) { message = message + "
" + obj.getLogName(); } diff --git a/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java new file mode 100644 index 00000000000..96b73cd7a23 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java @@ -0,0 +1,162 @@ +package mage.cards.s; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.*; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author jimga150 + */ +public final class SeasonOfGathering extends CardImpl { + + public SeasonOfGathering(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}{G}"); + + // Choose up to five {P} worth of modes. You may choose the same mode more than once. + this.getSpellAbility().getModes().setMaxPawPrints(5); + this.getSpellAbility().getModes().setMinModes(0); + this.getSpellAbility().getModes().setMaxModes(5); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); + + // {P} -- Put a +1/+1 counter on a creature you control. It gains vigilance and trample until end of turn. + this.getSpellAbility().addEffect(new SeasonOfGatheringCounterEffect()); + this.spellAbility.getModes().getMode().withPawPrintValue(1); + + // {P}{P} -- Choose artifact or enchantment. Destroy all permanents of the chosen type. + Mode mode2 = new Mode(new SeasonOfGatheringRemovalEffect()); + this.getSpellAbility().addMode(mode2.withPawPrintValue(2)); + + // {P}{P}{P} -- Draw cards equal to the greatest power among creatures you control. + Mode mode3 = new Mode( + new DrawCardSourceControllerEffect(GreatestPowerAmongControlledCreaturesValue.instance) + .setText("Draw cards equal to the greatest power among creatures you control.") + ); + this.getSpellAbility().addMode(mode3.withPawPrintValue(3)); + } + + private SeasonOfGathering(final SeasonOfGathering card) { + super(card); + } + + @Override + public SeasonOfGathering copy() { + return new SeasonOfGathering(this); + } +} + +// Based on KaylasCommandCounterEffect +class SeasonOfGatheringCounterEffect extends OneShotEffect { + + SeasonOfGatheringCounterEffect() { + super(Outcome.BoostCreature); + this.staticText = "Put a +1/+1 counter on a creature you control. It gains vigilance and trample until end of turn."; + } + + private SeasonOfGatheringCounterEffect(final SeasonOfGatheringCounterEffect effect) { + super(effect); + } + + @Override + public SeasonOfGatheringCounterEffect copy() { + return new SeasonOfGatheringCounterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + TargetControlledCreaturePermanent target = new TargetControlledCreaturePermanent(); + target.withNotTarget(true); + controller.chooseTarget(outcome, target, source, game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + return false; + } + permanent.addCounters(CounterType.P1P1.createInstance(), source, game); + GainAbilityTargetEffect effect = new GainAbilityTargetEffect(VigilanceAbility.getInstance()); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + GainAbilityTargetEffect effect2 = new GainAbilityTargetEffect(TrampleAbility.getInstance()); + effect2.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect2, source); + return true; + } +} + +// Based on KindredDominanceEffect and TurnaboutEffect +class SeasonOfGatheringRemovalEffect extends OneShotEffect { + + private static final Set choice = new HashSet<>(); + + static { + choice.add(CardType.ARTIFACT.toString()); + choice.add(CardType.ENCHANTMENT.toString()); + } + + SeasonOfGatheringRemovalEffect() { + super(Outcome.DestroyPermanent); + this.staticText = "Choose artifact or enchantment. Destroy all permanents of the chosen type."; + } + + private SeasonOfGatheringRemovalEffect(final SeasonOfGatheringRemovalEffect effect) { + super(effect); + } + + @Override + public SeasonOfGatheringRemovalEffect copy() { + return new SeasonOfGatheringRemovalEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Choice choiceImpl = new ChoiceImpl(true); + choiceImpl.setMessage("Choose card type to destroy"); + choiceImpl.setChoices(choice); + if (!controller.choose(Outcome.Neutral, choiceImpl, game)) { + return false; + } + FilterPermanent filter; + switch (choiceImpl.getChoice()) { + case "Artifact": + filter = StaticFilters.FILTER_PERMANENT_ARTIFACT; + break; + case "Enchantment": + filter = StaticFilters.FILTER_PERMANENT_ENCHANTMENT; + break; + default: + throw new IllegalArgumentException("Choice is required"); + } + game.informPlayers(controller.getLogName() + " has chosen " + choiceImpl.getChoiceKey()); + return new DestroyAllEffect(filter).apply(game, source); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Bloomburrow.java b/Mage.Sets/src/mage/sets/Bloomburrow.java index 366e2315b16..d5529f240af 100644 --- a/Mage.Sets/src/mage/sets/Bloomburrow.java +++ b/Mage.Sets/src/mage/sets/Bloomburrow.java @@ -203,6 +203,7 @@ public final class Bloomburrow extends ExpansionSet { cards.add(new SetCardInfo("Scales of Shale", 110, Rarity.COMMON, mage.cards.s.ScalesOfShale.class)); cards.add(new SetCardInfo("Scavenger's Talent", 111, Rarity.RARE, mage.cards.s.ScavengersTalent.class)); cards.add(new SetCardInfo("Scrapshooter", 191, Rarity.RARE, mage.cards.s.Scrapshooter.class)); + cards.add(new SetCardInfo("Season of Gathering", 192, Rarity.MYTHIC, mage.cards.s.SeasonOfGathering.class)); cards.add(new SetCardInfo("Seasoned Warrenguard", 30, Rarity.UNCOMMON, mage.cards.s.SeasonedWarrenguard.class)); cards.add(new SetCardInfo("Seedglaive Mentor", 231, Rarity.UNCOMMON, mage.cards.s.SeedglaiveMentor.class)); cards.add(new SetCardInfo("Seedpod Squire", 232, Rarity.COMMON, mage.cards.s.SeedpodSquire.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/modal/PawPrintsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/modal/PawPrintsTest.java new file mode 100644 index 00000000000..b302b9cea0d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/modal/PawPrintsTest.java @@ -0,0 +1,218 @@ +package org.mage.test.cards.modal; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author jimga150 + */ +public class PawPrintsTest extends CardTestPlayerBase { + + @Test + public void test_Choose113() { + // Test that draw effect sees power affected by counter effect + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + // Choose up to five {P} worth of modes. You may choose the same mode more than once. + // {P} -- Put a +1/+1 counter on a creature you control. It gains vigilance and trample until end of turn. + // {P}{P} -- Choose artifact or enchantment. Destroy all permanents of the chosen type. + // {P}{P}{P} -- Draw cards equal to the greatest power among creatures you control. + addCard(Zone.HAND, playerA, "Season of Gathering"); // Instant {4}{G}{G} + + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Season of Gathering"); + setModeChoice(playerA, "1"); + setModeChoice(playerA, "1"); + setModeChoice(playerA, "3"); + addTarget(playerA, "Memnite"); // for 1 + addTarget(playerA, "Memnite"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertHandCount(playerA, 3); + + assertPowerToughness(playerA, "Memnite", 3, 3); + assertCounterCount("Memnite", CounterType.P1P1, 2); + + } + + @Test + public void test_Choose123() { + // Test that 1, 2, and 3 cannot all be selected (and that 1 and 2 will fire in that order) + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + // Choose up to five {P} worth of modes. You may choose the same mode more than once. + // {P} -- Put a +1/+1 counter on a creature you control. It gains vigilance and trample until end of turn. + // {P}{P} -- Choose artifact or enchantment. Destroy all permanents of the chosen type. + // {P}{P}{P} -- Draw cards equal to the greatest power among creatures you control. + addCard(Zone.HAND, playerA, "Season of Gathering"); // Instant {4}{G}{G} + + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + + // If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on it instead. + addCard(Zone.BATTLEFIELD, playerA, "Hardened Scales"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Season of Gathering"); + setModeChoice(playerA, "1"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, "3"); // Will be unused + addTarget(playerA, "Memnite"); // for 1 + setChoice(playerA, "Enchantment"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + // Add one more counter from Hardened Scales, which was still on the battlefield when the counter placing effect triggered + assertPowerToughness(playerA, "Memnite", 3, 3); + assertCounterCount("Memnite", CounterType.P1P1, 2); + + // But not anymore... + assertPermanentCount(playerA, "Hardened Scales", 0); + assertGraveyardCount(playerA, "Hardened Scales", 1); + + // Draw effect didnt trigger + assertHandCount(playerA, 0); + + } + + @Test + public void test_Choose2111() { + // Test that 1 and 2 will fire in that order when choices are made in reverse + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + // Choose up to five {P} worth of modes. You may choose the same mode more than once. + // {P} -- Put a +1/+1 counter on a creature you control. It gains vigilance and trample until end of turn. + // {P}{P} -- Choose artifact or enchantment. Destroy all permanents of the chosen type. + // {P}{P}{P} -- Draw cards equal to the greatest power among creatures you control. + addCard(Zone.HAND, playerA, "Season of Gathering"); // Instant {4}{G}{G} + + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + + // If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on it instead. + addCard(Zone.BATTLEFIELD, playerA, "Hardened Scales"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Season of Gathering"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, "1"); + setModeChoice(playerA, "1"); + setModeChoice(playerA, "1"); + addTarget(playerA, "Memnite"); // for 1 + addTarget(playerA, "Memnite"); // for 1 + addTarget(playerA, "Memnite"); // for 1 + setChoice(playerA, "Enchantment"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + // Add one more counter per choice from Hardened Scales, which was still on the battlefield when the counter placing effect triggered + assertPowerToughness(playerA, "Memnite", 7, 7); + assertCounterCount("Memnite", CounterType.P1P1, 6); + + // But not anymore... + assertPermanentCount(playerA, "Hardened Scales", 0); + assertGraveyardCount(playerA, "Hardened Scales", 1); + + } + + @Test + public void test_Choose1x5() { + // Test that max amount of modes can be chosen + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + // Choose up to five {P} worth of modes. You may choose the same mode more than once. + // {P} -- Put a +1/+1 counter on a creature you control. It gains vigilance and trample until end of turn. + // {P}{P} -- Choose artifact or enchantment. Destroy all permanents of the chosen type. + // {P}{P}{P} -- Draw cards equal to the greatest power among creatures you control. + addCard(Zone.HAND, playerA, "Season of Gathering"); // Instant {4}{G}{G} + + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Season of Gathering"); + for (int i = 0; i < 5; ++i){ + setModeChoice(playerA, "1"); + addTarget(playerA, "Memnite"); + } + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Memnite", 6, 6); + assertCounterCount("Memnite", CounterType.P1P1, 5); + + } + + @Test + public void test_Choose23() { + // Test that 2 and 3 fire in that order + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + // Choose up to five {P} worth of modes. You may choose the same mode more than once. + // {P} -- Put a +1/+1 counter on a creature you control. It gains vigilance and trample until end of turn. + // {P}{P} -- Choose artifact or enchantment. Destroy all permanents of the chosen type. + // {P}{P}{P} -- Draw cards equal to the greatest power among creatures you control. + addCard(Zone.HAND, playerA, "Season of Gathering"); // Instant {4}{G}{G} + + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + + // If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on it instead. + addCard(Zone.BATTLEFIELD, playerA, "Hardened Scales"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Season of Gathering"); + setModeChoice(playerA, "3"); + setModeChoice(playerA, "2"); + setChoice(playerA, "Artifact"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Hardened Scales", 1); + assertGraveyardCount(playerA, "Memnite", 1); + + // Draw effect saw no creatures, so no cards + assertHandCount(playerA, 0); + + } + + @Test + public void test_Choose122() { + // Test destroying both artifacts and enchantments + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + // Choose up to five {P} worth of modes. You may choose the same mode more than once. + // {P} -- Put a +1/+1 counter on a creature you control. It gains vigilance and trample until end of turn. + // {P}{P} -- Choose artifact or enchantment. Destroy all permanents of the chosen type. + // {P}{P}{P} -- Draw cards equal to the greatest power among creatures you control. + addCard(Zone.HAND, playerA, "Season of Gathering"); // Instant {4}{G}{G} + + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + + // If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on it instead. + addCard(Zone.BATTLEFIELD, playerA, "Hardened Scales"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Season of Gathering"); + setModeChoice(playerA, "1"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, "2"); + addTarget(playerA, "Memnite"); + setChoice(playerA, "Artifact"); + setChoice(playerA, "Enchantment"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Hardened Scales", 1); + assertGraveyardCount(playerA, "Memnite", 1); + + } +} diff --git a/Mage/src/main/java/mage/abilities/Mode.java b/Mage/src/main/java/mage/abilities/Mode.java index 8c6e6747e4a..0cc7e2b1add 100644 --- a/Mage/src/main/java/mage/abilities/Mode.java +++ b/Mage/src/main/java/mage/abilities/Mode.java @@ -19,6 +19,7 @@ public class Mode implements Serializable { protected final Effects effects; protected String flavorWord; protected Cost cost = null; + protected int pawPrintValue = 0; //0 = does not use pawprints /** * Optional Tag to distinguish this mode from others. * In the case of modes that players can only choose once, @@ -42,6 +43,7 @@ public class Mode implements Serializable { this.flavorWord = mode.flavorWord; this.modeTag = mode.modeTag; this.cost = mode.cost != null ? mode.cost.copy() : null; + this.pawPrintValue = mode.pawPrintValue; } public UUID setRandomId() { @@ -119,4 +121,13 @@ public class Mode implements Serializable { public Cost getCost() { return cost; } + + public Mode withPawPrintValue(int pawPrintValue) { + this.pawPrintValue = pawPrintValue; + return this; + } + + public int getPawPrintValue() { + return pawPrintValue; + } } diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index f13921adeb8..165001add60 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -35,6 +35,7 @@ public class Modes extends LinkedHashMap implements Copyable private int minModes; private int maxModes; + private int maxPawPrints; private Filter maxModesFilter; // calculates the max number of available modes private Condition moreCondition; // allows multiple modes choose (example: choose one... if condition, you may choose both) @@ -53,6 +54,7 @@ public class Modes extends LinkedHashMap implements Copyable this.put(currentMode.getId(), currentMode); this.minModes = 1; this.maxModes = 1; + this.maxPawPrints = 0; // 0 = does not use pawprints this.addSelectedMode(currentMode.getId()); this.chooseController = TargetController.YOU; } @@ -68,6 +70,7 @@ public class Modes extends LinkedHashMap implements Copyable this.minModes = modes.minModes; this.maxModes = modes.maxModes; + this.maxPawPrints = modes.maxPawPrints; this.maxModesFilter = modes.maxModesFilter; // can't change so no copy needed this.moreCondition = modes.moreCondition; @@ -194,6 +197,12 @@ public class Modes extends LinkedHashMap implements Copyable return count; } + public int getSelectedPawPrints(){ + return this.selectedModes.stream() + .mapToInt(modeID -> get(modeID).getPawPrintValue()) + .sum(); + } + public void setMinModes(int minModes) { this.minModes = minModes; } @@ -256,6 +265,14 @@ public class Modes extends LinkedHashMap implements Copyable return realMaxModes; } + public void setMaxPawPrints(int maxPawPrints) { + this.maxPawPrints = maxPawPrints; + } + + public int getMaxPawPrints() { + return this.maxPawPrints; + } + public void setChooseController(TargetController chooseController) { this.chooseController = chooseController; } @@ -275,6 +292,12 @@ public class Modes extends LinkedHashMap implements Copyable } public void addMode(Mode mode) { + if (this.maxPawPrints > 0 && mode.getPawPrintValue() == 0){ + throw new IllegalArgumentException("Mode must have nonzero pawprints value in a pawprints mode set."); + } + if (this.maxPawPrints == 0 && mode.getPawPrintValue() > 0){ + throw new IllegalArgumentException("Cannot add pawprints mode to non-pawprints mode set."); + } this.put(mode.getId(), mode); } @@ -317,7 +340,7 @@ public class Modes extends LinkedHashMap implements Copyable return isSelectedValid(source, game); } - // modal spells must show choose dialog even for 1 option, so check this.size instead evailableModes.size here + // modal spells must show choose dialog even for 1 option, so check this.size instead availableModes.size here if (this.size() > 1) { // multiple modes @@ -375,7 +398,8 @@ public class Modes extends LinkedHashMap implements Copyable this.currentMode = null; int currentMaxModes = this.getMaxModes(game, source); - while (this.selectedModes.size() < currentMaxModes) { + while ((this.selectedModes.size() < currentMaxModes && maxPawPrints == 0) || + (this.getSelectedPawPrints() < maxPawPrints && maxPawPrints > 0)) { Mode choice = player.chooseMode(this, source, game); if (choice == null) { // user press cancel/stop in choose dialog or nothing to choose @@ -437,8 +461,10 @@ public class Modes extends LinkedHashMap implements Copyable throw new IllegalArgumentException("Unknown modeId to select"); } + Mode mode = get(modeId); + if (selectedModes.contains(modeId) && mayChooseSameModeMoreThanOnce) { - Mode duplicateMode = get(modeId).copy(); + Mode duplicateMode = mode.copy(); UUID originalId = modeId; duplicateMode.setRandomId(); modeId = duplicateMode.getId(); @@ -526,6 +552,9 @@ public class Modes extends LinkedHashMap implements Copyable if (isLimitUsageByOnce() && nonAvailableModes.contains(mode.getId())) { continue; } + if (getMaxPawPrints() > 0 && getSelectedPawPrints() + mode.getPawPrintValue() > getMaxPawPrints()){ + continue; + } availableModes.add(mode); } return availableModes; @@ -545,7 +574,9 @@ public class Modes extends LinkedHashMap implements Copyable } sb.append("choose "); } - if (this.getMinModes() == 0 && this.getMaxModes(null, null) == 1) { + if (this.getMaxPawPrints() > 0){ + sb.append("up to ").append(CardUtil.numberToText(this.getMaxPawPrints())).append(" {P} worth of modes"); + } else if (this.getMinModes() == 0 && this.getMaxModes(null, null) == 1) { sb.append("up to one"); } else if (this.getMinModes() == 0 && this.getMaxModes(null, null) > 2) { sb.append("any number");