- Added new code for serialization/deserializaiton of full games including initial game and all the player actions.
- Added an undo menu using this code (beware, it's still very very alpha). - Removed various warning - Cleaned up avatar loading - Added full random lists load/save including the deck shuffling (not sure if I could not replace that with seed load/save) - Moved momir and Co rules configuration out of GameStateDuel - Create a GameType type to avoid mixing int everywhere
This commit is contained in:
@@ -10,14 +10,12 @@
|
||||
#include <JRenderer.h>
|
||||
#include "MTGGamePhase.h"
|
||||
#include "GuiPhaseBar.h"
|
||||
|
||||
GameObserver::GameObserver()
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
#include "AIPlayerBaka.h"
|
||||
#include "MTGRules.h"
|
||||
|
||||
void GameObserver::initialize()
|
||||
{
|
||||
mGameType = GAME_TYPE_CLASSIC;
|
||||
currentPlayer = NULL;
|
||||
currentActionPlayer = NULL;
|
||||
isInterrupting = NULL;
|
||||
@@ -32,6 +30,61 @@ void GameObserver::initialize()
|
||||
combatStep = BLOCKERS;
|
||||
mRules = NULL;
|
||||
connectRule = false;
|
||||
mLoading = false;
|
||||
}
|
||||
|
||||
void GameObserver::cleanup()
|
||||
{
|
||||
SAFE_DELETE(targetChooser);
|
||||
SAFE_DELETE(mLayers);
|
||||
SAFE_DELETE(phaseRing);
|
||||
SAFE_DELETE(replacementEffects);
|
||||
for (size_t i = 0; i < players.size(); ++i)
|
||||
{
|
||||
SAFE_DELETE(players[i]);
|
||||
}
|
||||
players.clear();
|
||||
|
||||
currentPlayer = NULL;
|
||||
currentActionPlayer = NULL;
|
||||
isInterrupting = NULL;
|
||||
currentPlayerId = 0;
|
||||
currentGamePhase = -1;
|
||||
targetChooser = NULL;
|
||||
cardWaitingForTargets = NULL;
|
||||
mExtraPayment = NULL;
|
||||
gameOver = NULL;
|
||||
phaseRing = NULL;
|
||||
replacementEffects = NEW ReplacementEffects();
|
||||
combatStep = BLOCKERS;
|
||||
connectRule = false;
|
||||
actionsList.clear();
|
||||
}
|
||||
|
||||
GameObserver::~GameObserver()
|
||||
{
|
||||
LOG("==Destroying GameObserver==");
|
||||
SAFE_DELETE(targetChooser);
|
||||
SAFE_DELETE(mLayers);
|
||||
SAFE_DELETE(phaseRing);
|
||||
SAFE_DELETE(replacementEffects);
|
||||
for (size_t i = 0; i < players.size(); ++i)
|
||||
{
|
||||
SAFE_DELETE(players[i]);
|
||||
}
|
||||
players.clear();
|
||||
LOG("==GameObserver Destroyed==");
|
||||
}
|
||||
|
||||
GameObserver::GameObserver()
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
|
||||
GameObserver::GameObserver(vector<Player *> _players)
|
||||
{
|
||||
initialize();
|
||||
setPlayers(_players);
|
||||
}
|
||||
|
||||
void GameObserver::setPlayers(vector<Player *> _players)
|
||||
@@ -43,12 +96,6 @@ void GameObserver::setPlayers(vector<Player *> _players)
|
||||
}
|
||||
}
|
||||
|
||||
GameObserver::GameObserver(vector<Player *> _players)
|
||||
{
|
||||
initialize();
|
||||
setPlayers(_players);
|
||||
}
|
||||
|
||||
int GameObserver::getCurrentGamePhase()
|
||||
{
|
||||
return currentGamePhase;
|
||||
@@ -231,7 +278,7 @@ void GameObserver::userRequestNextGamePhase()
|
||||
{
|
||||
nextGamePhase();
|
||||
}
|
||||
|
||||
logAction(currentPlayer, "next");
|
||||
}
|
||||
|
||||
int GameObserver::forceShuffleLibraries()
|
||||
@@ -250,8 +297,9 @@ int GameObserver::forceShuffleLibraries()
|
||||
return result;
|
||||
}
|
||||
|
||||
void GameObserver::startGame(Rules * rules)
|
||||
void GameObserver::startGame(GameType gtype, Rules * rules)
|
||||
{
|
||||
mGameType = gtype;
|
||||
turn = 0;
|
||||
mRules = rules;
|
||||
if (rules)
|
||||
@@ -266,6 +314,11 @@ void GameObserver::startGame(Rules * rules)
|
||||
currentPlayer = players[0];
|
||||
currentActionPlayer = currentPlayer;
|
||||
phaseRing = NEW PhaseRing(this);
|
||||
|
||||
stringstream stream;
|
||||
stream << *this;
|
||||
startupGameSerialized = stream.str();
|
||||
|
||||
if (rules)
|
||||
rules->initGame(this);
|
||||
|
||||
@@ -311,6 +364,26 @@ void GameObserver::startGame(Rules * rules)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(gtype) {
|
||||
case GAME_TYPE_MOMIR:
|
||||
{
|
||||
addObserver(NEW MTGMomirRule(this, -1, MTGCollection()));
|
||||
break;
|
||||
}
|
||||
case GAME_TYPE_STONEHEWER:
|
||||
{
|
||||
addObserver(NEW MTGStoneHewerRule(this, -1,MTGCollection()));
|
||||
break;
|
||||
}
|
||||
case GAME_TYPE_HERMIT:
|
||||
{
|
||||
addObserver(NEW MTGHermitRule(this, -1));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GameObserver::addObserver(MTGAbility * observer)
|
||||
@@ -328,21 +401,6 @@ bool GameObserver::removeObserver(ActionElement * observer)
|
||||
|
||||
}
|
||||
|
||||
GameObserver::~GameObserver()
|
||||
{
|
||||
LOG("==Destroying GameObserver==");
|
||||
SAFE_DELETE(targetChooser);
|
||||
SAFE_DELETE(mLayers);
|
||||
SAFE_DELETE(phaseRing);
|
||||
SAFE_DELETE(replacementEffects);
|
||||
for (size_t i = 0; i < players.size(); ++i)
|
||||
{
|
||||
SAFE_DELETE(players[i]);
|
||||
}
|
||||
players.clear();
|
||||
LOG("==GameObserver Destroyed==");
|
||||
}
|
||||
|
||||
void GameObserver::Update(float dt)
|
||||
{
|
||||
Player * player = currentPlayer;
|
||||
@@ -890,8 +948,12 @@ bool GameObserver::WaitForExtraPayment(MTGCardInstance * card)
|
||||
int GameObserver::cardClick(MTGCardInstance * card, Targetable * object)
|
||||
{
|
||||
Player * clickedPlayer = NULL;
|
||||
if (!card)
|
||||
if (!card) {
|
||||
clickedPlayer = ((Player *) object);
|
||||
logAction(clickedPlayer);
|
||||
} else {
|
||||
logAction(card);
|
||||
}
|
||||
if (targetChooser)
|
||||
{
|
||||
int result;
|
||||
@@ -928,8 +990,8 @@ int GameObserver::cardClick(MTGCardInstance * card, Targetable * object)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (WaitForExtraPayment(card))
|
||||
return 1;
|
||||
if (WaitForExtraPayment(card))
|
||||
return 1;
|
||||
|
||||
int reaction = 0;
|
||||
|
||||
@@ -959,18 +1021,18 @@ int GameObserver::cardClick(MTGCardInstance * card, Targetable * object)
|
||||
}
|
||||
|
||||
reaction = mLayers->actionLayer()->isReactingToClick(card);
|
||||
if (reaction == -1)
|
||||
return mLayers->actionLayer()->reactToClick(card);
|
||||
if (reaction == -1)
|
||||
return mLayers->actionLayer()->reactToClick(card);
|
||||
}
|
||||
else
|
||||
{//this handles abilities on a menu...not just when card is being played
|
||||
reaction = mLayers->actionLayer()->isReactingToTargetClick(object);
|
||||
if (reaction == -1)
|
||||
return mLayers->actionLayer()->reactToTargetClick(object);
|
||||
if (reaction == -1)
|
||||
return mLayers->actionLayer()->reactToTargetClick(object);
|
||||
}
|
||||
|
||||
if (!card)
|
||||
return 0;
|
||||
if (!card)
|
||||
return 0;
|
||||
|
||||
//Current player's hand
|
||||
if (currentPlayer->game->hand->hasCard(card) && currentGamePhase == Constants::MTG_PHASE_CLEANUP
|
||||
@@ -998,7 +1060,6 @@ int GameObserver::cardClick(MTGCardInstance * card, Targetable * object)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int GameObserver::untap(MTGCardInstance * card)
|
||||
@@ -1118,3 +1179,247 @@ int GameObserver::targetListIsSet(MTGCardInstance * card)
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
ostream& operator<<(ostream& out, GameObserver& g)
|
||||
{
|
||||
if(g.startupGameSerialized == "")
|
||||
{
|
||||
out << "[init]" << endl;
|
||||
out << "player=" << g.currentPlayerId + 1 << endl;
|
||||
if(g.currentGamePhase != -1)
|
||||
out << "phase=" << g.phaseRing->phaseName(g.currentGamePhase) << endl;
|
||||
out << "[player1]" << endl;
|
||||
out << *(g.players[0]) << endl;
|
||||
out << "[player2]" << endl;
|
||||
out << *(g.players[1]) << endl;
|
||||
return out;
|
||||
}
|
||||
else
|
||||
{
|
||||
out << "rvalues:";
|
||||
out << saveRandValues(out);
|
||||
out << endl;
|
||||
out << g.startupGameSerialized;
|
||||
}
|
||||
|
||||
out << "[do]" << endl;
|
||||
list<string>::iterator it;
|
||||
|
||||
for(it = (g.actionsList.begin()); it != (g.actionsList.end()); it++)
|
||||
{
|
||||
out << (*it) << endl;
|
||||
}
|
||||
|
||||
out << "[end]" << endl;
|
||||
return out;
|
||||
}
|
||||
|
||||
bool GameObserver::parseLine(const string& s)
|
||||
{
|
||||
size_t limiter = s.find("=");
|
||||
if (limiter == string::npos) limiter = s.find(":");
|
||||
string areaS;
|
||||
if (limiter != string::npos)
|
||||
{
|
||||
areaS = s.substr(0, limiter);
|
||||
if (areaS.compare("player") == 0)
|
||||
{
|
||||
currentPlayerId = atoi(s.substr(limiter + 1).c_str()) - 1;
|
||||
return true;
|
||||
}
|
||||
else if (areaS.compare("phase") == 0)
|
||||
{
|
||||
currentGamePhase = PhaseRing::phaseStrToInt(s.substr(limiter + 1).c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameObserver::load(const string& ss, bool undo)
|
||||
{
|
||||
int state = -1;
|
||||
string s;
|
||||
stringstream stream(ss);
|
||||
string deckFile = players[0]->deckFile;
|
||||
string deckFileSmall = players[0]->deckFileSmall;
|
||||
|
||||
DebugTrace("Loading " + ss);
|
||||
|
||||
cleanup();
|
||||
|
||||
while (std::getline(stream, s))
|
||||
{
|
||||
if (!s.size()) continue;
|
||||
if (s[s.size() - 1] == '\r') s.erase(s.size() - 1); //Handle DOS files
|
||||
if (!s.size()) continue;
|
||||
if (s[0] == '#') continue;
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||
if (s.find("seed ") == 0)
|
||||
{
|
||||
// seed = atoi(s.substr(5).c_str());
|
||||
continue;
|
||||
}
|
||||
if (s.find("rvalues:") == 0)
|
||||
{
|
||||
loadRandValues(s.substr(8).c_str());
|
||||
continue;
|
||||
}
|
||||
switch (state)
|
||||
{
|
||||
case -1:
|
||||
if (s.compare("[init]") == 0)
|
||||
state++;
|
||||
break;
|
||||
case 0:
|
||||
if (s.compare("[player1]") == 0)
|
||||
{
|
||||
state++;
|
||||
}
|
||||
else
|
||||
{
|
||||
parseLine(s);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (s.compare("[player2]") == 0)
|
||||
{
|
||||
state++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!players[0])
|
||||
players.push_back(new HumanPlayer(this, deckFile, deckFileSmall));
|
||||
players[0]->parseLine(s);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (s.compare("[do]") == 0)
|
||||
{
|
||||
state++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!players[1]) {
|
||||
AIPlayerFactory playerCreator;
|
||||
players.push_back(playerCreator.createAIPlayer(this, MTGCollection(), players[0]));
|
||||
}
|
||||
players[1]->parseLine(s);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (s.compare("[end]") == 0)
|
||||
{
|
||||
turn = 0;
|
||||
mLayers = NEW DuelLayers();
|
||||
mLayers->init(this);
|
||||
currentPlayer = players[currentPlayerId];
|
||||
phaseRing = NEW PhaseRing(this);
|
||||
startedAt = time(0);
|
||||
|
||||
mRules->initGame(this);
|
||||
phaseRing->goToPhase(0, currentPlayer, false);
|
||||
phaseRing->goToPhase(currentGamePhase, currentPlayer);
|
||||
processActions(undo);
|
||||
}
|
||||
else
|
||||
{
|
||||
logAction(s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameObserver::processActions(bool undo)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
list<string> copyList = actionsList;
|
||||
actionsList.clear();
|
||||
|
||||
mLoading = true;
|
||||
list<string>::iterator ite;
|
||||
float counter = 0.0f;
|
||||
|
||||
// To handle undo, we'll remove the last P1 action and all P2 actions after.
|
||||
if(undo) {
|
||||
while(copyList.back().find("p2") != string::npos)
|
||||
copyList.pop_back();
|
||||
copyList.pop_back();
|
||||
}
|
||||
for(ite = copyList.begin(); ite != copyList.end(); ite++)
|
||||
{
|
||||
string s = *ite;
|
||||
Player* p = players[1];
|
||||
if (s.find("p1") != string::npos)
|
||||
p = players[0];
|
||||
|
||||
MTGGameZone* zone = NULL;
|
||||
if(s.find(p->game->hand->getName()) != string::npos)
|
||||
zone = p->game->hand;
|
||||
else if(s.find(p->game->battlefield->getName()) != string::npos)
|
||||
zone = p->game->battlefield;
|
||||
else if(s.find(p->game->graveyard->getName()) != string::npos)
|
||||
zone = p->game->graveyard;
|
||||
else if(s.find(p->game->library->getName()) != string::npos)
|
||||
zone = p->game->library;
|
||||
|
||||
if(zone) {
|
||||
size_t begin = s.find("[")+1;
|
||||
size_t size = s.find("]")-begin;
|
||||
int index = atoi(s.substr(begin, size).c_str());
|
||||
cardClick(zone->cards[index], zone->cards[index]);
|
||||
} else if (s.find("yes") != string::npos) {
|
||||
mLayers->stackLayer()->setIsInterrupting(p);
|
||||
} else if (s.find("no") != string::npos) {
|
||||
mLayers->stackLayer()->cancelInterruptOffer();
|
||||
} else if (s.find("endinterruption") != string::npos) {
|
||||
mLayers->stackLayer()->endOfInterruption();
|
||||
} else if (s.find("next") != string::npos) {
|
||||
userRequestNextGamePhase();
|
||||
} else if (s.find("choice") != string::npos) {
|
||||
int choice = atoi(s.substr(s.find("choice ") + 7).c_str());
|
||||
mLayers->actionLayer()->doReactTo(choice);
|
||||
} else if (s == "p1" || s == "p2") {
|
||||
cardClick(NULL, p);
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
// let's fake an update
|
||||
Update(counter);
|
||||
counter += 1.000f;
|
||||
// or two
|
||||
Update(counter);
|
||||
counter += 1.000f;
|
||||
}
|
||||
|
||||
mLoading = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
void GameObserver::logAction(Player* player, const string& s) {
|
||||
if(player == players[0])
|
||||
logAction("p1." + s);
|
||||
else
|
||||
logAction("p2." + s);
|
||||
}
|
||||
|
||||
void GameObserver::logAction(MTGCardInstance* card, MTGGameZone* zone) {
|
||||
stringstream stream;
|
||||
if(zone == NULL) zone = card->currentZone;
|
||||
stream << "p" << ((card->controller()==players[0])?"1.":"2.")
|
||||
<< zone->getName()<< "[" << zone->getIndex(card) << "]";
|
||||
logAction(stream.str());
|
||||
}
|
||||
|
||||
bool GameObserver::undo()
|
||||
{
|
||||
stringstream stream;
|
||||
stream << *this;
|
||||
DebugTrace(stream.str());
|
||||
return load(stream.str(), true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user