Wednesday, May 12, 2010

The card editor

The last weeks had no updates in my main project, but there's a good reason for that. I worked on a card editor that lets you download gatherer card descriptions, edit them and lets you test if the card is correct.

I think I'll explain a bit more how Laterna Magica handles cards. A fundamental thought in my system was extensibility. I'm currently using a text format, but Laterna Magica allows you to use any format, even multiple ones at the same time. So, if I sometime see a reason to use e.g. XML, I can still use the old cards while developing cards for the new one.

A similar thought is true for the text format itself. It is key-value based, and every key is handled by its own class. If a line is "name Llanowar Elves", then the class for "name" gets the input "Llanowar Elves" and sets the name accordingly. If the line is "text {T}: Add {G} to your mana pool.", the "text" handler tries to find an ability parser that can parse the line. Even if I can't represent an ability, I can make a class specifically for that ability and add a line handler that applies an arbitrary class to the card, like "apply laterna.magica.cards.DestroyAllCreatures".

The output of the card parser is a CardTemplate object. All the CardTemplates created are stored in a zip file, so you can think of the card parser like a compiler: There's input of different types, like plain text and XML, but the parser(s) convert it into a uniform format that the program can work with (serialized java objects).
And here, my editor comes into play. After creating a card, the editor can directly feed it into the parser, which implicitly checks if all the lines in the file are correct. The card parser will ignore lines that can't be parsed (like "Dryad Arbor is green." in my last post), but the card creator (e.g. you^^) won't: You can see the parser's warning and correct the lines.

