#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 #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 (atof(sX.c_str())); if (x > 0 && x < 1) { x = SCREEN_WIDTH_F * x; } string sY = safeAttribute(element, "y"); float y = static_cast (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::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::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); }