From c2c3f6497b0814d10f5e6bc0f223ca0ebdfeaa50 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Thu, 17 Jun 2021 07:53:53 -0400 Subject: [PATCH] [AFR] Implemented Lolth, Spider Queen --- .../src/mage/cards/l/LolthSpiderQueen.java | 62 +++++++++++++++ .../sets/AdventuresInTheForgottenRealms.java | 1 + ...ealCombatDamagePlayerTriggeredAbility.java | 58 ++++++++------ .../src/main/java/mage/constants/SubType.java | 1 + .../emblems/LolthSpiderQueenEmblem.java | 79 +++++++++++++++++++ .../permanent/token/LolthSpiderToken.java | 32 ++++++++ 6 files changed, 209 insertions(+), 24 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/l/LolthSpiderQueen.java create mode 100644 Mage/src/main/java/mage/game/command/emblems/LolthSpiderQueenEmblem.java create mode 100644 Mage/src/main/java/mage/game/permanent/token/LolthSpiderToken.java diff --git a/Mage.Sets/src/mage/cards/l/LolthSpiderQueen.java b/Mage.Sets/src/mage/cards/l/LolthSpiderQueen.java new file mode 100644 index 00000000000..8d19a5a36a1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LolthSpiderQueen.java @@ -0,0 +1,62 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.command.emblems.LolthSpiderQueenEmblem; +import mage.game.permanent.token.LolthSpiderToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LolthSpiderQueen extends CardImpl { + + public LolthSpiderQueen(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.LOLTH); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + + // Whenever a creature you control dies, put a loyalty counter on Lolth, Spider Queen. + this.addAbility(new DiesCreatureTriggeredAbility( + new AddCountersSourceEffect(CounterType.LOYALTY.createInstance()), + false, StaticFilters.FILTER_CONTROLLED_A_CREATURE + )); + + // 0: You draw a card and you lose 1 life. + Ability ability = new LoyaltyAbility(new DrawCardSourceControllerEffect(1).setText("you draw a card"), 0); + ability.addEffect(new LoseLifeSourceControllerEffect(1).concatBy("and")); + this.addAbility(ability); + + // −3: Create two 2/1 black Spider creature tokens with menace and reach. + this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new LolthSpiderToken(), 2), -3)); + + // −8: You get an emblem with "Whenever an opponent is dealt combat damage by one or more creatures you control, if that player lost less than 8 life this turn, they lose life equal to the difference." + this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new LolthSpiderQueenEmblem()))); + } + + private LolthSpiderQueen(final LolthSpiderQueen card) { + super(card); + } + + @Override + public LolthSpiderQueen copy() { + return new LolthSpiderQueen(this); + } +} diff --git a/Mage.Sets/src/mage/sets/AdventuresInTheForgottenRealms.java b/Mage.Sets/src/mage/sets/AdventuresInTheForgottenRealms.java index 945ef174f2a..157f7ac41c9 100644 --- a/Mage.Sets/src/mage/sets/AdventuresInTheForgottenRealms.java +++ b/Mage.Sets/src/mage/sets/AdventuresInTheForgottenRealms.java @@ -28,6 +28,7 @@ public final class AdventuresInTheForgottenRealms extends ExpansionSet { cards.add(new SetCardInfo("Flumph", 15, Rarity.RARE, mage.cards.f.Flumph.class)); cards.add(new SetCardInfo("Forest", 281, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 269, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lolth, Spider Queen", 112, Rarity.MYTHIC, mage.cards.l.LolthSpiderQueen.class)); cards.add(new SetCardInfo("Mountain", 277, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 265, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Portable Hole", 33, Rarity.UNCOMMON, mage.cards.p.PortableHole.class)); diff --git a/Mage/src/main/java/mage/abilities/common/ControlledCreaturesDealCombatDamagePlayerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/ControlledCreaturesDealCombatDamagePlayerTriggeredAbility.java index 23eef19b0d9..e661950e23f 100644 --- a/Mage/src/main/java/mage/abilities/common/ControlledCreaturesDealCombatDamagePlayerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/ControlledCreaturesDealCombatDamagePlayerTriggeredAbility.java @@ -1,10 +1,5 @@ - package mage.abilities.common; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.Zone; @@ -15,14 +10,18 @@ import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author LevelX2 */ public class ControlledCreaturesDealCombatDamagePlayerTriggeredAbility extends TriggeredAbilityImpl { - private Set damagedPlayerIds = new HashSet<>(); - private boolean setTargetPointer; + private final Set damagedPlayerIds = new HashSet<>(); + private final boolean setTargetPointer; + private final boolean onlyOpponents; public ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(Effect effect) { this(Zone.BATTLEFIELD, effect); @@ -33,13 +32,19 @@ public class ControlledCreaturesDealCombatDamagePlayerTriggeredAbility extends T } public ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(Zone zone, Effect effect, boolean setTargetPointer) { + this(zone, effect, setTargetPointer, false); + } + + public ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(Zone zone, Effect effect, boolean setTargetPointer, boolean onlyOpponents) { super(zone, effect, false); this.setTargetPointer = setTargetPointer; + this.onlyOpponents = onlyOpponents; } public ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(final ControlledCreaturesDealCombatDamagePlayerTriggeredAbility ability) { super(ability); - this.damagedPlayerIds = new HashSet<>(); + this.setTargetPointer = ability.setTargetPointer; + this.onlyOpponents = ability.onlyOpponents; } @Override @@ -56,28 +61,33 @@ public class ControlledCreaturesDealCombatDamagePlayerTriggeredAbility extends T @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { - DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; - Permanent p = game.getPermanent(event.getSourceId()); - if (damageEvent.isCombatDamage() && p != null && p.isControlledBy(this.getControllerId()) && !damagedPlayerIds.contains(event.getPlayerId())) { - damagedPlayerIds.add(event.getPlayerId()); - if (setTargetPointer) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getPlayerId())); - } - } - return true; - } - } if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_PRIORITY || (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(getSourceId()))) { damagedPlayerIds.clear(); + return false; } - return false; + if (event.getType() != EventType.DAMAGED_PLAYER) { + return false; + } + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; + Permanent p = game.getPermanent(event.getSourceId()); + if (!damageEvent.isCombatDamage() + || p == null + || !p.isControlledBy(this.getControllerId()) + || damagedPlayerIds.contains(event.getPlayerId()) + || (onlyOpponents && !game.getOpponents(getControllerId()).contains(event.getPlayerId()))) { + return false; + } + damagedPlayerIds.add(event.getPlayerId()); + if (setTargetPointer) { + this.getEffects().setTargetPointer(new FixedTarget(event.getPlayerId())); + } + return true; } @Override public String getRule() { - return "Whenever one or more creatures you control deal combat damage to a player, " + super.getRule(); + return "Whenever one or more creatures you control deal combat damage to " + + (onlyOpponents ? "an opponent" : "a player") + ", " + super.getRule(); } } diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index bced59b4181..5049bc7cc4b 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -219,6 +219,7 @@ public enum SubType { LICID("Licid", SubTypeSet.CreatureType), LIZARD("Lizard", SubTypeSet.CreatureType), LOBSTER("Lobster", SubTypeSet.CreatureType, true), // Unglued + LOLTH("Lolth", SubTypeSet.CreatureType), LUKE("Luke", SubTypeSet.PlaneswalkerType, true), // Star Wars // M MANTELLIAN("Mantellian", SubTypeSet.CreatureType, true), // Star Wars diff --git a/Mage/src/main/java/mage/game/command/emblems/LolthSpiderQueenEmblem.java b/Mage/src/main/java/mage/game/command/emblems/LolthSpiderQueenEmblem.java new file mode 100644 index 00000000000..9293b282ef9 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/LolthSpiderQueenEmblem.java @@ -0,0 +1,79 @@ +package mage.game.command.emblems; + +import mage.abilities.Ability; +import mage.abilities.common.ControlledCreaturesDealCombatDamagePlayerTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.command.Emblem; +import mage.players.Player; +import mage.watchers.common.PlayerLostLifeWatcher; + +import java.util.Objects; + +/** + * @author TheElk801 + */ +public final class LolthSpiderQueenEmblem extends Emblem { + + // −8: You get an emblem with "Whenever an opponent is dealt combat damage by one or more creatures you control, if that player lost less than 8 life this turn, they lose life equal to the difference." + public LolthSpiderQueenEmblem() { + this.setName("Emblem Lolth"); + this.setExpansionSetCodeForImage("AFR"); + this.getAbilities().add(new ConditionalInterveningIfTriggeredAbility( + new ControlledCreaturesDealCombatDamagePlayerTriggeredAbility( + Zone.COMMAND, new LolthSpiderQueenEmblemEffect(), true, true + ), LolthSpiderQueenEmblemCondition.instance, "Whenever an opponent " + + "is dealt combat damage by one or more creatures you control, " + + "if that player lost less than 8 life this turn, they lose life equal to the difference." + )); + } +} + +enum LolthSpiderQueenEmblemCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Player player = source + .getEffects() + .stream() + .map(Effect::getTargetPointer) + .map(tp -> tp.getFirst(game, source)) + .map(game::getPlayer) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + PlayerLostLifeWatcher watcher = game.getState().getWatcher(PlayerLostLifeWatcher.class); + return player != null && watcher != null && watcher.getLifeLost(player.getId()) < 8; + } +} + +class LolthSpiderQueenEmblemEffect extends OneShotEffect { + + LolthSpiderQueenEmblemEffect() { + super(Outcome.Benefit); + } + + private LolthSpiderQueenEmblemEffect(final LolthSpiderQueenEmblemEffect effect) { + super(effect); + } + + @Override + public LolthSpiderQueenEmblemEffect copy() { + return new LolthSpiderQueenEmblemEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + PlayerLostLifeWatcher watcher = game.getState().getWatcher(PlayerLostLifeWatcher.class); + return player != null && watcher != null && player.loseLife( + Math.max(8 - watcher.getLifeLost(player.getId()), 0), game, source, false + ) > 0; + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/LolthSpiderToken.java b/Mage/src/main/java/mage/game/permanent/token/LolthSpiderToken.java new file mode 100644 index 00000000000..9e0ecf2ccab --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/LolthSpiderToken.java @@ -0,0 +1,32 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.ReachAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class LolthSpiderToken extends TokenImpl { + + public LolthSpiderToken() { + super("Spider", "2/1 black Spider creature token with menace and reach"); + cardType.add(CardType.CREATURE); + color.setBlack(true); + subtype.add(SubType.SPIDER); + power = new MageInt(2); + toughness = new MageInt(1); + addAbility(new MenaceAbility()); + addAbility(ReachAbility.getInstance()); + } + + public LolthSpiderToken(final LolthSpiderToken token) { + super(token); + } + + public LolthSpiderToken copy() { + return new LolthSpiderToken(this); + } +}