diff --git a/projects/mtg/bin/Res/rules/momir.txt b/projects/mtg/bin/Res/rules/momir.txt index 1585cf48c..5de867fde 100644 --- a/projects/mtg/bin/Res/rules/momir.txt +++ b/projects/mtg/bin/Res/rules/momir.txt @@ -1,4 +1,6 @@ include mtg.txt +name=Momir Basic +unlock=prx_rimom [INIT] mode=momir [PLAYERS] diff --git a/projects/mtg/bin/Res/rules/mtg.txt b/projects/mtg/bin/Res/rules/mtg.txt index 572a22a73..c40c5eab7 100644 --- a/projects/mtg/bin/Res/rules/mtg.txt +++ b/projects/mtg/bin/Res/rules/mtg.txt @@ -1,3 +1,4 @@ +name=Classic [INIT] mode=mtg [PLAYERS] diff --git a/projects/mtg/bin/Res/rules/random1.txt b/projects/mtg/bin/Res/rules/random1.txt index f26b3f37c..68c458003 100644 --- a/projects/mtg/bin/Res/rules/random1.txt +++ b/projects/mtg/bin/Res/rules/random1.txt @@ -1,3 +1,5 @@ include mtg.txt +name=Random 1 Color +unlock=prx_rnddeck [INIT] mode=random1 \ No newline at end of file diff --git a/projects/mtg/bin/Res/rules/random2.txt b/projects/mtg/bin/Res/rules/random2.txt index f1c02ba85..647ed1fbe 100644 --- a/projects/mtg/bin/Res/rules/random2.txt +++ b/projects/mtg/bin/Res/rules/random2.txt @@ -1,3 +1,5 @@ include mtg.txt +name=Random 2 Colors +unlock=prx_rnddeck [INIT] mode=random2 \ No newline at end of file diff --git a/projects/mtg/bin/Res/rules/story.txt b/projects/mtg/bin/Res/rules/story.txt new file mode 100644 index 000000000..145311781 --- /dev/null +++ b/projects/mtg/bin/Res/rules/story.txt @@ -0,0 +1,4 @@ +include mtg.txt +name=Story +[INIT] +mode=story \ No newline at end of file diff --git a/projects/mtg/bin/Res/rules/testsuite.txt b/projects/mtg/bin/Res/rules/testsuite.txt index da38c9016..21f017e4c 100644 --- a/projects/mtg/bin/Res/rules/testsuite.txt +++ b/projects/mtg/bin/Res/rules/testsuite.txt @@ -1,4 +1,5 @@ #make sure this file always looks like mtg.txt, minus the initial shuffle and draw +hidden [INIT] mode=mtg [PLAYERS] diff --git a/projects/mtg/include/GameApp.h b/projects/mtg/include/GameApp.h index 05f001b1f..c4ee2caf5 100644 --- a/projects/mtg/include/GameApp.h +++ b/projects/mtg/include/GameApp.h @@ -25,6 +25,8 @@ #include "JNetwork.h" #endif //NETWORK_SUPPORT + +class Rules; enum { PLAYER_TYPE_CPU = 0, @@ -68,6 +70,7 @@ private: public: int gameType; + Rules * rules; CardEffect *effect; #ifdef NETWORK_SUPPORT JNetwork* mpNetwork; diff --git a/projects/mtg/include/GameStateDuel.h b/projects/mtg/include/GameStateDuel.h index e76a80720..e712151b1 100644 --- a/projects/mtg/include/GameStateDuel.h +++ b/projects/mtg/include/GameStateDuel.h @@ -14,7 +14,6 @@ class TestSuite; #endif class Credits; -class Rules; #ifdef NETWORK_SUPPORT class JNetwork; #endif @@ -41,7 +40,6 @@ private: bool premadeDeck; int OpponentsDeckid; string musictrack; - Rules * rules; bool MusicExist(string FileName); void loadPlayer(int playerId, int decknb = 0, bool isAI = false, bool isNetwork = false); diff --git a/projects/mtg/include/Rules.h b/projects/mtg/include/Rules.h index 39e7ce423..6b4bead31 100644 --- a/projects/mtg/include/Rules.h +++ b/projects/mtg/include/Rules.h @@ -70,11 +70,20 @@ public: }; string bg; - - Rules(string filename, string bg = ""); - int load(string filename); + string filename; int gamemode; + bool hidden; + string displayName; + int unlockOption; + static vector RulesList; + + Rules(string bg = ""); + int load(string _filename); + static int loadAllRules(); + static void unloadAllRules(); + static Rules * getRulesByFilename(string _filename); void initPlayers(); + bool canChooseDeck(); //True if the players get to select their decks, false if the decks are automatically generated by the mode void addExtraRules(); void initGame(); void cleanup(); @@ -85,4 +94,6 @@ public: }; + + #endif diff --git a/projects/mtg/src/AIStats.cpp b/projects/mtg/src/AIStats.cpp index 6aabc4086..df5189d79 100644 --- a/projects/mtg/src/AIStats.cpp +++ b/projects/mtg/src/AIStats.cpp @@ -34,6 +34,9 @@ AIStats::~AIStats() void AIStats::updateStatsCard(MTGCardInstance * cardInstance, Damage * damage, float multiplier) { MTGCard * card = cardInstance->model; + if (!card) + return; //card can be null because some special cardInstances (such as ExtraRules) don't have a "model" + AIStat * stat = find(card); if (!stat) { diff --git a/projects/mtg/src/GameApp.cpp b/projects/mtg/src/GameApp.cpp index 12675c7c0..7366f0d36 100644 --- a/projects/mtg/src/GameApp.cpp +++ b/projects/mtg/src/GameApp.cpp @@ -26,6 +26,7 @@ #include "DeckManager.h" #include "Translate.h" #include "WFilter.h" +#include "Rules.h" #define DEFAULT_DURATION .25 @@ -247,6 +248,9 @@ void GameApp::Create() mCurrentState = NULL; mNextState = mGameStates[GAME_STATE_MENU]; + LOG("--Load Game rules"); + Rules::loadAllRules(); + //Set Audio volume JSoundSystem::GetInstance()->SetSfxVolume(options[Options::SFXVOLUME].number); JSoundSystem::GetInstance()->SetMusicVolume(options[Options::MUSICVOLUME].number); @@ -298,6 +302,8 @@ void GameApp::Destroy() DeckEditorMenu::destroy(); options.theGame = NULL; + + Rules::unloadAllRules(); LOG("==Destroying GameApp Successful=="); #ifdef TRACK_FILE_USAGE_STATS diff --git a/projects/mtg/src/GameStateDuel.cpp b/projects/mtg/src/GameStateDuel.cpp index 8e98b44c3..23e225294 100644 --- a/projects/mtg/src/GameStateDuel.cpp +++ b/projects/mtg/src/GameStateDuel.cpp @@ -81,7 +81,6 @@ GameState(parent) #endif credits = NULL; - rules = NULL; #ifdef NETWORK_SUPPORT RegisterNetworkPlayers(); @@ -233,7 +232,7 @@ void GameStateDuel::loadTestSuitePlayers() GameObserver::Init(mPlayers, 2); game = GameObserver::GetInstance(); - game->startGame(rules); + game->startGame(mParent->rules); if (mParent->gameType == GAME_TYPE_MOMIR) { game->addObserver(NEW MTGMomirRule(-1, MTGCollection())); @@ -267,7 +266,6 @@ void GameStateDuel::End() mPlayers[i] = NULL; } SAFE_DELETE(credits); - SAFE_DELETE(rules); SAFE_DELETE(menu); SAFE_DELETE(opponentMenu); @@ -326,19 +324,8 @@ void GameStateDuel::Update(float dt) break; case DUEL_STATE_CHOOSE_DECK1: - if (mParent->gameType == GAME_TYPE_MOMIR) + if (!mParent->rules->canChooseDeck()) { - rules = NEW Rules("momir.txt"); - mGamePhase = DUEL_STATE_PLAY; - } - else if (mParent->gameType == GAME_TYPE_RANDOM1) - { - rules = NEW Rules("random1.txt"); - mGamePhase = DUEL_STATE_PLAY; - } - else if (mParent->gameType == GAME_TYPE_RANDOM2) - { - rules = NEW Rules("random2.txt"); mGamePhase = DUEL_STATE_PLAY; } #ifdef TESTSUITE @@ -346,7 +333,6 @@ void GameStateDuel::Update(float dt) { if (testSuite && testSuite->loadNext()) { - rules = NEW Rules("testsuite.txt"); loadTestSuitePlayers(); mGamePhase = DUEL_STATE_PLAY; testSuite->pregameTests(); @@ -367,7 +353,6 @@ void GameStateDuel::Update(float dt) #endif else { - if (!rules) rules = NEW Rules("mtg.txt"); if (mParent->players[0] == PLAYER_TYPE_HUMAN) { if (!popupScreen || popupScreen->isClosed()) deckmenu->Update(dt); @@ -424,7 +409,7 @@ void GameStateDuel::Update(float dt) { GameObserver::Init(mPlayers, 2); game = GameObserver::GetInstance(); - game->startGame(rules); + game->startGame(mParent->rules); if (mParent->gameType == GAME_TYPE_MOMIR) { game->addObserver(NEW MTGMomirRule(-1, MTGCollection())); diff --git a/projects/mtg/src/GameStateMenu.cpp b/projects/mtg/src/GameStateMenu.cpp index 63e2552db..29c65ba3a 100644 --- a/projects/mtg/src/GameStateMenu.cpp +++ b/projects/mtg/src/GameStateMenu.cpp @@ -18,6 +18,7 @@ #include "utils.h" #include "WFont.h" #include +#include "Rules.h" #ifdef NETWORK_SUPPORT #include #endif//NETWORK_SUPPORT @@ -69,11 +70,7 @@ enum #endif //NETWORK_SUPPORT SUBMENUITEM_DEMO, SUBMENUITEM_TESTSUITE, - SUBMENUITEM_MOMIR, - SUBMENUITEM_CLASSIC, - SUBMENUITEM_RANDOM1, - SUBMENUITEM_RANDOM2, - SUBMENUITEM_STORY + SUBMENUITEM_END_OFFSET }; GameStateMenu::GameStateMenu(GameApp* parent) : @@ -136,6 +133,7 @@ void GameStateMenu::Create() scrollerSet = 0; splashTex = NULL; + } void GameStateMenu::Destroy() @@ -470,8 +468,6 @@ void GameStateMenu::Update(float dt) if (primitivesLoadCounter == -1) { listPrimitives(); - // Move translator init to GameApp::Create(). - // Translator::GetInstance()->init(); } if (primitivesLoadCounter < (int) (primitives.size())) { @@ -600,15 +596,14 @@ void GameStateMenu::Update(float dt) subMenuController = NEW SimpleMenu(MENU_FIRST_DUEL_SUBMENU, this, Fonts::MENU_FONT, 150, 60); if (subMenuController) { - subMenuController->Add(SUBMENUITEM_CLASSIC, "Classic"); - if (options[Options::MOMIR_MODE_UNLOCKED].number) - subMenuController->Add(SUBMENUITEM_MOMIR, "Momir Basic"); - if (options[Options::RANDOMDECK_MODE_UNLOCKED].number) + for (size_t i = 0; i < Rules::RulesList.size(); ++i) { - subMenuController->Add(SUBMENUITEM_RANDOM1, "Random 1 Color"); - subMenuController->Add(SUBMENUITEM_RANDOM2, "Random 2 Colors"); + Rules * rules = Rules::RulesList[i]; + if (!rules->hidden && (rules->unlockOption == INVALID_OPTION || options[rules->unlockOption].number)) + { + subMenuController->Add(SUBMENUITEM_END_OFFSET + i, rules->displayName.c_str()); + } } - subMenuController->Add(SUBMENUITEM_STORY, "Story"); subMenuController->Add(SUBMENUITEM_CANCEL, "Cancel"); } } @@ -885,49 +880,24 @@ void GameStateMenu::ButtonPressed(int controllerId, int controlId) currentState = MENU_STATE_MAJOR_MAINMENU | MENU_STATE_MINOR_SUBMENU_CLOSING; break; - case SUBMENUITEM_CLASSIC: - this->hasChosenGameType = true; - mParent->gameType = GAME_TYPE_CLASSIC; - subMenuController->Close(); - currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; - break; - - case SUBMENUITEM_MOMIR: - this->hasChosenGameType = true; - mParent->gameType = GAME_TYPE_MOMIR; - subMenuController->Close(); - currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; - break; - - case SUBMENUITEM_RANDOM1: - this->hasChosenGameType = true; - mParent->gameType = GAME_TYPE_RANDOM1; - subMenuController->Close(); - currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; - break; - - case SUBMENUITEM_RANDOM2: - this->hasChosenGameType = true; - mParent->gameType = GAME_TYPE_RANDOM2; - subMenuController->Close(); - currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; - break; - - case SUBMENUITEM_STORY: - this->hasChosenGameType = true; - mParent->gameType = GAME_TYPE_STORY; - subMenuController->Close(); - currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; - break; - #ifdef TESTSUITE case SUBMENUITEM_TESTSUITE: + mParent->rules = Rules::getRulesByFilename("testsuite.txt"); + this->hasChosenGameType = true; + mParent->gameType = GAME_TYPE_CLASSIC; 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 + default: //Game modes + this->hasChosenGameType = true; + mParent->rules = Rules::RulesList[controlId - SUBMENUITEM_END_OFFSET]; + mParent->gameType = (mParent->rules->gamemode); //TODO can we get rid of gameType in the long run, since it is also stored in the rules object ? + subMenuController->Close(); + currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; + break; } break; } diff --git a/projects/mtg/src/Rules.cpp b/projects/mtg/src/Rules.cpp index 3416680c2..a35563a45 100644 --- a/projects/mtg/src/Rules.cpp +++ b/projects/mtg/src/Rules.cpp @@ -13,6 +13,59 @@ #include "AIPlayer.h" #include + +vector Rules::RulesList = vector(); + +//Sorting by dissplayName +struct RulesMenuCmp{ + bool operator()(const Rules * a,const Rules * b) const{ + return a->displayName < b->displayName; + } +} RulesMenuCmp_; + +Rules * Rules::getRulesByFilename(string _filename) +{ + for (size_t i = 0; i < RulesList.size(); ++i) + { + if (RulesList[i]->filename == _filename) + return RulesList[i]; + } + return NULL; +} + +int Rules::loadAllRules() +{ + DIR *dip = opendir(JGE_GET_RES("rules").c_str()); + struct dirent *dit; + + while ((dit = readdir(dip))) + { + Rules * rules = NEW Rules(); + if (rules->load(dit->d_name)) + { + RulesList.push_back(rules); + } + else + { + SAFE_DELETE(rules); + } + } + //Kind of a hack here, we sort Rules alphabetically because it turns out to be matching + // The historical order of Game modes: Classic, Momir Basic, Random 1, Random 2, Story + std::sort(RulesList.begin(),RulesList.end(),RulesMenuCmp_); + closedir(dip); + return 1; +} + +void Rules::unloadAllRules() +{ + for (size_t i = 0; i < RulesList.size(); ++i) + { + SAFE_DELETE(RulesList[i]); + } + RulesList.clear(); +} + int Rules::getMTGId(string cardName) { int cardnb = atoi(cardName.c_str()); @@ -195,7 +248,7 @@ void Rules::addExtraRules() #ifdef NETWORK_SUPPORT && !g->players[1] == PLAYER_TYPE_REMOTE #endif //NETWORK_SUPPORT - )//keep this out of mimor and other game modes. + )//keep this out of momir and other game modes. { difficultyRating = DeckManager::getDifficultyRating(g->players[0], g->players[1]); } @@ -389,10 +442,7 @@ void Rules::initGame() DebugTrace("RULES Init Game\n"); //Set the current player/phase - if (g->currentPlayer->playMode - != Player::MODE_TEST_SUITE && /*g->mRules->gamemode != GAME_TYPE_MOMIR && g->mRules->gamemode - != GAME_TYPE_RANDOM1 && g->mRules->gamemode != GAME_TYPE_RANDOM2 &&*/ g->mRules->gamemode - != GAME_TYPE_STORY) + if (g->currentPlayer->playMode!= Player::MODE_TEST_SUITE && g->mRules->gamemode!= GAME_TYPE_STORY) { if(OptionWhosFirst::WHO_R == options[Options::FIRSTPLAYER].number) initState.player = WRand() % 2; @@ -490,25 +540,33 @@ void Rules::cleanup() initState.cleanup(); } -Rules::Rules(string filename, string _bg) +Rules::Rules(string _bg) { bg = _bg; - load(filename); + unlockOption = INVALID_OPTION; + hidden = false; + filename = ""; +} + +bool Rules::canChooseDeck() +{ + return (gamemode == GAME_TYPE_CLASSIC); } int Rules::load(string _filename) { - - char filename[4096]; + if (!filename.size()) //this check is necessary because of the recursive calls (a fil loads other files) + filename = _filename; + char c_filename[4096]; if (fileExists(_filename.c_str())) { - sprintf(filename, "%s", _filename.c_str()); + sprintf(c_filename, "%s", _filename.c_str()); } else { - sprintf(filename, JGE_GET_RES("rules/%s").c_str(), _filename.c_str()); + sprintf(c_filename, JGE_GET_RES("rules/%s").c_str(), _filename.c_str()); } - wagic::ifstream file(filename); + wagic::ifstream file(c_filename); std::string s; int state = PARSE_UNDEFINED; @@ -522,6 +580,7 @@ int Rules::load(string _filename) if (!s.size()) continue; if (s[s.size() - 1] == '\r') s.erase(s.size() - 1); //Handle DOS files if (s[0] == '#') continue; + string scopy = s; std::transform(s.begin(), s.end(), s.begin(), ::tolower); if (s.find("include ") == 0) { @@ -552,6 +611,18 @@ int Rules::load(string _filename) switch (state) { case PARSE_UNDEFINED: + if (s.find("name=") == 0) + { + displayName = scopy.substr(5); + } + else if (s.find("unlock=") == 0) + { + unlockOption = Options::getID(s.substr(7)); + } + else if (s.find("hidden") == 0) + { + hidden = true; + } break; case PARSE_INIT: if (s.find("auto=") == 0) @@ -592,5 +663,6 @@ int Rules::strToGameMode(string s) if (s.compare("momir") == 0) return GAME_TYPE_MOMIR; if (s.compare("random1") == 0) return GAME_TYPE_RANDOM1; if (s.compare("random2") == 0) return GAME_TYPE_RANDOM2; + if (s.compare("story") == 0) return GAME_TYPE_STORY; return GAME_TYPE_CLASSIC; } diff --git a/projects/mtg/src/StoryFlow.cpp b/projects/mtg/src/StoryFlow.cpp index 8672386df..80f6fdb43 100644 --- a/projects/mtg/src/StoryFlow.cpp +++ b/projects/mtg/src/StoryFlow.cpp @@ -314,7 +314,8 @@ void StoryDuel::init() string rulesFile = folder; rulesFile.append("/rules.txt"); - rules = NEW Rules(rulesFile, bg); + rules = NEW Rules(bg); + rules->load(rulesFile); GameObserver::Init(players, 2); game = GameObserver::GetInstance();