From cb28fb5a56d1653ca4a18946593c3d0a9cc092e2 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Thu, 28 Mar 2024 23:19:20 -0400 Subject: [PATCH] Refactor batch events (#11995) * create new abstract class for batch event framework * adjust CardUtil.getEventTargets to support new framework * update TappedBatchEvent to new framework * update UntappedBatchEvent to new framework * slight cleanup * update LifeLostBatchEvent to new framework * update ZoneChangeBatchEvent to new framework * complete refactor by moving damage events to new framework * remove old code no longer used * clean up some nonsense code in star wars card * fix watcher checking id before event type * fix wrong id usage * fix missed wrong id usage --- .../mage/cards/a/AlelaCunningConqueror.java | 2 +- Mage.Sets/src/mage/cards/b/BenSolo.java | 126 ++------------- .../mage/cards/b/BloodSpatterAnalysis.java | 4 +- .../mage/cards/b/BreechesBrazenPlunderer.java | 9 +- .../src/mage/cards/c/ContestedGameBall.java | 4 +- .../src/mage/cards/c/CourtOfLocthwain.java | 7 +- .../src/mage/cards/f/FelineSovereign.java | 2 +- .../src/mage/cards/h/HowlpackAvenger.java | 5 +- .../src/mage/cards/k/KayaSpiritsJustice.java | 3 - .../src/mage/cards/m/MagmaticGalleon.java | 5 +- .../mage/cards/o/ObNixilisCaptiveKingpin.java | 8 +- .../src/mage/cards/o/OliviasAttendants.java | 7 +- .../mage/cards/r/RisonaAsariCommander.java | 20 +-- .../src/mage/cards/w/WaytaTrainerProdigy.java | 40 ++--- .../src/mage/cards/w/WrathfulRaptors.java | 10 +- .../src/mage/cards/z/ZurgoAndOjutai.java | 8 +- ...ecomesTappedOneOrMoreTriggeredAbility.java | 2 +- ...sCombatDamageEquippedTriggeredAbility.java | 21 +-- .../DealsCombatDamageTriggeredAbility.java | 23 +-- .../OneOrMoreDealDamageTriggeredAbility.java | 6 +- Mage/src/main/java/mage/game/GameState.java | 149 +++++++++--------- .../java/mage/game/events/BatchEvent.java | 101 ++++++++++++ .../java/mage/game/events/BatchGameEvent.java | 19 --- .../game/events/DamagedBatchAllEvent.java | 11 ++ .../mage/game/events/DamagedBatchEvent.java | 78 --------- .../DamagedBatchForOnePermanentEvent.java | 8 +- .../events/DamagedBatchForOnePlayerEvent.java | 14 +- .../DamagedBatchForPermanentsEvent.java | 7 +- .../events/DamagedBatchForPlayersEvent.java | 7 +- .../main/java/mage/game/events/GameEvent.java | 7 +- .../mage/game/events/LifeLostBatchEvent.java | 55 +------ .../java/mage/game/events/LifeLostEvent.java | 9 +- .../mage/game/events/TappedBatchEvent.java | 50 +----- .../mage/game/events/UntappedBatchEvent.java | 50 +----- .../java/mage/game/events/UntappedEvent.java | 1 + .../game/events/ZoneChangeBatchEvent.java | 50 +----- .../mage/game/permanent/PermanentImpl.java | 4 +- .../main/java/mage/players/PlayerImpl.java | 2 +- Mage/src/main/java/mage/util/CardUtil.java | 9 +- 39 files changed, 308 insertions(+), 635 deletions(-) create mode 100644 Mage/src/main/java/mage/game/events/BatchEvent.java delete mode 100644 Mage/src/main/java/mage/game/events/BatchGameEvent.java create mode 100644 Mage/src/main/java/mage/game/events/DamagedBatchAllEvent.java delete mode 100644 Mage/src/main/java/mage/game/events/DamagedBatchEvent.java diff --git a/Mage.Sets/src/mage/cards/a/AlelaCunningConqueror.java b/Mage.Sets/src/mage/cards/a/AlelaCunningConqueror.java index 1095469bd99..e98dd15413b 100644 --- a/Mage.Sets/src/mage/cards/a/AlelaCunningConqueror.java +++ b/Mage.Sets/src/mage/cards/a/AlelaCunningConqueror.java @@ -81,7 +81,7 @@ class AlelaCunningConquerorTriggeredAbility extends DealCombatDamageControlledTr if (!super.checkTrigger(event, game)) { return false; } - Player opponent = game.getPlayer(event.getPlayerId()); + Player opponent = game.getPlayer(event.getTargetId()); if (opponent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/b/BenSolo.java b/Mage.Sets/src/mage/cards/b/BenSolo.java index 0cb1245c9fd..5c3ef6b4e19 100644 --- a/Mage.Sets/src/mage/cards/b/BenSolo.java +++ b/Mage.Sets/src/mage/cards/b/BenSolo.java @@ -2,27 +2,24 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.DealtDamageToSourceTriggeredAbility; +import mage.abilities.dynamicvalue.common.SavedDamageValue; +import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.events.DamagedEvent; -import mage.game.events.DamagedBatchForPermanentsEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.target.common.TargetPlayerOrPlaneswalker; -import java.util.Set; import java.util.UUID; /** * @author Merlingilb */ public class BenSolo extends CardImpl { + public BenSolo(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R/W}{W}"); this.supertype.add(SuperType.LEGENDARY); @@ -31,11 +28,14 @@ public class BenSolo extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - //Vigilance + // Vigilance this.addAbility(VigilanceAbility.getInstance()); - //Whenever Ben Solo is dealt damage, it deals that much damage to target player or planeswalker. - this.addAbility(new BenSoloTriggeredAbility()); + // Whenever Ben Solo is dealt damage, it deals that much damage to target player or planeswalker. + Ability ability = new DealtDamageToSourceTriggeredAbility( + new DamageTargetEffect(SavedDamageValue.MUCH, "it"), false); + ability.addTarget(new TargetPlayerOrPlaneswalker()); + this.addAbility(ability); } private BenSolo(final BenSolo card) { @@ -47,103 +47,3 @@ public class BenSolo extends CardImpl { return new BenSolo(this); } } - -class BenSoloTriggeredAbility extends TriggeredAbilityImpl { - - UUID benSoloID; - - BenSoloTriggeredAbility() { - super(Zone.BATTLEFIELD, new BenSoloEffect(), false); - this.addTarget(new TargetPlayerOrPlaneswalker()); - benSoloID = this.getSourceId(); - } - - private BenSoloTriggeredAbility(final BenSoloTriggeredAbility ability) { - super(ability); - benSoloID = ability.benSoloID; - } - - @Override - public BenSoloTriggeredAbility copy() { - return new BenSoloTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_PERMANENTS; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (benSoloID == null) { - benSoloID = this.getSourceId(); - if (benSoloID == null) { - return false; - } - } - - int damage = 0; - DamagedBatchForPermanentsEvent dEvent = (DamagedBatchForPermanentsEvent) event; - Set set = dEvent.getEvents(); - for (DamagedEvent damagedEvent : set) { - UUID targetID = damagedEvent.getTargetId(); - if (targetID == null) { - continue; - } - - if (targetID == benSoloID) { - damage += damagedEvent.getAmount(); - } - } - - if (damage > 0) { - this.getEffects().setValue("damage", damage); - this.getEffects().setValue("benSoloID", benSoloID); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever Ben Solo is dealt damage, it deals that much damage to target player or planeswalker."; - } -} - -class BenSoloEffect extends OneShotEffect { - - BenSoloEffect() { - super(Outcome.Benefit); - } - - private BenSoloEffect(final BenSoloEffect effect) { - super(effect); - } - - @Override - public BenSoloEffect copy() { - return new BenSoloEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Integer damage = (Integer)getValue("damage"); - UUID benSoloID = (UUID)getValue("benSoloID"); - - if (benSoloID == null || damage == null || damage < 1) { - return false; - } - - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - permanent.damage(damage, benSoloID, source, game); - return true; - } - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - player.damage(damage, benSoloID, source, game); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BloodSpatterAnalysis.java b/Mage.Sets/src/mage/cards/b/BloodSpatterAnalysis.java index 764b63a052d..592cb644a0c 100644 --- a/Mage.Sets/src/mage/cards/b/BloodSpatterAnalysis.java +++ b/Mage.Sets/src/mage/cards/b/BloodSpatterAnalysis.java @@ -85,9 +85,7 @@ class BloodSpatterAnalysisTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeBatchEvent zBatchEvent = (ZoneChangeBatchEvent) event; - - for (ZoneChangeEvent zEvent : zBatchEvent.getEvents()) { + for (ZoneChangeEvent zEvent : ((ZoneChangeBatchEvent) event).getEvents()) { if (zEvent.isDiesEvent()) { Permanent permanent = game.getPermanentOrLKIBattlefield(zEvent.getTargetId()); if (permanent != null && permanent.isCreature(game)) { diff --git a/Mage.Sets/src/mage/cards/b/BreechesBrazenPlunderer.java b/Mage.Sets/src/mage/cards/b/BreechesBrazenPlunderer.java index d4a12de49d9..07cdf8199e3 100644 --- a/Mage.Sets/src/mage/cards/b/BreechesBrazenPlunderer.java +++ b/Mage.Sets/src/mage/cards/b/BreechesBrazenPlunderer.java @@ -9,8 +9,8 @@ import mage.abilities.keyword.PartnerAbility; import mage.cards.*; import mage.constants.*; import mage.game.Game; -import mage.game.events.DamagedEvent; import mage.game.events.DamagedBatchForPlayersEvent; +import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; @@ -72,9 +72,8 @@ class BreechesBrazenPlundererTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - DamagedBatchForPlayersEvent dEvent = (DamagedBatchForPlayersEvent) event; Set opponents = new HashSet<>(); - for (DamagedEvent damagedEvent : dEvent.getEvents()) { + for (DamagedEvent damagedEvent : ((DamagedBatchForPlayersEvent) event).getEvents()) { Permanent permanent = game.getPermanent(damagedEvent.getSourceId()); if (permanent == null || !permanent.isControlledBy(getControllerId()) @@ -84,7 +83,7 @@ class BreechesBrazenPlundererTriggeredAbility extends TriggeredAbilityImpl { } opponents.add(damagedEvent.getTargetId()); } - if (opponents.size() < 1) { + if (opponents.isEmpty()) { return false; } this.getEffects().clear(); @@ -148,4 +147,4 @@ class BreechesBrazenPlundererEffect extends OneShotEffect { } return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/ContestedGameBall.java b/Mage.Sets/src/mage/cards/c/ContestedGameBall.java index 9f1f5ea487b..c9493559c85 100644 --- a/Mage.Sets/src/mage/cards/c/ContestedGameBall.java +++ b/Mage.Sets/src/mage/cards/c/ContestedGameBall.java @@ -22,7 +22,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; -import mage.game.events.DamagedBatchEvent; +import mage.game.events.DamagedBatchForOnePlayerEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.TreasureToken; @@ -84,7 +84,7 @@ class ContestedGameBallTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((DamagedBatchEvent) event).isCombatDamage() && event.getPlayerId().equals(this.getControllerId())) { + if (((DamagedBatchForOnePlayerEvent) event).isCombatDamage() && event.getTargetId().equals(this.getControllerId())) { this.getAllEffects().setTargetPointer(new FixedTarget(game.getActivePlayerId())); // attacking player is active player return true; diff --git a/Mage.Sets/src/mage/cards/c/CourtOfLocthwain.java b/Mage.Sets/src/mage/cards/c/CourtOfLocthwain.java index e3d9bcf489f..cab532de7cb 100644 --- a/Mage.Sets/src/mage/cards/c/CourtOfLocthwain.java +++ b/Mage.Sets/src/mage/cards/c/CourtOfLocthwain.java @@ -89,7 +89,7 @@ class CourtOfLocthwainFirstEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (controller == null || opponent == null || source == null) { + if (controller == null || opponent == null) { return false; } Card card = opponent.getLibrary().getFromTop(game); @@ -220,12 +220,11 @@ class CourtOfLocthwainWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - UUID playerId = event.getPlayerId(); if (event.getType() == GameEvent.EventType.SPELL_CAST && event.hasApprovingIdentifier(MageIdentifier.CourtOfLocthwainWatcher) - && playerId != null) { + && event.getPlayerId() != null) { decrementCastAvailable( - playerId, + event.getPlayerId(), event.getAdditionalReference().getApprovingMageObjectReference() ); } diff --git a/Mage.Sets/src/mage/cards/f/FelineSovereign.java b/Mage.Sets/src/mage/cards/f/FelineSovereign.java index 0eca44ca7f3..a2201d40efa 100644 --- a/Mage.Sets/src/mage/cards/f/FelineSovereign.java +++ b/Mage.Sets/src/mage/cards/f/FelineSovereign.java @@ -89,7 +89,7 @@ class FelineSovereignTriggeredAbility extends DealCombatDamageControlledTriggere } this.getTargets().clear(); FilterArtifactOrEnchantmentPermanent filter = new FilterArtifactOrEnchantmentPermanent(); - filter.add(new ControllerIdPredicate(event.getPlayerId())); + filter.add(new ControllerIdPredicate(event.getTargetId())); this.addTarget(new TargetPermanent(0, 1, filter, false)); return true; } diff --git a/Mage.Sets/src/mage/cards/h/HowlpackAvenger.java b/Mage.Sets/src/mage/cards/h/HowlpackAvenger.java index 7e56790709b..de1d6947b17 100644 --- a/Mage.Sets/src/mage/cards/h/HowlpackAvenger.java +++ b/Mage.Sets/src/mage/cards/h/HowlpackAvenger.java @@ -15,7 +15,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedBatchEvent; +import mage.game.events.DamagedBatchForPermanentsEvent; import mage.game.events.GameEvent; import mage.target.common.TargetAnyTarget; @@ -81,8 +81,7 @@ class HowlpackAvengerTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - DamagedBatchEvent dEvent = (DamagedBatchEvent) event; - int damage = dEvent + int damage = ((DamagedBatchForPermanentsEvent) event) .getEvents() .stream() .filter(damagedEvent -> isControlledBy(game.getControllerId(damagedEvent.getTargetId()))) diff --git a/Mage.Sets/src/mage/cards/k/KayaSpiritsJustice.java b/Mage.Sets/src/mage/cards/k/KayaSpiritsJustice.java index 531853ca51a..ee1be9f3d26 100644 --- a/Mage.Sets/src/mage/cards/k/KayaSpiritsJustice.java +++ b/Mage.Sets/src/mage/cards/k/KayaSpiritsJustice.java @@ -120,9 +120,6 @@ class KayaSpiritsJusticeTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeBatchEvent zEvent = (ZoneChangeBatchEvent) event; - if (zEvent == null) { - return false; - } Set battlefieldCards = zEvent.getEvents() .stream() diff --git a/Mage.Sets/src/mage/cards/m/MagmaticGalleon.java b/Mage.Sets/src/mage/cards/m/MagmaticGalleon.java index 57d3ee069a4..0b1019cd047 100644 --- a/Mage.Sets/src/mage/cards/m/MagmaticGalleon.java +++ b/Mage.Sets/src/mage/cards/m/MagmaticGalleon.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedBatchEvent; +import mage.game.events.DamagedBatchForPermanentsEvent; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -80,8 +80,7 @@ class MagmaticGalleonTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - DamagedBatchEvent dEvent = (DamagedBatchEvent) event; - int damage = dEvent + int damage = ((DamagedBatchForPermanentsEvent) event) .getEvents() .stream() .filter(damagedEvent -> { diff --git a/Mage.Sets/src/mage/cards/o/ObNixilisCaptiveKingpin.java b/Mage.Sets/src/mage/cards/o/ObNixilisCaptiveKingpin.java index 2d0915b14de..13c3eaff2e5 100644 --- a/Mage.Sets/src/mage/cards/o/ObNixilisCaptiveKingpin.java +++ b/Mage.Sets/src/mage/cards/o/ObNixilisCaptiveKingpin.java @@ -82,15 +82,15 @@ class ObNixilisCaptiveKingpinAbility extends TriggeredAbilityImpl { boolean opponentLostLife = false; boolean allis1 = true; - for (UUID targetPlayer : CardUtil.getEventTargets(lifeLostBatchEvent)){ + for (UUID targetPlayer : CardUtil.getEventTargets(lifeLostBatchEvent)) { // skip controller - if (targetPlayer.equals(getControllerId())){ + if (targetPlayer.equals(getControllerId())) { continue; } opponentLostLife = true; - int lifelost = lifeLostBatchEvent.getLifeLostByPlayer(targetPlayer); - if (lifelost != 1){ + int lifeLost = lifeLostBatchEvent.getLifeLostByPlayer(targetPlayer); + if (lifeLost != 1) { allis1 = false; break; } diff --git a/Mage.Sets/src/mage/cards/o/OliviasAttendants.java b/Mage.Sets/src/mage/cards/o/OliviasAttendants.java index 71fcee001dd..294986114fb 100644 --- a/Mage.Sets/src/mage/cards/o/OliviasAttendants.java +++ b/Mage.Sets/src/mage/cards/o/OliviasAttendants.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedBatchEvent; +import mage.game.events.DamagedBatchAllEvent; import mage.game.events.GameEvent; import mage.game.permanent.token.BloodToken; import mage.target.common.TargetAnyTarget; @@ -72,13 +72,12 @@ class OliviasAttendantsTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_PLAYERS - || event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_PERMANENTS; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ALL; } @Override public boolean checkTrigger(GameEvent event, Game game) { - int amount = ((DamagedBatchEvent) event) + int amount = ((DamagedBatchAllEvent) event) .getEvents() .stream() .filter(e -> e.getAttackerId().equals(this.getSourceId())) diff --git a/Mage.Sets/src/mage/cards/r/RisonaAsariCommander.java b/Mage.Sets/src/mage/cards/r/RisonaAsariCommander.java index f1b2debb6fb..910cfe40ec0 100644 --- a/Mage.Sets/src/mage/cards/r/RisonaAsariCommander.java +++ b/Mage.Sets/src/mage/cards/r/RisonaAsariCommander.java @@ -18,7 +18,7 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; -import mage.game.events.DamagedBatchEvent; +import mage.game.events.DamagedBatchForPlayersEvent; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -85,19 +85,11 @@ class RisonaAsariCommanderTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (controllerId == null) { - return false; - } - if (!(event instanceof DamagedBatchEvent)) { - return false; - } - DamagedBatchEvent batchEvent = (DamagedBatchEvent) event; - for (DamagedEvent damageEvent : batchEvent.getEvents()) { - if (damageEvent.isCombatDamage() && controllerId.equals(damageEvent.getTargetId())) { - return true; - } - } - return false; + return ((DamagedBatchForPlayersEvent) event) + .getEvents() + .stream() + .filter(DamagedEvent::isCombatDamage) + .anyMatch(e -> e.getTargetId().equals(getControllerId())); } } diff --git a/Mage.Sets/src/mage/cards/w/WaytaTrainerProdigy.java b/Mage.Sets/src/mage/cards/w/WaytaTrainerProdigy.java index 14226112377..7101a6ad3f2 100644 --- a/Mage.Sets/src/mage/cards/w/WaytaTrainerProdigy.java +++ b/Mage.Sets/src/mage/cards/w/WaytaTrainerProdigy.java @@ -1,9 +1,5 @@ package mage.cards.w; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.Objects; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -14,13 +10,12 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.FightTargetsEffect; import mage.abilities.effects.common.InfoEffect; -import mage.constants.*; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.BatchGameEvent; import mage.game.events.GameEvent; import mage.game.events.NumberOfTriggersEvent; import mage.game.permanent.Permanent; @@ -29,6 +24,9 @@ import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; import mage.util.CardUtil; +import java.util.Objects; +import java.util.UUID; + /** * @@ -158,35 +156,27 @@ class WaytaTrainerProdigyEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - - NumberOfTriggersEvent numberOfTriggersEvent = (NumberOfTriggersEvent) event; - // Permanent whose ability is being triggered (and will be retriggered) - Permanent triggeredPermanent = game.getPermanentOrLKIBattlefield(numberOfTriggersEvent.getSourceId()); + Permanent triggeredPermanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); if (triggeredPermanent == null || !triggeredPermanent.isControlledBy(source.getControllerId())) { return false; } - - GameEvent sourceEvent = numberOfTriggersEvent.getSourceEvent(); + GameEvent sourceEvent = ((NumberOfTriggersEvent) event).getSourceEvent(); if (sourceEvent == null) { return false; } - - if (!(sourceEvent.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_PERMANENTS - || sourceEvent.getType() == GameEvent.EventType.DAMAGED_PERMANENT)){ - return false; + switch (sourceEvent.getType()) { + case DAMAGED_PERMANENT: + case DAMAGED_BATCH_FOR_ONE_PERMANENT: + case DAMAGED_BATCH_FOR_PERMANENTS: + case DAMAGED_BATCH_FOR_ALL: + break; + default: + return false; } - // Get the one or more permanents damaged in this event - List damagedPermanents = new ArrayList<>(); - if (sourceEvent instanceof BatchGameEvent) { - damagedPermanents.addAll(((BatchGameEvent) sourceEvent).getTargets()); - } else { - damagedPermanents.add(sourceEvent.getTargetId()); - } - // If none of them are controlled by you, this ability doesn't apply. - return damagedPermanents + return CardUtil.getEventTargets(sourceEvent) .stream() .map(game::getPermanentOrLKIBattlefield) .filter(Objects::nonNull) diff --git a/Mage.Sets/src/mage/cards/w/WrathfulRaptors.java b/Mage.Sets/src/mage/cards/w/WrathfulRaptors.java index e65a5a5c3e7..f994d91f537 100644 --- a/Mage.Sets/src/mage/cards/w/WrathfulRaptors.java +++ b/Mage.Sets/src/mage/cards/w/WrathfulRaptors.java @@ -20,7 +20,6 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetPermanentOrPlayer; -import mage.util.CardUtil; /** * @author arcox @@ -81,15 +80,8 @@ class WrathfulRaptorsTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - - UUID dinosaurUUID = CardUtil.getEventTargets(event) - .stream() - .findFirst() - .orElse(null); - - Permanent dinosaur = game.getPermanent(dinosaurUUID); + Permanent dinosaur = game.getPermanent(event.getTargetId()); int damage = event.getAmount(); - if (dinosaur == null || damage < 1 || !dinosaur.isControlledBy(getControllerId()) || !dinosaur.hasSubtype(SubType.DINOSAUR, game)) { diff --git a/Mage.Sets/src/mage/cards/z/ZurgoAndOjutai.java b/Mage.Sets/src/mage/cards/z/ZurgoAndOjutai.java index a1d54af0cb7..e033dbe1a28 100644 --- a/Mage.Sets/src/mage/cards/z/ZurgoAndOjutai.java +++ b/Mage.Sets/src/mage/cards/z/ZurgoAndOjutai.java @@ -20,7 +20,7 @@ import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.PermanentIdPredicate; import mage.game.Game; -import mage.game.events.DamagedBatchEvent; +import mage.game.events.DamagedBatchAllEvent; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -92,14 +92,12 @@ class ZurgoAndOjutaiTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_PLAYERS - || event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_PERMANENTS; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ALL; } @Override public boolean checkTrigger(GameEvent event, Game game) { - DamagedBatchEvent dEvent = (DamagedBatchEvent) event; - List permanents = dEvent + List permanents = ((DamagedBatchAllEvent) event) .getEvents() .stream() .filter(DamagedEvent::isCombatDamage) diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTappedOneOrMoreTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTappedOneOrMoreTriggeredAbility.java index 8ca7082e7c6..7b96abf685c 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTappedOneOrMoreTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTappedOneOrMoreTriggeredAbility.java @@ -40,7 +40,7 @@ public class BecomesTappedOneOrMoreTriggeredAbility extends TriggeredAbilityImpl public boolean checkTrigger(GameEvent event, Game game) { TappedBatchEvent batchEvent = (TappedBatchEvent) event; return batchEvent - .getTargets() + .getTargetIds() .stream() .map(game::getPermanent) .anyMatch(p -> filter.match(p, getControllerId(), this, game)); diff --git a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageEquippedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageEquippedTriggeredAbility.java index 82877928a76..9620d538d1c 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageEquippedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageEquippedTriggeredAbility.java @@ -4,7 +4,7 @@ import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedBatchEvent; +import mage.game.events.DamagedBatchAllEvent; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -14,21 +14,17 @@ import mage.game.permanent.Permanent; */ public class DealsCombatDamageEquippedTriggeredAbility extends TriggeredAbilityImpl { - private boolean usedThisStep; - public DealsCombatDamageEquippedTriggeredAbility(Effect effect) { this(effect, false); } public DealsCombatDamageEquippedTriggeredAbility(Effect effect, boolean optional) { super(Zone.BATTLEFIELD, effect, optional); - this.usedThisStep = false; setTriggerPhrase("Whenever equipped creature deals combat damage, "); } protected DealsCombatDamageEquippedTriggeredAbility(final DealsCombatDamageEquippedTriggeredAbility ability) { super(ability); - this.usedThisStep = ability.usedThisStep; } @Override @@ -38,23 +34,16 @@ public class DealsCombatDamageEquippedTriggeredAbility extends TriggeredAbilityI @Override public boolean checkEventType(GameEvent event, Game game) { - return event instanceof DamagedBatchEvent || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_PRE; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ALL; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_PRE) { - usedThisStep = false; // clear before damage - return false; - } - if (usedThisStep || !(event instanceof DamagedBatchEvent)) { - return false; // trigger only on DamagedEvent and if not yet triggered this step - } Permanent sourcePermanent = getSourcePermanentOrLKI(game); if (sourcePermanent == null || sourcePermanent.getAttachedTo() == null) { return false; } - int amount = ((DamagedBatchEvent) event) + int amount = ((DamagedBatchAllEvent) event) .getEvents() .stream() .filter(DamagedEvent::isCombatDamage) @@ -64,11 +53,7 @@ public class DealsCombatDamageEquippedTriggeredAbility extends TriggeredAbilityI if (amount < 1) { return false; } - usedThisStep = true; this.getEffects().setValue("damage", amount); - // TODO: this value saved will not be correct if both permanent and player damaged by a single creature - // Need to rework engine logic to fire a single DamagedBatchEvent including both permanents and players - // Only Sword of Hours is currently affected. return true; } } diff --git a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageTriggeredAbility.java index d59c7a6bf50..ea1be8fa952 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageTriggeredAbility.java @@ -4,7 +4,7 @@ import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedBatchEvent; +import mage.game.events.DamagedBatchAllEvent; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; @@ -17,18 +17,14 @@ import mage.game.events.GameEvent; */ public class DealsCombatDamageTriggeredAbility extends TriggeredAbilityImpl { - private boolean usedThisStep; - public DealsCombatDamageTriggeredAbility(Effect effect, boolean optional) { super(Zone.BATTLEFIELD, effect, optional); - this.usedThisStep = false; setTriggerPhrase(getWhen() + "{this} deals combat damage, "); this.replaceRuleText = true; } protected DealsCombatDamageTriggeredAbility(final DealsCombatDamageTriggeredAbility ability) { super(ability); - this.usedThisStep = ability.usedThisStep; } @Override @@ -38,33 +34,22 @@ public class DealsCombatDamageTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event instanceof DamagedBatchEvent || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_PRE; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ALL; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_PRE) { - usedThisStep = false; // clear before damage - return false; - } - if (usedThisStep || !(event instanceof DamagedBatchEvent)) { - return false; // trigger only on DamagedEvent and if not yet triggered this step - } - int amount = ((DamagedBatchEvent) event) + int amount = ((DamagedBatchAllEvent) event) .getEvents() .stream() .filter(DamagedEvent::isCombatDamage) - .filter(e -> e.getAttackerId().equals(this.sourceId)) + .filter(e -> e.getAttackerId().equals(getSourceId())) .mapToInt(GameEvent::getAmount) .sum(); if (amount < 1) { return false; } - usedThisStep = true; this.getEffects().setValue("damage", amount); - // TODO: this value saved will not be correct if both permanent and player damaged by a single creature - // Need to rework engine logic to fire a single DamagedBatchEvent including both permanents and players - // Only Aisha of Sparks and Smoke is currently affected. return true; } } diff --git a/Mage/src/main/java/mage/abilities/common/OneOrMoreDealDamageTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/OneOrMoreDealDamageTriggeredAbility.java index ecfb902c84f..c7cf17da6ed 100644 --- a/Mage/src/main/java/mage/abilities/common/OneOrMoreDealDamageTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/OneOrMoreDealDamageTriggeredAbility.java @@ -6,7 +6,7 @@ import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.game.Game; -import mage.game.events.DamagedBatchEvent; +import mage.game.events.DamagedBatchForOnePlayerEvent; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -60,7 +60,7 @@ public class OneOrMoreDealDamageTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - DamagedBatchEvent dEvent = (DamagedBatchEvent) event; + DamagedBatchForOnePlayerEvent dEvent = (DamagedBatchForOnePlayerEvent) event; if (onlyCombat && !dEvent.isCombatDamage()) { return false; } @@ -86,7 +86,7 @@ public class OneOrMoreDealDamageTriggeredAbility extends TriggeredAbilityImpl { this.getAllEffects().setValue("damage", events.stream().mapToInt(DamagedEvent::getAmount).sum()); switch (setTargetPointer) { case PLAYER: - this.getAllEffects().setTargetPointer(new FixedTarget(event.getPlayerId())); + this.getAllEffects().setTargetPointer(new FixedTarget(event.getTargetId())); break; case NONE: break; diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index d6200769503..ee6dd0abef7 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -808,7 +808,80 @@ public class GameState implements Serializable, Copyable { return !simultaneousEvents.isEmpty(); } - public void addSimultaneousLifeLossEventToBatches(LifeLostEvent lifeLossEvent, Game game) { + public void addSimultaneousDamage(DamagedEvent damagedEvent, Game game) { + // Combine multiple damage events in the single event (batch) + // Note: one event can be stored in multiple batches + if (damagedEvent instanceof DamagedPlayerEvent) { + // DAMAGED_BATCH_FOR_PLAYERS + DAMAGED_BATCH_FOR_ONE_PLAYER + addSimultaneousDamageToPlayerBatches((DamagedPlayerEvent) damagedEvent, game); + } else if (damagedEvent instanceof DamagedPermanentEvent) { + // DAMAGED_BATCH_FOR_PERMANENTS + DAMAGED_BATCH_FOR_ONE_PERMANENT + addSimultaneousDamageToPermanentBatches((DamagedPermanentEvent) damagedEvent, game); + } + // DAMAGED_BATCH_FOR_ALL + addSimultaneousDamageToBatchForAll(damagedEvent, game); + } + + public void addSimultaneousDamageToPlayerBatches(DamagedPlayerEvent damagedPlayerEvent, Game game) { + // find existing batches first + boolean isTotalBatchUsed = false; + boolean isPlayerBatchUsed = false; + for (GameEvent event : simultaneousEvents) { + if (event instanceof DamagedBatchForPlayersEvent) { + ((DamagedBatchForPlayersEvent) event).addEvent(damagedPlayerEvent); + isTotalBatchUsed = true; + } else if (event instanceof DamagedBatchForOnePlayerEvent + && damagedPlayerEvent.getTargetId().equals(event.getTargetId())) { + ((DamagedBatchForOnePlayerEvent) event).addEvent(damagedPlayerEvent); + isPlayerBatchUsed = true; + } + } + // new batches if necessary + if (!isTotalBatchUsed) { + addSimultaneousEvent(new DamagedBatchForPlayersEvent(damagedPlayerEvent), game); + } + if (!isPlayerBatchUsed) { + addSimultaneousEvent(new DamagedBatchForOnePlayerEvent(damagedPlayerEvent), game); + } + } + + public void addSimultaneousDamageToPermanentBatches(DamagedPermanentEvent damagedPermanentEvent, Game game) { + // find existing batches first + boolean isTotalBatchUsed = false; + boolean isSingleBatchUsed = false; + for (GameEvent event : simultaneousEvents) { + if (event instanceof DamagedBatchForPermanentsEvent) { + ((DamagedBatchForPermanentsEvent) event).addEvent(damagedPermanentEvent); + isTotalBatchUsed = true; + } else if (event instanceof DamagedBatchForOnePermanentEvent + && damagedPermanentEvent.getTargetId().equals(event.getTargetId())) { + ((DamagedBatchForOnePermanentEvent) event).addEvent(damagedPermanentEvent); + isSingleBatchUsed = true; + } + } + // new batches if necessary + if (!isTotalBatchUsed) { + addSimultaneousEvent(new DamagedBatchForPermanentsEvent(damagedPermanentEvent), game); + } + if (!isSingleBatchUsed) { + addSimultaneousEvent(new DamagedBatchForOnePermanentEvent(damagedPermanentEvent), game); + } + } + + public void addSimultaneousDamageToBatchForAll(DamagedEvent damagedEvent, Game game) { + boolean isBatchUsed = false; + for (GameEvent event : simultaneousEvents) { + if (event instanceof DamagedBatchAllEvent) { + ((DamagedBatchAllEvent) event).addEvent(damagedEvent); + isBatchUsed = true; + } + } + if (!isBatchUsed) { + addSimultaneousEvent(new DamagedBatchAllEvent(damagedEvent), game); + } + } + + public void addSimultaneousLifeLossToBatch(LifeLostEvent lifeLossEvent, Game game) { // Combine multiple life loss events in the single event (batch) // see GameEvent.LOST_LIFE_BATCH @@ -827,69 +900,7 @@ public class GameState implements Serializable, Copyable { } } - public void addSimultaneousDamage(DamagedEvent damagedEvent, Game game) { - // 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) { - // per player - DamagedBatchForOnePlayerEvent oldPlayerBatch = (DamagedBatchForOnePlayerEvent) event; - if (oldPlayerBatch.getDamageClazz().isInstance(damagedEvent) - && event.getPlayerId().equals(damagedEvent.getTargetId())) { - 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 - // If the batch event isn't DAMAGED_BATCH_FOR_ONE_PLAYER, the targetIDs need not match, - // since "event" is a generic batch in this case - // (either DAMAGED_BATCH_FOR_PERMANENTS or DAMAGED_BATCH_FOR_PLAYERS) - // Just needs to be a permanent-damaging event for DAMAGED_BATCH_FOR_PERMANENTS, - // or a player-damaging event for DAMAGED_BATCH_FOR_PLAYERS - ((DamagedBatchEvent) event).addEvent(damagedEvent); - isDamageBatchUsed = true; - } - - } - - // new batch - if (!isDamageBatchUsed) { - addSimultaneousEvent(DamagedBatchEvent.makeEvent(damagedEvent), game); - } - if (!isPlayerBatchUsed && isPlayerDamage) { - DamagedBatchEvent event = new DamagedBatchForOnePlayerEvent(damagedEvent); - addSimultaneousEvent(event, game); - } - if (!isPermanentBatchUsed && isPermanentDamage) { - DamagedBatchEvent event = new DamagedBatchForOnePermanentEvent(damagedEvent); - addSimultaneousEvent(event, game); - } - } - - public void addSimultaneousTapped(TappedEvent tappedEvent, Game game) { + public void addSimultaneousTappedToBatch(TappedEvent tappedEvent, Game game) { // Combine multiple tapped events in the single event (batch) boolean isTappedBatchUsed = false; @@ -904,13 +915,11 @@ public class GameState implements Serializable, Copyable { // new batch if (!isTappedBatchUsed) { - TappedBatchEvent batch = new TappedBatchEvent(); - batch.addEvent(tappedEvent); - addSimultaneousEvent(batch, game); + addSimultaneousEvent(new TappedBatchEvent(tappedEvent), game); } } - public void addSimultaneousUntapped(UntappedEvent untappedEvent, Game game) { + public void addSimultaneousUntappedToBatch(UntappedEvent untappedEvent, Game game) { // Combine multiple untapped events in the single event (batch) boolean isUntappedBatchUsed = false; @@ -925,9 +934,7 @@ public class GameState implements Serializable, Copyable { // new batch if (!isUntappedBatchUsed) { - UntappedBatchEvent batch = new UntappedBatchEvent(); - batch.addEvent(untappedEvent); - addSimultaneousEvent(batch, game); + addSimultaneousEvent(new UntappedBatchEvent(untappedEvent), game); } } diff --git a/Mage/src/main/java/mage/game/events/BatchEvent.java b/Mage/src/main/java/mage/game/events/BatchEvent.java new file mode 100644 index 00000000000..65c005c9568 --- /dev/null +++ b/Mage/src/main/java/mage/game/events/BatchEvent.java @@ -0,0 +1,101 @@ +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; + +/** + * Special events created by game engine to track batches of events that occur simultaneously, + * for triggers that need such information + * @author xenohedron + */ +public abstract class BatchEvent extends GameEvent { + + private final Set events = new HashSet<>(); + private final boolean singleTargetId; + + /** + * @param eventType specific type of event + * @param singleTargetId if true, all included events must have same target id + * @param firstEvent added to initialize the batch (batch is never empty) + */ + protected BatchEvent(EventType eventType, boolean singleTargetId, T firstEvent) { + super(eventType, (singleTargetId ? firstEvent.getTargetId() : null), null, null); + this.singleTargetId = singleTargetId; + if (firstEvent instanceof BatchEvent) { // sanity check, if you need it then think twice and research carefully + throw new UnsupportedOperationException("Wrong code usage: nesting batch events not supported"); + } + this.addEvent(firstEvent); + } + + /** + * For alternate event structure logic used by ZoneChangeBatchEvent, list of events starts empty. + */ + protected BatchEvent(EventType eventType) { + super(eventType, null, null, null); + this.singleTargetId = false; + } + + public void addEvent(T event) { + if (singleTargetId && !getTargetId().equals(event.getTargetId())) { + throw new IllegalStateException("Wrong code usage. Batch event initiated with single target id, but trying to add event with different target id"); + } + this.events.add(event); + } + + public Set getEvents() { + return events; + } + + public Set getTargetIds() { + return events.stream() + .map(GameEvent::getTargetId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + public Set getSourceIds() { + return events.stream() + .map(GameEvent::getSourceId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + public Set getPlayerIds() { + return events.stream() + .map(GameEvent::getPlayerId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + @Override + public int getAmount() { + return events + .stream() + .mapToInt(GameEvent::getAmount) + .sum(); + } + + @Override // events can store a diff value, so search it from events list instead + public UUID getTargetId() { + if (singleTargetId) { + return super.getTargetId(); + } + throw new IllegalStateException("Wrong code usage. Must search value from a getEvents list or use CardUtil.getEventTargets(event)"); + } + + @Override // events can store a diff value, so search it from events list instead + @Deprecated // no use case currently supported + public UUID getSourceId() { + throw new IllegalStateException("Wrong code usage. Must search value from a getEvents list"); + } + + @Override // events can store a diff value, so search it from events list instead + @Deprecated // no use case currently supported + public UUID getPlayerId() { + throw new IllegalStateException("Wrong code usage. Must search value from a getEvents list"); + } + +} diff --git a/Mage/src/main/java/mage/game/events/BatchGameEvent.java b/Mage/src/main/java/mage/game/events/BatchGameEvent.java deleted file mode 100644 index e41e67a30bd..00000000000 --- a/Mage/src/main/java/mage/game/events/BatchGameEvent.java +++ /dev/null @@ -1,19 +0,0 @@ -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/DamagedBatchAllEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchAllEvent.java new file mode 100644 index 00000000000..f57161ea7c3 --- /dev/null +++ b/Mage/src/main/java/mage/game/events/DamagedBatchAllEvent.java @@ -0,0 +1,11 @@ +package mage.game.events; + +/** + * @author xenohedron + */ +public class DamagedBatchAllEvent extends BatchEvent { + + public DamagedBatchAllEvent(DamagedEvent firstEvent) { + super(EventType.DAMAGED_BATCH_FOR_ALL, false, firstEvent); + } +} diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java deleted file mode 100644 index 5ecdee7e036..00000000000 --- a/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java +++ /dev/null @@ -1,78 +0,0 @@ -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 implements BatchGameEvent { - - private final Class damageClazz; - private final Set events = new HashSet<>(); - - protected DamagedBatchEvent(EventType type, Class damageClazz) { - super(type, null, null, null); - 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 - .stream() - .mapToInt(GameEvent::getAmount) - .sum(); - } - - public boolean isCombatDamage() { - return events.stream().anyMatch(DamagedEvent::isCombatDamage); - } - - @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 or use CardUtil.getEventTargets(event)"); - } - - @Override - @Deprecated // events can store a diff value, so search it from events list instead - public UUID getSourceId() { - throw new IllegalStateException("Wrong code usage. Must search value from a getEvents list."); - } - - public void addEvent(DamagedEvent event) { - this.events.add(event); - } - - public Class getDamageClazz() { - return damageClazz; - } - - public static DamagedBatchEvent makeEvent(DamagedEvent damagedEvent) { - DamagedBatchEvent event; - if (damagedEvent instanceof DamagedPlayerEvent) { - event = new DamagedBatchForPlayersEvent(damagedEvent); - } else if (damagedEvent instanceof DamagedPermanentEvent) { - event = new DamagedBatchForPermanentsEvent(damagedEvent); - } else { - throw new IllegalArgumentException("Wrong code usage. Unknown damage event for a new batch: " + damagedEvent.getClass().getName()); - } - return event; - } -} diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchForOnePermanentEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchForOnePermanentEvent.java index 68ef57b5e06..007afd6fcf1 100644 --- a/Mage/src/main/java/mage/game/events/DamagedBatchForOnePermanentEvent.java +++ b/Mage/src/main/java/mage/game/events/DamagedBatchForOnePermanentEvent.java @@ -1,10 +1,8 @@ package mage.game.events; -public class DamagedBatchForOnePermanentEvent extends DamagedBatchEvent { +public class DamagedBatchForOnePermanentEvent extends BatchEvent { - public DamagedBatchForOnePermanentEvent(DamagedEvent firstEvent) { - super(GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT, DamagedPermanentEvent.class); - addEvent(firstEvent); - setTargetId(firstEvent.getTargetId()); + public DamagedBatchForOnePermanentEvent(DamagedPermanentEvent firstEvent) { + super(GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT, true, firstEvent); } } diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchForOnePlayerEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchForOnePlayerEvent.java index bedc02423a6..121120f804d 100644 --- a/Mage/src/main/java/mage/game/events/DamagedBatchForOnePlayerEvent.java +++ b/Mage/src/main/java/mage/game/events/DamagedBatchForOnePlayerEvent.java @@ -3,11 +3,15 @@ package mage.game.events; /** * @author Susucr */ -public class DamagedBatchForOnePlayerEvent extends DamagedBatchEvent { +public class DamagedBatchForOnePlayerEvent extends BatchEvent { - public DamagedBatchForOnePlayerEvent(DamagedEvent firstEvent) { - super(EventType.DAMAGED_BATCH_FOR_ONE_PLAYER, DamagedPlayerEvent.class); - setPlayerId(firstEvent.getPlayerId()); - addEvent(firstEvent); + public DamagedBatchForOnePlayerEvent(DamagedPlayerEvent firstEvent) { + super(EventType.DAMAGED_BATCH_FOR_ONE_PLAYER, true, firstEvent); + } + + public boolean isCombatDamage() { + return getEvents() + .stream() + .anyMatch(DamagedEvent::isCombatDamage); } } diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchForPermanentsEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchForPermanentsEvent.java index aa5b2b9a6bc..4f7b1f6a776 100644 --- a/Mage/src/main/java/mage/game/events/DamagedBatchForPermanentsEvent.java +++ b/Mage/src/main/java/mage/game/events/DamagedBatchForPermanentsEvent.java @@ -3,10 +3,9 @@ package mage.game.events; /** * @author TheElk801 */ -public class DamagedBatchForPermanentsEvent extends DamagedBatchEvent { +public class DamagedBatchForPermanentsEvent extends BatchEvent { - public DamagedBatchForPermanentsEvent(DamagedEvent firstEvent) { - super(EventType.DAMAGED_BATCH_FOR_PERMANENTS, DamagedPermanentEvent.class); - addEvent(firstEvent); + public DamagedBatchForPermanentsEvent(DamagedPermanentEvent firstEvent) { + super(EventType.DAMAGED_BATCH_FOR_PERMANENTS, false, firstEvent); } } diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchForPlayersEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchForPlayersEvent.java index d5fbaeea10e..172a4bcf6d1 100644 --- a/Mage/src/main/java/mage/game/events/DamagedBatchForPlayersEvent.java +++ b/Mage/src/main/java/mage/game/events/DamagedBatchForPlayersEvent.java @@ -3,10 +3,9 @@ package mage.game.events; /** * @author TheElk801 */ -public class DamagedBatchForPlayersEvent extends DamagedBatchEvent { +public class DamagedBatchForPlayersEvent extends BatchEvent { - public DamagedBatchForPlayersEvent(DamagedEvent firstEvent) { - super(GameEvent.EventType.DAMAGED_BATCH_FOR_PLAYERS, DamagedPlayerEvent.class); - addEvent(firstEvent); + public DamagedBatchForPlayersEvent(DamagedPlayerEvent firstEvent) { + super(GameEvent.EventType.DAMAGED_BATCH_FOR_PLAYERS, false, firstEvent); } } diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 2ec2a860c5d..a7e6e65efa3 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -120,10 +120,15 @@ public class GameEvent implements Serializable { /* DAMAGED_BATCH_FOR_ONE_PLAYER combines all player damage events to a single batch (event) and split it per damaged player - playerId the id of the damaged player + targetId the id of the damaged player (playerId won't work for batch) */ DAMAGED_BATCH_FOR_ONE_PLAYER, + /* DAMAGED_BATCH_FOR_ALL + includes all damage events, both permanent damage and player damage, in single batch event + */ + DAMAGED_BATCH_FOR_ALL, + /* DAMAGE_CAUSES_LIFE_LOSS, targetId the id of the damaged player sourceId sourceId of the ability which caused the damage, can be null for default events like combat diff --git a/Mage/src/main/java/mage/game/events/LifeLostBatchEvent.java b/Mage/src/main/java/mage/game/events/LifeLostBatchEvent.java index 2ec2fc41b71..bed29be5456 100644 --- a/Mage/src/main/java/mage/game/events/LifeLostBatchEvent.java +++ b/Mage/src/main/java/mage/game/events/LifeLostBatchEvent.java @@ -1,69 +1,22 @@ 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 jimga150 */ -public class LifeLostBatchEvent extends GameEvent implements BatchGameEvent { +public class LifeLostBatchEvent extends BatchEvent { - private final Set events = new HashSet<>(); - - public LifeLostBatchEvent(LifeLostEvent event) { - super(EventType.LOST_LIFE_BATCH, null, null, null); - addEvent(event); - } - - @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 - .stream() - .mapToInt(GameEvent::getAmount) - .sum(); + public LifeLostBatchEvent(LifeLostEvent firstEvent) { + super(EventType.LOST_LIFE_BATCH, false, firstEvent); } public int getLifeLostByPlayer(UUID playerID) { - return events + return getEvents() .stream() .filter(ev -> ev.getTargetId().equals(playerID)) .mapToInt(GameEvent::getAmount) .sum(); } - public boolean isLifeLostByCombatDamage() { - return events.stream().anyMatch(LifeLostEvent::isLifeLostByCombatDamage); - } - - @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 or use CardUtil.getEventTargets(event)"); - } - - @Override - @Deprecated // events can store a diff value, so search it from events list instead - public UUID getSourceId() { - throw new IllegalStateException("Wrong code usage. Must search value from a getEvents list."); - } - - public void addEvent(LifeLostEvent event) { - this.events.add(event); - } } diff --git a/Mage/src/main/java/mage/game/events/LifeLostEvent.java b/Mage/src/main/java/mage/game/events/LifeLostEvent.java index e46bc09100b..c8f5e4c860b 100644 --- a/Mage/src/main/java/mage/game/events/LifeLostEvent.java +++ b/Mage/src/main/java/mage/game/events/LifeLostEvent.java @@ -7,13 +7,10 @@ import java.util.UUID; /** * @author jimga150 */ -public class LifeLostEvent extends GameEvent{ +public class LifeLostEvent extends GameEvent { + public LifeLostEvent(UUID playerId, Ability source, int amount, boolean atCombat){ - super(GameEvent.EventType.LOST_LIFE, - playerId, source, playerId, amount, atCombat); + super(GameEvent.EventType.LOST_LIFE, playerId, source, playerId, amount, atCombat); } - public boolean isLifeLostByCombatDamage() { - return flag; - } } diff --git a/Mage/src/main/java/mage/game/events/TappedBatchEvent.java b/Mage/src/main/java/mage/game/events/TappedBatchEvent.java index ff7a253284e..18d82863927 100644 --- a/Mage/src/main/java/mage/game/events/TappedBatchEvent.java +++ b/Mage/src/main/java/mage/game/events/TappedBatchEvent.java @@ -1,56 +1,12 @@ 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 Susucr */ -public class TappedBatchEvent extends GameEvent implements BatchGameEvent { +public class TappedBatchEvent extends BatchEvent { - private final Set events = new HashSet<>(); - - public TappedBatchEvent() { - super(EventType.TAPPED_BATCH, null, null, null); + public TappedBatchEvent(TappedEvent firstEvent) { + super(EventType.TAPPED_BATCH, false, firstEvent); } - @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 - .stream() - .mapToInt(GameEvent::getAmount) - .sum(); - } - - @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 or use CardUtil.getEventTargets(event)"); - } - - @Override - @Deprecated // events can store a diff value, so search it from events list instead - public UUID getSourceId() { - throw new IllegalStateException("Wrong code usage. Must search value from a getEvents list."); - } - - public void addEvent(TappedEvent event) { - this.events.add(event); - } } diff --git a/Mage/src/main/java/mage/game/events/UntappedBatchEvent.java b/Mage/src/main/java/mage/game/events/UntappedBatchEvent.java index 8fbd1f9975d..65387957ec5 100644 --- a/Mage/src/main/java/mage/game/events/UntappedBatchEvent.java +++ b/Mage/src/main/java/mage/game/events/UntappedBatchEvent.java @@ -1,56 +1,12 @@ 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 Susucr */ -public class UntappedBatchEvent extends GameEvent implements BatchGameEvent { +public class UntappedBatchEvent extends BatchEvent { - private final Set events = new HashSet<>(); - - public UntappedBatchEvent() { - super(EventType.UNTAPPED_BATCH, null, null, null); + public UntappedBatchEvent(UntappedEvent firstEvent) { + super(EventType.UNTAPPED_BATCH, false, firstEvent); } - @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 - .stream() - .mapToInt(GameEvent::getAmount) - .sum(); - } - - @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 or use CardUtil.getEventTargets(event)"); - } - - @Override - @Deprecated // events can store a diff value, so search it from events list instead - public UUID getSourceId() { - throw new IllegalStateException("Wrong code usage. Must search value from a getEvents list."); - } - - public void addEvent(UntappedEvent event) { - this.events.add(event); - } } diff --git a/Mage/src/main/java/mage/game/events/UntappedEvent.java b/Mage/src/main/java/mage/game/events/UntappedEvent.java index d279cde56bc..03b3dfcbc5f 100644 --- a/Mage/src/main/java/mage/game/events/UntappedEvent.java +++ b/Mage/src/main/java/mage/game/events/UntappedEvent.java @@ -6,6 +6,7 @@ import java.util.UUID; * @author Susucr */ public class UntappedEvent extends GameEvent { + public UntappedEvent(UUID targetId, UUID playerId, boolean duringUntapPhase) { super(EventType.UNTAPPED, targetId, null, playerId, 0, duringUntapPhase); } diff --git a/Mage/src/main/java/mage/game/events/ZoneChangeBatchEvent.java b/Mage/src/main/java/mage/game/events/ZoneChangeBatchEvent.java index da2b0bae48a..3e17972b2d6 100644 --- a/Mage/src/main/java/mage/game/events/ZoneChangeBatchEvent.java +++ b/Mage/src/main/java/mage/game/events/ZoneChangeBatchEvent.java @@ -1,54 +1,8 @@ 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; - -public class ZoneChangeBatchEvent extends GameEvent implements BatchGameEvent { - - private final Set events = new HashSet<>(); +public class ZoneChangeBatchEvent extends BatchEvent { public ZoneChangeBatchEvent() { - super(EventType.ZONE_CHANGE_BATCH, null, null, null); - } - - @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 - .stream() - .mapToInt(GameEvent::getAmount) - .sum(); - } - - @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 or use CardUtil.getEventTargets(event)"); - } - - @Override - @Deprecated // events can store a diff value, so search it from events list instead - public UUID getSourceId() { - throw new IllegalStateException("Wrong code usage. Must search value from a getEvents list."); - } - - public void addEvent(ZoneChangeEvent event) { - this.events.add(event); + super(EventType.ZONE_CHANGE_BATCH); } } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 5d77012f736..4c880619af0 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -599,7 +599,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { game.getTurnStepType() == PhaseStep.UNTAP ); game.fireEvent(event); - game.getState().addSimultaneousUntapped(event, game); + game.getState().addSimultaneousUntappedToBatch(event, game); return true; } return false; @@ -617,7 +617,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { this.tapped = true; TappedEvent event = new TappedEvent(objectId, source, source == null ? null : source.getControllerId(), forCombat); game.fireEvent(event); - game.getState().addSimultaneousTapped(event, game); + game.getState().addSimultaneousTappedToBatch(event, game); return true; } return false; diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 451bca12037..ea49f9e1350 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2177,7 +2177,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (event.getAmount() > 0) { LifeLostEvent lifeLostEvent = new LifeLostEvent(playerId, source, event.getAmount(), atCombat); game.fireEvent(lifeLostEvent); - game.getState().addSimultaneousLifeLossEventToBatches(lifeLostEvent, game); + game.getState().addSimultaneousLifeLossToBatch(lifeLostEvent, game); } return event.getAmount(); } diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index fe884e5b028..fde99657878 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -30,7 +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.BatchEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; @@ -2203,14 +2203,11 @@ public final class CardUtil { /** * 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()); + if (event instanceof BatchEvent) { + res.addAll(((BatchEvent) event).getTargetIds()); } else if (event != null && event.getTargetId() != null) { res.add(event.getTargetId()); }