mirror of
https://github.com/correl/mage.git
synced 2024-11-25 19:19:55 +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;
|
package mage.cards.l;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import mage.MageObjectReference;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.effects.common.DestroyTargetEffect;
|
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||||
|
@ -15,14 +11,18 @@ import mage.choices.Choice;
|
||||||
import mage.choices.ChoiceImpl;
|
import mage.choices.ChoiceImpl;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
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.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.common.TargetCreatureOrPlaneswalker;
|
import mage.target.common.TargetCreatureOrPlaneswalker;
|
||||||
import mage.watchers.common.EachCreatureThatConvokedSourceWatcher;
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Susucre
|
* @author Susucre
|
||||||
*/
|
*/
|
||||||
public final class LethalScheme extends CardImpl {
|
public final class LethalScheme extends CardImpl {
|
||||||
|
@ -37,7 +37,6 @@ public final class LethalScheme extends CardImpl {
|
||||||
this.getSpellAbility().addEffect(new DestroyTargetEffect());
|
this.getSpellAbility().addEffect(new DestroyTargetEffect());
|
||||||
this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker());
|
this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker());
|
||||||
// Each creature that convoked Lethal Scheme connives.
|
// Each creature that convoked Lethal Scheme connives.
|
||||||
this.getSpellAbility().addWatcher(new EachCreatureThatConvokedSourceWatcher());
|
|
||||||
this.getSpellAbility().addEffect(new LethalSchemeEffect());
|
this.getSpellAbility().addEffect(new LethalSchemeEffect());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +53,12 @@ public final class LethalScheme extends CardImpl {
|
||||||
// Based loosely on "Venerated Loxodon" and "Change of Plans"
|
// Based loosely on "Venerated Loxodon" and "Change of Plans"
|
||||||
class LethalSchemeEffect extends OneShotEffect {
|
class LethalSchemeEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter = new FilterCreaturePermanent();
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(ConvokedSourcePredicate.SPELL);
|
||||||
|
}
|
||||||
|
|
||||||
public LethalSchemeEffect() {
|
public LethalSchemeEffect() {
|
||||||
super(Outcome.Benefit);
|
super(Outcome.Benefit);
|
||||||
this.staticText = "Each creature that convoked Lethal Scheme connives.";
|
this.staticText = "Each creature that convoked Lethal Scheme connives.";
|
||||||
|
@ -70,22 +75,10 @@ class LethalSchemeEffect extends OneShotEffect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
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 =
|
Set<AbstractMap.SimpleEntry<UUID, Permanent>> playerPermanentsPairs =
|
||||||
creatures
|
game.getBattlefield()
|
||||||
|
.getActivePermanents(filter, source.getControllerId(), source, game)
|
||||||
.stream()
|
.stream()
|
||||||
.map(creatureMOR -> creatureMOR.getPermanentOrLKIBattlefield(game))
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.map(permanent -> new AbstractMap.SimpleEntry<>(permanent.getControllerId(), permanent))
|
.map(permanent -> new AbstractMap.SimpleEntry<>(permanent.getControllerId(), permanent))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
package mage.cards.v;
|
package mage.cards.v;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.MageObjectReference;
|
|
||||||
import mage.abilities.Ability;
|
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.common.counter.AddCountersAllEffect;
|
||||||
import mage.abilities.keyword.ConvokeAbility;
|
import mage.abilities.keyword.ConvokeAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
import mage.game.Game;
|
import mage.filter.FilterPermanent;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.watchers.common.EachCreatureThatConvokedSourceWatcher;
|
import mage.filter.predicate.permanent.ConvokedSourcePredicate;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public final class VeneratedLoxodon extends CardImpl {
|
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) {
|
public VeneratedLoxodon(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}");
|
||||||
|
|
||||||
|
@ -36,7 +38,9 @@ public final class VeneratedLoxodon extends CardImpl {
|
||||||
this.addAbility(new ConvokeAbility());
|
this.addAbility(new ConvokeAbility());
|
||||||
|
|
||||||
// When Venerated Loxodon enters the battlefield, put a +1/+1 counter on each creature that convoked it.
|
// 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) {
|
private VeneratedLoxodon(final VeneratedLoxodon card) {
|
||||||
|
@ -48,40 +52,3 @@ public final class VeneratedLoxodon extends CardImpl {
|
||||||
return new VeneratedLoxodon(this);
|
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("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("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("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("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 Lancer", 45, Rarity.UNCOMMON, mage.cards.z.ZhalfirinLancer.class));
|
||||||
cards.add(new SetCardInfo("Zhalfirin Shapecraft", 87, Rarity.COMMON, mage.cards.z.ZhalfirinShapecraft.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.players.Player;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
import mage.target.common.TargetControlledCreaturePermanent;
|
import mage.target.common.TargetControlledCreaturePermanent;
|
||||||
|
import mage.watchers.common.ConvokeWatcher;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -79,6 +80,7 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana
|
||||||
public ConvokeAbility() {
|
public ConvokeAbility() {
|
||||||
super(Zone.ALL, null); // all AlternateManaPaymentAbility must use ALL zone to calculate playable abilities
|
super(Zone.ALL, null); // all AlternateManaPaymentAbility must use ALL zone to calculate playable abilities
|
||||||
this.setRuleAtTheTop(true);
|
this.setRuleAtTheTop(true);
|
||||||
|
this.addWatcher(new ConvokeWatcher());
|
||||||
this.addHint(new ValueHint("Untapped creatures you control", new PermanentsOnBattlefieldCount(filterUntapped)));
|
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
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class EachCreatureThatConvokedSourceWatcher extends Watcher {
|
public class ConvokeWatcher extends Watcher {
|
||||||
|
|
||||||
private final Map<MageObjectReference, Set<MageObjectReference>> convokingCreatures = new HashMap<>();
|
private final Map<MageObjectReference, Set<MageObjectReference>> convokingCreatures = new HashMap<>();
|
||||||
|
|
||||||
public EachCreatureThatConvokedSourceWatcher() {
|
public ConvokeWatcher() {
|
||||||
super(WatcherScope.GAME);
|
super(WatcherScope.GAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,26 +29,28 @@ public class EachCreatureThatConvokedSourceWatcher extends Watcher {
|
||||||
if (event.getType() != GameEvent.EventType.CONVOKED) {
|
if (event.getType() != GameEvent.EventType.CONVOKED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Spell spell = game.getSpell(event.getSourceId());
|
Spell spell = game.getSpell(event.getSourceId());
|
||||||
Permanent tappedCreature = game.getPermanentOrLKIBattlefield(event.getTargetId());
|
Permanent tappedCreature = game.getPermanentOrLKIBattlefield(event.getTargetId());
|
||||||
if (spell == null || tappedCreature == null) {
|
if (spell == null || tappedCreature == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
convokingCreatures
|
||||||
MageObjectReference convokedSpell = new MageObjectReference(spell.getSourceId(), game);
|
.computeIfAbsent(new MageObjectReference(spell.getSourceId(), game), x -> new HashSet<>())
|
||||||
Set<MageObjectReference> creatures;
|
.add(new MageObjectReference(tappedCreature, game));
|
||||||
if (convokingCreatures.containsKey(convokedSpell)) {
|
|
||||||
creatures = convokingCreatures.get(convokedSpell);
|
|
||||||
} else {
|
|
||||||
creatures = new HashSet<>();
|
|
||||||
convokingCreatures.put(convokedSpell, creatures);
|
|
||||||
}
|
|
||||||
creatures.add(new MageObjectReference(tappedCreature, game));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<MageObjectReference> getConvokingCreatures(MageObjectReference mor) {
|
public static Set<MageObjectReference> getConvokingCreatures(MageObjectReference mor, Game game) {
|
||||||
return convokingCreatures.get(mor);
|
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
|
@Override
|
Loading…
Reference in a new issue