#include "../include/config.h" #include "../include/utils.h" #include "../include/MTGDeck.h" #include "../include/GameOptions.h" #include "../include/OptionItem.h" #include #include #include #include #include const char * Options::optionNames[] = { //Global options "Profile", "prx_handler", "prx_rimom", "prx_eviltwin", "prx_rnddeck", //Options set on a per-profile basis "Theme", "Mode", "musicVolume", "sfxVolume", "difficulty", "cheatmode", "displayOSD", "closed_hand", "hand_direction", "mana_display", "reverse_triggers", "disable_cards", "interruptSeconds", "interruptMySpells", "interruptMyAbilities", //General interrupts "interruptBeforeBegin", "interruptUntap", "interruptUpkeep", "interruptDraw", "interruptFirstMain", "interruptBeginCombat", "interruptAttackers", "interruptBlockers", "interruptDamage", "interruptEndCombat", "interruptSecondMain", "interruptEndTurn", "interruptCleanup", "interruptAfterEnd", }; int Options::getID(string name){ if(!name.size()) INVALID_OPTION; std::transform(name.begin(),name.end(),name.begin(),::tolower); //Is it a named option? for(int x = 0; x < LAST_NAMED; x++){ string lower = Options::optionNames[x]; std::transform(lower.begin(),lower.end(),lower.begin(),::tolower); if(lower == name) return x; } //Is it an unlocked set? string setname = name.substr(strlen("unlocked_")); if(MtgSets::SetsList && MtgSets::SetsList->nb_items){ int unlocked = MtgSets::SetsList->find(setname); if(unlocked != -1) return Options::optionSet(unlocked); } //Failure. return INVALID_OPTION; } string Options::getName(int option){ //Invalid options if(option < 0) return ""; //Standard named options if(option < LAST_NAMED) return optionNames[option]; //Unlocked sets. if(MtgSets::SetsList){ int setID = option - SET_UNLOCKS; if(setID >= 0 && setID < MtgSets::SetsList->nb_items){ char buf[512]; sprintf(buf,"unlocked_%s",MtgSets::SetsList->values[setID].c_str()); return buf; } } //Failed. return ""; } int Options::optionSet(int setID){ //Sanity check if possible if(setID < 0 || (MtgSets::SetsList && setID > MtgSets::SetsList->nb_items)) return INVALID_OPTION; return SET_UNLOCKS + setID; } int Options::optionInterrupt(int gamePhase){ //Huge, nearly illegible switch block spread out to improve readability. switch(gamePhase){ case Constants::MTG_PHASE_BEFORE_BEGIN: return INTERRUPT_BEFOREBEGIN; case Constants::MTG_PHASE_UNTAP: return INTERRUPT_UNTAP; case Constants::MTG_PHASE_UPKEEP: return INTERRUPT_UPKEEP; case Constants::MTG_PHASE_DRAW: return INTERRUPT_DRAW; case Constants::MTG_PHASE_FIRSTMAIN: return INTERRUPT_FIRSTMAIN; case Constants::MTG_PHASE_COMBATBEGIN: return INTERRUPT_BEGINCOMBAT; case Constants::MTG_PHASE_COMBATATTACKERS: return INTERRUPT_ATTACKERS; case Constants::MTG_PHASE_COMBATBLOCKERS: return INTERRUPT_BLOCKERS; case Constants::MTG_PHASE_COMBATDAMAGE: return INTERRUPT_DAMAGE; case Constants::MTG_PHASE_COMBATEND: return INTERRUPT_ENDCOMBAT; case Constants::MTG_PHASE_SECONDMAIN: return INTERRUPT_SECONDMAIN; case Constants::MTG_PHASE_ENDOFTURN: return INTERRUPT_ENDTURN; case Constants::MTG_PHASE_CLEANUP: return INTERRUPT_CLEANUP; case Constants::MTG_PHASE_AFTER_EOT: return INTERRUPT_AFTEREND; } return INVALID_OPTION; } GameOption::GameOption(int value) : number(value){} GameOption::GameOption(string value) : number(0), str(value) {} GameOption::GameOption(int num, string str) : number(num), str(str) {} bool GameOption::isDefault(){ string test = str; std::transform(test.begin(),test.end(),test.begin(),::tolower); if(!test.size() || test == "default") return true; return false; } PIXEL_TYPE GameOption::asColor(PIXEL_TYPE fallback) { unsigned char color[4]; string temp; int subpixel=0; //The absolute shortest a color could be is 5 characters: "0,0,0" (implicit 255 alpha) if(str.length() < 5) return fallback; for(size_t i=0;i 3) return fallback; temp += str[i]; } if(temp != "") color[subpixel] = (unsigned char) atoi(temp.c_str()); if(subpixel == 2) color[3] = 255; return ARGB(color[3],color[0],color[1],color[2]); } bool GameOption::read(string input){ bool bNumeric = true; if(!input.size()){ return true; //Default reader doesn't care about invalid formatting. } //Is it a number? for(size_t x=0;xwrite(&file, name); } file.close(); } return 1; } GameOption& GameOptions::operator[](int optionID){ GameOption * go = get(optionID); if(!go) return GameSettings::invalid_option; return *go; } GameOption * GameOptions::get(int optionID) { GameOption * go = NULL; GameOptionEnum * goEnum = NULL; //Invalid options! if(optionID < 0) return NULL; //Option doesn't exist, so build it int x = (int) values.size(); values.reserve(optionID); while(x <= optionID){ switch(x){ case Options::HANDDIRECTION: goEnum = NEW GameOptionEnum(); goEnum->def = OptionHandDirection::getInstance(); go = goEnum; break; case Options::CLOSEDHAND: goEnum = NEW GameOptionEnum(); goEnum->def = OptionClosedHand::getInstance(); go = goEnum; break; case Options::MANADISPLAY: goEnum = NEW GameOptionEnum(); goEnum->def = OptionManaDisplay::getInstance(); go = goEnum; break; default: go = NEW GameOption(); break; } values.push_back(go); x++; } return values[optionID]; } GameOptions::~GameOptions(){ for(vector::iterator it=values.begin();it!=values.end();it++) SAFE_DELETE(*it); values.clear(); } GameSettings options; GameSettings::GameSettings() { globalOptions = NULL; theGame = NULL; profileOptions = NULL; //reloadProfile should be before using options. } GameSettings::~GameSettings(){ //Destructor no longer saves, to prevent conflicts when MtgSets::SetsList == NULL SAFE_DELETE(globalOptions); SAFE_DELETE(profileOptions); SAFE_DELETE(keypad); } GameOption GameSettings::invalid_option = GameOption(0); GameOption& GameSettings::operator[](int optionID){ GameOption * go = get(optionID); if(!go) return invalid_option; return *go; } GameOption* GameSettings::get(int optionID){ string option_name = Options::getName(optionID); if(optionID < 0) return &invalid_option; else if(globalOptions && optionID <= Options::LAST_GLOBAL) return globalOptions->get(optionID); else if(profileOptions) return profileOptions->get(optionID); return &invalid_option; } int GameSettings::save(){ if(globalOptions) globalOptions->save(); if(profileOptions){ //Force our directories to exist. MAKEDIR(RESPATH"/profiles"); string temp = profileFile("","",false,false); MAKEDIR(temp.c_str()); temp+="/stats"; MAKEDIR(temp.c_str()); temp = profileFile(PLAYER_SETTINGS,"",false); profileOptions->save(); } checkProfile(); return 1; } string GameSettings::profileFile(string filename, string fallback,bool sanity, bool relative) { char buf[512]; string profile = (*this)[Options::ACTIVE_PROFILE].str; if(!(*this)[Options::ACTIVE_PROFILE].isDefault()) { //No file, return root of profile directory if(filename == ""){ sprintf(buf,"%sprofiles/%s",( relative ? "" : RESPATH"/" ),profile.c_str()); return buf; } //Return file sprintf(buf,RESPATH"/profiles/%s/%s",profile.c_str(),filename.c_str()); if(fileExists(buf)){ if(relative) sprintf(buf,"profiles/%s/%s",profile.c_str(),filename.c_str()); return buf; } } else{ //Use the default directory. sprintf(buf,"%splayer%s%s",(relative ? "" : RESPATH"/"),(filename == "" ? "" : "/"), filename.c_str()); return buf; } //Don't fallback if sanity checking is disabled.. if(!sanity){ sprintf(buf,"%sprofiles/%s%s%s",(relative ? "" : RESPATH"/"),profile.c_str(),(filename == "" ? "" : "/"), filename.c_str()); return buf; } //No fallback directory. This is often a crash. if(fallback == "") return ""; sprintf(buf,"%s%s%s%s",(relative ? "" : RESPATH"/"),fallback.c_str(),(filename == "" ? "" : "/"), filename.c_str()); return buf; } void GameSettings::reloadProfile(bool images){ SAFE_DELETE(profileOptions); checkProfile(); if(images) resources.Refresh(); //Update images } void GameSettings::checkProfile(){ if(!globalOptions) globalOptions = NEW GameOptions(GLOBAL_SETTINGS); //If it doesn't exist, load current profile. if(!profileOptions) profileOptions = NEW GameOptions(profileFile(PLAYER_SETTINGS,"",false)); //Validation of collection, etc, only happens if the game is up. if(theGame == NULL || theGame->collection == NULL) return; string pcFile = profileFile(PLAYER_COLLECTION,"",false); if(!pcFile.size() || !fileExists(pcFile.c_str())) { //If we had any default settings, we'd set them here. //Find the set for which we have the most variety int setId = 0; int maxcards = 0; for (int i=0; i< MtgSets::SetsList->nb_items; i++){ int value = theGame->collection->countBySet(i); if (value > maxcards){ maxcards = value; setId = i; } } //Make the proper directories if(profileOptions){ //Force our directories to exist. MAKEDIR(RESPATH"/profiles"); string temp = profileFile("","",false,false); MAKEDIR(temp.c_str()); temp+="/stats"; MAKEDIR(temp.c_str()); temp = profileFile(PLAYER_SETTINGS,"",false); profileOptions->save(); } //Save this set as "unlocked" (*profileOptions)[Options::optionSet(setId)]=1; profileOptions->save(); //Give the player their first deck createUsersFirstDeck(setId); } } void GameSettings::createUsersFirstDeck(int setId){ #if defined (WIN32) || defined (LINUX) char buf[4096]; sprintf(buf, "setID: %i", setId); OutputDebugString(buf); #endif if(theGame == NULL || theGame->collection == NULL) return; MTGDeck *mCollection = NEW MTGDeck(options.profileFile(PLAYER_COLLECTION,"",false).c_str(), theGame->collection); //10 lands of each int sets[] = {setId}; if (!mCollection->addRandomCards(10, sets,1, Constants::RARITY_L,"Forest")){ mCollection->addRandomCards(10, 0,0,Constants::RARITY_L,"Forest"); } if (!mCollection->addRandomCards(10, sets,1,Constants::RARITY_L,"Plains")){ mCollection->addRandomCards(10, 0,0,Constants::RARITY_L,"Plains"); } if (!mCollection->addRandomCards(10, sets,1,Constants::RARITY_L,"Swamp")){ mCollection->addRandomCards(10, 0,0,Constants::RARITY_L,"Swamp"); } if (!mCollection->addRandomCards(10, sets,1,Constants::RARITY_L,"Mountain")){ mCollection->addRandomCards(10, 0,0,Constants::RARITY_L,"Mountain"); } if (!mCollection->addRandomCards(10, sets,1,Constants::RARITY_L,"Island")){ mCollection->addRandomCards(10, 0,0,Constants::RARITY_L,"Island"); } #if defined (WIN32) || defined (LINUX) OutputDebugString("1\n"); #endif //Starter Deck mCollection->addRandomCards(3, sets,1,Constants::RARITY_R,NULL); mCollection->addRandomCards(9, sets,1,Constants::RARITY_U,NULL); mCollection->addRandomCards(48, sets,1,Constants::RARITY_C,NULL); #if defined (WIN32) || defined (LINUX) OutputDebugString("2\n"); #endif //Boosters for (int i = 0; i< 2; i++){ mCollection->addRandomCards(1, sets,1,Constants::RARITY_R); mCollection->addRandomCards(3, sets,1,Constants::RARITY_U); mCollection->addRandomCards(11, sets,1,Constants::RARITY_C); } mCollection->save(); SAFE_DELETE(mCollection); } void GameSettings::keypadTitle(string set){ if(keypad != NULL) keypad->title = set; } SimplePad * GameSettings::keypadStart(string input, string * _dest,bool _cancel, bool _numpad, int _x,int _y ){ if(keypad == NULL) keypad = NEW SimplePad(); keypad->bShowCancel = _cancel; keypad->bShowNumpad = _numpad; keypad->mX = _x; keypad->mY = _y; keypad->Start(input,_dest); return keypad; } string GameSettings::keypadFinish(){ if(keypad == NULL) return ""; return keypad->Finish(); } void GameSettings::keypadShutdown(){ SAFE_DELETE(keypad); } int EnumDefinition::findIndex(int value){ vector::iterator it; for(it = values.begin();it!=values.end();it++){ if(it->first == value) return it - values.begin(); } return INVALID_ID; //Failed! } string GameOptionEnum::menuStr(){ if(def){ int idx = def->findIndex(number); if(idx != INVALID_ID) return def->values[idx].second; } char buf[32]; sprintf(buf,"%d",number); return buf; } bool GameOptionEnum::write(std::ofstream * file, string name){ if(!file || !def || number < 0 || number >= (int) def->values.size()) return false; char writer[1024]; sprintf(writer,"%s=%s\n", name.c_str(), menuStr().c_str()); (*file)<::iterator it; for(it=def->values.begin();it != def->values.end();it++){ string v = it->second; std::transform(v.begin(),v.end(),v.begin(),::tolower); if(v == input){ number = it->first; return true; } } return false; } OptionClosedHand OptionClosedHand::mDef; OptionClosedHand::OptionClosedHand(){ mDef.values.push_back(EnumDefinition::assoc(INVISIBLE, "invisible")); mDef.values.push_back(EnumDefinition::assoc(VISIBLE, "visible")); }; OptionHandDirection OptionHandDirection::mDef; OptionHandDirection::OptionHandDirection(){ mDef.values.push_back(EnumDefinition::assoc(VERTICAL, "vertical")); mDef.values.push_back(EnumDefinition::assoc(HORIZONTAL, "horizontal")); }; OptionManaDisplay OptionManaDisplay::mDef; OptionManaDisplay::OptionManaDisplay(){ mDef.values.push_back(EnumDefinition::assoc(DYNAMIC, "Eye candy")); mDef.values.push_back(EnumDefinition::assoc(STATIC, "Simple")); mDef.values.push_back(EnumDefinition::assoc(BOTH, "Both")); }; OptionVolume OptionVolume::mDef; OptionVolume::OptionVolume(){ mDef.values.push_back(EnumDefinition::assoc(MUTE, "Mute")); mDef.values.push_back(EnumDefinition::assoc(MAX, "Max")); }; OptionDifficulty OptionDifficulty::mDef; OptionDifficulty::OptionDifficulty(){ mDef.values.push_back(EnumDefinition::assoc(NORMAL, "Normal")); mDef.values.push_back(EnumDefinition::assoc(HARD, "Hard")); mDef.values.push_back(EnumDefinition::assoc(HARDER, "Harder")); mDef.values.push_back(EnumDefinition::assoc(EVIL, "Evil")); };