game: fixed wrong usage of batch events (related to #10870)

This commit is contained in:
Oleg Agafonov 2023-09-13 04:42:46 +04:00
parent ba3b0e4cb7
commit be4bff6097
6 changed files with 64 additions and 8 deletions

View file

@ -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<String, TriggeredAbili
if (event == null || !game.getContinuousEffects().preventedByRuleModification(event, ability, game, false)) {
if (object != null) {
boolean controllerSet = false;
Set<UUID> 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())) {

View file

@ -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<UUID> 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.

View file

@ -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)
* <p>
* Used by game engine to support event lifecycle for triggers
*
* @author JayDi85
*/
public interface BatchGameEvent<T extends GameEvent> {
Set<T> getEvents();
Set<UUID> getTargets();
}

View file

@ -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<DamagedEvent> {
private final Class<? extends DamagedEvent> damageClazz;
private final Set<DamagedEvent> events = new HashSet<>();
@ -17,10 +19,19 @@ public abstract class DamagedBatchEvent extends GameEvent {
this.damageClazz = damageClazz;
}
@Override
public Set<DamagedEvent> getEvents() {
return events;
}
@Override
public Set<UUID> 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

View file

@ -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;
}

View file

@ -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<UUID> getEventTargets(GameEvent event) {
Set<UUID> 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;
}
}