From f893503acdd34cd2f91dc4673d83f31eb411aa60 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 20 Oct 2014 17:49:40 +0200 Subject: [PATCH] Fixed empty mana pool handling to handle multiple effects at the same time correctly (fixes #482). Added support for mana that empties only at end of turn. --- .../journeyintonyx/KruphixGodOfHorizons.java | 19 +----- Mage.Sets/src/mage/sets/tenth/Upwelling.java | 53 +++++++++------ .../sets/worldwake/OmnathLocusOfMana.java | 64 ++++++++----------- .../common/AddManaToManaPoolEffect.java | 14 +++- Mage/src/mage/constants/Duration.java | 1 + Mage/src/mage/game/GameImpl.java | 10 ++- Mage/src/mage/game/events/GameEvent.java | 2 +- Mage/src/mage/players/ManaPool.java | 64 +++++++++++++------ Mage/src/mage/players/ManaPoolItem.java | 60 +++++++++++++++++ Mage/src/mage/players/PlayerImpl.java | 1 + 10 files changed, 185 insertions(+), 103 deletions(-) diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/KruphixGodOfHorizons.java b/Mage.Sets/src/mage/sets/journeyintonyx/KruphixGodOfHorizons.java index ac40cdad3d..b8ea5e5209 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/KruphixGodOfHorizons.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/KruphixGodOfHorizons.java @@ -97,15 +97,6 @@ public class KruphixGodOfHorizons extends CardImpl { class KruphixGodOfHorizonsEffect extends ReplacementEffectImpl { - private static final List manaTypes = new ArrayList<>(); - static { - manaTypes.add(ManaType.BLACK); - manaTypes.add(ManaType.BLUE); - manaTypes.add(ManaType.RED); - manaTypes.add(ManaType.WHITE); - manaTypes.add(ManaType.GREEN); - } - public KruphixGodOfHorizonsEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); staticText = "If unused mana would empty from your mana pool, that mana becomes colorless instead"; @@ -127,15 +118,7 @@ class KruphixGodOfHorizonsEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player player = game.getPlayer(event.getPlayerId()); - if (player != null){ - ManaPool pool = player.getManaPool(); - int coloredMana = pool.getGreen() + pool.getBlack() + pool.getBlue()+ pool.getWhite()+ pool.getRed(); - player.getManaPool().emptyManaType(manaTypes); - pool.addMana(Mana.ColorlessMana(coloredMana), game, source); - return true; - } - return false; + return true; } @Override diff --git a/Mage.Sets/src/mage/sets/tenth/Upwelling.java b/Mage.Sets/src/mage/sets/tenth/Upwelling.java index 142d552d4a..c8fe14f018 100644 --- a/Mage.Sets/src/mage/sets/tenth/Upwelling.java +++ b/Mage.Sets/src/mage/sets/tenth/Upwelling.java @@ -30,15 +30,19 @@ package mage.sets.tenth; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.ManaType; import mage.constants.Outcome; import mage.constants.Rarity; +import mage.constants.SubLayer; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; +import mage.players.ManaPool; +import mage.players.Player; /** * @@ -53,7 +57,7 @@ public class Upwelling extends CardImpl { this.color.setGreen(true); // Mana pools don't empty as steps and phases end. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UpwellingReplacementEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UpwellingRuleEffect())); } @@ -67,20 +71,41 @@ public class Upwelling extends CardImpl { } } -class UpwellingReplacementEffect extends ReplacementEffectImpl { +class UpwellingRuleEffect extends ContinuousEffectImpl { - public UpwellingReplacementEffect() { + public UpwellingRuleEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); staticText = "Mana pools don't empty as steps and phases end"; } - public UpwellingReplacementEffect(final UpwellingReplacementEffect effect) { + public UpwellingRuleEffect(final UpwellingRuleEffect effect) { super(effect); } @Override - public UpwellingReplacementEffect copy() { - return new UpwellingReplacementEffect(this); + public UpwellingRuleEffect copy() { + return new UpwellingRuleEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId: controller.getInRange()) { + Player player = game.getPlayer(playerId); + if (player != null){ + ManaPool pool = player.getManaPool(); + pool.addDoNotEmptyManaType(ManaType.WHITE); + pool.addDoNotEmptyManaType(ManaType.GREEN); + pool.addDoNotEmptyManaType(ManaType.BLUE); + pool.addDoNotEmptyManaType(ManaType.RED); + pool.addDoNotEmptyManaType(ManaType.BLACK); + pool.addDoNotEmptyManaType(ManaType.COLORLESS); + } + } + return true; + } + return false; } @Override @@ -89,15 +114,7 @@ class UpwellingReplacementEffect extends ReplacementEffectImpl { } @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.EMPTY_MANA_POOLS) { - return true; - } - return false; + public boolean hasLayer(Layer layer) { + return layer == Layer.RulesEffects; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/OmnathLocusOfMana.java b/Mage.Sets/src/mage/sets/worldwake/OmnathLocusOfMana.java index 422b7d3f7e..a53e9bc85f 100644 --- a/Mage.Sets/src/mage/sets/worldwake/OmnathLocusOfMana.java +++ b/Mage.Sets/src/mage/sets/worldwake/OmnathLocusOfMana.java @@ -27,21 +27,24 @@ */ package mage.sets.worldwake; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; - -import mage.constants.*; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.ManaTypeInManaPoolCount; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.continious.BoostSourceEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.ManaType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SubLayer; +import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; import mage.players.Player; /** @@ -60,7 +63,7 @@ public class OmnathLocusOfMana extends CardImpl { this.toughness = new MageInt(1); // Green mana doesn't empty from your mana pool as steps and phases end. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OmnathReplacementEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OmnathRuleEffect())); // Omnath, Locus of Mana gets +1/+1 for each green mana in your mana pool DynamicValue boost = new ManaTypeInManaPoolCount(ManaType.GREEN); @@ -78,50 +81,37 @@ public class OmnathLocusOfMana extends CardImpl { } } -class OmnathReplacementEffect extends ReplacementEffectImpl { - - private static final List manaTypes = new ArrayList<>(); - static { - manaTypes.add(ManaType.BLACK); - manaTypes.add(ManaType.BLUE); - manaTypes.add(ManaType.RED); - manaTypes.add(ManaType.WHITE); - manaTypes.add(ManaType.COLORLESS); - } +class OmnathRuleEffect extends ContinuousEffectImpl { - public OmnathReplacementEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); + public OmnathRuleEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment); staticText = "Green mana doesn't empty from your mana pool as steps and phases end"; } - public OmnathReplacementEffect(final OmnathReplacementEffect effect) { + public OmnathRuleEffect(final OmnathRuleEffect effect) { super(effect); } @Override - public OmnathReplacementEffect copy() { - return new OmnathReplacementEffect(this); + public OmnathRuleEffect copy() { + return new OmnathRuleEffect(this); } + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null){ + player.getManaPool().addDoNotEmptyManaType(ManaType.GREEN); + } + return false; } + @Override public boolean apply(Game game, Ability source) { return false; } @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player player = game.getPlayer(event.getPlayerId()); - if (player != null){ - player.getManaPool().emptyManaType(manaTypes); - } - return true; + public boolean hasLayer(Layer layer) { + return layer == Layer.RulesEffects; } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.EMPTY_MANA_POOL && event.getPlayerId().equals(source.getControllerId())) { - return true; - } - return false; - } -} \ No newline at end of file +} diff --git a/Mage/src/mage/abilities/effects/common/AddManaToManaPoolEffect.java b/Mage/src/mage/abilities/effects/common/AddManaToManaPoolEffect.java index 10b3586967..8c80f92cd1 100644 --- a/Mage/src/mage/abilities/effects/common/AddManaToManaPoolEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddManaToManaPoolEffect.java @@ -21,22 +21,30 @@ import mage.players.Player; public class AddManaToManaPoolEffect extends OneShotEffect { protected Mana mana; + protected boolean emptyOnlyOnTurnsEnd; + public AddManaToManaPoolEffect(Mana mana, String textManaPoolOwner) { + this(mana, textManaPoolOwner, false); + } /** * Adds mana to the mana pool of target pointer player * * @param mana mana that will be added to the pool * @param textManaPoolOwner text that references to the mana pool owner (e.g. "damaged player's") - */ - public AddManaToManaPoolEffect(Mana mana, String textManaPoolOwner) { + * @param emptyOnTurnsEnd if set, the mana will empty only on end of turnstep + * + */ + public AddManaToManaPoolEffect(Mana mana, String textManaPoolOwner, boolean emptyOnTurnsEnd) { super(Outcome.PutManaInPool); this.mana = mana; + this.emptyOnlyOnTurnsEnd = emptyOnTurnsEnd; this.staticText = "add " + mana.toString() + " to " + textManaPoolOwner + " mana pool"; } public AddManaToManaPoolEffect(final AddManaToManaPoolEffect effect) { super(effect); this.mana = effect.mana; + this.emptyOnlyOnTurnsEnd = effect.emptyOnlyOnTurnsEnd; } @Override @@ -48,7 +56,7 @@ public class AddManaToManaPoolEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); if (player != null) { - player.getManaPool().addMana(mana, game, source); + player.getManaPool().addMana(mana, game, source, emptyOnlyOnTurnsEnd); return true; } return false; diff --git a/Mage/src/mage/constants/Duration.java b/Mage/src/mage/constants/Duration.java index 3fc54202ee..790891b3da 100644 --- a/Mage/src/mage/constants/Duration.java +++ b/Mage/src/mage/constants/Duration.java @@ -13,6 +13,7 @@ public enum Duration { EndOfTurn("until end of turn"), UntilYourNextTurn("until your next turn"), EndOfCombat("until end of combat"), + EndOfStep("until end of phase step"), Custom(""); private final String text; diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 3ff934624b..be42d9a6a0 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -1168,11 +1168,9 @@ public abstract class GameImpl implements Game, Serializable { @Override public void emptyManaPools() { - if (!replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOLS, null, null, null))) { - for (Player player: getPlayers().values()) { - if (!replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, player.getId(), null, player.getId()))) { - player.getManaPool().emptyPool(); - } + for (Player player: getPlayers().values()) { + if (!replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, player.getId(), null, player.getId()))) { + player.getManaPool().emptyPool(this); } } } @@ -1186,7 +1184,7 @@ public abstract class GameImpl implements Game, Serializable { public void addEffect(ContinuousEffect continuousEffect, Ability source) { Ability newAbility = source.copy(); - ContinuousEffect newEffect = (ContinuousEffect)continuousEffect.copy(); + ContinuousEffect newEffect = continuousEffect.copy(); newEffect.newId(); newEffect.setTimestamp(); newEffect.init(newAbility, this); diff --git a/Mage/src/mage/game/events/GameEvent.java b/Mage/src/mage/game/events/GameEvent.java index 682aa68902..4a749081f6 100644 --- a/Mage/src/mage/game/events/GameEvent.java +++ b/Mage/src/mage/game/events/GameEvent.java @@ -75,7 +75,7 @@ public class GameEvent { END_PHASE, END_PHASE_PRE, END_PHASE_POST, END_TURN_STEP_PRE, END_TURN_STEP, END_TURN_STEP_POST, CLEANUP_STEP_PRE, CLEANUP_STEP, CLEANUP_STEP_POST, - EMPTY_MANA_POOLS, EMPTY_MANA_POOL, + EMPTY_MANA_POOL, AT_END_OF_TURN, //player events diff --git a/Mage/src/mage/players/ManaPool.java b/Mage/src/mage/players/ManaPool.java index 18b53262d9..891e5f6f68 100644 --- a/Mage/src/mage/players/ManaPool.java +++ b/Mage/src/mage/players/ManaPool.java @@ -30,13 +30,17 @@ package mage.players; import java.io.Serializable; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import mage.ConditionalMana; import mage.Mana; import mage.abilities.Ability; import mage.constants.AsThoughEffectType; +import mage.constants.Duration; import mage.constants.ManaType; +import mage.constants.TurnPhase; import mage.filter.Filter; import mage.filter.FilterMana; import mage.game.Game; @@ -54,6 +58,8 @@ public class ManaPool implements Serializable { private boolean autoPayment; // auto payment from mana pool: true - mode is active private ManaType unlockedManaType; // type of mana that was selected to pay manually + + private final Set doNotEmptyManaTypes = new HashSet<>(); public ManaPool() { autoPayment = true; @@ -66,6 +72,7 @@ public class ManaPool implements Serializable { } this.autoPayment = pool.autoPayment; this.unlockedManaType = pool.unlockedManaType; + this.doNotEmptyManaTypes.addAll(pool.doNotEmptyManaTypes); } public int getRed() { @@ -156,30 +163,35 @@ public class ManaPool implements Serializable { return get(ManaType.COLORLESS); } - public int emptyManaType(List manaTypeArray) { - int total = count(); + public void clearEmptyManaPoolRules() { + doNotEmptyManaTypes.clear(); + } + + public void addDoNotEmptyManaType(ManaType manaType) { + doNotEmptyManaTypes.add(manaType); + } + + public int emptyPool(Game game) { + int total = 0; Iterator it = manaItems.iterator(); while (it.hasNext()) { ManaPoolItem item = it.next(); - for (ManaType manaType: manaTypeArray) { - if (item.get(manaType) > 0) { - total += item.get(manaType); - while (item.get(manaType) > 0) { - item.remove(manaType); + for (ManaType manaType : ManaType.values()) { + if (item.get(manaType) > 0 && !doNotEmptyManaTypes.contains(manaType)) { + if (!item.getDuration().equals(Duration.EndOfTurn) || game.getPhase().getType().equals(TurnPhase.END)) { + if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, null, null, null))) { + int amount = item.get(manaType); + item.clear(manaType); + item.add(ManaType.COLORLESS, amount); + } else { + total += item.get(manaType); + item.clear(manaType); + } } } } - if (item.count() == 0) { - it.remove(); - } - } - return total; - } - - public int emptyPool() { - int total = count(); - manaItems.clear(); - return total; + } + return total; } private int payX(Ability ability, Game game) { @@ -305,12 +317,24 @@ public class ManaPool implements Serializable { } public void addMana(Mana manaToAdd, Game game, Ability source) { + addMana(manaToAdd, game, source, false); + } + + public void addMana(Mana manaToAdd, Game game, Ability source, boolean emptyOnTurnsEnd) { Mana mana = manaToAdd.copy(); if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source.getSourceId(), source.getControllerId(), mana))) { if (mana instanceof ConditionalMana) { - this.manaItems.add(new ManaPoolItem((ConditionalMana)mana, source.getSourceId())); + ManaPoolItem item = new ManaPoolItem((ConditionalMana)mana, source.getSourceId()); + if (emptyOnTurnsEnd) { + item.setDuration(Duration.EndOfTurn); + } + this.manaItems.add(item); } else { - this.manaItems.add(new ManaPoolItem(mana.getRed(), mana.getGreen(), mana.getBlue(), mana.getWhite(), mana.getBlack(), mana.getColorless(), source.getSourceId(), mana.getFlag())); + ManaPoolItem item = new ManaPoolItem(mana.getRed(), mana.getGreen(), mana.getBlue(), mana.getWhite(), mana.getBlack(), mana.getColorless(), source.getSourceId(), mana.getFlag()); + if (emptyOnTurnsEnd) { + item.setDuration(Duration.EndOfTurn); + } + this.manaItems.add(item); } GameEvent event = GameEvent.getEvent(GameEvent.EventType.MANA_ADDED, source.getId(), source.getSourceId(), source.getControllerId()); event.setData(mana.toString()); diff --git a/Mage/src/mage/players/ManaPoolItem.java b/Mage/src/mage/players/ManaPoolItem.java index 3c65b91567..26984cd1bb 100644 --- a/Mage/src/mage/players/ManaPoolItem.java +++ b/Mage/src/mage/players/ManaPoolItem.java @@ -31,6 +31,7 @@ import java.io.Serializable; import java.util.UUID; import mage.ConditionalMana; import mage.Mana; +import mage.constants.Duration; import mage.constants.ManaType; /** @@ -48,6 +49,7 @@ public class ManaPoolItem implements Serializable { private ConditionalMana conditionalMana; private UUID sourceId; private boolean flag = false; + private Duration duration; public ManaPoolItem() {} @@ -60,6 +62,7 @@ public class ManaPoolItem implements Serializable { this.colorless = colorless; this.sourceId = sourceId; this.flag = flag; + this.duration = Duration.EndOfStep; } public ManaPoolItem(ConditionalMana conditionalMana, UUID sourceId) { @@ -67,6 +70,7 @@ public class ManaPoolItem implements Serializable { this.sourceId = sourceId; this.conditionalMana.setManaProducerId(sourceId); this.flag = conditionalMana.getFlag(); + this.duration = Duration.EndOfStep; } public ManaPoolItem(final ManaPoolItem item) { @@ -81,6 +85,7 @@ public class ManaPoolItem implements Serializable { } this.sourceId = item.sourceId; this.flag = item.flag; + this.duration = item.duration; } public ManaPoolItem copy() { @@ -242,4 +247,59 @@ public class ManaPoolItem implements Serializable { break; } } + + public void clear(ManaType manaType) { + switch(manaType) { + case BLACK: + black = 0; + break; + case BLUE: + blue = 0; + break; + case GREEN: + green = 0; + break; + case RED: + red = 0; + break; + case WHITE: + white = 0; + break; + case COLORLESS: + colorless = 0; + break; + } + } + + public void add(ManaType manaType, int amount) { + switch(manaType) { + case BLACK: + black += amount; + break; + case BLUE: + blue += amount;; + break; + case GREEN: + green += amount;; + break; + case RED: + red += amount;; + break; + case WHITE: + white += amount;; + break; + case COLORLESS: + colorless += amount;; + break; + } + } + + public Duration getDuration() { + return duration; + } + + public void setDuration(Duration duration) { + this.duration = duration; + } + } diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 7b35bd14ad..300072b3be 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -433,6 +433,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.topCardRevealed = false; this.alternativeSourceCosts.clear(); this.castSourceIdWithoutMana = null; + this.getManaPool().clearEmptyManaPoolRules(); } @Override