diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index af09e275ed6..c643a567a42 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1601,7 +1601,7 @@ public class ComputerUtilCard { pumped.addChangedCardKeywords(kws, null, false, false, timestamp); Set types = c.getCounters().keySet(); for(CounterType ct : types) { - pumped.addCounterFireNoEvents(ct, c.getCounters(ct), ai, true); + pumped.addCounterFireNoEvents(ct, c.getCounters(ct), ai, true, null); } //Copies tap-state and extra keywords (auras, equipment, etc.) if (c.isTapped()) { diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index 8d5ea58fb29..693ebba2690 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -1016,7 +1016,7 @@ public abstract class GameState { String[] allCounterStrings = counterString.split(","); for (final String counterPair : allCounterStrings) { String[] pair = counterPair.split("=", 2); - entity.addCounter(CounterType.valueOf(pair[0]), Integer.parseInt(pair[1]), null, false, false); + entity.addCounter(CounterType.valueOf(pair[0]), Integer.parseInt(pair[1]), null, false, false, null); } } diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersRemoveAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersRemoveAi.java index 67b835ffc8a..ee6335ac95b 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersRemoveAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersRemoveAi.java @@ -6,6 +6,7 @@ import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilMana; import forge.ai.SpellAbilityAi; import forge.game.Game; +import forge.game.GameEntity; import forge.game.GlobalRuleChange; import forge.game.ability.AbilityUtils; import forge.game.card.*; @@ -354,7 +355,25 @@ public class CountersRemoveAi extends SpellAbilityAi { */ @Override public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map params) { - // TODO Auto-generated method stub + GameEntity target = (GameEntity) params.get("Target"); + CounterType type = (CounterType) params.get("CounterType"); + + if (target instanceof Card) { + Card targetCard = (Card) target; + if (targetCard.getController().isOpponentOf(player)) { + return !ComputerUtil.isNegativeCounter(type, targetCard) ? max : min; + } else { + return ComputerUtil.isNegativeCounter(type, targetCard) ? max : min; + } + } else if (target instanceof Player) { + Player targetPlayer = (Player) target; + if (targetPlayer.isOpponentOf(player)) { + return !type.equals(CounterType.POISON) ? max : min; + } else { + return type.equals(CounterType.POISON) ? max : min; + } + } + return super.chooseNumber(player, sa, min, max, params); } @@ -370,30 +389,49 @@ public class CountersRemoveAi extends SpellAbilityAi { return super.chooseCounterType(options, sa, params); } Player ai = sa.getActivatingPlayer(); - Card target = (Card) params.get("Target"); + GameEntity target = (GameEntity) params.get("Target"); - if (target.getController().isOpponentOf(ai)) { - // if its a Planeswalker try to remove Loyality first - if (target.isPlaneswalker()) { - return CounterType.LOYALTY; - } - for (CounterType type : options) { - if (!ComputerUtil.isNegativeCounter(type, target)) { - return type; + if (target instanceof Card) { + Card targetCard = (Card) target; + if (targetCard.getController().isOpponentOf(ai)) { + // if its a Planeswalker try to remove Loyality first + if (targetCard.isPlaneswalker()) { + return CounterType.LOYALTY; + } + for (CounterType type : options) { + if (!ComputerUtil.isNegativeCounter(type, targetCard)) { + return type; + } + } + } else { + if (options.contains(CounterType.M1M1) && targetCard.hasKeyword(Keyword.PERSIST)) { + return CounterType.M1M1; + } else if (options.contains(CounterType.P1P1) && targetCard.hasKeyword(Keyword.UNDYING)) { + return CounterType.P1P1; + } + for (CounterType type : options) { + if (ComputerUtil.isNegativeCounter(type, targetCard)) { + return type; + } } } - } else { - if (options.contains(CounterType.M1M1) && target.hasKeyword(Keyword.PERSIST)) { - return CounterType.M1M1; - } else if (options.contains(CounterType.P1P1) && target.hasKeyword(Keyword.UNDYING)) { - return CounterType.M1M1; - } - for (CounterType type : options) { - if (ComputerUtil.isNegativeCounter(type, target)) { - return type; + } else if (target instanceof Player) { + Player targetPlayer = (Player) target; + if (targetPlayer.isOpponentOf(ai)) { + for (CounterType type : options) { + if (!type.equals(CounterType.POISON)) { + return type; + } + } + } else { + for (CounterType type : options) { + if (type.equals(CounterType.POISON)) { + return type; + } } } } + return super.chooseCounterType(options, sa, params); } } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 9b1577b1c68..6f0701f7e16 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -247,7 +247,7 @@ public class GameAction { // fake etb counters thing, then if something changed, // need to apply checkStaticAbilities again if(!noLandLKI.isLand()) { - if (noLandLKI.putEtbCounters()) { + if (noLandLKI.putEtbCounters(null)) { // counters are added need to check again checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList); } @@ -380,11 +380,34 @@ public class GameAction { } } + GameEntityCounterTable table = new GameEntityCounterTable(); + + // need to suspend cards own replacement effects + if (!suppress) { + if (toBattlefield && !copied.getEtbCounters().isEmpty()) { + for (final ReplacementEffect re : copied.getReplacementEffects()) { + re.setSuppressed(true); + } + } + } + // "enter the battlefield as a copy" - apply code here // but how to query for input here and continue later while the callers assume synchronous result? zoneTo.add(copied, position, c); // the modified state of the card is also reported here (e.g. for Morbid + Awaken) c.setZone(zoneTo); + // do ETB counters after zone add + if (!suppress) { + if (toBattlefield ) { + copied.putEtbCounters(table); + // enable replacement effects again + for (final ReplacementEffect re : copied.getReplacementEffects()) { + re.setSuppressed(false); + } + } + copied.clearEtbCounters(); + } + if (fromBattlefield) { c.setDamage(0); //clear damage after a card leaves the battlefield c.setHasBeenDealtDeathtouchDamage(false); @@ -400,16 +423,7 @@ public class GameAction { game.getTriggerHandler().clearInstrinsicActiveTriggers(c, zoneFrom); game.getTriggerHandler().registerActiveTrigger(lastKnownInfo, false); - // do ETB counters after StaticAbilities check - if (!suppress) { - if (toBattlefield) { - if (copied.putEtbCounters()) { - // if counter where put of card, call checkStaticAbilities again - checkStaticAbilities(); - } - } - copied.clearEtbCounters(); - } + table.triggerCountersPutAll(game); // play the change zone sound game.fireEvent(new GameEventCardChangeZone(c, zoneFrom, zoneTo)); diff --git a/forge-game/src/main/java/forge/game/GameEntity.java b/forge-game/src/main/java/forge/game/GameEntity.java index 0bf288d9e25..1e7ecd2d3f9 100644 --- a/forge-game/src/main/java/forge/game/GameEntity.java +++ b/forge-game/src/main/java/forge/game/GameEntity.java @@ -66,52 +66,52 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { } public final int addDamage(final int damage, final Card source, boolean isCombat, boolean noPrevention, - final CardDamageMap damageMap, final CardDamageMap preventMap, final SpellAbility cause) { + final CardDamageMap damageMap, final CardDamageMap preventMap, GameEntityCounterTable counterTable, final SpellAbility cause) { if (noPrevention) { - return addDamageWithoutPrevention(damage, source, damageMap, preventMap, cause); + return addDamageWithoutPrevention(damage, source, damageMap, preventMap, counterTable, cause); } else if (isCombat) { - return addCombatDamage(damage, source, damageMap, preventMap); + return addCombatDamage(damage, source, damageMap, preventMap, counterTable); } else { - return addDamage(damage, source, damageMap, preventMap, cause); + return addDamage(damage, source, damageMap, preventMap, counterTable, cause); } } public int addDamage(final int damage, final Card source, final CardDamageMap damageMap, - final CardDamageMap preventMap, final SpellAbility cause) { + final CardDamageMap preventMap, GameEntityCounterTable counterTable, final SpellAbility cause) { int damageToDo = damage; - damageToDo = replaceDamage(damageToDo, source, false, true, damageMap, preventMap, cause); + damageToDo = replaceDamage(damageToDo, source, false, true, damageMap, preventMap, counterTable, cause); damageToDo = preventDamage(damageToDo, source, false, preventMap, cause); - return addDamageAfterPrevention(damageToDo, source, false, damageMap); + return addDamageAfterPrevention(damageToDo, source, false, damageMap, counterTable); } public final int addCombatDamage(final int damage, final Card source, final CardDamageMap damageMap, - final CardDamageMap preventMap) { + final CardDamageMap preventMap, GameEntityCounterTable counterTable) { int damageToDo = damage; - damageToDo = replaceDamage(damageToDo, source, true, true, damageMap, preventMap, null); + damageToDo = replaceDamage(damageToDo, source, true, true, damageMap, preventMap, counterTable, null); damageToDo = preventDamage(damageToDo, source, true, preventMap, null); if (damageToDo > 0) { source.getDamageHistory().registerCombatDamage(this); } // damage prevention is already checked - return addCombatDamageBase(damageToDo, source, damageMap); + return addCombatDamageBase(damageToDo, source, damageMap, counterTable); } - protected int addCombatDamageBase(final int damage, final Card source, CardDamageMap damageMap) { - return addDamageAfterPrevention(damage, source, true, damageMap); + protected int addCombatDamageBase(final int damage, final Card source, CardDamageMap damageMap, GameEntityCounterTable counterTable) { + return addDamageAfterPrevention(damage, source, true, damageMap, counterTable); } public int addDamageWithoutPrevention(final int damage, final Card source, final CardDamageMap damageMap, - final CardDamageMap preventMap, final SpellAbility cause) { - int damageToDo = replaceDamage(damage, source, false, false, damageMap, preventMap, cause); - return addDamageAfterPrevention(damageToDo, source, false, damageMap); + final CardDamageMap preventMap, GameEntityCounterTable counterTable, final SpellAbility cause) { + int damageToDo = replaceDamage(damage, source, false, false, damageMap, preventMap, counterTable, cause); + return addDamageAfterPrevention(damageToDo, source, false, damageMap, counterTable); } public int replaceDamage(final int damage, final Card source, final boolean isCombat, final boolean prevention, - final CardDamageMap damageMap, final CardDamageMap preventMap, final SpellAbility cause) { + final CardDamageMap damageMap, final CardDamageMap preventMap, GameEntityCounterTable counterTable, final SpellAbility cause) { // Replacement effects final Map repParams = Maps.newHashMap(); repParams.put("Event", "DamageDone"); @@ -122,6 +122,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { repParams.put("NoPreventDamage", !prevention); repParams.put("DamageMap", damageMap); repParams.put("PreventMap", preventMap); + repParams.put("CounterTable", counterTable); if (cause != null) { repParams.put("Cause", cause); } @@ -139,7 +140,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { if (prevention) { newDamage = newTarget.preventDamage(newDamage, source, isCombat, preventMap, cause); } - newTarget.addDamageAfterPrevention(newDamage, source, isCombat, damageMap); + newTarget.addDamageAfterPrevention(newDamage, source, isCombat, damageMap, counterTable); } default: return 0; @@ -147,7 +148,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { } // This function handles damage after replacement and prevention effects are applied - public abstract int addDamageAfterPrevention(final int damage, final Card source, final boolean isCombat, CardDamageMap damageMap); + public abstract int addDamageAfterPrevention(final int damage, final Card source, final boolean isCombat, CardDamageMap damageMap, GameEntityCounterTable counterTable); // This should be also usable by the AI to forecast an effect (so it must // not change the game state) @@ -470,7 +471,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { abstract public void setCounters(final Map allCounters); abstract public boolean canReceiveCounters(final CounterType type); - abstract public int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, final boolean fireEvents); + abstract public int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, final boolean fireEvents, GameEntityCounterTable table); abstract public void subtractCounter(final CounterType counterName, final int n); abstract public void clearCounters(); diff --git a/forge-game/src/main/java/forge/game/GameEntityCounterTable.java b/forge-game/src/main/java/forge/game/GameEntityCounterTable.java new file mode 100644 index 00000000000..23035134714 --- /dev/null +++ b/forge-game/src/main/java/forge/game/GameEntityCounterTable.java @@ -0,0 +1,55 @@ +package forge.game; + +import java.util.Map; + +import com.google.common.collect.ForwardingTable; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Maps; +import com.google.common.collect.Table; + +import forge.game.card.Card; +import forge.game.card.CounterType; +import forge.game.spellability.SpellAbility; +import forge.game.trigger.TriggerType; + +public class GameEntityCounterTable extends ForwardingTable { + + private Table dataMap = HashBasedTable.create(); + + /* + * (non-Javadoc) + * @see com.google.common.collect.ForwardingTable#delegate() + */ + @Override + protected Table delegate() { + return dataMap; + } + + /* (non-Javadoc) + * @see com.google.common.collect.ForwardingTable#put(java.lang.Object, java.lang.Object, java.lang.Object) + */ + @Override + public Integer put(GameEntity rowKey, CounterType columnKey, Integer value) { + Integer old = contains(rowKey, columnKey) ? get(rowKey, columnKey) : 0; + return super.put(rowKey, columnKey, old + value); + } + + public Map filterTable(CounterType type, String valid, Card host, SpellAbility sa) { + Map result = Maps.newHashMap(); + + for (Map.Entry e : column(type).entrySet()) { + if (e.getKey().isValid(valid, host.getController(), host, sa)) { + result.put(e.getKey(), e.getValue()); + } + } + return result; + } + + public void triggerCountersPutAll(final Game game) { + if (!isEmpty()) { + final Map runParams = Maps.newHashMap(); + runParams.put("Objects", this); + game.getTriggerHandler().runTrigger(TriggerType.CounterAddedAll, runParams, false); + } + } +} diff --git a/forge-game/src/main/java/forge/game/ability/effects/AmassEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AmassEffect.java index 71022f407ec..1d15998fe9c 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AmassEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AmassEffect.java @@ -1,6 +1,7 @@ package forge.game.ability.effects; import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -62,14 +63,16 @@ public class AmassEffect extends SpellAbilityEffect { CardCollectionView tgtCards = CardLists.getType(activator.getCardsIn(ZoneType.Battlefield), "Army"); tgtCards = pc.chooseCardsForEffect(tgtCards, sa, "Choose an army to put counters on", 1, 1, false); + GameEntityCounterTable table = new GameEntityCounterTable(); for(final Card tgtCard : tgtCards) { - tgtCard.addCounter(CounterType.P1P1, amount, activator, true); + tgtCard.addCounter(CounterType.P1P1, amount, activator, true, table); game.updateLastStateForCard(tgtCard); if (remember) { card.addRemembered(tgtCard); } } + table.triggerCountersPutAll(game); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java index 2522abf8853..8e1a82e0d1e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java @@ -1,6 +1,7 @@ package forge.game.ability.effects; import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -78,6 +79,8 @@ public class CountersMoveEffect extends SpellAbilityEffect { } } + GameEntityCounterTable table = new GameEntityCounterTable(); + // uses for multi sources -> one defined/target // this needs given counter type if (sa.hasParam("ValidSource")) { @@ -146,8 +149,9 @@ public class CountersMoveEffect extends SpellAbilityEffect { } if (csum > 0) { - dest.addCounter(cType, csum, player, true); + dest.addCounter(cType, csum, player, true, table); game.updateLastStateForCard(dest); + table.triggerCountersPutAll(game); } return; } else if (sa.hasParam("ValidDefined")) { @@ -202,7 +206,7 @@ public class CountersMoveEffect extends SpellAbilityEffect { if (cnum > 0) { source.subtractCounter(cType, cnum); - cur.addCounter(cType, cnum, player, true); + cur.addCounter(cType, cnum, player, true, table); game.updateLastStateForCard(cur); updateSource = true; } @@ -210,6 +214,7 @@ public class CountersMoveEffect extends SpellAbilityEffect { if (updateSource) { // update source game.updateLastStateForCard(source); + table.triggerCountersPutAll(game); } return; } @@ -263,7 +268,7 @@ public class CountersMoveEffect extends SpellAbilityEffect { if (source.getCounters(cType) >= cntToMove) { source.subtractCounter(cType, cntToMove); - cur.addCounter(cType, cntToMove, player, true); + cur.addCounter(cType, cntToMove, player, true, table); game.updateLastStateForCard(cur); } } else { @@ -297,7 +302,7 @@ public class CountersMoveEffect extends SpellAbilityEffect { sa, sb.toString(), 0, Math.min(tgtCounters.get(chosenType), cntToMove), params); if (chosenAmount > 0) { - dest.addCounter(chosenType, chosenAmount, player, true); + dest.addCounter(chosenType, chosenAmount, player, true, table); source.subtractCounter(chosenType, chosenAmount); game.updateLastStateForCard(dest); cntToMove -= chosenAmount; @@ -307,5 +312,6 @@ public class CountersMoveEffect extends SpellAbilityEffect { } // update source game.updateLastStateForCard(source); + table.triggerCountersPutAll(game); } // moveCounterResolve } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersMultiplyEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersMultiplyEffect.java index 8eaced12614..84c1a609ab6 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersMultiplyEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersMultiplyEffect.java @@ -3,6 +3,7 @@ package forge.game.ability.effects; import java.util.Map; import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -43,6 +44,7 @@ public class CountersMultiplyEffect extends SpellAbilityEffect { final CounterType counterType = getCounterType(sa); final int n = Integer.valueOf(sa.getParamOrDefault("Multiplier", "2")) - 1; + GameEntityCounterTable table = new GameEntityCounterTable(); for (final Card tgtCard : getTargetCards(sa)) { Card gameCard = game.getCardState(tgtCard, null); // gameCard is LKI in that case, the card is not in game anymore @@ -52,14 +54,15 @@ public class CountersMultiplyEffect extends SpellAbilityEffect { continue; } if (counterType != null) { - gameCard.addCounter(counterType, gameCard.getCounters(counterType) * n, player, true); + gameCard.addCounter(counterType, gameCard.getCounters(counterType) * n, player, true, table); } else { for (Map.Entry e : gameCard.getCounters().entrySet()) { - gameCard.addCounter(e.getKey(), e.getValue() * n, player, true); + gameCard.addCounter(e.getKey(), e.getValue() * n, player, true, table); } } game.updateLastStateForCard(gameCard); } + table.triggerCountersPutAll(game); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersNoteEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersNoteEffect.java index 21a7f4c9d2c..b2705b69516 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersNoteEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersNoteEffect.java @@ -2,9 +2,10 @@ package forge.game.ability.effects; import java.util.Map.Entry; +import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; -import forge.game.card.CardCollection; import forge.game.card.CounterType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; @@ -20,16 +21,19 @@ public class CountersNoteEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { Card source = sa.getHostCard(); - CardCollection tgtCards = new CardCollection(); - tgtCards.addAll(getDefinedCardsOrTargeted(sa)); + final Game game = source.getGame(); + Player p = sa.getActivatingPlayer(); String mode = sa.getParamOrDefault("Mode", "Load"); - for (Card c : tgtCards) { + + GameEntityCounterTable table = new GameEntityCounterTable(); + for (Card c : getDefinedCardsOrTargeted(sa)) { if (mode.equals(MODE_STORE)) { noteCounters(c, source); } else if (mode.equals(MODE_LOAD)) { - loadCounters(c, source, sa.getActivatingPlayer()); + loadCounters(c, source, p, table); } } + table.triggerCountersPutAll(game); } private void noteCounters(Card notee, Card source) { @@ -40,11 +44,13 @@ public class CountersNoteEffect extends SpellAbilityEffect { } } - private void loadCounters(Card notee, Card source, final Player p) { + private void loadCounters(Card notee, Card source, final Player p, GameEntityCounterTable table) { for(Entry svar : source.getSVars().entrySet()) { String key = svar.getKey(); if (key.startsWith(NOTE_COUNTERS)) { - notee.addCounter(CounterType.getType(key.substring(NOTE_COUNTERS.length())), Integer.parseInt(svar.getValue()), p, false); + notee.addCounter( + CounterType.getType(key.substring(NOTE_COUNTERS.length())), + Integer.parseInt(svar.getValue()), p, false, table); } // TODO Probably should "remove" the svars that were temporarily used } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java index 28616c87f91..5f6d56a5218 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java @@ -2,6 +2,7 @@ package forge.game.ability.effects; import forge.game.Game; import forge.game.GameEntity; +import forge.game.GameEntityCounterTable; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardLists; @@ -43,14 +44,16 @@ public class CountersProliferateEffect extends SpellAbilityEffect { List result = pc.chooseEntitiesForEffect(list, 0, list.size(), null, sa, "Choose any number of permanents and/or players for proliferate", p); + GameEntityCounterTable table = new GameEntityCounterTable(); for (final GameEntity ge : result) { for (final CounterType ct : ge.getCounters().keySet()) { - ge.addCounter(ct, 1, p, true, true); + ge.addCounter(ct, 1, p, true, true, table); } if (ge instanceof Card) { Card c = (Card) ge; game.updateLastStateForCard(c); } } + table.triggerCountersPutAll(game); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java index cf27b138ff8..d611df3baf0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java @@ -1,6 +1,7 @@ package forge.game.ability.effects; import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -63,15 +64,13 @@ public class CountersPutAllEffect extends SpellAbilityEffect { placer = AbilityUtils.getDefinedPlayers(host, pstr, sa).get(0); } + GameEntityCounterTable table = new GameEntityCounterTable(); for (final Card tgtCard : cards) { - if (game.getZoneOf(tgtCard).is(ZoneType.Battlefield)) { - tgtCard.addCounter(CounterType.valueOf(type), counterAmount, placer, true); - } else { - // adding counters to something like re-suspend cards - tgtCard.addCounter(CounterType.valueOf(type), counterAmount, placer, false); - } + boolean inBattlefield = game.getZoneOf(tgtCard).is(ZoneType.Battlefield); + tgtCard.addCounter(CounterType.valueOf(type), counterAmount, placer, inBattlefield, table); game.updateLastStateForCard(tgtCard); } + table.triggerCountersPutAll(game); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java index 164dd0542fa..a16037a32ed 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java @@ -6,6 +6,7 @@ import com.google.common.collect.Sets; import forge.game.Game; import forge.game.GameEntity; +import forge.game.GameEntityCounterTable; import forge.game.GameObject; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; @@ -133,6 +134,8 @@ public class CountersPutEffect extends SpellAbilityEffect { tgtObjects.addAll(getDefinedOrTargeted(sa, "Defined")); } + GameEntityCounterTable table = new GameEntityCounterTable(); + for (final GameObject obj : tgtObjects) { // check if the object is still in game or if it was moved Card gameCard = null; @@ -162,10 +165,10 @@ public class CountersPutEffect extends SpellAbilityEffect { if (eachExistingCounter) { for(CounterType ct : choices) { if (obj instanceof Player) { - ((Player) obj).addCounter(ct, counterAmount, placer, true); + ((Player) obj).addCounter(ct, counterAmount, placer, true, table); } if (obj instanceof Card) { - gameCard.addCounter(ct, counterAmount, placer, true); + gameCard.addCounter(ct, counterAmount, placer, true, table); } } continue; @@ -249,7 +252,7 @@ public class CountersPutEffect extends SpellAbilityEffect { if (etbcounter) { tgtCard.addEtbCounter(counterType, counterAmount, placer); } else { - tgtCard.addCounter(counterType, counterAmount, placer, true); + tgtCard.addCounter(counterType, counterAmount, placer, true, table); } if (remember) { final int value = tgtCard.getTotalCountersToAdd(); @@ -287,7 +290,7 @@ public class CountersPutEffect extends SpellAbilityEffect { if (etbcounter) { tgtCard.addEtbCounter(counterType, counterAmount, placer); } else { - tgtCard.addCounter(counterType, counterAmount, placer, false); + tgtCard.addCounter(counterType, counterAmount, placer, false, table); } } game.updateLastStateForCard(tgtCard); @@ -295,9 +298,10 @@ public class CountersPutEffect extends SpellAbilityEffect { } else if (obj instanceof Player) { // Add Counters to players! Player pl = (Player) obj; - pl.addCounter(counterType, counterAmount, placer, true); + pl.addCounter(counterType, counterAmount, placer, true, table); } } + table.triggerCountersPutAll(game); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java index 8a45a41f78d..fb1f4c58e51 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java @@ -1,6 +1,7 @@ package forge.game.ability.effects; import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -57,6 +58,8 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect { ctype = CounterType.valueOf(sa.getParam("CounterType")); } + GameEntityCounterTable table = new GameEntityCounterTable(); + for (final Card tgtCard : getDefinedCardsOrTargeted(sa)) { Card gameCard = game.getCardState(tgtCard, null); // gameCard is LKI in that case, the card is not in game anymore @@ -69,19 +72,20 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect { if (gameCard.hasCounters()) { if (sa.hasParam("EachExistingCounter")) { for (CounterType listType : Lists.newArrayList(gameCard.getCounters().keySet())) { - addOrRemoveCounter(sa, gameCard, listType, counterAmount); + addOrRemoveCounter(sa, gameCard, listType, counterAmount, table); } } else { - addOrRemoveCounter(sa, gameCard, ctype, counterAmount); + addOrRemoveCounter(sa, gameCard, ctype, counterAmount, table); } game.updateLastStateForCard(gameCard); } } } + table.triggerCountersPutAll(game); } private void addOrRemoveCounter(final SpellAbility sa, final Card tgtCard, CounterType ctype, - final int counterAmount) { + final int counterAmount, GameEntityCounterTable table) { final Player pl = sa.getActivatingPlayer(); final PlayerController pc = pl.getController(); @@ -106,7 +110,7 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect { boolean apply = zone == null || zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Stack); - tgtCard.addCounter(chosenType, counterAmount, pl, apply); + tgtCard.addCounter(chosenType, counterAmount, pl, apply, table); } else { tgtCard.subtractCounter(chosenType, counterAmount); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java index f8142b029fd..15a4a69bcf2 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java @@ -1,6 +1,7 @@ package forge.game.ability.effects; import forge.game.Game; +import forge.game.GameEntity; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -106,10 +107,20 @@ public class CountersRemoveEffect extends SpellAbilityEffect { for (final Player tgtPlayer : getTargetPlayers(sa)) { // Removing energy if (!sa.usesTargeting() || tgtPlayer.canBeTargetedBy(sa)) { - if (num.equals("All")) { - cntToRemove = tgtPlayer.getCounters(counterType); + if (type.equals("All")) { + for (Map.Entry e : tgtPlayer.getCounters().entrySet()) { + tgtPlayer.subtractCounter(e.getKey(), e.getValue()); + } + } else { + if (num.equals("All")) { + cntToRemove = tgtPlayer.getCounters(counterType); + } + if (type.equals("Any")) { + removeAnyType(tgtPlayer, cntToRemove, sa); + } else { + tgtPlayer.subtractCounter(counterType, cntToRemove); + } } - tgtPlayer.subtractCounter(counterType, cntToRemove); } } @@ -152,32 +163,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect { } if (type.equals("Any")) { - while (cntToRemove > 0 && gameCard.hasCounters()) { - final Map tgtCounters = gameCard.getCounters(); - Map params = Maps.newHashMap(); - params.put("Target", gameCard); - - String prompt = "Select type of counters to remove"; - CounterType chosenType = pc.chooseCounterType( - ImmutableList.copyOf(tgtCounters.keySet()), sa, prompt, params); - prompt = "Select the number of " + chosenType.getName() + " counters to remove"; - int max = Math.min(cntToRemove, tgtCounters.get(chosenType)); - params = Maps.newHashMap(); - params.put("Target", gameCard); - params.put("CounterType", chosenType); - int chosenAmount = pc.chooseNumber(sa, prompt, 1, max, params); - - if (chosenAmount > 0) { - gameCard.subtractCounter(chosenType, chosenAmount); - game.updateLastStateForCard(gameCard); - if (rememberRemoved) { - for (int i = 0; i < chosenAmount; i++) { - card.addRemembered(Pair.of(chosenType, i)); - } - } - cntToRemove -= chosenAmount; - } - } + removeAnyType(gameCard, cntToRemove, sa); } else { cntToRemove = Math.min(cntToRemove, gameCard.getCounters(counterType)); @@ -189,7 +175,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect { String title = "Select the number of " + type + " counters to remove"; cntToRemove = pc.chooseNumber(sa, title, 0, cntToRemove, params); } - + } if (cntToRemove > 0) { gameCard.subtractCounter(counterType, cntToRemove); @@ -212,4 +198,47 @@ public class CountersRemoveEffect extends SpellAbilityEffect { } } + + protected void removeAnyType(GameEntity entity, int cntToRemove, SpellAbility sa) { + boolean rememberRemoved = sa.hasParam("RememberRemoved"); + + final Card card = sa.getHostCard(); + final Game game = card.getGame(); + final Player player = sa.getActivatingPlayer(); + + PlayerController pc = player.getController(); + + while (cntToRemove > 0 && entity.hasCounters()) { + final Map tgtCounters = entity.getCounters(); + Map params = Maps.newHashMap(); + params.put("Target", entity); + + String prompt = "Select type of counters to remove"; + CounterType chosenType = pc.chooseCounterType( + ImmutableList.copyOf(tgtCounters.keySet()), sa, prompt, params); + prompt = "Select the number of " + chosenType.getName() + " counters to remove"; + int max = Math.min(cntToRemove, tgtCounters.get(chosenType)); + params = Maps.newHashMap(); + params.put("Target", entity); + params.put("CounterType", chosenType); + int chosenAmount = pc.chooseNumber(sa, prompt, sa.hasParam("UpTo") ? 0 : 1, max, params); + + if (chosenAmount > 0) { + entity.subtractCounter(chosenType, chosenAmount); + if (entity instanceof Card) { + Card gameCard = (Card) entity; + game.updateLastStateForCard(gameCard); + } + + if (rememberRemoved) { + for (int i = 0; i < chosenAmount; i++) { + card.addRemembered(Pair.of(chosenType, i)); + } + } + cntToRemove -= chosenAmount; + } else if (sa.hasParam("UpTo")) { + break; + } + } + } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java index bc7abee91dc..c68659e0709 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java @@ -2,6 +2,7 @@ package forge.game.ability.effects; import forge.game.Game; import forge.game.GameEntity; +import forge.game.GameEntityCounterTable; import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardCollection; @@ -88,6 +89,7 @@ public class DamageAllEffect extends DamageBaseEffect { boolean usedDamageMap = true; CardDamageMap damageMap = sa.getDamageMap(); CardDamageMap preventMap = sa.getPreventMap(); + GameEntityCounterTable counterTable = new GameEntityCounterTable(); if (damageMap == null) { // make a new damage map @@ -97,13 +99,13 @@ public class DamageAllEffect extends DamageBaseEffect { } for (final Card c : list) { - c.addDamage(dmg, sourceLKI, damageMap, preventMap, sa); + c.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa); } if (!players.equals("")) { final List playerList = AbilityUtils.getDefinedPlayers(card, players, sa); for (final Player p : playerList) { - p.addDamage(dmg, sourceLKI, damageMap, preventMap, sa); + p.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa); } } @@ -126,6 +128,8 @@ public class DamageAllEffect extends DamageBaseEffect { damageMap.clear(); } + counterTable.triggerCountersPutAll(game); + replaceDying(sa); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java index 8c8f906de43..97b00f3c9be 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java @@ -3,6 +3,7 @@ package forge.game.ability.effects; import com.google.common.collect.Iterables; import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.GameObject; import forge.game.ability.AbilityUtils; import forge.game.card.Card; @@ -119,6 +120,7 @@ public class DamageDealEffect extends DamageBaseEffect { boolean usedDamageMap = true; CardDamageMap damageMap = sa.getDamageMap(); CardDamageMap preventMap = sa.getPreventMap(); + GameEntityCounterTable counterTable = new GameEntityCounterTable(); if (damageMap == null) { // make a new damage map @@ -159,7 +161,7 @@ public class DamageDealEffect extends DamageBaseEffect { Player assigningPlayer = players.get(0); Map map = assigningPlayer.getController().assignCombatDamage(sourceLKI, assigneeCards, dmg, null, true); for (Entry dt : map.entrySet()) { - dt.getKey().addDamage(dt.getValue(), sourceLKI, damageMap, preventMap, sa); + dt.getKey().addDamage(dt.getValue(), sourceLKI, damageMap, preventMap, counterTable, sa); } if (!usedDamageMap) { @@ -170,6 +172,8 @@ public class DamageDealEffect extends DamageBaseEffect { preventMap.clear(); damageMap.clear(); } + + counterTable.triggerCountersPutAll(game); replaceDying(sa); return; } @@ -194,13 +198,13 @@ public class DamageDealEffect extends DamageBaseEffect { c.clearAssignedDamage(); } else { - c.addDamage(dmg, sourceLKI, false, noPrevention, damageMap, preventMap, sa); + c.addDamage(dmg, sourceLKI, false, noPrevention, damageMap, preventMap, counterTable, sa); } } } else if (o instanceof Player) { final Player p = (Player) o; if (!targeted || p.canBeTargetedBy(sa)) { - p.addDamage(dmg, sourceLKI, false, noPrevention, damageMap, preventMap, sa); + p.addDamage(dmg, sourceLKI, false, noPrevention, damageMap, preventMap, counterTable, sa); } } } @@ -217,6 +221,7 @@ public class DamageDealEffect extends DamageBaseEffect { preventMap.clear(); damageMap.clear(); } + counterTable.triggerCountersPutAll(game); replaceDying(sa); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java index a485b4d4114..8887b60bfb6 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java @@ -1,5 +1,7 @@ package forge.game.ability.effects; +import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.GameObject; import forge.game.ability.AbilityUtils; import forge.game.card.Card; @@ -60,8 +62,9 @@ public class DamageEachEffect extends DamageBaseEffect { @Override public void resolve(SpellAbility sa) { final Card card = sa.getHostCard(); + final Game game = card.getGame(); - FCollectionView sources = card.getGame().getCardsIn(ZoneType.Battlefield); + FCollectionView sources = game.getCardsIn(ZoneType.Battlefield); if (sa.hasParam("ValidCards")) { sources = CardLists.getValidCards(sources, sa.getParam("ValidCards"), card.getController(), card); } @@ -73,6 +76,7 @@ public class DamageEachEffect extends DamageBaseEffect { boolean usedDamageMap = true; CardDamageMap damageMap = sa.getDamageMap(); CardDamageMap preventMap = sa.getPreventMap(); + GameEntityCounterTable counterTable = new GameEntityCounterTable(); if (damageMap == null) { // make a new damage map @@ -83,21 +87,22 @@ public class DamageEachEffect extends DamageBaseEffect { for (final Object o : tgts) { for (final Card source : sources) { - final Card sourceLKI = source.getGame().getChangeZoneLKIInfo(source); + final Card sourceLKI = game.getChangeZoneLKIInfo(source); + // TODO shouldn't that be using Num or something first? final int dmg = CardFactoryUtil.xCount(source, sa.getSVar("X")); // System.out.println(source+" deals "+dmg+" damage to "+o.toString()); if (o instanceof Card) { final Card c = (Card) o; if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) { - c.addDamage(dmg, sourceLKI, damageMap, preventMap, sa); + c.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa); } } else if (o instanceof Player) { final Player p = (Player) o; if (!targeted || p.canBeTargetedBy(sa)) { - p.addDamage(dmg, sourceLKI, damageMap, preventMap, sa); + p.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa); } } } @@ -106,11 +111,11 @@ public class DamageEachEffect extends DamageBaseEffect { if (sa.hasParam("DefinedCards")) { if (sa.getParam("DefinedCards").equals("Self")) { for (final Card source : sources) { - final Card sourceLKI = source.getGame().getChangeZoneLKIInfo(source); + final Card sourceLKI = game.getChangeZoneLKIInfo(source); final int dmg = CardFactoryUtil.xCount(source, card.getSVar("X")); // System.out.println(source+" deals "+dmg+" damage to "+source); - source.addDamage(dmg, sourceLKI, damageMap, preventMap, sa); + source.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa); } } if (sa.getParam("DefinedCards").equals("Remembered")) { @@ -122,7 +127,7 @@ public class DamageEachEffect extends DamageBaseEffect { if (o instanceof Card) { Card rememberedcard = (Card) o; // System.out.println(source + " deals " + dmg + " damage to " + rememberedcard); - rememberedcard.addDamage(dmg, sourceLKI, damageMap, preventMap, sa); + rememberedcard.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa); } } } @@ -137,6 +142,8 @@ public class DamageEachEffect extends DamageBaseEffect { damageMap.clear(); } + counterTable.triggerCountersPutAll(game); + replaceDying(sa); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java index 9e5448cd3a8..381497e77ea 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java @@ -2,6 +2,7 @@ package forge.game.ability.effects; import forge.game.Game; import forge.game.GameActionUtil; +import forge.game.GameEntityCounterTable; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -110,6 +111,7 @@ public class DigEffect extends SpellAbilityEffect { } CardZoneTable table = new CardZoneTable(); + GameEntityCounterTable counterTable = new GameEntityCounterTable(); for (final Player p : tgtPlayers) { if (tgt != null && !p.canBeTargetedBy(sa)) { continue; @@ -378,7 +380,8 @@ public class DigEffect extends SpellAbilityEffect { } } else if (destZone2 == ZoneType.Exile) { if (sa.hasParam("ExileWithCounter")) { - c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")), 1, player, true); + c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")), + 1, player, true, counterTable); } c.setExiledWith(effectHost); } @@ -389,6 +392,7 @@ public class DigEffect extends SpellAbilityEffect { } //table trigger there table.triggerChangesZoneAll(game); + counterTable.triggerCountersPutAll(game); } // TODO This should be somewhere else, maybe like CardUtil or something like that diff --git a/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java index c06be791574..0b01f6a1b25 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java @@ -3,6 +3,7 @@ package forge.game.ability.effects; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardCollection; @@ -47,9 +48,9 @@ public class ExploreEffect extends SpellAbilityEffect { final Player pl = sa.getActivatingPlayer(); final PlayerController pc = pl.getController(); final Game game = pl.getGame(); - List tgt = getTargetCards(sa); - - for (final Card c : tgt) { + + GameEntityCounterTable table = new GameEntityCounterTable(); + for (final Card c : getTargetCards(sa)) { // revealed land card boolean revealedLand = false; CardCollection top = pl.getTopXCardsFromLibrary(1); @@ -76,8 +77,8 @@ public class ExploreEffect extends SpellAbilityEffect { Card gamec = game.getCardState(c); // if the card is not more in the game anymore // this might still return true but its no problem - if (game.getZoneOf(gamec).is(ZoneType.Battlefield) && gamec.getTimestamp() == c.getTimestamp()) { - c.addCounter(CounterType.P1P1, 1, pl, true); + if (game.getZoneOf(gamec).is(ZoneType.Battlefield) && gamec.equalsWithTimestamp(c)) { + c.addCounter(CounterType.P1P1, 1, pl, true, table); } } @@ -86,6 +87,7 @@ public class ExploreEffect extends SpellAbilityEffect { runParams.put("Card", c); game.getTriggerHandler().runTrigger(TriggerType.Explores, runParams, false); } + table.triggerCountersPutAll(game); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java b/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java index 900492e35bb..4029bfd4d12 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java @@ -4,6 +4,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardDamageMap; @@ -130,6 +131,7 @@ public class FightEffect extends DamageBaseEffect { boolean usedDamageMap = true; CardDamageMap damageMap = sa.getDamageMap(); CardDamageMap preventMap = sa.getPreventMap(); + GameEntityCounterTable counterTable = new GameEntityCounterTable(); if (damageMap == null) { // make a new damage map @@ -142,12 +144,12 @@ public class FightEffect extends DamageBaseEffect { final int dmg1 = fightToughness ? fighterA.getNetToughness() : fighterA.getNetPower(); if (fighterA.equals(fighterB)) { - fighterA.addDamage(dmg1 * 2, fighterA, damageMap, preventMap, sa); + fighterA.addDamage(dmg1 * 2, fighterA, damageMap, preventMap, counterTable, sa); } else { final int dmg2 = fightToughness ? fighterB.getNetToughness() : fighterB.getNetPower(); - fighterB.addDamage(dmg1, fighterA, damageMap, preventMap, sa); - fighterA.addDamage(dmg2, fighterB, damageMap, preventMap, sa); + fighterB.addDamage(dmg1, fighterA, damageMap, preventMap, counterTable, sa); + fighterA.addDamage(dmg2, fighterB, damageMap, preventMap, counterTable, sa); } if (!usedDamageMap) { @@ -157,6 +159,7 @@ public class FightEffect extends DamageBaseEffect { preventMap.clear(); damageMap.clear(); } + counterTable.triggerCountersPutAll(sa.getHostCard().getGame()); replaceDying(sa); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java index bb883716706..21d6f14715a 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java @@ -1,7 +1,10 @@ package forge.game.ability.effects; +import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; +import forge.game.card.Card; import forge.game.card.CounterType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; @@ -21,17 +24,21 @@ import java.util.List; */ @Override public void resolve(SpellAbility sa) { - final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Num"), sa); + final Card host = sa.getHostCard(); + final Game game = host.getGame(); + final int amount = AbilityUtils.calculateAmount(host, sa.getParam("Num"), sa); + GameEntityCounterTable table = new GameEntityCounterTable(); for (final Player p : getTargetPlayers(sa)) { if ((!sa.usesTargeting()) || p.canBeTargetedBy(sa)) { if (amount >= 0) { - p.addPoisonCounters(amount, sa.getHostCard()); + p.addPoisonCounters(amount, host, table); } else { - p.removePoisonCounters(-amount, sa.getHostCard()); + p.removePoisonCounters(-amount, host); } } } + table.triggerCountersPutAll(game); } /* (non-Javadoc) diff --git a/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java index 5ed6907f405..e9a0fe75a89 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java @@ -33,7 +33,7 @@ public class PumpEffect extends SpellAbilityEffect { final Game game = host.getGame(); //if host is not on the battlefield don't apply // Suspend should does Affect the Stack - if (sa.hasParam("UntilLoseControlOfHost") + if ((sa.hasParam("UntilLoseControlOfHost") || sa.hasParam("UntilHostLeavesPlay")) && !(host.isInPlay() || host.isInZone(ZoneType.Stack))) { return; } @@ -104,7 +104,7 @@ public class PumpEffect extends SpellAbilityEffect { final Card host = sa.getHostCard(); //if host is not on the battlefield don't apply // Suspend should does Affect the Stack - if (sa.hasParam("UntilLoseControlOfHost") + if ((sa.hasParam("UntilLoseControlOfHost") || sa.hasParam("UntilHostLeavesPlay")) && !(host.isInPlay() || host.isInZone(ZoneType.Stack))) { return; } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ReplaceSplitDamageEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ReplaceSplitDamageEffect.java index c7f0201c785..45c31fb3749 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ReplaceSplitDamageEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ReplaceSplitDamageEffect.java @@ -9,6 +9,7 @@ import com.google.common.collect.Maps; import forge.game.Game; import forge.game.GameEntity; +import forge.game.GameEntityCounterTable; import forge.game.GameObject; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; @@ -57,6 +58,7 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect { CardDamageMap damageMap = (CardDamageMap) originalParams.get("DamageMap"); CardDamageMap preventMap = (CardDamageMap) originalParams.get("PreventMap"); + GameEntityCounterTable counterTable = (GameEntityCounterTable) originalParams.get("CounterTable"); SpellAbility cause = (SpellAbility) originalParams.get("Cause"); boolean isCombat = (Boolean) originalParams.get("IsCombat"); @@ -64,7 +66,7 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect { GameEntity obj = (GameEntity) list.get(0); - obj.addDamage(n, sourceLKI, isCombat, noPrevention, damageMap, preventMap, cause); + obj.addDamage(n, sourceLKI, isCombat, noPrevention, damageMap, preventMap, counterTable, cause); } // no damage for original target anymore @@ -74,7 +76,6 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect { } params.put("DamageAmount", dmg); - //try to call replacementHandler with new Params ReplacementResult result = game.getReplacementHandler().run(params); switch (result) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java index d45e8617977..1c9bb983029 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java @@ -4,6 +4,7 @@ import com.google.common.collect.Maps; import forge.card.mana.ManaCost; import forge.game.Game; import forge.game.GameActionUtil; +import forge.game.GameEntityCounterTable; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.*; @@ -43,7 +44,11 @@ public class SacrificeEffect extends SpellAbilityEffect { return; } } else if (sa.hasParam("CumulativeUpkeep")) { - card.addCounter(CounterType.AGE, 1, activator, true); + GameEntityCounterTable table = new GameEntityCounterTable(); + card.addCounter(CounterType.AGE, 1, activator, true, table); + + table.triggerCountersPutAll(game); + Cost cumCost = new Cost(sa.getParam("CumulativeUpkeep"), true); Cost payCost = new Cost(ManaCost.ZERO, true); int n = card.getCounters(CounterType.AGE); diff --git a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java index c7292e45a60..7e810439403 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java @@ -2,6 +2,7 @@ package forge.game.ability.effects; import forge.card.CardStateName; import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.GameLogEntryType; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -49,7 +50,6 @@ public class SetStateEffect extends SpellAbilityEffect { final String mode = sa.getParam("Mode"); final Card host = sa.getHostCard(); final Game game = host.getGame(); - final List tgtCards = getTargetCards(sa); final boolean remChanged = sa.hasParam("RememberChanged"); final boolean morphUp = sa.hasParam("MorphUp"); @@ -57,7 +57,9 @@ public class SetStateEffect extends SpellAbilityEffect { final boolean hiddenAgenda = sa.hasParam("HiddenAgenda"); final boolean optional = sa.hasParam("Optional"); - for (final Card tgt : tgtCards) { + GameEntityCounterTable table = new GameEntityCounterTable(); + + for (final Card tgt : getTargetCards(sa)) { if (sa.usesTargeting() && !tgt.canBeTargetedBy(sa)) { continue; } @@ -125,12 +127,13 @@ public class SetStateEffect extends SpellAbilityEffect { } game.fireEvent(new GameEventCardStatsChanged(tgt)); if (sa.hasParam("Mega")) { - tgt.addCounter(CounterType.P1P1, 1, p, true); + tgt.addCounter(CounterType.P1P1, 1, p, true, table); } if (remChanged) { host.addRemembered(tgt); } } } + table.triggerCountersPutAll(game); } } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 525a3c754dd..b545cfcd9c6 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -1131,16 +1131,18 @@ public class Card extends GameEntity implements Comparable { @Override public final boolean canReceiveCounters(final CounterType type) { - if (hasKeyword("CARDNAME can't have counters put on it.")) { - return false; - } - if (isCreature() && type == CounterType.M1M1) { - for (final Card c : getController().getCreaturesInPlay()) { // look for Melira, Sylvok Outcast - if (c.hasKeyword("Creatures you control can't have -1/-1 counters put on them.")) { + + // CantPutCounter static abilities + for (final Card ca : getGame().getCardsIn(ZoneType.listValueOf("Battlefield,Command"))) { + for (final StaticAbility stAb : ca.getStaticAbilities()) { + if (stAb.applyAbility("CantPutCounter", this, type)) { return false; } } - } else if (type == CounterType.DREAM) { + } + + if (type == CounterType.DREAM) { + // need to be done extra because it is also a state based action if (hasKeyword("CARDNAME can't have more than seven dream counters on it.") && getCounters(CounterType.DREAM) > 6) { return false; } @@ -1156,17 +1158,17 @@ public class Card extends GameEntity implements Comparable { countersAdded = value; } - public final int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier) { - return addCounter(counterType, n, source, applyMultiplier, true); + public final int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, GameEntityCounterTable table) { + return addCounter(counterType, n, source, applyMultiplier, true, table); } - public final int addCounterFireNoEvents(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier) { - return addCounter(counterType, n, source, applyMultiplier, false); + public final int addCounterFireNoEvents(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, GameEntityCounterTable table) { + return addCounter(counterType, n, source, applyMultiplier, false, table); } @Override - public int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, final boolean fireEvents) { + public int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, final boolean fireEvents, GameEntityCounterTable table) { int addAmount = n; - if(addAmount < 0) { + if(addAmount <= 0) { addAmount = 0; // As per rule 107.1b return 0; } @@ -1245,6 +1247,9 @@ public class Card extends GameEntity implements Comparable { getController().addCounterToPermThisTurn(counterType, addAmount); view.updateCounters(this); } + if (table != null) { + table.put(this, counterType, addAmount); + } return addAmount; } @@ -4622,15 +4627,20 @@ public class Card extends GameEntity implements Comparable { return total; } - public final void addCombatDamage(final Map map, final CardDamageMap damageMap, final CardDamageMap preventMap) { + public final void addCombatDamage(final Map map, final CardDamageMap damageMap, final CardDamageMap preventMap, GameEntityCounterTable counterTable) { for (final Entry entry : map.entrySet()) { - addCombatDamage(entry.getValue(), entry.getKey(), damageMap, preventMap); + addCombatDamage(entry.getValue(), entry.getKey(), damageMap, preventMap, counterTable); } } - protected int addCombatDamageBase(final int damage, final Card source, CardDamageMap damageMap) { + /* + * (non-Javadoc) + * @see forge.game.GameEntity#addCombatDamageBase(int, forge.game.card.Card, forge.game.card.CardDamageMap, forge.game.GameEntityCounterTable) + */ + @Override + protected int addCombatDamageBase(final int damage, final Card source, CardDamageMap damageMap, GameEntityCounterTable counterTable) { if (isInPlay()) { - return super.addCombatDamageBase(damage, source, damageMap); + return super.addCombatDamageBase(damage, source, damageMap, counterTable); } return 0; } @@ -4857,10 +4867,10 @@ public class Card extends GameEntity implements Comparable { return restDamage; } - public final void addDamage(final Map sourcesMap, CardDamageMap damageMap) { + public final void addDamage(final Map sourcesMap, CardDamageMap damageMap, GameEntityCounterTable counterTable) { for (final Entry entry : sourcesMap.entrySet()) { // damage prevention is already checked! - addDamageAfterPrevention(entry.getValue(), entry.getKey(), true, damageMap); + addDamageAfterPrevention(entry.getValue(), entry.getKey(), true, damageMap, counterTable); } } @@ -4869,7 +4879,7 @@ public class Card extends GameEntity implements Comparable { * applied. */ @Override - public final int addDamageAfterPrevention(final int damageIn, final Card source, final boolean isCombat, CardDamageMap damageMap) { + public final int addDamageAfterPrevention(final int damageIn, final Card source, final boolean isCombat, CardDamageMap damageMap, GameEntityCounterTable counterTable) { if (damageIn == 0) { return 0; // Rule 119.8 @@ -4903,7 +4913,7 @@ public class Card extends GameEntity implements Comparable { if (isInPlay()) { if (wither) { - addCounter(CounterType.M1M1, damageIn, source.getController(), true); + addCounter(CounterType.M1M1, damageIn, source.getController(), true, counterTable); damageType = DamageType.M1M1Counters; } else { @@ -6052,7 +6062,7 @@ public class Card extends GameEntity implements Comparable { return etbCounters.cellSet(); } - public final boolean putEtbCounters() { + public final boolean putEtbCounters(GameEntityCounterTable table) { boolean changed = false; for (Table.Cell e : etbCounters.cellSet()) { CounterType ct = e.getColumnKey(); @@ -6062,7 +6072,7 @@ public class Card extends GameEntity implements Comparable { changed = true; } } else { - changed |= addCounter(ct, e.getValue(), e.getRowKey(), true) > 0; + changed |= addCounter(ct, e.getValue(), e.getRowKey(), true, table) > 0; } } return changed; diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index 3d69ca554b3..b898d7dd1f5 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -32,6 +32,7 @@ import forge.card.mana.ManaCostParser; import forge.card.mana.ManaCostShard; import forge.game.Game; import forge.game.GameEntity; +import forge.game.GameEntityCounterTable; import forge.game.GameLogEntryType; import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityUtils; @@ -4277,7 +4278,9 @@ public class CardFactoryUtil { final Card c = game.getAction().exile(this.getHostCard(), this); int counters = AbilityUtils.calculateAmount(c, k[1], this); - c.addCounter(CounterType.TIME, counters, getActivatingPlayer(), true); + GameEntityCounterTable table = new GameEntityCounterTable(); + c.addCounter(CounterType.TIME, counters, getActivatingPlayer(), true, table); + table.triggerCountersPutAll(game); String sb = TextUtil.concatWithSpace(getActivatingPlayer().toString(),"has suspended", c.getName(), "with", String.valueOf(counters),"time counters on it."); game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb); diff --git a/forge-game/src/main/java/forge/game/combat/Combat.java b/forge-game/src/main/java/forge/game/combat/Combat.java index 830251c9986..7d636b92b91 100644 --- a/forge-game/src/main/java/forge/game/combat/Combat.java +++ b/forge-game/src/main/java/forge/game/combat/Combat.java @@ -21,6 +21,7 @@ import com.google.common.base.Function; import com.google.common.collect.*; import forge.game.Game; import forge.game.GameEntity; +import forge.game.GameEntityCounterTable; import forge.game.GameLogEntryType; import forge.game.GameObjectMap; import forge.game.card.Card; @@ -783,18 +784,20 @@ public class Combat { } public void dealAssignedDamage() { - playerWhoAttacks.getGame().copyLastState(); + final Game game = playerWhoAttacks.getGame(); + game.copyLastState(); - CardDamageMap preventMap = new CardDamageMap(); + CardDamageMap preventMap = new CardDamageMap(); + GameEntityCounterTable counterTable = new GameEntityCounterTable(); // This function handles both Regular and First Strike combat assignment for (final Entry entry : defendingDamageMap.entrySet()) { GameEntity defender = getDefenderByAttacker(entry.getKey()); if (defender instanceof Player) { // player - ((Player) defender).addCombatDamage(entry.getValue(), entry.getKey(), dealtDamageTo, preventMap); + ((Player) defender).addCombatDamage(entry.getValue(), entry.getKey(), dealtDamageTo, preventMap, counterTable); } else if (defender instanceof Card) { // planeswalker - ((Card) defender).getController().addCombatDamage(entry.getValue(), entry.getKey(), dealtDamageTo, preventMap); + ((Card) defender).getController().addCombatDamage(entry.getValue(), entry.getKey(), dealtDamageTo, preventMap, counterTable); } } @@ -811,7 +814,7 @@ public class Combat { continue; } - c.addCombatDamage(c.getAssignedDamageMap(), dealtDamageTo, preventMap); + c.addCombatDamage(c.getAssignedDamageMap(), dealtDamageTo, preventMap, counterTable); c.clearAssignedDamage(); } @@ -823,6 +826,9 @@ public class Combat { // LifeLink for Combat Damage at this place dealtDamageTo.triggerDamageDoneOnce(true, null); dealtDamageTo.clear(); + + counterTable.triggerCountersPutAll(game); + counterTable.clear(); } public final boolean isUnblocked(final Card att) { diff --git a/forge-game/src/main/java/forge/game/cost/CostDamage.java b/forge-game/src/main/java/forge/game/cost/CostDamage.java index eac5e94d289..cc33d7c6501 100644 --- a/forge-game/src/main/java/forge/game/cost/CostDamage.java +++ b/forge-game/src/main/java/forge/game/cost/CostDamage.java @@ -17,6 +17,7 @@ */ package forge.game.cost; +import forge.game.GameEntityCounterTable; import forge.game.card.Card; import forge.game.card.CardDamageMap; import forge.game.player.Player; @@ -47,7 +48,7 @@ public class CostDamage extends CostPart { @Override public final String toString() { final StringBuilder sb = new StringBuilder(); - sb.append("Pay ").append(this.getAmount()).append(" Life"); + sb.append("Deal ").append(this.getAmount()).append(" damage to you"); return sb.toString(); } @@ -68,14 +69,17 @@ public class CostDamage extends CostPart { final Card source = sa.getHostCard(); CardDamageMap damageMap = new CardDamageMap(); CardDamageMap preventMap = new CardDamageMap(); + GameEntityCounterTable table = new GameEntityCounterTable(); - payer.addDamage(decision.c, source, damageMap, preventMap, sa); + payer.addDamage(decision.c, source, damageMap, preventMap, table, sa); preventMap.triggerPreventDamage(false); damageMap.triggerDamageDoneOnce(false, sa); + table.triggerCountersPutAll(payer.getGame()); preventMap.clear(); damageMap.clear(); + table.clear(); return decision.c > 0; } diff --git a/forge-game/src/main/java/forge/game/cost/CostPartWithList.java b/forge-game/src/main/java/forge/game/cost/CostPartWithList.java index 787afda62dd..495457493be 100644 --- a/forge-game/src/main/java/forge/game/cost/CostPartWithList.java +++ b/forge-game/src/main/java/forge/game/cost/CostPartWithList.java @@ -53,7 +53,7 @@ public abstract class CostPartWithList extends CostPart { /** * Reset list. */ - public final void resetLists() { + public void resetLists() { lkiList.clear(); cardList.clear(); table.clear(); @@ -121,7 +121,7 @@ public abstract class CostPartWithList extends CostPart { } // always returns true, made this to inline with return - public final boolean executePayment(SpellAbility ability, CardCollectionView targetCards) { + public boolean executePayment(SpellAbility ability, CardCollectionView targetCards) { if (canPayListAtOnce()) { // This is used by reveal. Without it when opponent would reveal hand, you'll get N message boxes. lkiList.addAll(targetCards); cardList.addAll(doListPayment(ability, targetCards)); diff --git a/forge-game/src/main/java/forge/game/cost/CostPutCounter.java b/forge-game/src/main/java/forge/game/cost/CostPutCounter.java index c33bbea5441..3738d839398 100644 --- a/forge-game/src/main/java/forge/game/cost/CostPutCounter.java +++ b/forge-game/src/main/java/forge/game/cost/CostPutCounter.java @@ -6,18 +6,18 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package forge.game.cost; -import forge.game.ability.AbilityUtils; +import forge.game.GameEntityCounterTable; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.card.CardPredicates; @@ -40,13 +40,15 @@ public class CostPutCounter extends CostPartWithList { private final CounterType counter; private int lastPaidAmount = 0; + private final GameEntityCounterTable counterTable = new GameEntityCounterTable(); + public final CounterType getCounter() { return this.counter; } /** * Sets the last paid amount. - * + * * @param paidAmount * the new last paid amount */ @@ -56,7 +58,7 @@ public class CostPutCounter extends CostPartWithList { /** * Instantiates a new cost put counter. - * + * * @param amount * the amount * @param cntr @@ -73,7 +75,7 @@ public class CostPutCounter extends CostPartWithList { @Override public int paymentOrder() { return 8; } - + @Override public boolean isReusable() { return counter != CounterType.M1M1; @@ -81,7 +83,7 @@ public class CostPutCounter extends CostPartWithList { /* * (non-Javadoc) - * + * * @see forge.card.cost.CostPart#toString() */ @Override @@ -112,22 +114,24 @@ public class CostPutCounter extends CostPartWithList { /* * (non-Javadoc) - * + * * @see forge.card.cost.CostPart#refund(forge.Card) */ @Override public final void refund(final Card source) { if(this.payCostFromSource()) source.subtractCounter(this.counter, this.lastPaidAmount); - else + else { + final Integer i = this.convertAmount(); for (final Card c : this.getCardList()) { - c.subtractCounter(this.counter, 1); + c.subtractCounter(this.counter, i); } + } } /* * (non-Javadoc) - * + * * @see * forge.card.cost.CostPart#canPay(forge.card.spellability.SpellAbility, * forge.Card, forge.Player, forge.card.cost.Cost) @@ -143,7 +147,7 @@ public class CostPutCounter extends CostPartWithList { // 3 Cards have Put a -1/-1 Counter on a Creature you control. List typeList = CardLists.getValidCards(source.getGame().getCardsIn(ZoneType.Battlefield), this.getType().split(";"), payer, source, ability); - + typeList = CardLists.filter(typeList, CardPredicates.canReceiveCounters(this.counter)); if (typeList.isEmpty()) { @@ -156,55 +160,30 @@ public class CostPutCounter extends CostPartWithList { /* * (non-Javadoc) - * + * * @see forge.card.cost.CostPart#payAI(forge.card.spellability.SpellAbility, * forge.Card, forge.card.cost.Cost_Payment) */ @Override public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) { - Integer c = getNumberOfCounters(ability); - if (this.payCostFromSource()) { - executePayment(ability, ability.getHostCard(), c); + executePayment(ability, ability.getHostCard()); } else { - // Put counter on chosen card - for (int i = 0; i < c; i++) - executePayment(ability, decision.cards); - ability.getHostCard().setSVar("CostCountersAdded", Integer.toString(Math.min(decision.cards.size(), c))); + executePayment(ability, decision.cards); } + triggerCounterPutAll(ability); return true; } - public Integer getNumberOfCounters(final SpellAbility ability) { - Integer c = this.convertAmount(); - if (c == null) { - c = AbilityUtils.calculateAmount(ability.getHostCard(), this.getAmount(), ability); - } - return c; - } - /* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ @Override protected Card doPayment(SpellAbility ability, Card targetCard){ - targetCard.addCounter(this.getCounter(), 1, ability.getActivatingPlayer(), false); + final Integer i = this.convertAmount(); + targetCard.addCounter(this.getCounter(), i, ability.getActivatingPlayer(), false, counterTable); return targetCard; } - - protected void executePayment(SpellAbility ability, Card targetCard, int c) { - CounterType counterType = this.getCounter(); - if( c > 1 ) { - Integer oldValue = targetCard.getCounters().get(counterType); - int newValue = c + (oldValue == null ? 0 : oldValue.intValue()) - 1; - targetCard.getCounters().put(counterType, Integer.valueOf(newValue)); - } - // added c - 1 without firing triggers, the last counter added should fire trigger. - if (c > 0) { - executePayment(ability, targetCard); - } - } - @Override public String getHashForLKIList() { @@ -219,4 +198,24 @@ public class CostPutCounter extends CostPartWithList { return visitor.visit(this); } + protected void triggerCounterPutAll(final SpellAbility ability) { + if (counterTable.isEmpty()) { + return; + } + + GameEntityCounterTable tempTable = new GameEntityCounterTable(); + tempTable.putAll(counterTable); + tempTable.triggerCountersPutAll(ability.getHostCard().getGame()); + } + + + /* (non-Javadoc) + * @see forge.game.cost.CostPartWithList#resetLists() + */ + @Override + public void resetLists() { + super.resetLists(); + counterTable.clear(); + } + } diff --git a/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java b/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java index d4b2d6b9c5c..f74a892516d 100644 --- a/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java +++ b/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java @@ -114,7 +114,7 @@ public class CostRemoveCounter extends CostPartWithList { public final void refund(final Card source) { int refund = this.getCardList().size() == 1 ? this.cntRemoved : 1; // is wrong for Ooze Flux and Novijen Sages for (final Card c : this.getCardList()) { - c.addCounter(this.counter, refund, source.getController(), false); + c.addCounter(this.counter, refund, source.getController(), false, false, null); } } diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java index e6c3a3dce21..cecb307572d 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -261,12 +261,14 @@ public class PhaseHandler implements java.io.Serializable { if (playerTurn.isArchenemy()) { playerTurn.setSchemeInMotion(); } + GameEntityCounterTable table = new GameEntityCounterTable(); // all Saga get Lore counter at the begin of pre combat for (Card c : playerTurn.getCardsIn(ZoneType.Battlefield)) { if (c.getType().hasSubtype("Saga")) { - c.addCounter(CounterType.LORE, 1, null, false); + c.addCounter(CounterType.LORE, 1, null, false, table); } } + table.triggerCountersPutAll(game); } break; diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 9ffa1832cca..79670788306 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -559,7 +559,7 @@ public class Player extends GameEntity implements Comparable { // This function handles damage after replacement and prevention effects are applied @Override - public final int addDamageAfterPrevention(final int amount, final Card source, final boolean isCombat, CardDamageMap damageMap) { + public final int addDamageAfterPrevention(final int amount, final Card source, final boolean isCombat, CardDamageMap damageMap, GameEntityCounterTable counterTable) { if (amount <= 0) { return 0; } @@ -570,7 +570,7 @@ public class Player extends GameEntity implements Comparable { || hasKeyword("All damage is dealt to you as though its source had infect."); if (infect) { - addPoisonCounters(amount, source); + addPoisonCounters(amount, source, counterTable); } else { // Worship does not reduce the damage dealt but changes the effect @@ -882,23 +882,23 @@ public class Player extends GameEntity implements Comparable { } public final boolean canReceiveCounters(final CounterType type) { - if (hasKeyword("PLAYER can't have counters put on him or her.")) { - return false; - } - if (type == CounterType.POISON) { - if (hasKeyword("You can't get poison counters")) { - return false; + // CantPutCounter static abilities + for (final Card ca : getGame().getCardsIn(ZoneType.listValueOf("Battlefield,Command"))) { + for (final StaticAbility stAb : ca.getStaticAbilities()) { + if (stAb.applyAbility("CantPutCounter", this, type)) { + return false; + } } } return true; } - public final int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier) { - return addCounter(counterType, n, source, applyMultiplier, true); + public final int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, GameEntityCounterTable table) { + return addCounter(counterType, n, source, applyMultiplier, true, table); } @Override - public int addCounter(CounterType counterType, int n, final Player source, boolean applyMultiplier, boolean fireEvents) { + public int addCounter(CounterType counterType, int n, final Player source, boolean applyMultiplier, boolean fireEvents, GameEntityCounterTable table) { if (!canReceiveCounters(counterType)) { return 0; } @@ -942,6 +942,9 @@ public class Player extends GameEntity implements Comparable { if (addAmount > 0) { getGame().getTriggerHandler().runTrigger(TriggerType.CounterAddedOnce, runParams, false); } + if (table != null) { + table.put(this, counterType, addAmount); + } return addAmount; } @@ -999,9 +1002,9 @@ public class Player extends GameEntity implements Comparable { setCounters(CounterType.POISON, num, true); game.fireEvent(new GameEventPlayerPoisoned(this, source, oldPoison, num)); } - public final void addPoisonCounters(final int num, final Card source) { + public final void addPoisonCounters(final int num, final Card source, GameEntityCounterTable table) { int oldPoison = getCounters(CounterType.POISON); - addCounter(CounterType.POISON, num, source.getController(), false, true); + addCounter(CounterType.POISON, num, source.getController(), false, true, table); if (oldPoison != getCounters(CounterType.POISON)) { game.fireEvent(new GameEventPlayerPoisoned(this, source, oldPoison, num)); diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java index ad5f8602410..bf1cd3984c9 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java @@ -29,6 +29,7 @@ import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; +import forge.game.card.CounterType; import forge.game.cost.Cost; import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; @@ -386,6 +387,43 @@ public class StaticAbility extends CardTraitBase implements Comparable params, Card host, boolean intrinsic) { + super(params, host, intrinsic); + } + + @Override + public boolean performTest(Map runParams2) { + final GameEntityCounterTable table = (GameEntityCounterTable) runParams2.get("Objects"); + + if (filterTable(table).isEmpty()) { + return false; + } + + return true; + } + + @Override + public void setTriggeringObjects(SpellAbility sa) { + final GameEntityCounterTable table = (GameEntityCounterTable) getRunParams().get("Objects"); + + Map all = this.filterTable(table); + + int amount = 0; + for (final Integer v : all.values()) { + amount += v; + } + + sa.setTriggeringObject("Objects", Lists.newArrayList(all.keySet())); + sa.setTriggeringObject("Amount", amount); + } + + @Override + public String getImportantStackObjects(SpellAbility sa) { + StringBuilder sb = new StringBuilder(); + sb.append("Amount: ").append(sa.getTriggeringObject("Amount")); + return sb.toString(); + } + + private Map filterTable(GameEntityCounterTable table) { + CounterType counterType = CounterType.getType(getParam("CounterType")); + String valid = getParam("Valid"); + + return table.filterTable(counterType, valid, getHostCard(), null); + } +} diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerType.java b/forge-game/src/main/java/forge/game/trigger/TriggerType.java index 27456ad5579..94a312db0c6 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerType.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerType.java @@ -36,6 +36,7 @@ public enum TriggerType { Clashed(TriggerClashed.class), CounterAdded(TriggerCounterAdded.class), CounterAddedOnce(TriggerCounterAddedOnce.class), + CounterAddedAll(TriggerCounterAddedAll.class), Countered(TriggerCountered.class), CounterRemoved(TriggerCounterRemoved.class), CounterRemovedOnce(TriggerCounterRemovedOnce.class), diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java index 63f48d0e7f8..4d8f6ed4ec2 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java @@ -217,7 +217,7 @@ public class GameSimulatorTest extends SimulationTestCase { Game game = initAndCreateGame(); Player p = game.getPlayers().get(1); Card sorin = addCard("Sorin, Solemn Visitor", p); - sorin.addCounter(CounterType.LOYALTY, 5, p, false); + sorin.addCounter(CounterType.LOYALTY, 5, p, false, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -261,7 +261,7 @@ public class GameSimulatorTest extends SimulationTestCase { String bearCardName = "Runeclaw Bear"; addCard(bearCardName, p); Card gideon = addCard("Gideon, Ally of Zendikar", p); - gideon.addCounter(CounterType.LOYALTY, 4, p, false); + gideon.addCounter(CounterType.LOYALTY, 4, p, false, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -380,7 +380,7 @@ public class GameSimulatorTest extends SimulationTestCase { Game game = initAndCreateGame(); Player p = game.getPlayers().get(1); Card sarkhan = addCard(sarkhanCardName, p); - sarkhan.addCounter(CounterType.LOYALTY, 4, p, false); + sarkhan.addCounter(CounterType.LOYALTY, 4, p, false, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -414,7 +414,7 @@ public class GameSimulatorTest extends SimulationTestCase { addCard(ornithoperCardName, p); addCard(bearCardName, p); Card ajani = addCard(ajaniCardName, p); - ajani.addCounter(CounterType.LOYALTY, 4, p, false); + ajani.addCounter(CounterType.LOYALTY, 4, p, false, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -445,7 +445,7 @@ public class GameSimulatorTest extends SimulationTestCase { SpellAbility boltSA = boltCard.getFirstSpellAbility(); Card ajani = addCard(ajaniCardName, p); - ajani.addCounter(CounterType.LOYALTY, 8, p, false); + ajani.addCounter(CounterType.LOYALTY, 8, p, false, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -494,7 +494,7 @@ public class GameSimulatorTest extends SimulationTestCase { addCard("Swamp", p); addCard("Swamp", p); Card depths = addCard("Dark Depths", p); - depths.addCounter(CounterType.ICE, 10, p, false); + depths.addCounter(CounterType.ICE, 10, p, false, null); Card thespian = addCard("Thespian's Stage", p); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); diff --git a/forge-gui/res/cardsfolder/h/hungering_hydra.txt b/forge-gui/res/cardsfolder/h/hungering_hydra.txt index 5c4f7ba4135..35b09305881 100644 --- a/forge-gui/res/cardsfolder/h/hungering_hydra.txt +++ b/forge-gui/res/cardsfolder/h/hungering_hydra.txt @@ -1,12 +1,12 @@ Name:Hungering Hydra ManaCost:X G Types:Creature Hydra +PT:0/0 K:CantBeBlockedByAmount GT1 K:etbCounter:P1P1:X SVar:X:Count$xPaid T:Mode$ DamageDoneOnce | ValidTarget$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever CARDNAME is dealt damage, put that many +1/+1 counters on CARDNAME. SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ Y | References$ Y SVar:Y:TriggerCount$DamageAmount -DeckHas:Ability$Token +DeckHas:Ability$Counter Oracle:Hungering Hydra enters the battlefield with X +1/+1 counters on it.\nHungering Hydra can't be blocked by more than one creature.\nWhenever Hungering Hydra is dealt damage, put that many +1/+1 counters on it. (It must survive the damage to get the counters.) -PT:0/0 \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/m/melira_sylvok_outcast.txt b/forge-gui/res/cardsfolder/m/melira_sylvok_outcast.txt index 6a40ea9bf2f..97873dfa6a1 100644 --- a/forge-gui/res/cardsfolder/m/melira_sylvok_outcast.txt +++ b/forge-gui/res/cardsfolder/m/melira_sylvok_outcast.txt @@ -2,8 +2,8 @@ Name:Melira, Sylvok Outcast ManaCost:1 G Types:Legendary Creature Human Scout PT:2/2 -K:Creatures you control can't have -1/-1 counters put on them. -S:Mode$ Continuous | Affected$ You | AddKeyword$ You can't get poison counters | Description$ You can't get poison counters. +S:Mode$ CantPutCounter | ValidPlayer$ You | CounterType$ POISON | Description$ You can't get poison counters. +S:Mode$ CantPutCounter | ValidCard$ Creature.YouCtrl | CounterType$ M1M1 | Description$ Creatures you control can't have -1/-1 counters put on them. S:Mode$ Continuous | Affected$ Creature.OppCtrl | RemoveKeyword$ Infect | Description$ Creatures your opponents control lose infect. S:Mode$ Continuous | Affected$ Creature.YouCtrl+withPersist | AddSVar$ Sac SVar:Sac:SVar:SacMe:3 diff --git a/forge-gui/res/cardsfolder/m/meliras_keepers.txt b/forge-gui/res/cardsfolder/m/meliras_keepers.txt index 80841441169..acefcd12f78 100644 --- a/forge-gui/res/cardsfolder/m/meliras_keepers.txt +++ b/forge-gui/res/cardsfolder/m/meliras_keepers.txt @@ -2,6 +2,6 @@ Name:Melira's Keepers ManaCost:4 G Types:Creature Human Warrior PT:4/4 -K:CARDNAME can't have counters put on it. +S:Mode$ CantPutCounter | ValidCard$ Card.Self | Description$ CARDNAME can't have counters put on it. SVar:Picture:http://www.wizards.com/global/images/magic/general/meliras_keepers.jpg Oracle:Melira's Keepers can't have counters put on it. diff --git a/forge-gui/res/cardsfolder/s/solemnity.txt b/forge-gui/res/cardsfolder/s/solemnity.txt index aa8fe2ba2fe..339e235c4a1 100644 --- a/forge-gui/res/cardsfolder/s/solemnity.txt +++ b/forge-gui/res/cardsfolder/s/solemnity.txt @@ -1,7 +1,7 @@ Name:Solemnity ManaCost:2 W Types:Enchantment -S:Mode$ Continuous | Affected$ Player | AddKeyword$ PLAYER can't have counters put on him or her. | Description$ Players can't get counters. -S:Mode$ Continuous | Affected$ Creature,Artifact,Enchantment,Land | AddKeyword$ CARDNAME can't have counters put on it. | Description$ Counters can't be put on artifacts, creatures, enchantments or lands. +S:Mode$ CantPutCounter | ValidPlayer$ Player | Description$ Players can't get counters. +S:Mode$ CantPutCounter | ValidCard$ Creature,Artifact,Enchantment,Land | Description$ Counters can't be put on artifacts, creatures, enchantments or lands. SVar:Picture:http://www.wizards.com/global/images/magic/general/solemnity.jpg Oracle:Players can't get counters.\nCounters can't be put on artifacts, creatures, enchantments, or lands. diff --git a/forge-gui/res/cardsfolder/s/suncleanser.txt b/forge-gui/res/cardsfolder/s/suncleanser.txt index f583334b479..c7fd081295a 100644 --- a/forge-gui/res/cardsfolder/s/suncleanser.txt +++ b/forge-gui/res/cardsfolder/s/suncleanser.txt @@ -4,8 +4,10 @@ Types:Creature Human Cleric PT:1/4 T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigCharm | TriggerDescription$ When CARDNAME enters the battlefield, ABILITY SVar:TrigCharm:DB$ Charm | Choices$ CreatureDBRemoveCounter,OpponentDBRemoveCounter | CharmNum$ 1 -SVar:CreatureDBRemoveCounter:DB$ RemoveCounter | ValidTgts$ Creature | RememberObjects$ Targeted | TgtPrompt$ Select target creature | CounterType$ All | CounterNum$ All | SubAbility$ DBPumpCreature | SpellDescription$ Remove all counters from target creature. It can't have counters put on it for as long as CARDNAME remains on the battlefield. -SVar:OpponentDBRemoveCounter:DB$ RemoveCounter | ValidTgts$ Opponent | RememberObjects$ Targeted | TgtPrompt$ Select target opponent | CounterType$ All | CounterNum$ All | SubAbility$ DBPumpOpponent | SpellDescription$ Target opponent loses all counters. That player can't get counters for as long as CARDNAME remains on the battlefield. -SVar:DBPumpCreature:DB$ Pump | Defined$ Targeted | KW$ CARDNAME can't have counters put on it. | UntilLoseControlOfHost$ True -SVar:DBPumpOpponent:DB$ Pump | Defined$ Targeted | KW$ PLAYER can't have counters put on him or her. | UntilLoseControlOfHost$ True -Oracle:When Suncleanser enters the battlefield, choose one —\n• Remove all counters from target creature. It can't have counters put on it for as long as Suncleanser remains on the battlefield.\n• Target opponent loses all counters. That player can't get counters for as long as Suncleanser remains on the battlefield. \ No newline at end of file +SVar:CreatureDBRemoveCounter:DB$ RemoveCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ All | CounterNum$ All | SubAbility$ DBPumpCreature | SpellDescription$ Remove all counters from target creature. It can't have counters put on it for as long as CARDNAME remains on the battlefield. +SVar:OpponentDBRemoveCounter:DB$ RemoveCounter | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | CounterType$ All | CounterNum$ All | SubAbility$ DBPumpOpponent | SpellDescription$ Target opponent loses all counters. That player can't get counters for as long as CARDNAME remains on the battlefield. +SVar:DBPumpCreature:DB$ Effect | StaticAbilities$ DBCantPutCounterCreature | RememberObjects$ Targeted | Duration$ UntilHostLeavesPlay | References$ DBCantPutCounterCreature +SVar:DBCantPutCounterCreature:Mode$ CantPutCounter | ValidCard$ Card.IsRemembered | EffectZone$ Command | Description$ It can't have counters put on it for as long as EFFECTSOURCE remains on the battlefield. +SVar:DBPumpOpponent:DB$ Effect | StaticAbilities$ DBCantPutCounterPlayer | RememberObjects$ Targeted | Duration$ UntilHostLeavesPlay | References$ DBCantPutCounterPlayer +SVar:DBCantPutCounterPlayer:Mode$ CantPutCounter | ValidPlayer$ Player.IsRemembered | EffectZone$ Command | Description$ That player can't get counters for as long as Suncleanser remains on the battlefield. +Oracle:When Suncleanser enters the battlefield, choose one —\n• Remove all counters from target creature. It can't have counters put on it for as long as Suncleanser remains on the battlefield.\n• Target opponent loses all counters. That player can't get counters for as long as Suncleanser remains on the battlefield. diff --git a/forge-gui/res/cardsfolder/t/tatterkite.txt b/forge-gui/res/cardsfolder/t/tatterkite.txt index 2521f0d332b..d6ccfc9bbb4 100644 --- a/forge-gui/res/cardsfolder/t/tatterkite.txt +++ b/forge-gui/res/cardsfolder/t/tatterkite.txt @@ -3,6 +3,6 @@ ManaCost:3 Types:Artifact Creature Scarecrow PT:2/1 K:Flying -K:CARDNAME can't have counters put on it. +S:Mode$ CantPutCounter | ValidCard$ Card.Self | Description$ CARDNAME can't have counters put on it. SVar:Picture:http://www.wizards.com/global/images/magic/general/tatterkite.jpg Oracle:Flying\nTatterkite can't have counters put on it. diff --git a/forge-gui/res/cardsfolder/upcoming/bioessence_hydra.txt b/forge-gui/res/cardsfolder/upcoming/bioessence_hydra.txt new file mode 100644 index 00000000000..5571a0a2500 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/bioessence_hydra.txt @@ -0,0 +1,14 @@ +Name:Bioessence Hydra +ManaCost:3 G U +Types:Creature Hydra Mutant +PT:4/4 +K:Trample +K:etbCounter:P1P1:Y:no Condition:CARDNAME enters the battlefield with a +1/+1 counter on it for each loyalty counter on planeswalkers you control. +SVar:Y:Count$TotalCounters_LOYALTY_Planeswalker.YouCtrl +T:Mode$ CounterAddedAll | CounterType$ LOYALTY | Valid$ Planeswalker.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever one or more loyalty counters are put on planeswalkers you control, put that many +1/+1 counters on CARDNAME. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ Z | References$ Z +SVar:Z:TriggerCount$Amount +DeckHints:Type$Planeswalker +DeckHas:Ability$Token +Oracle:Trample\nBioessence Hydra enters the battlefield with a +1/+1 counter on it for each loyalty counter on planeswalkers you control.\nWhenever one or more loyalty counters are put on planeswalkers you control, put that many +1/+1 counters on Bioessence Hydra. + diff --git a/forge-gui/res/cardsfolder/upcoming/price_of_betrayal.txt b/forge-gui/res/cardsfolder/upcoming/price_of_betrayal.txt new file mode 100644 index 00000000000..a28ba2078bb --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/price_of_betrayal.txt @@ -0,0 +1,5 @@ +Name:Price of Betrayal +ManaCost:B +Types:Sorcery +A:SP$ RemoveCounter | Cost$ B | ValidTgts$ Artifact,Creature,Planeswalker,Opponent | TgtPrompt$ Select target artifact, creature, planeswalker, or opponent. | CounterType$ Any | CounterNum$ 5 | UpTo$ True | StackDescription$ SpellDescription | SpellDescription$ Remove up to five counters from target artifact, creature, planeswalker, or opponent. +Oracle:Remove up to five counters from target artifact, creature, planeswalker, or opponent. diff --git a/forge-gui/src/main/java/forge/player/HumanCostDecision.java b/forge-gui/src/main/java/forge/player/HumanCostDecision.java index 2621cea7ac2..a335630ef3a 100644 --- a/forge-gui/src/main/java/forge/player/HumanCostDecision.java +++ b/forge-gui/src/main/java/forge/player/HumanCostDecision.java @@ -201,7 +201,7 @@ public class HumanCostDecision extends CostDecisionMakerBase { } } - if (player.canPayLife(c) && player.getController().confirmPayment(cost, "Pay " + c + " Life?", ability)) { + if (controller.confirmPayment(cost, "Do you want " + source + " to deal " + c + " damage to you?", ability)) { return PaymentDecision.number(c); } return null; @@ -738,7 +738,7 @@ public class HumanCostDecision extends CostDecisionMakerBase { @Override public PaymentDecision visit(final CostPutCounter cost) { - final Integer c = cost.getNumberOfCounters(ability); + final Integer c = cost.convertAmount(); if (cost.payCostFromSource()) { cost.setLastPaidAmount(c); diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java index 989501299f7..d55ca8fa3a7 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/player/HumanPlay.java @@ -6,7 +6,6 @@ import forge.FThreads; import forge.card.mana.ManaCost; import forge.game.Game; import forge.game.GameActionUtil; -import forge.game.GameLogEntryType; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; import forge.game.ability.effects.CharmEffect; @@ -394,58 +393,21 @@ public class HumanPlay { } } else if (part instanceof CostDamage) { - int amount = getAmountFromPartX(part, source, sourceAbility); - if (!p.canPayLife(amount)) { + // not a pay life but damage! + PaymentDecision pd = part.accept(hcd); + + if (pd == null) return false; - } - - if (!p.getController().confirmPayment(part, "Do you want " + source + " to deal " + amount + " damage to you?", sourceAbility)) { - return false; - } - CardDamageMap damageMap = new CardDamageMap(); - CardDamageMap preventMap = new CardDamageMap(); - - p.addDamage(amount, source, damageMap, preventMap, sourceAbility); - - preventMap.triggerPreventDamage(false); - damageMap.triggerDamageDoneOnce(false, sourceAbility); + else + part.payAsDecided(p, pd, sourceAbility); } else if (part instanceof CostPutCounter) { - CounterType counterType = ((CostPutCounter) part).getCounter(); - int amount = getAmountFromPartX(part, source, sourceAbility); - if (part.payCostFromSource()) { - if (!source.canReceiveCounters(counterType)) { - String message = TextUtil.concatNoSpace("Won't be able to pay upkeep for ", source.toString(), " but it can't have ", counterType.getName(), " counters put on it."); - p.getGame().getGameLog().add(GameLogEntryType.STACK_RESOLVE, message); - return false; - } + PaymentDecision pd = part.accept(hcd); - if (!p.getController().confirmPayment(part, "Do you want to put " + Lang.nounWithAmount(amount, counterType.getName() + " counter") + " on " + source + "?", sourceAbility)) { - return false; - } - - source.addCounter(counterType, amount, p, false); - } - else { - CardCollectionView list = p.getGame().getCardsIn(ZoneType.Battlefield); - list = CardLists.getValidCards(list, part.getType().split(";"), p, source, sourceAbility); - if (list.isEmpty()) { return false; } - if (!p.getController().confirmPayment(part, "Do you want to put " + Lang.nounWithAmount(amount, counterType.getName() + " counter") + " on " + part.getTypeDescription() + "?", sourceAbility)) { - return false; - } - while (amount > 0) { - InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, 1, 1, list, sourceAbility); - inp.setMessage("Select a card to add a counter to"); - inp.setCancelAllowed(true); - inp.showAndWait(); - if (inp.hasCancelled()) { - continue; - } - Card selected = inp.getFirstSelected(); - selected.addCounter(counterType, 1, p, false); - amount--; - } - } + if (pd == null) + return false; + else + part.payAsDecided(p, pd, sourceAbility); } else if (part instanceof CostRemoveCounter) { CounterType counterType = ((CostRemoveCounter) part).counter; diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 245bf31b830..cf8f7d6bbaf 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -2190,7 +2190,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont if (subtract) { card.subtractCounter(counter, count); } else { - card.addCounter(counter, count, card.getController(), false); + card.addCounter(counter, count, card.getController(), false, null); } }