mirror of
https://github.com/correl/mage.git
synced 2024-11-29 03:00:12 +00:00
[MOM] Implement Zephyr Singer
This commit is contained in:
parent
47fe90458f
commit
2c0486673f
7 changed files with 150 additions and 95 deletions
|
@ -1,9 +1,5 @@
|
|||
package mage.cards.l;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||
|
@ -15,14 +11,18 @@ import mage.choices.Choice;
|
|||
import mage.choices.ChoiceImpl;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.ConvokedSourcePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCreatureOrPlaneswalker;
|
||||
import mage.watchers.common.EachCreatureThatConvokedSourceWatcher;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Susucre
|
||||
*/
|
||||
public final class LethalScheme extends CardImpl {
|
||||
|
@ -37,7 +37,6 @@ public final class LethalScheme extends CardImpl {
|
|||
this.getSpellAbility().addEffect(new DestroyTargetEffect());
|
||||
this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker());
|
||||
// Each creature that convoked Lethal Scheme connives.
|
||||
this.getSpellAbility().addWatcher(new EachCreatureThatConvokedSourceWatcher());
|
||||
this.getSpellAbility().addEffect(new LethalSchemeEffect());
|
||||
}
|
||||
|
||||
|
@ -54,6 +53,12 @@ public final class LethalScheme extends CardImpl {
|
|||
// Based loosely on "Venerated Loxodon" and "Change of Plans"
|
||||
class LethalSchemeEffect extends OneShotEffect {
|
||||
|
||||
private static final FilterPermanent filter = new FilterCreaturePermanent();
|
||||
|
||||
static {
|
||||
filter.add(ConvokedSourcePredicate.SPELL);
|
||||
}
|
||||
|
||||
public LethalSchemeEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "Each creature that convoked Lethal Scheme connives.";
|
||||
|
@ -70,30 +75,18 @@ class LethalSchemeEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
EachCreatureThatConvokedSourceWatcher watcher = game.getState().getWatcher(EachCreatureThatConvokedSourceWatcher.class);
|
||||
if (watcher == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MageObjectReference mor = new MageObjectReference(source.getSourceId(), game);
|
||||
Set<MageObjectReference> creatures = watcher.getConvokingCreatures(mor);
|
||||
if (creatures == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<AbstractMap.SimpleEntry<UUID, Permanent>> playerPermanentsPairs =
|
||||
creatures
|
||||
.stream()
|
||||
.map(creatureMOR -> creatureMOR.getPermanentOrLKIBattlefield(game))
|
||||
.filter(Objects::nonNull)
|
||||
.map(permanent -> new AbstractMap.SimpleEntry<>(permanent.getControllerId(),permanent))
|
||||
.collect(Collectors.toSet());
|
||||
game.getBattlefield()
|
||||
.getActivePermanents(filter, source.getControllerId(), source, game)
|
||||
.stream()
|
||||
.map(permanent -> new AbstractMap.SimpleEntry<>(permanent.getControllerId(), permanent))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Map<Player, Set<Permanent>> permanentsPerPlayer = new HashMap<>();
|
||||
|
||||
playerPermanentsPairs.forEach(pair -> {
|
||||
Player player = game.getPlayer(pair.getKey());
|
||||
if(!permanentsPerPlayer.containsKey(player)){
|
||||
if (!permanentsPerPlayer.containsKey(player)) {
|
||||
permanentsPerPlayer.put(player, new HashSet<>());
|
||||
}
|
||||
permanentsPerPlayer.get(player).add(pair.getValue());
|
||||
|
@ -104,13 +97,13 @@ class LethalSchemeEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
for (Player player : game
|
||||
.getState()
|
||||
.getPlayersInRange(source.getControllerId(), game)
|
||||
.stream()
|
||||
.map(game::getPlayer)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(permanentsPerPlayer::containsKey)
|
||||
.collect(Collectors.toList())) {
|
||||
.getState()
|
||||
.getPlayersInRange(source.getControllerId(), game)
|
||||
.stream()
|
||||
.map(game::getPlayer)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(permanentsPerPlayer::containsKey)
|
||||
.collect(Collectors.toList())) {
|
||||
|
||||
Set<Permanent> permanents = permanentsPerPlayer.get(player);
|
||||
|
||||
|
|
|
@ -1,29 +1,31 @@
|
|||
package mage.cards.v;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersAllEffect;
|
||||
import mage.abilities.keyword.ConvokeAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.watchers.common.EachCreatureThatConvokedSourceWatcher;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.ConvokedSourcePredicate;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class VeneratedLoxodon extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterCreaturePermanent("creature that convoked it");
|
||||
|
||||
static {
|
||||
filter.add(ConvokedSourcePredicate.PERMANENT);
|
||||
}
|
||||
|
||||
public VeneratedLoxodon(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}");
|
||||
|
||||
|
@ -36,7 +38,9 @@ public final class VeneratedLoxodon extends CardImpl {
|
|||
this.addAbility(new ConvokeAbility());
|
||||
|
||||
// When Venerated Loxodon enters the battlefield, put a +1/+1 counter on each creature that convoked it.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new VeneratedLoxodonEffect(), false), new EachCreatureThatConvokedSourceWatcher());
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(
|
||||
new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter), false
|
||||
));
|
||||
}
|
||||
|
||||
private VeneratedLoxodon(final VeneratedLoxodon card) {
|
||||
|
@ -48,40 +52,3 @@ public final class VeneratedLoxodon extends CardImpl {
|
|||
return new VeneratedLoxodon(this);
|
||||
}
|
||||
}
|
||||
|
||||
class VeneratedLoxodonEffect extends OneShotEffect {
|
||||
|
||||
public VeneratedLoxodonEffect() {
|
||||
super(Outcome.BoostCreature);
|
||||
this.staticText = "put a +1/+1 counter on each creature that convoked it";
|
||||
}
|
||||
|
||||
public VeneratedLoxodonEffect(final VeneratedLoxodonEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VeneratedLoxodonEffect copy() {
|
||||
return new VeneratedLoxodonEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
EachCreatureThatConvokedSourceWatcher watcher = game.getState().getWatcher(EachCreatureThatConvokedSourceWatcher.class);
|
||||
if (watcher != null) {
|
||||
MageObjectReference mor = new MageObjectReference(source.getSourceId(), source.getSourceObjectZoneChangeCounter() - 1, game); // -1 because of spell on the stack
|
||||
Set<MageObjectReference> creatures = watcher.getConvokingCreatures(mor);
|
||||
if (creatures != null) {
|
||||
for (MageObjectReference creatureMOR : creatures) {
|
||||
Permanent creature = creatureMOR.getPermanent(game);
|
||||
if (creature != null) {
|
||||
creature.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
62
Mage.Sets/src/mage/cards/z/ZephyrSinger.java
Normal file
62
Mage.Sets/src/mage/cards/z/ZephyrSinger.java
Normal file
|
@ -0,0 +1,62 @@
|
|||
package mage.cards.z;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.common.counter.AddCountersAllEffect;
|
||||
import mage.abilities.keyword.ConvokeAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.VigilanceAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.ConvokedSourcePredicate;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class ZephyrSinger extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterCreaturePermanent("creature that convoked it");
|
||||
|
||||
static {
|
||||
filter.add(ConvokedSourcePredicate.PERMANENT);
|
||||
}
|
||||
|
||||
public ZephyrSinger(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}");
|
||||
|
||||
this.subtype.add(SubType.SIREN);
|
||||
this.subtype.add(SubType.PIRATE);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Convoke
|
||||
this.addAbility(new ConvokeAbility());
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Vigilance
|
||||
this.addAbility(VigilanceAbility.getInstance());
|
||||
|
||||
// When Zephyr Singer enters the battlefield, put a flying counter on each creature that convoked it.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(
|
||||
new AddCountersAllEffect(CounterType.FLYING.createInstance(), filter)
|
||||
));
|
||||
}
|
||||
|
||||
private ZephyrSinger(final ZephyrSinger card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZephyrSinger copy() {
|
||||
return new ZephyrSinger(this);
|
||||
}
|
||||
}
|
|
@ -237,6 +237,7 @@ public final class MarchOfTheMachine extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Wrenn's Resolve", 173, Rarity.COMMON, mage.cards.w.WrennsResolve.class));
|
||||
cards.add(new SetCardInfo("Xerex Strobe-Knight", 85, Rarity.UNCOMMON, mage.cards.x.XerexStrobeKnight.class));
|
||||
cards.add(new SetCardInfo("Yargle and Multani", 256, Rarity.RARE, mage.cards.y.YargleAndMultani.class));
|
||||
cards.add(new SetCardInfo("Zephyr Singer", 86, Rarity.RARE, mage.cards.z.ZephyrSinger.class));
|
||||
cards.add(new SetCardInfo("Zephyr Winder", 328, Rarity.COMMON, mage.cards.z.ZephyrWinder.class));
|
||||
cards.add(new SetCardInfo("Zhalfirin Lancer", 45, Rarity.UNCOMMON, mage.cards.z.ZhalfirinLancer.class));
|
||||
cards.add(new SetCardInfo("Zhalfirin Shapecraft", 87, Rarity.COMMON, mage.cards.z.ZhalfirinShapecraft.class));
|
||||
|
|
|
@ -30,6 +30,7 @@ import mage.players.ManaPool;
|
|||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.watchers.common.ConvokeWatcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -79,6 +80,7 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana
|
|||
public ConvokeAbility() {
|
||||
super(Zone.ALL, null); // all AlternateManaPaymentAbility must use ALL zone to calculate playable abilities
|
||||
this.setRuleAtTheTop(true);
|
||||
this.addWatcher(new ConvokeWatcher());
|
||||
this.addHint(new ValueHint("Untapped creatures you control", new PermanentsOnBattlefieldCount(filterUntapped)));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package mage.filter.predicate.permanent;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.filter.predicate.ObjectSourcePlayer;
|
||||
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.watchers.common.ConvokeWatcher;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public enum ConvokedSourcePredicate implements ObjectSourcePlayerPredicate<Permanent> {
|
||||
PERMANENT(-1),
|
||||
SPELL(0);
|
||||
private final int offset;
|
||||
|
||||
ConvokedSourcePredicate(int offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(ObjectSourcePlayer<Permanent> input, Game game) {
|
||||
return ConvokeWatcher.checkConvoke(
|
||||
new MageObjectReference(input.getSource(), offset), input.getObject(), game
|
||||
);
|
||||
}
|
||||
}
|
|
@ -16,11 +16,11 @@ import java.util.Set;
|
|||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class EachCreatureThatConvokedSourceWatcher extends Watcher {
|
||||
public class ConvokeWatcher extends Watcher {
|
||||
|
||||
private final Map<MageObjectReference, Set<MageObjectReference>> convokingCreatures = new HashMap<>();
|
||||
|
||||
public EachCreatureThatConvokedSourceWatcher() {
|
||||
public ConvokeWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
|
@ -29,26 +29,28 @@ public class EachCreatureThatConvokedSourceWatcher extends Watcher {
|
|||
if (event.getType() != GameEvent.EventType.CONVOKED) {
|
||||
return;
|
||||
}
|
||||
|
||||
Spell spell = game.getSpell(event.getSourceId());
|
||||
Permanent tappedCreature = game.getPermanentOrLKIBattlefield(event.getTargetId());
|
||||
if (spell == null || tappedCreature == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
MageObjectReference convokedSpell = new MageObjectReference(spell.getSourceId(), game);
|
||||
Set<MageObjectReference> creatures;
|
||||
if (convokingCreatures.containsKey(convokedSpell)) {
|
||||
creatures = convokingCreatures.get(convokedSpell);
|
||||
} else {
|
||||
creatures = new HashSet<>();
|
||||
convokingCreatures.put(convokedSpell, creatures);
|
||||
}
|
||||
creatures.add(new MageObjectReference(tappedCreature, game));
|
||||
convokingCreatures
|
||||
.computeIfAbsent(new MageObjectReference(spell.getSourceId(), game), x -> new HashSet<>())
|
||||
.add(new MageObjectReference(tappedCreature, game));
|
||||
}
|
||||
|
||||
public Set<MageObjectReference> getConvokingCreatures(MageObjectReference mor) {
|
||||
return convokingCreatures.get(mor);
|
||||
public static Set<MageObjectReference> getConvokingCreatures(MageObjectReference mor, Game game) {
|
||||
return game
|
||||
.getState()
|
||||
.getWatcher(ConvokeWatcher.class)
|
||||
.convokingCreatures
|
||||
.get(mor);
|
||||
}
|
||||
|
||||
public static boolean checkConvoke(MageObjectReference mor, Permanent permanent, Game game) {
|
||||
return getConvokingCreatures(mor, game)
|
||||
.stream()
|
||||
.anyMatch(m -> m.refersTo(permanent, game));
|
||||
}
|
||||
|
||||
@Override
|
Loading…
Reference in a new issue