From 7a6253bf44041558f2f381f8f2a8b6238180270d Mon Sep 17 00:00:00 2001 From: jimga150 Date: Sat, 27 Jul 2024 16:18:14 -0400 Subject: [PATCH 01/12] Add skeleton --- .../src/mage/cards/s/SeasonOfGathering.java | 32 +++++++++++++++++++ Mage.Sets/src/mage/sets/Bloomburrow.java | 1 + 2 files changed, 33 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SeasonOfGathering.java 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 000000000000..622d9147feb1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java @@ -0,0 +1,32 @@ +package mage.cards.s; + +import java.util.UUID; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +/** + * + * @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. + // {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. + } + + private SeasonOfGathering(final SeasonOfGathering card) { + super(card); + } + + @Override + public SeasonOfGathering copy() { + return new SeasonOfGathering(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Bloomburrow.java b/Mage.Sets/src/mage/sets/Bloomburrow.java index 59fe146b5c72..bcf9e79021e1 100644 --- a/Mage.Sets/src/mage/sets/Bloomburrow.java +++ b/Mage.Sets/src/mage/sets/Bloomburrow.java @@ -190,6 +190,7 @@ private Bloomburrow() { 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)); From 771a2239fb63efdd87277828dc319ef65efc8b28 Mon Sep 17 00:00:00 2001 From: jimga150 Date: Sun, 28 Jul 2024 20:30:16 -0400 Subject: [PATCH 02/12] Implement Pawprints modal functionality --- .../java/mage/player/ai/ComputerPlayer.java | 4 ++ .../src/mage/player/human/HumanPlayer.java | 31 ++++++++++++-- .../java/org/mage/test/player/TestPlayer.java | 4 ++ Mage/src/main/java/mage/abilities/Mode.java | 15 +++++++ Mage/src/main/java/mage/abilities/Modes.java | 40 +++++++++++++++++-- 5 files changed, 86 insertions(+), 8 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 4170990cf4a1..9541331172e7 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -2217,6 +2217,10 @@ public Mode chooseMode(Modes modes, Ability source, Game game) { continue AvailableMode; } } + if (modes.getMaxPawPrints() >= 0 && modes.getSelectedPawPrints() + mode.getPawPrintValue() > modes.getMaxPawPrints()){ + // Choosing this mode would exceed the number of pawprints available for this mode set. + continue; + } if (mode.getTargets().canChoose(source.getControllerId(), source, game)) { // and where targets are available return mode; } 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 6f64239b8d71..9de38f617999 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 @@ -2572,6 +2572,11 @@ public Mode chooseMode(Modes modes, Ability source, Game game) { } } + if (modes.getMaxPawPrints() >= 0 && modes.getSelectedPawPrints() + mode.getPawPrintValue() > modes.getMaxPawPrints()){ + // Choosing this mode would exceed the number of pawprints available for this mode set. + continue; + } + if (mode.getTargets().canChoose(source.getControllerId(), source, game)) { // and needed targets have to be available String modeText = mode.getEffects().getText(mode); if (obj != null) { @@ -2585,20 +2590,38 @@ public Mode chooseMode(Modes modes, Ability source, Game game) { if (!modeText.isEmpty()) { modeText = Character.toUpperCase(modeText.charAt(0)) + modeText.substring(1); } - modeMap.put(mode.getId(), modeIndex + ". " + modeText); + StringBuilder sb = new StringBuilder(); + if (modes.getMaxPawPrints() >= 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.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 7b656cf461eb..54902885ac2d 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 @@ -2082,6 +2082,10 @@ public Mode chooseMode(Modes modes, Ability source, Game game) { int needMode = Integer.parseInt(modesSet.get(0)); int i = 1; for (Mode mode : modes.getAvailableModes(source, game)) { + if (modes.getMaxPawPrints() >= 0 && modes.getSelectedPawPrints() + mode.getPawPrintValue() > modes.getMaxPawPrints()){ + // Choosing this mode would exceed the number of pawprints available for this mode set. + continue; + } if (i == needMode) { modesSet.remove(0); return mode; diff --git a/Mage/src/main/java/mage/abilities/Mode.java b/Mage/src/main/java/mage/abilities/Mode.java index 8c6e6747e4a3..11389f2cb0b7 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 = -1; /** * Optional Tag to distinguish this mode from others. * In the case of modes that players can only choose once, @@ -42,6 +43,7 @@ protected Mode(final Mode mode) { 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,17 @@ public Mode withCost(Cost cost) { public Cost getCost() { return cost; } + + public void setPawPrintValue(int pawPrintValue) { + this.pawPrintValue = pawPrintValue; + } + + public Mode withPawPrintValue(int pawPrintValue) { + this.setPawPrintValue(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 f13921adeb82..7d73ad0f02b5 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -32,9 +32,11 @@ public class Modes extends LinkedHashMap implements Copyable private final List selectedModes = new ArrayList<>(); // all selected modes (this + duplicate), use getSelectedModes all the time to keep modes order private final Map selectedDuplicateModes = new LinkedHashMap<>(); // for 2x selects: additional selected modes private final Map selectedDuplicateToOriginalModeRefs = new LinkedHashMap<>(); // for 2x selects: stores ref from duplicate to original mode + private int selectedPawPrints = 0; 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 +55,7 @@ public Modes() { this.put(currentMode.getId(), currentMode); this.minModes = 1; this.maxModes = 1; + this.maxPawPrints = -1; this.addSelectedMode(currentMode.getId()); this.chooseController = TargetController.YOU; } @@ -65,9 +68,11 @@ protected Modes(final Modes modes) { selectedDuplicateModes.put(entry.getKey(), entry.getValue().copy()); } selectedDuplicateToOriginalModeRefs.putAll(modes.selectedDuplicateToOriginalModeRefs); + this.selectedPawPrints = modes.selectedPawPrints; 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; @@ -166,6 +171,7 @@ public void clearSelectedModes() { this.selectedModes.clear(); this.selectedDuplicateModes.clear(); this.selectedDuplicateToOriginalModeRefs.clear(); + this.selectedPawPrints = 0; } public int getSelectedStats(UUID modeId) { @@ -194,6 +200,10 @@ public int getSelectedStats(UUID modeId) { return count; } + public int getSelectedPawPrints(){ + return selectedPawPrints; + } + public void setMinModes(int minModes) { this.minModes = minModes; } @@ -256,6 +266,14 @@ public int getMaxModes(Game game, Ability source) { return realMaxModes; } + public void setMaxPawPrints(int maxPawPrints) { + this.maxPawPrints = maxPawPrints; + } + + public int getMaxPawPrints() { + return this.maxPawPrints; + } + public void setChooseController(TargetController chooseController) { this.chooseController = chooseController; } @@ -317,7 +335,7 @@ public boolean choose(Game game, Ability source) { 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 +393,8 @@ public boolean choose(Game game, Ability source) { this.currentMode = null; int currentMaxModes = this.getMaxModes(game, source); - while (this.selectedModes.size() < currentMaxModes) { + while ((this.selectedModes.size() < currentMaxModes && maxPawPrints < 0) || + (selectedPawPrints < 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 +456,19 @@ public void addSelectedMode(UUID modeId) { throw new IllegalArgumentException("Unknown modeId to select"); } + Mode mode = get(modeId); + + if (mode.getPawPrintValue() < 0 && this.getMaxPawPrints() >= 0){ + throw new RuntimeException("Selected mode has no pawprint value, but this Modes object requires pawprint modes!"); + } + if (mode.getPawPrintValue() >= 0 && this.getMaxPawPrints() < 0) { + throw new RuntimeException("Selected mode has a pawprint value, but this Modes object has no pawprints!"); + } + + selectedPawPrints += mode.pawPrintValue; + if (selectedModes.contains(modeId) && mayChooseSameModeMoreThanOnce) { - Mode duplicateMode = get(modeId).copy(); + Mode duplicateMode = mode.copy(); UUID originalId = modeId; duplicateMode.setRandomId(); modeId = duplicateMode.getId(); @@ -545,7 +575,9 @@ public String getText() { } 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"); From caa732040dd97dafa8467714cd601ae83882b2ed Mon Sep 17 00:00:00 2001 From: jimga150 Date: Sun, 28 Jul 2024 20:30:29 -0400 Subject: [PATCH 03/12] Implement Seasons of Gathering --- .../src/mage/cards/s/SeasonOfGathering.java | 145 +++++++++++++++++- .../ControlledCreaturesGreatestPower.java | 41 +++++ 2 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 Mage/src/main/java/mage/abilities/dynamicvalue/common/ControlledCreaturesGreatestPower.java diff --git a/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java index 622d9147feb1..d93bcda0605d 100644 --- a/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java +++ b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java @@ -1,9 +1,44 @@ 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.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.ControlledCreaturesGreatestPower; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.*; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.ProliferateEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +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.ChoiceCreatureType; +import mage.choices.ChoiceImpl; import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.PlanewideCelebrationToken; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; /** * @@ -13,12 +48,27 @@ 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().setPawPrintValue(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(ControlledCreaturesGreatestPower.instance) + .setText("Draw cards equal to the greatest power among creatures you control.") + ); + this.getSpellAbility().addMode(mode3.withPawPrintValue(3)); } private SeasonOfGathering(final SeasonOfGathering card) { @@ -30,3 +80,96 @@ 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/src/main/java/mage/abilities/dynamicvalue/common/ControlledCreaturesGreatestPower.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ControlledCreaturesGreatestPower.java new file mode 100644 index 000000000000..5132ec8c86c0 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ControlledCreaturesGreatestPower.java @@ -0,0 +1,41 @@ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * @author jimga150 + */ +public enum ControlledCreaturesGreatestPower implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + int amount = 0; + for (Permanent p : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURE, sourceAbility.getControllerId(), game)) { + if (p.getPower().getValue() > amount) { + amount = p.getPower().getValue(); + } + } + return amount; + } + + @Override + public ControlledCreaturesGreatestPower copy() { + return this; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "the greatest power among creatures you control"; + } +} From bb89a47ea3ef3a6bb6d4d0b244a2d7a3bee94296 Mon Sep 17 00:00:00 2001 From: jimga150 Date: Sun, 28 Jul 2024 21:40:00 -0400 Subject: [PATCH 04/12] remove unused imports --- Mage.Sets/src/mage/cards/s/SeasonOfGathering.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java index d93bcda0605d..0c2f5bc8a9a6 100644 --- a/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java +++ b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java @@ -6,37 +6,24 @@ import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.common.delayed.ReflexiveTriggeredAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.common.ControlledCreaturesGreatestPower; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.*; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; -import mage.abilities.effects.common.counter.ProliferateEffect; -import mage.abilities.keyword.DoubleStrikeAbility; 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.ChoiceCreatureType; import mage.choices.ChoiceImpl; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.token.PlanewideCelebrationToken; import mage.players.Player; -import mage.target.TargetPermanent; -import mage.target.TargetPlayer; -import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; From 381b3e75830422a5034cc6d8101cadb7d7ef7e7a Mon Sep 17 00:00:00 2001 From: jimga150 Date: Sun, 28 Jul 2024 21:40:08 -0400 Subject: [PATCH 05/12] Add Pawprints test --- .../mage/test/cards/modal/PawPrintsTest.java | 218 ++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/modal/PawPrintsTest.java 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 000000000000..b302b9cea0d1 --- /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); + + } +} From 84490db9feca37ded279094831c9215af1dbc9d0 Mon Sep 17 00:00:00 2001 From: jimga150 Date: Tue, 30 Jul 2024 20:08:23 -0400 Subject: [PATCH 06/12] use withPawPRintValue() instead of setter --- Mage.Sets/src/mage/cards/s/SeasonOfGathering.java | 2 +- Mage/src/main/java/mage/abilities/Mode.java | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java index 0c2f5bc8a9a6..cd51b93be59b 100644 --- a/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java +++ b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java @@ -44,7 +44,7 @@ public SeasonOfGathering(UUID ownerId, CardSetInfo setInfo) { // {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().setPawPrintValue(1); + 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()); diff --git a/Mage/src/main/java/mage/abilities/Mode.java b/Mage/src/main/java/mage/abilities/Mode.java index 11389f2cb0b7..838a07b07ede 100644 --- a/Mage/src/main/java/mage/abilities/Mode.java +++ b/Mage/src/main/java/mage/abilities/Mode.java @@ -122,12 +122,8 @@ public Cost getCost() { return cost; } - public void setPawPrintValue(int pawPrintValue) { - this.pawPrintValue = pawPrintValue; - } - public Mode withPawPrintValue(int pawPrintValue) { - this.setPawPrintValue(pawPrintValue); + this.pawPrintValue = pawPrintValue; return this; } From 2025f9fd0b91843b94e1cbfa509f0f399c318985 Mon Sep 17 00:00:00 2001 From: jimga150 Date: Tue, 30 Jul 2024 20:08:58 -0400 Subject: [PATCH 07/12] use 0 for non-pawprint mode and modes classes and move mode validation to addMode --- Mage/src/main/java/mage/abilities/Mode.java | 2 +- Mage/src/main/java/mage/abilities/Modes.java | 21 ++++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/Mode.java b/Mage/src/main/java/mage/abilities/Mode.java index 838a07b07ede..0cc7e2b1add9 100644 --- a/Mage/src/main/java/mage/abilities/Mode.java +++ b/Mage/src/main/java/mage/abilities/Mode.java @@ -19,7 +19,7 @@ public class Mode implements Serializable { protected final Effects effects; protected String flavorWord; protected Cost cost = null; - protected int pawPrintValue = -1; + 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, diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index 7d73ad0f02b5..71f7a22d57bf 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -55,7 +55,7 @@ public Modes() { this.put(currentMode.getId(), currentMode); this.minModes = 1; this.maxModes = 1; - this.maxPawPrints = -1; + this.maxPawPrints = 0; // 0 = does not use pawprints this.addSelectedMode(currentMode.getId()); this.chooseController = TargetController.YOU; } @@ -293,6 +293,12 @@ public void setActiveMode(UUID modeId) { } 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); } @@ -393,8 +399,8 @@ public boolean choose(Game game, Ability source) { this.currentMode = null; int currentMaxModes = this.getMaxModes(game, source); - while ((this.selectedModes.size() < currentMaxModes && maxPawPrints < 0) || - (selectedPawPrints < maxPawPrints && maxPawPrints >= 0)) { + while ((this.selectedModes.size() < currentMaxModes && maxPawPrints == 0) || + (selectedPawPrints < maxPawPrints && maxPawPrints > 0)) { Mode choice = player.chooseMode(this, source, game); if (choice == null) { // user press cancel/stop in choose dialog or nothing to choose @@ -458,13 +464,6 @@ public void addSelectedMode(UUID modeId) { Mode mode = get(modeId); - if (mode.getPawPrintValue() < 0 && this.getMaxPawPrints() >= 0){ - throw new RuntimeException("Selected mode has no pawprint value, but this Modes object requires pawprint modes!"); - } - if (mode.getPawPrintValue() >= 0 && this.getMaxPawPrints() < 0) { - throw new RuntimeException("Selected mode has a pawprint value, but this Modes object has no pawprints!"); - } - selectedPawPrints += mode.pawPrintValue; if (selectedModes.contains(modeId) && mayChooseSameModeMoreThanOnce) { @@ -575,7 +574,7 @@ public String getText() { } sb.append("choose "); } - if (this.getMaxPawPrints() >= 0){ + 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"); From 876aa38cd2aa2634397aecb3d29cc97c8a0f1bd4 Mon Sep 17 00:00:00 2001 From: jimga150 Date: Tue, 30 Jul 2024 20:51:48 -0400 Subject: [PATCH 08/12] Use GreatestPowerAmongControlledCreaturesValue --- .../src/mage/cards/s/SeasonOfGathering.java | 4 +- .../ControlledCreaturesGreatestPower.java | 41 ------------------- 2 files changed, 2 insertions(+), 43 deletions(-) delete mode 100644 Mage/src/main/java/mage/abilities/dynamicvalue/common/ControlledCreaturesGreatestPower.java diff --git a/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java index cd51b93be59b..96b73cd7a238 100644 --- a/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java +++ b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java @@ -6,7 +6,7 @@ import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.dynamicvalue.common.ControlledCreaturesGreatestPower; +import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.*; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -52,7 +52,7 @@ public SeasonOfGathering(UUID ownerId, CardSetInfo setInfo) { // {P}{P}{P} -- Draw cards equal to the greatest power among creatures you control. Mode mode3 = new Mode( - new DrawCardSourceControllerEffect(ControlledCreaturesGreatestPower.instance) + new DrawCardSourceControllerEffect(GreatestPowerAmongControlledCreaturesValue.instance) .setText("Draw cards equal to the greatest power among creatures you control.") ); this.getSpellAbility().addMode(mode3.withPawPrintValue(3)); diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ControlledCreaturesGreatestPower.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ControlledCreaturesGreatestPower.java deleted file mode 100644 index 5132ec8c86c0..000000000000 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ControlledCreaturesGreatestPower.java +++ /dev/null @@ -1,41 +0,0 @@ -package mage.abilities.dynamicvalue.common; - -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * @author jimga150 - */ -public enum ControlledCreaturesGreatestPower implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - int amount = 0; - for (Permanent p : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURE, sourceAbility.getControllerId(), game)) { - if (p.getPower().getValue() > amount) { - amount = p.getPower().getValue(); - } - } - return amount; - } - - @Override - public ControlledCreaturesGreatestPower copy() { - return this; - } - - @Override - public String toString() { - return "X"; - } - - @Override - public String getMessage() { - return "the greatest power among creatures you control"; - } -} From 7cce01b13a37262cf7080151c2240377b70cfd78 Mon Sep 17 00:00:00 2001 From: jimga150 Date: Tue, 30 Jul 2024 21:11:12 -0400 Subject: [PATCH 09/12] Fix pawprints check --- .../Mage.Player.Human/src/mage/player/human/HumanPlayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9de38f617999..a8ab4c286ec0 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 @@ -2591,7 +2591,7 @@ public Mode chooseMode(Modes modes, Ability source, Game game) { modeText = Character.toUpperCase(modeText.charAt(0)) + modeText.substring(1); } StringBuilder sb = new StringBuilder(); - if (modes.getMaxPawPrints() >= 0){ + if (mode.getPawPrintValue() > 0){ for (int i = 0; i < mode.getPawPrintValue(); ++i){ sb.append("{P}"); } From 0f72b5a1dc71b6d0cc208becfe16e5d55278d300 Mon Sep 17 00:00:00 2001 From: jimga150 Date: Tue, 30 Jul 2024 21:11:40 -0400 Subject: [PATCH 10/12] calcualte sleected pawprint count based on selected modes --- Mage/src/main/java/mage/abilities/Modes.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index 71f7a22d57bf..e3be66345710 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -32,7 +32,6 @@ public class Modes extends LinkedHashMap implements Copyable private final List selectedModes = new ArrayList<>(); // all selected modes (this + duplicate), use getSelectedModes all the time to keep modes order private final Map selectedDuplicateModes = new LinkedHashMap<>(); // for 2x selects: additional selected modes private final Map selectedDuplicateToOriginalModeRefs = new LinkedHashMap<>(); // for 2x selects: stores ref from duplicate to original mode - private int selectedPawPrints = 0; private int minModes; private int maxModes; @@ -68,7 +67,6 @@ protected Modes(final Modes modes) { selectedDuplicateModes.put(entry.getKey(), entry.getValue().copy()); } selectedDuplicateToOriginalModeRefs.putAll(modes.selectedDuplicateToOriginalModeRefs); - this.selectedPawPrints = modes.selectedPawPrints; this.minModes = modes.minModes; this.maxModes = modes.maxModes; @@ -171,7 +169,6 @@ public void clearSelectedModes() { this.selectedModes.clear(); this.selectedDuplicateModes.clear(); this.selectedDuplicateToOriginalModeRefs.clear(); - this.selectedPawPrints = 0; } public int getSelectedStats(UUID modeId) { @@ -201,7 +198,9 @@ public int getSelectedStats(UUID modeId) { } public int getSelectedPawPrints(){ - return selectedPawPrints; + return this.selectedModes.stream() + .mapToInt(modeID -> get(modeID).getPawPrintValue()) + .sum(); } public void setMinModes(int minModes) { @@ -400,7 +399,7 @@ public boolean choose(Game game, Ability source) { int currentMaxModes = this.getMaxModes(game, source); while ((this.selectedModes.size() < currentMaxModes && maxPawPrints == 0) || - (selectedPawPrints < maxPawPrints && 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 @@ -464,8 +463,6 @@ public void addSelectedMode(UUID modeId) { Mode mode = get(modeId); - selectedPawPrints += mode.pawPrintValue; - if (selectedModes.contains(modeId) && mayChooseSameModeMoreThanOnce) { Mode duplicateMode = mode.copy(); UUID originalId = modeId; From 5d9b59decc178aa5857caac1fac14a33b1f3fb84 Mon Sep 17 00:00:00 2001 From: jimga150 Date: Wed, 31 Jul 2024 14:48:31 -0400 Subject: [PATCH 11/12] move max pawprints check to getAvailableModes --- .../src/main/java/mage/player/ai/ComputerPlayer.java | 4 ---- .../Mage.Player.Human/src/mage/player/human/HumanPlayer.java | 5 ----- .../src/test/java/org/mage/test/player/TestPlayer.java | 4 ---- Mage/src/main/java/mage/abilities/Modes.java | 3 +++ 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 9541331172e7..4170990cf4a1 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -2217,10 +2217,6 @@ public Mode chooseMode(Modes modes, Ability source, Game game) { continue AvailableMode; } } - if (modes.getMaxPawPrints() >= 0 && modes.getSelectedPawPrints() + mode.getPawPrintValue() > modes.getMaxPawPrints()){ - // Choosing this mode would exceed the number of pawprints available for this mode set. - continue; - } if (mode.getTargets().canChoose(source.getControllerId(), source, game)) { // and where targets are available return mode; } 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 a8ab4c286ec0..b9e11606a67c 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 @@ -2572,11 +2572,6 @@ public Mode chooseMode(Modes modes, Ability source, Game game) { } } - if (modes.getMaxPawPrints() >= 0 && modes.getSelectedPawPrints() + mode.getPawPrintValue() > modes.getMaxPawPrints()){ - // Choosing this mode would exceed the number of pawprints available for this mode set. - continue; - } - if (mode.getTargets().canChoose(source.getControllerId(), source, game)) { // and needed targets have to be available String modeText = mode.getEffects().getText(mode); if (obj != null) { 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 54902885ac2d..7b656cf461eb 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 @@ -2082,10 +2082,6 @@ public Mode chooseMode(Modes modes, Ability source, Game game) { int needMode = Integer.parseInt(modesSet.get(0)); int i = 1; for (Mode mode : modes.getAvailableModes(source, game)) { - if (modes.getMaxPawPrints() >= 0 && modes.getSelectedPawPrints() + mode.getPawPrintValue() > modes.getMaxPawPrints()){ - // Choosing this mode would exceed the number of pawprints available for this mode set. - continue; - } if (i == needMode) { modesSet.remove(0); return mode; diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index e3be66345710..165001add60e 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -552,6 +552,9 @@ public List getAvailableModes(Ability source, Game game) { if (isLimitUsageByOnce() && nonAvailableModes.contains(mode.getId())) { continue; } + if (getMaxPawPrints() > 0 && getSelectedPawPrints() + mode.getPawPrintValue() > getMaxPawPrints()){ + continue; + } availableModes.add(mode); } return availableModes; From f656f46e299e804f7541d92984c751e9ee895cda Mon Sep 17 00:00:00 2001 From: jimga150 Date: Wed, 14 Aug 2024 10:07:43 -0400 Subject: [PATCH 12/12] fix max pawprints checks --- .../src/mage/player/human/HumanPlayer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 b9e11606a67c..481eb129ed50 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 @@ -2599,8 +2599,8 @@ public Mode chooseMode(Modes modes, Ability source, Game game) { } // done button for "for up" choices only - boolean canEndChoice = (modes.getSelectedModes().size() >= modes.getMinModes() && modes.getMaxPawPrints() < 0) || - (modes.getSelectedPawPrints() >= modes.getMaxPawPrints() && modes.getMaxPawPrints() >= 0) || + 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"); @@ -2609,7 +2609,7 @@ public Mode chooseMode(Modes modes, Ability source, Game game) { // prepare dialog String message; - if (modes.getMaxPawPrints() < 0){ + if (modes.getMaxPawPrints() == 0){ message = "Choose mode (selected " + modes.getSelectedModes().size() + " of " + modes.getMaxModes(game, source) + ", min " + modes.getMinModes() + ")"; } else {