Files
wagic/projects/mtg/src/GameOptions.cpp
T
omegablast2002@yahoo.com 473abd9814 im forced to do this commit in whole instead of parts as originally planned, and before my beta test period of the changes is complete BECAUSE there are people doing "clean up" and the MASSIVE amount of conflicts i have to resolve from it is WAY too much for me to take on after nearly 200 hours of coding this patch. i cant seem to get enough respect to have people hold off on "clean up" so this brings me to being forced to do a full commit before playtest period is done, so they can go ahead with there *super important* clean up.
ok i WAS going to write a full change log with code exsamples ect, but since im rushed you will get the short version of this log.

first bug fixes, and there were many, 
indestructible creature bug fixed
halimar execavator *embearessing youtube video" bug is fixed

token text now displays source name and tokens abilities

fixed a card view null pointer in an iterator when code used combinations of foreach and aslongas with CD.

epic struggle bug fixed, aslongas was only parsing one space to the right of the operator.

extra cost containing targetting fixed, cards can now have multiple extra cost in all mana...this includes giving a card 2 targeted sacrifices as its main cost.

angelic chorus bug fixed, the card will be soft coded now.

and many other minor bugs fixed, hard to remember all which were fixed.

now, new abilities = words
"legendarylandwalk",
"desertlandwalk",
"snowforestlandwalk",
"snowplainslandwalk",
"snowmountainlandwalk",
"snowislandlandwalk",
"snowswamplandwalk",
"snowlandwalk",
"nonbasiclandwalk",
"strong",//cant be blocked by creature with less power
"weak",//cant block creatures with more power
"phasing",

all true landwalks will now be supported.

new cost types:
morph which is coded as follows
[card]
name=Bloodstoke Howler
facedown={3}
autofacedown={6}{R}:morph
autofaceup=3/0 all(beast|mybattlefield))
text=Morph {6}{R} (You may cast this face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.) -- When Bloodstoke Howler is turned 
face up, Beast creatures you control get +3/+0 until end of turn.
mana={5}{R}
type=Creature
subtype=Beast
power=3
toughness=4
[/card]

you will notice new auto lines autofaceup and autofacedown
these are abilities the cards will have when theyre in that state.
the cost is coded as
facedown={cost}
when a card is faced up it gains auto= lines also.
tho is played normally it will NOT gain autofaceup=lines

card restrictions:
cards can now have restrictions placed on them the restrictions are.
all previous restrictions usable in activated abilities
with the follow additions
control two or more vampires
control less creatures
control snow land
casted a spell
one of a kind
fourth turn
before battle damage
after battle
during battle

[card]
name=Blood Frenzy
target=creature[attacking;blocking]
restriction=before battle damage
auto=4/0
auto=treason
text=Cast Blood Frenzy only before the combat damage step. -- Target attacking or blocking creature gets +4/+0 until end of turn. Destroy that creature 
at the beginning of the next end step.
mana={1}{R}
type=Instant
[/card]

other cost now can have specail restrictions also:
otherrestriction=mytypemin:1 type(swamp),opponenttypemin:1 opponenttype(plains)
these are minimums required inplay of a type
it can be just you, or you and opponent or just opponent
you can also use the words "more" and "less" and * to compare the 2 players fields.

[card]
name=Cho-Arrim Legate
abilities=protection from black
other={0}
otherrestriction=mytypemin:1 type(swamp) , opponenttypemin:1 opponenttype(plains)
text=Protection from black -- If an opponent controls a Swamp and you control a Plains, you may cast Cho-Arrim Legate without paying its mana cost.
mana={2}{W}
type=Creature
subtype=Human Soldier
power=1
toughness=2
[/card]

activated ability gained a new restriction "opponentturnonly"

variables will now be recalculated during the resolve of the major abilities to produce the most current number.
{x}:draw:x <----
new number variables words:
using draw as an exsample
draw:auras <--auras on a creature

draw:type:ally <---counts the allys in your field. self explanitory

draw:thatmuch <--mostly a triggered effects number.
when you take damage draw that much

draw:lifelost
draw:oplifelost
these return the value of the life lost that turn.

