#include "../include/config.h" #include "../include/utils.h" #include "../include/MTGDeck.h" #include "../include/GameOptions.h" #include "../include/Translate.h" #include "../include/OptionItem.h" #include "../include/StyleManager.h" #include #include #include #include #include #include const string Options::optionNames[] = { //Global options "Profile", "Lang", //Options set on a per-profile basis "Theme", "Mode", "musicVolume", "sfxVolume", "difficulty", "cheatmode", "displayOSD", "closed_hand", "hand_direction", "mana_display", "reverse_triggers", "disable_cards", "maxGrade", "ASPhases", "economic_difficulty", "transitions", "bgStyle", "interruptSeconds", #if defined(WIN32) "keybindings_win", #else #if defined(LINUX) "keybindings_x", #else "keybindings_psp", #endif #endif "interruptMySpells", "interruptMyAbilities", //General interrupts "interruptBeforeBegin", "interruptUntap", "interruptUpkeep", "interruptDraw", "interruptFirstMain", "interruptBeginCombat", "interruptAttackers", "interruptBlockers", "interruptDamage", "interruptEndCombat", "interruptSecondMain", "interruptEndTurn", "interruptCleanup", "interruptAfterEnd", //Unlocked modes "prx_handler", "prx_rimom", "prx_eviltwin", "prx_rnddeck", "aw_collector", }; int Options::getID(string name){ if (0 == 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? size_t un = strlen("unlocked_"); if(un < name.size()){ string setname = name.substr(un); if(setlist.size()){ int unlocked = setlist[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. int setID = option - SET_UNLOCKS; char buf[512]; if(setID < 0 || setID > setlist.size()) return ""; sprintf(buf,"unlocked_%s",setlist[setID].c_str()); return buf; //Failed. return ""; } int Options::optionSet(int setID){ //Sanity check if possible if(setID < 0 || (setID > setlist.size())) 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); } for(vector::size_type t=0;tdef = 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; case Options::MAX_GRADE: goEnum = NEW GameOptionEnum(); goEnum->def = OptionMaxGrade::getInstance(); go = goEnum; break; case Options::ASPHASES: goEnum = NEW GameOptionEnum(); goEnum->def = OptionASkipPhase::getInstance(); go = goEnum; break; case Options::KEY_BINDINGS: go = NEW GameOptionKeyBindings(); break; case Options::ECON_DIFFICULTY: goEnum = NEW GameOptionEnum(); goEnum->def = OptionEconDifficulty::getInstance(); go = goEnum; break; default: if(x >= Options::BEGIN_AWARDS) go = NEW GameOptionAward(); else 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() { styleMan = NULL; globalOptions = NULL; theGame = NULL; profileOptions = NULL; //reloadProfile should be before using options. } WStyle * GameSettings::getStyle() { if(!styleMan) styleMan = new StyleManager(); return styleMan->get(); } StyleManager * GameSettings::getStyleMan() { if(!styleMan) styleMan = new StyleManager(); return styleMan; } void GameSettings::automaticStyle(Player * p1, Player * p2){ if(!styleMan) styleMan = new StyleManager(); MTGDeck * decks[2]; for(int i=0;i<2;i++){ decks[i] = new MTGDeck(GameApp::collection); Player * p; if(i == 0) p = p1; else p = p2; map::iterator it; for(it = p->game->library->cardsMap.begin();it != p->game->library->cardsMap.end();it++){ decks[i]->add(it->first); } } styleMan->determineActive(decks[0],decks[1]); for(int i=0;i<2;i++){ SAFE_DELETE(decks[i]); } } GameSettings::~GameSettings(){ SAFE_DELETE(globalOptions); SAFE_DELETE(profileOptions); SAFE_DELETE(keypad); SAFE_DELETE(styleMan); } bool GameSettings::newAward(){ if(!profileOptions) return false; for(int x=Options::BEGIN_AWARDS;x < Options::SET_UNLOCKS + setlist.size();x++){ GameOptionAward * goa = dynamic_cast(profileOptions->get(x)); if(!goa) continue; if(!goa->isViewed()) return true; } return false; } 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)); //Backwards compatability hack for unlocked modes. for(int x=Options::BEGIN_AWARDS;x(globalOptions->get(x)); if(goa){ GameOptionAward * dupe = dynamic_cast(profileOptions->get(x)); if(dupe && goa->number && !dupe->number) dupe->giveAward(); } } } //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. //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(); } } //Find the set for which we have the most variety int setId = -1; int maxcards = 0; int ok = 0; for (int i=0; i< setlist.size(); i++){ int value = theGame->collection->countBySet(i); if (value > maxcards){ maxcards = value; setId = i; } if(options[Options::optionSet(i)].number){ ok = 1; break; } } if(!ok && setId >= 0){ //Save this set as "unlocked" (*profileOptions)[Options::optionSet(setId)]=1; profileOptions->save(); //Give the player their first deck createUsersFirstDeck(setId); } getStyleMan()->determineActive(NULL,NULL); } void GameSettings::createUsersFirstDeck(int setId){ if(theGame == NULL || theGame->collection == NULL) return; MTGDeck *mCollection = NEW MTGDeck(options.profileFile(PLAYER_COLLECTION,"",false).c_str(), theGame->collection); if(mCollection->totalCards() > 0) return; //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"); //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); //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); } //EnumDefinition 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! } //GameOptionEnum 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; (*file) << name << "=" << menuStr() << endl; return true; } bool GameOptionEnum::read(string input){ if(!def) return false; number = 0; std::transform(input.begin(),input.end(),input.begin(),::tolower); vector::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; } //Enum Definitions OptionMaxGrade OptionMaxGrade::mDef; OptionMaxGrade::OptionMaxGrade(){ mDef.values.push_back(EnumDefinition::assoc(Constants::GRADE_SUPPORTED, "1: 100% Supported")); mDef.values.push_back(EnumDefinition::assoc(Constants::GRADE_BORDERLINE, "0: Borderline (99% OK)")); mDef.values.push_back(EnumDefinition::assoc(Constants::GRADE_UNOFFICIAL, "-1: Unofficial (unverified cards)")); mDef.values.push_back(EnumDefinition::assoc(Constants::GRADE_CRAPPY, "-2: Crappy (bugs)")); mDef.values.push_back(EnumDefinition::assoc(Constants::GRADE_UNSUPPORTED, "-3: Unsupported")); mDef.values.push_back(EnumDefinition::assoc(Constants::GRADE_DANGEROUS, "-4: Dangerous (risk of crash)")); }; OptionASkipPhase OptionASkipPhase::mDef; OptionASkipPhase::OptionASkipPhase(){ mDef.values.push_back(EnumDefinition::assoc(Constants::ASKIP_NONE, "Off")); mDef.values.push_back(EnumDefinition::assoc(Constants::ASKIP_SAFE, "Safe")); mDef.values.push_back(EnumDefinition::assoc(Constants::ASKIP_FULL, "Full")); }; 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")); }; OptionEconDifficulty OptionEconDifficulty::mDef; OptionEconDifficulty::OptionEconDifficulty(){ mDef.values.push_back(EnumDefinition::assoc(Constants::ECON_NORMAL, "Normal")); mDef.values.push_back(EnumDefinition::assoc(Constants::ECON_HARD, "Hard")); mDef.values.push_back(EnumDefinition::assoc(Constants::ECON_LUCK, "Luck")); mDef.values.push_back(EnumDefinition::assoc(Constants::ECON_EASY, "Easy")); }; //GameOptionAward GameOptionAward::GameOptionAward(){ achieved = time(NULL); number = 0; viewed = false; } bool GameOptionAward::read(string input){ //This is quick and dirty. bool bNumeric = true; achieved = time(NULL); tm * at = localtime(&achieved); viewed = false; size_t inlen = input.size(); if(!inlen) return true; //Default reader doesn't care about invalid formatting. else if(inlen < 8 || input != "0") //Regardless of what garbage this is fed, a non-zero value is "Awarded" number = 1; size_t w = input.find("V"); if(w != string::npos) viewed = true; //TODO: Something cleaner. int tvals[5]; int i; for(i=0;i<5;i++) tvals[i] = 0; string buf; for (size_t t=0,i=0; t < input.size();t++) { if(!isdigit(input[t])) { if(!isspace(input[t]) && buf.size()) { tvals[i] = atoi(buf.c_str()); if(tvals[i] < 0) tvals[i] = 0; buf.clear(); i++; //Advance through input. } } else buf+= input[t]; if (i >= 5) break; } if(tvals[0] >= 1900) tvals[0] -= 1900; if(tvals[1] > 0) tvals[1]--; at->tm_year = tvals[0]; at->tm_mon = tvals[1]; at->tm_mday = tvals[2]; if(tvals[3]) at->tm_hour = tvals[3]; if(tvals[4]) at->tm_min = tvals[4]; at->tm_isdst = -1; achieved = mktime(at); if(achieved == -1) achieved = time(NULL); return true; } bool GameOptionAward::write(std::ofstream * file, string name){ char writer[1024]; if(!file) return false; if(number == 0) //Is not unlocked. Don't write. return true; tm * at = localtime(&achieved); if(!at) return false; //Hurrah for paranoia. sprintf(writer,"%s=%d/%d/%d@%d:%d %s\n", name.c_str(), at->tm_year + 1900, at->tm_mon + 1, at->tm_mday, at->tm_hour, at->tm_min, (viewed ? "V" : "")); (*file)<(b); else return JGE_BTN_NONE; } bool GameOptionKeyBindings::read(string input){ istringstream iss(input); vector< pair > assoc; while (iss.good()) { stringstream s; iss.get(*(s.rdbuf()), ','); iss.get(); LocalKeySym local; char sep; u32 button; s >> local >> sep >> button; if (':' != sep) return false; assoc.push_back(make_pair(local, u32_to_button(button))); } if (assoc.empty()) return false; JGE* j = JGE::GetInstance(); j->ClearBindings(); for (vector< pair >::const_iterator it = assoc.begin(); it != assoc.end(); ++it) j->BindKey(it->first, it->second); return true; } bool GameOptionKeyBindings::write(std::ofstream* file, string name) { JGE* j = JGE::GetInstance(); *file << name << "="; JGE::keybindings_it start = j->KeyBindings_begin(), end = j->KeyBindings_end(); if (start != end) { *file << start->first << ":" << start->second; ++start; } for (JGE::keybindings_it it = start; it != end; ++it) *file << "," << it->first << ":" << it->second; *file << endl; return true; }