diff --git a/Mage.Sets/src/mage/cards/a/AniktheaHandOfErebos.java b/Mage.Sets/src/mage/cards/a/AniktheaHandOfErebos.java index b4a9337ac86..0b338b08ef6 100644 --- a/Mage.Sets/src/mage/cards/a/AniktheaHandOfErebos.java +++ b/Mage.Sets/src/mage/cards/a/AniktheaHandOfErebos.java @@ -96,7 +96,7 @@ class AniktheaHandOfErebosEffect extends OneShotEffect { } player.moveCards(card, Zone.EXILED, source, game); new CreateTokenCopyTargetEffect() - .setPermanentModifier((token, g) -> { + .setPermanentModifier((token) -> { token.addCardType(CardType.CREATURE); token.addSubType(SubType.ZOMBIE); token.setColor(ObjectColor.BLACK); diff --git a/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java b/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java index be7e1456581..7c1c9e65c9a 100644 --- a/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java +++ b/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java @@ -99,7 +99,7 @@ class KinzuOfTheBleakCovenEffect extends OneShotEffect { player.moveCards(card, Zone.EXILED, source, game); return new CreateTokenCopyTargetEffect().setSavedPermanent( new PermanentCard(card, source.getControllerId(), game) - ).setPermanentModifier((token, g) -> { + ).setPermanentModifier((token) -> { token.setPower(1); // 1/1 token.setToughness(1); token.addAbility(new ToxicAbility(1)); // Add Toxic (is additive) diff --git a/Mage.Sets/src/mage/cards/m/MishraEminentOne.java b/Mage.Sets/src/mage/cards/m/MishraEminentOne.java index 7290fe7228b..d227bf03180 100644 --- a/Mage.Sets/src/mage/cards/m/MishraEminentOne.java +++ b/Mage.Sets/src/mage/cards/m/MishraEminentOne.java @@ -79,13 +79,14 @@ class MishraEminentOneEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect().setPermanentModifier((token, g) -> { - token.setName("Mishra's Warform"); - token.setPower(4); - token.setToughness(4); - token.addCardType(CardType.ARTIFACT, CardType.CREATURE); - token.addSubType(SubType.CONSTRUCT); - }); + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect().setPermanentModifier( + (token) -> { + token.setName("Mishra's Warform"); + token.setPower(4); + token.setToughness(4); + token.addCardType(CardType.ARTIFACT, CardType.CREATURE); + token.addSubType(SubType.CONSTRUCT); + }); effect.apply(game, source); game.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn) .setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game)), source); diff --git a/Mage.Sets/src/mage/cards/m/MyrkulLordOfBones.java b/Mage.Sets/src/mage/cards/m/MyrkulLordOfBones.java index 85e1332cb2d..3e714a6e49a 100644 --- a/Mage.Sets/src/mage/cards/m/MyrkulLordOfBones.java +++ b/Mage.Sets/src/mage/cards/m/MyrkulLordOfBones.java @@ -103,10 +103,11 @@ class MyrkulLordOfBonesEffect extends OneShotEffect { player.moveCards(card, Zone.EXILED, source, game); return new CreateTokenCopyTargetEffect().setSavedPermanent( new PermanentCard(CardUtil.getDefaultCardSideForBattlefield(game, card), source.getControllerId(), game) - ).setPermanentModifier((token, g) -> { + ).setPermanentModifier((token) -> { token.removeAllCardTypes(); token.addCardType(CardType.ENCHANTMENT); - token.retainAllEnchantmentSubTypes(g); + // We keep enchantment subtypes, clearing the rest. + token.getSubtype().retainAll(SubType.getEnchantmentTypes()); }).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/s/SauronTheNecromancer.java b/Mage.Sets/src/mage/cards/s/SauronTheNecromancer.java index 0d8829d5d8d..a0f519322b9 100644 --- a/Mage.Sets/src/mage/cards/s/SauronTheNecromancer.java +++ b/Mage.Sets/src/mage/cards/s/SauronTheNecromancer.java @@ -89,7 +89,7 @@ class SauronTheNecromancerEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect( null, null, false, 1, true, true ); - effect.setPermanentModifier(((token, g) -> { + effect.setPermanentModifier(((token) -> { token.setColor(ObjectColor.BLACK); token.addSubType(SubType.WRAITH); token.setPower(3); diff --git a/Mage.Sets/src/mage/cards/s/ShelobChildOfUngoliant.java b/Mage.Sets/src/mage/cards/s/ShelobChildOfUngoliant.java new file mode 100644 index 00000000000..7fb386dee92 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShelobChildOfUngoliant.java @@ -0,0 +1,236 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * + * @author Susucr + */ +public final class ShelobChildOfUngoliant extends CardImpl { + + private static FilterControlledCreaturePermanent filterSpiders = + new FilterControlledCreaturePermanent(SubType.SPIDER, "other spiders you control"); + + static { + filterSpiders.add(AnotherPredicate.instance); + } + + public ShelobChildOfUngoliant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.DEMON); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Ward {2} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"), false)); + + // Other Spiders you control have deathtouch and ward {2}. + Ability buff = new SimpleStaticAbility(new GainAbilityControlledEffect( + DeathtouchAbility.getInstance(), Duration.WhileOnBattlefield, + filterSpiders + ).setText("other Spiders you control have deathtouch")); + buff.addEffect(new GainAbilityControlledEffect( + new WardAbility(new ManaCostsImpl<>("{2}")), Duration.WhileOnBattlefield, + filterSpiders + ).setText(" and ward {2}")); + this.addAbility(buff); + + // Whenever another creature dealt damage this turn by a Spider you controlled dies, + // create a token that's a copy of that creature, except it's a Food artifact with + // "{2}, {T}, Sacrifice this artifact: You gain 3 life," and it loses all other card types. + this.addAbility( + new ShelobChildOfUngoliantTriggeredAbility( + new ShelobChildOfUngoliantEffect() + ) + ); + } + + private ShelobChildOfUngoliant(final ShelobChildOfUngoliant card) { + super(card); + } + + @Override + public ShelobChildOfUngoliant copy() { + return new ShelobChildOfUngoliant(this); + } +} + +class ShelobChildOfUngoliantWatcher extends Watcher { + private static FilterControlledCreaturePermanent spiderFilter = + new FilterControlledCreaturePermanent(SubType.SPIDER,"spiders you controlled"); + + // We store every permanent, as a non-creature may be dealt damage, + // then become a creature then die. + // map players to damaged permanents by a spider under that player's control. + private final Map> damagedPermanents = new HashMap<>(); + + public ShelobChildOfUngoliantWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT && !game.isSimulation()) { + Permanent damagedPermanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + Permanent spider = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (damagedPermanent == null || spider == null) { + return; + } + if(!spiderFilter.match(spider, game)){ + return; + } + + UUID playerUUID = spider.getControllerId(); + Set setForThatPlayer = damagedPermanents.getOrDefault(spider.getControllerId(), new HashSet<>()); + // Not sure this test is necessary, as the spiderFilter is a FilterControlledPermanent + if (controllerId != null && controllerId.equals(game.getControllerId(event.getSourceId()))) { + setForThatPlayer.add(new MageObjectReference(event.getTargetId(), game)); + damagedPermanents.put(playerUUID, setForThatPlayer); + } + } + } + + @Override + public void reset() { + super.reset(); + damagedPermanents.clear(); + } + + public boolean wasDamaged(UUID playerUUID, Permanent permanent, Game game) { + if(!damagedPermanents.containsKey(playerUUID)){ + return false; + } + + return damagedPermanents.get(playerUUID) + .contains(new MageObjectReference(permanent, game)); + } +} + +class ShelobChildOfUngoliantTriggeredAbility extends TriggeredAbilityImpl { + + public ShelobChildOfUngoliantTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect); + this.addWatcher(new ShelobChildOfUngoliantWatcher()); + this.setTriggerPhrase("Whenever another creature dealt damage this turn by a Spider you controlled dies, "); + } + + public ShelobChildOfUngoliantTriggeredAbility(final ShelobChildOfUngoliantTriggeredAbility ability) { + super(ability); + } + + @Override + public ShelobChildOfUngoliantTriggeredAbility copy() { + return new ShelobChildOfUngoliantTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (((ZoneChangeEvent) event).isDiesEvent()) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + Permanent dyingPermanent = zEvent.getTarget(); + if (StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE.match(dyingPermanent, game)) { + ShelobChildOfUngoliantWatcher watcher = game.getState().getWatcher(ShelobChildOfUngoliantWatcher.class); + if(watcher == null){ + return false; + } + + if(!watcher.wasDamaged(this.controllerId, dyingPermanent, game)){ + return false; + } + + for (Effect effect : getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); + } + + return true; + } + } + return false; + } +} + +class ShelobChildOfUngoliantEffect extends OneShotEffect { + + public ShelobChildOfUngoliantEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "create a token that's a copy of that creature, except it's a Food artifact with " + + "\"{2}, {T}, Sacrifice this artifact: You gain 3 life,\" and it loses all other card types."; + } + + public ShelobChildOfUngoliantEffect(final ShelobChildOfUngoliantEffect effect) { + super(effect); + } + + @Override + public ShelobChildOfUngoliantEffect copy() { + return new ShelobChildOfUngoliantEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent copyFrom = targetPointer.getFirstTargetPermanentOrLKI(game, source); + if(controller == null || copyFrom == null) { + return false; + } + + return new CreateTokenCopyTargetEffect().setSavedPermanent(copyFrom) + .setPermanentModifier((token) -> { + token.removeAllCardTypes(); + // We keep artifact subtypes, clearing the rest. + token.getSubtype().retainAll(SubType.getArtifactTypes()); + token.addCardType(CardType.ARTIFACT); + token.addSubType(SubType.FOOD); + + // {2}, {T}, Sacrifice this artifact: You gain 3 life. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainLifeEffect(3), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); + SacrificeSourceCost cost = new SacrificeSourceCost(); + cost.setText("Sacrifice this artifact"); + ability.addCost(cost); + token.addAbility(ability); + }).apply(game, source); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java b/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java index 6bc71797215..487861015a1 100644 --- a/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java +++ b/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java @@ -231,6 +231,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Shadowfax, Lord of Horses", 227, Rarity.UNCOMMON, mage.cards.s.ShadowfaxLordOfHorses.class)); cards.add(new SetCardInfo("Shagrat, Loot Bearer", 228, Rarity.RARE, mage.cards.s.ShagratLootBearer.class)); cards.add(new SetCardInfo("Shelob's Ambush", 108, Rarity.COMMON, mage.cards.s.ShelobsAmbush.class)); + cards.add(new SetCardInfo("Shelob, Child of Ungoliant", 230, Rarity.RARE, mage.cards.s.ShelobChildOfUngoliant.class)); cards.add(new SetCardInfo("Shire Scarecrow", 249, Rarity.COMMON, mage.cards.s.ShireScarecrow.class)); cards.add(new SetCardInfo("Shire Shirriff", 30, Rarity.UNCOMMON, mage.cards.s.ShireShirriff.class)); cards.add(new SetCardInfo("Shire Terrace", 261, Rarity.COMMON, mage.cards.s.ShireTerrace.class)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java index aabf29e231c..de46df9383a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -34,7 +34,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { @FunctionalInterface public interface PermanentModifier { - void apply(Token token, Game game); + void apply(Token token); } private final Set> abilityClazzesToRemove; @@ -241,7 +241,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { } additionalAbilities.stream().forEach(token::addAbility); if (permanentModifier != null) { - permanentModifier.apply(token, game); + permanentModifier.apply(token); } if (!this.abilityClazzesToRemove.isEmpty()) {