diff --git a/Mage.Sets/src/mage/cards/b/BlazingTorch.java b/Mage.Sets/src/mage/cards/b/BlazingTorch.java index 2898d7e6cd1..c670d994054 100644 --- a/Mage.Sets/src/mage/cards/b/BlazingTorch.java +++ b/Mage.Sets/src/mage/cards/b/BlazingTorch.java @@ -1,15 +1,14 @@ package mage.cards.b; import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.Mode; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.costs.common.SacrificeAttachmentCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityWithAttachmentEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -21,7 +20,6 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetAnyTarget; -import java.util.List; import java.util.UUID; /** @@ -32,8 +30,10 @@ public final class BlazingTorch extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Vampires or Zombies"); static { - filter.add(Predicates.or(SubType.VAMPIRE.getPredicate(), - SubType.ZOMBIE.getPredicate())); + filter.add(Predicates.or( + SubType.VAMPIRE.getPredicate(), + SubType.ZOMBIE.getPredicate() + )); } public BlazingTorch(UUID ownerId, CardSetInfo setInfo) { @@ -41,15 +41,15 @@ public final class BlazingTorch extends CardImpl { this.subtype.add(SubType.EQUIPMENT); // Equipped creature can't be blocked by Vampires or Zombies. (!this is a static ability of the equipment) - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, filter, AttachmentType.EQUIPMENT))); + this.addAbility(new SimpleStaticAbility(new CantBeBlockedByCreaturesAttachedEffect( + Duration.WhileOnBattlefield, filter, AttachmentType.EQUIPMENT + ))); - - // Equipped creature has "{tap}, Sacrifice Blazing Torch: Blazing Torch deals 2 damage to any target.") - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BlazingTorchDamageEffect(), new TapSourceCost()); - ability.addCost(new BlazingTorchCost()); - ability.addTarget(new TargetAnyTarget()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ability, AttachmentType.EQUIPMENT))); + // Equipped creature has "{tap}, Sacrifice Blazing Torch: Blazing Torch deals 2 damage to any target." + this.addAbility(new SimpleStaticAbility(new GainAbilityWithAttachmentEffect( + "equipped creature has \"{tap}, Sacrifice {this}: {this} deals 2 damage to any target.\"", + new BlazingTorchEffect(), new TargetAnyTarget(), new SacrificeAttachmentCost(), new TapSourceCost() + ))); // Equip {1} this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(1))); @@ -65,76 +65,48 @@ public final class BlazingTorch extends CardImpl { } } -class BlazingTorchCost extends CostImpl { +class BlazingTorchEffect extends OneShotEffect { - public BlazingTorchCost() { - this.text = "Sacrifice Blazing Torch"; + BlazingTorchEffect() { + super(Outcome.Benefit); } - public BlazingTorchCost(BlazingTorchCost cost) { - super(cost); - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { - Permanent permanent = game.getPermanent(ability.getSourceId()); - List attachments = permanent.getAttachments(); - for (UUID attachmentId : attachments) { - Permanent attachment = game.getPermanent(attachmentId); - if (attachment != null && attachment.getName().equals("Blazing Torch")) { - ((BlazingTorchDamageEffect) ability.getEffects().get(0)).setSourceId(attachmentId); - paid |= attachment.sacrifice(sourceId, game); - return paid; - } - } - return paid; - } - - @Override - public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { - return true; - } - - @Override - public BlazingTorchCost copy() { - return new BlazingTorchCost(this); - } -} - -class BlazingTorchDamageEffect extends OneShotEffect { - - private UUID sourceId; - - public BlazingTorchDamageEffect() { - super(Outcome.Damage); - this.staticText = "Blazing Torch deals 2 damage to any target"; - } - - public BlazingTorchDamageEffect(final BlazingTorchDamageEffect effect) { + private BlazingTorchEffect(final BlazingTorchEffect effect) { super(effect); } @Override - public BlazingTorchDamageEffect copy() { - return new BlazingTorchDamageEffect(this); + public BlazingTorchEffect copy() { + return new BlazingTorchEffect(this); } @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); - if (permanent != null && sourceId != null) { - permanent.damage(2, sourceId, game, false, true); - return true; + Object object = getValue("attachedPermanent"); + Player player = game.getPlayer(source.getControllerId()); + if (!(object instanceof Permanent) || player == null) { + return false; } - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null && sourceId != null) { - player.damage(2, sourceId, game); - return true; + Permanent permanent = (Permanent) object; + Permanent targetedPermanent = game.getPermanent(source.getFirstTarget()); + if (targetedPermanent == null) { + Player targetedPlayer = game.getPlayer(source.getFirstTarget()); + if (targetedPlayer != null) { + targetedPlayer.damage(2, permanent.getId(), game); + } + } else { + targetedPermanent.damage(2, permanent.getId(), game); } - return false; + return true; } - public void setSourceId(UUID sourceId) { - this.sourceId = sourceId; + @Override + public String getText(Mode mode) { + String name = "Blazing Torch"; + Object object = getValue("attachedPermanent"); + if (object instanceof Permanent) { + name = ((Permanent) object).getName(); + } + return name + "deals 2 damage to target any target."; } } diff --git a/Mage.Sets/src/mage/cards/b/BlindingPowder.java b/Mage.Sets/src/mage/cards/b/BlindingPowder.java index 7e351b98cce..20afa6caf46 100644 --- a/Mage.Sets/src/mage/cards/b/BlindingPowder.java +++ b/Mage.Sets/src/mage/cards/b/BlindingPowder.java @@ -1,35 +1,38 @@ - package mage.cards.b; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.UnattachCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.PreventCombatDamageToSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityWithAttachmentEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class BlindingPowder extends CardImpl { public BlindingPowder(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature has "Unattach Blinding Powder: Prevent all combat damage that would be dealt to this creature this turn." - Effect effect = new PreventCombatDamageToSourceEffect(Duration.EndOfTurn); - effect.setText("Prevent all combat damage that would be dealt to this creature this turn"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new UnattachCost(this.getName(), this.getId())); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ability, AttachmentType.EQUIPMENT, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(new GainAbilityWithAttachmentEffect( + "equipped creature has \"Unattach {this}: Prevent all combat damage " + + "that would be dealt to this creature this turn.\"", + new PreventCombatDamageToSourceEffect(Duration.EndOfTurn) + .setText("Prevent all combat damage that would be dealt to this creature this turn"), + null, new UnattachCost() + ))); + // Equip {2} this.addAbility(new EquipAbility(Outcome.PreventDamage, new GenericManaCost(2))); } diff --git a/Mage.Sets/src/mage/cards/h/Heartseeker.java b/Mage.Sets/src/mage/cards/h/Heartseeker.java index 55f557e07e5..7d828d57ed2 100644 --- a/Mage.Sets/src/mage/cards/h/Heartseeker.java +++ b/Mage.Sets/src/mage/cards/h/Heartseeker.java @@ -1,42 +1,39 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.UnattachCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.continuous.BoostEnchantedEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityWithAttachmentEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Galatolol */ public final class Heartseeker extends CardImpl { public Heartseeker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - + this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +2/+1 and has "{tap}, Unattach Heartseeker: Destroy target creature." - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(2, 1, Duration.WhileOnBattlefield)); - Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new TapSourceCost()); - gainedAbility.addCost(new UnattachCost(this.getName(), this.getId())); - gainedAbility.addTarget(new TargetCreaturePermanent()); - Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.EQUIPMENT); - effect.setText("and has \"{T}, Unattach {this}: Destroy target creature.\""); - ability.addEffect(effect); + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 1)); + ability.addEffect(new GainAbilityWithAttachmentEffect( + "and has \"{T}, Unattach {this}: Destroy target creature.\"", new DestroyTargetEffect(), + new TargetCreaturePermanent(), new UnattachCost(), new TapSourceCost() + )); this.addAbility(ability); // Equip {5} diff --git a/Mage.Sets/src/mage/cards/l/LeoninBola.java b/Mage.Sets/src/mage/cards/l/LeoninBola.java index 402dc06b1fb..3655a42d347 100644 --- a/Mage.Sets/src/mage/cards/l/LeoninBola.java +++ b/Mage.Sets/src/mage/cards/l/LeoninBola.java @@ -1,37 +1,36 @@ - package mage.cards.l; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.UnattachCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.TapTargetEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityWithAttachmentEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Plopman */ public final class LeoninBola extends CardImpl { public LeoninBola(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature has "{tap}, Unattach Leonin Bola: Tap target creature." - Ability gainAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapTargetEffect(), new TapSourceCost()); - gainAbility.addCost(new UnattachCost(this.getName(), this.getId())); - gainAbility.addTarget(new TargetCreaturePermanent()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(gainAbility, AttachmentType.EQUIPMENT))); - + this.addAbility(new SimpleStaticAbility(new GainAbilityWithAttachmentEffect( + "equipped creature has \"{tap}, Unattach {this}: Tap target creature.\"", + new TapTargetEffect(), new TargetCreaturePermanent(), new UnattachCost(), new TapSourceCost() + ))); + // Equip {1} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(1))); } diff --git a/Mage.Sets/src/mage/cards/m/MasterworkOfIngenuity.java b/Mage.Sets/src/mage/cards/m/MasterworkOfIngenuity.java index 4625bff67da..5b3e88334db 100644 --- a/Mage.Sets/src/mage/cards/m/MasterworkOfIngenuity.java +++ b/Mage.Sets/src/mage/cards/m/MasterworkOfIngenuity.java @@ -1,37 +1,29 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.effects.common.CopyPermanentEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class MasterworkOfIngenuity extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("equipment"); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - filter.add(SubType.EQUIPMENT.getPredicate()); - } - public MasterworkOfIngenuity(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); this.subtype.add(SubType.EQUIPMENT); // You may have Masterwork of Ingenuity enter the battlefield as a copy of any Equipment on the battlefield. - this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect(filter), true)); + this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect(StaticFilters.FILTER_PERMANENT_EQUIPMENT), true)); } - public MasterworkOfIngenuity(final MasterworkOfIngenuity card) { + private MasterworkOfIngenuity(final MasterworkOfIngenuity card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/r/RazorBoomerang.java b/Mage.Sets/src/mage/cards/r/RazorBoomerang.java index 0541ef18cac..bde95d286c3 100644 --- a/Mage.Sets/src/mage/cards/r/RazorBoomerang.java +++ b/Mage.Sets/src/mage/cards/r/RazorBoomerang.java @@ -1,17 +1,21 @@ package mage.cards.r; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.UnattachCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityWithAttachmentEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -29,10 +33,12 @@ public final class RazorBoomerang extends CardImpl { this.subtype.add(SubType.EQUIPMENT); // Equipped creature has "{tap}, Unattach Razor Boomerang: Razor Boomerang deals 1 damage to any target. Return Razor Boomerang to its owner's hand." - Ability gainAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RazorBoomerangEffect(this.getId()), new TapSourceCost()); - gainAbility.addCost(new UnattachCost(this.getName(), this.getId())); - gainAbility.addTarget(new TargetAnyTarget()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(gainAbility, AttachmentType.EQUIPMENT))); + this.addAbility(new SimpleStaticAbility(new GainAbilityWithAttachmentEffect( + "equipped creature has \"{tap}, Unattach {this}: " + + "{this} deals 1 damage to any target. Return {this} to its owner's hand.\"", + new RazorBoomerangEffect(), new TargetAnyTarget(), + new UnattachCost(), new TapSourceCost() + ))); // Equip {2} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2))); @@ -50,41 +56,46 @@ public final class RazorBoomerang extends CardImpl { class RazorBoomerangEffect extends OneShotEffect { - private static String text = "Razor Boomerang deals 1 damage to any target. Return Razor Boomerang to its owner's hand"; - private UUID attachmentid; - - RazorBoomerangEffect(UUID attachmentid) { - super(Outcome.Damage); - this.attachmentid = attachmentid; - staticText = text; + RazorBoomerangEffect() { + super(Outcome.Benefit); } - RazorBoomerangEffect(RazorBoomerangEffect effect) { + private RazorBoomerangEffect(final RazorBoomerangEffect effect) { super(effect); - this.attachmentid = effect.attachmentid; - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID target : targetPointer.getTargets(game, source)) { - Permanent creature = game.getPermanent(target); - if (creature != null) { - creature.damage(1, attachmentid, game, false, true); - } - Player player = game.getPlayer(target); - if (player != null) { - player.damage(1, attachmentid, game); - } - } - Permanent razor = game.getPermanent(attachmentid); - if (razor != null) { - razor.moveToZone(Zone.HAND, source.getSourceId(), game, true); - } - return true; } @Override public RazorBoomerangEffect copy() { return new RazorBoomerangEffect(this); } + + @Override + public boolean apply(Game game, Ability source) { + Object object = getValue("attachedPermanent"); + Player player = game.getPlayer(source.getControllerId()); + if (!(object instanceof Permanent) || player == null) { + return false; + } + Permanent permanent = (Permanent) object; + Permanent targetedPermanent = game.getPermanent(source.getFirstTarget()); + if (targetedPermanent == null) { + Player targetedPlayer = game.getPlayer(source.getFirstTarget()); + if (targetedPlayer != null) { + targetedPlayer.damage(1, permanent.getId(), game); + } + } else { + targetedPermanent.damage(1, permanent.getId(), game); + } + return player.moveCards(permanent, Zone.HAND, source, game); + } + + @Override + public String getText(Mode mode) { + String name = "Razor Boomerang"; + Object object = getValue("attachedPermanent"); + if (object instanceof Permanent) { + name = ((Permanent) object).getName(); + } + return name + " deals 1 damage to any target. Return " + name + " to its owner's hand."; + } } diff --git a/Mage.Sets/src/mage/cards/s/Shuriken.java b/Mage.Sets/src/mage/cards/s/Shuriken.java index dfcc4fa4280..1051f59e059 100644 --- a/Mage.Sets/src/mage/cards/s/Shuriken.java +++ b/Mage.Sets/src/mage/cards/s/Shuriken.java @@ -1,44 +1,44 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.common.UnattachCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityWithAttachmentEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * - * @author LevelX2 + * @author TheElk801 */ public final class Shuriken extends CardImpl { public Shuriken(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature has "{tap}, Unattach Shuriken: Shuriken deals 2 damage to target creature. That creature's controller gains control of Shuriken unless it was unattached from a Ninja." - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ShurikenDamageEffect(), new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent()); - ability.addCost(new ShurikenUnattachCost()); - ability.addEffect(new ShurikenControlEffect()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ability, AttachmentType.EQUIPMENT, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(new GainAbilityWithAttachmentEffect( + "equipped creature has \"{tap}, Unattach {this}: {this} deals 2 damage to target creature. " + + "That creature's controller gains control of {this} unless it was unattached from a Ninja.\"", + new ShurikenEffect(), new TargetCreaturePermanent(), new UnattachCost(), new TapSourceCost() + ))); // Equip {2} this.addAbility(new EquipAbility(Outcome.PreventDamage, new GenericManaCost(2))); @@ -54,181 +54,49 @@ public final class Shuriken extends CardImpl { } } -class ShurikenDamageEffect extends OneShotEffect { +class ShurikenEffect extends OneShotEffect { - public ShurikenDamageEffect() { - super(Outcome.Damage); - this.staticText = "Shuriken deals 2 damage to target creature"; + ShurikenEffect() { + super(Outcome.Benefit); } - public ShurikenDamageEffect(final ShurikenDamageEffect effect) { + private ShurikenEffect(final ShurikenEffect effect) { super(effect); } @Override - public ShurikenDamageEffect copy() { - return new ShurikenDamageEffect(this); + public ShurikenEffect copy() { + return new ShurikenEffect(this); } @Override public boolean apply(Game game, Ability source) { - Permanent equipment = null; - for(Cost cost : source.getCosts()) { - if (cost instanceof ShurikenUnattachCost) { - equipment = ((ShurikenUnattachCost) cost).getEquipment(); - break; - } + Object object = getValue("attachedPermanent"); + Player player = game.getPlayer(source.getControllerId()); + Permanent targetedPermanent = game.getPermanent(source.getFirstTarget()); + if (!(object instanceof Permanent) || player == null || targetedPermanent == null) { + return false; } - if (equipment != null) { - Permanent creature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - if (creature != null) { - creature.damage(2, equipment.getId(), game, false, true); - } + Permanent equipment = (Permanent) object; + targetedPermanent.damage(2, equipment.getId(), game); + Permanent attached = source.getSourcePermanentOrLKI(game); + if (attached != null && attached.hasSubtype(SubType.NINJA, game)) { return true; } - return false; - } -} - - -class ShurikenUnattachCost extends CostImpl { - - Permanent equipment; - - public ShurikenUnattachCost() { - this.text = "Unattach Shuriken"; - } - - public ShurikenUnattachCost(final ShurikenUnattachCost cost) { - super(cost); - this.equipment = cost.equipment; - } - - @Override - public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { - Permanent permanent = game.getPermanent(sourceId); - if (permanent != null) { - for (UUID attachmentId :permanent.getAttachments()) { - Permanent attachment = game.getPermanent(attachmentId); - if (attachment != null && attachment.getName().equals("Shuriken")) { - paid = permanent.removeAttachment(attachmentId, game); - if (paid) { - equipment = attachment; - break; - } - } - } - - } - return paid; - } - - @Override - public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { - Permanent permanent = game.getPermanent(sourceId); - if (permanent != null) { - for (UUID attachmentId :permanent.getAttachments()) { - Permanent attachment = game.getPermanent(attachmentId); - if (attachment != null && attachment.getName().equals("Shuriken")) { - return true; - } - } - - } - return false; - } - - @Override - public ShurikenUnattachCost copy() { - return new ShurikenUnattachCost(this); - } - - public Permanent getEquipment() { - return equipment; - } -} - -class ShurikenControlEffect extends OneShotEffect { - - public ShurikenControlEffect() { - super(Outcome.Benefit); - this.staticText = "That creature's controller gains control of Shuriken unless it was unattached from a Ninja"; - } - - public ShurikenControlEffect(final ShurikenControlEffect effect) { - super(effect); - } - - @Override - public ShurikenControlEffect copy() { - return new ShurikenControlEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent equipment = null; // TODO: Meanwhile blinked object would be found regardless - for(Cost cost : source.getCosts()) { - if (cost instanceof ShurikenUnattachCost) { - equipment = ((ShurikenUnattachCost) cost).getEquipment(); - } - } - if (equipment != null) { - Permanent creature = game.getPermanent(source.getSourceId()); - if (creature != null) { - if (!creature.hasSubtype(SubType.NINJA, game)) { - Permanent damagedCreature = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - if (damagedCreature == null) { - damagedCreature = (Permanent) game.getLastKnownInformation(this.getTargetPointer().getFirst(game, source), Zone.BATTLEFIELD); - } - if (damagedCreature != null) { - ContinuousEffect effect = new ShurikenGainControlEffect(Duration.EndOfGame, damagedCreature.getControllerId()); - effect.setTargetPointer(new FixedTarget(equipment.getId())); - game.addEffect(effect, source); - return true; - } else { - return false; - } - } - return true; - } - } - return false; - } -} - -class ShurikenGainControlEffect extends ContinuousEffectImpl { - - UUID controller; - - public ShurikenGainControlEffect(Duration duration, UUID controller) { - super(duration, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); - this.controller = controller; - } - - public ShurikenGainControlEffect(final ShurikenGainControlEffect effect) { - super(effect); - this.controller = effect.controller; - } - - @Override - public ShurikenGainControlEffect copy() { - return new ShurikenGainControlEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (targetPointer != null) { - permanent = game.getPermanent(targetPointer.getFirst(game, source)); - } - if (permanent != null) { - return permanent.changeControllerId(controller, game); - } - return false; + game.addEffect(new GainControlTargetEffect( + Duration.Custom, true, attached.getControllerId() + ).setTargetPointer(new FixedTarget(equipment, game)), source); + return true; } @Override public String getText(Mode mode) { - return "Gain control of Shuriken"; + String name = "Shuriken"; + Object object = getValue("attachedPermanent"); + if (object instanceof Permanent) { + name = ((Permanent) object).getName(); + } + return name + "deals 2 damage to target creature. That creature's controller gains control of " + + name + " unless it was unattached from a Ninja."; } } diff --git a/Mage.Sets/src/mage/cards/s/SurestrikeTrident.java b/Mage.Sets/src/mage/cards/s/SurestrikeTrident.java index 027c82394c8..72a3cb5125b 100644 --- a/Mage.Sets/src/mage/cards/s/SurestrikeTrident.java +++ b/Mage.Sets/src/mage/cards/s/SurestrikeTrident.java @@ -1,50 +1,49 @@ package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.UnattachCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityWithAttachmentEffect; import mage.abilities.keyword.EquipAbility; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AttachmentType; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.target.common.TargetPlayerOrPlaneswalker; +import mage.constants.SubType; +import mage.target.TargetPlayer; + +import java.util.UUID; /** - * * @author AlumiuN */ public final class SurestrikeTrident extends CardImpl { + private static final DynamicValue xValue = new SourcePermanentPowerCount(); + public SurestrikeTrident(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature has first strike and "{T}, Unattach Surestrike Trident: This creature deals damage equal to its power to target player." - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT)); - DynamicValue xValue = new SourcePermanentPowerCount(); - Effect effect = new DamageTargetEffect(xValue); - effect.setText("This creature deals damage equal to its power to target player or planeswalker"); - Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); - gainedAbility.addTarget(new TargetPlayerOrPlaneswalker()); - gainedAbility.addCost(new UnattachCost(this.getName(), this.getId())); - effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.EQUIPMENT); - effect.setText("and \"{T}, Unattach {this}: This creature deals damage equal to its power to target player.\""); - ability.addEffect(effect); + Ability ability = new SimpleStaticAbility(new GainAbilityAttachedEffect( + FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT + )); + ability.addEffect(new GainAbilityWithAttachmentEffect( + "and \"{T}, Unattach {this}: This creature deals damage equal to its power to target player.\"", + new DamageTargetEffect(xValue) + .setText("This creature deals damage equal to its power to target player or planeswalker"), + new TargetPlayer(), new UnattachCost(), new TapSourceCost() + )); this.addAbility(ability); // Equip {4} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/BlazingTorchTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/BlazingTorchTest.java new file mode 100644 index 00000000000..172aa191e1f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/zen/BlazingTorchTest.java @@ -0,0 +1,84 @@ +package org.mage.test.cards.single.zen; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class BlazingTorchTest extends CardTestPlayerBase { + + private static final String plains = "Plains"; + private static final String torch = "Blazing Torch"; + private static final String lion = "Silvercoat Lion"; + private static final String masterwork = "Masterwork of Ingenuity"; + private static final String halo = "Runed Halo"; + + @Test + public void testCard() { + addCard(Zone.BATTLEFIELD, playerA, plains); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.BATTLEFIELD, playerA, torch); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T},", playerB); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 18); + assertPermanentCount(playerA, lion, 1); + assertPermanentCount(playerA, torch, 0); + assertGraveyardCount(playerA, torch, 1); + } + + @Test + public void testCardCopied() { + addCard(Zone.BATTLEFIELD, playerA, plains, 2); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.HAND, playerA, masterwork); + addCard(Zone.BATTLEFIELD, playerB, torch); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, masterwork); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T},", playerB); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 18); + assertPermanentCount(playerA, lion, 1); + assertPermanentCount(playerA, torch, 0); + assertPermanentCount(playerB, torch, 1); + assertGraveyardCount(playerA, masterwork, 1); + assertGraveyardCount(playerB, torch, 0); + } + + @Test + public void testRunedHalo() { + addCard(Zone.BATTLEFIELD, playerA, plains); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.BATTLEFIELD, playerA, torch); + addCard(Zone.BATTLEFIELD, playerB, plains, 2); + addCard(Zone.HAND, playerB, halo); + + setChoice(playerB, torch); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, halo); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip"); + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T},", playerB); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); // Ability resolves as it's from the equipped creature but damage is from torch and is prevented + assertPermanentCount(playerA, lion, 1); + assertPermanentCount(playerA, torch, 0); + assertGraveyardCount(playerA, torch, 1); + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/UseAttachedCost.java b/Mage/src/main/java/mage/abilities/costs/UseAttachedCost.java new file mode 100644 index 00000000000..6640fb93864 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/costs/UseAttachedCost.java @@ -0,0 +1,53 @@ +package mage.abilities.costs; + +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public abstract class UseAttachedCost extends CostImpl { + + protected MageObjectReference mageObjectReference; + protected String name = "{this}"; + + protected UseAttachedCost() { + super(); + } + + protected UseAttachedCost(final UseAttachedCost cost) { + super(cost); + this.mageObjectReference = cost.mageObjectReference; + this.name = cost.name; + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + if (mageObjectReference == null) { + return false; + } + Permanent permanent = game.getPermanent(sourceId); + return permanent != null + && permanent + .getAttachments() + .stream() + .anyMatch(uuid -> mageObjectReference.refersTo(uuid, game)); + } + + public UseAttachedCost setMageObjectReference(Ability source, Game game) { + this.mageObjectReference = new MageObjectReference(source.getSourceId(), game); + MageObject object = game.getObject(source.getSourceId()); + if (object != null) { + this.name = object.getName(); + } + return this; + } + + @Override + public abstract UseAttachedCost copy(); +} diff --git a/Mage/src/main/java/mage/abilities/costs/common/SacrificeAttachmentCost.java b/Mage/src/main/java/mage/abilities/costs/common/SacrificeAttachmentCost.java new file mode 100644 index 00000000000..b8a52ab9003 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/costs/common/SacrificeAttachmentCost.java @@ -0,0 +1,56 @@ +package mage.abilities.costs.common; + +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.UseAttachedCost; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class SacrificeAttachmentCost extends UseAttachedCost { + + public SacrificeAttachmentCost() { + super(); + } + + public SacrificeAttachmentCost(final SacrificeAttachmentCost cost) { + super(cost); + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + if (mageObjectReference == null) { + return false; + } + Permanent permanent = game.getPermanent(sourceId); + if (permanent == null) { + return paid; + } + for (UUID attachmentId : permanent.getAttachments()) { + if (!this.mageObjectReference.refersTo(attachmentId, game)) { + continue; + } + Permanent attachment = game.getPermanent(attachmentId); + paid = attachment != null && attachment.sacrifice(sourceId, game); + if (paid) { + break; + } + } + + return paid; + } + + @Override + public SacrificeAttachmentCost copy() { + return new SacrificeAttachmentCost(this); + } + + @Override + public String getText() { + return "sacrifice " + this.name; + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/common/UnattachCost.java b/Mage/src/main/java/mage/abilities/costs/common/UnattachCost.java index 9cdc11a23a6..6a496bced91 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/UnattachCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/UnattachCost.java @@ -1,64 +1,55 @@ package mage.abilities.costs.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.costs.UseAttachedCost; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * @author Galatolol + * @author TheElk801 */ -public class UnattachCost extends CostImpl { +public class UnattachCost extends UseAttachedCost { - protected UUID sourceEquipmentId; - - public UnattachCost(String name, UUID sourceId) { - this.text = "Unattach " + name; - this.sourceEquipmentId = sourceId; + public UnattachCost() { + super(); } public UnattachCost(final UnattachCost cost) { super(cost); - this.sourceEquipmentId = cost.sourceEquipmentId; } @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { - Permanent permanent = game.getPermanent(sourceId); - if (permanent != null) { - for (UUID attachmentId : permanent.getAttachments()) { - Permanent attachment = game.getPermanent(attachmentId); - if (attachment != null && attachment.getId().equals(sourceEquipmentId)) { - paid = permanent.removeAttachment(attachmentId, game); - if (paid) { - break; - } - } - } - + if (mageObjectReference == null) { + return false; } + Permanent permanent = game.getPermanent(sourceId); + if (permanent == null) { + return paid; + } + for (UUID attachmentId : permanent.getAttachments()) { + if (!this.mageObjectReference.refersTo(attachmentId, game)) { + continue; + } + paid = permanent.removeAttachment(attachmentId, game); + if (paid) { + break; + } + } + return paid; } - @Override - public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { - Permanent permanent = game.getPermanent(sourceId); - if (permanent != null) { - for (UUID attachmentId : permanent.getAttachments()) { - Permanent attachment = game.getPermanent(attachmentId); - if (attachment != null && attachment.getId().equals(sourceEquipmentId)) { - return true; - } - } - - } - return false; - } - @Override public UnattachCost copy() { return new UnattachCost(this); } + + @Override + public String getText() { + return "unattach " + this.name; + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityWithAttachmentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityWithAttachmentEffect.java new file mode 100644 index 00000000000..6018a71566a --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityWithAttachmentEffect.java @@ -0,0 +1,119 @@ +package mage.abilities.effects.common.continuous; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.UseAttachedCost; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.Effects; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.Target; +import mage.target.Targets; +import mage.target.targetpointer.FixedTarget; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public class GainAbilityWithAttachmentEffect extends ContinuousEffectImpl { + + private final Effects effects = new Effects(); + private final Targets targets = new Targets(); + private final Costs costs = new CostsImpl(); + private final UseAttachedCost useAttachedCost; + + public GainAbilityWithAttachmentEffect(String rule, Effect effect, Target target, UseAttachedCost attachedCost, Cost... costs) { + this(rule, new Effects(effect), new Targets(target), attachedCost, costs); + } + + public GainAbilityWithAttachmentEffect(String rule, Effects effects, Targets targets, UseAttachedCost attachedCost, Cost... costs) { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + this.staticText = rule; + this.effects.addAll(effects); + this.targets.addAll(targets); + this.costs.addAll(Arrays.asList(costs)); + this.useAttachedCost = attachedCost; + this.generateGainAbilityDependencies(makeAbility(this.effects, this.targets, this.costs), null); + } + + public GainAbilityWithAttachmentEffect(final GainAbilityWithAttachmentEffect effect) { + super(effect); + this.effects.addAll(effect.effects); + this.targets.addAll(effect.targets); + this.costs.addAll(effect.costs); + this.useAttachedCost = effect.useAttachedCost.copy(); + } + + @Override + public GainAbilityWithAttachmentEffect copy() { + return new GainAbilityWithAttachmentEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + if (affectedObjectsSet) { + Permanent equipment = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (equipment != null && equipment.getAttachedTo() != null) { + this.setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game.getState().getZoneChangeCounter(equipment.getAttachedTo()))); + } + } + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = null; + if (affectedObjectsSet) { + permanent = game.getPermanent(targetPointer.getFirst(game, source)); + if (permanent == null) { + discard(); + return true; + } + } else { + Permanent equipment = game.getPermanent(source.getSourceId()); + if (equipment != null && equipment.getAttachedTo() != null) { + permanent = game.getPermanentOrLKIBattlefield(equipment.getAttachedTo()); + } + } + if (permanent == null) { + return true; + } + Ability ability = makeAbility(this.effects, this.targets, this.costs); + ability.getEffects().setValue("attachedPermanent", game.getPermanent(source.getSourceId())); + ability.addCost(useAttachedCost.copy().setMageObjectReference(source, game)); + permanent.addAbility(ability, source.getSourceId(), game); + return true; + } + + private static Ability makeAbility(Effects effects, Targets targets, Cost... costs) { + Ability ability = new SimpleActivatedAbility(null, null); + for (Effect effect : effects) { + if (effect == null) { + continue; + } + ability.addEffect(effect.copy()); + } + for (Target target : targets) { + if (target == null) { + continue; + } + ability.addTarget(target); + } + for (Cost cost : costs) { + if (cost == null) { + continue; + } + ability.addCost(cost.copy()); + } + return ability; + } +}