Files
wagic/projects/mtg/src/GameOptions.cpp
T
wagic.the.homebrew 52b83a135c - Added TutorialMessage ability
-- Tutorial Messages are an ability like any other, except it can only be displayed once. Subsequent calls are ignored, the ability is removed from the game as soon as it is added
-- This allows to add event triggered messages ingame. Messages are either text, or images (I don't have an image sample, but rules/classic.txt has a few examples that might help)
-- only tested on Windows, although I made sure the PSP version compiles. Hopefully I also made the necessary for it to work in the touch version (touching the screen should be enough to close the tuto message)
-- Room for improvement: possibility to choose a title in text mode, possibility to have some messages depending on others (e.g.: don't show message X until message Y has been shown), improve some of the abilities and triggers to give more flexibility, add events outside of game, to allow tuto messages in deck creator, etc...
2011-07-03 08:47:51 +00:00

1187 lines
30 KiB
C++

#include "PrecompiledHeader.h"
#include "utils.h"
#include "MTGDeck.h"
#include "Translate.h"
#include "OptionItem.h"
#include "StyleManager.h"
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_rimom",
"prx_rewehenots",
"prx_timreh",
"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?
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 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(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;
}
GameOptions::GameOptions(string filename)
{
mFilename = filename;
GameOptions::load();
}
int GameOptions::load()
{
wagic::ifstream file(mFilename.c_str());
std::string s;
if (file)
{
while (std::getline(file, 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] = NEW GameOption(val);
continue;
}
(*this)[id].read(val);
}
file.close();
}
// (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(mFilename.c_str());
if (file)
{
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<string, GameOption *>::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::get(string optionName)
{
if (!unknownMap[optionName])
unknownMap[optionName] = NEW GameOption(0);
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<GameOption*>::iterator it = values.begin(); it != values.end(); it++)
SAFE_DELETE(*it);
values.clear();
for (map<string, GameOption*>::iterator it = unknownMap.begin(); it != unknownMap.end(); it++)
SAFE_DELETE(it->second);
unknownMap.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();
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<MTGCardInstance *, int>::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<GameOptionAward *> (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)
{
#ifdef DEBUG
string option_name = Options::getName(optionID);
#endif
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(JGE_GET_RES("profiles").c_str());
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 ? "" : JGE_GET_RES("").c_str()), profile.c_str());
return buf;
}
//Return file
sprintf(buf, JGE_GET_RES("profiles/%s/%s").c_str(), 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 ? "" : JGE_GET_RES("").c_str()), (filename == "" ? "" : "/"), filename.c_str());
return buf;
}
//Don't fallback if sanity checking is disabled..
if (!sanity)
{
sprintf(buf, "%sprofiles/%s%s%s", (relative ? "" : JGE_GET_RES("").c_str()), 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 ? "" : JGE_GET_RES("").c_str()), fallback.c_str(), (filename == "" ? "" : "/"),
filename.c_str());
return buf;
}
void GameSettings::reloadProfile()
{
SAFE_DELETE(profileOptions);
checkProfile();
}
void GameSettings::checkProfile()
{
if (!globalOptions)
globalOptions = NEW GameOptions(JGE_GET_RES(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 < Options::LAST_NAMED; x++)
{
GameOptionAward * goa = dynamic_cast<GameOptionAward *> (globalOptions->get(x));
if (goa)
{
GameOptionAward * dupe = dynamic_cast<GameOptionAward *> (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.
//Make the proper directories
if (profileOptions)
{
//Force our directories to exist.
MAKEDIR(JGE_GET_RES("profiles").c_str());
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 = 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();
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<assoc>::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<EnumDefinition::assoc>::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"));
}
;
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"));
}
;
//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<JButton> (b);
else
return JGE_BTN_NONE;
}
bool GameOptionKeyBindings::read(string input)
{
istringstream iss(input);
vector<pair<LocalKeySym, JButton> > 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<LocalKeySym, JButton> >::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;
}