diff --git a/Mage.Sets/src/mage/cards/g/GetawayCar.java b/Mage.Sets/src/mage/cards/g/GetawayCar.java index 96e17e69328..5809b5097c7 100644 --- a/Mage.Sets/src/mage/cards/g/GetawayCar.java +++ b/Mage.Sets/src/mage/cards/g/GetawayCar.java @@ -1,7 +1,6 @@ package mage.cards.g; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.AttacksOrBlocksTriggeredAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; @@ -10,29 +9,26 @@ import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.ComparisonType; import mage.constants.SubType; -import mage.constants.WatcherScope; 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.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.CrewedSourceThisTurnPredicate; import mage.target.TargetPermanent; -import mage.target.targetadjustment.TargetAdjuster; -import mage.util.CardUtil; -import mage.watchers.Watcher; +import mage.watchers.common.CrewedVehicleWatcher; -import java.util.*; -import java.util.stream.Collectors; +import java.util.UUID; /** * @author TheElk801 */ public final class GetawayCar extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent("creature that crewed it this turn"); + + static { + filter.add(CrewedSourceThisTurnPredicate.instance); + } + public GetawayCar(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); @@ -44,11 +40,9 @@ public final class GetawayCar extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Whenever Getaway Car attacks or blocks, return up to one target creature that crewed it this turn to its owner's hand. - this.addAbility(new AttacksOrBlocksTriggeredAbility( - new ReturnToHandTargetEffect() - .setText("return up to one target creature that crewed it this turn to its owner's hand"), - false - ).setTargetAdjuster(GetawayCarAdjuster.instance), new GetawayCarWatcher()); + Ability ability = new AttacksOrBlocksTriggeredAbility(new ReturnToHandTargetEffect(), false); + ability.addTarget(new TargetPermanent(0, 1, filter)); + this.addAbility(ability, new CrewedVehicleWatcher()); // Crew 1 this.addAbility(new CrewAbility(1)); @@ -63,81 +57,3 @@ public final class GetawayCar extends CardImpl { return new GetawayCar(this); } } - -enum GetawayCarAdjuster implements TargetAdjuster { - instance; - - @Override - public void adjustTargets(Ability ability, Game game) { - ability.getTargets().clear(); - ability.addTarget(new TargetPermanent( - 0, 1, GetawayCarWatcher.makeFilter(ability, game) - )); - } -} - -class GetawayCarWatcher extends Watcher { - - private final Map> crewMap = new HashMap<>(); - private static final FilterPermanent invalidFilter = new FilterPermanent(); - - static { - invalidFilter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, -2)); - } - - GetawayCarWatcher() { - 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; - } - crewMap.computeIfAbsent( - new MageObjectReference(vehicle, game), x -> new HashSet<>() - ).add(new MageObjectReference(crewer, game)); - } - - @Override - public void reset() { - super.reset(); - crewMap.clear(); - } - - public static FilterPermanent makeFilter(Ability source, Game game) { - Set predicates = game - .getState() - .getWatcher(GetawayCarWatcher.class) - .crewMap - .computeIfAbsent(new MageObjectReference(source), x -> new HashSet<>()) - .stream() - .filter(mor -> { - Permanent permanent = mor.getPermanent(game); - return permanent != null && permanent.isCreature(game); - }).map(MageObjectReferencePredicate::new) - .collect(Collectors.toSet()); - if (predicates.isEmpty()) { - return invalidFilter; - } - FilterPermanent filterPermanent = new FilterPermanent( - "creature that crewed " + CardUtil.getSourceName(game, source) + " this turn" - ); - filterPermanent.add(Predicates.or(predicates)); - return filterPermanent; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SubterraneanSchooner.java b/Mage.Sets/src/mage/cards/s/SubterraneanSchooner.java new file mode 100644 index 00000000000..e7ed7507361 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SubterraneanSchooner.java @@ -0,0 +1,55 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.keyword.ExploreTargetEffect; +import mage.abilities.keyword.CrewAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.CrewedSourceThisTurnPredicate; +import mage.target.TargetPermanent; +import mage.watchers.common.CrewedVehicleWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SubterraneanSchooner extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature that crewed it this turn"); + + static { + filter.add(CrewedSourceThisTurnPredicate.instance); + } + + public SubterraneanSchooner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{U}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Whenever Subterranean Schooner attacks, target creature that crewed it this turn explores. + Ability ability = new AttacksTriggeredAbility(new ExploreTargetEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability, new CrewedVehicleWatcher()); + + // Crew 1 + this.addAbility(new CrewAbility(1)); + } + + private SubterraneanSchooner(final SubterraneanSchooner card) { + super(card); + } + + @Override + public SubterraneanSchooner copy() { + return new SubterraneanSchooner(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TheLostCavernsOfIxalan.java b/Mage.Sets/src/mage/sets/TheLostCavernsOfIxalan.java index 2d425335028..7054b09f1b0 100644 --- a/Mage.Sets/src/mage/sets/TheLostCavernsOfIxalan.java +++ b/Mage.Sets/src/mage/sets/TheLostCavernsOfIxalan.java @@ -284,6 +284,7 @@ public final class TheLostCavernsOfIxalan extends ExpansionSet { cards.add(new SetCardInfo("Starving Revenant", 123, Rarity.RARE, mage.cards.s.StarvingRevenant.class)); cards.add(new SetCardInfo("Staunch Crewmate", 79, Rarity.UNCOMMON, mage.cards.s.StaunchCrewmate.class)); cards.add(new SetCardInfo("Stinging Cave Crawler", 124, Rarity.UNCOMMON, mage.cards.s.StingingCaveCrawler.class)); + cards.add(new SetCardInfo("Subterranean Schooner", 80, Rarity.RARE, mage.cards.s.SubterraneanSchooner.class)); cards.add(new SetCardInfo("Sunbird Effigy", 262, Rarity.UNCOMMON, mage.cards.s.SunbirdEffigy.class)); cards.add(new SetCardInfo("Sunbird Standard", 262, Rarity.UNCOMMON, mage.cards.s.SunbirdStandard.class)); cards.add(new SetCardInfo("Sunfire Torch", 167, Rarity.COMMON, mage.cards.s.SunfireTorch.class)); diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/CrewedSourceThisTurnPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/CrewedSourceThisTurnPredicate.java new file mode 100644 index 00000000000..6d545633a2b --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/permanent/CrewedSourceThisTurnPredicate.java @@ -0,0 +1,26 @@ +package mage.filter.predicate.permanent; + +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.watchers.common.CrewedVehicleWatcher; + +/** + * @author TheElk801 + */ +public enum CrewedSourceThisTurnPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return CrewedVehicleWatcher.checkIfCrewedThisTurn( + input.getObject(), input.getSource().getSourcePermanentOrLKI(game), game + ); + } + + @Override + public String toString() { + return "crewed {this} this turn"; + } +} diff --git a/Mage/src/main/java/mage/watchers/common/CrewedVehicleWatcher.java b/Mage/src/main/java/mage/watchers/common/CrewedVehicleWatcher.java new file mode 100644 index 00000000000..1b43c367e35 --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/CrewedVehicleWatcher.java @@ -0,0 +1,46 @@ +package mage.watchers.common; + +import mage.MageObjectReference; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public class CrewedVehicleWatcher extends Watcher { + + private final Map> crewMap = new HashMap<>(); + + public CrewedVehicleWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.CREWED_VEHICLE) { + crewMap.computeIfAbsent(new MageObjectReference(event.getSourceId(), game), x -> new HashSet<>()) + .add(new MageObjectReference(event.getTargetId(), game)); + } + } + + @Override + public void reset() { + super.reset(); + crewMap.clear(); + } + + public static boolean checkIfCrewedThisTurn(Permanent crewer, Permanent crewed, Game game) { + return game + .getState() + .getWatcher(CrewedVehicleWatcher.class) + .crewMap + .getOrDefault(new MageObjectReference(crewed, game), Collections.emptySet()) + .stream() + .anyMatch(mor -> mor.refersTo(crewer, game)); + } +}