diff --git a/forge-game/src/main/java/forge/game/CardTraitBase.java b/forge-game/src/main/java/forge/game/CardTraitBase.java
index e63ae76c366..1df09691ee1 100644
--- a/forge-game/src/main/java/forge/game/CardTraitBase.java
+++ b/forge-game/src/main/java/forge/game/CardTraitBase.java
@@ -561,4 +561,12 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
// this does overwrite the original MapParams
this.originalMapParams = Maps.newHashMap(this.mapParams);
}
+
+ protected void copyHelper(CardTraitBase copy, Card host) {
+ copy.originalMapParams = Maps.newHashMap(originalMapParams);
+ copy.mapParams = Maps.newHashMap(originalMapParams);
+ copy.sVars = Maps.newHashMap(sVars);
+ // dont use setHostCard to not trigger the not copied parts yet
+ copy.hostCard = host;
+ }
}
diff --git a/forge-game/src/main/java/forge/game/GameObjectPredicates.java b/forge-game/src/main/java/forge/game/GameObjectPredicates.java
new file mode 100644
index 00000000000..9331bcb6a89
--- /dev/null
+++ b/forge-game/src/main/java/forge/game/GameObjectPredicates.java
@@ -0,0 +1,44 @@
+/*
+ * Forge: Play Magic: the Gathering.
+ * Copyright (C) 2011 Forge Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package forge.game;
+
+import com.google.common.base.Predicate;
+
+import forge.game.card.Card;
+import forge.game.player.Player;
+import forge.game.spellability.SpellAbility;
+
+
+/**
+ *
+ * Predicate interface.
+ *
+ *
+ * @author Forge
+ */
+public final class GameObjectPredicates {
+
+ public static final Predicate restriction(final String[] restrictions, final Player sourceController, final Card source, final SpellAbility spellAbility) {
+ return new Predicate() {
+ @Override
+ public boolean apply(final GameObject c) {
+ return (c != null) && c.isValid(restrictions, sourceController, source, spellAbility);
+ }
+ };
+ }
+}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java
index c68659e0709..a4e8e6c1217 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java
@@ -122,7 +122,7 @@ public class DamageAllEffect extends DamageBaseEffect {
if (!usedDamageMap) {
preventMap.triggerPreventDamage(false);
- damageMap.triggerDamageDoneOnce(false, sa);
+ damageMap.triggerDamageDoneOnce(false, game, sa);
preventMap.clear();
damageMap.clear();
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java
index 5ba5f4d8203..6092fde9af8 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java
@@ -166,7 +166,7 @@ public class DamageDealEffect extends DamageBaseEffect {
if (!usedDamageMap) {
preventMap.triggerPreventDamage(false);
// non combat damage cause lifegain there
- damageMap.triggerDamageDoneOnce(false, sa);
+ damageMap.triggerDamageDoneOnce(false, game, sa);
preventMap.clear();
damageMap.clear();
@@ -215,7 +215,7 @@ public class DamageDealEffect extends DamageBaseEffect {
if (!usedDamageMap) {
preventMap.triggerPreventDamage(false);
// non combat damage cause lifegain there
- damageMap.triggerDamageDoneOnce(false, sa);
+ damageMap.triggerDamageDoneOnce(false, game, sa);
preventMap.clear();
damageMap.clear();
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java
index 8887b60bfb6..913b6e979a2 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java
@@ -136,7 +136,7 @@ public class DamageEachEffect extends DamageBaseEffect {
if (!usedDamageMap) {
preventMap.triggerPreventDamage(false);
- damageMap.triggerDamageDoneOnce(false, sa);
+ damageMap.triggerDamageDoneOnce(false, game, sa);
preventMap.clear();
damageMap.clear();
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageResolveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageResolveEffect.java
index 6ed532889a6..a9d7c35a677 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DamageResolveEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DamageResolveEffect.java
@@ -25,7 +25,7 @@ public class DamageResolveEffect extends SpellAbilityEffect {
}
// non combat damage cause lifegain there
if (damageMap != null) {
- damageMap.triggerDamageDoneOnce(false, sa);
+ damageMap.triggerDamageDoneOnce(false, sa.getHostCard().getGame(), sa);
damageMap.clear();
}
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java b/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java
index 1596cbea793..7ae2fc18dc7 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java
@@ -154,7 +154,7 @@ public class FightEffect extends DamageBaseEffect {
if (!usedDamageMap) {
preventMap.triggerPreventDamage(false);
- damageMap.triggerDamageDoneOnce(false, sa);
+ damageMap.triggerDamageDoneOnce(false, fighterA.getGame(), sa);
preventMap.clear();
damageMap.clear();
diff --git a/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java
index e7ed46e2b1e..54e32d8e6d6 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java
@@ -236,7 +236,7 @@ public class RepeatEachEffect extends SpellAbilityEffect {
sa.getPreventMap().triggerPreventDamage(false);
sa.setPreventMap(null);
// non combat damage cause lifegain there
- sa.getDamageMap().triggerDamageDoneOnce(false, sa);
+ sa.getDamageMap().triggerDamageDoneOnce(false, game, sa);
sa.setDamageMap(null);
}
if (sa.hasParam("ChangeZoneTable")) {
diff --git a/forge-game/src/main/java/forge/game/card/CardDamageMap.java b/forge-game/src/main/java/forge/game/card/CardDamageMap.java
index 8a742dacf68..b5cf899f352 100644
--- a/forge-game/src/main/java/forge/game/card/CardDamageMap.java
+++ b/forge-game/src/main/java/forge/game/card/CardDamageMap.java
@@ -1,16 +1,20 @@
/**
- *
+ *
*/
package forge.game.card;
import java.util.Map;
+import java.util.Set;
import com.google.common.collect.ForwardingTable;
import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
+import forge.game.Game;
import forge.game.GameEntity;
+import forge.game.GameObjectPredicates;
import forge.game.ability.AbilityKey;
import forge.game.keyword.Keyword;
import forge.game.spellability.SpellAbility;
@@ -18,7 +22,7 @@ import forge.game.trigger.TriggerType;
public class CardDamageMap extends ForwardingTable {
private Table dataMap = HashBasedTable.create();
-
+
public CardDamageMap(Table damageMap) {
this.putAll(damageMap);
}
@@ -38,13 +42,13 @@ public class CardDamageMap extends ForwardingTable {
runParams.put(AbilityKey.DamageTarget, ge);
runParams.put(AbilityKey.DamageAmount, sum);
runParams.put(AbilityKey.IsCombatDamage, isCombat);
-
+
ge.getGame().getTriggerHandler().runTrigger(TriggerType.DamagePreventedOnce, runParams, false);
}
}
}
- public void triggerDamageDoneOnce(boolean isCombat, final SpellAbility sa) {
+ public void triggerDamageDoneOnce(boolean isCombat, final Game game, final SpellAbility sa) {
// Source -> Targets
for (Map.Entry> e : this.rowMap().entrySet()) {
final Card sourceLKI = e.getKey();
@@ -58,9 +62,9 @@ public class CardDamageMap extends ForwardingTable {
runParams.put(AbilityKey.DamageTargets, Sets.newHashSet(e.getValue().keySet()));
runParams.put(AbilityKey.DamageAmount, sum);
runParams.put(AbilityKey.IsCombatDamage, isCombat);
-
- sourceLKI.getGame().getTriggerHandler().runTrigger(TriggerType.DamageDealtOnce, runParams, false);
-
+
+ game.getTriggerHandler().runTrigger(TriggerType.DamageDealtOnce, runParams, false);
+
if (sourceLKI.hasKeyword(Keyword.LIFELINK)) {
sourceLKI.getController().gainLife(sum, sourceLKI, sa);
}
@@ -79,10 +83,15 @@ public class CardDamageMap extends ForwardingTable {
runParams.put(AbilityKey.DamageSources, Sets.newHashSet(e.getValue().keySet()));
runParams.put(AbilityKey.DamageAmount, sum);
runParams.put(AbilityKey.IsCombatDamage, isCombat);
-
- ge.getGame().getTriggerHandler().runTrigger(TriggerType.DamageDoneOnce, runParams, false);
+
+ game.getTriggerHandler().runTrigger(TriggerType.DamageDoneOnce, runParams, false);
}
}
+
+ final Map runParams = AbilityKey.newMap();
+ runParams.put(AbilityKey.DamageMap, new CardDamageMap(this));
+ runParams.put(AbilityKey.IsCombatDamage, isCombat);
+ game.getTriggerHandler().runTrigger(TriggerType.DamageAll, runParams, false);
}
/**
* special put logic, sum the values
@@ -98,4 +107,29 @@ public class CardDamageMap extends ForwardingTable {
return dataMap;
}
+ public int filteredAmount(String validSource, String validTarget, Card host, SpellAbility sa) {
+ int result = 0;
+
+ Set filteredSource = null;
+ Set filteredTarget = null;
+ if (validSource != null) {
+ filteredSource = Sets.newHashSet(Iterables.filter(rowKeySet(), GameObjectPredicates.restriction(validSource.split(","), host.getController(), host, sa)));
+ }
+ if (validTarget != null) {
+ filteredTarget = Sets.newHashSet(Iterables.filter(columnKeySet(), GameObjectPredicates.restriction(validTarget.split(","), host.getController(), host, sa)));
+ }
+
+ for (Table.Cell c : cellSet()) {
+ if (filteredSource != null && !filteredSource.contains(c.getRowKey())) {
+ continue;
+ }
+ if (filteredTarget != null && !filteredTarget.contains(c.getColumnKey())) {
+ continue;
+ }
+ result += c.getValue();
+ }
+
+ return result;
+ }
+
}
diff --git a/forge-game/src/main/java/forge/game/combat/Combat.java b/forge-game/src/main/java/forge/game/combat/Combat.java
index 72099af6fb4..4fc83734d74 100644
--- a/forge-game/src/main/java/forge/game/combat/Combat.java
+++ b/forge-game/src/main/java/forge/game/combat/Combat.java
@@ -836,7 +836,7 @@ public class Combat {
// Run the trigger to deal combat damage once
// LifeLink for Combat Damage at this place
- dealtDamageTo.triggerDamageDoneOnce(true, null);
+ dealtDamageTo.triggerDamageDoneOnce(true, game, null);
dealtDamageTo.clear();
counterTable.triggerCountersPutAll(game);
diff --git a/forge-game/src/main/java/forge/game/cost/CostDamage.java b/forge-game/src/main/java/forge/game/cost/CostDamage.java
index cc33d7c6501..198ac700ea8 100644
--- a/forge-game/src/main/java/forge/game/cost/CostDamage.java
+++ b/forge-game/src/main/java/forge/game/cost/CostDamage.java
@@ -74,7 +74,7 @@ public class CostDamage extends CostPart {
payer.addDamage(decision.c, source, damageMap, preventMap, table, sa);
preventMap.triggerPreventDamage(false);
- damageMap.triggerDamageDoneOnce(false, sa);
+ damageMap.triggerDamageDoneOnce(false, source.getGame(), sa);
table.triggerCountersPutAll(payer.getGame());
preventMap.clear();
diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java b/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java
index 34f48bcc28c..2ff6a07d9ec 100644
--- a/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java
+++ b/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java
@@ -164,9 +164,8 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
*/
public final ReplacementEffect copy(final Card host, final boolean lki) {
final ReplacementEffect res = (ReplacementEffect) clone();
- for (String key : getSVars()) {
- res.setSVar(key, getSVar(key));
- }
+
+ copyHelper(res, host);
final SpellAbility sa = this.getOverridingAbility();
if (sa != null) {
@@ -182,8 +181,6 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
res.setOtherChoices(null);
}
- res.setHostCard(host);
-
res.setActiveZone(validHostZones);
res.setLayer(getLayer());
return res;
diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
index d467b6c0ffe..75479eaadfc 100644
--- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
+++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
@@ -870,13 +870,11 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
clone.view = new SpellAbilityView(clone);
// dont use setHostCard to not trigger the not copied parts yet
- clone.hostCard = host;
+
+ copyHelper(clone, host);
if (!lki && host != null && host.getGame() != null) {
host.getGame().addSpellAbility(clone);
}
- // need to clone the maps too so they can be changed
- clone.originalMapParams = Maps.newHashMap(this.originalMapParams);
- clone.mapParams = Maps.newHashMap(this.mapParams);
clone.triggeringObjects = AbilityKey.newMap(this.triggeringObjects);
@@ -902,7 +900,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
// clear maps for copy, the values will be added later
clone.additionalAbilities = Maps.newHashMap();
clone.additionalAbilityLists = Maps.newHashMap();
- clone.sVars = Maps.newHashMap();
// run special copy Ability to make a deep copy
CardFactory.copySpellAbility(this, clone, host, activ, lki);
} catch (final CloneNotSupportedException e) {
diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java
index 3f3b33d26b9..f8442310ec0 100644
--- a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java
+++ b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java
@@ -819,13 +819,7 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
clone = (StaticAbility) clone();
clone.id = lki ? id : nextId();
- // dont use setHostCard to not trigger the not copied parts yet
- clone.hostCard = host;
- // need to clone the maps too so they can be changed
- clone.originalMapParams = Maps.newHashMap(this.originalMapParams);
- clone.mapParams = Maps.newHashMap(this.mapParams);
-
- clone.sVars = Maps.newHashMap(this.sVars);
+ copyHelper(clone, host);
clone.layers = this.generateLayer();
diff --git a/forge-game/src/main/java/forge/game/trigger/Trigger.java b/forge-game/src/main/java/forge/game/trigger/Trigger.java
index b9130a8cbd8..8388fa3ccdf 100644
--- a/forge-game/src/main/java/forge/game/trigger/Trigger.java
+++ b/forge-game/src/main/java/forge/game/trigger/Trigger.java
@@ -37,7 +37,6 @@ import forge.game.zone.ZoneType;
import java.util.*;
import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import forge.util.TextUtil;
@@ -523,9 +522,7 @@ public abstract class Trigger extends TriggerReplacementBase {
public final Trigger copy(Card newHost, boolean lki) {
final Trigger copy = (Trigger) clone();
- copy.originalMapParams = Maps.newHashMap(originalMapParams);
- copy.mapParams = Maps.newHashMap(originalMapParams);
- copy.setHostCard(newHost);
+ copyHelper(copy, newHost);
if (getOverridingAbility() != null) {
copy.setOverridingAbility(getOverridingAbility().copy(newHost, lki));
diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerDamageAll.java b/forge-game/src/main/java/forge/game/trigger/TriggerDamageAll.java
new file mode 100644
index 00000000000..4df7cf871b8
--- /dev/null
+++ b/forge-game/src/main/java/forge/game/trigger/TriggerDamageAll.java
@@ -0,0 +1,52 @@
+package forge.game.trigger;
+
+import java.util.Map;
+
+import forge.game.ability.AbilityKey;
+import forge.game.card.Card;
+import forge.game.card.CardDamageMap;
+import forge.game.spellability.SpellAbility;
+import forge.util.Localizer;
+
+public class TriggerDamageAll extends Trigger {
+
+ public TriggerDamageAll(Map params, Card host, boolean intrinsic) {
+ super(params, host, intrinsic);
+ }
+
+ @Override
+ public boolean performTest(Map runParams) {
+
+ if (hasParam("CombatDamage")) {
+ if (getParam("CombatDamage").equals("True")) {
+ if (!((Boolean) runParams.get(AbilityKey.IsCombatDamage))) {
+ return false;
+ }
+ } else if (getParam("CombatDamage").equals("False")) {
+ if (((Boolean) runParams.get(AbilityKey.IsCombatDamage))) {
+ return false;
+ }
+ }
+ }
+ final CardDamageMap table = (CardDamageMap) runParams.get(AbilityKey.DamageMap);
+ return filterTable(table) > 0;
+ }
+
+ @Override
+ public void setTriggeringObjects(SpellAbility sa, Map runParams) {
+ final CardDamageMap table = (CardDamageMap) runParams.get(AbilityKey.DamageMap);
+
+ sa.setTriggeringObject(AbilityKey.DamageAmount, filterTable(table));
+ }
+
+ @Override
+ public String getImportantStackObjects(SpellAbility sa) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Localizer.getInstance().getMessage("lblAmount")).append(": ").append(sa.getTriggeringObject(AbilityKey.DamageAmount));
+ return sb.toString();
+ }
+
+ private int filterTable(CardDamageMap table) {
+ return table.filteredAmount(getParam("ValidSource"), getParam("ValidTarget"), getHostCard(), null);
+ }
+}
diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerType.java b/forge-game/src/main/java/forge/game/trigger/TriggerType.java
index 952a21bb6f9..b0f82c1c153 100644
--- a/forge-game/src/main/java/forge/game/trigger/TriggerType.java
+++ b/forge-game/src/main/java/forge/game/trigger/TriggerType.java
@@ -43,6 +43,7 @@ public enum TriggerType {
CounterRemovedOnce(TriggerCounterRemovedOnce.class),
Crewed(TriggerCrewed.class),
Cycled(TriggerCycled.class),
+ DamageAll(TriggerDamageAll.class),
DamageDealtOnce(TriggerDamageDealtOnce.class),
DamageDone(TriggerDamageDone.class),
DamageDoneOnce(TriggerDamageDoneOnce.class),
diff --git a/forge-gui/res/cardsfolder/m/mindblade_render.txt b/forge-gui/res/cardsfolder/m/mindblade_render.txt
new file mode 100644
index 00000000000..0be61532af9
--- /dev/null
+++ b/forge-gui/res/cardsfolder/m/mindblade_render.txt
@@ -0,0 +1,8 @@
+Name:Mindblade Render
+ManaCost:1 B
+Types:Creature Azra Warrior
+PT:1/3
+T:Mode$ DamageAll | ValidSource$ Creature.Warrior | ValidTarget$ Player.Opponent | CombatDamage$ True | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever your opponents are dealt combat damage, if any of that damage was dealt by a Warrior, you draw a card and you lose 1 life.
+SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ DBLoseLife
+SVar:DBLoseLife:DB$LoseLife | Defined$ You | LifeAmount$ 1
+Oracle: Whenever your opponents are dealt combat damage, if any of that damage was dealt by a Warrior, you draw a card and you lose 1 life.