mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
Bioessence Hydra: add new Trigger for counters on many game entities at once
This commit is contained in:
committed by
swordshine
parent
f7546eca32
commit
f408950aad
@@ -1601,7 +1601,7 @@ public class ComputerUtilCard {
|
||||
pumped.addChangedCardKeywords(kws, null, false, false, timestamp);
|
||||
Set<CounterType> 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()) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String, Object> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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<String, Object> 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<CounterType, Integer> 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();
|
||||
|
||||
|
||||
@@ -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<GameEntity, CounterType, Integer> {
|
||||
|
||||
private Table<GameEntity, CounterType, Integer> dataMap = HashBasedTable.create();
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.google.common.collect.ForwardingTable#delegate()
|
||||
*/
|
||||
@Override
|
||||
protected Table<GameEntity, CounterType, Integer> 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<GameEntity, Integer> filterTable(CounterType type, String valid, Card host, SpellAbility sa) {
|
||||
Map<GameEntity, Integer> result = Maps.newHashMap();
|
||||
|
||||
for (Map.Entry<GameEntity, Integer> 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<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Objects", this);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.CounterAddedAll, runParams, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<CounterType, Integer> 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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<String, String> 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
|
||||
}
|
||||
|
||||
@@ -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<GameEntity> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<CounterType, Integer> 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<CounterType, Integer> tgtCounters = gameCard.getCounters();
|
||||
Map<String, Object> 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<CounterType, Integer> tgtCounters = entity.getCounters();
|
||||
Map<String, Object> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Player> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(sourceLKI, assigneeCards, dmg, null, true);
|
||||
for (Entry<Card, Integer> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Card> sources = card.getGame().getCardsIn(ZoneType.Battlefield);
|
||||
FCollectionView<Card> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Card> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<Card> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1131,16 +1131,18 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
|
||||
@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<Card> {
|
||||
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<Card> {
|
||||
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<Card> {
|
||||
return total;
|
||||
}
|
||||
|
||||
public final void addCombatDamage(final Map<Card, Integer> map, final CardDamageMap damageMap, final CardDamageMap preventMap) {
|
||||
public final void addCombatDamage(final Map<Card, Integer> map, final CardDamageMap damageMap, final CardDamageMap preventMap, GameEntityCounterTable counterTable) {
|
||||
for (final Entry<Card, Integer> 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<Card> {
|
||||
return restDamage;
|
||||
}
|
||||
|
||||
public final void addDamage(final Map<Card, Integer> sourcesMap, CardDamageMap damageMap) {
|
||||
public final void addDamage(final Map<Card, Integer> sourcesMap, CardDamageMap damageMap, GameEntityCounterTable counterTable) {
|
||||
for (final Entry<Card, Integer> 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<Card> {
|
||||
* 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<Card> {
|
||||
|
||||
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<Card> {
|
||||
return etbCounters.cellSet();
|
||||
}
|
||||
|
||||
public final boolean putEtbCounters() {
|
||||
public final boolean putEtbCounters(GameEntityCounterTable table) {
|
||||
boolean changed = false;
|
||||
for (Table.Cell<Player, CounterType, Integer> e : etbCounters.cellSet()) {
|
||||
CounterType ct = e.getColumnKey();
|
||||
@@ -6062,7 +6072,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
changed |= addCounter(ct, e.getValue(), e.getRowKey(), true) > 0;
|
||||
changed |= addCounter(ct, e.getValue(), e.getRowKey(), true, table) > 0;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<Card, Integer> 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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<Card> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -559,7 +559,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
|
||||
// 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<Player> {
|
||||
|| 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<Player> {
|
||||
}
|
||||
|
||||
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<Player> {
|
||||
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<Player> {
|
||||
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));
|
||||
|
||||
@@ -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<StaticAbi
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean applyAbility(String mode, Card card, CounterType type) {
|
||||
|
||||
// don't apply the ability if it hasn't got the right mode
|
||||
if (!getParam("Mode").equals(mode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.isSuppressed() || !this.checkConditions()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode.equals("CantPutCounter")) {
|
||||
return StaticAbilityCantPutCounter.applyCantPutCounter(this, card, type);
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean applyAbility(String mode, Player player, CounterType type) {
|
||||
|
||||
// don't apply the ability if it hasn't got the right mode
|
||||
if (!getParam("Mode").equals(mode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.isSuppressed() || !this.checkConditions()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode.equals("CantPutCounter")) {
|
||||
return StaticAbilityCantPutCounter.applyCantPutCounter(this, player, type);
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply ability.
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package forge.game.staticability;
|
||||
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.player.Player;
|
||||
|
||||
public class StaticAbilityCantPutCounter {
|
||||
|
||||
public static boolean applyCantPutCounter(final StaticAbility staticAbility, final Card card,
|
||||
final CounterType type) {
|
||||
final Card hostCard = staticAbility.getHostCard();
|
||||
|
||||
if (staticAbility.hasParam("CounterType")) {
|
||||
CounterType t = CounterType.valueOf(staticAbility.getParam("CounterType"));
|
||||
if (t != null && !type.equals(t)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (staticAbility.hasParam("ValidCard")) {
|
||||
if (!card.isValid(staticAbility.getParam("ValidCard").split(","), hostCard.getController(), hostCard, null)) {
|
||||
return false;
|
||||
}
|
||||
} else if (staticAbility.hasParam("ValidPlayer")) {
|
||||
// for the other part
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean applyCantPutCounter(final StaticAbility staticAbility, final Player player,
|
||||
final CounterType type) {
|
||||
final Card hostCard = staticAbility.getHostCard();
|
||||
|
||||
if (staticAbility.hasParam("CounterType")) {
|
||||
CounterType t = CounterType.valueOf(staticAbility.getParam("CounterType"));
|
||||
if (t != null && !type.equals(t)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (staticAbility.hasParam("ValidPlayer")) {
|
||||
if (!player.isValid(staticAbility.getParam("ValidPlayer").split(","), hostCard.getController(), hostCard, null)) {
|
||||
return false;
|
||||
}
|
||||
} else if (staticAbility.hasParam("ValidCard")) {
|
||||
// for the other part
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package forge.game.trigger;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameEntityCounterTable;
|
||||
import forge.game.card.*;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
public class TriggerCounterAddedAll extends Trigger {
|
||||
|
||||
public TriggerCounterAddedAll(Map<String, String> params, Card host, boolean intrinsic) {
|
||||
super(params, host, intrinsic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performTest(Map<String, Object> 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<GameEntity, Integer> 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<GameEntity, Integer> filterTable(GameEntityCounterTable table) {
|
||||
CounterType counterType = CounterType.getType(getParam("CounterType"));
|
||||
String valid = getParam("Valid");
|
||||
|
||||
return table.filterTable(counterType, valid, getHostCard(), null);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
14
forge-gui/res/cardsfolder/upcoming/bioessence_hydra.txt
Normal file
14
forge-gui/res/cardsfolder/upcoming/bioessence_hydra.txt
Normal file
@@ -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.
|
||||
|
||||
5
forge-gui/res/cardsfolder/upcoming/price_of_betrayal.txt
Normal file
5
forge-gui/res/cardsfolder/upcoming/price_of_betrayal.txt
Normal file
@@ -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.
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user