Basic editing, opening, saving and even downloading already works in the editor, but it still needs a few tweaks before it really has a benefit over normal text editors (which you will be more familiar with for sure):
  • Compiling cards or creating the zip file doesn't work yet
  • The card downloader does not convert a card's name, I would like to have "Shock deals two damage to target creature or player." as "~ deals two damage to target creature or player."
    Maybe even more complex transformations like "target((zone/battlefield & type/creature) | player)" ;)
    (that's just speculation. i have no idea how to do targets or even target parsing)
  • There is a file system tree with all the cards, but currently you can't open a file by double-clicking it
  • "Save As" should be possible by dragging an opened file (they are shown in a list) onto a folder in the tree.
  • I want to display already opened/modified files with a different icon
Okay, the last one is just bells and whistles, but the others will really influence the usability. I have already created the shadowmoor hybrid lands, which was lots of copy/paste and changing names and colors. Although these cards are not parseable yet as-is, downloading the cards would have made it easier. It's not in the GUI yet, - currently, you can only download cards by multiverse-ID or card name - but the download functionality can use all of the strength of gatherer searching, so downloading exactly those ten hybrid lands shouldn't be too hard.

This editor is like a pet to me. I know I have better things to do - combat, namely - but right now I want to get this working.

Sunday, May 9, 2010

Implementing some cards

Okay, I think I've spent enough time on how abilities work in my problem, but it's definitely easier when viewing some real cards.

In reality, I haven't done much of the work yet that would make making cards interesting - no targeting, no damage, no destruction or sacrificing - it's essentially creating and spending mana right now.

Before I come to the interesting cards which can't be done yet, I'll show you what already works in this post. I have done a simple parser for cards. In fact, it's quite extensible and can be configured to plug-in different file formats. The only file format by now is a plain text format, for which in turn different line-parsers can be plugged in.

And now to the cards:

name    Forest
super   basic
type    land - forest

The basic land types have the mana producing ability implied, which are also parsed as any other mana ability. I'm not sure how this will work with adding and removing basic land types, but it's like that for now.

name    Graven Cairns
type    land
text    {T}: Add {1} to your mana pool.
text    {B/R}, {T}: Add {B}{B} to your mana pool.
text    {B/R}, {T}: Add {B}{R} to your mana pool.
text    {B/R}, {T}: Add {R}{R} to your mana pool.

Right now, there are only activated abilities. Costs and Effect are parsed separately, and every cost is parsed on its own. By now, any combination of mana costs and Tap or untap costs are working. The only Effect is "Add ... to your mana pool." Things like "{B}{B}, {B}{R} or {R}{R}" are not possible by now, neither are hybrid symbols (they only work for costs), so I write it as three abilities.

name    Dryad Arbor
color   green
type    land - forest
type    creature - dryad
pt      1/1
text    (Dryad Arbor isn't a spell, it's affected by summoning sickness, and it has "{T}: Add {G} to your mana pool.")
text    Dryad Arbor is green.

Here you can see that every type is specified on its own line only with its corresponding subtypes. The two "text" lines here are actually nonfunctional. The "Dryad Arbor is green." Ability is actually implemented by the "color" line, and the mana ability comes from the forest subtype.

Wednesday, May 5, 2010

Implementing activated abilities: Divide and conquer

Last time, I showed the technical concept behind abilities (and spells) in Laterna. Today, I want to explain the PlayInformation interface from another perspective.

I hope I have given a good impression of why I use the approach I do: Abilities using a function to create PlayInformation objects which do the work. But why is PlayInformation so "big"? Why does it do all the work from paying costs over targeting to dispatching the effect?

The reason is a simple one: Because it's easier this way. Imagine a "complex" ability: "{X}: ~ Deals X damage to target creature." What this means is that the variable X is mentioned in both cost and effect, as most of the time, and this gives two possibilities:
  • Make Cost and Effect both interfaces and define some kind of communication, likely using another object between them, or
  • only use that object from "in between" and make the communication between cost and effect an implementation detail.
The process of playing a spell/ability is a complex one, and putting it into a single class enables more compact code to be written for individual abilities.

Now there's only the question from last time left: How can I still define cost and effect separately, giving me the ability to reuse them?

Delegation is a key concept in OO programming: If a class gets too big, try to find some separated piece of work that it does, and transfer it to a new class. Then, let the original class use the new one. An example in LaternaMagica is the CardCharacteristics class: Cards have characteristics that can modified by various effects. But instead of implementing the characteristics in the MagicObject class, every MagicObject object has a CardCharacteristics object. That does this work.

In that case, it's defined in the interface. With PlayAbility, it isn't. It's an implementation detail of AbstractPlayInformation, which implements PlayInformation. The AbstractPlayInformation class allows you to combine multiple PlayInformation objects into a single, big PlayInformation that carries out your instructions in order.
If you have a cantrip spell, just add in a DrawCardInformation at the end!
If you have a complicated activated ability, implement the effect, but use standard cost Informations and combine them with the AbstractPlayInformation.

Oh, and don't forget to add an information that removes the spell/ability from the stack...

Saturday, May 1, 2010

Implementing activated abilities: Getting the effect to the game

There are two paragraphs in my previous post that I want to further examine:
  • ActivatedAbility has a method getPlayInformation() which takes an ActivateAction and returns a PlayInformation.
  • The PlayInformation is the real worker of abilities and implements modes, targeting, costs, effects etc. It depends on the game, so an activated ability can only create the PlayInformation if a game is provided.
First, let me show you the ActivatedAbility and PlayInformation interfaces, so you can see what I'm talking about:

public interface ActivatedAbility extends NonStaticAbility {
    public boolean isLegal(ActivateAction a);
  
    public PlayInformation getPlayInformation(ActivateAction a);
   
    //inherited from NonStaticAbility
    //public boolean isManaAbility();
}

public interface PlayInformation extends GameContent {
    /**
     * Returns the action that is used in playing.
     */
    public PlayAction getAction();
  
    /**
     * Returns the object that was played using this play information.
     */
    public MagicObject getObject();
  
    public void makeChoices();
  
    public void chooseTargets();
  
    public void distributeEffect();
  
    /**
     * This action is only defined for activated abilities and spells, but not for triggered abilities.
     */
    public GameAction getCost();
  
    public GameAction getEffect();
   
    //inherited from GameContent
    //public Game getGame();
}


As you can see, the Activated ability interface is pretty small; its job is primarily to create PlayInformation objects. PlayInformation does the whole work.

This means especially two things:
  • Although it would seem intuitive, to make a new activated ability, you don't implement ActivatedAbility but PlayInformation. This may be confusing for the first time, but you can get used to it. But:
  • You still have to implement the getPlayInformation() method to return your new PlayInformation. And this is what really bugged me. An ActivatedAbility class would usually exactly follow a template, and only replace the PlayInformation.
So I thought about what I could do to save me from creating all these redundant classes. The first step was to extract the job "create a PlayInformation for an ActivateAction" out of the ActivatedAbility class. As luck would have it, the Google Collect API, which I was already using, has the right interface for that: Function (simplified):

public interface Function<F, T> {
    T apply(F from);
}


Which led me to the ActivatedAbilityImpl class:

public class ActivatedAbilityImpl extends NonStaticAbilityImpl implements ActivatedAbility {
    private static final long                                           serialVersionUID = -8987886570576715112L;
   
    private Function<? super ActivateAction, ? extends PlayInformation> object;
    private Predicate<? super ActivateAction>                           legal;
   
    public ActivatedAbilityImpl(boolean manaAbility, Function<? super ActivateAction, ? extends PlayInformation> object, Predicate<? super ActivateAction> legal, String text) {
        super(manaAbility, text);
        this.object = object;
        this.legal = legal;
    }
   
    /**
     * Returns if casting the spell using the specified CastAction is legal.
     */
    @Override
    public boolean isLegal(ActivateAction a) {
        return legal.apply(a);
    }
   
   
    @Override
    public PlayInformation getPlayInformation(ActivateAction a) {
        return object.apply(a);
    }
}


(Predicate is also from Google Collect and represents a true/false decision about an object)

But that step doesn't really solve the problem. Now I don't have to implement ActivatedAbility, but instead Function. The real solution only came when diving deep into Java's pool of features: Reflection. Reflection means to analyze a program's parts, like, classes, methods, constructors and so on while it's running. So my idea was to make a Function which creates a new object of any class you want:

class B {
    public B(A a) {}
}

Function<A, B> f = new MyFunction<A, B>(A.class, B.class);
B b = f.apply(new A());


...and creating a new class has transformed into creating an object! However, there is still a problem: what if your PlayInformation needs configuration? Your PlayInformation might Implement the ability "<mana cost>: Tap target permanent." How do you set the mana cost? The ActivateAction, which is the function's input, can't provide it. What you need is a per-function configuration in addition to the per-apply parameter:

class B {
    public B(C c, A a) {}
}

C config = new C();
Function<A, B> f = new MyFunction<A, B, C>(C.class, config, A.class, B.class);
B b = f.apply(new A());
//b is equal to
//new B(config, new A());


And that's exactly how I ended up, and I'm quite satisfied with my work. You can look at the source code at FactoryFunction.java.

And the last note for today: PlayInformation has many things to do, among them providing the ability's (or spell's, PlayInformation is used for both) cost and effect. By Implementing both in the same class, you take yourself the opportunity to reuse costs in other abilities. Next time, I'll show you how to mix-and-match these.