new TRIGGER restricitions
sourcenottap
sourceTap
foelostthree<--card cycle uses opponent lost life
foelosttwo<--same as above
once<--this trigger will only ever trigger one time and never again.

new card discriptor words
[multicolor]
[leveler]
[enchanted]
[blackandgreen]
[blackandwhite]
[redandblue]
[blueandgreen]
[redandwhite]
CD will now recalculate the number again on resolve
meaning {x}:target(CreatureTargetChooser[manacost <=x]) will work, with an added bonus {x}:target(CreatureTargetChooser[manacost <=any word variable])

new this(:
this(tapped)<--for strange case cards.
this(untapped)
this(auras)

new MTGAbility keywords
(blink)
(blink)forsrc <--stay blinked while source inplay
hand(blink <---adding hand to the front makes it target hand.

livingweapon
this is an extension of token, simple attach the words "livingweapon" to the front of token( and it will autoamtically token that and attach the card to it.

token( gained:
"targetcontroller" targetting.
"battleready" if put in the tokens abilities it will be a attacker and tapped as it is entering play.


phaseout <--self explanitory

spiritlink <--stacking lifelink style effect that benifits the OWNER of the card.
combatspiritlink same as above.

stacking flanking, requires 2 abilities unfortunately

[card]
name=Agility
target=creature
auto=teach(creature) flanker
auto=teach(creature) flanking
text=Enchant creature -- Enchanted creature gets +1/+1 and has flanking. (Whenever a creature without flanking blocks this creature, the blocking 
creature gets -1/-1 until end of turn.)
mana={1}{R}
type=Enchantment
subtype=Aura
[/card]

removeallcounters(number/number,name)
removes all counters of the type from a card, can all be
"all"
vampire hexmage effect.

added new tools for transforms
,setpower=number
,settoughness=number
removetypes

morph
autofacedown={0}:morph

eradicate <---same as the card name.

cumulativeupcost[ <--self explanitory

upcostmulti[ <--an upcost that will resolve with a && ability

phaseaction[ phase name ] ability

an ability that will trigger on the stated phase name.
also support for phaseactionmulti[

new triggers added:
@vampired( <--sengir vampire effect
@targeted( 
@lifeloss(
@lifed(

add a special ability builder called dynamicability
it acts alot like a choose your own adventure book

dynamicability<! variable 1, variable 2, variable 3,variable 4!> optional ability targetting the original target.

variable list 1:
this is the primary amount source
source
mytgt
myself
myfoe
variable list 2:
this is the variable we're after, or the amount
power
toughness
manacost
colors
age
charge
oneonecounters
thatmuch
variable list 3:
this is the main effect
strike
draw
lifeloss
lifegain
pumppow
pumptough
pumpboth
deplete
countersoneone
variable list 4:
how it will do this effect to.
itself
eachother
targetcontroller
targetopponent
tosrc
srccontroller
srcopponent

the best way to explain its usage is to look at cards coded with this ability. or experiment with combinations.

new gameoption
First turn player:player, opponent, random
who takes the first turn

added poisoned status, tho not complete since MBS hasnt spoiled enough cards to see where this variable will be used.

taught ai how to counter spell
improved ai, it will now cast instants during interupts and during your turn.
previously ai treated instant cards the same as it treated sorceries, which was not fair to the ai.

im sure there is some messed items, but the rev directly before this one had formatting in the code that created hundreds of conflicts with this one, so i had to dig this info out of red and green sections.

cards and test are coming soon, i ask PLEASE do not alter these new additions until the test are commited.
im commiting without the test because instead of allowing me to proceed with my beta test period, there are some that wish to rush me into a commit. if you do not like this commit revert it, i absolutely on no grounds give permission to recommit afterwards. and i will not recommit if a revert is called.
2011-01-21 20:18:56 +00:00

1122 lines
28 KiB
C++

#include "PrecompiledHeader.h"
#include "utils.h"
#include "MTGDeck.h"
#include "GameOptions.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",
"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",
//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::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();
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<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)
{
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(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 || 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(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 = 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<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"));
}
;
//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;
}