mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 09:48:02 +00:00
Merge branch 'triggerDamageAll' into 'master'
TriggerDamageAll: add new Trigger for Mindblade Render See merge request core-developers/forge!2519
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.game;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Predicate<GameObject> interface.
|
||||
* </p>
|
||||
*
|
||||
* @author Forge
|
||||
*/
|
||||
public final class GameObjectPredicates {
|
||||
|
||||
public static final Predicate<GameObject> restriction(final String[] restrictions, final Player sourceController, final Card source, final SpellAbility spellAbility) {
|
||||
return new Predicate<GameObject>() {
|
||||
@Override
|
||||
public boolean apply(final GameObject c) {
|
||||
return (c != null) && c.isValid(restrictions, sourceController, source, spellAbility);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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<Card, GameEntity, Integer> {
|
||||
private Table<Card, GameEntity, Integer> dataMap = HashBasedTable.create();
|
||||
|
||||
|
||||
public CardDamageMap(Table<Card, GameEntity, Integer> damageMap) {
|
||||
this.putAll(damageMap);
|
||||
}
|
||||
@@ -38,13 +42,13 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
||||
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<Card, Map<GameEntity, Integer>> e : this.rowMap().entrySet()) {
|
||||
final Card sourceLKI = e.getKey();
|
||||
@@ -58,9 +62,9 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
||||
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<Card, GameEntity, Integer> {
|
||||
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<AbilityKey, Object> 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<Card, GameEntity, Integer> {
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
public int filteredAmount(String validSource, String validTarget, Card host, SpellAbility sa) {
|
||||
int result = 0;
|
||||
|
||||
Set<Card> filteredSource = null;
|
||||
Set<GameEntity> 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<Card, GameEntity, Integer> c : cellSet()) {
|
||||
if (filteredSource != null && !filteredSource.contains(c.getRowKey())) {
|
||||
continue;
|
||||
}
|
||||
if (filteredTarget != null && !filteredTarget.contains(c.getColumnKey())) {
|
||||
continue;
|
||||
}
|
||||
result += c.getValue();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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<String, String> params, Card host, boolean intrinsic) {
|
||||
super(params, host, intrinsic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performTest(Map<AbilityKey, Object> 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<AbilityKey, Object> 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);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
8
forge-gui/res/cardsfolder/m/mindblade_render.txt
Normal file
8
forge-gui/res/cardsfolder/m/mindblade_render.txt
Normal file
@@ -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.
|
||||
Reference in New Issue
Block a user