forked from External/mage
[WOC] Implement Korvold, Gleeful Glutton (#11072)
This commit is contained in:
parent
5fd8255a8a
commit
f9708c663e
3 changed files with 224 additions and 0 deletions
166
Mage.Sets/src/mage/cards/k/KorvoldGleefulGlutton.java
Normal file
166
Mage.Sets/src/mage/cards/k/KorvoldGleefulGlutton.java
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
package mage.cards.k;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.watchers.Watcher;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Xanderhall
|
||||
*/
|
||||
public final class KorvoldGleefulGlutton extends CardImpl {
|
||||
|
||||
public KorvoldGleefulGlutton(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{R}{G}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.DRAGON);
|
||||
this.subtype.add(SubType.NOBLE);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// This spell costs {1} less to cast for each card type among permanents you've sacrificed this turn.
|
||||
Ability staticAbility = new SimpleStaticAbility(Zone.ALL,
|
||||
new SpellCostReductionSourceEffect(CardTypesAmongSacrificedPermanentsCount.instance)
|
||||
.setText("this spell costs {1} less to cast for each card type among permanents you've sacrificed this turn")
|
||||
);
|
||||
staticAbility.addHint(new ValueHint("Card types among permanents you've sacrificed this turn", CardTypesAmongSacrificedPermanentsCount.instance));
|
||||
|
||||
this.addAbility(staticAbility, new KorvoldGleefulGluttonWatcher());
|
||||
|
||||
// Flying, Trample, Haste
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
this.addAbility(TrampleAbility.getInstance());
|
||||
this.addAbility(HasteAbility.getInstance());
|
||||
|
||||
// Whenever Korvold deals combat damage to a player, put X +1/+1 counters on Korvold and draw X cards, where X is the number of permanent types among cards in your graveyard.
|
||||
Ability combatDamageAbility = new DealsCombatDamageToAPlayerTriggeredAbility(
|
||||
new AddCountersSourceEffect(CounterType.P1P1.createInstance(), PermanentTypesInGraveyardCount.instance, true),
|
||||
false
|
||||
);
|
||||
combatDamageAbility.addEffect(new DrawCardSourceControllerEffect(PermanentTypesInGraveyardCount.instance).setText("and draw X cards, where X is the number of permanent types among cards in your graveyard"));
|
||||
combatDamageAbility.addHint(new ValueHint("Permanent types among cards in your graveyard", PermanentTypesInGraveyardCount.instance));
|
||||
this.addAbility(combatDamageAbility);
|
||||
}
|
||||
|
||||
private KorvoldGleefulGlutton(final KorvoldGleefulGlutton card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KorvoldGleefulGlutton copy() {
|
||||
return new KorvoldGleefulGlutton(this);
|
||||
}
|
||||
}
|
||||
|
||||
class KorvoldGleefulGluttonWatcher extends Watcher {
|
||||
|
||||
private final Map<UUID, Set<CardType>> map = new HashMap<>();
|
||||
|
||||
KorvoldGleefulGluttonWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() != GameEvent.EventType.SACRIFICED_PERMANENT) {
|
||||
return;
|
||||
}
|
||||
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
|
||||
|
||||
if (permanent != null) {
|
||||
permanent.getCardType(game).forEach(type ->
|
||||
map.computeIfAbsent(event.getPlayerId(), (key) -> new HashSet<>()).add(type)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
map.clear();
|
||||
}
|
||||
|
||||
public int getNumberOfTypes(UUID playerId) {
|
||||
return map.computeIfAbsent(playerId, (key) -> new HashSet<>()).size();
|
||||
};
|
||||
}
|
||||
|
||||
enum CardTypesAmongSacrificedPermanentsCount implements DynamicValue {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
KorvoldGleefulGluttonWatcher watcher = game.getState().getWatcher(KorvoldGleefulGluttonWatcher.class);
|
||||
return watcher == null ? 0 : watcher.getNumberOfTypes(sourceAbility.getControllerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardTypesAmongSacrificedPermanentsCount copy() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "card types among permanents you sacrificed";
|
||||
}
|
||||
}
|
||||
|
||||
enum PermanentTypesInGraveyardCount implements DynamicValue {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public PermanentTypesInGraveyardCount copy() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
Player player = game.getPlayer(sourceAbility.getControllerId());
|
||||
if (player == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return player.getGraveyard().getCards(game).stream()
|
||||
.map(card -> card.getCardType(game))
|
||||
.flatMap(types -> types.stream().filter(CardType::isPermanentType))
|
||||
.distinct()
|
||||
.mapToInt(x -> 1)
|
||||
.sum();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "permanent types among cards in your graveyard";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -81,6 +81,7 @@ public final class WildsOfEldraineCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Kindred Dominance", 113, Rarity.RARE, mage.cards.k.KindredDominance.class));
|
||||
cards.add(new SetCardInfo("Knickknack Ouphe", 18, Rarity.RARE, mage.cards.k.KnickknackOuphe.class));
|
||||
cards.add(new SetCardInfo("Kor Spiritdancer", 69, Rarity.RARE, mage.cards.k.KorSpiritdancer.class));
|
||||
cards.add(new SetCardInfo("Korvold, Gleeful Glutton", 26, Rarity.MYTHIC, mage.cards.k.KorvoldGleefulGlutton.class));
|
||||
cards.add(new SetCardInfo("Krosan Verge", 163, Rarity.UNCOMMON, mage.cards.k.KrosanVerge.class));
|
||||
cards.add(new SetCardInfo("Loamcrafter Faun", 19, Rarity.RARE, mage.cards.l.LoamcrafterFaun.class));
|
||||
cards.add(new SetCardInfo("Malleable Impostor", 10, Rarity.RARE, mage.cards.m.MalleableImpostor.class));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
package org.mage.test.cards.single.woc;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
|
||||
public class KorvoldGleefulGluttonTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
This spell costs {1} less to cast for each card type among permanents you’ve sacrificed this turn.
|
||||
Flying, trample, haste
|
||||
Whenever Korvold deals combat damage to a player, put X +1/+1 counters on Korvold and draw X cards, where X is the number of permanent types among cards in your graveyard.
|
||||
*/
|
||||
private final String KORVOLD = "Korvold, Gleeful Glutton";
|
||||
private final String OVEN = "Witch's Oven";
|
||||
private final String CAT = "Cauldron Familiar";
|
||||
private final String DOG = "Spirited Companion";
|
||||
|
||||
@Test
|
||||
public void testEffects() {
|
||||
addCard(Zone.HAND, playerA, KORVOLD);
|
||||
addCard(Zone.BATTLEFIELD, playerA, OVEN);
|
||||
addCard(Zone.BATTLEFIELD, playerA, DOG);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||
addCard(Zone.GRAVEYARD, playerA, CAT);
|
||||
|
||||
// Sacrifice dog to oven
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice");
|
||||
setChoice(playerA, DOG);
|
||||
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
// Sacrifice food to cat
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sacrifice a Food");
|
||||
setChoice(playerA, "Food Token");
|
||||
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
// Korvold should be reduced by 3 (enchantment, creature, artifact sacrificed)
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, KORVOLD);
|
||||
|
||||
// Korvold attacks, effect should trigger for X=2 because of dog
|
||||
attack(1, playerA, KORVOLD);
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertCounterCount(KORVOLD, CounterType.P1P1, 2);
|
||||
assertHandCount(playerA, 2);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue