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.

This commit is contained in:
LevelX2 2014-10-20 17:49:40 +02:00
parent 59702e4867
commit f893503acd
10 changed files with 185 additions and 103 deletions

View file

@ -97,15 +97,6 @@ public class KruphixGodOfHorizons extends CardImpl {
class KruphixGodOfHorizonsEffect extends ReplacementEffectImpl { class KruphixGodOfHorizonsEffect extends ReplacementEffectImpl {
private static final List<ManaType> 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() { public KruphixGodOfHorizonsEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit); super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "If unused mana would empty from your mana pool, that mana becomes colorless instead"; staticText = "If unused mana would empty from your mana pool, that mana becomes colorless instead";
@ -127,15 +118,7 @@ class KruphixGodOfHorizonsEffect extends ReplacementEffectImpl {
@Override @Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) { public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Player player = game.getPlayer(event.getPlayerId()); return true;
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;
} }
@Override @Override

View file

@ -30,15 +30,19 @@ package mage.sets.tenth;
import java.util.UUID; import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.ContinuousEffectImpl;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.ManaType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.Rarity; import mage.constants.Rarity;
import mage.constants.SubLayer;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; 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); this.color.setGreen(true);
// Mana pools don't empty as steps and phases end. // 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); super(Duration.WhileOnBattlefield, Outcome.Detriment);
staticText = "Mana pools don't empty as steps and phases end"; staticText = "Mana pools don't empty as steps and phases end";
} }
public UpwellingReplacementEffect(final UpwellingReplacementEffect effect) { public UpwellingRuleEffect(final UpwellingRuleEffect effect) {
super(effect); super(effect);
} }
@Override @Override
public UpwellingReplacementEffect copy() { public UpwellingRuleEffect copy() {
return new UpwellingReplacementEffect(this); 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 @Override
@ -89,15 +114,7 @@ class UpwellingReplacementEffect extends ReplacementEffectImpl {
} }
@Override @Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) { public boolean hasLayer(Layer layer) {
return true; return layer == Layer.RulesEffects;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (event.getType() == GameEvent.EventType.EMPTY_MANA_POOLS) {
return true;
}
return false;
} }
} }

View file

@ -27,21 +27,24 @@
*/ */
package mage.sets.worldwake; package mage.sets.worldwake;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import mage.constants.*;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.ManaTypeInManaPoolCount; 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.abilities.effects.common.continious.BoostSourceEffect;
import mage.cards.CardImpl; 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.Game;
import mage.game.events.GameEvent;
import mage.players.Player; import mage.players.Player;
/** /**
@ -60,7 +63,7 @@ public class OmnathLocusOfMana extends CardImpl {
this.toughness = new MageInt(1); this.toughness = new MageInt(1);
// Green mana doesn't empty from your mana pool as steps and phases end. // 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 // Omnath, Locus of Mana gets +1/+1 for each green mana in your mana pool
DynamicValue boost = new ManaTypeInManaPoolCount(ManaType.GREEN); DynamicValue boost = new ManaTypeInManaPoolCount(ManaType.GREEN);
@ -78,50 +81,37 @@ public class OmnathLocusOfMana extends CardImpl {
} }
} }
class OmnathReplacementEffect extends ReplacementEffectImpl { class OmnathRuleEffect extends ContinuousEffectImpl {
private static final List<ManaType> manaTypes = new ArrayList<>();
static {
manaTypes.add(ManaType.BLACK);
manaTypes.add(ManaType.BLUE);
manaTypes.add(ManaType.RED);
manaTypes.add(ManaType.WHITE);
manaTypes.add(ManaType.COLORLESS);
}
public OmnathReplacementEffect() { public OmnathRuleEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit); super(Duration.WhileOnBattlefield, Outcome.Detriment);
staticText = "Green mana doesn't empty from your mana pool as steps and phases end"; 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); super(effect);
} }
@Override @Override
public OmnathReplacementEffect copy() { public OmnathRuleEffect copy() {
return new OmnathReplacementEffect(this); 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 @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
return false; return false;
} }
@Override @Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) { public boolean hasLayer(Layer layer) {
Player player = game.getPlayer(event.getPlayerId()); return layer == Layer.RulesEffects;
if (player != null){
player.getManaPool().emptyManaType(manaTypes);
}
return true;
} }
}
@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;
}
}

View file

@ -21,22 +21,30 @@ import mage.players.Player;
public class AddManaToManaPoolEffect extends OneShotEffect { public class AddManaToManaPoolEffect extends OneShotEffect {
protected Mana mana; 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 * Adds mana to the mana pool of target pointer player
* *
* @param mana mana that will be added to the pool * @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") * @param textManaPoolOwner text that references to the mana pool owner (e.g. "damaged player's")
*/ * @param emptyOnTurnsEnd if set, the mana will empty only on end of turnstep
public AddManaToManaPoolEffect(Mana mana, String textManaPoolOwner) { *
*/
public AddManaToManaPoolEffect(Mana mana, String textManaPoolOwner, boolean emptyOnTurnsEnd) {
super(Outcome.PutManaInPool); super(Outcome.PutManaInPool);
this.mana = mana; this.mana = mana;
this.emptyOnlyOnTurnsEnd = emptyOnTurnsEnd;
this.staticText = "add " + mana.toString() + " to " + textManaPoolOwner + " mana pool"; this.staticText = "add " + mana.toString() + " to " + textManaPoolOwner + " mana pool";
} }
public AddManaToManaPoolEffect(final AddManaToManaPoolEffect effect) { public AddManaToManaPoolEffect(final AddManaToManaPoolEffect effect) {
super(effect); super(effect);
this.mana = effect.mana; this.mana = effect.mana;
this.emptyOnlyOnTurnsEnd = effect.emptyOnlyOnTurnsEnd;
} }
@Override @Override
@ -48,7 +56,7 @@ public class AddManaToManaPoolEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
if (player != null) { if (player != null) {
player.getManaPool().addMana(mana, game, source); player.getManaPool().addMana(mana, game, source, emptyOnlyOnTurnsEnd);
return true; return true;
} }
return false; return false;

