It is currently 19 Apr 2024, 08:05
   
Text Size

Help implementing a card

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

Re: Help implementing a card

Postby MarcoMarin » 12 Apr 2016, 21:48

there you go:
| Open
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.arabiannights;

import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.counters.Counter;
import mage.counters.Counters;
import mage.filter.Filter;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.common.FilterEnchantmentPermanent;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.Token;
import mage.target.Target;
import mage.target.TargetPermanent;

/**
 *
 * @author MarcoMarin
 */
public class Oubliette extends CardImpl {

    public Counters godHelpMe=null;
           
    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("target creature");
   
    public Oubliette(UUID ownerId) {
        super(ownerId, 11, "Oubliette", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}{B}");
        this.expansionSetCode = "ARN";

        // When Oubliette enters the battlefield, exile target creature and all Auras attached to it. Note the number and kind of counters that were on that creature.
        Ability ability1 = new EntersBattlefieldTriggeredAbility(new OublietteEffect(), false);
        Target target = new TargetPermanent(filter);
        ability1.addTarget(target);
        this.addAbility(ability1);
       
        // When Oubliette leaves the battlefield, return the exiled card to the battlefield under its owner's control tapped with the noted number and kind of counters on it. If you do, return the exiled Aura cards to the battlefield under their owner's control attached to that permanent.
        //Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD, true), false);
        //this.addAbility(ability2);
    }

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

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


class OublietteEffect extends OneShotEffect {

    private static final FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent();

    static {
        filter.add(new SubtypePredicate("Aura"));
    }

    public OublietteEffect() {
        super(Outcome.Detriment);
        this.staticText = "4Exile target creature and all Auras attached to it. Note the number and kind of counters that were on that creature. When Oubliette leaves the battlefield, return the exiled card to the battlefield under its owner's control tapped with the noted number and kind of counters on it. If you do, return the exiled Aura cards to the battlefield under their owner's control attached to that permanent.";
    }

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

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

    @Override
    public boolean apply(Game game, Ability source) {
        // Exile enchanted creature and all Auras attached to it.
        Permanent enchantment = game.getPermanent(source.getSourceId());
        if (enchantment == null) {
            enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
        }       
        UUID targetId=source.getFirstTarget();
       
        if (targetId==null) return false; // if previous scan somehow failed, simply quit
       
        if (enchantment != null) { //back to code (mostly) copied from Flickerform
            Permanent enchantedCreature = game.getPermanent(targetId);
            if (enchantedCreature != null) {
                UUID exileZoneId = UUID.randomUUID();
                enchantedCreature.moveToExile(exileZoneId, enchantment.getName(), source.getSourceId(), game);
                for (UUID attachementId : enchantedCreature.getAttachments()) {
                    Permanent attachment = game.getPermanent(attachementId);
                    if (attachment != null && filter.match(attachment, game)) {
                        attachment.moveToExile(exileZoneId, enchantment.getName(), source.getSourceId(), game);
                    }
                }
               
   
                if (!(enchantedCreature instanceof Token)) {
               
                    // If you do, return the other cards exiled this way to the battlefield under their owners' control attached to that creature
                    LeavesBattlefieldTriggeredAbility triggeredAbility = new LeavesBattlefieldTriggeredAbility(
                            new OublietteReturnEffect(enchantedCreature.getId(), exileZoneId, enchantment), false);
                    enchantment.addAbility(triggeredAbility, source.getSourceId(), game);                   
                }
                return true;
            }
        }

        return false;
    }
}

class OublietteReturnEffect extends OneShotEffect {

    private static final FilterCard filterAura = new FilterCard();

    static {
        filterAura.add(new CardTypePredicate(CardType.ENCHANTMENT));
        filterAura.add(new SubtypePredicate("Aura"));
    }

    private final UUID enchantedCardId;
    private final UUID exileZoneId;
    private final Permanent oubliette;
   
