ad56dfa8d0
first as requested, kicker will now act like the other cost, offering a menu choice, heres the catch tho, it was also thought up that we should maintain the "pay automatically" method of it as it feels more natural to some(even tho as per MTG rules its supposed to be a choice). so here is what i did that i hope satisfies everyone, i added a new menu option under advanced tab..."kicker payment" with 2 setting, by defualt "always pay" but also an option to "always offer choice"... 2nd, minor tweaks to player avatar, every tme i saw it i was like "i need to do something about that", the avatar getting completely sucked into the corner just looked bad imo, so i about doubled the "inactive" size, so it looks a little more uniform with the opponents avatar. also move the library and grave icons just a thin hair to the left so they don't grossly overlap the players avatar as much when active, and increased the dark box theyre contained in my just a few pixels. 3rd, something else thats really bothered me to no end was that the title text of simple menus which display the cards name which owns the box was using small face font, which on pc was *barely* ok...but on psp(smaller devices) looks like white smears and dots. i changed it to share the font and size used inside the menubox itself, the end result is a lot nicer look...and alot easier to read on psp. now if only we can convince wololo that "spades" is alot like a lava lamp, cool at first, but *extremely* dated. the menu box should have a much slicker look, maybe rounded corners instead and lose the street light poles? minor fix for phaseaction, becuase of the nature of this ability finding a happy safe medium without losing function is tough. hopefully this corrects it for good. dropped cast methods menutext returns to lower case, for uniformity.
1138 lines
29 KiB
C++
1138 lines
29 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(QT_CONFIG)
|
|
"keybindings_qt",
|
|
#else
|
|
#if defined(WIN32)
|
|
"keybindings_win",
|
|
#else
|
|
#if defined(LINUX)
|
|
"keybindings_x",
|
|
#else
|
|
"keybindings_psp",
|
|
#endif
|
|
#endif
|
|
#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_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 < 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)
|
|
{
|
|
unknown.push_back(s);
|
|
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 (vector<string>::size_type t = 0; t < unknown.size(); t++)
|
|
file << unknown[t] << "\n";
|
|
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)
|
|
{
|
|
//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();
|
|
}
|
|
|
|
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::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, 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<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;
|
|
}
|