[WOC] Implement Korvold, Gleeful Glutton (#11072)

This commit is contained in:
Vivian Greenslade 2023-08-31 21:30:03 -02:30 committed by GitHub
parent 5fd8255a8a
commit f9708c663e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 224 additions and 0 deletions

View 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";
}
}

View file

@ -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));

View file

@ -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 youve 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);
}
}