From c84d9d2168f5f92347e69b5abec3674ab4586bf1 Mon Sep 17 00:00:00 2001 From: "Alex W. Jackson" Date: Mon, 12 Sep 2022 10:43:21 -0400 Subject: [PATCH] Don't erase a permanent's attacking/blocking info when it leaves the battlefield. Kithkin Mourncaller and Kardur Doomscourge no longer need their own special TriggeredAbility class --- .../src/mage/cards/k/KardurDoomscourge.java | 18 +-- .../src/mage/cards/k/KithkinMourncaller.java | 7 +- Mage.Sets/src/mage/cards/s/SorrowsPath.java | 6 +- Mage.Sets/src/mage/cards/t/TheWretched.java | 1 - ...aturePutIntoGraveyardTriggeredAbility.java | 119 ------------------ .../main/java/mage/game/combat/Combat.java | 17 ++- .../java/mage/game/permanent/Permanent.java | 14 ++- .../mage/game/permanent/PermanentImpl.java | 17 +-- 8 files changed, 32 insertions(+), 167 deletions(-) delete mode 100644 Mage/src/main/java/mage/abilities/common/AttackingCreaturePutIntoGraveyardTriggeredAbility.java diff --git a/Mage.Sets/src/mage/cards/k/KardurDoomscourge.java b/Mage.Sets/src/mage/cards/k/KardurDoomscourge.java index f2f4874277..41ba0c6858 100644 --- a/Mage.Sets/src/mage/cards/k/KardurDoomscourge.java +++ b/Mage.Sets/src/mage/cards/k/KardurDoomscourge.java @@ -2,6 +2,7 @@ package mage.cards.k; import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -11,25 +12,18 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; +import mage.filter.common.FilterAttackingCreature; import mage.game.Game; import mage.game.permanent.Permanent; import mage.watchers.common.AttackedThisTurnWatcher; import java.util.UUID; -import mage.abilities.common.AttackingCreaturePutIntoGraveyardTriggeredAbility; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AttackingPredicate; /** * @author TheElk801 */ public final class KardurDoomscourge extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("an attacking creature"); - - static { - filter.add(AttackingPredicate.instance); - } + private static final FilterAttackingCreature filter = new FilterAttackingCreature("an attacking creature"); public KardurDoomscourge(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); @@ -49,9 +43,9 @@ public final class KardurDoomscourge extends CardImpl { this.addAbility(ability); // Whenever an attacking creature dies, each opponent loses 1 life and you gain 1 life. - Ability ability2 = new AttackingCreaturePutIntoGraveyardTriggeredAbility(new LoseLifeOpponentsEffect(1), filter, false, true, false); - ability2.addEffect(new GainLifeEffect(1).concatBy("and")); - this.addAbility(ability2); + ability = new DiesCreatureTriggeredAbility(new LoseLifeOpponentsEffect(1), false, filter); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); + this.addAbility(ability); } private KardurDoomscourge(final KardurDoomscourge card) { diff --git a/Mage.Sets/src/mage/cards/k/KithkinMourncaller.java b/Mage.Sets/src/mage/cards/k/KithkinMourncaller.java index 575b2a1344..ecd931883f 100644 --- a/Mage.Sets/src/mage/cards/k/KithkinMourncaller.java +++ b/Mage.Sets/src/mage/cards/k/KithkinMourncaller.java @@ -3,7 +3,7 @@ package mage.cards.k; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.AttackingCreaturePutIntoGraveyardTriggeredAbility; +import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -32,7 +32,10 @@ public final class KithkinMourncaller extends CardImpl { this.toughness = new MageInt(2); // Whenever an attacking Kithkin or Elf is put into your graveyard from the battlefield, you may draw a card. - this.addAbility(new AttackingCreaturePutIntoGraveyardTriggeredAbility(new DrawCardSourceControllerEffect(1), filter, true, false, true)); + this.addAbility(new PutIntoGraveFromBattlefieldAllTriggeredAbility( + new DrawCardSourceControllerEffect(1), + true, filter, false, true + )); } private KithkinMourncaller(final KithkinMourncaller card) { diff --git a/Mage.Sets/src/mage/cards/s/SorrowsPath.java b/Mage.Sets/src/mage/cards/s/SorrowsPath.java index e16319ab0a..82c220c5c8 100644 --- a/Mage.Sets/src/mage/cards/s/SorrowsPath.java +++ b/Mage.Sets/src/mage/cards/s/SorrowsPath.java @@ -112,10 +112,8 @@ class SorrowsPathSwitchBlockersEffect extends OneShotEffect { // 10/1/2009: When the first ability resolves, if all the creatures that one of the targeted creatures was blocking have left combat, then the other targeted creature // is considered to be able to block all creatures the first creature is blocking. If the ability has its full effect, the second creature will be removed from combat // but not returned to combat; it doesn't block anything. - game.getCombat().removeFromCombat(blocker1.getId(), game, false); - game.getCombat().removeFromCombat(blocker2.getId(), game, false); - blocker1.setRemovedFromCombat(attackers2.isEmpty()); - blocker2.setRemovedFromCombat(attackers1.isEmpty()); + blocker1.removeFromCombat(game); + blocker2.removeFromCombat(game); // 10/1/2009: Abilities that trigger whenever one of the targeted creatures blocks will trigger when the first ability resolves, because those creatures will change from // not blocking (since they're removed from combat) to blocking. It doesn't matter if those abilities triggered when those creatures blocked the first time. Abilities diff --git a/Mage.Sets/src/mage/cards/t/TheWretched.java b/Mage.Sets/src/mage/cards/t/TheWretched.java index 77190199ea..4582b6ba0b 100644 --- a/Mage.Sets/src/mage/cards/t/TheWretched.java +++ b/Mage.Sets/src/mage/cards/t/TheWretched.java @@ -89,7 +89,6 @@ class TheWretchedEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent theWretched = source.getSourcePermanentIfItStillExists(game); if (theWretched == null - || theWretched.isRemovedFromCombat() || !theWretched.isAttacking() || !source.isControlledBy(theWretched.getControllerId())) { return false; diff --git a/Mage/src/main/java/mage/abilities/common/AttackingCreaturePutIntoGraveyardTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttackingCreaturePutIntoGraveyardTriggeredAbility.java deleted file mode 100644 index 4173003c5e..0000000000 --- a/Mage/src/main/java/mage/abilities/common/AttackingCreaturePutIntoGraveyardTriggeredAbility.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package mage.abilities.common; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import static mage.game.events.GameEvent.EventType.END_COMBAT_STEP_POST; -import static mage.game.events.GameEvent.EventType.REMOVED_FROM_COMBAT; -import static mage.game.events.GameEvent.EventType.ZONE_CHANGE; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; - -/** - * - * @author weirddan455 and jeffwadsworth - */ -public class AttackingCreaturePutIntoGraveyardTriggeredAbility extends TriggeredAbilityImpl { - - protected FilterPermanent filterPermanent; - private final boolean onlyToControllerGraveyard; - private final boolean itDies; - - public AttackingCreaturePutIntoGraveyardTriggeredAbility(Effect effect, FilterPermanent filterPermanent, Boolean onlyToControllerGraveyard, Boolean itDies, Boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); - this.filterPermanent = filterPermanent; - this.onlyToControllerGraveyard = onlyToControllerGraveyard; - this.itDies = itDies; - setTriggerPhrase("Whenever " + filterPermanent.getMessage() + - (itDies ? - " dies, " : - " is put into " + (onlyToControllerGraveyard ? "your" : "a") + " graveyard from the battlefield, ") - ); - } - - private AttackingCreaturePutIntoGraveyardTriggeredAbility(final AttackingCreaturePutIntoGraveyardTriggeredAbility ability) { - super(ability); - this.filterPermanent = ability.filterPermanent; - this.onlyToControllerGraveyard = ability.onlyToControllerGraveyard; - this.itDies = ability.itDies; - } - - @Override - public AttackingCreaturePutIntoGraveyardTriggeredAbility copy() { - return new AttackingCreaturePutIntoGraveyardTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - switch (event.getType()) { - case ATTACKER_DECLARED: - case END_COMBAT_STEP_POST: - case ZONE_CHANGE: - case REMOVED_FROM_COMBAT: - return true; - default: - return false; - } - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - switch (event.getType()) { - case ATTACKER_DECLARED: - Permanent permanent = game.getPermanent(event.getSourceId()); - if (permanent != null - && !filterPermanent.match(permanent, game)) { - return false; - } - List attackersList = new ArrayList<>(); - List attackersListCopy = (List) game.getState().getValue(this.getSourceId() + "Attackers"); - if (attackersListCopy == null) { - attackersListCopy = attackersList; - } - attackersListCopy.add(event.getSourceId()); // add the filtered creature to the list - game.getState().setValue(this.getSourceId() + "Attackers", attackersListCopy); - return false; - case END_COMBAT_STEP_POST: - game.getState().setValue(this.getSourceId() + "Attackers", null); - return false; - case ZONE_CHANGE: - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD - && zEvent.getToZone() == Zone.GRAVEYARD) { - if (onlyToControllerGraveyard - && !this.isControlledBy(game.getOwnerId(zEvent.getTargetId()))) { - return false; - } - if (itDies - && !zEvent.isDiesEvent()) { - return false; - } - List attackers = (List) game.getState().getValue(this.getSourceId() + "Attackers"); - return attackers != null - && attackers.contains(zEvent.getTargetId()); - } - case REMOVED_FROM_COMBAT: - // a card removed from combat is no longer an attacker or blocker so remove it from the list - List attackersListRFC = (List) game.getState().getValue(this.getSourceId() + "Attackers"); - if (attackersListRFC != null - && attackersListRFC.contains(event.getTargetId())) { - attackersListRFC.remove(event.getTargetId()); - game.getState().setValue(this.getSourceId() + "Attackers", attackersListRFC); - } - - default: - return false; - } - } -} diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index fe9e3b11bf..353c9c9f70 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -1447,7 +1447,7 @@ public class Combat implements Serializable, Copyable { } } - public boolean removePlaneswalkerFromCombat(UUID planeswalkerId, Game game, boolean withInfo) { + public boolean removePlaneswalkerFromCombat(UUID planeswalkerId, Game game) { boolean result = false; for (CombatGroup group : groups) { if (group.getDefenderId() != null && group.getDefenderId().equals(planeswalkerId)) { @@ -1458,13 +1458,14 @@ public class Combat implements Serializable, Copyable { return result; } - public boolean removeFromCombat(UUID creatureId, Game game, boolean withInfo) { + public boolean removeFromCombat(UUID creatureId, Game game, boolean withEvent) { boolean result = false; Permanent creature = game.getPermanent(creatureId); if (creature != null) { - creature.setAttacking(false); - creature.setBlocking(0); - creature.setRemovedFromCombat(true); + if (withEvent) { + creature.setAttacking(false); + creature.setBlocking(0); + } for (CombatGroup group : groups) { for (UUID attackerId : group.attackers) { Permanent attacker = game.getPermanent(attackerId); @@ -1479,7 +1480,7 @@ public class Combat implements Serializable, Copyable { } creature.clearBandedCards(); blockingGroups.remove(creatureId); - if (result && withInfo) { + if (result && withEvent) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.REMOVED_FROM_COMBAT, creatureId, null, null)); game.informPlayers(creature.getLogName() + " removed from combat"); } @@ -1507,10 +1508,6 @@ public class Combat implements Serializable, Copyable { } } } - // reset the removeFromCombat flag on all creatures on the battlefield - for (Permanent creaturePermanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, game)) { - creaturePermanent.setRemovedFromCombat(false); - } clear(); } diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index db6e336e45..fd173a185d 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -254,10 +254,6 @@ public interface Permanent extends Card, Controllable { int getMaxBlockedBy(); - boolean isRemovedFromCombat(); - - void setRemovedFromCombat(boolean removedFromCombat); - /** * Sets the maximum number of blockers the creature can be blocked by. * Default = 0 which means there is no restriction in the number of @@ -297,9 +293,17 @@ public interface Permanent extends Card, Controllable { */ boolean canUseActivatedAbilities(Game game); + /** + * Removes this permanent from combat + * + * @param game + * @param withEvent true if removed from combat by an effect (default) + * false if removed because it left the battlefield + * @return true if permanent was attacking or blocking + */ boolean removeFromCombat(Game game); - boolean removeFromCombat(Game game, boolean withInfo); + boolean removeFromCombat(Game game, boolean withEvent); boolean isDeathtouched(); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 0517530525..ac95e3b0f3 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -90,7 +90,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { protected int minBlockedBy = 1; // maximal number of creatures the creature can be blocked by 0 = no restriction protected int maxBlockedBy = 0; - protected boolean removedFromCombat; protected boolean deathtouched; protected Map> connectedCards = new HashMap<>(); @@ -720,11 +719,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return maxBlockedBy; } - @Override - public boolean isRemovedFromCombat() { - return removedFromCombat; - } - @Override public UUID getControllerId() { return this.controllerId; @@ -1477,22 +1471,17 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } @Override - public boolean removeFromCombat(Game game, boolean withInfo) { + public boolean removeFromCombat(Game game, boolean withEvent) { if (this.isAttacking() || this.blocking > 0) { - return game.getCombat().removeFromCombat(objectId, game, withInfo); + return game.getCombat().removeFromCombat(objectId, game, withEvent); } else if (this.isPlaneswalker(game)) { if (game.getCombat().getDefenders().contains(getId())) { - game.getCombat().removePlaneswalkerFromCombat(objectId, game, withInfo); + game.getCombat().removePlaneswalkerFromCombat(objectId, game); } } return false; } - @Override - public void setRemovedFromCombat(boolean removedFromCombat) { - this.removedFromCombat = removedFromCombat; - } - @Override public boolean imprint(UUID imprintedCard, Game game) { if (!game.getExile().containsId(imprintedCard, game)) {