From 20b7a115daf92cf302b0000643eb3c2ab57f4cb4 Mon Sep 17 00:00:00 2001 From: Susucre <34709007+Susucre@users.noreply.github.com> Date: Wed, 29 May 2024 22:34:54 +0200 Subject: [PATCH] prevent direct access of Player->counters ; some cleanup on counter removal effects ; implement [MH3] Izzet Generatorium (#12314) --- .../java/mage/client/game/PlayerPanelExt.java | 24 ++- .../src/main/java/mage/view/PlayerView.java | 11 +- .../src/mage/player/ai/util/CombatUtil.java | 2 +- Mage.Sets/src/mage/cards/a/AetherSnap.java | 10 +- .../src/mage/cards/a/AnimationModule.java | 21 +-- .../mage/cards/a/AnointWithAffliction.java | 11 +- Mage.Sets/src/mage/cards/a/Anthroplasm.java | 6 +- .../src/mage/cards/a/AshlingThePilgrim.java | 11 +- Mage.Sets/src/mage/cards/a/Aurification.java | 5 +- .../mage/cards/b/BewitchingLeechcraft.java | 23 +-- Mage.Sets/src/mage/cards/b/BlitzLeech.java | 41 +---- Mage.Sets/src/mage/cards/b/BombSquad.java | 2 +- .../src/mage/cards/b/BountyOfTheLuxa.java | 38 ++-- .../src/mage/cards/b/BringTheEnding.java | 9 +- .../mage/cards/c/CattiBrieOfMithralHall.java | 20 +-- .../src/mage/cards/c/ChainedThroatseeker.java | 2 +- .../src/mage/cards/c/CoalitionRelic.java | 9 +- Mage.Sets/src/mage/cards/c/Corrosion.java | 22 +-- .../src/mage/cards/c/CorruptedResolve.java | 9 +- Mage.Sets/src/mage/cards/c/CyclopeanTomb.java | 4 +- .../src/mage/cards/d/DelayingShield.java | 12 +- Mage.Sets/src/mage/cards/d/DieYoung.java | 8 +- .../src/mage/cards/e/EzuriClawOfProgress.java | 2 +- .../src/mage/cards/f/FeedTheInfection.java | 2 +- Mage.Sets/src/mage/cards/f/FinalAct.java | 9 +- Mage.Sets/src/mage/cards/f/FrayingLine.java | 5 +- .../src/mage/cards/f/FreyalisesWinds.java | 2 +- .../src/mage/cards/g/GalvanicDischarge.java | 2 +- Mage.Sets/src/mage/cards/g/GethsSummons.java | 2 +- Mage.Sets/src/mage/cards/g/GiantOyster.java | 4 +- Mage.Sets/src/mage/cards/g/GiveTake.java | 30 ++-- .../src/mage/cards/g/GlissasRetriever.java | 2 +- Mage.Sets/src/mage/cards/h/HammerJammer.java | 2 +- Mage.Sets/src/mage/cards/h/Hankyu.java | 4 +- Mage.Sets/src/mage/cards/h/HapatrasMark.java | 6 +- .../src/mage/cards/h/HarnessedLightning.java | 2 +- .../src/mage/cards/i/IxhelScionOfAtraxa.java | 2 +- .../src/mage/cards/i/IzzetGeneratorium.java | 170 ++++++++++++++++++ Mage.Sets/src/mage/cards/j/JarOfEyeballs.java | 4 +- Mage.Sets/src/mage/cards/l/Leeches.java | 4 +- .../src/mage/cards/l/LesserWerewolf.java | 4 +- .../src/mage/cards/l/LightningCoils.java | 12 +- .../src/mage/cards/l/LightningRunner.java | 8 +- .../mage/cards/l/LilyBowenRagingGrandma.java | 21 ++- Mage.Sets/src/mage/cards/l/LivingArmor.java | 10 +- .../src/mage/cards/l/LudevicsTestSubject.java | 24 +-- .../src/mage/cards/l/LumberingMegasloth.java | 4 +- .../mage/cards/m/MarchesaResoluteMonarch.java | 35 +--- .../mage/cards/m/MaulfistRevolutionary.java | 8 +- .../src/mage/cards/m/MerenOfClanNelToth.java | 2 +- Mage.Sets/src/mage/cards/m/MineLayer.java | 5 +- .../src/mage/cards/m/MizzixOfTheIzmagnus.java | 4 +- .../src/mage/cards/n/NightkinAmbusher.java | 2 +- Mage.Sets/src/mage/cards/o/OblivionStone.java | 17 +- .../src/mage/cards/p/PhyresisOutbreak.java | 2 +- .../src/mage/cards/p/PhyrexianAtlas.java | 2 +- .../src/mage/cards/p/PriceOfBetrayal.java | 13 +- .../src/mage/cards/q/QuicksilverFountain.java | 12 +- .../src/mage/cards/r/ReplicatingRing.java | 4 +- .../src/mage/cards/r/RogueSkycaptain.java | 7 +- .../src/mage/cards/s/SarulfRealmEater.java | 5 +- Mage.Sets/src/mage/cards/s/SepticRats.java | 13 +- .../src/mage/cards/s/SkyshipPlunderer.java | 8 +- Mage.Sets/src/mage/cards/s/Sporogenesis.java | 6 +- Mage.Sets/src/mage/cards/s/Suncleanser.java | 48 ++--- .../src/mage/cards/s/SurvivorsMedKit.java | 8 +- .../src/mage/cards/t/TemporalDistortion.java | 6 +- Mage.Sets/src/mage/cards/t/ThiefOfBlood.java | 8 +- .../src/mage/cards/v/VampireHexmage.java | 53 ++---- .../cards/v/Vault112SadisticSimulation.java | 2 +- .../mage/cards/v/Vault12TheNecropolis.java | 2 +- .../src/mage/cards/v/VentifactBottle.java | 13 +- Mage.Sets/src/mage/cards/v/VexingRadgull.java | 2 +- .../src/mage/cards/v/ViridianBetrayers.java | 7 +- .../mage/cards/v/VraskaBetrayalsSting.java | 2 +- .../src/mage/cards/w/WhisperingSpecter.java | 10 +- .../src/mage/cards/w/WrathOfTheSkies.java | 7 +- Mage.Sets/src/mage/cards/w/Wurmquake.java | 15 +- Mage.Sets/src/mage/sets/ModernHorizons3.java | 1 + .../cards/abilities/keywords/PersistTest.java | 3 +- .../single/mh3/IzzetGeneratoriumTest.java | 95 ++++++++++ .../single/onc/OtharriSunsGloryTest.java | 2 +- .../cards/triggers/RadCounterTriggerTest.java | 2 +- .../java/org/mage/test/player/TestPlayer.java | 33 +++- .../base/impl/CardTestPlayerAPIImpl.java | 2 +- .../ActivateIfConditionActivatedAbility.java | 4 + .../AttackedPlayersPoisonedCondition.java | 4 +- .../condition/common/CorruptedCondition.java | 4 +- .../abilities/costs/common/PayEnergyCost.java | 8 +- .../common/RemoveAllCountersSourceCost.java | 5 +- .../common/OpponentsPoisonCountersCount.java | 2 +- .../common/SourceControllerCountersCount.java | 2 +- .../common/RemoveAllCountersSourceEffect.java | 3 +- .../common/counter/ProliferateEffect.java | 3 +- ...emoveAllCountersPermanentTargetEffect.java | 53 ++++++ .../RemoveAllCountersTargetEffect.java | 45 ----- Mage/src/main/java/mage/cards/Card.java | 57 +++++- Mage/src/main/java/mage/cards/CardImpl.java | 39 ++-- .../java/mage/cards/ModalDoubleFacedCard.java | 4 +- .../src/main/java/mage/counters/Counters.java | 35 +--- .../FilterPermanentOrPlayerWithCounter.java | 4 +- Mage/src/main/java/mage/game/GameImpl.java | 4 +- .../game/command/emblems/RadiationEmblem.java | 6 +- .../main/java/mage/game/events/GameEvent.java | 4 +- .../permanent/token/DaxosSpiritToken.java | 2 +- Mage/src/main/java/mage/game/stack/Spell.java | 14 +- Mage/src/main/java/mage/game/turn/Turn.java | 2 +- Mage/src/main/java/mage/players/Player.java | 44 ++++- .../main/java/mage/players/PlayerImpl.java | 54 ++++-- .../test/java/mage/counters/CountersTest.java | 36 ++-- 110 files changed, 895 insertions(+), 646 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/i/IzzetGeneratorium.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/IzzetGeneratoriumTest.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/counter/RemoveAllCountersPermanentTargetEffect.java delete mode 100644 Mage/src/main/java/mage/abilities/effects/common/counter/RemoveAllCountersTargetEffect.java diff --git a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java index b0c2a500bb5..178a8fa09a6 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java @@ -16,8 +16,6 @@ import mage.components.ImagePanelStyle; import mage.constants.CardType; import static mage.constants.Constants.*; import mage.constants.ManaType; -import mage.counters.Counter; -import mage.counters.CounterType; import mage.designations.DesignationType; import mage.utils.timer.PriorityTimer; import mage.view.*; @@ -186,6 +184,18 @@ public class PlayerPanelExt extends javax.swing.JPanel { return false; } + // Not the most optimized, but we just query a few counterName here. + // More optimized would use a Map + private static int counterOfName(PlayerView player, String name) { + return player + .getCounters() + .stream() + .filter(counter -> counter.getName().equals(name)) + .map(CounterView::getCount) + .findFirst() + .orElse(0); + } + public void update(GameView game, PlayerView player, Set possibleTargets) { this.player = player; int pastLife = player.getLife(); @@ -226,10 +236,10 @@ public class PlayerPanelExt extends javax.swing.JPanel { changedFontLife = false; } setTextForLabel("life", lifeLabel, life, playerLife, true); - setTextForLabel("poison", poisonLabel, poison, player.getCounters().getCount(CounterType.POISON), false); - setTextForLabel("energy", energyLabel, energy, player.getCounters().getCount(CounterType.ENERGY), false); - setTextForLabel("experience", experienceLabel, experience, player.getCounters().getCount(CounterType.EXPERIENCE), false); - setTextForLabel("rad", radLabel, rad, player.getCounters().getCount(CounterType.RAD), false); + setTextForLabel("poison", poisonLabel, poison, counterOfName(player, "poison"), false); + setTextForLabel("energy", energyLabel, energy, counterOfName(player, "energy"), false); + setTextForLabel("experience", experienceLabel, experience, counterOfName(player, "experience"), false); + setTextForLabel("rad", radLabel, rad, counterOfName(player, "rad"), false); setTextForLabel("hand zone", handLabel, hand, player.getHandCount(), true); int libraryCards = player.getLibraryCount(); if (libraryCards > 99) { @@ -417,7 +427,7 @@ public class PlayerPanelExt extends javax.swing.JPanel { } // counters - for (Counter counter : player.getCounters().values()) { + for (CounterView counter : player.getCounters()) { tooltipText.append("
").append(counter.getName()).append(" counters: ").append(counter.getCount()); } diff --git a/Mage.Common/src/main/java/mage/view/PlayerView.java b/Mage.Common/src/main/java/mage/view/PlayerView.java index a18cad86475..d77be7461a3 100644 --- a/Mage.Common/src/main/java/mage/view/PlayerView.java +++ b/Mage.Common/src/main/java/mage/view/PlayerView.java @@ -1,7 +1,7 @@ package mage.view; import mage.cards.Card; -import mage.counters.Counters; +import mage.counters.Counter; import mage.designations.Designation; import mage.game.ExileZone; import mage.game.Game; @@ -27,7 +27,7 @@ public class PlayerView implements Serializable { private final boolean controlled; // gui: player is current user private final boolean isHuman; // human or computer private final int life; - private final Counters counters; + private final List counters; private final int wins; private final int winsNeeded; private final int libraryCount; @@ -64,7 +64,6 @@ public class PlayerView implements Serializable { this.controlled = player.getId().equals(createdForPlayerId); this.isHuman = player.isHuman(); this.life = player.getLife(); - this.counters = player.getCounters(); this.wins = player.getMatchPlayer().getWins(); this.winsNeeded = player.getMatchPlayer().getWinsNeeded(); this.libraryCount = player.getLibrary().size(); @@ -158,6 +157,10 @@ public class PlayerView implements Serializable { for (Designation designation : player.getDesignations()) { this.designationNames.add(designation.getName()); } + this.counters = new ArrayList<>(); + for (Counter counter : player.getCountersAsCopy().values()) { + counters.add(new CounterView(counter)); + } } private boolean showInBattlefield(Permanent permanent, GameState state) { @@ -187,7 +190,7 @@ public class PlayerView implements Serializable { return this.life; } - public Counters getCounters() { + public List getCounters() { return this.counters; } diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/util/CombatUtil.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/util/CombatUtil.java index 87bb4136d15..2859a047ca3 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/util/CombatUtil.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/util/CombatUtil.java @@ -57,7 +57,7 @@ public final class CombatUtil { return blockableAttackers; } - if (sumPoisonDamage(attackersThatWontBeBlocked, defender) >= 10 - defender.getCounters().getCount(CounterType.POISON)) { + if (sumPoisonDamage(attackersThatWontBeBlocked, defender) >= 10 - defender.getCountersCount(CounterType.POISON)) { blockableAttackers.addAll(unblockableAttackers); return blockableAttackers; } diff --git a/Mage.Sets/src/mage/cards/a/AetherSnap.java b/Mage.Sets/src/mage/cards/a/AetherSnap.java index 7e159e6096a..dedd0ffe57f 100644 --- a/Mage.Sets/src/mage/cards/a/AetherSnap.java +++ b/Mage.Sets/src/mage/cards/a/AetherSnap.java @@ -9,8 +9,6 @@ import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.counters.Counter; -import mage.counters.Counters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; @@ -67,13 +65,7 @@ class AetherSnapEffect extends OneShotEffect { if (permanent instanceof PermanentToken) { tokens.add(permanent); } - if (permanent.getCounters(game).isEmpty()) { - continue; - } - Counters counters = permanent.getCounters(game).copy(); - for (Counter counter : counters.values()) { - permanent.removeCounters(counter, source, game); - } + permanent.removeAllCounters(source, game); } controller.moveCards(tokens, Zone.EXILED, source, game); return true; diff --git a/Mage.Sets/src/mage/cards/a/AnimationModule.java b/Mage.Sets/src/mage/cards/a/AnimationModule.java index 7d49932519a..b3326370682 100644 --- a/Mage.Sets/src/mage/cards/a/AnimationModule.java +++ b/Mage.Sets/src/mage/cards/a/AnimationModule.java @@ -1,10 +1,6 @@ package mage.cards.a; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleActivatedAbility; @@ -22,6 +18,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.Counter; import mage.counters.CounterType; +import mage.counters.Counters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -29,8 +26,11 @@ import mage.game.permanent.token.ServoToken; import mage.players.Player; import mage.target.common.TargetPermanentOrPlayer; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author emerald000 */ public final class AnimationModule extends CardImpl { @@ -148,22 +148,23 @@ class AnimationModuleEffect extends OneShotEffect { } else { Player player = game.getPlayer(this.getTargetPointer().getFirst(game, source)); if (player != null) { - if (!player.getCounters().isEmpty()) { - if (player.getCounters().size() == 1) { - for (Counter counter : player.getCounters().values()) { + Counters counters = player.getCountersAsCopy(); + if (!counters.isEmpty()) { + if (counters.size() == 1) { + for (Counter counter : counters.values()) { Counter newCounter = new Counter(counter.getName()); player.addCounters(newCounter, source.getControllerId(), source, game); } } else { Choice choice = new ChoiceImpl(true); Set choices = new LinkedHashSet<>(); - for (Counter counter : player.getCounters().values()) { + for (Counter counter : counters.values()) { choices.add(counter.getName()); } choice.setChoices(choices); choice.setMessage("Choose a counter"); if (controller.choose(Outcome.Benefit, choice, game)) { - for (Counter counter : player.getCounters().values()) { + for (Counter counter : counters.values()) { if (counter.getName().equals(choice.getChoice())) { Counter newCounter = new Counter(counter.getName()); player.addCounters(newCounter, source.getControllerId(), source, game); diff --git a/Mage.Sets/src/mage/cards/a/AnointWithAffliction.java b/Mage.Sets/src/mage/cards/a/AnointWithAffliction.java index 200ca1a23a1..b3f2cacbbec 100644 --- a/Mage.Sets/src/mage/cards/a/AnointWithAffliction.java +++ b/Mage.Sets/src/mage/cards/a/AnointWithAffliction.java @@ -1,9 +1,5 @@ package mage.cards.a; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -18,6 +14,10 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + /** * @author TheElk801 */ @@ -68,8 +68,7 @@ class AnointWithAfflictionEffect extends OneShotEffect { || Optional .ofNullable(game.getPlayer(permanent.getControllerId())) .filter(Objects::nonNull) - .map(Player::getCounters) - .map(counters -> counters.getCount(CounterType.POISON) >= 3) + .map(p -> p.getCountersCount(CounterType.POISON) >= 3) .orElse(false)) && player.moveCards(permanent, Zone.EXILED, source, game); } diff --git a/Mage.Sets/src/mage/cards/a/Anthroplasm.java b/Mage.Sets/src/mage/cards/a/Anthroplasm.java index 571f4d79189..a6dcedf41aa 100644 --- a/Mage.Sets/src/mage/cards/a/Anthroplasm.java +++ b/Mage.Sets/src/mage/cards/a/Anthroplasm.java @@ -1,7 +1,6 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; @@ -20,8 +19,9 @@ import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author Plopman */ public final class Anthroplasm extends CardImpl { @@ -73,7 +73,7 @@ class AnthroplasmEffect extends OneShotEffect { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { //Remove all +1/+1 counters - permanent.removeCounters(permanent.getCounters(game).get(CounterType.P1P1.getName()), source, game); + permanent.removeAllCounters(CounterType.P1P1.getName(), source, game); //put X +1/+1 counters permanent.addCounters(CounterType.P1P1.createInstance(source.getManaCostsToPay().getX()), source.getControllerId(), source, game); return true; diff --git a/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java b/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java index ac7654eeaa3..9abbd6b8542 100644 --- a/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java +++ b/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java @@ -75,14 +75,11 @@ class AshlingThePilgrimEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (sourcePermanent != null) { - int counters = sourcePermanent.getCounters(game).getCount(CounterType.P1P1); - if (counters < 1) { - return false; - } - sourcePermanent.removeCounters(CounterType.P1P1.createInstance(counters), source, game); - return new DamageEverythingEffect(counters, StaticFilters.FILTER_PERMANENT_CREATURE).apply(game, source); + if (sourcePermanent == null) { + return false; } + int amountRemoved = sourcePermanent.removeAllCounters(CounterType.P1P1.getName(), source, game); + new DamageEverythingEffect(amountRemoved, StaticFilters.FILTER_PERMANENT_CREATURE).apply(game, source); return true; } } diff --git a/Mage.Sets/src/mage/cards/a/Aurification.java b/Mage.Sets/src/mage/cards/a/Aurification.java index 729135bfd55..82609fde897 100644 --- a/Mage.Sets/src/mage/cards/a/Aurification.java +++ b/Mage.Sets/src/mage/cards/a/Aurification.java @@ -123,10 +123,7 @@ public final class Aurification extends CardImpl { public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(CardType.CREATURE, game)) { if (permanent != null) { - int numToRemove = permanent.getCounters(game).getCount(CounterType.GOLD); - if (numToRemove > 0) { - permanent.removeCounters(CounterType.GOLD.getName(), numToRemove, source, game); - } + permanent.removeAllCounters(CounterType.GOLD.getName(), source, game); } } return true; diff --git a/Mage.Sets/src/mage/cards/b/BewitchingLeechcraft.java b/Mage.Sets/src/mage/cards/b/BewitchingLeechcraft.java index 572922a4655..1aaeaa5565a 100644 --- a/Mage.Sets/src/mage/cards/b/BewitchingLeechcraft.java +++ b/Mage.Sets/src/mage/cards/b/BewitchingLeechcraft.java @@ -21,14 +21,13 @@ import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * * @author Susucr */ public final class BewitchingLeechcraft extends CardImpl { public BewitchingLeechcraft(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); - + this.subtype.add(SubType.AURA); // Enchant creature @@ -42,10 +41,10 @@ public final class BewitchingLeechcraft extends CardImpl { // Enchanted creature has "If this creature would untap during your untap step, remove a +1/+1 counter from it instead. If you do, untap it." this.addAbility(new SimpleStaticAbility( - new GainAbilityAttachedEffect( - new SimpleStaticAbility(new BewitchingLeechcraftReplacementEffect()), - AttachmentType.AURA - ) + new GainAbilityAttachedEffect( + new SimpleStaticAbility(new BewitchingLeechcraftReplacementEffect()), + AttachmentType.AURA + ) )); } @@ -64,7 +63,7 @@ class BewitchingLeechcraftReplacementEffect extends ReplacementEffectImpl { BewitchingLeechcraftReplacementEffect() { super(Duration.EndOfGame, Outcome.Detriment); staticText = "If this creature would untap during your untap step, " + - "remove a +1/+1 counter from it instead. If you do, untap it."; + "remove a +1/+1 counter from it instead. If you do, untap it."; } private BewitchingLeechcraftReplacementEffect(final BewitchingLeechcraftReplacementEffect effect) { @@ -84,18 +83,20 @@ class BewitchingLeechcraftReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent permanentUntapping = game.getPermanent(source.getSourceId()); - if(!applies(event,source,game)){ + if (!applies(event, source, game)) { return false; } - + int amountBefore = permanentUntapping.getCounters(game).getCount(CounterType.P1P1); + permanentUntapping.removeCounters(CounterType.P1P1.getName(), 1, source, game); + int amountAfter = permanentUntapping.getCounters(game).getCount(CounterType.P1P1); // If we could not remove a counter, we are replacing the UNTAP event. // If we could remove a counter, we are not replacing the UNTAP, just adding to it. - return !permanentUntapping.getCounters(game).removeCounter(CounterType.P1P1, 1); + return amountBefore < amountAfter; } @Override public boolean applies(GameEvent event, Ability source, Game game) { return game.getTurnStepType() == PhaseStep.UNTAP - && event.getTargetId().equals(source.getSourceId()); + && event.getTargetId().equals(source.getSourceId()); } } diff --git a/Mage.Sets/src/mage/cards/b/BlitzLeech.java b/Mage.Sets/src/mage/cards/b/BlitzLeech.java index 55336f12e67..2d637e0d77e 100644 --- a/Mage.Sets/src/mage/cards/b/BlitzLeech.java +++ b/Mage.Sets/src/mage/cards/b/BlitzLeech.java @@ -3,21 +3,16 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.counter.RemoveAllCountersPermanentTargetEffect; import mage.abilities.keyword.FlashAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetOpponentsCreaturePermanent; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; /** * @author TheElk801 @@ -36,7 +31,7 @@ public final class BlitzLeech extends CardImpl { // When Blitz Leech enters the battlefield, target creature an opponent controls gets -2/-2 until end of turn. Remove all counters from that creature. Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(-2, -2)); - ability.addEffect(new BlitzLeechEffect()); + ability.addEffect(new RemoveAllCountersPermanentTargetEffect().setText("Remove all counters from that creature")); ability.addTarget(new TargetOpponentsCreaturePermanent()); this.addAbility(ability); } @@ -49,36 +44,4 @@ public final class BlitzLeech extends CardImpl { public BlitzLeech copy() { return new BlitzLeech(this); } -} - -class BlitzLeechEffect extends OneShotEffect { - - BlitzLeechEffect() { - super(Outcome.Benefit); - staticText = "Remove all counters from that creature."; - } - - private BlitzLeechEffect(final BlitzLeechEffect effect) { - super(effect); - } - - @Override - public BlitzLeechEffect copy() { - return new BlitzLeechEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent == null) { - return false; - } - Set counterTypes = permanent - .getCounters(game) - .keySet() - .stream() - .collect(Collectors.toSet()); - counterTypes.forEach(permanent.getCounters(game)::removeAllCounters); - return true; - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BombSquad.java b/Mage.Sets/src/mage/cards/b/BombSquad.java index 9172dae1f44..69f0d6664e6 100644 --- a/Mage.Sets/src/mage/cards/b/BombSquad.java +++ b/Mage.Sets/src/mage/cards/b/BombSquad.java @@ -122,7 +122,7 @@ class BombSquadDamgeEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent creature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (creature != null) { - creature.removeCounters(CounterType.FUSE.getName(), creature.getCounters(game).getCount(CounterType.FUSE), source, game); + creature.removeAllCounters(CounterType.FUSE.getName(), source, game); creature.destroy(source, game, false); } if (creature == null) { diff --git a/Mage.Sets/src/mage/cards/b/BountyOfTheLuxa.java b/Mage.Sets/src/mage/cards/b/BountyOfTheLuxa.java index 23c6f701870..cad12b0c265 100644 --- a/Mage.Sets/src/mage/cards/b/BountyOfTheLuxa.java +++ b/Mage.Sets/src/mage/cards/b/BountyOfTheLuxa.java @@ -1,7 +1,6 @@ package mage.cards.b; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility; @@ -17,8 +16,9 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class BountyOfTheLuxa extends CardImpl { @@ -64,29 +64,25 @@ class BountyOfTheLuxaEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent bountyOfLuxa = game.getPermanent(source.getSourceId()); - if (bountyOfLuxa != null && bountyOfLuxa.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter()) { - bountyOfLuxa = null; - } if (controller == null) { return false; } - - if (bountyOfLuxa != null - && bountyOfLuxa.getCounters(game).getCount(CounterType.FLOOD) > 0) { - bountyOfLuxa.removeCounters(CounterType.FLOOD.createInstance(bountyOfLuxa.getCounters(game).getCount(CounterType.FLOOD)), source, game); - if (bountyOfLuxa.getCounters(game).getCount(CounterType.FLOOD) == 0) { - Mana manaToAdd = new Mana(); - manaToAdd.increaseColorless(); - manaToAdd.increaseGreen(); - manaToAdd.increaseBlue(); - controller.getManaPool().addMana(manaToAdd, game, source); - } - } else { - if (bountyOfLuxa != null) { - new AddCountersSourceEffect(CounterType.FLOOD.createInstance()).apply(game, source); - } + Permanent bountyOfLuxa = source.getSourcePermanentIfItStillExists(game); + if (bountyOfLuxa == null) { + // No flood counters will be removed. Only the draw part of the effect will apply. controller.drawCards(1, source, game); + return true; + } + int amountRemoved = bountyOfLuxa.removeAllCounters(CounterType.FLOOD.getName(), source, game); + if (amountRemoved == 0) { + new AddCountersSourceEffect(CounterType.FLOOD.createInstance()).apply(game, source); + controller.drawCards(1, source, game); + } else { + Mana manaToAdd = new Mana(); + manaToAdd.increaseColorless(); + manaToAdd.increaseGreen(); + manaToAdd.increaseBlue(); + controller.getManaPool().addMana(manaToAdd, game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/b/BringTheEnding.java b/Mage.Sets/src/mage/cards/b/BringTheEnding.java index c3780fd728c..e938b3a8bef 100644 --- a/Mage.Sets/src/mage/cards/b/BringTheEnding.java +++ b/Mage.Sets/src/mage/cards/b/BringTheEnding.java @@ -1,7 +1,5 @@ package mage.cards.b; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.Effect; @@ -18,15 +16,16 @@ import mage.game.Game; import mage.players.Player; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author @stwalsh4118 */ public final class BringTheEnding extends CardImpl { public BringTheEnding(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); - + // Counter target spell unless its controller pays {2}. // Corrupted -- Counter that spell instead if its controller has three or more poison counters. @@ -70,7 +69,7 @@ class BringTheEndingCounterEffect extends OneShotEffect { UUID controllerId = game.getControllerId(targetId); Player player = game.getPlayer(controllerId); - if (player != null && player.getCounters().getCount(CounterType.POISON) >= 3) { + if (player != null && player.getCountersCount(CounterType.POISON) >= 3) { hardCounterEffect.setTargetPointer(this.getTargetPointer().copy()); return hardCounterEffect.apply(game, source); } else { diff --git a/Mage.Sets/src/mage/cards/c/CattiBrieOfMithralHall.java b/Mage.Sets/src/mage/cards/c/CattiBrieOfMithralHall.java index b4f23533181..131a13f1aae 100644 --- a/Mage.Sets/src/mage/cards/c/CattiBrieOfMithralHall.java +++ b/Mage.Sets/src/mage/cards/c/CattiBrieOfMithralHall.java @@ -1,7 +1,5 @@ package mage.cards.c; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -18,18 +16,15 @@ import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.ReachAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterAttackingOrBlockingCreature; import mage.game.Game; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author zeffirojoe */ public final class CattiBrieOfMithralHall extends CardImpl { @@ -42,7 +37,7 @@ public final class CattiBrieOfMithralHall extends CardImpl { } public CattiBrieOfMithralHall(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{G}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{W}"); this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); @@ -56,15 +51,12 @@ public final class CattiBrieOfMithralHall extends CardImpl { // Reach this.addAbility(ReachAbility.getInstance()); - // Whenever Catti-brie of Mithral Hall attacks, put a +1/+1 counter on it for - // each Equipment attached to it. + // Whenever Catti-brie of Mithral Hall attacks, put a +1/+1 counter on it for each Equipment attached to it. EquipmentAttachedCount amount = new EquipmentAttachedCount(); this.addAbility(new AttacksTriggeredAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance(), amount, false).setText("put a +1/+1 counter on it for each Equipment attached to it"))); - // {1}, Remove all +1/+1 counters from Catti-brie: It deals X damage to target - // attacking or blocking creature an opponent controls, where X is the number of - // counters removed this way. + // {1}, Remove all +1/+1 counters from Catti-brie: It deals X damage to target attacking or blocking creature an opponent controls, where X is the number of counters removed this way. Ability damageAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(CattiBrieRemovedCounterValue.instance).setText("it deals X damage to target attacking or blocking creature an opponent controls, where X is the number of counters removed this way"), new ManaCostsImpl<>("{1}")); damageAbility.addTarget(new TargetCreaturePermanent(filter)); diff --git a/Mage.Sets/src/mage/cards/c/ChainedThroatseeker.java b/Mage.Sets/src/mage/cards/c/ChainedThroatseeker.java index 9b7e2601297..9d214a09afc 100644 --- a/Mage.Sets/src/mage/cards/c/ChainedThroatseeker.java +++ b/Mage.Sets/src/mage/cards/c/ChainedThroatseeker.java @@ -66,7 +66,7 @@ class ChainedThroatseekerCantAttackEffect extends RestrictionEffect { @Override public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { Player targetPlayer = game.getPlayerOrPlaneswalkerController(defenderId); - return targetPlayer != null && targetPlayer.getCounters().containsKey(CounterType.POISON); + return targetPlayer != null && targetPlayer.getCountersCount(CounterType.POISON) > 0; } @Override diff --git a/Mage.Sets/src/mage/cards/c/CoalitionRelic.java b/Mage.Sets/src/mage/cards/c/CoalitionRelic.java index 66d93363f10..685af10368e 100644 --- a/Mage.Sets/src/mage/cards/c/CoalitionRelic.java +++ b/Mage.Sets/src/mage/cards/c/CoalitionRelic.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility; @@ -22,8 +21,9 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CoalitionRelic extends CardImpl { @@ -70,11 +70,10 @@ class CoalitionRelicEffect extends OneShotEffect { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Player player = game.getPlayer(source.getControllerId()); if (sourcePermanent != null && player != null) { - int chargeCounters = sourcePermanent.getCounters(game).getCount(CounterType.CHARGE); - sourcePermanent.removeCounters(CounterType.CHARGE.createInstance(chargeCounters), source, game); + int amountRemoved = sourcePermanent.removeAllCounters(CounterType.CHARGE.getName(), source, game); Mana mana = new Mana(); ChoiceColor choice = new ChoiceColor(); - for (int i = 0; i < chargeCounters; i++) { + for (int i = 0; i < amountRemoved; i++) { if (!player.choose(outcome, choice, game)) { return false; } diff --git a/Mage.Sets/src/mage/cards/c/Corrosion.java b/Mage.Sets/src/mage/cards/c/Corrosion.java index d181ed8987e..fc63a4c1468 100644 --- a/Mage.Sets/src/mage/cards/c/Corrosion.java +++ b/Mage.Sets/src/mage/cards/c/Corrosion.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; @@ -20,14 +19,15 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author L_J */ public final class Corrosion extends CardImpl { public Corrosion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{B}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}{R}"); // Cumulative upkeep-Pay {1}. this.addAbility(new CumulativeUpkeepAbility(new GenericManaCost(1))); @@ -36,9 +36,9 @@ public final class Corrosion extends CardImpl { Ability ability = new BeginningOfUpkeepTriggeredAbility(new CorrosionUpkeepEffect(), TargetController.YOU, false); ability.addTarget(new TargetOpponent()); this.addAbility(ability); - + // When Corrosion leaves the battlefield, remove all rust counters from all permanents. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new CorrosionRemoveCountersEffect(), false)); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new CorrosionRemoveCountersEffect(), false)); } private Corrosion(final Corrosion card) { @@ -52,23 +52,23 @@ public final class Corrosion extends CardImpl { } class CorrosionUpkeepEffect extends OneShotEffect { - + private static final FilterArtifactPermanent filter = new FilterArtifactPermanent(); - + CorrosionUpkeepEffect() { super(Outcome.DestroyPermanent); this.staticText = "put a rust counter on each artifact target opponent controls. Then destroy each artifact with mana value less than or equal to the number of rust counters on it. Artifacts destroyed this way can't be regenerated"; } - + private CorrosionUpkeepEffect(final CorrosionUpkeepEffect effect) { super(effect); } - + @Override public CorrosionUpkeepEffect copy() { return new CorrosionUpkeepEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); @@ -112,7 +112,7 @@ class CorrosionRemoveCountersEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { - permanent.removeCounters(CounterType.RUST.createInstance(permanent.getCounters(game).getCount(CounterType.RUST)), source, game); + permanent.removeAllCounters(CounterType.RUST.getName(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/CorruptedResolve.java b/Mage.Sets/src/mage/cards/c/CorruptedResolve.java index b9e8ef5f6ce..8874b1ea776 100644 --- a/Mage.Sets/src/mage/cards/c/CorruptedResolve.java +++ b/Mage.Sets/src/mage/cards/c/CorruptedResolve.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -14,14 +13,15 @@ import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author Loki */ public final class CorruptedResolve extends CardImpl { public CorruptedResolve(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); // Counter target spell if its controller is poisoned. @@ -54,8 +54,9 @@ class CorruptedResolveEffect extends OneShotEffect { Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); if (spell != null) { Player player = game.getPlayer(spell.getControllerId()); - if (player != null && player.getCounters().containsKey(CounterType.POISON)) + if (player != null && player.getCountersCount(CounterType.POISON) > 0) { return game.getStack().counter(getTargetPointer().getFirst(game, source), source, game); + } } return false; } diff --git a/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java b/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java index 86a1f7440d1..d010d30c720 100644 --- a/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java +++ b/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java @@ -13,7 +13,7 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.abilities.effects.common.counter.RemoveAllCountersTargetEffect; +import mage.abilities.effects.common.counter.RemoveAllCountersPermanentTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -168,7 +168,7 @@ class CyclopeanTombEffect extends OneShotEffect { if (controller.chooseTarget(Outcome.Neutral, target, source, game)) { Permanent chosenLand = game.getPermanent(target.getFirstTarget()); if (chosenLand != null) { - Effect effect = new RemoveAllCountersTargetEffect(CounterType.MIRE); + Effect effect = new RemoveAllCountersPermanentTargetEffect(CounterType.MIRE); effect.setTargetPointer(new FixedTarget(chosenLand, game)); effect.apply(game, source); landRef.remove(new MageObjectReference(chosenLand, game)); diff --git a/Mage.Sets/src/mage/cards/d/DelayingShield.java b/Mage.Sets/src/mage/cards/d/DelayingShield.java index 10e536a3cb8..6c96c95b81a 100644 --- a/Mage.Sets/src/mage/cards/d/DelayingShield.java +++ b/Mage.Sets/src/mage/cards/d/DelayingShield.java @@ -1,7 +1,6 @@ package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -18,18 +17,18 @@ import mage.counters.CounterType; import mage.game.Game; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author emerald000 */ public final class DelayingShield extends CardImpl { public DelayingShield(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); // If damage would be dealt to you, put that many delay counters on Delaying Shield instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DelayingShieldReplacementEffect())); @@ -103,9 +102,8 @@ class DelayingShieldUpkeepEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getSourceId()); if (controller != null && permanent != null) { - int numCounters = permanent.getCounters(game).getCount(CounterType.DELAY); - permanent.removeCounters(CounterType.DELAY.createInstance(numCounters), source, game); - for (int i = numCounters; i > 0; i--) { + int countersRemoved = permanent.removeAllCounters(CounterType.DELAY.getName(), source, game); + for (int i = countersRemoved; i > 0; i--) { if (controller.chooseUse(Outcome.Benefit, "Pay {1}{W}? (" + i + " counters left to pay)", source, game)) { Cost cost = new ManaCostsImpl<>("{1}{W}"); if (cost.pay(source, game, source, source.getControllerId(), false, null)) { diff --git a/Mage.Sets/src/mage/cards/d/DieYoung.java b/Mage.Sets/src/mage/cards/d/DieYoung.java index 203c8d3530e..50567d87692 100644 --- a/Mage.Sets/src/mage/cards/d/DieYoung.java +++ b/Mage.Sets/src/mage/cards/d/DieYoung.java @@ -1,7 +1,6 @@ package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayEnergyCost; @@ -21,14 +20,15 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class DieYoung extends CardImpl { public DieYoung(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // Choose target creature. You get {E}{E}, then you may pay any amount of {E}. The creature gets -1/-1 until end of turn for each {E} paid this way. this.getSpellAbility().addEffect(new DieYoungEffect()); @@ -66,7 +66,7 @@ class DieYoungEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { new GetEnergyCountersControllerEffect(2).apply(game, source); - int max = controller.getCounters().getCount(CounterType.ENERGY); + int max = controller.getCountersCount(CounterType.ENERGY); int numberToPayed = controller.getAmount(0, max, "How many energy counters do you like to pay? (maximum = " + max + ')', game); if (numberToPayed > 0) { Cost cost = new PayEnergyCost(numberToPayed); diff --git a/Mage.Sets/src/mage/cards/e/EzuriClawOfProgress.java b/Mage.Sets/src/mage/cards/e/EzuriClawOfProgress.java index 27f1f0ed1ee..39c45b32e70 100644 --- a/Mage.Sets/src/mage/cards/e/EzuriClawOfProgress.java +++ b/Mage.Sets/src/mage/cards/e/EzuriClawOfProgress.java @@ -86,7 +86,7 @@ class EzuriClawOfProgressEffect extends OneShotEffect { if (controller != null) { Permanent target = game.getPermanent(getTargetPointer().getFirst(game, source)); if (target != null) { - int amount = controller.getCounters().getCount(CounterType.EXPERIENCE); + int amount = controller.getCountersCount(CounterType.EXPERIENCE); target.addCounters(CounterType.P1P1.createInstance(amount), source.getControllerId(), source, game); } return true; diff --git a/Mage.Sets/src/mage/cards/f/FeedTheInfection.java b/Mage.Sets/src/mage/cards/f/FeedTheInfection.java index 7e73e380d3d..a01361309d2 100644 --- a/Mage.Sets/src/mage/cards/f/FeedTheInfection.java +++ b/Mage.Sets/src/mage/cards/f/FeedTheInfection.java @@ -62,7 +62,7 @@ class FeedTheInfectionEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { for (UUID playerId : game.getOpponents(source.getControllerId())) { Player player = game.getPlayer(playerId); - if (player != null && player.getCounters().getCount(CounterType.POISON) >= 3) { + if (player != null && player.getCountersCount(CounterType.POISON) >= 3) { player.loseLife(3, game, source, false); } } diff --git a/Mage.Sets/src/mage/cards/f/FinalAct.java b/Mage.Sets/src/mage/cards/f/FinalAct.java index 32c7caa21d7..a172a29eaf8 100644 --- a/Mage.Sets/src/mage/cards/f/FinalAct.java +++ b/Mage.Sets/src/mage/cards/f/FinalAct.java @@ -9,7 +9,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.counters.Counter; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; @@ -77,13 +76,7 @@ class FinalActEffect extends OneShotEffect { if (player == null) { continue; } - player - .getCounters() - .values() - .stream() - .map(Counter::copy) - .forEach(counter -> player.removeCounters(counter.getName(), counter.getCount(), source, game)); - + player.loseAllCounters(source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/f/FrayingLine.java b/Mage.Sets/src/mage/cards/f/FrayingLine.java index aec8e5c0fb0..3df695d2200 100644 --- a/Mage.Sets/src/mage/cards/f/FrayingLine.java +++ b/Mage.Sets/src/mage/cards/f/FrayingLine.java @@ -112,10 +112,7 @@ class FrayingLineEffect extends OneShotEffect { StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source, game )) { - int counters = permanent.getCounters(game).getCount(CounterType.ROPE); - if (counters > 0) { - permanent.removeCounters(CounterType.ROPE.createInstance(counters), source, game); - } + permanent.removeAllCounters(CounterType.ROPE.getName(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/f/FreyalisesWinds.java b/Mage.Sets/src/mage/cards/f/FreyalisesWinds.java index fa7180b8435..c4b08978b6b 100644 --- a/Mage.Sets/src/mage/cards/f/FreyalisesWinds.java +++ b/Mage.Sets/src/mage/cards/f/FreyalisesWinds.java @@ -65,7 +65,7 @@ class FreyalisesWindsReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent permanentUntapping = game.getPermanent(event.getTargetId()); if (permanentUntapping != null) { - permanentUntapping.getCounters(game).removeAllCounters(CounterType.WIND); + permanentUntapping.removeAllCounters(CounterType.WIND.getName(), source, game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/g/GalvanicDischarge.java b/Mage.Sets/src/mage/cards/g/GalvanicDischarge.java index ed814b9cd02..725f42f9893 100644 --- a/Mage.Sets/src/mage/cards/g/GalvanicDischarge.java +++ b/Mage.Sets/src/mage/cards/g/GalvanicDischarge.java @@ -63,7 +63,7 @@ class GalvanicDischargeEffect extends OneShotEffect { return false; } new GetEnergyCountersControllerEffect(3).apply(game, source); - int numberToPay = controller.getAmount(0, controller.getCounters().getCount(CounterType.ENERGY), "How many {E} do you like to pay?", game); + int numberToPay = controller.getAmount(0, controller.getCountersCount(CounterType.ENERGY), "How many {E} do you like to pay?", game); if (numberToPay <= 0) { return true; } diff --git a/Mage.Sets/src/mage/cards/g/GethsSummons.java b/Mage.Sets/src/mage/cards/g/GethsSummons.java index b2a2d31fa37..0dc0148c3f6 100644 --- a/Mage.Sets/src/mage/cards/g/GethsSummons.java +++ b/Mage.Sets/src/mage/cards/g/GethsSummons.java @@ -64,7 +64,7 @@ enum GethsSummonsAdjuster implements TargetAdjuster { // corrupted opponents' graveyards for (UUID opponentId : game.getOpponents(ability.getControllerId(), true)) { Player opponent = game.getPlayer(opponentId); - if (opponent == null || opponent.getCounters().getCount(CounterType.POISON) < 3) { + if (opponent == null || opponent.getCountersCount(CounterType.POISON) < 3) { continue; } FilterCard filter = new FilterCard("creature card from " + opponent.getLogName() + "'s graveyard"); diff --git a/Mage.Sets/src/mage/cards/g/GiantOyster.java b/Mage.Sets/src/mage/cards/g/GiantOyster.java index 3d4a7c74d48..c4d1a745531 100644 --- a/Mage.Sets/src/mage/cards/g/GiantOyster.java +++ b/Mage.Sets/src/mage/cards/g/GiantOyster.java @@ -12,7 +12,7 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DontUntapAsLongAsSourceTappedEffect; import mage.abilities.effects.common.RemoveDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.abilities.effects.common.counter.RemoveAllCountersTargetEffect; +import mage.abilities.effects.common.counter.RemoveAllCountersPermanentTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -127,7 +127,7 @@ class GiantOysterCreateDelayedTriggerEffects extends OneShotEffect { class GiantOysterLeaveUntapDelayedTriggeredAbility extends DelayedTriggeredAbility { public GiantOysterLeaveUntapDelayedTriggeredAbility(UUID abilityToCancel) { - super(new RemoveAllCountersTargetEffect(CounterType.M1M1), Duration.EndOfGame, true, false); + super(new RemoveAllCountersPermanentTargetEffect(CounterType.M1M1), Duration.EndOfGame, true, false); this.addEffect(new RemoveDelayedTriggeredAbilityEffect(abilityToCancel)); } diff --git a/Mage.Sets/src/mage/cards/g/GiveTake.java b/Mage.Sets/src/mage/cards/g/GiveTake.java index 2e0a6a5b8ba..4595add44aa 100644 --- a/Mage.Sets/src/mage/cards/g/GiveTake.java +++ b/Mage.Sets/src/mage/cards/g/GiveTake.java @@ -1,7 +1,6 @@ package mage.cards.g; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -17,8 +16,9 @@ import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class GiveTake extends SplitCard { @@ -65,20 +65,18 @@ class TakeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (creature != null) { - int numberCounters = creature.getCounters(game).getCount(CounterType.P1P1); - if (numberCounters > 0) { - creature.removeCounters(CounterType.P1P1.getName(), numberCounters, source, game); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.drawCards(numberCounters, source, game); - } else { - throw new UnsupportedOperationException("Controller missing"); - } - } - return true; + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; } - return false; + Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (creature == null) { + return false; + } + int countersRemoved = creature.removeAllCounters(CounterType.P1P1.getName(), source, game); + if (countersRemoved > 0) { + controller.drawCards(countersRemoved, source, game); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/g/GlissasRetriever.java b/Mage.Sets/src/mage/cards/g/GlissasRetriever.java index bc586e48fb0..5bb9270855b 100644 --- a/Mage.Sets/src/mage/cards/g/GlissasRetriever.java +++ b/Mage.Sets/src/mage/cards/g/GlissasRetriever.java @@ -80,7 +80,7 @@ enum GlissasRetrieverAdjuster implements TargetAdjuster { int amount = 0; for (UUID opponentId : game.getOpponents(ability.getControllerId(), true)) { Player player = game.getPlayer(opponentId); - if (player != null && player.getCounters().getCount(CounterType.POISON) >= 3) { + if (player != null && player.getCountersCount(CounterType.POISON) >= 3) { amount++; } } diff --git a/Mage.Sets/src/mage/cards/h/HammerJammer.java b/Mage.Sets/src/mage/cards/h/HammerJammer.java index c0e04eee326..03599290a08 100644 --- a/Mage.Sets/src/mage/cards/h/HammerJammer.java +++ b/Mage.Sets/src/mage/cards/h/HammerJammer.java @@ -141,7 +141,7 @@ class HammerJammerEffect extends OneShotEffect { if (controller != null && permanent != null) { Integer amount = (Integer) getValue("rolled"); if (amount != null) { - permanent.removeCounters(CounterType.P1P1.createInstance(permanent.getCounters(game).getCount(CounterType.P1P1)), source, game); + permanent.removeAllCounters(CounterType.P1P1.getName(), source, game); if (amount > 0) { permanent.addCounters(CounterType.P1P1.createInstance(amount), source.getControllerId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/h/Hankyu.java b/Mage.Sets/src/mage/cards/h/Hankyu.java index 0738177750b..747dd9347a9 100644 --- a/Mage.Sets/src/mage/cards/h/Hankyu.java +++ b/Mage.Sets/src/mage/cards/h/Hankyu.java @@ -149,10 +149,8 @@ class HankyuCost extends UseAttachedCost { if (equipment == null) { continue; } - int count = equipment.getCounters(game).getCount(CounterType.AIM); - equipment.removeCounters(CounterType.AIM.createInstance(count), source, game); + removedCounters = equipment.removeAllCounters(CounterType.AIM.getName(), source, game); paid = true; - removedCounters = count; break; } diff --git a/Mage.Sets/src/mage/cards/h/HapatrasMark.java b/Mage.Sets/src/mage/cards/h/HapatrasMark.java index b5251543924..65a6051f28e 100644 --- a/Mage.Sets/src/mage/cards/h/HapatrasMark.java +++ b/Mage.Sets/src/mage/cards/h/HapatrasMark.java @@ -2,8 +2,9 @@ package mage.cards.h; import java.util.UUID; + import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; -import mage.abilities.effects.common.counter.RemoveAllCountersTargetEffect; +import mage.abilities.effects.common.counter.RemoveAllCountersPermanentTargetEffect; import mage.abilities.keyword.HexproofAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -13,7 +14,6 @@ import mage.counters.CounterType; import mage.target.common.TargetControlledCreaturePermanent; /** - * * @author fireshoes */ public final class HapatrasMark extends CardImpl { @@ -23,7 +23,7 @@ public final class HapatrasMark extends CardImpl { // Target creature you control gains hexproof until end of turn. Remove all -1/-1 counters from it. getSpellAbility().addEffect(new GainAbilityTargetEffect(HexproofAbility.getInstance(), Duration.EndOfTurn)); - getSpellAbility().addEffect(new RemoveAllCountersTargetEffect(CounterType.M1M1)); + getSpellAbility().addEffect(new RemoveAllCountersPermanentTargetEffect(CounterType.M1M1)); getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/h/HarnessedLightning.java b/Mage.Sets/src/mage/cards/h/HarnessedLightning.java index c38dd4e6ed6..baf4f978f17 100644 --- a/Mage.Sets/src/mage/cards/h/HarnessedLightning.java +++ b/Mage.Sets/src/mage/cards/h/HarnessedLightning.java @@ -62,7 +62,7 @@ class HarnessedLightningEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { new GetEnergyCountersControllerEffect(3).apply(game, source); - int numberToPay = controller.getAmount(0, controller.getCounters().getCount(CounterType.ENERGY), "How many {E} do you like to pay?", game); + int numberToPay = controller.getAmount(0, controller.getCountersCount(CounterType.ENERGY), "How many {E} do you like to pay?", game); if (numberToPay > 0) { Cost cost = new PayEnergyCost(numberToPay); if (cost.pay(source, game, source, source.getControllerId(), true)) { diff --git a/Mage.Sets/src/mage/cards/i/IxhelScionOfAtraxa.java b/Mage.Sets/src/mage/cards/i/IxhelScionOfAtraxa.java index 209bcbefdd2..01417a703df 100644 --- a/Mage.Sets/src/mage/cards/i/IxhelScionOfAtraxa.java +++ b/Mage.Sets/src/mage/cards/i/IxhelScionOfAtraxa.java @@ -79,7 +79,7 @@ class IxhelScionOfAtraxaEffect extends OneShotEffect { Cards cards = new CardsImpl(); for (UUID opponentId : game.getOpponents(source.getControllerId(), true)) { Player opponent = game.getPlayer(opponentId); - if (opponent == null || opponent.getCounters().getCount(CounterType.POISON) < 3 || !opponent.getLibrary().hasCards()) { + if (opponent == null || opponent.getCountersCount(CounterType.POISON) < 3 || !opponent.getLibrary().hasCards()) { continue; } Card card = opponent.getLibrary().getFromTop(game); diff --git a/Mage.Sets/src/mage/cards/i/IzzetGeneratorium.java b/Mage.Sets/src/mage/cards/i/IzzetGeneratorium.java new file mode 100644 index 00000000000..437928cd840 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IzzetGeneratorium.java @@ -0,0 +1,170 @@ +package mage.cards.i; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.IntCompareCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author Susucr + */ +public final class IzzetGeneratorium extends CardImpl { + + private static final Condition condition = new IzzetGeneratoriumCondition(); + private static final Hint hint = new ValueHint("{E} paid or lost this turn", IzzetGeneratoriumValue.instance); + + public IzzetGeneratorium(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{U}{R}"); + + // If you would get one or more {E}, you get that many plus one {E} instead. + this.addAbility(new SimpleStaticAbility(new IzzetGeneratoriumReplacementEffect())); + + // {T}: Draw a card. Activate only if you've paid or lost four or more {E} this turn. + this.addAbility(new ActivateIfConditionActivatedAbility( + new DrawCardSourceControllerEffect(1), + new TapSourceCost(), + condition + ).addHint(hint), new IzzetGeneratoriumWatcher()); + } + + private IzzetGeneratorium(final IzzetGeneratorium card) { + super(card); + } + + @Override + public IzzetGeneratorium copy() { + return new IzzetGeneratorium(this); + } +} + +class IzzetGeneratoriumReplacementEffect extends ReplacementEffectImpl { + + IzzetGeneratoriumReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "if you would get one or more {E}, you get that many plus one {E} instead"; + } + + private IzzetGeneratoriumReplacementEffect(final IzzetGeneratoriumReplacementEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmountForCounters(CardUtil.overflowInc(event.getAmount(), 1), true); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ADD_COUNTERS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getTargetId()) + && CounterType.ENERGY.getName().equals(event.getData()) + && event.getAmount() > 0; + } + + @Override + public IzzetGeneratoriumReplacementEffect copy() { + return new IzzetGeneratoriumReplacementEffect(this); + } +} + +class IzzetGeneratoriumCondition extends IntCompareCondition { + + IzzetGeneratoriumCondition() { + super(ComparisonType.OR_GREATER, 4); + } + + @Override + protected int getInputValue(Game game, Ability source) { + return IzzetGeneratoriumValue.instance.calculate(game, source, null); + } + + @Override + public String toString() { + return "if you've paid or lost four or more {E} this turn"; + } +} + +enum IzzetGeneratoriumValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return IzzetGeneratoriumWatcher.getAmountEnergyLostOrSpentThisTurn(game, sourceAbility.getControllerId()); + } + + @Override + public IzzetGeneratoriumValue copy() { + return this; + } + + @Override + public String getMessage() { + return "{E} spent or lost this turn"; + } + + @Override + public String toString() { + return "X"; + } +} + +class IzzetGeneratoriumWatcher extends Watcher { + + // player -> amount of energy spent or lost this turn + private final Map energyLostOrSpent = new HashMap<>(); + + IzzetGeneratoriumWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.COUNTERS_REMOVED) { + return; + } + if (!event.getData().equals(CounterType.ENERGY.getName())) { + return; + } + int amount = event.getAmount(); + if (amount <= 0) { + return; + } + energyLostOrSpent.compute(event.getTargetId(), (k, i) -> i == null ? amount : Integer.sum(i, amount)); + } + + @Override + public void reset() { + super.reset(); + energyLostOrSpent.clear(); + } + + public static int getAmountEnergyLostOrSpentThisTurn(Game game, UUID playerId) { + IzzetGeneratoriumWatcher watcher = game.getState().getWatcher(IzzetGeneratoriumWatcher.class); + return watcher == null ? 0 : watcher.energyLostOrSpent.getOrDefault(playerId, 0); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/j/JarOfEyeballs.java b/Mage.Sets/src/mage/cards/j/JarOfEyeballs.java index 2d7a8242d2a..e101ffd2c8e 100644 --- a/Mage.Sets/src/mage/cards/j/JarOfEyeballs.java +++ b/Mage.Sets/src/mage/cards/j/JarOfEyeballs.java @@ -35,9 +35,7 @@ public final class JarOfEyeballs extends CardImpl { false, StaticFilters.FILTER_CONTROLLED_A_CREATURE )); - // {3}, {tap}, Remove all eyeball counters from Jar of Eyeballs: - // Look at the top X cards of your library, where X is the number of eyeball counters removed this way. - // Put one of them into your hand and the rest on the bottom of your library in any order. + // {3}, {tap}, Remove all eyeball counters from Jar of Eyeballs: Look at the top X cards of your library, where X is the number of eyeball counters removed this way. Put one of them into your hand and the rest on the bottom of your library in any order. SimpleActivatedAbility ability = new SimpleActivatedAbility( new LookLibraryAndPickControllerEffect(JarOfEyeballsValue.instance, 1, PutCards.HAND, PutCards.BOTTOM_ANY), new GenericManaCost(3)); diff --git a/Mage.Sets/src/mage/cards/l/Leeches.java b/Mage.Sets/src/mage/cards/l/Leeches.java index 45d676d153d..705441d1fab 100644 --- a/Mage.Sets/src/mage/cards/l/Leeches.java +++ b/Mage.Sets/src/mage/cards/l/Leeches.java @@ -60,9 +60,9 @@ class LeechesEffect extends OneShotEffect { return false; } - int countPoisonCounters = targetPlayer.getCounters().getCount(CounterType.POISON); + int countPoisonCounters = targetPlayer.getCountersCount(CounterType.POISON); if (countPoisonCounters > 0) { - targetPlayer.removeCounters(CounterType.POISON.getName(), countPoisonCounters, source, game); + targetPlayer.loseAllCounters(CounterType.POISON.getName(), source, game); targetPlayer.damage(countPoisonCounters, source.getSourceId(), source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/l/LesserWerewolf.java b/Mage.Sets/src/mage/cards/l/LesserWerewolf.java index 5b0d51b003f..25f4f90f75f 100644 --- a/Mage.Sets/src/mage/cards/l/LesserWerewolf.java +++ b/Mage.Sets/src/mage/cards/l/LesserWerewolf.java @@ -11,7 +11,7 @@ import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.counters.BoostCounter; +import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.BlockingOrBlockedBySourcePredicate; @@ -85,7 +85,7 @@ class LesserWerewolfEffect extends OneShotEffect { } if (sourcePermanent.getPower().getValue() >= 1) { game.addEffect(new BoostSourceEffect(-1, 0, Duration.EndOfTurn), source); - new AddCountersTargetEffect(new BoostCounter(0, -1), outcome).apply(game, source); + new AddCountersTargetEffect(CounterType.M0M1.createInstance(), outcome).apply(game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/l/LightningCoils.java b/Mage.Sets/src/mage/cards/l/LightningCoils.java index 81378d99a70..6220b2923be 100644 --- a/Mage.Sets/src/mage/cards/l/LightningCoils.java +++ b/Mage.Sets/src/mage/cards/l/LightningCoils.java @@ -34,9 +34,7 @@ public final class LightningCoils extends CardImpl { new AddCountersSourceEffect(CounterType.CHARGE.createInstance(), true), false, StaticFilters.FILTER_CONTROLLED_CREATURE_NON_TOKEN)); - // At the beginning of your upkeep, if Lightning Coils has five or more charge counters on it, remove all of them from it - // and put that many 3/1 red Elemental creature tokens with haste onto the battlefield. - // Exile them at the beginning of the next end step. + // At the beginning of your upkeep, if Lightning Coils has five or more charge counters on it, remove all of them from it and put that many 3/1 red Elemental creature tokens with haste onto the battlefield. Exile them at the beginning of the next end step. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new LightningCoilsEffect(), TargetController.YOU, false)); } @@ -64,12 +62,12 @@ class LightningCoilsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent p = game.getPermanent(source.getSourceId()); - if (p != null && controller != null) { - int counters = p.getCounters(game).getCount(CounterType.CHARGE); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null && controller != null) { + int counters = permanent.getCounters(game).getCount(CounterType.CHARGE); if (counters >= 5) { // remove all the counters and create that many tokens - p.removeCounters(CounterType.CHARGE.getName(), p.getCounters(game).getCount(CounterType.CHARGE), source, game); + permanent.removeAllCounters(CounterType.CHARGE.getName(), source, game); CreateTokenEffect effect = new CreateTokenEffect(new ElementalTokenWithHaste(), counters); effect.apply(game, source); diff --git a/Mage.Sets/src/mage/cards/l/LightningRunner.java b/Mage.Sets/src/mage/cards/l/LightningRunner.java index bbe15e87b4c..1c97f3afb83 100644 --- a/Mage.Sets/src/mage/cards/l/LightningRunner.java +++ b/Mage.Sets/src/mage/cards/l/LightningRunner.java @@ -1,6 +1,5 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -15,15 +14,16 @@ import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author fireshoes */ public final class LightningRunner extends CardImpl { @@ -74,7 +74,7 @@ class LightningRunnerEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { new GetEnergyCountersControllerEffect(2).apply(game, source); - if (controller.getCounters().getCount(CounterType.ENERGY) > 7) { + if (controller.getCountersCount(CounterType.ENERGY) > 7) { Cost cost = new PayEnergyCost(8); if (controller.chooseUse(outcome, "Pay {E}{E}{E}{E}{E}{E}{E}{E} to use this? ", diff --git a/Mage.Sets/src/mage/cards/l/LilyBowenRagingGrandma.java b/Mage.Sets/src/mage/cards/l/LilyBowenRagingGrandma.java index 0441dfde7e9..cdb10cf8743 100644 --- a/Mage.Sets/src/mage/cards/l/LilyBowenRagingGrandma.java +++ b/Mage.Sets/src/mage/cards/l/LilyBowenRagingGrandma.java @@ -1,7 +1,5 @@ package mage.cards.l; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -12,16 +10,18 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DoubleCountersSourceEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.constants.*; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** * @author Cguy7777 */ @@ -51,8 +51,7 @@ public final class LilyBowenRagingGrandma extends CardImpl { CounterType.P1P1.createInstance(2)), "with two +1/+1 counters on it")); - // At the beginning of your upkeep, double the number of +1/+1 counters on Lily Bowen if its power is 16 or less. - // Otherwise, remove all but one +1/+1 counter from it, then you gain 1 life for each +1/+1 counter removed this way. + // At the beginning of your upkeep, double the number of +1/+1 counters on Lily Bowen if its power is 16 or less. Otherwise, remove all but one +1/+1 counter from it, then you gain 1 life for each +1/+1 counter removed this way. this.addAbility(new BeginningOfUpkeepTriggeredAbility( new ConditionalOneShotEffect( new DoubleCountersSourceEffect(CounterType.P1P1), @@ -93,14 +92,18 @@ class LilyBowenRagingGrandmaEffect extends OneShotEffect { } // Remove all but one +1/+1 counter from it, then you gain 1 life for each +1/+1 counter removed this way. - int count = permanent.getCounters(game).getCount(CounterType.P1P1); - if (count <= 1) { + int countBefore = permanent.getCounters(game).getCount(CounterType.P1P1); + if (countBefore <= 1) { return true; } - int countToRemove = count - 1; + int countToRemove = countBefore - 1; permanent.removeCounters(CounterType.P1P1.createInstance(countToRemove), source, game); - new GainLifeEffect(countToRemove).apply(game, source); + int countAfter = permanent.getCounters(game).getCount(CounterType.P1P1); + int countersRemoved = Math.max(0, countBefore - countAfter); + if (countersRemoved > 0) { + new GainLifeEffect(countersRemoved).apply(game, source); + } return true; } diff --git a/Mage.Sets/src/mage/cards/l/LivingArmor.java b/Mage.Sets/src/mage/cards/l/LivingArmor.java index 4e38d3ba87e..7d6ebcca862 100644 --- a/Mage.Sets/src/mage/cards/l/LivingArmor.java +++ b/Mage.Sets/src/mage/cards/l/LivingArmor.java @@ -1,7 +1,6 @@ package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -12,19 +11,20 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.counters.BoostCounter; +import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Derpthemeus */ public final class LivingArmor extends CardImpl { public LivingArmor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // {tap}, Sacrifice Living Armor: Put X +0/+1 counters on target creature, where X is that creature's converted mana cost. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LivingArmorEffect(), new TapSourceCost()); @@ -63,7 +63,7 @@ public final class LivingArmor extends CardImpl { Permanent creature = game.getPermanent(source.getTargets().getFirstTarget()); if (creature != null) { int amount = creature.getManaValue(); - creature.addCounters(new BoostCounter(0, 1, amount), source.getControllerId(), source, game); + creature.addCounters(CounterType.P0P1.createInstance(amount), source.getControllerId(), source, game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/l/LudevicsTestSubject.java b/Mage.Sets/src/mage/cards/l/LudevicsTestSubject.java index 1c51a253af2..794967ffa17 100644 --- a/Mage.Sets/src/mage/cards/l/LudevicsTestSubject.java +++ b/Mage.Sets/src/mage/cards/l/LudevicsTestSubject.java @@ -1,7 +1,6 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -14,21 +13,22 @@ import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author Loki */ public final class LudevicsTestSubject extends CardImpl { public LudevicsTestSubject(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.LIZARD, SubType.EGG); this.power = new MageInt(0); @@ -67,13 +67,15 @@ class LudevicsTestSubjectEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent p = game.getPermanent(source.getSourceId()); - if (p != null) { - if (p.getCounters(game).getCount(CounterType.HATCHLING) >= 5) { - p.removeCounters(CounterType.HATCHLING.getName(), p.getCounters(game).getCount(CounterType.HATCHLING), source, game); - TransformSourceEffect effect = new TransformSourceEffect(); - return effect.apply(game, source); - } + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + return false; + } + + if (permanent.getCounters(game).getCount(CounterType.HATCHLING) >= 5) { + permanent.removeAllCounters(CounterType.HATCHLING.getName(), source, game); + TransformSourceEffect effect = new TransformSourceEffect(); + return effect.apply(game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/l/LumberingMegasloth.java b/Mage.Sets/src/mage/cards/l/LumberingMegasloth.java index e332e7b1ba2..008e902f52a 100644 --- a/Mage.Sets/src/mage/cards/l/LumberingMegasloth.java +++ b/Mage.Sets/src/mage/cards/l/LumberingMegasloth.java @@ -82,9 +82,7 @@ enum LumberingMegaslothValue implements DynamicValue { .stream() .map(game::getPlayer) .filter(Objects::nonNull) - .map(Player::getCounters) - .flatMap(counters -> counters.values().stream()) - .mapToInt(Counter::getCount) + .mapToInt(Player::getCountersTotalCount) .sum(); return onPermanents + onPlayers; } diff --git a/Mage.Sets/src/mage/cards/m/MarchesaResoluteMonarch.java b/Mage.Sets/src/mage/cards/m/MarchesaResoluteMonarch.java index dad2301eb78..92c9ee1fec4 100644 --- a/Mage.Sets/src/mage/cards/m/MarchesaResoluteMonarch.java +++ b/Mage.Sets/src/mage/cards/m/MarchesaResoluteMonarch.java @@ -5,9 +5,9 @@ import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.effects.common.counter.RemoveAllCountersPermanentTargetEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.MenaceAbility; import mage.cards.CardImpl; @@ -17,11 +17,9 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.watchers.Watcher; -import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import java.util.UUID; @@ -49,7 +47,7 @@ public final class MarchesaResoluteMonarch extends CardImpl { this.addAbility(DeathtouchAbility.getInstance()); // Whenever Marchesa, Resolute Monarch attacks, remove all counters from up to one target permanent. - Ability ability = new AttacksTriggeredAbility(new MarchesaResoluteMonarchEffect()); + Ability ability = new AttacksTriggeredAbility(new RemoveAllCountersPermanentTargetEffect()); ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_PERMANENT)); this.addAbility(ability); @@ -78,35 +76,6 @@ public final class MarchesaResoluteMonarch extends CardImpl { } } -class MarchesaResoluteMonarchEffect extends OneShotEffect { - - MarchesaResoluteMonarchEffect() { - super(Outcome.Benefit); - staticText = "remove all counters from up to one target permanent"; - } - - private MarchesaResoluteMonarchEffect(final MarchesaResoluteMonarchEffect effect) { - super(effect); - } - - @Override - public MarchesaResoluteMonarchEffect copy() { - return new MarchesaResoluteMonarchEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (permanent == null) { - return false; - } - for (String counterType : new ArrayList<>(permanent.getCounters(game).keySet())) { - permanent.getCounters(game).removeAllCounters(counterType); - } - return true; - } -} - class MarchesaResoluteMonarchWatcher extends Watcher { private final Set players = new HashSet<>(); diff --git a/Mage.Sets/src/mage/cards/m/MaulfistRevolutionary.java b/Mage.Sets/src/mage/cards/m/MaulfistRevolutionary.java index 296f0db18d6..0614945e089 100644 --- a/Mage.Sets/src/mage/cards/m/MaulfistRevolutionary.java +++ b/Mage.Sets/src/mage/cards/m/MaulfistRevolutionary.java @@ -1,6 +1,5 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldOrDiesSourceTriggeredAbility; @@ -9,8 +8,8 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.Counter; import mage.counters.CounterType; import mage.counters.Counters; @@ -19,8 +18,9 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetPermanentOrPlayer; +import java.util.UUID; + /** - * * @author fireshoes */ public final class MaulfistRevolutionary extends CardImpl { @@ -70,7 +70,7 @@ class MaulfistRevolutionaryEffect extends OneShotEffect { if (controller != null) { Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); if (player != null) { - Counters counters = player.getCounters().copy(); + Counters counters = player.getCountersAsCopy(); for (Counter counter : counters.values()) { CounterType counterType = CounterType.findByName(counter.getName()); Counter counterToAdd; diff --git a/Mage.Sets/src/mage/cards/m/MerenOfClanNelToth.java b/Mage.Sets/src/mage/cards/m/MerenOfClanNelToth.java index 12ac00e7406..155d1b701b0 100644 --- a/Mage.Sets/src/mage/cards/m/MerenOfClanNelToth.java +++ b/Mage.Sets/src/mage/cards/m/MerenOfClanNelToth.java @@ -78,7 +78,7 @@ class MerenOfClanNelTothEffect extends OneShotEffect { if (player == null || card == null) { return false; } - boolean flag = card.getManaValue() <= player.getCounters().getCount(CounterType.EXPERIENCE); + boolean flag = card.getManaValue() <= player.getCountersCount(CounterType.EXPERIENCE); return player.moveCards(card, flag ? Zone.BATTLEFIELD : Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/m/MineLayer.java b/Mage.Sets/src/mage/cards/m/MineLayer.java index 605c7a27c73..6ce2331ff59 100644 --- a/Mage.Sets/src/mage/cards/m/MineLayer.java +++ b/Mage.Sets/src/mage/cards/m/MineLayer.java @@ -85,10 +85,7 @@ class RemoveAllMineCountersEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(CardType.LAND, game)) { if (permanent != null) { - int numToRemove = permanent.getCounters(game).getCount(CounterType.MINE); - if (numToRemove > 0) { - permanent.removeCounters(CounterType.MINE.getName(), numToRemove, source, game); - } + permanent.removeAllCounters(CounterType.MINE.getName(), source, game); } } return true; diff --git a/Mage.Sets/src/mage/cards/m/MizzixOfTheIzmagnus.java b/Mage.Sets/src/mage/cards/m/MizzixOfTheIzmagnus.java index 54856164bac..6ff0f984bec 100644 --- a/Mage.Sets/src/mage/cards/m/MizzixOfTheIzmagnus.java +++ b/Mage.Sets/src/mage/cards/m/MizzixOfTheIzmagnus.java @@ -69,7 +69,7 @@ class MizzixOfTheIzmagnusPredicate implements Predicate { if (spell != null) { Player controller = game.getPlayer(spell.getControllerId()); if (controller != null) { - if (spell.getManaValue() > controller.getCounters().getCount(CounterType.EXPERIENCE)) { + if (spell.getManaValue() > controller.getCountersCount(CounterType.EXPERIENCE)) { return true; } } @@ -99,7 +99,7 @@ class MizzixOfTheIzmagnusCostReductionEffect extends CostModificationEffectImpl Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { SpellAbility spellAbility = (SpellAbility) abilityToModify; - CardUtil.adjustCost(spellAbility, controller.getCounters().getCount(CounterType.EXPERIENCE)); + CardUtil.adjustCost(spellAbility, controller.getCountersCount(CounterType.EXPERIENCE)); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/n/NightkinAmbusher.java b/Mage.Sets/src/mage/cards/n/NightkinAmbusher.java index af6f559e950..33f5d25afd9 100644 --- a/Mage.Sets/src/mage/cards/n/NightkinAmbusher.java +++ b/Mage.Sets/src/mage/cards/n/NightkinAmbusher.java @@ -64,6 +64,6 @@ enum NightkinAmbusherCondition implements Condition { @Override public boolean apply(Game game, Ability source) { Player defendingPlayer = game.getPlayer(game.getCombat().getDefendingPlayerId(source.getSourceId(), game)); - return defendingPlayer != null && defendingPlayer.getCounters().getCount(CounterType.RAD) >= 1; + return defendingPlayer != null && defendingPlayer.getCountersCount(CounterType.RAD) >= 1; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/o/OblivionStone.java b/Mage.Sets/src/mage/cards/o/OblivionStone.java index 4f715138c5d..31420a3138b 100644 --- a/Mage.Sets/src/mage/cards/o/OblivionStone.java +++ b/Mage.Sets/src/mage/cards/o/OblivionStone.java @@ -1,7 +1,6 @@ package mage.cards.o; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -19,13 +18,15 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import java.util.UUID; + /** * @author Loki */ public final class OblivionStone extends CardImpl { public OblivionStone(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // {4}, {tap}: Put a fate counter on target permanent. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.FATE.createInstance()), new GenericManaCost(4)); @@ -62,15 +63,13 @@ class OblivionStoneEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - for (Permanent p : game.getBattlefield().getAllActivePermanents()) { - if (!(p.isLand(game) || p.getCounters(game).containsKey(CounterType.FATE))) { - p.destroy(source, game, false); + for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { + if (!(permanent.isLand(game) || permanent.getCounters(game).containsKey(CounterType.FATE))) { + permanent.destroy(source, game, false); } } - for (Permanent p : game.getBattlefield().getAllActivePermanents()) { - if (p.getCounters(game).containsKey(CounterType.FATE)) { - p.removeCounters(CounterType.FATE.getName(), p.getCounters(game).getCount(CounterType.FATE), source, game); - } + for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { + permanent.removeAllCounters(CounterType.FATE.getName(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/p/PhyresisOutbreak.java b/Mage.Sets/src/mage/cards/p/PhyresisOutbreak.java index 374c3c5d2a1..373791466f3 100644 --- a/Mage.Sets/src/mage/cards/p/PhyresisOutbreak.java +++ b/Mage.Sets/src/mage/cards/p/PhyresisOutbreak.java @@ -64,7 +64,7 @@ class PhyresisOutbreakEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { for (UUID opponentId : game.getOpponents(source.getControllerId(), true)) { - int totalPoison = game.getPlayer(opponentId).getCounters().getCount(CounterType.POISON); + int totalPoison = game.getPlayer(opponentId).getCountersCount(CounterType.POISON); BoostTargetEffect effect = new BoostTargetEffect(totalPoison * -1, totalPoison * -1, Duration.EndOfTurn); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_A_CREATURE, opponentId, game)) { effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianAtlas.java b/Mage.Sets/src/mage/cards/p/PhyrexianAtlas.java index 20671845947..4286e6c645c 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianAtlas.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianAtlas.java @@ -60,7 +60,7 @@ class PhyrexianAtlasEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { for (UUID playerId : game.getOpponents(source.getControllerId())) { Player player = game.getPlayer(playerId); - if (player != null && player.getCounters().getCount(CounterType.POISON) >= 3) { + if (player != null && player.getCountersCount(CounterType.POISON) >= 3) { player.loseLife(1, game, source, false); } } diff --git a/Mage.Sets/src/mage/cards/p/PriceOfBetrayal.java b/Mage.Sets/src/mage/cards/p/PriceOfBetrayal.java index 5bec20e5d4a..bf0d5d6285f 100644 --- a/Mage.Sets/src/mage/cards/p/PriceOfBetrayal.java +++ b/Mage.Sets/src/mage/cards/p/PriceOfBetrayal.java @@ -6,6 +6,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.counters.Counter; import mage.filter.FilterOpponent; import mage.filter.FilterPermanent; import mage.filter.common.FilterPermanentOrPlayer; @@ -106,17 +107,17 @@ class PriceOfBetrayalEffect extends OneShotEffect { if (player != null) { int toRemove = 5; int removed = 0; - String[] counterNames = player.getCounters().keySet().toArray(new String[0]); - for (String counterName : counterNames) { + for (Counter counter : player.getCountersAsCopy().values()) { + String counterName = counter.getName(); if (controller.chooseUse(Outcome.Neutral, "Remove " + counterName + " counters?", source, game)) { - if (player.getCounters().get(counterName).getCount() == 1 || (toRemove - removed == 1)) { - player.removeCounters(counterName, 1, source, game); + if (player.getCountersCount(counterName) == 1 || (toRemove - removed == 1)) { + player.loseCounters(counterName, 1, source, game); removed++; } else { - int amount = controller.getAmount(1, Math.min(player.getCounters().get(counterName).getCount(), toRemove - removed), "How many?", game); + int amount = controller.getAmount(1, Math.min(player.getCountersCount(counterName), toRemove - removed), "How many?", game); if (amount > 0) { removed += amount; - player.removeCounters(counterName, amount, source, game); + player.loseCounters(counterName, amount, source, game); } } } diff --git a/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java b/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java index c2b4cfad396..16ba6c83c04 100644 --- a/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java +++ b/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java @@ -28,22 +28,20 @@ import java.util.UUID; */ public final class QuicksilverFountain extends CardImpl { + private static final Condition condition = new AllLandsAreSubtypeCondition(SubType.ISLAND); + public QuicksilverFountain(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); - // At the beginning of each player's upkeep, that player puts a flood - // counter on target non-Island land they control of their choice. - // That land is an Island for as long as it has a flood counter on it. + // At the beginning of each player's upkeep, that player puts a flood counter on target non-Island land they control of their choice. That land is an Island for as long as it has a flood counter on it. Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new QuicksilverFountainEffect(), TargetController.ANY, false, true); ability.addTarget(new TargetLandPermanent()); ability.setTargetAdjuster(QuicksilverFountainAdjuster.instance); this.addAbility(ability); - // At the beginning of each end step, if all lands on the battlefield are - // Islands, remove all flood counters from them. + // At the beginning of each end step, if all lands on the battlefield are Islands, remove all flood counters from them. // Note: This applies only if Quicksilver Fountain is on the battlefield - Condition condition = new AllLandsAreSubtypeCondition(SubType.ISLAND); this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new QuicksilverFountainEffect2(), TargetController.ANY, condition, false)); } @@ -130,7 +128,7 @@ class QuicksilverFountainEffect2 extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { for (Permanent land : game.getBattlefield().getAllActivePermanents(CardType.LAND, game)) { - land.removeCounters(CounterType.FLOOD.createInstance(land.getCounters(game).getCount(CounterType.FLOOD)), source, game); + land.removeAllCounters(CounterType.FLOOD.getName(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/r/ReplicatingRing.java b/Mage.Sets/src/mage/cards/r/ReplicatingRing.java index 73d50c7e71c..ba44d82e025 100644 --- a/Mage.Sets/src/mage/cards/r/ReplicatingRing.java +++ b/Mage.Sets/src/mage/cards/r/ReplicatingRing.java @@ -74,9 +74,7 @@ class ReplicatingRingEffect extends OneShotEffect { if (permanent == null || permanent.getCounters(game).getCount(CounterType.NIGHT) < 8) { return true; } - permanent.removeCounters(CounterType.NIGHT.createInstance( - permanent.getCounters(game).getCount(CounterType.NIGHT) - ),source,game); + permanent.removeAllCounters(CounterType.NIGHT.getName(), source, game); new ReplicatedRingToken().putOntoBattlefield(8, game, source, source.getControllerId()); return true; } diff --git a/Mage.Sets/src/mage/cards/r/RogueSkycaptain.java b/Mage.Sets/src/mage/cards/r/RogueSkycaptain.java index f662447c233..14903c91361 100644 --- a/Mage.Sets/src/mage/cards/r/RogueSkycaptain.java +++ b/Mage.Sets/src/mage/cards/r/RogueSkycaptain.java @@ -41,9 +41,7 @@ public class RogueSkycaptain extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // At the beginning of your upkeep, put a wage counter on Rogue Skycaptain. You - // may pay 2 for each wage counter on it. If you don't, remove all wage counters - // from Rogue Skycaptain and an opponent gains control of it. + // At the beginning of your upkeep, put a wage counter on Rogue Skycaptain. You may pay 2 for each wage counter on it. If you don't, remove all wage counters from Rogue Skycaptain and an opponent gains control of it. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new RogueSkycaptainEffect(), TargetController.YOU, false)); } @@ -62,7 +60,8 @@ class RogueSkycaptainEffect extends OneShotEffect { RogueSkycaptainEffect() { super(Outcome.GainControl); - staticText = "put a wage counter on {this}. You may pay {2} for each wage counter on it. If you don't, remove all wage counters from {this} and an opponent gains control of it"; + staticText = "put a wage counter on {this}. You may pay {2} for each wage counter on it. " + + "If you don't, remove all wage counters from {this} and an opponent gains control of it"; } private RogueSkycaptainEffect(final RogueSkycaptainEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SarulfRealmEater.java b/Mage.Sets/src/mage/cards/s/SarulfRealmEater.java index c8505911dd4..f6c766feb02 100644 --- a/Mage.Sets/src/mage/cards/s/SarulfRealmEater.java +++ b/Mage.Sets/src/mage/cards/s/SarulfRealmEater.java @@ -96,10 +96,9 @@ class SarulfRealmEaterEffect extends OneShotEffect { if (player == null || permanent == null) { return false; } - int counterCount = permanent.getCounters(game).getCount(CounterType.P1P1); - permanent.removeCounters(CounterType.P1P1.createInstance(counterCount), source, game); + int removedThisWay = permanent.removeAllCounters(CounterType.P1P1.getName(), source, game); FilterPermanent filter = new FilterNonlandPermanent(); - filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, counterCount + 1)); + filter.add(new ManaValuePredicate(ComparisonType.OR_LESS, removedThisWay)); filter.add(AnotherPredicate.instance); Cards cards = new CardsImpl(); game.getBattlefield() diff --git a/Mage.Sets/src/mage/cards/s/SepticRats.java b/Mage.Sets/src/mage/cards/s/SepticRats.java index 74cfe79d7e9..7581d8a16ee 100644 --- a/Mage.Sets/src/mage/cards/s/SepticRats.java +++ b/Mage.Sets/src/mage/cards/s/SepticRats.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -9,23 +8,23 @@ import mage.abilities.keyword.InfectAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.players.Player; +import java.util.UUID; + /** - * * @author Loki */ public final class SepticRats extends CardImpl { public SepticRats(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); this.subtype.add(SubType.PHYREXIAN); this.subtype.add(SubType.RAT); @@ -67,10 +66,10 @@ class SepticRatsTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.getSourceId()) ) { + if (event.getSourceId().equals(this.getSourceId())) { Player target = game.getPlayer(event.getTargetId()); if (target != null) { - if (target.getCounters().getCount(CounterType.POISON) > 0) { + if (target.getCountersCount(CounterType.POISON) > 0) { return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SkyshipPlunderer.java b/Mage.Sets/src/mage/cards/s/SkyshipPlunderer.java index af43f046d7e..2678b79ed97 100644 --- a/Mage.Sets/src/mage/cards/s/SkyshipPlunderer.java +++ b/Mage.Sets/src/mage/cards/s/SkyshipPlunderer.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; @@ -9,8 +8,8 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.Counter; import mage.counters.CounterType; import mage.counters.Counters; @@ -19,8 +18,9 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetPermanentOrPlayer; +import java.util.UUID; + /** - * * @author Styxo */ public final class SkyshipPlunderer extends CardImpl { @@ -69,7 +69,7 @@ class SkyshipPlundererEffect extends OneShotEffect { if (controller != null) { Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); if (player != null) { - Counters counters = player.getCounters().copy(); + Counters counters = player.getCountersAsCopy(); for (Counter counter : counters.values()) { CounterType counterType = CounterType.findByName(counter.getName()); Counter counterToAdd; diff --git a/Mage.Sets/src/mage/cards/s/Sporogenesis.java b/Mage.Sets/src/mage/cards/s/Sporogenesis.java index 5d0c18be527..21a186f9dca 100644 --- a/Mage.Sets/src/mage/cards/s/Sporogenesis.java +++ b/Mage.Sets/src/mage/cards/s/Sporogenesis.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -27,8 +26,9 @@ import mage.game.permanent.token.SaprolingToken; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author L_J */ public final class Sporogenesis extends CardImpl { @@ -152,7 +152,7 @@ class SporogenesisRemoveCountersEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(CardType.CREATURE, game)) { - permanent.removeCounters(CounterType.FUNGUS.createInstance(permanent.getCounters(game).getCount(CounterType.FUNGUS)), source, game); + permanent.removeAllCounters(CounterType.FUNGUS.getName(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/s/Suncleanser.java b/Mage.Sets/src/mage/cards/s/Suncleanser.java index aa542ae8887..a0eff681763 100644 --- a/Mage.Sets/src/mage/cards/s/Suncleanser.java +++ b/Mage.Sets/src/mage/cards/s/Suncleanser.java @@ -1,19 +1,18 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.OneShotEffect; -import mage.constants.SubType; +import mage.abilities.effects.common.counter.RemoveAllCountersPermanentTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.counters.Counter; +import mage.constants.SubType; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -21,8 +20,9 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class Suncleanser extends CardImpl { @@ -38,13 +38,13 @@ public final class Suncleanser extends CardImpl { // When Suncleanser enters the battlefield, choose one — // • Remove all counters from target creature. It can't have counters put on it for as long as Suncleanser remains on the battlefield. Ability ability = new EntersBattlefieldTriggeredAbility( - new SuncleanserRemoveCountersEffect(false), false + new RemoveAllCountersPermanentTargetEffect(), false ); ability.addEffect(new SuncleanserPreventCountersEffect(false)); ability.addTarget(new TargetCreaturePermanent()); // • Target opponent loses all counters. That player can't get counters for as long as Suncleanser remains on the battlefield. - Mode mode = new Mode(new SuncleanserRemoveCountersEffect(true)); + Mode mode = new Mode(new SuncleanserRemoveCountersPlayerEffect()); mode.addEffect(new SuncleanserPreventCountersEffect(true)); mode.addTarget(new TargetOpponent()); ability.addMode(mode); @@ -61,45 +61,31 @@ public final class Suncleanser extends CardImpl { } } -class SuncleanserRemoveCountersEffect extends OneShotEffect { +class SuncleanserRemoveCountersPlayerEffect extends OneShotEffect { - SuncleanserRemoveCountersEffect(boolean player) { - super(Outcome.Benefit); - if (player) { - staticText = "Target opponent loses all counters"; - } else { - staticText = "Remove all counters from target creature"; - } + SuncleanserRemoveCountersPlayerEffect() { + super(Outcome.Detriment); + staticText = "Target opponent loses all counters"; } - private SuncleanserRemoveCountersEffect(final SuncleanserRemoveCountersEffect effect) { + private SuncleanserRemoveCountersPlayerEffect(final SuncleanserRemoveCountersPlayerEffect effect) { super(effect); } @Override - public SuncleanserRemoveCountersEffect copy() { - return new SuncleanserRemoveCountersEffect(this); + public SuncleanserRemoveCountersPlayerEffect copy() { + return new SuncleanserRemoveCountersPlayerEffect(this); } @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (permanent != null) { - for (Counter counter : permanent.getCounters(game).copy().values()) { // copy to prevent ConcurrentModificationException - permanent.removeCounters(counter, source, game); - } - return true; - } Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (player != null) { - for (Counter counter : player.getCounters().copy().values()) { // copy to prevent ConcurrentModificationException - player.removeCounters(counter.getName(), counter.getCount(), source, game); - } - return true; + if (player == null) { + return false; } - return false; + player.loseAllCounters(source, game); + return true; } - } class SuncleanserPreventCountersEffect extends ContinuousRuleModifyingEffectImpl { diff --git a/Mage.Sets/src/mage/cards/s/SurvivorsMedKit.java b/Mage.Sets/src/mage/cards/s/SurvivorsMedKit.java index e42f8f634c1..f6a026a28e1 100644 --- a/Mage.Sets/src/mage/cards/s/SurvivorsMedKit.java +++ b/Mage.Sets/src/mage/cards/s/SurvivorsMedKit.java @@ -79,11 +79,7 @@ class SurvivorsMedKitEffect extends OneShotEffect { if (player == null) { return false; } - int count = player.getCounters().getCount(CounterType.RAD); - if (count > 0) { - player.removeCounters("rad", count, source, game); - return true; - } - return false; + player.loseAllCounters(CounterType.RAD.getName(), source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/t/TemporalDistortion.java b/Mage.Sets/src/mage/cards/t/TemporalDistortion.java index bbbd7863a5e..771ffcdd748 100644 --- a/Mage.Sets/src/mage/cards/t/TemporalDistortion.java +++ b/Mage.Sets/src/mage/cards/t/TemporalDistortion.java @@ -1,6 +1,5 @@ package mage.cards.t; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BecomesTappedTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -21,8 +20,9 @@ import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class TemporalDistortion extends CardImpl { @@ -74,7 +74,7 @@ class TemporalDistortionRemovalEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(game.getActivePlayerId())) { - permanent.removeCounters(CounterType.HOURGLASS.createInstance(permanent.getCounters(game).getCount(CounterType.HOURGLASS)), source, game); + permanent.removeAllCounters(CounterType.HOURGLASS.getName(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/t/ThiefOfBlood.java b/Mage.Sets/src/mage/cards/t/ThiefOfBlood.java index 409b50f5504..fd6678367df 100644 --- a/Mage.Sets/src/mage/cards/t/ThiefOfBlood.java +++ b/Mage.Sets/src/mage/cards/t/ThiefOfBlood.java @@ -10,9 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.counters.Counter; import mage.counters.CounterType; -import mage.counters.Counters; import mage.filter.FilterPermanent; import mage.filter.predicate.permanent.CounterAnyPredicate; import mage.game.Game; @@ -74,11 +72,7 @@ class ThiefOfBloodEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { int countersRemoved = 0; for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { - Counters counters = permanent.getCounters(game).copy(); - for (Counter counter : counters.values()) { - permanent.removeCounters(counter.getName(), counter.getCount(), source, game); - countersRemoved += counter.getCount(); - } + countersRemoved += permanent.removeAllCounters(source, game); } if (countersRemoved > 0) { Permanent sourcePermanent = game.getPermanentEntering(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/v/VampireHexmage.java b/Mage.Sets/src/mage/cards/v/VampireHexmage.java index b4a5bbfdb23..565802094f4 100644 --- a/Mage.Sets/src/mage/cards/v/VampireHexmage.java +++ b/Mage.Sets/src/mage/cards/v/VampireHexmage.java @@ -1,26 +1,21 @@ package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.RemoveAllCountersPermanentTargetEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.counters.Counter; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author Loki, nantuko */ public final class VampireHexmage extends CardImpl { @@ -35,9 +30,13 @@ public final class VampireHexmage extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); - SimpleActivatedAbility vampireHexmageAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new VampireHexmageEffect(), new SacrificeSourceCost()); - vampireHexmageAbility.addTarget(new TargetPermanent()); - this.addAbility(vampireHexmageAbility); + // Sacrifice Vampire Hexmage: Remove all counters from target permanent. + Ability ability = new SimpleActivatedAbility( + new RemoveAllCountersPermanentTargetEffect(), + new SacrificeSourceCost() + ); + ability.addTarget(new TargetPermanent()); + this.addAbility(ability); } private VampireHexmage(final VampireHexmage card) { @@ -48,34 +47,4 @@ public final class VampireHexmage extends CardImpl { public VampireHexmage copy() { return new VampireHexmage(this); } -} - -class VampireHexmageEffect extends OneShotEffect { - - VampireHexmageEffect() { - super(Outcome.Benefit); - staticText = "Remove all counters from target permanent"; - } - - private VampireHexmageEffect(final VampireHexmageEffect effect) { - super(effect); - } - - @Override - public VampireHexmageEffect copy() { - return new VampireHexmageEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (permanent != null) { - for (Counter counter : permanent.getCounters(game).copy().values()) { // copy to prevent ConcurrentModificationException - permanent.removeCounters(counter, source, game); - } - return true; - } - return false; - } - -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/v/Vault112SadisticSimulation.java b/Mage.Sets/src/mage/cards/v/Vault112SadisticSimulation.java index c50056b32b6..60292d91034 100644 --- a/Mage.Sets/src/mage/cards/v/Vault112SadisticSimulation.java +++ b/Mage.Sets/src/mage/cards/v/Vault112SadisticSimulation.java @@ -91,7 +91,7 @@ class Vault112SadisticSimulationChapterEffect extends OneShotEffect { return false; } int numberToPay = controller.getAmount( - 0, controller.getCounters().getCount(CounterType.ENERGY), + 0, controller.getCountersCount(CounterType.ENERGY), "How many {E} do you like to pay?", game ); if (numberToPay <= 0) { diff --git a/Mage.Sets/src/mage/cards/v/Vault12TheNecropolis.java b/Mage.Sets/src/mage/cards/v/Vault12TheNecropolis.java index 37fae7b3a21..bd3805c8db7 100644 --- a/Mage.Sets/src/mage/cards/v/Vault12TheNecropolis.java +++ b/Mage.Sets/src/mage/cards/v/Vault12TheNecropolis.java @@ -97,7 +97,7 @@ enum Vault12TheNecropolisValue implements DynamicValue { .stream() .map(game::getPlayer) .filter(Objects::nonNull) - .mapToInt(p -> p.getCounters().getCount(CounterType.RAD)) + .mapToInt(p -> p.getCountersCount(CounterType.RAD)) .sum(); } diff --git a/Mage.Sets/src/mage/cards/v/VentifactBottle.java b/Mage.Sets/src/mage/cards/v/VentifactBottle.java index e1bf1300246..aa5d41ea03f 100644 --- a/Mage.Sets/src/mage/cards/v/VentifactBottle.java +++ b/Mage.Sets/src/mage/cards/v/VentifactBottle.java @@ -1,7 +1,6 @@ package mage.cards.v; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.TriggeredAbility; @@ -25,8 +24,9 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class VentifactBottle extends CardImpl { @@ -45,8 +45,8 @@ public final class VentifactBottle extends CardImpl { this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability2, new SourceHasCounterCondition(CounterType.CHARGE, 1, Integer.MAX_VALUE), "At the beginning of your precombat main phase, " - + "if {this} has a charge counter on it, tap it and remove all charge counters from it. " - + "Add {C} for each charge counter removed this way.")); + + "if {this} has a charge counter on it, tap it and remove all charge counters from it. " + + "Add {C} for each charge counter removed this way.")); } private VentifactBottle(final VentifactBottle card) { @@ -79,11 +79,10 @@ class VentifactBottleEffect extends OneShotEffect { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Player player = game.getPlayer(source.getControllerId()); if (sourcePermanent != null && player != null) { - int chargeCounters = sourcePermanent.getCounters(game).getCount(CounterType.CHARGE); - sourcePermanent.removeCounters(CounterType.CHARGE.createInstance(chargeCounters), source, game); + int amountRemoved = sourcePermanent.removeAllCounters(CounterType.CHARGE.getName(), source, game); sourcePermanent.tap(source, game); Mana mana = new Mana(); - mana.setColorless(chargeCounters); + mana.setColorless(amountRemoved); player.getManaPool().addMana(mana, game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/v/VexingRadgull.java b/Mage.Sets/src/mage/cards/v/VexingRadgull.java index 68c892575fc..0a5635632bd 100644 --- a/Mage.Sets/src/mage/cards/v/VexingRadgull.java +++ b/Mage.Sets/src/mage/cards/v/VexingRadgull.java @@ -70,7 +70,7 @@ class VexingRadgullEffect extends OneShotEffect { if (player == null) { return false; } - if (!player.getCounters().containsKey(CounterType.RAD)) { + if (player.getCountersCount(CounterType.RAD) == 0) { return new AddCountersTargetEffect(CounterType.RAD.createInstance(2)) .setTargetPointer(getTargetPointer().copy()) .apply(game, source); diff --git a/Mage.Sets/src/mage/cards/v/ViridianBetrayers.java b/Mage.Sets/src/mage/cards/v/ViridianBetrayers.java index 0e448b1ddce..2494d967ae3 100644 --- a/Mage.Sets/src/mage/cards/v/ViridianBetrayers.java +++ b/Mage.Sets/src/mage/cards/v/ViridianBetrayers.java @@ -1,7 +1,5 @@ package mage.cards.v; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -18,6 +16,9 @@ import mage.counters.CounterType; import mage.game.Game; import mage.players.Player; +import java.util.Set; +import java.util.UUID; + /** * @author North */ @@ -61,7 +62,7 @@ enum PoisonedCondition implements Condition { Player opponent = game.getPlayer(opponentUuid); if (opponent != null && opponent.isInGame() - && opponent.getCounters().getCount(CounterType.POISON) > 0) { + && opponent.getCountersCount(CounterType.POISON) > 0) { return true; } } diff --git a/Mage.Sets/src/mage/cards/v/VraskaBetrayalsSting.java b/Mage.Sets/src/mage/cards/v/VraskaBetrayalsSting.java index 4a93363da4a..6210ec146ef 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaBetrayalsSting.java +++ b/Mage.Sets/src/mage/cards/v/VraskaBetrayalsSting.java @@ -89,7 +89,7 @@ class VraskaBetrayalsStingEffect extends OneShotEffect { if (targetPlayer == null) { return false; } - int totalPoison = targetPlayer.getCounters().getCount(CounterType.POISON); + int totalPoison = targetPlayer.getCountersCount(CounterType.POISON); if (totalPoison < 9) { targetPlayer.addCounters(CounterType.POISON.createInstance(9 - totalPoison), source.getControllerId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/w/WhisperingSpecter.java b/Mage.Sets/src/mage/cards/w/WhisperingSpecter.java index 6bc9fea537a..202d2c24d21 100644 --- a/Mage.Sets/src/mage/cards/w/WhisperingSpecter.java +++ b/Mage.Sets/src/mage/cards/w/WhisperingSpecter.java @@ -1,6 +1,5 @@ package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; @@ -12,20 +11,21 @@ import mage.abilities.keyword.InfectAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.CounterType; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author Loki */ public final class WhisperingSpecter extends CardImpl { public WhisperingSpecter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); this.subtype.add(SubType.PHYREXIAN); this.subtype.add(SubType.SPECTER); @@ -67,7 +67,7 @@ class WhisperingSpecterEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); if (player != null) { - int value = player.getCounters().getCount(CounterType.POISON); + int value = player.getCountersCount(CounterType.POISON); if (value > 0) { player.discard(value, false, false, source, game); return true; diff --git a/Mage.Sets/src/mage/cards/w/WrathOfTheSkies.java b/Mage.Sets/src/mage/cards/w/WrathOfTheSkies.java index d3bf7ca5c9b..8a8d5971689 100644 --- a/Mage.Sets/src/mage/cards/w/WrathOfTheSkies.java +++ b/Mage.Sets/src/mage/cards/w/WrathOfTheSkies.java @@ -1,7 +1,5 @@ package mage.cards.w; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayEnergyCost; @@ -15,8 +13,9 @@ import mage.counters.CounterType; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author DominionSpy */ public final class WrathOfTheSkies extends CardImpl { @@ -67,7 +66,7 @@ class WrathOfTheSkiesEffect extends OneShotEffect { new GetEnergyCountersControllerEffect(xValue).apply(game, source); } - int numberToPay = controller.getAmount(0, controller.getCounters().getCount(CounterType.ENERGY), + int numberToPay = controller.getAmount(0, controller.getCountersCount(CounterType.ENERGY), "Pay any amount of {E}", game); Cost cost = new PayEnergyCost(numberToPay); if (cost.pay(source, game, source, source.getControllerId(), true)) { diff --git a/Mage.Sets/src/mage/cards/w/Wurmquake.java b/Mage.Sets/src/mage/cards/w/Wurmquake.java index 35a9d82c223..c286589e676 100644 --- a/Mage.Sets/src/mage/cards/w/Wurmquake.java +++ b/Mage.Sets/src/mage/cards/w/Wurmquake.java @@ -1,31 +1,30 @@ package mage.cards.w; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.ManaSpentToCastCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.FlashbackAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Outcome; -import mage.abilities.dynamicvalue.common.ManaSpentToCastCount; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateTokenEffect; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.token.PhyrexianWurmToken; +import java.util.UUID; + /** - * * @author @stwalsh4118 */ public final class Wurmquake extends CardImpl { public Wurmquake(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}{G}"); - + // Corrupted -- Create an X/X green Phyrexian Wurm creature token with trample and toxic 1, where X is the amount of mana spent to cast this spell. Then for each opponent with three or more poison counters, you create another one of those tokens. this.getSpellAbility().addEffect(new WurmquakeEffect()); @@ -67,7 +66,7 @@ class WurmquakeEffect extends OneShotEffect { new CreateTokenEffect(new PhyrexianWurmToken(xValue)).apply(game, source); int amount = 0; for (UUID opponentId : game.getOpponents(source.getControllerId())) { - if (game.getPlayer(opponentId).getCounters().getCount(CounterType.POISON) >= 3) { + if (game.getPlayer(opponentId).getCountersCount(CounterType.POISON) >= 3) { amount++; } } diff --git a/Mage.Sets/src/mage/sets/ModernHorizons3.java b/Mage.Sets/src/mage/sets/ModernHorizons3.java index 73ce94ef5c3..6b20011a02e 100644 --- a/Mage.Sets/src/mage/sets/ModernHorizons3.java +++ b/Mage.Sets/src/mage/sets/ModernHorizons3.java @@ -105,6 +105,7 @@ public final class ModernHorizons3 extends ExpansionSet { cards.add(new SetCardInfo("Idol of False Gods", 210, Rarity.UNCOMMON, mage.cards.i.IdolOfFalseGods.class)); cards.add(new SetCardInfo("Island", 305, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("It That Heralds the End", 9, Rarity.UNCOMMON, mage.cards.i.ItThatHeraldsTheEnd.class)); + cards.add(new SetCardInfo("Izzet Generatorium", 191, Rarity.UNCOMMON, mage.cards.i.IzzetGeneratorium.class)); cards.add(new SetCardInfo("Jet Medallion", 292, Rarity.RARE, mage.cards.j.JetMedallion.class)); cards.add(new SetCardInfo("Junk Diver", 293, Rarity.UNCOMMON, mage.cards.j.JunkDiver.class)); cards.add(new SetCardInfo("K'rrik, Son of Yawgmoth", 274, Rarity.RARE, mage.cards.k.KrrikSonOfYawgmoth.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/PersistTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/PersistTest.java index da00dccfe59..52726725d4b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/PersistTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/PersistTest.java @@ -21,7 +21,6 @@ public class PersistTest extends CardTestPlayerBase { /** * Tests Safehold Elite don't returns from Persist if already a -1/-1 * counter was put on it from another source - * */ @Test public void testUndyingdoesntTriggerWithMinusCounter() { @@ -48,7 +47,7 @@ public class PersistTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Safehold Elite", 1); // one poison counter from Virulent Wound - Assert.assertEquals(1, playerA.getCounters().getCount(CounterType.POISON)); + Assert.assertEquals(1, playerA.getCountersCount(CounterType.POISON)); } /** diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/IzzetGeneratoriumTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/IzzetGeneratoriumTest.java new file mode 100644 index 00000000000..4141d0e485d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/IzzetGeneratoriumTest.java @@ -0,0 +1,95 @@ +package org.mage.test.cards.single.mh3; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.players.Player; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class IzzetGeneratoriumTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.i.IzzetGeneratorium Izzet Generatorium} {U}{R} + * Artifact + * If you would get one or more {E} (energy counters), you get that many plus one {E} instead. + * {T}: Draw a card. Activate only if you’ve paid or lost four or more {E} this turn. + */ + private static final String generator = "Izzet Generatorium"; + + private static void checkEnergyCount(String message, Player player, int expected) { + Assert.assertEquals(message, expected, player.getCountersCount(CounterType.ENERGY)); + } + + @Test + public void test_Pay_Energy() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, generator); + // When Bristling Hydra enters the battlefield, you get {E}{E}{E} (three energy counters). + // Pay {E}{E}{E}: Put a +1/+1 counter on Bristling Hydra. It gains hexproof until end of turn. + addCard(Zone.HAND, playerA, "Bristling Hydra", 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 8); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bristling Hydra", true); + runCode("energy counter is 4", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> checkEnergyCount(info, player, 4)); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bristling Hydra", true); + runCode("energy counter is 8", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> checkEnergyCount(info, player, 8)); + + checkPlayableAbility("1: condition not met before activing once", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw", false); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pay {E}{E}{E}"); + runCode("energy counter is 5", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> checkEnergyCount(info, player, 5)); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("2: condition not met before activing twice", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw", false); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pay {E}{E}{E}"); + runCode("energy counter is 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> checkEnergyCount(info, player, 2)); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("3: condition met after activing twice", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw", true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertHandCount(playerA, 1); + } + + @Test + public void test_Lose_Energy() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, generator); + // When Bristling Hydra enters the battlefield, you get {E}{E}{E} (three energy counters). + // Pay {E}{E}{E}: Put a +1/+1 counter on Bristling Hydra. It gains hexproof until end of turn. + addCard(Zone.HAND, playerA, "Bristling Hydra"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + // Choose one or more — + //• Destroy all creatures. + //• Destroy all planeswalkers. + //• Destroy all battles. + //• Exile all graveyards. + //• Each opponent loses all counters. + addCard(Zone.HAND, playerB, "Final Act"); + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bristling Hydra", true); + runCode("energy counter is 4", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> checkEnergyCount(info, player, 4)); + + checkPlayableAbility("1: condition not met before losing counters", 2, PhaseStep.UPKEEP, playerA, "{T}: Draw", false); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Final Act"); + setModeChoice(playerB, "5"); // each opponent loses all counters. + waitStackResolved(2, PhaseStep.PRECOMBAT_MAIN); + runCode("energy counter is 0", 2, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> checkEnergyCount(info, player, 0)); + checkPlayableAbility("2: condition met after losing counters", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw", true); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertHandCount(playerA, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/onc/OtharriSunsGloryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/onc/OtharriSunsGloryTest.java index 09dd73b0d59..cb419caf552 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/onc/OtharriSunsGloryTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/onc/OtharriSunsGloryTest.java @@ -24,7 +24,7 @@ public class OtharriSunsGloryTest extends CardTestPlayerBase { private static final String otharri = "Otharri, Suns' Glory"; private static void checkExperienceCounter(String message, Player player, int expected) { - Assert.assertEquals(message, expected, player.getCounters().getCount(CounterType.EXPERIENCE)); + Assert.assertEquals(message, expected, player.getCountersCount(CounterType.EXPERIENCE)); } @Test diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/RadCounterTriggerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/RadCounterTriggerTest.java index f7dbdcfdc47..3e74ffca4af 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/RadCounterTriggerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/RadCounterTriggerTest.java @@ -29,7 +29,7 @@ public class RadCounterTriggerTest extends CardTestPlayerBase { private static final String fallout = "Nuclear Fallout"; private static void checkRadCounterCount(String message, Player player, int expected) { - Assert.assertEquals(message, expected, player.getCounters().getCount(CounterType.RAD)); + Assert.assertEquals(message, expected, player.getCountersCount(CounterType.RAD)); } private static void checkGraveyardSize(String message, Player player, int expected) { 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 19dec2f4d2b..6c6543cfe1e 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 @@ -2961,8 +2961,8 @@ public class TestPlayer implements Player { } @Override - public Counters getCounters() { - return computerPlayer.getCounters(); + public Counters getCountersAsCopy() { + return computerPlayer.getCountersAsCopy(); } @Override @@ -3441,8 +3441,33 @@ public class TestPlayer implements Player { } @Override - public void removeCounters(String name, int amount, Ability source, Game game) { - computerPlayer.removeCounters(name, amount, source, game); + public void loseCounters(String counterName, int amount, Ability source, Game game) { + computerPlayer.loseCounters(counterName, amount, source, game); + } + + @Override + public int loseAllCounters(Ability source, Game game) { + return computerPlayer.loseAllCounters(source, game); + } + + @Override + public int loseAllCounters(String counterName, Ability source, Game game) { + return computerPlayer.loseAllCounters(counterName, source, game); + } + + @Override + public int getCountersCount(CounterType counterType) { + return computerPlayer.getCountersCount(counterType); + } + + @Override + public int getCountersCount(String counterName) { + return computerPlayer.getCountersCount(counterName); + } + + @Override + public int getCountersTotalCount() { + return computerPlayer.getCountersTotalCount(); } @Override diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index 2e67f2b473b..4188480cb6f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -1143,7 +1143,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param count Expected count. */ public void assertCounterCount(Player player, CounterType type, int count) throws AssertionError { - Assert.assertEquals("(Battlefield) Counter counts are not equal (" + player.getName() + ':' + type + ')', count, player.getCounters().getCount(type)); + Assert.assertEquals("(Battlefield) Counter counts are not equal (" + player.getName() + ':' + type + ')', count, player.getCountersCount(type)); } /** diff --git a/Mage/src/main/java/mage/abilities/common/ActivateIfConditionActivatedAbility.java b/Mage/src/main/java/mage/abilities/common/ActivateIfConditionActivatedAbility.java index 4e3ca72ef92..9a07d574402 100644 --- a/Mage/src/main/java/mage/abilities/common/ActivateIfConditionActivatedAbility.java +++ b/Mage/src/main/java/mage/abilities/common/ActivateIfConditionActivatedAbility.java @@ -13,6 +13,10 @@ import mage.constants.Zone; */ public class ActivateIfConditionActivatedAbility extends ActivatedAbilityImpl { + public ActivateIfConditionActivatedAbility(Effect effect, Cost cost, Condition condition) { + this(Zone.BATTLEFIELD, effect, cost, condition, TimingRule.INSTANT); + } + public ActivateIfConditionActivatedAbility(Zone zone, Effect effect, Cost cost, Condition condition) { this(zone, effect, cost, condition, TimingRule.INSTANT); } diff --git a/Mage/src/main/java/mage/abilities/condition/common/AttackedPlayersPoisonedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/AttackedPlayersPoisonedCondition.java index 2ab5bbf468e..aa6cf7b4315 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/AttackedPlayersPoisonedCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/AttackedPlayersPoisonedCondition.java @@ -11,7 +11,7 @@ import java.util.Objects; /** * A condition which checks whether any players being attacked are poisoned * (have one or more poison counters on them) - * + * * @author alexander-novo */ public enum AttackedPlayersPoisonedCondition implements Condition { @@ -27,7 +27,7 @@ public enum AttackedPlayersPoisonedCondition implements Condition { .distinct() .map(game::getPlayer) .filter(Objects::nonNull) - .anyMatch(player -> player.getCounters().containsKey(CounterType.POISON)); + .anyMatch(player -> player.getCountersCount(CounterType.POISON) > 0); } @Override diff --git a/Mage/src/main/java/mage/abilities/condition/common/CorruptedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CorruptedCondition.java index 315e78c9b77..7d7668bfd73 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/CorruptedCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/CorruptedCondition.java @@ -6,7 +6,6 @@ import mage.abilities.hint.ConditionHint; import mage.abilities.hint.Hint; import mage.counters.CounterType; import mage.game.Game; -import mage.players.Player; import java.util.Objects; @@ -24,8 +23,7 @@ public enum CorruptedCondition implements Condition { .stream() .map(game::getPlayer) .filter(Objects::nonNull) - .map(Player::getCounters) - .anyMatch(counters -> counters.getCount(CounterType.POISON) >= 3); + .anyMatch(player -> player.getCountersCount(CounterType.POISON) >= 3); } @Override diff --git a/Mage/src/main/java/mage/abilities/costs/common/PayEnergyCost.java b/Mage/src/main/java/mage/abilities/costs/common/PayEnergyCost.java index 7e363a1980f..90d5101f9cd 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PayEnergyCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PayEnergyCost.java @@ -11,6 +11,8 @@ import mage.util.CardUtil; import java.util.UUID; +import java.util.UUID; + /** * @author emerald000 */ @@ -31,14 +33,14 @@ public class PayEnergyCost extends CostImpl { @Override public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { Player player = game.getPlayer(controllerId); - return player != null && player.getCounters().getCount(CounterType.ENERGY) >= amount; + return player != null && player.getCountersCount(CounterType.ENERGY) >= amount; } @Override public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { Player player = game.getPlayer(controllerId); - if (player != null && player.getCounters().getCount(CounterType.ENERGY) >= amount) { - player.getCounters().removeCounter(CounterType.ENERGY, amount); + if (player != null && player.getCountersCount(CounterType.ENERGY) >= amount) { + player.loseCounters(CounterType.ENERGY.getName(), amount, source, game); paid = true; } return paid; diff --git a/Mage/src/main/java/mage/abilities/costs/common/RemoveAllCountersSourceCost.java b/Mage/src/main/java/mage/abilities/costs/common/RemoveAllCountersSourceCost.java index babfc40c0cf..0ebe23a3063 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/RemoveAllCountersSourceCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/RemoveAllCountersSourceCost.java @@ -38,10 +38,7 @@ public class RemoveAllCountersSourceCost extends CostImpl { public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { Permanent permanent = game.getPermanent(ability.getSourceId()); if (permanent != null) { - this.removedCounters = permanent.getCounters(game).getCount(counterType); - if (this.removedCounters > 0) { - permanent.removeCounters(counterType.createInstance(this.removedCounters), source, game); - } + this.removedCounters = permanent.removeAllCounters(counterType.getName(), source, game); } this.paid = true; return true; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/OpponentsPoisonCountersCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/OpponentsPoisonCountersCount.java index 9d672b3587f..d077faa16e7 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/OpponentsPoisonCountersCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/OpponentsPoisonCountersCount.java @@ -20,7 +20,7 @@ public enum OpponentsPoisonCountersCount implements DynamicValue { for (UUID playerUUID : playerList) { Player player = game.getPlayer(playerUUID); if (player != null) { - amount += player.getCounters().getCount(CounterType.POISON); + amount += player.getCountersCount(CounterType.POISON); } } return amount; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourceControllerCountersCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourceControllerCountersCount.java index b4f151e8d39..872ca82b41c 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourceControllerCountersCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SourceControllerCountersCount.java @@ -32,7 +32,7 @@ public enum SourceControllerCountersCount implements DynamicValue { int amount = 0; Player player = game.getPlayer(sourceAbility.getControllerId()); if (player != null) { - amount = player.getCounters().getCount(counterType); + amount = player.getCountersCount(counterType); } return amount; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/RemoveAllCountersSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RemoveAllCountersSourceEffect.java index b63c0913657..4c08de6dc00 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RemoveAllCountersSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RemoveAllCountersSourceEffect.java @@ -35,8 +35,7 @@ public class RemoveAllCountersSourceEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (sourcePermanent != null) { - int count = sourcePermanent.getCounters(game).getCount(counterType); - sourcePermanent.removeCounters(counterType.getName(), count, source, game); + sourcePermanent.removeAllCounters(counterType.getName(), source, game); return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/ProliferateEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/ProliferateEffect.java index 48324602438..de4ae045576 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/ProliferateEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/ProliferateEffect.java @@ -76,7 +76,8 @@ public class ProliferateEffect extends OneShotEffect { if (player == null) { continue; } - for (Counter counter : player.getCounters().values()) { + for (Counter counter : player.getCountersAsCopy().values()) { + // TODO: this does not work with ability counters that are not explicitly in CounterType. Like the Hexproof from XXX counters from Indominus Rex, Alpha Counter newCounter = CounterType.findByName(counter.getName()).createInstance(); if (player.addCounters(newCounter, source.getControllerId(), source, game)) { game.informPlayers(player.getLogName() + " had " + newCounter.getDescription() + " added to them."); diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveAllCountersPermanentTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveAllCountersPermanentTargetEffect.java new file mode 100644 index 00000000000..913e677ac58 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveAllCountersPermanentTargetEffect.java @@ -0,0 +1,53 @@ + +package mage.abilities.effects.common.counter; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * Removes all counters (optionally of a given counter type) from the target permanent. + * + * @author MTGfan + */ +public class RemoveAllCountersPermanentTargetEffect extends OneShotEffect { + + private final CounterType counterType; // If not null, remove counters of that type only. + + public RemoveAllCountersPermanentTargetEffect() { + this((CounterType) null); + } + + public RemoveAllCountersPermanentTargetEffect(CounterType counterType) { + super(Outcome.Neutral); + this.counterType = counterType; + staticText = "remove all " + (counterType == null ? "" : counterType.getName() + " ") + "counters from it."; + } + + public RemoveAllCountersPermanentTargetEffect(RemoveAllCountersPermanentTargetEffect effect) { + super(effect); + this.counterType = effect.counterType; + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent != null) { + if (counterType == null) { + permanent.removeAllCounters(source, game); + } else { + permanent.removeAllCounters(counterType.getName(), source, game); + } + return true; + } + return false; + } + + @Override + public RemoveAllCountersPermanentTargetEffect copy() { + return new RemoveAllCountersPermanentTargetEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveAllCountersTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveAllCountersTargetEffect.java deleted file mode 100644 index cb135ba07bb..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveAllCountersTargetEffect.java +++ /dev/null @@ -1,45 +0,0 @@ - -package mage.abilities.effects.common.counter; - -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * @author MTGfan - */ -public class RemoveAllCountersTargetEffect extends OneShotEffect { - - private final CounterType counterType; - - public RemoveAllCountersTargetEffect(CounterType counterType) { - super(Outcome.Neutral); - this.counterType = counterType; - staticText = "remove all " + counterType.getName() + " counters from it."; - } - - public RemoveAllCountersTargetEffect(RemoveAllCountersTargetEffect effect) { - super(effect); - this.counterType = effect.counterType; - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if(permanent != null) { - int count = permanent.getCounters(game).getCount(counterType); - permanent.removeCounters(counterType.getName(), count, source, game); - return true; - } - return false; - } - - @Override - public RemoveAllCountersTargetEffect copy() { - return new RemoveAllCountersTargetEffect(this); - } -} diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java index 4febe0b70d7..b134c74e121 100644 --- a/Mage/src/main/java/mage/cards/Card.java +++ b/Mage/src/main/java/mage/cards/Card.java @@ -88,11 +88,13 @@ public interface Card extends MageObject, Ownerable { /** * Is this an extra deck card? (such as contraptions and attractions) + * * @return true if this is an extra deck card, false otherwise */ default boolean isExtraDeckCard() { return false; } + void assignNewId(); void addInfo(String key, String value, Game game); @@ -168,17 +170,64 @@ public interface Card extends MageObject, Ownerable { boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game, List appliedEffects, boolean isEffect, int maxCounters); - default void removeCounters(String name, int amount, Ability source, Game game){ - removeCounters(name, amount, source, game, false); + /** + * Remove {@param amount} counters of the specified kind. + */ + default void removeCounters(String counterName, int amount, Ability source, Game game) { + removeCounters(counterName, amount, source, game, false); } - void removeCounters(String name, int amount, Ability source, Game game, boolean damage); + /** + * Remove {@param amount} counters of the specified kind. + * + * @param isDamage if the counter removal is a result of being damaged (e.g. for Deification to work) + */ + void removeCounters(String counterName, int amount, Ability source, Game game, boolean isDamage); default void removeCounters(Counter counter, Ability source, Game game) { removeCounters(counter, source, game, false); } - void removeCounters(Counter counter, Ability source, Game game, boolean damage); + /** + * Remove all counters of any kind. + * + * @param isDamage if the counter removal is a result of being damaged (e.g. for Deification to work) + */ + void removeCounters(Counter counter, Ability source, Game game, boolean isDamage); + + /** + * Remove all counters of any kind. + * + * @return the amount of counters removed this way. + */ + default int removeAllCounters(Ability source, Game game) { + return removeAllCounters(source, game, false); + } + + /** + * Remove all counters of any kind. + * + * @param isDamage if the counter removal is a result of being damaged (e.g. for Deification to work) + * @return the amount of counters removed this way. + */ + int removeAllCounters(Ability source, Game game, boolean isDamage); + + /** + * Remove all counters of a specific kind. Return the amount of counters removed this way. + * + * @return the amount of counters removed this way. + */ + default int removeAllCounters(String counterName, Ability source, Game game) { + return removeAllCounters(counterName, source, game, false); + } + + /** + * Remove all counters of a specific kind. Return the amount of counters removed this way. + * + * @param isDamage if the counter removal is a result of being damaged (e.g. for Deification to work) + * @return the amount of counters removed this way. + */ + int removeAllCounters(String counterName, Ability source, Game game, boolean isDamage); @Override Card copy(); diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index 1ee55b8c6fd..87e51d36b66 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -803,39 +803,40 @@ public abstract class CardImpl extends MageObjectImpl implements Card { } @Override - public void removeCounters(String name, int amount, Ability source, Game game, boolean isDamage) { + public void removeCounters(String counterName, int amount, Ability source, Game game, boolean isDamage) { - if (amount <= 0){ + if (amount <= 0) { return; } - if (getCounters(game).getCount(name) <= 0){ + if (getCounters(game).getCount(counterName) <= 0) { return; } - GameEvent removeCountersEvent = new RemoveCountersEvent(name, this, source, amount, isDamage); - if (game.replaceEvent(removeCountersEvent)){ + GameEvent removeCountersEvent = new RemoveCountersEvent(counterName, this, source, amount, isDamage); + if (game.replaceEvent(removeCountersEvent)) { return; } int finalAmount = 0; for (int i = 0; i < removeCountersEvent.getAmount(); i++) { - GameEvent event = new RemoveCounterEvent(name, this, source, isDamage); - if (game.replaceEvent(event)){ + GameEvent event = new RemoveCounterEvent(counterName, this, source, isDamage); + if (game.replaceEvent(event)) { continue; } - if (!getCounters(game).removeCounter(name, 1)) { + if (!getCounters(game).removeCounter(counterName, 1)) { break; } - event = new CounterRemovedEvent(name, this, source, isDamage); + event = new CounterRemovedEvent(counterName, this, source, isDamage); game.fireEvent(event); finalAmount++; } - GameEvent event = new CountersRemovedEvent(name, this, source, finalAmount, isDamage); + + GameEvent event = new CountersRemovedEvent(counterName, this, source, finalAmount, isDamage); game.fireEvent(event); } @@ -846,6 +847,24 @@ public abstract class CardImpl extends MageObjectImpl implements Card { } } + @Override + public int removeAllCounters(Ability source, Game game, boolean isDamage) { + int amountBefore = getCounters(game).getTotalCount(); + for (Counter counter : getCounters(game).copy().values()) { + removeCounters(counter.getName(), counter.getCount(), source, game, isDamage); + } + int amountAfter = getCounters(game).getTotalCount(); + return Math.max(0, amountBefore - amountAfter); + } + + @Override + public int removeAllCounters(String counterName, Ability source, Game game, boolean isDamage) { + int amountBefore = getCounters(game).getCount(counterName); + removeCounters(counterName, amountBefore, source, game, isDamage); + int amountAfter = getCounters(game).getCount(counterName); + return Math.max(0, amountBefore - amountAfter); + } + @Override public String getLogName() { if (name.isEmpty()) { diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacedCard.java b/Mage/src/main/java/mage/cards/ModalDoubleFacedCard.java index 6a71064c7ba..41466a0e156 100644 --- a/Mage/src/main/java/mage/cards/ModalDoubleFacedCard.java +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacedCard.java @@ -223,8 +223,8 @@ public abstract class ModalDoubleFacedCard extends CardImpl implements CardWithH } @Override - public void removeCounters(String name, int amount, Ability source, Game game) { - leftHalfCard.removeCounters(name, amount, source, game); + public void removeCounters(String counterName, int amount, Ability source, Game game) { + leftHalfCard.removeCounters(counterName, amount, source, game); } @Override diff --git a/Mage/src/main/java/mage/counters/Counters.java b/Mage/src/main/java/mage/counters/Counters.java index 89c569d6da6..dd7948c4681 100644 --- a/Mage/src/main/java/mage/counters/Counters.java +++ b/Mage/src/main/java/mage/counters/Counters.java @@ -27,35 +27,17 @@ public class Counters extends HashMap implements Serializable, return new Counters(this); } - public Counters addCounter(String name, int amount) { - putIfAbsent(name, new Counter(name)); - this.get(name).add(amount); - return this; - } - public Counters addCounter(Counter counter) { if (!containsKey(counter.name)) { put(counter.name, counter); } else { - get(counter.name).add(counter.getCount()); } return this; } - public boolean removeCounter(String name) { - return removeCounter(name, 1); - } - public boolean removeCounter(CounterType counterType, int amount) { - if (this.containsKey(counterType.getName())) { - get(counterType.getName()).remove(amount); - if (get(counterType.getName()).count == 0) { - this.remove(counterType.getName()); - } - return true; - } - return false; + return removeCounter(counterType.getName(), amount); } public boolean removeCounter(String name, int amount) { @@ -69,17 +51,6 @@ public class Counters extends HashMap implements Serializable, return false; } - public void removeAllCounters(CounterType counterType) { - removeAllCounters(counterType.getName()); - } - - public void removeAllCounters(String name) { - if (this.containsKey(name)) { - this.remove(name); - - } - } - public int getCount(String name) { if (this.containsKey(name)) { return this.get(name).getCount(); @@ -91,6 +62,10 @@ public class Counters extends HashMap implements Serializable, return getCount(counterType) > 0; } + public int getTotalCount() { + return this.values().stream().mapToInt(Counter::getCount).sum(); + } + public int getCount(CounterType type) { if (this.containsKey(type.getName())) { return this.get(type.getName()).getCount(); diff --git a/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayerWithCounter.java b/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayerWithCounter.java index 4f0cd220149..7282ec19866 100644 --- a/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayerWithCounter.java +++ b/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayerWithCounter.java @@ -29,7 +29,7 @@ public class FilterPermanentOrPlayerWithCounter extends FilterPermanentOrPlayer public boolean match(MageItem o, Game game) { if (super.match(o, game)) { if (o instanceof Player) { - return !((Player) o).getCounters().isEmpty(); + return ((Player) o).getCountersTotalCount() > 0; } else if (o instanceof Permanent) { return !((Permanent) o).getCounters(game).isEmpty(); } @@ -41,7 +41,7 @@ public class FilterPermanentOrPlayerWithCounter extends FilterPermanentOrPlayer public boolean match(MageItem o, UUID playerId, Ability source, Game game) { if (super.match(o, playerId, source, game)) { // same as parent class, so can call with full params if (o instanceof Player) { - return !((Player) o).getCounters().isEmpty(); + return ((Player) o).getCountersTotalCount() > 0; } else if (o instanceof Permanent) { return !((Permanent) o).getCounters(game).isEmpty(); } diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 80723d76af6..6bdea86122c 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -2195,7 +2195,7 @@ public abstract class GameImpl implements Game { public UUID fireReflexiveTriggeredAbility(ReflexiveTriggeredAbility reflexiveAbility, Ability source) { return fireReflexiveTriggeredAbility(reflexiveAbility, source, false); } - + @Override public UUID fireReflexiveTriggeredAbility(ReflexiveTriggeredAbility reflexiveAbility, Ability source, boolean fireAsSimultaneousEvent) { UUID uuid = this.addDelayedTriggeredAbility(reflexiveAbility, source); @@ -2300,7 +2300,7 @@ public abstract class GameImpl implements Game { if (!player.hasLost() && ((player.getLife() <= 0 && player.canLoseByZeroOrLessLife()) || player.getLibrary().isEmptyDraw() - || player.getCounters().getCount(CounterType.POISON) >= 10)) { + || player.getCountersCount(CounterType.POISON) >= 10)) { player.lost(this); } } diff --git a/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java b/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java index 1a04ce61c9a..0dbd3388593 100644 --- a/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java @@ -66,7 +66,7 @@ enum RadiationCondition implements Condition { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - return player != null && player.getCounters().getCount(CounterType.RAD) > 0; + return player != null && player.getCountersCount(CounterType.RAD) > 0; } } @@ -102,13 +102,13 @@ class RadiationEffect extends OneShotEffect { if (player == null) { return false; } - int amount = player.getCounters().getCount(CounterType.RAD); + int amount = player.getCountersCount(CounterType.RAD); Cards milled = player.millCards(amount, source, game); int countNonLand = milled.count(StaticFilters.FILTER_CARD_NON_LAND, player.getId(), source, game); if (countNonLand > 0) { // TODO: support gaining life instead with [[Strong, the Brutish Thespian]] player.loseLife(countNonLand, game, source, false); - player.removeCounters(CounterType.RAD.getName(), countNonLand, source, game); + player.loseCounters(CounterType.RAD.getName(), countNonLand, source, game); } return true; } diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 4cac8db1e47..24ed059b6b2 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -548,8 +548,8 @@ public class GameEvent implements Serializable { amount number of counters being removed data name of the counter(s) being removed */ - REMOVE_COUNTER, REMOVE_COUNTERS, - COUNTER_REMOVED, COUNTERS_REMOVED, + REMOVE_COUNTER, COUNTER_REMOVED, + REMOVE_COUNTERS, COUNTERS_REMOVED, LOSE_CONTROL, /* LOST_CONTROL targetId id of the creature that lost control diff --git a/Mage/src/main/java/mage/game/permanent/token/DaxosSpiritToken.java b/Mage/src/main/java/mage/game/permanent/token/DaxosSpiritToken.java index 7983c0c1ede..1b17d54bcbf 100644 --- a/Mage/src/main/java/mage/game/permanent/token/DaxosSpiritToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/DaxosSpiritToken.java @@ -65,7 +65,7 @@ class DaxosSpiritSetPTEffect extends ContinuousEffectImpl { return false; } - int amount = controller.getCounters().getCount(CounterType.EXPERIENCE); + int amount = controller.getCountersCount(CounterType.EXPERIENCE); permanent.getPower().setModifiedBaseValue(amount); permanent.getToughness().setModifiedBaseValue(amount); return true; diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index eb04218198a..d3cb54c8c4a 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -1090,8 +1090,8 @@ public class Spell extends StackObjectImpl implements Card { } @Override - public void removeCounters(String name, int amount, Ability source, Game game, boolean isDamage) { - card.removeCounters(name, amount, source, game, isDamage); + public void removeCounters(String counterName, int amount, Ability source, Game game, boolean isDamage) { + card.removeCounters(counterName, amount, source, game, isDamage); } @Override @@ -1099,6 +1099,16 @@ public class Spell extends StackObjectImpl implements Card { card.removeCounters(counter, source, game, isDamage); } + @Override + public int removeAllCounters(Ability source, Game game, boolean isDamage) { + return card.removeAllCounters(source, game, isDamage); + } + + @Override + public int removeAllCounters(String counterName, Ability source, Game game, boolean isDamage) { + return card.removeAllCounters(counterName, source, game, isDamage); + } + public Card getCard() { return card; } diff --git a/Mage/src/main/java/mage/game/turn/Turn.java b/Mage/src/main/java/mage/game/turn/Turn.java index 3a5985e1b49..c0c5f93e7f6 100644 --- a/Mage/src/main/java/mage/game/turn/Turn.java +++ b/Mage/src/main/java/mage/game/turn/Turn.java @@ -372,7 +372,7 @@ public class Turn implements Serializable { int delimiter = game.getPlayers().size() - 1; for (Player gamePlayer : game.getPlayers().values()) { sb.append(gamePlayer.getLife()); - int poison = gamePlayer.getCounters().getCount(CounterType.POISON); + int poison = gamePlayer.getCountersCount(CounterType.POISON); if (poison > 0) { sb.append("[P:").append(poison).append(']'); } diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index e3cc56c0673..dac27d6f6d7 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -15,6 +15,7 @@ import mage.cards.decks.Deck; import mage.choices.Choice; import mage.constants.*; import mage.counters.Counter; +import mage.counters.CounterType; import mage.counters.Counters; import mage.designations.Designation; import mage.designations.DesignationType; @@ -105,7 +106,11 @@ public interface Player extends MageItem, Copyable { void addAbility(Ability ability); - Counters getCounters(); + /** + * The counters should be manipulated (adding or removing counters) with the appropriate addCounters/removeCounters/getCounterCount methods. + * This returns a copy for specific usage, to make sure the Player's counters are not altered from there. + */ + Counters getCountersAsCopy(); int getLife(); @@ -832,9 +837,44 @@ public interface Player extends MageItem, Copyable { Map getPlayableActivatedAbilities(MageObject object, Zone zone, Game originalGame); + /** + * add counter to the player (action verb is `get`, but not using that one to avoid ambiguity with getters) + */ boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game); - void removeCounters(String name, int amount, Ability source, Game game); + /** + * lose {@param amount} counters of the specified kind. + */ + void loseCounters(String counterName, int amount, Ability source, Game game); + + /** + * lose all counters of any kind. + * + * @return the amount of counters removed this way. + */ + int loseAllCounters(Ability source, Game game); + + /** + * lose all counters of a specific kind. + * + * @return the amount of counters removed this way. + */ + int loseAllCounters(String counterName, Ability source, Game game); + + /** + * @return the amount of counters of the specified kind the player has + */ + int getCountersCount(CounterType counterType); + + /** + * @return the amount of counters of the specified kind the player has + */ + int getCountersCount(String counterName); + + /** + * @return the amount of counters in total of any kind the player has + */ + int getCountersTotalCount(); List getAttachments(); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 8df1a12963f..831ab10fc79 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -337,7 +337,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.commandersIds = new HashSet<>(player.getCommandersIds()); this.abilities = player.getAbilities().copy(); - this.counters = player.getCounters().copy(); + this.counters = player.getCountersAsCopy(); this.landsPlayed = player.getLandsPlayed(); this.landsPerTurn = player.getLandsPerTurn(); @@ -531,8 +531,8 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public Counters getCounters() { - return counters; + public Counters getCountersAsCopy() { + return counters.copy(); } @Override @@ -2369,7 +2369,7 @@ public abstract class PlayerImpl implements Player, Serializable { ); addingOneEvent.setFlag(isEffectFlag); if (!game.replaceEvent(addingOneEvent)) { - getCounters().addCounter(eventCounter); + counters.addCounter(eventCounter); GameEvent addedOneEvent = GameEvent.getEvent( GameEvent.EventType.COUNTER_ADDED, playerId, source, playerAddingCounters, counter.getName(), 1 @@ -2396,9 +2396,9 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public void removeCounters(String name, int amount, Ability source, Game game) { + public void loseCounters(String counterName, int amount, Ability source, Game game) { - GameEvent removeCountersEvent = new RemoveCountersEvent(name, this, source, amount, false); + GameEvent removeCountersEvent = new RemoveCountersEvent(counterName, this, source, amount, false); if (game.replaceEvent(removeCountersEvent)) { return; } @@ -2406,22 +2406,56 @@ public abstract class PlayerImpl implements Player, Serializable { int finalAmount = 0; for (int i = 0; i < amount; i++) { - GameEvent event = new RemoveCounterEvent(name, this, source, false); + GameEvent event = new RemoveCounterEvent(counterName, this, source, false); if (game.replaceEvent(event)) { continue; } - if (!counters.removeCounter(name, 1)) { + if (!counters.removeCounter(counterName, 1)) { break; } - event = new CounterRemovedEvent(name, this, source, false); + event = new CounterRemovedEvent(counterName, this, source, false); game.fireEvent(event); finalAmount++; } - GameEvent event = new CountersRemovedEvent(name, this, source, finalAmount, false); + + GameEvent event = new CountersRemovedEvent(counterName, this, source, finalAmount, false); game.fireEvent(event); } + @Override + public int loseAllCounters(Ability source, Game game) { + int amountBefore = getCountersTotalCount(); + for (Counter counter : getCountersAsCopy().values()) { + loseCounters(counter.getName(), counter.getCount(), source, game); + } + int amountAfter = getCountersTotalCount(); + return Math.max(0, amountBefore - amountAfter); + } + + @Override + public int loseAllCounters(String counterName, Ability source, Game game) { + int amountBefore = getCountersCount(counterName); + loseCounters(counterName, amountBefore, source, game); + int amountAfter = getCountersCount(counterName); + return Math.max(0, amountBefore - amountAfter); + } + + @Override + public int getCountersCount(CounterType counterType) { + return counters.getCount(counterType); + } + + @Override + public int getCountersCount(String counterName) { + return counters.getCount(counterName); + } + + @Override + public int getCountersTotalCount() { + return counters.getTotalCount(); + } + @Override public Abilities getAbilities() { return this.abilities; diff --git a/Mage/src/test/java/mage/counters/CountersTest.java b/Mage/src/test/java/mage/counters/CountersTest.java index 4edc8b5bba1..e9135f104d3 100644 --- a/Mage/src/test/java/mage/counters/CountersTest.java +++ b/Mage/src/test/java/mage/counters/CountersTest.java @@ -16,7 +16,7 @@ public class CountersTest { @Test public void testCopyCounter() { // given - counters.addCounter("test", 4); + counters.addCounter(new Counter("test", 5)); // when Counters copy = counters.copy(); @@ -29,8 +29,8 @@ public class CountersTest { @Test public void testRemoveCounter() { // given - counters.addCounter("test1", 5); - counters.addCounter("test2", 5); + counters.addCounter(new Counter("test1", 5)); + counters.addCounter(new Counter("test2", 5)); // when @@ -45,35 +45,23 @@ public class CountersTest { @Test public void testAddCounterWithNewCounter() { // given - counters.addCounter("test1", 5); + counters.addCounter(new Counter("test1", 5)); // when - counters.addCounter("test1", 10); - counters.addCounter("test2", 5); + counters.addCounter(new Counter("test1", 10)); + counters.addCounter(new Counter("test2", 5)); // then - assertNotEquals(15, counters.getCount("test1")); - assertEquals(16, counters.getCount("test1")); + assertTrue(counters.containsKey("test1")); + assertEquals(15, counters.getCount("test1")); assertTrue(counters.containsKey("test2")); - assertEquals(6, counters.getCount("test2")); - } - - @Test - public void testRemoveAllCounter() { - // given - counters.addCounter("test", 10); - - // when - counters.removeAllCounters("test"); - - // then - assertFalse(counters.containsKey("test")); + assertEquals(5, counters.getCount("test2")); } @Test public void testGetCount() { // given - counters.addCounter("test1", 5); + counters.addCounter(new Counter("test1", 5)); // when int count1 = counters.getCount("test1"); @@ -81,13 +69,13 @@ public class CountersTest { // then assertEquals(0, count2); - assertEquals(6, count1); + assertEquals(5, count1); } @Test public void testContainsKey() { // given - counters.addCounter("test1", 5); + counters.addCounter(new Counter("test1", 5)); // when