mirror of
https://github.com/correl/mage.git
synced 2024-12-25 11:11:16 +00:00
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
This commit is contained in:
parent
7c2f76b46b
commit
c84d9d2168
8 changed files with 32 additions and 167 deletions
|
@ -2,6 +2,7 @@ package mage.cards.k;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.DiesCreatureTriggeredAbility;
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
import mage.abilities.effects.RestrictionEffect;
|
import mage.abilities.effects.RestrictionEffect;
|
||||||
import mage.abilities.effects.common.GainLifeEffect;
|
import mage.abilities.effects.common.GainLifeEffect;
|
||||||
|
@ -11,25 +12,18 @@ import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.*;
|
import mage.constants.*;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.filter.common.FilterAttackingCreature;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.watchers.common.AttackedThisTurnWatcher;
|
import mage.watchers.common.AttackedThisTurnWatcher;
|
||||||
import java.util.UUID;
|
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
|
* @author TheElk801
|
||||||
*/
|
*/
|
||||||
public final class KardurDoomscourge extends CardImpl {
|
public final class KardurDoomscourge extends CardImpl {
|
||||||
|
|
||||||
private static final FilterPermanent filter = new FilterCreaturePermanent("an attacking creature");
|
private static final FilterAttackingCreature filter = new FilterAttackingCreature("an attacking creature");
|
||||||
|
|
||||||
static {
|
|
||||||
filter.add(AttackingPredicate.instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KardurDoomscourge(UUID ownerId, CardSetInfo setInfo) {
|
public KardurDoomscourge(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}");
|
||||||
|
@ -49,9 +43,9 @@ public final class KardurDoomscourge extends CardImpl {
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
||||||
// Whenever an attacking creature dies, each opponent loses 1 life and you gain 1 life.
|
// 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);
|
ability = new DiesCreatureTriggeredAbility(new LoseLifeOpponentsEffect(1), false, filter);
|
||||||
ability2.addEffect(new GainLifeEffect(1).concatBy("and"));
|
ability.addEffect(new GainLifeEffect(1).concatBy("and"));
|
||||||
this.addAbility(ability2);
|
this.addAbility(ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KardurDoomscourge(final KardurDoomscourge card) {
|
private KardurDoomscourge(final KardurDoomscourge card) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ package mage.cards.k;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.common.AttackingCreaturePutIntoGraveyardTriggeredAbility;
|
import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility;
|
||||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
|
@ -32,7 +32,10 @@ public final class KithkinMourncaller extends CardImpl {
|
||||||
this.toughness = new MageInt(2);
|
this.toughness = new MageInt(2);
|
||||||
|
|
||||||
// Whenever an attacking Kithkin or Elf is put into your graveyard from the battlefield, you may draw a card.
|
// 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) {
|
private KithkinMourncaller(final KithkinMourncaller card) {
|
||||||
|
|
|
@ -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
|
// 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
|
// 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.
|
// but not returned to combat; it doesn't block anything.
|
||||||
game.getCombat().removeFromCombat(blocker1.getId(), game, false);
|
blocker1.removeFromCombat(game);
|
||||||
game.getCombat().removeFromCombat(blocker2.getId(), game, false);
|
blocker2.removeFromCombat(game);
|
||||||
blocker1.setRemovedFromCombat(attackers2.isEmpty());
|
|
||||||
blocker2.setRemovedFromCombat(attackers1.isEmpty());
|
|
||||||
|
|
||||||
// 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
|
// 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
|
// 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
|
||||||
|
|
|
@ -89,7 +89,6 @@ class TheWretchedEffect extends OneShotEffect {
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Permanent theWretched = source.getSourcePermanentIfItStillExists(game);
|
Permanent theWretched = source.getSourcePermanentIfItStillExists(game);
|
||||||
if (theWretched == null
|
if (theWretched == null
|
||||||
|| theWretched.isRemovedFromCombat()
|
|
||||||
|| !theWretched.isAttacking()
|
|| !theWretched.isAttacking()
|
||||||
|| !source.isControlledBy(theWretched.getControllerId())) {
|
|| !source.isControlledBy(theWretched.getControllerId())) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -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<UUID> attackersList = new ArrayList<>();
|
|
||||||
List<UUID> attackersListCopy = (List<UUID>) 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<UUID> attackers = (List<UUID>) 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<UUID> attackersListRFC = (List<UUID>) 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1447,7 +1447,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean removePlaneswalkerFromCombat(UUID planeswalkerId, Game game, boolean withInfo) {
|
public boolean removePlaneswalkerFromCombat(UUID planeswalkerId, Game game) {
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
for (CombatGroup group : groups) {
|
for (CombatGroup group : groups) {
|
||||||
if (group.getDefenderId() != null && group.getDefenderId().equals(planeswalkerId)) {
|
if (group.getDefenderId() != null && group.getDefenderId().equals(planeswalkerId)) {
|
||||||
|
@ -1458,13 +1458,14 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean removeFromCombat(UUID creatureId, Game game, boolean withInfo) {
|
public boolean removeFromCombat(UUID creatureId, Game game, boolean withEvent) {
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
Permanent creature = game.getPermanent(creatureId);
|
Permanent creature = game.getPermanent(creatureId);
|
||||||
if (creature != null) {
|
if (creature != null) {
|
||||||
creature.setAttacking(false);
|
if (withEvent) {
|
||||||
creature.setBlocking(0);
|
creature.setAttacking(false);
|
||||||
creature.setRemovedFromCombat(true);
|
creature.setBlocking(0);
|
||||||
|
}
|
||||||
for (CombatGroup group : groups) {
|
for (CombatGroup group : groups) {
|
||||||
for (UUID attackerId : group.attackers) {
|
for (UUID attackerId : group.attackers) {
|
||||||
Permanent attacker = game.getPermanent(attackerId);
|
Permanent attacker = game.getPermanent(attackerId);
|
||||||
|
@ -1479,7 +1480,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
}
|
}
|
||||||
creature.clearBandedCards();
|
creature.clearBandedCards();
|
||||||
blockingGroups.remove(creatureId);
|
blockingGroups.remove(creatureId);
|
||||||
if (result && withInfo) {
|
if (result && withEvent) {
|
||||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.REMOVED_FROM_COMBAT, creatureId, null, null));
|
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.REMOVED_FROM_COMBAT, creatureId, null, null));
|
||||||
game.informPlayers(creature.getLogName() + " removed from combat");
|
game.informPlayers(creature.getLogName() + " removed from combat");
|
||||||
}
|
}
|
||||||
|
@ -1507,10 +1508,6 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 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();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -254,10 +254,6 @@ public interface Permanent extends Card, Controllable {
|
||||||
|
|
||||||
int getMaxBlockedBy();
|
int getMaxBlockedBy();
|
||||||
|
|
||||||
boolean isRemovedFromCombat();
|
|
||||||
|
|
||||||
void setRemovedFromCombat(boolean removedFromCombat);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the maximum number of blockers the creature can be blocked by.
|
* Sets the maximum number of blockers the creature can be blocked by.
|
||||||
* Default = 0 which means there is no restriction in the number of
|
* 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);
|
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 removeFromCombat(Game game, boolean withInfo);
|
boolean removeFromCombat(Game game, boolean withEvent);
|
||||||
|
|
||||||
boolean isDeathtouched();
|
boolean isDeathtouched();
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
||||||
protected int minBlockedBy = 1;
|
protected int minBlockedBy = 1;
|
||||||
// maximal number of creatures the creature can be blocked by 0 = no restriction
|
// maximal number of creatures the creature can be blocked by 0 = no restriction
|
||||||
protected int maxBlockedBy = 0;
|
protected int maxBlockedBy = 0;
|
||||||
protected boolean removedFromCombat;
|
|
||||||
protected boolean deathtouched;
|
protected boolean deathtouched;
|
||||||
|
|
||||||
protected Map<String, List<UUID>> connectedCards = new HashMap<>();
|
protected Map<String, List<UUID>> connectedCards = new HashMap<>();
|
||||||
|
@ -720,11 +719,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
||||||
return maxBlockedBy;
|
return maxBlockedBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRemovedFromCombat() {
|
|
||||||
return removedFromCombat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UUID getControllerId() {
|
public UUID getControllerId() {
|
||||||
return this.controllerId;
|
return this.controllerId;
|
||||||
|
@ -1477,22 +1471,17 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeFromCombat(Game game, boolean withInfo) {
|
public boolean removeFromCombat(Game game, boolean withEvent) {
|
||||||
if (this.isAttacking() || this.blocking > 0) {
|
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)) {
|
} else if (this.isPlaneswalker(game)) {
|
||||||
if (game.getCombat().getDefenders().contains(getId())) {
|
if (game.getCombat().getDefenders().contains(getId())) {
|
||||||
game.getCombat().removePlaneswalkerFromCombat(objectId, game, withInfo);
|
game.getCombat().removePlaneswalkerFromCombat(objectId, game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setRemovedFromCombat(boolean removedFromCombat) {
|
|
||||||
this.removedFromCombat = removedFromCombat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean imprint(UUID imprintedCard, Game game) {
|
public boolean imprint(UUID imprintedCard, Game game) {
|
||||||
if (!game.getExile().containsId(imprintedCard, game)) {
|
if (!game.getExile().containsId(imprintedCard, game)) {
|
||||||
|
|
Loading…
Reference in a new issue