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 22041ea0c16..85c25650cbf 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 @@ -2409,7 +2409,7 @@ public class HumanPlayer extends PlayerImpl { Mode selectedMode = modes.get(selectedModeId); if (mode.getId().equals(selectedMode.getId())) { // mode selected - if (modes.isEachModeMoreThanOnce()) { + if (modes.isMayChooseSameModeMoreThanOnce()) { // can select again } else { // hide mode from dialog @@ -2423,7 +2423,7 @@ public class HumanPlayer extends PlayerImpl { if (obj != null) { modeText = modeText.replace("{this}", obj.getName()); } - if (modes.isEachModeMoreThanOnce()) { + if (modes.isMayChooseSameModeMoreThanOnce()) { if (timesSelected > 0) { modeText = "(selected " + timesSelected + "x) " + modeText; } diff --git a/Mage.Sets/src/mage/cards/b/BreechesEagerPillager.java b/Mage.Sets/src/mage/cards/b/BreechesEagerPillager.java index eacf23cd4da..90a3e7d04a6 100644 --- a/Mage.Sets/src/mage/cards/b/BreechesEagerPillager.java +++ b/Mage.Sets/src/mage/cards/b/BreechesEagerPillager.java @@ -46,8 +46,7 @@ public final class BreechesEagerPillager extends CardImpl { new CreateTokenEffect(new TreasureToken()), false, filter ); ability.setModeTag("treasure"); - ability.getModes().setEachModeOnlyOnce(true); - ability.getModes().setResetEachTurn(true); + ability.getModes().setLimitUsageByOnce(true); // * Target creature can't block this turn. Mode mode = new Mode(new CantBlockTargetEffect(Duration.EndOfTurn)) diff --git a/Mage.Sets/src/mage/cards/b/BrokersConfluence.java b/Mage.Sets/src/mage/cards/b/BrokersConfluence.java index 7b8a5d603a3..c7b775af6a3 100644 --- a/Mage.Sets/src/mage/cards/b/BrokersConfluence.java +++ b/Mage.Sets/src/mage/cards/b/BrokersConfluence.java @@ -23,7 +23,7 @@ public final class BrokersConfluence extends CardImpl { // Choose three. You may choose the same mode more than once. this.getSpellAbility().getModes().setMinModes(3); this.getSpellAbility().getModes().setMaxModes(3); - this.getSpellAbility().getModes().setEachModeMoreThanOnce(true); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); // • Proliferate. this.getSpellAbility().addEffect(new ProliferateEffect()); diff --git a/Mage.Sets/src/mage/cards/c/CabarettiConfluence.java b/Mage.Sets/src/mage/cards/c/CabarettiConfluence.java index 1fc1a4cb52c..df1c375b952 100644 --- a/Mage.Sets/src/mage/cards/c/CabarettiConfluence.java +++ b/Mage.Sets/src/mage/cards/c/CabarettiConfluence.java @@ -40,7 +40,7 @@ public final class CabarettiConfluence extends CardImpl { // Choose three. You may choose the same mode more than once. this.getSpellAbility().getModes().setMinModes(3); this.getSpellAbility().getModes().setMaxModes(3); - this.getSpellAbility().getModes().setEachModeMoreThanOnce(true); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); // • Create a token that's a copy of target creature you control. It gains haste. Sacrifice it at the beginning of the next end step. this.getSpellAbility().addEffect(new CabarettiConfluenceEffect()); diff --git a/Mage.Sets/src/mage/cards/c/CaptiveAudience.java b/Mage.Sets/src/mage/cards/c/CaptiveAudience.java index 7b728bb0de3..6d2c1f37fee 100644 --- a/Mage.Sets/src/mage/cards/c/CaptiveAudience.java +++ b/Mage.Sets/src/mage/cards/c/CaptiveAudience.java @@ -34,7 +34,7 @@ public final class CaptiveAudience extends CardImpl { new SetPlayerLifeSourceEffect(4), TargetController.YOU, false ); ability.setModeTag("life total becomes 4"); - ability.getModes().setEachModeOnlyOnce(true); + ability.getModes().setLimitUsageByOnce(false); // • Discard your hand. ability.addMode( diff --git a/Mage.Sets/src/mage/cards/d/DemonicPact.java b/Mage.Sets/src/mage/cards/d/DemonicPact.java index 6b4d337a523..c74308e2c30 100644 --- a/Mage.Sets/src/mage/cards/d/DemonicPact.java +++ b/Mage.Sets/src/mage/cards/d/DemonicPact.java @@ -33,7 +33,7 @@ public final class DemonicPact extends CardImpl { // - Demonic Pact deals 4 damage to any target and you gain 4 life; Ability ability = new BeginningOfUpkeepTriggeredAbility(new DamageTargetEffect(4), TargetController.YOU, false); ability.setModeTag("deals damage and gain life"); - ability.getModes().setEachModeOnlyOnce(true); + ability.getModes().setLimitUsageByOnce(false); ability.addTarget(new TargetAnyTarget()); Effect effect = new GainLifeEffect(4); effect.setText("and you gain 4 life"); diff --git a/Mage.Sets/src/mage/cards/f/FatalLore.java b/Mage.Sets/src/mage/cards/f/FatalLore.java index 8ea2a4cb7cd..9c1e72abffc 100644 --- a/Mage.Sets/src/mage/cards/f/FatalLore.java +++ b/Mage.Sets/src/mage/cards/f/FatalLore.java @@ -30,7 +30,7 @@ public final class FatalLore extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); // An opponent chooses one — - this.getSpellAbility().getModes().setModeChooser(TargetController.OPPONENT); + this.getSpellAbility().getModes().setChooseController(TargetController.OPPONENT); // • You draw three cards. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).setText("you draw three cards")); diff --git a/Mage.Sets/src/mage/cards/f/FieryConfluence.java b/Mage.Sets/src/mage/cards/f/FieryConfluence.java index 0c8be5af7d4..45c475466d1 100644 --- a/Mage.Sets/src/mage/cards/f/FieryConfluence.java +++ b/Mage.Sets/src/mage/cards/f/FieryConfluence.java @@ -25,7 +25,7 @@ public final class FieryConfluence extends CardImpl { // Choose three. You may choose the same mode more than once. this.getSpellAbility().getModes().setMinModes(3); this.getSpellAbility().getModes().setMaxModes(3); - this.getSpellAbility().getModes().setEachModeMoreThanOnce(true); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); // - Fiery Confluence deals 1 damage to each creature; this.getSpellAbility().addEffect(new DamageAllEffect(1, new FilterCreaturePermanent())); diff --git a/Mage.Sets/src/mage/cards/g/GalaGreeters.java b/Mage.Sets/src/mage/cards/g/GalaGreeters.java index 6b9cec9b537..2ea8794473d 100644 --- a/Mage.Sets/src/mage/cards/g/GalaGreeters.java +++ b/Mage.Sets/src/mage/cards/g/GalaGreeters.java @@ -34,8 +34,7 @@ public final class GalaGreeters extends CardImpl { // • Put a +1/+1 counter on Gala Greeters. Ability ability = new AllianceAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())); ability.setModeTag("put +1/+1 counter"); - ability.getModes().setEachModeOnlyOnce(true); - ability.getModes().setResetEachTurn(true); + ability.getModes().setLimitUsageByOnce(true); // • Create a tapped Treasure token. ability.addMode( diff --git a/Mage.Sets/src/mage/cards/g/GaladrielLightOfValinor.java b/Mage.Sets/src/mage/cards/g/GaladrielLightOfValinor.java index 0bfca2a3537..9185b487a4f 100644 --- a/Mage.Sets/src/mage/cards/g/GaladrielLightOfValinor.java +++ b/Mage.Sets/src/mage/cards/g/GaladrielLightOfValinor.java @@ -38,8 +38,7 @@ public final class GaladrielLightOfValinor extends CardImpl { // • Add {G}{G}{G}. Ability ability = new AllianceAbility(new AddManaToManaPoolSourceControllerEffect(Mana.GreenMana(3))); ability.setModeTag("add mana"); - ability.getModes().setEachModeOnlyOnce(true); - ability.getModes().setResetEachTurn(true); + ability.getModes().setLimitUsageByOnce(true); // • Put a +1/+1 counter on each creature you control. ability.addMode( diff --git a/Mage.Sets/src/mage/cards/g/GandalfTheGrey.java b/Mage.Sets/src/mage/cards/g/GandalfTheGrey.java index 662d27071b6..663a4dff2a1 100644 --- a/Mage.Sets/src/mage/cards/g/GandalfTheGrey.java +++ b/Mage.Sets/src/mage/cards/g/GandalfTheGrey.java @@ -51,7 +51,7 @@ public final class GandalfTheGrey extends CardImpl { ); ability.setModeTag("tap or untap"); ability.addTarget(new TargetPermanent()); - ability.getModes().setEachModeOnlyOnce(true); + ability.getModes().setLimitUsageByOnce(false); // * Gandalf the Grey deals 3 damage to each opponent. ability.addMode( diff --git a/Mage.Sets/src/mage/cards/h/HenrikaDomnathi.java b/Mage.Sets/src/mage/cards/h/HenrikaDomnathi.java index 4fc88b8e6a7..e70f220cca7 100644 --- a/Mage.Sets/src/mage/cards/h/HenrikaDomnathi.java +++ b/Mage.Sets/src/mage/cards/h/HenrikaDomnathi.java @@ -44,7 +44,7 @@ public final class HenrikaDomnathi extends CardImpl { 1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT ), TargetController.YOU, false); ability.setModeTag("each player sacrifice"); - ability.getModes().setEachModeOnlyOnce(true); + ability.getModes().setLimitUsageByOnce(false); // • You draw a card and you lose 1 life. Mode mode = new Mode(new DrawCardSourceControllerEffect(1).setText("you draw a card")); diff --git a/Mage.Sets/src/mage/cards/k/KarganIntimidator.java b/Mage.Sets/src/mage/cards/k/KarganIntimidator.java index 65a2804463a..98207fe74cb 100644 --- a/Mage.Sets/src/mage/cards/k/KarganIntimidator.java +++ b/Mage.Sets/src/mage/cards/k/KarganIntimidator.java @@ -49,8 +49,7 @@ public final class KarganIntimidator extends CardImpl { new BoostSourceEffect(1, 1, Duration.EndOfTurn), new GenericManaCost(1) ); ability.setModeTag("gets +1/+1"); - ability.getModes().setEachModeOnlyOnce(true); - ability.getModes().setResetEachTurn(true); + ability.getModes().setLimitUsageByOnce(true); // • Target creature becomes a Coward until end of turn. Mode mode = new Mode(new BecomesCreatureTypeTargetEffect( diff --git a/Mage.Sets/src/mage/cards/l/LibraryOfLatNam.java b/Mage.Sets/src/mage/cards/l/LibraryOfLatNam.java index cff075692ff..f3cded01623 100644 --- a/Mage.Sets/src/mage/cards/l/LibraryOfLatNam.java +++ b/Mage.Sets/src/mage/cards/l/LibraryOfLatNam.java @@ -22,7 +22,7 @@ public final class LibraryOfLatNam extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}"); // An opponent chooses one - this.getSpellAbility().getModes().setModeChooser(TargetController.OPPONENT); + this.getSpellAbility().getModes().setChooseController(TargetController.OPPONENT); // You draw three cards at the beginning of the next turn's upkeep; this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(3).setText("you draw three cards")), false)); diff --git a/Mage.Sets/src/mage/cards/m/MaestrosConfluence.java b/Mage.Sets/src/mage/cards/m/MaestrosConfluence.java index 0b646f38971..97ffdb1fda7 100644 --- a/Mage.Sets/src/mage/cards/m/MaestrosConfluence.java +++ b/Mage.Sets/src/mage/cards/m/MaestrosConfluence.java @@ -39,7 +39,7 @@ public final class MaestrosConfluence extends CardImpl { // Choose three. You may choose the same mode more than once. this.getSpellAbility().getModes().setMinModes(3); this.getSpellAbility().getModes().setMaxModes(3); - this.getSpellAbility().getModes().setEachModeMoreThanOnce(true); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); // • Return target monocolored instant or sorcery card from your graveyard to your hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/m/Misfortune.java b/Mage.Sets/src/mage/cards/m/Misfortune.java index 18703b68c3e..69e34376b6c 100644 --- a/Mage.Sets/src/mage/cards/m/Misfortune.java +++ b/Mage.Sets/src/mage/cards/m/Misfortune.java @@ -27,7 +27,7 @@ public final class Misfortune extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{R}{G}"); // An opponent chooses one — - this.getSpellAbility().getModes().setModeChooser(TargetController.OPPONENT); + this.getSpellAbility().getModes().setChooseController(TargetController.OPPONENT); // • You put a +1/+1 counter on each creature you control and gain 4 life. this.getSpellAbility().addEffect(new AddCountersAllEffect( diff --git a/Mage.Sets/src/mage/cards/m/MysticConfluence.java b/Mage.Sets/src/mage/cards/m/MysticConfluence.java index 41d4d5379ce..0e0ec16f45c 100644 --- a/Mage.Sets/src/mage/cards/m/MysticConfluence.java +++ b/Mage.Sets/src/mage/cards/m/MysticConfluence.java @@ -25,7 +25,7 @@ public final class MysticConfluence extends CardImpl { // Choose three. You may choose the same mode more than once. this.getSpellAbility().getModes().setMinModes(3); this.getSpellAbility().getModes().setMaxModes(3); - this.getSpellAbility().getModes().setEachModeMoreThanOnce(true); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); // - Counter target spell unless its controller pays {3}; this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(new GenericManaCost(3))); diff --git a/Mage.Sets/src/mage/cards/o/ObscuraConfluence.java b/Mage.Sets/src/mage/cards/o/ObscuraConfluence.java index df9d1fca1bd..eb639ca5f18 100644 --- a/Mage.Sets/src/mage/cards/o/ObscuraConfluence.java +++ b/Mage.Sets/src/mage/cards/o/ObscuraConfluence.java @@ -34,7 +34,7 @@ public final class ObscuraConfluence extends CardImpl { // Choose three. You may choose the same mode more than once. this.getSpellAbility().getModes().setMinModes(3); this.getSpellAbility().getModes().setMaxModes(3); - this.getSpellAbility().getModes().setEachModeMoreThanOnce(true); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); // • Until end of turn, target creature loses all abilities and has base power and toughness 1/1. this.getSpellAbility().addEffect(new LoseAllAbilitiesTargetEffect(Duration.EndOfTurn) diff --git a/Mage.Sets/src/mage/cards/p/PlanewideCelebration.java b/Mage.Sets/src/mage/cards/p/PlanewideCelebration.java index e7868f0f28c..bc3bef8d25c 100644 --- a/Mage.Sets/src/mage/cards/p/PlanewideCelebration.java +++ b/Mage.Sets/src/mage/cards/p/PlanewideCelebration.java @@ -28,7 +28,7 @@ public final class PlanewideCelebration extends CardImpl { // Choose four. You may choose the same mode more than once. this.getSpellAbility().getModes().setMinModes(4); this.getSpellAbility().getModes().setMaxModes(4); - this.getSpellAbility().getModes().setEachModeMoreThanOnce(true); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); // • Create a 2/2 Citizen creature token that's all colors. this.getSpellAbility().addEffect(new CreateTokenEffect(new PlanewideCelebrationToken())); diff --git a/Mage.Sets/src/mage/cards/r/RighteousConfluence.java b/Mage.Sets/src/mage/cards/r/RighteousConfluence.java index f3cceaa4c5c..efca2f427be 100644 --- a/Mage.Sets/src/mage/cards/r/RighteousConfluence.java +++ b/Mage.Sets/src/mage/cards/r/RighteousConfluence.java @@ -24,7 +24,7 @@ public final class RighteousConfluence extends CardImpl { // Choose three - You may choose the same mode more than once. this.getSpellAbility().getModes().setMinModes(3); this.getSpellAbility().getModes().setMaxModes(3); - this.getSpellAbility().getModes().setEachModeMoreThanOnce(true); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); // - Create a 2/2 white Knight creature token with vigilance; this.getSpellAbility().addEffect(new CreateTokenEffect(new KnightToken())); diff --git a/Mage.Sets/src/mage/cards/r/RiveteersConfluence.java b/Mage.Sets/src/mage/cards/r/RiveteersConfluence.java index 74d83442449..89569821a5a 100644 --- a/Mage.Sets/src/mage/cards/r/RiveteersConfluence.java +++ b/Mage.Sets/src/mage/cards/r/RiveteersConfluence.java @@ -32,7 +32,7 @@ public class RiveteersConfluence extends CardImpl { // Choose three. You may choose the same mode more than once. this.getSpellAbility().getModes().setMinModes(3); this.getSpellAbility().getModes().setMaxModes(3); - this.getSpellAbility().getModes().setEachModeMoreThanOnce(true); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); //• You draw a card and you lose 1 life. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).setText("you draw a card")); diff --git a/Mage.Sets/src/mage/cards/s/SolKanarTheTainted.java b/Mage.Sets/src/mage/cards/s/SolKanarTheTainted.java index 4edd450e31b..412a85341e7 100644 --- a/Mage.Sets/src/mage/cards/s/SolKanarTheTainted.java +++ b/Mage.Sets/src/mage/cards/s/SolKanarTheTainted.java @@ -53,7 +53,7 @@ public final class SolKanarTheTainted extends CardImpl { new DrawCardSourceControllerEffect(1), TargetController.YOU, false ); ability.setModeTag("draw"); - ability.getModes().setEachModeOnlyOnce(true); + ability.getModes().setLimitUsageByOnce(false); // * Each opponent loses 2 life and you gain 2 life. ability.addMode(new Mode(new LoseLifeOpponentsEffect(2)) diff --git a/Mage.Sets/src/mage/cards/t/ThreeBowlsOfPorridge.java b/Mage.Sets/src/mage/cards/t/ThreeBowlsOfPorridge.java index 5972bb412a7..abcf873c1ab 100644 --- a/Mage.Sets/src/mage/cards/t/ThreeBowlsOfPorridge.java +++ b/Mage.Sets/src/mage/cards/t/ThreeBowlsOfPorridge.java @@ -36,7 +36,7 @@ public final class ThreeBowlsOfPorridge extends CardImpl { ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); ability.setModeTag("deals damage"); - ability.getModes().setEachModeOnlyOnce(true); + ability.getModes().setLimitUsageByOnce(false); // * Tap target creature. Mode mode = new Mode(new TapTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/u/UniteTheCoalition.java b/Mage.Sets/src/mage/cards/u/UniteTheCoalition.java index 3429a2913b5..ffed2ad4c20 100644 --- a/Mage.Sets/src/mage/cards/u/UniteTheCoalition.java +++ b/Mage.Sets/src/mage/cards/u/UniteTheCoalition.java @@ -23,7 +23,7 @@ public final class UniteTheCoalition extends CardImpl { // Choose five. You may choose the same mode more than once. this.getSpellAbility().getModes().setMinModes(5); this.getSpellAbility().getModes().setMaxModes(5); - this.getSpellAbility().getModes().setEachModeMoreThanOnce(true); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); // • Target permanent phases out. this.getSpellAbility().addEffect(new PhaseOutTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/v/VerdantConfluence.java b/Mage.Sets/src/mage/cards/v/VerdantConfluence.java index 7cb9519ed2d..5c42f633e73 100644 --- a/Mage.Sets/src/mage/cards/v/VerdantConfluence.java +++ b/Mage.Sets/src/mage/cards/v/VerdantConfluence.java @@ -29,7 +29,7 @@ public final class VerdantConfluence extends CardImpl { // Choose three. You may choose the same mode more than once. this.getSpellAbility().getModes().setMinModes(3); this.getSpellAbility().getModes().setMaxModes(3); - this.getSpellAbility().getModes().setEachModeMoreThanOnce(true); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); // - Put two +1/+1 counters on target creature; this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2))); diff --git a/Mage.Sets/src/mage/cards/v/VindictiveLich.java b/Mage.Sets/src/mage/cards/v/VindictiveLich.java index 288ab66e1f8..f2d9492cb35 100644 --- a/Mage.Sets/src/mage/cards/v/VindictiveLich.java +++ b/Mage.Sets/src/mage/cards/v/VindictiveLich.java @@ -50,7 +50,7 @@ public final class VindictiveLich extends CardImpl { )); ability.getModes().setMinModes(1); ability.getModes().setMaxModes(3); - ability.getModes().setEachModeOnlyOnce(true); + ability.getModes().setLimitUsageByOnce(false); ability.getModes().setMaxModesFilter(filter0); ability.addTarget(new TargetPlayer(filter1).setTargetTag(1).withChooseHint("to sacrifice a creature")); diff --git a/Mage.Sets/src/mage/cards/w/WretchedConfluence.java b/Mage.Sets/src/mage/cards/w/WretchedConfluence.java index 68572eef2e3..822b18e3e83 100644 --- a/Mage.Sets/src/mage/cards/w/WretchedConfluence.java +++ b/Mage.Sets/src/mage/cards/w/WretchedConfluence.java @@ -29,7 +29,7 @@ public final class WretchedConfluence extends CardImpl { // Choose three. You may choose the same mode more than once. this.getSpellAbility().getModes().setMinModes(3); this.getSpellAbility().getModes().setMaxModes(3); - this.getSpellAbility().getModes().setEachModeMoreThanOnce(true); + this.getSpellAbility().getModes().setMayChooseSameModeMoreThanOnce(true); // - Target player draws a card and loses 1 life; Effect effect = new LoseLifeTargetEffect(1); 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 89f6f7d5f38..7485abfb40f 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 @@ -454,7 +454,7 @@ public class TestPlayer implements Player { Mode selectedMode; if (targetName.startsWith("mode=")) { int modeNr = Integer.parseInt(targetName.substring(5, 6)); - if (modeNr == 0 || modeNr > (ability.getModes().isEachModeMoreThanOnce() ? ability.getModes().getSelectedModes().size() : ability.getModes().size())) { + if (modeNr == 0 || modeNr > (ability.getModes().isMayChooseSameModeMoreThanOnce() ? ability.getModes().getSelectedModes().size() : ability.getModes().size())) { throw new UnsupportedOperationException("Given mode number (" + modeNr + ") not available for " + ability.toString()); } UUID modeId = ability.getModes().getModeId(modeNr); diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 9cdf2ed6802..1d2de48df72 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -986,7 +986,7 @@ public abstract class AbilityImpl implements Ability { if (validTargets) { found++; - if (modes.isEachModeMoreThanOnce()) { + if (modes.isMayChooseSameModeMoreThanOnce()) { return true; } if (found >= modes.getMinModes()) { diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index dd85fb9024b..4a17f4588ff 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -12,6 +12,7 @@ import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; import mage.util.CardUtil; +import mage.util.Copyable; import mage.util.RandomUtil; import java.util.*; @@ -20,38 +21,40 @@ import java.util.stream.Stream; /** * @author BetaSteward_at_googlemail.com */ -public class Modes extends LinkedHashMap { +public class Modes extends LinkedHashMap implements Copyable { // choose ID for options in ability/mode picker dialogs public static final UUID CHOOSE_OPTION_DONE_ID = UUID.fromString("33e72ad6-17ae-4bfb-a097-6e7aa06b49e9"); public static final UUID CHOOSE_OPTION_CANCEL_ID = UUID.fromString("0125bd0c-5610-4eba-bc80-fc6d0a7b9de6"); - private Mode currentMode; // the current mode of the selected modes + private Mode currentMode; // current active mode for resolving + 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: copy mode and put it to duplicate list + 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 minModes; private int maxModes; - private TargetController modeChooser; - private boolean eachModeMoreThanOnce; // each mode can be selected multiple times during one choice - private boolean eachModeOnlyOnce; // state if each mode can be chosen only once as long as the source object exists - private Filter maxModesFilter = null; // calculates the max number of available modes - private boolean isRandom = false; + 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) + + private boolean limitUsageByOnce = false; // limit mode selection to once per game + private boolean limitUsageResetOnNewTurn = false; // reset once per game limit on new turn, example: Galadriel, Light of Valinor + private String chooseText = null; - private boolean resetEachTurn = false; - private Condition moreCondition; + private TargetController chooseController; + private boolean mayChooseSameModeMoreThanOnce = false; // example: choose three... you may choose the same mode more than once private boolean mayChooseNone = false; + private boolean isRandom = false; public Modes() { + // add default mode this.currentMode = new Mode((Effect) null); this.put(currentMode.getId(), currentMode); this.minModes = 1; this.maxModes = 1; this.addSelectedMode(currentMode.getId()); - this.modeChooser = TargetController.YOU; - this.eachModeOnlyOnce = false; - this.eachModeMoreThanOnce = false; + this.chooseController = TargetController.YOU; } protected Modes(final Modes modes) { @@ -65,25 +68,28 @@ public class Modes extends LinkedHashMap { this.minModes = modes.minModes; this.maxModes = modes.maxModes; - this.selectedModes.addAll(modes.getSelectedModes()); - - this.modeChooser = modes.modeChooser; - this.eachModeOnlyOnce = modes.eachModeOnlyOnce; - this.eachModeMoreThanOnce = modes.eachModeMoreThanOnce; this.maxModesFilter = modes.maxModesFilter; // can't change so no copy needed + this.moreCondition = modes.moreCondition; + + this.limitUsageByOnce = modes.limitUsageByOnce; + this.limitUsageResetOnNewTurn = modes.limitUsageResetOnNewTurn; - this.isRandom = modes.isRandom; this.chooseText = modes.chooseText; - this.resetEachTurn = modes.resetEachTurn; + this.chooseController = modes.chooseController; + this.mayChooseSameModeMoreThanOnce = modes.mayChooseSameModeMoreThanOnce; + this.mayChooseNone = modes.mayChooseNone; + this.isRandom = modes.isRandom; + + // current mode must be "copied" at the end + this.selectedModes.addAll(modes.getSelectedModes()); // TODO: bugged - can lost multi selects here? if (modes.getSelectedModes().isEmpty()) { this.currentMode = values().iterator().next(); } else { - this.currentMode = get(modes.getMode().getId()); // need fix? + this.currentMode = get(modes.getMode().getId()); // TODO: bugged - can lost multi selects here? } - this.moreCondition = modes.moreCondition; - this.mayChooseNone = modes.mayChooseNone; } + @Override public Modes copy() { return new Modes(this); } @@ -91,21 +97,21 @@ public class Modes extends LinkedHashMap { @Override public Mode get(Object key) { Mode modeToGet = super.get(key); - if (modeToGet == null && eachModeMoreThanOnce) { + if (modeToGet == null && mayChooseSameModeMoreThanOnce) { modeToGet = selectedDuplicateModes.get(key); } return modeToGet; } - public Stream stream() { - return super.values().stream(); - } - - public Stream streamAlreadySelected(Ability source, Game game) { + public Stream streamAlreadySelectedModes(Ability source, Game game) { Set selected = getAlreadySelectedModes(source, game, true); - return stream().filter(m -> selected.contains(m.getId())); + return super.values().stream().filter(m -> selected.contains(m.getId())); } + /** + * For card constructor: returns first/default mode + * For game: returns current resolving mode + */ public Mode getMode() { return currentMode; } @@ -119,7 +125,7 @@ public class Modes extends LinkedHashMap { */ public UUID getModeId(int index) { int idx = 0; - if (eachModeMoreThanOnce) { + if (mayChooseSameModeMoreThanOnce) { for (UUID modeId : this.getSelectedModes()) { idx++; if (idx == index) { @@ -137,6 +143,9 @@ public class Modes extends LinkedHashMap { return null; } + /** + * Return full list of selected modes in default/rules order (without multi selects) + */ public List getSelectedModes() { // modes can be selected in any order by user, but execution must be in rule's order List res = new ArrayList<>(this.size()); @@ -202,6 +211,11 @@ public class Modes extends LinkedHashMap { } public void setMaxModesFilter(Filter maxModesFilter) { + // verify check + if (maxModesFilter != null && !(maxModesFilter instanceof FilterPlayer)) { + throw new IllegalArgumentException("Wrong code usage: max modes filter support only FilterPlayer"); + } + this.maxModesFilter = maxModesFilter; } @@ -223,7 +237,7 @@ public class Modes extends LinkedHashMap { realMaxModes = 2; } - // use case: limit max modes by opponents (wtf?!) + // use case: limit max modes by opponents (example: choose one or more... each mode must target a different player) if (getMaxModesFilter() != null) { if (this.maxModesFilter instanceof FilterPlayer) { realMaxModes = 0; @@ -242,12 +256,12 @@ public class Modes extends LinkedHashMap { return realMaxModes; } - public void setModeChooser(TargetController modeChooser) { - this.modeChooser = modeChooser; + public void setChooseController(TargetController chooseController) { + this.chooseController = chooseController; } - public TargetController getModeChooser() { - return this.modeChooser; + public TargetController getChooseController() { + return this.chooseController; } public void setActiveMode(Mode mode) { @@ -268,12 +282,14 @@ public class Modes extends LinkedHashMap { this.moreCondition = moreCondition; } + private boolean isAlreadySelectedModesOutdated(Game game, Ability source) { + return this.isLimitUsageResetOnNewTurn() + && getOnceTurnNum(game, source) != game.getTurnNum(); + } + public boolean choose(Game game, Ability source) { - if (this.isResetEachTurn()) { - if (getTurnNum(game, source) != game.getTurnNum()) { - this.clearAlreadySelectedModes(source, game); - setTurnNum(game, source); - } + if (isAlreadySelectedModesOutdated(game, source)) { + this.clearAlreadySelectedModes(source, game); } if (this.size() > 1) { this.clearSelectedModes(); @@ -295,19 +311,19 @@ public class Modes extends LinkedHashMap { } // check if all modes can be activated automatically - if (this.size() == this.getMinModes() && !isEachModeMoreThanOnce()) { + if (this.size() == this.getMinModes() && !isMayChooseSameModeMoreThanOnce()) { Set onceSelectedModes = null; - if (isEachModeOnlyOnce()) { + if (isLimitUsageByOnce()) { onceSelectedModes = getAlreadySelectedModes(source, game, true); } for (Mode mode : this.values()) { - if ((!isEachModeOnlyOnce() || onceSelectedModes == null || !onceSelectedModes.contains(mode.getId())) + if ((!isLimitUsageByOnce() || onceSelectedModes == null || !onceSelectedModes.contains(mode.getId())) && mode.getTargets().canChoose(source.getControllerId(), source, game)) { this.addSelectedMode(mode.getId()); } } - if (isEachModeOnlyOnce()) { - setAlreadySelectedModes(source, game); + if (isLimitUsageByOnce()) { + setOnceSelectedModes(source, game); } return !selectedModes.isEmpty(); } @@ -317,7 +333,7 @@ public class Modes extends LinkedHashMap { // In that case, the other player does so when the spell or ability's controller normally would do so. // If there is more than one other player who could make such a choice, the spell or ability's controller decides which of those players will make the choice. UUID playerId; - if (modeChooser == TargetController.OPPONENT) { + if (chooseController == TargetController.OPPONENT) { TargetOpponent targetOpponent = new TargetOpponent(); targetOpponent.choose(Outcome.Benefit, source.getControllerId(), source.getSourceId(), source, game); playerId = targetOpponent.getFirstTarget(); @@ -336,8 +352,8 @@ public class Modes extends LinkedHashMap { while (this.selectedModes.size() < currentMaxModes) { Mode choice = player.chooseMode(this, source, game); if (choice == null) { - if (isEachModeOnlyOnce()) { - setAlreadySelectedModes(source, game); + if (isLimitUsageByOnce()) { + setOnceSelectedModes(source, game); } return this.selectedModes.size() >= this.getMinModes() || (this.selectedModes.size() == 0 && mayChooseNone); @@ -347,10 +363,10 @@ public class Modes extends LinkedHashMap { currentMode = choice; } } - if (isEachModeOnlyOnce()) { - setAlreadySelectedModes(source, game); + if (isLimitUsageByOnce()) { + setOnceSelectedModes(source, game); } - if (modeChooser == TargetController.OPPONENT) { + if (chooseController == TargetController.OPPONENT) { selectedModes .stream() .map(this::get) @@ -359,12 +375,10 @@ public class Modes extends LinkedHashMap { } } else { // only one mode available - if (currentMode == null) { - this.clearSelectedModes(); - Mode mode = this.values().iterator().next(); - this.addSelectedMode(mode.getId()); - this.setActiveMode(mode); - } + this.clearSelectedModes(); + Mode mode = this.values().iterator().next(); + this.addSelectedMode(mode.getId()); + this.setActiveMode(mode); } return true; } @@ -375,18 +389,20 @@ public class Modes extends LinkedHashMap { * @param source * @param game */ - private void setAlreadySelectedModes(Ability source, Game game) { + private void setOnceSelectedModes(Ability source, Game game) { for (UUID modeId : getSelectedModes()) { - String key = getKey(source, game, modeId); + String key = getSelectedModesKey(source, game, modeId); game.getState().setValue(key, true); } } private void clearAlreadySelectedModes(Ability source, Game game) { + // need full list to clear outdated data for (UUID modeId : getAlreadySelectedModes(source, game, false)) { - String key = getKey(source, game, modeId); + String key = getSelectedModesKey(source, game, modeId); game.getState().setValue(key, false); } + setOnceTurnNum(game, source); } /** @@ -400,15 +416,15 @@ public class Modes extends LinkedHashMap { throw new IllegalArgumentException("Unknown modeId to select"); } - if (selectedModes.contains(modeId) && eachModeMoreThanOnce) { + if (selectedModes.contains(modeId) && mayChooseSameModeMoreThanOnce) { Mode duplicateMode = get(modeId).copy(); UUID originalId = modeId; duplicateMode.setRandomId(); modeId = duplicateMode.getId(); selectedDuplicateModes.put(modeId, duplicateMode); selectedDuplicateToOriginalModeRefs.put(duplicateMode.getId(), originalId); - } + // TODO: bugged and allows to choose same mode multiple times without mayChooseSameModeMoreThanOnce? this.selectedModes.add(modeId); } @@ -418,39 +434,51 @@ public class Modes extends LinkedHashMap { this.selectedDuplicateToOriginalModeRefs.remove(modeId); } - // The already once selected modes for a modal card are stored as a state value - // That's important for modal abilities with modes that can only selected once while the object stays in its zone - @SuppressWarnings("unchecked") - private Set getAlreadySelectedModes(Ability source, Game game, boolean ignoreOldData) { - Set onceSelectedModes = new HashSet<>(); - if (ignoreOldData && this.isResetEachTurn() && getTurnNum(game, source) != game.getTurnNum()) { - // Selected modes is not for current turn, so we ignore any value that may be there. - return onceSelectedModes; + /** + * Return already selected modes, used for GUI and modal cards check + * Can be outdated if each turn reset enabled + *

+ * Warning, works with limitUsageByOnce only, other cards will not contain that info + * + * @param source + * @param game + * @param ignoreOutdatedData if true then return full selected modes (used in clear code on new turn) + * @return + */ + private Set getAlreadySelectedModes(Ability source, Game game, boolean ignoreOutdatedData) { + Set res = new HashSet<>(); + + // if selected modes is not for current turn, so we ignore any value that may be there + if (!ignoreOutdatedData && isAlreadySelectedModesOutdated(game, source)) { + return res; } + for (UUID modeId : this.keySet()) { - Object exist = game.getState().getValue(getKey(source, game, modeId)); + Object exist = game.getState().getValue(getSelectedModesKey(source, game, modeId)); if (exist == Boolean.TRUE) { - onceSelectedModes.add(modeId); + res.add(modeId); } } - return onceSelectedModes; + return res; } - // creates the key the selected modes are saved with to the state values - private String getKey(Ability source, Game game, UUID modeId) { + private String getSelectedModesKey(Ability source, Game game, UUID modeId) { return source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()) + modeId.toString(); } - private static int getTurnNum(Game game, Ability source) { - String key = source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()) + "turnNum"; - Object object = game.getState().getValue(key); + private String getOnceTurnNumKey(Ability source, Game game) { + return source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()) + "turnNum"; + } + + private int getOnceTurnNum(Game game, Ability source) { + Object object = game.getState().getValue(getOnceTurnNumKey(source, game)); if (object instanceof Integer) { return (Integer) object; } return 0; } - private static void setTurnNum(Game game, Ability source) { + private void setOnceTurnNum(Game game, Ability source) { String key = source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()) + "turnNum"; game.getState().setValue(key, game.getTurnNum()); } @@ -465,13 +493,13 @@ public class Modes extends LinkedHashMap { public List getAvailableModes(Ability source, Game game) { List availableModes = new ArrayList<>(); Set nonAvailableModes; - if (isEachModeMoreThanOnce()) { + if (isMayChooseSameModeMoreThanOnce()) { nonAvailableModes = new HashSet<>(); } else { nonAvailableModes = getAlreadySelectedModes(source, game, true); } for (Mode mode : this.values()) { - if (isEachModeOnlyOnce() && nonAvailableModes.contains(mode.getId())) { + if (isLimitUsageByOnce() && nonAvailableModes.contains(mode.getId())) { continue; } availableModes.add(mode); @@ -488,7 +516,7 @@ public class Modes extends LinkedHashMap { sb.append("you may "); } if (this.chooseText == null) { - if (modeChooser == TargetController.OPPONENT) { + if (chooseController == TargetController.OPPONENT) { sb.append("an opponent chooses "); } else { sb.append("choose "); @@ -514,16 +542,16 @@ public class Modes extends LinkedHashMap { sb.append(chooseText); } - if (isEachModeOnlyOnce() && this.getMaxModesFilter() == null) { + if (isLimitUsageByOnce() && this.getMaxModesFilter() == null) { sb.append(" that hasn't been chosen"); } - if (isResetEachTurn()) { + if (isLimitUsageResetOnNewTurn()) { sb.append(" this turn"); } if (this.getMaxModesFilter() != null) { sb.append(". Each mode must target ").append(getMaxModesFilter().getMessage()).append('.'); - } else if (isEachModeMoreThanOnce()) { + } else if (isMayChooseSameModeMoreThanOnce()) { sb.append(". You may choose the same mode more than once."); } else if (chooseText == null) { sb.append(" —"); @@ -543,32 +571,32 @@ public class Modes extends LinkedHashMap { return getText().replace("{this}", sourceName); } - public boolean isEachModeOnlyOnce() { - return eachModeOnlyOnce; + public boolean isLimitUsageByOnce() { + return limitUsageByOnce; } - public void setEachModeOnlyOnce(boolean eachModeOnlyOnce) { - this.eachModeOnlyOnce = eachModeOnlyOnce; + /** + * Limit modes usage to once per game or once per turn + */ + public void setLimitUsageByOnce(boolean resetOnNewTurn) { + this.limitUsageByOnce = true; + this.limitUsageResetOnNewTurn = resetOnNewTurn; } - public boolean isEachModeMoreThanOnce() { - return eachModeMoreThanOnce; + public boolean isMayChooseSameModeMoreThanOnce() { + return mayChooseSameModeMoreThanOnce; } - public void setEachModeMoreThanOnce(boolean eachModeMoreThanOnce) { - this.eachModeMoreThanOnce = eachModeMoreThanOnce; + public void setMayChooseSameModeMoreThanOnce(boolean mayChooseSameModeMoreThanOnce) { + this.mayChooseSameModeMoreThanOnce = mayChooseSameModeMoreThanOnce; } public void setRandom(boolean isRandom) { this.isRandom = isRandom; } - public boolean isResetEachTurn() { - return resetEachTurn; - } - - public void setResetEachTurn(boolean resetEachTurn) { - this.resetEachTurn = resetEachTurn; + public boolean isLimitUsageResetOnNewTurn() { + return limitUsageResetOnNewTurn; } public void setChooseText(String chooseText) { diff --git a/Mage/src/main/java/mage/abilities/hint/common/ModesAlreadyUsedHint.java b/Mage/src/main/java/mage/abilities/hint/common/ModesAlreadyUsedHint.java index 925bea52c2b..5717c768f78 100644 --- a/Mage/src/main/java/mage/abilities/hint/common/ModesAlreadyUsedHint.java +++ b/Mage/src/main/java/mage/abilities/hint/common/ModesAlreadyUsedHint.java @@ -23,7 +23,7 @@ public enum ModesAlreadyUsedHint implements Hint { public String getText(Game game, Ability ability) { List used = ability .getModes() - .streamAlreadySelected(ability, game) + .streamAlreadySelectedModes(ability, game) .map(Mode::getModeTag) .filter(tag -> tag != null && !tag.isEmpty()) .collect(Collectors.toList()); @@ -33,7 +33,7 @@ public enum ModesAlreadyUsedHint implements Hint { } return "Already used" - + (ability.getModes().isResetEachTurn() ? " this turn" : "") + + (ability.getModes().isLimitUsageResetOnNewTurn() ? " this turn" : "") + ": [" + String.join(", ", used) + "]"; } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index fec60c350de..c110093d8b8 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -4300,9 +4300,10 @@ public abstract class PlayerImpl implements Player, Serializable { } private void addModeOptions(List options, Ability option, Game game) { - // TODO: Support modal spells with more than one selectable mode + // TODO: support modal spells with more than one selectable mode (also must use max modes filter) for (Mode mode : option.getModes().values()) { Ability newOption = option.copy(); + // TODO: bugged? Research option.getModes().isMayChooseSameModeMoreThanOnce() - is it affected here newOption.getModes().clearSelectedModes(); newOption.getModes().addSelectedMode(mode.getId()); newOption.getModes().setActiveMode(mode);