From 2a8f8074e66ecc137678fbd888078df1865ddf6d Mon Sep 17 00:00:00 2001 From: "techdragon.nguyen@gmail.com" Date: Thu, 18 Nov 2010 15:48:48 +0000 Subject: [PATCH] New descriptive text popup feature for deck selection http://wololo.net/forum/viewtopic.php?f=13&t=2423 --- JGE/include/JGui.h | 247 +- JGE/src/JGui.cpp | 503 +-- projects/mtg/Makefile | 2 +- projects/mtg/include/DeckMenu.h | 138 +- projects/mtg/include/DeckStats.h | 146 +- projects/mtg/include/GameStateDeckViewer.h | 313 +- projects/mtg/include/GameStateDuel.h | 149 +- projects/mtg/include/Player.h | 180 +- projects/mtg/include/SimplePopup.h | 48 + projects/mtg/src/DeckEditorMenu.cpp | 205 +- projects/mtg/src/DeckMenu.cpp | 609 ++-- projects/mtg/src/DeckStats.cpp | 708 ++-- projects/mtg/src/GameStateDeckViewer.cpp | 3650 ++++++++++---------- projects/mtg/src/GameStateDuel.cpp | 1504 ++++---- projects/mtg/src/MTGDeck.cpp | 2384 +++++++------ projects/mtg/src/SimplePopup.cpp | 115 + projects/mtg/template.vcproj | 12 + 17 files changed, 5715 insertions(+), 5198 deletions(-) create mode 100644 projects/mtg/include/SimplePopup.h create mode 100644 projects/mtg/src/SimplePopup.cpp diff --git a/JGE/include/JGui.h b/JGE/include/JGui.h index 210f62d2d..d5ca0bbad 100644 --- a/JGE/include/JGui.h +++ b/JGE/include/JGui.h @@ -1,123 +1,124 @@ -//------------------------------------------------------------------------------------- -// -// JGE++ is a hardware accelerated 2D game SDK for PSP/Windows. -// -// Licensed under the BSD license, see LICENSE in JGE root for details. -// -// Copyright (c) 2007 James Hui (a.k.a. Dr.Watson) -// -//------------------------------------------------------------------------------------- - - -#ifndef _JGUI_H -#define _JGUI_H - -#include -#include "JGE.h" -#include "JSprite.h" - -#define MAX_GUIOBJECT 64 - -#define JGUI_STYLE_LEFTRIGHT 0x01 -#define JGUI_STYLE_UPDOWN 0x02 -#define JGUI_STYLE_WRAPPING 0x04 - -#define JGUI_INITIAL_DELAY 0.4 -#define JGUI_REPEAT_DELAY 0.2 - -const int kCancelMenuID = -1; - -class JGuiListener -{ - public: - virtual ~JGuiListener() {} - virtual void ButtonPressed(int controllerId, int controlId) = 0; -}; - - -class JGuiObject -{ - protected: - static JGE* mEngine; - - private: - int mId; - - - public: - JGuiObject(int id); - virtual ~JGuiObject(); - - virtual void Render() = 0; - virtual std::ostream& toString(std::ostream&) const = 0; - virtual void Update(float dt); - - virtual void Entering(); // when focus is transferring to this obj - virtual bool Leaving(JButton key); // when focus is transferring away from this obj, true to go ahead - virtual bool ButtonPressed(); // action button pressed, return false to ignore - - // Used for mouse support so that the GUI engine can found out which Object was selected - virtual bool getTopLeft(int& top, int& left) {return false;}; - - int GetId(); -}; - - -class JGuiController -{ - protected: - static JGE* mEngine; - - int mId; - bool mActive; - - - JButton mActionButton; - JButton mCancelButton; - int mCurr; - int mStyle; - - JSprite* mCursor; - bool mShowCursor; - int mCursorX; - int mCursorY; - - int mBgX; - int mBgY; - const JTexture* mBg; - PIXEL_TYPE mShadingColor; - Rect* mShadingBg; - - JGuiListener* mListener; - //int mKeyHoldTime; - - public: - vector mObjects; - int mCount; - - JGuiController(int id, JGuiListener* listener); - ~JGuiController(); - - virtual void Render(); - virtual void Update(float dt); - virtual bool CheckUserInput(JButton key); - - void Add(JGuiObject* ctrl); - void RemoveAt(int i); - void Remove(int id); - void Remove(JGuiObject* ctrl); - - void SetActionButton(JButton button); - void SetStyle(int style); - void SetCursor(JSprite* cursor); - - bool IsActive(); - void SetActive(bool flag); - - //void SetImageBackground(const JTexture* tex, int x, int y); - //void SetShadingBackground(int x, int y, int width, int height, PIXEL_TYPE color); -}; - -ostream& operator<<(ostream &out, const JGuiObject &j); - -#endif +//------------------------------------------------------------------------------------- +// +// JGE++ is a hardware accelerated 2D game SDK for PSP/Windows. +// +// Licensed under the BSD license, see LICENSE in JGE root for details. +// +// Copyright (c) 2007 James Hui (a.k.a. Dr.Watson) +// +//------------------------------------------------------------------------------------- + + +#ifndef _JGUI_H +#define _JGUI_H + +#include +#include "JGE.h" +#include "JSprite.h" + +#define MAX_GUIOBJECT 64 + +#define JGUI_STYLE_LEFTRIGHT 0x01 +#define JGUI_STYLE_UPDOWN 0x02 +#define JGUI_STYLE_WRAPPING 0x04 + +#define JGUI_INITIAL_DELAY 0.4 +#define JGUI_REPEAT_DELAY 0.2 + +const int kCancelMenuID = -1; +const int kInfoMenuID = -200; + +class JGuiListener +{ +public: + virtual ~JGuiListener() + { + } + virtual void ButtonPressed(int controllerId, int controlId) = 0; +}; + +class JGuiObject +{ +protected: + static JGE* mEngine; + +private: + int mId; + +public: + JGuiObject(int id); + virtual ~JGuiObject(); + + virtual void Render() = 0; + virtual std::ostream& toString(std::ostream&) const = 0; + virtual void Update(float dt); + + virtual void Entering(); // when focus is transferring to this obj + virtual bool Leaving(JButton key); // when focus is transferring away from this obj, true to go ahead + virtual bool ButtonPressed(); // action button pressed, return false to ignore + + // Used for mouse support so that the GUI engine can found out which Object was selected + virtual bool getTopLeft(int& top, int& left) + { + return false; + } + ; + + int GetId(); +}; + +class JGuiController +{ +protected: + static JGE* mEngine; + + int mId; + bool mActive; + + JButton mActionButton; + JButton mCancelButton; + int mCurr; + int mStyle; + + JSprite* mCursor; + bool mShowCursor; + int mCursorX; + int mCursorY; + + int mBgX; + int mBgY; + const JTexture* mBg; + PIXEL_TYPE mShadingColor; + Rect* mShadingBg; + + JGuiListener* mListener; + //int mKeyHoldTime; + +public: + vector mObjects; + int mCount; + + JGuiController(int id, JGuiListener* listener); + ~JGuiController(); + + virtual void Render(); + virtual void Update(float dt); + virtual bool CheckUserInput(JButton key); + + void Add(JGuiObject* ctrl); + void RemoveAt(int i); + void Remove(int id); + void Remove(JGuiObject* ctrl); + + void SetActionButton(JButton button); + void SetStyle(int style); + void SetCursor(JSprite* cursor); + + bool IsActive(); + void SetActive(bool flag); + +}; + +ostream& operator<<(ostream &out, const JGuiObject &j); + +#endif diff --git a/JGE/src/JGui.cpp b/JGE/src/JGui.cpp index b93388a78..0f4f2cf6e 100644 --- a/JGE/src/JGui.cpp +++ b/JGE/src/JGui.cpp @@ -1,247 +1,256 @@ -//------------------------------------------------------------------------------------- -// -// JGE++ is a hardware accelerated 2D game SDK for PSP/Windows. -// -// Licensed under the BSD license, see LICENSE in JGE root for details. -// -// Copyright (c) 2007 James Hui (a.k.a. Dr.Watson) -// -//------------------------------------------------------------------------------------- - -#include "../include/JGE.h" -#include "../include/JGui.h" - -JGE* JGuiObject::mEngine = NULL; - -JGE* JGuiController::mEngine = NULL; - - -JGuiObject::JGuiObject(int id): mId(id) -{ - mEngine = JGE::GetInstance(); -} - - -JGuiObject::~JGuiObject() -{ - // JGERelease(); -} - - -bool JGuiObject::Leaving(JButton key __attribute__((unused))) -{ - return true; -} - - -bool JGuiObject::ButtonPressed() -{ - return false; -} - - -void JGuiObject::Entering() -{ - -} - - -int JGuiObject::GetId() -{ - return mId; -} - - -void JGuiObject::Update(float dt __attribute__((unused))) -{ -} - -ostream& operator<<(ostream &out, const JGuiObject &j) -{ - return j.toString(out); -} - -JGuiController::JGuiController(int id, JGuiListener* listener) : mId(id), mListener(listener) -{ - mEngine = JGE::GetInstance(); - - mBg = NULL; - mShadingBg = NULL; - - mCount = 0; - mCurr = 0; - - mCursorX = SCREEN_WIDTH/2; - mCursorY = SCREEN_HEIGHT/2; - mShowCursor = false; - - mActionButton = JGE_BTN_OK; - mCancelButton = JGE_BTN_MENU; - - mStyle = JGUI_STYLE_WRAPPING; - - mActive = true; -} - - -JGuiController::~JGuiController() -{ - for (int i=0;iRender(); -} -bool JGuiController::CheckUserInput(JButton key){ - - if (!mCount) return false; - if (key == mActionButton) - { - if (mObjects[mCurr] != NULL && mObjects[mCurr]->ButtonPressed()) - { - if (mListener != NULL) - mListener->ButtonPressed(mId, mObjects[mCurr]->GetId()); - return true; - } - } - else if (key == mCancelButton) - { - if (mListener != NULL) - { - mListener->ButtonPressed(mId, kCancelMenuID); - } - } - else if ((JGE_BTN_LEFT == key) || (JGE_BTN_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(JGE_BTN_UP)) - { - mCurr = n; - mObjects[mCurr]->Entering(); - } - return true; - } - else if ((JGE_BTN_RIGHT == key) || (JGE_BTN_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(JGE_BTN_DOWN)) - { - mCurr = n; - mObjects[mCurr]->Entering(); - } - return true; - } - else - { // a dude may have clicked somewhere, we're gonna select the closest object from where he clicked - int x, y; - unsigned int distance2; - unsigned int minDistance2 = -1; - int n = mCurr; - if(mEngine->GetLeftClickCoordinates(x, y)) - { - for(int i = 0; i < mCount; i++) - { - int top, left; - if(mObjects[i]->getTopLeft(top, left)) - { - distance2 = (top-y)*(top-y) + (left-x)*(left-x); - if(distance2 < minDistance2) - { - minDistance2 = distance2; - n = i; - } - } - } - - if (n != mCurr && mObjects[mCurr] != NULL && mObjects[mCurr]->Leaving(JGE_BTN_DOWN)) - { - mCurr = n; - mObjects[mCurr]->Entering(); - } - mEngine->LeftClickedProcessed(); - return true; - } - } - return false; -} -void JGuiController::Update(float dt) -{ - for (int i=0;iUpdate(dt); - - JButton key = mEngine->ReadButton(); - CheckUserInput(key); -} - - -void JGuiController::Add(JGuiObject* ctrl) -{ - mObjects.push_back(ctrl); - mCount++; -} - -void JGuiController::RemoveAt(int i){ - if (!mObjects[i]) return; - mObjects.erase(mObjects.begin()+i); - delete mObjects[i]; - mCount--; - if (mCurr == mCount) - mCurr = 0; - return; -} - -void JGuiController::Remove(int id) -{ - for (int i=0;iGetId()==id) { - RemoveAt(i); - return; - } - } -} - - -void JGuiController::Remove(JGuiObject* ctrl) -{ - for (int i=0;i +// +//------------------------------------------------------------------------------------- + +#include "../include/JGE.h" +#include "../include/JGui.h" + +JGE* JGuiObject::mEngine = NULL; + +JGE* JGuiController::mEngine = NULL; + +JGuiObject::JGuiObject(int id) : + mId(id) +{ + mEngine = JGE::GetInstance(); +} + +JGuiObject::~JGuiObject() +{ + // JGERelease(); +} + +bool JGuiObject::Leaving(JButton key __attribute__((unused))) +{ + return true; +} + +bool JGuiObject::ButtonPressed() +{ + return false; +} + +void JGuiObject::Entering() +{ + +} + +int JGuiObject::GetId() +{ + return mId; +} + +void JGuiObject::Update(float dt __attribute__((unused))) +{ +} + +ostream& operator<<(ostream &out, const JGuiObject &j) +{ + return j.toString(out); +} + +JGuiController::JGuiController(int id, JGuiListener* listener) : + mId(id), mListener(listener) +{ + mEngine = JGE::GetInstance(); + + mBg = NULL; + mShadingBg = NULL; + + mCount = 0; + mCurr = 0; + + mCursorX = SCREEN_WIDTH / 2; + mCursorY = SCREEN_HEIGHT / 2; + mShowCursor = false; + + mActionButton = JGE_BTN_OK; + mCancelButton = JGE_BTN_MENU; + + mStyle = JGUI_STYLE_WRAPPING; + + mActive = true; +} + +JGuiController::~JGuiController() +{ + for (int i = 0; i < mCount; i++) + if (mObjects[i] != NULL) delete mObjects[i]; + +} + +void JGuiController::Render() +{ + for (int i = 0; i < mCount; i++) + if (mObjects[i] != NULL) mObjects[i]->Render(); +} +bool JGuiController::CheckUserInput(JButton key) +{ + + if (!mCount) return false; + if (key == mActionButton) + { + if (!mObjects.empty() && mObjects[mCurr] != NULL && mObjects[mCurr]->ButtonPressed()) + { + if (mListener != NULL) mListener->ButtonPressed(mId, mObjects[mCurr]->GetId()); + return true; + } + } + else if (key == mCancelButton) + { + if (mListener != NULL) + { + mListener->ButtonPressed(mId, kCancelMenuID); + } + } + else if (JGE_BTN_CANCEL == key) + { + if (mListener != NULL) mListener->ButtonPressed(mId, kInfoMenuID); + } + + else if ((JGE_BTN_LEFT == key) || (JGE_BTN_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(JGE_BTN_UP)) + { + mCurr = n; + mObjects[mCurr]->Entering(); + } + return true; + } + else if ((JGE_BTN_RIGHT == key) || (JGE_BTN_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(JGE_BTN_DOWN)) + { + mCurr = n; + mObjects[mCurr]->Entering(); + } + return true; + } + else + { // a dude may have clicked somewhere, we're gonna select the closest object from where he clicked + int x, y; + unsigned int distance2; + unsigned int minDistance2 = -1; + int n = mCurr; + if (mEngine->GetLeftClickCoordinates(x, y)) + { + for (int i = 0; i < mCount; i++) + { + int top, left; + if (mObjects[i]->getTopLeft(top, left)) + { + distance2 = (top - y) * (top - y) + (left - x) * (left - x); + if (distance2 < minDistance2) + { + minDistance2 = distance2; + n = i; + } + } + } + + if (n != mCurr && mObjects[mCurr] != NULL && mObjects[mCurr]->Leaving(JGE_BTN_DOWN)) + { + mCurr = n; + mObjects[mCurr]->Entering(); + } + mEngine->LeftClickedProcessed(); + return true; + } + } + return false; +} +void JGuiController::Update(float dt) +{ + for (int i = 0; i < mCount; i++) + if (mObjects[i] != NULL) mObjects[i]->Update(dt); + + JButton key = mEngine->ReadButton(); + CheckUserInput(key); +} + +void JGuiController::Add(JGuiObject* ctrl) +{ + mObjects.push_back(ctrl); + mCount++; +} + +void JGuiController::RemoveAt(int i) +{ + if (!mObjects[i]) return; + mObjects.erase(mObjects.begin() + i); + delete mObjects[i]; + mCount--; + if (mCurr == mCount) mCurr = 0; + return; +} + +void JGuiController::Remove(int id) +{ + for (int i = 0; i < mCount; i++) + { + if (mObjects[i] != NULL && mObjects[i]->GetId() == id) + { + RemoveAt(i); + return; + } + } +} + +void JGuiController::Remove(JGuiObject* ctrl) +{ + for (int i = 0; i < mCount; i++) + { + if (mObjects[i] != NULL && mObjects[i] == ctrl) + { + RemoveAt(i); + return; + } + } +} + +void JGuiController::SetActionButton(JButton button) +{ + mActionButton = button; +} +void JGuiController::SetStyle(int style) +{ + mStyle = style; +} +void JGuiController::SetCursor(JSprite* cursor) +{ + mCursor = cursor; +} +bool JGuiController::IsActive() +{ + return mActive; +} +void JGuiController::SetActive(bool flag) +{ + mActive = flag; +} diff --git a/projects/mtg/Makefile b/projects/mtg/Makefile index 30e8d756b..df960fc71 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/AllAbilities.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/CardSelectorSingleton.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckEditorMenu.o objs/DeckMenu.o objs/DeckMenuItem.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/DeckManager.o objs/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.o objs/GameStateStory.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/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/MTGPack.o objs/MTGRules.o objs/Navigator.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/Pos.o objs/PrecompiledHeader.o objs/PriceList.o objs/ReplacementEffects.o objs/Rules.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/SimplePad.o objs/StoryFlow.o objs/StyleManager.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TextScroller.o objs/ThisDescriptor.o objs/Token.o objs/Translate.o objs/TranslateKeys.o objs/Trash.o objs/utils.o objs/WEvent.o objs/WResourceManager.o objs/WCachedResource.o objs/WDataSrc.o objs/WGui.o objs/WFilter.o objs/Tasks.o objs/WFont.o +OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/AllAbilities.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/CardSelectorSingleton.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckEditorMenu.o objs/DeckMenu.o objs/DeckMenuItem.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/DeckManager.o objs/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.o objs/GameStateStory.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/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/MTGPack.o objs/MTGRules.o objs/Navigator.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/Pos.o objs/PrecompiledHeader.o objs/PriceList.o objs/ReplacementEffects.o objs/Rules.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/SimplePad.o objs/SimplePopup.o objs/StoryFlow.o objs/StyleManager.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TextScroller.o objs/ThisDescriptor.o objs/Token.o objs/Translate.o objs/TranslateKeys.o objs/Trash.o objs/utils.o objs/WEvent.o objs/WResourceManager.o objs/WCachedResource.o objs/WDataSrc.o objs/WGui.o objs/WFilter.o objs/Tasks.o objs/WFont.o DEPS = $(patsubst objs/%.o, deps/%.d, $(OBJS)) RESULT = $(shell psp-config --psp-prefix 2> Makefile.cache) diff --git a/projects/mtg/include/DeckMenu.h b/projects/mtg/include/DeckMenu.h index 2b21ec783..a248a27b9 100644 --- a/projects/mtg/include/DeckMenu.h +++ b/projects/mtg/include/DeckMenu.h @@ -1,63 +1,75 @@ -/* - A class for very simple menus structure -*/ -#ifndef _DeckMenu_H_ -#define _DeckMenu_H_ - -#include -#include "WFont.h" -#include "hge/hgeparticle.h" -#include "DeckMetaData.h" -#include "TextScroller.h" - -class DeckMenu:public JGuiController{ - protected: - - float mHeight, mWidth, mX, mY; - float titleX, titleY, titleWidth; - float descX, descY, descHeight, descWidth; - float statsX, statsY, statsHeight, statsWidth; - float avatarX, avatarY; - float starsOffsetX; - - bool menuInitialized; - string backgroundName; - - int fontId; - string title; - string displayTitle; - WFont * mFont; - float menuFontScale; - float titleFontScale; - - int maxItems, startId; - - float selectionT, selectionY; - float timeOpen; - - static hgeParticleSystem* stars; - - void initMenuItems(); - string getDescription(); - string getMetaInformation(); - - public: - TextScroller * scroller; - bool autoTranslate; - DeckMenu(int id, JGuiListener* listener, int fontId, const string _title = "", const float& mFontScale = 1.0f ); - ~DeckMenu(); - - void Render(); - void Update(float dt); - void Add(int id, const char * Text, string desc = "", bool forceFocus = false, DeckMetaData *deckMetaData = NULL); - void Close(); - void updateScroller(); - void RenderBackground(); - - float selectionTargetY; - bool closed; - static void destroy(); -}; - - -#endif +/* + A class for menus with a fixed layout + */ +#ifndef _DeckMenu_H_ +#define _DeckMenu_H_ + +#include +#include "WFont.h" +#include "hge/hgeparticle.h" +#include "DeckMetaData.h" +#include "TextScroller.h" + +class DeckMenu: public JGuiController +{ +protected: + + float mHeight, mWidth, mX, mY; + float titleX, titleY, titleWidth; + float descX, descY, descHeight, descWidth; + float statsX, statsY, statsHeight, statsWidth; + float avatarX, avatarY; + float detailedInfoBoxX, detailedInfoBoxY; + float starsOffsetX; + + bool menuInitialized; + string backgroundName; + + int fontId; + string title; + string displayTitle; + WFont * mFont; + float menuFontScale; + float titleFontScale; + + int maxItems, startId; + + float selectionT, selectionY; + float timeOpen; + + static hgeParticleSystem* stars; + + void initMenuItems(); + string getDescription(); + string getMetaInformation(); + DeckMetaData *selectedDeck; + +public: + TextScroller * scroller; + bool autoTranslate; + + //used for detailed info button + JQuad * pspIcons[8]; + JTexture * pspIconsTexture; + DeckMetaData * getSelectedDeck(); + bool selectedDeckHasDetails(); + int selectedDeckId; + bool showDetailsScreen; + bool enableDetails; + float selectionTargetY; + bool closed; + + DeckMenu(int id, JGuiListener* listener, int fontId, const string _title = "", const int& startIndex = 0, const float& mFontScale = 1.0f); + ~DeckMenu(); + + void Render(); + void Update(float dt); + void Add(int id, const char * Text, string desc = "", bool forceFocus = false, DeckMetaData *deckMetaData = NULL); + void Close(); + void updateScroller(); + void RenderBackground(); + + static void destroy(); +}; + +#endif diff --git a/projects/mtg/include/DeckStats.h b/projects/mtg/include/DeckStats.h index efa577f9b..6096d808e 100644 --- a/projects/mtg/include/DeckStats.h +++ b/projects/mtg/include/DeckStats.h @@ -1,89 +1,101 @@ #ifndef _DECKSTATS_H_ #define _DECKSTATS_H_ - #include #include #include #include "MTGDefinitions.h" +#include + using namespace std; class Player; class GameObserver; -class DeckStat{ +class DeckStat +{ public: - int nbgames; - int victories; - DeckStat(int _nbgames = 0 , int _victories = 0):nbgames(_nbgames),victories(_victories){}; - int percentVictories(); + int nbgames; + int victories; + DeckStat(int _nbgames = 0, int _victories = 0); + int percentVictories(); }; -class DeckStats{ +class DeckStats +{ protected: - static DeckStats * mInstance; + static DeckStats * mInstance; public: - mapstats; - static DeckStats * GetInstance(); - void saveStats(Player * player, Player * opponent, GameObserver * game); - void save(const char * filename); - void save(Player * player); - void load(const char * filename); - void load(Player * player); - void cleanStats(); - ~DeckStats(); - int percentVictories(string opponentsDeckFile); - int percentVictories(); - DeckStat * getDeckStat(string opponentsFile); - int nbGames(); + map stats; + static DeckStats * GetInstance(); + void saveStats(Player * player, Player * opponent, GameObserver * game); + void save(const char * filename); + void save(Player * player); + void load(const char * filename); + void load(Player * player); + void cleanStats(); + ~DeckStats(); + int percentVictories(string opponentsDeckFile); + int percentVictories(); + DeckStat * getDeckStat(string opponentsFile); + int nbGames(); }; +class StatsWrapper +{ -class StatsWrapper { - -public: - - StatsWrapper( int deckId ); - ~StatsWrapper(); - - // Stats parameters and status - int currentPage; - int pageCount; - bool needUpdate; - - // Actual stats - int percentVictories; - int gamesPlayed; - int cardCount; - int countLands; - int totalPrice; - int totalManaCost; - float avgManaCost; - int totalCreatureCost; - float avgCreatureCost; - int totalSpellCost; - float avgSpellCost; - int countManaProducers; - - int countCreatures, countSpells, countInstants, countEnchantments, countSorceries, countArtifacts; - - float noLandsProbInTurn[Constants::STATS_FOR_TURNS]; - float noCreaturesProbInTurn[Constants::STATS_FOR_TURNS]; - - int countCardsPerCost[Constants::STATS_MAX_MANA_COST+1]; - int countCardsPerCostAndColor[Constants::STATS_MAX_MANA_COST+1][Constants::MTG_NB_COLORS+1]; - int countCreaturesPerCost[Constants::STATS_MAX_MANA_COST+1]; - int countCreaturesPerCostAndColor[Constants::STATS_MAX_MANA_COST+1][Constants::MTG_NB_COLORS+1]; - int countSpellsPerCost[Constants::STATS_MAX_MANA_COST+1]; - int countSpellsPerCostAndColor[Constants::STATS_MAX_MANA_COST+1][Constants::MTG_NB_COLORS+1]; - int countLandsPerColor[Constants::MTG_NB_COLORS+1]; - int countBasicLandsPerColor[Constants::MTG_NB_COLORS+1]; - int countNonLandProducersPerColor[Constants::MTG_NB_COLORS+1]; - int totalCostPerColor[Constants::MTG_NB_COLORS+1]; - int totalColoredSymbols; - - vector aiDeckNames; - vector aiDeckStats; -}; +public: + + StatsWrapper(int deckId); + StatsWrapper(string filename); + ~StatsWrapper(); + + void initStatistics(string deckstats); + + // Stats parameters and status + int mDeckId; + int currentPage; + int pageCount; + bool needUpdate; + + // Actual stats + int percentVictories; + int gamesPlayed; + int cardCount; + int countLands; + int totalPrice; + int totalManaCost; + float avgManaCost; + int totalCreatureCost; + float avgCreatureCost; + int totalSpellCost; + float avgSpellCost; + int countManaProducers; + + int countCreatures, countSpells, countInstants, countEnchantments, countSorceries, countArtifacts; + + float noLandsProbInTurn[Constants::STATS_FOR_TURNS]; + float noCreaturesProbInTurn[Constants::STATS_FOR_TURNS]; + + int countCardsPerCost[Constants::STATS_MAX_MANA_COST + 1]; + int countCardsPerCostAndColor[Constants::STATS_MAX_MANA_COST + 1][Constants::MTG_NB_COLORS + 1]; + int countCreaturesPerCost[Constants::STATS_MAX_MANA_COST + 1]; + int countCreaturesPerCostAndColor[Constants::STATS_MAX_MANA_COST + 1][Constants::MTG_NB_COLORS + 1]; + int countSpellsPerCost[Constants::STATS_MAX_MANA_COST + 1]; + int countSpellsPerCostAndColor[Constants::STATS_MAX_MANA_COST + 1][Constants::MTG_NB_COLORS + 1]; + int countLandsPerColor[Constants::MTG_NB_COLORS + 1]; + int countBasicLandsPerColor[Constants::MTG_NB_COLORS + 1]; + int countNonLandProducersPerColor[Constants::MTG_NB_COLORS + 1]; + int totalCostPerColor[Constants::MTG_NB_COLORS + 1]; + int totalColoredSymbols; + + void updateStats(string filename, MTGAllCards * collection); + void updateStats(DeckDataWrapper *mtgDeck); + int countCardsByType(const char * _type, DeckDataWrapper * myDeck); + float noLuck(int n, int a, int x); + + vector aiDeckNames; + vector aiDeckStats; +}; #endif diff --git a/projects/mtg/include/GameStateDeckViewer.h b/projects/mtg/include/GameStateDeckViewer.h index cc280a17a..c773d44fe 100644 --- a/projects/mtg/include/GameStateDeckViewer.h +++ b/projects/mtg/include/GameStateDeckViewer.h @@ -1,157 +1,156 @@ -#ifndef _GAME_STATE_DECK_VIEWER_H_ -#define _GAME_STATE_DECK_VIEWER_H_ - -#include -#include - -#include - -#include "GameState.h" -#include "DeckEditorMenu.h" -#include "SimpleMenu.h" -#include "WResourceManager.h" -#include "CardGui.h" -#include "GameOptions.h" -#include "PriceList.h" -#include "PlayerData.h" -#include "DeckDataWrapper.h" -#include "DeckStats.h" -#include "WDataSrc.h" -#include "WGui.h" - -#define NO_USER_ACTIVITY_HELP_DELAY 10 -#define NO_USER_ACTIVITY_SHOWCARD_DELAY 0.1 - -enum - { - STAGE_TRANSITION_RIGHT = 0, - STAGE_TRANSITION_LEFT = 1, - STAGE_WAITING = 2, - STAGE_TRANSITION_UP = 3, - STAGE_TRANSITION_DOWN = 4, - STAGE_ONSCREEN_MENU = 5, - STAGE_WELCOME = 6, - STAGE_MENU = 7, - STAGE_FILTERS = 8 - }; - - -// TODO: need a better name for MENU_FIRST_MENU, this is reused for the 1st submenu of -// available options in the duel menu -enum -{ - MENU_CARD_PURCHASE = 2, - MENU_DECK_SELECTION = 10, - MENU_DECK_BUILDER = 11, - MENU_FIRST_DUEL_SUBMENU = 102, - MENU_LANGUAGE_SELECTION = 103, -}; - - // enums for menu options - // TODO: make these enums a little more descriptive. (ie should reflect what menu they are attached to ) -enum DECK_VIEWER_MENU_ITEMS - { - MENU_ITEM_NEW_DECK = -30, - MENU_ITEM_CHEAT_MODE = -12, - MENU_ITEM_CANCEL = kCancelMenuID, - MENU_ITEM_SAVE_RETURN_MAIN_MENU = 0, - MENU_ITEM_SAVE_RENAME = 1, - MENU_ITEM_SWITCH_DECKS_NO_SAVE = 2, - MENU_ITEM_MAIN_MENU = 3, - MENU_ITEM_EDITOR_CANCEL = kCancelMenuID, - MENU_ITEM_SAVE_AS_AI_DECK = 5, - MENU_ITEM_YES = 20, - MENU_ITEM_NO = 21, - MENU_ITEM_FILTER_BY = 22 - - - }; - -#define ALL_COLORS -1 - -#define ROTATE_LEFT 1; -#define ROTATE_RIGHT 0; - -#define HIGH_SPEED 15.0 -#define MED_SPEED 5.0f -#define LOW_SPEED 1.5 - -#define MAX_SAVED_FILTERS 8 - -class GameStateDeckViewer: public GameState, public JGuiListener -{ -private: - JQuad * mIcons[7]; - JQuad * pspIcons[8]; - JTexture * pspIconsTexture; - float last_user_activity; - float onScreenTransition; - float mRotation; - float mSlide; - int mAlpha; - int mStage; - int nbDecks; - int deckNum; - int useFilter; - JMusic * bgMusic; - JQuad * backQuad; - int lastPos; - int lastTotal; - - WGuiFilters * filterMenu; - WSrcDeckViewer * source; - - DeckEditorMenu * welcome_menu; - SimpleMenu * subMenu; - DeckEditorMenu * menu; - PriceList* pricelist; - PlayerData * playerdata; - int price; - DeckDataWrapper * displayed_deck; - DeckDataWrapper * myDeck; - DeckDataWrapper * myCollection; - MTGCard * cardIndex[7]; - StatsWrapper *stw; - - int hudAlpha; - string newDeckname; - bool isAIDeckSave; - bool mSwitching; - void saveDeck(); //Saves the deck and additional necessary information - void saveAsAIDeck(string deckName); // saves deck as an AI Deck - int getCurrentPos(); - -public: - GameStateDeckViewer(GameApp* parent); - virtual ~GameStateDeckViewer(); - void updateDecks(); - void rotateCards(int direction); - void loadIndexes(); - void updateFilters(); - void rebuildFilters(); - void switchDisplay(); - void Start(); - virtual void End(); - void addRemove(MTGCard * card); - virtual void Update(float dt); - void renderOnScreenBasicInfo(); - void renderSlideBar(); - void renderDeckBackground(); - void renderOnScreenMenu(); - virtual void renderCard(int id, float rotation); - virtual void renderCard (int id); - virtual void Render(); - int loadDeck(int deckid); - void LoadDeckStatistics(int deckId); - - void buildEditorMenu(); - virtual void ButtonPressed(int controllerId, int controlId); - void updateStats(); - int countCardsByType(const char * _type); -}; - -// n cards total, a of them are desired, x drawn -// returns probability of no A's -float noLuck(int n, int a, int x); - -#endif +#ifndef _GAME_STATE_DECK_VIEWER_H_ +#define _GAME_STATE_DECK_VIEWER_H_ + +#include +#include + +#include + +#include "GameState.h" +#include "DeckEditorMenu.h" +#include "SimpleMenu.h" +#include "WResourceManager.h" +#include "CardGui.h" +#include "GameOptions.h" +#include "PriceList.h" +#include "PlayerData.h" +#include "DeckDataWrapper.h" +#include "DeckStats.h" +#include "WDataSrc.h" +#include "WGui.h" + +#define NO_USER_ACTIVITY_HELP_DELAY 10 +#define NO_USER_ACTIVITY_SHOWCARD_DELAY 0.1 + +enum +{ + STAGE_TRANSITION_RIGHT = 0, + STAGE_TRANSITION_LEFT = 1, + STAGE_WAITING = 2, + STAGE_TRANSITION_UP = 3, + STAGE_TRANSITION_DOWN = 4, + STAGE_ONSCREEN_MENU = 5, + STAGE_WELCOME = 6, + STAGE_MENU = 7, + STAGE_FILTERS = 8 +}; + +// TODO: need a better name for MENU_FIRST_MENU, this is reused for the 1st submenu of +// available options in the duel menu +enum +{ + MENU_CARD_PURCHASE = 2, + MENU_DECK_SELECTION = 10, + MENU_DECK_BUILDER = 11, + MENU_FIRST_DUEL_SUBMENU = 102, + MENU_LANGUAGE_SELECTION = 103, +}; + +// enums for menu options +// TODO: make these enums a little more descriptive. (ie should reflect what menu they are attached to ) +enum DECK_VIEWER_MENU_ITEMS +{ + MENU_ITEM_NEW_DECK = -30, + MENU_ITEM_CHEAT_MODE = -12, + MENU_ITEM_CANCEL = kCancelMenuID, + MENU_ITEM_SAVE_RETURN_MAIN_MENU = 0, + MENU_ITEM_SAVE_RENAME = 1, + MENU_ITEM_SWITCH_DECKS_NO_SAVE = 2, + MENU_ITEM_MAIN_MENU = 3, + MENU_ITEM_EDITOR_CANCEL = kCancelMenuID, + MENU_ITEM_SAVE_AS_AI_DECK = 5, + MENU_ITEM_YES = 20, + MENU_ITEM_NO = 21, + MENU_ITEM_FILTER_BY = 22, + MENUITEM_MORE_INFO = kInfoMenuID + +}; + +#define ALL_COLORS -1 + +#define ROTATE_LEFT 1; +#define ROTATE_RIGHT 0; + +#define HIGH_SPEED 15.0 +#define MED_SPEED 5.0f +#define LOW_SPEED 1.5 + +#define MAX_SAVED_FILTERS 8 + +class GameStateDeckViewer: public GameState, public JGuiListener +{ +private: + JQuad * mIcons[7]; + JQuad * pspIcons[8]; + JTexture * pspIconsTexture; + float last_user_activity; + float onScreenTransition; + float mRotation; + float mSlide; + int mAlpha; + int mStage; + int nbDecks; + int deckNum; + int useFilter; + JMusic * bgMusic; + JQuad * backQuad; + int lastPos; + int lastTotal; + + WGuiFilters * filterMenu; + WSrcDeckViewer * source; + + DeckEditorMenu * welcome_menu; + SimpleMenu * subMenu; + DeckEditorMenu * menu; + PriceList* pricelist; + PlayerData * playerdata; + int price; + DeckDataWrapper * displayed_deck; + DeckDataWrapper * myDeck; + DeckDataWrapper * myCollection; + MTGCard * cardIndex[7]; + StatsWrapper *stw; + + int hudAlpha; + string newDeckname; + bool isAIDeckSave; + bool mSwitching; + void saveDeck(); //Saves the deck and additional necessary information + void saveAsAIDeck(string deckName); // saves deck as an AI Deck + int getCurrentPos(); + +public: + GameStateDeckViewer(GameApp* parent); + virtual ~GameStateDeckViewer(); + void updateDecks(); + void rotateCards(int direction); + void loadIndexes(); + void updateFilters(); + void rebuildFilters(); + void switchDisplay(); + void Start(); + virtual void End(); + void addRemove(MTGCard * card); + virtual void Update(float dt); + void renderOnScreenBasicInfo(); + void renderSlideBar(); + void renderDeckBackground(); + void renderOnScreenMenu(); + virtual void renderCard(int id, float rotation); + virtual void renderCard(int id); + virtual void Render(); + int loadDeck(int deckid); + void LoadDeckStatistics(int deckId); + + void buildEditorMenu(); + virtual void ButtonPressed(int controllerId, int controlId); + void updateStats(); + int countCardsByType(const char * _type); +}; + +// n cards total, a of them are desired, x drawn +// returns probability of no A's +float noLuck(int n, int a, int x); + +#endif diff --git a/projects/mtg/include/GameStateDuel.h b/projects/mtg/include/GameStateDuel.h index 9868e0aef..bffcef3f3 100644 --- a/projects/mtg/include/GameStateDuel.h +++ b/projects/mtg/include/GameStateDuel.h @@ -1,73 +1,76 @@ -#ifndef _GAME_STATE_DUEL_H_ -#define _GAME_STATE_DUEL_H_ - - -#include "GameState.h" -#include "SimpleMenu.h" -#include "DeckMenu.h" -#include "MTGDeck.h" -#include "GameObserver.h" - -#define CHOOSE_OPPONENT 7 - -#ifdef TESTSUITE -class TestSuite; -#endif -class Credits; -class Rules; - - -class GameStateDuel: public GameState, public JGuiListener -{ - private: -#ifdef TESTSUITE - TestSuite * testSuite; -#endif - Credits * credits; - int mGamePhase; - Player * mCurrentPlayer; - Player * mPlayers[2]; - MTGPlayerCards * deck[2]; - GameObserver * game; - DeckMenu * deckmenu; - DeckMenu * opponentMenu; - SimpleMenu * menu; - bool premadeDeck; - int OpponentsDeckid; - string musictrack; - Rules * rules; - - bool MusicExist(string FileName); - void loadPlayer(int playerId, int decknb = 0, int isAI = 0); - void ensureOpponentMenu(); //loads the opponentMenu if it doesn't exist - void initScroller(); - - public: - GameStateDuel(GameApp* parent); - virtual ~GameStateDuel(); -#ifdef TESTSUITE - void loadTestSuitePlayers(); -#endif - virtual void ButtonPressed(int ControllerId, int ControlId); - virtual void Start(); - virtual void End(); - virtual void Update(float dt); - virtual void Render(); - void initRand (unsigned seed = 0); - - enum ENUM_DUEL_STATE_MENU_ITEM - { - MENUITEM_CANCEL = kCancelMenuID, - MENUITEM_NEW_DECK = -10, - MENUITEM_RANDOM_PLAYER = -11, - MENUITEM_RANDOM_AI = -12, - MENUITEM_MAIN_MENU = -13, - MENUITEM_EVIL_TWIN = -14, - MENUITEM_MULLIGAN = -15 - }; - -}; - - -#endif - +#ifndef _GAME_STATE_DUEL_H_ +#define _GAME_STATE_DUEL_H_ + +#include "GameState.h" +#include "SimpleMenu.h" +#include "SimplePopup.h" +#include "DeckMenu.h" +#include "MTGDeck.h" +#include "GameObserver.h" + +#define CHOOSE_OPPONENT 7 + +#ifdef TESTSUITE +class TestSuite; +#endif +class Credits; +class Rules; + +class GameStateDuel: public GameState, public JGuiListener +{ +private: +#ifdef TESTSUITE + TestSuite * testSuite; +#endif + Credits * credits; + int mGamePhase; + Player * mCurrentPlayer; + Player * mPlayers[2]; + MTGPlayerCards * deck[2]; + GameObserver * game; + DeckMenu * deckmenu; + DeckMenu * opponentMenu; + SimpleMenu * menu; + SimplePopup * popupScreen; // used for informational screens, modal + static int selectedPlayerDeckId; + static int selectedAIDeckId; + + bool premadeDeck; + int OpponentsDeckid; + string musictrack; + Rules * rules; + + bool MusicExist(string FileName); + void loadPlayer(int playerId, int decknb = 0, int isAI = 0); + void ensureOpponentMenu(); //loads the opponentMenu if it doesn't exist + void initScroller(); + +public: + GameStateDuel(GameApp* parent); + virtual ~GameStateDuel(); +#ifdef TESTSUITE + void loadTestSuitePlayers(); +#endif + virtual void ButtonPressed(int ControllerId, int ControlId); + virtual void Start(); + virtual void End(); + virtual void Update(float dt); + virtual void Render(); + void initRand(unsigned seed = 0); + + enum ENUM_DUEL_STATE_MENU_ITEM + { + MENUITEM_CANCEL = kCancelMenuID, + MENUITEM_NEW_DECK = -10, + MENUITEM_RANDOM_PLAYER = -11, + MENUITEM_RANDOM_AI = -12, + MENUITEM_MAIN_MENU = -13, + MENUITEM_EVIL_TWIN = -14, + MENUITEM_MULLIGAN = -15, + MENUITEM_MORE_INFO = kInfoMenuID + }; + +}; + +#endif + diff --git a/projects/mtg/include/Player.h b/projects/mtg/include/Player.h index 8a0604b12..9753f3cf5 100644 --- a/projects/mtg/include/Player.h +++ b/projects/mtg/include/Player.h @@ -1,78 +1,102 @@ -#ifndef _PLAYER_H_ -#define _PLAYER_H_ - -#include "JGE.h" -#include "MTGGameZones.h" -#include "Damage.h" -#include "Targetable.h" - -class MTGDeck; -class MTGPlayerCards; -class MTGInPlay; -class ManaPool; - -class Player: public Damageable{ - protected: - ManaPool * manaPool; - - public: - enum ENUM_PLAY_MODE - { - MODE_TEST_SUITE, - MODE_HUMAN, - MODE_AI, - }; - - - virtual void End(); - int typeAsTarget(){return TARGET_PLAYER;} - const string getDisplayName() const; - virtual int displayStack(){return 1;} - JTexture * mAvatarTex; - JQuad * mAvatar; - int playMode; - int canPutLandsIntoPlay; - int nomaxhandsize; - int castedspellsthisturn; - int onlyonecast; - int castcount; - int nocreatureinstant; - int nospellinstant; - int onlyoneinstant; - int castrestrictedcreature; - int castrestrictedspell; - MTGPlayerCards * game; - int afterDamage(); - int poisoned(); - int damaged(); - int prevented(); - Player(MTGDeck * deck, string deckFile, string deckFileSmall); - virtual ~Player(); - void unTapPhase(); - MTGInPlay * inPlay(); - ManaPool * getManaPool(); - void cleanupPhase(); - virtual int Act(float dt){return 0;}; - virtual int isAI(){return 0;}; - Player * opponent(); - int getId(); - JQuad * getIcon(); - string deckFile; - string deckFileSmall; - string deckName; - - virtual int receiveEvent(WEvent * event){return 0;}; - virtual void Render(){}; - void loadAvatar(string file); -}; - -class HumanPlayer: public Player{ - public: - HumanPlayer(MTGDeck * deck, string deckFile, string deckFileSmall); - HumanPlayer(string deckFile); - -}; - -ostream& operator<<(ostream&, const Player&); - -#endif +#ifndef _PLAYER_H_ +#define _PLAYER_H_ + +#include "JGE.h" +#include "MTGGameZones.h" +#include "Damage.h" +#include "Targetable.h" + +class MTGDeck; +class MTGPlayerCards; +class MTGInPlay; +class ManaPool; + +class Player: public Damageable +{ +protected: + ManaPool * manaPool; + +public: + enum ENUM_PLAY_MODE + { + MODE_TEST_SUITE, MODE_HUMAN, MODE_AI, + }; + + JTexture * mAvatarTex; + JQuad * mAvatar; + int playMode; + int canPutLandsIntoPlay; + int nomaxhandsize; + int castedspellsthisturn; + int onlyonecast; + int castcount; + int nocreatureinstant; + int nospellinstant; + int onlyoneinstant; + int castrestrictedcreature; + int castrestrictedspell; + MTGPlayerCards * game; + string deckFile; + string deckFileSmall; + string deckName; + + Player(MTGDeck * deck, string deckFile, string deckFileSmall); + virtual ~Player(); + + virtual void End(); + virtual int displayStack() + { + return 1; + } + const string getDisplayName() const; + int typeAsTarget() + { + return TARGET_PLAYER; + } + + int afterDamage(); + int poisoned(); + int damaged(); + int prevented(); + void unTapPhase(); + MTGInPlay * inPlay(); + ManaPool * getManaPool(); + void cleanupPhase(); + virtual int Act(float dt) + { + return 0; + } + + + virtual int isAI() + { + return 0; + } + + Player * opponent(); + int getId(); + JQuad * getIcon(); + + virtual int receiveEvent(WEvent * event) + { + return 0; + } + + virtual void Render() + { + } + + void loadAvatar(string file); +}; + +class HumanPlayer: public Player +{ +public: + HumanPlayer(MTGDeck * deck, string deckFile, string deckFileSmall); + HumanPlayer(string deckFile); + +}; + +ostream& operator<<(ostream&, const Player&); + +#endif diff --git a/projects/mtg/include/SimplePopup.h b/projects/mtg/include/SimplePopup.h new file mode 100644 index 000000000..8c8f75295 --- /dev/null +++ b/projects/mtg/include/SimplePopup.h @@ -0,0 +1,48 @@ +/* + * SimplePopup.h + * Created on: Nov 18, 2010 + * + * Simple popup dialog box for displaying information only. + */ + +#ifndef SIMPLEPOPUP_H_ +#define SIMPLEPOPUP_H_ + +#pragma once +#include +#include +#include +#include + +class SimplePopup: public JGuiController +{ + +private: + float mHeight, mWidth, mX, mY; + int mMaxLines; + int mFontId; + DeckMetaData * mDeckInformation; + string mTitle; + WFont *mTextFont; + StatsWrapper *stw; + + void drawHorzPole(float x, float y, float width); + void drawVertPole(float x, float y, float height); + +public: + MTGAllCards * mCollection; + bool autoTranslate; + bool closed; + + SimplePopup(int id, JGuiListener* listener, const int fontId, const char * _title = "", DeckMetaData* deckInfo = NULL, MTGAllCards * collection = NULL); + ~SimplePopup(void); + void Render(); + void Update(DeckMetaData* deckMetaData); + + string getDetailedInformation(string deckFilename); + + void Update(float dt); + void Close(); +}; + +#endif /* SIMPLEPOPUP_H_ */ diff --git a/projects/mtg/src/DeckEditorMenu.cpp b/projects/mtg/src/DeckEditorMenu.cpp index aa87021f0..14babea76 100644 --- a/projects/mtg/src/DeckEditorMenu.cpp +++ b/projects/mtg/src/DeckEditorMenu.cpp @@ -1,103 +1,102 @@ -#include "PrecompiledHeader.h" -#include "DeckEditorMenu.h" -#include "DeckDataWrapper.h" -#include "DeckStats.h" -#include "JTypes.h" -#include "GameApp.h" -#include - -DeckEditorMenu::DeckEditorMenu(int id, JGuiListener* listener, int fontId, const char * _title, DeckDataWrapper *_selectedDeck, - StatsWrapper *stats) : - DeckMenu(id, listener, fontId, _title), selectedDeck(_selectedDeck), stw(stats) -{ - backgroundName = "DeckEditorMenuBackdrop"; - - deckTitle = selectedDeck ? selectedDeck->parent->meta_name : ""; - - mX = 123; - mY = 70; - starsOffsetX = 50; - - titleX = 110; // center point in title box - titleY = 25; - titleWidth = 180; // width of inner box of title - - descX = 275; - descY = 80; - descHeight = 154; - descWidth = 175; - - statsHeight = 50; - statsWidth = 185; - statsX = 280; - statsY = 12; - - avatarX = 222; - avatarY = 8; - - float scrollerWidth = 80; - SAFE_DELETE(scroller); // need to delete the scroller init in the base class - scroller = NEW TextScroller(Fonts::MAIN_FONT, 40, 230, scrollerWidth, 100, 1, 1); - -} - -void DeckEditorMenu::Render() -{ - JRenderer *r = JRenderer::GetInstance(); - r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(200,0,0,0)); - - DeckMenu::Render(); - if (deckTitle.size() > 0) - { - WFont *mainFont = resources.GetWFont(Fonts::OPTION_FONT); - DWORD currentColor = mainFont->GetColor(); - mainFont->SetColor(ARGB(255,255,255,255)); - mainFont->DrawString(deckTitle.c_str(), statsX + (statsWidth / 2), statsHeight / 2, JGETEXT_CENTER); - mainFont->SetColor(currentColor); - } - - if (stw && selectedDeck) - drawDeckStatistics(); - -} - -void DeckEditorMenu::drawDeckStatistics() -{ - ostringstream deckStatsString; - - deckStatsString - << "------- Deck Summary -----" << endl - << "Cards: "<< selectedDeck->getCount() << endl - << "Creatures: "<< setw(2) << stw->countCreatures - << " Enchantments: " << stw->countEnchantments << endl - << "Instants: " << setw(4) << stw->countInstants - << " Sorceries: " << setw(2) << stw->countSorceries << endl - << "Lands: " - << "A: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_ARTIFACT ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_ARTIFACT ] << " " - << "G: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_GREEN ] + stw->countLandsPerColor[ Constants::MTG_COLOR_GREEN ] << " " - << "R: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_RED ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_RED ] << " " - << "U: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_BLUE ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_BLUE ] << " " - << "B: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_BLACK ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_BLACK ] << " " - << "W: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_WHITE ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_WHITE ] << endl - - << " --- Card color count --- " << endl - << "A: " << setw(2) << left << selectedDeck->getCount(Constants::MTG_COLOR_ARTIFACT) << " " - << "G: " << setw(2) << left << selectedDeck->getCount(Constants::MTG_COLOR_GREEN) << " " - << "U: " << setw(2) << left << selectedDeck->getCount(Constants::MTG_COLOR_BLUE) << " " - << "R: " << setw(2) << left << selectedDeck->getCount(Constants::MTG_COLOR_RED) << " " - << "B: " << setw(2) << left << selectedDeck->getCount(Constants::MTG_COLOR_BLACK) << " " - << "W: " << setw(2) << left << selectedDeck->getCount(Constants::MTG_COLOR_WHITE) << endl - - << " --- Average Cost --- " << endl - << "Creature: "<< setprecision(2) << stw->avgCreatureCost << endl - << "Mana: " << setprecision(2) << stw->avgManaCost << " " - << "Spell: " << setprecision(2) << stw->avgSpellCost << endl; - - WFont *mainFont = resources.GetWFont( Fonts::MAIN_FONT ); - mainFont->DrawString( deckStatsString.str().c_str(), descX, descY + 25 ); -} - -DeckEditorMenu::~DeckEditorMenu() -{ - SAFE_DELETE( scroller ); -} +#include "PrecompiledHeader.h" +#include "DeckEditorMenu.h" +#include "DeckDataWrapper.h" +#include "DeckStats.h" +#include "JTypes.h" +#include "GameApp.h" +#include + +DeckEditorMenu::DeckEditorMenu(int id, JGuiListener* listener, int fontId, const char * _title, DeckDataWrapper *_selectedDeck, StatsWrapper *stats) : + DeckMenu(id, listener, fontId, _title), selectedDeck(_selectedDeck), stw(stats) +{ + backgroundName = "DeckEditorMenuBackdrop"; + + deckTitle = selectedDeck ? selectedDeck->parent->meta_name : ""; + enableDetails = false; + + mX = 123; + mY = 70; + starsOffsetX = 50; + + titleX = 110; // center point in title box + titleY = 25; + titleWidth = 180; // width of inner box of title + + descX = 275; + descY = 80; + descHeight = 154; + descWidth = 175; + + statsHeight = 50; + statsWidth = 185; + statsX = 280; + statsY = 12; + + avatarX = 222; + avatarY = 8; + + float scrollerWidth = 80; + SAFE_DELETE(scroller); // need to delete the scroller init in the base class + this->showDetailsScreen = false; + scroller = NEW TextScroller(Fonts::MAIN_FONT, 40, 230, scrollerWidth, 100, 1, 1); + +} + +void DeckEditorMenu::Render() +{ + JRenderer *r = JRenderer::GetInstance(); + r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(200,0,0,0)); + + DeckMenu::Render(); + if (deckTitle.size() > 0) + { + WFont *mainFont = resources.GetWFont(Fonts::OPTION_FONT); + DWORD currentColor = mainFont->GetColor(); + mainFont->SetColor(ARGB(255,255,255,255)); + mainFont->DrawString(deckTitle.c_str(), statsX + (statsWidth / 2), statsHeight / 2, JGETEXT_CENTER); + mainFont->SetColor(currentColor); + } + + if (stw && selectedDeck) drawDeckStatistics(); + +} + +void DeckEditorMenu::drawDeckStatistics() +{ + ostringstream deckStatsString; + + deckStatsString + << "------- Deck Summary -----" << endl + << "Cards: "<< stw->cardCount << endl + << "Creatures: "<< setw(2) << stw->countCreatures + << " Enchantments: " << stw->countEnchantments << endl + << "Instants: " << setw(4) << stw->countInstants + << " Sorceries: " << setw(2) << stw->countSorceries << endl + << "Lands: " + << "A: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_ARTIFACT ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_ARTIFACT ] << " " + << "G: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_GREEN ] + stw->countLandsPerColor[ Constants::MTG_COLOR_GREEN ] << " " + << "R: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_RED ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_RED ] << " " + << "U: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_BLUE ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_BLUE ] << " " + << "B: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_BLACK ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_BLACK ] << " " + << "W: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_WHITE ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_WHITE ] << endl + << " --- Card color count --- " << endl + << "A: " << setw(2) << left << selectedDeck->getCount(Constants::MTG_COLOR_ARTIFACT) << " " + << "G: " << setw(2) << left << selectedDeck->getCount(Constants::MTG_COLOR_GREEN) << " " + << "U: " << setw(2) << left << selectedDeck->getCount(Constants::MTG_COLOR_BLUE) << " " + << "R: " << setw(2) << left << selectedDeck->getCount(Constants::MTG_COLOR_RED) << " " + << "B: " << setw(2) << left << selectedDeck->getCount(Constants::MTG_COLOR_BLACK) << " " + << "W: " << setw(2) << left << selectedDeck->getCount(Constants::MTG_COLOR_WHITE) << endl + + << " --- Average Cost --- " << endl + << "Creature: "<< setprecision(2) << stw->avgCreatureCost << endl + << "Mana: " << setprecision(2) << stw->avgManaCost << " " + << "Spell: " << setprecision(2) << stw->avgSpellCost << endl; + + WFont *mainFont = resources.GetWFont(Fonts::MAIN_FONT); + mainFont->DrawString(deckStatsString.str().c_str(), descX, descY + 25); +} + +DeckEditorMenu::~DeckEditorMenu() +{ + SAFE_DELETE( scroller ); +} diff --git a/projects/mtg/src/DeckMenu.cpp b/projects/mtg/src/DeckMenu.cpp index 63b7fd436..acc5f322e 100644 --- a/projects/mtg/src/DeckMenu.cpp +++ b/projects/mtg/src/DeckMenu.cpp @@ -1,281 +1,328 @@ -#include "PrecompiledHeader.h" - -#include "DeckMenu.h" -#include "DeckMenuItem.h" -#include "DeckMetaData.h" -#include "JTypes.h" -#include "GameApp.h" -#include "Translate.h" -#include "TextScroller.h" -#include "Tasks.h" -#include - -namespace -{ - const float kVerticalMargin = 16; - const float kHorizontalMargin = 20; - const float kLineHeight = 20; - const float kDescriptionVerticalBoxPadding = 5; - const float kDescriptionHorizontalBoxPadding = 5; -} - -hgeParticleSystem* DeckMenu::stars = NULL; - -// -// For the additional info window, maximum characters per line is roughly 30 characters across. -// TODO: figure a way to get incoming text to wrap. -// -// used fixed locations where the menu, title and descriptive text are located. -// * menu at (125, 60 ) -// * descriptive information 125 -// *** Need to make this configurable in a file somewhere to allow for class reuse - -DeckMenu::DeckMenu(int id, JGuiListener* listener, int fontId, const string _title, const float& mFontScale) : - JGuiController(id, listener), fontId(fontId), menuFontScale(mFontScale) -{ - - backgroundName = "DeckMenuBackdrop"; - - mY = 55; - mWidth = 176; - mX = 125; - - titleX = 130; // center point in title box - titleY = 28; - titleWidth = 180; // width of inner box of title - - descX = 230 + kDescriptionVerticalBoxPadding; - descY = 65 + kDescriptionHorizontalBoxPadding; - descHeight = 145; - descWidth = 220; - - starsOffsetX = 50; - - statsX = 280; - statsY = 8; - statsHeight = 50; - statsWidth = 227; - - avatarX = 230; - avatarY = 8; - - menuInitialized = false; - - float scrollerWidth = 80; - scroller = NEW TextScroller(Fonts::MAIN_FONT, 40, 230, scrollerWidth, 100, 1, 1); - - autoTranslate = true; - maxItems = 7; - mHeight = 2 * kVerticalMargin + (maxItems * kLineHeight); - - // we want to cap the deck titles to 15 characters to avoid overflowing deck names - title = _(_title); - displayTitle = title; - mFont = resources.GetWFont(fontId); - - startId = 0; - selectionT = 0; - timeOpen = 0; - closed = false; - - if (mFont->GetStringWidth(title.c_str()) > titleWidth) - titleFontScale = 0.75f; - else - titleFontScale = 1.0f; - - selectionTargetY = selectionY = kVerticalMargin; - - if (NULL == stars) - stars = NEW hgeParticleSystem(resources.RetrievePSI("stars.psi", resources.GetQuad("stars"))); - stars->FireAt(mX, mY); - - updateScroller(); -} - -void DeckMenu::RenderBackground() -{ - ostringstream bgFilename; - bgFilename << backgroundName << ".png"; - - static bool loadBackground = true; - if (loadBackground) - { - JQuad *background = resources.RetrieveTempQuad(bgFilename.str(), TEXTURE_SUB_5551); - if (background) - JRenderer::GetInstance()->RenderQuad(background, 0, 0); - else - loadBackground = false; - } -} - -void DeckMenu::initMenuItems() -{ - float sY = mY + kVerticalMargin; - for (int i = startId; i < startId + mCount; ++i) - { - float y = mY + kVerticalMargin + i * kLineHeight; - DeckMenuItem * currentMenuItem = static_cast (mObjects[i]); - currentMenuItem->Relocate(mX, y); - if (currentMenuItem->hasFocus()) - sY = y; - } - selectionTargetY = selectionY = sY; -} - -void DeckMenu::Render() -{ - JRenderer * renderer = JRenderer::GetInstance(); - float height = mHeight; - - if (!menuInitialized) - { - initMenuItems(); - stars->Fire(); - timeOpen = 0; - menuInitialized = true; - } - if (timeOpen < 1) - height *= timeOpen > 0 ? timeOpen : -timeOpen; - - renderer->SetTexBlend(BLEND_SRC_ALPHA, BLEND_ONE); - stars->Render(); - renderer->SetTexBlend(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA); - - for (int i = startId; i < startId + maxItems; i++) - { - if (i > mCount - 1) - break; - DeckMenuItem *currentMenuItem = static_cast (mObjects[i]); - if (currentMenuItem->mY - kLineHeight * startId < mY + height - kLineHeight + 7) - { - if (currentMenuItem->hasFocus()) - { - // display the avatar image - if (currentMenuItem->imageFilename.size() > 0) - { - JQuad * quad = resources.RetrieveTempQuad(currentMenuItem->imageFilename, TEXTURE_SUB_AVATAR); - if (quad) - renderer->RenderQuad(quad, avatarX, avatarY); - } - // fill in the description part of the screen - string text = currentMenuItem->desc; - WFont *mainFont = resources.GetWFont(Fonts::MAIN_FONT); - mainFont->DrawString(text.c_str(), descX, descY); - mFont->SetColor(ARGB(255,255,255,255)); - - // fill in the statistical portion - if (currentMenuItem->meta) - { - ostringstream oss; - oss << "Deck: " << currentMenuItem->meta->getName() << endl; - oss << currentMenuItem->meta->getStatsSummary(); - - mainFont->DrawString(oss.str(), statsX, statsY); - } - } - else - { - mFont->SetColor(ARGB(150,255,255,255)); - } - mFont->SetScale(menuFontScale); - currentMenuItem->RenderWithOffset(-kLineHeight * startId); - } - } - - RenderBackground(); - - if (!title.empty()) - { - mFont->SetColor(ARGB(255,255,255,255)); - mFont->SetScale(titleFontScale); - mFont->DrawString(title.c_str(), titleX, titleY, JGETEXT_CENTER); - } - mFont->SetScale(1.0f); - scroller->Render(); - -} - -void DeckMenu::Update(float dt) -{ - JGuiController::Update(dt); - if (mCurr > startId + maxItems - 1) - startId = mCurr - maxItems + 1; - else if (mCurr < startId) - startId = mCurr; - stars->Update(dt); - selectionT += 3 * dt; - selectionY += (selectionTargetY - selectionY) * 8 * dt; - - float starsX = starsOffsetX + ((mWidth - 2 * kHorizontalMargin) * (1 + cos(selectionT)) / 2); - float starsY = selectionY + 5 * cos(selectionT * 2.35f) + kLineHeight / 2 - kLineHeight * startId; - stars->MoveTo(starsX, starsY); - if (timeOpen < 0) - { - timeOpen += dt * 10; - if (timeOpen >= 0) - { - timeOpen = 0; - closed = true; - stars->FireAt(mX, mY); - } - } - else - { - closed = false; - timeOpen += dt * 10; - } - - scroller->Update(dt); -} - -void DeckMenu::Add(int id, const char * text, string desc, bool forceFocus, DeckMetaData * deckMetaData) -{ - DeckMenuItem * menuItem = NEW DeckMenuItem(this, id, fontId, text, 0, mY + kVerticalMargin + mCount * kLineHeight, - (mCount == 0), autoTranslate, deckMetaData); - Translator * t = Translator::GetInstance(); - map::iterator it = t->deckValues.find(text); - if (it != t->deckValues.end()) //translate decks desc - menuItem->desc = it->second; - else - menuItem->desc = deckMetaData ? deckMetaData->getDescription() : desc; - - JGuiController::Add(menuItem); - if (mCount <= maxItems) - mHeight += kLineHeight; - if (forceFocus) - { - mObjects[mCurr]->Leaving(JGE_BTN_DOWN); - mCurr = mCount - 1; - menuItem->Entering(); - } -} - -void DeckMenu::updateScroller() -{ - // add all the items from the Tasks db. - TaskList taskList; - scroller->Reset(); - for (vector::iterator it = taskList.tasks.begin(); it != taskList.tasks.end(); it++) - { - ostringstream taskDescription; - taskDescription << "[ " << setw(4) << (*it)->getReward() << " / " << (*it)->getExpiration() << " ] " - << (*it)->getDesc() << endl; - scroller->Add(taskDescription.str()); - } -} - -void DeckMenu::Close() -{ - timeOpen = -1.0; - stars->Stop(true); -} - -void DeckMenu::destroy() -{ - SAFE_DELETE(DeckMenu::stars); -} - -DeckMenu::~DeckMenu() -{ - SAFE_DELETE(scroller); -} +#include "PrecompiledHeader.h" + +#include "DeckMenu.h" +#include "DeckMenuItem.h" +#include "DeckMetaData.h" +#include "JTypes.h" +#include "GameApp.h" +#include "Translate.h" +#include "TextScroller.h" +#include "Tasks.h" +#include + +namespace +{ + const float kVerticalMargin = 16; + const float kHorizontalMargin = 20; + const float kLineHeight = 20; + const float kDescriptionVerticalBoxPadding = 5; + const float kDescriptionHorizontalBoxPadding = 5; + const int DETAILED_INFO_THRESHOLD = 4; +} + +hgeParticleSystem* DeckMenu::stars = NULL; + +// +// For the additional info window, maximum characters per line is roughly 30 characters across. +// TODO: figure a way to get incoming text to wrap. +// +// used fixed locations where the menu, title and descriptive text are located. +// * menu at (125, 60 ) +// * descriptive information 125 +// *** Need to make this configurable in a file somewhere to allow for class reuse + +DeckMenu::DeckMenu(int id, JGuiListener* listener, int fontId, const string _title, const int& startIndex, const float& mFontScale) : + JGuiController(id, listener), fontId(fontId), menuFontScale(mFontScale) +{ + + backgroundName = "DeckMenuBackdrop"; + + selectedDeck = NULL; + enableDetails = true; + mY = 55; + mWidth = 176; + mX = 125; + + titleX = 130; // center point in title box + titleY = 28; + titleWidth = 180; // width of inner box of title + + descX = 230 + kDescriptionVerticalBoxPadding; + descY = 65 + kDescriptionHorizontalBoxPadding; + descHeight = 145; + descWidth = 220; + + detailedInfoBoxX = 400; + detailedInfoBoxY = 235; + starsOffsetX = 50; + + statsX = 280; + statsY = 8; + statsHeight = 50; + statsWidth = 227; + + selectedDeckId = startIndex; + + avatarX = 230; + avatarY = 8; + + menuInitialized = false; + + float scrollerWidth = 80; + scroller = NEW TextScroller(Fonts::MAIN_FONT, 40, 230, scrollerWidth, 100, 1, 1); + + autoTranslate = true; + maxItems = 7; + mHeight = 2 * kVerticalMargin + (maxItems * kLineHeight); + + // we want to cap the deck titles to 15 characters to avoid overflowing deck names + title = _(_title); + displayTitle = title; + mFont = resources.GetWFont(fontId); + + startId = 0; + selectionT = 0; + timeOpen = 0; + closed = false; + + if (mFont->GetStringWidth(title.c_str()) > titleWidth) + titleFontScale = 0.75f; + else + titleFontScale = 1.0f; + + selectionTargetY = selectionY = kVerticalMargin; + + if (NULL == stars) stars = NEW hgeParticleSystem(resources.RetrievePSI("stars.psi", resources.GetQuad("stars"))); + stars->FireAt(mX, mY); + + updateScroller(); +} + +void DeckMenu::RenderBackground() +{ + ostringstream bgFilename; + bgFilename << backgroundName << ".png"; + + static bool loadBackground = true; + if (loadBackground) + { + JQuad *background = resources.RetrieveTempQuad(bgFilename.str(), TEXTURE_SUB_5551); + if (background) + JRenderer::GetInstance()->RenderQuad(background, 0, 0); + else + loadBackground = false; + } +} + +DeckMetaData * DeckMenu::getSelectedDeck() +{ + if (selectedDeck) return selectedDeck; + + return NULL; +} + +bool DeckMenu::selectedDeckHasDetails() +{ + DeckMetaData * currentMenuItem = getSelectedDeck(); + if (currentMenuItem) return (enableDetails && currentMenuItem->getGamesPlayed() > DETAILED_INFO_THRESHOLD); + + return false; +} + +void DeckMenu::initMenuItems() +{ + float sY = mY + kVerticalMargin; + for (int i = startId; i < startId + mCount; ++i) + { + float y = mY + kVerticalMargin + i * kLineHeight; + DeckMenuItem * currentMenuItem = static_cast (mObjects[i]); + currentMenuItem->Relocate(mX, y); + if (currentMenuItem->hasFocus()) sY = y; + } + selectionTargetY = selectionY = sY; + + //Grab a texture in VRAM. + pspIconsTexture = resources.RetrieveTexture("iconspsp.png", RETRIEVE_LOCK); + + char buf[512]; + for (int i = 0; i < 8; i++) + { + sprintf(buf, "iconspsp%d", i); + pspIcons[i] = resources.RetrieveQuad("iconspsp.png", (float) i * 32, 0, 32, 32, buf); + pspIcons[i]->SetHotSpot(16, 16); + } + +} + +void DeckMenu::Render() +{ + JRenderer * renderer = JRenderer::GetInstance(); + float height = mHeight; + + if (!menuInitialized) + { + initMenuItems(); + stars->Fire(); + timeOpen = 0; + menuInitialized = true; + } + if (timeOpen < 1) height *= timeOpen > 0 ? timeOpen : -timeOpen; + + for (int i = startId; i < startId + maxItems; i++) + { + if (i > mCount - 1) break; + DeckMenuItem *currentMenuItem = static_cast (mObjects[i]); + if (currentMenuItem->mY - kLineHeight * startId < mY + height - kLineHeight + 7) + { + if (currentMenuItem->hasFocus()) + { + selectedDeckId = i; + selectedDeck = currentMenuItem->meta; + + WFont *mainFont = resources.GetWFont(Fonts::MAIN_FONT); + + // display the "more info" button if special condition is met + if (selectedDeckHasDetails()) + { + showDetailsScreen = true; + float pspIconsSize = 0.5; + const string detailedInfoString = "Detailed Info"; + float stringWidth = mainFont->GetStringWidth(detailedInfoString.c_str()); + float boxStartX = detailedInfoBoxX - stringWidth / 2; + DWORD currentColor = mainFont->GetColor(); + renderer->FillRoundRect( boxStartX, detailedInfoBoxY - 5, stringWidth, + mainFont->GetHeight() + 15, .5, ARGB( 125, 0, 255, 255) ); + renderer->RenderQuad(pspIcons[5], detailedInfoBoxX, detailedInfoBoxY + 2, 0, pspIconsSize, pspIconsSize); + mainFont->SetColor(currentColor); + mainFont->DrawString(detailedInfoString, boxStartX, detailedInfoBoxY + 10); + } + else + showDetailsScreen = false; + + // display the avatar image + if (currentMenuItem->imageFilename.size() > 0) + { + JQuad * quad = resources.RetrieveTempQuad(currentMenuItem->imageFilename, TEXTURE_SUB_AVATAR); + if (quad) renderer->RenderQuad(quad, avatarX, avatarY); + } + // fill in the description part of the screen + string text = currentMenuItem->desc; + mainFont->DrawString(text.c_str(), descX, descY); + mFont->SetColor(ARGB(255,255,255,255)); + + // fill in the statistical portion + if (currentMenuItem->meta) + { + ostringstream oss; + oss << "Deck: " << currentMenuItem->meta->getName() << endl; + oss << currentMenuItem->meta->getStatsSummary(); + + mainFont->DrawString(oss.str(), statsX, statsY); + } + } + else + { + mFont->SetColor(ARGB(150,255,255,255)); + } + mFont->SetScale(menuFontScale); + currentMenuItem->RenderWithOffset(-kLineHeight * startId); + } + } + + RenderBackground(); + + if (!title.empty()) + { + mFont->SetColor(ARGB(255,255,255,255)); + mFont->SetScale(titleFontScale); + mFont->DrawString(title.c_str(), titleX, titleY, JGETEXT_CENTER); + } + mFont->SetScale(1.0f); + scroller->Render(); + +} + +void DeckMenu::Update(float dt) +{ + JGuiController::Update(dt); + if (mCurr > startId + maxItems - 1) + startId = mCurr - maxItems + 1; + else if (mCurr < startId) startId = mCurr; + stars->Update(dt); + selectionT += 3 * dt; + selectionY += (selectionTargetY - selectionY) * 8 * dt; + + float starsX = starsOffsetX + ((mWidth - 2 * kHorizontalMargin) * (1 + cos(selectionT)) / 2); + float starsY = selectionY + 5 * cos(selectionT * 2.35f) + kLineHeight / 2 - kLineHeight * startId; + stars->MoveTo(starsX, starsY); + if (timeOpen < 0) + { + timeOpen += dt * 10; + if (timeOpen >= 0) + { + timeOpen = 0; + closed = true; + stars->FireAt(mX, mY); + } + } + else + { + closed = false; + timeOpen += dt * 10; + } + if (scroller) scroller->Update(dt); +} + +void DeckMenu::Add(int id, const char * text, string desc, bool forceFocus, DeckMetaData * deckMetaData) +{ + DeckMenuItem * menuItem = NEW DeckMenuItem(this, id, fontId, text, 0, mY + kVerticalMargin + mCount * kLineHeight, + (mCount == 0), autoTranslate, deckMetaData); + Translator * t = Translator::GetInstance(); + map::iterator it = t->deckValues.find(text); + if (it != t->deckValues.end()) //translate decks desc + menuItem->desc = it->second; + else + menuItem->desc = deckMetaData ? deckMetaData->getDescription() : desc; + + JGuiController::Add(menuItem); + if (mCount <= maxItems) mHeight += kLineHeight; + + if (forceFocus) + { + mObjects[mCurr]->Leaving(JGE_BTN_DOWN); + mCurr = mCount - 1; + menuItem->Entering(); + } +} + +void DeckMenu::updateScroller() +{ + // add all the items from the Tasks db. + TaskList taskList; + scroller->Reset(); + for (vector::iterator it = taskList.tasks.begin(); it != taskList.tasks.end(); it++) + { + ostringstream taskDescription; + taskDescription << "[ " << setw(4) << (*it)->getReward() << " / " << (*it)->getExpiration() << " ] " + << (*it)->getDesc() << endl; + scroller->Add(taskDescription.str()); + } +} + +void DeckMenu::Close() +{ + timeOpen = -1.0; + stars->Stop(true); +} + +void DeckMenu::destroy() +{ + SAFE_DELETE(DeckMenu::stars); +} + +DeckMenu::~DeckMenu() +{ + resources.Release(pspIconsTexture); + SAFE_DELETE(scroller); + scroller = NULL; +} diff --git a/projects/mtg/src/DeckStats.cpp b/projects/mtg/src/DeckStats.cpp index ccea6a606..20a95f0a3 100644 --- a/projects/mtg/src/DeckStats.cpp +++ b/projects/mtg/src/DeckStats.cpp @@ -1,242 +1,466 @@ -#include "PrecompiledHeader.h" - -#include "DeckStats.h" -#include "Player.h" -#include "GameObserver.h" - -DeckStats * DeckStats::mInstance = NULL; - -int DeckStat::percentVictories() -{ - if (nbgames == 0) - return 50; - return (100 * victories / nbgames); -} - -DeckStats * DeckStats::GetInstance() -{ - if (!mInstance) - { - mInstance = NEW DeckStats(); - - } - return mInstance; -} - -void DeckStats::cleanStats() -{ - map::iterator it; - for (it = stats.begin(); it != stats.end(); it++) - { - SAFE_DELETE(it->second); - } - - stats.clear(); -} - -DeckStats::~DeckStats() -{ - cleanStats(); -} - -int DeckStats::percentVictories(string opponentsFile) -{ - map::iterator it = stats.find(opponentsFile); - if (it == stats.end()) - { - return 50; - } - else - { - return (it->second->percentVictories()); - } -} - -DeckStat* DeckStats::getDeckStat(string opponentsFile) -{ - map::iterator it = stats.find(opponentsFile); - if (it == stats.end()) - { - return NULL; - } - else - { - return it->second; - } -} - -int DeckStats::nbGames() -{ - int nbgames = 0; - map::iterator it; - for (it = stats.begin(); it != stats.end(); it++) - { - DeckStat * d = it->second; - nbgames += d->nbgames; - } - return nbgames; -} - -int DeckStats::percentVictories() -{ - int victories = 0; - int nbgames = 0; - map::iterator it; - for (it = stats.begin(); it != stats.end(); it++) - { - DeckStat * d = it->second; - nbgames += d->nbgames; - victories += d->victories; - } - if (nbgames) - { - return (victories * 100) / nbgames; - } - return 50; -} - -void DeckStats::load(Player * player) -{ - char filename[512]; - sprintf(filename, "stats/%s.txt", player->deckFileSmall.c_str()); - load(options.profileFile(filename).c_str()); -} - -void DeckStats::load(const char * filename) -{ - cleanStats(); - std::ifstream file(filename); - std::string s; - - if (file) - { - while (std::getline(file, s)) - { - string deckfile = s; - std::getline(file, s); - int games = atoi(s.c_str()); - std::getline(file, s); - int victories = atoi(s.c_str()); - map::iterator it = stats.find(deckfile); - if (it == stats.end()) - { - stats[deckfile] = NEW DeckStat(games, victories); - } - } - file.close(); - } -} - -void DeckStats::save(Player * player) -{ - char filename[512]; - sprintf(filename, "stats/%s.txt", player->deckFileSmall.c_str()); - save(options.profileFile(filename).c_str()); -} - -void DeckStats::save(const char * filename) -{ - std::ofstream file(filename); - char writer[512]; - if (file) - { - map::iterator it; - for (it = stats.begin(); it != stats.end(); it++) - { - sprintf(writer, "%s\n", it->first.c_str()); - file << writer; - sprintf(writer, "%i\n", it->second->nbgames); - file << writer; - sprintf(writer, "%i\n", it->second->victories); - file << writer; - } - file.close(); - } -} - -void DeckStats::saveStats(Player *player, Player *opponent, GameObserver * game) -{ - int victory = 1; - if (!game->gameOver) - { - if (player->life == opponent->life) - return; - if (player->life < opponent->life) - victory = 0; - } - else if (game->gameOver == player) - { - victory = 0; - } - load(player); - map::iterator it = stats.find(opponent->deckFileSmall); - if (it == stats.end()) - { - stats[opponent->deckFileSmall] = NEW DeckStat(1, victory); - } - else - { - it->second->victories += victory; - it->second->nbgames += 1; - } - save(player); -} - -StatsWrapper::StatsWrapper(int deckId) -{ - // Load deck statistics - char buffer[512]; - DeckStats * stats = DeckStats::GetInstance(); - aiDeckNames.clear(); - aiDeckStats.clear(); - - sprintf(buffer, "stats/player_deck%i.txt", deckId); - string deckstats = options.profileFile(buffer); - - if (fileExists(deckstats.c_str())) - { - stats->load(deckstats.c_str()); - percentVictories = stats->percentVictories(); - gamesPlayed = stats->nbGames(); - - // Detailed deck statistics against AI - int found = 1; - int nbDecks = 0; - while (found) - { - found = 0; - char buffer[512]; - char smallDeckName[512]; - sprintf(buffer, "%s/deck%i.txt", RESPATH"/ai/baka", nbDecks + 1); - if (fileExists(buffer)) - { - MTGDeck * mtgd = NEW MTGDeck(buffer, NULL, 1); - found = 1; - nbDecks++; - - sprintf(smallDeckName, "%s_deck%i", "ai_baka", nbDecks); - DeckStat* deckStat = stats->getDeckStat(string(smallDeckName)); - - if ((deckStat != NULL) && (deckStat->nbgames > 0)) - { - int percentVictories = stats->percentVictories(string(smallDeckName)); - aiDeckNames.push_back(string(mtgd->meta_name)); - aiDeckStats.push_back(deckStat); - } - - delete mtgd; - } - } - } - else - { - gamesPlayed = 0; - percentVictories = 0; - } -} - -StatsWrapper::~StatsWrapper() -{ - aiDeckNames.clear(); - aiDeckStats.clear(); -} +#include "PrecompiledHeader.h" + +#include "DeckStats.h" +#include "Player.h" +#include "GameObserver.h" +#include "MTGDeck.h" +#include "ManaCostHybrid.h" + +DeckStats * DeckStats::mInstance = NULL; + +DeckStat::DeckStat(int _nbgames, int _victories) : nbgames(_nbgames), victories(_victories) +{ +} + +int DeckStat::percentVictories() +{ + if (nbgames == 0) return 50; + return (100 * victories / nbgames); +} + +DeckStats * DeckStats::GetInstance() +{ + if (!mInstance) + { + mInstance = NEW DeckStats(); + + } + return mInstance; +} + +void DeckStats::cleanStats() +{ + map::iterator it; + for (it = stats.begin(); it != stats.end(); it++) + { + SAFE_DELETE(it->second); + } + + stats.clear(); +} + +DeckStats::~DeckStats() +{ + cleanStats(); +} + +int DeckStats::percentVictories(string opponentsFile) +{ + map::iterator it = stats.find(opponentsFile); + if (it == stats.end()) + { + return 50; + } + else + { + return (it->second->percentVictories()); + } +} + +DeckStat* DeckStats::getDeckStat(string opponentsFile) +{ + map::iterator it = stats.find(opponentsFile); + if (it == stats.end()) + { + return NULL; + } + else + { + return it->second; + } +} + +int DeckStats::nbGames() +{ + int nbgames = 0; + map::iterator it; + for (it = stats.begin(); it != stats.end(); it++) + { + DeckStat * d = it->second; + nbgames += d->nbgames; + } + return nbgames; +} + +int DeckStats::percentVictories() +{ + int victories = 0; + int nbgames = 0; + map::iterator it; + for (it = stats.begin(); it != stats.end(); it++) + { + DeckStat * d = it->second; + nbgames += d->nbgames; + victories += d->victories; + } + if (nbgames) + { + return (victories * 100) / nbgames; + } + return 50; +} + +void DeckStats::load(Player * player) +{ + char filename[512]; + sprintf(filename, "stats/%s.txt", player->deckFileSmall.c_str()); + load(options.profileFile(filename).c_str()); +} + +void DeckStats::load(const char * filename) +{ + cleanStats(); + std::ifstream file(filename); + std::string s; + + if (file) + { + while (std::getline(file, s)) + { + string deckfile = s; + std::getline(file, s); + int games = atoi(s.c_str()); + std::getline(file, s); + int victories = atoi(s.c_str()); + map::iterator it = stats.find(deckfile); + if (it == stats.end()) + { + stats[deckfile] = NEW DeckStat(games, victories); + } + } + file.close(); + } +} + +void DeckStats::save(Player * player) +{ + char filename[512]; + sprintf(filename, "stats/%s.txt", player->deckFileSmall.c_str()); + save(options.profileFile(filename).c_str()); +} + +void DeckStats::save(const char * filename) +{ + std::ofstream file(filename); + char writer[512]; + if (file) + { + map::iterator it; + for (it = stats.begin(); it != stats.end(); it++) + { + sprintf(writer, "%s\n", it->first.c_str()); + file << writer; + sprintf(writer, "%i\n", it->second->nbgames); + file << writer; + sprintf(writer, "%i\n", it->second->victories); + file << writer; + } + file.close(); + } +} + +void DeckStats::saveStats(Player *player, Player *opponent, GameObserver * game) +{ + int victory = 1; + if (!game->gameOver) + { + if (player->life == opponent->life) return; + if (player->life < opponent->life) victory = 0; + } + else if (game->gameOver == player) + { + victory = 0; + } + load(player); + map::iterator it = stats.find(opponent->deckFileSmall); + if (it == stats.end()) + { + stats[opponent->deckFileSmall] = NEW DeckStat(1, victory); + } + else + { + it->second->victories += victory; + it->second->nbgames += 1; + } + save(player); +} +StatsWrapper::StatsWrapper(int deckId) +{ + mDeckId = deckId; + char buffer[512]; + sprintf(buffer, "stats/player_deck%i.txt", deckId); + string deckstats = options.profileFile(buffer); + initStatistics(deckstats); +} + +StatsWrapper::StatsWrapper(string deckstats) +{ + initStatistics(deckstats); +} + +void StatsWrapper::initStatistics(string deckstats) +{ + // Load deck statistics + DeckStats * stats = DeckStats::GetInstance(); + aiDeckNames.clear(); + aiDeckStats.clear(); + + if (fileExists(deckstats.c_str())) + { + stats->load(deckstats.c_str()); + percentVictories = stats->percentVictories(); + gamesPlayed = stats->nbGames(); + + // Detailed deck statistics against AI + int found = 1; + int nbDecks = 0; + found = 0; + char buffer[512]; + char smallDeckName[512]; + sprintf(buffer, "%s/deck%i.txt", RESPATH"/ai/baka", nbDecks + 1); + if (fileExists(buffer)) + { + MTGDeck * mtgd = NEW MTGDeck(buffer, NULL, 1); + found = 1; + nbDecks++; + + sprintf(smallDeckName, "%s_deck%i", "ai_baka", nbDecks); + DeckStat* deckStat = stats->getDeckStat(string(smallDeckName)); + + if ((deckStat != NULL) && (deckStat->nbgames > 0)) + { + int percentVictories = stats->percentVictories(string(smallDeckName)); + aiDeckNames.push_back(string(mtgd->meta_name)); + aiDeckStats.push_back(deckStat); + } + + delete mtgd; + } + } + else + { + gamesPlayed = 0; + percentVictories = 0; + } +} + +void StatsWrapper::updateStats(string filename, MTGAllCards *collection) +{ + if (fileExists(filename.c_str())) + { + MTGDeck * mtgd = NEW MTGDeck(filename.c_str(), collection); + DeckDataWrapper *deckDataWrapper = NEW DeckDataWrapper(mtgd); + updateStats(deckDataWrapper); + SAFE_DELETE( mtgd ); + SAFE_DELETE( deckDataWrapper ); + } + +} + +void StatsWrapper::updateStats(DeckDataWrapper *myDeck) +{ + if (!this->needUpdate || !myDeck) return; + this->needUpdate = false; + this->cardCount = myDeck->getCount(WSrcDeck::UNFILTERED_COPIES); + this->countLands = myDeck->getCount(Constants::MTG_COLOR_LAND); + this->totalPrice = myDeck->totalPrice(); + + this->countManaProducers = 0; + // Mana cost + int currentCount, convertedCost; + ManaCost * currentCost; + this->totalManaCost = 0; + this->totalCreatureCost = 0; + this->totalSpellCost = 0; + MTGCard * current = myDeck->getCard(); + + // Clearing arrays + for (int i = 0; i <= Constants::STATS_MAX_MANA_COST; i++) + { + this->countCardsPerCost[i] = 0; + this->countCreaturesPerCost[i] = 0; + this->countSpellsPerCost[i] = 0; + } + + for (int i = 0; i <= Constants::MTG_NB_COLORS; i++) + { + this->totalCostPerColor[i] = 0; + this->countLandsPerColor[i] = 0; + this->countBasicLandsPerColor[i] = 0; + this->countNonLandProducersPerColor[i] = 0; + } + + for (int i = 0; i <= Constants::STATS_MAX_MANA_COST; i++) + { + for (int k = 0; k <= Constants::MTG_NB_COLORS; k++) + { + this->countCardsPerCostAndColor[i][k] = 0; + this->countCreaturesPerCostAndColor[i][k] = 0; + this->countSpellsPerCostAndColor[i][k] = 0; + } + } + + for (int ic = 0; ic < myDeck->Size(true); ic++) + { + current = myDeck->getCard(ic, true); + currentCost = current->data->getManaCost(); + convertedCost = currentCost->getConvertedCost(); + currentCount = myDeck->count(current); + + // Add to the cards per cost counters + this->totalManaCost += convertedCost * currentCount; + if (convertedCost > Constants::STATS_MAX_MANA_COST) + { + convertedCost = Constants::STATS_MAX_MANA_COST; + } + this->countCardsPerCost[convertedCost] += currentCount; + if (current->data->isCreature()) + { + this->countCreaturesPerCost[convertedCost] += currentCount; + this->totalCreatureCost += convertedCost * currentCount; + } + else if (current->data->isSpell()) + { + this->countSpellsPerCost[convertedCost] += currentCount; + this->totalSpellCost += convertedCost * currentCount; + } + + // Lets look for mana producing abilities + + vector abilitiesVector; + string thisstring = current->data->magicText; + abilitiesVector = split(thisstring, '\n'); + + for (int v = 0; v < (int) abilitiesVector.size(); v++) + { + string s = abilitiesVector[v]; + size_t t = s.find("add"); + if (t != string::npos) + { + s = s.substr(t + 3); + ManaCost * mc = ManaCost::parseManaCost(s); + for (int j = 0; j < Constants::MTG_NB_COLORS; j++) + { + if (mc->hasColor(j)) + { + if (current->data->isLand()) + { + if (current->data->hasType("Basic")) + { + this->countBasicLandsPerColor[j] += currentCount; + } + else + { + this->countLandsPerColor[j] += currentCount; + } + } + else + { + this->countNonLandProducersPerColor[j] += currentCount; + } + } + } + SAFE_DELETE(mc); + } + } + + // Add to the per color counters + // a. regular costs + for (int j = 0; j < Constants::MTG_NB_COLORS; j++) + { + this->totalCostPerColor[j] += currentCost->getCost(j) * currentCount; + if (current->data->hasColor(j)) + { + // Add to the per cost and color counter + this->countCardsPerCostAndColor[convertedCost][j] += currentCount; + if (current->data->isCreature()) + { + this->countCreaturesPerCostAndColor[convertedCost][j] += currentCount; + } + else if (current->data->isSpell()) + { + this->countSpellsPerCostAndColor[convertedCost][j] += currentCount; + } + } + } + + // b. Hybrid costs + ManaCostHybrid * hybridCost; + int i; + i = 0; + + while ((hybridCost = currentCost->getHybridCost(i++)) != NULL) + { + this->totalCostPerColor[hybridCost->color1] += hybridCost->value1 * currentCount; + this->totalCostPerColor[hybridCost->color2] += hybridCost->value2 * currentCount; + } + } + + this->totalColoredSymbols = 0; + for (int j = 1; j < Constants::MTG_NB_COLORS; j++) + { + this->totalColoredSymbols += this->totalCostPerColor[j]; + } + + this->countCardsPerCost[0] -= this->countLands; + + // Counts by type + this->countCreatures = countCardsByType("Creature", myDeck); + this->countInstants = countCardsByType("Instant", myDeck); + this->countEnchantments = countCardsByType("Enchantment", myDeck); + this->countSorceries = countCardsByType("Sorcery", myDeck); + this->countSpells = this->countInstants + this->countEnchantments + this->countSorceries; + //this->countArtifacts = countCardsByType("Artifact", myDeck); + + // Average mana costs + this->avgManaCost = ((this->cardCount - this->countLands) <= 0) ? 0 : (float) this->totalManaCost / (this->cardCount + - this->countLands); + this->avgCreatureCost = (this->countCreatures <= 0) ? 0 : (float) this->totalCreatureCost / this->countCreatures; + this->avgSpellCost = (this->countSpells <= 0) ? 0 : (float) this->totalSpellCost / this->countSpells; + + // Probabilities + // TODO: this could be optimized by reusing results + for (int i = 0; i < Constants::STATS_FOR_TURNS; i++) + { + this->noLandsProbInTurn[i] = noLuck(this->cardCount, this->countLands, 7 + i) * 100; + this->noCreaturesProbInTurn[i] = noLuck(this->cardCount, this->countCreatures, 7 + i) * 100; + } +} + +// This should probably be cached in DeckDataWrapper +// or at least be calculated for all common types in one go +int StatsWrapper::countCardsByType(const char * _type, DeckDataWrapper * myDeck) +{ + int result = 0; + for (int i = 0; i < myDeck->Size(true); i++) + { + MTGCard * current = myDeck->getCard(i, true); + if (current->data->hasType(_type)) + { + result += myDeck->count(current); + } + } + return result; +} + +// n cards total, a of them are of desired type (A), x drawn +// returns probability of no A's +float StatsWrapper::noLuck(int n, int a, int x) +{ + if ((a >= n) || (a == 0)) return 1; + if ((n == 0) || (x == 0) || (x > n) || (n - a < x)) return 0; + + a = n - a; + float result = 1; + + for (int i = 0; i < x; i++) + result *= (float) (a - i) / (n - i); + return result; +} + +StatsWrapper::~StatsWrapper() +{ + aiDeckNames.clear(); + aiDeckStats.clear(); +} + diff --git a/projects/mtg/src/GameStateDeckViewer.cpp b/projects/mtg/src/GameStateDeckViewer.cpp index 2831429c9..2e4ef3227 100644 --- a/projects/mtg/src/GameStateDeckViewer.cpp +++ b/projects/mtg/src/GameStateDeckViewer.cpp @@ -1,1838 +1,1812 @@ -/* - * GameStateDeckViewer.cpp - * Class handling the Deck Editor - */ - -#include "PrecompiledHeader.h" - -#include -#include - -#include "DeckManager.h" -#include "GameStateDuel.h" -#include "GameStateDeckViewer.h" -#include "Translate.h" -#include "ManaCostHybrid.h" -#include "MTGCardInstance.h" -#include "WFilter.h" -#include "WDataSrc.h" -#include "DeckEditorMenu.h" -#include "SimpleMenu.h" - -//!! helper function; this is probably handled somewhere in the code already. -// If not, should be placed in general library -void StringExplode(string str, string separator, vector* results) -{ - size_t found; - found = str.find_first_of(separator); - while (found != string::npos) - { - if (found > 0) - results->push_back(str.substr(0, found)); - str = str.substr(found + 1); - found = str.find_first_of(separator); - } - if (str.length() > 0) - results->push_back(str); -} - -GameStateDeckViewer::GameStateDeckViewer(GameApp* parent) : - GameState(parent) -{ - bgMusic = NULL; - nbDecks = 0; - deckNum = 0; - useFilter = 0; - isAIDeckSave = false; - mSwitching = false; - welcome_menu = NULL; - myCollection = NULL; - myDeck = NULL; - filterMenu = NULL; - source = NULL; - hudAlpha = 0; - subMenu = NULL; - mRotation = 0; - mSlide = 0; - mAlpha = 255; - menu = NULL; - stw = NULL; -} - -GameStateDeckViewer::~GameStateDeckViewer() -{ - SAFE_DELETE(bgMusic); - if (myDeck) - { - SAFE_DELETE(myDeck->parent); - SAFE_DELETE(myDeck); - } - if (myCollection) - { - SAFE_DELETE(myCollection->parent); - SAFE_DELETE(myCollection); - } - SAFE_DELETE(stw); - SAFE_DELETE(filterMenu); -} - -void GameStateDeckViewer::rotateCards(int direction) -{ - int left = direction; - if (left) - displayed_deck->next(); - else - displayed_deck->prev(); - loadIndexes(); - - int total = displayed_deck->Size(); - if (total) - { - lastPos = getCurrentPos(); - lastTotal = total; - } - int i = 0; -} -void GameStateDeckViewer::rebuildFilters() -{ - if (!filterMenu) - filterMenu = NEW WGuiFilters("Filter by...", NULL); - if (source) - SAFE_DELETE(source); - source = NEW WSrcDeckViewer(myDeck, myCollection); - filterMenu->setSrc(source); - if (displayed_deck != myDeck) - source->swapSrc(); - filterMenu->Finish(true); - updateStats(); -} -void GameStateDeckViewer::updateFilters() -{ - if (!displayed_deck) - return; - - filterMenu->recolorFilter(useFilter - 1); - filterMenu->Finish(true); - int totalAfter = displayed_deck->Size(); - if (totalAfter && lastTotal) - { - - //This part is a hack. I don't understand why in some cases "displayed_deck's" currentPos is not 0 at this stage - { - while (int currentPos = displayed_deck->getOffset()) - { - if (currentPos > 0) - displayed_deck->prev(); - else - displayed_deck->next(); - } - } - - int pos = (totalAfter * lastPos) / lastTotal; - for (int i = 0; i < pos - 3; ++i) - { // "-3" because card "0" is displayed at position 3 initially - displayed_deck->next(); - } - } - updateStats(); - return; -} -void GameStateDeckViewer::loadIndexes() -{ - for (int i = 0; i < 7; i++) - { - cardIndex[i] = displayed_deck->getCard(i); - } -} - -void GameStateDeckViewer::switchDisplay() -{ - if (displayed_deck == myCollection) - { - displayed_deck = myDeck; - } - else - { - displayed_deck = myCollection; - } - source->swapSrc(); - updateFilters(); - loadIndexes(); -} - -void GameStateDeckViewer::updateDecks() -{ - SAFE_DELETE(welcome_menu); - welcome_menu = NEW DeckEditorMenu(MENU_DECK_SELECTION, this, Fonts::OPTION_FONT, "Choose Deck To Edit"); - DeckManager * deckManager = DeckManager::GetInstance(); - vector playerDeckList = fillDeckMenu(welcome_menu, options.profileFile()); - - deckNum = 0; - newDeckname = ""; - nbDecks = playerDeckList.size() + 1; - welcome_menu->Add(MENU_ITEM_NEW_DECK, "--NEW--"); - if (options[Options::CHEATMODE].number && (!myCollection || myCollection->getCount(WSrcDeck::UNFILTERED_MIN_COPIES) < 4)) - welcome_menu->Add(MENU_ITEM_CHEAT_MODE, "--UNLOCK CARDS--"); - welcome_menu->Add(MENU_ITEM_CANCEL, "Cancel"); - - // update the deckmanager with the latest information - deckManager->updateMetaDataList(&playerDeckList, false); - // is this necessary to ensure no memory leaks? - playerDeckList.clear(); -} - -void GameStateDeckViewer::buildEditorMenu() -{ - ostringstream deckSummaryInformation; - deckSummaryInformation << "All changes are final." << endl; - - if (menu) - SAFE_DELETE( menu ); - //Build menu. - JRenderer::GetInstance()->FillRoundRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 100, ARGB(0, 0, 0, 0) ); - menu = NEW DeckEditorMenu(MENU_DECK_BUILDER, this, Fonts::OPTION_FONT, "Deck Editor", myDeck, stw); - - menu->Add(MENU_ITEM_FILTER_BY, "Filter By...", "Narrow down the list of cards. "); - menu->Add(MENU_ITEM_SWITCH_DECKS_NO_SAVE, "Switch Decks", "Do not make any changes.\nView another deck."); - menu->Add(MENU_ITEM_SAVE_RENAME, "Rename Deck", "Change the name of the deck"); - menu->Add(MENU_ITEM_SAVE_RETURN_MAIN_MENU, "Save & Quit Editor", "Save changes.\nReturn to the main menu"); - menu->Add(MENU_ITEM_SAVE_AS_AI_DECK, "Save As AI Deck", deckSummaryInformation.str()); - menu->Add(MENU_ITEM_MAIN_MENU, "Quit Editor", "Do not make any changes to deck.\nReturn to the main menu."); - menu->Add(MENU_ITEM_EDITOR_CANCEL, "Cancel", "Close menu."); - -} - -void GameStateDeckViewer::Start() -{ - hudAlpha = 0; - mSwitching = false; - subMenu = NULL; - myDeck = NULL; - mStage = STAGE_WELCOME; - mRotation = 0; - mSlide = 0; - mAlpha = 255; - last_user_activity = NO_USER_ACTIVITY_HELP_DELAY + 1; - onScreenTransition = 0; - useFilter = 0; - lastPos = 0; - lastTotal = 0; - - pricelist = NEW PriceList(JGE_GET_RES("settings/prices.dat").c_str(), mParent->collection); - playerdata = NEW PlayerData(mParent->collection); - myCollection = NEW DeckDataWrapper(playerdata->collection); - myCollection->Sort(WSrcCards::SORT_ALPHA); - displayed_deck = myCollection; - - //Icons - mIcons[Constants::MTG_COLOR_ARTIFACT] = resources.GetQuad("c_artifact"); - mIcons[Constants::MTG_COLOR_LAND] = resources.GetQuad("c_land"); - mIcons[Constants::MTG_COLOR_WHITE] = resources.GetQuad("c_white"); - mIcons[Constants::MTG_COLOR_RED] = resources.GetQuad("c_red"); - mIcons[Constants::MTG_COLOR_BLACK] = resources.GetQuad("c_black"); - mIcons[Constants::MTG_COLOR_BLUE] = resources.GetQuad("c_blue"); - mIcons[Constants::MTG_COLOR_GREEN] = resources.GetQuad("c_green"); - for (int i = 0; i < 7; i++) - { - mIcons[i]->SetHotSpot(16, 16); - } - - //Grab a texture in VRAM. - pspIconsTexture = resources.RetrieveTexture("iconspsp.png", RETRIEVE_LOCK); - - char buf[512]; - for (int i = 0; i < 8; i++) - { - sprintf(buf, "iconspsp%d", i); - pspIcons[i] = resources.RetrieveQuad("iconspsp.png", (float) i * 32, 0, 32, 32, buf); - pspIcons[i]->SetHotSpot(16, 16); - } - - backQuad = resources.GetQuad("back"); - - //init welcome menu - updateDecks(); - - GameApp::playMusic("track1.mp3"); - - loadIndexes(); - mEngine->ResetInput(); - JRenderer::GetInstance()->EnableVSync(true); -} - -void GameStateDeckViewer::End() -{ - JRenderer::GetInstance()->EnableVSync(false); - - SAFE_DELETE(welcome_menu); - SAFE_DELETE(menu); - SAFE_DELETE(subMenu); - - resources.Release(pspIconsTexture); - if (myCollection) - { - SAFE_DELETE(myCollection); - } - if (myDeck) - { - SAFE_DELETE(myDeck->parent); - SAFE_DELETE(myDeck); - } - SAFE_DELETE(pricelist); - SAFE_DELETE(playerdata); - SAFE_DELETE(filterMenu); - SAFE_DELETE(source); -} - -void GameStateDeckViewer::addRemove(MTGCard * card) -{ - if (!card) - return; - if (displayed_deck->Remove(card, 1, (displayed_deck == myDeck))) - { - if (displayed_deck == myCollection) - { - myDeck->Add(card); - myDeck->Sort(WSrcCards::SORT_ALPHA); - } - else - { - myCollection->Add(card); - } - } - myCollection->validate(); - myDeck->validate(); - stw->needUpdate = true; - loadIndexes(); -} - -void GameStateDeckViewer::saveDeck() -{ - myDeck->save(); - playerdata->save(); - pricelist->save(); -} - -/** - save the deck in a readable format to allow people to edit the file offline - */ -void GameStateDeckViewer::saveAsAIDeck(string deckName) -{ - DeckManager * deckManager = DeckManager::GetInstance(); - vector aiDecks = GameState::getValidDeckMetaData(JGE_GET_RES("ai/baka"), "ai_baka", NULL); - int nbAiDecks = aiDecks.size() + 1; - aiDecks.clear(); - - string defaultAiDeckName = "deck"; - std::ostringstream oss; - oss << "deck" << nbAiDecks; - defaultAiDeckName = oss.str(); - oss.str(""); - if (myDeck->parent->meta_desc == "") - oss << endl << "Can you beat your own creations?" << endl << "User created AI Deck # " << nbAiDecks; - else - oss << myDeck->parent->meta_desc; - string deckDesc = oss.str(); - string filepath = JGE_GET_RES("ai/baka/"); - filepath.append(defaultAiDeckName).append(".txt"); - DebugTrace("saving AI deck " << filepath); - myDeck->save(filepath, true, deckName, deckDesc); -} - -void GameStateDeckViewer::Update(float dt) -{ - - int myD = (displayed_deck == myDeck); - - if (options.keypadActive()) - { - options.keypadUpdate(dt); - - if (newDeckname != "") - { - newDeckname = options.keypadFinish(); - - if (newDeckname != "") - { - if (isAIDeckSave) - { - saveAsAIDeck(newDeckname); - isAIDeckSave = false; - } - else if (myDeck && myDeck->parent) - { - myDeck->parent->meta_name = newDeckname; - saveDeck(); - } - mStage = STAGE_WAITING; - } - newDeckname = ""; - } - //Prevent screen from updating. - return; - } - hudAlpha = 255 - ((int) last_user_activity * 500); - if (hudAlpha < 0) - hudAlpha = 0; - if (subMenu) - { - subMenu->Update(dt); - if (subMenu->closed) - { - SAFE_DELETE(subMenu); - } - return; - } - if (mStage == STAGE_WAITING || mStage == STAGE_ONSCREEN_MENU) - { - switch (mEngine->ReadButton()) - { - case JGE_BTN_LEFT: - last_user_activity = 0; - mStage = STAGE_TRANSITION_LEFT; - break; - case JGE_BTN_RIGHT: - last_user_activity = 0; - mStage = STAGE_TRANSITION_RIGHT; - break; - case JGE_BTN_UP: - last_user_activity = 0; - mStage = STAGE_TRANSITION_UP; - useFilter++; - if (useFilter >= MAX_SAVED_FILTERS) - useFilter = 0; - break; - case JGE_BTN_DOWN: - last_user_activity = 0; - mStage = STAGE_TRANSITION_DOWN; - useFilter--; - if (useFilter < 0) - useFilter = MAX_SAVED_FILTERS - 1; - break; - case JGE_BTN_CANCEL: - options[Options::DISABLECARDS].number = !options[Options::DISABLECARDS].number; - break; - case JGE_BTN_PRI: - if (last_user_activity > 0.2) - { - last_user_activity = 0; - switchDisplay(); - } - break; - case JGE_BTN_OK: - last_user_activity = 0; - addRemove(cardIndex[2]); - break; - case JGE_BTN_SEC: - last_user_activity = 0; - SAFE_DELETE(subMenu); - char buffer[4096]; - { - MTGCard * card = cardIndex[2]; - if (card && displayed_deck->count(card)) - { - price = pricelist->getSellPrice(card->getMTGId()); - sprintf(buffer, "%s : %i %s", _(card->data->getName()).c_str(), price, _("credits").c_str()); - const float menuXOffset = SCREEN_WIDTH_F - 300; - const float menuYOffset = SCREEN_HEIGHT_F / 2; - subMenu = NEW SimpleMenu(MENU_CARD_PURCHASE, this, Fonts::MAIN_FONT, menuXOffset, menuYOffset, buffer); - subMenu->Add(MENU_ITEM_YES, "Yes"); - subMenu->Add(MENU_ITEM_NO, "No", "", true); - } - } - stw->needUpdate = true; - break; - - case JGE_BTN_MENU: - mStage = STAGE_MENU; - buildEditorMenu(); - break; - case JGE_BTN_CTRL: - mStage = STAGE_FILTERS; - if (!filterMenu) - { - filterMenu = NEW WGuiFilters("Filter by...", NULL); - if (source) - SAFE_DELETE(source); - source = NEW WSrcDeckViewer(myDeck, myCollection); - filterMenu->setSrc(source); - if (displayed_deck != myDeck) - source->swapSrc(); - } - filterMenu->Entering(JGE_BTN_NONE); - break; - case JGE_BTN_PREV: - if (last_user_activity < NO_USER_ACTIVITY_HELP_DELAY) - last_user_activity = NO_USER_ACTIVITY_HELP_DELAY + 1; - else if ((mStage == STAGE_ONSCREEN_MENU) && (--stw->currentPage < 0)) - stw->currentPage = stw->pageCount; - break; - case JGE_BTN_NEXT: - if (last_user_activity < NO_USER_ACTIVITY_HELP_DELAY) - last_user_activity = NO_USER_ACTIVITY_HELP_DELAY + 1; - else if ((mStage == STAGE_ONSCREEN_MENU) && (++stw->currentPage > stw->pageCount)) - stw->currentPage = 0; - break; - default: // no keypress - if (last_user_activity > NO_USER_ACTIVITY_HELP_DELAY) - { - if (mStage != STAGE_ONSCREEN_MENU) - { - mStage = STAGE_ONSCREEN_MENU; - onScreenTransition = 1; - } - else - { - if (onScreenTransition > 0) - onScreenTransition -= 0.05f; - else - onScreenTransition = 0; - } - } - else - last_user_activity += dt; - } - - } - if (mStage == STAGE_TRANSITION_RIGHT || mStage == STAGE_TRANSITION_LEFT) - { - if (mStage == STAGE_TRANSITION_RIGHT) - { - mRotation -= dt * MED_SPEED; - if (mRotation < -1.0f) - { - do - { - rotateCards(mStage); - mRotation += 1; - } while (mRotation < -1.0f); - mStage = STAGE_WAITING; - mRotation = 0; - } - } - else if (mStage == STAGE_TRANSITION_LEFT) - { - mRotation += dt * MED_SPEED; - if (mRotation > 1.0f) - { - do - { - rotateCards(mStage); - mRotation -= 1; - } while (mRotation > 1.0f); - mStage = STAGE_WAITING; - mRotation = 0; - } - } - } - if (mStage == STAGE_TRANSITION_DOWN || mStage == STAGE_TRANSITION_UP) - { - if (mStage == STAGE_TRANSITION_DOWN) - { - mSlide -= 0.05f; - if (mSlide < -1.0f) - { - updateFilters(); - loadIndexes(); - mSlide = 1; - } - else if (mSlide > 0 && mSlide < 0.05) - { - mStage = STAGE_WAITING; - mSlide = 0; - } - } - if (mStage == STAGE_TRANSITION_UP) - { - mSlide += 0.05f; - if (mSlide > 1.0f) - { - updateFilters(); - loadIndexes(); - mSlide = -1; - } - else if (mSlide < 0 && mSlide > -0.05) - { - mStage = STAGE_WAITING; - mSlide = 0; - } - } - - } - else if (mStage == STAGE_WELCOME) - welcome_menu->Update(dt); - else if (mStage == STAGE_MENU) - menu->Update(dt); - else if (mStage == STAGE_FILTERS) - { - JButton key = mEngine->ReadButton(); - if (filterMenu) - { - if (key == JGE_BTN_CTRL) - { - //useFilter = 0; - filterMenu->Finish(true); - filterMenu->Update(dt); - loadIndexes(); - return; - } - if (!filterMenu->isFinished()) - { - filterMenu->CheckUserInput(key); - filterMenu->Update(dt); - } - else - { - mStage = STAGE_WAITING; - updateFilters(); - loadIndexes(); - } - } - } -} - -void GameStateDeckViewer::renderOnScreenBasicInfo() -{ - JRenderer *renderer = JRenderer::GetInstance(); - WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT); - char buffer[256]; - int myD = (displayed_deck == myDeck); - - float y = 0; - int allCopies, nowCopies; - nowCopies = displayed_deck->getCount(WSrcDeck::FILTERED_COPIES); - allCopies = displayed_deck->getCount(WSrcDeck::UNFILTERED_COPIES); - WCardFilter * wc = displayed_deck->getFiltersRoot(); - - if (wc) - sprintf(buffer, "%s %i of %i cards (%i unique)", (displayed_deck == myDeck) ? "DECK: " : " ", - nowCopies, allCopies, displayed_deck->getCount(WSrcDeck::FILTERED_UNIQUE)); - else - sprintf(buffer, "%s%i cards (%i unique)", (displayed_deck == myDeck) ? "DECK: " : " ", - allCopies, displayed_deck->getCount(WSrcDeck::UNFILTERED_UNIQUE)); - - float w = mFont->GetStringWidth(buffer); - renderer->FillRoundRect(SCREEN_WIDTH - (w + 27), y + 5, w + 10, 15, 5, ARGB(128,0,0,0)); - - mFont->DrawString(buffer, SCREEN_WIDTH - 22, y + 15, JGETEXT_RIGHT); - if (useFilter != 0) - renderer->RenderQuad(mIcons[useFilter - 1], SCREEN_WIDTH - 10, y + 15, 0.0f, 0.5, 0.5); -} - -//returns position of the current card (cusor) in the currently viewed color/filter -int GameStateDeckViewer::getCurrentPos() -{ - int total = displayed_deck->Size(); - - int currentPos = displayed_deck->getOffset(); - currentPos += 2; //we start by displaying card number 3 - currentPos = currentPos % total + 1; - if (currentPos < 0) - currentPos = (total + currentPos); - if (!currentPos) - currentPos = total; - return currentPos; -} - -void GameStateDeckViewer::renderSlideBar() -{ - WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT); - - int total = displayed_deck->Size(); - if (total == 0) - return; - - float filler = 15; - float y = SCREEN_HEIGHT_F - 25; - float bar_size = SCREEN_WIDTH_F - 2 * filler; - JRenderer * r = JRenderer::GetInstance(); - int currentPos = getCurrentPos(); - - float cursor_pos = bar_size * currentPos / total; - - r->FillRoundRect(filler + 5, y + 5, bar_size, 0, 3, ARGB(hudAlpha/2,0,0,0)); - r->DrawLine(filler + cursor_pos + 5, y + 5, filler + cursor_pos + 5, y + 10, ARGB(hudAlpha/2,0,0,0)); - - r->FillRoundRect(filler, y, bar_size, 0, 3, ARGB(hudAlpha/2,128,128,128)); - r->DrawLine(filler + cursor_pos, y, filler + cursor_pos, y + 5, ARGB(hudAlpha,255,255,255)); - char buffer[256]; - string deckname = _("Collection"); - if (displayed_deck == myDeck) - { - deckname = _("Deck"); - } - sprintf(buffer, "%s - %i/%i", deckname.c_str(), currentPos, total); - mFont->SetColor(ARGB(hudAlpha,255,255,255)); - mFont->DrawString(buffer, SCREEN_WIDTH / 2, y, JGETEXT_CENTER); - - mFont->SetColor(ARGB(255,255,255,255)); -} - -void GameStateDeckViewer::renderDeckBackground() -{ - int max1 = 0; - int maxC1 = 4; - int max2 = 0; - int maxC2 = 4; - - for (int i = 0; i < Constants::MTG_NB_COLORS - 1; i++) - { - int value = myDeck->getCount(i); - if (value > max1) - { - max2 = max1; - maxC2 = maxC1; - max1 = value; - maxC1 = i; - } - else if (value > max2) - { - max2 = value; - maxC2 = i; - } - } - if (max2 < max1 / 2) - { - maxC2 = maxC1; - } - PIXEL_TYPE colors[] = { ARGB(255, Constants::_r[maxC1], Constants::_g[maxC1], Constants::_b[maxC1]), - ARGB(255, Constants::_r[maxC1], Constants::_g[maxC1], Constants::_b[maxC1]), - ARGB(255, Constants::_r[maxC2], Constants::_g[maxC2], Constants::_b[maxC2]), - ARGB(255, Constants::_r[maxC2], Constants::_g[maxC2], Constants::_b[maxC2]), }; - - JRenderer::GetInstance()->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, colors); - -} - -void GameStateDeckViewer::renderOnScreenMenu() -{ - - WFont * font = resources.GetWFont(Fonts::MAIN_FONT); - font->SetColor(ARGB(255,255,255,255)); - JRenderer * r = JRenderer::GetInstance(); - float pspIconsSize = 0.5; - float fH = font->GetHeight() + 1; - - float leftTransition = onScreenTransition * 84; - float rightTransition = onScreenTransition * 204; - float leftPspX = 40 - leftTransition; - float leftPspY = SCREEN_HEIGHT / 2 - 20; - float rightPspX = SCREEN_WIDTH - 100 + rightTransition; - float rightPspY = SCREEN_HEIGHT / 2 - 20; - - if (stw->currentPage == 0) - { - //FillRects - r->FillRect(0 - (onScreenTransition * 84), 0, 84, SCREEN_HEIGHT, ARGB(128,0,0,0)); - r->FillRect(SCREEN_WIDTH - 204 + (onScreenTransition * 204), 0, 200, SCREEN_HEIGHT, ARGB(128,0,0,0)); - - //LEFT PSP CIRCLE render - r->FillCircle(leftPspX, leftPspY, 40, ARGB(128,50,50,50)); - - r->RenderQuad(pspIcons[0], leftPspX, leftPspY - 20, 0, pspIconsSize, pspIconsSize); - r->RenderQuad(pspIcons[1], leftPspX, leftPspY + 20, 0, pspIconsSize, pspIconsSize); - r->RenderQuad(pspIcons[2], leftPspX - 20, leftPspY, 0, pspIconsSize, pspIconsSize); - r->RenderQuad(pspIcons[3], leftPspX + 20, leftPspY, 0, pspIconsSize, pspIconsSize); - - font->DrawString(_("Prev."), leftPspX - 35, leftPspY - 15); - font->DrawString(_("Next"), leftPspX + 15, leftPspY - 15); - font->DrawString(_("card"), leftPspX - 35, leftPspY); - font->DrawString(_("card"), leftPspX + 15, leftPspY); - font->DrawString(_("Next edition"), leftPspX - 33, leftPspY - 35); - font->DrawString(_("Prev. edition"), leftPspX - 33, leftPspY + 25); - - //RIGHT PSP CIRCLE render - r->FillCircle(rightPspX + (onScreenTransition * 204), rightPspY, 40, ARGB(128,50,50,50)); - r->RenderQuad(pspIcons[4], rightPspX + 20, rightPspY, 0, pspIconsSize, pspIconsSize); - r->RenderQuad(pspIcons[5], rightPspX, rightPspY - 20, 0, pspIconsSize, pspIconsSize); - r->RenderQuad(pspIcons[6], rightPspX - 20, rightPspY, 0, pspIconsSize, pspIconsSize); - r->RenderQuad(pspIcons[7], rightPspX, rightPspY + 20, 0, pspIconsSize, pspIconsSize); - - font->DrawString(_("Toggle Images"), rightPspX - 35, rightPspY - 40); - - if (displayed_deck == myCollection) - { - font->DrawString(_("Add card"), rightPspX + 20, rightPspY - 15); - font->DrawString(_("View Deck"), rightPspX - 20, rightPspY - 15, JGETEXT_RIGHT); - } - else - { - font->DrawString(_("Remove card"), rightPspX + 20, rightPspY - 15); - font->DrawString(_("View Collection"), rightPspX - 20, rightPspY - 15, JGETEXT_RIGHT); - } - font->DrawString(_("Sell card"), rightPspX - 30, rightPspY + 20); - //Bottom menus - font->DrawString(_("menu"), SCREEN_WIDTH - 35 + rightTransition, SCREEN_HEIGHT - 15); - font->DrawString(_("filter"), SCREEN_WIDTH - 95 + rightTransition, SCREEN_HEIGHT - 15); - - //Your Deck Information - char buffer[300]; - int nb_letters = 0; - for (int j = 0; j < Constants::MTG_NB_COLORS; j++) - { - int value = myDeck->getCount(j); - if (value > 0) - { - sprintf(buffer, "%i", value); - font->DrawString(buffer, SCREEN_WIDTH - 190 + rightTransition + nb_letters * 13, SCREEN_HEIGHT / 2 + 40); - r->RenderQuad(mIcons[j], SCREEN_WIDTH - 197 + rightTransition + nb_letters * 13, SCREEN_HEIGHT / 2 + 46, 0, 0.5, - 0.5); - if (value > 9) - { - nb_letters += 3; - } - else - { - nb_letters += 2; - } - } - } - int value = myDeck->getCount(WSrcDeck::UNFILTERED_COPIES); - sprintf(buffer, _("Your Deck: %i cards").c_str(), value); - font->DrawString(buffer, SCREEN_WIDTH - 200 + rightTransition, SCREEN_HEIGHT / 2 + 25); - - if (displayed_deck == myCollection) - { - font->DrawString(_("in: collection"), 5 - leftTransition, 5); - font->DrawString(_("Use SQUARE to view your deck,"), SCREEN_WIDTH - 200 + rightTransition, 5); - } - else - { - font->DrawString(_("in: deck"), 5 - leftTransition, 5); - font->DrawString(_("Use SQUARE to view collection,"), SCREEN_WIDTH - 200 + rightTransition, 5); - } - font->DrawString(_("Press L/R to cycle through"), SCREEN_WIDTH - 200 + rightTransition, 5 + fH); - font->DrawString(_("deck statistics."), SCREEN_WIDTH - 200 + rightTransition, 5 + fH * 2); - - } - else - { - if (stw->needUpdate) - { - updateStats(); - } - - char buffer[300]; - - leftTransition = -(onScreenTransition / 2) * SCREEN_WIDTH; - rightTransition = -leftTransition; - - r->FillRect(0 + leftTransition, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT, ARGB(128,0,0,0)); - r->FillRect(SCREEN_WIDTH / 2 + rightTransition, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT, ARGB(128,0,0,0)); - r->FillRect(10 + leftTransition, 10, SCREEN_WIDTH / 2 - 10, SCREEN_HEIGHT - 20, ARGB(128,0,0,0)); - r->FillRect(SCREEN_WIDTH / 2 + rightTransition, 10, SCREEN_WIDTH / 2 - 10, SCREEN_HEIGHT - 20, ARGB(128,0,0,0)); - font->DrawString(_("menu"), SCREEN_WIDTH - 35 + rightTransition, SCREEN_HEIGHT - 15); - font->DrawString(_("filter"), SCREEN_WIDTH - 95 + rightTransition, SCREEN_HEIGHT - 15); - - int nb_letters = 0; - float posX, posY; - DWORD graphColor; - - graphColor = ARGB(200, 155, 155, 155); - string STATS_TITLE_FORMAT = _("%i: %s"); - - switch (stw->currentPage) - { - case 1: // Counts, price - // Title - sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Statistics Summary").c_str()); - font->DrawString(buffer, 10 + leftTransition, 10); - - posY = 30; - posX = 180; - sprintf(buffer, _("Your Deck: %i cards").c_str(), stw->cardCount); - font->DrawString(buffer, 20 + leftTransition, posY); - posY += 10; - - // Counts by color - for (int j = 0; j < Constants::MTG_NB_COLORS; j++) - { - int value = myDeck->getCount(j); - if (value > 0) - { - sprintf(buffer, "%i", value); - font->DrawString(buffer, 38 + nb_letters * 13 + leftTransition, posY + 5); - r->RenderQuad(mIcons[j], 30 + nb_letters * 13 + leftTransition, posY + 11, 0, 0.5, 0.5); - if (value > 9) - { - nb_letters += 3; - } - else - { - nb_letters += 2; - } - } - } - posY += 25; - - r->DrawLine(posX - 4 + leftTransition, posY - 1, posX - 4 + leftTransition, posY + 177, ARGB(128, 255, 255, 255)); - r->DrawLine(19 + leftTransition, posY - 1, 19 + leftTransition, posY + 177, ARGB(128, 255, 255, 255)); - r->DrawLine(posX + 40 + leftTransition, posY - 1, posX + 40 + leftTransition, posY + 177, ARGB(128, 255, 255, 255)); - - r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); - - font->DrawString(_("Lands"), 20 + leftTransition, posY); - sprintf(buffer, _("%i").c_str(), stw->countLands); - font->DrawString(buffer, posX + leftTransition, posY); - - posY += 14; - r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); - font->DrawString(_("Creatures"), 20 + leftTransition, posY); - sprintf(buffer, _("%i").c_str(), stw->countCreatures); - font->DrawString(buffer, posX + leftTransition, posY); - - posY += 14; - r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); - font->DrawString(_("Spells"), 20 + leftTransition, posY); - sprintf(buffer, _("%i").c_str(), stw->countSpells); - font->DrawString(buffer, posX + leftTransition, posY); - - posY += 10; - font->DrawString(_("Instants"), 30 + leftTransition, posY); - sprintf(buffer, _("%i").c_str(), stw->countInstants); - font->DrawString(buffer, posX + leftTransition, posY); - - posY += 10; - font->DrawString(_("Enchantments"), 30 + leftTransition, posY); - sprintf(buffer, _("%i").c_str(), stw->countEnchantments); - font->DrawString(buffer, posX + leftTransition, posY); - - posY += 10; - font->DrawString(_("Sorceries"), 30 + leftTransition, posY); - sprintf(buffer, _("%i").c_str(), stw->countSorceries); - font->DrawString(buffer, posX + leftTransition, posY); - //sprintf(buffer, "Artifacts: %i", stw->countArtifacts); - //mFont->DrawString(buffer, 20, 123); - - posY += 14; - r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); - - font->DrawString(_("Average converted mana cost"), 20 + leftTransition, posY); - sprintf(buffer, _("%2.2f").c_str(), stw->avgManaCost); - font->DrawString(buffer, posX + leftTransition, posY); - - posY += 14; - r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); - font->DrawString(_("Probabilities"), 20 + leftTransition, posY); - - posY += 10; - font->DrawString(_("No land in 1st hand"), 30 + leftTransition, posY); - sprintf(buffer, _("%2.2f%%").c_str(), stw->noLandsProbInTurn[0]); - font->DrawString(buffer, posX + leftTransition, posY); - - posY += 10; - font->DrawString(_("No land in 9 cards"), 30 + leftTransition, posY); - sprintf(buffer, _("%2.2f%%").c_str(), stw->noLandsProbInTurn[2]); - font->DrawString(buffer, posX + leftTransition, posY); - - posY += 10; - font->DrawString(_("No creatures in 1st hand"), 30 + leftTransition, posY); - sprintf(buffer, _("%2.2f%%").c_str(), stw->noCreaturesProbInTurn[0]); - font->DrawString(buffer, posX + leftTransition, posY); - - // Playgame Statistics - posY += 14; - r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); - font->DrawString(_("Playgame statistics"), 20 + leftTransition, posY); - - posY += 10; - font->DrawString(_("Games played"), 30 + leftTransition, posY); - sprintf(buffer, _("%i").c_str(), stw->gamesPlayed); - font->DrawString(buffer, posX + leftTransition, posY); - - posY += 10; - font->DrawString(_("Victory ratio"), 30 + leftTransition, posY); - sprintf(buffer, _("%i%%").c_str(), stw->percentVictories); - font->DrawString(buffer, posX + leftTransition, posY); - - posY += 15; - r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); - font->DrawString(_("Total price (credits)"), 20 + leftTransition, posY); - sprintf(buffer, _("%i ").c_str(), stw->totalPrice); - font->DrawString(buffer, posX + leftTransition, posY); - r->DrawLine(20 + leftTransition, posY + 13, posX + 40 + leftTransition, posY + 13, ARGB(128, 255, 255, 255)); - - break; - - case 5: // Land statistics - sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Mana production").c_str()); - font->DrawString(buffer, 10 + leftTransition, 10); - - font->DrawString(_("Counts of manasources per type and color:"), 20 + leftTransition, 30); - - posY = 70; - - // Column titles - for (int j = 0; j < Constants::MTG_NB_COLORS - 1; j++) - { - r->RenderQuad(mIcons[j], 52 + j * 15 + leftTransition, posY - 10, 0, 0.5, 0.5); - } - - //font->DrawString(_("C"), 30 + leftTransition, posY-16); - //font->DrawString(_("Ty"), 27 + leftTransition, posY-16); - - // Horizontal table lines - r->DrawLine(27 + leftTransition, posY - 20, 60 + (Constants::MTG_NB_COLORS - 2) * 15 + leftTransition, posY - 20, - ARGB(128, 255, 255, 255)); - r->DrawLine(27 + leftTransition, posY - 1, 60 + (Constants::MTG_NB_COLORS - 2) * 15 + leftTransition, posY - 1, - ARGB(128, 255, 255, 255)); - r->DrawLine(27 + leftTransition, 2 * 10 + posY + 12, 60 + (Constants::MTG_NB_COLORS - 2) * 15 + leftTransition, 2 * 10 - + posY + 12, ARGB(128, 255, 255, 255)); - r->DrawLine(27 + leftTransition, 3 * 10 + posY + 14, 60 + (Constants::MTG_NB_COLORS - 2) * 15 + leftTransition, 3 * 10 - + posY + 14, ARGB(128, 255, 255, 255)); - - // Vertical table lines - r->DrawLine(26 + leftTransition, posY - 20, 26 + leftTransition, 3 * 10 + posY + 14, ARGB(128, 255, 255, 255)); - r->DrawLine(43 + leftTransition, posY - 20, 43 + leftTransition, 3 * 10 + posY + 14, ARGB(128, 255, 255, 255)); - r->DrawLine(60 + leftTransition + (Constants::MTG_NB_COLORS - 2) * 15, posY - 20, 60 + leftTransition - + (Constants::MTG_NB_COLORS - 2) * 15, 3 * 10 + posY + 14, ARGB(128, 255, 255, 255)); - - font->DrawString(_("BL"), 27 + leftTransition, posY); - font->DrawString(_("NB"), 27 + leftTransition, posY + 10); - font->DrawString(_("O"), 30 + leftTransition, posY + 20); - font->DrawString(_("T"), 30 + leftTransition, posY + 33); - - int curCount; - - for (int j = 0; j < Constants::MTG_NB_COLORS - 1; j++) - { - curCount = stw->countBasicLandsPerColor[j]; - sprintf(buffer, (curCount == 0 ? "." : "%i"), curCount); - font->DrawString(buffer, 49 + leftTransition + j * 15, posY); - - curCount = stw->countLandsPerColor[j]; - sprintf(buffer, (curCount == 0 ? "." : "%i"), curCount); - font->DrawString(buffer, 49 + leftTransition + j * 15, posY + 10); - - curCount = stw->countNonLandProducersPerColor[j]; - sprintf(buffer, (curCount == 0 ? "." : "%i"), curCount); - font->DrawString(buffer, 49 + leftTransition + j * 15, posY + 20); - - curCount = stw->countLandsPerColor[j] + stw->countBasicLandsPerColor[j] + stw->countNonLandProducersPerColor[j]; - sprintf(buffer, (curCount == 0 ? "." : "%i"), curCount); - font->DrawString(buffer, 49 + leftTransition + j * 15, posY + 33); - } - - posY += 55; - font->DrawString(_("BL - Basic lands"), 20 + leftTransition, posY); - posY += 10; - font->DrawString(_("NB - Non-basic lands"), 20 + leftTransition, posY); - posY += 10; - font->DrawString(_("O - Other (non-land) manasources"), 26 + leftTransition, posY); - posY += 10; - font->DrawString(_("T - Totals"), 26 + leftTransition, posY); - - break; - - case 6: // Land statistics - in symbols - sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Mana production - in mana symbols").c_str()); - font->DrawString(buffer, 10 + leftTransition, 10); - font->DrawString(_("Total colored manasymbols in lands' production:"), 20 + leftTransition, 30); - - int totalProducedSymbols; - totalProducedSymbols = 0; - for (int i = 1; i < Constants::MTG_NB_COLORS - 1; i++) - { - totalProducedSymbols += stw->countLandsPerColor[i] + stw->countBasicLandsPerColor[i]; //!! Move to updatestats! - } - - posY = 50; - for (int i = 1; i < Constants::MTG_NB_COLORS - 1; i++) - { - if (stw->countLandsPerColor[i] + stw->countBasicLandsPerColor[i] > 0) - { - sprintf(buffer, _("%i").c_str(), stw->countLandsPerColor[i] + stw->countBasicLandsPerColor[i]); - font->DrawString(buffer, 20 + leftTransition, posY); - sprintf(buffer, _("(%i%%)").c_str(), (int) (100 * (float) (stw->countLandsPerColor[i] - + stw->countBasicLandsPerColor[i]) / totalProducedSymbols)); - font->DrawString(buffer, 33 + leftTransition, posY); - posX = 72; - for (int j = 0; j < stw->countLandsPerColor[i] + stw->countBasicLandsPerColor[i]; j++) - { - r->RenderQuad(mIcons[i], posX + leftTransition, posY + 6, 0, 0.5, 0.5); - posX += ((j + 1) % 10 == 0) ? 17 : 13; - if ((((j + 1) % 30) == 0) && (j < stw->countLandsPerColor[i] + stw->countBasicLandsPerColor[i] - 1)) - { - posX = 72; - posY += 15; - } - } - posY += 17; - } - } - - break; - - case 2: // Mana cost detail - case 3: - case 4: - int (*countPerCost)[Constants::STATS_MAX_MANA_COST + 1]; - int (*countPerCostAndColor)[Constants::STATS_MAX_MANA_COST + 1][Constants::MTG_NB_COLORS + 1]; - float avgCost; - - switch (stw->currentPage) - { // Nested switch on the same variable. Oh yes. - case 2: // Total counts - // Title - sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Mana cost detail").c_str()); - font->DrawString(buffer, 10 + leftTransition, 10); - font->DrawString(_("Card counts per mana cost:"), 20 + leftTransition, 30); - avgCost = stw->avgManaCost; - countPerCost = &stw->countCardsPerCost; - countPerCostAndColor = &stw->countCardsPerCostAndColor; - break; - case 3: // Creature counts - // Title - sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Mana cost detail - Creatures").c_str()); - font->DrawString(buffer, 10 + leftTransition, 10); - font->DrawString(_("Creature counts per mana cost:"), 20 + leftTransition, 30); - avgCost = stw->avgCreatureCost; - countPerCost = &stw->countCreaturesPerCost; - countPerCostAndColor = &stw->countCreaturesPerCostAndColor; - break; - case 4: // Spell counts - // Title - sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Mana cost detail - Spells").c_str()); - font->DrawString(buffer, 10 + leftTransition, 10); - font->DrawString(_("Non-creature spell counts per mana cost:"), 20 + leftTransition, 30); - avgCost = stw->avgSpellCost; - countPerCost = &stw->countSpellsPerCost; - countPerCostAndColor = &stw->countSpellsPerCostAndColor; - break; - default: - countPerCost = NULL; - countPerCostAndColor = NULL; - avgCost = 0; - break; - } - - posY = 70; - - // Column titles - for (int j = 0; j < Constants::MTG_NB_COLORS - 1; j++) - { - r->RenderQuad(mIcons[j], 67 + j * 15 + leftTransition, posY - 10, 0, 0.5, 0.5); - } - - font->DrawString(_("C"), 30 + leftTransition, posY - 16); - font->DrawString(_("#"), 45 + leftTransition, posY - 16); - - // Horizontal table lines - r->DrawLine(27 + leftTransition, posY - 20, 75 + (Constants::MTG_NB_COLORS - 2) * 15 + leftTransition, posY - 20, - ARGB(128, 255, 255, 255)); - r->DrawLine(27 + leftTransition, posY - 1, 75 + (Constants::MTG_NB_COLORS - 2) * 15 + leftTransition, posY - 1, - ARGB(128, 255, 255, 255)); - r->DrawLine(27 + leftTransition, Constants::STATS_MAX_MANA_COST * 10 + posY + 12, 75 + (Constants::MTG_NB_COLORS - 2) - * 15 + leftTransition, Constants::STATS_MAX_MANA_COST * 10 + posY + 12, ARGB(128, 255, 255, 255)); - - // Vertical table lines - r->DrawLine(26 + leftTransition, posY - 20, 26 + leftTransition, Constants::STATS_MAX_MANA_COST * 10 + posY + 12, - ARGB(128, 255, 255, 255)); - r->DrawLine(41 + leftTransition, posY - 20, 41 + leftTransition, Constants::STATS_MAX_MANA_COST * 10 + posY + 12, - ARGB(128, 255, 255, 255)); - r->DrawLine(58 + leftTransition, posY - 20, 58 + leftTransition, Constants::STATS_MAX_MANA_COST * 10 + posY + 12, - ARGB(128, 255, 255, 255)); - r->DrawLine(75 + leftTransition + (Constants::MTG_NB_COLORS - 2) * 15, posY - 20, 75 + leftTransition - + (Constants::MTG_NB_COLORS - 2) * 15, Constants::STATS_MAX_MANA_COST * 10 + posY + 12, - ARGB(128, 255, 255, 255)); - - for (int i = 0; i <= Constants::STATS_MAX_MANA_COST; i++) - { - sprintf(buffer, _("%i").c_str(), i); - font->DrawString(buffer, 30 + leftTransition, posY); - sprintf(buffer, ((*countPerCost)[i] > 0) ? _("%i").c_str() : ".", (*countPerCost)[i]); - font->DrawString(buffer, 45 + leftTransition, posY); - for (int j = 0; j < Constants::MTG_NB_COLORS - 1; j++) - { - sprintf(buffer, ((*countPerCostAndColor)[i][j] > 0) ? _("%i").c_str() : ".", (*countPerCostAndColor)[i][j]); - font->DrawString(buffer, 64 + leftTransition + j * 15, posY); - } - r->FillRect(77.f + leftTransition + (Constants::MTG_NB_COLORS - 2) * 15.0f, posY + 2.0f, (*countPerCost)[i] * 5.0f, - 8.0f, graphColor); - posY += 10; - } - - posY += 10; - sprintf(buffer, _("Average converted mana cost: %2.2f").c_str(), avgCost); - font->DrawString(buffer, 20 + leftTransition, posY); - posY += 15; - sprintf(buffer, _("C - Converted mana cost. Cards with cost>%i are included in the last row.").c_str(), - Constants::STATS_MAX_MANA_COST); - font->DrawString(buffer, 20 + leftTransition, posY); - posY += 10; - font->DrawString(_("# - Total number of cards with given cost"), 20 + leftTransition, posY); - - break; - - case 8: - // Title - sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Probabilities").c_str()); - font->DrawString(buffer, 10 + leftTransition, 10); - - // No lands detail - float graphScale, graphWidth; - graphWidth = 100; - graphScale = (stw->noLandsProbInTurn[0] == 0) ? 0 : (graphWidth / stw->noLandsProbInTurn[0]); - font->DrawString(_("No lands in first n cards:"), 20 + leftTransition, 30); - - posY = 50; - for (int i = 0; i < Constants::STATS_FOR_TURNS; i++) - { - sprintf(buffer, _("%i:").c_str(), i + 7); - font->DrawString(buffer, 30 + leftTransition, posY); - sprintf(buffer, _("%2.2f%%").c_str(), stw->noLandsProbInTurn[i]); - font->DrawString(buffer, 45 + leftTransition, posY); - r->FillRect(84 + leftTransition, posY + 2, graphScale * stw->noLandsProbInTurn[i], 8, graphColor); - posY += 10; - } - - // No creatures probability detail - posY += 10; - font->DrawString(_("No creatures in first n cards:"), 20 + leftTransition, posY); - posY += 20; - graphScale = (stw->noCreaturesProbInTurn[0] == 0) ? 0 : (graphWidth / stw->noCreaturesProbInTurn[0]); - - for (int i = 0; i < Constants::STATS_FOR_TURNS; i++) - { - sprintf(buffer, _("%i:").c_str(), i + 7); - font->DrawString(buffer, 30 + leftTransition, posY); - sprintf(buffer, _("%2.2f%%").c_str(), stw->noCreaturesProbInTurn[i]); - font->DrawString(buffer, 45 + leftTransition, posY); - r->FillRect(84 + leftTransition, posY + 2, graphScale * stw->noCreaturesProbInTurn[i], 8, graphColor); - posY += 10; - } - - break; - - case 7: // Total mana cost per color - // Title - sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Mana cost per color").c_str()); - font->DrawString(buffer, 10 + leftTransition, 10); - - font->DrawString(_("Total colored manasymbols in cards' casting costs:"), 20 + leftTransition, 30); - - posY = 50; - for (int i = 1; i < Constants::MTG_NB_COLORS - 1; i++) - { - if (stw->totalCostPerColor[i] > 0) - { - sprintf(buffer, _("%i").c_str(), stw->totalCostPerColor[i]); - font->DrawString(buffer, 20 + leftTransition, posY); - sprintf(buffer, _("(%i%%)").c_str(), (int) (100 * (float) stw->totalCostPerColor[i] / stw->totalColoredSymbols)); - font->DrawString(buffer, 33 + leftTransition, posY); - posX = 72; - for (int j = 0; j < stw->totalCostPerColor[i]; j++) - { - r->RenderQuad(mIcons[i], posX + leftTransition, posY + 6, 0, 0.5, 0.5); - posX += ((j + 1) % 10 == 0) ? 17 : 13; - if ((((j + 1) % 30) == 0) && (j < stw->totalCostPerColor[i] - 1)) - { - posX = 72; - posY += 15; - } - } - posY += 17; - } - } - break; - - case 9: // Victory statistics - // Title - sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Victory statistics").c_str()); - font->DrawString(buffer, 10 + leftTransition, 10); - - font->DrawString(_("Victories against AI:"), 20 + leftTransition, 30); - - sprintf(buffer, _("Games played: %i").c_str(), stw->gamesPlayed); - font->DrawString(buffer, 20 + leftTransition, 45); - sprintf(buffer, _("Victory ratio: %i%%").c_str(), stw->percentVictories); - font->DrawString(buffer, 20 + leftTransition, 55); - - int AIsPerColumn = 19; - posY = 70; - posX = 20; - - // ToDo: Multiple pages when too many AI decks are present - for (int i = 0; i < (int) stw->aiDeckStats.size(); i++) - { - sprintf(buffer, _("%.14s").c_str(), stw->aiDeckNames.at(i).c_str()); - font->DrawString(buffer, posX + (i < 2 * AIsPerColumn ? leftTransition : rightTransition), posY); - sprintf(buffer, _("%i/%i").c_str(), stw->aiDeckStats.at(i)->victories, stw->aiDeckStats.at(i)->nbgames); - font->DrawString(buffer, posX + (i < AIsPerColumn ? leftTransition : rightTransition) + 80, posY); - sprintf(buffer, _("%i%%").c_str(), stw->aiDeckStats.at(i)->percentVictories()); - font->DrawString(buffer, posX + (i < AIsPerColumn ? leftTransition : rightTransition) + 110, posY); - posY += 10; - if (((i + 1) % AIsPerColumn) == 0) - { - posY = 70; - posX += 155; - } - } - break; - } - } -} - -void GameStateDeckViewer::updateStats() -{ - if (!stw->needUpdate || !myDeck) - return; - stw->needUpdate = false; - stw->cardCount = myDeck->getCount(WSrcDeck::UNFILTERED_COPIES); - stw->countLands = myDeck->getCount(Constants::MTG_COLOR_LAND); - stw->totalPrice = myDeck->totalPrice(); - - stw->countManaProducers = 0; - // Mana cost - int currentCount, convertedCost; - ManaCost * currentCost; - stw->totalManaCost = 0; - stw->totalCreatureCost = 0; - stw->totalSpellCost = 0; - MTGCard * current = myDeck->getCard(); - - // Clearing arrays - for (int i = 0; i <= Constants::STATS_MAX_MANA_COST; i++) - { - stw->countCardsPerCost[i] = 0; - stw->countCreaturesPerCost[i] = 0; - stw->countSpellsPerCost[i] = 0; - } - - for (int i = 0; i <= Constants::MTG_NB_COLORS; i++) - { - stw->totalCostPerColor[i] = 0; - stw->countLandsPerColor[i] = 0; - stw->countBasicLandsPerColor[i] = 0; - stw->countNonLandProducersPerColor[i] = 0; - } - - for (int i = 0; i <= Constants::STATS_MAX_MANA_COST; i++) - { - for (int k = 0; k <= Constants::MTG_NB_COLORS; k++) - { - stw->countCardsPerCostAndColor[i][k] = 0; - stw->countCreaturesPerCostAndColor[i][k] = 0; - stw->countSpellsPerCostAndColor[i][k] = 0; - } - } - - for (int ic = 0; ic < myDeck->Size(true); ic++) - { - current = myDeck->getCard(ic, true); - currentCost = current->data->getManaCost(); - convertedCost = currentCost->getConvertedCost(); - currentCount = myDeck->count(current); - - // Add to the cards per cost counters - stw->totalManaCost += convertedCost * currentCount; - if (convertedCost > Constants::STATS_MAX_MANA_COST) - { - convertedCost = Constants::STATS_MAX_MANA_COST; - } - stw->countCardsPerCost[convertedCost] += currentCount; - if (current->data->isCreature()) - { - stw->countCreaturesPerCost[convertedCost] += currentCount; - stw->totalCreatureCost += convertedCost * currentCount; - } - else if (current->data->isSpell()) - { - stw->countSpellsPerCost[convertedCost] += currentCount; - stw->totalSpellCost += convertedCost * currentCount; - } - - // Lets look for mana producing abilities - - vector abilityStrings; - string thisstring = current->data->magicText; - StringExplode(thisstring, "\n", &abilityStrings); - - for (int v = 0; v < (int) abilityStrings.size(); v++) - { - string s = abilityStrings[v]; - size_t t = s.find("add"); - if (t != string::npos) - { - s = s.substr(t + 3); - ManaCost * mc = ManaCost::parseManaCost(s); - for (int j = 0; j < Constants::MTG_NB_COLORS; j++) - { - if (mc->hasColor(j)) - { - if (current->data->isLand()) - { - if (current->data->hasType("Basic")) - { - stw->countBasicLandsPerColor[j] += currentCount; - } - else - { - stw->countLandsPerColor[j] += currentCount; - } - } - else - { - stw->countNonLandProducersPerColor[j] += currentCount; - } - } - } - SAFE_DELETE(mc); - } - } - - // Add to the per color counters - // a. regular costs - for (int j = 0; j < Constants::MTG_NB_COLORS; j++) - { - stw->totalCostPerColor[j] += currentCost->getCost(j) * currentCount; - if (current->data->hasColor(j)) - { - // Add to the per cost and color counter - stw->countCardsPerCostAndColor[convertedCost][j] += currentCount; - if (current->data->isCreature()) - { - stw->countCreaturesPerCostAndColor[convertedCost][j] += currentCount; - } - else if (current->data->isSpell()) - { - stw->countSpellsPerCostAndColor[convertedCost][j] += currentCount; - } - } - } - - // b. Hybrid costs - ManaCostHybrid * hybridCost; - int i; - i = 0; - - while ((hybridCost = currentCost->getHybridCost(i++)) != NULL) - { - stw->totalCostPerColor[hybridCost->color1] += hybridCost->value1 * currentCount; - stw->totalCostPerColor[hybridCost->color2] += hybridCost->value2 * currentCount; - } - } - - stw->totalColoredSymbols = 0; - for (int j = 1; j < Constants::MTG_NB_COLORS; j++) - { - stw->totalColoredSymbols += stw->totalCostPerColor[j]; - } - - stw->countCardsPerCost[0] -= stw->countLands; - - // Counts by type - stw->countCreatures = countCardsByType("Creature"); - stw->countInstants = countCardsByType("Instant"); - stw->countEnchantments = countCardsByType("Enchantment"); - stw->countSorceries = countCardsByType("Sorcery"); - stw->countSpells = stw->countInstants + stw->countEnchantments + stw->countSorceries; - //stw->countArtifacts = countCardsByType("Artifact"); - - // Average mana costs - stw->avgManaCost = ((stw->cardCount - stw->countLands) <= 0) ? 0 : (float) stw->totalManaCost / (stw->cardCount - - stw->countLands); - stw->avgCreatureCost = (stw->countCreatures <= 0) ? 0 : (float) stw->totalCreatureCost / stw->countCreatures; - stw->avgSpellCost = (stw->countSpells <= 0) ? 0 : (float) stw->totalSpellCost / stw->countSpells; - - // Probabilities - // TODO: this could be optimized by reusing results - for (int i = 0; i < Constants::STATS_FOR_TURNS; i++) - { - stw->noLandsProbInTurn[i] = noLuck(stw->cardCount, stw->countLands, 7 + i) * 100; - stw->noCreaturesProbInTurn[i] = noLuck(stw->cardCount, stw->countCreatures, 7 + i) * 100; - } -} - -// This should probably be cached in DeckDataWrapper -// or at least be calculated for all common types in one go -int GameStateDeckViewer::countCardsByType(const char * _type) -{ - int result = 0; - for (int i = 0; i < myDeck->Size(true); i++) - { - MTGCard * current = myDeck->getCard(i, true); - if (current->data->hasType(_type)) - { - result += myDeck->count(current); - } - } - return result; -} - -void GameStateDeckViewer::renderCard(int id, float rotation) -{ - WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT); - MTGCard * card = cardIndex[id]; - - float max_scale = 0.96f; - float x_center_0 = 180; - float right_border = SCREEN_WIDTH - 20; - - float x_center = x_center_0 + cos((rotation + 8 - id) * M_PI / 12) * (right_border - x_center_0); - float scale = max_scale / 1.12f * cos((x_center - x_center_0) * 1.5f / (right_border - x_center_0)) + 0.2f * max_scale * cos( - cos((x_center - x_center_0) * 0.15f / (right_border - x_center_0))); - float x = x_center; // ; - - float y = (SCREEN_HEIGHT_F) / 2.0f + SCREEN_HEIGHT_F * mSlide * (scale + 0.2f); - - int alpha = (int) (255 * (scale + 1.0 - max_scale)); - - if (!card) - return; - JQuad * quad = NULL; - - int cacheError = CACHE_ERROR_NONE; - - if (!options[Options::DISABLECARDS].number) - { - quad = resources.RetrieveCard(card, RETRIEVE_EXISTING); - cacheError = resources.RetrieveError(); - if (!quad && cacheError != CACHE_ERROR_404) - { - if (last_user_activity > (abs(2 - id) + 1) * NO_USER_ACTIVITY_SHOWCARD_DELAY) - quad = resources.RetrieveCard(card); - else - { - quad = backQuad; - } - } - } - - int quadAlpha = alpha; - if (!displayed_deck->count(card)) - quadAlpha /= 2; - if (quad) - { - if (quad == backQuad) - { - quad->SetColor(ARGB(255,255,255,255)); - float _scale = scale * (285 / quad->mHeight); - JRenderer::GetInstance()->RenderQuad(quad, x, y, 0.0f, _scale, _scale); - } - else - { - Pos pos = Pos(x, y, scale * 285 / 250, 0.0, 255); - CardGui::DrawCard(card, pos); - } - } - else - { - Pos pos = Pos(x, y, scale * 285 / 250, 0.0, 255); - CardGui::DrawCard(card, pos, DrawMode::kText); - if (!options[Options::DISABLECARDS].number) - quad = resources.RetrieveCard(card, CACHE_THUMB); - if (quad) - { - float _scale = 285 * scale / quad->mHeight; - quad->SetColor(ARGB(40,255,255,255)); - JRenderer::GetInstance()->RenderQuad(quad, x, y, 0, _scale, _scale); - } - } - quadAlpha = 255 - quadAlpha; - if (quadAlpha > 0) - { - JRenderer::GetInstance()->FillRect(x - scale * 100.0f, y - scale * 142.5f, scale * 200.0f, scale * 285.0f, - ARGB(quadAlpha,0,0,0)); - } - if (last_user_activity < 3) - { - int fontAlpha = alpha; - float qtY = y - 135 * scale; - float qtX = x + 40 * scale; - char buffer[4096]; - sprintf(buffer, "x%i", displayed_deck->count(card)); - WFont * font = mFont; - font->SetColor(ARGB(fontAlpha/2,0,0,0)); - JRenderer::GetInstance()->FillRect(qtX, qtY, font->GetStringWidth(buffer) + 6, 16, ARGB(fontAlpha/2,0,0,0)); - font->DrawString(buffer, qtX + 4, qtY + 4); - font->SetColor(ARGB(fontAlpha,255,255,255)); - font->DrawString(buffer, qtX + 2, qtY + 2); - font->SetColor(ARGB(255,255,255,255)); - } -} - -void GameStateDeckViewer::renderCard(int id) -{ - renderCard(id, 0); -} - -void GameStateDeckViewer::Render() -{ - - WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT); - - JRenderer * r = JRenderer::GetInstance(); - r->ClearScreen(ARGB(0,0,0,0)); - if (displayed_deck == myDeck && mStage != STAGE_MENU) - renderDeckBackground(); - int order[3] = { 1, 2, 3 }; - if (mRotation < 0.5 && mRotation > -0.5) - { - order[1] = 3; - order[2] = 2; - } - else if (mRotation < -0.5) - { - order[0] = 3; - order[2] = 1; - } - - renderCard(6, mRotation); - renderCard(5, mRotation); - renderCard(4, mRotation); - renderCard(0, mRotation); - - for (int i = 0; i < 3; i++) - { - renderCard(order[i], mRotation); - } - - if (displayed_deck->Size() > 0) - { - renderSlideBar(); - } - else - { - mFont->DrawString(_("No Card"), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, JGETEXT_CENTER); - } - if (mStage == STAGE_ONSCREEN_MENU) - { - renderOnScreenMenu(); - } - else if (mStage == STAGE_WELCOME) - { - welcome_menu->Render(); - } - else - { - renderOnScreenBasicInfo(); - - } - if (mStage == STAGE_MENU) - { - menu->Render(); - } - if (subMenu) - subMenu->Render(); - - if (filterMenu && !filterMenu->isFinished()) - filterMenu->Render(); - - if (options.keypadActive()) - options.keypadRender(); - -} - -int GameStateDeckViewer::loadDeck(int deckid) -{ - - if (!stw) - stw = new StatsWrapper(deckid); - - stw->currentPage = 0; - stw->pageCount = 9; - stw->needUpdate = true; - - if (!playerdata) - playerdata = NEW PlayerData(mParent->collection); - SAFE_DELETE(myCollection); - myCollection = NEW DeckDataWrapper(playerdata->collection); - myCollection->Sort(WSrcCards::SORT_ALPHA); - displayed_deck = myCollection; - - char deckname[256]; - sprintf(deckname, "deck%i.txt", deckid); - if (myDeck) - { - SAFE_DELETE(myDeck->parent); - SAFE_DELETE(myDeck); - } - myDeck = NEW DeckDataWrapper(NEW MTGDeck(options.profileFile(deckname, "", false, false).c_str(), mParent->collection)); - - // Check whether the cards in the deck are actually available in the player's collection: - int cheatmode = options[Options::CHEATMODE].number; - bool bPure = true; - for (int i = 0; i < myDeck->Size(true); i++) - { - MTGCard * current = myDeck->getCard(i, true); - int howmanyinDeck = myDeck->count(current); - for (int i = myCollection->count(current); i < howmanyinDeck; i++) - { - bPure = false; - if (cheatmode) - { //Are we cheating? - playerdata->collection->add(current); //Yup, add it to collection permanently. - myCollection->Add(current); - } - else - { - myDeck->Remove(current); //Nope. Remove it from deck. - break; - } - } - - myCollection->Remove(current, myDeck->count(current)); - } - if (!bPure) - { - myDeck->validate(); - myCollection->validate(); - } - - myDeck->Sort(WSrcCards::SORT_ALPHA); - SAFE_DELETE(filterMenu); - rebuildFilters(); - loadIndexes(); - return 1; -} - -void GameStateDeckViewer::ButtonPressed(int controllerId, int controlId) -{ - int deckIdNumber = controlId; - int deckListSize = 0; - string defaultAiName; - DeckManager *deckManager = DeckManager::GetInstance(); - vector * deckList; - switch (controllerId) - { - case MENU_DECK_SELECTION: //Deck menu - if (controlId == MENU_ITEM_CANCEL) - { - if (!mSwitching) - mParent->DoTransition(TRANSITION_FADE, GAME_STATE_MENU); - else - mStage = STAGE_WAITING; - - mSwitching = false; - break; - } - else if (controlId == MENU_ITEM_CHEAT_MODE) - { // (PSY) Cheatmode: Complete the collection - playerdata->collection->complete(); // Add the cards - playerdata->collection->save(); // Save the new collection - for (int i = 0; i < setlist.size(); i++) - { // Update unlocked sets - GameOptionAward * goa = dynamic_cast (&options[Options::optionSet(i)]); - if (goa) - goa->giveAward(); - } - options.save(); - SAFE_DELETE(myCollection); - myCollection = NEW DeckDataWrapper(playerdata->collection); - myCollection->Sort(WSrcCards::SORT_ALPHA); - displayed_deck = myCollection; - rebuildFilters(); - loadIndexes(); - mStage = STAGE_WELCOME; - break; - } - mStage = STAGE_WAITING; - deckList = deckManager->getPlayerDeckOrderList(); - deckListSize = deckList->size(); - - if (controlId == MENU_ITEM_NEW_DECK) // new deck option selected - deckIdNumber = deckList->size() + 1; - else if (deckListSize > 0 && controlId <= deckListSize) - deckIdNumber = deckList->at(controlId - 1)-> getDeckId(); - else - deckIdNumber = controlId; - - loadDeck(deckIdNumber); - mStage = STAGE_WAITING; - deckNum = controlId; - break; - - case MENU_DECK_BUILDER: //Save / exit menu - switch (controlId) - { - - case MENU_ITEM_SAVE_RETURN_MAIN_MENU: - saveDeck(); - mParent->DoTransition(TRANSITION_FADE, GAME_STATE_MENU); - break; - - case MENU_ITEM_SAVE_RENAME: - if (myDeck && myDeck->parent) - { - options.keypadStart(myDeck->parent->meta_name, &newDeckname); - options.keypadTitle("Rename deck"); - } - break; - - case MENU_ITEM_SAVE_AS_AI_DECK: - // find the next unused ai deck number - // warn user that once saved, no edits can be made - // save entire collection to ai as spelled out card with count - // bring user to main deck editor menu. - isAIDeckSave = true; - defaultAiName = myDeck && myDeck->parent ? myDeck->parent->meta_name : "Custom AI Deck"; - options.keypadStart(defaultAiName, &newDeckname); - options.keypadTitle("Name Custom AI Deck"); - updateDecks(); - mStage = STAGE_WELCOME; - mSwitching = true; - break; - - case MENU_ITEM_SWITCH_DECKS_NO_SAVE: - updateDecks(); - mStage = STAGE_WELCOME; - mSwitching = true; - break; - case MENU_ITEM_MAIN_MENU: - mParent->DoTransition(TRANSITION_FADE, GAME_STATE_MENU); - break; - case MENU_ITEM_EDITOR_CANCEL: - mStage = STAGE_WAITING; - break; - case MENU_ITEM_FILTER_BY: - mStage = STAGE_FILTERS; - if (!filterMenu) - rebuildFilters(); - filterMenu->Entering(JGE_BTN_NONE); - break; - } - break; - - case MENU_CARD_PURCHASE: // Yes/ No sub menu. - switch (controlId) - { - case MENU_ITEM_YES: - { - MTGCard * card = cardIndex[2]; - if (card) - { - int rnd = (rand() % 25); - playerdata->credits += price; - price = price - (rnd * price) / 100; - pricelist->setPrice(card->getMTGId(), price); - playerdata->collection->remove(card->getMTGId()); - displayed_deck->Remove(card, 1); - displayed_deck->validate(); - stw->needUpdate = true; - loadIndexes(); - } - } - case MENU_ITEM_NO: - subMenu->Close(); - break; - } - } -} - -// n cards total, a of them are of desired type (A), x drawn -// returns probability of no A's -float noLuck(int n, int a, int x) -{ - if ((a >= n) || (a == 0)) - return 1; - if ((n == 0) || (x == 0) || (x > n) || (n - a < x)) - return 0; - - a = n - a; - float result = 1; - - for (int i = 0; i < x; i++) - result *= (float) (a - i) / (n - i); - return result; -} +/* + * GameStateDeckViewer.cpp + * Class handling the Deck Editor + */ + +#include "PrecompiledHeader.h" + +#include +#include + +#include "DeckManager.h" +#include "GameStateDuel.h" +#include "GameStateDeckViewer.h" +#include "Translate.h" +#include "ManaCostHybrid.h" +#include "MTGCardInstance.h" +#include "WFilter.h" +#include "WDataSrc.h" +#include "DeckEditorMenu.h" +#include "SimpleMenu.h" +#include "utils.h" + +//!! helper function; this is probably handled somewhere in the code already. +// If not, should be placed in general library +void StringExplode(string str, string separator, vector* results) +{ + size_t found; + found = str.find_first_of(separator); + while (found != string::npos) + { + if (found > 0) results->push_back(str.substr(0, found)); + str = str.substr(found + 1); + found = str.find_first_of(separator); + } + if (str.length() > 0) results->push_back(str); +} + +GameStateDeckViewer::GameStateDeckViewer(GameApp* parent) : + GameState(parent) +{ + bgMusic = NULL; + nbDecks = 0; + deckNum = 0; + useFilter = 0; + isAIDeckSave = false; + mSwitching = false; + welcome_menu = NULL; + myCollection = NULL; + myDeck = NULL; + filterMenu = NULL; + source = NULL; + hudAlpha = 0; + subMenu = NULL; + mRotation = 0; + mSlide = 0; + mAlpha = 255; + menu = NULL; + stw = NULL; +} + +GameStateDeckViewer::~GameStateDeckViewer() +{ + SAFE_DELETE(bgMusic); + if (myDeck) + { + SAFE_DELETE(myDeck->parent); + SAFE_DELETE(myDeck); + } + if (myCollection) + { + SAFE_DELETE(myCollection->parent); + SAFE_DELETE(myCollection); + } + SAFE_DELETE(stw); + SAFE_DELETE(filterMenu); +} + +void GameStateDeckViewer::rotateCards(int direction) +{ + int left = direction; + if (left) + displayed_deck->next(); + else + displayed_deck->prev(); + loadIndexes(); + + int total = displayed_deck->Size(); + if (total) + { + lastPos = getCurrentPos(); + lastTotal = total; + } + int i = 0; +} +void GameStateDeckViewer::rebuildFilters() +{ + if (!filterMenu) filterMenu = NEW WGuiFilters("Filter by...", NULL); + if (source) + SAFE_DELETE(source); + source = NEW WSrcDeckViewer(myDeck, myCollection); + filterMenu->setSrc(source); + if (displayed_deck != myDeck) source->swapSrc(); + filterMenu->Finish(true); + updateStats(); +} +void GameStateDeckViewer::updateFilters() +{ + if (!displayed_deck) return; + + filterMenu->recolorFilter(useFilter - 1); + filterMenu->Finish(true); + int totalAfter = displayed_deck->Size(); + if (totalAfter && lastTotal) + { + + //This part is a hack. I don't understand why in some cases "displayed_deck's" currentPos is not 0 at this stage + { + while (int currentPos = displayed_deck->getOffset()) + { + if (currentPos > 0) + displayed_deck->prev(); + else + displayed_deck->next(); + } + } + + int pos = (totalAfter * lastPos) / lastTotal; + for (int i = 0; i < pos - 3; ++i) + { // "-3" because card "0" is displayed at position 3 initially + displayed_deck->next(); + } + } + updateStats(); + return; +} +void GameStateDeckViewer::loadIndexes() +{ + for (int i = 0; i < 7; i++) + { + cardIndex[i] = displayed_deck->getCard(i); + } +} + +void GameStateDeckViewer::switchDisplay() +{ + if (displayed_deck == myCollection) + { + displayed_deck = myDeck; + } + else + { + displayed_deck = myCollection; + } + source->swapSrc(); + updateFilters(); + loadIndexes(); +} + +void GameStateDeckViewer::updateDecks() +{ + SAFE_DELETE(welcome_menu); + welcome_menu = NEW DeckEditorMenu(MENU_DECK_SELECTION, this, Fonts::OPTION_FONT, "Choose Deck To Edit"); + DeckManager * deckManager = DeckManager::GetInstance(); + vector playerDeckList = fillDeckMenu(welcome_menu, options.profileFile()); + + deckNum = 0; + newDeckname = ""; + nbDecks = playerDeckList.size() + 1; + welcome_menu->Add(MENU_ITEM_NEW_DECK, "--NEW--"); + if (options[Options::CHEATMODE].number && (!myCollection || myCollection->getCount(WSrcDeck::UNFILTERED_MIN_COPIES) < 4)) welcome_menu->Add( + MENU_ITEM_CHEAT_MODE, "--UNLOCK CARDS--"); + welcome_menu->Add(MENU_ITEM_CANCEL, "Cancel"); + + // update the deckmanager with the latest information + deckManager->updateMetaDataList(&playerDeckList, false); + // is this necessary to ensure no memory leaks? + playerDeckList.clear(); +} + +void GameStateDeckViewer::buildEditorMenu() +{ + ostringstream deckSummaryInformation; + deckSummaryInformation << "All changes are final." << endl; + + if (menu) + SAFE_DELETE( menu ); + //Build menu. + JRenderer::GetInstance()->FillRoundRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 100, ARGB(0, 0, 0, 0) ); + menu = NEW DeckEditorMenu(MENU_DECK_BUILDER, this, Fonts::OPTION_FONT, "Deck Editor", myDeck, stw); + + menu->Add(MENU_ITEM_FILTER_BY, "Filter By...", "Narrow down the list of cards. "); + menu->Add(MENU_ITEM_SWITCH_DECKS_NO_SAVE, "Switch Decks", "Do not make any changes.\nView another deck."); + menu->Add(MENU_ITEM_SAVE_RENAME, "Rename Deck", "Change the name of the deck"); + menu->Add(MENU_ITEM_SAVE_RETURN_MAIN_MENU, "Save & Quit Editor", "Save changes.\nReturn to the main menu"); + menu->Add(MENU_ITEM_SAVE_AS_AI_DECK, "Save As AI Deck", deckSummaryInformation.str()); + menu->Add(MENU_ITEM_MAIN_MENU, "Quit Editor", "Do not make any changes to deck.\nReturn to the main menu."); + menu->Add(MENU_ITEM_EDITOR_CANCEL, "Cancel", "Close menu."); + +} + +void GameStateDeckViewer::Start() +{ + hudAlpha = 0; + mSwitching = false; + subMenu = NULL; + myDeck = NULL; + mStage = STAGE_WELCOME; + mRotation = 0; + mSlide = 0; + mAlpha = 255; + last_user_activity = NO_USER_ACTIVITY_HELP_DELAY + 1; + onScreenTransition = 0; + useFilter = 0; + lastPos = 0; + lastTotal = 0; + + pricelist = NEW PriceList(JGE_GET_RES("settings/prices.dat").c_str(), mParent->collection); + playerdata = NEW PlayerData(mParent->collection); + myCollection = NEW DeckDataWrapper(playerdata->collection); + myCollection->Sort(WSrcCards::SORT_ALPHA); + displayed_deck = myCollection; + + //Icons + mIcons[Constants::MTG_COLOR_ARTIFACT] = resources.GetQuad("c_artifact"); + mIcons[Constants::MTG_COLOR_LAND] = resources.GetQuad("c_land"); + mIcons[Constants::MTG_COLOR_WHITE] = resources.GetQuad("c_white"); + mIcons[Constants::MTG_COLOR_RED] = resources.GetQuad("c_red"); + mIcons[Constants::MTG_COLOR_BLACK] = resources.GetQuad("c_black"); + mIcons[Constants::MTG_COLOR_BLUE] = resources.GetQuad("c_blue"); + mIcons[Constants::MTG_COLOR_GREEN] = resources.GetQuad("c_green"); + for (int i = 0; i < 7; i++) + { + mIcons[i]->SetHotSpot(16, 16); + } + + //Grab a texture in VRAM. + pspIconsTexture = resources.RetrieveTexture("iconspsp.png", RETRIEVE_LOCK); + + char buf[512]; + for (int i = 0; i < 8; i++) + { + sprintf(buf, "iconspsp%d", i); + pspIcons[i] = resources.RetrieveQuad("iconspsp.png", (float) i * 32, 0, 32, 32, buf); + pspIcons[i]->SetHotSpot(16, 16); + } + + backQuad = resources.GetQuad("back"); + + //init welcome menu + updateDecks(); + + GameApp::playMusic("track1.mp3"); + + loadIndexes(); + mEngine->ResetInput(); + JRenderer::GetInstance()->EnableVSync(true); +} + +void GameStateDeckViewer::End() +{ + JRenderer::GetInstance()->EnableVSync(false); + + SAFE_DELETE(welcome_menu); + SAFE_DELETE(menu); + SAFE_DELETE(subMenu); + + resources.Release(pspIconsTexture); + if (myCollection) + { + SAFE_DELETE(myCollection); + } + if (myDeck) + { + SAFE_DELETE(myDeck->parent); + SAFE_DELETE(myDeck); + } + SAFE_DELETE(pricelist); + SAFE_DELETE(playerdata); + SAFE_DELETE(filterMenu); + SAFE_DELETE(source); +} + +void GameStateDeckViewer::addRemove(MTGCard * card) +{ + if (!card) return; + if (displayed_deck->Remove(card, 1, (displayed_deck == myDeck))) + { + if (displayed_deck == myCollection) + { + myDeck->Add(card); + myDeck->Sort(WSrcCards::SORT_ALPHA); + } + else + { + myCollection->Add(card); + } + } + myCollection->validate(); + myDeck->validate(); + stw->needUpdate = true; + loadIndexes(); +} + +void GameStateDeckViewer::saveDeck() +{ + myDeck->save(); + playerdata->save(); + pricelist->save(); +} + +/** + save the deck in a readable format to allow people to edit the file offline + */ +void GameStateDeckViewer::saveAsAIDeck(string deckName) +{ + DeckManager * deckManager = DeckManager::GetInstance(); + vector aiDecks = GameState::getValidDeckMetaData(JGE_GET_RES("ai/baka"), "ai_baka", NULL); + int nbAiDecks = aiDecks.size() + 1; + aiDecks.clear(); + + string defaultAiDeckName = "deck"; + std::ostringstream oss; + oss << "deck" << nbAiDecks; + defaultAiDeckName = oss.str(); + oss.str(""); + if (myDeck->parent->meta_desc == "") + oss << endl << "Can you beat your own creations?" << endl << "User created AI Deck # " << nbAiDecks; + else + oss << myDeck->parent->meta_desc; + string deckDesc = oss.str(); + string filepath = JGE_GET_RES("ai/baka/"); + filepath.append(defaultAiDeckName).append(".txt"); + DebugTrace("saving AI deck " << filepath); + myDeck->save(filepath, true, deckName, deckDesc); +} + +void GameStateDeckViewer::Update(float dt) +{ + + int myD = (displayed_deck == myDeck); + + if (options.keypadActive()) + { + options.keypadUpdate(dt); + + if (newDeckname != "") + { + newDeckname = options.keypadFinish(); + + if (newDeckname != "") + { + if (isAIDeckSave) + { + saveAsAIDeck(newDeckname); + isAIDeckSave = false; + } + else if (myDeck && myDeck->parent) + { + myDeck->parent->meta_name = newDeckname; + saveDeck(); + } + mStage = STAGE_WAITING; + } + newDeckname = ""; + } + //Prevent screen from updating. + return; + } + hudAlpha = 255 - ((int) last_user_activity * 500); + if (hudAlpha < 0) hudAlpha = 0; + if (subMenu) + { + subMenu->Update(dt); + if (subMenu->closed) + { + SAFE_DELETE(subMenu); + } + return; + } + if (mStage == STAGE_WAITING || mStage == STAGE_ONSCREEN_MENU) + { + switch (mEngine->ReadButton()) + { + case JGE_BTN_LEFT: + last_user_activity = 0; + mStage = STAGE_TRANSITION_LEFT; + break; + case JGE_BTN_RIGHT: + last_user_activity = 0; + mStage = STAGE_TRANSITION_RIGHT; + break; + case JGE_BTN_UP: + last_user_activity = 0; + mStage = STAGE_TRANSITION_UP; + useFilter++; + if (useFilter >= MAX_SAVED_FILTERS) useFilter = 0; + break; + case JGE_BTN_DOWN: + last_user_activity = 0; + mStage = STAGE_TRANSITION_DOWN; + useFilter--; + if (useFilter < 0) useFilter = MAX_SAVED_FILTERS - 1; + break; + case JGE_BTN_CANCEL: + options[Options::DISABLECARDS].number = !options[Options::DISABLECARDS].number; + break; + case JGE_BTN_PRI: + if (last_user_activity > 0.2) + { + last_user_activity = 0; + switchDisplay(); + } + break; + case JGE_BTN_OK: + last_user_activity = 0; + addRemove(cardIndex[2]); + break; + case JGE_BTN_SEC: + last_user_activity = 0; + SAFE_DELETE(subMenu); + char buffer[4096]; + { + MTGCard * card = cardIndex[2]; + if (card && displayed_deck->count(card)) + { + price = pricelist->getSellPrice(card->getMTGId()); + sprintf(buffer, "%s : %i %s", _(card->data->getName()).c_str(), price, _("credits").c_str()); + const float menuXOffset = SCREEN_WIDTH_F - 300; + const float menuYOffset = SCREEN_HEIGHT_F / 2; + subMenu = NEW SimpleMenu(MENU_CARD_PURCHASE, this, Fonts::MAIN_FONT, menuXOffset, menuYOffset, buffer); + subMenu->Add(MENU_ITEM_YES, "Yes"); + subMenu->Add(MENU_ITEM_NO, "No", "", true); + } + } + stw->needUpdate = true; + break; + + case JGE_BTN_MENU: + mStage = STAGE_MENU; + buildEditorMenu(); + break; + case JGE_BTN_CTRL: + mStage = STAGE_FILTERS; + if (!filterMenu) + { + filterMenu = NEW WGuiFilters("Filter by...", NULL); + if (source) + SAFE_DELETE(source); + source = NEW WSrcDeckViewer(myDeck, myCollection); + filterMenu->setSrc(source); + if (displayed_deck != myDeck) source->swapSrc(); + } + filterMenu->Entering(JGE_BTN_NONE); + break; + case JGE_BTN_PREV: + if (last_user_activity < NO_USER_ACTIVITY_HELP_DELAY) + last_user_activity = NO_USER_ACTIVITY_HELP_DELAY + 1; + else if ((mStage == STAGE_ONSCREEN_MENU) && (--stw->currentPage < 0)) stw->currentPage = stw->pageCount; + break; + case JGE_BTN_NEXT: + if (last_user_activity < NO_USER_ACTIVITY_HELP_DELAY) + last_user_activity = NO_USER_ACTIVITY_HELP_DELAY + 1; + else if ((mStage == STAGE_ONSCREEN_MENU) && (++stw->currentPage > stw->pageCount)) stw->currentPage = 0; + break; + default: // no keypress + if (last_user_activity > NO_USER_ACTIVITY_HELP_DELAY) + { + if (mStage != STAGE_ONSCREEN_MENU) + { + mStage = STAGE_ONSCREEN_MENU; + onScreenTransition = 1; + } + else + { + if (onScreenTransition > 0) + onScreenTransition -= 0.05f; + else + onScreenTransition = 0; + } + } + else + last_user_activity += dt; + } + + } + if (mStage == STAGE_TRANSITION_RIGHT || mStage == STAGE_TRANSITION_LEFT) + { + if (mStage == STAGE_TRANSITION_RIGHT) + { + mRotation -= dt * MED_SPEED; + if (mRotation < -1.0f) + { + do + { + rotateCards(mStage); + mRotation += 1; + } while (mRotation < -1.0f); + mStage = STAGE_WAITING; + mRotation = 0; + } + } + else if (mStage == STAGE_TRANSITION_LEFT) + { + mRotation += dt * MED_SPEED; + if (mRotation > 1.0f) + { + do + { + rotateCards(mStage); + mRotation -= 1; + } while (mRotation > 1.0f); + mStage = STAGE_WAITING; + mRotation = 0; + } + } + } + if (mStage == STAGE_TRANSITION_DOWN || mStage == STAGE_TRANSITION_UP) + { + if (mStage == STAGE_TRANSITION_DOWN) + { + mSlide -= 0.05f; + if (mSlide < -1.0f) + { + updateFilters(); + loadIndexes(); + mSlide = 1; + } + else if (mSlide > 0 && mSlide < 0.05) + { + mStage = STAGE_WAITING; + mSlide = 0; + } + } + if (mStage == STAGE_TRANSITION_UP) + { + mSlide += 0.05f; + if (mSlide > 1.0f) + { + updateFilters(); + loadIndexes(); + mSlide = -1; + } + else if (mSlide < 0 && mSlide > -0.05) + { + mStage = STAGE_WAITING; + mSlide = 0; + } + } + + } + else if (mStage == STAGE_WELCOME) + welcome_menu->Update(dt); + else if (mStage == STAGE_MENU) + menu->Update(dt); + else if (mStage == STAGE_FILTERS) + { + JButton key = mEngine->ReadButton(); + if (filterMenu) + { + if (key == JGE_BTN_CTRL) + { + //useFilter = 0; + filterMenu->Finish(true); + filterMenu->Update(dt); + loadIndexes(); + return; + } + if (!filterMenu->isFinished()) + { + filterMenu->CheckUserInput(key); + filterMenu->Update(dt); + } + else + { + mStage = STAGE_WAITING; + updateFilters(); + loadIndexes(); + } + } + } +} + +void GameStateDeckViewer::renderOnScreenBasicInfo() +{ + JRenderer *renderer = JRenderer::GetInstance(); + WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT); + char buffer[256]; + int myD = (displayed_deck == myDeck); + + float y = 0; + int allCopies, nowCopies; + nowCopies = displayed_deck->getCount(WSrcDeck::FILTERED_COPIES); + allCopies = displayed_deck->getCount(WSrcDeck::UNFILTERED_COPIES); + WCardFilter * wc = displayed_deck->getFiltersRoot(); + + if (wc) + sprintf(buffer, "%s %i of %i cards (%i unique)", (displayed_deck == myDeck) ? "DECK: " : " ", nowCopies, allCopies, + displayed_deck->getCount(WSrcDeck::FILTERED_UNIQUE)); + else + sprintf(buffer, "%s%i cards (%i unique)", (displayed_deck == myDeck) ? "DECK: " : " ", allCopies, displayed_deck->getCount( + WSrcDeck::UNFILTERED_UNIQUE)); + + float w = mFont->GetStringWidth(buffer); + renderer->FillRoundRect(SCREEN_WIDTH - (w + 27), y + 5, w + 10, 15, 5, ARGB(128,0,0,0)); + + mFont->DrawString(buffer, SCREEN_WIDTH - 22, y + 15, JGETEXT_RIGHT); + if (useFilter != 0) renderer->RenderQuad(mIcons[useFilter - 1], SCREEN_WIDTH - 10, y + 15, 0.0f, 0.5, 0.5); +} + +//returns position of the current card (cusor) in the currently viewed color/filter +int GameStateDeckViewer::getCurrentPos() +{ + int total = displayed_deck->Size(); + + int currentPos = displayed_deck->getOffset(); + currentPos += 2; //we start by displaying card number 3 + currentPos = currentPos % total + 1; + if (currentPos < 0) currentPos = (total + currentPos); + if (!currentPos) currentPos = total; + return currentPos; +} + +void GameStateDeckViewer::renderSlideBar() +{ + WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT); + + int total = displayed_deck->Size(); + if (total == 0) return; + + float filler = 15; + float y = SCREEN_HEIGHT_F - 25; + float bar_size = SCREEN_WIDTH_F - 2 * filler; + JRenderer * r = JRenderer::GetInstance(); + int currentPos = getCurrentPos(); + + float cursor_pos = bar_size * currentPos / total; + + r->FillRoundRect(filler + 5, y + 5, bar_size, 0, 3, ARGB(hudAlpha/2,0,0,0)); + r->DrawLine(filler + cursor_pos + 5, y + 5, filler + cursor_pos + 5, y + 10, ARGB(hudAlpha/2,0,0,0)); + + r->FillRoundRect(filler, y, bar_size, 0, 3, ARGB(hudAlpha/2,128,128,128)); + r->DrawLine(filler + cursor_pos, y, filler + cursor_pos, y + 5, ARGB(hudAlpha,255,255,255)); + char buffer[256]; + string deckname = _("Collection"); + if (displayed_deck == myDeck) + { + deckname = _("Deck"); + } + sprintf(buffer, "%s - %i/%i", deckname.c_str(), currentPos, total); + mFont->SetColor(ARGB(hudAlpha,255,255,255)); + mFont->DrawString(buffer, SCREEN_WIDTH / 2, y, JGETEXT_CENTER); + + mFont->SetColor(ARGB(255,255,255,255)); +} + +void GameStateDeckViewer::renderDeckBackground() +{ + int max1 = 0; + int maxC1 = 4; + int max2 = 0; + int maxC2 = 4; + + for (int i = 0; i < Constants::MTG_NB_COLORS - 1; i++) + { + int value = myDeck->getCount(i); + if (value > max1) + { + max2 = max1; + maxC2 = maxC1; + max1 = value; + maxC1 = i; + } + else if (value > max2) + { + max2 = value; + maxC2 = i; + } + } + if (max2 < max1 / 2) + { + maxC2 = maxC1; + } + PIXEL_TYPE colors[] = { ARGB(255, Constants::_r[maxC1], Constants::_g[maxC1], Constants::_b[maxC1]), + ARGB(255, Constants::_r[maxC1], Constants::_g[maxC1], Constants::_b[maxC1]), + ARGB(255, Constants::_r[maxC2], Constants::_g[maxC2], Constants::_b[maxC2]), + ARGB(255, Constants::_r[maxC2], Constants::_g[maxC2], Constants::_b[maxC2]), }; + + JRenderer::GetInstance()->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, colors); + +} + +void GameStateDeckViewer::renderOnScreenMenu() +{ + + WFont * font = resources.GetWFont(Fonts::MAIN_FONT); + font->SetColor(ARGB(255,255,255,255)); + JRenderer * r = JRenderer::GetInstance(); + float pspIconsSize = 0.5; + float fH = font->GetHeight() + 1; + + float leftTransition = onScreenTransition * 84; + float rightTransition = onScreenTransition * 204; + float leftPspX = 40 - leftTransition; + float leftPspY = SCREEN_HEIGHT / 2 - 20; + float rightPspX = SCREEN_WIDTH - 100 + rightTransition; + float rightPspY = SCREEN_HEIGHT / 2 - 20; + + if (stw->currentPage == 0) + { + //FillRects + r->FillRect(0 - (onScreenTransition * 84), 0, 84, SCREEN_HEIGHT, ARGB(128,0,0,0)); + r->FillRect(SCREEN_WIDTH - 204 + (onScreenTransition * 204), 0, 200, SCREEN_HEIGHT, ARGB(128,0,0,0)); + + //LEFT PSP CIRCLE render + r->FillCircle(leftPspX, leftPspY, 40, ARGB(128,50,50,50)); + + r->RenderQuad(pspIcons[0], leftPspX, leftPspY - 20, 0, pspIconsSize, pspIconsSize); + r->RenderQuad(pspIcons[1], leftPspX, leftPspY + 20, 0, pspIconsSize, pspIconsSize); + r->RenderQuad(pspIcons[2], leftPspX - 20, leftPspY, 0, pspIconsSize, pspIconsSize); + r->RenderQuad(pspIcons[3], leftPspX + 20, leftPspY, 0, pspIconsSize, pspIconsSize); + + font->DrawString(_("Prev."), leftPspX - 35, leftPspY - 15); + font->DrawString(_("Next"), leftPspX + 15, leftPspY - 15); + font->DrawString(_("card"), leftPspX - 35, leftPspY); + font->DrawString(_("card"), leftPspX + 15, leftPspY); + font->DrawString(_("Next edition"), leftPspX - 33, leftPspY - 35); + font->DrawString(_("Prev. edition"), leftPspX - 33, leftPspY + 25); + + //RIGHT PSP CIRCLE render + r->FillCircle(rightPspX + (onScreenTransition * 204), rightPspY, 40, ARGB(128,50,50,50)); + r->RenderQuad(pspIcons[4], rightPspX + 20, rightPspY, 0, pspIconsSize, pspIconsSize); + r->RenderQuad(pspIcons[5], rightPspX, rightPspY - 20, 0, pspIconsSize, pspIconsSize); + r->RenderQuad(pspIcons[6], rightPspX - 20, rightPspY, 0, pspIconsSize, pspIconsSize); + r->RenderQuad(pspIcons[7], rightPspX, rightPspY + 20, 0, pspIconsSize, pspIconsSize); + + font->DrawString(_("Toggle Images"), rightPspX - 35, rightPspY - 40); + + if (displayed_deck == myCollection) + { + font->DrawString(_("Add card"), rightPspX + 20, rightPspY - 15); + font->DrawString(_("View Deck"), rightPspX - 20, rightPspY - 15, JGETEXT_RIGHT); + } + else + { + font->DrawString(_("Remove card"), rightPspX + 20, rightPspY - 15); + font->DrawString(_("View Collection"), rightPspX - 20, rightPspY - 15, JGETEXT_RIGHT); + } + font->DrawString(_("Sell card"), rightPspX - 30, rightPspY + 20); + //Bottom menus + font->DrawString(_("menu"), SCREEN_WIDTH - 35 + rightTransition, SCREEN_HEIGHT - 15); + font->DrawString(_("filter"), SCREEN_WIDTH - 95 + rightTransition, SCREEN_HEIGHT - 15); + + //Your Deck Information + char buffer[300]; + int nb_letters = 0; + for (int j = 0; j < Constants::MTG_NB_COLORS; j++) + { + int value = myDeck->getCount(j); + if (value > 0) + { + sprintf(buffer, "%i", value); + font->DrawString(buffer, SCREEN_WIDTH - 190 + rightTransition + nb_letters * 13, SCREEN_HEIGHT / 2 + 40); + r->RenderQuad(mIcons[j], SCREEN_WIDTH - 197 + rightTransition + nb_letters * 13, SCREEN_HEIGHT / 2 + 46, 0, 0.5, + 0.5); + if (value > 9) + { + nb_letters += 3; + } + else + { + nb_letters += 2; + } + } + } + int value = myDeck->getCount(WSrcDeck::UNFILTERED_COPIES); + sprintf(buffer, _("Your Deck: %i cards").c_str(), value); + font->DrawString(buffer, SCREEN_WIDTH - 200 + rightTransition, SCREEN_HEIGHT / 2 + 25); + + if (displayed_deck == myCollection) + { + font->DrawString(_("in: collection"), 5 - leftTransition, 5); + font->DrawString(_("Use SQUARE to view your deck,"), SCREEN_WIDTH - 200 + rightTransition, 5); + } + else + { + font->DrawString(_("in: deck"), 5 - leftTransition, 5); + font->DrawString(_("Use SQUARE to view collection,"), SCREEN_WIDTH - 200 + rightTransition, 5); + } + font->DrawString(_("Press L/R to cycle through"), SCREEN_WIDTH - 200 + rightTransition, 5 + fH); + font->DrawString(_("deck statistics."), SCREEN_WIDTH - 200 + rightTransition, 5 + fH * 2); + + } + else + { + if (stw->needUpdate) + { + updateStats(); + } + + char buffer[300]; + + leftTransition = -(onScreenTransition / 2) * SCREEN_WIDTH; + rightTransition = -leftTransition; + + r->FillRect(0 + leftTransition, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT, ARGB(128,0,0,0)); + r->FillRect(SCREEN_WIDTH / 2 + rightTransition, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT, ARGB(128,0,0,0)); + r->FillRect(10 + leftTransition, 10, SCREEN_WIDTH / 2 - 10, SCREEN_HEIGHT - 20, ARGB(128,0,0,0)); + r->FillRect(SCREEN_WIDTH / 2 + rightTransition, 10, SCREEN_WIDTH / 2 - 10, SCREEN_HEIGHT - 20, ARGB(128,0,0,0)); + font->DrawString(_("menu"), SCREEN_WIDTH - 35 + rightTransition, SCREEN_HEIGHT - 15); + font->DrawString(_("filter"), SCREEN_WIDTH - 95 + rightTransition, SCREEN_HEIGHT - 15); + + int nb_letters = 0; + float posX, posY; + DWORD graphColor; + + graphColor = ARGB(200, 155, 155, 155); + string STATS_TITLE_FORMAT = _("%i: %s"); + + switch (stw->currentPage) + { + case 1: // Counts, price + // Title + sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Statistics Summary").c_str()); + font->DrawString(buffer, 10 + leftTransition, 10); + + posY = 30; + posX = 180; + sprintf(buffer, _("Your Deck: %i cards").c_str(), stw->cardCount); + font->DrawString(buffer, 20 + leftTransition, posY); + posY += 10; + + // Counts by color + for (int j = 0; j < Constants::MTG_NB_COLORS; j++) + { + int value = myDeck->getCount(j); + if (value > 0) + { + sprintf(buffer, "%i", value); + font->DrawString(buffer, 38 + nb_letters * 13 + leftTransition, posY + 5); + r->RenderQuad(mIcons[j], 30 + nb_letters * 13 + leftTransition, posY + 11, 0, 0.5, 0.5); + if (value > 9) + { + nb_letters += 3; + } + else + { + nb_letters += 2; + } + } + } + posY += 25; + + r->DrawLine(posX - 4 + leftTransition, posY - 1, posX - 4 + leftTransition, posY + 177, ARGB(128, 255, 255, 255)); + r->DrawLine(19 + leftTransition, posY - 1, 19 + leftTransition, posY + 177, ARGB(128, 255, 255, 255)); + r->DrawLine(posX + 40 + leftTransition, posY - 1, posX + 40 + leftTransition, posY + 177, ARGB(128, 255, 255, 255)); + + r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); + + font->DrawString(_("Lands"), 20 + leftTransition, posY); + sprintf(buffer, _("%i").c_str(), stw->countLands); + font->DrawString(buffer, posX + leftTransition, posY); + + posY += 14; + r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); + font->DrawString(_("Creatures"), 20 + leftTransition, posY); + sprintf(buffer, _("%i").c_str(), stw->countCreatures); + font->DrawString(buffer, posX + leftTransition, posY); + + posY += 14; + r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); + font->DrawString(_("Spells"), 20 + leftTransition, posY); + sprintf(buffer, _("%i").c_str(), stw->countSpells); + font->DrawString(buffer, posX + leftTransition, posY); + + posY += 10; + font->DrawString(_("Instants"), 30 + leftTransition, posY); + sprintf(buffer, _("%i").c_str(), stw->countInstants); + font->DrawString(buffer, posX + leftTransition, posY); + + posY += 10; + font->DrawString(_("Enchantments"), 30 + leftTransition, posY); + sprintf(buffer, _("%i").c_str(), stw->countEnchantments); + font->DrawString(buffer, posX + leftTransition, posY); + + posY += 10; + font->DrawString(_("Sorceries"), 30 + leftTransition, posY); + sprintf(buffer, _("%i").c_str(), stw->countSorceries); + font->DrawString(buffer, posX + leftTransition, posY); + //sprintf(buffer, "Artifacts: %i", stw->countArtifacts); + //mFont->DrawString(buffer, 20, 123); + + posY += 14; + r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); + + font->DrawString(_("Average converted mana cost"), 20 + leftTransition, posY); + sprintf(buffer, _("%2.2f").c_str(), stw->avgManaCost); + font->DrawString(buffer, posX + leftTransition, posY); + + posY += 14; + r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); + font->DrawString(_("Probabilities"), 20 + leftTransition, posY); + + posY += 10; + font->DrawString(_("No land in 1st hand"), 30 + leftTransition, posY); + sprintf(buffer, _("%2.2f%%").c_str(), stw->noLandsProbInTurn[0]); + font->DrawString(buffer, posX + leftTransition, posY); + + posY += 10; + font->DrawString(_("No land in 9 cards"), 30 + leftTransition, posY); + sprintf(buffer, _("%2.2f%%").c_str(), stw->noLandsProbInTurn[2]); + font->DrawString(buffer, posX + leftTransition, posY); + + posY += 10; + font->DrawString(_("No creatures in 1st hand"), 30 + leftTransition, posY); + sprintf(buffer, _("%2.2f%%").c_str(), stw->noCreaturesProbInTurn[0]); + font->DrawString(buffer, posX + leftTransition, posY); + + // Playgame Statistics + posY += 14; + r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); + font->DrawString(_("Playgame statistics"), 20 + leftTransition, posY); + + posY += 10; + font->DrawString(_("Games played"), 30 + leftTransition, posY); + sprintf(buffer, _("%i").c_str(), stw->gamesPlayed); + font->DrawString(buffer, posX + leftTransition, posY); + + posY += 10; + font->DrawString(_("Victory ratio"), 30 + leftTransition, posY); + sprintf(buffer, _("%i%%").c_str(), stw->percentVictories); + font->DrawString(buffer, posX + leftTransition, posY); + + posY += 15; + r->DrawLine(20 + leftTransition, posY - 1, posX + 40 + leftTransition, posY - 1, ARGB(128, 255, 255, 255)); + font->DrawString(_("Total price (credits)"), 20 + leftTransition, posY); + sprintf(buffer, _("%i ").c_str(), stw->totalPrice); + font->DrawString(buffer, posX + leftTransition, posY); + r->DrawLine(20 + leftTransition, posY + 13, posX + 40 + leftTransition, posY + 13, ARGB(128, 255, 255, 255)); + + break; + + case 5: // Land statistics + sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Mana production").c_str()); + font->DrawString(buffer, 10 + leftTransition, 10); + + font->DrawString(_("Counts of manasources per type and color:"), 20 + leftTransition, 30); + + posY = 70; + + // Column titles + for (int j = 0; j < Constants::MTG_NB_COLORS - 1; j++) + { + r->RenderQuad(mIcons[j], 52 + j * 15 + leftTransition, posY - 10, 0, 0.5, 0.5); + } + + //font->DrawString(_("C"), 30 + leftTransition, posY-16); + //font->DrawString(_("Ty"), 27 + leftTransition, posY-16); + + // Horizontal table lines + r->DrawLine(27 + leftTransition, posY - 20, 60 + (Constants::MTG_NB_COLORS - 2) * 15 + leftTransition, posY - 20, + ARGB(128, 255, 255, 255)); + r->DrawLine(27 + leftTransition, posY - 1, 60 + (Constants::MTG_NB_COLORS - 2) * 15 + leftTransition, posY - 1, + ARGB(128, 255, 255, 255)); + r->DrawLine(27 + leftTransition, 2 * 10 + posY + 12, 60 + (Constants::MTG_NB_COLORS - 2) * 15 + leftTransition, 2 * 10 + + posY + 12, ARGB(128, 255, 255, 255)); + r->DrawLine(27 + leftTransition, 3 * 10 + posY + 14, 60 + (Constants::MTG_NB_COLORS - 2) * 15 + leftTransition, 3 * 10 + + posY + 14, ARGB(128, 255, 255, 255)); + + // Vertical table lines + r->DrawLine(26 + leftTransition, posY - 20, 26 + leftTransition, 3 * 10 + posY + 14, ARGB(128, 255, 255, 255)); + r->DrawLine(43 + leftTransition, posY - 20, 43 + leftTransition, 3 * 10 + posY + 14, ARGB(128, 255, 255, 255)); + r->DrawLine(60 + leftTransition + (Constants::MTG_NB_COLORS - 2) * 15, posY - 20, 60 + leftTransition + + (Constants::MTG_NB_COLORS - 2) * 15, 3 * 10 + posY + 14, ARGB(128, 255, 255, 255)); + + font->DrawString(_("BL"), 27 + leftTransition, posY); + font->DrawString(_("NB"), 27 + leftTransition, posY + 10); + font->DrawString(_("O"), 30 + leftTransition, posY + 20); + font->DrawString(_("T"), 30 + leftTransition, posY + 33); + + int curCount; + + for (int j = 0; j < Constants::MTG_NB_COLORS - 1; j++) + { + curCount = stw->countBasicLandsPerColor[j]; + sprintf(buffer, (curCount == 0 ? "." : "%i"), curCount); + font->DrawString(buffer, 49 + leftTransition + j * 15, posY); + + curCount = stw->countLandsPerColor[j]; + sprintf(buffer, (curCount == 0 ? "." : "%i"), curCount); + font->DrawString(buffer, 49 + leftTransition + j * 15, posY + 10); + + curCount = stw->countNonLandProducersPerColor[j]; + sprintf(buffer, (curCount == 0 ? "." : "%i"), curCount); + font->DrawString(buffer, 49 + leftTransition + j * 15, posY + 20); + + curCount = stw->countLandsPerColor[j] + stw->countBasicLandsPerColor[j] + stw->countNonLandProducersPerColor[j]; + sprintf(buffer, (curCount == 0 ? "." : "%i"), curCount); + font->DrawString(buffer, 49 + leftTransition + j * 15, posY + 33); + } + + posY += 55; + font->DrawString(_("BL - Basic lands"), 20 + leftTransition, posY); + posY += 10; + font->DrawString(_("NB - Non-basic lands"), 20 + leftTransition, posY); + posY += 10; + font->DrawString(_("O - Other (non-land) manasources"), 26 + leftTransition, posY); + posY += 10; + font->DrawString(_("T - Totals"), 26 + leftTransition, posY); + + break; + + case 6: // Land statistics - in symbols + sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Mana production - in mana symbols").c_str()); + font->DrawString(buffer, 10 + leftTransition, 10); + font->DrawString(_("Total colored manasymbols in lands' production:"), 20 + leftTransition, 30); + + int totalProducedSymbols; + totalProducedSymbols = 0; + for (int i = 1; i < Constants::MTG_NB_COLORS - 1; i++) + { + totalProducedSymbols += stw->countLandsPerColor[i] + stw->countBasicLandsPerColor[i]; //!! Move to updatestats! + } + + posY = 50; + for (int i = 1; i < Constants::MTG_NB_COLORS - 1; i++) + { + if (stw->countLandsPerColor[i] + stw->countBasicLandsPerColor[i] > 0) + { + sprintf(buffer, _("%i").c_str(), stw->countLandsPerColor[i] + stw->countBasicLandsPerColor[i]); + font->DrawString(buffer, 20 + leftTransition, posY); + sprintf(buffer, _("(%i%%)").c_str(), (int) (100 * (float) (stw->countLandsPerColor[i] + + stw->countBasicLandsPerColor[i]) / totalProducedSymbols)); + font->DrawString(buffer, 33 + leftTransition, posY); + posX = 72; + for (int j = 0; j < stw->countLandsPerColor[i] + stw->countBasicLandsPerColor[i]; j++) + { + r->RenderQuad(mIcons[i], posX + leftTransition, posY + 6, 0, 0.5, 0.5); + posX += ((j + 1) % 10 == 0) ? 17 : 13; + if ((((j + 1) % 30) == 0) && (j < stw->countLandsPerColor[i] + stw->countBasicLandsPerColor[i] - 1)) + { + posX = 72; + posY += 15; + } + } + posY += 17; + } + } + + break; + + case 2: // Mana cost detail + case 3: + case 4: + int (*countPerCost)[Constants::STATS_MAX_MANA_COST + 1]; + int (*countPerCostAndColor)[Constants::STATS_MAX_MANA_COST + 1][Constants::MTG_NB_COLORS + 1]; + float avgCost; + + switch (stw->currentPage) + { // Nested switch on the same variable. Oh yes. + case 2: // Total counts + // Title + sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Mana cost detail").c_str()); + font->DrawString(buffer, 10 + leftTransition, 10); + font->DrawString(_("Card counts per mana cost:"), 20 + leftTransition, 30); + avgCost = stw->avgManaCost; + countPerCost = &stw->countCardsPerCost; + countPerCostAndColor = &stw->countCardsPerCostAndColor; + break; + case 3: // Creature counts + // Title + sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Mana cost detail - Creatures").c_str()); + font->DrawString(buffer, 10 + leftTransition, 10); + font->DrawString(_("Creature counts per mana cost:"), 20 + leftTransition, 30); + avgCost = stw->avgCreatureCost; + countPerCost = &stw->countCreaturesPerCost; + countPerCostAndColor = &stw->countCreaturesPerCostAndColor; + break; + case 4: // Spell counts + // Title + sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Mana cost detail - Spells").c_str()); + font->DrawString(buffer, 10 + leftTransition, 10); + font->DrawString(_("Non-creature spell counts per mana cost:"), 20 + leftTransition, 30); + avgCost = stw->avgSpellCost; + countPerCost = &stw->countSpellsPerCost; + countPerCostAndColor = &stw->countSpellsPerCostAndColor; + break; + default: + countPerCost = NULL; + countPerCostAndColor = NULL; + avgCost = 0; + break; + } + + posY = 70; + + // Column titles + for (int j = 0; j < Constants::MTG_NB_COLORS - 1; j++) + { + r->RenderQuad(mIcons[j], 67 + j * 15 + leftTransition, posY - 10, 0, 0.5, 0.5); + } + + font->DrawString(_("C"), 30 + leftTransition, posY - 16); + font->DrawString(_("#"), 45 + leftTransition, posY - 16); + + // Horizontal table lines + r->DrawLine(27 + leftTransition, posY - 20, 75 + (Constants::MTG_NB_COLORS - 2) * 15 + leftTransition, posY - 20, + ARGB(128, 255, 255, 255)); + r->DrawLine(27 + leftTransition, posY - 1, 75 + (Constants::MTG_NB_COLORS - 2) * 15 + leftTransition, posY - 1, + ARGB(128, 255, 255, 255)); + r->DrawLine(27 + leftTransition, Constants::STATS_MAX_MANA_COST * 10 + posY + 12, 75 + (Constants::MTG_NB_COLORS - 2) + * 15 + leftTransition, Constants::STATS_MAX_MANA_COST * 10 + posY + 12, ARGB(128, 255, 255, 255)); + + // Vertical table lines + r->DrawLine(26 + leftTransition, posY - 20, 26 + leftTransition, Constants::STATS_MAX_MANA_COST * 10 + posY + 12, + ARGB(128, 255, 255, 255)); + r->DrawLine(41 + leftTransition, posY - 20, 41 + leftTransition, Constants::STATS_MAX_MANA_COST * 10 + posY + 12, + ARGB(128, 255, 255, 255)); + r->DrawLine(58 + leftTransition, posY - 20, 58 + leftTransition, Constants::STATS_MAX_MANA_COST * 10 + posY + 12, + ARGB(128, 255, 255, 255)); + r->DrawLine(75 + leftTransition + (Constants::MTG_NB_COLORS - 2) * 15, posY - 20, 75 + leftTransition + + (Constants::MTG_NB_COLORS - 2) * 15, Constants::STATS_MAX_MANA_COST * 10 + posY + 12, + ARGB(128, 255, 255, 255)); + + for (int i = 0; i <= Constants::STATS_MAX_MANA_COST; i++) + { + sprintf(buffer, _("%i").c_str(), i); + font->DrawString(buffer, 30 + leftTransition, posY); + sprintf(buffer, ((*countPerCost)[i] > 0) ? _("%i").c_str() : ".", (*countPerCost)[i]); + font->DrawString(buffer, 45 + leftTransition, posY); + for (int j = 0; j < Constants::MTG_NB_COLORS - 1; j++) + { + sprintf(buffer, ((*countPerCostAndColor)[i][j] > 0) ? _("%i").c_str() : ".", (*countPerCostAndColor)[i][j]); + font->DrawString(buffer, 64 + leftTransition + j * 15, posY); + } + r->FillRect(77.f + leftTransition + (Constants::MTG_NB_COLORS - 2) * 15.0f, posY + 2.0f, (*countPerCost)[i] * 5.0f, + 8.0f, graphColor); + posY += 10; + } + + posY += 10; + sprintf(buffer, _("Average converted mana cost: %2.2f").c_str(), avgCost); + font->DrawString(buffer, 20 + leftTransition, posY); + posY += 15; + sprintf(buffer, _("C - Converted mana cost. Cards with cost>%i are included in the last row.").c_str(), + Constants::STATS_MAX_MANA_COST); + font->DrawString(buffer, 20 + leftTransition, posY); + posY += 10; + font->DrawString(_("# - Total number of cards with given cost"), 20 + leftTransition, posY); + + break; + + case 8: + // Title + sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Probabilities").c_str()); + font->DrawString(buffer, 10 + leftTransition, 10); + + // No lands detail + float graphScale, graphWidth; + graphWidth = 100; + graphScale = (stw->noLandsProbInTurn[0] == 0) ? 0 : (graphWidth / stw->noLandsProbInTurn[0]); + font->DrawString(_("No lands in first n cards:"), 20 + leftTransition, 30); + + posY = 50; + for (int i = 0; i < Constants::STATS_FOR_TURNS; i++) + { + sprintf(buffer, _("%i:").c_str(), i + 7); + font->DrawString(buffer, 30 + leftTransition, posY); + sprintf(buffer, _("%2.2f%%").c_str(), stw->noLandsProbInTurn[i]); + font->DrawString(buffer, 45 + leftTransition, posY); + r->FillRect(84 + leftTransition, posY + 2, graphScale * stw->noLandsProbInTurn[i], 8, graphColor); + posY += 10; + } + + // No creatures probability detail + posY += 10; + font->DrawString(_("No creatures in first n cards:"), 20 + leftTransition, posY); + posY += 20; + graphScale = (stw->noCreaturesProbInTurn[0] == 0) ? 0 : (graphWidth / stw->noCreaturesProbInTurn[0]); + + for (int i = 0; i < Constants::STATS_FOR_TURNS; i++) + { + sprintf(buffer, _("%i:").c_str(), i + 7); + font->DrawString(buffer, 30 + leftTransition, posY); + sprintf(buffer, _("%2.2f%%").c_str(), stw->noCreaturesProbInTurn[i]); + font->DrawString(buffer, 45 + leftTransition, posY); + r->FillRect(84 + leftTransition, posY + 2, graphScale * stw->noCreaturesProbInTurn[i], 8, graphColor); + posY += 10; + } + + break; + + case 7: // Total mana cost per color + // Title + sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Mana cost per color").c_str()); + font->DrawString(buffer, 10 + leftTransition, 10); + + font->DrawString(_("Total colored manasymbols in cards' casting costs:"), 20 + leftTransition, 30); + + posY = 50; + for (int i = 1; i < Constants::MTG_NB_COLORS - 1; i++) + { + if (stw->totalCostPerColor[i] > 0) + { + sprintf(buffer, _("%i").c_str(), stw->totalCostPerColor[i]); + font->DrawString(buffer, 20 + leftTransition, posY); + sprintf(buffer, _("(%i%%)").c_str(), (int) (100 * (float) stw->totalCostPerColor[i] / stw->totalColoredSymbols)); + font->DrawString(buffer, 33 + leftTransition, posY); + posX = 72; + for (int j = 0; j < stw->totalCostPerColor[i]; j++) + { + r->RenderQuad(mIcons[i], posX + leftTransition, posY + 6, 0, 0.5, 0.5); + posX += ((j + 1) % 10 == 0) ? 17 : 13; + if ((((j + 1) % 30) == 0) && (j < stw->totalCostPerColor[i] - 1)) + { + posX = 72; + posY += 15; + } + } + posY += 17; + } + } + break; + + case 9: // Victory statistics + // Title + sprintf(buffer, STATS_TITLE_FORMAT.c_str(), stw->currentPage, _("Victory statistics").c_str()); + font->DrawString(buffer, 10 + leftTransition, 10); + + font->DrawString(_("Victories against AI:"), 20 + leftTransition, 30); + + sprintf(buffer, _("Games played: %i").c_str(), stw->gamesPlayed); + font->DrawString(buffer, 20 + leftTransition, 45); + sprintf(buffer, _("Victory ratio: %i%%").c_str(), stw->percentVictories); + font->DrawString(buffer, 20 + leftTransition, 55); + + int AIsPerColumn = 19; + posY = 70; + posX = 20; + + // ToDo: Multiple pages when too many AI decks are present + for (int i = 0; i < (int) stw->aiDeckStats.size(); i++) + { + sprintf(buffer, _("%.14s").c_str(), stw->aiDeckNames.at(i).c_str()); + font->DrawString(buffer, posX + (i < 2 * AIsPerColumn ? leftTransition : rightTransition), posY); + sprintf(buffer, _("%i/%i").c_str(), stw->aiDeckStats.at(i)->victories, stw->aiDeckStats.at(i)->nbgames); + font->DrawString(buffer, posX + (i < AIsPerColumn ? leftTransition : rightTransition) + 80, posY); + sprintf(buffer, _("%i%%").c_str(), stw->aiDeckStats.at(i)->percentVictories()); + font->DrawString(buffer, posX + (i < AIsPerColumn ? leftTransition : rightTransition) + 110, posY); + posY += 10; + if (((i + 1) % AIsPerColumn) == 0) + { + posY = 70; + posX += 155; + } + } + break; + } + } +} + +void GameStateDeckViewer::updateStats() +{ + if (!stw->needUpdate || !myDeck) return; + stw->needUpdate = false; + stw->cardCount = myDeck->getCount(WSrcDeck::UNFILTERED_COPIES); + stw->countLands = myDeck->getCount(Constants::MTG_COLOR_LAND); + stw->totalPrice = myDeck->totalPrice(); + + stw->countManaProducers = 0; + // Mana cost + int currentCount, convertedCost; + ManaCost * currentCost; + stw->totalManaCost = 0; + stw->totalCreatureCost = 0; + stw->totalSpellCost = 0; + MTGCard * current = myDeck->getCard(); + + // Clearing arrays + for (int i = 0; i <= Constants::STATS_MAX_MANA_COST; i++) + { + stw->countCardsPerCost[i] = 0; + stw->countCreaturesPerCost[i] = 0; + stw->countSpellsPerCost[i] = 0; + } + + for (int i = 0; i <= Constants::MTG_NB_COLORS; i++) + { + stw->totalCostPerColor[i] = 0; + stw->countLandsPerColor[i] = 0; + stw->countBasicLandsPerColor[i] = 0; + stw->countNonLandProducersPerColor[i] = 0; + } + + for (int i = 0; i <= Constants::STATS_MAX_MANA_COST; i++) + { + for (int k = 0; k <= Constants::MTG_NB_COLORS; k++) + { + stw->countCardsPerCostAndColor[i][k] = 0; + stw->countCreaturesPerCostAndColor[i][k] = 0; + stw->countSpellsPerCostAndColor[i][k] = 0; + } + } + + for (int ic = 0; ic < myDeck->Size(true); ic++) + { + current = myDeck->getCard(ic, true); + currentCost = current->data->getManaCost(); + convertedCost = currentCost->getConvertedCost(); + currentCount = myDeck->count(current); + + // Add to the cards per cost counters + stw->totalManaCost += convertedCost * currentCount; + if (convertedCost > Constants::STATS_MAX_MANA_COST) + { + convertedCost = Constants::STATS_MAX_MANA_COST; + } + stw->countCardsPerCost[convertedCost] += currentCount; + if (current->data->isCreature()) + { + stw->countCreaturesPerCost[convertedCost] += currentCount; + stw->totalCreatureCost += convertedCost * currentCount; + } + else if (current->data->isSpell()) + { + stw->countSpellsPerCost[convertedCost] += currentCount; + stw->totalSpellCost += convertedCost * currentCount; + } + + // Lets look for mana producing abilities + + vector abilityStrings; + abilityStrings = split(current->data->magicText, '\n'); + + for (int v = 0; v < (int) abilityStrings.size(); v++) + { + string s = abilityStrings[v]; + size_t t = s.find("add"); + if (t != string::npos) + { + s = s.substr(t + 3); + ManaCost * mc = ManaCost::parseManaCost(s); + for (int j = 0; j < Constants::MTG_NB_COLORS; j++) + { + if (mc->hasColor(j)) + { + if (current->data->isLand()) + { + if (current->data->hasType("Basic")) + { + stw->countBasicLandsPerColor[j] += currentCount; + } + else + { + stw->countLandsPerColor[j] += currentCount; + } + } + else + { + stw->countNonLandProducersPerColor[j] += currentCount; + } + } + } + SAFE_DELETE(mc); + } + } + + // Add to the per color counters + // a. regular costs + for (int j = 0; j < Constants::MTG_NB_COLORS; j++) + { + stw->totalCostPerColor[j] += currentCost->getCost(j) * currentCount; + if (current->data->hasColor(j)) + { + // Add to the per cost and color counter + stw->countCardsPerCostAndColor[convertedCost][j] += currentCount; + if (current->data->isCreature()) + { + stw->countCreaturesPerCostAndColor[convertedCost][j] += currentCount; + } + else if (current->data->isSpell()) + { + stw->countSpellsPerCostAndColor[convertedCost][j] += currentCount; + } + } + } + + // b. Hybrid costs + ManaCostHybrid * hybridCost; + int i; + i = 0; + + while ((hybridCost = currentCost->getHybridCost(i++)) != NULL) + { + stw->totalCostPerColor[hybridCost->color1] += hybridCost->value1 * currentCount; + stw->totalCostPerColor[hybridCost->color2] += hybridCost->value2 * currentCount; + } + } + + stw->totalColoredSymbols = 0; + for (int j = 1; j < Constants::MTG_NB_COLORS; j++) + { + stw->totalColoredSymbols += stw->totalCostPerColor[j]; + } + + stw->countCardsPerCost[0] -= stw->countLands; + + // Counts by type + stw->countCreatures = countCardsByType("Creature"); + stw->countInstants = countCardsByType("Instant"); + stw->countEnchantments = countCardsByType("Enchantment"); + stw->countSorceries = countCardsByType("Sorcery"); + stw->countSpells = stw->countInstants + stw->countEnchantments + stw->countSorceries; + //stw->countArtifacts = countCardsByType("Artifact"); + + // Average mana costs + stw->avgManaCost = ((stw->cardCount - stw->countLands) <= 0) ? 0 : (float) stw->totalManaCost / (stw->cardCount + - stw->countLands); + stw->avgCreatureCost = (stw->countCreatures <= 0) ? 0 : (float) stw->totalCreatureCost / stw->countCreatures; + stw->avgSpellCost = (stw->countSpells <= 0) ? 0 : (float) stw->totalSpellCost / stw->countSpells; + + // Probabilities + // TODO: this could be optimized by reusing results + for (int i = 0; i < Constants::STATS_FOR_TURNS; i++) + { + stw->noLandsProbInTurn[i] = noLuck(stw->cardCount, stw->countLands, 7 + i) * 100; + stw->noCreaturesProbInTurn[i] = noLuck(stw->cardCount, stw->countCreatures, 7 + i) * 100; + } +} + +// This should probably be cached in DeckDataWrapper +// or at least be calculated for all common types in one go +int GameStateDeckViewer::countCardsByType(const char * _type) +{ + int result = 0; + for (int i = 0; i < myDeck->Size(true); i++) + { + MTGCard * current = myDeck->getCard(i, true); + if (current->data->hasType(_type)) + { + result += myDeck->count(current); + } + } + return result; +} + +void GameStateDeckViewer::renderCard(int id, float rotation) +{ + WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT); + MTGCard * card = cardIndex[id]; + + float max_scale = 0.96f; + float x_center_0 = 180; + float right_border = SCREEN_WIDTH - 20; + + float x_center = x_center_0 + cos((rotation + 8 - id) * M_PI / 12) * (right_border - x_center_0); + float scale = max_scale / 1.12f * cos((x_center - x_center_0) * 1.5f / (right_border - x_center_0)) + 0.2f * max_scale * cos( + cos((x_center - x_center_0) * 0.15f / (right_border - x_center_0))); + float x = x_center; // ; + + float y = (SCREEN_HEIGHT_F) / 2.0f + SCREEN_HEIGHT_F * mSlide * (scale + 0.2f); + + int alpha = (int) (255 * (scale + 1.0 - max_scale)); + + if (!card) return; + JQuad * quad = NULL; + + int cacheError = CACHE_ERROR_NONE; + + if (!options[Options::DISABLECARDS].number) + { + quad = resources.RetrieveCard(card, RETRIEVE_EXISTING); + cacheError = resources.RetrieveError(); + if (!quad && cacheError != CACHE_ERROR_404) + { + if (last_user_activity > (abs(2 - id) + 1) * NO_USER_ACTIVITY_SHOWCARD_DELAY) + quad = resources.RetrieveCard(card); + else + { + quad = backQuad; + } + } + } + + int quadAlpha = alpha; + if (!displayed_deck->count(card)) quadAlpha /= 2; + if (quad) + { + if (quad == backQuad) + { + quad->SetColor(ARGB(255,255,255,255)); + float _scale = scale * (285 / quad->mHeight); + JRenderer::GetInstance()->RenderQuad(quad, x, y, 0.0f, _scale, _scale); + } + else + { + Pos pos = Pos(x, y, scale * 285 / 250, 0.0, 255); + CardGui::DrawCard(card, pos); + } + } + else + { + Pos pos = Pos(x, y, scale * 285 / 250, 0.0, 255); + CardGui::DrawCard(card, pos, DrawMode::kText); + if (!options[Options::DISABLECARDS].number) quad = resources.RetrieveCard(card, CACHE_THUMB); + if (quad) + { + float _scale = 285 * scale / quad->mHeight; + quad->SetColor(ARGB(40,255,255,255)); + JRenderer::GetInstance()->RenderQuad(quad, x, y, 0, _scale, _scale); + } + } + quadAlpha = 255 - quadAlpha; + if (quadAlpha > 0) + { + JRenderer::GetInstance()->FillRect(x - scale * 100.0f, y - scale * 142.5f, scale * 200.0f, scale * 285.0f, + ARGB(quadAlpha,0,0,0)); + } + if (last_user_activity < 3) + { + int fontAlpha = alpha; + float qtY = y - 135 * scale; + float qtX = x + 40 * scale; + char buffer[4096]; + sprintf(buffer, "x%i", displayed_deck->count(card)); + WFont * font = mFont; + font->SetColor(ARGB(fontAlpha/2,0,0,0)); + JRenderer::GetInstance()->FillRect(qtX, qtY, font->GetStringWidth(buffer) + 6, 16, ARGB(fontAlpha/2,0,0,0)); + font->DrawString(buffer, qtX + 4, qtY + 4); + font->SetColor(ARGB(fontAlpha,255,255,255)); + font->DrawString(buffer, qtX + 2, qtY + 2); + font->SetColor(ARGB(255,255,255,255)); + } +} + +void GameStateDeckViewer::renderCard(int id) +{ + renderCard(id, 0); +} + +void GameStateDeckViewer::Render() +{ + + WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT); + + JRenderer * r = JRenderer::GetInstance(); + r->ClearScreen(ARGB(0,0,0,0)); + if (displayed_deck == myDeck && mStage != STAGE_MENU) renderDeckBackground(); + int order[3] = { 1, 2, 3 }; + if (mRotation < 0.5 && mRotation > -0.5) + { + order[1] = 3; + order[2] = 2; + } + else if (mRotation < -0.5) + { + order[0] = 3; + order[2] = 1; + } + + renderCard(6, mRotation); + renderCard(5, mRotation); + renderCard(4, mRotation); + renderCard(0, mRotation); + + for (int i = 0; i < 3; i++) + { + renderCard(order[i], mRotation); + } + + if (displayed_deck->Size() > 0) + { + renderSlideBar(); + } + else + { + mFont->DrawString(_("No Card"), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, JGETEXT_CENTER); + } + if (mStage == STAGE_ONSCREEN_MENU) + { + renderOnScreenMenu(); + } + else if (mStage == STAGE_WELCOME) + { + welcome_menu->Render(); + } + else + { + renderOnScreenBasicInfo(); + + } + if (mStage == STAGE_MENU) + { + menu->Render(); + } + if (subMenu) subMenu->Render(); + + if (filterMenu && !filterMenu->isFinished()) filterMenu->Render(); + + if (options.keypadActive()) options.keypadRender(); + +} + +int GameStateDeckViewer::loadDeck(int deckid) +{ + + if (!stw) stw = new StatsWrapper(deckid); + + stw->currentPage = 0; + stw->pageCount = 9; + stw->needUpdate = true; + + if (!playerdata) playerdata = NEW PlayerData(mParent->collection); + SAFE_DELETE(myCollection); + myCollection = NEW DeckDataWrapper(playerdata->collection); + myCollection->Sort(WSrcCards::SORT_ALPHA); + displayed_deck = myCollection; + + char deckname[256]; + sprintf(deckname, "deck%i.txt", deckid); + if (myDeck) + { + SAFE_DELETE(myDeck->parent); + SAFE_DELETE(myDeck); + } + myDeck = NEW DeckDataWrapper(NEW MTGDeck(options.profileFile(deckname, "", false, false).c_str(), mParent->collection)); + + // Check whether the cards in the deck are actually available in the player's collection: + int cheatmode = options[Options::CHEATMODE].number; + bool bPure = true; + for (int i = 0; i < myDeck->Size(true); i++) + { + MTGCard * current = myDeck->getCard(i, true); + int howmanyinDeck = myDeck->count(current); + for (int i = myCollection->count(current); i < howmanyinDeck; i++) + { + bPure = false; + if (cheatmode) + { //Are we cheating? + playerdata->collection->add(current); //Yup, add it to collection permanently. + myCollection->Add(current); + } + else + { + myDeck->Remove(current); //Nope. Remove it from deck. + break; + } + } + + myCollection->Remove(current, myDeck->count(current)); + } + if (!bPure) + { + myDeck->validate(); + myCollection->validate(); + } + + myDeck->Sort(WSrcCards::SORT_ALPHA); + SAFE_DELETE(filterMenu); + rebuildFilters(); + loadIndexes(); + return 1; +} + +void GameStateDeckViewer::ButtonPressed(int controllerId, int controlId) +{ + int deckIdNumber = controlId; + int deckListSize = 0; + string defaultAiName; + DeckManager *deckManager = DeckManager::GetInstance(); + vector * deckList; + switch (controllerId) + { + case MENU_DECK_SELECTION: //Deck menu + if (controlId == MENU_ITEM_CANCEL) + { + if (!mSwitching) + mParent->DoTransition(TRANSITION_FADE, GAME_STATE_MENU); + else + mStage = STAGE_WAITING; + + mSwitching = false; + break; + } + else if (controlId == MENUITEM_MORE_INFO) + { + break; + } + else if (controlId == MENU_ITEM_CHEAT_MODE) + { // (PSY) Cheatmode: Complete the collection + playerdata->collection->complete(); // Add the cards + playerdata->collection->save(); // Save the new collection + for (int i = 0; i < setlist.size(); i++) + { // Update unlocked sets + GameOptionAward * goa = dynamic_cast (&options[Options::optionSet(i)]); + if (goa) goa->giveAward(); + } + options.save(); + SAFE_DELETE(myCollection); + myCollection = NEW DeckDataWrapper(playerdata->collection); + myCollection->Sort(WSrcCards::SORT_ALPHA); + displayed_deck = myCollection; + rebuildFilters(); + loadIndexes(); + mStage = STAGE_WELCOME; + break; + } + mStage = STAGE_WAITING; + deckList = deckManager->getPlayerDeckOrderList(); + deckListSize = deckList->size(); + + if (controlId == MENU_ITEM_NEW_DECK) // new deck option selected + deckIdNumber = deckList->size() + 1; + else if (deckListSize > 0 && controlId <= deckListSize) + deckIdNumber = deckList->at(controlId - 1)-> getDeckId(); + else + deckIdNumber = controlId; + + loadDeck(deckIdNumber); + mStage = STAGE_WAITING; + deckNum = controlId; + break; + + case MENU_DECK_BUILDER: //Save / exit menu + switch (controlId) + { + + case MENU_ITEM_SAVE_RETURN_MAIN_MENU: + saveDeck(); + mParent->DoTransition(TRANSITION_FADE, GAME_STATE_MENU); + break; + + case MENU_ITEM_SAVE_RENAME: + if (myDeck && myDeck->parent) + { + options.keypadStart(myDeck->parent->meta_name, &newDeckname); + options.keypadTitle("Rename deck"); + } + break; + + case MENU_ITEM_SAVE_AS_AI_DECK: + // find the next unused ai deck number + // warn user that once saved, no edits can be made + // save entire collection to ai as spelled out card with count + // bring user to main deck editor menu. + isAIDeckSave = true; + defaultAiName = myDeck && myDeck->parent ? myDeck->parent->meta_name : "Custom AI Deck"; + options.keypadStart(defaultAiName, &newDeckname); + options.keypadTitle("Name Custom AI Deck"); + updateDecks(); + mStage = STAGE_WELCOME; + mSwitching = true; + break; + + case MENU_ITEM_SWITCH_DECKS_NO_SAVE: + updateDecks(); + mStage = STAGE_WELCOME; + mSwitching = true; + break; + case MENU_ITEM_MAIN_MENU: + mParent->DoTransition(TRANSITION_FADE, GAME_STATE_MENU); + break; + case MENU_ITEM_EDITOR_CANCEL: + mStage = STAGE_WAITING; + break; + case MENU_ITEM_FILTER_BY: + mStage = STAGE_FILTERS; + if (!filterMenu) rebuildFilters(); + filterMenu->Entering(JGE_BTN_NONE); + break; + } + break; + + case MENU_CARD_PURCHASE: // Yes/ No sub menu. + switch (controlId) + { + case MENU_ITEM_YES: + { + MTGCard * card = cardIndex[2]; + if (card) + { + int rnd = (rand() % 25); + playerdata->credits += price; + price = price - (rnd * price) / 100; + pricelist->setPrice(card->getMTGId(), price); + playerdata->collection->remove(card->getMTGId()); + displayed_deck->Remove(card, 1); + displayed_deck->validate(); + stw->needUpdate = true; + loadIndexes(); + } + } + case MENU_ITEM_NO: + subMenu->Close(); + break; + } + } +} + +// n cards total, a of them are of desired type (A), x drawn +// returns probability of no A's +float noLuck(int n, int a, int x) +{ + if ((a >= n) || (a == 0)) return 1; + if ((n == 0) || (x == 0) || (x > n) || (n - a < x)) return 0; + + a = n - a; + float result = 1; + + for (int i = 0; i < x; i++) + result *= (float) (a - i) / (n - i); + return result; +} diff --git a/projects/mtg/src/GameStateDuel.cpp b/projects/mtg/src/GameStateDuel.cpp index 8e7f93a99..4ab60d7a9 100644 --- a/projects/mtg/src/GameStateDuel.cpp +++ b/projects/mtg/src/GameStateDuel.cpp @@ -1,705 +1,799 @@ -#include "PrecompiledHeader.h" - -#include "DeckMenu.h" -#include "GameStateDuel.h" -#include "GameOptions.h" -#include "utils.h" -#include "AIPlayer.h" -#include "AIMomirPlayer.h" -#include "PlayerData.h" -#include "DeckStats.h" -#include "DeckManager.h" - -#include "DeckMetaData.h" -#include "MTGRules.h" -#include "Credits.h" -#include "Translate.h" -#include "Rules.h" - -#ifdef TESTSUITE -#include "TestSuiteAI.h" -#endif - -#if defined (WIN32) || defined (LINUX) -#include -#endif - -const float MENU_FONT_SCALE = 1.0f; - -enum ENUM_DUEL_STATE -{ - DUEL_STATE_START, - DUEL_STATE_END, - DUEL_STATE_CHOOSE_DECK1, - DUEL_STATE_CHOOSE_DECK1_TO_2, - DUEL_STATE_CHOOSE_DECK2, - DUEL_STATE_CHOOSE_DECK2_TO_PLAY, - DUEL_STATE_ERROR_NO_DECK, - DUEL_STATE_CANCEL, - DUEL_STATE_PLAY, - DUEL_STATE_BACK_TO_MAIN_MENU, - DUEL_STATE_MENU, - DUEL_STATE_ERROR -}; - -enum ENUM_DUEL_MENUS -{ - DUEL_MENU_GAME_MENU, - DUEL_MENU_CHOOSE_DECK, - DUEL_MENU_CHOOSE_OPPONENT -}; - -GameStateDuel::GameStateDuel(GameApp* parent) : - GameState(parent) -{ - for (int i = 0; i < 2; i++) - { - deck[i] = NULL; - mPlayers[i] = NULL; - } - premadeDeck = false; - game = NULL; - deckmenu = NULL; - opponentMenu = NULL; - menu = NULL; -#ifdef TESTSUITE - testSuite = NULL; -#endif - - credits = NULL; - rules = NULL; - -} - -GameStateDuel::~GameStateDuel() -{ - End(); - -} - -void GameStateDuel::Start() -{ - JRenderer * renderer = JRenderer::GetInstance(); - renderer->EnableVSync(true); - OpponentsDeckid = 0; - -#ifdef TESTSUITE - SAFE_DELETE(testSuite); - testSuite = NEW TestSuite(JGE_GET_RES("test/_tests.txt").c_str(),mParent->collection); -#endif - - mGamePhase = DUEL_STATE_CHOOSE_DECK1; - credits = NEW Credits(); - - menu = NULL; - - int decksneeded = 0; - for (int i = 0; i < 2; i++) - { - if (mParent->players[i] == PLAYER_TYPE_HUMAN) - { - decksneeded = 1; - - deckmenu = NEW DeckMenu(DUEL_MENU_CHOOSE_DECK, this, Fonts::OPTION_FONT, "Choose a Deck", MENU_FONT_SCALE); - - DeckManager *deckManager = DeckManager::GetInstance(); - vector playerDeckList = getValidDeckMetaData(options.profileFile()); - int nbDecks = playerDeckList.size(); - - if (nbDecks) - { - decksneeded = 0; - if (nbDecks > 1) - deckmenu->Add(MENUITEM_RANDOM_PLAYER, "Random", "Play with a random deck."); - } - - renderDeckMenu(deckmenu, playerDeckList); - // save the changes to the player deck list maintained in DeckManager - deckManager->updateMetaDataList(&playerDeckList, false); - playerDeckList.clear(); - - break; - } - } - - if (deckmenu) - { - if (decksneeded) - { - //translate deck creating desc - Translator * t = Translator::GetInstance(); - map::iterator it = t->deckValues.find("Create your Deck!"); - if (it != t->deckValues.end()) - deckmenu->Add(MENUITEM_NEW_DECK, "Create your Deck!", it->second); - else - deckmenu->Add(MENUITEM_NEW_DECK, "Create your Deck!", "Highly recommended to get\nthe full Wagic experience!"); - premadeDeck = true; - fillDeckMenu(deckmenu, JGE_GET_RES("player/premade")); - } - deckmenu->Add(MENUITEM_NEW_DECK, "New Deck...", "Create a new deck to play with."); - deckmenu->Add(MENUITEM_CANCEL, "Main Menu", "Return to Main Menu"); - } - - for (int i = 0; i < 2; ++i) - { - mPlayers[i] = NULL; - } - -} - -void GameStateDuel::loadPlayer(int playerId, int decknb, int isAI) -{ - if (decknb) - { - if (!isAI) - { //Human Player - char deckFile[255]; - if (premadeDeck) - sprintf(deckFile, JGE_GET_RES("player/premade/deck%i.txt").c_str(), decknb); - else - sprintf(deckFile, "%s/deck%i.txt", options.profileFile().c_str(), decknb); - char deckFileSmall[255]; - sprintf(deckFileSmall, "player_deck%i", decknb); - MTGDeck * tempDeck = NEW MTGDeck(deckFile, mParent->collection); - mPlayers[playerId] = NEW HumanPlayer(tempDeck, deckFile, deckFileSmall); - - deck[playerId] = mPlayers[playerId]->game; - delete tempDeck; - } - else - { //AI Player, chooses deck - AIPlayerFactory playerCreator; - Player * opponent = NULL; - if (playerId == 1) - opponent = mPlayers[0]; - mPlayers[playerId] = playerCreator.createAIPlayer(mParent->collection, opponent, decknb); - deck[playerId] = mPlayers[playerId]->game; - } - } - else - { //Random deck - AIPlayerFactory playerCreator; - Player * opponent = NULL; - if (playerId == 1) - opponent = mPlayers[0]; - mPlayers[playerId] = playerCreator.createAIPlayer(mParent->collection, opponent); - deck[playerId] = mPlayers[playerId]->game; - } -} - -void GameStateDuel::initRand(unsigned int seed) -{ - if (!seed) - seed = time(0); - srand(seed); -} - -#ifdef TESTSUITE -void GameStateDuel::loadTestSuitePlayers() -{ - if (!testSuite) return; - initRand(testSuite->seed); - SAFE_DELETE(game); - for (int i = 0; i < 2; i++) - { - mPlayers[i] = NEW TestSuiteAI(testSuite, i); - deck[i] = mPlayers[i]->game; - } - mParent->gameType = testSuite->gameType; - - GameObserver::Init(mPlayers, 2); - game = GameObserver::GetInstance(); - game->startGame(rules); - if (mParent->gameType == GAME_TYPE_MOMIR) - { - game->addObserver(NEW MTGMomirRule(-1, mParent->collection)); - } -} -#endif - -void GameStateDuel::End() -{ - DebugTrace("Ending GameStateDuel"); - - JRenderer::GetInstance()->EnableVSync(false); - DeckManager::EndInstance(); - - if (mPlayers[0] && mPlayers[1]) // save the stats for the game - mPlayers[0]->End(); - else - // clean up player object - SAFE_DELETE( mPlayers[0] ); - - GameObserver::EndInstance(); - game = NULL; - premadeDeck = false; - - for (int i = 0; i < 2; i++) - { - mPlayers[i] = NULL; - deck[i] = NULL; - } - - SAFE_DELETE(credits); - SAFE_DELETE(rules); - - SAFE_DELETE(menu); - SAFE_DELETE(opponentMenu); - SAFE_DELETE(deckmenu); -#ifdef TESTSUITE - SAFE_DELETE(testSuite); -#endif -} - -//TODO Move This to utils or ResourceManager. Don't we have more generic functions that can do that? -bool GameStateDuel::MusicExist(string FileName) -{ - string filepath = JGE_GET_RES(resources.musicFile(FileName)); - std::ifstream file(filepath.c_str()); - if (file) - { - file.close(); - return true; - } - else - return false; -} - -void GameStateDuel::ensureOpponentMenu() -{ - if (!opponentMenu) - { - opponentMenu = NEW DeckMenu(DUEL_MENU_CHOOSE_OPPONENT, this, Fonts::OPTION_FONT, "Choose Your Opponent", MENU_FONT_SCALE); - opponentMenu->Add(MENUITEM_RANDOM_AI, "Random"); - if (options[Options::EVILTWIN_MODE_UNLOCKED].number) - opponentMenu->Add(MENUITEM_EVIL_TWIN, "Evil Twin", _("Can you play against yourself?").c_str()); - DeckManager * deckManager = DeckManager::GetInstance(); - vector opponentDeckList = fillDeckMenu(opponentMenu, JGE_GET_RES("ai/baka"), "ai_baka", mPlayers[0]); - deckManager->updateMetaDataList(&opponentDeckList, true); - opponentMenu->Add(MENUITEM_CANCEL, "Cancel", _("Choose a different player deck").c_str()); - opponentDeckList.clear(); - } -} - -void GameStateDuel::Update(float dt) -{ - switch (mGamePhase) - { - case DUEL_STATE_ERROR_NO_DECK: - if (JGE_BTN_OK == mEngine->ReadButton()) - mParent->SetNextState(GAME_STATE_DECK_VIEWER); - break; - case DUEL_STATE_CHOOSE_DECK1: - if (mParent->gameType == GAME_TYPE_MOMIR) - { - rules = NEW Rules("momir.txt"); - mGamePhase = DUEL_STATE_PLAY; - } - else if (mParent->gameType == GAME_TYPE_RANDOM1) - { - rules = NEW Rules("random1.txt"); - mGamePhase = DUEL_STATE_PLAY; - } - else if (mParent->gameType == GAME_TYPE_RANDOM2) - { - rules = NEW Rules("random2.txt"); - mGamePhase = DUEL_STATE_PLAY; - } -#ifdef TESTSUITE - else if (mParent->players[1] == PLAYER_TYPE_TESTSUITE) - { - if (testSuite && testSuite->loadNext()) - { - rules = NEW Rules("testsuite.txt"); - loadTestSuitePlayers(); - mGamePhase = DUEL_STATE_PLAY; - testSuite->pregameTests(); - testSuite->initGame(); - } - else - { - if (!game) - { - mGamePhase = DUEL_STATE_ERROR; - } - else - { - mGamePhase = DUEL_STATE_END; - } - } - } -#endif - else - { - if (!rules) - rules = NEW Rules("mtg.txt"); - if (mParent->players[0] == PLAYER_TYPE_HUMAN) - deckmenu->Update(dt); - else - { - loadPlayer(0); - mGamePhase = DUEL_STATE_CHOOSE_DECK2; - } - } - break; - case DUEL_STATE_CHOOSE_DECK1_TO_2: - if (deckmenu->closed) - mGamePhase = DUEL_STATE_CHOOSE_DECK2; - else - deckmenu->Update(dt); - break; - case DUEL_STATE_CHOOSE_DECK2: - if (mParent->players[1] == PLAYER_TYPE_HUMAN) - deckmenu->Update(dt); - else - { - if (mParent->players[0] == PLAYER_TYPE_HUMAN) - { - ensureOpponentMenu(); - opponentMenu->Update(dt); - } - else - { - loadPlayer(1); - mGamePhase = DUEL_STATE_PLAY; - } - } - break; - case DUEL_STATE_CHOOSE_DECK2_TO_PLAY: - if (mParent->players[1] == PLAYER_TYPE_HUMAN) - { - if (deckmenu->closed) - mGamePhase = DUEL_STATE_PLAY; - else - deckmenu->Update(dt); - } - else - { - ensureOpponentMenu(); - if (opponentMenu->closed) - mGamePhase = DUEL_STATE_PLAY; - else - opponentMenu->Update(dt); - } - break; - case DUEL_STATE_PLAY: - if (!game) - { - GameObserver::Init(mPlayers, 2); - game = GameObserver::GetInstance(); - game->startGame(rules); - if (mParent->gameType == GAME_TYPE_MOMIR) - { - game->addObserver(NEW MTGMomirRule(-1, mParent->collection)); - } - - //start of in game music code - musictrack = ""; - //check opponent id and choose the music track based on it - if (OpponentsDeckid) - { - char temp[4096]; - sprintf(temp, "ai_baka_music%i.mp3", OpponentsDeckid); - musictrack.assign(temp); - } - else if (mParent->gameType == GAME_TYPE_CLASSIC) - musictrack = "ai_baka_music.mp3"; - else if (mParent->gameType == GAME_TYPE_MOMIR) - musictrack = "ai_baka_music_momir.mp3"; - else if (mParent->gameType == GAME_TYPE_RANDOM1 || mParent->gameType == GAME_TYPE_RANDOM2) - musictrack = "ai_baka_music_random.mp3"; - - if (!MusicExist(musictrack)) - musictrack = "ai_baka_music.mp3"; - - GameApp::playMusic(musictrack); - } - game->Update(dt); - if (game->gameOver) - { - if (game->players[1]->playMode != Player::MODE_TEST_SUITE) - credits->compute(game->players[0], game->players[1], mParent); - mGamePhase = DUEL_STATE_END; -#ifdef TESTSUITE - if (mParent->players[1] == PLAYER_TYPE_TESTSUITE) - { - if (testSuite->loadNext()) - { - loadTestSuitePlayers(); - mGamePhase = DUEL_STATE_PLAY; - testSuite->initGame(); - } - else - mGamePhase = DUEL_STATE_END; - } - else -#endif - if (mParent->players[0] == PLAYER_TYPE_CPU && mParent->players[1] == PLAYER_TYPE_CPU) - { - End(); - Start(); - } - } - if (mEngine->GetButtonClick(JGE_BTN_MENU)) - { - if (!menu) - { - menu = NEW SimpleMenu(DUEL_MENU_GAME_MENU, this, Fonts::MENU_FONT, SCREEN_WIDTH / 2 - 100, 25, - game->players[1]->deckName.c_str()); - int cardsinhand = game->players[0]->game->hand->nb_cards; - - //almosthumane - mulligan - if ((game->turn < 1) && (cardsinhand != 0) && game->currentGamePhase == Constants::MTG_PHASE_FIRSTMAIN - && game->players[0]->game->inPlay->nb_cards == 0 && game->players[0]->game->graveyard->nb_cards - == 0 && game->players[0]->game->exile->nb_cards == 0) //1st Play Check - //IF there was no play at the moment automatically mulligan - { - menu->Add(MENUITEM_MULLIGAN, "Mulligan"); - } - //END almosthumane - mulligan - menu->Add(MENUITEM_MAIN_MENU, "Back to main menu"); - menu->Add(MENUITEM_CANCEL, "Cancel"); - } - mGamePhase = DUEL_STATE_MENU; - } - break; - case DUEL_STATE_MENU: - menu->Update(dt); - break; - case DUEL_STATE_CANCEL: - menu->Update(dt); - if (menu->closed) - { - mGamePhase = DUEL_STATE_PLAY; - SAFE_DELETE(menu); - } - break; - case DUEL_STATE_BACK_TO_MAIN_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); - - break; - default: - if (JGE_BTN_OK == mEngine->ReadButton()) - mParent->SetNextState(GAME_STATE_MENU); - } -} - -void GameStateDuel::Render() -{ - WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT); - JRenderer * r = JRenderer::GetInstance(); - r->ClearScreen(ARGB(0,0,0,0)); - - if (game) - game->Render(); - - switch (mGamePhase) - { - case DUEL_STATE_END: - { - JRenderer * r = JRenderer::GetInstance(); - r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(200,0,0,0)); - credits->Render(); -#ifdef TESTSUITE - if (mParent->players[1] == PLAYER_TYPE_TESTSUITE) - { - r->ClearScreen(ARGB(255,0,0,0)); - char buf[4096]; - int nbFailed = testSuite->nbFailed; - int nbTests = testSuite->nbTests; - if (!nbFailed) - { - sprintf(buf, "All %i tests successful!", nbTests); - } - else - { - sprintf(buf, "%i tests out of %i FAILED!", nbFailed, nbTests); - } - mFont->SetColor(ARGB(255,255,255,255)); - mFont->DrawString(buf,0,SCREEN_HEIGHT/2); - nbFailed = testSuite->nbAIFailed; - nbTests = testSuite->nbAITests; - if (nbTests) - { - if (!nbFailed) - { - sprintf(buf, "AI Tests: All %i tests successful!", nbTests); - } - else - { - sprintf(buf, "AI Tests: %i tests out of %i FAILED!", nbFailed, nbTests); - } - mFont->DrawString(buf,0,SCREEN_HEIGHT/2+20); - } - } -#endif - break; - } - case DUEL_STATE_ERROR: - { - r->ClearScreen(ARGB(200,0,0,0)); - mFont->DrawString(_("AN ERROR OCCURRED, CHECK FILE NAMES").c_str(), 0, SCREEN_HEIGHT / 2); - break; - } - case DUEL_STATE_CHOOSE_DECK1: - case DUEL_STATE_CHOOSE_DECK1_TO_2: - case DUEL_STATE_CHOOSE_DECK2: - case DUEL_STATE_CHOOSE_DECK2_TO_PLAY: - if (mParent->gameType != GAME_TYPE_CLASSIC) - mFont->DrawString(_("LOADING DECKS").c_str(), 0, SCREEN_HEIGHT / 2); - else - { - if (opponentMenu) - opponentMenu->Render(); - else if (deckmenu) - deckmenu->Render(); - - } - break; - case DUEL_STATE_ERROR_NO_DECK: - mFont->DrawString(_("NO DECK AVAILABLE,").c_str(), 0, SCREEN_HEIGHT / 2); - mFont->DrawString(_("PRESS CIRCLE TO GO TO THE DECK EDITOR!").c_str(), 0, SCREEN_HEIGHT / 2 + 20); - break; - case DUEL_STATE_MENU: - case DUEL_STATE_CANCEL: - case DUEL_STATE_BACK_TO_MAIN_MENU: - if (game) - { - r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(100,0,0,0)); - char buffer[4096]; - sprintf(buffer, _("Turn:%i").c_str(), game->turn); - mFont->SetColor(ARGB(255,255,255,255)); - mFont->DrawString(buffer, SCREEN_WIDTH / 2, 0, JGETEXT_CENTER); - } - if (menu) - menu->Render(); - } -} - -void GameStateDuel::ButtonPressed(int controllerId, int controlId) -{ - int deckNumber = controlId; - DeckManager * deckManager = DeckManager::GetInstance(); - int aiDeckSize = deckManager->getAIDeckOrderList()->size(); - switch (controllerId) - { - case DUEL_MENU_CHOOSE_OPPONENT: - { - switch (controlId) - { - case MENUITEM_RANDOM_AI: - loadPlayer(1); - opponentMenu->Close(); - mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; - break; - default: - // cancel option. return to player deck selection - - if (controlId == MENUITEM_CANCEL) - { - opponentMenu->Close(); - deckmenu->Close(); - mParent->SetNextState(DUEL_STATE_CHOOSE_DECK1); - mGamePhase = DUEL_MENU_GAME_MENU; - break; - } - else if (controlId != MENUITEM_EVIL_TWIN && aiDeckSize > 0) // evil twin - deckNumber = deckManager->getAIDeckOrderList()->at(controlId - 1)->getDeckId(); - - loadPlayer(1, deckNumber, 1); - OpponentsDeckid = deckNumber; - opponentMenu->Close(); - mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; - break; - } - break; - } - case DUEL_MENU_CHOOSE_DECK: - { - if (controlId == MENUITEM_RANDOM_PLAYER) // Random Player Deck Selection - { - vector * playerDeckList = deckManager->getPlayerDeckOrderList(); - deckNumber = playerDeckList->at(WRand() * 1001 % (playerDeckList->size()))->getDeckId(); - loadPlayer(0, deckNumber); - deckmenu->Close(); - mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; - break; - } - else if (controlId == MENUITEM_MAIN_MENU || controlId == MENUITEM_CANCEL) // user clicked on "Cancel" - { - if (deckmenu) - deckmenu->Close(); - mGamePhase = DUEL_STATE_BACK_TO_MAIN_MENU; - break; - } - if (controlId < 0) - { - mParent->SetNextState(GAME_STATE_DECK_VIEWER); - return; - } - if (mGamePhase == DUEL_STATE_CHOOSE_DECK1) - { - vector * playerDeck = deckManager->getPlayerDeckOrderList(); - if (!premadeDeck && controlId > 0) - deckNumber = playerDeck->at(controlId - 1)->getDeckId(); - loadPlayer(0, deckNumber); - deckmenu->Close(); - mGamePhase = DUEL_STATE_CHOOSE_DECK1_TO_2; - playerDeck = NULL; - } - else - { - loadPlayer(1, controlId); - deckmenu->Close(); - mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; - } - break; - } - default: - { - switch (controlId) - { - - case MENUITEM_MAIN_MENU: - menu->Close(); - mGamePhase = DUEL_STATE_BACK_TO_MAIN_MENU; - break; - case MENUITEM_CANCEL: - menu->Close(); - mGamePhase = DUEL_STATE_CANCEL; - break; - case MENUITEM_MULLIGAN: - //almosthumane - mulligan - { - - int cardsinhand = game->players[0]->game->hand->nb_cards; - - for (int i = 0; i < cardsinhand; i++) //Discard hand - game->currentPlayer->game->putInZone(game->currentPlayer->game->hand->cards[0], game->currentPlayer->game->hand, - game->currentPlayer->game->library); - - game->currentPlayer->game->library->shuffle(); //Shuffle - - for (int i = 0; i < (cardsinhand - 1); i++) - game->draw(); //Draw hand with 1 less card penalty //almhum - menu->Close(); - mGamePhase = DUEL_STATE_CANCEL; - break; - } - //END almosthumane - mulligan - } - } - } -} +#include "PrecompiledHeader.h" + +#include "DeckMenu.h" +#include "GameStateDuel.h" +#include "GameOptions.h" +#include "utils.h" +#include "AIPlayer.h" +#include "AIMomirPlayer.h" +#include "PlayerData.h" +#include "DeckStats.h" +#include "DeckManager.h" + +#include "DeckMetaData.h" +#include "MTGRules.h" +#include "Credits.h" +#include "Translate.h" +#include "Rules.h" + +#ifdef TESTSUITE +#include "TestSuiteAI.h" +#endif + +#if defined (WIN32) || defined (LINUX) +#include +#endif + +const float MENU_FONT_SCALE = 1.0f; + +enum ENUM_DUEL_STATE +{ + DUEL_STATE_START, + DUEL_STATE_END, + DUEL_STATE_CHOOSE_DECK1, + DUEL_STATE_DECK1_DETAILED_INFO, + DUEL_STATE_DECK2_DETAILED_INFO, + DUEL_STATE_CHOOSE_DECK1_TO_2, + DUEL_STATE_CHOOSE_DECK2, + DUEL_STATE_CHOOSE_DECK2_TO_PLAY, + DUEL_STATE_ERROR_NO_DECK, + DUEL_STATE_CANCEL, + DUEL_STATE_PLAY, + DUEL_STATE_BACK_TO_MAIN_MENU, + DUEL_STATE_MENU, + DUEL_STATE_ERROR +}; + +enum ENUM_DUEL_MENUS +{ + DUEL_MENU_GAME_MENU, + DUEL_MENU_CHOOSE_DECK, + DUEL_MENU_CHOOSE_OPPONENT, + DUEL_MENU_DETAILED_DECK1_INFO, + DUEL_MENU_DETAILED_DECK2_INFO +}; + +int GameStateDuel::selectedPlayerDeckId = 0; +int GameStateDuel::selectedAIDeckId = 0; + +GameStateDuel::GameStateDuel(GameApp* parent) : + GameState(parent) +{ + for (int i = 0; i < 2; i++) + { + deck[i] = NULL; + mPlayers[i] = NULL; + } + premadeDeck = false; + game = NULL; + deckmenu = NULL; + opponentMenu = NULL; + menu = NULL; + popupScreen = NULL; + +#ifdef TESTSUITE + testSuite = NULL; +#endif + + credits = NULL; + rules = NULL; + +} + +GameStateDuel::~GameStateDuel() +{ + End(); + +} + +void GameStateDuel::Start() +{ + JRenderer * renderer = JRenderer::GetInstance(); + renderer->EnableVSync(true); + OpponentsDeckid = 0; + +#ifdef TESTSUITE + SAFE_DELETE(testSuite); + testSuite = NEW TestSuite(JGE_GET_RES("test/_tests.txt").c_str(),mParent->collection); +#endif + + mGamePhase = DUEL_STATE_CHOOSE_DECK1; + credits = NEW Credits(); + + menu = NULL; + + int decksneeded = 0; + for (int i = 0; i < 2; i++) + { + if (mParent->players[i] == PLAYER_TYPE_HUMAN) + { + decksneeded = 1; + + deckmenu = NEW DeckMenu(DUEL_MENU_CHOOSE_DECK, this, Fonts::OPTION_FONT, "Choose a Deck", + GameStateDuel::selectedPlayerDeckId); + + DeckManager *deckManager = DeckManager::GetInstance(); + vector playerDeckList = getValidDeckMetaData(options.profileFile()); + int nbDecks = playerDeckList.size(); + + if (nbDecks) + { + decksneeded = 0; + if (nbDecks > 1) deckmenu->Add(MENUITEM_RANDOM_PLAYER, "Random", "Play with a random deck."); + } + + renderDeckMenu(deckmenu, playerDeckList); + // save the changes to the player deck list maintained in DeckManager + deckManager->updateMetaDataList(&playerDeckList, false); + playerDeckList.clear(); + + DebugTrace("INFO: Player Deck menu has a size of " << sizeof( deckmenu ) ); + break; + } + } + + if (deckmenu) + { + if (decksneeded) + { + //translate deck creating desc + Translator * t = Translator::GetInstance(); + map::iterator it = t->deckValues.find("Create your Deck!"); + if (it != t->deckValues.end()) + deckmenu->Add(MENUITEM_NEW_DECK, "Create your Deck!", it->second); + else + deckmenu->Add(MENUITEM_NEW_DECK, "Create your Deck!", "Highly recommended to get\nthe full Wagic experience!"); + premadeDeck = true; + fillDeckMenu(deckmenu, JGE_GET_RES("player/premade")); + } + deckmenu->Add(MENUITEM_NEW_DECK, "New Deck...", "Create a new deck to play with."); + deckmenu->Add(MENUITEM_CANCEL, "Main Menu", "Return to Main Menu"); + } + + for (int i = 0; i < 2; ++i) + { + mPlayers[i] = NULL; + } + +} + +void GameStateDuel::loadPlayer(int playerId, int decknb, int isAI) +{ + if (decknb) + { + if (!isAI) + { //Human Player + char deckFile[255]; + if (premadeDeck) + sprintf(deckFile, JGE_GET_RES("player/premade/deck%i.txt").c_str(), decknb); + else + sprintf(deckFile, "%s/deck%i.txt", options.profileFile().c_str(), decknb); + char deckFileSmall[255]; + sprintf(deckFileSmall, "player_deck%i", decknb); + MTGDeck * tempDeck = NEW MTGDeck(deckFile, mParent->collection); + mPlayers[playerId] = NEW HumanPlayer(tempDeck, deckFile, deckFileSmall); + + deck[playerId] = mPlayers[playerId]->game; + delete tempDeck; + } + else + { //AI Player, chooses deck + AIPlayerFactory playerCreator; + Player * opponent = NULL; + if (playerId == 1) opponent = mPlayers[0]; + mPlayers[playerId] = playerCreator.createAIPlayer(mParent->collection, opponent, decknb); + deck[playerId] = mPlayers[playerId]->game; + } + } + else + { //Random deck + AIPlayerFactory playerCreator; + Player * opponent = NULL; + if (playerId == 1) opponent = mPlayers[0]; + mPlayers[playerId] = playerCreator.createAIPlayer(mParent->collection, opponent); + deck[playerId] = mPlayers[playerId]->game; + } +} + +void GameStateDuel::initRand(unsigned int seed) +{ + if (!seed) seed = time(0); + srand(seed); +} + +#ifdef TESTSUITE +void GameStateDuel::loadTestSuitePlayers() +{ + if (!testSuite) return; + initRand(testSuite->seed); + SAFE_DELETE(game); + for (int i = 0; i < 2; i++) + { + mPlayers[i] = NEW TestSuiteAI(testSuite, i); + deck[i] = mPlayers[i]->game; + } + mParent->gameType = testSuite->gameType; + + GameObserver::Init(mPlayers, 2); + game = GameObserver::GetInstance(); + game->startGame(rules); + if (mParent->gameType == GAME_TYPE_MOMIR) + { + game->addObserver(NEW MTGMomirRule(-1, mParent->collection)); + } +} +#endif + +void GameStateDuel::End() +{ + DebugTrace("Ending GameStateDuel"); + + JRenderer::GetInstance()->EnableVSync(false); + DeckManager::EndInstance(); + + if (mPlayers[0] && mPlayers[1]) // save the stats for the game + mPlayers[0]->End(); + else + // clean up player object + SAFE_DELETE( mPlayers[0] ); + + GameObserver::EndInstance(); + game = NULL; + premadeDeck = false; + + for (int i = 0; i < 2; i++) + { + mPlayers[i] = NULL; + deck[i] = NULL; + } + + SAFE_DELETE(credits); + SAFE_DELETE(rules); + + SAFE_DELETE(menu); + SAFE_DELETE(opponentMenu); + SAFE_DELETE(deckmenu); + SAFE_DELETE(popupScreen); + +#ifdef TESTSUITE + SAFE_DELETE(testSuite); +#endif +} + +//TODO Move This to utils or ResourceManager. Don't we have more generic functions that can do that? +bool GameStateDuel::MusicExist(string FileName) +{ + string filepath = JGE_GET_RES(resources.musicFile(FileName)); + std::ifstream file(filepath.c_str()); + if (file) + { + file.close(); + return true; + } + else + return false; +} + +void GameStateDuel::ensureOpponentMenu() +{ + if (opponentMenu == NULL) + { + opponentMenu = NEW DeckMenu(DUEL_MENU_CHOOSE_OPPONENT, this, Fonts::OPTION_FONT, "Choose Your Opponent", + GameStateDuel::selectedAIDeckId); + opponentMenu->Add(MENUITEM_RANDOM_AI, "Random"); + if (options[Options::EVILTWIN_MODE_UNLOCKED].number) opponentMenu->Add(MENUITEM_EVIL_TWIN, "Evil Twin", _( + "Can you play against yourself?").c_str()); + DeckManager * deckManager = DeckManager::GetInstance(); + vector opponentDeckList = fillDeckMenu(opponentMenu, JGE_GET_RES("ai/baka"), "ai_baka", mPlayers[0]); + deckManager->updateMetaDataList(&opponentDeckList, true); + opponentMenu->Add(MENUITEM_CANCEL, "Cancel", _("Choose a different player deck").c_str()); + opponentDeckList.clear(); + } +} + +void GameStateDuel::Update(float dt) +{ + switch (mGamePhase) + { + case DUEL_STATE_ERROR_NO_DECK: + if (JGE_BTN_OK == mEngine->ReadButton()) mParent->SetNextState(GAME_STATE_DECK_VIEWER); + break; + + case DUEL_STATE_DECK1_DETAILED_INFO: + case DUEL_STATE_DECK2_DETAILED_INFO: + popupScreen->Update(dt); + break; + + case DUEL_STATE_CHOOSE_DECK1: + if (mParent->gameType == GAME_TYPE_MOMIR) + { + rules = NEW Rules("momir.txt"); + mGamePhase = DUEL_STATE_PLAY; + } + else if (mParent->gameType == GAME_TYPE_RANDOM1) + { + rules = NEW Rules("random1.txt"); + mGamePhase = DUEL_STATE_PLAY; + } + else if (mParent->gameType == GAME_TYPE_RANDOM2) + { + rules = NEW Rules("random2.txt"); + mGamePhase = DUEL_STATE_PLAY; + } +#ifdef TESTSUITE + else if (mParent->players[1] == PLAYER_TYPE_TESTSUITE) + { + if (testSuite && testSuite->loadNext()) + { + rules = NEW Rules("testsuite.txt"); + loadTestSuitePlayers(); + mGamePhase = DUEL_STATE_PLAY; + testSuite->pregameTests(); + testSuite->initGame(); + } + else + { + if (!game) + { + mGamePhase = DUEL_STATE_ERROR; + } + else + { + mGamePhase = DUEL_STATE_END; + } + } + } +#endif + else + { + if (!rules) rules = NEW Rules("mtg.txt"); + if (mParent->players[0] == PLAYER_TYPE_HUMAN) + { + if (!popupScreen || popupScreen->closed) deckmenu->Update(dt); + } + else + { + loadPlayer(0); + mGamePhase = DUEL_STATE_CHOOSE_DECK2; + } + } + break; + case DUEL_STATE_CHOOSE_DECK1_TO_2: + if (deckmenu->closed) + mGamePhase = DUEL_STATE_CHOOSE_DECK2; + else + deckmenu->Update(dt); + break; + case DUEL_STATE_CHOOSE_DECK2: + if (mParent->players[1] == PLAYER_TYPE_HUMAN) + deckmenu->Update(dt); + else + { + if (mParent->players[0] == PLAYER_TYPE_HUMAN) + { + ensureOpponentMenu(); + opponentMenu->Update(dt); + } + else + { + loadPlayer(1); + mGamePhase = DUEL_STATE_PLAY; + } + } + break; + case DUEL_STATE_CHOOSE_DECK2_TO_PLAY: + if (mParent->players[1] == PLAYER_TYPE_HUMAN) + { + if (deckmenu->closed) + mGamePhase = DUEL_STATE_PLAY; + else + deckmenu->Update(dt); + } + else + { + ensureOpponentMenu(); + if (opponentMenu->closed) + mGamePhase = DUEL_STATE_PLAY; + else + opponentMenu->Update(dt); + } + break; + case DUEL_STATE_PLAY: + if (!game) + { + GameObserver::Init(mPlayers, 2); + game = GameObserver::GetInstance(); + game->startGame(rules); + if (mParent->gameType == GAME_TYPE_MOMIR) + { + game->addObserver(NEW MTGMomirRule(-1, mParent->collection)); + } + + //start of in game music code + musictrack = ""; + //check opponent id and choose the music track based on it + if (OpponentsDeckid) + { + char temp[4096]; + sprintf(temp, "ai_baka_music%i.mp3", OpponentsDeckid); + musictrack.assign(temp); + } + else if (mParent->gameType == GAME_TYPE_CLASSIC) + musictrack = "ai_baka_music.mp3"; + else if (mParent->gameType == GAME_TYPE_MOMIR) + musictrack = "ai_baka_music_momir.mp3"; + else if (mParent->gameType == GAME_TYPE_RANDOM1 || mParent->gameType == GAME_TYPE_RANDOM2) musictrack + = "ai_baka_music_random.mp3"; + + if (!MusicExist(musictrack)) musictrack = "ai_baka_music.mp3"; + + GameApp::playMusic(musictrack); + } + game->Update(dt); + if (game->gameOver) + { + if (game->players[1]->playMode != Player::MODE_TEST_SUITE) credits->compute(game->players[0], game->players[1], mParent); + mGamePhase = DUEL_STATE_END; +#ifdef TESTSUITE + if (mParent->players[1] == PLAYER_TYPE_TESTSUITE) + { + if (testSuite->loadNext()) + { + loadTestSuitePlayers(); + mGamePhase = DUEL_STATE_PLAY; + testSuite->initGame(); + } + else + mGamePhase = DUEL_STATE_END; + } + else +#endif + if (mParent->players[0] == PLAYER_TYPE_CPU && mParent->players[1] == PLAYER_TYPE_CPU) + { + End(); + Start(); + } + } + if (mEngine->GetButtonClick(JGE_BTN_MENU)) + { + if (!menu) + { + menu = NEW SimpleMenu(DUEL_MENU_GAME_MENU, this, Fonts::MENU_FONT, SCREEN_WIDTH / 2 - 100, 25, + game->players[1]->deckName.c_str()); + int cardsinhand = game->players[0]->game->hand->nb_cards; + + //almosthumane - mulligan + if ((game->turn < 1) && (cardsinhand != 0) && game->currentGamePhase == Constants::MTG_PHASE_FIRSTMAIN + && game->players[0]->game->inPlay->nb_cards == 0 && game->players[0]->game->graveyard->nb_cards == 0 + && game->players[0]->game->exile->nb_cards == 0) //1st Play Check + //IF there was no play at the moment automatically mulligan + { + menu->Add(MENUITEM_MULLIGAN, "Mulligan"); + } + //END almosthumane - mulligan + menu->Add(MENUITEM_MAIN_MENU, "Back to main menu"); + menu->Add(MENUITEM_CANCEL, "Cancel"); + } + mGamePhase = DUEL_STATE_MENU; + } + break; + case DUEL_STATE_MENU: + menu->Update(dt); + break; + case DUEL_STATE_CANCEL: + menu->Update(dt); + if (menu->closed) + { + mGamePhase = DUEL_STATE_PLAY; + SAFE_DELETE(menu); + } + break; + case DUEL_STATE_BACK_TO_MAIN_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); + + break; + default: + if (JGE_BTN_OK == mEngine->ReadButton()) mParent->SetNextState(GAME_STATE_MENU); + } +} + +void GameStateDuel::Render() +{ + WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT); + JRenderer * r = JRenderer::GetInstance(); + r->ClearScreen(ARGB(0,0,0,0)); + + if (game) game->Render(); + + switch (mGamePhase) + { + case DUEL_STATE_END: + { + JRenderer * r = JRenderer::GetInstance(); + r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(200,0,0,0)); + credits->Render(); +#ifdef TESTSUITE + if (mParent->players[1] == PLAYER_TYPE_TESTSUITE) + { + r->ClearScreen(ARGB(255,0,0,0)); + char buf[4096]; + int nbFailed = testSuite->nbFailed; + int nbTests = testSuite->nbTests; + if (!nbFailed) + { + sprintf(buf, "All %i tests successful!", nbTests); + } + else + { + sprintf(buf, "%i tests out of %i FAILED!", nbFailed, nbTests); + } + mFont->SetColor(ARGB(255,255,255,255)); + mFont->DrawString(buf,0,SCREEN_HEIGHT/2); + nbFailed = testSuite->nbAIFailed; + nbTests = testSuite->nbAITests; + if (nbTests) + { + if (!nbFailed) + { + sprintf(buf, "AI Tests: All %i tests successful!", nbTests); + } + else + { + sprintf(buf, "AI Tests: %i tests out of %i FAILED!", nbFailed, nbTests); + } + mFont->DrawString(buf,0,SCREEN_HEIGHT/2+20); + } + } +#endif + break; + } + case DUEL_STATE_ERROR: + { + r->ClearScreen(ARGB(200,0,0,0)); + mFont->DrawString(_("AN ERROR OCCURRED, CHECK FILE NAMES").c_str(), 0, SCREEN_HEIGHT / 2); + break; + } + case DUEL_STATE_CHOOSE_DECK1: + case DUEL_STATE_CHOOSE_DECK1_TO_2: + case DUEL_STATE_CHOOSE_DECK2: + case DUEL_STATE_CHOOSE_DECK2_TO_PLAY: + case DUEL_STATE_DECK1_DETAILED_INFO: + case DUEL_STATE_DECK2_DETAILED_INFO: + if (mParent->gameType != GAME_TYPE_CLASSIC) + mFont->DrawString(_("LOADING DECKS").c_str(), 0, SCREEN_HEIGHT / 2); + else + { + if (opponentMenu) + opponentMenu->Render(); + else if (deckmenu && !deckmenu->closed) deckmenu->Render(); + + if (menu) menu->Render(); + + if (popupScreen && !popupScreen->closed) popupScreen->Render(); + } + break; + case DUEL_STATE_ERROR_NO_DECK: + mFont->DrawString(_("NO DECK AVAILABLE,").c_str(), 0, SCREEN_HEIGHT / 2); + mFont->DrawString(_("PRESS CIRCLE TO GO TO THE DECK EDITOR!").c_str(), 0, SCREEN_HEIGHT / 2 + 20); + break; + case DUEL_STATE_MENU: + case DUEL_STATE_CANCEL: + case DUEL_STATE_BACK_TO_MAIN_MENU: + if (game) + { + r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(100,0,0,0)); + char buffer[4096]; + sprintf(buffer, _("Turn:%i").c_str(), game->turn); + mFont->SetColor(ARGB(255,255,255,255)); + mFont->DrawString(buffer, SCREEN_WIDTH / 2, 0, JGETEXT_CENTER); + } + if (menu) menu->Render(); + } +} + +void GameStateDuel::ButtonPressed(int controllerId, int controlId) +{ + int deckNumber = controlId; + DeckManager * deckManager = DeckManager::GetInstance(); + int aiDeckSize = deckManager->getAIDeckOrderList()->size(); + switch (controllerId) + { + + case DUEL_MENU_DETAILED_DECK1_INFO: + if ((popupScreen || deckmenu->selectedDeckHasDetails())) + { + DeckMetaData* selectedDeck = deckmenu->getSelectedDeck(); + if (!popupScreen->closed) + { + popupScreen->Close(); + mGamePhase = DUEL_STATE_CHOOSE_DECK1; + SAFE_DELETE( popupScreen ); + } + else + { + popupScreen->Update(selectedDeck); + popupScreen->Render(); + } + } + break; + case DUEL_MENU_DETAILED_DECK2_INFO: + if ((popupScreen || opponentMenu->selectedDeckHasDetails())) + { + DeckMetaData* selectedDeck = opponentMenu->getSelectedDeck(); + if (!popupScreen->closed) + { + popupScreen->Close(); + mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; + SAFE_DELETE( popupScreen ); + } + else + { + popupScreen->Update(selectedDeck); + popupScreen->Render(); + } + } + break; + + case DUEL_MENU_CHOOSE_OPPONENT: + + switch (controlId) + { + case MENUITEM_RANDOM_AI: + loadPlayer(1); + opponentMenu->Close(); + mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; + break; + default: + // cancel option. return to player deck selection + + if (controlId == MENUITEM_CANCEL) + { + opponentMenu->Close(); + deckmenu->Close(); + mParent->SetNextState(DUEL_STATE_CHOOSE_DECK1); + mGamePhase = DUEL_STATE_CHOOSE_DECK1; + break; + } + + else if (controlId == MENUITEM_MORE_INFO && opponentMenu->showDetailsScreen) + { + DeckMetaData* selectedDeck = opponentMenu->getSelectedDeck(); + if (!popupScreen) + { + popupScreen = NEW SimplePopup(DUEL_MENU_DETAILED_DECK2_INFO, this, Fonts::MAIN_FONT, "Detailed Information", + selectedDeck, mParent->collection); + popupScreen->Render(); + selectedAIDeckId = selectedDeck->getDeckId(); + } + else + { + popupScreen->Update(selectedDeck); + } + mGamePhase = DUEL_STATE_DECK2_DETAILED_INFO; + break; + } + else if (controlId == MENUITEM_MORE_INFO && !opponentMenu->showDetailsScreen) + { + // do nothing, ignore all key requests until popup is dismissed. + break; + } + else if (controlId != MENUITEM_EVIL_TWIN && aiDeckSize > 0) // evil twin + deckNumber = deckManager->getAIDeckOrderList()->at(controlId - 1)->getDeckId(); + loadPlayer(1, deckNumber, 1); + OpponentsDeckid = deckNumber; + opponentMenu->Close(); + mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; + break; + } + break; + + case DUEL_MENU_CHOOSE_DECK: + + if (controlId == MENUITEM_RANDOM_PLAYER) // Random Player Deck Selection + { + vector * playerDeckList = deckManager->getPlayerDeckOrderList(); + deckNumber = playerDeckList->at(WRand() * 1001 % (playerDeckList->size()))->getDeckId(); + loadPlayer(0, deckNumber); + deckmenu->Close(); + mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; + break; + } + else if (controlId == MENUITEM_MAIN_MENU || controlId == MENUITEM_CANCEL) // user clicked on "Cancel" + { + if (deckmenu) deckmenu->Close(); + mGamePhase = DUEL_STATE_BACK_TO_MAIN_MENU; + break; + } + else if (controlId == MENUITEM_MORE_INFO && deckmenu->showDetailsScreen) + { + DeckMetaData* selectedDeck = deckmenu->getSelectedDeck(); + if (!popupScreen) + { + popupScreen = NEW SimplePopup(DUEL_MENU_DETAILED_DECK1_INFO, this, Fonts::MAIN_FONT, "Detailed Information", + selectedDeck, mParent->collection); + popupScreen->Render(); + selectedPlayerDeckId = deckmenu->selectedDeckId; + } + else + { + popupScreen->Update(selectedDeck); + } + mGamePhase = DUEL_STATE_DECK1_DETAILED_INFO; + break; + } + else if (controlId == MENUITEM_MORE_INFO) + { + // do nothing + break; + } + if (controlId < 0) + { + mParent->SetNextState(GAME_STATE_DECK_VIEWER); + return; + } + if (mGamePhase == DUEL_STATE_CHOOSE_DECK1) + { + vector * playerDeck = deckManager->getPlayerDeckOrderList(); + if (!premadeDeck && controlId > 0) deckNumber = playerDeck->at(controlId - 1)->getDeckId(); + loadPlayer(0, deckNumber); + deckmenu->Close(); + mGamePhase = DUEL_STATE_CHOOSE_DECK1_TO_2; + playerDeck = NULL; + } + else + { + loadPlayer(1, controlId); + deckmenu->Close(); + mGamePhase = DUEL_STATE_CHOOSE_DECK2_TO_PLAY; + } + break; + + default: + + switch (controlId) + { + + case MENUITEM_MAIN_MENU: + menu->Close(); + mGamePhase = DUEL_STATE_BACK_TO_MAIN_MENU; + break; + case MENUITEM_CANCEL: + menu->Close(); + mGamePhase = DUEL_STATE_CANCEL; + break; + case MENUITEM_MULLIGAN: + //almosthumane - mulligan + + + int cardsinhand = game->players[0]->game->hand->nb_cards; + + for (int i = 0; i < cardsinhand; i++) //Discard hand + game->currentPlayer->game->putInZone(game->currentPlayer->game->hand->cards[0], + game->currentPlayer->game->hand, + game->currentPlayer->game->library); + + game->currentPlayer->game->library->shuffle(); //Shuffle + + for (int i = 0; i < (cardsinhand - 1); i++) + game->draw(); //Draw hand with 1 less card penalty //almhum + menu->Close(); + mGamePhase = DUEL_STATE_CANCEL; + break; + + //END almosthumane - mulligan + } + + } +} diff --git a/projects/mtg/src/MTGDeck.cpp b/projects/mtg/src/MTGDeck.cpp index b30dd0982..7864a550a 100644 --- a/projects/mtg/src/MTGDeck.cpp +++ b/projects/mtg/src/MTGDeck.cpp @@ -1,1220 +1,1164 @@ -#include "PrecompiledHeader.h" - -#include "MTGDeck.h" -#include "utils.h" -#include "Subtypes.h" -#include "Translate.h" -#include "DeckMetaData.h" -#include "PriceList.h" -#include "WDataSrc.h" -#include "MTGPack.h" -#include "utils.h" - -#if defined (WIN32) || defined (LINUX) -#include -#endif - -static inline int getGrade(int v) -{ - switch (v) - { - case 'P': - case 'p': - return Constants::GRADE_SUPPORTED; - case 'R': - case 'r': - return Constants::GRADE_BORDERLINE; - case 'O': - case 'o': - return Constants::GRADE_UNOFFICIAL; - case 'A': - case 'a': - return Constants::GRADE_CRAPPY; - case 'S': - case 's': - return Constants::GRADE_UNSUPPORTED; - case 'N': - case 'n': - return Constants::GRADE_DANGEROUS; - } - return 0; -} - -//MTGAllCards -int MTGAllCards::processConfLine(string &s, MTGCard *card, CardPrimitive * primitive) -{ - if ('#' == s[0]) - return 0; - size_t i = s.find_first_of('='); - if (i == string::npos || 0 == i) - { - DebugTrace("MTGDECK: Bad Line:\n\t" << s); - return 0; - } - - char* key = const_cast (s.c_str()); // I know what I'm doing, let me do it - key[i] = 0; - char* val = key + i + 1; - - switch (key[0]) - { - case 'a': - if (0 == strcmp("auto", key)) - { - if (!primitive) - primitive = NEW CardPrimitive(); - primitive->addMagicText(val); - } - else if (0 == strncmp("auto", key, 4)) - { - if (!primitive) - primitive = NEW CardPrimitive(); - primitive->addMagicText(val, key + 4); - } - else if (0 == strcmp("alias", key)) - { - if (!primitive) - primitive = NEW CardPrimitive(); - primitive->alias = atoi(val); - } - else if (0 == strcmp("abilities", key)) - { - if (!primitive) - primitive = NEW CardPrimitive(); - string value = val; - //Specific Abilities - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - while (value.size()) - { - string attribute; - size_t found2 = value.find(','); - if (found2 != string::npos) - { - attribute = value.substr(0, found2); - value = value.substr(found2 + 1); - } - else - { - attribute = value; - value = ""; - } - for (int j = Constants::NB_BASIC_ABILITIES - 1; j >= 0; --j) - { - size_t found = attribute.find(Constants::MTGBasicAbilities[j]); - if (found != string::npos) - { - primitive->basicAbilities[j] = 1; - break; - } - } - } - } - break; - - case 'c': //color - if (!primitive) - primitive = NEW CardPrimitive(); - { - string value = val; - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - vector values = split(value, ','); - int removeAllOthers = 1; - for (size_t values_i = 0; values_i < values.size(); ++values_i) - { - primitive->setColor(values[values_i], removeAllOthers); - removeAllOthers = 0; - } - } - break; - - case 'g': //grade - if (s.size() - i - 1 > 2) - currentGrade = getGrade(val[2]); - break; - case 'k': //kicker - if (!primitive) - primitive = NEW CardPrimitive(); - if (ManaCost * cost = primitive->getManaCost()) - { - string value = val; - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - cost->kicker = ManaCost::parseManaCost(value); - } - break; - case 'o': //othercost - if (!primitive) - primitive = NEW CardPrimitive(); - if (ManaCost * cost = primitive->getManaCost()) - { - string value = val; - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - cost->alternative = ManaCost::parseManaCost(value); - } - break; - case 'b': //buyback - if (!primitive) - primitive = NEW CardPrimitive(); - if (ManaCost * cost = primitive->getManaCost()) - { - string value = val; - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - cost->BuyBack = ManaCost::parseManaCost(value); - } - break; - case 'f': //flashback - if (!primitive) - primitive = NEW CardPrimitive(); - if (ManaCost * cost = primitive->getManaCost()) - { - string value = val; - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - cost->FlashBack = ManaCost::parseManaCost(value); - } - break; - - case 'i': //id - if (!card) - card = NEW MTGCard(); - card->setMTGId(atoi(val)); - break; - - case 'm': //mana - if (!primitive) - primitive = NEW CardPrimitive(); - { - string value = val; - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - primitive->setManaCost(value); - } - break; - - case 'n': //name - if (!primitive) - primitive = NEW CardPrimitive(); - if (0 == strcmp("Bloodrock Cyclops", val)) - cout << "val" << endl; - primitive->setName(val); - break; - - case 'p': - if ('r' == key[1]) - { // primitive - if (!card) - card = NEW MTGCard(); - map::iterator it = primitives.find(val); - if (it != primitives.end()) - card->setPrimitive(it->second); - } - else - { //power - if (!primitive) - primitive = NEW CardPrimitive(); - primitive->setPower(atoi(val)); - } - break; - - case 'r': //retrace/rarity - if ('e' == key[1]) - { //retrace - if (!primitive) - primitive = NEW CardPrimitive(); - if (ManaCost * cost = primitive->getManaCost()) - { - string value = val; - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - cost->Retrace = ManaCost::parseManaCost(value); - } - } - else - {//rarity - if (!card) - card = NEW MTGCard(); - card->setRarity(val[0]); - } - break; - - case 's': //subtype - if (!primitive) - primitive = NEW CardPrimitive(); - while (true) - { - char* found = strchr(val, ' '); - if (found) - { - string value(val, found - val); - primitive->setSubtype(value); - val = found + 1; - } - else - { - primitive->setSubtype(val); - break; - } - } - break; - - case 't': - if (!primitive) - primitive = NEW CardPrimitive(); - if (0 == strcmp("target", key)) - { - string value = val; - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - primitive->spellTargetType = value; - } - else if (0 == strcmp("text", key)) - primitive->setText(val); - else if (0 == strcmp("type", key)) - { - while (true) - { - char* found = strchr(val, ' '); - if (found) - { - string value(val, found - val); - primitive->setType(value); - val = found + 1; - } - else - { - primitive->setType(val); - break; - } - } - } - else if (0 == strcmp("toughness", key)) - primitive->setToughness(atoi(val)); - break; - - default: - DebugTrace("MTGDECK Parsing Error: " << s); - break; - } - - tempPrimitive = primitive; - tempCard = card; - - return i; - -} - -void MTGAllCards::initCounters() -{ - for (int i = 0; i < Constants::MTG_NB_COLORS; i++) - { - colorsCount[i] = NULL; - } -} - -void MTGAllCards::init() -{ - tempCard = NULL; - tempPrimitive = NULL; - total_cards = 0; - initCounters(); -} - -int MTGAllCards::load(const char * config_file, const char * set_name, int autoload) -{ - conf_read_mode = 0; - const int set_id = set_name ? setlist.Add(set_name) : MTGSets::INTERNAL_SET; - MTGSetInfo *si = setlist.getInfo(set_id); - - std::ifstream setFile(config_file, ios::in | ios::ate); - if (!setFile) - return total_cards; - - streampos fileSize = setFile.tellg(); - setFile.seekg(0, ios::beg); - - std::string contents; - contents.resize((std::string::size_type) fileSize); - setFile.read(&contents[0], fileSize); - std::stringstream stream(contents); - - string s; - - while (true) - { - if (!std::getline(stream, s)) - return total_cards; - if (!s.size()) - continue; - - if (s[s.size() - 1] == '\r') - s.erase(s.size() - 1); // Handle DOS files - switch (conf_read_mode) - { - case MTGAllCards::READ_ANYTHING: - if (s[0] == '[') - { - currentGrade = Constants::GRADE_SUPPORTED; // Default value - conf_read_mode = ('m' == s[1]) ? MTGAllCards::READ_METADATA : MTGAllCards::READ_CARD; // M for metadata. - } - else - { - //Global grade for file, to avoid reading the entire file if unnnecessary - if (s[0] == 'g' && s.size() > 8) - { - int fileGrade = getGrade(s[8]); - int maxGrade = options[Options::MAX_GRADE].number; - if (!maxGrade) - maxGrade = Constants::GRADE_BORDERLINE; //Default setting for grade is borderline? - if (fileGrade > maxGrade) - { - return total_cards; - } - } - } - continue; - case MTGAllCards::READ_METADATA: - if (s[0] == '[' && s[1] == '/') - conf_read_mode = MTGAllCards::READ_ANYTHING; - else if (si) - si->processConfLine(s); - continue; - case MTGAllCards::READ_CARD: - if (s[0] == '[' && s[1] == '/') - { - conf_read_mode = MTGAllCards::READ_ANYTHING; - if (tempPrimitive) - tempPrimitive = addPrimitive(tempPrimitive, tempCard); - if (tempCard) - { - if (tempPrimitive) - tempCard->setPrimitive(tempPrimitive); - addCardToCollection(tempCard, set_id); - } - tempCard = NULL; - tempPrimitive = NULL; - } - else - { - processConfLine(s, tempCard, tempPrimitive); - } - continue; - } - } - - return total_cards; -} - -MTGAllCards::MTGAllCards() -{ - init(); -} - -MTGAllCards::~MTGAllCards() -{ - //Why don't we call destroyAllCards from here ??? -} - -void MTGAllCards::destroyAllCards() -{ - - for (map::iterator it = collection.begin(); it != collection.end(); it++) - delete (it->second); - collection.clear(); - ids.clear(); - - for (map::iterator it = primitives.begin(); it != primitives.end(); it++) - delete (it->second); - primitives.clear(); - -} - -MTGAllCards::MTGAllCards(const char * config_file, const char * set_name) -{ - init(); - load(config_file, set_name, 0); -} - -int MTGAllCards::randomCardId() -{ - int id = (rand() % ids.size()); - return ids[id]; -} - -int MTGAllCards::countBySet(int setId) -{ - int result = 0; - map::iterator it; - - for (it = collection.begin(); it != collection.end(); it++) - { - MTGCard * c = it->second; - if (c->setId == setId) - { - result++; - } - } - return result; -} - -//TODO more efficient way ? -int MTGAllCards::countByType(const char * _type) -{ - int result = 0; - map::iterator it; - for (it = collection.begin(); it != collection.end(); it++) - { - MTGCard * c = it->second; - if (c->data->hasType(_type)) - { - result++; - } - } - return result; -} - -int MTGAllCards::countByColor(int color) -{ - if (colorsCount[color] == 0) - { - for (int i = 0; i < Constants::MTG_NB_COLORS; i++) - { - colorsCount[i] = 0; - } - map::iterator it; - for (it = collection.begin(); it != collection.end(); it++) - { - MTGCard * c = it->second; - int j = c->data->getColor(); - - colorsCount[j]++; - } - } - return colorsCount[color]; -} - -int MTGAllCards::totalCards() -{ - return (total_cards); -} - -bool MTGAllCards::addCardToCollection(MTGCard * card, int setId) -{ - card->setId = setId; - int newId = card->getId(); - if (collection.find(newId) != collection.end()) - { -#if defined (_DEBUG) - string cardName = card->data ? card->data->name : card->getImageName(); - string setName = setId != -1 ? setlist.getInfo(setId)->getName() : ""; - DebugTrace("warning, card id collision! : " << newId << " -> " << cardName << "(" << setName << ")"); -#endif - SAFE_DELETE(card); - return false; - } - - //Don't add cards that don't have a primitive - if (!card->data) - { - SAFE_DELETE(card); - return false; - } - ids.push_back(newId); - - collection[newId] = card; //Push card into collection. - MTGSetInfo * si = setlist.getInfo(setId); - if (si) - si->count(card); //Count card in set info - ++total_cards; - return true; -} - -CardPrimitive * MTGAllCards::addPrimitive(CardPrimitive * primitive, MTGCard * card) -{ - int maxGrade = options[Options::MAX_GRADE].number; - if (!maxGrade) - maxGrade = Constants::GRADE_BORDERLINE; //Default setting for grade is borderline? - if (currentGrade > maxGrade) - { - SAFE_DELETE(primitive); - return NULL; - } - string key; - if (card) - { - std::stringstream ss; - ss << card->getId(); - ss >> key; - } - else - key = primitive->name; - if (primitives.find(key) != primitives.end()) - { - //ERROR - //Todo move the deletion somewhere else ? -DebugTrace ("MTGDECK: primitives conflict: "<< key); - SAFE_DELETE(primitive); - return NULL; - } - //translate cards text - Translator * t = Translator::GetInstance(); - map::iterator it = t->tempValues.find(primitive->name); - if (it != t->tempValues.end()) - { - primitive->setText(it->second); - } - - //Legacy: - //For the Deck editor, we need Lands and Artifact to be colors... - if (primitive->hasType(Subtypes::TYPE_LAND)) primitive->setColor(Constants::MTG_COLOR_LAND); - if (primitive->hasType(Subtypes::TYPE_ARTIFACT)) primitive->setColor(Constants::MTG_COLOR_ARTIFACT); - - primitives[key] = primitive; - return primitive; -} - -MTGCard * MTGAllCards::getCardById(int id) -{ - map::iterator it = collection.find(id); - if (it != collection.end()) - { - return (it->second); - } - return 0; -} - -MTGCard * MTGAllCards::_(int index) -{ - if (index >= total_cards) - return NULL; - return getCardById(ids[index]); -} - -MTGCard * MTGAllCards::getCardByName(string name) -{ - if (!name.size()) - return NULL; - if (name[0] == '#') - return NULL; - - int cardnb = atoi(name.c_str()); - if (cardnb) - { - return getCardById(cardnb); - } - - std::transform(name.begin(), name.end(), name.begin(), ::tolower); - int setId = -1; - size_t found = name.find(" ("); - if (found != string::npos) - { - size_t end = name.find(")"); - string setName = name.substr(found + 2, end - found - 2); - trim(setName); - name = name.substr(0, found); - trim(name); - setId = setlist[setName]; - } - map::iterator it; - for (it = collection.begin(); it != collection.end(); it++) - { - MTGCard * c = it->second; - if (setId != -1 && setId != c->setId) - continue; - string cardName = c->data->name; - std::transform(cardName.begin(), cardName.end(), cardName.begin(), ::tolower); - if (cardName.compare(name) == 0) - return c; - - } - return NULL; -} - -//MTGDeck -MTGDeck::MTGDeck(MTGAllCards * _allcards) -{ - total_cards = 0; - database = _allcards; - filename = ""; - meta_name = ""; -} -int MTGDeck::totalPrice() -{ - int total = 0; - PriceList * pricelist = NEW PriceList(JGE_GET_RES("settings/prices.dat").c_str(), GameApp::collection); - map::iterator it; - for (it = cards.begin(); it != cards.end(); it++) - { - int nb = it->second; - if (nb) - total += pricelist->getPrice(it->first); - } - SAFE_DELETE(pricelist); - return total; -} -MTGDeck::MTGDeck(const char * config_file, MTGAllCards * _allcards, int meta_only) -{ - total_cards = 0; - database = _allcards; - filename = config_file; - size_t slash = filename.find_last_of("/"); - size_t dot = filename.find("."); - meta_name = filename.substr(slash + 1, dot - slash - 1); - std::ifstream file(config_file); - 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] == '#') - { - size_t found = s.find("NAME:"); - if (found != string::npos) - { - meta_name = s.substr(found + 5); - continue; - } - found = s.find("DESC:"); - if (found != string::npos) - { - if (meta_desc.size()) - meta_desc.append("\n"); - meta_desc.append(s.substr(found + 5)); - continue; - } - continue; - } - if (meta_only) - break; - int cardnb = atoi(s.c_str()); - if (cardnb) - { - add(cardnb); - } - else - { - int nb = 1; - size_t found = s.find(" *"); - if (found != string::npos) - { - nb = atoi(s.substr(found + 2).c_str()); - s = s.substr(0, found); - } - MTGCard * card = database->getCardByName(s); - if (card) - { - for (int i = 0; i < nb; i++) - { - add(card); - } - } - else - { - DebugTrace("could not find Card matching name: " << s); - } - } - } - file.close(); - } - else - { - //TODO Error management - } -} - -int MTGDeck::totalCards() -{ - return total_cards; -} - -MTGCard * MTGDeck::getCardById(int mtgId) -{ - return database->getCardById(mtgId); -} - -int MTGDeck::addRandomCards(int howmany, int * setIds, int nbSets, int rarity, const char * _subtype, int * colors, int nbcolors) -{ - if (howmany <= 0) - return 1; - - int unallowedColors[Constants::MTG_NB_COLORS + 1]; - for (int i = 0; i < Constants::MTG_NB_COLORS; ++i) - { - if (nbcolors) - unallowedColors[i] = 1; - else - unallowedColors[i] = 0; - } - for (int i = 0; i < nbcolors; ++i) - { - unallowedColors[colors[i]] = 0; - } - - int collectionTotal = database->totalCards(); - if (!collectionTotal) - return 0; - - char subtype[4096]; - if (_subtype) - sprintf(subtype, "%s", _subtype); - - vector subcollection; - int subtotal = 0; - for (int i = 0; i < collectionTotal; i++) - { - MTGCard * card = database->_(i); - int r = card->getRarity(); - if (r != Constants::RARITY_T && (rarity == -1 || r == rarity) && // remove tokens - card->setId != MTGSets::INTERNAL_SET && //remove cards that are defined in primitives. Those are workarounds (usually tokens) and should only be used internally - (!_subtype || card->data->hasSubtype(subtype))) - { - int ok = 0; - - if (!nbSets) - ok = 1; - for (int j = 0; j < nbSets; ++j) - { - if (card->setId == setIds[j]) - { - ok = 1; - break; - } - } - - if (ok) - { - for (int j = 0; j < Constants::MTG_NB_COLORS; ++j) - { - if (unallowedColors[j] && card->data->hasColor(j)) - { - ok = 0; - break; - } - } - } - - if (ok) - { - subcollection.push_back(card->getId()); - subtotal++; - } - } - } - if (subtotal == 0) - { - if (rarity == Constants::RARITY_M) - return addRandomCards(howmany, setIds, nbSets, Constants::RARITY_R, _subtype, colors, nbcolors); - return 0; - } - for (int i = 0; i < howmany; i++) - { - int id = (rand() % subtotal); - add(subcollection[id]); - } - return 1; -} - -int MTGDeck::add(MTGDeck * deck) -{ - map::iterator it; - for (it = deck->cards.begin(); it != deck->cards.end(); it++) - { - for (int i = 0; i < it->second; i++) - { - add(it->first); - } - } - return deck->totalCards(); -} - -int MTGDeck::add(int cardid) -{ - if (!database->getCardById(cardid)) - return 0; - if (cards.find(cardid) == cards.end()) - { - cards[cardid] = 1; - } - else - { - cards[cardid]++; - } - ++total_cards; - //initCounters(); - return total_cards; -} - -int MTGDeck::add(MTGCard * card) -{ - if (!card) - return 0; - return (add(card->getId())); -} - -int MTGDeck::complete() -{ - /* (PSY) adds cards to the deck/collection. Makes sure that the deck - or collection has at least 4 of every implemented card. Does not - change the number of cards of which already 4 or more are present. */ - int id, n; - bool StypeIsNothing; - size_t databaseSize = database->ids.size(); - for (size_t it = 0; it < databaseSize; it++) - { - id = database->ids[it]; - StypeIsNothing = false; - if (database->getCardById(id)->data->hasType("nothing")) - { - StypeIsNothing = true; - } - if (!StypeIsNothing == true) - { - if (cards.find(id) == cards.end()) - { - cards[id] = 4; - total_cards += 4; - } - else - { - n = cards[id]; - if (n < 4) - { - total_cards += 4 - n; - cards[id] = 4; - } - } - } - } - return 1; -} - -int MTGDeck::removeAll() -{ - total_cards = 0; - cards.clear(); - //initCounters(); - return 1; -} - -int MTGDeck::remove(int cardid) -{ - if (cards.find(cardid) == cards.end() || cards[cardid] == 0) - return 0; - cards[cardid]--; - total_cards--; - //initCounters(); - return 1; -} - -int MTGDeck::remove(MTGCard * card) -{ - if (!card) - return 0; - return (remove(card->getId())); -} - -int MTGDeck::save() -{ - return save(filename, false, meta_name, meta_desc); -} - -int MTGDeck::save(string destFileName, bool useExpandedDescriptions, string &deckTitle, string &deckDesc) -{ - string tmp = destFileName; - tmp.append(".tmp"); //not thread safe - std::ofstream file(tmp.c_str()); - char writer[512]; - if (file) - { - DebugTrace("Saving Deck: " << deckTitle << " to " << destFileName ); - if (meta_name.size()) - { - file << "#NAME:" << deckTitle << '\n'; - } - - if (meta_desc.size()) - { - size_t found = 0; - string desc = deckDesc; - found = desc.find_first_of("\n"); - while (found != string::npos) - { - file << "#DESC:" << desc.substr(0, found + 1); - desc = desc.substr(found + 1); - found = desc.find_first_of("\n"); - } - file << "#DESC:" << desc << "\n"; - } - - if (useExpandedDescriptions) - { - map::iterator it; - for (it = cards.begin(); it != cards.end(); it++) - { - int nbCards = it->second; - MTGCard *card = this->getCardById(it->first); - if (card == NULL) - { - continue; - } - MTGSetInfo *setInfo = setlist.getInfo(card->setId); - string setName = setInfo->id; - string cardName = card->data->getName(); - file << cardName << "\t " << "(" << setName << ") *" << nbCards << endl; - setInfo = NULL; - } - } - else - { - map::iterator it; - for (it = cards.begin(); it != cards.end(); it++) - { - sprintf(writer, "%i\n", it->first); - for (int j = 0; j < it->second; j++) - { - file << writer; - } - } - } - file.close(); - std::remove(destFileName.c_str()); - rename(tmp.c_str(), destFileName.c_str()); - } - DeckMetaDataList::decksMetaData->invalidate(destFileName); - return 1; -} - -//MTGSets -MTGSets setlist; //Our global. - -MTGSets::MTGSets() -{ -} - -MTGSets::~MTGSets() -{ - for (size_t i = 0; i < setinfo.size(); ++i) - { - delete (setinfo[i]); - } -} - -MTGSetInfo* MTGSets::getInfo(int setID) -{ - if (setID < 0 || setID >= (int) setinfo.size()) - return NULL; - - return setinfo[setID]; -} - -MTGSetInfo* MTGSets::randomSet(int blockId, int atleast) -{ - char * unlocked = (char *) calloc(size(), sizeof(char)); - int attempts = 50; - //Figure out which sets are available. - for (int i = 0; i < size(); i++) - { - unlocked[i] = options[Options::optionSet(i)].number; - } - //No luck randomly. Now iterate from a random location. - int a = 0, iter = 0; - while (iter < 3) - { - a = rand() % size(); - for (int i = a; i < size(); i++) - { - if (unlocked[i] && (blockId == -1 || setinfo[i]->block == blockId) && (atleast == -1 || setinfo[i]->totalCards() - >= atleast)) - { - free(unlocked); - return setinfo[i]; - } - } - for (int i = 0; i < a; i++) - { - if (unlocked[i] && (blockId == -1 || setinfo[i]->block == blockId) && (atleast == -1 || setinfo[i]->totalCards() - >= atleast)) - { - free(unlocked); - return setinfo[i]; - } - } - blockId = -1; - iter++; - if (iter == 2) - atleast = -1; - } - free(unlocked); - return NULL; -} -int blockSize(int blockId); - -int MTGSets::Add(const char * name) -{ - int setid = findSet(name); - if (setid != -1) - return setid; - - MTGSetInfo* s = NEW MTGSetInfo(name); - setinfo.push_back(s); - setid = (int) setinfo.size(); - - return setid - 1; -} - -int MTGSets::findSet(string name) -{ - std::transform(name.begin(), name.end(), name.begin(), ::tolower); - - for (int i = 0; i < (int) setinfo.size(); i++) - { - MTGSetInfo* s = setinfo[i]; - if (!s) - continue; - string set = s->id; - std::transform(set.begin(), set.end(), set.begin(), ::tolower); - if (set.compare(name) == 0) - return i; - } - return -1; -} - -int MTGSets::findBlock(string s) -{ - if (!s.size()) - return -1; - - string comp = s; - std::transform(comp.begin(), comp.end(), comp.begin(), ::tolower); - for (int i = 0; i < (int) blocks.size(); i++) - { - string b = blocks[i]; - std::transform(b.begin(), b.end(), b.begin(), ::tolower); - if (b.compare(comp) == 0) - return i; - } - - blocks.push_back(s); - return ((int) blocks.size()) - 1; -} - -int MTGSets::operator[](string id) -{ - return findSet(id); -} -string MTGSets::operator[](int id) -{ - if (id < 0 || id >= (int) setinfo.size()) - return ""; - - MTGSetInfo * si = setinfo[id]; - if (!si) - return ""; - - return si->id; -} -int MTGSets::getSetNum(MTGSetInfo*i) -{ - int it; - for (it = 0; it < size(); it++) - { - if (setinfo[it] == i) - return it; - } - return -1; -} -int MTGSets::size() -{ - return (int) setinfo.size(); -} - -//MTGSetInfo -MTGSetInfo::~MTGSetInfo() -{ - SAFE_DELETE(mPack); -} -MTGSetInfo::MTGSetInfo(string _id) -{ - string whitespaces(" \t\f\v\n\r"); - id = _id; - block = -1; - year = -1; - - for (int i = 0; i < MTGSetInfo::MAX_COUNT; i++) - counts[i] = 0; - - char myFilename[4096]; - sprintf(myFilename, JGE_GET_RES("sets/%s/booster.txt").c_str(), id.c_str()); - mPack = NEW MTGPack(myFilename); - if (!mPack->isValid()) - { - SAFE_DELETE(mPack); - } - bZipped = false; - bThemeZipped = false; -} - -void MTGSetInfo::count(MTGCard*c) -{ - if (!c) - return; - - switch (c->getRarity()) - { - case Constants::RARITY_M: - counts[MTGSetInfo::MYTHIC]++; - break; - case Constants::RARITY_R: - counts[MTGSetInfo::RARE]++; - break; - case Constants::RARITY_U: - counts[MTGSetInfo::UNCOMMON]++; - break; - case Constants::RARITY_C: - counts[MTGSetInfo::COMMON]++; - break; - default: - case Constants::RARITY_L: - counts[MTGSetInfo::LAND]++; - break; - } - - counts[MTGSetInfo::TOTAL_CARDS]++; -} - -int MTGSetInfo::totalCards() -{ - return counts[MTGSetInfo::TOTAL_CARDS]; -} - -string MTGSetInfo::getName() -{ - if (name.size()) - return name; //Pretty name is translated when rendering. - return id; //Ugly name as well. -} -string MTGSetInfo::getBlock() -{ - if (block < 0 || block >= (int) setlist.blocks.size()) - return "None"; - - return setlist.blocks[block]; -} -void MTGSetInfo::processConfLine(string line) -{ - size_t i = line.find_first_of("="); - if (i == string::npos) - return; - - string key = line.substr(0, i); - std::transform(key.begin(), key.end(), key.begin(), ::tolower); - string value = line.substr(i + 1); - - if (key.compare("name") == 0) - name = value; - else if (key.compare("author") == 0) - author = value; - else if (key.compare("block") == 0) - block = setlist.findBlock(value.c_str()); - else if (key.compare("year") == 0) - year = atoi(value.c_str()); -} +#include "PrecompiledHeader.h" + +#include "MTGDeck.h" +#include "utils.h" +#include "Subtypes.h" +#include "Translate.h" +#include "DeckMetaData.h" +#include "PriceList.h" +#include "WDataSrc.h" +#include "MTGPack.h" +#include "utils.h" + +#if defined (WIN32) || defined (LINUX) +#include +#endif + +static inline int getGrade(int v) +{ + switch (v) + { + case 'P': + case 'p': + return Constants::GRADE_SUPPORTED; + case 'R': + case 'r': + return Constants::GRADE_BORDERLINE; + case 'O': + case 'o': + return Constants::GRADE_UNOFFICIAL; + case 'A': + case 'a': + return Constants::GRADE_CRAPPY; + case 'S': + case 's': + return Constants::GRADE_UNSUPPORTED; + case 'N': + case 'n': + return Constants::GRADE_DANGEROUS; + } + return 0; +} + +//MTGAllCards +int MTGAllCards::processConfLine(string &s, MTGCard *card, CardPrimitive * primitive) +{ + if ('#' == s[0]) return 0; + size_t i = s.find_first_of('='); + if (i == string::npos || 0 == i) + { + DebugTrace("MTGDECK: Bad Line:\n\t" << s); + return 0; + } + + char* key = const_cast (s.c_str()); // I know what I'm doing, let me do it + key[i] = 0; + char* val = key + i + 1; + + switch (key[0]) + { + case 'a': + if (0 == strcmp("auto", key)) + { + if (!primitive) primitive = NEW CardPrimitive(); + primitive->addMagicText(val); + } + else if (0 == strncmp("auto", key, 4)) + { + if (!primitive) primitive = NEW CardPrimitive(); + primitive->addMagicText(val, key + 4); + } + else if (0 == strcmp("alias", key)) + { + if (!primitive) primitive = NEW CardPrimitive(); + primitive->alias = atoi(val); + } + else if (0 == strcmp("abilities", key)) + { + if (!primitive) primitive = NEW CardPrimitive(); + string value = val; + //Specific Abilities + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + while (value.size()) + { + string attribute; + size_t found2 = value.find(','); + if (found2 != string::npos) + { + attribute = value.substr(0, found2); + value = value.substr(found2 + 1); + } + else + { + attribute = value; + value = ""; + } + for (int j = Constants::NB_BASIC_ABILITIES - 1; j >= 0; --j) + { + size_t found = attribute.find(Constants::MTGBasicAbilities[j]); + if (found != string::npos) + { + primitive->basicAbilities[j] = 1; + break; + } + } + } + } + break; + + case 'c': //color + if (!primitive) primitive = NEW CardPrimitive(); + { + string value = val; + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + vector values = split(value, ','); + int removeAllOthers = 1; + for (size_t values_i = 0; values_i < values.size(); ++values_i) + { + primitive->setColor(values[values_i], removeAllOthers); + removeAllOthers = 0; + } + } + break; + + case 'g': //grade + if (s.size() - i - 1 > 2) currentGrade = getGrade(val[2]); + break; + + case 'k': //kicker + if (!primitive) primitive = NEW CardPrimitive(); + if (ManaCost * cost = primitive->getManaCost()) + { + string value = val; + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + cost->kicker = ManaCost::parseManaCost(value); + } + break; + + case 'o': //othercost + if (!primitive) primitive = NEW CardPrimitive(); + if (ManaCost * cost = primitive->getManaCost()) + { + string value = val; + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + cost->alternative = ManaCost::parseManaCost(value); + } + break; + + case 'b': //buyback + if (!primitive) primitive = NEW CardPrimitive(); + if (ManaCost * cost = primitive->getManaCost()) + { + string value = val; + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + cost->BuyBack = ManaCost::parseManaCost(value); + } + break; + case 'f': //flashback + if (!primitive) primitive = NEW CardPrimitive(); + if (ManaCost * cost = primitive->getManaCost()) + { + string value = val; + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + cost->FlashBack = ManaCost::parseManaCost(value); + } + break; + + case 'i': //id + if (!card) card = NEW MTGCard(); + card->setMTGId(atoi(val)); + break; + + case 'm': //mana + if (!primitive) primitive = NEW CardPrimitive(); + { + string value = val; + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + primitive->setManaCost(value); + } + break; + + case 'n': //name + if (!primitive) primitive = NEW CardPrimitive(); + if (0 == strcmp("Bloodrock Cyclops", val)) cout << "val" << endl; + primitive->setName(val); + break; + + case 'p': + if ('r' == key[1]) + { // primitive + if (!card) card = NEW MTGCard(); + map::iterator it = primitives.find(val); + if (it != primitives.end()) card->setPrimitive(it->second); + } + else + { //power + if (!primitive) primitive = NEW CardPrimitive(); + primitive->setPower(atoi(val)); + } + break; + + case 'r': //retrace/rarity + if ('e' == key[1]) + { //retrace + if (!primitive) primitive = NEW CardPrimitive(); + if (ManaCost * cost = primitive->getManaCost()) + { + string value = val; + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + cost->Retrace = ManaCost::parseManaCost(value); + } + } + else + {//rarity + if (!card) card = NEW MTGCard(); + card->setRarity(val[0]); + } + break; + + case 's': //subtype + if (!primitive) primitive = NEW CardPrimitive(); + while (true) + { + char* found = strchr(val, ' '); + if (found) + { + string value(val, found - val); + primitive->setSubtype(value); + val = found + 1; + } + else + { + primitive->setSubtype(val); + break; + } + } + break; + + case 't': + if (!primitive) primitive = NEW CardPrimitive(); + if (0 == strcmp("target", key)) + { + string value = val; + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + primitive->spellTargetType = value; + } + else if (0 == strcmp("text", key)) + primitive->setText(val); + else if (0 == strcmp("type", key)) + { + while (true) + { + char* found = strchr(val, ' '); + if (found) + { + string value(val, found - val); + primitive->setType(value); + val = found + 1; + } + else + { + primitive->setType(val); + break; + } + } + } + else if (0 == strcmp("toughness", key)) primitive->setToughness(atoi(val)); + break; + + default: + DebugTrace( endl << "MTGDECK Parsing Error: " << " [" << primitive->getName() << "]" << s << std::endl); + break; + } + + tempPrimitive = primitive; + tempCard = card; + + return i; + +} + +void MTGAllCards::initCounters() +{ + for (int i = 0; i < Constants::MTG_NB_COLORS; i++) + { + colorsCount[i] = NULL; + } +} + +void MTGAllCards::init() +{ + tempCard = NULL; + tempPrimitive = NULL; + total_cards = 0; + initCounters(); +} + +int MTGAllCards::load(const char * config_file, const char * set_name, int autoload) +{ + conf_read_mode = 0; + const int set_id = set_name ? setlist.Add(set_name) : MTGSets::INTERNAL_SET; + MTGSetInfo *si = setlist.getInfo(set_id); + + std::ifstream setFile(config_file, ios::in | ios::ate); + if (!setFile) return total_cards; + + streampos fileSize = setFile.tellg(); + setFile.seekg(0, ios::beg); + + std::string contents; + contents.resize((std::string::size_type) fileSize); + setFile.read(&contents[0], fileSize); + std::stringstream stream(contents); + + string s; + + while (true) + { + if (!std::getline(stream, s)) return total_cards; + if (!s.size()) continue; + + if (s[s.size() - 1] == '\r') s.erase(s.size() - 1); // Handle DOS files + switch (conf_read_mode) + { + case MTGAllCards::READ_ANYTHING: + if (s[0] == '[') + { + currentGrade = Constants::GRADE_SUPPORTED; // Default value + conf_read_mode = ('m' == s[1]) ? MTGAllCards::READ_METADATA : MTGAllCards::READ_CARD; // M for metadata. + } + else + { + //Global grade for file, to avoid reading the entire file if unnnecessary + if (s[0] == 'g' && s.size() > 8) + { + int fileGrade = getGrade(s[8]); + int maxGrade = options[Options::MAX_GRADE].number; + if (!maxGrade) maxGrade = Constants::GRADE_BORDERLINE; //Default setting for grade is borderline? + if (fileGrade > maxGrade) + { + return total_cards; + } + } + } + continue; + case MTGAllCards::READ_METADATA: + if (s[0] == '[' && s[1] == '/') + conf_read_mode = MTGAllCards::READ_ANYTHING; + else if (si) si->processConfLine(s); + continue; + case MTGAllCards::READ_CARD: + if (s[0] == '[' && s[1] == '/') + { + conf_read_mode = MTGAllCards::READ_ANYTHING; + if (tempPrimitive) tempPrimitive = addPrimitive(tempPrimitive, tempCard); + if (tempCard) + { + if (tempPrimitive) tempCard->setPrimitive(tempPrimitive); + addCardToCollection(tempCard, set_id); + } + tempCard = NULL; + tempPrimitive = NULL; + } + else + { + processConfLine(s, tempCard, tempPrimitive); + } + continue; + } + } + return total_cards; +} + +MTGAllCards::MTGAllCards() +{ + init(); +} + +MTGAllCards::~MTGAllCards() +{ + //Why don't we call destroyAllCards from here ??? +} + +void MTGAllCards::destroyAllCards() +{ + + for (map::iterator it = collection.begin(); it != collection.end(); it++) + delete (it->second); + collection.clear(); + ids.clear(); + + for (map::iterator it = primitives.begin(); it != primitives.end(); it++) + delete (it->second); + primitives.clear(); + +} + +MTGAllCards::MTGAllCards(const char * config_file, const char * set_name) +{ + init(); + load(config_file, set_name, 0); +} + +int MTGAllCards::randomCardId() +{ + int id = (rand() % ids.size()); + return ids[id]; +} + +int MTGAllCards::countBySet(int setId) +{ + int result = 0; + map::iterator it; + + for (it = collection.begin(); it != collection.end(); it++) + { + MTGCard * c = it->second; + if (c->setId == setId) + { + result++; + } + } + return result; +} + +//TODO more efficient way ? +int MTGAllCards::countByType(const char * _type) +{ + int result = 0; + map::iterator it; + for (it = collection.begin(); it != collection.end(); it++) + { + MTGCard * c = it->second; + if (c->data->hasType(_type)) + { + result++; + } + } + return result; +} + +int MTGAllCards::countByColor(int color) +{ + if (colorsCount[color] == 0) + { + for (int i = 0; i < Constants::MTG_NB_COLORS; i++) + { + colorsCount[i] = 0; + } + map::iterator it; + for (it = collection.begin(); it != collection.end(); it++) + { + MTGCard * c = it->second; + int j = c->data->getColor(); + + colorsCount[j]++; + } + } + return colorsCount[color]; +} + +int MTGAllCards::totalCards() +{ + return (total_cards); +} + +bool MTGAllCards::addCardToCollection(MTGCard * card, int setId) +{ + card->setId = setId; + int newId = card->getId(); + if (collection.find(newId) != collection.end()) + { +#if defined (_DEBUG) + string cardName = card->data ? card->data->name : card->getImageName(); + string setName = setId != -1 ? setlist.getInfo(setId)->getName() : ""; + DebugTrace("warning, card id collision! : " << newId << " -> " << cardName << "(" << setName << ")"); +#endif + SAFE_DELETE(card); + return false; + } + + //Don't add cards that don't have a primitive + if (!card->data) + { + SAFE_DELETE(card); + return false; + } + ids.push_back(newId); + + collection[newId] = card; //Push card into collection. + MTGSetInfo * si = setlist.getInfo(setId); + if (si) si->count(card); //Count card in set info + ++total_cards; + return true; +} + +CardPrimitive * MTGAllCards::addPrimitive(CardPrimitive * primitive, MTGCard * card) +{ + int maxGrade = options[Options::MAX_GRADE].number; + if (!maxGrade) maxGrade = Constants::GRADE_BORDERLINE; //Default setting for grade is borderline? + if (currentGrade > maxGrade) + { + SAFE_DELETE(primitive); + return NULL; + } + string key; + if (card) + { + std::stringstream ss; + ss << card->getId(); + ss >> key; + } + else + key = primitive->name; + if (primitives.find(key) != primitives.end()) + { + //ERROR + //Todo move the deletion somewhere else ? + DebugTrace("MTGDECK: primitives conflict: "<< key); + SAFE_DELETE(primitive); + return NULL; + } + //translate cards text + Translator * t = Translator::GetInstance(); + map::iterator it = t->tempValues.find(primitive->name); + if (it != t->tempValues.end()) + { + primitive->setText(it->second); + } + + //Legacy: + //For the Deck editor, we need Lands and Artifact to be colors... + if (primitive->hasType(Subtypes::TYPE_LAND)) primitive->setColor(Constants::MTG_COLOR_LAND); + if (primitive->hasType(Subtypes::TYPE_ARTIFACT)) primitive->setColor(Constants::MTG_COLOR_ARTIFACT); + + primitives[key] = primitive; + return primitive; +} + +MTGCard * MTGAllCards::getCardById(int id) +{ + map::iterator it = collection.find(id); + if (it != collection.end()) + { + return (it->second); + } + return 0; +} + +MTGCard * MTGAllCards::_(int index) +{ + if (index >= total_cards) return NULL; + return getCardById(ids[index]); +} + +MTGCard * MTGAllCards::getCardByName(string name) +{ + if (!name.size()) return NULL; + if (name[0] == '#') return NULL; + + int cardnb = atoi(name.c_str()); + if (cardnb) + { + return getCardById(cardnb); + } + + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + int setId = -1; + size_t found = name.find(" ("); + if (found != string::npos) + { + size_t end = name.find(")"); + string setName = name.substr(found + 2, end - found - 2); + trim(setName); + name = name.substr(0, found); + trim(name); + setId = setlist[setName]; + } + map::iterator it; + for (it = collection.begin(); it != collection.end(); it++) + { + MTGCard * c = it->second; + if (setId != -1 && setId != c->setId) continue; + string cardName = c->data->name; + std::transform(cardName.begin(), cardName.end(), cardName.begin(), ::tolower); + if (cardName.compare(name) == 0) return c; + + } + return NULL; +} + +//MTGDeck +MTGDeck::MTGDeck(MTGAllCards * _allcards) +{ + total_cards = 0; + database = _allcards; + filename = ""; + meta_name = ""; +} + +int MTGDeck::totalPrice() +{ + int total = 0; + PriceList * pricelist = NEW PriceList(JGE_GET_RES("settings/prices.dat").c_str(), GameApp::collection); + map::iterator it; + for (it = cards.begin(); it != cards.end(); it++) + { + int nb = it->second; + if (nb) total += pricelist->getPrice(it->first); + } + SAFE_DELETE(pricelist); + return total; +} + +MTGDeck::MTGDeck(const char * config_file, MTGAllCards * _allcards, int meta_only) +{ + total_cards = 0; + database = _allcards; + filename = config_file; + size_t slash = filename.find_last_of("/"); + size_t dot = filename.find("."); + meta_name = filename.substr(slash + 1, dot - slash - 1); + std::ifstream file(config_file); + 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] == '#') + { + size_t found = s.find("NAME:"); + if (found != string::npos) + { + meta_name = s.substr(found + 5); + continue; + } + found = s.find("DESC:"); + if (found != string::npos) + { + if (meta_desc.size()) meta_desc.append("\n"); + meta_desc.append(s.substr(found + 5)); + continue; + } + continue; + } + if (meta_only) break; + int cardnb = atoi(s.c_str()); + if (cardnb) + { + add(cardnb); + } + else + { + int nb = 1; + size_t found = s.find(" *"); + if (found != string::npos) + { + nb = atoi(s.substr(found + 2).c_str()); + s = s.substr(0, found); + } + MTGCard * card = database->getCardByName(s); + if (card) + { + for (int i = 0; i < nb; i++) + { + add(card); + } + } + else + { + DebugTrace("could not find Card matching name: " << s); + } + } + } + file.close(); + } + else + { + //TODO Error management + } +} + +int MTGDeck::totalCards() +{ + return total_cards; +} + +MTGCard * MTGDeck::getCardById(int mtgId) +{ + return database->getCardById(mtgId); +} + +int MTGDeck::addRandomCards(int howmany, int * setIds, int nbSets, int rarity, const char * _subtype, int * colors, int nbcolors) +{ + if (howmany <= 0) return 1; + + int unallowedColors[Constants::MTG_NB_COLORS + 1]; + for (int i = 0; i < Constants::MTG_NB_COLORS; ++i) + { + if (nbcolors) + unallowedColors[i] = 1; + else + unallowedColors[i] = 0; + } + for (int i = 0; i < nbcolors; ++i) + { + unallowedColors[colors[i]] = 0; + } + + int collectionTotal = database->totalCards(); + if (!collectionTotal) return 0; + + char subtype[4096]; + if (_subtype) sprintf(subtype, "%s", _subtype); + + vector subcollection; + int subtotal = 0; + for (int i = 0; i < collectionTotal; i++) + { + MTGCard * card = database->_(i); + int r = card->getRarity(); + if (r != Constants::RARITY_T && (rarity == -1 || r == rarity) && // remove tokens + card->setId != MTGSets::INTERNAL_SET && //remove cards that are defined in primitives. Those are workarounds (usually tokens) and should only be used internally + (!_subtype || card->data->hasSubtype(subtype))) + { + int ok = 0; + + if (!nbSets) ok = 1; + for (int j = 0; j < nbSets; ++j) + { + if (card->setId == setIds[j]) + { + ok = 1; + break; + } + } + + if (ok) + { + for (int j = 0; j < Constants::MTG_NB_COLORS; ++j) + { + if (unallowedColors[j] && card->data->hasColor(j)) + { + ok = 0; + break; + } + } + } + + if (ok) + { + subcollection.push_back(card->getId()); + subtotal++; + } + } + } + if (subtotal == 0) + { + if (rarity == Constants::RARITY_M) return addRandomCards(howmany, setIds, nbSets, Constants::RARITY_R, _subtype, colors, + nbcolors); + return 0; + } + for (int i = 0; i < howmany; i++) + { + int id = (rand() % subtotal); + add(subcollection[id]); + } + return 1; +} + +int MTGDeck::add(MTGDeck * deck) +{ + map::iterator it; + for (it = deck->cards.begin(); it != deck->cards.end(); it++) + { + for (int i = 0; i < it->second; i++) + { + add(it->first); + } + } + return deck->totalCards(); +} + +int MTGDeck::add(int cardid) +{ + if (!database->getCardById(cardid)) return 0; + if (cards.find(cardid) == cards.end()) + { + cards[cardid] = 1; + } + else + { + cards[cardid]++; + } + ++total_cards; + //initCounters(); + return total_cards; +} + +int MTGDeck::add(MTGCard * card) +{ + if (!card) return 0; + return (add(card->getId())); +} + +int MTGDeck::complete() +{ + /* (PSY) adds cards to the deck/collection. Makes sure that the deck + or collection has at least 4 of every implemented card. Does not + change the number of cards of which already 4 or more are present. */ + int id, n; + bool StypeIsNothing; + size_t databaseSize = database->ids.size(); + for (size_t it = 0; it < databaseSize; it++) + { + id = database->ids[it]; + StypeIsNothing = false; + if (database->getCardById(id)->data->hasType("nothing")) + { + StypeIsNothing = true; + } + if (!StypeIsNothing == true) + { + if (cards.find(id) == cards.end()) + { + cards[id] = 4; + total_cards += 4; + } + else + { + n = cards[id]; + if (n < 4) + { + total_cards += 4 - n; + cards[id] = 4; + } + } + } + } + return 1; +} + +int MTGDeck::removeAll() +{ + total_cards = 0; + cards.clear(); + //initCounters(); + return 1; +} + +int MTGDeck::remove(int cardid) +{ + if (cards.find(cardid) == cards.end() || cards[cardid] == 0) return 0; + cards[cardid]--; + total_cards--; + //initCounters(); + return 1; +} + +int MTGDeck::remove(MTGCard * card) +{ + if (!card) return 0; + return (remove(card->getId())); +} + +int MTGDeck::save() +{ + return save(filename, false, meta_name, meta_desc); +} + +int MTGDeck::save(string destFileName, bool useExpandedDescriptions, string &deckTitle, string &deckDesc) +{ + string tmp = destFileName; + tmp.append(".tmp"); //not thread safe + std::ofstream file(tmp.c_str()); + char writer[512]; + if (file) + { + DebugTrace("Saving Deck: " << deckTitle << " to " << destFileName ); + if (meta_name.size()) + { + file << "#NAME:" << deckTitle << '\n'; + } + + if (meta_desc.size()) + { + size_t found = 0; + string desc = deckDesc; + found = desc.find_first_of("\n"); + while (found != string::npos) + { + file << "#DESC:" << desc.substr(0, found + 1); + desc = desc.substr(found + 1); + found = desc.find_first_of("\n"); + } + file << "#DESC:" << desc << "\n"; + } + + if (useExpandedDescriptions) + { + map::iterator it; + for (it = cards.begin(); it != cards.end(); it++) + { + int nbCards = it->second; + MTGCard *card = this->getCardById(it->first); + if (card == NULL) + { + continue; + } + MTGSetInfo *setInfo = setlist.getInfo(card->setId); + string setName = setInfo->id; + string cardName = card->data->getName(); + file << cardName << "\t " << "(" << setName << ") *" << nbCards << endl; + setInfo = NULL; + } + } + else + { + map::iterator it; + for (it = cards.begin(); it != cards.end(); it++) + { + sprintf(writer, "%i\n", it->first); + for (int j = 0; j < it->second; j++) + { + file << writer; + } + } + } + file.close(); + std::remove(destFileName.c_str()); + rename(tmp.c_str(), destFileName.c_str()); + } + DeckMetaDataList::decksMetaData->invalidate(destFileName); + return 1; +} + +//MTGSets +MTGSets setlist; //Our global. + +MTGSets::MTGSets() +{ +} + +MTGSets::~MTGSets() +{ + for (size_t i = 0; i < setinfo.size(); ++i) + { + delete (setinfo[i]); + } +} + +MTGSetInfo* MTGSets::getInfo(int setID) +{ + if (setID < 0 || setID >= (int) setinfo.size()) return NULL; + + return setinfo[setID]; +} + +MTGSetInfo* MTGSets::randomSet(int blockId, int atleast) +{ + char * unlocked = (char *) calloc(size(), sizeof(char)); + int attempts = 50; + //Figure out which sets are available. + for (int i = 0; i < size(); i++) + { + unlocked[i] = options[Options::optionSet(i)].number; + } + //No luck randomly. Now iterate from a random location. + int a = 0, iter = 0; + while (iter < 3) + { + a = rand() % size(); + for (int i = a; i < size(); i++) + { + if (unlocked[i] && (blockId == -1 || setinfo[i]->block == blockId) && + (atleast == -1 || setinfo[i]->totalCards() >= atleast)) + { + free(unlocked); + return setinfo[i]; + } + } + for (int i = 0; i < a; i++) + { + if (unlocked[i] && (blockId == -1 || setinfo[i]->block == blockId) && + (atleast == -1 || setinfo[i]->totalCards() >= atleast)) + { + free(unlocked); + return setinfo[i]; + } + } + blockId = -1; + iter++; + if (iter == 2) atleast = -1; + } + free(unlocked); + return NULL; +} + +int blockSize(int blockId); + +int MTGSets::Add(const char * name) +{ + int setid = findSet(name); + if (setid != -1) return setid; + + MTGSetInfo* s = NEW MTGSetInfo(name); + setinfo.push_back(s); + setid = (int) setinfo.size(); + + return setid - 1; +} + +int MTGSets::findSet(string name) +{ + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + + for (int i = 0; i < (int) setinfo.size(); i++) + { + MTGSetInfo* s = setinfo[i]; + if (!s) continue; + string set = s->id; + std::transform(set.begin(), set.end(), set.begin(), ::tolower); + if (set.compare(name) == 0) return i; + } + return -1; +} + +int MTGSets::findBlock(string s) +{ + if (!s.size()) return -1; + + string comp = s; + std::transform(comp.begin(), comp.end(), comp.begin(), ::tolower); + for (int i = 0; i < (int) blocks.size(); i++) + { + string b = blocks[i]; + std::transform(b.begin(), b.end(), b.begin(), ::tolower); + if (b.compare(comp) == 0) return i; + } + + blocks.push_back(s); + return ((int) blocks.size()) - 1; +} + +int MTGSets::operator[](string id) +{ + return findSet(id); +} + +string MTGSets::operator[](int id) +{ + if (id < 0 || id >= (int) setinfo.size()) return ""; + + MTGSetInfo * si = setinfo[id]; + if (!si) return ""; + + return si->id; +} + +int MTGSets::getSetNum(MTGSetInfo*i) +{ + int it; + for (it = 0; it < size(); it++) + { + if (setinfo[it] == i) return it; + } + return -1; +} +int MTGSets::size() +{ + return (int) setinfo.size(); +} + +//MTGSetInfo +MTGSetInfo::~MTGSetInfo() +{ + SAFE_DELETE(mPack); +} + +MTGSetInfo::MTGSetInfo(string _id) +{ + string whitespaces(" \t\f\v\n\r"); + id = _id; + block = -1; + year = -1; + + for (int i = 0; i < MTGSetInfo::MAX_COUNT; i++) + counts[i] = 0; + + char myFilename[4096]; + sprintf(myFilename, JGE_GET_RES("sets/%s/booster.txt").c_str(), id.c_str()); + mPack = NEW MTGPack(myFilename); + if (!mPack->isValid()) + { + SAFE_DELETE(mPack); + } + bZipped = false; + bThemeZipped = false; +} + +void MTGSetInfo::count(MTGCard*c) +{ + if (!c) return; + + switch (c->getRarity()) + { + case Constants::RARITY_M: + counts[MTGSetInfo::MYTHIC]++; + break; + case Constants::RARITY_R: + counts[MTGSetInfo::RARE]++; + break; + case Constants::RARITY_U: + counts[MTGSetInfo::UNCOMMON]++; + break; + case Constants::RARITY_C: + counts[MTGSetInfo::COMMON]++; + break; + default: + case Constants::RARITY_L: + counts[MTGSetInfo::LAND]++; + break; + } + + counts[MTGSetInfo::TOTAL_CARDS]++; +} + +int MTGSetInfo::totalCards() +{ + return counts[MTGSetInfo::TOTAL_CARDS]; +} + +string MTGSetInfo::getName() +{ + if (name.size()) return name; //Pretty name is translated when rendering. + return id; //Ugly name as well. +} + +string MTGSetInfo::getBlock() +{ + if (block < 0 || block >= (int) setlist.blocks.size()) return "None"; + + return setlist.blocks[block]; +} + +void MTGSetInfo::processConfLine(string line) +{ + size_t i = line.find_first_of("="); + if (i == string::npos) return; + + string key = line.substr(0, i); + std::transform(key.begin(), key.end(), key.begin(), ::tolower); + string value = line.substr(i + 1); + + if (key.compare("name") == 0) + name = value; + else if (key.compare("author") == 0) + author = value; + else if (key.compare("block") == 0) + block = setlist.findBlock(value.c_str()); + else if (key.compare("year") == 0) year = atoi(value.c_str()); +} diff --git a/projects/mtg/src/SimplePopup.cpp b/projects/mtg/src/SimplePopup.cpp new file mode 100644 index 000000000..3c5ffa0ee --- /dev/null +++ b/projects/mtg/src/SimplePopup.cpp @@ -0,0 +1,115 @@ +/* + * SimplePopup.cpp + * + * Created on: Nov 18, 2010 + * Author: Michael + */ + +#include "PrecompiledHeader.h" +#include "SimplePopup.h" +#include "JTypes.h" +#include "GameApp.h" +#include "DeckStats.h" +#include "DeckManager.h" +#include + +SimplePopup::SimplePopup(int id, JGuiListener* listener, const int fontId, const char * _title, DeckMetaData* deckMetaData, MTGAllCards * collection) : + JGuiController(id, listener), mFontId(fontId), mCollection(collection) +{ + mX = 35; + mY = 50; + mTitle = _title; + mMaxLines = 10; + mTextFont = resources.GetWFont(fontId); + this->mCount = 1; + stw = NULL; + Update(deckMetaData); +} + +void SimplePopup::Render() +{ + closed = false; + + JRenderer *r = JRenderer::GetInstance(); + string detailedInformation = getDetailedInformation(mDeckInformation->getFilename()); + + mTextFont->SetScale(0.85f); + const float textWidth = 183.0f; + const float textHeight = mTextFont->GetHeight() * 10; + r->DrawRoundRect(mX, mY, textWidth, textHeight, 2.0f, ARGB( 255, 125, 255, 0) ); + r->FillRoundRect(mX, mY, textWidth, textHeight, 2.0f, ARGB( 255, 0, 0, 0 ) ); + + mTextFont->DrawString(detailedInformation.c_str(), mX + 20 , mY + 10); + +} +void SimplePopup::Update(DeckMetaData* selectedDeck) +{ + mDeckInformation = selectedDeck; + SAFE_DELETE(stw); + stw = NEW StatsWrapper(mDeckInformation->getDeckId()); + stw->updateStats(mDeckInformation->getFilename(), mCollection); +} + + +string SimplePopup::getDetailedInformation(string filename) +{ + ostringstream oss; + oss + << "------- Deck Summary -----" << endl + << "Cards: "<< stw->cardCount << endl + << "Creatures: "<< setw(2) << stw->countCreatures + << " Enchantments: " << stw->countEnchantments << endl + << "Instants: " << setw(4) << stw->countInstants + << " Sorceries: " << setw(2) << stw->countSorceries << endl + << "Lands: " + << "A: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_ARTIFACT ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_ARTIFACT ] << " " + << "G: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_GREEN ] + stw->countLandsPerColor[ Constants::MTG_COLOR_GREEN ] << " " + << "R: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_RED ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_RED ] << " " + << "U: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_BLUE ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_BLUE ] << " " + << "B: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_BLACK ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_BLACK ] << " " + << "W: " << setw(2) << left << stw->countLandsPerColor[ Constants::MTG_COLOR_WHITE ] + stw->countBasicLandsPerColor[ Constants::MTG_COLOR_WHITE ] << endl + << " --- Mana Curve --- " << endl; + + for ( int costIdx = 0; costIdx < 15; ++costIdx ) + if ( stw->countCardsPerCost[ costIdx ] > 0 ) + oss << costIdx << ": " << setw(2) << left << stw->countCardsPerCost[ costIdx ] << " "; + + oss << endl; + + oss + << " --- Average Cost --- " << endl + << "Creature: "<< setprecision(2) << stw->avgCreatureCost << endl + << "Mana: " << setprecision(2) << stw->avgManaCost << " " + << "Spell: " << setprecision(2) << stw->avgSpellCost << endl; + + return oss.str(); +} + + void SimplePopup::Update(float dt) +{ + JButton key = mEngine->ReadButton(); + CheckUserInput(key); +} + +void SimplePopup::Close() +{ + closed = true; + mCount = 0; +} + +SimplePopup::~SimplePopup(void) +{ + mTextFont = NULL; + mDeckInformation = NULL; + SAFE_DELETE(stw); +} + +void SimplePopup::drawHorzPole(float x, float y, float width) +{ + +} + +void SimplePopup::drawVertPole(float x, float y, float height) +{ + +} diff --git a/projects/mtg/template.vcproj b/projects/mtg/template.vcproj index ed9504dfd..c44125b49 100644 --- a/projects/mtg/template.vcproj +++ b/projects/mtg/template.vcproj @@ -837,6 +837,10 @@ RelativePath=".\src\SimplePad.cpp" > + + @@ -1254,6 +1258,10 @@ RelativePath=".\include\Rules.h" > + + @@ -1266,6 +1274,10 @@ RelativePath=".\include\SimplePad.h" > + +