diff --git a/projects/mtg/Makefile b/projects/mtg/Makefile index 2428bd5a8..7d3952501 100644 --- a/projects/mtg/Makefile +++ b/projects/mtg/Makefile @@ -1,4 +1,4 @@ -OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/Blocker.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardSelector.o objs/ConstraintResolver.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckStats.o objs/DuelLayers.o objs/Effects.o objs/ExtraCost.o objs/GameApp.o objs/GameLauncher.o objs/GameObserver.o objs/GameOptions.o objs/GameState.o objs/GameStateAwards.o objs/GameStateDeckViewer.o objs/GameStateDuel.o objs/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.o objs/GuiAvatars.o objs/GuiBackground.o objs/GuiCardsController.o objs/GuiCombat.o objs/GuiFrame.o objs/GuiHand.o objs/GuiLayers.o objs/GuiMana.o objs/GuiPhaseBar.o objs/GuiPlay.o objs/GuiStatic.o objs/Logger.o objs/ManaCost.o objs/ManaCostHybrid.o objs/MenuItem.o objs/MTGAbility.o objs/MTGCardInstance.o objs/MTGCard.o objs/MTGDeck.o objs/MTGDefinitions.o objs/MTGGamePhase.o objs/MTGGameZones.o objs/MTGRules.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/Pos.o objs/PriceList.o objs/ReplacementEffects.o objs/Rules.o objs/ShopItem.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/SimplePad.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TextScroller.o objs/Token.o objs/Translate.o objs/Trash.o objs/utils.o objs/WEvent.o objs/WResourceManager.o objs/WCachedResource.o +OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/Blocker.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardSelector.o objs/ConstraintResolver.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckStats.o objs/DuelLayers.o objs/Effects.o objs/ExtraCost.o objs/GameApp.o objs/GameLauncher.o objs/GameObserver.o objs/GameOptions.o objs/GameState.o objs/GameStateAwards.o objs/GameStateDeckViewer.o objs/GameStateDuel.o objs/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.o objs/GuiAvatars.o objs/GuiBackground.o objs/GuiCardsController.o objs/GuiCombat.o objs/GuiFrame.o objs/GuiHand.o objs/GuiLayers.o objs/GuiMana.o objs/GuiPhaseBar.o objs/GuiPlay.o objs/GuiStatic.o objs/Logger.o objs/ManaCost.o objs/ManaCostHybrid.o objs/MenuItem.o objs/MTGAbility.o objs/MTGCardInstance.o objs/MTGCard.o objs/MTGDeck.o objs/MTGDefinitions.o objs/MTGGamePhase.o objs/MTGGameZones.o objs/MTGRules.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/Pos.o objs/PriceList.o objs/ReplacementEffects.o objs/Rules.o objs/ShopItem.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/SimplePad.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TextScroller.o objs/Token.o objs/Translate.o objs/Trash.o objs/utils.o objs/WEvent.o objs/WResourceManager.o objs/WCachedResource.o objs/Tasks.o DEPS = $(patsubst objs/%.o, deps/%.d, $(OBJS)) RESULT = $(shell psp-config --psp-prefix 2> Makefile.cache) diff --git a/projects/mtg/include/AIPlayer.h b/projects/mtg/include/AIPlayer.h index 2c7a1b34f..186e47e9e 100644 --- a/projects/mtg/include/AIPlayer.h +++ b/projects/mtg/include/AIPlayer.h @@ -94,6 +94,7 @@ class AIPlayerBaka: public AIPlayer{ float timer; MTGCardInstance * FindCardToPlay(ManaCost * potentialMana, const char * type); public: + int deckId; AIPlayerBaka(MTGPlayerCards * deck, string deckFile, string deckfileSmall, string avatarFile); virtual int Act(float dt); void initTimer(); diff --git a/projects/mtg/include/Credits.h b/projects/mtg/include/Credits.h index cc947c9f2..83b73153c 100644 --- a/projects/mtg/include/Credits.h +++ b/projects/mtg/include/Credits.h @@ -6,7 +6,9 @@ #include #include #include +#include #include "../include/Player.h" + class GameApp; using namespace std; @@ -22,6 +24,7 @@ public: class Credits{ private: + time_t gameLength; int isDifficultyUnlocked(); int isMomirUnlocked(); int isEvilTwinUnlocked(); diff --git a/projects/mtg/include/GameObserver.h b/projects/mtg/include/GameObserver.h index 0e1ac978e..f73ce8205 100644 --- a/projects/mtg/include/GameObserver.h +++ b/projects/mtg/include/GameObserver.h @@ -11,6 +11,7 @@ #include "ReplacementEffects.h" #include "GuiStatic.h" #include +#include class MTGGamePhase; class MTGAbility; @@ -46,6 +47,7 @@ class GameObserver{ ReplacementEffects *replacementEffects; Player * gameOver; Player * players[2]; //created outside + time_t startedAt; TargetChooser * getCurrentTargetChooser(); void stackObjectClicked(Interruptible * action); diff --git a/projects/mtg/include/GameOptions.h b/projects/mtg/include/GameOptions.h index 33c157212..ded131ff4 100644 --- a/projects/mtg/include/GameOptions.h +++ b/projects/mtg/include/GameOptions.h @@ -14,6 +14,7 @@ using std::string; #define PLAYER_SAVEFILE "data.dat" #define PLAYER_SETTINGS "options.txt" #define PLAYER_COLLECTION "collection.dat" +#define PLAYER_TASKS "tasks.dat" #define SECRET_PROFILE "Maxglee" #define INVALID_OPTION -1 diff --git a/projects/mtg/include/GameStateMenu.h b/projects/mtg/include/GameStateMenu.h index 2b6e145e8..12caf4460 100644 --- a/projects/mtg/include/GameStateMenu.h +++ b/projects/mtg/include/GameStateMenu.h @@ -44,6 +44,7 @@ class GameStateMenu: public GameState, public JGuiListener string getLang(string s); void loadLangMenu(); bool langChoices; + void runTest(); //!! public: GameStateMenu(GameApp* parent); virtual ~GameStateMenu(); diff --git a/projects/mtg/include/GameStateShop.h b/projects/mtg/include/GameStateShop.h index 6222e1a55..b1687d755 100644 --- a/projects/mtg/include/GameStateShop.h +++ b/projects/mtg/include/GameStateShop.h @@ -5,13 +5,14 @@ #include "../include/GameState.h" #include "../include/SimpleMenu.h" #include "../include/ShopItem.h" +#include "../include/Tasks.h" #define STATE_BUY 1 #define STATE_SELL 2 #define STAGE_SHOP_MENU 3 #define STAGE_SHOP_SHOP 4 - +#define STAGE_SHOP_TASKS 5 class GameStateShop: public GameState, public JGuiListener @@ -23,6 +24,7 @@ class GameStateShop: public GameState, public JGuiListener JQuad * mBack; JQuad * mBg; JTexture * mBgTex; + TaskList * taskList; SimpleMenu * menu; int mStage; diff --git a/projects/mtg/include/PlayerData.h b/projects/mtg/include/PlayerData.h index 7f484cd8d..6721fc3a8 100644 --- a/projects/mtg/include/PlayerData.h +++ b/projects/mtg/include/PlayerData.h @@ -2,12 +2,14 @@ #define _PLAYER_DATA_H_ #include "../include/MTGDeck.h" +#include "../include/Tasks.h" class PlayerData{ protected: public: int credits; MTGDeck * collection; + TaskList * taskList; PlayerData(MTGAllCards * allcards); ~PlayerData(); int save(); diff --git a/projects/mtg/include/Tasks.h b/projects/mtg/include/Tasks.h new file mode 100644 index 000000000..f9026bd68 --- /dev/null +++ b/projects/mtg/include/Tasks.h @@ -0,0 +1,178 @@ +#ifndef TASK_H +#define TASK_H + +#include + +// Task type constant + +#define TASK_BASIC 'B' + +#define TASK_WIN_AGAINST 'W' +#define TASK_SLAUGHTER 'S' +#define TASK_DELAY 'D' +#define TASK_IMMORTAL 'I' +#define TASK_MASSIVE_BURIAL 'M' +#define TASKS_ALL "WSDIM" + +#define ITEM_SEPARATOR "|" + +#define COMMON_ATTRIBS_COUNT 7 + +class Task { +protected: + int reward; // TODO: Complex rewards. Be consistent with other planned modes with rewards. + int opponent; + bool accepted; + char type; + int expiresIn; + string description; + string opponentName; + vector persistentAttribs; // persistentAttributes + + void storeCommonAttribs(); + int restoreCommonAttribs(); + string getOpponentName(); + virtual void storeCustomAttribs(); + virtual void restoreCustomAttribs(); + virtual void randomize(); + + virtual int computeReward() = 0; + +public: + // variable to store and method to obtain names of AI decks + //!! Todo: This should _really_ be handled elsewhere (dedicated class?) + static vector AIDeckNames; + static void loadAIDeckNames(); + static int getAIDeckCount(); + static string getAIDeckName(int id); + // End of AI deck buffering code + + Task(char _type = ' '); + + static Task* createFromStr(string params, bool rand = FALSE); + virtual string toString(); + string getDesc(); + virtual string createDesc() = 0; + virtual string getShortDesc() = 0; + int getExpiration(); + int getReward(); + virtual bool isDone(Player * _p1, Player * _p2, GameApp * _app) = 0; + bool isExpired(); + void setExpiration(int _expiresIn); + void passOneDay(); +}; + +class TaskList { +protected: + string fileName; + +public: + vector tasks; + + TaskList(string _fileName = ""); + int load(string _fileName = ""); + int save(string _fileName = ""); + void addTask(string params, bool rand = FALSE); + void addTask(Task *task); + void addRandomTask(int diff = 100); + void removeTask(Task *task); + void passOneDay(); + void getDoneTasks(Player * _p1, Player * _p2, GameApp * _app, vector* result); + int getTaskCount(); + + //!!virtual void Update(float dt); + virtual void Render(); + //!!virtual void ButtonPressed(int controllerId, int controlId); + + ~TaskList(); +}; + +class TaskWinAgainst : public Task { +protected: + virtual int computeReward(); +public: + TaskWinAgainst(int _opponent = 0); + virtual string createDesc(); + virtual string getShortDesc(); + virtual bool isDone(Player * _p1, Player * _p2, GameApp * _app); +}; + +class TaskSlaughter : public TaskWinAgainst { +protected: + int targetLife; + virtual int computeReward(); +public: + TaskSlaughter(int _opponent = 0, int _targetLife = -15); + virtual string createDesc(); + virtual string getShortDesc(); + virtual bool isDone(Player * _p1, Player * _p2, GameApp * _app); + virtual void storeCustomAttribs(); + virtual void restoreCustomAttribs(); + virtual void randomize(); +}; + +class TaskDelay : public TaskWinAgainst { +protected: + int turn; + bool afterTurn; + virtual int computeReward(); +public: + TaskDelay(int _opponent = 0, int _turn = 20); + virtual string createDesc(); + virtual string getShortDesc(); + virtual bool isDone(Player * _p1, Player * _p2, GameApp * _app); + virtual void storeCustomAttribs(); + virtual void restoreCustomAttribs(); + virtual void randomize(); +}; + +class TaskImmortal : public Task { +protected: + int targetLife; + int level; + virtual int computeReward(); +public: + TaskImmortal(int _targetLife = 20); + + virtual string createDesc(); + virtual string getShortDesc(); + virtual bool isDone(Player * _p1, Player * _p2, GameApp * _app); + virtual void storeCustomAttribs(); + virtual void restoreCustomAttribs(); + virtual void randomize(); +}; + +class TaskMassiveBurial : public Task { +protected: + int color; + int bodyCount; + virtual int computeReward(); +public: + TaskMassiveBurial(int _color = 0, int _bodyCount = 0); + + virtual string createDesc(); + virtual string getShortDesc(); + virtual bool isDone(Player * _p1, Player * _p2, GameApp * _app); + virtual void storeCustomAttribs(); + virtual void restoreCustomAttribs(); + virtual void randomize(); +}; + +/* ------------ Task template ------------ + +class TaskXX : public Task { +protected: + virtual int computeReward(); +public: + TaskXX(); + + virtual string createDesc(); + virtual string getShortDesc(); + virtual bool isDone(Player * _p1, Player * _p2, GameApp * _app); + virtual void storeCustomAttribs(); + virtual void restoreCustomAttribs(); + virtual void randomize(); +}; +*/ + +#endif diff --git a/projects/mtg/src/AIPlayer.cpp b/projects/mtg/src/AIPlayer.cpp index 647e20dc2..8de3123e3 100644 --- a/projects/mtg/src/AIPlayer.cpp +++ b/projects/mtg/src/AIPlayer.cpp @@ -592,11 +592,12 @@ AIPlayer * AIPlayerFactory::createAIPlayer(MTGAllCards * collection, Player * op sprintf(avatarFile, "avatar%i.jpg",deckid); sprintf(deckFileSmall, "ai_baka_deck%i",deckid); } - + MTGDeck * tempDeck = NEW MTGDeck(deckFile, collection); MTGPlayerCards * deck = NEW MTGPlayerCards(tempDeck); delete tempDeck; AIPlayerBaka * baka = NEW AIPlayerBaka(deck,deckFile, deckFileSmall, avatarFile); + baka->deckId = deckid; return baka; } diff --git a/projects/mtg/src/Credits.cpp b/projects/mtg/src/Credits.cpp index 187777974..e673bc6f1 100644 --- a/projects/mtg/src/Credits.cpp +++ b/projects/mtg/src/Credits.cpp @@ -41,7 +41,9 @@ void Credits::compute(Player * _p1, Player * _p2, GameApp * _app){ showMsg = (WRand() % 5); GameObserver * g = GameObserver::GetInstance(); if (!g->turn) return; + PlayerData * playerdata = NEW PlayerData(app->collection); if (!p1->isAI() && p2->isAI() && p1!= g->gameOver){ + gameLength = time(0) - g->startedAt; value = 400; if (app->gameType != GAME_TYPE_CLASSIC) value = 200; int difficulty = options[Options::DIFFICULTY].number; @@ -79,6 +81,20 @@ void Credits::compute(Player * _p1, Player * _p2, GameApp * _app){ } GameOptionAward * goa = NULL; + // + vector finishedTasks; + playerdata->taskList->getDoneTasks(_p1, _p2, _app, &finishedTasks); + + char buffer[512]; + + for (vector::iterator it = finishedTasks.begin(); it!=finishedTasks.end(); it++) { + sprintf(buffer, _("Task: %s").c_str(), (*it)->getShortDesc().c_str()); + CreditBonus * b = NEW CreditBonus((*it)->getReward(), buffer); + bonus.push_back(b); + playerdata->taskList->removeTask(*it); + } + // + if (unlocked == -1){ unlocked = isDifficultyUnlocked(); if (unlocked){ @@ -132,13 +148,19 @@ void Credits::compute(Player * _p1, Player * _p2, GameApp * _app){ - PlayerData * playerdata = NEW PlayerData(app->collection); - playerdata->credits+= value; + playerdata->credits += value; + playerdata->taskList->passOneDay(); + if (playerdata->taskList->getTaskCount() < 6) { + playerdata->taskList->addRandomTask(); + playerdata->taskList->addRandomTask(); + } playerdata->save(); SAFE_DELETE(playerdata); }else{ - unlocked = 0; + unlocked = 0; + playerdata->taskList->passOneDay(); + playerdata->taskList->save(); } } @@ -182,6 +204,7 @@ void Credits::Render(){ } float y = 130; + if (showMsg == 1) y = 50; vector:: iterator it; for ( it=bonus.begin() ; it < bonus.end(); ++it){ @@ -191,6 +214,17 @@ void Credits::Render(){ f2->DrawString(buffer, 10, y); y+=15; + //!! + if (g->gameOver != p1) { + sprintf(buffer, _("Game length: %i seconds").c_str(), this->gameLength); + f->DrawString(buffer, 10, y); + y += 10; + sprintf(buffer, _("Credits per minute: %i").c_str(), (int)(60*value/this->gameLength)); + f->DrawString(buffer, 10, y); + y += 10; + showMsg = 0; + } + if (showMsg == 1){ f2->DrawString(_("Please support this project!").c_str() ,10,y+15); f->DrawString(_("Wagic is free, open source, and developed on the little free time I have").c_str() ,10,y+30); diff --git a/projects/mtg/src/GameObserver.cpp b/projects/mtg/src/GameObserver.cpp index 21a7db277..c762873d5 100644 --- a/projects/mtg/src/GameObserver.cpp +++ b/projects/mtg/src/GameObserver.cpp @@ -209,6 +209,9 @@ void GameObserver::startGame(Rules * rules){ } } + startedAt = time(0); + + //Difficult mode special stuff if (!players[0]->isAI() && players[1]->isAI()){ int difficulty = options[Options::DIFFICULTY].number; diff --git a/projects/mtg/src/GameStateDeckViewer.cpp b/projects/mtg/src/GameStateDeckViewer.cpp index a3fa1ff7f..aa821952d 100644 --- a/projects/mtg/src/GameStateDeckViewer.cpp +++ b/projects/mtg/src/GameStateDeckViewer.cpp @@ -883,7 +883,7 @@ void GameStateDeckViewer::renderOnScreenMenu(){ for (int i=0; i<=STATS_MAX_MANA_COST; i++) { sprintf(buffer, _("%i").c_str(), i); font->DrawString(buffer, 30 + leftTransition, posY); - sprintf(buffer, _("%i").c_str(), (*countPerCost)[i]); + sprintf(buffer, ((*countPerCost)[i]>0)?_("%i").c_str():".", (*countPerCost)[i]); font->DrawString(buffer, 45 + leftTransition, posY); for (int j=0; j0)?_("%i").c_str():".", (*countPerCostAndColor)[i][j]); diff --git a/projects/mtg/src/GameStateDuel.cpp b/projects/mtg/src/GameStateDuel.cpp index 11d46f824..713233d4f 100644 --- a/projects/mtg/src/GameStateDuel.cpp +++ b/projects/mtg/src/GameStateDuel.cpp @@ -370,8 +370,13 @@ void GameStateDuel::Update(float dt) case DUEL_STATE_BACK_TO_MAIN_MENU: // mParent->effect->UpdateSmall(dt); menu->Update(dt); - if (menu->closed) - mParent->SetNextState(GAME_STATE_MENU); + if (menu->closed) { + PlayerData * playerdata = NEW PlayerData(mParent->collection); + playerdata->taskList->passOneDay(); + playerdata->taskList->save(); + SAFE_DELETE(playerdata); + mParent->SetNextState(GAME_STATE_MENU); + } break; default: if (PSP_CTRL_CIRCLE == mEngine->ReadButton()){ diff --git a/projects/mtg/src/GameStateMenu.cpp b/projects/mtg/src/GameStateMenu.cpp index 8a6f08bfe..762ca7ee9 100644 --- a/projects/mtg/src/GameStateMenu.cpp +++ b/projects/mtg/src/GameStateMenu.cpp @@ -57,6 +57,7 @@ enum SUBMENUITEM_CLASSIC, SUBMENUITEM_RANDOM1, SUBMENUITEM_RANDOM2, + SUBMENUITEM_QUICK_TEST, //!! }; @@ -569,6 +570,7 @@ JLBFont * mFont = resources.GetJLBFont(Constants::MENU_FONT); #ifdef TESTSUITE subMenuController->Add(SUBMENUITEM_TESTSUITE, "Test Suite"); #endif + subMenuController->Add(SUBMENUITEM_QUICK_TEST, "Quick test"); //!! currentState = MENU_STATE_MAJOR_SUBMENU | MENU_STATE_MINOR_NONE; } break; @@ -642,11 +644,30 @@ JLBFont * mFont = resources.GetJLBFont(Constants::MENU_FONT); currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; break; #endif + case SUBMENUITEM_QUICK_TEST: //!! + subMenuController->Close(); + runTest(); + currentState = MENU_STATE_MAJOR_MAINMENU | MENU_STATE_MINOR_SUBMENU_CLOSING; + break; } break; } } +void GameStateMenu::runTest() { //!! + /*TaskList *tlist = new TaskList(); + TaskWinAgainst *t1 = new TaskWinAgainst((rand()%10)+1); + TaskWinAgainst *t2 = new TaskWinAgainst((rand()%10)+1); + t1->setExpiration((rand()%5)+1); + t2->setExpiration((rand()%5)+1); + tlist->passOneDay(); + tlist->addTask(t1); + tlist->addTask(t2); + tlist->save(); + SAFE_DELETE(tlist); + //SAFE_DELETE(t);*/ +} + ostream& GameStateMenu::toString(ostream& out) const { return out << "GameStateMenu ::: scroller : " << scroller diff --git a/projects/mtg/src/GameStateShop.cpp b/projects/mtg/src/GameStateShop.cpp index af5b07f31..62daef538 100644 --- a/projects/mtg/src/GameStateShop.cpp +++ b/projects/mtg/src/GameStateShop.cpp @@ -51,6 +51,7 @@ void GameStateShop::Start() JRenderer::GetInstance()->EnableVSync(true); shop = NULL; + taskList = NULL; load(); } @@ -127,6 +128,7 @@ void GameStateShop::End() SAFE_DELETE(shop); SAFE_DELETE(menu); + SAFE_DELETE(taskList); } void GameStateShop::Destroy(){ @@ -142,17 +144,34 @@ void GameStateShop::Update(float dt) }else{ menu = NEW SimpleMenu(11,this,Constants::MENU_FONT,SCREEN_WIDTH/2-100,20); menu->Add(12,"Save & Back to Main Menu"); + menu->Add(14,"See available tasks"); menu->Add(13, "Cancel"); - } + } }else{ if (mEngine->GetButtonClick(PSP_CTRL_START)){ mStage = STAGE_SHOP_MENU; } - if (mEngine->GetButtonClick(PSP_CTRL_SQUARE)){ - load(); + if (mStage == STAGE_SHOP_TASKS){ + if ( (mEngine->GetButtonClick(PSP_CTRL_CROSS)) || + (mEngine->GetButtonClick(PSP_CTRL_TRIANGLE)) ) { + mStage = STAGE_SHOP_SHOP; + return; + } + if ((mEngine->GetButtonClick(PSP_CTRL_SQUARE)) && (taskList)) { + taskList->passOneDay(); + if (taskList->getTaskCount() < 6) { + taskList->addRandomTask(); + taskList->addRandomTask(); + } + taskList->save(); + } + } else { + if (mEngine->GetButtonClick(PSP_CTRL_SQUARE)){ + load(); + } + if (shop) + shop->Update(dt); } - if (shop) - shop->Update(dt); } } @@ -169,7 +188,14 @@ void GameStateShop::Render() if (shop) shop->Render(); - + + if (mStage == STAGE_SHOP_TASKS) { + if (!taskList) { + taskList = NEW TaskList(); + } + taskList->Render(); + } + if (mStage == STAGE_SHOP_MENU && menu){ menu->Render(); } @@ -184,8 +210,11 @@ void GameStateShop::ButtonPressed(int controllerId, int controlId) case 11: if (controlId == 12){ if (shop) shop->saveAll(); + if (taskList) taskList->save(); mParent->SetNextState(GAME_STATE_MENU); - }else{ + } else if (controlId == 14){ + mStage = STAGE_SHOP_TASKS; + } else { mStage = STAGE_SHOP_SHOP; } break; diff --git a/projects/mtg/src/PlayerData.cpp b/projects/mtg/src/PlayerData.cpp index 5ddbcc491..02ec36bc0 100644 --- a/projects/mtg/src/PlayerData.cpp +++ b/projects/mtg/src/PlayerData.cpp @@ -22,9 +22,9 @@ PlayerData::PlayerData(MTGAllCards * allcards){ //COLLECTION collection = NEW MTGDeck(options.profileFile(PLAYER_COLLECTION).c_str(), allcards); + taskList = NEW TaskList(options.profileFile(PLAYER_TASKS).c_str()); } - int PlayerData::save(){ std::ofstream file(options.profileFile(PLAYER_SAVEFILE).c_str()); char writer[64]; @@ -34,9 +34,11 @@ int PlayerData::save(){ file.close(); } collection->save(); + taskList->save(); return 1; } PlayerData::~PlayerData(){ SAFE_DELETE(collection); + SAFE_DELETE(taskList); } diff --git a/projects/mtg/src/Tasks.cpp b/projects/mtg/src/Tasks.cpp new file mode 100644 index 000000000..3ce5c1f06 --- /dev/null +++ b/projects/mtg/src/Tasks.cpp @@ -0,0 +1,806 @@ +#include "../include/config.h" +#include "../include/GameApp.h" +#include "../include/Player.h" +#include "../include/Tasks.h" +#include "../include/AIPlayer.h" +#include "../include/Translate.h" +#include "../include/MTGDefinitions.h" +#include + +vector Task::AIDeckNames; + +/*---------------- Utils -----------------*/ +// TODO: Move to dedicated file + +//!! Copypaste from GameStateDeckViewer.cpp StringExplode. Move and #include here and there +void ExplodeStr(string str, string separator, vector* results){ + int found; + results->clear(); + found = str.find_first_of(separator); + while(found != (int)string::npos){ + if(found > 0){ + results->push_back(str.substr(0,found)); + } else { + results->push_back(" "); + } + str = str.substr(found+1); + found = str.find_first_of(separator); + } + if(str.length() > 0){ + results->push_back(str); + } +} + +string ImplodeStr(string separator, vector strs){ + string result = ""; + for (vector::iterator it = strs.begin(); it!=strs.end(); it++){ + result += (it == strs.begin()?"":separator) + (*it); + } + return result; +} + +/*---------------- Task -----------------*/ + +Task::Task(char _type) { + reward = 0; + expiresIn = 1; + accepted = FALSE; + if (_type == ' ') { + type = TASK_BASIC; + } else { + type = _type; + } +} + +int Task::getReward() { + if (reward == 0) { + reward = computeReward(); + } + + return reward; +} + +string Task::toString() { + storeCommonAttribs(); + storeCustomAttribs(); + return ImplodeStr(ITEM_SEPARATOR, persistentAttribs); +} + +// Store basic attributes to vector, for saving +void Task::storeCommonAttribs() { + char buff[256]; + + persistentAttribs.clear(); + persistentAttribs.push_back(string(1, type)); + + sprintf(buff, "%i", expiresIn); + persistentAttribs.push_back(string(buff)); + + sprintf(buff, "%i", accepted?1:0); + persistentAttribs.push_back(string(buff)); + + sprintf(buff, "%i", opponent); + persistentAttribs.push_back(string(buff)); + + sprintf(buff, "%i", reward); + persistentAttribs.push_back(string(buff)); + + persistentAttribs.push_back(getDesc()); + + persistentAttribs.push_back(getOpponentName()); +} + +int Task::restoreCommonAttribs() { + if (persistentAttribs.size() < COMMON_ATTRIBS_COUNT) { + #if defined (WIN32) || defined (LINUX) + OutputDebugString("\nTasks.cpp::restoreCommonAttribs: Not enough attributes loaded\n"); + #endif + + return -1; + } + expiresIn = atoi(persistentAttribs[1].c_str()); + accepted = (persistentAttribs[2].compare("1") == 0); + opponent = atoi(persistentAttribs[3].c_str()); + reward = atoi(persistentAttribs[4].c_str()); + description = persistentAttribs[5]; + opponentName = persistentAttribs[6]; + return 1; +} + +void Task::storeCustomAttribs() { + // To be extended in child class +} + +void Task::restoreCustomAttribs() { + // To be extended in child class +} + +string Task::getOpponentName() { + if (opponentName == "") { + opponentName = Task::getAIDeckName(opponent); + } + + return opponentName; +} + +string Task::getDesc() { + return (description == "") ? (description = createDesc()) : description; +} + +void Task::randomize() { + opponent = rand() % getAIDeckCount() + 1; + opponentName = ""; + setExpiration((rand()%3)+1); +} + +bool Task::isExpired() { + return (expiresIn <= 0); +} + +int Task::getExpiration() { + return expiresIn; +} + +void Task::setExpiration(int _expiresIn) { + expiresIn = _expiresIn; +} + +void Task::passOneDay() { + expiresIn--; + reward = (int) reward * 0.9; // Todo: degradation and minreward constants + if (reward < 33) { + reward = 33; + } +} + + +// AI deck buffering code + +void Task::loadAIDeckNames() { + int found = 1; + int nbDecks = 0; + while (found){ + found = 0; + char buffer[512]; + char smallDeckName[512]; + char deckDesc[512]; + sprintf(buffer, "%s/deck%i.txt",RESPATH"/ai/baka",nbDecks + 1); + + if(fileExists(buffer)){ + found = 1; + nbDecks++; + // TODO: Creating MTGDeck only for getting decks name. Find an easier way. + MTGDeck * mtgd = NEW MTGDeck(buffer,NULL,1); + AIDeckNames.push_back(mtgd->meta_name); + delete mtgd; + } + } +} + +int Task::getAIDeckCount() { + if (AIDeckNames.size() == 0) { + loadAIDeckNames(); + } + return AIDeckNames.size(); +} + +string Task::getAIDeckName(int id) { + if (AIDeckNames.size() == 0) { + loadAIDeckNames(); + } + return ((unsigned int)id <= AIDeckNames.size()) ? AIDeckNames.at(id-1) : ""; +} + +// End of AI deck buffering code + +// Each child class has to be added to the switch in this function (clumsy..) +Task* Task::createFromStr(string params, bool rand) { + vector exploded; + Task *result; + + ExplodeStr(params, ITEM_SEPARATOR, &exploded); + + switch (exploded[0][0]) { + case TASK_WIN_AGAINST: + result = new TaskWinAgainst(); + break; + case TASK_SLAUGHTER: + result = new TaskSlaughter(); + break; + case TASK_DELAY: + result = new TaskDelay(); + break; + case TASK_IMMORTAL: + result = new TaskImmortal(); + break; + case TASK_MASSIVE_BURIAL: + result = new TaskMassiveBurial(); + break; + default: + #if defined (WIN32) || defined (LINUX) + OutputDebugString("\nTasks.cpp::createFromStr: Undefined class type\n"); + #endif + result = new TaskWinAgainst(); + } + + if (!result) { + return NULL; + } + + result->persistentAttribs = exploded; + + if (exploded.size() >= COMMON_ATTRIBS_COUNT) { + result->restoreCommonAttribs(); + if (exploded.size() > COMMON_ATTRIBS_COUNT) { + result->restoreCustomAttribs(); + } + } else if (rand) { + result->randomize(); + } + + return result; +} + +/*---------------- TaskList -----------------*/ + +TaskList::TaskList(string _fileName) { + fileName = _fileName; + if (fileName == "") { + fileName = options.profileFile(PLAYER_TASKS).c_str(); + } + load(fileName); +} + +int TaskList::save(string _fileName) { + if (_fileName != "") { + fileName = _fileName; + } + if (fileName == "") { + return -1; + } + + std::ofstream file(fileName.c_str()); + if (file){ + #if defined (WIN32) || defined (LINUX) + OutputDebugString("\nsaving\n"); + #endif + + file << "# Format: |||||[|Additional attributes]\n"; + + for (vector::iterator it = tasks.begin(); it!=tasks.end(); it++){ + file << (*it)->toString() << "\n"; + } + file.close(); + } + + return 1; +} + +int TaskList::load(string _fileName) { + Task *task; + if (_fileName != "") { + fileName = _fileName; + } + if (fileName == "") { + return -1; + } + + std::ifstream file(fileName.c_str()); + std::string s; + + if (file) { + while(std::getline(file,s)) { + if (!s.size()) continue; + if (s[s.size()-1] == '\r') s.erase(s.size()-1); //Handle DOS files + if (s[0] == '#') { + continue; + } + + task = Task::createFromStr(s); + if (task) { + this->addTask(task); + } else { + #if defined (WIN32) || defined (LINUX) + OutputDebugString("\nTaskList::load: error creating task\n"); + #endif + } + } + file.close(); + } else { + return -1; + } + + return 1; +} + +void TaskList::addTask(Task *task) { + tasks.push_back(task); +} + +void TaskList::addTask(string params, bool rand) { + addTask(Task::createFromStr(params, rand)); +} + +void TaskList::removeTask(Task *task) { + vector::iterator it; + + it = find(tasks.begin(), tasks.end(), task); + + if (it != tasks.end()) { + SAFE_DELETE(*it); + tasks.erase(it); + } else { + // TODO: task not found handling. + } +} + +void TaskList::passOneDay() { + // TODO: "You have failed the task" message to the user when accepted task expires + for (vector::iterator it = tasks.begin(); it!=tasks.end(); ){ + (*it)->passOneDay(); + if ((*it)->isExpired()) { + SAFE_DELETE(*it); + it = tasks.erase(it); + } else { + it++; + } + } +} + +void TaskList::getDoneTasks(Player * _p1, Player * _p2, GameApp * _app, vector* result) { + result->clear(); + // TODO: Return only accepted tasks + for (vector::iterator it = tasks.begin(); it!=tasks.end(); it++) { + if ((*it)->isDone(_p1, _p2, _app)) { + result->push_back(*it); + } + } +} + +int TaskList::getTaskCount() { + return tasks.size(); +} + +void TaskList::Render() { + JRenderer * r = JRenderer::GetInstance(); + JLBFont * f = resources.GetJLBFont(Constants::MAIN_FONT); + JLBFont * f2 = resources.GetJLBFont(Constants::MAGIC_FONT); + JLBFont * f3 = resources.GetJLBFont(Constants::MENU_FONT); //OPTION_FONT + f2->SetColor(ARGB(255, 205, 237, 240)); + f3->SetColor(ARGB(255, 219, 206, 151)); + + r->FillRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,ARGB(128,0,0,0)); + r->FillRect(10,10,SCREEN_WIDTH-10,SCREEN_HEIGHT-10,ARGB(128,0,0,0)); + + float posX = 20, posY = 20; + char buffer[300]; + string title = _("Task Board"); + + f3->DrawString(title.c_str(), (SCREEN_WIDTH-20)/2 - title.length()*4, posY); + posY += 20; + + if (0 == tasks.size()) { + f->DrawString(_("These are no tasks that need to be done. Come again tomorrow.").c_str(), posX, posY); + posY += 20; + return; + } + + for (vector::iterator it = tasks.begin(); it!=tasks.end(); it++) { + sprintf(buffer, "%s", (*it)->getShortDesc().c_str()); + f2->DrawString(buffer, posX, posY); + sprintf(buffer, "Days left: %i", (*it)->getExpiration()); + f->DrawString(buffer, SCREEN_WIDTH - 180, posY); + sprintf(buffer, "Reward: %i", (*it)->getReward()); + f->DrawString(buffer, SCREEN_WIDTH - 90, posY); + posY += 15; + + sprintf(buffer, "%s", (*it)->getDesc().c_str()); + f->DrawString(buffer, posX+10, posY); + posY += 15; + //r->DrawLine((SCREEN_WIDTH)/2 - 200, posY, (SCREEN_WIDTH)/2 + 200, posY, ARGB(128, 255, 255, 255)); + } +} + +void TaskList::addRandomTask(int diff) { + // TODO: Weighted random (rarity of tasks) + // - based on counts of finished tasks? + // Winning a task several times may slightly lessen the probability of it being generated + string s (TASKS_ALL); + char taskType[2]; + sprintf(taskType, "%c", s[rand()%s.length()]); + addTask(string(taskType), TRUE); +} + + +TaskList::~TaskList() { + for (unsigned int i=0; iisAI()) && (_p2->isAI()) && (g->gameOver != _p1) // Human player wins + && (baka->deckId == opponent) + ); +} + +/*----------- TaskSlaughter -------------*/ + +TaskSlaughter::TaskSlaughter(int _opponent, int _targetLife) : TaskWinAgainst(_opponent) { + type = TASK_SLAUGHTER; + targetLife = _targetLife; +} + +int TaskSlaughter::computeReward() { + return 2*TaskWinAgainst::computeReward() - targetLife*9; +} + +void TaskSlaughter::randomize() { + Task::randomize(); + targetLife = -15 - rand()%10; +} + +string TaskSlaughter::createDesc() { + char buffer[4096]; + switch (rand()%2) { + case 0: + sprintf(buffer, _("Defeat %s in a way it won't forget. Bring it to %i life.").c_str(), getOpponentName().c_str(), targetLife); + break; + case 1: + sprintf(buffer, _("Slaughter %s! Beat it to %i life or less.").c_str(), getOpponentName().c_str(), targetLife); + break; + } + return buffer; +} + +string TaskSlaughter::getShortDesc(){ + char buffer[4096]; + sprintf(buffer, _("Slaughter %s (%i lives)").c_str(), getOpponentName().c_str(), targetLife); + return buffer; +} + +bool TaskSlaughter::isDone(Player * _p1, Player * _p2, GameApp * _app) { + return TaskWinAgainst::isDone(_p1, _p2, _app) + && (_p2->life <= targetLife); +} + +void TaskSlaughter::storeCustomAttribs() { + char buff[256]; + + sprintf(buff, "%i", targetLife); + persistentAttribs.push_back(buff); +} + +void TaskSlaughter::restoreCustomAttribs() { + targetLife = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT].c_str()); +} + +/*----------- TaskDelay -------------*/ +// Now serves as both 'Delay' and 'Fast defeat' task. + +TaskDelay::TaskDelay(int _opponent, int _turn) : TaskWinAgainst(_opponent) { + type = TASK_DELAY; + turn = _turn; + afterTurn = TRUE; +} + +int TaskDelay::computeReward() { + return TaskWinAgainst::computeReward() + (afterTurn ? turn*30 : (17-turn)*(17-turn)*24); +} + +void TaskDelay::randomize() { + Task::randomize(); + afterTurn = rand()%2; + turn = afterTurn ? rand()%15 + 20 : 15 - rand()%7; +} + +string TaskDelay::createDesc() { + char buffer[4096]; + if (afterTurn) { + switch (rand()%2) { + case 0: + sprintf(buffer, _("Defeat %s after keeping it occupied for %i turns.").c_str(), getOpponentName().c_str(), turn); + break; + case 1: + sprintf(buffer, _("Defeat %s, but play with it for %i turns first.").c_str(), getOpponentName().c_str(), turn); + break; + } + } else { + switch (rand()%2) { + case 0: + sprintf(buffer, _("Defeat %s and make sure it doesn't take more than %i turns.").c_str(), getOpponentName().c_str(), turn); + break; + case 1: + sprintf(buffer, _("Defeat %s, in a duel no longer than %i turns.").c_str(), getOpponentName().c_str(), turn); + break; + } + } + return buffer; +} + +string TaskDelay::getShortDesc(){ + char buffer[4096]; + if (afterTurn) { + sprintf(buffer, _("Delay %s for %i turns").c_str(), getOpponentName().c_str(), turn); + } else { + sprintf(buffer, _("Defeat %s before turn %i").c_str(), getOpponentName().c_str(), turn + 1); + } + return buffer; +} + +bool TaskDelay::isDone(Player * _p1, Player * _p2, GameApp * _app) { + GameObserver * g = GameObserver::GetInstance(); + return TaskWinAgainst::isDone(_p1, _p2, _app) + && (afterTurn ? (g->turn >= turn) : (g->turn <= turn)); +} + +void TaskDelay::storeCustomAttribs() { + char buff[256]; + + sprintf(buff, "%i", turn); + persistentAttribs.push_back(buff); + sprintf(buff, "%i", afterTurn); + persistentAttribs.push_back(buff); +} + +void TaskDelay::restoreCustomAttribs() { + turn = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT].c_str()); + if (persistentAttribs.size() > COMMON_ATTRIBS_COUNT + 1) { + afterTurn = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT+1].c_str()); + } +} + +/* ------------ TaskImmortal ------------ */ + +TaskImmortal::TaskImmortal(int _targetLife) : Task(TASK_IMMORTAL) { + targetLife = _targetLife; + level = (targetLife < 100) ? 0 : ((targetLife < 1000) ? 1 : 2); +} + +int TaskImmortal::computeReward() { + return targetLife*2 + 150 + rand()%50; +} + +string TaskImmortal::createDesc() { + char buffer[4096]; + + sprintf(buffer, _("Defeat any opponent, having at least %i lives in the end.").c_str(), targetLife); + + return buffer; +} + +string TaskImmortal::getShortDesc(){ + char buffer[4096]; + + switch (level) { + case 0: + sprintf(buffer, _("Win flawlessly (%i lives)").c_str(), targetLife); + break; + case 1: + sprintf(buffer, _("Reach Invulnerability (%i lives)").c_str(), targetLife); + break; + case 2: + sprintf(buffer, _("Reach Immortality! (%i lives)").c_str(), targetLife); + break; + } + + return buffer; +} + +bool TaskImmortal::isDone(Player * _p1, Player * _p2, GameApp * _app) { + GameObserver * g = GameObserver::GetInstance(); + return (!_p1->isAI()) && (_p2->isAI()) && (g->gameOver != _p1) // Human player wins + && (_p1->life >= targetLife); +} + +void TaskImmortal::storeCustomAttribs() { + char buff[256]; + + sprintf(buff, "%i", targetLife); + persistentAttribs.push_back(buff); + + sprintf(buff, "%i", level); + persistentAttribs.push_back(buff); +} + +void TaskImmortal::restoreCustomAttribs() { + targetLife = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT].c_str()); + level = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT+1].c_str()); +} + +void TaskImmortal::randomize() { + Task::randomize(); + level = rand() % 3; + switch (level) { + case 0: + targetLife = 20 + rand()%10; + break; + case 1: + targetLife = 100 + 5*(rand()%5); + break; + case 2: + targetLife = 1000 + 50*(rand()%10); + break; + } +} +/* ------------ TaskMassiveBurial ------------ */ + +TaskMassiveBurial::TaskMassiveBurial(int _color, int _bodyCount) : Task(TASK_MASSIVE_BURIAL) { + color = _color; + bodyCount = _bodyCount; + if ( (0 == color) || (0 == bodyCount) ) { + randomize(); + } +} + +int TaskMassiveBurial::computeReward() { + return rand()%150 + bodyCount * ((Constants::MTG_COLOR_LAND == color) ? 70 : 50); +} + +string TaskMassiveBurial::createDesc() { + char buffer[4096]; + + sprintf(buffer, _("Bury %i %s cards to your opponent's graveyard and defeat him.").c_str(), bodyCount, Constants::MTGColorStrings[color]); + + return buffer; +} + +string TaskMassiveBurial::getShortDesc(){ + char buffer[4096]; + switch (color) { + case Constants::MTG_COLOR_GREEN: + sprintf(buffer, _("Tame the nature (%i)").c_str(), bodyCount); + break; + case Constants::MTG_COLOR_BLUE: + sprintf(buffer, _("Evaporation (%i)").c_str(), bodyCount); + break; + case Constants::MTG_COLOR_RED: + sprintf(buffer, _("Bring the order (%i)").c_str(), bodyCount); + break; + case Constants::MTG_COLOR_BLACK: + sprintf(buffer, _("Exorcism (%i)").c_str(), bodyCount); + break; + case Constants::MTG_COLOR_WHITE: + sprintf(buffer, _("Dusk (%i)").c_str(), bodyCount); + break; + case Constants::MTG_COLOR_LAND: + sprintf(buffer, _("Selective disaster (%i)").c_str(), bodyCount); + break; + } + return buffer; +} + +bool TaskMassiveBurial::isDone(Player * _p1, Player * _p2, GameApp * _app) { + int countColor = 0; + vector cards = _p2->game->graveyard->cards; + + for (vector::iterator it = cards.begin(); it!=cards.end(); it++){ + if ((*it)->hasColor(color)) { + countColor++; + } + } + + return (countColor >= bodyCount); +} + +void TaskMassiveBurial::storeCustomAttribs() { + char buff[256]; + sprintf(buff, "%i", color); + persistentAttribs.push_back(buff); + + sprintf(buff, "%i", bodyCount); + persistentAttribs.push_back(buff); +} + +void TaskMassiveBurial::restoreCustomAttribs() { + color = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT].c_str()); + bodyCount = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT+1].c_str()); +} + +void TaskMassiveBurial::randomize() { + Task::randomize(); + color = rand()%(Constants::MTG_NB_COLORS - 1) + 1; + bodyCount = 5 + ((Constants::MTG_COLOR_LAND == color) ? rand()%10 : rand()%20); +} + +/* ------------ Task template ------------ + +TaskXX::TaskXX() : Task(TASK_XX) { + // TODO: Implement +} + +int TaskXX::computeReward() { + // TODO: Implement + return 100; +} + +string TaskXX::createDesc() { + // TODO: Implement + char buffer[4096]; + + switch (rand()%2) { + case 0: + sprintf(buffer, _("%s").c_str(), getOpponentName().c_str()); + break; + case 1: + sprintf(buffer, _("%s").c_str(), getOpponentName().c_str()); + break; + } + return buffer; +} + +string TaskXX::getShortDesc(){ + // TODO: Implement + char buffer[4096]; + sprintf(buffer, _("%s").c_str(), getOpponentName().c_str()); + return buffer; +} + +bool TaskXX::isDone(Player * _p1, Player * _p2, GameApp * _app) { + // TODO: Implement + return TRUE; +} + +void TaskXX::storeCustomAttribs() { + // TODO: Implement + char buff[256]; + persistentAttribs.push_back(VarXX); + + sprintf(buff, "%i", VarXY); + persistentAttribs.push_back(buff); +} + +void TaskXX::restoreCustomAttribs() { + // TODO: Implement + VarXX = persistentAttribs[COMMON_ATTRIBS_COUNT]; + VarXY = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT+1].c_str()); +} + +void TaskXX::randomize() { + // TODO: Implement + Task::randomize(); + VarXX = rand()%10 + 1; +} + +*/