- Files in Res/rules are now loaded dynamically. This allows people to add their own mods fairly easily

- also has a basic support for unlocks (does not yet allow people to create their own unlock rules, though...)
- Some things are still hardcoded and should progressively move to these rules files (such as Game Type, how random decks are created, etc...)
- Test suite doesn't foolishly ask you to choose a game mode (momir, etc...) anymore
- UPDATE YOUR Res/rules/ folder !!!
This commit is contained in:
wagic.the.homebrew
2011-04-23 13:24:19 +00:00
parent ba07ca2334
commit 66ad0f4694
15 changed files with 146 additions and 85 deletions

View File

@@ -1,4 +1,6 @@
include mtg.txt
name=Momir Basic
unlock=prx_rimom
[INIT]
mode=momir
[PLAYERS]

View File

@@ -1,3 +1,4 @@
name=Classic
[INIT]
mode=mtg
[PLAYERS]

View File

@@ -1,3 +1,5 @@
include mtg.txt
name=Random 1 Color
unlock=prx_rnddeck
[INIT]
mode=random1

View File

@@ -1,3 +1,5 @@
include mtg.txt
name=Random 2 Colors
unlock=prx_rnddeck
[INIT]
mode=random2

View File

@@ -0,0 +1,4 @@
include mtg.txt
name=Story
[INIT]
mode=story

View File

@@ -1,4 +1,5 @@
#make sure this file always looks like mtg.txt, minus the initial shuffle and draw
hidden
[INIT]
mode=mtg
[PLAYERS]

View File

@@ -25,6 +25,8 @@
#include "JNetwork.h"
#endif //NETWORK_SUPPORT
class Rules;
enum
{
PLAYER_TYPE_CPU = 0,
@@ -68,6 +70,7 @@ private:
public:
int gameType;
Rules * rules;
CardEffect *effect;
#ifdef NETWORK_SUPPORT
JNetwork* mpNetwork;

View File

@@ -14,7 +14,6 @@
class TestSuite;
#endif
class Credits;
class Rules;
#ifdef NETWORK_SUPPORT
class JNetwork;
#endif
@@ -41,7 +40,6 @@ private:
bool premadeDeck;
int OpponentsDeckid;
string musictrack;
Rules * rules;
bool MusicExist(string FileName);
void loadPlayer(int playerId, int decknb = 0, bool isAI = false, bool isNetwork = false);

View File

@@ -70,11 +70,20 @@ public:
};
string bg;
Rules(string filename, string bg = "");
int load(string filename);
string filename;
int gamemode;
bool hidden;
string displayName;
int unlockOption;
static vector<Rules *> RulesList;
Rules(string bg = "");
int load(string _filename);
static int loadAllRules();
static void unloadAllRules();
static Rules * getRulesByFilename(string _filename);
void initPlayers();
bool canChooseDeck(); //True if the players get to select their decks, false if the decks are automatically generated by the mode
void addExtraRules();
void initGame();
void cleanup();
@@ -85,4 +94,6 @@ public:
};
#endif

View File