    public OublietteReturnEffect(UUID enchantedCardId, UUID exileZoneId, Permanent oubliette) {
        super(Outcome.Benefit);
        this.enchantedCardId = enchantedCardId;
        this.exileZoneId = exileZoneId;
        this.oubliette = oubliette;
        this.staticText = "return the exiled card to the battlefield under its owner's control tapped with the noted number and kind of counters on it. If you do, return the exiled Aura cards to the battlefield under their owner's control attached to that permanent.";
       
    }

    public OublietteReturnEffect(final OublietteReturnEffect effect) {
        super(effect);
        this.enchantedCardId = effect.enchantedCardId;
        this.exileZoneId = effect.exileZoneId;
        this.oubliette = effect.oubliette;
    }

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

    @Override
    public boolean apply(Game game, Ability source) {
        ExileZone exileZone = game.getExile().getExileZone(exileZoneId);
        Card enchantedCard = exileZone.get(enchantedCardId, game);
        if (enchantedCard != null) {
            enchantedCard.putOntoBattlefield(game, Zone.EXILED, source.getSourceId(), enchantedCard.getOwnerId());
            Permanent newPermanent = game.getPermanent(enchantedCardId);
            if (newPermanent != null) {
                newPermanent.tap(game);
                for (Card enchantment : exileZone.getCards(game)) {
                    if (filterAura.match(enchantment, game)) {
                        boolean canTarget = false;
                        for (Target target : enchantment.getSpellAbility().getTargets()) {
                            Filter filter = target.getFilter();
                            if (filter.match(newPermanent, game)) {
                                canTarget = true;
                                break;
                            }
                        }
                        if (!canTarget) {
                            // Aura stays exiled
                            continue;
                        }
                        game.getState().setValue("attachTo:" + enchantment.getId(), newPermanent);
                    }
                    if (enchantment.putOntoBattlefield(game, Zone.EXILED, source.getSourceId(), enchantment.getOwnerId())) {
                        if (filterAura.match(enchantment, game)) {
                            newPermanent.addAttachment(enchantment.getId(), game);
                        }
                    }
                }
               
                   
            }
            return true;
        }
        return false;
    }
}

MarcoMarin
 
Posts: 32
Joined: 20 Mar 2016, 07:49
Has thanked: 16 times
Been thanked: 2 times

Re: Help implementing a card

Postby Doctor Weird » 12 Apr 2016, 22:08

MarcoMarin wrote:
Code: Select all
enchantedCreature
Okay, I told myself I wouldn't keep replying to this because these are code questions and I shouldn't keep meddling with stuff I don't fully dominate (XMage's inner code). But even so, I can't help but look at that and wonder why you have "enchantedCreature" in there in ways that seem to me like the code is looking for a card enchanted by Oubliette, when Oubliette is not an aura itself and is not meant to enchant anything. I could be just understanding the code wrong, but it looks like it's referring to its target there.

I'm going to, again, run through what I personally would do now if I was in your shoes, with little knowledge of how to code a new card. I promise this is the last time I do it, though, I already feel like this is a huge waste of your time for something a dev could just easily come here later and explain to you in a couple of lines, but at the same time running into a wall and finding stuff out by your own experimentation instead of just having the explanation handed to you is a good practice method.

  • First, start from scratch using the code for Journey to Nowhere (from Zendikar) as a starting point, since Oubliette does all the same things as that card plus more.
  • Then look at the code for Hubris (from Journey into Nyx) to figure out how to filter for all auras attached to the targeted creature (without targeting the auras themselves) and instead of returning them to hand like Hubris does exile them in the same space with the creature.
  • Then, once the leaving ability triggers (Journey to Nowhere's code should already have this covered) and all those cards return (the one creature and all the auras) have the auras attach to that creature, perhaps looking at something like Glamer Spinners (from Shadowmoor) as to how to properly use addAttachment.
  • The creature should return tapped, which is something I imagine should be easy to do by looking at any land that enters the field tapped, like Boreal Shelf (from Coldsnap).
  • After, and only after, all of this is done and working, would I worry about the counters part, and there my first shot would be to look up the Suspend ability (Epochrasite, or Durkwood Baloth, for example) to see about exiling the creature with all its counters remaining on top of it (since having the counters stay on the exiled card should make no difference) and then have them come back with the creature once it returns from exile too. If that didn't work then chances are this whole thing about the counters needs to be a whole new function worked into XMage.

Basically, dig, dig a lot and find how to make good use of the code you're reading while making sure you understand it so you can over time start figuring out to change things on your own here and there according to your needs.

But feel free to ignore any or all of what I'm saying, because I'm not qualified to give proper answers anyway and I'm just trying to give you ideas of where to look for ideas.
Doctor Weird
 
Posts: 180
Joined: 25 May 2015, 01:33
Has thanked: 7 times
Been thanked: 52 times

Re: Help implementing a card

Postby MarcoMarin » 13 Apr 2016, 00:22

heh, no way, Dr Weird. Please keep doing it, I actually appreciate very much, even if the ideas themselves don't apply immediately, they probably will some time later. But most importantly to me, it helps keep the motivation up, something which I'm kinda low now, I was even mentioning to the devs, as I was even resisting setting up the testing environment. :) So thank you.

