Skip to content
Closed
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));
}
}
199 changes: 114 additions & 85 deletions Mage/src/main/java/mage/abilities/keyword/OffspringAbility.java
Original file line number Diff line number Diff line change
@@ -1,38 +1,40 @@
package mage.abilities.keyword;

package mage.abilities.keyword;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.costs.*;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;

/**
* @author TheElk801
*/
public class OffspringAbility extends StaticAbility implements OptionalAdditionalSourceCosts {

private static final String keywordText = "Offspring";
private static final String reminderText = "You may pay an additional %s as you cast this spell. If you do, when this creature enters, create a 1/1 token copy of it.";
private final String rule;

public static final String OFFSPRING_ACTIVATION_VALUE_KEY = "offspringActivation";

protected OptionalAdditionalCost additionalCost;

public OffspringAbility(String manaString) {
this(new ManaCostsImpl<>(manaString));
}

import mage.target.targetpointer.FixedTarget;
/**
* @author TheElk801
*/
public class OffspringAbility extends StaticAbility implements OptionalAdditionalSourceCosts {
private static final String keywordText = "Offspring";
private static final String reminderText = "You may pay an additional %s as you cast this spell. If you do, when this creature enters, create a 1/1 token copy of it.";
private final String rule;
public static final String OFFSPRING_ACTIVATION_VALUE_KEY = "offspringActivation";
protected OptionalAdditionalCost additionalCost;
public OffspringAbility(String manaString) {
this(new ManaCostsImpl<>(manaString));
}
public OffspringAbility(Cost cost) {
super(Zone.STACK, null);
this.additionalCost = new OptionalAdditionalCostImpl(
Expand All @@ -42,89 +44,116 @@ public OffspringAbility(Cost cost) {
this.additionalCost.setRepeatable(false);
this.rule = additionalCost.getName() + ' ' + additionalCost.getReminderText();
this.setRuleAtTheTop(true);
this.addSubAbility(new EntersBattlefieldTriggeredAbility(new OffspringEffect())
.withInterveningIf(OffspringCondition.instance).setRuleVisible(false));
}

private OffspringAbility(final OffspringAbility ability) {
super(ability);
this.rule = ability.rule;
this.additionalCost = ability.additionalCost.copy();
}

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

@Override
public void addOptionalAdditionalCosts(Ability ability, Game game) {
if (!(ability instanceof SpellAbility)) {
return;
}
Player player = game.getPlayer(ability.getControllerId());
if (player == null) {
return;
}
additionalCost.reset();
if (!additionalCost.canPay(ability, this, ability.getControllerId(), game)
|| !player.chooseUse(Outcome.PutCreatureInPlay, "Pay " + additionalCost.getText(true) + " for offspring?", ability, game)) {
return;
}
additionalCost.activate();

private OffspringAbility(final OffspringAbility ability) {
super(ability);
this.rule = ability.rule;
this.additionalCost = ability.additionalCost.copy();
}

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

@Override
public void addOptionalAdditionalCosts(Ability ability, Game game) {
if (!(ability instanceof SpellAbility)) {
return;
}
Player player = game.getPlayer(ability.getControllerId());
if (player == null) {
return;
}
additionalCost.reset();
if (!additionalCost.canPay(ability, this, ability.getControllerId(), game)
|| !player.chooseUse(Outcome.PutCreatureInPlay, "Pay " + additionalCost.getText(true) + " for offspring?", ability, game)) {
return;
}
additionalCost.activate();
for (Cost cost : ((Costs<Cost>) additionalCost)) {
ability.getCosts().add(cost.copy());
}
ability.setCostsTag(OFFSPRING_ACTIVATION_VALUE_KEY, null);
game.addDelayedTriggeredAbility(new OffspringDelayedTriggeredAbility(), ability);
}

@Override
public String getCastMessageSuffix() {
return additionalCost.getCastSuffixMessage(0);
}


@Override
public String getCastMessageSuffix() {
return additionalCost.getCastSuffixMessage(0);
}

@Override
public String getRule() {
return rule;
}
}

class OffspringEffect extends OneShotEffect {

OffspringEffect() {
super(Outcome.Benefit);
staticText = "create a 1/1 token copy of it";
}

private OffspringEffect(final OffspringEffect effect) {
super(effect);
}

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

@Override
public String getRule() {
return rule;
public boolean apply(Game game, Ability source) {
Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source);
if (permanent == null) {
permanent = source.getSourcePermanentOrLKI(game);
}
return permanent != null && new CreateTokenCopyTargetEffect(
null, null, false, 1, false,
false, null, 1, 1, false
).setSavedPermanent(permanent).apply(game, source);
}
}

class OffspringEffect extends OneShotEffect {
class OffspringDelayedTriggeredAbility extends DelayedTriggeredAbility {

OffspringEffect() {
super(Outcome.Benefit);
staticText = "create a 1/1 token copy of it";
OffspringDelayedTriggeredAbility() {
super(new OffspringEffect(), Duration.EndOfTurn, true);
this.setRuleVisible(false);
}

private OffspringEffect(final OffspringEffect effect) {
super(effect);
private OffspringDelayedTriggeredAbility(final OffspringDelayedTriggeredAbility ability) {
super(ability);
}

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

@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = source.getSourcePermanentOrLKI(game);
return permanent != null && new CreateTokenCopyTargetEffect(
null, null, false, 1, false,
false, null, 1, 1, false
).setSavedPermanent(permanent).apply(game, source);
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
}
}

enum OffspringCondition implements Condition {
instance;

@Override
public boolean apply(Game game, Ability source) {
return CardUtil.checkSourceCostsTagExists(game, source, OffspringAbility.OFFSPRING_ACTIVATION_VALUE_KEY);
}

@Override
public String toString() {
return "its offspring cost was paid";
public boolean checkTrigger(GameEvent event, Game game) {
if (!event.getTargetId().equals(getSourceId())) {
return false;
}
if (!(event instanceof EntersTheBattlefieldEvent)) {
return false;
}
Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget();
if (permanent == null
|| permanent.getZoneChangeCounter(game) != getStackMomentSourceZCC() + 1) {
return false;
}
getEffects().setTargetPointer(new FixedTarget(permanent, game));
return true;
}
}