diff --git a/Mage.Sets/src/mage/sets/alarareborn/UnscytheKillerOfKings.java b/Mage.Sets/src/mage/sets/alarareborn/UnscytheKillerOfKings.java index 8d4efa95e97..0fbf55ffaad 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/UnscytheKillerOfKings.java +++ b/Mage.Sets/src/mage/sets/alarareborn/UnscytheKillerOfKings.java @@ -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 equippedDamagedTargets = new ArrayList(); - - 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(); - } -} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/UnscytheKillerOfKingsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/UnscytheKillerOfKingsTest.java index 8c5a7783a44..50cc29abca4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/UnscytheKillerOfKingsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/UnscytheKillerOfKingsTest.java @@ -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(); diff --git a/Mage/src/mage/MageObjectReference.java b/Mage/src/mage/MageObjectReference.java index 5573e784a7c..c19835e89c3 100644 --- a/Mage/src/mage/MageObjectReference.java +++ b/Mage/src/mage/MageObjectReference.java @@ -55,6 +55,17 @@ public class MageObjectReference implements Comparable { 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. diff --git a/Mage/src/mage/abilities/common/DiesAndDealtDamageThisTurnTriggeredAbility.java b/Mage/src/mage/abilities/common/DiesAndDealtDamageThisTurnTriggeredAbility.java index 0092e5d280e..7875f1e589c 100644 --- a/Mage/src/mage/abilities/common/DiesAndDealtDamageThisTurnTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/DiesAndDealtDamageThisTurnTriggeredAbility.java @@ -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; diff --git a/Mage/src/mage/game/permanent/Permanent.java b/Mage/src/mage/game/permanent/Permanent.java index f4f02115f7c..99745dad18d 100644 --- a/Mage/src/mage/game/permanent/Permanent.java +++ b/Mage/src/mage/game/permanent/Permanent.java @@ -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 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 getDealtDamageByThisTurn(); + HashSet getDealtDamageByThisTurn(); /** * Imprint some other card to this one. diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index 193ec415a3d..5ad9148bc78 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -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 attachments = new ArrayList<>(); protected Map> connectedCards = new HashMap<>(); - protected List dealtDamageByThisTurn; + protected HashSet 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 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 getDealtDamageByThisTurn() { + public HashSet 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;