Same story about the "EnchantedCreature" name, short answer: laziness. :| I copied it from flickerform and left it unchanged. But it does hold a 'target' now :)

My plan for Oubliette is precisely that, leaving the counters for last. Once I get the auras going back and forth like flickerform does, then (and yes, only then) can I try the ideas for them. I have 1 idea I'll test, but if it doesn't work, your suggestions are my next destination. :)

Then, once the leaving ability triggers
It doesn't, that's where I'm stuck. :? I'm copying almost verbatim from flickerform, it should have worked directly so there is probably something very silly (or something more serious?), the only difference is I'm adding the ability to oubliette(which is where I think it checks if it's leaving the BF) instead of the entire game object(which is where I think it checks if flickerform's phase has arrived), maybe I have to register it there as well, I dunno. :) Hopefully escplan9 will figure out, now with the code in hand.

Meanwhile I coded Dwarven Catapult and managed to fix my problems with Git (and using this opportunity to learn/practice more with it, which was 1 of my goals), and set up (and incorporate into the development cycle, that is, actually test stuff, lol) the testing process, so I don't inadvertently mess up people's servers. :oops:

(Epochrasite, or Durkwood Baloth, for example
Thanks, if my crazy idea doesn't work, that's where I'm looking next. :)
MarcoMarin
 
Posts: 32
Joined: 20 Mar 2016, 07:49
Has thanked: 16 times
Been thanked: 2 times

Re: Help implementing a card

Postby escplan9 » 13 Apr 2016, 01:45

A few things on the Oubliette code:

Clearer naming of the variables would help a lot. You named a variable "enchantment" which really is "creature targetted by Oubliette". Then you have "enchantedCreature" which is the "create exiled by Oubliette".

There should be two triggered abilities for Oubliette - EntersTheBattleField, and LeavestheBattlefield. Then due to all the special handling needed for Oubliette, there should be two separate classes for the OublietteEntersEffect and OublietteLeavesEffect.

I would follow the thinking process DoctorWeird described to figure out more from there. But these guidelines should help. By the way, the reason the LeavesTheBattlefield is not doing anything is because your OublietteEffect as is right now is just a One Shot Effect. So once it's immediate effect is done, the card itself is done and nothing else will happen.
escplan9
 
Posts: 257
Joined: 10 Aug 2015, 22:38
Has thanked: 26 times
Been thanked: 40 times

Re: Help implementing a card

Postby Doctor Weird » 13 Apr 2016, 01:58

Don't fall for the common coder's fallacy of staring at code that doesn't work and to keep chipping away at it, banging your head on the wall wondering why it doesn't work, refusing to start fresh because you're determined on not wasting time doing it all over again that you end up spending even more time trying to find an answer where you've already looked a dozen times.

You're too fixated on Flickerform, forget it for a moment and look elsewhere. Like I said, go look at Journey to Nowhere here in this link; At line 63 it does exactly what you want:

| Open
Code: Select all
// When Journey to Nowhere leaves the battlefield, return the exiled card to the battlefield under its owner's control.
        Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false);
        this.addAbility(ability2);
I see you have this in the code you posted above but it's commented out. I don't think you'd have missed something so obvious, so I'll assume you were testing it before and then put it aside. Go back to that, right now Journey to Nowhere is a more useful template for you to start than Flickerform. And make sure you don't forget to include mage.abilities.effects.common.ExileTargetForSourceEffect as well as ReturnFromExileForSourceEffect, which I notice the code you posted doesn't.

I'm not qualified to give you absolute answers about this because I'm not a dev (and it's taking me a lot of self-control to not just give in and start finding out the answers myself), but I'm trying to give you leads for you to follow and research on your own, you just have to go look at them.

Edit: didn't see escplan9's post until I finished mine. There's some more answers for you to go through. As for me, I'm done for the night, I've spammed enough for one day.
Doctor Weird
 
Posts: 180
Joined: 25 May 2015, 01:33
Has thanked: 7 times
Been thanked: 52 times

Re: Help implementing a card

Postby escplan9 » 13 Apr 2016, 02:59

Thanks for all the help and suggestions DoctorWeird! You are the best backseat developer ever :) I mean that in the best possible sense :lol: . What I typically first do when I'm about to work on a card is make note of a few other cards that do similar effects for each part. Like DoctorWeird said, don't get hung up on just one card you're templating it off from. Look at other similar cards and see what they did - try to understand what was done. See what's in common and different between them and think about why that might be.

It might also help to look into what some of those ReturnFromExile... effects do behind the scenes, so in NetBeans jump to the source of those classes and take a look. You very likely will not be able to use these mage.abilities.common effects directly for a card with additional conditions like Oubliette. You will need to customize the existing functionality to offer what Oubliette does.
escplan9
 
Posts: 257
Joined: 10 Aug 2015, 22:38
Has thanked: 26 times
Been thanked: 40 times

Re: Help implementing a card

Postby MarcoMarin » 13 Apr 2016, 11:40

named a variable "enchantment" which really is "creature targetted by Oubliette". Then you have "enchantedCreature" which is the "create exiled by Oubliette".
I believe enchantment is Oubliette itself. "enchantedCreature" is the target of an enchantment (oubliette), although the enchantment is not an aura.
There should be two triggered abilities for Oubliette - EntersTheBattleField, and LeavestheBattlefield. Then due to all the special handling needed for Oubliette, there should be two separate classes for the OublietteEntersEffect and OublietteLeavesEffect.
I see you have this in the code you posted above but it's commented out.
Yes guys but, how do I pass the data from the 1st effect to the 2nd effect? Flickerform does this by creating the 2nd inside the 1st (so those bits of info are readily available)

And make sure you don't forget to include mage.abilities.effects.common.ExileTargetForSourceEffect as well as ReturnFromExileForSourceEffect, which I notice the code you posted doesn't.
I do manually what those do. Once I comment the lines that use them, the automated inclusion mechanism removes them, if I need them again it will be added automagically again.

What I typically first do when I'm about to work on a card is
That's exactly what I've been doing these first couple weeks :D

Anyway, I've marked this card (and tawnos's coffin) as bugged. If a solution doesn't rise up before next release, it may be best to remove them for a while. I'm moving on to FEM. I'm sure I'll learn more to eventually come back and solve this if I don't stay fixated on Oubliette.
MarcoMarin
 
