#include "PrecompiledHeader.h" #include "DeckMenu.h" #include "GameStateDuel.h" #include "utils.h" #include "AIPlayer.h" #include "AIMomirPlayer.h" #include "PlayerData.h" #include "DeckStats.h" #include "DeckManager.h" #include "DeckMetaData.h" #include "MTGRules.h" #include "Credits.h" #include "Translate.h" #include "Rules.h" #include "ModRules.h" #ifdef TESTSUITE #include "TestSuiteAI.h" #endif #ifdef NETWORK_SUPPORT #include "NetworkPlayer.h" #endif #if defined (WIN32) || defined (LINUX) #include #endif const float MENU_FONT_SCALE = 1.0f; enum ENUM_DUEL_STATE { DUEL_STATE_START, DUEL_STATE_END, DUEL_STATE_CHOOSE_DECK1, DUEL_STATE_DECK1_DETAILED_INFO, DUEL_STATE_DECK2_DETAILED_INFO, DUEL_STATE_CHOOSE_DECK1_TO_2, DUEL_STATE_CHOOSE_DECK2, DUEL_STATE_CHOOSE_DECK2_TO_PLAY, DUEL_STATE_ERROR_NO_DECK, DUEL_STATE_CANCEL, DUEL_STATE_PLAY, DUEL_STATE_BACK_TO_MAIN_MENU, DUEL_STATE_MENU, #ifdef NETWORK_SUPPORT DUEL_STATE_OPPONENT_WAIT, #endif //NETWORK_SUPPORT DUEL_STATE_ERROR }; enum ENUM_DUEL_MENUS { DUEL_MENU_GAME_MENU, DUEL_MENU_CHOOSE_DECK, DUEL_MENU_CHOOSE_OPPONENT, DUEL_MENU_DETAILED_DECK1_INFO, DUEL_MENU_DETAILED_DECK2_INFO }; int GameStateDuel::selectedPlayerDeckId = 0; int GameStateDuel::selectedAIDeckId = 0; GameStateDuel::GameStateDuel(GameApp* parent) : GameState(parent) { for (int i = 0; i < 2; i++) { mPlayers[i] = NULL; } premadeDeck = false; game = NULL; deckmenu = NULL; opponentMenu = NULL; menu = NULL; popupScreen = NULL; #ifdef TESTSUITE testSuite = NULL; #endif credits = NULL; #ifdef NETWORK_SUPPORT RegisterNetworkPlayers(); #endif //NETWORK_SUPPORT } GameStateDuel::~GameStateDuel() { End(); } void GameStateDuel::Start() { JRenderer * renderer = JRenderer::GetInstance(); renderer->EnableVSync(true); OpponentsDeckid = 0; #ifdef TESTSUITE SAFE_DELETE(testSuite); testSuite = NEW TestSuite(JGE_GET_RES("test/_tests.txt").c_str(),MTGCollection()); #endif mGamePhase = DUEL_STATE_CHOOSE_DECK1; credits = NEW Credits(); menu = NULL; int decksneeded = 0; for (int i = 0; i < 2; i++) { if (mParent->players[i] == PLAYER_TYPE_HUMAN) { DeckManager::EndInstance(); decksneeded = 1; deckmenu = NEW DeckMenu(DUEL_MENU_CHOOSE_DECK, this, Fonts::OPTION_FONT, "Choose a Deck", GameStateDuel::selectedPlayerDeckId, true); deckmenu->enableDisplayDetailsOverride(); DeckManager *deckManager = DeckManager::GetInstance(); vector playerDeckList = BuildDeckList(options.profileFile()); int nbDecks = playerDeckList.size(); if (nbDecks > 1) { decksneeded = 0; deckmenu->Add(MENUITEM_RANDOM_PLAYER, "Random", "Play with a random deck."); } renderDeckMenu(deckmenu, playerDeckList); // save the changes to the player deck list maintained in DeckManager deckManager->updateMetaDataList(&playerDeckList, false); playerDeckList.clear(); break; } } if (deckmenu) { if (decksneeded) { if (gModRules.general.hasDeckEditor()) { //translate deck creating desc Translator * t = Translator::GetInstance(); string desc = "Highly recommended to get\nthe full Wagic experience!"; map::iterator it = t->deckValues.find("Create your Deck!"); if (it != t->deckValues.end()) desc = it->second; deckmenu->Add(MENUITEM_NEW_DECK, "Create your Deck!", desc); } premadeDeck = true; fillDeckMenu(deckmenu, JGE_GET_RES("player/premade")); } else if (gModRules.general.hasDeckEditor()) { deckmenu->Add(MENUITEM_NEW_DECK, "New Deck...", "Create a new deck to play with."); } deckmenu->Add(MENUITEM_CANCEL, "Main Menu", "Return to Main Menu"); } for (int i = 0; i < 2; ++i) { mPlayers[i] = NULL; } } void GameStateDuel::loadPlayer(int playerId, int decknb, bool isAI, bool isNetwork) { if (decknb) { if (!isAI) { //Human Player if(playerId == 0) { char deckFile[255]; if (premadeDeck) sprintf(deckFile, JGE_GET_RES("player/premade/deck%i.txt").c_str(), decknb); else sprintf(deckFile, "%s/deck%i.txt", options.profileFile().c_str(), decknb); char deckFileSmall[255]; sprintf(deckFileSmall, "player_deck%i", decknb); mPlayers[playerId] = NEW HumanPlayer(deckFile, deckFileSmall); #ifdef NETWORK_SUPPORT if(isNetwork) { ProxyPlayer* mProxy; mProxy = NEW ProxyPlayer(mPlayers[playerId], mParent->mpNetwork); } } else { //Remote player mPlayers[playerId] = NEW RemotePlayer(mParent->mpNetwork); #endif //NETWORK_SUPPORT } } else { //AI Player, chooses deck AIPlayerFactory playerCreator; Player * opponent = NULL; if (playerId == 1) opponent = mPlayers[0]; mPlayers[playerId] = playerCreator.createAIPlayer(MTGCollection(), opponent, decknb); } } else { //Random deck AIPlayerFactory playerCreator; Player * opponent = NULL; if (playerId == 1) opponent = mPlayers[0]; mPlayers[playerId] = playerCreator.createAIPlayer(MTGCollection(), opponent); } } void GameStateDuel::initRand(unsigned int seed) { if (!seed) seed = time(0); srand(seed); } #ifdef TESTSUITE void GameStateDuel::loadTestSuitePlayers() { if (!testSuite) return; initRand(testSuite->seed); SAFE_DELETE(game); for (int i = 0; i < 2; i++) { mPlayers[i] = NEW TestSuiteAI(testSuite, i); } mParent->gameType = testSuite->gameType; GameObserver::Init(mPlayers, 2); game = GameObserver::GetInstance(); game->startGame(mParent->rules); if (mParent->gameType == GAME_TYPE_MOMIR) { game->addObserver(NEW MTGMomirRule(-1, MTGCollection())); } } #endif void GameStateDuel::End() { DebugTrace("Ending GameStateDuel"); #ifdef TRACK_OBJECT_USAGE ObjectAnalytics::DumpStatistics(); #endif JRenderer::GetInstance()->EnableVSync(false); if (!premadeDeck && mPlayers[0] && mPlayers[1]) { // save the stats for the game mPlayers[0]->End(); } else if ( !mPlayers[1] && mPlayers[0] ) // clean up player object SAFE_DELETE( mPlayers[0] ); GameObserver::EndInstance(); // this will delete both player objects if a game has been played game = NULL; premadeDeck = false; for (int i = 0; i < 2; i++) { mPlayers[i] = NULL; } SAFE_DELETE(credits); SAFE_DELETE(menu); SAFE_DELETE(opponentMenu); SAFE_DELETE(deckmenu); SAFE_DELETE(popupScreen); #ifdef TESTSUITE SAFE_DELETE(testSuite); #endif } //TODO Move This to utils or ResourceManager. Don't we have more generic functions that can do that? bool GameStateDuel::MusicExist(string FileName) { string filepath = JGE_GET_RES(WResourceManager::Instance()->musicFile(FileName)); wagic::ifstream file(filepath.c_str()); if (file) { file.close(); return true; } else return false; } void GameStateDuel::ConstructOpponentMenu() { if (opponentMenu == NULL) { opponentMenu = NEW DeckMenu(DUEL_MENU_CHOOSE_OPPONENT, this, Fonts::OPTION_FONT, "Choose Opponent", GameStateDuel::selectedAIDeckId, true); opponentMenu->Add(MENUITEM_RANDOM_AI, "Random"); if (options[Options::EVILTWIN_MODE_UNLOCKED].number) opponentMenu->Add(MENUITEM_EVIL_TWIN, "Evil Twin", _("Can you defeat yourself?").c_str()); DeckManager * deckManager = DeckManager::GetInstance(); vector opponentDeckList; int nbUnlockedDecks = options[Options::CHEATMODEAIDECK].number ? 1000 : options[Options::AIDECKS_UNLOCKED].number; opponentDeckList = fillDeckMenu(opponentMenu, JGE_GET_RES("ai/baka"), "ai_baka", mPlayers[0], nbUnlockedDecks); deckManager->updateMetaDataList(&opponentDeckList, true); opponentMenu->Add(MENUITEM_CANCEL, "Cancel", _("Choose a different player deck").c_str()); opponentDeckList.clear(); } } void GameStateDuel::Update(float dt) { switch (mGamePhase) { case DUEL_STATE_ERROR_NO_DECK: if (JGE_BTN_OK == mEngine->ReadButton()) mParent->SetNextState(GAME_STATE_DECK_VIEWER); break; case DUEL_STATE_DECK1_DETAILED_INFO: case DUEL_STATE_DECK2_DETAILED_INFO: popupScreen->Update(dt); break; case DUEL_STATE_CHOOSE_DECK1: if (!mParent->rules->canChooseDeck()) { mGamePhase = DUEL_STATE_PLAY; } #ifdef TESTSUITE else if (mParent->players[1] == PLAYER_TYPE_TESTSUITE) { if (testSuite && testSuite->loadNext()) { loadTestSuitePlayers(); mGamePhase = DUEL_STATE_PLAY; testSuite->pregameTests(); testSuite->initGame(); } else { if (!game) { mGamePhase = DUEL_STATE_ERROR; } else { mGamePhase = DUEL_STATE_END; } } } #endif else { if (mParent->players[0] == PLAYER_TYPE_HUMAN) { if (!popupScreen || popupScreen->isClosed()) deckmenu->Update(dt); } else { loadPlayer(0); mGamePhase = DUEL_STATE_CHOOSE_DECK2; } } break; case DUEL_STATE_CHOOSE_DECK1_TO_2: if (deckmenu->isClosed()) mGamePhase = DUEL_STATE_CHOOSE_DECK2; else deckmenu->Update(dt); break; case DUEL_STATE_CHOOSE_DECK2: if (mParent->players[1] == PLAYER_TYPE_HUMAN) deckmenu->Update(dt); else { if (mParent->players[0] == PLAYER_TYPE_HUMAN) { ConstructOpponentMenu(); opponentMenu->Update(dt); } else { loadPlayer(1); mGamePhase = DUEL_STATE_PLAY; } } break; case DUEL_STATE_CHOOSE_DECK2_TO_PLAY: if (mParent->players[1] == PLAYER_TYPE_HUMAN) { if (deckmenu->isClosed()) mGamePhase = DUEL_STATE_PLAY; else deckmenu->Update(dt); } else { ConstructOpponentMenu(); if (opponentMenu->isClosed()) mGamePhase = DUEL_STATE_PLAY; else opponentMenu->Update(dt); } break; case DUEL_STATE_PLAY: if (!game) { GameObserver::Init(mPlayers, 2); game = GameObserver::GetInstance(); game->startGame(mParent->rules); if (mParent->gameType == GAME_TYPE_MOMIR) { game->addObserver(NEW MTGMomirRule(-1, MTGCollection())); } //start of in game music code musictrack = ""; //check opponent id and choose the music track based on it if (OpponentsDeckid) { char temp[4096]; sprintf(temp, "ai_baka_music%i.mp3", OpponentsDeckid); musictrack.assign(temp); } else if (mParent->gameType == GAME_TYPE_CLASSIC) musictrack = "ai_baka_music.mp3"; else if (mParent->gameType == GAME_TYPE_MOMIR) musictrack = "ai_baka_music_momir.mp3"; else if (mParent->gameType == GAME_TYPE_RANDOM1 || mParent->gameType == GAME_TYPE_RANDOM2) musictrack = "ai_baka_music_random.mp3"; if (!MusicExist(musictrack)) musictrack = "ai_baka_music.mp3"; GameApp::playMusic(musictrack); } game->Update(dt); if (game->gameOver) { if (game->players[1]->playMode != Player::MODE_TEST_SUITE) credits->compute(game->players[0], game->players[1], mParent); mGamePhase = DUEL_STATE_END; #ifdef TESTSUITE if (mParent->players[1] == PLAYER_TYPE_TESTSUITE) { if (testSuite->loadNext()) { loadTestSuitePlayers(); mGamePhase = DUEL_STATE_PLAY; testSuite->initGame(); } else mGamePhase = DUEL_STATE_END; } else #endif if (mParent->players[0] == PLAYER_TYPE_CPU && mParent->players[1] == PLAYER_TYPE_CPU) { End(); Start(); } } if (mEngine->GetButtonClick(JGE_BTN_MENU)) { if (!menu) { menu = NEW SimpleMenu(DUEL_MENU_GAME_MENU, this, Fonts::MENU_FONT, SCREEN_WIDTH / 2 - 100, 25, game->players[1]->deckName.c_str()); int cardsinhand = game->players[0]->game->hand->nb_cards; //almosthumane - mulligan if ((game->turn < 1) && (cardsinhand != 0) && game->currentGamePhase == Constants::MTG_PHASE_FIRSTMAIN && game->players[0]->game->inPlay->nb_cards == 0 && game->players[0]->game->graveyard->nb_cards == 0 && game->players[0]->game->exile->nb_cards == 0) //1st Play Check //IF there was no play at the moment automatically mulligan { menu->Add(MENUITEM_MULLIGAN, "Mulligan"); } //END almosthumane - mulligan menu->Add(MENUITEM_MAIN_MENU, "Back to main menu"); menu->Add(MENUITEM_CANCEL, "Cancel"); } mGamePhase = DUEL_STATE_MENU; } break; #ifdef NETWORK_SUPPORT case DUEL_STATE_OPPONENT_WAIT: { if(mPlayers[1] && mPlayers[1]->game) { // Player loaded menu->Close(); SAFE_DELETE(menu); mGamePhase = DUEL_STATE_PLAY; } else if(menu == NULL) { loadPlayer(1, 42/* 0 not good*/, false, true); menu = NEW SimpleMenu(DUEL_STATE_OPPONENT_WAIT, this, Fonts::MENU_FONT, 150, 60); if (menu) { menu->Add(MENUITEM_MAIN_MENU, "Back to main menu"); } } else { menu->Update(dt); } } break; #endif //NETWORK_SUPPORT case DUEL_STATE_MENU: menu->Update(dt); break; case DUEL_STATE_CANCEL: menu->Update(dt); if (menu->isClosed()) { mGamePhase = DUEL_STATE_PLAY; SAFE_DELETE(menu); } break; case DUEL_STATE_BACK_TO_MAIN_MENU: if (menu) { menu->Update(dt); if (menu->isClosed()) { PlayerData * playerdata = NEW PlayerData(MTGCollection()); playerdata->taskList->passOneDay(); playerdata->taskList->save(); SAFE_DELETE(playerdata); SAFE_DELETE(menu); } } mParent->DoTransition(TRANSITION_FADE, GAME_STATE_MENU); break; default: if (JGE_BTN_OK == mEngine->ReadButton()) mParent->SetNextState(GAME_STATE_MENU); } } void GameStateDuel::Render() { WFont * mFont = WResourceManager::Instance()->GetWFont(Fonts::MAIN_FONT); JRenderer * r = JRenderer::GetInstance(); r->ClearScreen(ARGB(0,0,0,0)); if (game) game->Render(); switch (mGamePhase) { case DUEL_STATE_END: { r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(200,0,0,0)); credits->Render(); #ifdef TESTSUITE if (mParent->players[1] == PLAYER_TYPE_TESTSUITE) { r->ClearScreen(ARGB(255,0,0,0)); char buf[4096]; int nbFailed = testSuite->nbFailed; int nbTests = testSuite->nbTests; if (!nbFailed) { sprintf(buf, "All %i tests successful!", nbTests); } else { sprintf(buf, "%i tests out of %i FAILED!", nbFailed, nbTests); } mFont->SetColor(ARGB(255,255,255,255)); mFont->DrawString(buf,0,SCREEN_HEIGHT/2); nbFailed = testSuite->nbAIFailed; nbTests = testSuite->nbAITests; if (nbTests) { if (!nbFailed) { sprintf(buf, "AI Tests: All %i tests successful!", nbTests); } else { sprintf(buf, "AI Tests: %i tests out of %i FAILED!", nbFailed, nbTests); } mFont->DrawString(buf,0,SCREEN_HEIGHT/2+20); } } #endif break; } case DUEL_STATE_ERROR: { r->ClearScreen(ARGB(200,0,0,0)); mFont->DrawString(_("AN ERROR OCCURRED, CHECK FILE NAMES").c_str(), 0, SCREEN_HEIGHT / 2); break; } case DUEL_STATE_CHOOSE_DECK1: case DUEL_STATE_CHOOSE_DECK1_TO_2: case DUEL_STATE_CHOOSE_DECK2: case DUEL_STATE_CHOOSE_DECK2_TO_PLAY: case DUEL_STATE_DECK1_DETAILED_INFO: case DUEL_STATE_DECK2_DETAILED_INFO: if (mParent->gameType != GAME_TYPE_CLASSIC #ifdef NETWORK_SUPPORT && mParent->gameType != GAME_TYPE_SLAVE #endif //NETWORK_SUPPORT ) mFont->DrawString(_("LOADING DECKS").c_str(), 0, SCREEN_HEIGHT / 2); else { if (opponentMenu) opponentMenu->Render(); else if (deckmenu && !deckmenu->isClosed()) deckmenu->Render(); if (menu) menu->Render(); if (popupScreen && !popupScreen->isClosed()) popupScreen->Render(); } break; case DUEL_STATE_ERROR_NO_DECK: mFont->DrawString(_("NO DECK AVAILABLE,").c_str(), 0, SCREEN_HEIGHT / 2); mFont->DrawString(_("PRESS CIRCLE TO GO TO THE DECK EDITOR!").c_str(), 0, SCREEN_HEIGHT / 2 + 20); break; #ifdef NETWORK_SUPPORT case DUEL_STATE_OPPONENT_WAIT: if (menu) menu->Render(); break; #endif //NETWORK_SUPPORT case DUEL_STATE_MENU: case DUEL_STATE_CANCEL: case DUEL_STATE_BACK_TO_MAIN_MENU: if (game) { r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(100,0,0,0)); char buffer[4096]; sprintf(buffer, _("Turn:%i").c_str(), game->turn); mFont->SetColor(ARGB(255,255,255,255)); mFont->DrawString(buffer, SCREEN_WIDTH / 2, 0, JGETEXT_CENTER); } if (menu) menu->Render(); } } void GameStateDuel::ButtonPressed(int controllerId, int controlId) { int deckNumber = controlId; DeckManager * deckManager = DeckManager::GetInstance(); int aiDeckSize = deckManager->getAIDeckOrderList()->size(); switch (controllerId) { case DUEL_MENU_DETAILED_DECK1_INFO: if ((popupScreen || deckmenu->showDetailsScreen())) { DeckMetaData* selectedDeck = deckmenu->getSelectedDeck(); if (!popupScreen->isClosed()) { popupScreen->Close(); mGamePhase = DUEL_STATE_CHOOSE_DECK1; SAFE_DELETE( popupScreen ); } else { popupScreen->Update(selectedDeck); popupScreen->Render(); } } break; case DUEL_MENU_DETAILED_DECK2_INFO: if ((popupScreen || opponentMenu->showDetailsScreen())) { DeckMetaData* selectedDeck = opponentMenu->getSelectedDeck(); if (!popupScreen->isClosed()) { popupScreen->Close(); mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; SAFE_DELETE( popupScreen ); } else { popupScreen->Update(selectedDeck); popupScreen->Render(); } } break; case DUEL_MENU_CHOOSE_OPPONENT: switch (controlId) { case MENUITEM_RANDOM_AI: loadPlayer(1); opponentMenu->Close(); mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; break; default: // cancel option. return to player deck selection if (controlId == MENUITEM_CANCEL) { opponentMenu->Close(); deckmenu->Close(); mParent->SetNextState(DUEL_STATE_CHOOSE_DECK1); mGamePhase = DUEL_STATE_CHOOSE_DECK1; break; } else if (controlId == MENUITEM_MORE_INFO && opponentMenu->showDetailsScreen()) { DeckMetaData* selectedDeck = opponentMenu->getSelectedDeck(); if (!popupScreen) { popupScreen = NEW SimplePopup(DUEL_MENU_DETAILED_DECK2_INFO, this, Fonts::MAIN_FONT, "Detailed Information", selectedDeck, MTGCollection()); popupScreen->Render(); selectedAIDeckId = selectedDeck->getDeckId(); } else { popupScreen->Update(selectedDeck); } mGamePhase = DUEL_STATE_DECK2_DETAILED_INFO; break; } else if (controlId == MENUITEM_MORE_INFO && !opponentMenu->showDetailsScreen()) { // do nothing, ignore all key requests until popup is dismissed. break; } else if (controlId != MENUITEM_EVIL_TWIN && aiDeckSize > 0) // evil twin deckNumber = deckManager->getAIDeckOrderList()->at(controlId - 1)->getDeckId(); loadPlayer(1, deckNumber, 1); OpponentsDeckid = deckNumber; opponentMenu->Close(); mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; break; } break; case DUEL_MENU_CHOOSE_DECK: if (controlId == MENUITEM_RANDOM_PLAYER) // Random Player Deck Selection { vector * playerDeckList = deckManager->getPlayerDeckOrderList(); deckNumber = playerDeckList->at(WRand() % (playerDeckList->size()))->getDeckId(); loadPlayer(0, deckNumber); deckmenu->Close(); mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; break; } else if (controlId == MENUITEM_MAIN_MENU || controlId == MENUITEM_CANCEL) // user clicked on "Cancel" { if (deckmenu) deckmenu->Close(); mGamePhase = DUEL_STATE_BACK_TO_MAIN_MENU; break; } else if (controlId == MENUITEM_MORE_INFO && deckmenu->showDetailsScreen()) { DeckMetaData* selectedDeck = deckmenu->getSelectedDeck(); if (!popupScreen) { popupScreen = NEW SimplePopup(DUEL_MENU_DETAILED_DECK1_INFO, this, Fonts::MAIN_FONT, "Detailed Information", selectedDeck, MTGCollection()); popupScreen->Render(); selectedPlayerDeckId = deckmenu->getSelectedDeckId(); } else { popupScreen->Update(selectedDeck); } mGamePhase = DUEL_STATE_DECK1_DETAILED_INFO; break; } else if (controlId == MENUITEM_MORE_INFO) { // do nothing break; } if (controlId < 0) { mParent->SetNextState(GAME_STATE_DECK_VIEWER); return; } if (mGamePhase == DUEL_STATE_CHOOSE_DECK1) { vector * playerDeck = deckManager->getPlayerDeckOrderList(); if (!premadeDeck && controlId > 0) deckNumber = playerDeck->at(controlId - 1)->getDeckId(); loadPlayer(0, deckNumber, false #ifdef NETWORK_SUPPORT ,(mParent->players[1] == PLAYER_TYPE_REMOTE) #endif //NETWORK_SUPPORT ); deckmenu->Close(); #ifdef NETWORK_SUPPORT if(mParent->players[1] == PLAYER_TYPE_REMOTE) { // no need to choose an opponent deck in network mode mGamePhase = DUEL_STATE_OPPONENT_WAIT; } else #endif //NETWORK_SUPPORT { mGamePhase = DUEL_STATE_CHOOSE_DECK1_TO_2; } playerDeck = NULL; } else { loadPlayer(1, controlId); deckmenu->Close(); mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; } break; default: switch (controlId) { case MENUITEM_MAIN_MENU: menu->Close(); mGamePhase = DUEL_STATE_BACK_TO_MAIN_MENU; break; case MENUITEM_CANCEL: menu->Close(); mGamePhase = DUEL_STATE_CANCEL; break; case MENUITEM_MULLIGAN: //almosthumane - mulligan game->currentPlayer->takeMulligan(); menu->Close(); mGamePhase = DUEL_STATE_CANCEL; break; //END almosthumane - mulligan } } }