@@ -34,6 +34,9 @@ AIStats::~AIStats()
void AIStats::updateStatsCard(MTGCardInstance * cardInstance, Damage * damage, float multiplier)
{
MTGCard * card = cardInstance->model;
if (!card)
return; //card can be null because some special cardInstances (such as ExtraRules) don't have a "model"
AIStat * stat = find(card);
if (!stat)
{

View File

@@ -26,6 +26,7 @@
#include "DeckManager.h"
#include "Translate.h"
#include "WFilter.h"
#include "Rules.h"
#define DEFAULT_DURATION .25
@@ -247,6 +248,9 @@ void GameApp::Create()
mCurrentState = NULL;
mNextState = mGameStates[GAME_STATE_MENU];
LOG("--Load Game rules");
Rules::loadAllRules();
//Set Audio volume
JSoundSystem::GetInstance()->SetSfxVolume(options[Options::SFXVOLUME].number);
JSoundSystem::GetInstance()->SetMusicVolume(options[Options::MUSICVOLUME].number);
@@ -298,6 +302,8 @@ void GameApp::Destroy()
DeckEditorMenu::destroy();
options.theGame = NULL;
Rules::unloadAllRules();
LOG("==Destroying GameApp Successful==");
#ifdef TRACK_FILE_USAGE_STATS

View File

@@ -81,7 +81,6 @@ GameState(parent)
#endif
credits = NULL;
rules = NULL;
#ifdef NETWORK_SUPPORT
RegisterNetworkPlayers();
@@ -233,7 +232,7 @@ void GameStateDuel::loadTestSuitePlayers()
GameObserver::Init(mPlayers, 2);
game = GameObserver::GetInstance();
game->startGame(rules);
game->startGame(mParent->rules);
if (mParent->gameType == GAME_TYPE_MOMIR)
{
game->addObserver(NEW MTGMomirRule(-1, MTGCollection()));
@@ -267,7 +266,6 @@ void GameStateDuel::End()
mPlayers[i] = NULL;
}
SAFE_DELETE(credits);
SAFE_DELETE(rules);
SAFE_DELETE(menu);
SAFE_DELETE(opponentMenu);
@@ -326,19 +324,8 @@ void GameStateDuel::Update(float dt)
break;
case DUEL_STATE_CHOOSE_DECK1:
if (mParent->gameType == GAME_TYPE_MOMIR)
if (!mParent->rules->canChooseDeck())
{
rules = NEW Rules("momir.txt");
mGamePhase = DUEL_STATE_PLAY;
}
else if (mParent->gameType == GAME_TYPE_RANDOM1)
{
rules = NEW Rules("random1.txt");
mGamePhase = DUEL_STATE_PLAY;
}
else if (mParent->gameType == GAME_TYPE_RANDOM2)
{
rules = NEW Rules("random2.txt");
mGamePhase = DUEL_STATE_PLAY;
}
#ifdef TESTSUITE
@@ -346,7 +333,6 @@ void GameStateDuel::Update(float dt)
{
if (testSuite && testSuite->loadNext())
{
rules = NEW Rules("testsuite.txt");
loadTestSuitePlayers();
mGamePhase = DUEL_STATE_PLAY;
testSuite->pregameTests();
@@ -367,7 +353,6 @@ void GameStateDuel::Update(float dt)
#endif
else
{
if (!rules) rules = NEW Rules("mtg.txt");
if (mParent->players[0] == PLAYER_TYPE_HUMAN)
{
if (!popupScreen || popupScreen->isClosed()) deckmenu->Update(dt);
@@ -424,7 +409,7 @@ void GameStateDuel::Update(float dt)
{
GameObserver::Init(mPlayers, 2);
game = GameObserver::GetInstance();
game->startGame(rules);
game->startGame(mParent->rules);
if (mParent->gameType == GAME_TYPE_MOMIR)
{
game->addObserver(NEW MTGMomirRule(-1, MTGCollection()));

View File

@@ -18,6 +18,7 @@
#include "utils.h"
#include "WFont.h"
#include <JLogger.h>
#include "Rules.h"
#ifdef NETWORK_SUPPORT
#include <JNetwork.h>
#endif//NETWORK_SUPPORT
@@ -69,11 +70,7 @@ enum
#endif //NETWORK_SUPPORT
SUBMENUITEM_DEMO,
SUBMENUITEM_TESTSUITE,
SUBMENUITEM_MOMIR,
SUBMENUITEM_CLASSIC,
SUBMENUITEM_RANDOM1,
SUBMENUITEM_RANDOM2,
SUBMENUITEM_STORY
SUBMENUITEM_END_OFFSET
};
GameStateMenu::GameStateMenu(GameApp* parent) :
@@ -136,6 +133,7 @@ void GameStateMenu::Create()
scrollerSet = 0;
splashTex = NULL;
}
void GameStateMenu::Destroy()
@@ -470,8 +468,6 @@ void GameStateMenu::Update(float dt)
if (primitivesLoadCounter == -1)
{
listPrimitives();
// Move translator init to GameApp::Create().
// Translator::GetInstance()->init();
}
if (primitivesLoadCounter < (int) (primitives.size()))
{
@@ -600,15 +596,14 @@ void GameStateMenu::Update(float dt)
subMenuController = NEW SimpleMenu(MENU_FIRST_DUEL_SUBMENU, this, Fonts::MENU_FONT, 150, 60);
if (subMenuController)
{
subMenuController->Add(SUBMENUITEM_CLASSIC, "Classic");
if (options[Options::MOMIR_MODE_UNLOCKED].number)
subMenuController->Add(SUBMENUITEM_MOMIR, "Momir Basic");
if (options[Options::RANDOMDECK_MODE_UNLOCKED].number)
for (size_t i = 0; i < Rules::RulesList.size(); ++i)
{
subMenuController->Add(SUBMENUITEM_RANDOM1, "Random 1 Color");
subMenuController->Add(SUBMENUITEM_RANDOM2, "Random 2 Colors");
Rules * rules = Rules::RulesList[i];
if (!rules->hidden && (rules->unlockOption == INVALID_OPTION || options[rules->unlockOption].number))
{
subMenuController->Add(SUBMENUITEM_END_OFFSET + i, rules->displayName.c_str());
}
}
subMenuController->Add(SUBMENUITEM_STORY, "Story");
subMenuController->Add(SUBMENUITEM_CANCEL, "Cancel");
}
}
@@ -885,49 +880,24 @@ void GameStateMenu::ButtonPressed(int controllerId, int controlId)
currentState = MENU_STATE_MAJOR_MAINMENU | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_CLASSIC:
this->hasChosenGameType = true;
mParent->gameType = GAME_TYPE_CLASSIC;
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_MOMIR:
this->hasChosenGameType = true;
mParent->gameType = GAME_TYPE_MOMIR;
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_RANDOM1:
this->hasChosenGameType = true;
mParent->gameType = GAME_TYPE_RANDOM1;
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_RANDOM2:
this->hasChosenGameType = true;
mParent->gameType = GAME_TYPE_RANDOM2;
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
case SUBMENUITEM_STORY:
this->hasChosenGameType = true;
mParent->gameType = GAME_TYPE_STORY;
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
#ifdef TESTSUITE
case SUBMENUITEM_TESTSUITE:
mParent->rules = Rules::getRulesByFilename("testsuite.txt");
this->hasChosenGameType = true;
mParent->gameType = GAME_TYPE_CLASSIC;
mParent->players[0] = PLAYER_TYPE_TESTSUITE;
mParent->players[1] = PLAYER_TYPE_TESTSUITE;
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
#endif
default: //Game modes
this->hasChosenGameType = true;
mParent->rules = Rules::RulesList[controlId - SUBMENUITEM_END_OFFSET];
mParent->gameType = (mParent->rules->gamemode); //TODO can we get rid of gameType in the long run, since it is also stored in the rules object ?
subMenuController->Close();
currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING;
break;
}
break;
}

View File

@@ -13,6 +13,59 @@
#include "AIPlayer.h"
#include <JLogger.h>
vector<Rules *> Rules::RulesList = vector<Rules *>();
//Sorting by dissplayName
struct RulesMenuCmp{
bool operator()(const Rules * a,const Rules * b) const{
return a->displayName < b->displayName;
}
} RulesMenuCmp_;
Rules * Rules::getRulesByFilename(string _filename)
{
for (size_t i = 0; i < RulesList.size(); ++i)
{
if (RulesList[i]->filename == _filename)
return RulesList[i];
}
return NULL;
}
int Rules::loadAllRules()
{
DIR *dip = opendir(JGE_GET_RES("rules").c_str());
struct dirent *dit;
while ((dit = readdir(dip)))
{
Rules * rules = NEW Rules();
if (rules->load(dit->d_name))
{
RulesList.push_back(rules);
}
else
{
SAFE_DELETE(rules);
}
}
//Kind of a hack here, we sort Rules alphabetically because it turns out to be matching
// The historical order of Game modes: Classic, Momir Basic, Random 1, Random 2, Story
std::sort(RulesList.begin(),RulesList.end(),RulesMenuCmp_);
closedir(dip);
return 1;
}
void Rules::unloadAllRules()
{
for (size_t i = 0; i < RulesList.size(); ++i)
{
SAFE_DELETE(RulesList[i]);
}
RulesList.clear();
}
int Rules::getMTGId(string cardName)
{
int cardnb = atoi(cardName.c_str());
@@ -195,7 +248,7 @@ void Rules::addExtraRules()
#ifdef NETWORK_SUPPORT
&& !g->players[1] == PLAYER_TYPE_REMOTE
#endif //NETWORK_SUPPORT
)//keep this out of mimor and other game modes.
)//keep this out of momir and other game modes.
{
difficultyRating = DeckManager::getDifficultyRating(g->players[0], g->players[1]);
}
@@ -389,10 +442,7 @@ void Rules::initGame()
DebugTrace("RULES Init Game\n");
//Set the current player/phase
if (g->currentPlayer->playMode
!= Player::MODE_TEST_SUITE && /*g->mRules->gamemode != GAME_TYPE_MOMIR && g->mRules->gamemode
!= GAME_TYPE_RANDOM1 && g->mRules->gamemode != GAME_TYPE_RANDOM2 &&*/ g->mRules->gamemode
!= GAME_TYPE_STORY)
if (g->currentPlayer->playMode!= Player::MODE_TEST_SUITE && g->mRules->gamemode!= GAME_TYPE_STORY)
{
if(OptionWhosFirst::WHO_R == options[Options::FIRSTPLAYER].number)
initState.player = WRand() % 2;
@@ -490,25 +540,33 @@ void Rules::cleanup()
initState.cleanup();
}
Rules::Rules(string filename, string _bg)
Rules::Rules(string _bg)
{
bg = _bg;
load(filename);
unlockOption = INVALID_OPTION;
hidden = false;
filename = "";
}
bool Rules::canChooseDeck()
{
return (gamemode == GAME_TYPE_CLASSIC);
}
int Rules::load(string _filename)
{
char filename[4096];
if (!filename.size()) //this check is necessary because of the recursive calls (a fil loads other files)
filename = _filename;
char c_filename[4096];
if (fileExists(_filename.c_str()))
{
sprintf(filename, "%s", _filename.c_str());
sprintf(c_filename, "%s", _filename.c_str());
}
else
{
sprintf(filename, JGE_GET_RES("rules/%s").c_str(), _filename.c_str());
sprintf(c_filename, JGE_GET_RES("rules/%s").c_str(), _filename.c_str());
}
wagic::ifstream file(filename);
wagic::ifstream file(c_filename);
std::string s;
int state = PARSE_UNDEFINED;
@@ -522,6 +580,7 @@ int Rules::load(string _filename)
if (!s.size()) continue;
if (s[s.size() - 1] == '\r') s.erase(s.size() - 1); //Handle DOS files
if (s[0] == '#') continue;
string scopy = s;
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
if (s.find("include ") == 0)
{
@@ -552,6 +611,18 @@ int Rules::load(string _filename)
switch (state)
{
case PARSE_UNDEFINED:
if (s.find("name=") == 0)
{
displayName = scopy.substr(5);
}
else if (s.find("unlock=") == 0)
{
unlockOption = Options::getID(s.substr(7));
}
else if (s.find("hidden") == 0)
{
hidden = true;
}
break;
case PARSE_INIT:
if (s.find("auto=") == 0)
@@ -592,5 +663,6 @@ int Rules::strToGameMode(string s)
if (s.compare("momir") == 0) return GAME_TYPE_MOMIR;
if (s.compare("random1") == 0) return GAME_TYPE_RANDOM1;
if (s.compare("random2") == 0) return GAME_TYPE_RANDOM2;
if (s.compare("story") == 0) return GAME_TYPE_STORY;
return GAME_TYPE_CLASSIC;
}

View File

@@ -314,7 +314,8 @@ void StoryDuel::init()
string rulesFile = folder;
rulesFile.append("/rules.txt");
rules = NEW Rules(rulesFile, bg);
rules = NEW Rules(bg);
rules->load(rulesFile);
GameObserver::Init(players, 2);
game = GameObserver::GetInstance();