Posts: 32
Joined: 20 Mar 2016, 07:49
Has thanked: 16 times
Been thanked: 2 times

Re: Help implementing a card

Postby escplan9 » 13 Apr 2016, 12:30

Detention Sphere is a much closer card than any of those mentioned so far. It can exile multiple cards when it enters the battlefield, then when it leaves returns all of them from exile as well. You'll see it has both an EntersTheBattlefield and LeavesTheBattlefield ability, and respectively a custom EntersEffect and a custom LeavesEffect.
escplan9
 
Posts: 257
Joined: 10 Aug 2015, 22:38
Has thanked: 26 times
Been thanked: 40 times

Re: Help implementing a card

Postby MarcoMarin » 13 Apr 2016, 20:16

Alright, managed to find a workaround. Nevertheless it's important to find out why the later addition of the ability doesn't have the same privileges as when we add it from the constructor. This may be hiding a bug which may bite us later, or simply be useful for when workarounds are not as trivial.

I left that bit commented out, in case such experiment is to be conducted in the future.

escplan suggestion taught me Exile Zones doesn't need to have randomly generated UUIDs (thx :wink: ), this allowed 1 less data to pass to the 2nd effect. The others I could get by other means. Thus the effect could now be registered from the constructor, which correctly triggers it when the card leaves the BF.

