From 0a863bcbad3c60c292399e433cbbfc15f5cc3051 Mon Sep 17 00:00:00 2001 From: "wagic.jeck" Date: Mon, 1 Feb 2010 18:27:25 +0000 Subject: [PATCH] Jeck - [JGE recompile needed] Shop cleanup, Interface enhancements. Added basic transition system that works with GameApp's phases. Currently does a fade-out between elements, which works well in some places and not-so-well in others. We'll definitely want to think about where and where not to use it... they'd work a lot better if we could spawn a thread to handle loading the next state while transitioning. Also cleaned up the shop a bit, so it uses ReadButton() instead of GetButtonClick()-- hence the slight change to JGE. Added a tiled image for the task board, which loads conservatively (I tried 128x128, but it didn't look as good). --- JGE/include/JGui.h | 1 + JGE/src/JGui.cpp | 100 +- projects/mtg/Makefile | 2 +- projects/mtg/bin/Res/graphics/Taskboard.png | Bin 0 -> 79990 bytes projects/mtg/include/GameApp.h | 7 +- projects/mtg/include/GameState.h | 10 +- projects/mtg/include/GameStateShop.h | 6 +- projects/mtg/include/GameStateTransitions.h | 33 + projects/mtg/include/ShopItem.h | 2 + projects/mtg/include/Tasks.h | 405 ++-- projects/mtg/src/GameApp.cpp | 73 +- projects/mtg/src/GameStateAwards.cpp | 7 +- projects/mtg/src/GameStateDeckViewer.cpp | 4 +- projects/mtg/src/GameStateDuel.cpp | 25 +- projects/mtg/src/GameStateMenu.cpp | 32 +- projects/mtg/src/GameStateOptions.cpp | 2 +- projects/mtg/src/GameStateShop.cpp | 117 +- projects/mtg/src/GameStateTransitions.cpp | 56 + projects/mtg/src/ShopItem.cpp | 123 +- projects/mtg/src/Tasks.cpp | 1897 ++++++++++--------- projects/mtg/template.vcproj | 8 + 21 files changed, 1619 insertions(+), 1291 deletions(-) create mode 100644 projects/mtg/bin/Res/graphics/Taskboard.png create mode 100644 projects/mtg/include/GameStateTransitions.h create mode 100644 projects/mtg/src/GameStateTransitions.cpp diff --git a/JGE/include/JGui.h b/JGE/include/JGui.h index 0f4344302..144f9c221 100644 --- a/JGE/include/JGui.h +++ b/JGE/include/JGui.h @@ -94,6 +94,7 @@ class JGuiController virtual void Render(); virtual void Update(float dt); + virtual bool CheckUserInput(u32 key); void Add(JGuiObject* ctrl); void RemoveAt(int i); diff --git a/JGE/src/JGui.cpp b/JGE/src/JGui.cpp index 6e5aba35b..1cb62e3de 100644 --- a/JGE/src/JGui.cpp +++ b/JGE/src/JGui.cpp @@ -94,13 +94,61 @@ JGuiController::~JGuiController() void JGuiController::Render() { - - for (int i=0;iRender(); } +bool JGuiController::CheckUserInput(u32 key){ + if (key == mActionButton) + { + if (mObjects[mCurr] != NULL && mObjects[mCurr]->ButtonPressed()) + { + if (mListener != NULL) + mListener->ButtonPressed(mId, mObjects[mCurr]->GetId()); + return true; + } + } + else if ((PSP_CTRL_LEFT == key) || (PSP_CTRL_UP == key)) // || mEngine->GetAnalogY() < 64 || mEngine->GetAnalogX() < 64) + { + int n = mCurr; + n--; + if (n<0) + { + if ((mStyle&JGUI_STYLE_WRAPPING)) + n = mCount-1; + else + n = 0; + } + + if (n != mCurr && mObjects[mCurr] != NULL && mObjects[mCurr]->Leaving(PSP_CTRL_UP)) + { + mCurr = n; + mObjects[mCurr]->Entering(); + } + return true; + } + else if ((PSP_CTRL_RIGHT == key) || (PSP_CTRL_DOWN == key)) // || mEngine->GetAnalogY()>192 || mEngine->GetAnalogX()>192) + { + int n = mCurr; + n++; + if (n>mCount-1) + { + if ((mStyle&JGUI_STYLE_WRAPPING)) + n = 0; + else + n = mCount-1; + } + + if (n != mCurr && mObjects[mCurr] != NULL && mObjects[mCurr]->Leaving(PSP_CTRL_DOWN)) + { + mCurr = n; + mObjects[mCurr]->Entering(); + } + return true; + } + return false; +} void JGuiController::Update(float dt) { for (int i=0;iUpdate(dt); u32 key = mEngine->ReadButton(); - if (key == mActionButton) - { - if (mObjects[mCurr] != NULL && mObjects[mCurr]->ButtonPressed()) - { - if (mListener != NULL) - { - mListener->ButtonPressed(mId, mObjects[mCurr]->GetId()); - return; - } - } - } - else if ((PSP_CTRL_LEFT == key) || (PSP_CTRL_UP == key)) // || mEngine->GetAnalogY() < 64 || mEngine->GetAnalogX() < 64) - { - int n = mCurr; - n--; - if (n<0) - { - if ((mStyle&JGUI_STYLE_WRAPPING)) - n = mCount-1; - else - n = 0; - } - - if (n != mCurr && mObjects[mCurr] != NULL && mObjects[mCurr]->Leaving(PSP_CTRL_UP)) - { - mCurr = n; - mObjects[mCurr]->Entering(); - } - } - else if ((PSP_CTRL_RIGHT == key) || (PSP_CTRL_DOWN == key)) // || mEngine->GetAnalogY()>192 || mEngine->GetAnalogX()>192) - { - int n = mCurr; - n++; - if (n>mCount-1) - { - if ((mStyle&JGUI_STYLE_WRAPPING)) - n = 0; - else - n = mCount-1; - } - - if (n != mCurr && mObjects[mCurr] != NULL && mObjects[mCurr]->Leaving(PSP_CTRL_DOWN)) - { - mCurr = n; - mObjects[mCurr]->Entering(); - } - } + CheckUserInput(key); } diff --git a/projects/mtg/Makefile b/projects/mtg/Makefile index 7d1121fb7..89fb75e74 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/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckMetaData.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 +OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckMetaData.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/GameStateTransitions.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/bin/Res/graphics/Taskboard.png b/projects/mtg/bin/Res/graphics/Taskboard.png new file mode 100644 index 0000000000000000000000000000000000000000..b9d5437bc4806164b75b0e12da782f57e27a2e81 GIT binary patch literal 79990 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 literal 0 HcmV?d00001 diff --git a/projects/mtg/include/GameApp.h b/projects/mtg/include/GameApp.h index 72a15fb20..8cf087206 100644 --- a/projects/mtg/include/GameApp.h +++ b/projects/mtg/include/GameApp.h @@ -42,6 +42,7 @@ #define GAME_TYPE_RANDOM2 3 class MTGAllCards; +class TransitionBase; class GameApp: public JApp { @@ -57,7 +58,6 @@ class GameApp: public JApp GameState* mCurrentState; GameState* mNextState; GameState* mGameStates[GAME_STATE_MAX]; - public: @@ -74,15 +74,18 @@ class GameApp: public JApp virtual void Render(); virtual void Pause(); virtual void Resume(); + void LoadGameStates(); void SetNextState(int state); + void DoTransition(int trans, int tostate, float dur=-1, bool animonly = false); + void DoAnimation(int trans, float dur=-1); static hgeParticleSystem * Particles[6]; static int HasMusic; static string systemError; static JMusic* music; static MTGAllCards * collection; - static int players[2]; + static int players[2]; }; diff --git a/projects/mtg/include/GameState.h b/projects/mtg/include/GameState.h index 36e8faa1d..aa9ef3a0a 100644 --- a/projects/mtg/include/GameState.h +++ b/projects/mtg/include/GameState.h @@ -11,15 +11,23 @@ using namespace std; enum ENUM_GAME_STATE { + GAME_STATE_NONE = -1, GAME_STATE_MENU = 1, GAME_STATE_DUEL = 2, GAME_STATE_DECK_VIEWER = 3, GAME_STATE_SHOP = 4, GAME_STATE_OPTIONS = 5, GAME_STATE_AWARDS = 6, - GAME_STATE_MAX = 7, + GAME_STATE_TRANSITION = 7, + GAME_STATE_MAX = 8, }; +enum ENUM_GS_TRANSITION + { + TRANSITION_FADE = 0, + TRANSITION_FADE_IN = 1, + MAX_TRANSITION + }; class GameApp; class SimpleMenu; diff --git a/projects/mtg/include/GameStateShop.h b/projects/mtg/include/GameStateShop.h index b1687d755..acea3d472 100644 --- a/projects/mtg/include/GameStateShop.h +++ b/projects/mtg/include/GameStateShop.h @@ -13,12 +13,12 @@ #define STAGE_SHOP_MENU 3 #define STAGE_SHOP_SHOP 4 #define STAGE_SHOP_TASKS 5 +#define STAGE_FADE_IN 6 class GameStateShop: public GameState, public JGuiListener { private: - ShopItems * shop; JTexture * altThumb[8]; JQuad * mBack; @@ -43,10 +43,6 @@ class GameStateShop: public GameState, public JGuiListener virtual void Update(float dt); virtual void Render(); virtual void ButtonPressed(int controllerId, int controlId); - - - - }; diff --git a/projects/mtg/include/GameStateTransitions.h b/projects/mtg/include/GameStateTransitions.h new file mode 100644 index 000000000..8dab5e9eb --- /dev/null +++ b/projects/mtg/include/GameStateTransitions.h @@ -0,0 +1,33 @@ +#ifndef _GAME_STATE_TRANSITIONS_H_ +#define _GAME_STATE_TRANSITIONS_H_ + +#include +#include +#include "../include/GameState.h" + +class TransitionBase: public GameState, public JGuiListener{ +public: + TransitionBase(GameApp* parent, GameState* _from, GameState* _to, float duration); + virtual void Start(); + virtual void End(); + + virtual bool Finished() {return (mElapsed >= mDuration);}; + virtual void Update(float dt); + virtual void Render() = 0; + virtual void ButtonPressed(int controllerId, int controlId); + + float mElapsed; + float mDuration; + GameState* from; + GameState* to; + bool bAnimationOnly; //Does not call start or end on subordinates. +}; + +class TransitionFade: public TransitionBase { +public: + TransitionFade(GameApp* p, GameState* f, GameState* t, float dur, bool reversed); + virtual void Render(); + bool mReversed; +}; + +#endif \ No newline at end of file diff --git a/projects/mtg/include/ShopItem.h b/projects/mtg/include/ShopItem.h index 7fdbb7b5a..55bd3421d 100644 --- a/projects/mtg/include/ShopItem.h +++ b/projects/mtg/include/ShopItem.h @@ -31,6 +31,7 @@ class ShopItem:public JGuiObject{ float mTargetScale; hgeDistortionMesh* mesh; + void updateThumb(); public: int nameCount; @@ -83,6 +84,7 @@ class ShopItems:public JGuiController,public JGuiListener{ void Add(char * text, JQuad * quad, JQuad * thumb,int _price); void pricedialog(int id, int mode=1); virtual void ButtonPressed(int controllerId, int controlId); + bool CheckUserInput(u32 key); void savePriceList(); void saveAll(); static float _x1[],_y1[],_x2[],_y2[],_x3[],_y3[],_x4[],_y4[]; diff --git a/projects/mtg/include/Tasks.h b/projects/mtg/include/Tasks.h index 0a9e0a662..cf3bef9fd 100644 --- a/projects/mtg/include/Tasks.h +++ b/projects/mtg/include/Tasks.h @@ -1,194 +1,211 @@ -#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 TASK_WISDOM 'O' -#define TASKS_ALL "WSDIMO" - -#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(); -}; - -class TaskWisdom : public Task { -protected: - int color; - int cardCount; - virtual int computeReward(); -public: - TaskWisdom(int _color = 0, int _cardCount = 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 +#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 TASK_WISDOM 'O' +#define TASKS_ALL "WSDIMO" + +#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; + float vPos; + float mElapsed; + int mState; + JQuad * mBg[9]; + JTexture * mBgTex; + + +public: + vector tasks; + + enum{ + TASKS_IN, + TASKS_ACTIVE, + TASKS_OUT, + TASKS_INACTIVE, + }; + + TaskList(string _fileName = ""); + int load(string _fileName = ""); + int save(string _fileName = ""); + int getState() {return mState;}; + 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(); + + void Start(); + void End(); + + void Update(float dt); + 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(); +}; + +class TaskWisdom : public Task { +protected: + int color; + int cardCount; + virtual int computeReward(); +public: + TaskWisdom(int _color = 0, int _cardCount = 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/GameApp.cpp b/projects/mtg/src/GameApp.cpp index 077421b76..e45275a74 100644 --- a/projects/mtg/src/GameApp.cpp +++ b/projects/mtg/src/GameApp.cpp @@ -10,6 +10,7 @@ #include "../include/WResourceManager.h" #include "../include/GameApp.h" #include "../include/Subtypes.h" +#include "../include/GameStateTransitions.h" #include "../include/GameStateDeckViewer.h" #include "../include/GameStateMenu.h" #include "../include/GameStateDuel.h" @@ -20,6 +21,8 @@ #include "../include/DeckMetaData.h" #include "../include/Translate.h" +#define DEFAULT_DURATION .25 + hgeParticleSystem* GameApp::Particles[] = {NULL,NULL,NULL,NULL,NULL,NULL}; MTGAllCards * GameApp::collection = NULL; int GameApp::players[] = {0,0}; @@ -180,6 +183,8 @@ void GameApp::Create() mGameStates[GAME_STATE_AWARDS] = NEW GameStateAwards(this); mGameStates[GAME_STATE_AWARDS]->Create(); + mGameStates[GAME_STATE_TRANSITION] = NULL; + mCurrentState = NULL; mNextState = mGameStates[GAME_STATE_MENU]; @@ -263,16 +268,31 @@ void GameApp::Update() if (dt > 35.0f) // min 30 FPS ;) dt = 35.0f; - if (mCurrentState != NULL) - mCurrentState->Update(dt); - + TransitionBase * mTrans = NULL; + if (mCurrentState){ + mCurrentState->Update(dt); + if(mGameStates[GAME_STATE_TRANSITION] == mCurrentState) + mTrans = (TransitionBase *) mGameStates[GAME_STATE_TRANSITION]; + } + //Check for finished transitions. + if(mTrans && mTrans->Finished()){ + mTrans->End(); + if(mTrans->to != NULL && !mTrans->bAnimationOnly){ + mCurrentState = mTrans->to; + SAFE_DELETE(mGameStates[GAME_STATE_TRANSITION]); + mCurrentState->Start(); + } + else{ + mCurrentState = mTrans->from; + SAFE_DELETE(mGameStates[GAME_STATE_TRANSITION]); + } + } if (mNextState != NULL) - { + { if (mCurrentState != NULL) mCurrentState->End(); mCurrentState = mNextState; - #if defined (WIN32) || defined (LINUX) #else @@ -285,7 +305,6 @@ void GameApp::Update() */ #endif mCurrentState->Start(); - mNextState = NULL; } @@ -302,10 +321,9 @@ void GameApp::Render() if (mFont) mFont->DrawString(systemError.c_str(),1,1); return; } - if (mCurrentState != NULL) - { + + if (mCurrentState) mCurrentState->Render(); - } #ifdef DEBUG_CACHE resources.DebugRender(); @@ -340,3 +358,40 @@ void GameApp::Resume(){ } +void GameApp::DoTransition(int trans, int tostate, float dur, bool animonly){ + TransitionBase * tb = NULL; + GameState * toState = NULL; + if(tostate > GAME_STATE_NONE && tostate < GAME_STATE_MAX) + toState = mGameStates[tostate]; + + if(mGameStates[GAME_STATE_TRANSITION]){ + tb =(TransitionBase*) mGameStates[GAME_STATE_TRANSITION]; + if(toState) + tb->to = toState; //Additional calls to transition merely update the destination. + return; + } + + if(dur < 0) + dur = DEFAULT_DURATION; // Default to this value. + switch(trans){ + case TRANSITION_FADE_IN: + tb = NEW TransitionFade(this,mCurrentState,toState,dur,true); + break; + case TRANSITION_FADE: + default: + tb = NEW TransitionFade(this,mCurrentState,toState,dur,false); + } + if(tb){ + tb->bAnimationOnly = animonly; + mGameStates[GAME_STATE_TRANSITION] = tb; + mGameStates[GAME_STATE_TRANSITION]->Start(); + mCurrentState = tb; //The old current state is ended inside our transition. + } else if(toState) { //Somehow failed, just do standard SetNextState behaviour + mNextState = toState; + } +} + + +void GameApp::DoAnimation(int trans, float dur){ + DoTransition(trans,GAME_STATE_NONE,dur,true); +} \ No newline at end of file diff --git a/projects/mtg/src/GameStateAwards.cpp b/projects/mtg/src/GameStateAwards.cpp index e872fa477..88c55ffc3 100644 --- a/projects/mtg/src/GameStateAwards.cpp +++ b/projects/mtg/src/GameStateAwards.cpp @@ -37,6 +37,7 @@ void GameStateAwards::End() } void GameStateAwards::Start() { + mParent->DoAnimation(TRANSITION_FADE_IN); char buf[256]; mState = STATE_LISTVIEW; options.checkProfile(); @@ -177,11 +178,11 @@ void GameStateAwards::Update(float dt) menu->Add(3, "Cancel"); break; case PSP_CTRL_LTRIGGER: - mParent->SetNextState(GAME_STATE_MENU); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); break; case PSP_CTRL_CROSS: if(mState == STATE_LISTVIEW) - mParent->SetNextState(GAME_STATE_MENU); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); else{ mState = STATE_LISTVIEW; SAFE_DELETE(detailview); @@ -321,7 +322,7 @@ void GameStateAwards::ButtonPressed(int controllerId, int controlId) if(controllerId == -102) switch (controlId){ case 1: - mParent->SetNextState(GAME_STATE_MENU); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); showMenu = false; break; case 2: diff --git a/projects/mtg/src/GameStateDeckViewer.cpp b/projects/mtg/src/GameStateDeckViewer.cpp index f77d566a2..6c99a6076 100644 --- a/projects/mtg/src/GameStateDeckViewer.cpp +++ b/projects/mtg/src/GameStateDeckViewer.cpp @@ -1409,7 +1409,7 @@ void GameStateDeckViewer::ButtonPressed(int controllerId, int controlId) case 10: //Deck menu if (controlId == -1){ if(!mSwitching) - mParent->SetNextState(GAME_STATE_MENU); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); else mStage = STAGE_WAITING; @@ -1446,7 +1446,7 @@ void GameStateDeckViewer::ButtonPressed(int controllerId, int controlId) mSwitching = true; break; case 3: - mParent->SetNextState(GAME_STATE_MENU); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); break; case 4: mStage = STAGE_WAITING; diff --git a/projects/mtg/src/GameStateDuel.cpp b/projects/mtg/src/GameStateDuel.cpp index d2e992ce2..febc0ac16 100644 --- a/projects/mtg/src/GameStateDuel.cpp +++ b/projects/mtg/src/GameStateDuel.cpp @@ -19,7 +19,7 @@ #include #endif -enum ENUM_DUEL_STATEseems +enum ENUM_DUEL_STATE { DUEL_STATE_START, DUEL_STATE_END, @@ -97,11 +97,14 @@ void GameStateDuel::Start() } } + if(deckmenu){ if (decksneeded){ deckmenu->Add(-1,_("Create your Deck!").c_str(),"Highly recommended to get\nthe full Wagic experience!"); premadeDeck = true; fillDeckMenu(deckmenu,RESPATH"/player/premade"); } + deckmenu->Add(-1,_("New Deck...").c_str()); + } for (int i = 0; i < 2; ++i){ mPlayers[i] = NULL; @@ -364,13 +367,16 @@ void GameStateDuel::Update(float dt) mGamePhase = DUEL_STATE_PLAY; break; case DUEL_STATE_BACK_TO_MAIN_MENU: - menu->Update(dt); - if (menu->closed) { - PlayerData * playerdata = NEW PlayerData(mParent->collection); - playerdata->taskList->passOneDay(); - playerdata->taskList->save(); - SAFE_DELETE(playerdata); - mParent->SetNextState(GAME_STATE_MENU); + if(menu){ + menu->Update(dt); + if (menu->closed) { + PlayerData * playerdata = NEW PlayerData(mParent->collection); + playerdata->taskList->passOneDay(); + playerdata->taskList->save(); + SAFE_DELETE(playerdata); + SAFE_DELETE(menu); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU,1); + } } break; default: @@ -456,7 +462,8 @@ void GameStateDuel::Render() sprintf(buffer,_("Turn:%i").c_str(),game->turn); mFont->DrawString(buffer,SCREEN_WIDTH/2,0,JGETEXT_CENTER); } - menu->Render(); + if(menu) + menu->Render(); } LOG("End Render\n"); } diff --git a/projects/mtg/src/GameStateMenu.cpp b/projects/mtg/src/GameStateMenu.cpp index 2cf8b81f6..7f301e088 100644 --- a/projects/mtg/src/GameStateMenu.cpp +++ b/projects/mtg/src/GameStateMenu.cpp @@ -36,7 +36,7 @@ enum ENUM_MENU_STATE_MINOR { MENU_STATE_MINOR_NONE = 0, MENU_STATE_MINOR_SUBMENU_CLOSING = 0x100, - + MENU_STATE_MINOR_FADEIN = 0x200, MENU_STATE_MINOR = 0xF00 }; @@ -133,7 +133,7 @@ void GameStateMenu::Start(){ JRenderer::GetInstance()->EnableVSync(true); subMenuController = NULL; SAFE_DELETE(mGuiController); - + if (GameApp::HasMusic && !GameApp::music && options[Options::MUSICVOLUME].number > 0){ GameApp::music = resources.ssLoadMusic("Track0.mp3"); JSoundSystem::GetInstance()->PlayMusic(GameApp::music, true); @@ -153,9 +153,12 @@ void GameStateMenu::Start(){ mBg = resources.RetrieveQuad("menutitle.png", 0, 0, 256, 166); // Create background quad for rendering. mBg->SetHotSpot(128,50); + genNbCardsStr(); - genNbCardsStr(); + if(currentState == MENU_STATE_MAJOR_MAINMENU){ + currentState = currentState | MENU_STATE_MINOR_FADEIN; + } } void GameStateMenu::genNbCardsStr(){ @@ -431,7 +434,7 @@ void GameStateMenu::Update(float dt) if (mGuiController) mGuiController->Update(dt); if(mEngine->GetButtonState(PSP_CTRL_RTRIGGER)) //Hook for GameStateAward state - mParent->SetNextState(GAME_STATE_AWARDS); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_AWARDS); //TODO: A slide transition would be nice. break; case MENU_STATE_MAJOR_SUBMENU : subMenuController->Update(dt); @@ -453,7 +456,7 @@ void GameStateMenu::Update(float dt) subMenuController->Add(SUBMENUITEM_CANCEL, "Cancel"); } }else{ - mParent->SetNextState(GAME_STATE_DUEL); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_DUEL); currentState = MENU_STATE_MAJOR_MAINMENU; } } @@ -499,15 +502,11 @@ void GameStateMenu::Update(float dt) } scroller->Update(dt); -} - - - - + if((currentState & MENU_STATE_MINOR) == MENU_STATE_MINOR_FADEIN){ currentState = currentState ^ MENU_STATE_MINOR_FADEIN; mParent->DoAnimation(TRANSITION_FADE_IN,.15); }} void GameStateMenu::Render() { - JRenderer * renderer = JRenderer::GetInstance(); + if((currentState & MENU_STATE_MINOR) == MENU_STATE_MINOR_FADEIN) return; JRenderer * renderer = JRenderer::GetInstance(); renderer->ClearScreen(ARGB(0,0,0,0)); JLBFont * mFont = resources.GetJLBFont(Constants::MENU_FONT); if ((currentState & MENU_STATE_MAJOR) == MENU_STATE_MAJOR_LANG){ @@ -571,7 +570,6 @@ void GameStateMenu::Render() if (subMenuController){ subMenuController->Render(); } - } @@ -612,25 +610,25 @@ JLBFont * mFont = resources.GetJLBFont(Constants::MENU_FONT); } break; case MENUITEM_DECKEDITOR: - mParent->SetNextState(GAME_STATE_DECK_VIEWER); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_DECK_VIEWER); break; case MENUITEM_SHOP: - mParent->SetNextState(GAME_STATE_SHOP); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_SHOP); break; case MENUITEM_OPTIONS: - mParent->SetNextState(GAME_STATE_OPTIONS); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_OPTIONS); break; case MENUITEM_EXIT: mEngine->End(); break; case SUBMENUITEM_1PLAYER: - mParent->players[0] = PLAYER_TYPE_HUMAN; + mParent->players[0] = PLAYER_TYPE_HUMAN; mParent->players[1] = PLAYER_TYPE_CPU; subMenuController->Close(); currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; break; case SUBMENUITEM_2PLAYER: - mParent->players[0] = PLAYER_TYPE_HUMAN; + mParent->players[0] = PLAYER_TYPE_HUMAN; mParent->players[1] = PLAYER_TYPE_HUMAN; subMenuController->Close(); currentState = MENU_STATE_MAJOR_DUEL | MENU_STATE_MINOR_SUBMENU_CLOSING; diff --git a/projects/mtg/src/GameStateOptions.cpp b/projects/mtg/src/GameStateOptions.cpp index c81782982..52a5ff9a9 100644 --- a/projects/mtg/src/GameStateOptions.cpp +++ b/projects/mtg/src/GameStateOptions.cpp @@ -218,7 +218,7 @@ void GameStateOptions::ButtonPressed(int controllerId, int controlId) JSoundSystem::GetInstance()->SetSfxVolume(options[Options::SFXVOLUME].number); JSoundSystem::GetInstance()->SetMusicVolume(options[Options::MUSICVOLUME].number); case 2: - mParent->SetNextState(GAME_STATE_MENU); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); break; case 3: mState = SHOW_OPTIONS; diff --git a/projects/mtg/src/GameStateShop.cpp b/projects/mtg/src/GameStateShop.cpp index d15d2455c..00a2ebac5 100644 --- a/projects/mtg/src/GameStateShop.cpp +++ b/projects/mtg/src/GameStateShop.cpp @@ -26,9 +26,7 @@ void GameStateShop::Create(){ void GameStateShop::Start() { menu = NULL; - - - mStage = STAGE_SHOP_SHOP; + mStage = STAGE_FADE_IN; //alternateRender doesn't lock, so lock our thumbnails for hgeDistort. altThumb[0] = resources.RetrieveTexture("artifact_thumb.jpg", RETRIEVE_LOCK); @@ -53,7 +51,6 @@ void GameStateShop::Start() shop = NULL; taskList = NULL; load(); - } @@ -138,7 +135,9 @@ void GameStateShop::Update(float dt) { // mParent->effect->UpdateSmall(dt); // mParent->effect->UpdateBig(dt); - if (mStage == STAGE_SHOP_MENU){ + u32 btn; + switch(mStage){ + case STAGE_SHOP_MENU: if (menu){ menu->Update(dt); }else{ @@ -147,16 +146,32 @@ void GameStateShop::Update(float dt) menu->Add(14,"See available tasks"); menu->Add(13, "Cancel"); } - }else{ - if (mEngine->GetButtonClick(PSP_CTRL_START)){ - mStage = STAGE_SHOP_MENU; - } - if (mStage == STAGE_SHOP_TASKS){ - if ( (mEngine->GetButtonClick(PSP_CTRL_CROSS)) || - (mEngine->GetButtonClick(PSP_CTRL_TRIANGLE)) ) { - mStage = STAGE_SHOP_SHOP; + break; + case STAGE_SHOP_TASKS: + if(menu){ + menu->Update(dt); return; } + if(taskList){ + btn = mEngine->ReadButton(); + taskList->Update(dt); + if ( taskList->getState() != TaskList::TASKS_INACTIVE){ + if( btn == PSP_CTRL_CROSS || btn == PSP_CTRL_TRIANGLE ){ + taskList->End(); + return; + }else if(taskList->getState() == TaskList::TASKS_ACTIVE && btn == PSP_CTRL_START ){ + if(!menu){ + menu = NEW SimpleMenu(11,this,Constants::MENU_FONT,SCREEN_WIDTH/2-100,20); + menu->Add(12,"Save & Back to Main Menu"); + menu->Add(15,"Close tasks"); + menu->Add(13, "Cancel"); + } + } + } + else + mStage = STAGE_SHOP_SHOP; + } + #ifdef TESTSUITE if ((mEngine->GetButtonClick(PSP_CTRL_SQUARE)) && (taskList)) { taskList->passOneDay(); @@ -167,15 +182,25 @@ void GameStateShop::Update(float dt) taskList->save(); } #endif - } else { - if (mEngine->GetButtonClick(PSP_CTRL_SQUARE)){ + break; + case STAGE_SHOP_SHOP: + btn = mEngine->ReadButton(); + if (btn == PSP_CTRL_START){ + mStage = STAGE_SHOP_MENU; + return; + }else if(btn == PSP_CTRL_SQUARE){ load(); } - if (shop) + if (shop){ + shop->CheckUserInput(btn); shop->Update(dt); - } + } + break; + case STAGE_FADE_IN: + mParent->DoAnimation(TRANSITION_FADE_IN); + mStage = STAGE_SHOP_SHOP; + break; } - } @@ -184,43 +209,51 @@ void GameStateShop::Render() //Erase JRenderer * r = JRenderer::GetInstance(); r->ClearScreen(ARGB(0,0,0,0)); - + if(mStage == STAGE_FADE_IN) + return; + if (mBg) r->RenderQuad(mBg,0,0); if (shop) shop->Render(); - if (mStage == STAGE_SHOP_TASKS) { - if (!taskList) { - taskList = NEW TaskList(); - } + if (mStage == STAGE_SHOP_TASKS && taskList) { taskList->Render(); } - if (mStage == STAGE_SHOP_MENU && menu){ + if (menu){ menu->Render(); } } void GameStateShop::ButtonPressed(int controllerId, int controlId) { - switch (controllerId){ - case 10: - if (shop) shop->pricedialog(controlId); - break; - case 11: - if (controlId == 12){ - if (shop) shop->saveAll(); - if (taskList) taskList->save(); - mParent->SetNextState(GAME_STATE_MENU); - } else if (controlId == 14){ - mStage = STAGE_SHOP_TASKS; - } else { - mStage = STAGE_SHOP_SHOP; - } - break; + if (controllerId == 10){ + if (shop) + shop->pricedialog(controlId); } -} - - + else{ + switch(controlId){ + case 12: + if (shop) shop->saveAll(); + if (taskList) taskList->save(); + mParent->DoTransition(TRANSITION_FADE,GAME_STATE_MENU); + mStage = STAGE_SHOP_SHOP; + break; + case 14: + mStage = STAGE_SHOP_TASKS; + if (!taskList) + taskList = NEW TaskList(); + taskList->Start(); + break; + case 15: + if(taskList) + taskList->End(); + break; + default: + mStage = STAGE_SHOP_SHOP; + } + SAFE_DELETE(menu); + } +} \ No newline at end of file diff --git a/projects/mtg/src/GameStateTransitions.cpp b/projects/mtg/src/GameStateTransitions.cpp new file mode 100644 index 000000000..0ec2b3410 --- /dev/null +++ b/projects/mtg/src/GameStateTransitions.cpp @@ -0,0 +1,56 @@ +#include "../include/config.h" +#include +#include +#include "../include/GameApp.h" +#include "../include/GameStateTransitions.h" +#include "../include/MTGDeck.h" +#include "../include/Translate.h" +#include "../include/OptionItem.h" +#include "../include/GameOptions.h" +#include "../include/DeckDataWrapper.h" + +TransitionBase::TransitionBase(GameApp* parent, GameState* _from, GameState* _to, float duration): GameState(parent){ + from = _from; + to = _to; + mDuration = duration; + bAnimationOnly = false; +} + +void TransitionBase::Update(float dt){ + if(from && !Finished()) + from->Update(dt); + mElapsed += dt; +} + +void TransitionBase::ButtonPressed(int controllerId, int controlId){ + if(!from) return; + JGuiListener * jgl = dynamic_cast(from); + if(jgl) + jgl->ButtonPressed(controllerId,controlId); +} + +void TransitionBase::Start() { + mElapsed = 0; +}; + +void TransitionBase::End() { + mElapsed = 0; + if(!bAnimationOnly){ + if(from) + from->End(); + } +}; + +void TransitionFade::Render(){ + if(from) + from->Render(); + float fade = 255*mElapsed/mDuration; + if(mReversed) + fade = 255 - fade; + JRenderer::GetInstance()->FillRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,ARGB((int)fade,0,0,0)); +} + +TransitionFade::TransitionFade(GameApp* p, GameState* f, GameState* t, float dur, bool reversed): +TransitionBase(p, f,t,dur) { + mReversed = reversed; +}; \ No newline at end of file diff --git a/projects/mtg/src/ShopItem.cpp b/projects/mtg/src/ShopItem.cpp index a8b3e389b..2df2b210f 100644 --- a/projects/mtg/src/ShopItem.cpp +++ b/projects/mtg/src/ShopItem.cpp @@ -71,34 +71,9 @@ ShopItem::ShopItem(int id, JLBFont *font, int _cardid, float _xy[], bool hasFocu if (card->getRarity() == Constants::RARITY_L) quantity = 50; quad = NULL; - thumb = resources.RetrieveCard(card,RETRIEVE_LOCK,TEXTURE_SUB_THUMB); -#if defined (WIN32) || defined (LINUX) - //On pcs we render the big image if the thumbnail is not available - if (!thumb) thumb = resources.RetrieveCard(card,RETRIEVE_LOCK); -#endif - if (!thumb) - thumb = CardGui::alternateThumbQuad(card); - else - mRelease = true; - - if (thumb){ - mesh=NEW hgeDistortionMesh(2,2); - mesh->SetTexture(thumb->mTex); - float x0,y0,w0,h0; - thumb->GetTextureRect(&x0,&y0,&w0,&h0); - mesh->SetTextureRect(x0,y0,w0,h0); - mesh->Clear(ARGB(0xFF,0xFF,0xFF,0xFF)); - mesh->SetDisplacement(0, 0, xy[0],xy[1], HGEDISP_NODE); - mesh->SetDisplacement(1, 0, xy[2] - w0,xy[3], HGEDISP_NODE); - mesh->SetDisplacement(0, 1,xy[4],xy[5]-h0, HGEDISP_NODE); - mesh->SetDisplacement(1, 1, xy[6]-w0,xy[7]-h0, HGEDISP_NODE); - mesh->SetColor(1,1,ARGB(255,100,100,100)); - mesh->SetColor(0,1,ARGB(255,100,100,100)); - mesh->SetColor(1,0,ARGB(255,100,100,100)); - mesh->SetColor(0,0,ARGB(255,200,200,200)); - }else{ - mesh = NULL; - } + mesh = NULL; + thumb = NULL; + updateThumb(); } @@ -119,6 +94,49 @@ const char * ShopItem::getText(){ return mText.c_str(); } +void ShopItem::updateThumb(){ + if(card == NULL) + return; + + if(thumb && mRelease) + resources.Release(thumb->mTex); + mRelease = false; + + if(mesh) + SAFE_DELETE(mesh); + else if(thumb) + SAFE_DELETE(thumb); + + thumb = resources.RetrieveCard(card,RETRIEVE_LOCK,TEXTURE_SUB_THUMB); +#if defined (WIN32) || defined (LINUX) + //On pcs we render the big image if the thumbnail is not available + if (!thumb) thumb = resources.RetrieveCard(card,RETRIEVE_LOCK); +#endif + if (!thumb) + thumb = CardGui::alternateThumbQuad(card); + else + mRelease = true; + + + if (thumb){ + mesh=NEW hgeDistortionMesh(2,2); + mesh->SetTexture(thumb->mTex); + float x0,y0,w0,h0; + thumb->GetTextureRect(&x0,&y0,&w0,&h0); + mesh->SetTextureRect(x0,y0,w0,h0); + mesh->Clear(ARGB(0xFF,0xFF,0xFF,0xFF)); + mesh->SetDisplacement(0, 0, xy[0],xy[1], HGEDISP_NODE); + mesh->SetDisplacement(1, 0, xy[2] - w0,xy[3], HGEDISP_NODE); + mesh->SetDisplacement(0, 1,xy[4],xy[5]-h0, HGEDISP_NODE); + mesh->SetDisplacement(1, 1, xy[6]-w0,xy[7]-h0, HGEDISP_NODE); + mesh->SetColor(1,1,ARGB(255,100,100,100)); + mesh->SetColor(0,1,ARGB(255,100,100,100)); + mesh->SetColor(1,0,ARGB(255,100,100,100)); + mesh->SetColor(0,0,ARGB(255,200,200,200)); + }else{ + mesh = NULL; + } +} void ShopItem::Render(){ if (mHasFocus){ @@ -251,9 +269,9 @@ void ShopItems::Add(char * text, JQuad * quad,JQuad * thumb, int price){ } void ShopItems::Update(float dt){ + if (display){ - while (u32 key = JGE::GetInstance()->ReadButton()) display->CheckUserInput(key); - if (display) display->Update(dt); + display->Update(dt); }else{ if (showPriceDialog!=-1){ ShopItem * item = ((ShopItem *)mObjects[showPriceDialog]); @@ -272,19 +290,6 @@ void ShopItems::Update(float dt){ dialog->Update(dt); } }else{ - u32 buttons[] = {PSP_CTRL_LEFT,PSP_CTRL_DOWN,PSP_CTRL_RIGHT,PSP_CTRL_UP,PSP_CTRL_CIRCLE}; - for (int i = 0; i < 5; ++i){ - if (JGE::GetInstance()->GetButtonClick(buttons[i])){ - showCardList = false; - } - } - - if(JGE::GetInstance()->GetButtonClick(PSP_CTRL_TRIANGLE)){ - options[Options::DISABLECARDS].number = !options[Options::DISABLECARDS].number; - } - if (JGE::GetInstance()->GetButtonClick(PSP_CTRL_CROSS)){ - showCardList = !showCardList; - } SAFE_DELETE(dialog); JGuiController::Update(dt); } @@ -319,9 +324,7 @@ void ShopItems::Render(){ if (display) display->Render(); - if (showPriceDialog==-1){ - - }else{ + if (showPriceDialog!=-1){ if(dialog){ dialog->Render(); } @@ -518,3 +521,31 @@ ostream& ShopItem::toString(ostream& out) const << " ; card : " << card << " ; price : " << price; } + +bool ShopItems::CheckUserInput(u32 key){ + if (display && display->CheckUserInput(key)) + return true; + if(showPriceDialog==-1){ + u32 buttons[] = {PSP_CTRL_LEFT,PSP_CTRL_DOWN,PSP_CTRL_RIGHT,PSP_CTRL_UP,PSP_CTRL_CIRCLE}; + for (int i = 0; i < 5; ++i){ + if (key == buttons[i]) + showCardList = false; + } + + if(key == PSP_CTRL_TRIANGLE){ + options[Options::DISABLECARDS].number = !options[Options::DISABLECARDS].number; + vector::iterator it; + for(it=mObjects.begin();it!=mObjects.end();it++){ + ShopItem * si = dynamic_cast(*it); + if(si) + si->updateThumb(); + } + return true; + } + if (key == PSP_CTRL_CROSS){ + showCardList = !showCardList; + return true; + } + } + return JGuiController::CheckUserInput(key); +} \ No newline at end of file diff --git a/projects/mtg/src/Tasks.cpp b/projects/mtg/src/Tasks.cpp index a91f0c9a2..400b00a22 100644 --- a/projects/mtg/src/Tasks.cpp +++ b/projects/mtg/src/Tasks.cpp @@ -1,910 +1,987 @@ -#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 -#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); - getReward(); -} - -bool Task::isExpired() { - return (expiresIn <= 0); -} - -int Task::getExpiration() { - return expiresIn; -} - -void Task::setExpiration(int _expiresIn) { - expiresIn = _expiresIn; -} - -void Task::passOneDay() { - expiresIn--; - reward = (int) getReward() * 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; - case TASK_WISDOM: - result = new TaskWisdom(); - break; - default: - #if defined (WIN32) || defined (LINUX) - OutputDebugString("\nTasks.cpp::createFromStr: Undefined class type\n"); - #endif - result = new TaskWinAgainst(); - } - - if (!result) { - #if defined (WIN32) || defined (LINUX) - OutputDebugString("\nTask::createFromStr: Failed to create task\n"); - #endif - 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 == "") { - #if defined (WIN32) || defined (LINUX) - OutputDebugString("\nTaskList::save: No filename specified\n"); - #endif - return -1; - } - - std::ofstream file(fileName.c_str()); - if (file){ - #if defined (WIN32) || defined (LINUX) - OutputDebugString("\nsaving tasks\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 == "") { - #if defined (WIN32) || defined (LINUX) - OutputDebugString("\nTaskList::load: No filename specified\n"); - #endif - 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 { - #if defined (WIN32) || defined (LINUX) - OutputDebugString("\nTaskList::load: Failed to open file\n"); - #endif - 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(_("There 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").c_str(), (*it)->getExpiration()); - f->DrawString(buffer, SCREEN_WIDTH - 180, posY); - sprintf(buffer, _("Reward: %i").c_str(), (*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() - 9*targetLife*(targetLife<-50 ? 2 : 1); -} - -void TaskSlaughter::randomize() { - targetLife = -15 - rand()%10; - if (!(rand()%7)) { - targetLife *= 5; - } - Task::randomize(); -} - -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 life)").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*33 : (17-turn)*(17-turn)*17); -} - -void TaskDelay::randomize() { - afterTurn = rand()%2; - turn = afterTurn ? rand()%15 + 20 : 15 - rand()%9; - Task::randomize(); -} - -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 life in the end.").c_str(), targetLife); - - return buffer; -} - -string TaskImmortal::getShortDesc(){ - char buffer[4096]; - - switch (level) { - case 0: - sprintf(buffer, _("Win flawlessly (%i life)").c_str(), targetLife); - break; - case 1: - sprintf(buffer, _("Reach Invulnerability (%i life)").c_str(), targetLife); - break; - case 2: - sprintf(buffer, _("Reach Immortality! (%i life)").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() { - 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; - } - Task::randomize(); -} -/* ------------ 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() { - color = rand()%(Constants::MTG_NB_COLORS - 1) + 1; - bodyCount = 5 + ((Constants::MTG_COLOR_LAND == color) ? rand()%10 : rand()%20); - Task::randomize(); -} - -/* ------------ TaskWisdom ------------ */ - -TaskWisdom::TaskWisdom(int _color, int _cardCount) : Task(TASK_WISDOM) { - color = _color; - cardCount = _cardCount; - if ( (0 == color) || (0 == cardCount) ) { - randomize(); - } -} - -int TaskWisdom::computeReward() { - return rand()%150 + pow(cardCount, 1.4) * 50 + (cardCount>7)*200; -} - -string TaskWisdom::createDesc() { - char buffer[4096]; - - sprintf(buffer, _("Win a game with at least %i %s cards in your hand.").c_str(), cardCount, Constants::MTGColorStrings[color]); - - return buffer; -} - -string TaskWisdom::getShortDesc(){ - char buffer[4096]; - switch (color) { - case Constants::MTG_COLOR_GREEN: - sprintf(buffer, _("Animal herder (%i)").c_str(), cardCount); - break; - case Constants::MTG_COLOR_BLUE: - sprintf(buffer, _("Accumulated knowledge (%i)").c_str(), cardCount); - break; - case Constants::MTG_COLOR_RED: - sprintf(buffer, _("Retained anger (%i)").c_str(), cardCount); - break; - case Constants::MTG_COLOR_BLACK: - sprintf(buffer, _("Necropotence (%i)").c_str(), cardCount); - break; - case Constants::MTG_COLOR_WHITE: - sprintf(buffer, _("Dawn of crusade (%i)").c_str(), cardCount); - break; - case Constants::MTG_COLOR_LAND: - sprintf(buffer, _("Mana reserves (%i)").c_str(), cardCount); - break; - } - return buffer; -} - -bool TaskWisdom::isDone(Player * _p1, Player * _p2, GameApp * _app) { - GameObserver * g = GameObserver::GetInstance(); - int countColor = 0; - vector cards = _p1->game->hand->cards; - - for (vector::iterator it = cards.begin(); it!=cards.end(); it++){ - if ((*it)->hasColor(color)) { - countColor++; - } - } - - return (!_p1->isAI()) && (_p2->isAI()) && (g->gameOver != _p1) // Human player wins - && (countColor >= cardCount); -} - -void TaskWisdom::storeCustomAttribs() { - char buff[256]; - sprintf(buff, "%i", color); - persistentAttribs.push_back(buff); - - sprintf(buff, "%i", cardCount); - persistentAttribs.push_back(buff); -} - -void TaskWisdom::restoreCustomAttribs() { - color = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT].c_str()); - cardCount = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT+1].c_str()); -} - -void TaskWisdom::randomize() { - color = rand()%(Constants::MTG_NB_COLORS - 1) + 1; - cardCount = 2 + ((Constants::MTG_COLOR_LAND == color) ? rand()%5 : rand()%11); - Task::randomize(); -} - -/* ------------ 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) { - GameObserver * g = GameObserver::GetInstance(); - // TODO: Implement - return (!_p1->isAI()) && (_p2->isAI()) && (g->gameOver != _p1) // Human player wins - && ; -} - -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 - VarXX = rand()%10 + 1; - Task::randomize(); -} - -*/ +#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 +#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); + getReward(); +} + +bool Task::isExpired() { + return (expiresIn <= 0); +} + +int Task::getExpiration() { + return expiresIn; +} + +void Task::setExpiration(int _expiresIn) { + expiresIn = _expiresIn; +} + +void Task::passOneDay() { + expiresIn--; + reward = (int) getReward() * 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; + case TASK_WISDOM: + result = new TaskWisdom(); + break; + default: + #if defined (WIN32) || defined (LINUX) + OutputDebugString("\nTasks.cpp::createFromStr: Undefined class type\n"); + #endif + result = new TaskWinAgainst(); + } + + if (!result) { + #if defined (WIN32) || defined (LINUX) + OutputDebugString("\nTask::createFromStr: Failed to create task\n"); + #endif + 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); + for(int i=0;i<9;i++) + mBg[i] = NULL; + mBgTex = NULL; //We only load the background if we use the task screen. +} + +int TaskList::save(string _fileName) { + if (_fileName != "") { + fileName = _fileName; + } + if (fileName == "") { + #if defined (WIN32) || defined (LINUX) + OutputDebugString("\nTaskList::save: No filename specified\n"); + #endif + return -1; + } + + std::ofstream file(fileName.c_str()); + if (file){ + #if defined (WIN32) || defined (LINUX) + OutputDebugString("\nsaving tasks\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 == "") { + #if defined (WIN32) || defined (LINUX) + OutputDebugString("\nTaskList::load: No filename specified\n"); + #endif + 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 { + #if defined (WIN32) || defined (LINUX) + OutputDebugString("\nTaskList::load: Failed to open file\n"); + #endif + 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::Start(){ + vPos = -SCREEN_HEIGHT; //Offscreen + mElapsed = 0; + mState = TASKS_IN; + if(!mBgTex){ + mBgTex = resources.RetrieveTexture("taskboard.png"); + if(mBgTex){ + mBg[0] = NEW JQuad(mBgTex,0,0,64,64); + mBg[1] = NEW JQuad(mBgTex,64,0,128,64); + mBg[2] = NEW JQuad(mBgTex,192,0,64,64); + mBg[3] = NEW JQuad(mBgTex,0,64,64,128); + mBg[4] = NEW JQuad(mBgTex,64,64,128,128); + mBg[5] = NEW JQuad(mBgTex,192,64,64,128); + mBg[6] = NEW JQuad(mBgTex,0,192,64,64); + mBg[7] = NEW JQuad(mBgTex,64,192,128,64); + mBg[8] = NEW JQuad(mBgTex,192,192,64,64); + } + else + for(int i=0;i<9;i++) + SAFE_DELETE(mBg[i]); + } +} +void TaskList::End(){ + mState = TASKS_OUT; + mElapsed = 0; +} +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::Update(float dt) { + mElapsed += dt; + + if(mState == TASKS_IN && vPos < 0){ + vPos = -SCREEN_HEIGHT+(SCREEN_HEIGHT*mElapsed/.75); //Todo: more physical drop-in. + if(vPos >= 0){ + vPos = 0; + mState = TaskList::TASKS_ACTIVE; + } + }else if(mState == TASKS_OUT && vPos > -SCREEN_HEIGHT){ + vPos = -(SCREEN_HEIGHT*mElapsed/.75); + if(vPos <= -SCREEN_HEIGHT) + mState = TASKS_INACTIVE; + } +} +void TaskList::Render() { + JRenderer * r = JRenderer::GetInstance(); + //Setup fonts. + 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)); + + //Render background board + if(mBgTex){ + r->FillRect(0,vPos,SCREEN_WIDTH,SCREEN_HEIGHT,ARGB(128,0,0,0)); + r->RenderQuad(mBg[0],0,vPos); //TL + r->RenderQuad(mBg[2],SCREEN_WIDTH-64,vPos); //TR + r->RenderQuad(mBg[6],0,vPos+SCREEN_HEIGHT-64); //BL + r->RenderQuad(mBg[8],SCREEN_WIDTH-64,vPos+SCREEN_HEIGHT-64); //BR + + //Stretch the sides + float stretchV = 144.0f/128.0f; + float stretchH = 176.0f/128.0f; + r->RenderQuad(mBg[3],0,vPos+64,0,1,stretchV); //L + r->RenderQuad(mBg[5],SCREEN_WIDTH-64,vPos+64,0,1,stretchV); //R + r->RenderQuad(mBg[1],64,vPos,0,stretchH,1); //T1 + r->RenderQuad(mBg[1],240,vPos,0,stretchH,1); //T1 + r->RenderQuad(mBg[7],64,vPos+208,0,stretchH,1); //B1 + r->RenderQuad(mBg[7],240,vPos+208,0,stretchH,1); //B1 + r->RenderQuad(mBg[4],64,vPos+64,0,stretchH,stretchV); //Center1 + r->RenderQuad(mBg[4],240,vPos+64,0,stretchH,stretchV); //Center2 + + f2->SetColor(ARGB(255, 55, 46, 34)); + f=f2; + }else{ + r->FillRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,ARGB(128,0,0,0)); + r->FillRect(10,10+vPos,SCREEN_WIDTH-10,SCREEN_HEIGHT-10,ARGB(128,0,0,0)); + } + + + + float posX = 40, posY = vPos+20; + char buffer[300]; + string title = _("Task Board"); + + f3->DrawString(title.c_str(), (SCREEN_WIDTH-20)/2 - title.length()*4, posY); + posY += 30; + + if (0 == tasks.size()) { + f->DrawString(_("There 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); + if(mBgTex) + f->SetScale(.8); + sprintf(buffer, _("Days left: %i").c_str(), (*it)->getExpiration()); + f->DrawString(buffer, SCREEN_WIDTH - 190, posY); + sprintf(buffer, _("Reward: %i").c_str(), (*it)->getReward()); + f->DrawString(buffer, SCREEN_WIDTH - 100, 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)); + } + f->SetScale(1); +} + +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() - 9*targetLife*(targetLife<-50 ? 2 : 1); +} + +void TaskSlaughter::randomize() { + targetLife = -15 - rand()%10; + if (!(rand()%7)) { + targetLife *= 5; + } + Task::randomize(); +} + +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 life)").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*33 : (17-turn)*(17-turn)*17); +} + +void TaskDelay::randomize() { + afterTurn = rand()%2; + turn = afterTurn ? rand()%15 + 20 : 15 - rand()%9; + Task::randomize(); +} + +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 life in the end.").c_str(), targetLife); + + return buffer; +} + +string TaskImmortal::getShortDesc(){ + char buffer[4096]; + + switch (level) { + case 0: + sprintf(buffer, _("Win flawlessly (%i life)").c_str(), targetLife); + break; + case 1: + sprintf(buffer, _("Reach Invulnerability (%i life)").c_str(), targetLife); + break; + case 2: + sprintf(buffer, _("Reach Immortality! (%i life)").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() { + 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; + } + Task::randomize(); +} +/* ------------ 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() { + color = rand()%(Constants::MTG_NB_COLORS - 1) + 1; + bodyCount = 5 + ((Constants::MTG_COLOR_LAND == color) ? rand()%10 : rand()%20); + Task::randomize(); +} + +/* ------------ TaskWisdom ------------ */ + +TaskWisdom::TaskWisdom(int _color, int _cardCount) : Task(TASK_WISDOM) { + color = _color; + cardCount = _cardCount; + if ( (0 == color) || (0 == cardCount) ) { + randomize(); + } +} + +int TaskWisdom::computeReward() { + return rand()%150 + pow(cardCount, 1.4) * 50 + (cardCount>7)*200; +} + +string TaskWisdom::createDesc() { + char buffer[4096]; + + sprintf(buffer, _("Win a game with at least %i %s cards in your hand.").c_str(), cardCount, Constants::MTGColorStrings[color]); + + return buffer; +} + +string TaskWisdom::getShortDesc(){ + char buffer[4096]; + switch (color) { + case Constants::MTG_COLOR_GREEN: + sprintf(buffer, _("Animal herder (%i)").c_str(), cardCount); + break; + case Constants::MTG_COLOR_BLUE: + sprintf(buffer, _("Accumulated knowledge (%i)").c_str(), cardCount); + break; + case Constants::MTG_COLOR_RED: + sprintf(buffer, _("Retained anger (%i)").c_str(), cardCount); + break; + case Constants::MTG_COLOR_BLACK: + sprintf(buffer, _("Necropotence (%i)").c_str(), cardCount); + break; + case Constants::MTG_COLOR_WHITE: + sprintf(buffer, _("Dawn of crusade (%i)").c_str(), cardCount); + break; + case Constants::MTG_COLOR_LAND: + sprintf(buffer, _("Mana reserves (%i)").c_str(), cardCount); + break; + } + return buffer; +} + +bool TaskWisdom::isDone(Player * _p1, Player * _p2, GameApp * _app) { + GameObserver * g = GameObserver::GetInstance(); + int countColor = 0; + vector cards = _p1->game->hand->cards; + + for (vector::iterator it = cards.begin(); it!=cards.end(); it++){ + if ((*it)->hasColor(color)) { + countColor++; + } + } + + return (!_p1->isAI()) && (_p2->isAI()) && (g->gameOver != _p1) // Human player wins + && (countColor >= cardCount); +} + +void TaskWisdom::storeCustomAttribs() { + char buff[256]; + sprintf(buff, "%i", color); + persistentAttribs.push_back(buff); + + sprintf(buff, "%i", cardCount); + persistentAttribs.push_back(buff); +} + +void TaskWisdom::restoreCustomAttribs() { + color = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT].c_str()); + cardCount = atoi(persistentAttribs[COMMON_ATTRIBS_COUNT+1].c_str()); +} + +void TaskWisdom::randomize() { + color = rand()%(Constants::MTG_NB_COLORS - 1) + 1; + cardCount = 2 + ((Constants::MTG_COLOR_LAND == color) ? rand()%5 : rand()%11); + Task::randomize(); +} + +/* ------------ 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) { + GameObserver * g = GameObserver::GetInstance(); + // TODO: Implement + return (!_p1->isAI()) && (_p2->isAI()) && (g->gameOver != _p1) // Human player wins + && ; +} + +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 + VarXX = rand()%10 + 1; + Task::randomize(); +} + +*/ diff --git a/projects/mtg/template.vcproj b/projects/mtg/template.vcproj index b912402ab..859254d7a 100644 --- a/projects/mtg/template.vcproj +++ b/projects/mtg/template.vcproj @@ -492,6 +492,10 @@ RelativePath=".\src\GameStateShop.cpp" > + + @@ -849,6 +853,10 @@ RelativePath=".\include\GameStateShop.h" > + +