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 {
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() {
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

View file

@ -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;
}
}

View file

@ -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 {
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() {
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;
}
@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;
public boolean hasLayer(Layer layer) {
return layer == Layer.RulesEffects;
}
}

View file

@ -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")
* @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);
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;

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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;
@ -55,6 +59,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<ManaType> doNotEmptyManaTypes = new HashSet<>();
public ManaPool() {
autoPayment = true;
unlockedManaType = null;
@ -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,32 +163,37 @@ public class ManaPool implements Serializable {
return get(ManaType.COLORLESS);
}
public int emptyManaType(List<ManaType> 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<ManaPoolItem> 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;
}
private int payX(Ability ability, Game game) {
int total = 0;
Iterator<ManaPoolItem> it = manaItems.iterator();
@ -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());

View file

@ -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;
}
}

View file

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