Even better news: The crazy idea worked for counters as well \o/ and Dwarven Soldier got coded in the meantime. : )
MarcoMarin
 
Posts: 32
Joined: 20 Mar 2016, 07:49
Has thanked: 16 times
Been thanked: 2 times

Re: Help implementing a card

Postby escplan9 » 13 Apr 2016, 20:44

MarcoMarin wrote:Yes guys but, how do I pass the data from the 1st effect to the 2nd effect? Flickerform does this by creating the 2nd inside the 1st (so those bits of info are readily available)
Also, just so you understand why Flickerform works the way it did - it has a known time when the cards exiled will return to the battlefield, so it doesn't need to "listen" for when it leaves the battlefield. It only needs to wait until the next endstep. So the code does the immediate effect, then sets up a delayed triggered ability since it will always return the card (and auras attached to it) at the beginning of the next end step. This won't work with Oubliette and Detention Sphere where you need to have separate triggered abilities upon entering and leaving the battlefield. One Shot Effects mean just that - it applies the immediate effect, then that is it. So you need to add any additional abilities or effects separately from that. You do not know when Oubliette will leave the battlefield, and thus, needs a separate LeavesTheBattlefield trigger.
escplan9
 
Posts: 257
Joined: 10 Aug 2015, 22:38
Has thanked: 26 times
Been thanked: 40 times

Re: Help implementing a card

Postby MarcoMarin » 13 Apr 2016, 22:20

Yes, but both are triggered abilities. One is triggered by the endstep, the other by leaving the BF.
thus, needs a separate LeavesTheBattlefield trigger.
Yep, and I do use that. My point is, I'm using the exact same code. Except the now working code adds it as the card is created, the other adds it just after it enters the BF. The curious fact is that it even shows up on the debugger, under Oubliette abilities.

But it seems the constructor command (this.addAbility) does something else, maybe register it to the game object as well.. I don't know.

Oh.. maybe adding to the instance of the card (in this case, the permanent) doesn't work, maybe I'd have to add to the class itself to have the game "see" it. Is there a way to do that? Java isn't a dynamic programming language, so I doubt it.

It is curious that adding and removing abilities works (say like in Titania's Song I coded last week), but maybe triggered ones are different as it would depend on the "triggering mechanism" to "see" it?

Anyways, it works now. [-o<
MarcoMarin
 
Posts: 32
Joined: 20 Mar 2016, 07:49
Has thanked: 16 times
Been thanked: 2 times

Re: Help implementing a card

Postby escplan9 » 13 Apr 2016, 23:23

MarcoMarin wrote:Yes, but both are triggered abilities. One is triggered by the endstep, the other by leaving the BF.
You do not see a difference between those two triggered abilities? Good to hear you figured out what you needed to from the card anyways.
escplan9
 
Posts: 257
Joined: 10 Aug 2015, 22:38
Has thanked: 26 times
Been thanked: 40 times

Re: Help implementing a card

Postby MarcoMarin » 14 Apr 2016, 01:05

I think I do but how does that help answer why "this.addAbility" works on the constructor, while "card.addAbility" doesn't? Both got to listen to the 'leaveBF' event, no?

Good to hear you figured out what you needed to from the card anyways.
Teamwork =D> and there will be plenty to come.. : ) I just made my 1st foray into coding a Framework class, I hope the older devs can take a look after I commit, to ensure I made no fatal mistakes. :^o
MarcoMarin
 
Posts: 32
Joined: 20 Mar 2016, 07:49
Has thanked: 16 times
Been thanked: 2 times

Previous

Return to Developers Talk

Who is online

Users browsing this forum: No registered users and 12 guests

cron

Who is online

In total there are 12 users online :: 0 registered, 0 hidden and 12 guests (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 12 guests

Login Form