View file

@ -13,6 +13,7 @@ public enum Duration {
EndOfTurn("until end of turn"), EndOfTurn("until end of turn"),
UntilYourNextTurn("until your next turn"), UntilYourNextTurn("until your next turn"),
EndOfCombat("until end of combat"), EndOfCombat("until end of combat"),
EndOfStep("until end of phase step"),
Custom(""); Custom("");
private final String text; private final String text;

View file

@ -1168,11 +1168,9 @@ public abstract class GameImpl implements Game, Serializable {
@Override @Override
public void emptyManaPools() { public void emptyManaPools() {
if (!replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOLS, null, null, null))) { for (Player player: getPlayers().values()) {
for (Player player: getPlayers().values()) { if (!replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, player.getId(), null, player.getId()))) {
if (!replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, player.getId(), null, player.getId()))) { player.getManaPool().emptyPool(this);
player.getManaPool().emptyPool();
}
} }
} }
} }
@ -1186,7 +1184,7 @@ public abstract class GameImpl implements Game, Serializable {
public void addEffect(ContinuousEffect continuousEffect, Ability source) { public void addEffect(ContinuousEffect continuousEffect, Ability source) {
Ability newAbility = source.copy(); Ability newAbility = source.copy();
ContinuousEffect newEffect = (ContinuousEffect)continuousEffect.copy(); ContinuousEffect newEffect = continuousEffect.copy();
newEffect.newId(); newEffect.newId();
newEffect.setTimestamp(); newEffect.setTimestamp();
newEffect.init(newAbility, this); newEffect.init(newAbility, this);

View file

@ -75,7 +75,7 @@ public class GameEvent {
END_PHASE, END_PHASE_PRE, END_PHASE_POST, END_PHASE, END_PHASE_PRE, END_PHASE_POST,
END_TURN_STEP_PRE, END_TURN_STEP, END_TURN_STEP_POST, END_TURN_STEP_PRE, END_TURN_STEP, END_TURN_STEP_POST,
CLEANUP_STEP_PRE, CLEANUP_STEP, CLEANUP_STEP_POST, CLEANUP_STEP_PRE, CLEANUP_STEP, CLEANUP_STEP_POST,
EMPTY_MANA_POOLS, EMPTY_MANA_POOL, EMPTY_MANA_POOL,
AT_END_OF_TURN, AT_END_OF_TURN,
//player events //player events

View file

@ -30,13 +30,17 @@ package mage.players;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set;
import mage.ConditionalMana; import mage.ConditionalMana;
import mage.Mana; import mage.Mana;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.constants.AsThoughEffectType; import mage.constants.AsThoughEffectType;
import mage.constants.Duration;
import mage.constants.ManaType; import mage.constants.ManaType;
import mage.constants.TurnPhase;
import mage.filter.Filter; import mage.filter.Filter;
import mage.filter.FilterMana; import mage.filter.FilterMana;
import mage.game.Game; 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 boolean autoPayment; // auto payment from mana pool: true - mode is active
private ManaType unlockedManaType; // type of mana that was selected to pay manually private ManaType unlockedManaType; // type of mana that was selected to pay manually
private final Set<ManaType> doNotEmptyManaTypes = new HashSet<>();
public ManaPool() { public ManaPool() {
autoPayment = true; autoPayment = true;
@ -66,6 +72,7 @@ public class ManaPool implements Serializable {
} }
this.autoPayment = pool.autoPayment; this.autoPayment = pool.autoPayment;
this.unlockedManaType = pool.unlockedManaType; this.unlockedManaType = pool.unlockedManaType;
this.doNotEmptyManaTypes.addAll(pool.doNotEmptyManaTypes);
} }
public int getRed() { public int getRed() {
@ -156,30 +163,35 @@ public class ManaPool implements Serializable {
return get(ManaType.COLORLESS); return get(ManaType.COLORLESS);
} }
public int emptyManaType(List<ManaType> manaTypeArray) { public void clearEmptyManaPoolRules() {
int total = count(); doNotEmptyManaTypes.clear();
}
public void addDoNotEmptyManaType(ManaType manaType) {
doNotEmptyManaTypes.add(manaType);
}
public int emptyPool(Game game) {
int total = 0;
Iterator<ManaPoolItem> it = manaItems.iterator(); Iterator<ManaPoolItem> it = manaItems.iterator();
while (it.hasNext()) { while (it.hasNext()) {
ManaPoolItem item = it.next(); ManaPoolItem item = it.next();
for (ManaType manaType: manaTypeArray) { for (ManaType manaType : ManaType.values()) {
if (item.get(manaType) > 0) { if (item.get(manaType) > 0 && !doNotEmptyManaTypes.contains(manaType)) {
total += item.get(manaType); if (!item.getDuration().equals(Duration.EndOfTurn) || game.getPhase().getType().equals(TurnPhase.END)) {
while (item.get(manaType) > 0) { if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, null, null, null))) {
item.remove(manaType); 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;
}
}
return total;
}
public int emptyPool() {
int total = count();
manaItems.clear();
return total;
} }
private int payX(Ability ability, Game game) { 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) { 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(); Mana mana = manaToAdd.copy();
if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source.getSourceId(), source.getControllerId(), mana))) { if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source.getSourceId(), source.getControllerId(), mana))) {
if (mana instanceof ConditionalMana) { 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 { } 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()); GameEvent event = GameEvent.getEvent(GameEvent.EventType.MANA_ADDED, source.getId(), source.getSourceId(), source.getControllerId());
event.setData(mana.toString()); event.setData(mana.toString());

View file

@ -31,6 +31,7 @@ import java.io.Serializable;
import java.util.UUID; import java.util.UUID;
import mage.ConditionalMana; import mage.ConditionalMana;
import mage.Mana; import mage.Mana;
import mage.constants.Duration;
import mage.constants.ManaType; import mage.constants.ManaType;
/** /**
@ -48,6 +49,7 @@ public class ManaPoolItem implements Serializable {
private ConditionalMana conditionalMana; private ConditionalMana conditionalMana;
private UUID sourceId; private UUID sourceId;
private boolean flag = false; private boolean flag = false;
private Duration duration;
public ManaPoolItem() {} public ManaPoolItem() {}
@ -60,6 +62,7 @@ public class ManaPoolItem implements Serializable {
this.colorless = colorless; this.colorless = colorless;
this.sourceId = sourceId; this.sourceId = sourceId;
this.flag = flag; this.flag = flag;
this.duration = Duration.EndOfStep;
} }
public ManaPoolItem(ConditionalMana conditionalMana, UUID sourceId) { public ManaPoolItem(ConditionalMana conditionalMana, UUID sourceId) {
@ -67,6 +70,7 @@ public class ManaPoolItem implements Serializable {
this.sourceId = sourceId; this.sourceId = sourceId;
this.conditionalMana.setManaProducerId(sourceId); this.conditionalMana.setManaProducerId(sourceId);
this.flag = conditionalMana.getFlag(); this.flag = conditionalMana.getFlag();
this.duration = Duration.EndOfStep;
} }
public ManaPoolItem(final ManaPoolItem item) { public ManaPoolItem(final ManaPoolItem item) {
@ -81,6 +85,7 @@ public class ManaPoolItem implements Serializable {
} }
this.sourceId = item.sourceId; this.sourceId = item.sourceId;
this.flag = item.flag; this.flag = item.flag;
this.duration = item.duration;
} }
public ManaPoolItem copy() { public ManaPoolItem copy() {
@ -242,4 +247,59 @@ public class ManaPoolItem implements Serializable {
break; 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;
}
} }

View file

@ -433,6 +433,7 @@ public abstract class PlayerImpl implements Player, Serializable {
this.topCardRevealed = false; this.topCardRevealed = false;
this.alternativeSourceCosts.clear(); this.alternativeSourceCosts.clear();
this.castSourceIdWithoutMana = null; this.castSourceIdWithoutMana = null;
this.getManaPool().clearEmptyManaPoolRules();
} }
@Override @Override