Wednesday, May 26, 2010

How Forge Processes the Mouse - most boring post ever

(This is probably my least exciting article ever. Processing the mouse is very important but very boring. This post is more likely to be interesting to people who want to write a program like Forge.)

Handling the mouse can become very complicated since a "mouse click" can do many different actions depending on the context: choose targets for a spell or ability, advance to the next phase, or is used to declare attacking creatures. Forge uses the state pattern to process all of the mouse input. Without the state pattern I have no idea what I would have done. I think it works well but it can be tricky to understand.

Below is the Input class that handles the mouse. To better understand the Input class let me briefly explain how Forge implements different phases. Each phase extends Input and shows the name of the phase to the user through showMessage(). Usually a phase will let a user do a specific action like the Main phases allow the user to play any type of card or a land. MainInput.selectCard() handles the processing of playing a card or land.

Input class is also used whenever a spell or ability needs to choose a target. The Input for Shock would use showMessage() to show the user a message like "Select target creature or player to receive 2 damage." If the player clicks on a card, selectCard() checks to see is that card a creature. If the user clicks on a player, selectPlayer() is called.
class Input
{
//showMessage() is always the first method called
public void showMessage()
public void selectCard(Card c, PlayerZone zone)
public void selectPlayer(String player)
public void selectButtonOK()
public void selectButtonCancel()
}
There are a few details that I have left out. First, how does an Input tell the rest of the program that it is done and the next Input should be processed? Forge handles all of the Inputs using a stack and the Input class has a method called stop() which just pops the stack. Second, how do the phases advance? I just hardcoded them, nothing fancy. Main 1 always advances to "Before Declare Attackers". When the Main 1 Input method selectButtonOK() is called, it pops itself and pushes the next Input onto the stack.

Since the method showMessage() is always the first method called, I can put code in there that I would usually put into a constructor. Another trick that I do with showMessage() relates to planeswalkers. Forge was written before planeswalkers were added to Magic so it didn't have the concept of "you can only use one of these abilities once a turn" or loyalty counters but I used showMessage() to fake it. I won't show you the code since it is very crufty and hacked-together even by my standards. Where there is a will, there is a way. :--)

Random quote, “Time is a great teacher, but unfortunately it kills all its pupils.”

p.s.
I lied when I said that "Forge handles all of the Inputs like a stack". In reality Forge doesn't stack inputs. It has a stack but it is only one deep. This can be seen if you play two legendary creatures that have an effect when they go to the graveyard like Keiga, the Tide Star. If you play a 2nd Keiga, you only get to steal one creature. And knowing is half the battle. (Yes, I pretend to be funny even though I'm not.)

p.p.s.
The Input class is also used for mulligans.

9 comments:

Anonymous said...

You can steal two creatures if you have two Keigas go the the graveyard simultaneously. It works for the other legendary dragon spirits too. Forge works better than you realize. :-)
That said, I don't see what that exactly has to do with Input's stack ... that's part of the in-game stack zone. On that subject, are the developers considering letting players choose which order simultaneous events go on the stack? It's probably difficult and doesn't have that much of an impact, so I figure it's low priority. :-/ As long as you're pumping out new cards, features, and fixes, though, I can't complain. Thanks for all the good work!

-slowe

Forge said...

"Are the developers considering letting players choose which order simultaneous events go on the stack?"

No, I understand why you want that but it is just much easier to not give the player the choice. (This isn't to be mean, it just greatly simplifies the programming.)

Forge said...

"You can steal two creatures if you have two Keigas go the the graveyard simultaneously. It works for the other legendary dragon spirits too. Forge works better than you realize. :-)"

Maybe Inputs stack now? I know that it used to not work that way. (It is complicated to _always_ test stuff in order to make sure what you say is up-to-date. On the positive side, at least a bunch of stuff gets updated and fixed.)

"Thanks for all the good work!"

Everybody enjoys a good pat on the back. :-)

Anonymous said...

Don't know if this is the place to leave the comment, but I guess I will anyway. I had an Akroma, Angel of Wrath in play and the next turn added another one. Once I paid the summoning cost both Angels disappeared and I was still charged the mana cost. JSYK.

GuillermoMila said...

Hello Forge, I have a question: did the Magic Forge runs on PSP? Tks

GuillermoMila said...

Man, I've got an error on the last version: could not find the main class: forge.Gui_NewGame.

Silly Freak said...

"I had an Akroma, Angel of Wrath in play and the next turn added another one. Once I paid the summoning cost both Angels disappeared and I was still charged the mana cost."

This is called the "legend rule" you can't have the same legendary card (like akroma) on the field twice

"could not find the main class: forge.Gui_NewGame"

such bugs are most of the time dependent on your very installation. if you want, you can go into the forums. i can then help you find and fix the bug

DennisBergkamp said...

"Hello Forge, I have a question: did the Magic Forge runs on PSP? Tks"

For magic on the PSP, grab Wagic at http://wololo.net/wagic/download/

viagra online said...

It was a great article, and I can see your talent to redact the information, specially with the structural forms, all is about good literature, I mean you're a good writer. 23jj