From 336ba66ab9ca56baae8ce3c2d5b8cecfa07e6526 Mon Sep 17 00:00:00 2001 From: Evan Kranzler <theelk801@gmail.com> Date: Mon, 7 Feb 2022 08:18:18 -0500 Subject: [PATCH] [NEO] Implemented Mindlink Mech --- Mage.Sets/src/mage/cards/m/MindlinkMech.java | 215 ++++++++++++++++++ Mage.Sets/src/mage/cards/m/MobilizerMech.java | 4 +- .../src/mage/sets/KamigawaNeonDynasty.java | 1 + 3 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/m/MindlinkMech.java diff --git a/Mage.Sets/src/mage/cards/m/MindlinkMech.java b/Mage.Sets/src/mage/cards/m/MindlinkMech.java new file mode 100644 index 0000000000..31d24eb697 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MindlinkMech.java @@ -0,0 +1,215 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.util.CardUtil; +import mage.util.functions.CopyApplier; +import mage.watchers.Watcher; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class MindlinkMech extends CardImpl { + + public MindlinkMech(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Mindlink Mech becomes crewed for the first time each turn, until end of turn, Mindlink Mech becomes a copy of target nonlegendary creature that crewed it this turn, except it's 4/3, it's a Vehicle artifact in addition to its other types, and it has flying. + this.addAbility(new MindlinkMechTriggeredAbility()); + + // Crew 1 + this.addAbility(new CrewAbility(1)); + } + + private MindlinkMech(final MindlinkMech card) { + super(card); + } + + @Override + public MindlinkMech copy() { + return new MindlinkMech(this); + } +} + +class MindlinkMechTriggeredAbility extends TriggeredAbilityImpl { + + MindlinkMechTriggeredAbility() { + super(Zone.BATTLEFIELD, new MindlinkMechEffect()); + this.addWatcher(new MindlinkMechWatcher()); + } + + private MindlinkMechTriggeredAbility(final MindlinkMechTriggeredAbility ability) { + super(ability); + } + + @Override + public MindlinkMechTriggeredAbility copy() { + return new MindlinkMechTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.VEHICLE_CREWED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getSourceId().equals(getSourceId()) || !MindlinkMechWatcher.checkVehicle(this, game)) { + return false; + } + this.getTargets().clear(); + this.addTarget(new TargetPermanent(MindlinkMechWatcher.makeFilter(this, game))); + return true; + } + + @Override + public String getRule() { + return "Whenever {this} becomes crewed for the first time each turn, until end of turn, " + + "{this} becomes a copy of target nonlegendary creature that crewed it this turn, " + + "except it's 4/3, it's a Vehicle artifact in addition to its other types, and it has flying."; + } +} + +class MindlinkMechWatcher extends Watcher { + + private final Map<MageObjectReference, Integer> crewCount = new HashMap<>(); + private final Map<MageObjectReference, Set<MageObjectReference>> crewMap = new HashMap<>(); + private static final FilterPermanent invalidFilter = new FilterPermanent(); + + static { + invalidFilter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, -2)); + } + + MindlinkMechWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + Permanent vehicle; + Permanent crewer; + switch (event.getType()) { + case VEHICLE_CREWED: + vehicle = game.getPermanent(event.getTargetId()); + crewer = null; + break; + case CREWED_VEHICLE: + vehicle = game.getPermanent(event.getSourceId()); + crewer = game.getPermanent(event.getTargetId()); + break; + default: + return; + } + if (vehicle == null) { + return; + } + if (crewer == null) { + crewCount.compute(new MageObjectReference(vehicle, game), (m, i) -> Integer.sum(i, 1)); + return; + } + crewMap.computeIfAbsent(new MageObjectReference(vehicle, game), x -> new HashSet<>()).add(new MageObjectReference(crewer, game)); + } + + @Override + public void reset() { + super.reset(); + crewCount.clear(); + crewMap.clear(); + } + + public static boolean checkVehicle(Ability source, Game game) { + return game + .getState() + .getWatcher(MindlinkMechWatcher.class) + .crewCount + .getOrDefault(new MageObjectReference(source), 0) < 2; + } + + public static FilterPermanent makeFilter(Ability source, Game game) { + Set<MageObjectReferencePredicate> predicates = game + .getState() + .getWatcher(MindlinkMechWatcher.class) + .crewMap + .computeIfAbsent(new MageObjectReference(source), x -> new HashSet<>()) + .stream() + .filter(mor -> { + Permanent permanent = mor.getPermanent(game); + return permanent != null && !permanent.isLegendary() && permanent.isCreature(game); + }).map(MageObjectReferencePredicate::new) + .collect(Collectors.toSet()); + if (predicates.isEmpty()) { + return invalidFilter; + } + FilterPermanent filterPermanent = new FilterPermanent( + "nonlegendary creature that crewed " + CardUtil.getSourceName(game, source) + " this turn" + ); + filterPermanent.add(Predicates.or(predicates)); + return filterPermanent; + } +} + +class MindlinkMechEffect extends OneShotEffect { + + MindlinkMechEffect() { + super(Outcome.Benefit); + } + + private MindlinkMechEffect(final MindlinkMechEffect effect) { + super(effect); + } + + @Override + public MindlinkMechEffect copy() { + return new MindlinkMechEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null || creature == null) { + return false; + } + game.copyPermanent(Duration.EndOfTurn, creature, permanent.getId(), source, new MindlinkMechApplier()); + return true; + } +} + +class MindlinkMechApplier extends CopyApplier { + @Override + public boolean apply(Game game, MageObject blueprint, Ability source, UUID targetObjectId) { + blueprint.getPower().modifyBaseValue(4); + blueprint.getToughness().modifyBaseValue(3); + blueprint.addCardType(game, CardType.ARTIFACT); + blueprint.addSubType(game, SubType.VEHICLE); + blueprint.getAbilities().add(FlyingAbility.getInstance()); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MobilizerMech.java b/Mage.Sets/src/mage/cards/m/MobilizerMech.java index c6752e963b..199f37bab0 100644 --- a/Mage.Sets/src/mage/cards/m/MobilizerMech.java +++ b/Mage.Sets/src/mage/cards/m/MobilizerMech.java @@ -64,7 +64,7 @@ class MobilizerMechTriggeredAbility extends TriggeredAbilityImpl { MobilizerMechTriggeredAbility() { super(Zone.BATTLEFIELD, new AddCardTypeTargetEffect(Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE)); - this.addTarget(new TargetPermanent(0,1,filter)); + this.addTarget(new TargetPermanent(0, 1, filter)); } private MobilizerMechTriggeredAbility(final MobilizerMechTriggeredAbility ability) { @@ -78,7 +78,7 @@ class MobilizerMechTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.CREWED_VEHICLE; + return event.getType() == GameEvent.EventType.VEHICLE_CREWED; } @Override diff --git a/Mage.Sets/src/mage/sets/KamigawaNeonDynasty.java b/Mage.Sets/src/mage/sets/KamigawaNeonDynasty.java index 16608699d4..d191591433 100644 --- a/Mage.Sets/src/mage/sets/KamigawaNeonDynasty.java +++ b/Mage.Sets/src/mage/sets/KamigawaNeonDynasty.java @@ -176,6 +176,7 @@ public final class KamigawaNeonDynasty extends ExpansionSet { cards.add(new SetCardInfo("Mech Hangar", 270, Rarity.UNCOMMON, mage.cards.m.MechHangar.class)); cards.add(new SetCardInfo("Memory of Toshiro", 108, Rarity.UNCOMMON, mage.cards.m.MemoryOfToshiro.class)); cards.add(new SetCardInfo("Michiko's Reign of Truth", 29, Rarity.UNCOMMON, mage.cards.m.MichikosReignOfTruth.class)); + cards.add(new SetCardInfo("Mindlink Mech", 62, Rarity.RARE, mage.cards.m.MindlinkMech.class)); cards.add(new SetCardInfo("Mirrorshell Crab", 63, Rarity.COMMON, mage.cards.m.MirrorshellCrab.class)); cards.add(new SetCardInfo("Mnemonic Sphere", 64, Rarity.COMMON, mage.cards.m.MnemonicSphere.class)); cards.add(new SetCardInfo("Mobilizer Mech", 65, Rarity.UNCOMMON, mage.cards.m.MobilizerMech.class));