From 67286aa1a05fe1f613c5585d21eebfa746b9f3c7 Mon Sep 17 00:00:00 2001 From: jimga150 Date: Sun, 17 Mar 2024 16:15:50 -0400 Subject: [PATCH] Implement Damage Batch for Permanent event (#11841) * implement [WHO] Donna Noble * Changed trigger to DAMAGED_BATCH_FOR_PERMANENTS, check for need of separate targets * fix short circuit operator * simplify control path in paired damage trigger * Initial commit, missing tests * use CardUtil.getEventTargets * Implement Donna Noble using DamagedBatchForOnePermanentEvent * fix double-effect bug * remove unnecessary custom effect * Fix addSimultaneousDamage to avoid adding damage events to existing DamagedBatchForOnePlayerEvent instances when they shouldnt * Add clarifying comment * Incorporate batching of DAMAGED_BATCH_FOR_ONE_PERMANENT into if-else if tree to match new logic * Add tests * make ability inline * Move DamageBatchTests * Change batch events to take first event in constructor --- Mage.Sets/src/mage/cards/d/DonnaNoble.java | 114 +++++++++++++++ Mage.Sets/src/mage/sets/DoctorWho.java | 1 + .../triggers/damage/DonnaNobleTests.java | 138 ++++++++++++++++++ .../triggers/delayed/DamagedBatchTests.java | 71 +++++++++ Mage/src/main/java/mage/game/GameState.java | 19 ++- .../mage/game/events/DamagedBatchEvent.java | 6 +- .../DamagedBatchForOnePermanentEvent.java | 10 ++ .../events/DamagedBatchForOnePlayerEvent.java | 7 +- .../DamagedBatchForPermanentsEvent.java | 3 +- .../events/DamagedBatchForPlayersEvent.java | 3 +- .../main/java/mage/game/events/GameEvent.java | 5 + 11 files changed, 365 insertions(+), 12 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/d/DonnaNoble.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/DonnaNobleTests.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/DamagedBatchTests.java create mode 100644 Mage/src/main/java/mage/game/events/DamagedBatchForOnePermanentEvent.java diff --git a/Mage.Sets/src/mage/cards/d/DonnaNoble.java b/Mage.Sets/src/mage/cards/d/DonnaNoble.java new file mode 100644 index 00000000000..f3ad5f05598 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DonnaNoble.java @@ -0,0 +1,114 @@ +package mage.cards.d; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.dynamicvalue.common.SavedDamageValue; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.constants.*; +import mage.abilities.keyword.SoulbondAbility; +import mage.abilities.keyword.DoctorsCompanionAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.game.Game; +import mage.game.events.DamagedBatchForOnePermanentEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetOpponent; +import mage.util.CardUtil; + +/** + * + * @author jimga150 + */ +public final class DonnaNoble extends CardImpl { + + public DonnaNoble(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Soulbond + this.addAbility(new SoulbondAbility()); + + // Whenever Donna or a creature it's paired with is dealt damage, Donna deals that much damage to target opponent. + this.addAbility(new DonnaNobleTriggeredAbility()); + + // If Donna is paired with another creature and they are both dealt damage at the same time, + // the second ability triggers twice. (2023-10-13) + + // Donna's ability triggers when either creature is dealt damage even if one or both were dealt lethal damage. + // (2023-10-13) + + // Doctor's companion + this.addAbility(DoctorsCompanionAbility.getInstance()); + + } + + private DonnaNoble(final DonnaNoble card) { + super(card); + } + + @Override + public DonnaNoble copy() { + return new DonnaNoble(this); + } +} +// Based on DealtDamageToSourceTriggeredAbility, except this uses DamagedBatchForOnePermanentEvent, +// which batches all damage dealt at the same time on a permanent-by-permanent basis +class DonnaNobleTriggeredAbility extends TriggeredAbilityImpl { + + DonnaNobleTriggeredAbility() { + super(Zone.BATTLEFIELD, new DamageTargetEffect(SavedDamageValue.MUCH)); + this.addTarget(new TargetOpponent()); + this.setTriggerPhrase("Whenever {this} or a creature it's paired with is dealt damage, "); + } + + private DonnaNobleTriggeredAbility(final DonnaNobleTriggeredAbility ability) { + super(ability); + } + + @Override + public DonnaNobleTriggeredAbility copy() { + return new DonnaNobleTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + DamagedBatchForOnePermanentEvent dEvent = (DamagedBatchForOnePermanentEvent) event; + + // check if the permanent is Donna or its paired card + if (!CardUtil.getEventTargets(dEvent).contains(getSourceId())){ + Permanent paired; + Permanent permanent = game.getPermanent(getSourceId()); + if (permanent != null && permanent.getPairedCard() != null) { + paired = permanent.getPairedCard().getPermanent(game); + if (paired == null || paired.getPairedCard() == null || !paired.getPairedCard().equals(new MageObjectReference(permanent, game))) { + return false; + } + } else { + return false; + } + if (!CardUtil.getEventTargets(dEvent).contains(paired.getId())){ + return false; + } + } + + int damage = dEvent.getAmount(); + if (damage < 1) { + return false; + } + this.getEffects().setValue("damage", damage); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/DoctorWho.java b/Mage.Sets/src/mage/sets/DoctorWho.java index a76d3a62f93..0e7a70792eb 100644 --- a/Mage.Sets/src/mage/sets/DoctorWho.java +++ b/Mage.Sets/src/mage/sets/DoctorWho.java @@ -72,6 +72,7 @@ public final class DoctorWho extends ExpansionSet { cards.add(new SetCardInfo("Desolate Lighthouse", 271, Rarity.RARE, mage.cards.d.DesolateLighthouse.class)); cards.add(new SetCardInfo("Dinosaurs on a Spaceship", 122, Rarity.RARE, mage.cards.d.DinosaursOnASpaceship.class)); cards.add(new SetCardInfo("Displaced Dinosaurs", 100, Rarity.UNCOMMON, mage.cards.d.DisplacedDinosaurs.class)); + cards.add(new SetCardInfo("Donna Noble", 82, Rarity.RARE, mage.cards.d.DonnaNoble.class)); cards.add(new SetCardInfo("Dragonskull Summit", 272, Rarity.RARE, mage.cards.d.DragonskullSummit.class)); cards.add(new SetCardInfo("Dreamroot Cascade", 273, Rarity.RARE, mage.cards.d.DreamrootCascade.class)); cards.add(new SetCardInfo("Drowned Catacomb", 274, Rarity.RARE, mage.cards.d.DrownedCatacomb.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/DonnaNobleTests.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/DonnaNobleTests.java new file mode 100644 index 00000000000..826afcc4757 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/DonnaNobleTests.java @@ -0,0 +1,138 @@ +package org.mage.test.cards.triggers.damage; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4Players; + +/** + * + * @author jimga150 + */ +public class DonnaNobleTests extends CardTestCommander4Players { + + @Test + public void PairedCreature2To1Test() { + + //Check that paired creature being dealt damage by 2 sources at the same time = 1 trigger with correct amount + + addCard(Zone.BATTLEFIELD, playerA, "Donna Noble", 1); // Legendary Creature — Human 2/4 {3}{R} + + addCard(Zone.HAND, playerA, "Impervious Greatwurm", 1); // Creature — Wurm 16/16 {7}{G}{G}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 10); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Impervious Greatwurm", true); + + //Yes, soul bond donna noble with IG + setChoice(playerA, "Yes"); + + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); // Artifact Creature — Construct 1/1 {0} + addCard(Zone.BATTLEFIELD, playerB, "Expedition Envoy", 1); // Creature — Human Scout Ally 2/1 {W} + + attack(5, playerA, "Impervious Greatwurm", playerB); + block(5, playerB, "Memnite", "Impervious Greatwurm"); + block(5, playerB, "Expedition Envoy", "Impervious Greatwurm"); + + //Assign this much damage to the first blocking creature + setChoice(playerA, "X=1"); + + //Assign this much damage to the second blocking creature + setChoice(playerA, "X=1"); + + //Target this player with Donna Noble + addTarget(playerA, playerB); + + setStopAt(5, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertLife(playerA, currentGame.getStartingLife()); + assertLife(playerB, currentGame.getStartingLife() - 3); + + } + + @Test + public void DonnaAndPairedBothDamagedSingleSourceTest() { + + //Check that Donna and paired creature both damaged at the same time by one source = 2 triggers with correct amounts + + addCard(Zone.BATTLEFIELD, playerA, "Donna Noble", 1); // Legendary Creature — Human 2/4 {3}{R} + + addCard(Zone.HAND, playerA, "Impervious Greatwurm", 1); // Creature — Wurm 16/16 {7}{G}{G}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 10); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Impervious Greatwurm", true); + + //Yes, soul bond donna noble with IG + setChoice(playerA, "Yes"); + + // Kicker {R} (You may pay an additional {R} as you cast this spell.) + // Cinderclasm deals 1 damage to each creature. If it was kicked, it deals 2 damage to each creature instead. + addCard(Zone.HAND, playerA, "Cinderclasm", 1); // Instant {1}{R} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cinderclasm", true); + + //Yes, pay kicker for Cinderclasm + setChoice(playerA, "Yes"); + + //pick triggered ability starting with this string to enter the stack first + setChoice(playerA, "Whenever"); + + //Target this player with Donna Noble + addTarget(playerA, playerB); + + //Target this player with Donna Noble (second trigger) + addTarget(playerA, playerB); + + setStopAt(2, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertLife(playerA, currentGame.getStartingLife()); + assertLife(playerB, currentGame.getStartingLife() - 4); + + } + + @Test + public void DonnaAndPairedBothDamagedDiffSourceTest() { + + //Check that Donna and paired creature both damaged at the same time by different sources = 2 triggers with correct amounts + + addCard(Zone.BATTLEFIELD, playerA, "Donna Noble", 1); // Legendary Creature — Human 2/4 {3}{R} + + addCard(Zone.HAND, playerA, "Impervious Greatwurm", 1); // Creature — Wurm 16/16 {7}{G}{G}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 10); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Impervious Greatwurm", true); + + //Yes, soul bond donna noble with IG + setChoice(playerA, "Yes"); + + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); // Artifact Creature — Construct 1/1 {0} + addCard(Zone.BATTLEFIELD, playerB, "Expedition Envoy", 1); // Creature — Human Scout Ally 2/1 {W} + + attack(4, playerB, "Memnite", playerA); + attack(4, playerB, "Expedition Envoy", playerA); + block(4, playerA, "Impervious Greatwurm", "Memnite"); + block(4, playerA, "Donna Noble", "Expedition Envoy"); + + //pick triggered ability starting with this string to enter the stack first + setChoice(playerA, "Whenever"); + + //Target this player with Donna Noble + addTarget(playerA, playerB); + + //Target this player with Donna Noble (second trigger) + addTarget(playerA, playerB); + + setStopAt(4, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertLife(playerA, currentGame.getStartingLife()); + assertLife(playerB, currentGame.getStartingLife() - 3); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/DamagedBatchTests.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/DamagedBatchTests.java new file mode 100644 index 00000000000..e4c30fd1535 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/DamagedBatchTests.java @@ -0,0 +1,71 @@ + +package org.mage.test.cards.triggers.delayed; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4Players; + +/** + * + * @author jimga150 + */ +public class DamagedBatchTests extends CardTestCommander4Players { + + @Test + public void DamageBatchForOnePermanent2To1Test() { + + //Check that one creature being dealt damage by 2 sources at the same time = 1 trigger of DAMAGED_BATCH_FOR_ONE_PERMANENT + + addCard(Zone.BATTLEFIELD, playerA, "Donna Noble", 1); // Legendary Creature — Human 2/4 {3}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); // Artifact Creature — Construct 1/1 {0} + addCard(Zone.BATTLEFIELD, playerB, "Expedition Envoy", 1); // Creature — Human Scout Ally 2/1 {W} + + attack(1, playerA, "Donna Noble", playerB); + block(1, playerB, "Memnite", "Donna Noble"); + block(1, playerB, "Expedition Envoy", "Donna Noble"); + + //Assign this much damage to the first blocking creature + setChoice(playerA, "X=1"); + + //Target this player with Donna Noble + addTarget(playerA, playerB); + + setStopAt(2, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertLife(playerA, currentGame.getStartingLife()); + assertLife(playerB, currentGame.getStartingLife() - 3); + + } + + @Test + public void DamageBatchForOnePermanent2EventTest() { + + //Check that one creature being dealt damage at 2 different times (double strike in this case) = 2 triggers of DAMAGED_BATCH_FOR_ONE_PERMANENT + + addCard(Zone.BATTLEFIELD, playerA, "Donna Noble", 1); // Legendary Creature — Human 2/4 {3}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Adorned Pouncer", 1); // Creature — Cat 1/1 {1}{W} + + attack(1, playerA, "Donna Noble", playerB); + block(1, playerB, "Adorned Pouncer", "Donna Noble"); + + //Target this player with Donna Noble + addTarget(playerA, playerB); + + //Target this player with Donna Noble (second trigger) + addTarget(playerA, playerB); + + setStopAt(2, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertLife(playerA, currentGame.getStartingLife()); + assertLife(playerB, currentGame.getStartingLife() - 2); + + } + +} diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 7b372019270..21158cc1c07 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -812,15 +812,18 @@ public class GameState implements Serializable, Copyable { // Combine multiple damage events in the single event (batch) // * per damage type (see GameEvent.DAMAGED_BATCH_FOR_PERMANENTS, GameEvent.DAMAGED_BATCH_FOR_PLAYERS) // * per player (see GameEvent.DAMAGED_BATCH_FOR_ONE_PLAYER) + // * per permanent (see GameEvent.DAMAGED_BATCH_FOR_ONE_PERMANENT) // // Warning, one event can be stored in multiple batches, // example: DAMAGED_BATCH_FOR_PLAYERS + DAMAGED_BATCH_FOR_ONE_PLAYER boolean isPlayerDamage = damagedEvent instanceof DamagedPlayerEvent; + boolean isPermanentDamage = damagedEvent instanceof DamagedPermanentEvent; // existing batch boolean isDamageBatchUsed = false; boolean isPlayerBatchUsed = false; + boolean isPermanentBatchUsed = false; for (GameEvent event : simultaneousEvents) { if (isPlayerDamage && event instanceof DamagedBatchForOnePlayerEvent) { @@ -831,6 +834,14 @@ public class GameState implements Serializable, Copyable { oldPlayerBatch.addEvent(damagedEvent); isPlayerBatchUsed = true; } + } else if (isPermanentDamage && event instanceof DamagedBatchForOnePermanentEvent) { + // per permanent + DamagedBatchForOnePermanentEvent oldPermanentBatch = (DamagedBatchForOnePermanentEvent) event; + if (oldPermanentBatch.getDamageClazz().isInstance(damagedEvent) + && CardUtil.getEventTargets(event).contains(damagedEvent.getTargetId())) { + oldPermanentBatch.addEvent(damagedEvent); + isPermanentBatchUsed = true; + } } else if ((event instanceof DamagedBatchEvent) && ((DamagedBatchEvent) event).getDamageClazz().isInstance(damagedEvent)) { // per damage type @@ -842,6 +853,7 @@ public class GameState implements Serializable, Copyable { ((DamagedBatchEvent) event).addEvent(damagedEvent); isDamageBatchUsed = true; } + } // new batch @@ -849,8 +861,11 @@ public class GameState implements Serializable, Copyable { addSimultaneousEvent(DamagedBatchEvent.makeEvent(damagedEvent), game); } if (!isPlayerBatchUsed && isPlayerDamage) { - DamagedBatchEvent event = new DamagedBatchForOnePlayerEvent(damagedEvent.getTargetId()); - event.addEvent(damagedEvent); + DamagedBatchEvent event = new DamagedBatchForOnePlayerEvent(damagedEvent); + addSimultaneousEvent(event, game); + } + if (!isPermanentBatchUsed && isPermanentDamage) { + DamagedBatchEvent event = new DamagedBatchForOnePermanentEvent(damagedEvent); addSimultaneousEvent(event, game); } } diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java index 584ce2ac794..5ecdee7e036 100644 --- a/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java +++ b/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java @@ -67,11 +67,9 @@ public abstract class DamagedBatchEvent extends GameEvent implements BatchGameEv public static DamagedBatchEvent makeEvent(DamagedEvent damagedEvent) { DamagedBatchEvent event; if (damagedEvent instanceof DamagedPlayerEvent) { - event = new DamagedBatchForPlayersEvent(); - event.addEvent(damagedEvent); + event = new DamagedBatchForPlayersEvent(damagedEvent); } else if (damagedEvent instanceof DamagedPermanentEvent) { - event = new DamagedBatchForPermanentsEvent(); - event.addEvent(damagedEvent); + event = new DamagedBatchForPermanentsEvent(damagedEvent); } else { throw new IllegalArgumentException("Wrong code usage. Unknown damage event for a new batch: " + damagedEvent.getClass().getName()); } diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchForOnePermanentEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchForOnePermanentEvent.java new file mode 100644 index 00000000000..68ef57b5e06 --- /dev/null +++ b/Mage/src/main/java/mage/game/events/DamagedBatchForOnePermanentEvent.java @@ -0,0 +1,10 @@ +package mage.game.events; + +public class DamagedBatchForOnePermanentEvent extends DamagedBatchEvent { + + public DamagedBatchForOnePermanentEvent(DamagedEvent firstEvent) { + super(GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT, DamagedPermanentEvent.class); + addEvent(firstEvent); + setTargetId(firstEvent.getTargetId()); + } +} diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchForOnePlayerEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchForOnePlayerEvent.java index 01b632bddef..bedc02423a6 100644 --- a/Mage/src/main/java/mage/game/events/DamagedBatchForOnePlayerEvent.java +++ b/Mage/src/main/java/mage/game/events/DamagedBatchForOnePlayerEvent.java @@ -1,14 +1,13 @@ package mage.game.events; -import java.util.UUID; - /** * @author Susucr */ public class DamagedBatchForOnePlayerEvent extends DamagedBatchEvent { - public DamagedBatchForOnePlayerEvent(UUID playerId) { + public DamagedBatchForOnePlayerEvent(DamagedEvent firstEvent) { super(EventType.DAMAGED_BATCH_FOR_ONE_PLAYER, DamagedPlayerEvent.class); - this.setPlayerId(playerId); + setPlayerId(firstEvent.getPlayerId()); + addEvent(firstEvent); } } diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchForPermanentsEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchForPermanentsEvent.java index 7af949f8d6f..aa5b2b9a6bc 100644 --- a/Mage/src/main/java/mage/game/events/DamagedBatchForPermanentsEvent.java +++ b/Mage/src/main/java/mage/game/events/DamagedBatchForPermanentsEvent.java @@ -5,7 +5,8 @@ package mage.game.events; */ public class DamagedBatchForPermanentsEvent extends DamagedBatchEvent { - public DamagedBatchForPermanentsEvent() { + public DamagedBatchForPermanentsEvent(DamagedEvent firstEvent) { super(EventType.DAMAGED_BATCH_FOR_PERMANENTS, DamagedPermanentEvent.class); + addEvent(firstEvent); } } diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchForPlayersEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchForPlayersEvent.java index 54e0835d11e..d5fbaeea10e 100644 --- a/Mage/src/main/java/mage/game/events/DamagedBatchForPlayersEvent.java +++ b/Mage/src/main/java/mage/game/events/DamagedBatchForPlayersEvent.java @@ -5,7 +5,8 @@ package mage.game.events; */ public class DamagedBatchForPlayersEvent extends DamagedBatchEvent { - public DamagedBatchForPlayersEvent() { + public DamagedBatchForPlayersEvent(DamagedEvent firstEvent) { super(GameEvent.EventType.DAMAGED_BATCH_FOR_PLAYERS, DamagedPlayerEvent.class); + addEvent(firstEvent); } } diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 446d5505861..4a41084d94a 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -450,6 +450,11 @@ public class GameEvent implements Serializable { */ DAMAGED_BATCH_FOR_PERMANENTS, + /* DAMAGED_BATCH_FOR_ONE_PERMANENT + combines all permanent damage events to a single batch (event) and split it per damaged permanent + */ + DAMAGED_BATCH_FOR_ONE_PERMANENT, + DESTROY_PERMANENT, /* DESTROY_PERMANENT_BY_LEGENDARY_RULE targetId id of the permanent to destroy