Page 7 of 13

Re: Help implementing a card

PostPosted: 21 Jul 2015, 12:21
by fireshoes
Any hints as to why BoostOpponentsEffect isn't doing anything when Sickness wins the vote on Bite of the Black Rose?
Code: Select all
public class BiteOfTheBlackRose extends CardImpl {

    public BiteOfTheBlackRose(UUID ownerId) {
        super(ownerId, 26, "Bite of the Black Rose", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{B}");
        this.expansionSetCode = "CNS";

        // Will of the council - Starting with you, each player votes for sickness or psychosis. If sickness gets more votes, creatures your opponents control get -2/-2 until end of turn. If psychosis gets more votes or the vote is tied, each opponent discards two cards.
        this.getSpellAbility().addEffect(new BiteOfTheBlackRoseEffect());
    }

    public BiteOfTheBlackRose(final BiteOfTheBlackRose card) {
        super(card);
    }

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

class BiteOfTheBlackRoseEffect extends OneShotEffect {

    BiteOfTheBlackRoseEffect() {
        super(Outcome.Benefit);
        this.staticText = "<i>Will of the council</i> - Starting with you, each player votes for sickness or psychosis. If sickness gets more votes, creatures your opponents control get -2/-2 until end of turn. If psychosis gets more votes or the vote is tied, each opponent discards two cards";
    }

    BiteOfTheBlackRoseEffect(final BiteOfTheBlackRoseEffect effect) {
        super(effect);
    }

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

    @Override
    public boolean apply(Game game, Ability source) {
        Player controller = game.getPlayer(source.getControllerId());
        if (controller != null) {
            int sicknessCount = 0;
            int psychosisCount = 0;
            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
                Player player = game.getPlayer(playerId);
                if (player != null) {
                    if (player.chooseUse(Outcome.ExtraTurn, "Choose sickness?", source, game)) {
                        sicknessCount++;
                        game.informPlayers(player.getLogName() + " has chosen: sickness");
                    } else {
                        psychosisCount++;
                        game.informPlayers(player.getLogName() + " has chosen: psychosis");
                    }
                }
            }
            if (sicknessCount > psychosisCount) {
                new BoostOpponentsEffect(-2, -2, Duration.EndOfTurn).apply(game, source);
            } else {
                new DiscardEachPlayerEffect(new StaticValue(2), false, TargetController.OPPONENT).apply(game, source);
            }
            return true;
        }
        return false;
    }
}

Re: Help implementing a card

PostPosted: 21 Jul 2015, 12:46
by LevelX
You need to add the continuous effect to the game.
It's no one shot effect that you can simply apply once.

Re: Help implementing a card

PostPosted: 21 Jul 2015, 16:06
by fireshoes
Sent you a PM about some cards yesterday too, in case you didn't get a notification. :)

Re: Help implementing a card

PostPosted: 21 Jul 2015, 17:27
by EvilGeek
I'm having trouble with the card Override. Its name is conflicting with the @Override annotation and my IDE doesn't like it (I use Eclipse btw). Just wondering if anyone has a fix, is it possible to have the class name different to the card name but still have it work normally?

Re: Help implementing a card

PostPosted: 28 Jul 2015, 19:58
by LoneFox
Code: Select all
public class ScorchingLava extends CardImpl {

    public ScorchingLava(UUID ownerId) {
        super(ownerId, 164, "Scorching Lava", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{R}");
        this.expansionSetCode = "INV";

        // Kicker {R}
        this.addAbility(new KickerAbility("{R}"));
        // Scorching Lava deals 2 damage to target creature or player. If Scorching Lava was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead.
        this.getSpellAbility().addEffect(new DamageTargetEffect(2));
        this.getSpellAbility().addEffect(new ConditionalContinuousRuleModifyingEffect(
            new CantRegenerateTargetEffect(Duration.EndOfTurn, "That creature"), KickedCondition.getInstance()));
        Effect effect = new ConditionalReplacementEffect(new DealtDamageToCreatureBySourceDies(this, Duration.EndOfTurn),
            KickedCondition.getInstance());
        effect.setText("and if it would die this turn, exile it instead");
        this.getSpellAbility().addEffect(effect);
        this.getSpellAbility().addTarget(new TargetCreatureOrPlayer());
        this.getSpellAbility().addWatcher(new DamagedByWatcher());
    }

    public ScorchingLava(final ScorchingLava card) {
        super(card);
    }

    @Override
    public ScorchingLava copy() {
        return new ScorchingLava(this);
    }
}
Why is the kicker not working here?

Re: Help implementing a card

PostPosted: 28 Jul 2015, 20:50
by LevelX
Look e.g. to Savage Offensive to see how to do it.

Code: Select all
// If Savage Offensive was kicked, they get +1/+1 until end of turn.
   this.getSpellAbility().addEffect(new ConditionalOneShotEffect(
      new AddContinuousEffectToGame(new BoostControlledEffect(1, 1, Duration.EndOfTurn)),
      KickedCondition.getInstance(),
      "If {this} was kicked, they get +1/+1 until end of turn."));

Re: Help implementing a card

PostPosted: 29 Jul 2015, 07:35
by LoneFox
Turns out I was using a buggy card (Explosive Growth) as reference...
The simplest solution appears to be:
Code: Select all
new LockedInCondition(KickedCondition.getInstance())

Re: Help implementing a card

PostPosted: 29 Jul 2015, 18:05
by fireshoes
Feel like I'm missing something simple here. The exiled cards don't return when the second ability is used. It's same effect as Helvault, except Helvault is triggered while this activated.

Code: Select all
        // {3}: Exile target creature you control.
        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetForSourceEffect(), new GenericManaCost(3));
        ability.addTarget(new TargetControlledCreaturePermanent());
        this.addAbility(ability);
       
