#include "PrecompiledHeader.h" #include "utils.h" #include "MTGDeck.h" #include "Translate.h" #include "OptionItem.h" #include "StyleManager.h" #include "Credits.h" #ifdef IOS #include "JGE.h" #endif const string Options::optionNames[] = { //Global options "Profile", "Lang", //Options set on a per-profile basis "Theme", "Mode", "musicVolume", "sfxVolume", "difficulty", "cheatmode", "optimizedhand", "cheatmodedecks", "displayOSD", "closed_hand", "hand_direction", "mana_display", "reverse_triggers", "disable_cards", "maxGrade", "ASPhases", "FirstPlayer", "KickerPay", "economic_difficulty", "transitions", "bgStyle", "interruptSeconds", #if defined(SDL_CONFIG) "keybindings_sdl", #elif defined(QT_CONFIG) "keybindings_qt", #elif defined(WIN32) "keybindings_win", #elif defined(LINUX) "keybindings_x", #else "keybindings_psp", #endif "aidecks", "interruptMySpells", "interruptMyAbilities", "saveDetailedDeckInfo", //General interrupts "interruptBeforeBegin", "interruptUntap", "interruptUpkeep", "interruptDraw", "interruptFirstMain", "interruptBeginCombat", "interruptAttackers", "interruptBlockers", "interruptDamage", "interruptEndCombat", "interruptSecondMain", "interruptEndTurn", "interruptCleanup", "interruptAfterEnd", //Unlocked modes "prx_handler", "prx_eviltwin", "prx_rnddeck", "aw_collector", }; #pragma mark Options int Options::getID(string name) { if (0 == name.size()) return 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? if (name.find("unlocked_") == 0) { string setname = name.substr(strlen("unlocked_")); 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 MTG_PHASE_BEFORE_BEGIN: return INTERRUPT_BEFOREBEGIN; case MTG_PHASE_UNTAP: return INTERRUPT_UNTAP; case MTG_PHASE_UPKEEP: return INTERRUPT_UPKEEP; case MTG_PHASE_DRAW: return INTERRUPT_DRAW; case MTG_PHASE_FIRSTMAIN: return INTERRUPT_FIRSTMAIN; case MTG_PHASE_COMBATBEGIN: return INTERRUPT_BEGINCOMBAT; case MTG_PHASE_COMBATATTACKERS: return INTERRUPT_ATTACKERS; case MTG_PHASE_COMBATBLOCKERS: return INTERRUPT_BLOCKERS; case MTG_PHASE_COMBATDAMAGE: return INTERRUPT_DAMAGE; case MTG_PHASE_COMBATEND: return INTERRUPT_ENDCOMBAT; case MTG_PHASE_SECONDMAIN: return INTERRUPT_SECONDMAIN; case MTG_PHASE_ENDOFTURN: return INTERRUPT_ENDTURN; case MTG_PHASE_CLEANUP: return INTERRUPT_CLEANUP; case MTG_PHASE_AFTER_EOT: return INTERRUPT_AFTEREND; } return INVALID_OPTION; } #pragma mark - #pragma mark GameOption GameOption::GameOption(int value) : number(value) { } GameOption::GameOption(string value) : number(atoi(value.c_str())), 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 < str.length(); i++) { if (isspace(str[i])) continue; if (str[i] == ',') { if (temp == "") return fallback; color[subpixel] = (unsigned char) atoi(temp.c_str()); temp = ""; subpixel++; continue; } else if (!isdigit(str[i])) return fallback; if (subpixel > 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; x < input.size(); x++) { if (!isdigit(input[x])) { bNumeric = false; break; } } if (bNumeric) number = atoi(input.c_str()); else str = input; return true; } string GameOption::menuStr() { if (number) { char buf[12]; sprintf(buf, "%d", number); } if (str.size()) return str; return "0"; } bool GameOption::write(std::ofstream * file, string name) { char writer[1024]; if (!file) return false; if (str == "") { if (number == 0) //This is absolutely default. No need to write it. return true; //It's a number! sprintf(writer, "%s=%d\n", name.c_str(), number); } else sprintf(writer, "%s=%s\n", name.c_str(), str.c_str()); (*file) << writer; return true; } #pragma mark - #pragma mark GameOptions GameOptions::GameOptions(string filename) { mFilename = filename; GameOptions::load(); } int GameOptions::load() { std::string contents; if (JFileSystem::GetInstance()->readIntoString(mFilename, contents)) { std::stringstream stream(contents); string s; while (std::getline(stream, s)) { if (!s.size()) continue; if (s[s.size() - 1] == '\r') s.erase(s.size() - 1); //Handle DOS files int found = s.find("="); string name = s.substr(0, found); string val = s.substr(found + 1); int id = Options::getID(name); if (id == INVALID_OPTION) { if (!unknownMap[name]) unknownMap[name] = factorNewGameOption(name, val); continue; } (*this)[id].read(val); } } // (PSY) Make sure that cheatmode is switched off for ineligible profiles: if (options[Options::ACTIVE_PROFILE].str != SECRET_PROFILE) { (*this)[Options::CHEATMODE].number = 0; (*this)[Options::OPTIMIZE_HAND].number = 0; (*this)[Options::CHEATMODEAIDECK].number = 0; } //Default values. Anywhere else to put those ? 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() { // (PSY) Make sure that cheatmode is switched off for ineligible profiles: if (options[Options::ACTIVE_PROFILE].str != SECRET_PROFILE) { (*this)[Options::CHEATMODE].number = 0; (*this)[Options::OPTIMIZE_HAND].number = 0; (*this)[Options::CHEATMODEAIDECK].number = 0; } std::ofstream file; if (JFileSystem::GetInstance()->openForWrite(file, mFilename)) { for (int x = 0; x < (int) values.size(); x++) { //Check that this is a valid option. string name = Options::getName(x); GameOption * opt = get(x); if (!name.size() || !opt) continue; //Save it. opt->write(&file, name); } for (map::iterator it = unknownMap.begin(); it != unknownMap.end(); it++) { if (it->second) { if (it->second->str.size()) file << it->first << "=" << it->second->str << "\n"; else if (it->second->number) file << it->first << "=" << it->second->number << "\n"; } } file.close(); } return 1; } GameOption& GameOptions::operator[](int optionID) { GameOption * go = get(optionID); if (!go) return GameSettings::invalid_option; return *go; } GameOption& GameOptions::operator[](string optionName) { int id = Options::getID(optionName); if (id != INVALID_OPTION) return operator[](id); GameOption * go = get(optionName); return * go; } GameOption * GameOptions::factorNewGameOption(string optionName, string value) { GameOption * result =( Unlockable::unlockables.find(optionName) != Unlockable::unlockables.end()) ? NEW GameOptionAward() : NEW GameOption(); if (value.size()) result->read(value); return result; } GameOption * GameOptions::get(string optionName) { if (!unknownMap[optionName]) { unknownMap[optionName] = factorNewGameOption(optionName); } return unknownMap[optionName]; } GameOption * GameOptions::get(int optionID) { //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) { GameOption * go = NULL; GameOptionEnum * goEnum = NULL; switch (x) { //Enum options 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; 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::FIRSTPLAYER: goEnum = NEW GameOptionEnum(); goEnum->def = OptionWhosFirst::getInstance(); go = goEnum; break; case Options::KICKERPAYMENT: goEnum = NEW GameOptionEnum(); goEnum->def = OptionKicker::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(); for (map::iterator it = unknownMap.begin(); it != unknownMap.end(); it++) SAFE_DELETE(it->second); unknownMap.clear(); } #pragma mark - #pragma mark GameSettings 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(); styleMan->determineActive(NULL,NULL); } 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(MTGCollection()); 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::operator[](string optionName) { int id = Options::getID(optionName); if (id != INVALID_OPTION) return operator[](id); if (!profileOptions) return invalid_option; GameOption * go = profileOptions->get(optionName); assert(go); return *go; } GameOption* GameSettings::get(int 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; } void GameSettings::createProfileFolders() { if (!profileOptions) return; string temp = profileFile("", "", false); JFileSystem::GetInstance()->MakeDir(temp); temp += "/stats"; JFileSystem::GetInstance()->MakeDir(temp); temp = profileFile(PLAYER_SETTINGS, "", false); profileOptions->save(); } int GameSettings::save() { if (globalOptions) globalOptions->save(); createProfileFolders(); checkProfile(); return 1; } string GameSettings::profileFile(string filename, string fallback, bool sanity) { 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, "profiles/%s", profile.c_str()); return buf; } //Return file sprintf(buf, "profiles/%s/%s", profile.c_str(), filename.c_str()); if (fileExists(buf)) { return buf; } } else { //Use the default directory. sprintf(buf, "player%s%s", (filename == "" ? "" : "/"), filename.c_str()); return buf; } //Don't fallback if sanity checking is disabled.. if (!sanity) { sprintf(buf, "profiles/%s%s%s", 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", fallback.c_str(), (filename == "" ? "" : "/"), filename.c_str()); return buf; } void GameSettings::reloadProfile() { SAFE_DELETE(profileOptions); checkProfile(); } 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 compatibility hack for unlocked modes. for (int x = Options::BEGIN_AWARDS; x < Options::LAST_NAMED; x++) { GameOptionAward * goa = dynamic_cast (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 || MTGCollection() == 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. //Create proper directories createProfileFolders(); } //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 = MTGCollection()->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 || MTGCollection() == NULL) return; MTGDeck *mCollection = NEW MTGDeck(options.profileFile(PLAYER_COLLECTION, "", false).c_str(), MTGCollection()); 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, float _x, float _y) { if (keypad == NULL) keypad = NEW SimplePad(); // show keyboard #ifdef IOS JGE *engine = JGE::GetInstance(); engine->SendCommand( "displayKeyboard", input); #elif ANDROID JGE *engine = JGE::GetInstance(); engine->SendCommand( "displayKeyboard:" << input); #endif 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); } #pragma mark - #pragma mark EnumDefinition //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; } #pragma mark - #pragma mark OptionMaxGrade //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)")); } ; #pragma mark - #pragma mark OptionASkipPhase 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")); } ; #pragma mark - #pragma mark OptionWhosFirst OptionWhosFirst OptionWhosFirst::mDef; OptionWhosFirst::OptionWhosFirst() { mDef.values.push_back(EnumDefinition::assoc(Constants::WHO_P, "Player")); mDef.values.push_back(EnumDefinition::assoc(Constants::WHO_O, "Opponent")); mDef.values.push_back(EnumDefinition::assoc(Constants::WHO_R, "Random")); } ; 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(NOSTARSDYNAMIC, "No Glitter")); mDef.values.push_back(EnumDefinition::assoc(BOTH, "Both"));//no luck in getting this to show up as an option. //Both should still work as always however the enum and this dont want to pair up, no "both" in options now. } ; 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")); } ; OptionKicker OptionKicker::mDef; OptionKicker::OptionKicker() { mDef.values.push_back(EnumDefinition::assoc(Constants::KICKER_ALWAYS, "Always Pay")); mDef.values.push_back(EnumDefinition::assoc(Constants::KICKER_CHOICE, "Offer Choice")); } ; #pragma mark - #pragma mark GameOptionAward //GameOptionAward GameOptionAward::GameOptionAward() { achieved = time(NULL); number = 0; viewed = false; } bool GameOptionAward::read(string input) { //This is quick and dirty. 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) << writer; return true; } bool GameOptionAward::giveAward() { if (number) return false; achieved = time(NULL); viewed = false; number = 1; options.save(); //TODO - Consider efficiency of this placement. return true; } bool GameOptionAward::isViewed() { if (!number) return true; return viewed; } ; string GameOptionAward::menuStr() { if (!number) return _("Not unlocked."); else if (achieved == 1) return _("Unlocked."); char buf[256]; tm * lt = localtime(&achieved); if (!lt) return "Error"; strftime(buf, 255, _("%B %d, %I:%M%p %Y").c_str(), lt); return buf; } static JButton u32_to_button(u32 b) { if (b < JGE_BTN_MAX) return static_cast (b); else return JGE_BTN_NONE; } #pragma mark - #pragma mark GameOptionKeyBindings bool GameOptionKeyBindings::read(string input) { istringstream iss(input); vector > 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 >::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; } #pragma mark -