It is currently 05 Nov 2025, 04:15
   
Text Size

Help implementing a card

Moderators: BetaSteward, noxx, jeffwadsworth, JayDi, TheElk801, LevelX, North, CCGHQ Admins

Re: Help implementing a card

Postby fireshoes » 21 Jul 2015, 12:21

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;
    }
}
User avatar
fireshoes
 
Posts: 536
Joined: 20 Aug 2014, 03:51
Has thanked: 201 times
Been thanked: 49 times

Re: Help implementing a card

Postby LevelX » 21 Jul 2015, 12:46

You need to add the continuous effect to the game.
It's no one shot effect that you can simply apply once.
User avatar
LevelX
DEVELOPER
 
Posts: 1677
Joined: 08 Dec 2011, 15:08
Has thanked: 174 times
Been thanked: 374 times

Re: Help implementing a card

Postby fireshoes » 21 Jul 2015, 16:06

Sent you a PM about some cards yesterday too, in case you didn't get a notification. :)
User avatar
fireshoes
 
Posts: 536
Joined: 20 Aug 2014, 03:51
Has thanked: 201 times
Been thanked: 49 times

Re: Help implementing a card

Postby EvilGeek » 21 Jul 2015, 17:27

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?
EvilGeek
 
Posts: 8
Joined: 27 Nov 2014, 16:56
Has thanked: 0 time
Been thanked: 0 time

Re: Help implementing a card

Postby LoneFox » 28 Jul 2015, 19:58

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?
LoneFox
Programmer
 
Posts: 71
Joined: 08 Mar 2009, 13:43
Has thanked: 0 time
Been thanked: 7 times

Re: Help implementing a card

Postby LevelX » 28 Jul 2015, 20:50

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."));
User avatar
LevelX
DEVELOPER
 
Posts: 1677
Joined: 08 Dec 2011, 15:08
Has thanked: 174 times
Been thanked: 374 times

Re: Help implementing a card

Postby LoneFox » 29 Jul 2015, 07:35

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())
LoneFox
Programmer
 
Posts: 71
Joined: 08 Mar 2009, 13:43
Has thanked: 0 time
Been thanked: 7 times

Re: Help implementing a card

Postby fireshoes » 29 Jul 2015, 18:05

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()));
User avatar
fireshoes
 
Posts: 536
Joined: 20 Aug 2014, 03:51
Has thanked: 201 times
Been thanked: 49 times

Re: Help implementing a card

Postby LevelX » 29 Jul 2015, 18:33

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.
User avatar
LevelX
DEVELOPER
 
Posts: 1677
Joined: 08 Dec 2011, 15:08
Has thanked: 174 times
Been thanked: 374 times

Re: Help implementing a card

Postby LoneFox » 03 Aug 2015, 08:25

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";
    }
}
LoneFox
Programmer
 
Posts: 71
Joined: 08 Mar 2009, 13:43
Has thanked: 0 time
Been thanked: 7 times

Re: Help implementing a card

Postby LevelX » 03 Aug 2015, 12:01

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;
    }
}
User avatar
LevelX
DEVELOPER
 
Posts: 1677
Joined: 08 Dec 2011, 15:08
Has thanked: 174 times
Been thanked: 374 times

Re: Help implementing a card

Postby LoneFox » 03 Aug 2015, 13:16

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.
LoneFox
Programmer
 
Posts: 71
Joined: 08 Mar 2009, 13:43
Has thanked: 0 time
Been thanked: 7 times

Re: Help implementing a card

Postby fireshoes » 03 Aug 2015, 18:35

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?
User avatar
fireshoes
 
Posts: 536
Joined: 20 Aug 2014, 03:51
Has thanked: 201 times
Been thanked: 49 times

Re: Help implementing a card

Postby LevelX » 03 Aug 2015, 20:52

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.
User avatar
LevelX
DEVELOPER
 
Posts: 1677
Joined: 08 Dec 2011, 15:08
Has thanked: 174 times
Been thanked: 374 times

Re: Help implementing a card

Postby EvilGeek » 04 Aug 2015, 16:17

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.
EvilGeek
 
Posts: 8
Joined: 27 Nov 2014, 16:56
Has thanked: 0 time
Been thanked: 0 time

PreviousNext

Return to Developers Talk

Who is online

Users browsing this forum: No registered users and 3 guests

Main Menu

User Menu

Our Partners


Who is online

In total there are 3 users online :: 0 registered, 0 hidden and 3 guests (based on users active over the past 10 minutes)
Most users ever online was 9298 on 10 Oct 2025, 12:54

Users browsing this forum: No registered users and 3 guests

Login Form