Global DamageHistory (#622)

* Global DamageHistory

* Add new cards

* Improve Ogre Enforcer

* Clean up

* Minor fixes
This commit is contained in:
tool4ever
2022-07-04 15:27:44 +02:00
committed by GitHub
parent 584c768039
commit 3d634b6dac
46 changed files with 359 additions and 414 deletions

View File

@@ -206,7 +206,7 @@ public abstract class GameState {
cardsReferencedByID.add(card.getExiledWith());
}
if (zone == ZoneType.Battlefield) {
if (!card.getAttachedCards().isEmpty()) {
if (card.hasCardAttachments()) {
// Remember the ID of cards that have attachments
cardsReferencedByID.add(card);
}
@@ -375,7 +375,7 @@ public abstract class GameState {
newText.append("|Imprinting:").append(TextUtil.join(imprintedCardIds, ","));
}
if (!c.getMergedCards().isEmpty()) {
if (c.hasMergedCard()) {
List<String> mergedCardNames = new ArrayList<>();
for (Card merged : c.getMergedCards()) {
if (c.getTopMergedCard() == merged) {

View File

@@ -91,6 +91,7 @@ public class GameCopier {
newPlayer.setLifeLostLastTurn(origPlayer.getLifeLostLastTurn());
newPlayer.setLifeLostThisTurn(origPlayer.getLifeLostThisTurn());
newPlayer.setLifeGainedThisTurn(origPlayer.getLifeGainedThisTurn());
// TODO creatureAttackedThisTurn
for (Mana m : origPlayer.getManaPool()) {
newPlayer.getManaPool().addMana(m, false);
}
@@ -207,6 +208,13 @@ public class GameCopier {
private void copyGameState(Game newGame) {
newGame.setAge(origGame.getAge());
// TODO countersAddedThisTurn
if (origGame.getMonarch() != null) {
newGame.setMonarch(playerMap.get(origGame.getMonarch()));
}
for (ZoneType zone : ZONES) {
for (Card card : origGame.getCardsIn(zone)) {
addCard(newGame, zone, card);
@@ -300,6 +308,7 @@ public class GameCopier {
newCard.setPTCharacterDefiningTable(c.getSetPTCharacterDefiningTable());
newCard.setPTBoost(c.getPTBoostTable());
// TODO copy by map
newCard.setDamage(c.getDamage());
newCard.setChangedCardColors(c.getChangedCardColorsTable());

View File

@@ -22,6 +22,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -47,6 +48,7 @@ import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardDamageHistory;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardUtil;
@@ -78,6 +80,7 @@ import forge.trackable.Tracker;
import forge.util.Aggregates;
import forge.util.MyRandom;
import forge.util.Visitor;
import forge.util.collect.FCollection;
/**
* Represents the state of a <i>single game</i>, a new instance is created for each game.
@@ -119,6 +122,9 @@ public class Game {
private Table<CounterType, Player, List<Pair<Card, Integer>>> countersAddedThisTurn = HashBasedTable.create();
private FCollection<CardDamageHistory> globalDamageHistory = new FCollection<>();
private IdentityHashMap<Pair<Integer, Boolean>, Pair<Card, GameEntity>> damageThisTurnLKI = new IdentityHashMap<>();
private Map<Player, Card> topLibsCast = Maps.newHashMap();
private Map<Card, Integer> facedownWhileCasting = Maps.newHashMap();
@@ -251,7 +257,7 @@ public class Game {
if (c == null) {
return null;
}
return changeZoneLKIInfo.containsKey(c.getId()) ? changeZoneLKIInfo.get(c.getId()) : c;
return changeZoneLKIInfo.getOrDefault(c.getId(), c);
}
public final void clearChangeZoneLKIInfo() {
changeZoneLKIInfo.clear();
@@ -1088,6 +1094,7 @@ public class Game {
public void onCleanupPhase() {
clearCounterAddedThisTurn();
clearGlobalDamageHistory();
// some cards need this info updated even after a player lost, so don't skip them
for (Player player : getRegisteredPlayers()) {
player.onCleanupPhase();
@@ -1129,6 +1136,48 @@ public class Game {
countersAddedThisTurn.clear();
}
/**
* Gets the damage instances done this turn.
* @param isCombat if true only combat damage matters, pass null for both
* @param anyIsEnough if true returns early once result has an entry
* @param validSourceCard
* @param validTargetEntity
* @param source
* @param sourceController
* @param ctb
* @return List<Integer> for each source
*/
public List<Integer> getDamageDoneThisTurn(Boolean isCombat, boolean anyIsEnough, String validSourceCard, String validTargetEntity, Card source, Player sourceController, CardTraitBase ctb) {
final List<Integer> dmgList = Lists.newArrayList();
for (CardDamageHistory cdh : globalDamageHistory) {
int dmg = cdh.getDamageDoneThisTurn(isCombat, anyIsEnough, validSourceCard, validTargetEntity, source, sourceController, ctb);
if (dmg == 0) {
continue;
}
dmgList.add(dmg);
if (anyIsEnough) {
break;
}
}
return dmgList;
}
public void addGlobalDamageHistory(CardDamageHistory cdh, Pair<Integer, Boolean> dmg, Card source, GameEntity target) {
globalDamageHistory.add(cdh);
damageThisTurnLKI.put(dmg, Pair.of(source, target));
}
public void clearGlobalDamageHistory() {
globalDamageHistory.clear();
damageThisTurnLKI.clear();
}
public Pair<Card, GameEntity> getDamageLKI(Pair<Integer, Boolean> dmg) {
return damageThisTurnLKI.get(dmg);
}
public Card getTopLibForPlayer(Player P) {
return topLibsCast.get(P);
}

View File

@@ -27,7 +27,6 @@ import java.util.Set;
import forge.util.*;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Predicate;
import com.google.common.collect.ComparisonChain;
@@ -1323,33 +1322,13 @@ public class GameAction {
noRegCreats.add(c);
checkAgain = true;
} else if (c.hasKeyword("CARDNAME can't be destroyed by lethal damage unless lethal damage dealt by a single source is marked on it.")) {
// merge entries with same source
List<Integer> dmgList = Lists.newArrayList();
List<Pair<Card, Integer>> remainingDamaged = Lists.newArrayList(c.getReceivedDamageFromThisTurn());
while (!remainingDamaged.isEmpty()) {
Pair <Card, Integer> damaged = remainingDamaged.get(0);
int sum = damaged.getRight();
remainingDamaged.remove(damaged);
for (Pair<Card, Integer> other : Lists.newArrayList(remainingDamaged)) {
if (other.getLeft().equalsWithTimestamp(damaged.getLeft())) {
sum += other.getRight();
// once it got counted keep it out
remainingDamaged.remove(other);
}
}
dmgList.add(sum);
}
for (final Integer dmg : dmgList) {
if (c.getLethal() <= dmg.intValue() || c.hasBeenDealtDeathtouchDamage()) {
if (desCreats == null) {
desCreats = new CardCollection();
}
desCreats.add(c);
c.setHasBeenDealtDeathtouchDamage(false);
checkAgain = true;
break;
if (c.getLethal() <= c.getMaxDamageFromSource() || c.hasBeenDealtDeathtouchDamage()) {
if (desCreats == null) {
desCreats = new CardCollection();
}
desCreats.add(c);
c.setHasBeenDealtDeathtouchDamage(false);
checkAgain = true;
}
}
// Rule 704.5g - Destroy due to lethal damage
@@ -2364,6 +2343,7 @@ public class GameAction {
game.getReplacementHandler().runReplaceDamage(isCombat, damageMap, preventMap, counterTable, cause);
Map<Card, Integer> lethalDamage = Maps.newHashMap();
Map<Integer, Card> lkiCache = Maps.newHashMap();
// Actually deal damage according to replaced damage map
for (Map.Entry<Card, Map<GameEntity, Integer>> et : damageMap.rowMap().entrySet()) {
@@ -2390,6 +2370,8 @@ public class GameAction {
e.setValue(Integer.valueOf(e.getKey().addDamageAfterPrevention(e.getValue(), sourceLKI, isCombat, counterTable)));
sum += e.getValue();
sourceLKI.getDamageHistory().registerDamage(e.getValue(), isCombat, sourceLKI, e.getKey(), lkiCache);
}
if (sum > 0 && sourceLKI.hasKeyword(Keyword.LIFELINK)) {

View File

@@ -210,7 +210,6 @@ public final class GameActionUtil {
final Cost escapeCost = new Cost(k[1], true);
final SpellAbility newSA = sa.copyWithManaCostReplaced(activator, escapeCost);
newSA.setActivatingPlayer(activator);
newSA.putParam("PrecostDesc", "Escape—");
newSA.putParam("CostDesc", escapeCost.toString());

View File

@@ -17,9 +17,13 @@
*/
package forge.game;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.game.ability.AbilityUtils;
@@ -46,6 +50,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
private String name = "";
protected CardCollection attachedCards = new CardCollection();
protected Map<CounterType, Integer> counters = Maps.newHashMap();
protected List<Pair<Integer, Boolean>> damageReceivedThisTurn = Lists.newArrayList();
protected GameEntity(int id0) {
id = id0;
@@ -330,6 +335,30 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
addCounterInternal(CounterType.get(counterType), n, source, fireEvents, table);
}
public void receiveDamage(Pair<Integer, Boolean> dmg) {
damageReceivedThisTurn.add(dmg);
}
public final int getAssignedDamage() {
return getAssignedDamage(null, null);
}
public final int getAssignedCombatDamage() {
return getAssignedDamage(true, null);
}
public final int getAssignedDamage(Boolean isCombat, final Card source) {
int num = 0;
for (Pair<Integer, Boolean> dmg : damageReceivedThisTurn) {
if (isCombat != null && dmg.getRight() != isCombat) {
continue;
}
if (source != null && !getGame().getDamageLKI(dmg).getLeft().equalsWithTimestamp(source)) {
continue;
}
num += dmg.getLeft();
}
return num;
}
@Override
public final boolean equals(Object o) {
if (o == null) { return false; }

View File

@@ -327,8 +327,8 @@ public final class AbilityFactory {
}
private static final TargetRestrictions readTarget(Map<String, String> mapParams) {
final String min = mapParams.containsKey("TargetMin") ? mapParams.get("TargetMin") : "1";
final String max = mapParams.containsKey("TargetMax") ? mapParams.get("TargetMax") : "1";
final String min = mapParams.getOrDefault("TargetMin", "1");
final String max = mapParams.getOrDefault("TargetMax", "1");
// TgtPrompt should only be needed for more complicated ValidTgts
String tgtWhat = mapParams.get("ValidTgts");

View File

@@ -14,6 +14,7 @@ import java.util.regex.Pattern;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
@@ -2030,7 +2031,7 @@ public class AbilityUtils {
return doXMath(c.getTotalDamageDoneBy(), expr, c, ctb);
}
if (sq[0].equals("TotalDamageReceivedThisTurn")) {
return doXMath(c.getTotalDamageReceivedThisTurn(), expr, c, ctb);
return doXMath(c.getAssignedDamage(), expr, c, ctb);
}
if (sq[0].contains("CardPower")) {
@@ -2091,13 +2092,6 @@ public class AbilityUtils {
return doXMath(c.getTimesMutated(), expr, c, ctb);
}
if (sq[0].startsWith("DamageDoneByPlayerThisTurn")) {
int sum = 0;
for (Player p : getDefinedPlayers(c, sq[1], ctb)) {
sum += c.getReceivedDamageByPlayerThisTurn(p);
}
return doXMath(sum, expr, c, ctb);
}
if (sq[0].equals("RegeneratedThisTurn")) {
return doXMath(c.getRegeneratedThisTurn(), expr, c, ctb);
}
@@ -2359,20 +2353,28 @@ public class AbilityUtils {
return doXMath(player.getOpponentsTotalPoisonCounters(), expr, c, ctb);
}
if (sq[0].equals("YourDamageThisTurn")) {
return doXMath(player.getAssignedDamage(), expr, c, ctb);
}
if (sq[0].equals("TotalOppDamageThisTurn")) {
return doXMath(player.getOpponentsAssignedDamage(), expr, c, ctb);
}
if (sq[0].equals("MaxOppDamageThisTurn")) {
return doXMath(player.getMaxOpponentAssignedDamage(), expr, c, ctb);
}
if (sq[0].startsWith("YourDamageSourcesThisTurn")) {
Iterable<Card> allSrc = player.getAssignedDamageSources();
String restriction = sq[0].split(" ")[1];
return doXMath(CardLists.getValidCardCount(allSrc, restriction, player, c, ctb), expr, c, ctb);
if (sq[0].contains("TotalDamageThisTurn")) {
String[] props = l[0].split(" ");
int sum = 0;
for (Pair<Integer, Boolean> p : c.getDamageReceivedThisTurn()) {
if (game.getDamageLKI(p).getLeft().isValid(props[1], player, c, ctb)) {
sum += p.getLeft();
}
}
return doXMath(sum, expr, c, ctb);
}
if (sq[0].contains("DamageThisTurn")) {
String[] props = l[0].split(" ");
Boolean isCombat = null;
if (sq[0].contains("CombatDamage")) {
isCombat = true;
}
return doXMath(game.getDamageDoneThisTurn(isCombat, false, props[1], props[2], c, player, ctb).size(), expr, c, ctb);
}
if (sq[0].equals("YourTurns")) {
@@ -3325,12 +3327,6 @@ public class AbilityUtils {
totDmg += p.getAssignedDamage();
}
return doXMath(totDmg, m, source, ctb);
} else if (sq[0].contains("LifeLostThisTurn")) {
int totDmg = 0;
for (Player p : players) {
totDmg += p.getLifeLostThisTurn();
}
return doXMath(totDmg, m, source, ctb);
}
if (players.size() > 0) {
@@ -3387,7 +3383,7 @@ public class AbilityUtils {
if (value.contains("DomainPlayer")) {
int n = 0;
final CardCollectionView someCards = player.getCardsIn(ZoneType.Battlefield);
final CardCollectionView someCards = player.getLandsInPlay();
final List<String> basic = MagicColor.Constant.BASIC_LANDS;
for (int i = 0; i < basic.size(); i++) {

View File

@@ -123,6 +123,7 @@ public class CounterEffect extends SpellAbilityEffect {
for (final SpellAbility tgtSA : sas) {
final Card tgtSACard = tgtSA.getHostCard();
// should remember even that spell cannot be countered, e.g. Dovescape
// TODO use LKI in case the spell gets countered before (else X amounts would be missing)
if (sa.hasParam("RememberCounteredCMC")) {
sa.getHostCard().addRemembered(Integer.valueOf(tgtSACard.getCMC()));
}
@@ -152,9 +153,7 @@ public class CounterEffect extends SpellAbilityEffect {
}
if (sa.hasParam("RememberCountered")) {
if (sa.getParam("RememberCountered").equals("True")) {
sa.getHostCard().addRemembered(tgtSACard);
}
sa.getHostCard().addRemembered(tgtSACard);
}
if (sa.hasParam("RememberSplicedOntoCounteredSpell")) {

View File

@@ -285,6 +285,7 @@ public class PlayEffect extends SpellAbilityEffect {
}
}
// TODO if cost isn't replaced should include alternative ones
// get basic spells (no flashback, etc.)
List<SpellAbility> sas = AbilityUtils.getBasicSpellsFromPlayEffect(tgtCard, controller);
if (sa.hasParam("ValidSA")) {

View File

@@ -180,9 +180,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private final Set<Object> rememberedObjects = Sets.newLinkedHashSet();
private Map<Player, String> flipResult;
private List<Pair<Card, Integer>> receivedDamageFromThisTurn = Lists.newArrayList();
private Map<Player, Integer> receivedDamageFromPlayerThisTurn = Maps.newHashMap();
private final Map<Card, Integer> assignedDamageMap = Maps.newTreeMap();
private boolean isCommander = false;
@@ -257,7 +254,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private String oracleText = "";
private int damage;
private Map<Integer, Integer> damage = Maps.newHashMap();
private boolean hasBeenDealtDeathtouchDamage;
// regeneration
@@ -951,7 +948,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
}
devouredCards.add(c);
}
public final void clearDevoured() {
devouredCards = null;
}
@@ -982,7 +978,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
delvedCards = null;
}
public final CardCollectionView getConvoked() {
return CardCollection.getView(convokedCards);
}
@@ -5292,62 +5287,15 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
damageHistory = history;
}
public final List<Pair<Card, Integer>> getReceivedDamageFromThisTurn() {
return receivedDamageFromThisTurn;
public List<Pair<Integer, Boolean>> getDamageReceivedThisTurn() {
return damageReceivedThisTurn;
}
public final void setReceivedDamageFromThisTurn(final List<Pair<Card, Integer>> receivedDamageList) {
receivedDamageFromThisTurn = Lists.newArrayList(receivedDamageList);
}
public final Map<Player, Integer> getReceivedDamageFromPlayerThisTurn() {
return receivedDamageFromPlayerThisTurn;
}
public final void setReceivedDamageFromPlayerThisTurn(final Map<Player, Integer> receivedDamageMap) {
receivedDamageFromPlayerThisTurn = Maps.newHashMap(receivedDamageMap);
}
public int getReceivedDamageByPlayerThisTurn(final Player p) {
if (receivedDamageFromPlayerThisTurn.containsKey(p)) {
return receivedDamageFromPlayerThisTurn.get(p);
}
return 0;
}
public final void addReceivedDamageFromThisTurn(Card c, final int damage) {
int currentDamage = 0;
// because Aegar cares about the past state we need to keep all LKI instances
receivedDamageFromThisTurn.add(Pair.of(c.isLKI() ? c : CardUtil.getLKICopy(c), damage));
Player p = c.getController();
if (p != null) {
if (receivedDamageFromPlayerThisTurn.containsKey(p)) {
currentDamage = receivedDamageFromPlayerThisTurn.get(p);
}
receivedDamageFromPlayerThisTurn.put(p, damage+currentDamage);
}
}
public final void resetReceivedDamageFromThisTurn() {
receivedDamageFromThisTurn.clear();
receivedDamageFromPlayerThisTurn.clear();
}
public final int getTotalDamageReceivedThisTurn() {
int total = 0;
for (Integer i : receivedDamageFromPlayerThisTurn.values()) {
total += i;
}
return total;
public void setDamageReceivedThisTurn(List<Pair<Integer, Boolean>> dmg) {
damageReceivedThisTurn.addAll(dmg);
}
public final boolean hasDealtDamageToOpponentThisTurn() {
for (final GameEntity e : getDamageHistory().getThisTurnDamaged().keySet()) {
if (e instanceof Player) {
final Player p = (Player) e;
if (getController().isOpponentOf(p)) {
return true;
}
}
}
return false;
return getDamageHistory().getDamageDoneThisTurn(null, true, null, "Player.Opponent", this, getController(), null) > 0;
}
/**
@@ -5356,11 +5304,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
* @return the damage done to player p this turn
*/
public final int getTotalDamageDoneBy() {
int sum = 0;
for (final GameEntity e : getDamageHistory().getThisTurnDamaged().keySet()) {
sum += getDamageHistory().getThisTurnDamaged().get(e);
}
return sum;
return getDamageHistory().getDamageDoneThisTurn(null, false, null, null, this, getController(), null);
}
// this is the amount of damage a creature needs to receive before it dies
@@ -5383,15 +5327,26 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
}
public final int getDamage() {
return damage;
int sum = 0;
for (int i : damage.values()) {
sum += i;
}
return sum;
}
public final void setDamage(int damage0) {
if (damage == damage0) { return; }
damage = damage0;
if (getDamage() == damage0) { return; }
damage.clear();
if (damage0 != 0) {
damage.put(0, damage0);
}
view.updateDamage(this);
getGame().fireEvent(new GameEventCardStatsChanged(this));
}
public int getMaxDamageFromSource() {
return damage.isEmpty() ? 0 : Collections.max(damage.values());
}
public final boolean hasBeenDealtDeathtouchDamage() {
return hasBeenDealtDeathtouchDamage;
}
@@ -5542,14 +5497,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
// Run replacement effects
getGame().getReplacementHandler().run(ReplacementType.DealtDamage, AbilityKey.mapFromAffected(this));
addReceivedDamageFromThisTurn(source, damageIn);
source.getDamageHistory().registerDamage(this, damageIn);
if (isCombat) {
source.getDamageHistory().registerCombatDamage(this, damageIn);
} else {
getDamageHistory().setHasBeenDealtNonCombatDamageThisTurn(true);
}
// Run triggers
Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.DamageSource, source);
@@ -5576,7 +5523,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
damageType = DamageType.M1M1Counters;
}
else { // 120.3e
damage += damageIn;
int old = damage.getOrDefault(Objects.hash(source.getId(), source.getTimestamp()), 0);
damage.put(Objects.hash(source.getId(), source.getTimestamp()), old + damageIn);
view.updateDamage(this);
}
@@ -5714,7 +5662,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
}
return false;
}
public final void setForetold(final boolean foretold) {
this.foretold = foretold;
}
@@ -5732,7 +5679,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
public final void setForetoldThisTurn(final boolean foretoldThisTurn) {
this.foretoldThisTurn = foretoldThisTurn;
}
public void resetForetoldThisTurn() {
foretoldThisTurn = false;
}
@@ -5803,7 +5749,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
public final long getBestowTimestamp() {
return bestowTimestamp;
}
public final void setBestowTimestamp(final long t) {
bestowTimestamp = t;
}
@@ -6206,7 +6151,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
setDamage(0);
}
setHasBeenDealtDeathtouchDamage(false);
resetReceivedDamageFromThisTurn();
setRegeneratedThisTurn(0);
resetShield();
setBecameTargetThisTurn(false);
@@ -6214,6 +6158,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
clearMustBlockCards();
getDamageHistory().setCreatureAttackedLastTurnOf(turn, getDamageHistory().getCreatureAttacksThisTurn() > 0);
getDamageHistory().newTurn();
damageReceivedThisTurn.clear();
clearBlockedByThisTurn();
clearBlockedThisTurn();
resetMayPlayTurn();

View File

@@ -4,9 +4,11 @@ package forge.game.card;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.Lists;
import forge.game.CardTraitBase;
import forge.game.GameEntity;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
@@ -22,7 +24,6 @@ public class CardDamageHistory {
private boolean creatureBlockedThisCombat = false;
private boolean creatureGotBlockedThisCombat = false;
boolean hasdealtDamagetoAny = false;
private List<GameEntity> attackedThisTurn = Lists.newArrayList();
private final List<Player> creatureAttackedLastTurnOf = Lists.newArrayList();
@@ -30,14 +31,13 @@ public class CardDamageHistory {
private final List<Player> NotBlockedSinceLastUpkeepOf = Lists.newArrayList();
private final List<Player> NotBeenBlockedSinceLastUpkeepOf = Lists.newArrayList();
private List<Pair<Integer, Boolean>> damageDoneThisTurn = Lists.newArrayList();
// only needed for Glen Elendra (Plane)
private final List<Player> damagedThisCombat = Lists.newArrayList();
// only needed for The Fallen
private final FCollection<GameEntity> damagedThisGame = new FCollection<>();
private final Map<GameEntity, Integer> damagedThisTurn = Maps.newHashMap();
private final Map<GameEntity, Integer> damagedThisTurnInCombat = Maps.newHashMap();
private boolean receivedNonCombatDamageThisTurn = false;
boolean hasdealtDamagetoAny = false;
public final boolean getHasdealtDamagetoAny() {
return hasdealtDamagetoAny;
@@ -225,38 +225,9 @@ public class CardDamageHistory {
return this.creatureGotBlockedThisCombat;
}
/**
* <p>
* Getter for the field <code>receivedNonCombatDamageThisTurn</code>.
* </p>
*
* @return a boolean.
*/
public boolean hasBeenDealtNonCombatDamageThisTurn() {
return this.receivedNonCombatDamageThisTurn;
}
/**
* <p>
* Setter for the field <code>receivedNonCombatDamageThisTurn</code>.
* </p>
*
* @param b
* a boolean.
*/
public void setHasBeenDealtNonCombatDamageThisTurn(boolean b) {
this.receivedNonCombatDamageThisTurn = b;
}
public final List<Player> getThisCombatDamaged() {
return damagedThisCombat;
}
public final Map<GameEntity, Integer> getThisTurnDamaged() {
return damagedThisTurn;
}
public final Map<GameEntity, Integer> getThisTurnCombatDamaged() {
return damagedThisTurnInCombat;
}
public final FCollection<GameEntity> getThisGameDamaged() {
return damagedThisGame;
}
@@ -264,38 +235,44 @@ public class CardDamageHistory {
* TODO: Write javadoc for this method.
* @param player
*/
public void registerCombatDamage(GameEntity entity, int amount) {
int old = 0;
if (entity instanceof Player) {
damagedThisCombat.add((Player) entity);
}
old = 0;
if (damagedThisTurnInCombat.containsKey(entity)) {
old = damagedThisTurnInCombat.get(entity);
}
damagedThisTurnInCombat.put(entity, old + amount);
public void registerDamage(int damage, boolean isCombat, Card sourceLKI, GameEntity target, Map<Integer, Card> lkiCache) {
damagedThisGame.add(target);
hasdealtDamagetoAny = true;
if (isCombat && target instanceof Player) {
damagedThisCombat.add((Player) target);
}
Pair<Integer, Boolean> dmg = Pair.of(damage, isCombat);
damageDoneThisTurn.add(dmg);
target.receiveDamage(dmg);
sourceLKI.getGame().addGlobalDamageHistory(this, dmg, sourceLKI.isLKI() ? sourceLKI : CardUtil.getLKICopy(sourceLKI, lkiCache), CardUtil.getLKICopy(target, lkiCache));
}
/**
* TODO: Write javadoc for this method.
* @param player
*/
public void registerDamage(GameEntity entity, int amount) {
int old = 0;
if (damagedThisTurn.containsKey(entity)) {
old = damagedThisTurn.get(entity);
public int getDamageDoneThisTurn(Boolean isCombat, boolean anyIsEnough, String validSourceCard, String validTargetEntity, Card source, Player sourceController, CardTraitBase ctb) {
int sum = 0;
for (Pair<Integer, Boolean> damage : damageDoneThisTurn) {
Pair<Card, GameEntity> sourceToTarget = sourceController.getGame().getDamageLKI(damage);
if (isCombat != null && damage.getRight() != isCombat) {
continue;
}
if (validSourceCard != null && !sourceToTarget.getLeft().isValid(validSourceCard.split(","), sourceController, source == null ? sourceToTarget.getLeft() : source, ctb)) {
continue;
}
if (validTargetEntity != null && !sourceToTarget.getRight().isValid(validTargetEntity.split(","), sourceController, source, ctb)) {
continue;
}
sum += damage.getLeft();
if (anyIsEnough) {
break;
}
}
damagedThisTurn.put(entity, old + amount);
damagedThisGame.add(entity);
hasdealtDamagetoAny = true;
return sum;
}
public void newTurn() {
damagedThisCombat.clear();
damagedThisTurnInCombat.clear();
damagedThisTurn.clear();
attackedThisTurn.clear();
damagedThisCombat.clear();
damageDoneThisTurn.clear();
// if card already LTB we can safely dereference (allows quite a few objects to be cleaned up earlier for bigger boardstates)
CardCollection toRemove = new CardCollection();
@@ -307,7 +284,6 @@ public class CardDamageHistory {
}
}
damagedThisGame.removeAll(toRemove);
setHasBeenDealtNonCombatDamageThisTurn(false);
}
public void endCombat() {

View File

@@ -608,35 +608,6 @@ public class CardProperty {
if ((card.getCloneOrigin() == null) || !card.getCloneOrigin().equals(source)) {
return false;
}
} else if (property.startsWith("DamagedBy")) {
List<Card> damaged = Lists.newArrayList();
for (Pair<Card, Integer> pair : card.getReceivedDamageFromThisTurn()) {
damaged.add(pair.getLeft());
}
if (property.endsWith("Source") || property.equals("DamagedBy")) {
if (!damaged.contains(source)) {
return false;
}
} else {
String prop = property.substring("DamagedBy".length());
boolean found = Iterables.any(damaged, CardPredicates.restriction(prop, sourceController, source, spellAbility));
if (!found) {
for (Card d : AbilityUtils.getDefinedCards(source, prop, spellAbility)) {
if (damaged.contains(d)) {
found = true;
break;
}
}
}
if (!found) {
return false;
}
}
} else if (property.startsWith("Damaged")) {
if (!card.getDamageHistory().getThisTurnDamaged().containsKey(source)) {
return false;
}
} else if (property.startsWith("SharesCMCWith")) {
if (property.equals("SharesCMCWith")) {
if (!card.sharesCMCWith(source)) {
@@ -1133,38 +1104,78 @@ public class CardProperty {
&& !card.getDamageHistory().hasBlockedSinceLastUpkeepOf(sourceController)) {
return false;
}
} else if (property.startsWith("DamagedBy")) {
String prop = property.substring("DamagedBy".length());
CardCollection def = null;
if (prop.startsWith(" ")) {
def = AbilityUtils.getDefinedCards(source, prop.substring(1), spellAbility);
}
boolean found = false;
for (Pair<Integer, Boolean> p : card.getDamageReceivedThisTurn()) {
Card dmgSource = game.getDamageLKI(p).getLeft();
if (def != null) {
for (Card c : def) {
if (dmgSource.equalsWithTimestamp(c)) {
found = true;
}
}
}
else if (prop.isEmpty() && dmgSource.equalsWithTimestamp(source)) {
found = true;
} else if (dmgSource.isValid(prop.split(","), sourceController, source, spellAbility)) {
found = true;
}
if (found) {
break;
}
}
if (!found) {
return false;
}
} else if (property.startsWith("Damaged")) {
for (Pair<Integer, Boolean> p : source.getDamageReceivedThisTurn()) {
boolean found = false;
if (game.getDamageLKI(p).getLeft().equalsWithTimestamp(card)) {
found = true;
break;
}
if (!found) {
return false;
}
}
} else if (property.startsWith("dealtCombatDamageThisCombat")) {
if (card.getDamageHistory().getThisCombatDamaged().isEmpty()) {
return false;
}
} else if (property.startsWith("dealtDamageToYouThisTurn")) {
if (!card.getDamageHistory().getThisTurnDamaged().containsKey(sourceController)) {
if (card.getDamageHistory().getDamageDoneThisTurn(null, true, null, "You", card, sourceController, spellAbility) == 0) {
return false;
}
} else if (property.startsWith("dealtDamageToOppThisTurn")) {
if (!card.hasDealtDamageToOpponentThisTurn()) {
return false;
}
//dealtCombatDamageThisCombat <valid>, dealtCombatDamageThisTurn <valid>, and notDealt versions
} else if (property.startsWith("dealtCombatDamage") || property.startsWith("notDealtCombatDamage")) {
final String[] v = property.split(" ")[1].split(",");
final Iterable<GameEntity> list = property.contains("ThisCombat") ?
Lists.newArrayList(card.getDamageHistory().getThisCombatDamaged()) :
card.getDamageHistory().getThisTurnCombatDamaged().keySet();
boolean found = Iterables.any(list, GameObjectPredicates.restriction(v, sourceController, source, spellAbility));
final String v = property.split(" ")[1];
boolean found = card.getDamageHistory().getDamageDoneThisTurn(true, true, null, v, card, sourceController, spellAbility) > 0;
if (found == property.startsWith("not")) {
return false;
}
} else if (property.startsWith("controllerWasDealtCombatDamageByThisTurn")) {
if (!source.getDamageHistory().getThisTurnCombatDamaged().containsKey(controller)) {
if (source.getDamageHistory().getDamageDoneThisTurn(true, true, null, "You", card, controller, spellAbility) == 0) {
return false;
}
} else if (property.startsWith("controllerWasDealtDamageByThisTurn")) {
if (!source.getDamageHistory().getThisTurnDamaged().containsKey(controller)) {
if (source.getDamageHistory().getDamageDoneThisTurn(null, true, null, "You", card, controller, spellAbility) == 0) {
return false;
}
} else if (property.startsWith("wasDealtDamageThisTurn")) {
if (card.getReceivedDamageFromPlayerThisTurn().isEmpty()) {
if (card.getAssignedDamage() == 0) {
return false;
}
} else if (property.equals("wasDealtNonCombatDamageThisTurn")) {
if (!card.getDamageHistory().hasBeenDealtNonCombatDamageThisTurn()) {
if (card.getAssignedDamage(false, null) == 0) {
return false;
}
} else if (property.startsWith("wasDealtDamageByThisGame")) {

View File

@@ -250,9 +250,8 @@ public final class CardUtil {
newCopy.setColor(in.getColor().getColor());
newCopy.setPhasedOut(in.isPhasedOut());
newCopy.setReceivedDamageFromThisTurn(in.getReceivedDamageFromThisTurn());
newCopy.setReceivedDamageFromPlayerThisTurn(in.getReceivedDamageFromPlayerThisTurn());
newCopy.setDamageHistory(in.getDamageHistory());
newCopy.setDamageReceivedThisTurn(in.getDamageReceivedThisTurn());
for (Card c : in.getBlockedThisTurn()) {
newCopy.addBlockedThisTurn(c);
}

View File

@@ -147,8 +147,6 @@ public class Player extends GameEntity implements Comparable<Player> {
private int life = 20;
private int startingLife = 20;
private int lifeStartedThisTurnWith = startingLife;
private final Map<Card, Integer> assignedDamage = Maps.newHashMap();
private final Map<Card, Integer> assignedCombatDamage = Maps.newHashMap();
private int spellsCastThisTurn;
private int spellsCastThisGame;
private int spellsCastLastTurn;
@@ -219,7 +217,6 @@ public class Player extends GameEntity implements Comparable<Player> {
private Map<Card, Card> maingameCardsMap = Maps.newHashMap();
private CardCollection currentPlanes = new CardCollection();
private Set<String> prowl = Sets.newHashSet();
private PlayerStatistics stats = new PlayerStatistics();
private PlayerController controller;
@@ -704,19 +701,6 @@ public class Player extends GameEntity implements Comparable<Player> {
}
}
int old = assignedDamage.containsKey(source) ? assignedDamage.get(source) : 0;
assignedDamage.put(source, old + amount);
source.getDamageHistory().registerDamage(this, amount);
if (isCombat) {
old = assignedCombatDamage.containsKey(source) ? assignedCombatDamage.get(source) : 0;
assignedCombatDamage.put(source, old + amount);
for (final String type : source.getType().getCreatureTypes()) {
source.getController().addProwlType(type);
}
source.getDamageHistory().registerCombatDamage(this, amount);
}
// Run triggers
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.DamageSource, source);
@@ -830,49 +814,6 @@ public class Player extends GameEntity implements Comparable<Player> {
simultaneousDamage = 0;
}
public final void clearAssignedDamage() {
assignedDamage.clear();
assignedCombatDamage.clear();
}
public final int getAssignedDamage() {
int num = 0;
for (final Integer value : assignedDamage.values()) {
num += value;
}
return num;
}
public final int getAssignedCombatDamage() {
int num = 0;
for (final Integer value : assignedCombatDamage.values()) {
num += value;
}
return num;
}
public final Iterable<Card> getAssignedDamageSources() {
return assignedDamage.keySet();
}
public final int getAssignedDamage(final Card c) {
return assignedDamage.get(c);
}
public final int getAssignedDamage(final String type) {
final Map<Card, Integer> valueMap = Maps.newHashMap();
for (final Card c : assignedDamage.keySet()) {
if (c.getType().hasStringType(type)) {
valueMap.put(c, assignedDamage.get(c));
}
}
int num = 0;
for (final Integer value : valueMap.values()) {
num += value;
}
return num;
}
/**
* Get the total damage assigned to this player's opponents this turn.
*/
@@ -2082,8 +2023,8 @@ public class Player extends GameEntity implements Comparable<Player> {
}
public final boolean hasBloodthirst() {
for (Player p : game.getRegisteredPlayers()) {
if (p.isOpponentOf(this) && p.getAssignedDamage() > 0) {
for (Player p : getRegisteredOpponents()) {
if (p.getAssignedDamage() > 0) {
return true;
}
}
@@ -2102,14 +2043,12 @@ public class Player extends GameEntity implements Comparable<Player> {
return lost;
}
public final boolean hasProwl(final String type) {
return prowl.contains(type);
}
public final void addProwlType(final String type) {
prowl.add(type);
}
public final void resetProwl() {
prowl.clear();
public final boolean hasProwl(final Set<String> types) {
StringBuilder sb = new StringBuilder();
for (String type : types) {
sb.append("Card.YouCtrl+").append(type).append(",");
}
return !game.getDamageDoneThisTurn(true, true, sb.toString(), "Player", null, this, null).isEmpty();
}
public final void setLibrarySearched(final int l) {
@@ -2150,7 +2089,7 @@ public class Player extends GameEntity implements Comparable<Player> {
}
} else if (incR[0].equals("EnchantedController")) {
final GameEntity enchanted = source.getEntityAttachedTo();
if ((enchanted == null) || !(enchanted instanceof Card)) {
if (enchanted == null || !(enchanted instanceof Card)) {
return false;
}
final Card enchantedCard = (Card) enchanted;
@@ -2239,10 +2178,6 @@ public class Player extends GameEntity implements Comparable<Player> {
investigatedThisTurn = 0;
}
public final List<Card> getSacrificedThisTurn() {
return sacrificedThisTurn;
}
public final void addSacrificedThisTurn(final Card c, final SpellAbility source) {
// Play the Sacrifice sound
game.fireEvent(new GameEventCardSacrificed());
@@ -2260,6 +2195,9 @@ public class Player extends GameEntity implements Comparable<Player> {
game.getTriggerHandler().runTrigger(TriggerType.Sacrificed, runParams, false);
}
public final List<Card> getSacrificedThisTurn() {
return sacrificedThisTurn;
}
public final void resetSacrificedThisTurn() {
sacrificedThisTurn.clear();
}
@@ -2452,10 +2390,8 @@ public class Player extends GameEntity implements Comparable<Player> {
resetCycledThisTurn();
resetEquippedThisTurn();
resetSacrificedThisTurn();
clearAssignedDamage();
resetVenturedThisTurn();
setRevolt(false);
resetProwl();
setSpellsCastLastTurn(getSpellsCastThisTurn());
resetSpellsCastThisTurn();
setLifeLostLastTurn(getLifeLostThisTurn());
@@ -2467,6 +2403,8 @@ public class Player extends GameEntity implements Comparable<Player> {
setLibrarySearched(0);
setNumManaConversion(0);
damageReceivedThisTurn.clear();
// set last turn nr
if (game.getPhaseHandler().isPlayerTurn(this)) {
setAttackedPlayersMyLastTurn(attackedPlayersThisTurn);

View File

@@ -1,6 +1,7 @@
package forge.game.player;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import forge.game.CardTraitBase;
@@ -86,81 +87,66 @@ public class PlayerProperty {
if (!player.hasBlessing()) {
return false;
}
} else if (property.startsWith("damageDoneSingleSource")) {
String props = property.split(" ")[1];
List<Integer> sourceDmg = game.getDamageDoneThisTurn(null, false, "Card.YouCtrl", null, source, sourceController, spellAbility);
int maxDmg = sourceDmg.isEmpty() ? 0 : Collections.max(sourceDmg);
if (!Expressions.compare(maxDmg, props.substring(0, 2), AbilityUtils.calculateAmount(source, props.substring(2), spellAbility))) {
return false;
}
} else if (property.startsWith("wasDealtCombatDamageThisCombatBy ")) {
String v = property.split(" ")[1];
int count = 1;
if (v.contains("_AtLeast")) {
count = Integer.parseInt(v.substring(v.indexOf("_AtLeast") + 8));
v = v.substring(0, v.indexOf("_AtLeast")).replace("Valid:", "Valid ");
}
boolean found = true;
final List<Card> cards = AbilityUtils.getDefinedCards(source, v, spellAbility);
int found = 0;
for (final Card card : cards) {
if (card.getDamageHistory().getThisCombatDamaged().contains(player)) {
found++;
found = true;
}
}
if (found < count) {
if (!found) {
return false;
}
} else if (property.startsWith("wasDealtDamageThisGameBy ")) {
String v = property.split(" ")[1];
int count = 1;
if (v.contains("_AtLeast")) {
count = Integer.parseInt(v.substring(v.indexOf("_AtLeast") + 8));
v = TextUtil.fastReplace(v.substring(0, v.indexOf("_AtLeast")), "Valid:", "Valid ");
}
boolean found = true;
final List<Card> cards = AbilityUtils.getDefinedCards(source, v, spellAbility);
int found = 0;
for (final Card card : cards) {
if (card.getDamageHistory().getThisGameDamaged().contains(player)) {
found++;
found = true;
}
}
if (found < count) {
if (!found) {
return false;
}
} else if (property.startsWith("wasDealtDamageThisTurnBy ")) {
String v = property.split(" ")[1];
int count = 1;
if (v.contains("_AtLeast")) {
count = Integer.parseInt(v.substring(v.indexOf("_AtLeast") + 8));
v = TextUtil.fastReplace(v.substring(0, v.indexOf("_AtLeast")), "Valid:", "Valid ");
} else if (property.startsWith("wasDealt")) {
boolean found = false;
String validCard = null;
Boolean combat = null;
if (property.contains("CombatDamage")) {
combat = true;
}
if (property.contains("ThisTurnBySource")) {
found = source.getDamageHistory().getDamageDoneThisTurn(combat, validCard == null, validCard, "You", source, player, spellAbility) > 0;
} else {
String comp = "GE";
int right = 1;
int numValid = 0;
final List<Card> cards = AbilityUtils.getDefinedCards(source, v, spellAbility);
int found = 0;
for (final Card card : cards) {
if (card.getDamageHistory().getThisTurnDamaged().containsKey(player)) {
found++;
if (property.contains("ThisTurnBy")) {
String[] props = property.split(" ");
validCard = props[1];
if (props.length > 2) {
comp = props[2].substring(0, 2);
right = AbilityUtils.calculateAmount(source, props[2].substring(2), spellAbility);
}
}
}
if (found < count) {
return false;
}
} else if (property.startsWith("wasDealtCombatDamageThisTurnBy ")) {
String v = property.split(" ")[1];
int count = 1;
if (v.contains("_AtLeast")) {
count = Integer.parseInt(v.substring(v.indexOf("_AtLeast") + 8));
v = TextUtil.fastReplace(v.substring(0, v.indexOf("_AtLeast")), "Valid:", "Valid ");
numValid = game.getDamageDoneThisTurn(combat, validCard == null, validCard, "You", source, player, spellAbility).size();
found = Expressions.compare(numValid, comp, right);
}
final List<Card> cards = AbilityUtils.getDefinedCards(source, v, spellAbility);
int found = 0;
for (final Card card : cards) {
if (card.getDamageHistory().getThisTurnCombatDamaged().containsKey(player)) {
found++;
}
}
if (found < count) {
if (!found) {
return false;
}
} else if (property.equals("attackedBySourceThisCombat")) {
@@ -171,18 +157,10 @@ public class PlayerProperty {
if (!source.getDamageHistory().hasAttackedThisTurn(player)) {
return false;
}
} else if (property.equals("wasDealtDamageThisTurn")) {
if (player.getAssignedDamage() == 0) {
return false;
}
} else if (property.equals("Defending")) {
} else if (property.equals("Defending")) {
if (!game.getCombat().getAttackersAndDefenders().values().contains(player)) {
return false;
}
} else if (property.equals("wasDealtCombatDamageThisTurn")) {
if (player.getAssignedCombatDamage() == 0) {
return false;
}
} else if (property.equals("LostLifeThisTurn")) {
if (player.getLifeLostThisTurn() <= 0) {
return false;

View File

@@ -87,8 +87,8 @@ public class AbilityManaPart implements java.io.Serializable {
public AbilityManaPart(final Card sourceCard, final Map<String, String> params) {
this.sourceCard = sourceCard;
origProduced = params.containsKey("Produced") ? params.get("Produced") : "1";
this.manaRestrictions = params.containsKey("RestrictValid") ? params.get("RestrictValid") : "";
origProduced = params.getOrDefault("Produced", "1");
this.manaRestrictions = params.getOrDefault("RestrictValid", "");
this.cannotCounterSpell = params.get("AddsNoCounter");
this.addsKeywords = params.get("AddsKeywords");
this.addsKeywordsType = params.get("AddsKeywordsType");

View File

@@ -434,13 +434,7 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
}
}
if (sa.isProwl()) {
boolean prowlFlag = false;
for (final String type : c.getType().getCreatureTypes()) {
if (activator.hasProwl(type)) {
prowlFlag = true;
}
}
if (!prowlFlag) {
if (!activator.hasProwl(c.getType().getCreatureTypes())) {
return false;
}
}

View File

@@ -166,7 +166,7 @@ public class TriggerChangesZone extends Trigger {
// need to check the ChangeZone LKI copy for damage, otherwise it'll return 0 for a new object in the new zone
Card lkiCard = card.getGame().getChangeZoneLKIInfo(card);
final boolean expr = Expressions.compare(lkiCard.getTotalDamageReceivedThisTurn(), cond, rightSide);
final boolean expr = Expressions.compare(lkiCard.getAssignedDamage(), cond, rightSide);
if (!expr) {
return false;
}

View File

@@ -101,7 +101,7 @@ public class TriggerDamageDone extends Trigger {
if (target instanceof Player) {
final Player trigTgt = (Player) target;
if (!Expressions.compare(trigTgt.getAssignedDamage(source), operator, operand)) {
if (!Expressions.compare(trigTgt.getAssignedDamage(null, source), operator, operand)) {
return false;
}
} else {

View File

@@ -4,6 +4,6 @@ Types:Legendary Creature Human Pirate
PT:3/3
S:Mode$ Continuous | Affected$ Creature.Pirate+Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other Pirates you control get +1/+1.
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigGainCtrl | TriggerDescription$ At the beginning of your end step, gain control of target nonland permanent controlled by a player who was dealt combat damage by three or more Pirates this turn.
SVar:TrigGainCtrl:DB$ GainControl | ValidTgts$ Permanent.nonLand+ControlledBy Player.wasDealtCombatDamageThisTurnBy Valid:Creature.Pirate_AtLeast3 | TgtPrompt$ Select target creature controlled by a player who was dealt damage by three or more Pirates this turn | SpellDescription$ Gain control of target nonland permanent controlled by a player who was dealt combat damage by three or more Pirates this turn.
SVar:TrigGainCtrl:DB$ GainControl | ValidTgts$ Permanent.nonLand+ControlledBy Player.wasDealtCombatDamageThisTurnBy Creature.Pirate GE3 | TgtPrompt$ Select target creature controlled by a player who was dealt damage by three or more Pirates this turn | SpellDescription$ Gain control of target nonland permanent controlled by a player who was dealt combat damage by three or more Pirates this turn.
DeckHints:Type$Pirate
Oracle:Other Pirates you control get +1/+1.\nAt the beginning of your end step, gain control of target nonland permanent controlled by a player who was dealt combat damage by three or more Pirates this turn.

View File

@@ -4,7 +4,7 @@ Types:Artifact Equipment
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ Y | Description$ Equipped creature gets +1/+0 for each opponent you have.
SVar:Y:PlayerCountOpponents$Amount
T:Mode$ DamageDoneOnce | Execute$ TrigDamage | ValidTarget$ Creature.EquippedBy | TriggerZones$ Battlefield | TriggerDescription$ Whenever equipped creature is dealt damage, it deals that much damage to any target.
SVar:TrigDamage:DB$ DealDamage | NumDmg$ X | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target
SVar:TrigDamage:DB$ DealDamage | NumDmg$ X | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | DamageSource$ TriggeredTargetLKICopy
SVar:X:TriggerCount$DamageAmount
K:Equip:4
Oracle:Equipped creature gets +1/+0 for each opponent you have.\nWhenever equipped creature is dealt damage, it deals that much damage to any target.\nEquip {4}

View File

@@ -5,7 +5,7 @@ K:Entwine:1
A:SP$ Charm | Cost$ 2 W | Choices$ DBTap,DBEffect | CharmNum$ 1
SVar:DBTap:DB$ Tap | ValidTgts$ Creature | TargetMin$ 2 | TargetMax$ 2 | TgtPrompt$ Select two target creatures | SpellDescription$ Tap two target creatures.
SVar:DBEffect:DB$ Effect | ValidTgts$ Player | TgtPrompt$ Select target player | IsCurse$ True | StaticAbilities$ DontUntap | Triggers$ RestoreSight | RememberObjects$ Targeted | Duration$ Permanent | SpellDescription$ Creatures don't untap during target player's next untap step.
SVar:DontUntap:Mode$ Continuous | ValidPlayer$ Player.IsRemembered | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature | AddHiddenKeyword$ This card doesn't untap.
SVar:DontUntap:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.RememberedPlayerCtrl | AddHiddenKeyword$ This card doesn't untap.
SVar:RestoreSight:Mode$ Phase | Phase$ Untap | ValidPlayer$ Player.IsRemembered | TriggerZones$ Command | Execute$ ExileEffect | Static$ True
SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
AI:RemoveDeck:All

View File

@@ -3,5 +3,5 @@ ManaCost:R
Types:Creature Goblin Berserker
PT:2/2
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ CARDNAME can't attack. | CheckSVar$ X | SVarCompare$ LT1 | Description$ CARDNAME can't attack unless an opponent has been dealt damage this turn.
SVar:X:Count$TotalOppDamageThisTurn
SVar:X:PlayerCountPropertyYou$DamageToOppsThisTurn
Oracle:Bloodcrazed Goblin can't attack unless an opponent has been dealt damage this turn.

View File

@@ -6,5 +6,5 @@ T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ Player.Opponent | TriggerZones
SVar:GhostCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ X
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ GhostClear | TriggerDescription$ At the beginning of your end step, remove all +1/+1 counters from CARDNAME.
SVar:GhostClear:DB$ RemoveCounter | CounterType$ P1P1 | CounterNum$ All
SVar:X:Count$YourDamageThisTurn
SVar:X:PlayerCountPropertyYou$DamageThisTurn
Oracle:At the beginning of each end step, if it's an opponent's turn, put a +1/+1 counter on Discordant Spirit for each 1 damage dealt to you this turn.\nAt the beginning of your end step, remove all +1/+1 counters from Discordant Spirit.

View File

@@ -5,8 +5,8 @@ A:SP$ ChangeZone | ValidTgts$ Card | TgtZone$ Stack | Origin$ Stack | Fizzle$ Tr
SVar:DBRoll:DB$ RollDice | Sides$ 20 | Modifier$ Y | ResultSubAbilities$ 1-14:DBMayPlay,Else:DBMayPlayWithoutCost | StackDescription$ SpellDescription | SpellDescription$ Roll a d20 and add that spell's mana value.
SVar:DBMayPlay:DB$ Effect | StaticAbilities$ STPlay | RememberObjects$ Remembered | Duration$ Permanent | ExileOnMoved$ Exile | SubAbility$ DBCleanup | SpellDescription$ 1—14 VERT You may cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast it.
SVar:DBMayPlayWithoutCost:DB$ Effect | StaticAbilities$ STPlayWithoutCost | RememberObjects$ Remembered | Duration$ Permanent | ExileOnMoved$ Exile | SubAbility$ DBCleanup | SpellDescription$ 15+ VERT You may cast that card without paying its mana cost for as long as it remains exiled.
SVar:STPlay:Mode$ Continuous | MayPlay$ True | MayPlayIgnoreColor$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast it.
SVar:STPlayWithoutCost:Mode$ Continuous | MayPlay$ True | MayPlayWithoutManaCost$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may cast that card without paying its mana cost for as long as it remains exiled.
SVar:STPlay:Mode$ Continuous | MayPlay$ True | MayPlayIgnoreColor$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand | AffectedZone$ Exile | Description$ You may cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast it.
SVar:STPlayWithoutCost:Mode$ Continuous | MayPlay$ True | MayPlayWithoutManaCost$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand | AffectedZone$ Exile | Description$ You may cast that card without paying its mana cost for as long as it remains exiled.
SVar:Y:RememberedLKI$CardManaCost
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
AI:RemoveDeck:All

View File

@@ -2,7 +2,7 @@ Name:Glen Elendra
ManaCost:no cost
Types:Plane Lorwyn
T:Mode$ Phase | Phase$ EndCombat | ValidPlayer$ You | TriggerZones$ Command | OptionalDecider$ You | Execute$ TrigExchange | TriggerDescription$ At end of combat, you may exchange control of target creature you control that dealt combat damage to a player this combat and target creature that player controls.
SVar:TrigExchange:DB$ Pump | ValidTgts$ Creature.YouCtrl+dealtCombatDamageThisCombat Player | TgtPrompt$ Select target creature you control that dealt combat damage to a player | SubAbility$ DBExchange
SVar:TrigExchange:DB$ Pump | ValidTgts$ Creature.YouCtrl+dealtCombatDamageThisCombat | TgtPrompt$ Select target creature you control that dealt combat damage to a player | SubAbility$ DBExchange
SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Creature.ControlledBy Player.wasDealtCombatDamageThisCombatBy ParentTarget | TgtPrompt$ Select target creature that player controls.
T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, gain control of target creature you own.
SVar:RolledChaos:DB$ GainControl | ValidTgts$ Creature.YouOwn | TgtPrompt$ Select target creature you own to gain control of

View File

@@ -8,6 +8,6 @@ SVar:GrothamaFight:DB$ Fight | Defined$ TriggeredAttackerLKICopy | ExtraDefined$
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigRepeat | TriggerDescription$ When CARDNAME leaves the battlefield, each player draws cards equal to the amount of damage dealt to Grothama this turn by sources they controlled.
SVar:TrigRepeat:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ TrigDraw
SVar:TrigDraw:DB$ Draw | Defined$ Remembered | NumCards$ X
SVar:X:TriggeredCard$DamageDoneByPlayerThisTurn.Remembered
SVar:X:TriggeredCard$TotalDamageThisTurn Card.RememberedPlayerCtrl
SVar:HasAttackEffect:TRUE
Oracle:Other creatures have "Whenever this creature attacks, you may have it fight Grothama, All-Devouring."\nWhen Grothama leaves the battlefield, each player draws cards equal to the amount of damage dealt to Grothama this turn by sources they controlled.

View File

@@ -3,6 +3,6 @@ ManaCost:1
Types:Legendary Artifact Creature Thopter
PT:1/1
K:Flying
A:AB$ Effect | Cost$ Sac<1/CARDNAME> | ValidTgts$ Player.wasDealtCombatDamageThisTurnBy Self | Name$ Hope of Ghirapur Effect | StaticAbilities$ STCantBeCast | RememberObjects$ Targeted | Duration$ UntilYourNextTurn | SpellDescription$ Until your next turn, target player who was dealt combat damage by Hope of Ghirapur can't cast noncreature spells.
A:AB$ Effect | Cost$ Sac<1/CARDNAME> | ValidTgts$ Player.wasDealtCombatDamageThisTurnBySource | Name$ Hope of Ghirapur Effect | StaticAbilities$ STCantBeCast | RememberObjects$ Targeted | Duration$ UntilYourNextTurn | SpellDescription$ Until your next turn, target player who was dealt combat damage by Hope of Ghirapur can't cast noncreature spells.
SVar:STCantBeCast:Mode$ CantBeCast | EffectZone$ Command | ValidCard$ Card.nonCreature | Caster$ Player.IsRemembered | Description$ Until your next turn, target player who was dealt combat damage by Hope of Ghirapur this turn can't cast noncreature spells.
Oracle:Flying\nSacrifice Hope of Ghirapur: Until your next turn, target player who was dealt combat damage by Hope of Ghirapur this turn can't cast noncreature spells.

View File

@@ -1,8 +1,8 @@
Name:Inferno Trap
ManaCost:3 R
Types:Instant Trap
SVar:AltCost:Cost$ R | CheckSVar$ CreaturesAttacked | SVarCompare$ GE2 | Description$ If you've been dealt damage by two or more creatures this turn, you may pay {R} rather than pay this spell's mana cost.
SVar:AltCost:Cost$ R | CheckSVar$ CreaturesDmg | SVarCompare$ GE2 | Description$ If you've been dealt damage by two or more creatures this turn, you may pay {R} rather than pay this spell's mana cost.
A:SP$ DealDamage | Cost$ 3 R | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumDmg$ 4 | SpellDescription$ CARDNAME deals 4 damage to target creature.
SVar:CreaturesAttacked:Count$YourDamageSourcesThisTurn Creature
SVar:CreaturesDmg:Count$NumDamageThisTurn Creature You
AI:RemoveDeck:All
Oracle:If you've been dealt damage by two or more creatures this turn, you may pay {R} rather than pay this spell's mana cost.\nInferno Trap deals 4 damage to target creature.

View File

@@ -25,6 +25,6 @@ Colors:red
Types:Enchantment Creature Human Shaman
PT:2/2
K:Haste
R:Event$ Moved | ValidLKI$ Creature.DamagedByCard.YouCtrl,Creature.DamagedByEmblem.YouCtrl | Destination$ Graveyard | ReplaceWith$ DBExile | ActiveZones$ Battlefield | Description$ If a creature dealt damage this turn by a source you controlled would die, exile it instead.
R:Event$ Moved | ValidLKI$ Creature.DamagedByCard.YouCtrl,Emblem.YouCtrl | Destination$ Graveyard | ReplaceWith$ DBExile | ActiveZones$ Battlefield | Description$ If a creature dealt damage this turn by a source you controlled would die, exile it instead.
SVar:DBExile:DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ Exile
Oracle:Haste\nIf a creature dealt damage this turn by a source you controlled would die, exile it instead.

View File

@@ -4,7 +4,7 @@ Types:Enchantment Aura
K:Flash
K:Enchant creature
A:SP$ Attach | Cost$ 2 R | ValidTgts$ Creature | AILogic$ Pump
R:Event$ Moved | ValidLKI$ Creature.DamagedByEnchanted | Destination$ Graveyard | ActiveZones$ Battlefield | ReplaceWith$ DBExile | Description$ If a creature dealt damage by enchanted creature this turn would die, exile it instead.
R:Event$ Moved | ValidLKI$ Creature.DamagedBy Enchanted | Destination$ Graveyard | ActiveZones$ Battlefield | ReplaceWith$ DBExile | Description$ If a creature dealt damage by enchanted creature this turn would die, exile it instead.
SVar:DBExile:DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ Exile
AI:RemoveDeck:Random
SVar:NonStackingAttachEffect:True

View File

@@ -3,7 +3,7 @@ ManaCost:5 W
Types:Creature Elephant Cleric
PT:4/6
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigLife | OptionalDecider$ You | TriggerDescription$ When CARDNAME enters the battlefield, you may have your life total become the total toughness of creatures you control.
SVar:TrigLife:DB$ SetLife | Defined$ You | LifeAmount$ Y | SubAbility$ DBCleanup
SVar:TrigLife:DB$ SetLife | Defined$ You | LifeAmount$ Y
SVar:Y:Count$Valid Creature.YouCtrl$SumToughness
A:AB$ Pump | Cost$ 5 W | NumAtt$ X | NumDef$ X | SpellDescription$ CARDNAME gets +X/+X until end of turn, where X is your life total.
SVar:X:Count$YourLifeTotal

View File

@@ -6,5 +6,5 @@ R:Event$ DamageDone | ActiveZones$ Command | ValidSource$ Card.OppCtrl,Emblem.Op
SVar:DBReplace:DB$ ReplaceDamage | Amount$ 1
T:Mode$ Phase | Phase$ End of Turn | CheckSVar$ X | SVarCompare$ GE5 | TriggerZones$ Command | Execute$ Abandon | TriggerDescription$ At the beginning of each end step, if you've been dealt 5 or more damage this turn, abandon this scheme.
SVar:Abandon:AB$ Abandon | Cost$ 0
SVar:X:Count$YourDamageThisTurn
SVar:X:PlayerCountPropertyYou$DamageThisTurn
Oracle:(An ongoing scheme remains face up until it's abandoned.)\nIf a source an opponent controls would deal damage to you, prevent 1 of that damage.\nAt the beginning of each end step, if you've been dealt 5 or more damage this turn, abandon this scheme.

View File

@@ -4,6 +4,6 @@ Types:Tribal Sorcery Rogue
K:Prowl:5 U
A:SP$ Token | Cost$ 3 U | TokenAmount$ X | TokenScript$ b_1_1_faerie_rogue_flying | TokenOwner$ You | SubAbility$ DBTakeTurn | SpellDescription$ Create X 1/1 black Faerie Rogue creature tokens with flying, where X is the damage dealt to your opponents this turn. If CARDNAME's prowl cost was paid, take an extra turn after this one.
SVar:DBTakeTurn:DB$ AddTurn | NumTurns$ 1 | ConditionDefined$ Self | ConditionPresent$ Card.prowled
SVar:X:Count$TotalOppDamageThisTurn
SVar:X:PlayerCountPropertyYou$DamageToOppsThisTurn
DeckNeeds:Type$Rogue
Oracle:Prowl {5}{U} (You may cast this for its prowl cost if you dealt combat damage to a player this turn with a Rogue.)\nCreate X 1/1 black Faerie Rogue creature tokens with flying, where X is the damage dealt to your opponents this turn. If this spell's prowl cost was paid, take an extra turn after this one.

View File

@@ -8,6 +8,6 @@ SVar:RuneswordSac:DB$ SacrificeAll | Defined$ Imprinted | SubAbility$ ExileEffec
SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
SVar:TrigNoregen:Mode$ DamageDone | ValidSource$ Card.IsRemembered | ValidTarget$ Creature | Execute$ PumpNogen | Static$ True | TriggerDescription$ If the creature deals damage to a creature this turn, the creature dealt damage can't be regenerated this turn.
SVar:PumpNogen:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ TriggeredTarget
SVar:RuneswordRep:Event$ Moved | ValidLKI$ Creature.DamagedByRemembered | Destination$ Graveyard | ReplaceWith$ RuneswordExile | Description$ If a creature dealt damage by CARDNAME this turn would die, exile it instead.
SVar:RuneswordRep:Event$ Moved | ValidLKI$ Creature.DamagedBy Remembered | Destination$ Graveyard | ReplaceWith$ RuneswordExile | Description$ If a creature dealt damage by CARDNAME this turn would die, exile it instead.
SVar:RuneswordExile:DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ Exile
Oracle:{3}, {T}: Target attacking creature gets +2/+0 until end of turn. When that creature leaves the battlefield this turn, sacrifice Runesword. If the creature deals damage to a creature this turn, the creature dealt damage can't be regenerated this turn. If a creature dealt damage by the targeted creature would die this turn, exile that creature instead.

View File

@@ -3,6 +3,6 @@ ManaCost:1 B
Types:Instant
A:SP$ GainLife | Cost$ 1 B | Defined$ You | LifeAmount$ X | SubAbility$ Dmg | SpellDescription$ You gain life equal to the damage dealt to you this turn. CARDNAME deals damage to target creature you control equal to the damage dealt to you this turn.
SVar:Dmg:DB$ DealDamage | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | NumDmg$ X
SVar:X:Count$YourDamageThisTurn
SVar:X:PlayerCountPropertyYou$DamageThisTurn
AI:RemoveDeck:All
Oracle:You gain life equal to the damage dealt to you this turn. Simulacrum deals damage to target creature you control equal to the damage dealt to you this turn.

View File

@@ -5,5 +5,5 @@ PT:3/3
K:Bloodthirst:3
K:Flying
A:AB$ ChangeZone | Cost$ R R R | Origin$ Graveyard | Destination$ Hand | ActivationZone$ Graveyard | CheckSVar$ OppDamaged | SVarCompare$ GE1 | SpellDescription$ Return CARDNAME from your graveyard to your hand. Activate only if an opponent was dealt damage this turn.
SVar:OppDamaged:Count$TotalOppDamageThisTurn
SVar:OppDamaged:PlayerCountPropertyYou$DamageToOppsThisTurn
Oracle:Bloodthirst 3 (If an opponent was dealt damage this turn, this creature enters the battlefield with three +1/+1 counters on it.)\nFlying\n{R}{R}{R}: Return Skarrgan Firebird from your graveyard to your hand. Activate only if an opponent was dealt damage this turn.

View File

@@ -3,7 +3,7 @@ ManaCost:1 R
Types:Creature Human Wizard
PT:1/2
K:Haste
A:AB$ SetState | Cost$ T | ValidTgts$ Creature.YouCtrl+faceDown | Mode$ TurnFace | SubAbility$ DBDelTrig | SpellDescription$ Turn target face-down creature you control face up. At the beginning of the next end step, sacrifice it.
A:AB$ SetState | Cost$ T | ValidTgts$ Creature.YouCtrl+faceDown | Mode$ TurnFace | SubAbility$ DBPump | SpellDescription$ Turn target face-down creature you control face up. At the beginning of the next end step, sacrifice it.
SVar:DBPump:DB$ Pump | Defined$ Targeted | AtEOT$ Sacrifice
AI:RemoveDeck:All
Oracle:Haste\n{T}: Turn target face-down creature you control face up. At the beginning of the next end step, sacrifice it.

View File

@@ -3,6 +3,6 @@ ManaCost:B
Types:Instant
A:SP$ Pump | Cost$ B | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +1 | KW$ Deathtouch | RememberObjects$ Targeted | SubAbility$ DBMoonglove | SpellDescription$ Target creature you control gets +1/+0 and gains deathtouch until end of turn. Whenever a creature dealt damage by that creature dies this turn, its controller loses 2 life. (Any amount of damage a creature with deathtouch deals to a creature is enough to destroy it.)
SVar:DBMoonglove:DB$ Effect | Triggers$ MoongloveTrigger | RememberObjects$ Remembered | StackDescription$ Whenever a creature dealt damage by that creature dies this turn, its controller loses 2 life.
SVar:MoongloveTrigger:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.DamagedByRemembered | TriggerZones$ Command | Execute$ TrigLoseLife | TriggerDescription$ Whenever a creature dealt damage by that creature dies this turn, its controller loses 2 life.
SVar:MoongloveTrigger:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.DamagedBy Remembered | TriggerZones$ Command | Execute$ TrigLoseLife | TriggerDescription$ Whenever a creature dealt damage by that creature dies this turn, its controller loses 2 life.
SVar:TrigLoseLife:DB$ LoseLife | LifeAmount$ 2 | Defined$ TriggeredCardController
Oracle:Target creature you control gets +1/+0 and gains deathtouch until end of turn. Whenever a creature dealt damage by that creature dies this turn, its controller loses 2 life. (Any amount of damage a creature with deathtouch deals to a creature is enough to destroy it.)

View File

@@ -3,7 +3,7 @@ ManaCost:U B B R
Types:Legendary Artifact Equipment
K:Equip:2
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 3 | AddToughness$ 3 | AddKeyword$ First Strike | Description$ Equipped creature gets +3/+3 and has first strike.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.DamagedByEquipped | Execute$ UnscytheTrigExile | TriggerZones$ Battlefield | OptionalDecider$ You | TriggerDescription$ Whenever a creature dealt damage by equipped creature this turn dies, you may exile that card. If you do, create a 2/2 black Zombie creature token.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.DamagedBy Equipped | Execute$ UnscytheTrigExile | TriggerZones$ Battlefield | OptionalDecider$ You | TriggerDescription$ Whenever a creature dealt damage by equipped creature this turn dies, you may exile that card. If you do, create a 2/2 black Zombie creature token.
SVar:UnscytheTrigExile:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Origin$ Graveyard | Destination$ Exile | RememberChanged$ True | SubAbility$ UnscytheDBToken
SVar:UnscytheDBToken:DB$ Token | TokenOwner$ You | TokenAmount$ 1 | TokenScript$ b_2_2_zombie | ConditionDefined$ Remembered | ConditionPresent$ Card | SubAbility$ UnscytheDBCleanup
SVar:UnscytheDBCleanup:DB$ Cleanup | ClearRemembered$ True

View File

@@ -0,0 +1,9 @@
Name:Dragon Cultist
ManaCost:4 R
Types:Legendary Enchantment Background
S:Mode$ Continuous | Affected$ Creature.IsCommander+YouOwn | AddTrigger$ TrigDragon | Description$ Commander creatures you own have "At the beginning of your end step, if a source you controlled dealt 5 or more damage this turn, create a 4/4 red Dragon creature token with flying."
SVar:TrigDragon:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | CheckDefinedPlayer$ You.damageDoneSingleSource GE5 | Execute$ DBDragon | TriggerDescription$ At the beginning of your end step, if a source you controlled dealt 5 or more damage this turn, create a 4/4 red Dragon creature token with flying.
SVar:DBDragon:DB$ Token | TokenScript$ r_4_4_dragon_flying
DeckHas:Ability$Token & Type$Dragon
AI:RemoveDeck:NonCommander
Oracle:Commander creatures you own have "At the beginning of your end step, if a source you controlled dealt 5 or more damage this turn, create a 4/4 red Dragon creature token with flying."

View File

@@ -0,0 +1,9 @@
Name:Gnoll War Band
ManaCost:5 R
Types:Creature Gnoll
PT:5/5
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Description$ This spell costs {1} less to cast for each opponent who was dealt damage this turn.
SVar:X:PlayerCountOpponents$HasPropertywasDealtDamageThisTurn
K:Menace
K:Myriad
Oracle:This spell costs {1} less to cast for each opponent who was dealt damage this turn.\nMenace\nMyriad

View File

@@ -0,0 +1,22 @@
Name:Indulge
ManaCost:2 R
Types:Sorcery
A:SP$ Effect | CounterType$ P1P1 | CounterNum$ 3 | Triggers$ TriggerAttacks | AddSVar$ AE | SpellDescription$ Whenever a creature you control attacks this turn, create a 1/1 green and white Citizen creature token that's tapped and attacking.
SVar:TriggerAttacks:Mode$ Attacks | ValidCard$ Creature.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever a creature you control attacks this turn, create a 1/1 green and white Citizen creature token that's tapped and attacking.
SVar:TrigToken:DB$ Token | TokenScript$ gw_1_1_citizen | TokenTapped$ True | TokenAttacking$ True
SVar:AE:SVar:HasAttackEffect:TRUE
DeckHas:Ability$Token & Type$Citizen
AlternateMode:Split
Oracle:Whenever a creature you control attacks this turn, create a 1/1 green and white Citizen creature token that's tapped and attacking.
ALTERNATE
Name:Excess
ManaCost:1 R
Types:Sorcery
K:Aftermath
A:SP$ Token | TokenAmount$ X | TokenScript$ c_a_treasure_sac | SpellDescription$ Create a Treasure token for each creature you controlled that dealt combat damage to a player this turn.
SVar:X:Count$NumCombatDamageThisTurn Creature.YouCtrl Player
DeckHas:Ability$Token|Sacrifice|Graveyard & Type$Artifact|Treasure
SVar:NeedsToPlayVar:X GE3
Oracle:Aftermath (Cast this spell only from your graveyard. Then exile it.)\nCreate a Treasure token for each creature you controlled that dealt combat damage to a player this turn.

View File

@@ -4,7 +4,7 @@ Types:Creature Elemental
PT:1/1
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSac | TriggerDescription$ When CARDNAME enters the battlefield, sacrifice it unless an opponent was dealt damage this turn.
SVar:TrigSac:DB$ Sacrifice | Defined$ Self | ConditionCheckSVar$ WarElementalX | ConditionSVarCompare$ EQ0
SVar:WarElementalX:Count$TotalOppDamageThisTurn
SVar:WarElementalX:PlayerCountPropertyYou$DamageToOppsThisTurn
T:Mode$ DamageDoneOnce | ValidSource$ Card | ValidTarget$ Opponent | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever an opponent is dealt damage, put that many +1/+1 counters on CARDNAME.
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ WarElementalY
SVar:WarElementalY:TriggerCount$DamageAmount

View File

@@ -2,5 +2,5 @@ Name:Wicked Akuba
ManaCost:B B
Types:Creature Spirit
PT:2/2
A:AB$ LoseLife | Cost$ B | ValidTgts$ Player.wasDealtDamageThisTurnBy Self | TgtPrompt$ Select target player that was dealt damage this turn | LifeAmount$ 1 | SpellDescription$ Target player dealt damage by CARDNAME this turn loses 1 life.
A:AB$ LoseLife | Cost$ B | ValidTgts$ Player.wasDealtDamageThisTurnBySource | TgtPrompt$ Select target player that was dealt damage this turn | LifeAmount$ 1 | SpellDescription$ Target player dealt damage by CARDNAME this turn loses 1 life.
Oracle:{B}: Target player dealt damage by Wicked Akuba this turn loses 1 life.