diff --git a/projects/mtg/bin/Res/campaigns/tutorial/duel1/deck.txt b/projects/mtg/bin/Res/campaigns/tutorial/duel1/deck.txt new file mode 100644 index 000000000..077fd776a --- /dev/null +++ b/projects/mtg/bin/Res/campaigns/tutorial/duel1/deck.txt @@ -0,0 +1,21 @@ +#NAME:Magnivore +#DESC:Do not underestimate +#DESC:this... thing... because +#DESC:it happens to be blind. +#DESC:For if you do, it will +#DESC:be your last mistake! +Hymn to Tourach (FEM) * 4 # +Magnivore (ODY) * 4 # +Stone Rain (CHK) * 4 # +Mountain (RAV) * 4 # +Swamp (RAV) * 4 # +Swamp (TSP) * 4 # +Mountain (TSP) * 4 # +Damnation (PLC) * 4 # +Demolish (10E) * 4 # +Mountain (LRW) * 4 # +Swamp (LRW) * 4 # +Blightning (ALA) * 4 # +Megrim (M10) * 4 # +Sign in Blood (M10) * 4 # +Pyroclasm (M10) * 4 # diff --git a/projects/mtg/bin/Res/campaigns/tutorial/duel1/rules.txt b/projects/mtg/bin/Res/campaigns/tutorial/duel1/rules.txt new file mode 100644 index 000000000..118fdad76 --- /dev/null +++ b/projects/mtg/bin/Res/campaigns/tutorial/duel1/rules.txt @@ -0,0 +1,9 @@ +[INIT] +mode=mtg +[PLAYERS] +life:4 +auto=shuffle +auto=draw:7 +auto=@each my draw:draw:1 +[PLAYER2] +library:forest,forest,forest,mountain,mountain,plains,raging goblin,goblin king \ No newline at end of file diff --git a/projects/mtg/bin/Res/campaigns/tutorial/story.xml b/projects/mtg/bin/Res/campaigns/tutorial/story.xml new file mode 100644 index 000000000..8cdef9738 --- /dev/null +++ b/projects/mtg/bin/Res/campaigns/tutorial/story.xml @@ -0,0 +1,31 @@ + + + +dialog +This is a test Story, what do you want to do? +Go left +Go right + + + +dialog +You found the Grail +click to continue + + + +dialog +You found the Arch of Noah +click to continue + + + +duel +End +1 + + + +End +The End + \ No newline at end of file diff --git a/projects/mtg/include/GameApp.h b/projects/mtg/include/GameApp.h index 8cf087206..745d5333d 100644 --- a/projects/mtg/include/GameApp.h +++ b/projects/mtg/include/GameApp.h @@ -40,6 +40,7 @@ #define GAME_TYPE_MOMIR 1 #define GAME_TYPE_RANDOM1 2 #define GAME_TYPE_RANDOM2 3 +#define GAME_TYPE_STORY 4 class MTGAllCards; class TransitionBase; diff --git a/projects/mtg/include/GameState.h b/projects/mtg/include/GameState.h index aa9ef3a0a..7acd78934 100644 --- a/projects/mtg/include/GameState.h +++ b/projects/mtg/include/GameState.h @@ -18,8 +18,9 @@ enum ENUM_GAME_STATE GAME_STATE_SHOP = 4, GAME_STATE_OPTIONS = 5, GAME_STATE_AWARDS = 6, - GAME_STATE_TRANSITION = 7, - GAME_STATE_MAX = 8, + GAME_STATE_STORY = 7, + GAME_STATE_TRANSITION = 8, + GAME_STATE_MAX = 9, }; enum ENUM_GS_TRANSITION diff --git a/projects/mtg/include/GameStateStory.h b/projects/mtg/include/GameStateStory.h new file mode 100644 index 000000000..02453f222 --- /dev/null +++ b/projects/mtg/include/GameStateStory.h @@ -0,0 +1,29 @@ +#ifndef _GAME_STATE_STORY_H_ +#define _GAME_STATE_STORY_H_ + + +#include "../include/GameState.h" +#include + +class StoryFlow; +class SimpleMenu; + +class GameStateStory: public GameState, public JGuiListener { +private: + StoryFlow * flow; + SimpleMenu * menu; + vector stories; + void loadStoriesMenu(const char * root); + public: + GameStateStory(GameApp* parent); + ~GameStateStory(); + void Start(); + void End(); + void Update(float dt); + void Render(); + void ButtonPressed(int controllerId, int controlId); + +}; + + +#endif \ No newline at end of file diff --git a/projects/mtg/include/Player.h b/projects/mtg/include/Player.h index a4086025a..8b535a851 100644 --- a/projects/mtg/include/Player.h +++ b/projects/mtg/include/Player.h @@ -45,6 +45,7 @@ class Player: public Damageable{ class HumanPlayer: public Player{ public: HumanPlayer(MTGPlayerCards * deck, string deckFile, string deckFileSmall); + HumanPlayer(string deckFile); }; diff --git a/projects/mtg/include/StoryFlow.h b/projects/mtg/include/StoryFlow.h new file mode 100644 index 000000000..2703375a1 --- /dev/null +++ b/projects/mtg/include/StoryFlow.h @@ -0,0 +1,86 @@ +#ifndef _STORYFLOW_H_ +#define _STORYFLOW_H_ + +#include +#include +#include +using namespace std; +#include "../../../JGE/src/TinyXML/tinyxml.h" +#include +class GameObserver; +#define CAMPAIGNS_FOLDER "Res/campaigns/" + +class StoryChoice:public JGuiObject { +public: + string pageId; + string text; + int mX; + int mY; + bool mHasFocus; + float mScale; + float mTargetScale; + StoryChoice(string id, string text, int JGOid, float mX, float mY, bool hasFocus); + void Render(); + void Update(float dt); + + void Entering(); + bool Leaving(u32 key); + bool ButtonPressed(); + bool hasFocus(); + virtual ostream& toString(ostream& out) const; +}; + +class StoryFlow; +class StoryPage { +public: + StoryFlow * mParent; + StoryPage(StoryFlow * mParent); + virtual void Update(float dt)=0; + virtual void Render()=0; + virtual ~StoryPage(){}; +}; + +class StoryDialog:public StoryPage, public JGuiListener,public JGuiController { +private: + string text; + +public: + StoryDialog(TiXmlElement* el,StoryFlow * mParent); + void Update(float dt); + void Render(); + void ButtonPressed(int,int); +}; + + +class Rules; +class StoryDuel:public StoryPage { +public: + string pageId; + string onWin, onLose; + GameObserver * game; + Rules * rules; + StoryDuel(TiXmlElement* el,StoryFlow * mParent); + virtual ~StoryDuel(); + void Update(float dt); + void Render(); + void init(); +}; + +class StoryFlow{ +private: + mappages; + bool parse(string filename); + StoryPage * loadPage(TiXmlElement* element); +public: + string currentPageId; + string folder; + StoryFlow(string folder); + ~StoryFlow(); + + bool gotoPage(string id); + void Update(float dt); + void Render(); +}; + + +#endif \ No newline at end of file diff --git a/projects/mtg/src/GameApp.cpp b/projects/mtg/src/GameApp.cpp index dc0c74b74..556746ea5 100644 --- a/projects/mtg/src/GameApp.cpp +++ b/projects/mtg/src/GameApp.cpp @@ -17,6 +17,8 @@ #include "../include/GameStateOptions.h" #include "../include/GameStateShop.h" #include "../include/GameStateAwards.h" +//Story mode not yet ready +//#include "../include/GameStateStory.h" #include "../include/DeckStats.h" #include "../include/DeckMetaData.h" #include "../include/Translate.h" @@ -184,6 +186,11 @@ void GameApp::Create() mGameStates[GAME_STATE_AWARDS] = NEW GameStateAwards(this); mGameStates[GAME_STATE_AWARDS]->Create(); + //Story mode not yet ready + //mGameStates[GAME_STATE_STORY] = NEW GameStateStory(this); + //mGameStates[GAME_STATE_STORY]->Create(); + + mGameStates[GAME_STATE_TRANSITION] = NULL; mCurrentState = NULL; diff --git a/projects/mtg/src/GameStateDuel.cpp b/projects/mtg/src/GameStateDuel.cpp index 671c8acb9..8a2ce4976 100644 --- a/projects/mtg/src/GameStateDuel.cpp +++ b/projects/mtg/src/GameStateDuel.cpp @@ -456,6 +456,7 @@ void GameStateDuel::Render() r->FillRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,ARGB(100,0,0,0)); char buffer[4096]; sprintf(buffer,_("Turn:%i").c_str(),game->turn); + mFont->SetColor(ARGB(255,255,255,255)); mFont->DrawString(buffer,SCREEN_WIDTH/2,0,JGETEXT_CENTER); } if(menu) diff --git a/projects/mtg/src/GameStateMenu.cpp b/projects/mtg/src/GameStateMenu.cpp index 7fb782665..602f65cf2 100644 --- a/projects/mtg/src/GameStateMenu.cpp +++ b/projects/mtg/src/GameStateMenu.cpp @@ -58,6 +58,7 @@ enum SUBMENUITEM_CLASSIC, SUBMENUITEM_RANDOM1, SUBMENUITEM_RANDOM2, + SUBMENUITEM_STORY, }; @@ -444,6 +445,8 @@ void GameStateMenu::Update(float dt) currentState = MENU_STATE_MAJOR_SUBMENU; subMenuController = NEW SimpleMenu(102, this, Constants::MENU_FONT, 150,60); if (subMenuController){ + //Story mode not yet ready + //subMenuController->Add(SUBMENUITEM_STORY,"Story"); subMenuController->Add(SUBMENUITEM_CLASSIC,"Classic"); if (options[Options::MOMIR_MODE_UNLOCKED].number) subMenuController->Add(SUBMENUITEM_MOMIR, "Momir Basic"); @@ -454,7 +457,10 @@ void GameStateMenu::Update(float dt) subMenuController->Add(SUBMENUITEM_CANCEL, "Cancel"); } }else{ - mParent->DoTransition(TRANSITION_FADE,GAME_STATE_DUEL); + if (mParent->gameType == GAME_TYPE_STORY) + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_STORY); + else + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_DUEL); currentState = MENU_STATE_MAJOR_MAINMENU; } } @@ -669,6 +675,14 @@ JLBFont * mFont = resources.GetJLBFont(Constants::MENU_FONT); subMenuController->Close(); currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; break; + + case SUBMENUITEM_STORY: + this->hasChosenGameType = 1; + mParent->gameType = GAME_TYPE_STORY; + subMenuController->Close(); + currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; + break; + #ifdef TESTSUITE case SUBMENUITEM_TESTSUITE: mParent->players[0] = PLAYER_TYPE_TESTSUITE; diff --git a/projects/mtg/src/GameStateStory.cpp b/projects/mtg/src/GameStateStory.cpp new file mode 100644 index 000000000..c167d9ad9 --- /dev/null +++ b/projects/mtg/src/GameStateStory.cpp @@ -0,0 +1,111 @@ +#include "../include/GameStateStory.h" +#include "../include/config.h" +#include "../include/StoryFlow.h" +#include "../include/SimpleMenu.h" +#include "../include/GameApp.h" +#include +#include + +GameStateStory::GameStateStory(GameApp* parent): GameState(parent) { + flow = NULL; + menu = NULL; +} + +GameStateStory::~GameStateStory() { + End(); +} + + +void GameStateStory::loadStoriesMenu(const char * root){ + SAFE_DELETE(menu); + stories.clear(); + DIR *mDip; + struct dirent *mDit; + + mDip = opendir(root); + + while ((mDit = readdir(mDip))){ + char buffer[4096]; + sprintf(buffer, "%s%s/story.xml", root, mDit->d_name); + std::ifstream file(buffer); + if(file){ + string fname = mDit->d_name; + stories.push_back(fname); + file.close(); + } + } + closedir(mDip); + + switch(stories.size()){ + case 0: + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); + break; + case 1: + flow = NEW StoryFlow(stories[0]); + break; + default: + menu = NEW SimpleMenu(103, this, Constants::MENU_FONT, 150,60); + for (size_t i = 0; i < stories.size(); ++i){ + menu->Add(i, stories[i].c_str()); + } + menu->Add(-1,"Cancel"); + } +} + +void GameStateStory::Start() { + flow = NULL; + menu = NULL; + loadStoriesMenu( RESPATH"/campaigns/"); +} + +void GameStateStory::Update(float dt) { + if (!menu && mEngine->GetButtonClick(JGE_BTN_MENU)){ + menu = NEW SimpleMenu(100, this, Constants::MENU_FONT, SCREEN_WIDTH/2-100, 25); + menu->Add(0,"Back to main menu"); + menu->Add(-1, "Cancel"); + } + if (menu) { + menu->Update(dt); + if (menu->closed) + SAFE_DELETE(menu); + //return; + } + if (flow){ + flow->Update(dt); + if (flow->currentPageId == "End") { + if (mEngine->GetButtonClick(JGE_BTN_OK) || mEngine->GetButtonClick(JGE_BTN_SEC)){ + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); + } + } + } +} + +void GameStateStory::Render() { + if (flow) flow->Render(); + if (menu) menu->Render(); +} + +void GameStateStory::End() { + SAFE_DELETE(flow); + SAFE_DELETE(menu); +} + +void GameStateStory::ButtonPressed(int controllerId, int controlId) { + menu->Close(); + + switch (controllerId){ + case 100: + if (controlId == -1){ + }else { + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); + } + break; + default: + if (controlId == -1){ + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); + }else { + flow = NEW StoryFlow(stories[controlId]); + } + } + +} \ No newline at end of file diff --git a/projects/mtg/src/Player.cpp b/projects/mtg/src/Player.cpp index 3733a9ed7..403db4fb1 100644 --- a/projects/mtg/src/Player.cpp +++ b/projects/mtg/src/Player.cpp @@ -65,6 +65,8 @@ HumanPlayer::HumanPlayer(MTGPlayerCards * deck, string file, string fileSmall) : mAvatar = NULL; } + + ManaPool * Player::getManaPool(){ return manaPool; } diff --git a/projects/mtg/src/Rules.cpp b/projects/mtg/src/Rules.cpp index 94233e65a..fb5f63b2c 100644 --- a/projects/mtg/src/Rules.cpp +++ b/projects/mtg/src/Rules.cpp @@ -246,6 +246,8 @@ void Rules::initPlayers(){ MTGDeck * deck = buildDeck(i); if (deck) { p->game->initDeck(deck); + SAFE_DELETE(deck); + p->game->setOwner(p); } } } @@ -321,7 +323,11 @@ Rules::Rules(string filename){ int Rules::load(string _filename){ char filename[4096]; - sprintf(filename, RESPATH"/rules/%s", _filename.c_str()); + if (fileExists(_filename.c_str())){ + sprintf(filename, "%s", _filename.c_str()); + }else{ + sprintf(filename, RESPATH"/rules/%s", _filename.c_str()); + } std::ifstream file(filename); std::string s; diff --git a/projects/mtg/src/StoryFlow.cpp b/projects/mtg/src/StoryFlow.cpp new file mode 100644 index 000000000..e8b67f715 --- /dev/null +++ b/projects/mtg/src/StoryFlow.cpp @@ -0,0 +1,270 @@ +#include "../include/StoryFlow.h" +#include "../include/MTGDefinitions.h" +#include "../include/config.h" +#include "../include/WResourceManager.h" +#include "../include/AIPlayer.h" +#include "../include/Rules.h" +#include +#include +#include + +StoryPage::StoryPage(StoryFlow * mParent):mParent(mParent){ +} + +StoryFlow::StoryFlow(string folder): folder(folder){ + string path = "campaigns/"; + path.append(folder).append("/story.xml"); + parse(path); +} + + + +void StoryChoice::Render() +{ + JLBFont * mFont = resources.GetJLBFont(Constants::MAIN_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, JGETEXT_CENTER); +} + +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(u32 key) +{ + 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, bool hasFocus):JGuiObject(JGOid),pageId(pageId),text(text),mX(mX),mY(mY),mHasFocus(hasFocus){ + mScale = 1.0f; + mTargetScale = 1.0f; + if(hasFocus) mTargetScale = 1.2f; +} + +//Actually loads "game" +void StoryDuel::init(){ + Player * players[2]; + + 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); + MTGDeck * tempDeck = NEW MTGDeck(deckFile, GameApp::collection); + sprintf(deckFileSmall, "campaign_%s", mParent->folder.c_str()); + players[0] = NEW HumanPlayer(NEW MTGPlayerCards(tempDeck),deckFile,deckFileSmall); + SAFE_DELETE(tempDeck); + + sprintf(deckFile,"%s/ennemy_deck.txt", folder); + tempDeck = NEW MTGDeck(deckFile, GameApp::collection); + sprintf(deckFileSmall, "campaign_ennemy_%s_%s", mParent->folder.c_str(), pageId.c_str()); + players[1] = NEW AIPlayerBaka(NEW MTGPlayerCards(tempDeck),deckFile,deckFileSmall,"baka.jpg"); + SAFE_DELETE(tempDeck); + + string rulesFile = folder; + rulesFile.append("/rules.txt"); + rules = NEW Rules(rulesFile); + + GameObserver::Init(players, 2); + game = GameObserver::GetInstance(); + game->startGame(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) { + if (strcmp(element->Value(), "onwin")==0) { + const char* textC = element->GetText(); + onWin = textC; + } + else if (strcmp(element->Value(), "onlose")==0) { + const char* textC = element->GetText(); + onLose = textC; + }else { + //Error + } + } + } +} + +StoryDuel::~StoryDuel(){ + SAFE_DELETE(rules); + if(game)GameObserver::EndInstance(); + game=NULL; +} + +void StoryDuel::Update(float dt){ + if (!game) init(); + game->Update(dt); + if (game->gameOver){ + if (game->gameOver == game->players[1]) mParent->gotoPage(onWin); + else mParent->gotoPage(onLose); + GameObserver::EndInstance(); + game=NULL; + } +} + +void StoryDuel::Render(){ + if(!game) return; + game->Render(); +} + + +StoryDialog::StoryDialog(TiXmlElement* root, StoryFlow * mParent):JGuiListener(), JGuiController(1,NULL), StoryPage(mParent) { + + for (TiXmlNode* node = root->FirstChild(); node; node = node->NextSibling()) { + TiXmlElement* element = node->ToElement(); + if (element) { + if (strcmp(element->Value(), "text")==0) { + const char* textC = element->GetText(); + text = textC; + } + else if (strcmp(element->Value(), "answer")==0){ + string id = element->Attribute("goto"); + const char* answerC = element->GetText(); + string answer = answerC; + int i = mObjects.size(); + StoryChoice * sc = NEW StoryChoice(id,answer,i,SCREEN_WIDTH/2, SCREEN_HEIGHT-20 - i *20 , (i==0)); + Add(sc); + }else { + //Error + } + } + } + this->mListener = this; + +} + +void StoryDialog::Update(float dt){ + JGuiController::Update(dt); +} + + +void StoryDialog::Render() { + JLBFont * mFont = resources.GetJLBFont(Constants::MAIN_FONT); + mFont->SetColor(ARGB(255,255,255,255)); + mFont->SetScale(1.0); + mFont->DrawString(text.c_str(), 0, 0); + JGuiController::Render(); +} + +void StoryDialog::ButtonPressed(int controllerid,int controlid) { + mParent->gotoPage(((StoryChoice *)mObjects[controlid])->pageId); +} + +StoryPage * StoryFlow::loadPage(TiXmlElement* element){ + TiXmlNode* typeNode = element->FirstChild("type"); + if (!typeNode) return NULL; + StoryPage * result = NULL; + const char* type = typeNode->ToElement()->GetText(); + if (strcmp(type, "duel")==0){ + result = NEW StoryDuel(element,this); + }else{ + result = NEW StoryDialog(element,this); + } + return result; + +} +// +bool StoryFlow::gotoPage(string id){ + currentPageId = id; + return true; +} + +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 (strcmp(element->Value(), "page")==0) { + string id = element->Attribute("id"); + StoryPage * sp = loadPage(element); + pages[id] = sp; + if (!currentPageId.size()) gotoPage(id); + } + else { + //Error + } + } + } + + 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(); +} \ No newline at end of file diff --git a/projects/mtg/template.vcproj b/projects/mtg/template.vcproj index a9d204eb4..c7d3ca745 100644 --- a/projects/mtg/template.vcproj +++ b/projects/mtg/template.vcproj @@ -608,6 +608,10 @@ RelativePath=".\src\GameStateShop.cpp" > + + @@ -760,6 +764,10 @@ RelativePath=".\src\SimplePad.cpp" > + + @@ -792,6 +800,10 @@ RelativePath=".\src\Translate.cpp" > + + @@ -981,6 +993,10 @@ RelativePath=".\include\GameStateShop.h" > + + @@ -1137,6 +1153,10 @@ RelativePath=".\include\SimplePad.h" > + + @@ -1173,6 +1193,10 @@ RelativePath=".\include\Translate.h" > + +