* Reworked dealtDamageByThisTurn handling of permanents to take zone change of referenced objects into account.

This commit is contained in:
LevelX2 2015-03-07 01:15:27 +01:00
parent 2e8eeff49f
commit 532ff611c4
6 changed files with 105 additions and 118 deletions

View file

@ -27,9 +27,8 @@
*/
package mage.sets.alarareborn;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleStaticAbility;
@ -46,17 +45,15 @@ import mage.constants.AttachmentType;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.ZombieToken;
import mage.players.Player;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
import mage.watchers.Watcher;
/**
*
@ -70,16 +67,12 @@ public class UnscytheKillerOfKings extends CardImpl {
this.supertype.add("Legendary");
this.subtype.add("Equipment");
// Equipped creature gets +3/+3 and has first strike.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(3, 3)));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT)));
// Whenever a creature dealt damage by equipped creature this turn dies, you may exile that card. If you do, put a 2/2 black Zombie creature token onto the battlefield.
this.addAbility(new UnscytheKillerOfKingsTriggeredAbility(new UnscytheEffect()), new EquippedDidDamageWatcher());
this.addAbility(new UnscytheKillerOfKingsTriggeredAbility(new UnscytheEffect()));
// Equip {2}
this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2), new TargetControlledCreaturePermanent()));
@ -98,11 +91,7 @@ public class UnscytheKillerOfKings extends CardImpl {
class UnscytheKillerOfKingsTriggeredAbility extends TriggeredAbilityImpl {
public UnscytheKillerOfKingsTriggeredAbility(Effect effect) {
this(effect, true);
}
public UnscytheKillerOfKingsTriggeredAbility(Effect effect, boolean optional) {
super(Zone.ALL, effect, optional);
super(Zone.ALL, effect, true);
}
public UnscytheKillerOfKingsTriggeredAbility(final UnscytheKillerOfKingsTriggeredAbility ability) {
@ -117,14 +106,23 @@ class UnscytheKillerOfKingsTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent) event).isDiesEvent()) {
Card card = game.getCard(event.getTargetId());
if (card != null) {
EquippedDidDamageWatcher watcher = (EquippedDidDamageWatcher) game.getState().getWatchers().get("equippedDamagedTargets", this.getSourceId());
if (watcher != null
&& watcher.equippedDamagedTargets.contains(event.getTargetId())) {
Effect effect = this.getEffects().get(0);
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
return true;
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.getTarget().getCardType().contains(CardType.CREATURE)) { // target token can't create Zombie
Permanent equipment = game.getPermanent(getSourceId());
// the currently equiped creature must have done damage to the dying creature
if (equipment != null && equipment.getAttachedTo() != null) {
boolean damageDealt = false;
for (MageObjectReference mor : zEvent.getTarget().getDealtDamageByThisTurn()) {
if (mor.refersTo(equipment.getAttachedTo(), game)) {
damageDealt = true;
break;
}
}
if (damageDealt) {
Effect effect = this.getEffects().get(0);
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
return true;
}
}
}
}
@ -133,7 +131,7 @@ class UnscytheKillerOfKingsTriggeredAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
return "Whenever a creature dealt damage by {this} this turn dies, " + super.getRule();
return "Whenever a creature dealt damage by equipped creature this turn dies, " + super.getRule();
}
}
@ -141,7 +139,7 @@ class UnscytheEffect extends OneShotEffect {
public UnscytheEffect() {
super(Outcome.PutCreatureInPlay);
this.staticText = "put a 2/2 black Zombie creature token onto the battlefield";
this.staticText = "you may exile that card. If you do, put a 2/2 black Zombie creature token onto the battlefield";
}
public UnscytheEffect(final UnscytheEffect effect) {
@ -155,52 +153,15 @@ class UnscytheEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Card card = game.getCard(targetPointer.getFirst(game, source));
if (card != null) {
if (card.moveToExile(null, "Unscythe Exile", source.getSourceId(), game)) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Card card = game.getCard(targetPointer.getFirst(game, source));
if (card != null && game.getState().getZone(card.getId()).equals(Zone.GRAVEYARD) && controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.GRAVEYARD)) {
ZombieToken zombie = new ZombieToken("ALA");
return zombie.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId());
}
return true;
}
return false;
}
}
class EquippedDidDamageWatcher extends Watcher {
public List<UUID> equippedDamagedTargets = new ArrayList<UUID>();
public EquippedDidDamageWatcher() {
super("equippedDamagedTargets", WatcherScope.CARD);
}
public EquippedDidDamageWatcher(final EquippedDidDamageWatcher watcher) {
super(watcher);
this.equippedDamagedTargets.addAll(watcher.equippedDamagedTargets);
}
@Override
public EquippedDidDamageWatcher copy() {
return new EquippedDidDamageWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == EventType.DAMAGED_CREATURE) {
Permanent permanent = game.getPermanent(event.getSourceId());
if (permanent != null) {
if (permanent.getAttachments().contains(this.getSourceId())) {
if (!equippedDamagedTargets.contains(event.getTargetId())) {
equippedDamagedTargets.add(event.getTargetId());
}
}
}
}
}
@Override
public void reset() {
super.reset();
equippedDamagedTargets.clear();
}
}

View file

@ -46,13 +46,14 @@ public class UnscytheKillerOfKingsTest extends CardTestPlayerBase {
public void testDamagedCreatureDiesAfterEquipped() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
addCard(Zone.BATTLEFIELD, playerA, "Unscythe, Killer of Kings");
// {T}: Prodigal Pyromancer deals 1 damage to target creature or player.
addCard(Zone.BATTLEFIELD, playerA, "Prodigal Pyromancer");
addCard(Zone.HAND, playerA, "Lightning Bolt");
addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals 1 damage to target creature or player.", "Craw Wurm");
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Equip", "Prodigal Pyromancer");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Craw Wurm");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Craw Wurm", "Equip", StackClause.WHILE_NOT_ON_STACK);
setStopAt(1, PhaseStep.END_TURN);
execute();

View file

@ -55,6 +55,17 @@ public class MageObjectReference implements Comparable<MageObjectReference> {
this.zoneChangeCounter = card.getZoneChangeCounter();
}
public MageObjectReference(MageObject mageObject) {
this.sourceId = mageObject.getId();
if (mageObject instanceof Card) {
this.zoneChangeCounter = ((Card)mageObject).getZoneChangeCounter();
} else if (mageObject instanceof Permanent) {
this.zoneChangeCounter = ((Permanent)mageObject).getZoneChangeCounter();
} else {
this.zoneChangeCounter = 0;
}
}
/**
* That values manually (can be used to let it reference to a Permanent
* that is not yet on the battlefield.

View file

@ -1,5 +1,6 @@
package mage.abilities.common;
import mage.MageObjectReference;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.CardType;
@ -37,12 +38,20 @@ public class DiesAndDealtDamageThisTurnTriggeredAbility extends TriggeredAbility
public boolean checkTrigger(GameEvent event, Game game) {
if (((ZoneChangeEvent)event).isDiesEvent()) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.getTarget().getCardType().contains(CardType.CREATURE) &&
zEvent.getTarget().getDealtDamageByThisTurn().contains(this.sourceId)) {
for (Effect effect : getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
if (zEvent.getTarget().getCardType().contains(CardType.CREATURE)) {
boolean damageDealt = false;
for (MageObjectReference mor : zEvent.getTarget().getDealtDamageByThisTurn()) {
if (mor.refersTo(getSourceObject(game))) {
damageDealt = true;
break;
}
}
if (damageDealt) {
for (Effect effect : getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
}
return true;
}
return true;
}
}
return false;

View file

@ -29,9 +29,11 @@
package mage.game.permanent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.cards.Card;
import mage.constants.Rarity;
@ -79,9 +81,9 @@ public interface Permanent extends Card, Controllable {
void setFlipCard(boolean flipCard);
void setFlipCardName(String flipCardName);
void setSecondCardFace(Card card);
Counters getCounters();
List<UUID> getAttachments();
UUID getAttachedTo();
void attachTo(UUID permanentId, Game game);
@ -164,19 +166,19 @@ public interface Permanent extends Card, Controllable {
* @param maxBlockedBy maximum number of blockers
*/
void setMaxBlockedBy(int maxBlockedBy);
boolean canAttack(Game game);
/**
*
*
* @param defenderId id of planeswalker or player to attack
* @param game
* @return
* @return
*/
boolean canAttack(UUID defenderId, Game game);
boolean canBlock(UUID attackerId, Game game);
boolean canBlockAny(Game game);
/**
* Checks by restriction effects if the permanent can use activated abilities
*
@ -190,10 +192,10 @@ public interface Permanent extends Card, Controllable {
boolean isDeathtouched();
/**
* Returns the list of sources that dealt damage this turn to this permanent
* Returns a list of object refrences that dealt damage this turn to this permanent
* @return
*/
List<UUID> getDealtDamageByThisTurn();
HashSet<MageObjectReference> getDealtDamageByThisTurn();
/**
* Imprint some other card to this one.

View file

@ -37,6 +37,7 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
@ -103,7 +104,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
protected boolean deathtouched;
protected List<UUID> attachments = new ArrayList<>();
protected Map<String, List<UUID>> connectedCards = new HashMap<>();
protected List<UUID> dealtDamageByThisTurn;
protected HashSet<MageObjectReference> dealtDamageByThisTurn;
protected UUID attachedTo;
protected UUID pairedCard;
protected Counters counters;
@ -145,12 +146,12 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
this.connectedCards.put(entry.getKey(), entry.getValue());
}
if (permanent.dealtDamageByThisTurn != null) {
dealtDamageByThisTurn = new ArrayList<>(permanent.dealtDamageByThisTurn);
if (permanent.markedDamage != null) {
markedDamage = new ArrayList<>();
for (Counter counter : permanent.markedDamage) {
markedDamage.add(counter.copy());
}
dealtDamageByThisTurn = new HashSet<>(permanent.dealtDamageByThisTurn);
}
if (permanent.markedDamage != null) {
markedDamage = new ArrayList<>();
for (Counter counter : permanent.markedDamage) {
markedDamage.add(counter.copy());
}
}
this.counters = permanent.counters.copy();
@ -251,7 +252,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
}
}
@Override
public void removeAllAbilities(UUID sourceId, Game game) {
getAbilities().clear();
@ -266,7 +267,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
public void addCounters(String name, int amount, Game game) {
addCounters(name, amount, game, null);
}
@Override
public void addCounters(String name, int amount, Game game, ArrayList<UUID> appliedEffects) {
GameEvent countersEvent = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTERS, objectId, controllerId, name, amount);
@ -316,7 +317,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
public void removeCounters(Counter counter, Game game) {
removeCounters(counter.getName(), counter.getCount(), game);
}
@Override
public int getTurnsOnBattlefield() {
return turnsOnBattlefield;
@ -449,7 +450,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (!replaceEvent(EventType.PHASE_IN, game)) {
this.phasedIn = true;
game.informPlayers(getLogName() + " phased in");
fireEvent(EventType.PHASED_IN, game);
fireEvent(EventType.PHASED_IN, game);
return true;
}
}
@ -462,7 +463,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (!replaceEvent(EventType.PHASE_OUT, game)) {
this.phasedIn = false;
game.informPlayers(getLogName() + " phased out");
fireEvent(EventType.PHASED_OUT, game);
fireEvent(EventType.PHASED_OUT, game);
return true;
}
}
@ -472,7 +473,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
public void removeSummoningSickness() {
this.controlledFromStartOfControllerTurn = true;
}
@Override
public boolean wasControlledFromStartOfControllerTurn() {
return this.controlledFromStartOfControllerTurn;
@ -507,7 +508,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
public int getMaxBlockedBy() {
return maxBlockedBy;
}
@Override
public boolean isRemovedFromCombat() {
return removedFromCombat;
@ -523,7 +524,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
this.beforeResetControllerId = this.controllerId;
this.controllerId = this.originalControllerId;
}
@Override
public boolean changeControllerId(UUID controllerId, Game game) {
Player newController = game.getPlayer(controllerId);
@ -539,13 +540,13 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (!controllerId.equals(beforeResetControllerId)) {
this.removeFromCombat(game);
this.controlledFromStartOfControllerTurn = false;
this.abilities.setControllerId(controllerId);
game.getContinuousEffects().setController(objectId, controllerId);
game.fireEvent(new GameEvent(EventType.LOST_CONTROL, objectId, objectId, beforeResetControllerId));
game.fireEvent(new GameEvent(EventType.GAINED_CONTROL, objectId, objectId, controllerId));
return true;
}
return false;
@ -692,17 +693,19 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
} else {
sourceControllerId = ((Permanent) source).getControllerId();
}
if (source != null && source.getAbilities().containsKey(LifelinkAbility.getInstance().getId())) {
Player player = game.getPlayer(sourceControllerId);
player.gainLife(damageAmount, game);
if (source != null) {
if (source.getAbilities().containsKey(LifelinkAbility.getInstance().getId())) {
Player player = game.getPlayer(sourceControllerId);
player.gainLife(damageAmount, game);
}
if (source.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) {
deathtouched = true;
}
if (dealtDamageByThisTurn == null) {
dealtDamageByThisTurn = new HashSet<>();
}
dealtDamageByThisTurn.add(new MageObjectReference(source));
}
if (source != null && source.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) {
deathtouched = true;
}
if (dealtDamageByThisTurn == null) {
dealtDamageByThisTurn = new ArrayList<>();
}
dealtDamageByThisTurn.add(sourceId);
}
}
return damageDone;
@ -803,7 +806,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
public void entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent) {
controlledFromStartOfControllerTurn = false;
if (this.isFaceDown()) {
// remove some attributes here, bceause first apply effects comes later otherwise abilities (e.g. color related) will unintended trigger
// remove some attributes here, bceause first apply effects comes later otherwise abilities (e.g. color related) will unintended trigger
MorphAbility.setPermanentToFaceDownCreature(this);
}
EntersTheBattlefieldEvent event = new EntersTheBattlefieldEvent(this, sourceId, getControllerId(), fromZone);
@ -876,7 +879,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if(abilities.containsKey(IndestructibleAbility.getInstance().getId())) {
return false;
}
if (!game.replaceEvent(GameEvent.getEvent(EventType.DESTROY_PERMANENT, objectId, sourceId, controllerId, noRegen ? 1 : 0))) {
if (moveToZone(Zone.GRAVEYARD, sourceId, game, false)) {
String logName;
@ -952,7 +955,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
public boolean canAttack(Game game) {
return canAttack(null, game);
}
@Override
public boolean canAttack(UUID defenderId, Game game) {
if (tapped) {
@ -1008,7 +1011,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (attacker == null) {
return false;
}
// controller of attacking permanent must be an opponent
// controller of attacking permanent must be an opponent
if (!game.getPlayer(this.getControllerId()).hasOpponent(attacker.getControllerId(), game)) {
return false;
}
@ -1111,7 +1114,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
return true;
}
@Override
public void setRemovedFromCombat(boolean removedFromCombat) {
this.removedFromCombat = removedFromCombat;
@ -1145,9 +1148,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public List<UUID> getDealtDamageByThisTurn() {
public HashSet<MageObjectReference> getDealtDamageByThisTurn() {
if (dealtDamageByThisTurn == null) {
return emptyList;
return new HashSet<>();
}
return dealtDamageByThisTurn;
}
@ -1228,7 +1231,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
public void setExpansionSetCode(String expansionSetCode) {
this.expansionSetCode = expansionSetCode;
}
@Override
public void setRarity(Rarity rarity) {
this.rarity = rarity;