It is currently 14 May 2025, 16:09
   
Text Size

Adding triggered abilities to spells as they're cast

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

Adding triggered abilities to spells as they're cast

Postby duncancmt » 16 Mar 2015, 01:07

I'm trying to implement Wort, the Raidmother (and I figure that I'll go ahead and implement Djinn Illuminatus and Thrumming Stone while I'm at it). After looking at Mycosynth Golem and Chief Engineer as examples, it seems like the correct approach is to abuse the replacement effect API to modify the spell as it goes on the stack. (this seems like the wrong approach because there's no way to track the layer that this effect belongs in, but I don't see any other way to do it) However, when I do something similar with Wort, it seems that the trigger gets lost in between adding the ability to the spell and triggers being checked. I added some functionality to the Conspire ability to export the trigger that it adds to spells and then explicitly added that trigger to the game state. When the SPELL_CAST event happens at the end of spell casting, my new trigger has been removed from the GameState's triggers.

Any idea what's going on here? Thanks!
Attachments
WortTheRaidmother.java.txt
Mage.Sets/src/mage/sets/shadowmoor/WortTheRaidmother.java
(6.12 KiB) Downloaded 238 times
ConspireAbility.java.txt
Mage/src/mage/abilities/keyword/ConspireAbility.java
(10.42 KiB) Downloaded 208 times
duncancmt
 
Posts: 7
Joined: 18 Jan 2015, 19:55
Has thanked: 0 time
Been thanked: 0 time

Re: Adding triggered abilities to spells as they're cast

Postby jeffwadsworth » 16 Mar 2015, 19:06

I could write a book about my foray into Wort land, but let me give a brief summary of the saga. I got it to the point where the Conspire ability was on the card while it was on the stack, but the Conspire trigger refused to work. I tried all kinds of ways to work around the issue, but to no avail. I hope you can work it out. Also, I put your code into the code brackets for easier reading.

Code: Select all
/*
 *  Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without modification, are
 *  permitted provided that the following conditions are met:
 *
 *     1. Redistributions of source code must retain the above copyright notice, this list of
 *        conditions and the following disclaimer.
 *
 *     2. Redistributions in binary form must reproduce the above copyright notice, this list
 *        of conditions and the following disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
 *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  The views and conclusions contained in the software and documentation are those of the
 *  authors and should not be interpreted as representing official policies, either expressed
 *  or implied, of BetaSteward_at_googlemail.com.
 */
package mage.sets.shadowmoor;

import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbility;
import mage.constants.CardType;
import mage.constants.Rarity;

import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.keyword.ConspireAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.token.Token;

/**
 * @author duncant
 */
public class WortTheRaidmother extends CardImpl {

    public WortTheRaidmother(UUID ownerId) {
        super(ownerId, 223, "Wort, the Raidmother", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{4}{R/G}{R/G}");
        this.expansionSetCode = "SHM";
        this.supertype.add("Legendary");
        this.subtype.add("Goblin");
        this.subtype.add("Shaman");
        this.power = new MageInt(3);
        this.toughness = new MageInt(3);

        // When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield.
        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WortTheRaidmotherToken(), 2), false));

        // Each red or green instant or sorcery spell you cast has conspire.
        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AddConspireEffect()));
    }

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

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

class AddConspireEffect extends ReplacementEffectImpl {

    public AddConspireEffect() {
        super(Duration.WhileOnBattlefield, Outcome.AddAbility);
        staticText = "Each red or green instant or sorcery spell you cast has conspire. <i>(As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.)</i>";
    }

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

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

    @Override
    public boolean apply(Game game, Ability source) {
        return true;
    }

    @Override
    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
        Card card = (Card)game.getObject(event.getSourceId());
        if (card != null) {
            ConspireAbility ability = new ConspireAbility(card);
            card.addAbility(ability);
            ability.setControllerId(source.getControllerId());
            ability.setSourceId(card.getId());
            game.getState().addAbility(ability, source.getSourceId(), card);
            TriggeredAbility trigger = ability.getTrigger();
            trigger.setControllerId(source.getControllerId());
            trigger.setSourceId(card.getId());
            game.getState().addAbility(trigger, source.getSourceId(), card);
        }
        return false;
    }

    @Override
    public boolean checksEventType(GameEvent event, Game game) {
        return event.getType() == GameEvent.EventType.CAST_SPELL;
    }

    @Override
    public boolean applies(GameEvent event, Ability source, Game game) {
        if ((event.getType() == GameEvent.EventType.CAST_SPELL)
            && event.getPlayerId() == source.getControllerId()) {
            MageObject spellObject = game.getObject(event.getSourceId());
            if (spellObject != null &&
                (spellObject.getColor().contains(ObjectColor.RED) ||
                 spellObject.getColor().contains(ObjectColor.GREEN)) &&
                (spellObject.getCardType().contains(CardType.INSTANT) ||
                 spellObject.getCardType().contains(CardType.SORCERY))) {
                return true;
            }
        }
        return false;
    }
}


class WortTheRaidmotherToken extends Token {

    public WortTheRaidmotherToken() {
        super("Goblin Warrior", "1/1 red and green Goblin Warrior creature token");
        cardType.add(CardType.CREATURE);
        color.setRed(true);
        color.setGreen(true);
        subtype.add("Goblin");
        subtype.add("Warrior");
        power = new MageInt(1);
        toughness = new MageInt(1);
    }
}
jeffwadsworth
Super Tester Elite
 
Posts: 1172
Joined: 20 Oct 2010, 04:47
Location: USA
Has thanked: 287 times
Been thanked: 70 times

Re: Adding triggered abilities to spells as they're cast

Postby duncancmt » 16 Mar 2015, 22:38

I might try my hand with tweaking the larger architecture to support this kind of effect, because as it is now, there are 3 cards that can't be implemented at all (Wort, the Raidmother, Djinn Illuminatus, and Thrumming Stone) and 2 others (Chief Engineer, Mycosynth Golem) that are implemented "wrong" because their effects don't interface with the layers system.

If you still have access to your old Wort, the Raidmother code, I'd love to see it so that I can avoid your pitfalls.
duncancmt
 
Posts: 7
Joined: 18 Jan 2015, 19:55
Has thanked: 0 time
Been thanked: 0 time

Re: Adding triggered abilities to spells as they're cast

Postby jeffwadsworth » 16 Mar 2015, 23:17

Take a look at the recently done Narset Transcendent by LevelX. Of course, the Conspire ability is different due to the passing of the card object, but it may help.
jeffwadsworth
Super Tester Elite
 
Posts: 1172
Joined: 20 Oct 2010, 04:47
Location: USA
Has thanked: 287 times
Been thanked: 70 times


Return to Developers Talk

Who is online

Users browsing this forum: No registered users and 1 guest


Who is online

In total there is 1 user online :: 0 registered, 0 hidden and 1 guest (based on users active over the past 10 minutes)
Most users ever online was 4143 on 23 Jan 2024, 08:21

Users browsing this forum: No registered users and 1 guest

Login Form