Thursday, April 29, 2010

Cards - Two views on the same thing

I would really like to know if you can imagine of what two views I'm going to talk about. I guess it's more obvious for people who have thought about how to structure a magic application. If you like, leave a comment and state what's your background.

When you open a booster pack, how do you read cards? They have symbols and text, and of course you can read them and determine what they do. However, the card can't do anything, because you're not playing a game.
During a game, the card has a complex context that influences how you read it; For example, its color may have changed.

Humans can read a card outside the game and inside the game, but it's hard for a computer. The difference between these two views is very important and currently makes me some problems. A card outside the game (template), like the one from the booster, is a constant thing - it can't change between different copies. This means that multipe cards inside the game can share the same template, but those are then individuals. They have the same template, but that's it.
The same thought can be applied to abilities and spells, I think. Abilities and spells have another, even harder problem: Their effects. While cards just sit there, spells and abilities can directly influence the game, and what influence that is depends on how the spell/ability was played.

So what's the solution to the problem? Well, in a nutshell, the data structure for cards, spells and abilities is duplicated.

Outside of the game is CardTemplate and CardParts, inside the game is CardObject and CardCharacteristics. A CardCharacteristics is assigned to a CardObject is responsible for providing the characteristics, respecting effects and the layer system, but also taking into account the values "printed" on a CardParts object.

Outside the game is ActivatedAbility, inside the game is ActivateAction, PlayInformation and AbilityObject.
  • 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.
  • And to make things easier, the activate action has some additional infos like who played the ability and what card the ability is on (like I said, ActivatedAbility is outside the game, so it doesn't know which cards it is on. the ActivateAction knows).
All that happens under the surface. The ActivateAction retrieves the PlayInformation and does all the necessary things, like putting an AbilityObject onto the stack etc.

I hope that you had a chance to look into my program; I know that this is very hard to imagine without directly looking at the code, So I won't try to go into more detail unless you wish so ;)

Monday, April 26, 2010

Hardcoded vs Softcoded

From a user's perspective, a program's job is to do work, look good etc. If you want to look into a program, and very deep into it, you will notice that all a program does is to move data around. Data and code are two very important things in programming, because the code does the work, and only with data there is work.

But the borders can become unclear when you think of it. Data and code do both lie on your hard disk, or in memory when a program runs, so where's the difference? Well, code is data that represents some kind of work.

Magic is all about doing work. "You lose one life" is a piece of work. After the work is done, I might have lost the game. Still, I wouldn't like to have that effect around in my code - in my Java code, at least.

Hardcoding and softcoding is all about how work is represented.
Hardcoding essentially means "compiled". For example, the five colors and the card types are hardcoded into laterna - to change one, you need the source files and a Java compiler to generate a runnable program from it once again.
Softcoding, on the other hand, means "interpreted". "You lose one life", which might be a spell's effect, could be written in a text file. That file is read when the program is started, and will do its work, although it never was java code.

So softcoding is the better way to do work in all situations? Clearly no. Enabling something to be softcoded needs massive effort. Not only do you need to parse the file and get the important information from it, you also need to translate it into a form the program can handle. For Magic cards, this is very complex, since the work is done much later than the card is read, And a card might do different things everytime you use it. I'll talk about it in more detail the next time.

Monday, April 19, 2010

Spells

I fear that my blog evolves into a redundant commit log of my project, but I really try to write interesting and original.

First, let me say I'm steadily making progress. Now there's a mana pool and you can play mana abilities. Lands do untap and mana is removed properly. I have Llanowar Elves, my first nonland card, which you can play. It's put on the stack and then resolves. You don't pay costs for it yet...

And that's exactly my topic: What does it take to play a spell? There are several reasons why I started with lands, but the most important is that playing a land is a relatively simple thing: "To play a land, a player puts that land onto the battlefield from the zone it was in".

Playing a land is a single line in a Subpoint of the Subpoint 114.2a of "Special Actions" in the comprehensive rules. "Casting Spells" is Section 601, and 601.2 describes the steps necessary to do so, a to h, each one much longer than 114.2a.

The best known steps for playing spells are choosing targets and paying costs. Targets are a somewhat advanced, and I will rest a little before implementing them, but costs are a really essential part. The hard part is not costs per se, but paying them. Just for paying a mana cost, the player must choose how to pay for hybrid or variable symbols and then choose which mana to use.

Now that I know that playing spells works in general, I can try to fill in the details. When I have costs, and can therefore play simple spells rules-compliant, I might take a break to do some cards, improve the GUI, or implement combat... And swoosh! A game isn't that far away any more!

PS: There's one cost working already: The tap cost ;)

Sunday, April 11, 2010

Finally...

I'm glad to announce the first "playable" version of Laterna Magica. Features:
  • A rather simple GUI
  • Drawing a card during your draw step
  • Playing Lands
  • An opponent who does... nothing
Before I present you some screenshots, let me explain the "glue" I've talked about the last time, especially the controller.

The class Player in laterna magica is more or less a data structure and can only perform tasks that don't need decisions - like loosing life, drawing a card or discarding a card at random.
Discarding a chosen card is different, because it needs knowledge, which is something the Player class should not have. Different players make different decisions, and in different ways.

The solution is the Actor class. Whenever some decision is needed, like which card to discard, the actor is asked for that decision. The actor may implement an AI algorithm for choosing, it may get the choice over the network, or, most naturally, depend on user input from the GUI.

Although separating actor from player was a clear choice for me, I don't know how that class will look in the end. Magic is a game with tons of choices - there's not only "discard a card" but also "discard two cards", "discard a creature card", "discard a card of each color"... you know what I mean, this can quickly get messy.
For now, there's basically only "You have priority. What do you want to do?", so that problem is still far away.


And now screenies! When you start the program, you get a GUI with an empty board, like this:

But it doesn't stay for long, it will quickly populate your game with cards. You can watch how your library grows and you draw cards!
After passing priority twice, It will be your main, and you can play one of your (many) Plains:
Looks great? Look at that, during your next turn, you can do it again^^
What you have just seen demonstrates some important things:
  • Laterna Magica has many events. Playing a land doesn't explicitly remove it from the panel. Instead, the panel listens for cards entering/exiting the zone (and later also controller changes) to keep track of what cards are to be shown. When the land is played, the rules engine moves it, and the GUI listens to the rules engine. Similar things are true for the zone size labels, the life total labels and the turn progress label.
  • And because swing supports it, the game loop (passing priority back and forth, basically) can run in an extra thread. Whenever the player "freak" has priority, the GameLoop queries the Actor for what to do. That actor is a GuiActor and waits for the user to either click "Pass priority" or a Plains card. Therefore, the GUI thread is not blocked when the game loop is working.
  • The GUI has a utility for finding all the actions possible for clicking a card. Currently, there's only playing a land, but it will also check for playing spells or activated abilities, unmorphing etc. in the future. That Utility also checks if the action is legal (timing, only one land per turn etc.)
  • what aparently doesn't work is discarding excess cards at end of turn. While it's easy, I've chosen to show you what I have as soon as drawing and playing a land works.
  • If you would click yourself all the way through until a library is empty, you would notice that the game doesn't end with that. Although the state based action does exist, the loseGame() method of player is a No-Op.
okay, enough for now. Have fun playing some real games ;)