From be4bff6097bde34d1f397a225587496142e40517 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 13 Sep 2023 04:42:46 +0400 Subject: [PATCH] game: fixed wrong usage of batch events (related to #10870) --- .../mage/abilities/TriggeredAbilities.java | 6 +++--- .../mage/abilities/TriggeredAbilityImpl.java | 10 +++++++--- .../java/mage/game/events/BatchGameEvent.java | 19 +++++++++++++++++++ .../mage/game/events/DamagedBatchEvent.java | 15 +++++++++++++-- .../main/java/mage/game/events/GameEvent.java | 5 +++++ Mage/src/main/java/mage/util/CardUtil.java | 17 +++++++++++++++++ 6 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 Mage/src/main/java/mage/game/events/BatchGameEvent.java diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java index 0b89a21a4a2..38a96c38643 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java @@ -1,5 +1,3 @@ - - package mage.abilities; import mage.MageObject; @@ -9,6 +7,7 @@ import mage.game.events.GameEvent; import mage.game.events.NumberOfTriggersEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import mage.util.CardUtil; import org.apache.log4j.Logger; import java.util.*; @@ -64,9 +63,10 @@ public class TriggeredAbilities extends ConcurrentHashMap eventTargets = CardUtil.getEventTargets(event); if (ability.getZone() != Zone.COMMAND && event != null - && event.getTargetId() != null + && !eventTargets.isEmpty() && ability.isLeavesTheBattlefieldTrigger() && game.getLKI().get(Zone.BATTLEFIELD) != null && game.getLKI().get(Zone.BATTLEFIELD).containsKey(ability.getSourceId())) { diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java index bea7fff3c9e..916114e6700 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java @@ -15,6 +15,7 @@ import mage.players.Player; import mage.util.CardUtil; import java.util.Locale; +import java.util.Set; import java.util.UUID; /** @@ -44,7 +45,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge // verify check: DoIfCostPaid effect already asks about action (optional), so no needs to ask it again in triggered ability if (effect instanceof DoIfCostPaid && (this.optional && ((DoIfCostPaid) effect).isOptional())) { - throw new IllegalArgumentException("DoIfCostPaid effect must have only one optional settings, but it have two (trigger + DoIfCostPaid): " + this.getClass().getSimpleName()); + throw new IllegalArgumentException("DoIfCostPaid effect must have only one optional settings, but it have two (trigger + DoIfCostPaid): " + this.getClass().getSimpleName()); } } @@ -296,13 +297,16 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge * latter triggers trigger from the game state after the move where the * Kozilek card is itself and has the ability. */ - if (event == null || event.getTargetId() == null || !event.getTargetId().equals(getSourceId())) { + + Set eventTargets = CardUtil.getEventTargets(event); + if (!eventTargets.contains(getSourceId())) { return super.isInUseableZone(game, source, event); } + switch (event.getType()) { case ZONE_CHANGE: ZoneChangeEvent zce = (ZoneChangeEvent) event; - if (event.getTargetId().equals(getSourceId()) && !zce.getToZone().isPublicZone()) { + if (eventTargets.contains(getSourceId()) && !zce.getToZone().isPublicZone()) { // If an ability triggers when the object that has it is put into a hidden zone from a graveyard, // that ability triggers from the graveyard, (such as Golgari Brownscale), // Yixlid Jailer will prevent that ability from triggering. diff --git a/Mage/src/main/java/mage/game/events/BatchGameEvent.java b/Mage/src/main/java/mage/game/events/BatchGameEvent.java new file mode 100644 index 00000000000..e41e67a30bd --- /dev/null +++ b/Mage/src/main/java/mage/game/events/BatchGameEvent.java @@ -0,0 +1,19 @@ +package mage.game.events; + +import java.util.Set; +import java.util.UUID; + +/** + * Game event with batch support (batch is an event that can contain multiple events, example: DAMAGED_BATCH_FOR_PLAYERS) + *

+ * Used by game engine to support event lifecycle for triggers + * + * @author JayDi85 + */ +public interface BatchGameEvent { + + Set getEvents(); + + Set getTargets(); + +} diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java index 355f9fbc423..d363c7fe11d 100644 --- a/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java +++ b/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java @@ -1,13 +1,15 @@ package mage.game.events; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; /** * @author TheElk801 */ -public abstract class DamagedBatchEvent extends GameEvent { +public abstract class DamagedBatchEvent extends GameEvent implements BatchGameEvent { private final Class damageClazz; private final Set events = new HashSet<>(); @@ -17,10 +19,19 @@ public abstract class DamagedBatchEvent extends GameEvent { this.damageClazz = damageClazz; } + @Override public Set getEvents() { return events; } + @Override + public Set getTargets() { + return events.stream() + .map(GameEvent::getTargetId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + @Override public int getAmount() { return events @@ -32,7 +43,7 @@ public abstract class DamagedBatchEvent extends GameEvent { @Override @Deprecated // events can store a diff value, so search it from events list instead public UUID getTargetId() { - throw new IllegalStateException("Wrong code usage. Must search value from a getEvents list."); + throw new IllegalStateException("Wrong code usage. Must search value from a getEvents list or use CardUtil.getEventTargets(event)"); } @Override diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 7b48453f409..ae4444daaff 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -593,6 +593,11 @@ public class GameEvent implements Serializable { return id; } + /** + * Some batch events can contain multiple events list, see BatchGameEvent for usage + * + * @return + */ public UUID getTargetId() { return targetId; } diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 87741da9a9e..18ecb0892f0 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -30,6 +30,7 @@ import mage.game.CardState; import mage.game.Game; import mage.game.GameState; import mage.game.command.Commander; +import mage.game.events.BatchGameEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; @@ -1840,4 +1841,20 @@ public final class CardUtil { targetToken.setCardNumber(newCardNumber); targetToken.setImageNumber(newImageNumber); } + + /** + * One single event can be a batch (contain multiple events) + * + * @param event + * @return + */ + public static Set getEventTargets(GameEvent event) { + Set res = new HashSet<>(); + if (event instanceof BatchGameEvent) { + res.addAll(((BatchGameEvent) event).getTargets()); + } else if (event != null && event.getTargetId() != null) { + res.add(event.getTargetId()); + } + return res; + } }