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".

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.


nantuko84 said...

such downloader is very usefull tool. you don't realize this until you start implementing lots of cards. even perl script that generates card templates from gatherer spoiler saved hours for me.

could you please give an example how you are planning to use different formats (didn't understand from article, sorry).
let's say we have Llanowar Elves in your (text) format, and I want to add Shock in xml format. what should I do? is it some type of plugin system?

Silly Freak said...

Yes, it is. The code is already on the SVN, so I'll just link you to it: AllCards is the "entry point". It finds the parsers and uses them. The configuration works through here, you declare which parsers exist. Currently, only a text parser exists.

Now, if you wanted to create a new parser, you just have to
-implement the CardCompiler interface (and of course any classes your parser needs internally)
-add the class and path properties to and
-create a new directory for the card files.

Extra: If you look at, there's much more than the minimum. As I wrote, the text parser uses individual parsers for the lines, and they use the same plugin system.
The TextHandler class uses individual classes for parsing different abilities, the ActivatedAbilityParser class uses separate classes for parsing sorts of costs and effects. And I'll do the same some time for targets ;)

Silly Freak said...

Oh, and you would have to implement targets first ;)

nantuko84 said...

looked through the code. it makes sense ;) and I like it.

about effects and abilities: I've seen an example with "apply". so it means that your would implement separate classes for such cards, right?

and also I'm interested in your idea how to implement cards that combine basic effects, e.g.
"Destroy target creature. Draw 2 cards.".

Silly Freak said...

>> about effects and abilities: I've seen an example with "apply". so it means that your would implement separate classes for such cards, right?

exactly. there is no class for "apply" yet, but that is what it means. in this case, DestroyAllCreatures would mean that it adds a single spell effect to the card, but you could also make a card solely through an "apply" line

>> and also I'm interested in your idea how to implement cards that combine basic effects, e.g.
"Destroy target creature. Draw 2 cards.".

I have this already with costs: "{W/U}, {T}" for example is splitted at the comma, and each partial cost is added separately. The same could be done for effects, altough my ActivatedAbilityParser doesn't do such things yet. how combining effects work is already here

nantuko84 said...

thanks for your detailed answers ;)

Silly Freak said...

thanks for reading my blog ;)