674 lines
16 KiB
C++
674 lines
16 KiB
C++
#include "PrecompiledHeader.h"
|
|
|
|
#include "StoryFlow.h"
|
|
#include "MTGDefinitions.h"
|
|
#include "WResourceManager.h"
|
|
#include "AIPlayer.h"
|
|
|
|
//TODO remove this dependency
|
|
#include "AIPlayerBaka.h"
|
|
|
|
#include "Rules.h"
|
|
#include "Credits.h"
|
|
#include "PlayerData.h"
|
|
#include "MTGDeck.h"
|
|
#include "WFont.h"
|
|
#include "GameApp.h"
|
|
#include <JFileSystem.h>
|
|
|
|
#define LINE_SPACE 2
|
|
#define SPACE_BEFORE_CHOICES 10
|
|
|
|
float StoryDialog::currentY = 2;
|
|
float StoryDialog::previousY = 2;
|
|
bool StoryReward::rewardSoundPlayed = false;
|
|
bool StoryReward::rewardsEnabled = true;
|
|
MTGDeck * StoryReward::collection = NULL;
|
|
|
|
StoryDialogElement::StoryDialogElement(float x, float y, int id) :
|
|
JGuiObject(id), mX(x), mY(y)
|
|
{
|
|
}
|
|
|
|
StoryText::StoryText(string text, float _mX, float _mY, string _align, int _font, int id) :
|
|
StoryDialogElement(_mX, _mY, id), text(text), font(_font)
|
|
{
|
|
align = JGETEXT_LEFT;
|
|
if (_align.compare("center") == 0)
|
|
{
|
|
align = JGETEXT_CENTER;
|
|
if (mX == 0) mX = SCREEN_WIDTH / 2;
|
|
}
|
|
else if (_align.compare("right") == 0)
|
|
{
|
|
align = JGETEXT_RIGHT;
|
|
if (mX == 0) mX = SCREEN_WIDTH - 10;
|
|
}
|
|
if (align == JGETEXT_LEFT && mX <= 0)
|
|
{
|
|
mX += 10; //left margin
|
|
}
|
|
}
|
|
void StoryText::Render()
|
|
{
|
|
WFont * mFont = WResourceManager::Instance()->GetWFont(font);
|
|
mFont->SetColor(ARGB(200,255,255,255));
|
|
mFont->SetScale(1.0);
|
|
mFont->DrawString(text.c_str(), mX, mY, align);
|
|
}
|
|
|
|
float StoryText::getHeight()
|
|
{
|
|
WFont * mFont = WResourceManager::Instance()->GetWFont(font);
|
|
return mFont->GetHeight();
|
|
}
|
|
|
|
void StoryText::Update(float)
|
|
{
|
|
//Nothing for now
|
|
}
|
|
|
|
StoryReward::StoryReward(string _type, string _value, string text, float _mX, float _mY, string _align, int _font, int id) :
|
|
StoryText(text, _mX, _mY, _align, _font, id)
|
|
{
|
|
type = STORY_REWARD_CREDITS;
|
|
if (_type.compare("unlockset") == 0)
|
|
{
|
|
type = STORY_REWARD_SET;
|
|
}
|
|
else if (_type.compare("card") == 0)
|
|
{
|
|
type = STORY_REWARD_CARD;
|
|
}
|
|
value = _value;
|
|
rewardDone = 0;
|
|
|
|
}
|
|
|
|
void StoryReward::Render()
|
|
{
|
|
if (rewardDone <= 0) return;
|
|
StoryText::Render();
|
|
}
|
|
|
|
void StoryReward::Update(float)
|
|
{
|
|
if (rewardDone) return;
|
|
|
|
int result = 0;
|
|
|
|
switch (type)
|
|
{
|
|
case STORY_REWARD_CREDITS:
|
|
result = Credits::addCreditBonus(atoi(value.c_str()));
|
|
break;
|
|
case STORY_REWARD_SET:
|
|
{
|
|
if (value.size())
|
|
{
|
|
result = Credits::unlockSetByName(value);
|
|
}
|
|
else
|
|
{
|
|
result = Credits::unlockRandomSet(true);
|
|
}
|
|
if (!result) break;
|
|
MTGSetInfo * si = setlist.getInfo(result - 1);
|
|
if (si)
|
|
{
|
|
string unlockedString = si->getName();
|
|
size_t pos = text.find("${SET}");
|
|
if (pos != string::npos)
|
|
{
|
|
text.replace(pos, pos + 6, unlockedString);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case STORY_REWARD_CARD:
|
|
{
|
|
int cardId = 0;
|
|
MTGCard * card = NULL;
|
|
if (value.size())
|
|
{
|
|
card = MTGCollection()->getCardByName(value);
|
|
if (card)
|
|
{
|
|
cardId = card->getId();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cardId = MTGCollection()->randomCardId();
|
|
card = MTGCollection()->getCardById(cardId);
|
|
}
|
|
|
|
if (!cardId) break;
|
|
|
|
if (!collection)
|
|
{
|
|
collection = NEW MTGDeck(options.profileFile(PLAYER_COLLECTION).c_str(), MTGCollection());
|
|
}
|
|
|
|
result = Credits::addCardToCollection(cardId, collection);
|
|
size_t pos = text.find("${CARD}");
|
|
if (pos != string::npos && card)
|
|
{
|
|
text.replace(pos, pos + 7, card->data->getName());
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!result)
|
|
{
|
|
rewardDone = -1;
|
|
return;
|
|
}
|
|
|
|
if (!rewardsEnabled)
|
|
{
|
|
rewardDone = -1;
|
|
return;
|
|
}
|
|
|
|
if (!rewardSoundPlayed && options[Options::SFXVOLUME].number > 0)
|
|
{
|
|
WResourceManager::Instance()->PlaySample("bonus.wav");
|
|
rewardSoundPlayed = 1;
|
|
}
|
|
rewardDone = 1;
|
|
}
|
|
|
|
ostream& StoryText::toString(ostream& out) const
|
|
{
|
|
return out << "StoryText ::: text : " << text;
|
|
}
|
|
|
|
StoryImage::StoryImage(string img, float mX, float mY) :
|
|
StoryDialogElement(mX, mY), img(img)
|
|
{
|
|
|
|
}
|
|
void StoryImage::Render()
|
|
{
|
|
JQuadPtr quad = WResourceManager::Instance()->RetrieveTempQuad(img);
|
|
if (quad)
|
|
{
|
|
float x = mX;
|
|
if (mX == -1)
|
|
{
|
|
x = SCREEN_WIDTH / 2;
|
|
quad->SetHotSpot(quad->mWidth / 2, 0);
|
|
}
|
|
JRenderer::GetInstance()->RenderQuad(quad.get(), x, mY);
|
|
}
|
|
}
|
|
|
|
float StoryImage::getHeight()
|
|
{
|
|
JQuadPtr quad = WResourceManager::Instance()->RetrieveQuad(img);
|
|
if (quad)
|
|
{
|
|
return quad->mHeight;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void StoryImage::Update(float)
|
|
{
|
|
//Nothing for now
|
|
}
|
|
|
|
ostream& StoryImage::toString(ostream& out) const
|
|
{
|
|
return out << "StoryImage ::: img : " << img;
|
|
}
|
|
|
|
StoryPage::StoryPage(StoryFlow * mParent) :
|
|
mParent(mParent)
|
|
{
|
|
}
|
|
|
|
void StoryChoice::Render()
|
|
{
|
|
WFont * mFont = WResourceManager::Instance()->GetWFont(font);
|
|
mFont->SetColor(ARGB(200,255,255,255));
|
|
if (mHasFocus) mFont->SetColor(ARGB(255,255,255,0));
|
|
mFont->SetScale(mScale);
|
|
mFont->DrawString(text.c_str(), mX, mY, align);
|
|
}
|
|
|
|
float StoryChoice::getHeight()
|
|
{
|
|
WFont * mFont = WResourceManager::Instance()->GetWFont(font);
|
|
return mFont->GetHeight() * mScale;
|
|
}
|
|
|
|
void StoryChoice::Update(float dt)
|
|
{
|
|
if (mScale < mTargetScale)
|
|
{
|
|
mScale += 8.0f * dt;
|
|
if (mScale > mTargetScale) mScale = mTargetScale;
|
|
}
|
|
else if (mScale > mTargetScale)
|
|
{
|
|
mScale -= 8.0f * dt;
|
|
if (mScale < mTargetScale) mScale = mTargetScale;
|
|
}
|
|
}
|
|
|
|
void StoryChoice::Entering()
|
|
{
|
|
mHasFocus = true;
|
|
mTargetScale = 1.2f;
|
|
}
|
|
|
|
bool StoryChoice::Leaving(JButton)
|
|
{
|
|
mHasFocus = false;
|
|
mTargetScale = 1.0f;
|
|
return true;
|
|
}
|
|
|
|
bool StoryChoice::ButtonPressed()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool StoryChoice::hasFocus()
|
|
{
|
|
return mHasFocus;
|
|
}
|
|
|
|
ostream& StoryChoice::toString(ostream& out) const
|
|
{
|
|
return out << "StoryChoice ::: mHasFocus : " << mHasFocus;
|
|
}
|
|
|
|
StoryChoice::StoryChoice(string pageId, string text, int JGOid, float mX, float mY, string _align, int _font, bool hasFocus) :
|
|
StoryText(text, mX, mY, _align, _font, JGOid), pageId(pageId), mHasFocus(hasFocus)
|
|
{
|
|
mScale = 1.0f;
|
|
mTargetScale = 1.0f;
|
|
if (hasFocus) mTargetScale = 1.2f;
|
|
}
|
|
|
|
//Actually loads a duel
|
|
void StoryDuel::init()
|
|
{
|
|
game = new GameObserver(WResourceManager::Instance(), JGE::GetInstance());
|
|
|
|
char folder[255], deckFile[255], deckFileSmall[255];
|
|
sprintf(folder, CAMPAIGNS_FOLDER"%s/%s", mParent->folder.c_str(), pageId.c_str());
|
|
|
|
sprintf(deckFile, "%s/deck.txt", folder);
|
|
sprintf(deckFileSmall, "campaign_%s", mParent->folder.c_str());
|
|
game->loadPlayer(0, NEW HumanPlayer(game, deckFile, deckFileSmall, true));
|
|
|
|
sprintf(deckFile, "%s/opponent_deck.txt", folder);
|
|
sprintf(deckFileSmall, "campaign_ennemy_%s_%s", mParent->folder.c_str(), pageId.c_str());
|
|
game->loadPlayer(1, NEW AI::AIPlayerBaka(game, deckFile, deckFileSmall, "baka.jpg"));
|
|
|
|
string rulesFile = folder;
|
|
rulesFile.append("/rules.txt");
|
|
rules = NEW Rules(bg);
|
|
rules->load(rulesFile);
|
|
|
|
rules->gamemode = GAME_TYPE_STORY;
|
|
game->startGame(GAME_TYPE_STORY, rules);
|
|
}
|
|
|
|
StoryDuel::StoryDuel(TiXmlElement* root, StoryFlow * mParent) :
|
|
StoryPage(mParent)
|
|
{
|
|
game = NULL;
|
|
rules = NULL;
|
|
pageId = root->Attribute("id");
|
|
for (TiXmlNode* node = root->FirstChild(); node; node = node->NextSibling())
|
|
{
|
|
TiXmlElement* element = node->ToElement();
|
|
if (element)
|
|
{
|
|
const char* textC = element->GetText();
|
|
if (element->ValueStr() == "onwin")
|
|
{
|
|
onWin = textC;
|
|
}
|
|
else if (element->ValueStr() == "onlose")
|
|
{
|
|
onLose = textC;
|
|
}
|
|
else if (element->ValueStr() == "bg")
|
|
{
|
|
string text = textC;
|
|
bg = string("campaigns/").append(mParent->folder).append("/").append(text);
|
|
if (!fileExists(bg.c_str())) bg = text;
|
|
}
|
|
else
|
|
{
|
|
StoryPage::loadElement(element); //Father
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StoryDuel::~StoryDuel()
|
|
{
|
|
SAFE_DELETE(rules);
|
|
SAFE_DELETE(game);
|
|
}
|
|
|
|
void StoryDuel::Update(float dt)
|
|
{
|
|
if (!game) init();
|
|
game->Update(dt);
|
|
if (game->didWin())
|
|
{
|
|
if (game->didWin(game->players[0]))
|
|
mParent->gotoPage(onWin);
|
|
else
|
|
mParent->gotoPage(onLose);
|
|
SAFE_DELETE(game);
|
|
}
|
|
}
|
|
|
|
void StoryDuel::Render()
|
|
{
|
|
if (!game) return;
|
|
game->Render();
|
|
}
|
|
|
|
string StoryPage::safeAttribute(TiXmlElement* element, string attribute)
|
|
{
|
|
string s;
|
|
if (element->Attribute(attribute.c_str()))
|
|
{
|
|
s = element->Attribute(attribute.c_str());
|
|
}
|
|
return s;
|
|
}
|
|
|
|
int StoryPage::loadElement(TiXmlElement* element)
|
|
{
|
|
if (!element) return 0;
|
|
const char* textC = element->GetText();
|
|
string text = textC;
|
|
if (element->ValueStr() == "music")
|
|
{
|
|
musicFile = string("campaigns/").append(mParent->folder).append("/").append(text);
|
|
if (!FileExists(musicFile)) musicFile = text;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
StoryDialog::StoryDialog(TiXmlElement* root, StoryFlow * mParent) :
|
|
StoryPage(mParent), JGuiListener(), JGuiController(JGE::GetInstance(), 1, NULL)
|
|
{
|
|
|
|
currentY = 0;
|
|
|
|
for (TiXmlNode* node = root->FirstChild(); node; node = node->NextSibling())
|
|
{
|
|
TiXmlElement* element = node->ToElement();
|
|
if (element)
|
|
{
|
|
string sX = safeAttribute(element, "x");
|
|
float x = static_cast<float> (atof(sX.c_str()));
|
|
if (x > 0 && x < 1)
|
|
{
|
|
x = SCREEN_WIDTH_F * x;
|
|
}
|
|
string sY = safeAttribute(element, "y");
|
|
float y = static_cast<float> (atof(sY.c_str()));
|
|
if (y > 0 && y < 1)
|
|
{
|
|
y = SCREEN_HEIGHT_F * y;
|
|
}
|
|
string align = safeAttribute(element, "align");
|
|
const char* textC = element->GetText();
|
|
string text = textC;
|
|
string sFont = safeAttribute(element, "font");
|
|
int font = atoi(sFont.c_str());
|
|
|
|
if (element->ValueStr() == "text")
|
|
{
|
|
graphics.push_back(NEW StoryText(text, x, y, align, font));
|
|
}
|
|
else if (element->ValueStr() == "title")
|
|
{
|
|
graphics.push_back(NEW StoryText(text, x, y, "center", Fonts::MENU_FONT));
|
|
}
|
|
else if (element->ValueStr() == "img")
|
|
{
|
|
//special case to force center
|
|
if (sX.compare("") == 0)
|
|
{
|
|
x = -1;
|
|
}
|
|
string img = string("campaigns/").append(mParent->folder).append("/").append(text);
|
|
graphics.push_back(NEW StoryImage(img, x, y));
|
|
}
|
|
else if (element->ValueStr() == "answer")
|
|
{
|
|
string id = element->Attribute("goto");
|
|
if (!align.size()) align = "center";
|
|
int i = mObjects.size();
|
|
StoryChoice * sc = NEW StoryChoice(id, text, i, x, y, align, font, (i == 0));
|
|
graphics.push_back(sc);
|
|
Add(sc);
|
|
}
|
|
else if (element->ValueStr() == "reward")
|
|
{
|
|
string type = safeAttribute(element, "type");
|
|
string value = safeAttribute(element, "value");
|
|
graphics.push_back(NEW StoryReward(type, value, text, x, y, align, font));
|
|
}
|
|
else
|
|
{
|
|
StoryPage::loadElement(element); //Father
|
|
}
|
|
}
|
|
}
|
|
this->mListener = this;
|
|
|
|
}
|
|
|
|
void StoryDialog::Update(float dt)
|
|
{
|
|
for (size_t i = 0; i < graphics.size(); ++i)
|
|
{
|
|
graphics[i]->Update(dt);
|
|
}
|
|
|
|
if (StoryReward::collection)
|
|
{
|
|
StoryReward::collection->save();
|
|
SAFE_DELETE(StoryReward::collection);
|
|
}
|
|
|
|
JButton key = mEngine->ReadButton();
|
|
CheckUserInput(key);
|
|
|
|
}
|
|
|
|
void StoryDialog::RenderElement(StoryDialogElement * elmt)
|
|
{
|
|
if (!elmt->mY) elmt->mY = currentY;
|
|
if (elmt->mY == -1)
|
|
{
|
|
elmt->mY = previousY;
|
|
}
|
|
elmt->Render();
|
|
previousY = currentY;
|
|
currentY = elmt->mY + elmt->getHeight() + LINE_SPACE;
|
|
}
|
|
|
|
void StoryDialog::Render()
|
|
{
|
|
currentY = 2;
|
|
previousY = currentY;
|
|
for (size_t i = 0; i < graphics.size(); ++i)
|
|
{
|
|
StoryDialogElement * elmt = (StoryDialogElement *) (graphics[i]);
|
|
if (mCount && elmt == mObjects[0]) currentY += SPACE_BEFORE_CHOICES;
|
|
RenderElement(elmt);
|
|
}
|
|
|
|
}
|
|
|
|
void StoryDialog::ButtonPressed(int, int controlid)
|
|
{
|
|
if ( controlid == kInfoMenuID )
|
|
return;
|
|
if ( controlid == kCancelMenuID )
|
|
return;
|
|
|
|
mParent->gotoPage(((StoryChoice *) mObjects[controlid])->pageId);
|
|
}
|
|
|
|
StoryDialog::~StoryDialog()
|
|
{
|
|
mCount = 0; //avoid parent deleting
|
|
for (size_t i = 0; i < graphics.size(); ++i)
|
|
{
|
|
delete (graphics[i]);
|
|
}
|
|
}
|
|
|
|
StoryFlow::StoryFlow(string folder) :
|
|
folder(folder)
|
|
{
|
|
string path = "campaigns/";
|
|
path.append(folder).append("/story.xml");
|
|
parse(path);
|
|
}
|
|
|
|
StoryPage * StoryFlow::loadPage(TiXmlElement* element)
|
|
{
|
|
TiXmlNode* typeNode = element->FirstChild("type");
|
|
if (!typeNode) return NULL;
|
|
StoryPage * result = NULL;
|
|
const char* type = typeNode->ToElement()->GetText();
|
|
if (string("duel") == type)
|
|
{
|
|
result = NEW StoryDuel(element, this);
|
|
}
|
|
else
|
|
{
|
|
result = NEW StoryDialog(element, this);
|
|
}
|
|
return result;
|
|
|
|
}
|
|
|
|
//
|
|
bool StoryFlow::_gotoPage(string id)
|
|
{
|
|
StoryReward::rewardSoundPlayed = false;
|
|
if (pages.find(id) == pages.end())
|
|
{
|
|
return false;
|
|
}
|
|
currentPageId = id;
|
|
if (pages[currentPageId]->musicFile.size())
|
|
{
|
|
GameApp::playMusic(pages[currentPageId]->musicFile);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool StoryFlow::gotoPage(string id)
|
|
{
|
|
StoryReward::rewardsEnabled = true;
|
|
return _gotoPage(id);
|
|
}
|
|
|
|
bool StoryFlow::loadPageId(string id)
|
|
{
|
|
StoryReward::rewardsEnabled = false;
|
|
return _gotoPage(id);
|
|
}
|
|
|
|
bool StoryFlow::parse(string path)
|
|
{
|
|
JFileSystem *fileSystem = JFileSystem::GetInstance();
|
|
if (!fileSystem) return false;
|
|
|
|
if (!fileSystem->OpenFile(path.c_str())) return false;
|
|
|
|
int size = fileSystem->GetFileSize();
|
|
char *xmlBuffer = NEW char[size];
|
|
fileSystem->ReadFile(xmlBuffer, size);
|
|
|
|
TiXmlDocument doc;
|
|
doc.Parse(xmlBuffer);
|
|
|
|
fileSystem->CloseFile();
|
|
delete[] xmlBuffer;
|
|
|
|
for (TiXmlNode* node = doc.FirstChild(); node; node = node->NextSibling())
|
|
{
|
|
TiXmlElement* element = node->ToElement();
|
|
if (element != NULL)
|
|
{
|
|
if (element->ValueStr() == "page")
|
|
{
|
|
string id = element->Attribute("id");
|
|
|
|
DebugTrace("parsing " << id << "...");
|
|
|
|
StoryPage * sp = loadPage(element);
|
|
pages[id] = sp;
|
|
if (!currentPageId.size()) gotoPage(id);
|
|
}
|
|
else
|
|
{
|
|
//Error
|
|
}
|
|
}
|
|
}
|
|
|
|
//autoLoad
|
|
PlayerData * pd = NEW PlayerData();
|
|
map<string, string>::iterator it = pd->storySaves.find(folder);
|
|
if (it != pd->storySaves.end())
|
|
{
|
|
if (it->second.compare("End") != 0) loadPageId(it->second);
|
|
}
|
|
SAFE_DELETE(pd);
|
|
|
|
return true;
|
|
}
|
|
|
|
void StoryFlow::Update(float dt)
|
|
{
|
|
pages[currentPageId]->Update(dt);
|
|
|
|
}
|
|
|
|
void StoryFlow::Render()
|
|
{
|
|
pages[currentPageId]->Render();
|
|
|
|
}
|
|
|
|
StoryFlow::~StoryFlow()
|
|
{
|
|
for (map<string, StoryPage*>::iterator i = pages.begin(); i != pages.end(); ++i)
|
|
{
|
|
SAFE_DELETE(i->second);
|
|
}
|
|
pages.clear();
|
|
|
|
//autoSave progress
|
|
PlayerData * pd = NEW PlayerData();
|
|
pd->storySaves[folder] = currentPageId;
|
|
pd->save();
|
|
SAFE_DELETE(pd);
|
|
}
|