Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions Mage.Sets/src/mage/cards/z/ZinniaValleysVoice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package mage.cards.z;

import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledSpellsEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.OffspringAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.common.FilterNonlandCard;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.filter.predicate.mageobject.BasePowerPredicate;
import java.util.UUID;

import static mage.abilities.dynamicvalue.common.StaticValue.get;
import static mage.constants.Duration.WhileOnBattlefield;


/**
* Zinnia, Valley's Voice
*
* Legendary Creature — Bird Bard
*
* Flying
* Zinnia, Valley's Voice gets +X/+0, where X is the number of other creatures
* you control with base power 1.
* Creature spells you cast have offspring {2}.
*
* @author DreamWaker and sneddigrolyat
*/
public final class ZinniaValleysVoice extends CardImpl {

// "other creatures you control with base power 1"
private static final FilterCreaturePermanent bfilter = new FilterCreaturePermanent("other creatures you control with base power 1");

static {
bfilter.add(new BasePowerPredicate(ComparisonType.EQUAL_TO, 1));
bfilter.add(TargetController.YOU.getControllerPredicate());
bfilter.add(AnotherPredicate.instance);
}

private static final PermanentsOnBattlefieldCount bcount = new PermanentsOnBattlefieldCount(bfilter);

// "creature spells you cast"
static final FilterNonlandCard cfilter = new FilterNonlandCard("creature spells you cast");

static {
cfilter.add(CardType.CREATURE.getPredicate());
}


public ZinniaValleysVoice(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{R}{W}");

this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.BIRD);
this.subtype.add(SubType.BARD);
this.power = new MageInt(1);
this.toughness = new MageInt(3);

// Flying
this.addAbility(FlyingAbility.getInstance());

// Creature spells you cast have offspring {2}.
this.addAbility(new SimpleStaticAbility(
new GainAbilityControlledSpellsEffect(new OffspringAbility("{2}"), cfilter)
.setText("Creature spells you cast have offspring {2}.")
));

// Zinnia, Valley's Voice gets +X/+0,
// where X is the number of other creatures you control with base power 1.
this.addAbility(new SimpleStaticAbility(
new BoostSourceEffect(bcount, get(0), WhileOnBattlefield)
));
}

private ZinniaValleysVoice(final ZinniaValleysVoice card) {
super(card);
}

@Override
public ZinniaValleysVoice copy() {
return new ZinniaValleysVoice(this);
}
}

1 change: 1 addition & 0 deletions Mage.Sets/src/mage/sets/BloomburrowCommander.java
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ private BloomburrowCommander() {
cards.add(new SetCardInfo("Wooded Ridgeline", 353, Rarity.COMMON, mage.cards.w.WoodedRidgeline.class));
cards.add(new SetCardInfo("Woodland Cemetery", 354, Rarity.RARE, mage.cards.w.WoodlandCemetery.class));
cards.add(new SetCardInfo("Yavimaya Coast", 355, Rarity.RARE, mage.cards.y.YavimayaCoast.class));
cards.add(new SetCardInfo("Zinnia, Valley's Voice", 4, Rarity.MYTHIC, mage.cards.z.ZinniaValleysVoice.class));
cards.add(new SetCardInfo("Zulaport Cutthroat", 190, Rarity.UNCOMMON, mage.cards.z.ZulaportCutthroat.class));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package org.mage.test.cards.abilities.keywords;

import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.continuous.GainAbilityControlledSpellsEffect;
import mage.abilities.keyword.OffspringAbility;
import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.filter.common.FilterNonlandCard;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import org.junit.Assert;
Expand All @@ -14,6 +19,8 @@
public class OffspringTest extends CardTestPlayerBase {

private static final String vinelasher = "Iridescent Vinelasher";
private static final String bandit = "Prosperous Bandit";
private static final String lion = "Silvercoat Lion";

private Permanent getCreature(String name, boolean isToken) {
for (Permanent permanent : currentGame.getBattlefield().getActivePermanents(playerA.getId(), currentGame)) {
Expand Down Expand Up @@ -80,4 +87,151 @@ public void testPay() {

checkOffspring(vinelasher, 1, 2, true);
}

@Test
public void testHumilityInResponseNoCopy() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.HAND, playerA, vinelasher);

addCard(Zone.BATTLEFIELD, playerB, "Plains", 4);
addCard(Zone.BATTLEFIELD, playerB, "Vedalken Orrery");
addCard(Zone.HAND, playerB, "Humility");

setChoice(playerA, true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vinelasher);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Humility", true);

setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();

assertPermanentCount(playerA, vinelasher, 1);
assertTokenCount(playerA, vinelasher, 0);
assertPowerToughness(playerA, vinelasher, 1, 1);
}

@Test
public void testHumilityInResponseNoCopyWithPrintedAndGrantedOffspring() {
addCard(Zone.BATTLEFIELD, playerA, "Zinnia, Valley's Voice");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
addCard(Zone.HAND, playerA, vinelasher);

addCard(Zone.BATTLEFIELD, playerB, "Plains", 4);
addCard(Zone.BATTLEFIELD, playerB, "Vedalken Orrery");
addCard(Zone.HAND, playerB, "Humility");

setChoice(playerA, true);
setChoice(playerA, true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vinelasher);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Humility", true);

setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();

assertPermanentCount(playerA, vinelasher, 1);
assertTokenCount(playerA, vinelasher, 0);
assertPowerToughness(playerA, vinelasher, 1, 1);
}

@Test
public void testHumilityInResponseNoCopyWithPrintedAndGrantedOffspringFromNonCreatureSource() {
FilterNonlandCard creatureSpells = new FilterNonlandCard("creature spells");
creatureSpells.add(CardType.CREATURE.getPredicate());
addCustomCardWithAbility(
"offspring grant source",
playerA,
new SimpleStaticAbility(
Zone.BATTLEFIELD,
new GainAbilityControlledSpellsEffect(new OffspringAbility("{2}"), creatureSpells)
),
null,
CardType.ENCHANTMENT,
"",
Zone.BATTLEFIELD
);

addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
addCard(Zone.HAND, playerA, vinelasher);

addCard(Zone.BATTLEFIELD, playerB, "Plains", 4);
addCard(Zone.BATTLEFIELD, playerB, "Vedalken Orrery");
addCard(Zone.HAND, playerB, "Humility");

// pay both offspring costs so the granted delayed trigger is definitely created
setChoice(playerA, true);
setChoice(playerA, true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vinelasher);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Humility", true);

setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();

assertPermanentCount(playerA, vinelasher, 1);
assertTokenCount(playerA, vinelasher, 0);
assertPowerToughness(playerA, vinelasher, 1, 1);
}

@Test
public void testGrantedOffspringSourceRemovedBeforeEtbNoCopy() {
addCard(Zone.BATTLEFIELD, playerA, "Zinnia, Valley's Voice");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
addCard(Zone.HAND, playerA, lion);
addCard(Zone.HAND, playerA, "Path to Exile");

setChoice(playerA, true);
setChoice(playerA, false);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lion);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Path to Exile", "Zinnia, Valley's Voice");

setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();

assertPermanentCount(playerA, lion, 1);
assertTokenCount(playerA, lion, 0);
}

@Test
public void testPrintedAndGrantedOffspringOnePayment() {
addCard(Zone.BATTLEFIELD, playerA, "Zinnia, Valley's Voice");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 6);
addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
addCard(Zone.HAND, playerA, bandit);

setChoice(playerA, true);
setChoice(playerA, false);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bandit);

setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();

assertPermanentCount(playerA, bandit, 2);
assertTokenCount(playerA, bandit, 1);
}

@Test
public void testPrintedAndGrantedOffspringTwoPayments() {
addCard(Zone.BATTLEFIELD, playerA, "Zinnia, Valley's Voice");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 6);
addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
addCard(Zone.HAND, playerA, bandit);

setChoice(playerA, true);
setChoice(playerA, true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bandit);
setChoice(playerA, "When this permanent enters");

setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();

assertPermanentCount(playerA, bandit, 3);
assertTokenCount(playerA, bandit, 2);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.mage.test.cards.single.blc;

import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;

public class ZinniaValleysVoiceTest extends CardTestPlayerBase {

private static final String zinnia = "Zinnia, Valley's Voice";
private static final String lion = "Silvercoat Lion";
private static final String bandit = "Prosperous Bandit";

@Test
public void testGrantsOffspringToCreatureSpells() {
addCard(Zone.BATTLEFIELD, playerA, zinnia);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.HAND, playerA, lion);

setChoice(playerA, true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lion);

setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();

assertPermanentCount(playerA, lion, 2);
assertTokenCount(playerA, lion, 1);
}

@Test
public void testGrantedOffspringSourceRemovedBeforeEtbNoCopy() {
addCard(Zone.BATTLEFIELD, playerA, zinnia);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
addCard(Zone.HAND, playerA, lion);
addCard(Zone.HAND, playerA, "Path to Exile");

setChoice(playerA, true);
setChoice(playerA, false);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lion);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Path to Exile", zinnia);

setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();

assertPermanentCount(playerA, lion, 1);
assertTokenCount(playerA, lion, 0);
}

@Test
public void testRemoveZinniaWhileOffspringTriggersOnStackBothStillResolve() {
addCard(Zone.BATTLEFIELD, playerA, zinnia);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 6);
addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
addCard(Zone.HAND, playerA, bandit);
addCard(Zone.BATTLEFIELD, playerB, "Plains");
addCard(Zone.HAND, playerB, "Path to Exile");

setChoice(playerA, true); // Pay printed offspring {1}
setChoice(playerA, true); // Pay granted offspring {2}
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bandit);
setChoice(playerA, "When this permanent enters");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Path to Exile", zinnia, "create a 1/1 token copy of it.");
setChoice(playerA, false); // Decline Path's basic land search

setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();

assertPermanentCount(playerA, zinnia, 0);
assertPermanentCount(playerA, bandit, 3);
assertTokenCount(playerA, bandit, 2);
}
}
Loading