        // Sacrifice Cold Storage: Return each creature card exiled with Cold Storage to the battlefield under your control.
        this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), new SacrificeSourceCost()));

Re: Help implementing a card

PostPosted: 29 Jul 2015, 18:33
by LevelX
LoneFox wrote:Turns out I was using a buggy card (Explosive Growth) as reference...
The simplest solution appears to be:
Code: Select all
new LockedInCondition(KickedCondition.getInstance())
I prefer the Savage Offensive solution because there is no conditional effect overhead for the duration of the continuous effect.

Re: Help implementing a card

PostPosted: 03 Aug 2015, 08:25
by LoneFox
Dralnu's Pet. I did find the DiscardCardCost under a large pile of object-oriented spaghetti, but why is the discarded card not stored in the cost? Trying to access it throws IndexOutOfBoundsException.
Code: Select all
public class DralnusPet extends CardImpl {

    public DralnusPet(UUID ownerId) {
        super(ownerId, 23, "Dralnu's Pet", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{1}{U}{U}");
        this.expansionSetCode = "PLS";
        this.subtype.add("Shapeshifter");
        this.power = new MageInt(2);
        this.toughness = new MageInt(2);

        // Kicker-{2}{B}, Discard a creature card.
        Costs<Cost> kickerCosts = new CostsImpl<>();
        kickerCosts.add(new ManaCostsImpl<>("{2}{B}"));
        kickerCosts.add(new DiscardCardCost(new FilterCreatureCard()));
        this.addAbility(new KickerAbility(kickerCosts));
        // If Dralnu's Pet was kicked, it enters the battlefield with flying and with X +1/+1 counters on it, where X is the discarded card's converted mana cost.
        Ability ability = new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(),
            new DralnusPetDiscardCMC(), true), KickedCondition.getInstance(), true,
            "If {this} was kicked, it enters the battlefield with flying and with X +1/+1 counters on it, where X is the discarded card's converted mana cost", "");
        ability.addEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield));
        this.addAbility(ability);
    }

    public DralnusPet(final DralnusPet card) {
        super(card);
    }

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


class DralnusPetDiscardCMC implements DynamicValue {

    public DralnusPetDiscardCMC() {
    }

    @Override
    public int calculate(Game game, Ability source, Effect effect) {
        int count = 0;
        Card card = game.getCard(source.getSourceId());
        if(card != null) {
            for(Ability ability: card.getAbilities()) {
                if(ability instanceof KickerAbility) {
                    for(OptionalAdditionalCost cost1: ((KickerAbility)ability).getKickerCosts()) {
                        for(Object cost2: (OptionalAdditionalCostImpl)cost1) {
                             for(Object cost3: (CostsImpl)cost2) {
                                if(cost3 instanceof DiscardCardCost) {
                                    return ((DiscardCardCost)cost3).getCards().get(0).getManaCost().convertedManaCost();
                                }
                            }
                        }
                    }
                }
            }
        }
        return 0;
    }

    @Override
    public DralnusPetDiscardCMC copy() {
        return new DralnusPetDiscardCMC();
    }

    @Override
    public String getMessage() {
        return "the discarded card's converted mana cost";
    }
}

Re: Help implementing a card

PostPosted: 03 Aug 2015, 12:01
by LevelX
LoneFox wrote:Dralnu's Pet. I did find the DiscardCardCost under a large pile of object-oriented spaghetti, but why is the discarded card not stored in the cost? Trying to access it throws IndexOutOfBoundsException.
Problem here is that the payed kicker cost is payed with the spell ability and is only saved there.
So you have to do it the way it's done for cards like Protean Hydra and get the spell ability.

Here's the code that works:

Code: Select all
public class DralnusPet extends CardImpl {

