diff --git a/projects/mtg/bin/Res/graphics/ai_unlocked.png b/projects/mtg/bin/Res/graphics/ai_unlocked.png new file mode 100644 index 000000000..ff36d39a7 Binary files /dev/null and b/projects/mtg/bin/Res/graphics/ai_unlocked.png differ diff --git a/projects/mtg/bin/Res/graphics/new.png b/projects/mtg/bin/Res/graphics/new.png new file mode 100644 index 000000000..32f8beca9 Binary files /dev/null and b/projects/mtg/bin/Res/graphics/new.png differ diff --git a/projects/mtg/include/Credits.h b/projects/mtg/include/Credits.h index 2da684670..99d228c2e 100644 --- a/projects/mtg/include/Credits.h +++ b/projects/mtg/include/Credits.h @@ -25,10 +25,11 @@ public: class Credits{ private: time_t gameLength; - int isDifficultyUnlocked(); + int isDifficultyUnlocked(DeckStats * stats); int isMomirUnlocked(); int isEvilTwinUnlocked(); int isRandomDeckUnlocked(); + int IsMoreAIDecksUnlocked(DeckStats * stats); public: int value; Player * p1, *p2; diff --git a/projects/mtg/include/DeckStats.h b/projects/mtg/include/DeckStats.h index 6096d808e..98fa82ea1 100644 --- a/projects/mtg/include/DeckStats.h +++ b/projects/mtg/include/DeckStats.h @@ -38,6 +38,8 @@ public: int percentVictories(string opponentsDeckFile); int percentVictories(); DeckStat * getDeckStat(string opponentsFile); + + //returns the total number of games played with this deck int nbGames(); }; diff --git a/projects/mtg/include/GameOptions.h b/projects/mtg/include/GameOptions.h index 815e7384d..1e1061ac8 100644 --- a/projects/mtg/include/GameOptions.h +++ b/projects/mtg/include/GameOptions.h @@ -52,6 +52,7 @@ public: GUI_STYLE, INTERRUPT_SECONDS, KEY_BINDINGS, + AIDECKS_UNLOCKED, //My interrupts INTERRUPTMYSPELLS, INTERRUPTMYABILITIES, @@ -75,7 +76,7 @@ public: MOMIR_MODE_UNLOCKED, EVILTWIN_MODE_UNLOCKED, RANDOMDECK_MODE_UNLOCKED, - AWARD_COLLECTOR, + AWARD_COLLECTOR, LAST_NAMED, //Any option after this does not look up in optionNames. SET_UNLOCKS = LAST_NAMED + 1, //For sets. }; diff --git a/projects/mtg/include/GameState.h b/projects/mtg/include/GameState.h index d8348661c..a58f13f72 100644 --- a/projects/mtg/include/GameState.h +++ b/projects/mtg/include/GameState.h @@ -69,10 +69,11 @@ class GameState static vector fillDeckMenu(SimpleMenu * _menu, const string& path, const string& smallDeckPrefix = "", Player * statsPlayer = NULL); // generate the Deck Meta Data and build the menu items of the menu given - static vector fillDeckMenu(DeckMenu * _menu, const string& path, const string& smallDeckPrefix = "", Player * statsPlayer = NULL); + // Will display up to maxDecks if maxDecks is non 0,all decks in path otherwise + static vector fillDeckMenu(DeckMenu * _menu, const string& path, const string& smallDeckPrefix = "", Player * statsPlayer = NULL, int maxDecks = 0); // build a vector of decks with the information passsed in. - static vector getValidDeckMetaData(const string& path, const string& smallDeckPrefix = "", Player * statsPlayer = NULL); + static vector getValidDeckMetaData(const string& path, const string& smallDeckPrefix = "", Player * statsPlayer = NULL, int maxDecks = 0); // build menu items based on the vector static void renderDeckMenu(SimpleMenu * _menu, const vector& deckMetaDataList); diff --git a/projects/mtg/src/AIPlayer.cpp b/projects/mtg/src/AIPlayer.cpp index 6e2d4e8fd..041ab3193 100644 --- a/projects/mtg/src/AIPlayer.cpp +++ b/projects/mtg/src/AIPlayer.cpp @@ -978,9 +978,10 @@ AIPlayer * AIPlayerFactory::createAIPlayer(MTGAllCards * collection, Player * op { if (!deckid) { + //random deck int nbdecks = 0; int found = 1; - while (found) + while (found && nbdecks < options[Options::AIDECKS_UNLOCKED].number) { found = 0; char buffer[512]; diff --git a/projects/mtg/src/Credits.cpp b/projects/mtg/src/Credits.cpp index 8fe9baf0c..fdd6d94ad 100644 --- a/projects/mtg/src/Credits.cpp +++ b/projects/mtg/src/Credits.cpp @@ -119,7 +119,9 @@ void Credits::compute(Player * _p1, Player * _p2, GameApp * _app) if (unlocked == -1) { - unlocked = isDifficultyUnlocked(); + DeckStats * stats = DeckStats::GetInstance(); + stats->load(p1); + unlocked = isDifficultyUnlocked(stats); if (unlocked) { unlockedTex = resources.RetrieveTexture("unlocked.png"); @@ -160,6 +162,12 @@ void Credits::compute(Player * _p1, Player * _p2, GameApp * _app) if (si) unlockedString = si->getName(); //Show the set's pretty name for unlocks. } + else if ((unlocked = IsMoreAIDecksUnlocked(stats))) + { + options[Options::AIDECKS_UNLOCKED].number += 10; + unlockedTex = resources.RetrieveTexture("ai_unlocked.png"); + unlockedQuad = resources.RetrieveQuad("ai_unlocked.png", 2, 2, 396, 96); + } if (unlocked && options[Options::SFXVOLUME].number > 0) { @@ -292,15 +300,14 @@ void Credits::Render() } -int Credits::isDifficultyUnlocked() +int Credits::isDifficultyUnlocked(DeckStats * stats) { if (options[Options::DIFFICULTY_MODE_UNLOCKED].number) return 0; int nbAIDecks = 0; int found = 1; int wins = 0; - DeckStats * stats = DeckStats::GetInstance(); - stats->load(p1); + while (found) { found = 0; @@ -424,3 +431,31 @@ int Credits::unlockRandomSet(bool force) options.save(); return setId + 1; //We add 1 here to show success/failure. Be sure to subtract later. } + + +int Credits::IsMoreAIDecksUnlocked(DeckStats * stats) { + int currentlyUnlocked = options[Options::AIDECKS_UNLOCKED].number; + + // Random rule: having played at least twice as much games as + // the number of currently unlocked decks in order to go through. + if (stats->nbGames() < currentlyUnlocked * 2) return 0; + + int nbdecks = 0; + int found = 1; + while (found) + { + found = 0; + char buffer[512]; + sprintf(buffer, JGE_GET_RES("ai/baka/deck%i.txt").c_str(), nbdecks + 1); + std::ifstream file(buffer); + if (file) + { + found = 1; + file.close(); + nbdecks++; + if (nbdecks > currentlyUnlocked) + return 1; + } + } + return 0; +} \ No newline at end of file diff --git a/projects/mtg/src/DeckMenu.cpp b/projects/mtg/src/DeckMenu.cpp index 77bc26c9b..5371a2122 100644 --- a/projects/mtg/src/DeckMenu.cpp +++ b/projects/mtg/src/DeckMenu.cpp @@ -336,5 +336,4 @@ DeckMenu::~DeckMenu() { resources.Release(pspIconsTexture); SAFE_DELETE(mScroller); - mScroller = NULL; } diff --git a/projects/mtg/src/DeckMenuItem.cpp b/projects/mtg/src/DeckMenuItem.cpp index 94d7389ae..ae1f6067a 100644 --- a/projects/mtg/src/DeckMenuItem.cpp +++ b/projects/mtg/src/DeckMenuItem.cpp @@ -3,6 +3,7 @@ #include "DeckMenuItem.h" #include "Translate.h" #include "WResourceManager.h" +#include #define ITEM_PX_WIDTH 190.f const int kHorizontalScrollSpeed = 10; // lower numbers mean faster scrolling @@ -62,6 +63,19 @@ void DeckMenuItem::RenderWithOffset(float yOffset) mScrollTimer = 0; else if (mHasFocus && mScrollEnabled) mScrollTimer++; + + //Render a "new" icon for decks that have never been played yet + if (meta && !meta->getGamesPlayed()) + { + JTexture * tex = resources.RetrieveTexture("new.png"); + if (tex) + { + JQuad * quad = resources.RetrieveQuad("new.png", 2, 2, tex->mWidth - 4, tex->mHeight - 4); //avoids weird rectangle aroudn the texture because of bilinear filtering + quad->SetHotSpot(quad->mWidth/2, quad->mHeight/2); + float x = mX + min(ITEM_PX_WIDTH - quad->mWidth, mFont->GetStringWidth(menuItemString.c_str()))/2 + quad->mWidth/2; + if (quad) JRenderer::GetInstance()->RenderQuad(quad, x , mY + yOffset + quad->mHeight/2, 0.5); + } + } } void DeckMenuItem::Render() diff --git a/projects/mtg/src/GameOptions.cpp b/projects/mtg/src/GameOptions.cpp index 52f245035..920f3d61a 100644 --- a/projects/mtg/src/GameOptions.cpp +++ b/projects/mtg/src/GameOptions.cpp @@ -39,6 +39,7 @@ const string Options::optionNames[] = { "keybindings_psp", #endif #endif + "aidecks", "interruptMySpells", "interruptMyAbilities", //General interrupts @@ -342,6 +343,9 @@ int GameOptions::load() if (!(*this)[Options::MAX_GRADE].number) (*this)[Options::MAX_GRADE].number = Constants::GRADE_BORDERLINE; + if (!(*this)[Options::AIDECKS_UNLOCKED].number) + (*this)[Options::AIDECKS_UNLOCKED].number = 10; + return 1; } int GameOptions::save() diff --git a/projects/mtg/src/GameState.cpp b/projects/mtg/src/GameState.cpp index 01d717d21..d53843610 100644 --- a/projects/mtg/src/GameState.cpp +++ b/projects/mtg/src/GameState.cpp @@ -26,25 +26,25 @@ vector GameState::fillDeckMenu(SimpleMenu * _menu, const string& } vector GameState::fillDeckMenu(DeckMenu * _menu, const string& path, const string& smallDeckPrefix, - Player * statsPlayer) + Player * statsPlayer, int maxDecks) { bool translate = _menu->mAutoTranslate; _menu->mAutoTranslate = false; - vector deckMetaDataVector = getValidDeckMetaData(path, smallDeckPrefix, statsPlayer); + vector deckMetaDataVector = getValidDeckMetaData(path, smallDeckPrefix, statsPlayer, maxDecks); renderDeckMenu(_menu, deckMetaDataVector); _menu->mAutoTranslate = translate; return deckMetaDataVector; } -vector GameState::getValidDeckMetaData(const string& path, const string& smallDeckPrefix, Player * statsPlayer) +vector GameState::getValidDeckMetaData(const string& path, const string& smallDeckPrefix, Player * statsPlayer, int maxDecks) { vector retList; DeckMetaDataList * metas = DeckMetaDataList::decksMetaData; int found = 1; int nbDecks = 1; - while (found) + while (found && (!maxDecks || nbDecks <= maxDecks)) { found = 0; std::ostringstream filename; diff --git a/projects/mtg/src/GameStateDuel.cpp b/projects/mtg/src/GameStateDuel.cpp index 83a727893..42c1c3dad 100644 --- a/projects/mtg/src/GameStateDuel.cpp +++ b/projects/mtg/src/GameStateDuel.cpp @@ -283,7 +283,7 @@ void GameStateDuel::ensureOpponentMenu() if (options[Options::EVILTWIN_MODE_UNLOCKED].number) opponentMenu->Add(MENUITEM_EVIL_TWIN, "Evil Twin", _( "Can you play against yourself?").c_str()); DeckManager * deckManager = DeckManager::GetInstance(); - vector opponentDeckList = fillDeckMenu(opponentMenu, JGE_GET_RES("ai/baka"), "ai_baka", mPlayers[0]); + vector opponentDeckList = fillDeckMenu(opponentMenu, JGE_GET_RES("ai/baka"), "ai_baka", mPlayers[0], options[Options::AIDECKS_UNLOCKED].number); deckManager->updateMetaDataList(&opponentDeckList, true); opponentMenu->Add(MENUITEM_CANCEL, "Cancel", _("Choose a different player deck").c_str()); opponentDeckList.clear(); diff --git a/projects/mtg/src/Tasks.cpp b/projects/mtg/src/Tasks.cpp index 44111a48b..366b9d00b 100644 --- a/projects/mtg/src/Tasks.cpp +++ b/projects/mtg/src/Tasks.cpp @@ -189,9 +189,13 @@ void Task::passOneDay() void Task::loadAIDeckNames() { + //check if cache is up to date + if (AIDeckNames.size() == (unsigned int)(options[Options::AIDECKS_UNLOCKED].number)) return; + + AIDeckNames.clear(); int found = 1; int nbDecks = 0; - while (found) + while (found && nbDecks < options[Options::AIDECKS_UNLOCKED].number) { found = 0; char buffer[512]; @@ -211,19 +215,13 @@ void Task::loadAIDeckNames() int Task::getAIDeckCount() { - if (AIDeckNames.size() == 0) - { - loadAIDeckNames(); - } + loadAIDeckNames(); return AIDeckNames.size(); } string Task::getAIDeckName(int id) { - if (AIDeckNames.size() == 0) - { - loadAIDeckNames(); - } + loadAIDeckNames(); return ((unsigned int) id <= AIDeckNames.size()) ? AIDeckNames.at(id - 1) : ""; } diff --git a/projects/mtg/src/utils.cpp b/projects/mtg/src/utils.cpp index f20376752..9ff09f6ad 100644 --- a/projects/mtg/src/utils.cpp +++ b/projects/mtg/src/utils.cpp @@ -265,39 +265,39 @@ std::string wordWrap(std::string sentence, int width) // Given a delimited string of abilities, add the ones to the list that are "Basic" MTG abilities void PopulateAbilityIndexVector( list& abilities, const string& abilityStringList, char delimiter ) { - vector abilitiesList = split( abilityStringList, delimiter); - for ( vector::iterator iter = abilitiesList.begin(); iter != abilitiesList.end(); ++iter) - { - int abilityIndex = Constants::GetBasicAbilityIndex( *iter ); - - if (abilityIndex != -1) - abilities.push_back( abilityIndex ); + vector abilitiesList = split( abilityStringList, delimiter); + for ( vector::iterator iter = abilitiesList.begin(); iter != abilitiesList.end(); ++iter) + { + int abilityIndex = Constants::GetBasicAbilityIndex( *iter ); + + if (abilityIndex != -1) + abilities.push_back( abilityIndex ); } } void PopulateColorIndexVector( list& colors, const string& colorStringList, char delimiter ) { - vector abilitiesList = split( colorStringList, delimiter); - for ( vector::iterator iter = abilitiesList.begin(); iter != abilitiesList.end(); ++iter) - { - for (int colorIndex = Constants::MTG_COLOR_ARTIFACT; colorIndex < Constants::MTG_NB_COLORS; ++colorIndex) - { - // if the text is not a basic ability but contains a valid color add it to the color vector - if ( (Constants::GetBasicAbilityIndex( *iter ) != -1) && ((*iter).find( Constants::MTGColorStrings[ colorIndex ] ) != string::npos) ) - colors.push_back(colorIndex); - } + vector abilitiesList = split( colorStringList, delimiter); + for ( vector::iterator iter = abilitiesList.begin(); iter != abilitiesList.end(); ++iter) + { + for (int colorIndex = Constants::MTG_COLOR_ARTIFACT; colorIndex < Constants::MTG_NB_COLORS; ++colorIndex) + { + // if the text is not a basic ability but contains a valid color add it to the color vector + if ( (Constants::GetBasicAbilityIndex( *iter ) != -1) && ((*iter).find( Constants::MTGColorStrings[ colorIndex ] ) != string::npos) ) + colors.push_back(colorIndex); + } } } void PopulateSubtypesIndexVector( list& types, const string& subTypesStringList, char delimiter) { - vector subTypesList = split( subTypesStringList, delimiter); - for (vector::iterator it = subTypesList.begin(); it != subTypesList.end(); ++it) - { - string subtype = *it; - size_t id = Subtypes::subtypesList->find( subtype ); - if ( id != string::npos ) - types.push_back(id); + vector subTypesList = split( subTypesStringList, delimiter); + for (vector::iterator it = subTypesList.begin(); it != subTypesList.end(); ++it) + { + string subtype = *it; + size_t id = Subtypes::subtypesList->find( subtype ); + if ( id != string::npos ) + types.push_back(id); } }