From bb696b4210a3fc084cc97658a79a3cb28341b955 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 15 Feb 2021 19:04:17 -0500 Subject: [PATCH] fixed implementation of cards which trigger on unblocked attackers --- .../src/mage/cards/c/CloakOfConfusion.java | 90 +++------- Mage.Sets/src/mage/cards/d/DelifsCone.java | 138 +++++++++++---- Mage.Sets/src/mage/cards/d/DelifsCube.java | 131 +++++++++----- Mage.Sets/src/mage/cards/f/FarrelsMantle.java | 167 +++++++++++++----- Mage.Sets/src/mage/cards/g/GazeOfPain.java | 100 ++++------- .../src/mage/cards/s/StinkdrinkerBandit.java | 38 ++-- ...ttacksAndIsNotBlockedTriggeredAbility.java | 24 +-- 7 files changed, 399 insertions(+), 289 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CloakOfConfusion.java b/Mage.Sets/src/mage/cards/c/CloakOfConfusion.java index 8aa29ebbf23..fa771e757cb 100644 --- a/Mage.Sets/src/mage/cards/c/CloakOfConfusion.java +++ b/Mage.Sets/src/mage/cards/c/CloakOfConfusion.java @@ -2,21 +2,18 @@ package mage.cards.c; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; @@ -43,7 +40,6 @@ public final class CloakOfConfusion extends CardImpl { // Whenever enchanted creature attacks and isn't blocked, you may have it assign no combat damage this turn. // If you do, defending player discards a card at random. this.addAbility(new CloakOfConfusionTriggeredAbility()); - } private CloakOfConfusion(final CloakOfConfusion card) { @@ -58,11 +54,12 @@ public final class CloakOfConfusion extends CardImpl { class CloakOfConfusionTriggeredAbility extends TriggeredAbilityImpl { - public CloakOfConfusionTriggeredAbility() { - super(Zone.BATTLEFIELD, new CloakOfConfusionEffect(), true); + CloakOfConfusionTriggeredAbility() { + super(Zone.BATTLEFIELD, new DiscardTargetEffect(1, true), false); + this.addEffect(new CloakOfConfusionEffect()); } - public CloakOfConfusionTriggeredAbility(final CloakOfConfusionTriggeredAbility ability) { + private CloakOfConfusionTriggeredAbility(final CloakOfConfusionTriggeredAbility ability) { super(ability); } @@ -73,45 +70,34 @@ class CloakOfConfusionTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARE_BLOCKERS_STEP; + return event.getType() == EventType.UNBLOCKED_ATTACKER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - Permanent aura = game.getPermanentOrLKIBattlefield(getSourceId()); - if (aura != null) { - Permanent enchantedCreature = game.getPermanent(aura.getAttachedTo()); - if (enchantedCreature != null - && enchantedCreature.isAttacking()) { - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getBlockers().isEmpty() - && combatGroup.getAttackers().contains(enchantedCreature.getId())) { - this.getEffects().setTargetPointer( - new FixedTarget(game.getCombat().getDefendingPlayerId( - enchantedCreature.getId(), game))); - return true; - } - } - } + Permanent aura = getSourcePermanentOrLKI(game); + if (aura != null && event.getTargetId().equals(aura.getAttachedTo())) { + this.getEffects().setTargetPointer(new FixedTarget(game.getCombat().getDefendingPlayerId(aura.getAttachedTo(), game))); + return true; } return false; } @Override public String getRule() { - return "Whenever enchanted creature attacks and isn't blocked, " + super.getRule(); + return "Whenever enchanted creature attacks and isn't blocked, " + + "you may have it assign no combat damage this turn. " + + "If you do, defending player discards a card at random"; } } -class CloakOfConfusionEffect extends OneShotEffect { +class CloakOfConfusionEffect extends ReplacementEffectImpl { - public CloakOfConfusionEffect() { - super(Outcome.Neutral); - this.staticText = "you may have it assign no combat damage this turn. " - + "If you do, defending player discards a card at random"; + CloakOfConfusionEffect() { + super(Duration.EndOfTurn, Outcome.Neutral); } - public CloakOfConfusionEffect(final CloakOfConfusionEffect effect) { + private CloakOfConfusionEffect(final CloakOfConfusionEffect effect) { super(effect); } @@ -120,40 +106,6 @@ class CloakOfConfusionEffect extends OneShotEffect { return new CloakOfConfusionEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent enchantedCreature = game.getPermanent(game.getPermanent(source.getSourceId()).getAttachedTo()); - if (controller != null && controller.chooseUse(outcome, "Have defending player discard a card at random? " + enchantedCreature.getName() + " will not assign combat damage.", source, game)) { - ContinuousEffect effect = new AssignNoCombatDamageTargetEffect(); - effect.setTargetPointer(new FixedTarget(enchantedCreature.getId())); - game.addEffect(effect, source); - Player defendingPlayer = game.getPlayer(targetPointer.getFirst(game, source)); - if (defendingPlayer != null) { - defendingPlayer.discard(1, true, false, source, game); - } - return true; - } - - return false; - } -} - -class AssignNoCombatDamageTargetEffect extends ReplacementEffectImpl { - - public AssignNoCombatDamageTargetEffect() { - super(Duration.EndOfTurn, Outcome.Neutral); - } - - public AssignNoCombatDamageTargetEffect(final AssignNoCombatDamageTargetEffect effect) { - super(effect); - } - - @Override - public AssignNoCombatDamageTargetEffect copy() { - return new AssignNoCombatDamageTargetEffect(this); - } - @Override public boolean apply(Game game, Ability source) { return true; @@ -178,8 +130,10 @@ class AssignNoCombatDamageTargetEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - DamageEvent damageEvent = (DamageEvent) event; - return event.getSourceId().equals(targetPointer.getFirst(game, source)) - && damageEvent.isCombatDamage(); + if (!((DamageEvent) event).isCombatDamage()) { + return false; + } + Permanent aura = source.getSourcePermanentOrLKI(game); + return aura != null && event.getSourceId().equals(aura.getAttachedTo()); } } diff --git a/Mage.Sets/src/mage/cards/d/DelifsCone.java b/Mage.Sets/src/mage/cards/d/DelifsCone.java index 209fa888223..1a03e705e77 100644 --- a/Mage.Sets/src/mage/cards/d/DelifsCone.java +++ b/Mage.Sets/src/mage/cards/d/DelifsCone.java @@ -1,46 +1,44 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.GainLifeEffect; -import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * * @author MarcoMarin */ public final class DelifsCone extends CardImpl { public DelifsCone(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{0}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}"); - Ability ability2 = new AttacksAndIsNotBlockedTriggeredAbility(new DelifsConeEffect(), true); - ability2.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn)); // {tap}, Sacrifice Delif's Cone: This turn, when target creature you control attacks and isn't blocked, you may gain life equal to its power. If you do, it assigns no combat damage this turn. - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new GainAbilityTargetEffect(ability2, Duration.EndOfTurn), - new TapSourceCost()); + Ability ability = new SimpleActivatedAbility( + new CreateDelayedTriggeredAbilityEffect( + new DelifsConeTriggeredAbility(), false + ), new TapSourceCost() + ); ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetControlledCreaturePermanent()); - this.addAbility(ability); - } private DelifsCone(final DelifsCone card) { @@ -52,26 +50,106 @@ public final class DelifsCone extends CardImpl { return new DelifsCone(this); } } -class DelifsConeEffect extends OneShotEffect{ - - public DelifsConeEffect() { - super(Outcome.Damage); - this.setText("you may gain life equal to its power"); + +class DelifsConeTriggeredAbility extends DelayedTriggeredAbility { + + DelifsConeTriggeredAbility() { + super(new DelifsConeLifeEffect(), Duration.EndOfTurn, false, true); + this.addEffect(new DelifsConePreventEffect()); } - - public DelifsConeEffect(final DelifsConeEffect effect) { + + private DelifsConeTriggeredAbility(final DelifsConeTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UNBLOCKED_ATTACKER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getTargetId().equals(getFirstTarget()); + } + + @Override + public DelifsConeTriggeredAbility copy() { + return new DelifsConeTriggeredAbility(this); + } + + @Override + public String getRule() { + return "This turn, when target creature you control attacks and isn't blocked, " + + "you may gain life equal to its power. If you do, it assigns no combat damage this turn."; + } +} + +class DelifsConeLifeEffect extends OneShotEffect { + + DelifsConeLifeEffect() { + super(Outcome.Benefit); + } + + private DelifsConeLifeEffect(final DelifsConeLifeEffect effect) { super(effect); } - + @Override - public DelifsConeEffect copy() { - return new DelifsConeEffect(this); + public DelifsConeLifeEffect copy() { + return new DelifsConeLifeEffect(this); } @Override public boolean apply(Game game, Ability source) { - Permanent perm = game.getPermanent(source.getSourceId()); - GainLifeEffect lifeEffect = new GainLifeEffect(perm.getPower().getValue()); - return lifeEffect.apply(game, source); + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); + if (player != null || permanent != null) { + player.gainLife(permanent.getPower().getValue(), game, source); + return true; + } + return false; } -} \ No newline at end of file +} + +class DelifsConePreventEffect extends ReplacementEffectImpl { + + DelifsConePreventEffect() { + super(Duration.EndOfTurn, Outcome.Neutral); + } + + private DelifsConePreventEffect(final DelifsConePreventEffect effect) { + super(effect); + } + + @Override + public DelifsConePreventEffect copy() { + return new DelifsConePreventEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_CREATURE: + case DAMAGE_PLAYER: + case DAMAGE_PLANESWALKER: + return true; + default: + return false; + } + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return ((DamageEvent) event).isCombatDamage() && event.getTargetId().equals(targetPointer.getFirst(game, source)); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DelifsCube.java b/Mage.Sets/src/mage/cards/d/DelifsCube.java index 939115beff7..a1da998ac8b 100644 --- a/Mage.Sets/src/mage/cards/d/DelifsCube.java +++ b/Mage.Sets/src/mage/cards/d/DelifsCube.java @@ -1,55 +1,49 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.RegenerateTargetEffect; -import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * - * @author MarcoMarin + * @author TheElk801 */ public final class DelifsCube extends CardImpl { public DelifsCube(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); - Ability ability2 = new AttacksAndIsNotBlockedTriggeredAbility(new DelifsCubeEffect(this.getId()), false); - ability2.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn)); // {2}, {tap}: This turn, when target creature you control attacks and isn't blocked, it assigns no combat damage this turn and you put a cube counter on Delif's Cube. - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new GainAbilityTargetEffect(ability2, Duration.EndOfTurn), - new ManaCostsImpl("{2}")); + Ability ability = new SimpleActivatedAbility( + new CreateDelayedTriggeredAbilityEffect(new DelifsCubeTriggeredAbility()), new GenericManaCost(2) + ); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetControlledCreaturePermanent()); - this.addAbility(ability); + // {2}, Remove a cube counter from Delif's Cube: Regenerate target creature. - SimpleActivatedAbility ability3 = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new RegenerateTargetEffect(), - new ManaCostsImpl("{2}")); - ability3.addCost(new RemoveCountersSourceCost(CounterType.CUBE.createInstance())); - ability3.addTarget(new TargetControlledCreaturePermanent()); - - this.addAbility(ability3); + ability = new SimpleActivatedAbility(new RegenerateTargetEffect(), new GenericManaCost(2)); + ability.addCost(new RemoveCountersSourceCost(CounterType.CUBE.createInstance())); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); } private DelifsCube(final DelifsCube card) { @@ -62,31 +56,78 @@ public final class DelifsCube extends CardImpl { } } -class DelifsCubeEffect extends OneShotEffect{ - - private UUID cubeId; - - public DelifsCubeEffect(UUID cubeId) { - super(Outcome.Benefit); - this.cubeId = cubeId; - this.setText("This turn, when target creature you control attacks and isn't blocked, it assigns no combat damage this turn and you put a cube counter on Delif's Cube"); +class DelifsCubeTriggeredAbility extends DelayedTriggeredAbility { + + DelifsCubeTriggeredAbility() { + super(new DelifsCubePreventEffect(), Duration.EndOfTurn, false, false); + this.addEffect(new AddCountersSourceEffect(CounterType.CUBE.createInstance())); } - - public DelifsCubeEffect(final DelifsCubeEffect effect) { - super(effect); - this.cubeId = effect.cubeId; + + private DelifsCubeTriggeredAbility(final DelifsCubeTriggeredAbility ability) { + super(ability); } - + @Override - public DelifsCubeEffect copy() { - return new DelifsCubeEffect(this); + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UNBLOCKED_ATTACKER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getTargetId().equals(getFirstTarget()); + } + + @Override + public DelifsCubeTriggeredAbility copy() { + return new DelifsCubeTriggeredAbility(this); + } + + @Override + public String getRule() { + return "This turn, when target creature you control attacks and isn't blocked, " + + "it assigns no combat damage this turn and you put a cube counter on {this}."; + } +} + +class DelifsCubePreventEffect extends ReplacementEffectImpl { + + DelifsCubePreventEffect() { + super(Duration.EndOfTurn, Outcome.Neutral); + } + + private DelifsCubePreventEffect(final DelifsCubePreventEffect effect) { + super(effect); + } + + @Override + public DelifsCubePreventEffect copy() { + return new DelifsCubePreventEffect(this); } @Override public boolean apply(Game game, Ability source) { - Permanent perm = game.getPermanent(cubeId); - if (perm == null) return false; - perm.addCounters(CounterType.CUBE.createInstance(), source.getControllerId(), source, game); - return true; + return true; } -} \ No newline at end of file + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_CREATURE: + case DAMAGE_PLAYER: + case DAMAGE_PLANESWALKER: + return true; + default: + return false; + } + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return ((DamageEvent) event).isCombatDamage() && event.getTargetId().equals(targetPointer.getFirst(game, source)); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FarrelsMantle.java b/Mage.Sets/src/mage/cards/f/FarrelsMantle.java index 918e9d7f5ae..157e80e20be 100644 --- a/Mage.Sets/src/mage/cards/f/FarrelsMantle.java +++ b/Mage.Sets/src/mage/cards/f/FarrelsMantle.java @@ -1,38 +1,36 @@ - package mage.cards.f; -import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; + +import java.util.UUID; /** - * - * @author MarcoMarin + * @author TheElk801 */ public final class FarrelsMantle extends CardImpl { - public FarrelsMantle(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); this.subtype.add(SubType.AURA); // Enchant creature @@ -41,17 +39,9 @@ public final class FarrelsMantle extends CardImpl { this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); - + // Whenever enchanted creature attacks and isn't blocked, its controller may have it deal damage equal to its power plus 2 to another target creature. If that player does, the attacking creature assigns no combat damage this turn. - FilterPermanent filter = new FilterCreaturePermanent(); - filter.add(Predicates.not(new AttachmentByUUIDPredicate(this.getId()))); - - Ability ability2 = new AttacksAndIsNotBlockedTriggeredAbility(new FarrelsMantleEffect(), true); - ability2.addTarget(new TargetPermanent(filter)); - ability2.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn)); - - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ability2, AttachmentType.AURA))); - + this.addAbility(new FarrelsMantleTriggeredAbility()); } private FarrelsMantle(final FarrelsMantle card) { @@ -63,36 +53,69 @@ public final class FarrelsMantle extends CardImpl { return new FarrelsMantle(this); } } -class AttachmentByUUIDPredicate implements Predicate { - private final UUID id; +class FarrelsMantleTriggeredAbility extends TriggeredAbilityImpl { - public AttachmentByUUIDPredicate(UUID id) { - this.id = id; + FarrelsMantleTriggeredAbility() { + super(Zone.BATTLEFIELD, null, false); + } + + private FarrelsMantleTriggeredAbility(final FarrelsMantleTriggeredAbility ability) { + super(ability); } @Override - public boolean apply(Permanent input, Game game) { - return input.getAttachments().contains(id); + public FarrelsMantleTriggeredAbility copy() { + return new FarrelsMantleTriggeredAbility(this); } @Override - public String toString() { - return "AttachmentUUID(" + id + ')'; + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UNBLOCKED_ATTACKER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent aura = getSourcePermanentOrLKI(game); + if (aura == null + || !event.getTargetId().equals(aura.getAttachedTo()) + || game.getPermanent(aura.getAttachedTo()) == null) { + return false; + } + MageObjectReference mor = new MageObjectReference(event.getTargetId(), game); + FilterPermanent filter = new FilterCreaturePermanent(); + filter.add(Predicates.not(new MageObjectReferencePredicate(mor))); + TargetPermanent target = new TargetPermanent(filter); + target.setTargetController(game.getControllerId(event.getTargetId())); + this.getTargets().clear(); + this.addTarget(target); + this.getEffects().clear(); + this.addEffect(new FarrelsMantleEffect(mor)); + return true; + } + + @Override + public String getRule() { + return "Whenever enchanted creature attacks and isn't blocked, its controller may have it deal damage " + + "equal to its power plus 2 to another target creature. If that player does, " + + "the attacking creature assigns no combat damage this turn."; } } -class FarrelsMantleEffect extends OneShotEffect{ - - public FarrelsMantleEffect() { - super(Outcome.Damage); - this.setText("its controller may have it deal damage equal to its power plus 2 to another target creature."); +class FarrelsMantleEffect extends OneShotEffect { + + private final MageObjectReference mor; + + FarrelsMantleEffect(MageObjectReference mor) { + super(Outcome.Benefit); + this.mor = mor; } - - public FarrelsMantleEffect(final FarrelsMantleEffect effect) { + + private FarrelsMantleEffect(final FarrelsMantleEffect effect) { super(effect); + this.mor = effect.mor; } - + @Override public FarrelsMantleEffect copy() { return new FarrelsMantleEffect(this); @@ -100,9 +123,67 @@ class FarrelsMantleEffect extends OneShotEffect{ @Override public boolean apply(Game game, Ability source) { - Permanent perm = game.getPermanent(source.getSourceId()); - int damage = CardUtil.overflowInc(perm.getPower().getValue(), 2); - DamageTargetEffect dmgEffect = new DamageTargetEffect(damage); - return dmgEffect.apply(game, source); + Permanent permanent = mor.getPermanent(game); + Permanent targeted = game.getPermanent(source.getFirstTarget()); + if (permanent == null || targeted == null) { + return false; + } + int damage = permanent.getPower().getValue() + 2; + Player player = game.getPlayer(permanent.getControllerId()); + if (damage > 0 && player != null && player.chooseUse( + outcome, "Have " + permanent.getIdName() + " deal " + + damage + " to " + targeted.getIdName() + '?', source, game + ) && targeted.damage(damage, permanent.getId(), source, game) > 0) { + game.addEffect(new FarrelsMantleDamageEffect(mor), source); + return true; + } + return false; + } +} + +class FarrelsMantleDamageEffect extends ReplacementEffectImpl { + + private final MageObjectReference mor; + + FarrelsMantleDamageEffect(MageObjectReference mor) { + super(Duration.EndOfTurn, Outcome.Neutral); + this.mor = mor; + } + + private FarrelsMantleDamageEffect(final FarrelsMantleDamageEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public FarrelsMantleDamageEffect copy() { + return new FarrelsMantleDamageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_CREATURE: + case DAMAGE_PLAYER: + case DAMAGE_PLANESWALKER: + return true; + default: + return false; + } + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return ((DamageEvent) event).isCombatDamage() && mor.refersTo(event.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/cards/g/GazeOfPain.java b/Mage.Sets/src/mage/cards/g/GazeOfPain.java index 0e3a8fee35d..4073979ae01 100644 --- a/Mage.Sets/src/mage/cards/g/GazeOfPain.java +++ b/Mage.Sets/src/mage/cards/g/GazeOfPain.java @@ -1,23 +1,17 @@ package mage.cards.g; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; -import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -25,8 +19,9 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class GazeOfPain extends CardImpl { @@ -50,38 +45,29 @@ public final class GazeOfPain extends CardImpl { class GazeOfPainDelayedTriggeredAbility extends DelayedTriggeredAbility { - Set creatures = new HashSet<>(); - - public GazeOfPainDelayedTriggeredAbility() { - super(new GazeOfPainEffect(), Duration.EndOfTurn, false, true); + GazeOfPainDelayedTriggeredAbility() { + super(null, Duration.EndOfTurn, false, true); + this.addTarget(new TargetCreaturePermanent()); } - public GazeOfPainDelayedTriggeredAbility(GazeOfPainDelayedTriggeredAbility ability) { + private GazeOfPainDelayedTriggeredAbility(final GazeOfPainDelayedTriggeredAbility ability) { super(ability); } @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARE_BLOCKERS_STEP; + return event.getType() == GameEvent.EventType.UNBLOCKED_ATTACKER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getBlockers().isEmpty()) { - for (UUID attackerId : combatGroup.getAttackers()) { - if (game.getPermanent(attackerId).getControllerId() == controllerId - && game.getPermanent(attackerId).isAttacking()) { - creatures.add(attackerId); - } - } - } + if (!isControlledBy(game.getControllerId(event.getTargetId()))) { + return false; } - if (!creatures.isEmpty()) { - game.getState().setValue("gazeOfPain", creatures); - return true; - } - return false; + this.getEffects().clear(); + this.addEffect(new GazeOfPainEffect(new MageObjectReference(event.getTargetId(), game))); + this.addEffect(new GazeOfPainDamageEffect().setTargetPointer(new FixedTarget(event.getTargetId(), game))); + return true; } @Override @@ -91,21 +77,24 @@ class GazeOfPainDelayedTriggeredAbility extends DelayedTriggeredAbility { @Override public String getRule() { - return "Until end of turn, whenever a creature you control attacks and isn't blocked, " + super.getRule(); + return "Until end of turn, whenever a creature you control attacks and isn't blocked, " + + "you may choose to have it deal damage equal to its power to a target creature. " + + "If you do, it assigns no combat damage this turn."; } } class GazeOfPainEffect extends OneShotEffect { - Set creatures = new HashSet<>(); + private final MageObjectReference mor; - GazeOfPainEffect() { + GazeOfPainEffect(MageObjectReference mor) { super(Outcome.Benefit); - this.staticText = "you may choose to have it deal damage equal to its power to a target creature. If you do, it assigns no combat damage this turn."; + this.mor = mor; } - GazeOfPainEffect(final GazeOfPainEffect effect) { + private GazeOfPainEffect(final GazeOfPainEffect effect) { super(effect); + this.mor = effect.mor; } @Override @@ -115,46 +104,29 @@ class GazeOfPainEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - creatures = (Set) game.getState().getValue("gazeOfPain"); - if (!creatures.isEmpty()) { - for (UUID attackerId : creatures) { - Permanent attacker = game.getPermanent(attackerId); - if (controller != null - && attacker != null) { - if (controller.chooseUse(outcome, "Do you wish to deal damage equal to " + attacker.getName() + "'s power to a target creature?", source, game)) { - TargetCreaturePermanent target = new TargetCreaturePermanent(); - if (target.canChoose(source.getSourceId(), controller.getId(), game) - && controller.choose(Outcome.Detriment, target, source.getSourceId(), game)) { - Effect effect = new DamageTargetEffect(attacker.getPower().getValue()); - effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); - effect.apply(game, source); - ContinuousEffect effect2 = new AssignNoCombatDamageTargetEffect(); - effect2.setTargetPointer(new FixedTarget(attackerId)); - game.addEffect(effect2, source); - } - } - } - } - return true; - } - return false; + Player player = game.getPlayer(source.getControllerId()); + Permanent creature = mor.getPermanent(game); + Permanent targeted = game.getPermanent(source.getFirstTarget()); + return player != null + && creature != null + && targeted != null + && targeted.damage(creature.getPower().getValue(), creature.getId(), source, game) > 0; } } -class AssignNoCombatDamageTargetEffect extends ReplacementEffectImpl { +class GazeOfPainDamageEffect extends ReplacementEffectImpl { - public AssignNoCombatDamageTargetEffect() { + GazeOfPainDamageEffect() { super(Duration.EndOfTurn, Outcome.Neutral); } - public AssignNoCombatDamageTargetEffect(final AssignNoCombatDamageTargetEffect effect) { + private GazeOfPainDamageEffect(final GazeOfPainDamageEffect effect) { super(effect); } @Override - public AssignNoCombatDamageTargetEffect copy() { - return new AssignNoCombatDamageTargetEffect(this); + public GazeOfPainDamageEffect copy() { + return new GazeOfPainDamageEffect(this); } @Override @@ -181,8 +153,6 @@ class AssignNoCombatDamageTargetEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - DamageEvent damageEvent = (DamageEvent) event; - return event.getSourceId().equals(targetPointer.getFirst(game, source)) - && damageEvent.isCombatDamage(); + return ((DamageEvent) event).isCombatDamage() && event.getSourceId().equals(targetPointer.getFirst(game, source)); } } diff --git a/Mage.Sets/src/mage/cards/s/StinkdrinkerBandit.java b/Mage.Sets/src/mage/cards/s/StinkdrinkerBandit.java index 28abcb11899..1631782334a 100644 --- a/Mage.Sets/src/mage/cards/s/StinkdrinkerBandit.java +++ b/Mage.Sets/src/mage/cards/s/StinkdrinkerBandit.java @@ -1,12 +1,8 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.ProwlAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -16,13 +12,14 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** - * * @author BursegSardaukar */ public final class StinkdrinkerBandit extends CardImpl { @@ -45,9 +42,7 @@ public final class StinkdrinkerBandit extends CardImpl { this.addAbility(new ProwlAbility(this, "{1}{B}")); // Whenever a Rogue you control attacks and isn't blocked, it gets +2/+1 until end of turn. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( - new StinkdrinkerBanditTriggeredAbility(), Duration.WhileOnBattlefield, - filter, "Whenever a Rogue you control attacks and isn't blocked, it gets +2/+1 until end of turn"))); + this.addAbility(new StinkdrinkerBanditTriggeredAbility()); } private StinkdrinkerBandit(final StinkdrinkerBandit card) { @@ -62,11 +57,11 @@ public final class StinkdrinkerBandit extends CardImpl { class StinkdrinkerBanditTriggeredAbility extends TriggeredAbilityImpl { - public StinkdrinkerBanditTriggeredAbility() { - super(Zone.BATTLEFIELD, new BoostSourceEffect(2, 1, Duration.EndOfTurn)); + StinkdrinkerBanditTriggeredAbility() { + super(Zone.BATTLEFIELD, new BoostTargetEffect(2, 1, Duration.EndOfTurn)); } - public StinkdrinkerBanditTriggeredAbility(final StinkdrinkerBanditTriggeredAbility ability) { + private StinkdrinkerBanditTriggeredAbility(final StinkdrinkerBanditTriggeredAbility ability) { super(ability); } @@ -77,20 +72,17 @@ class StinkdrinkerBanditTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_BLOCKERS; + return event.getType() == EventType.UNBLOCKED_ATTACKER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - Permanent sourcePermanent = game.getPermanent(getSourceId()); - if (sourcePermanent.isAttacking()) { - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getBlockers().isEmpty() - && combatGroup.getDefenderId().equals(event.getTargetId()) - && combatGroup.getAttackers().contains(getSourceId())) { - return true; - } - } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null + && isControlledBy(permanent.getControllerId()) + && permanent.hasSubtype(SubType.ROGUE, game)) { + getEffects().setTargetPointer(new FixedTarget(permanent, game)); + return true; } return false; } diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java index c76c3c5fd64..6ac4db886ce 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java @@ -1,14 +1,11 @@ - package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.game.Game; -import mage.game.combat.CombatGroup; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; public class AttacksAndIsNotBlockedTriggeredAbility extends TriggeredAbilityImpl { @@ -40,23 +37,20 @@ public class AttacksAndIsNotBlockedTriggeredAbility extends TriggeredAbilityImpl @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARE_BLOCKERS_STEP; + return event.getType() == EventType.UNBLOCKED_ATTACKER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(getSourceId()); - if (sourcePermanent != null && sourcePermanent.isAttacking()) { - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getBlockers().isEmpty() && combatGroup.getAttackers().contains(getSourceId())) { - if (setTargetPointer) { - this.getEffects().setTargetPointer(new FixedTarget(game.getCombat().getDefendingPlayerId(getSourceId(), game))); - } - return true; - } - } + if (!event.getTargetId().equals(getSourceId())) { + return false; } - return false; + if (setTargetPointer) { + this.getEffects().setTargetPointer(new FixedTarget( + game.getCombat().getDefendingPlayerId(getSourceId(), game), game + )); + } + return true; } @Override