    public DralnusPet(UUID ownerId) {
        super(ownerId, 23, "Dralnu's Pet", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{1}{U}{U}");
        this.expansionSetCode = "PLS";
        this.subtype.add("Shapeshifter");
        this.power = new MageInt(2);
        this.toughness = new MageInt(2);

        // Kicker-{2}{B}, Discard a creature card.
        Costs<Cost> kickerCosts = new CostsImpl<>();
        kickerCosts.add(new ManaCostsImpl<>("{2}{B}"));
        kickerCosts.add(new DiscardCardCost(new FilterCreatureCard()));
        this.addAbility(new KickerAbility(kickerCosts));
        // If Dralnu's Pet was kicked, it enters the battlefield with flying and with X +1/+1 counters on it, where X is the discarded card's converted mana cost.
        Ability ability = new EntersBattlefieldAbility(new DralnusPetEffect(), KickedCondition.getInstance(), true,
                "If {this} was kicked, it enters the battlefield with flying and with X +1/+1 counters on it, where X is the discarded card's converted mana cost", "");
        ability.addEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield));
        this.addAbility(ability);
    }

    public DralnusPet(final DralnusPet card) {
        super(card);
    }

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

class DralnusPetEffect extends OneShotEffect {

    public DralnusPetEffect() {
        super(Outcome.BoostCreature);
        this.staticText = "and with X +1/+1 counters on it, where X is the discarded card's converted mana cost";
    }

    public DralnusPetEffect(final DralnusPetEffect effect) {
        super(effect);
    }

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

    @Override
    public boolean apply(Game game, Ability source) {
        Player controller = game.getPlayer(source.getControllerId());
        Permanent permanent = game.getPermanent(source.getSourceId());
        if (controller != null && permanent != null) {
            Object obj = getValue(EntersBattlefieldEffect.SOURCE_CAST_SPELL_ABILITY);
            if (obj != null && obj instanceof SpellAbility) {
                int cmc = 0;
                for (Cost cost : ((SpellAbility) obj).getCosts()) {
                    if (cost instanceof DiscardCardCost && ((DiscardCardCost) cost).getCards().size() > 0) {
                        cmc = ((DiscardCardCost) cost).getCards().get(0).getManaCost().convertedManaCost();
                    }
                    if (cmc > 0) {
                        return new AddCountersSourceEffect(CounterType.P1P1.createInstance(cmc), true).apply(game, source);
                    }
                }
            }
            return true;
        }
        return false;
    }
}

Re: Help implementing a card

PostPosted: 03 Aug 2015, 13:16
by LoneFox
LevelX wrote:Problem here is that the payed kicker cost is payed with the spell ability and is only saved there.
So you have to do it the way it's done for cards like Protean Hydra and get the spell ability.

Here's the code that works:
Thanks! I pushed it and a few other new cards to my repository.

Re: Help implementing a card

PostPosted: 03 Aug 2015, 18:35
by fireshoes
fireshoes wrote:Feel like I'm missing something simple here. The exiled cards don't return when the second ability is used. It's same effect as Helvault, except Helvault is triggered while this activated.

Code: Select all
        // {3}: Exile target creature you control.
        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetForSourceEffect(), new GenericManaCost(3));
        ability.addTarget(new TargetControlledCreaturePermanent());
        this.addAbility(ability);
       
        // Sacrifice Cold Storage: Return each creature card exiled with Cold Storage to the battlefield under your control.
        this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), new SacrificeSourceCost()));
Got any hints for this one?

Re: Help implementing a card

PostPosted: 03 Aug 2015, 20:52
by LevelX
fireshoes wrote:
fireshoes wrote:Feel like I'm missing something simple here. The exiled cards don't return when the second ability is used. It's same effect as Helvault, except Helvault is triggered while this activated.

Code: Select all
        // {3}: Exile target creature you control.
        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetForSourceEffect(), new GenericManaCost(3));
        ability.addTarget(new TargetControlledCreaturePermanent());
        this.addAbility(ability);
       
        // Sacrifice Cold Storage: Return each creature card exiled with Cold Storage to the battlefield under your control.
        this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), new SacrificeSourceCost()));
Got any hints for this one?
I guess you can set previousZone to true for ReturnFromExileForSourceEffect. Because you sacrifice the Storage to activate the ability the zone change counter is already raised during resolution.

But I'm sure there are still counter cases where the return effect won't work.
E.g. the Storage was removed from graveyard meanwhile.

I guess it's better to use ReturnFromExileEffect with a fixed exileId related to the zoneChangeCounter of the activatedAbilities.

Re: Help implementing a card

PostPosted: 04 Aug 2015, 16:17
by EvilGeek
EvilGeek wrote:I'm having trouble with the card Override. Its name is conflicting with the @Override annotation and my IDE doesn't like it (I use Eclipse btw). Just wondering if anyone has a fix, is it possible to have the class name different to the card name but still have it work normally?
Still stuck on this.