From f959b87cdc14242afc542bb828e7a2d22eaaf195 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sat, 29 Apr 2023 19:52:06 -0400 Subject: [PATCH] [40K] Implement The Horus Heresy --- .../src/mage/cards/t/TheHorusHeresy.java | 167 ++++++++++++++++++ Mage.Sets/src/mage/sets/Warhammer40000.java | 1 + .../mage/abilities/common/SagaAbility.java | 4 + 3 files changed, 172 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TheHorusHeresy.java diff --git a/Mage.Sets/src/mage/cards/t/TheHorusHeresy.java b/Mage.Sets/src/mage/cards/t/TheHorusHeresy.java new file mode 100644 index 0000000000..9ab3ce1b82 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheHorusHeresy.java @@ -0,0 +1,167 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetpointer.EachTargetPointer; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheHorusHeresy extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("creature you control but don't own"); + + static { + filter.add(TargetController.NOT_YOU.getOwnerPredicate()); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 1); + private static final Hint hint = new ValueHint("Creatures you control but don't own", xValue); + + public TheHorusHeresy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{B}{R}"); + + this.subtype.add(SubType.SAGA); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- For each opponent, gain control of up to one target nonlegendary creature that player controls for as long as The Horus Heresy remains on the battlefield. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, ability -> { + ability.addEffect(new TheHorusHeresyControlEffect()); + ability.setTargetAdjuster(TheHorusHeresyAdjuster.instance); + }); + + // II -- Draw a card for each creature you control but don't own. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new DrawCardSourceControllerEffect(xValue)); + + // III -- Starting with you, each player chooses a creature. Destroy each creature chosen this way. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new TheHorusHeresyDestroyEffect()); + this.addAbility(sagaAbility.addHint(hint)); + } + + private TheHorusHeresy(final TheHorusHeresy card) { + super(card); + } + + @Override + public TheHorusHeresy copy() { + return new TheHorusHeresy(this); + } +} + +enum TheHorusHeresyAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + for (UUID opponentId : game.getOpponents(ability.getControllerId())) { + Player player = game.getPlayer(opponentId); + if (player == null) { + continue; + } + FilterPermanent filter = new FilterCreaturePermanent("nonlegendary creature controlled by " + player.getName()); + filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate())); + filter.add(new ControllerIdPredicate(opponentId)); + ability.addTarget(new TargetPermanent(0, 1, filter)); + } + } +} + +class TheHorusHeresyControlEffect extends GainControlTargetEffect { + + TheHorusHeresyControlEffect() { + super(Duration.Custom); + staticText = "for each opponent, gain control of up to one target nonlegendary creature " + + "that player controls for as long as {this} remains on the battlefield"; + this.setTargetPointer(new EachTargetPointer()); + } + + private TheHorusHeresyControlEffect(final TheHorusHeresyControlEffect effect) { + super(effect); + } + + @Override + public TheHorusHeresyControlEffect copy() { + return new TheHorusHeresyControlEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return source.getSourcePermanentIfItStillExists(game) != null && super.apply(game, source); + } +} + +class TheHorusHeresyDestroyEffect extends OneShotEffect { + + TheHorusHeresyDestroyEffect() { + super(Outcome.Benefit); + staticText = "starting with you, each player chooses a creature. Destroy each creature chosen this way"; + } + + private TheHorusHeresyDestroyEffect(final TheHorusHeresyDestroyEffect effect) { + super(effect); + } + + @Override + public TheHorusHeresyDestroyEffect copy() { + return new TheHorusHeresyDestroyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (game.getBattlefield().count( + StaticFilters.FILTER_PERMANENT_CREATURE, + source.getControllerId(), source, game + ) < 1) { + return false; + } + Set permanents = new HashSet<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + TargetPermanent target = new TargetCreaturePermanent(); + target.setNotTarget(true); + player.choose(Outcome.DestroyPermanent, target, source, game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + continue; + } + game.informPlayers(player.getLogName() + " chooses " + permanent.getLogName()); + } + for (Permanent permanent : permanents) { + permanent.destroy(source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/Warhammer40000.java b/Mage.Sets/src/mage/sets/Warhammer40000.java index 628464afef..fb181cf8a4 100644 --- a/Mage.Sets/src/mage/sets/Warhammer40000.java +++ b/Mage.Sets/src/mage/sets/Warhammer40000.java @@ -260,6 +260,7 @@ public final class Warhammer40000 extends ExpansionSet { cards.add(new SetCardInfo("The First Tyrannic War", 121, Rarity.RARE, mage.cards.t.TheFirstTyrannicWar.class)); cards.add(new SetCardInfo("The Flesh Is Weak", 122, Rarity.RARE, mage.cards.t.TheFleshIsWeak.class)); cards.add(new SetCardInfo("The Golden Throne", 157, Rarity.RARE, mage.cards.t.TheGoldenThrone.class)); + cards.add(new SetCardInfo("The Horus Heresy", 126, Rarity.RARE, mage.cards.t.TheHorusHeresy.class)); cards.add(new SetCardInfo("The Lost and the Damned", 129, Rarity.UNCOMMON, mage.cards.t.TheLostAndTheDamned.class)); cards.add(new SetCardInfo("The Swarmlord", 4, Rarity.MYTHIC, mage.cards.t.TheSwarmlord.class)); cards.add(new SetCardInfo("The War in Heaven", 69, Rarity.RARE, mage.cards.t.TheWarInHeaven.class)); diff --git a/Mage/src/main/java/mage/abilities/common/SagaAbility.java b/Mage/src/main/java/mage/abilities/common/SagaAbility.java index b707cff270..f7064b311e 100644 --- a/Mage/src/main/java/mage/abilities/common/SagaAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SagaAbility.java @@ -75,6 +75,10 @@ public class SagaAbility extends SimpleStaticAbility { addChapterEffect(card, chapter, chapter, new Effects(effects)); } + public void addChapterEffect(Card card, SagaChapter chapter, Consumer applier) { + addChapterEffect(card, chapter, chapter, applier); + } + public void addChapterEffect(Card card, SagaChapter fromChapter, SagaChapter toChapter, Effect effect) { addChapterEffect(card, fromChapter, toChapter, new Effects(effect), (Target) null); }