- 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:
@@ -1,4 +1,6 @@
|
||||
include mtg.txt
|
||||
name=Momir Basic
|
||||
unlock=prx_rimom
|
||||
[INIT]
|
||||
mode=momir
|
||||
[PLAYERS]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
name=Classic
|
||||
[INIT]
|
||||
mode=mtg
|
||||
[PLAYERS]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
include mtg.txt
|
||||
name=Random 1 Color
|
||||
unlock=prx_rnddeck
|
||||
[INIT]
|
||||
mode=random1
|
||||
@@ -1,3 +1,5 @@
|
||||
include mtg.txt
|
||||
name=Random 2 Colors
|
||||
unlock=prx_rnddeck
|
||||
[INIT]
|
||||
mode=random2
|
||||
4
projects/mtg/bin/Res/rules/story.txt
Normal file
4
projects/mtg/bin/Res/rules/story.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
include mtg.txt
|
||||
name=Story
|
||||
[INIT]
|
||||
mode=story
|
||||
@@ -1,4 +1,5 @@
|
||||
#make sure this file always looks like mtg.txt, minus the initial shuffle and draw
|
||||
hidden
|
||||
[INIT]
|
||||
mode=mtg
|
||||
[PLAYERS]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user