my memory must have failed me with certain things about the rules regarding mana.
OK guys, I really love the Wagic project, it's absolutely fantastic! However, I always had a little
problem with it, and the problem was in the basic rules compliance. There were some things that I
always wanted to see implemented in it, but since they were not implemented so far, I decided to try
and tinker with the source code and implement them myself. I used the latest post-0.12.1 SVN as a
base for my experiments, and I've more or less succeeded so far.
Here are the goals of this rules compliance patch, so that you can see what you will hopefully have if
you complete the steps in this guide:
1. Allow the starting player to be chosen randomly at start, thus emulating the flip of a coin,
instead of the always giving the priority to the human player. This, however, as of right now should
not apply to the Story Mode, because it would break it.
2. (OPTIONAL) Implement the mana burn rule, whereby all the mana that was still in the mana pool at
the end of the phase would not just go away, but instead damage the offending player. This rule was
apparently taken out with the invent of Magic 2010 (thanks wololo!), but if you want this old-school
option, I'll teach you how to implement it.
There are as of yet the following problems with the solution I propose below:
- I haven't yet found an elegant way to check for the current game mode from Rules.cpp (e.g. detect
whether we are in Classic game mode or Story game mode), thus I had to resort to implementing a
static parameter in GameApp just for that reason. It's very clunky and hackish, so if there's a
better way to do it, please tell me or propose an alternative solution).
- We won't be adding any new animations per se, so you won't see a coin flip itself - yes, it means
no beautiful animation of a coin being tossed in the air and landing on your MTGish table - but at
least the starting player WILL be randomized.
- I also want to implement the Mulligan rule, but as of right now I haven't gotten to it. Also, it's
relatively low priority right now due to my time constraints.
- This patch hasn't yet extensively been tested (though I successfully finished several games by now
without any problems), so it MIGHT, in fact, break your Wagic. So make backups and don't tell me I
didn't warn you.
*************** AND HERE WE GO ***************
You will need to obtain the source code from the SVN repository and you have to know how to compile
it and how to work in your favorite IDE, such as Visual Studio. If you don't know some of this or if
you don't feel comfortable working with the code, or if words such as programming or C++ make you
feel uneasy, stop at this point and read the Wiki for the compilation guide. That being said,
here we go.
STEP 1. IMPLEMENTING A WAY TO DETECT THE GAME MODE (HACKISH!)
1.1. Now, let's turn to our second problem - implementing a way to randomize the starting player. Before we
can do that properly, however, we need to make sure we can check whether we are in the Story mode or not.
We need to avoid randomizing the player in the story mode, at least for now, because it looks like the
Story mode is planned with the fixed starting player in mind. If that changes later on, I'll try to update
this guide accordingly. As I said, the current solution is not at all elegant and is hackish, and may
actually cause problems (even though it didn't so far for me), so bear with me and if you know of a better
way to do this, please let me know. So, first, let's open GameApp.h (in the "Header Files") and introduce
a new property to the GameApp class, which we can use from anywhere to see if we're in Story mode or not.
Around lines 60-70, look for the following line:
Code: Select all
int gameType;
Code: Select all
static int StoryMode;
this time) and look for the following line:
Code: Select all
MTGAllCards * GameApp::collection = NULL;
Code: Select all
int GameApp::StoryMode = 0;
default. Now, let's make sure the game actually sets this parameter depending on the game mode that was chosen. Open GameStateMenu.cpp
and look for the definition of the function ButtonPressed. Inside that function, look for the following large block of code that sets
certain stuff in the game engine depending on which game mode the user chooses:
Code: Select all
case SUBMENUITEM_CLASSIC:
this->hasChosenGameType = 1;
mParent->gameType = GAME_TYPE_CLASSIC;
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_MOMIR:
this->hasChosenGameType = 1;
mParent->gameType = GAME_TYPE_MOMIR;
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_RANDOM1:
this->hasChosenGameType = 1;
mParent->gameType = GAME_TYPE_RANDOM1;
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_RANDOM2:
this->hasChosenGameType = 1;
mParent->gameType = GAME_TYPE_RANDOM2;
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_STORY:
this->hasChosenGameType = 1;
mParent->gameType = GAME_TYPE_STORY;
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
#ifdef TESTSUITE
case SUBMENUITEM_TESTSUITE:
mParent->players[0] = PLAYER_TYPE_TESTSUITE;
mParent->players[1] = PLAYER_TYPE_TESTSUITE;
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
#endif
Story Mode, we'll set it to "1" in order for us to be able to tell that we're in the Story mode and we don't need to randomize the
starting player. For all other modes, we'll set it to "0" so that the starting player is randomized. Take a look at the modified code
block below and carefully insert the lines marked with the "++" comment into the appropriate locations in your file:
Code: Select all
case SUBMENUITEM_CLASSIC:
this->hasChosenGameType = 1;
mParent->gameType = GAME_TYPE_CLASSIC;
GameApp::StoryMode = 0; // ++ You need to insert this! ++
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_MOMIR:
this->hasChosenGameType = 1;
mParent->gameType = GAME_TYPE_MOMIR;
GameApp::StoryMode = 0; // ++ You need to insert this! ++
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_RANDOM1:
this->hasChosenGameType = 1;
mParent->gameType = GAME_TYPE_RANDOM1;
GameApp::StoryMode = 0; // ++ You need to insert this! ++
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_RANDOM2:
this->hasChosenGameType = 1;
mParent->gameType = GAME_TYPE_RANDOM2;
GameApp::StoryMode = 0; // ++ You need to insert this! ++
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_STORY:
this->hasChosenGameType = 1;
mParent->gameType = GAME_TYPE_STORY;
GameApp::StoryMode = 1; // ++ You need to insert this (note the "1")! ++
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
#ifdef TESTSUITE
case SUBMENUITEM_TESTSUITE:
mParent->players[0] = PLAYER_TYPE_TESTSUITE;
mParent->players[1] = PLAYER_TYPE_TESTSUITE;
GameApp::StoryMode = 0; // ++ You need to insert this! ++
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
#endif
STEP 2. MAKING THE GAME RANDOMIZE THE STARTING PLAYER.
2.1. Alrighty, now the actual fun part - making the game make use of our parameter implemented above, and making it actually randomize
the starting player, as we planned. To achieve that, open your Rules.cpp file (in "Source Files"), and look for the function (which is
actually a "constructor" in proper C++ terms) which is defined as RulesState::RulesState(). In its original form it will look something
like this:
Code: Select all
RulesState::RulesState(){
phase = Constants::MTG_PHASE_FIRSTMAIN;
player = 0;
}
game. That's still what we want in the Story Mode, remember, so we'll keep the original code as it is, but under it we're going to
insert some code that will check if we're NOT in Story Mode, and if we're not, we'll assign the "player" variable a random value - either
0 for the human player, or 1 for the AI player. So, change the code block above so it looks like this:
Code: Select all
RulesState::RulesState(){
phase = Constants::MTG_PHASE_FIRSTMAIN;
player = 0;
// ++ This is where our patch begins ++
if (GameApp::StoryMode == 0) // If we're NOT in story mode...
player = rand() % 2; // ... randomize the starting player
// -- And here our patch ends --
}
This part of the guide implements the optional Mana Burn rule which, per the MTG 2010 rules, was
taken out. If you don't want the mana burn rule, feel free to skip Step 3. Thanks to wololo for correcting
me on how and when it worked!
3.1. Open the file GameObserver.cpp (under "Source Files"), and locate a function called
nextGamePhase. It will be defined like this ("..." here and everywhere below means just many lines
of code that I skipped here):
Code: Select all
void GameObserver::nextGamePhase(){
...
}
pool at every phase shift:
Code: Select all
for (int i = 0; i < 2; ++i)
players[i]->getManaPool()->init();
3.3. Here we'll implement the Mana Burn rule. So, right above that "for" loop, we need to make
some modifications so that the mana burn is applied. Take a look carefully below and insert the lines
between the "++" and the "--" comments:
Code: Select all
// ++ This is where our patch begins ++
int manaburn = currentPlayer->getManaPool()->getConvertedCost(); // see how much mana is left
if (manaburn > 0) { // if there was any,
JSample * sample = resources.RetrieveSample("manaburn.wav"); // see if a sound manaburn.wav exists,
if (sample) JSoundSystem::GetInstance()->PlaySample(sample); // play it if it exists,
currentPlayer->dealDamage(manaburn); // deal damage to the player,
}
// -- And here our patch ends --
// the original "if" block and all the rest of the code follows
for (int i = 0; i < 2; ++i)
players[i]->getManaPool()->init();
...
game. If you want a sound to be played when the player's mana burns, you should put a file named "manaburn.wav" in your
"Res/sound/sfx" folder - either take your favorite mana burn sound, e.g. the Shandalar screech (yeech!), or be creative and invent
something for your mana burn. Note that the mana burn will still work even without that sound file, it just won't be accompanied with a
sound effect.
------------------------------------------------
If you're still with us at this point, congratulations!
Cross your fingers and try compiling and running the game. I hope I didn't miss any steps here, and if I didn't, it should work and you
should now have a random starting player and optionally the mana burn rule implemented. If I did miss something and if it
doesn't work for you, please provide some feedback.
If you think of an improvement to my code above, especially the part where I detect the game mode, or if you implement some
more MTG rules, please feel free to share them with the rest of the community here.
Good luck, and happy MTGing!
- Agetian (a.k.a. Gadget2006)