From 5a1e8e6ffe6368431ac8ee66599a18eef48f7f3c Mon Sep 17 00:00:00 2001 From: "wrenczes@gmail.com" Date: Sun, 31 Oct 2010 07:50:53 +0000 Subject: [PATCH] More reorganization work around CardSelector and the singleton pattern. Broke the source for the singleton into its own separate source file, to keep things clean. Also broke apart a circular header dependency: CardSelector defines and uses a SelectorZone, which is a member inside of CardView. CardView in turn is used heavily by CardSelector. Instead SelectorZone is now defined within CardView (where it's set & controlled anyway). I've also added my current work on the zone navigation system (class Navigator) - it's currently turned off for now (the override for this is inside of CardSelectorSingleton's Instance() call, simply comment out the NEW CardSelector and uncomment out the NEW Navigator line.) It's functional, but I want to do more testing before considering wiring it into the game options or something similar. (Also, note that it currently doesn't support the mouse functionality added by DJardin.) Lastly, there's a bug crash fix in ActionStack that I tripped across while testing - basically, an illegal index value would have us walk off the bounds of a vector. --- projects/mtg/Makefile | 4 +- projects/mtg/include/CardGui.h | 13 +- projects/mtg/include/CardSelector.h | 85 +- projects/mtg/include/CardSelectorSingleton.h | 30 + projects/mtg/include/DuelLayers.h | 4 +- projects/mtg/include/GuiAvatars.h | 1 - projects/mtg/include/GuiHand.h | 1 - projects/mtg/include/Navigator.h | 60 ++ projects/mtg/src/ActionStack.cpp | 19 +- projects/mtg/src/CardDisplay.cpp | 5 +- projects/mtg/src/CardGui.cpp | 4 +- projects/mtg/src/CardSelector.cpp | 114 +-- projects/mtg/src/CardSelectorSingleton.cpp | 36 + projects/mtg/src/DuelLayers.cpp | 1 + projects/mtg/src/GuiAvatars.cpp | 1 + projects/mtg/src/GuiCombat.cpp | 1 + projects/mtg/src/GuiHand.cpp | 12 +- projects/mtg/src/GuiPlay.cpp | 10 +- projects/mtg/src/GuiStatic.cpp | 8 +- projects/mtg/src/MTGRules.cpp | 5 +- projects/mtg/src/Navigator.cpp | 773 +++++++++++++++++++ projects/mtg/template.vcxproj | 8 + 22 files changed, 1041 insertions(+), 154 deletions(-) create mode 100644 projects/mtg/include/CardSelectorSingleton.h create mode 100644 projects/mtg/include/Navigator.h create mode 100644 projects/mtg/src/CardSelectorSingleton.cpp create mode 100644 projects/mtg/src/Navigator.cpp diff --git a/projects/mtg/Makefile b/projects/mtg/Makefile index cc69ee93a..2cf036426 100644 --- a/projects/mtg/Makefile +++ b/projects/mtg/Makefile @@ -1,4 +1,4 @@ -OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckMetaData.o objs/DeckStats.o objs/DuelLayers.o objs/Effects.o objs/ExtraCost.o objs/GameApp.o objs/GameLauncher.o objs/GameObserver.o objs/GameOptions.o objs/GameState.o objs/GameStateAwards.o objs/GameStateDeckViewer.o objs/GameStateDuel.o objs/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/Logger.o objs/ManaCost.o objs/ManaCostHybrid.o objs/MenuItem.o objs/MTGAbility.o objs/MTGCardInstance.o objs/MTGCard.o objs/MTGDeck.o objs/MTGDefinitions.o objs/MTGGamePhase.o objs/MTGGameZones.o objs/MTGPack.o objs/MTGRules.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/Pos.o objs/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/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/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/Logger.o objs/ManaCost.o objs/ManaCostHybrid.o objs/MenuItem.o objs/MTGAbility.o objs/MTGCardInstance.o objs/MTGCard.o objs/MTGDeck.o objs/MTGDefinitions.o objs/MTGGamePhase.o objs/MTGGameZones.o objs/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 DEPS = $(patsubst objs/%.o, deps/%.d, $(OBJS)) RESULT = $(shell psp-config --psp-prefix 2> Makefile.cache) @@ -89,7 +89,7 @@ clean: endif -PrecompiledHeader.h.gch: ../../projects/mtg/include/PrecompiledHeader.h +PrecompiledHeader.h.gch: ../../projects/mtg/include/PrecompiledHeader.h $(CXX) -c $(CXXFLAGS) $< $(OBJS): objs/%.o: src/%.cpp PrecompiledHeader.h.gch diff --git a/projects/mtg/include/CardGui.h b/projects/mtg/include/CardGui.h index f030c810a..cc2ae653f 100644 --- a/projects/mtg/include/CardGui.h +++ b/projects/mtg/include/CardGui.h @@ -8,7 +8,6 @@ #include "Pos.h" #include "PlayGuiObject.h" #include "MTGCardInstance.h" -#include "CardSelector.h" class MTGCardInstance; class PlayGuiObject; @@ -37,13 +36,19 @@ struct CardGui : public PlayGuiObject { virtual ostream& toString(ostream&) const; }; + class CardView : public CardGui { public: - const CardSelector::SelectorZone owner; + + typedef enum { + nullZone, handZone, playZone + } SelectorZone; + + const SelectorZone owner; MTGCardInstance* getCard(); // remove this when possible - CardView(const CardSelector::SelectorZone, MTGCardInstance* card, float x, float y); - CardView(const CardSelector::SelectorZone, MTGCardInstance* card, const Pos& ref); + CardView(const SelectorZone, MTGCardInstance* card, float x, float y); + CardView(const SelectorZone, MTGCardInstance* card, const Pos& ref); void Render(){CardGui::Render();}; void Render(JQuad* q){Pos::Render(q);}; virtual ostream& toString(ostream&) const; diff --git a/projects/mtg/include/CardSelector.h b/projects/mtg/include/CardSelector.h index 2351a8d73..4d95d5a04 100644 --- a/projects/mtg/include/CardSelector.h +++ b/projects/mtg/include/CardSelector.h @@ -3,6 +3,7 @@ #include #include +#include "CardGui.h" #include "GuiLayers.h" #include "Pos.h" @@ -11,6 +12,7 @@ using std::vector; class PlayGuiObject; class DuelLayers; + enum { BIG_MODE_SHOW = 0, BIG_MODE_TEXT = 1, @@ -26,38 +28,57 @@ struct LimitorFunctor typedef T Target; }; -template -class ObjectSelector : public GuiLayer +class CardSelectorBase : public GuiLayer +{ +public: + + CardSelectorBase(int inDrawMode) : mDrawMode(inDrawMode) {}; + virtual void Add(PlayGuiObject*) = 0; + virtual void Remove(PlayGuiObject*) = 0; + virtual bool CheckUserInput(JButton key) = 0; + virtual bool CheckUserInput(int x, int y) = 0; + virtual void PushLimitor() = 0; + virtual void PopLimitor() = 0; + virtual void Limit(LimitorFunctor* inLimitor, CardView::SelectorZone inZone) = 0; + virtual void Push() = 0; + virtual void Pop() = 0; + virtual int GetDrawMode() + { + return mDrawMode; + } + +protected: + int mDrawMode; +}; + + +class CardSelector : public CardSelectorBase { public: - typedef enum { - nullZone, handZone, playZone - } SelectorZone; struct SelectorMemory { - T* object; + PlayGuiObject* object; float x, y; - SelectorMemory(T* object) : object(object) { if (object) { x = object->x; y = object->y; } }; - SelectorMemory() { object = NULL; x = y = 0; }; + SelectorMemory(PlayGuiObject* object); + SelectorMemory(); }; protected: - vector cards; - T* active; + vector cards; + PlayGuiObject* active; DuelLayers* duel; - LimitorFunctor* limitor; + LimitorFunctor* limitor; Pos bigpos; - map lasts; - stack< pair*, SelectorZone> > limitorStack; + map lasts; + stack< pair*, CardView::SelectorZone> > limitorStack; stack memoryStack; - T* fetchMemory(SelectorMemory&); + PlayGuiObject* fetchMemory(SelectorMemory&); public: - ObjectSelector(DuelLayers*); - int bigMode; - void Add(T*); - void Remove(T*); + CardSelector(DuelLayers*); + void Add(PlayGuiObject*); + void Remove(PlayGuiObject*); bool CheckUserInput(JButton key); bool CheckUserInput(int x, int y); void Update(float dt); @@ -65,39 +86,15 @@ public: void Push(); void Pop(); - void Limit(LimitorFunctor* limitor, SelectorZone); + void Limit(LimitorFunctor* limitor, CardView::SelectorZone); void PushLimitor(); void PopLimitor(); - typedef T Target; + typedef PlayGuiObject Target; }; -typedef ObjectSelector<> CardSelector; typedef LimitorFunctor Limitor; - -namespace CardSelectorSingleton -{ - /* - ** CardSelector is essentially a singleton in its usage - ** It's not enforced, but it needs to eventually migrate to the real thing - ** For now, this function will fake it out - it's up to the client caller to make sure - ** that this gets destroyed via a Terminate call (this is currently handled in DualLayers's destructor) - */ - CardSelector* Instance(); - - /* - ** Create the singleton pointer. Instance() isn't valid until this is called. - */ - CardSelector* Create(DuelLayers* inDuelLayers); - - /* - ** Teardown the singleton pointer instance. - */ - void Terminate(); -} - - struct Exp { static inline bool test(CardSelector::Target*, CardSelector::Target*); diff --git a/projects/mtg/include/CardSelectorSingleton.h b/projects/mtg/include/CardSelectorSingleton.h new file mode 100644 index 000000000..22c76e0d1 --- /dev/null +++ b/projects/mtg/include/CardSelectorSingleton.h @@ -0,0 +1,30 @@ +#ifndef CARDSELECTORSINGLETON_H +#define CARDSELECTORSINGLETON_H + +#include "CardSelector.h" + +class DuelLayers; + +namespace CardSelectorSingleton +{ + /* + ** CardSelector is essentially a singleton in its usage + ** It's not enforced, but it needs to eventually migrate to the real thing + ** For now, this function will fake it out - it's up to the client caller to make sure + ** that this gets destroyed via a Terminate call (this is currently handled in DualLayers's destructor) + */ + CardSelectorBase* Instance(); + + /* + ** Create the singleton pointer. Instance() isn't valid until this is called. + */ + CardSelectorBase* Create(DuelLayers* inDuelLayers); + + /* + ** Teardown the singleton pointer instance. + */ + void Terminate(); +} + + +#endif //CARDSELECTORSINGLETON_H \ No newline at end of file diff --git a/projects/mtg/include/DuelLayers.h b/projects/mtg/include/DuelLayers.h index 24f349014..5a053d7c9 100644 --- a/projects/mtg/include/DuelLayers.h +++ b/projects/mtg/include/DuelLayers.h @@ -2,7 +2,6 @@ #define _DUELLAYERS_H_ #include "GuiLayers.h" -#include "CardSelector.h" class MTGGuiHand; class MTGGuiPlay; @@ -13,6 +12,7 @@ class GuiHandSelf; class GuiHandOpponent; class GuiCombat; class GuiAvatars; +class CardSelectorBase; struct Pos; class DuelLayers { @@ -43,7 +43,7 @@ public: int receiveEvent(WEvent * e); float RightBoundary(); - CardSelector* mCardSelector; + CardSelectorBase* mCardSelector; }; #include "ActionLayer.h" diff --git a/projects/mtg/include/GuiAvatars.h b/projects/mtg/include/GuiAvatars.h index 770ce173c..2cd73e782 100644 --- a/projects/mtg/include/GuiAvatars.h +++ b/projects/mtg/include/GuiAvatars.h @@ -2,7 +2,6 @@ #define _GUIAVATARS_H_ #include "GuiLayers.h" -#include "CardSelector.h" struct GuiAvatar; class GuiGraveyard; diff --git a/projects/mtg/include/GuiHand.h b/projects/mtg/include/GuiHand.h index 106401830..5f2f4aa55 100644 --- a/projects/mtg/include/GuiHand.h +++ b/projects/mtg/include/GuiHand.h @@ -6,7 +6,6 @@ #include "MTGGameZones.h" #include "CardGui.h" #include "GuiHand.h" -#include "CardSelector.h" class GuiHand; diff --git a/projects/mtg/include/Navigator.h b/projects/mtg/include/Navigator.h new file mode 100644 index 000000000..54e6da981 --- /dev/null +++ b/projects/mtg/include/Navigator.h @@ -0,0 +1,60 @@ +#ifndef NAVIGATOR_H +#define NAVIGATOR_H + +#include "JTypes.h" + +#include "CardSelector.h" +#include "DuelLayers.h" + +#include +#include + + +// private class only used by Navigator, see implementation file +class CardZone; + +class Navigator : public CardSelectorBase +{ +public: + + Navigator(DuelLayers* inDuelLayers); + virtual ~Navigator(); + + // Inherited functions from GuiLayer + bool CheckUserInput(JButton inKey); + bool CheckUserInput(int x, int y); + void Update(float dt); + void Render(); + + //Limitor operations + void PushLimitor(); + void PopLimitor(); + void Limit(LimitorFunctor* inLimitor, CardView::SelectorZone inZone); + + virtual void Add(PlayGuiObject*); + virtual void Remove(PlayGuiObject*); + virtual void Push() {} + virtual void Pop() {} + +protected: + PlayGuiObject* GetCurrentCard(); + + /** + ** Helper function that translates a card type into an internal zone ID (used as the index for the card zone map) + */ + int CardToCardZone(PlayGuiObject* card); + + void HandleKeyStroke(JButton inKey); + +private: + std::map mCardZones; + CardZone* mCurrentZone; + Pos mDrawPosition; + + DuelLayers* mDuelLayers; + + bool mLimitorEnabled; + std::stack mCurrentZoneStack; +}; + +#endif //NAVIGATOR_H diff --git a/projects/mtg/src/ActionStack.cpp b/projects/mtg/src/ActionStack.cpp index 62a591cf0..a2783d6aa 100644 --- a/projects/mtg/src/ActionStack.cpp +++ b/projects/mtg/src/ActionStack.cpp @@ -4,15 +4,16 @@ #include "PrecompiledHeader.h" #include "ActionStack.h" -#include "MTGAbility.h" -#include "GameObserver.h" -#include "Damage.h" -#include "ManaCost.h" -#include "GameOptions.h" -#include "WResourceManager.h" -#include "TargetChooser.h" #include "CardGui.h" +#include "CardSelectorSingleton.h" +#include "Damage.h" +#include "GameObserver.h" +#include "GameOptions.h" +#include "ManaCost.h" +#include "MTGAbility.h" +#include "TargetChooser.h" #include "Translate.h" +#include "WResourceManager.h" #include @@ -83,7 +84,7 @@ void Interruptible::Render(MTGCardInstance * source, JQuad * targetQuad, string } if (bigQuad){ - int showMode = CardSelectorSingleton::Instance()->bigMode; + int showMode = CardSelectorSingleton::Instance()->GetDrawMode(); Pos pos = Pos(CardGui::BigWidth / 2, CardGui::BigHeight / 2 - 10, 1.0, 0.0, 220); switch(showMode){ case BIG_MODE_SHOW: @@ -601,7 +602,7 @@ void ActionStack::Update(float dt){ for (int i = 0; i < mCount ; i++){ Interruptible * current = (Interruptible *)mObjects[i]; if (tc->canTarget(current)){ - if (mObjects[mCurr]) mObjects[mCurr]->Leaving(JGE_BTN_UP); + if (mCurr < (int)mObjects.size() && mObjects[mCurr]) mObjects[mCurr]->Leaving(JGE_BTN_UP); current->display = 1; mCurr = i; mObjects[mCurr]->Entering(); diff --git a/projects/mtg/src/CardDisplay.cpp b/projects/mtg/src/CardDisplay.cpp index 49a3a86af..75bc61423 100644 --- a/projects/mtg/src/CardDisplay.cpp +++ b/projects/mtg/src/CardDisplay.cpp @@ -2,6 +2,7 @@ #include "CardDisplay.h" #include "CardGui.h" +#include "CardSelectorSingleton.h" #include "TargetChooser.h" #include "MTGGameZones.h" #include "GameObserver.h" @@ -27,7 +28,7 @@ CardDisplay::CardDisplay(int id, GameObserver* game, int _x, int _y, JGuiListene } void CardDisplay::AddCard(MTGCardInstance * _card){ - CardGui * card = NEW CardView(CardSelector::nullZone, _card, static_cast(x + 20 + (mCount - start_item) * 30), static_cast(y + 25)); + CardGui * card = NEW CardView(CardView::nullZone, _card, static_cast(x + 20 + (mCount - start_item) * 30), static_cast(y + 25)); Add(card); } @@ -218,7 +219,7 @@ void CardDisplay::Render(){ Pos pos = Pos(CardGui::BigWidth / 2, CardGui::BigHeight / 2 - 10, 1.0, 0.0, 220); int showMode = BIG_MODE_SHOW; if (game){ - showMode = CardSelectorSingleton::Instance()->bigMode; + showMode = CardSelectorSingleton::Instance()->GetDrawMode(); pos.actY = 150; if (x < (CardGui::BigWidth / 2)) pos.actX = SCREEN_WIDTH - 10 - CardGui::BigWidth / 2; } diff --git a/projects/mtg/src/CardGui.cpp b/projects/mtg/src/CardGui.cpp index 0737ea486..6cdd57938 100644 --- a/projects/mtg/src/CardGui.cpp +++ b/projects/mtg/src/CardGui.cpp @@ -38,7 +38,7 @@ namespace CardGui::CardGui(MTGCardInstance* card, float x, float y) : PlayGuiObject(Height, x, y, false), card(card) {} CardGui::CardGui(MTGCardInstance* card, const Pos& ref) : PlayGuiObject(Height, ref, false), card(card) {} -CardView::CardView(const CardSelector::SelectorZone owner, MTGCardInstance* card, float x, float y) : CardGui(card, x, y), owner(owner) { +CardView::CardView(const SelectorZone owner, MTGCardInstance* card, float x, float y) : CardGui(card, x, y), owner(owner) { const Pos* ref = card->view; while (card) { @@ -47,7 +47,7 @@ CardView::CardView(const CardSelector::SelectorZone owner, MTGCardInstance* card } } -CardView::CardView(const CardSelector::SelectorZone owner, MTGCardInstance* card, const Pos& ref) : CardGui(card, ref), owner(owner) { +CardView::CardView(const SelectorZone owner, MTGCardInstance* card, const Pos& ref) : CardGui(card, ref), owner(owner) { const Pos* r = card->view; while (card) { diff --git a/projects/mtg/src/CardSelector.cpp b/projects/mtg/src/CardSelector.cpp index 014cd25d5..47a0d3c5f 100644 --- a/projects/mtg/src/CardSelector.cpp +++ b/projects/mtg/src/CardSelector.cpp @@ -29,10 +29,13 @@ struct Diff : public Exp { static inline bool test(CardSelector::Target* ref, Ca struct True : public Exp { static inline bool test(CardSelector::Target* ref, CardSelector::Target* test) { return true; } }; -template<> -CardSelector::ObjectSelector(DuelLayers* duel) : active(NULL), duel(duel), limitor(NULL), bigpos(300, 150, 1.0, 0.0, 220), bigMode(BIG_MODE_SHOW) {} -template<> +CardSelector::SelectorMemory::SelectorMemory(PlayGuiObject* object) : object(object) { if (object) { x = object->x; y = object->y; } } +CardSelector::SelectorMemory::SelectorMemory() { object = NULL; x = y = 0; } + + +CardSelector::CardSelector(DuelLayers* duel) : CardSelectorBase(BIG_MODE_SHOW), active(NULL), duel(duel), limitor(NULL), bigpos(300, 150, 1.0, 0.0, 220) {} + void CardSelector::Add(CardSelector::Target* target) { if (NULL == active) @@ -44,25 +47,24 @@ void CardSelector::Add(CardSelector::Target* target) if (c) c->zoom = 1.4f; cards.push_back(target); } -template<> + void CardSelector::Remove(CardSelector::Target* card) { for (vector::iterator it = cards.begin(); it != cards.end(); ++it) if (card == *it) + { + if (active == *it) { - if (active == *it) - { - CardView* c = dynamic_cast(active); if (c) c->zoom = 1.0f; - active = closest(cards, limitor, active); - c = dynamic_cast(active); if (c) c->zoom = 1.4f; - } - if (active == *it) active = NULL; - cards.erase(it); - return; + CardView* c = dynamic_cast(active); if (c) c->zoom = 1.0f; + active = closest(cards, limitor, active); + c = dynamic_cast(active); if (c) c->zoom = 1.4f; } + if (active == *it) active = NULL; + cards.erase(it); + return; + } } -template<> CardSelector::Target* CardSelector::fetchMemory(SelectorMemory& memory) { if (NULL == memory.object) return NULL; for (vector::iterator it = cards.begin(); it != cards.end(); ++it) @@ -75,19 +77,19 @@ CardSelector::Target* CardSelector::fetchMemory(SelectorMemory& memory) { // it is there but it is now refused by the limitor. return closest(cards, limitor, memory.x, memory.y); } -template<> + void CardSelector::Push() { memoryStack.push(SelectorMemory(active)); } -template<> + void CardSelector::Pop() { Target* oldactive = active; if (!memoryStack.empty()) { active = fetchMemory(memoryStack.top()); memoryStack.pop(); - SelectorZone oldowner; - if (CardView *q = dynamic_cast(oldactive)) oldowner = q->owner; else oldowner = nullZone; - if (nullZone != oldowner) lasts[oldowner] = SelectorMemory(oldactive); + CardView::SelectorZone oldowner; + if (CardView *q = dynamic_cast(oldactive)) oldowner = q->owner; else oldowner = CardView::nullZone; + if (CardView::nullZone != oldowner) lasts[oldowner] = SelectorMemory(oldactive); } if (active != oldactive) { { CardView* c = dynamic_cast(oldactive); if (c) c->zoom = 1.0f; } //Is this needed, I think it is one in Leaving(0) ? @@ -97,7 +99,6 @@ void CardSelector::Pop() { } } -template<> bool CardSelector::CheckUserInput(JButton key) { if (!active) { @@ -131,8 +132,8 @@ bool CardSelector::CheckUserInput(JButton key) active = closest(cards, limitor, active); break; case JGE_BTN_CANCEL: - bigMode = (bigMode+1) % NB_BIG_MODES; - if(bigMode == BIG_MODE_TEXT) + mDrawMode = (mDrawMode+1) % NB_BIG_MODES; + if(mDrawMode == BIG_MODE_TEXT) options[Options::DISABLECARDS].number = 1; else options[Options::DISABLECARDS].number = 0; @@ -141,11 +142,11 @@ bool CardSelector::CheckUserInput(JButton key) return false; } if (active != oldactive) { - SelectorZone oldowner, owner; - if (CardView *q = dynamic_cast(oldactive)) oldowner = q->owner; else oldowner = nullZone; - if (CardView *q = dynamic_cast(active)) owner = q->owner; else owner = nullZone; + CardView::SelectorZone oldowner, owner; + if (CardView *q = dynamic_cast(oldactive)) oldowner = q->owner; else oldowner = CardView::nullZone; + if (CardView *q = dynamic_cast(active)) owner = q->owner; else owner = CardView::nullZone; if (oldowner != owner) { - if (nullZone != owner) { + if (CardView::nullZone != owner) { if (PlayGuiObject* old = fetchMemory(lasts[owner])) switch (key) { @@ -185,8 +186,6 @@ bool CardSelector::CheckUserInput(JButton key) return true; } - -template<> bool CardSelector::CheckUserInput(int x, int y) { if (!active) { @@ -202,11 +201,11 @@ bool CardSelector::CheckUserInput(int x, int y) active = closest(cards, limitor, x, y); if (active != oldactive) { - SelectorZone oldowner, owner; - if (CardView *q = dynamic_cast(oldactive)) oldowner = q->owner; else oldowner = nullZone; - if (CardView *q = dynamic_cast(active)) owner = q->owner; else owner = nullZone; + CardView::SelectorZone oldowner, owner; + if (CardView *q = dynamic_cast(oldactive)) oldowner = q->owner; else oldowner = CardView::nullZone; + if (CardView *q = dynamic_cast(active)) owner = q->owner; else owner = CardView::nullZone; if (oldowner != owner) { - if (nullZone != owner) { + if (CardView::nullZone != owner) { if (PlayGuiObject* old = fetchMemory(lasts[owner])) if (old) active = old; } @@ -222,25 +221,23 @@ bool CardSelector::CheckUserInput(int x, int y) return true; } -template<> void CardSelector::Update(float dt) { float boundary = duel->RightBoundary(); float position = boundary - CardGui::BigWidth / 2; if (CardView* c = dynamic_cast(active)) if ((c->x + CardGui::Width / 2 > position - CardGui::BigWidth / 2) && - (c->x - CardGui::Width / 2 < position + CardGui::BigWidth / 2)) + (c->x - CardGui::Width / 2 < position + CardGui::BigWidth / 2)) position = CardGui::BigWidth / 2 - 10; if (position < CardGui::BigWidth / 2) position = CardGui::BigWidth / 2; bigpos.x = position; bigpos.Update(dt); } -template<> void CardSelector::Render() { if (active) { active->Render(); if (CardView* c = dynamic_cast(active)) { - switch(bigMode) { + switch(mDrawMode) { case BIG_MODE_SHOW: c->RenderBig(bigpos); break; @@ -254,15 +251,14 @@ void CardSelector::Render() { } } -template<> -void CardSelector::Limit(LimitorFunctor* limitor, SelectorZone destzone) { +void CardSelector::Limit(LimitorFunctor* limitor, CardView::SelectorZone destzone) { this->limitor = limitor; if (limitor && !limitor->select(active)) { - Target* oldactive = active; - SelectorZone oldowner; - if (CardView *q = dynamic_cast(oldactive)) oldowner = q->owner; else oldowner = nullZone; + PlayGuiObject* oldactive = active; + CardView::SelectorZone oldowner; + if (CardView *q = dynamic_cast(oldactive)) oldowner = q->owner; else oldowner = CardView::nullZone; if (oldowner != destzone) { - if (nullZone != destzone) + if (CardView::nullZone != destzone) if (PlayGuiObject* old = fetchMemory(lasts[destzone])) active = old; lasts[oldowner] = SelectorMemory(oldactive); @@ -270,7 +266,7 @@ void CardSelector::Limit(LimitorFunctor* limitor, SelectorZone destzone) if (limitor && !limitor->select(active)) { active = NULL; - for (vector::iterator it = cards.begin(); it != cards.end(); ++it) + for (vector::iterator it = cards.begin(); it != cards.end(); ++it) if (limitor->select(*it)) { active = *it; break; @@ -286,43 +282,15 @@ void CardSelector::Limit(LimitorFunctor* limitor, SelectorZone destzone) } } -template<> void CardSelector::PushLimitor() { if (NULL == limitor) return; - SelectorZone owner; - if (CardView *q = dynamic_cast(active)) owner = q->owner; else owner = nullZone; + CardView::SelectorZone owner; + if (CardView *q = dynamic_cast(active)) owner = q->owner; else owner = CardView::nullZone; limitorStack.push(make_pair(limitor, owner)); } -template<> void CardSelector::PopLimitor() { if (limitorStack.empty()) return; Limit(limitorStack.top().first, limitorStack.top().second); limitorStack.pop(); } - - -namespace CardSelectorSingleton -{ - static CardSelector* sCardSelectorInstance = NULL; - - CardSelector* Create(DuelLayers* inDuelLayers) - { - if (sCardSelectorInstance == NULL) - sCardSelectorInstance = NEW CardSelector(inDuelLayers); - - return sCardSelectorInstance; - } - - CardSelector* Instance() - { - assert(sCardSelectorInstance); - return sCardSelectorInstance; - } - - void Terminate() - { - SAFE_DELETE(sCardSelectorInstance); - sCardSelectorInstance = NULL; - } -} diff --git a/projects/mtg/src/CardSelectorSingleton.cpp b/projects/mtg/src/CardSelectorSingleton.cpp new file mode 100644 index 000000000..2e31abcec --- /dev/null +++ b/projects/mtg/src/CardSelectorSingleton.cpp @@ -0,0 +1,36 @@ +#include "PrecompiledHeader.h" + +#include "CardSelectorSingleton.h" + +#include "DuelLayers.h" +#include "Navigator.h" + +/* +** +*/ +namespace CardSelectorSingleton +{ + static CardSelectorBase* sCardSelectorInstance = NULL; + + CardSelectorBase* Create(DuelLayers* inDuelLayers) + { + if (sCardSelectorInstance == NULL) + sCardSelectorInstance = NEW CardSelector(inDuelLayers); + //sCardSelectorInstance = NEW Navigator(inDuelLayers); + + return sCardSelectorInstance; + } + + CardSelectorBase* Instance() + { + assert(sCardSelectorInstance); + return sCardSelectorInstance; + } + + void Terminate() + { + SAFE_DELETE(sCardSelectorInstance); + sCardSelectorInstance = NULL; + } +} + diff --git a/projects/mtg/src/DuelLayers.cpp b/projects/mtg/src/DuelLayers.cpp index 9445c5eae..81929eedc 100644 --- a/projects/mtg/src/DuelLayers.cpp +++ b/projects/mtg/src/DuelLayers.cpp @@ -1,6 +1,7 @@ #include "PrecompiledHeader.h" #include "MTGRules.h" +#include "CardSelectorSingleton.h" #include "GuiCombat.h" #include "GuiBackground.h" #include "GuiFrame.h" diff --git a/projects/mtg/src/GuiAvatars.cpp b/projects/mtg/src/GuiAvatars.cpp index 2daeb5c46..bc6c02b5b 100644 --- a/projects/mtg/src/GuiAvatars.cpp +++ b/projects/mtg/src/GuiAvatars.cpp @@ -1,5 +1,6 @@ #include "PrecompiledHeader.h" +#include "CardSelectorSingleton.h" #include "GameApp.h" #include "GuiAvatars.h" #include "GameObserver.h" diff --git a/projects/mtg/src/GuiCombat.cpp b/projects/mtg/src/GuiCombat.cpp index eee5c2529..7f0ce332b 100644 --- a/projects/mtg/src/GuiCombat.cpp +++ b/projects/mtg/src/GuiCombat.cpp @@ -5,6 +5,7 @@ #include "AIPlayer.h" #include "GameObserver.h" #include "Trash.h" +#include "CardSelector.h" #include "Closest.cpp" static const float MARGIN = 70; diff --git a/projects/mtg/src/GuiHand.cpp b/projects/mtg/src/GuiHand.cpp index e3110e28f..7434f2499 100644 --- a/projects/mtg/src/GuiHand.cpp +++ b/projects/mtg/src/GuiHand.cpp @@ -1,5 +1,7 @@ #include "PrecompiledHeader.h" +#include "CardSelectorSingleton.h" +#include "CardSelector.h" #include "GameApp.h" #include "Trash.h" #include "GuiHand.h" @@ -136,7 +138,7 @@ bool GuiHandSelf::CheckUserInput(JButton key) { state = (Open == state ? Closed : Open); if (Open == state) CardSelectorSingleton::Instance()->Push(); - CardSelectorSingleton::Instance()->Limit(Open == state ? limitor : NULL, CardSelector::handZone); + CardSelectorSingleton::Instance()->Limit(Open == state ? limitor : NULL, CardView::handZone); if (Closed == state) CardSelectorSingleton::Instance()->Pop(); if (OptionHandDirection::HORIZONTAL == options[Options::HANDDIRECTION].number) backpos.y = Open == state ? OpenY : ClosedY; @@ -212,10 +214,10 @@ int GuiHandSelf::receiveEventPlus(WEvent* e) // We don't want a card in the hand to have an alpha of 0 ev->card->view->alpha = 255; - card = NEW CardView(CardSelector::handZone, ev->card, *(ev->card->view)); + card = NEW CardView(CardView::handZone, ev->card, *(ev->card->view)); } else - card = NEW CardView(CardSelector::handZone, ev->card, ClosedRowX, 0); + card = NEW CardView(CardView::handZone, ev->card, ClosedRowX, 0); card->t = 6*M_PI; cards.push_back(card); CardSelectorSingleton::Instance()->Add(card); @@ -251,9 +253,9 @@ int GuiHandOpponent::receiveEventPlus(WEvent* e) { CardView* card; if (event->card->view) - card = NEW CardView(CardSelector::handZone, event->card, *(event->card->view)); + card = NEW CardView(CardView::handZone, event->card, *(event->card->view)); else - card = NEW CardView(CardSelector::handZone, event->card, ClosedRowX, 0); + card = NEW CardView(CardView::handZone, event->card, ClosedRowX, 0); card->alpha = 255; card->t = -4*M_PI; cards.push_back(card); return 1; diff --git a/projects/mtg/src/GuiPlay.cpp b/projects/mtg/src/GuiPlay.cpp index 9c6d57237..0ac3e4129 100644 --- a/projects/mtg/src/GuiPlay.cpp +++ b/projects/mtg/src/GuiPlay.cpp @@ -1,5 +1,6 @@ #include "PrecompiledHeader.h" +#include "CardSelectorSingleton.h" #include "GameApp.h" #include "GuiPlay.h" #include "Trash.h" @@ -232,15 +233,18 @@ int GuiPlay::receiveEventPlus(WEvent * e) // We don't want a card in the hand to have an alpha of 0 event->card->view->alpha = 255; - card = NEW CardView(CardSelector::playZone, event->card, *(event->card->view)); + card = NEW CardView(CardView::playZone, event->card, *(event->card->view)); } else - card = NEW CardView(CardSelector::playZone, event->card, 0, 0); + card = NEW CardView(CardView::playZone, event->card, 0, 0); cards.push_back(card); card->t = event->card->isTapped() ? M_PI / 2 : 0; card->alpha = 255; - CardSelectorSingleton::Instance()->Add(card); + + // Make sure that the card is repositioned before adding it to the CardSelector, as + // the card's position is a cue for certain CardSelector variants as to what zone the card is placed in Replace(); + CardSelectorSingleton::Instance()->Add(card); return 1; } } diff --git a/projects/mtg/src/GuiStatic.cpp b/projects/mtg/src/GuiStatic.cpp index c95e4acb5..c61fd8957 100644 --- a/projects/mtg/src/GuiStatic.cpp +++ b/projects/mtg/src/GuiStatic.cpp @@ -233,9 +233,9 @@ int GuiGraveyard::receiveEventPlus(WEvent* e) { CardView* t; if (event->card->view) - t = NEW CardView(CardSelector::nullZone, event->card, *(event->card->view)); + t = NEW CardView(CardView::nullZone, event->card, *(event->card->view)); else - t = NEW CardView(CardSelector::nullZone, event->card, x, y); + t = NEW CardView(CardView::nullZone, event->card, x, y); t->x = x + Width / 2; t->y = y + Height / 2; t->zoom = 0.6; t->alpha = 0; cards.push_back(t); return 1; @@ -276,9 +276,9 @@ int GuiOpponentHand::receiveEventPlus(WEvent* e) { CardView* t; if (event->card->view) - t = NEW CardView(CardSelector::nullZone, event->card, *(event->card->view)); + t = NEW CardView(CardView::nullZone, event->card, *(event->card->view)); else - t = NEW CardView(CardSelector::nullZone, event->card, x, y); + t = NEW CardView(CardView::nullZone, event->card, x, y); t->x = x + Width / 2; t->y = y + Height / 2; t->zoom = 0.6; t->alpha = 0; cards.push_back(t); return 1; diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index e3731d89f..3f7424b11 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -1,5 +1,6 @@ #include "PrecompiledHeader.h" +#include "CardSelectorSingleton.h" #include "MTGRules.h" #include "Translate.h" #include "Subtypes.h" @@ -726,9 +727,9 @@ int MTGAttackRule::reactToClick(MTGCardInstance * card){ //Graphically select the next card that can attack if(!card->isAttacker()){ CardSelectorSingleton::Instance()->PushLimitor(); - CardSelectorSingleton::Instance()->Limit(this,CardSelector::playZone); + CardSelectorSingleton::Instance()->Limit(this, CardView::playZone); CardSelectorSingleton::Instance()->CheckUserInput(JGE_BTN_RIGHT); - CardSelectorSingleton::Instance()->Limit(NULL,CardSelector::playZone); + CardSelectorSingleton::Instance()->Limit(NULL, CardView::playZone); CardSelectorSingleton::Instance()->PopLimitor(); } card->toggleAttacker(); diff --git a/projects/mtg/src/Navigator.cpp b/projects/mtg/src/Navigator.cpp new file mode 100644 index 000000000..ff1656221 --- /dev/null +++ b/projects/mtg/src/Navigator.cpp @@ -0,0 +1,773 @@ +#include "PrecompiledHeader.h" + +#include "CardGui.h" +#include "Navigator.h" + +namespace +{ + const Pos kDefaultCardPosition(300, 150, 1.0, 0.0, 220); + + enum + { + kCardZone_Unknown = -1, + kCardZone_PlayerHand = 0, + kCardZone_PlayerAvatar, + kCardZone_PlayerLibrary, + kCardZone_PlayerGraveyard, + kCardZone_PlayerLands, + kCardZone_PlayerCreatures, + kCardZone_PlayerEnchantmentsAndArtifacts, + kCardZone_AIHand, + kCardZone_AIAvatar, + kCardZone_AILibrary, + kCardZone_AIGraveyard, + kCardZone_AILands, + kCardZone_AICreatures, + kCardZone_AIEnchantmentsAndArtifacts + }; +} + +/** +** Helper class to Navigator. Represents a group of cards on the battlefield. +*/ +class CardZone +{ +public: + + /* + ** + */ + CardZone() : mCurrentCard(0) + { + } + + /* + ** + */ + void AddCard(PlayGuiObject* inCard) + { + mCards.push_back(inCard); + inCard->zoom = 1.0f; + } + + /* + ** + */ + void RemoveCard(PlayGuiObject* inCard) + { + bool found = false; + for (size_t index = 0; index < mCards.size(); ++index) + { + if (mCards[index] == inCard) + { + // if mCurrentCard points to a card at the end of an item but we're + // about to delete something earlier in the container, mCurrentCard + // won't be pointing anymore to the same element, so shift it + if (mCurrentCard >= index) + { + if (mCurrentCard > 0) + --mCurrentCard; + } + mCards[index]->zoom = 1.0f; + + mCards.erase(mCards.begin() + index); + found = true; + break; + } + } + + assert(found); + } + + /* + ** Generic handling of navigation - left/right moves through the container, + ** up/down is rejected & moves to the next zone + */ + virtual bool HandleSelection(JButton inKey) + { + bool changeZone = true; + size_t oldIndex = mCurrentCard; + if (inKey == JGE_BTN_LEFT) + { + if (mCurrentCard > 0) + { + --mCurrentCard; + changeZone = false; + } + } + + if (inKey == JGE_BTN_RIGHT) + { + if (mCurrentCard + 1 < mCards.size()) + { + ++mCurrentCard; + changeZone = false; + } + } + + if (oldIndex != mCurrentCard) + { + AnimateSelectionChange(oldIndex, true); + AnimateSelectionChange(mCurrentCard, false); + } + return changeZone; + } + + /* + ** + */ + void AnimateSelectionChange(size_t inIndex, bool inLeaving) + { + if (inIndex < mCards.size()) + { + if (inLeaving) + { + mCards[inIndex]->zoom = 1.0f; + mCards[inIndex]->Leaving(JGE_BTN_NONE); + } + else + { + mCards[inIndex]->zoom = 1.4f; + mCards[inIndex]->Entering(); + } + } + } + + /* + ** Return a neighbour CardZone (ie where to navigate to given a direction) + ** If there is no neighbour in that direction, return self (this ensures that + ** the parent Navigator class always has a legal currentZone pointer) + */ + CardZone* GetNeighbour(JButton inDirection) + { + CardZone* neighbour = this; + if (mNeighbours[inDirection]) + neighbour = mNeighbours[inDirection]; + + return neighbour; + } + + /* + ** When a zone change occurs, this will be called. This allows a zone + ** to 'pass through' so to speak, if a zone is empty, allow the navigation to + ** travel to the next neighbour + */ + virtual CardZone* EnterZone(JButton inDirection) + { + if (mCards.empty()) + { + if (inDirection != JGE_BTN_NONE) + { + CardZone* nextNeighbour = GetNeighbour(inDirection); + if (nextNeighbour) + return nextNeighbour->EnterZone(inDirection); + } + } + + // when entering a zone, animate the selection of the current card + AnimateSelectionChange(mCurrentCard, false); + return this; + } + + /* + ** + */ + void LeaveZone(JButton inDirection) + { + AnimateSelectionChange(mCurrentCard, true); + } + + /* + ** + */ + PlayGuiObject* GetCurrentCard() + { + PlayGuiObject* current = NULL; + if (mCards.size()) + { + if (mCurrentCard < mCards.size()) + { + current = mCards[mCurrentCard]; + } + } + return current; + } + + std::vector mCards; + size_t mCurrentCard; + + // you'll typically have up to 4 neighbours, ie left/right/up/down + std::map mNeighbours; +}; + +/* +** Derivation of CardZone, but with special key handling for the grid style layout, +** where we need to navigate up/down as well as left/right +*/ +class GridCardZone : public CardZone +{ +public: + GridCardZone(bool inEnforceAxisAlignment = false) : mEnforceAxisAlignment(inEnforceAxisAlignment) + { + } + + virtual bool HandleSelection(JButton inKey) + { + size_t oldIndex = mCurrentCard; + + float minDistance = 100000; + int selectedCardIndex = -1; + bool isHorizontal = (inKey == JGE_BTN_LEFT || inKey == JGE_BTN_RIGHT); + + for (size_t index = 0; index < mCards.size(); ++index) + { + // skip yourself + if (mCurrentCard == index) continue; + + // skip if the card isn't on the same axis that we're stepping in + // this flag is an optional override. If enabled, it forces you to only be able to thumb over to the next card + // that is exactly on the same x or y coordinate axis - any card not strictly parallel is ignored. + if (mEnforceAxisAlignment) + { + if ((isHorizontal && mCards[mCurrentCard]->y != mCards[index]->y) || + (!isHorizontal && mCards[mCurrentCard]->x != mCards[index]->x)) + { + continue; + } + } + + // if it's going in the wrong direction, skip + if (inKey == JGE_BTN_RIGHT && mCards[index]->x <= mCards[mCurrentCard]->x) continue; + if (inKey == JGE_BTN_LEFT && mCards[index]->x >= mCards[mCurrentCard]->x) continue; + if (inKey == JGE_BTN_DOWN && mCards[index]->y <= mCards[mCurrentCard]->y) continue; + if (inKey == JGE_BTN_UP && mCards[index]->y >= mCards[mCurrentCard]->y) continue; + + // we've found a card on the same axis, stash its value & compare against the previous + float yDiff = fabs(mCards[mCurrentCard]->y - mCards[index]->y); + float xDiff = fabs(mCards[mCurrentCard]->x - mCards[index]->x); + float distance = sqrtf(yDiff * yDiff + xDiff * xDiff); + if (distance < minDistance) + { + minDistance = distance; + selectedCardIndex = index; + } + } + + bool changeZone = true; + if (selectedCardIndex != -1) + { + mCurrentCard = selectedCardIndex; + changeZone = false; + + if (oldIndex != mCurrentCard) + { + AnimateSelectionChange(oldIndex, true); + AnimateSelectionChange(mCurrentCard, false); + } + } + + return changeZone; + } + +protected: + bool mEnforceAxisAlignment; +}; + +/* +** +*/ +class HandCardZone: public GridCardZone +{ +public: + + /* + ** the card hand zone operates slightly differently than the default zones: + ** if entering via up/down, + ** set the current card selection to the bottom/top card + */ + virtual CardZone* EnterZone(JButton inDirection) + { + // TODO, check if the hand is flattened + if (mCards.size()) + { + if (inDirection == JGE_BTN_UP) + mCurrentCard = mCards.size() - 1; + else if (inDirection == JGE_BTN_DOWN) + mCurrentCard = 0; + } + + return CardZone::EnterZone(inDirection); + } +}; + +/* +** +*/ +class LandCardZone : public GridCardZone +{ +public: + virtual CardZone* EnterZone(JButton inDirection); +}; + +/* +** +*/ +class CreatureCardZone : public GridCardZone +{ +public: + virtual CardZone* EnterZone(JButton inDirection); +}; + +/* +** The base class dictates normally, if you enter a zone and it's empty, move to +** the next zone in the same direction. +** Adding an override here - if there are no creatures in play but there are land, +** and we're moving horizontally, jump up to the land instead +*/ +CardZone* CreatureCardZone::EnterZone(JButton inDirection) +{ + if ((inDirection == JGE_BTN_LEFT || inDirection == JGE_BTN_RIGHT) && mCards.empty()) + { + LandCardZone* landZone = dynamic_cast(mNeighbours[JGE_BTN_DOWN]); + if (landZone == NULL) + { + landZone = dynamic_cast(mNeighbours[JGE_BTN_UP]); + } + + if (landZone && !landZone->mCards.empty()) + { + return landZone->EnterZone(inDirection); + } + } + + return CardZone::EnterZone(inDirection); +} + +/* +** Same pattern to the CreatureCardZone pattern - if moving through an empty land zone, +** set the focus on the land zone if it has cards +*/ +CardZone* LandCardZone::EnterZone(JButton inDirection) +{ + if ((inDirection == JGE_BTN_LEFT || inDirection == JGE_BTN_RIGHT) && mCards.empty()) + { + CreatureCardZone* creatureZone = dynamic_cast(mNeighbours[JGE_BTN_DOWN]); + if (creatureZone == NULL) + { + creatureZone = dynamic_cast(mNeighbours[JGE_BTN_UP]); + } + + if (creatureZone && !creatureZone->mCards.empty()) + { + return creatureZone->EnterZone(inDirection); + } + } + + return CardZone::EnterZone(inDirection); +} + + +/* +** Constructor. All the navigation logic is initialized here, by pairing up each card zone with a set of neighbours. +*/ +Navigator::Navigator(DuelLayers* inDuelLayers) + : CardSelectorBase(BIG_MODE_SHOW), mDrawPosition(kDefaultCardPosition), mDuelLayers(inDuelLayers), mLimitorEnabled(false) +{ + assert(mDuelLayers); + + // initialize the cardZone layout + mCardZones.insert(std::pair(kCardZone_PlayerHand, NEW HandCardZone())); + mCardZones.insert(std::pair(kCardZone_PlayerAvatar, NEW CardZone())); + mCardZones.insert(std::pair(kCardZone_PlayerLibrary, NEW CardZone())); + mCardZones.insert(std::pair(kCardZone_PlayerGraveyard, NEW CardZone())); + mCardZones.insert(std::pair(kCardZone_PlayerLands, NEW LandCardZone())); + mCardZones.insert(std::pair(kCardZone_PlayerCreatures, NEW CreatureCardZone())); + mCardZones.insert(std::pair(kCardZone_PlayerEnchantmentsAndArtifacts, NEW GridCardZone())); + mCardZones.insert(std::pair(kCardZone_AIHand, NEW CardZone())); + mCardZones.insert(std::pair(kCardZone_AIAvatar, NEW CardZone())); + mCardZones.insert(std::pair(kCardZone_AILibrary, NEW CardZone())); + mCardZones.insert(std::pair(kCardZone_AIGraveyard, NEW CardZone())); + mCardZones.insert(std::pair(kCardZone_AILands, NEW LandCardZone())); + mCardZones.insert(std::pair(kCardZone_AICreatures, NEW CreatureCardZone())); + mCardZones.insert(std::pair(kCardZone_AIEnchantmentsAndArtifacts, NEW GridCardZone())); + + // navigation rules: each zone has up to 4 neighbours, specified here + mCardZones[kCardZone_PlayerHand]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_AIAvatar]; + mCardZones[kCardZone_PlayerHand]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_PlayerAvatar]; + mCardZones[kCardZone_PlayerHand]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_PlayerCreatures]; + mCardZones[kCardZone_PlayerHand]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_AIAvatar]; + + mCardZones[kCardZone_PlayerAvatar]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_PlayerHand]; + mCardZones[kCardZone_PlayerAvatar]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_PlayerLibrary]; + mCardZones[kCardZone_PlayerAvatar]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_PlayerEnchantmentsAndArtifacts]; + mCardZones[kCardZone_PlayerAvatar]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_PlayerLands]; + + mCardZones[kCardZone_PlayerLibrary]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_PlayerGraveyard]; + mCardZones[kCardZone_PlayerLibrary]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_PlayerAvatar]; + mCardZones[kCardZone_PlayerLibrary]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_PlayerLands]; + mCardZones[kCardZone_PlayerLibrary]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_PlayerAvatar]; + + mCardZones[kCardZone_PlayerGraveyard]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_PlayerHand]; + mCardZones[kCardZone_PlayerGraveyard]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_PlayerLibrary]; + mCardZones[kCardZone_PlayerGraveyard]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_PlayerCreatures]; + mCardZones[kCardZone_PlayerGraveyard]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_PlayerAvatar]; + + mCardZones[kCardZone_PlayerLands]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_PlayerCreatures]; + mCardZones[kCardZone_PlayerLands]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_PlayerAvatar]; + mCardZones[kCardZone_PlayerLands]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_PlayerEnchantmentsAndArtifacts]; + mCardZones[kCardZone_PlayerLands]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_PlayerAvatar]; + + mCardZones[kCardZone_PlayerCreatures]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_AICreatures]; + mCardZones[kCardZone_PlayerCreatures]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_PlayerLands]; + mCardZones[kCardZone_PlayerCreatures]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_PlayerEnchantmentsAndArtifacts]; + mCardZones[kCardZone_PlayerCreatures]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_PlayerHand]; + + mCardZones[kCardZone_PlayerEnchantmentsAndArtifacts]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_PlayerCreatures]; + mCardZones[kCardZone_PlayerEnchantmentsAndArtifacts]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_PlayerLands]; + // experiment, allow round tripping from the left edge over to the right side + mCardZones[kCardZone_PlayerEnchantmentsAndArtifacts]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_PlayerAvatar]; + mCardZones[kCardZone_PlayerEnchantmentsAndArtifacts]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_PlayerCreatures]; + + mCardZones[kCardZone_AIHand]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_AIAvatar]; + mCardZones[kCardZone_AIHand]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_AIEnchantmentsAndArtifacts]; + mCardZones[kCardZone_AIHand]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_AILibrary]; + + mCardZones[kCardZone_AIAvatar]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_AILands]; + mCardZones[kCardZone_AIAvatar]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_AIHand]; + mCardZones[kCardZone_AIAvatar]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_AILibrary]; + mCardZones[kCardZone_AIAvatar]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_PlayerHand]; + + mCardZones[kCardZone_AILibrary]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_AIGraveyard]; + mCardZones[kCardZone_AILibrary]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_AILands]; + mCardZones[kCardZone_AILibrary]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_AIAvatar]; + mCardZones[kCardZone_AILibrary]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_AILands]; + + mCardZones[kCardZone_AIGraveyard]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_AIAvatar]; + mCardZones[kCardZone_AIGraveyard]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_AILibrary]; + mCardZones[kCardZone_AIGraveyard]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_AIAvatar]; + mCardZones[kCardZone_AIGraveyard]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_AILands]; + + mCardZones[kCardZone_AILands]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_AIAvatar]; + mCardZones[kCardZone_AILands]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_AICreatures]; + mCardZones[kCardZone_AILands]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_AIEnchantmentsAndArtifacts]; + mCardZones[kCardZone_AILands]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_PlayerHand]; + + mCardZones[kCardZone_AICreatures]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_AILands]; + mCardZones[kCardZone_AICreatures]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_PlayerCreatures]; + mCardZones[kCardZone_AICreatures]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_AIEnchantmentsAndArtifacts]; + mCardZones[kCardZone_AICreatures]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_PlayerHand]; + + mCardZones[kCardZone_AIEnchantmentsAndArtifacts]->mNeighbours[JGE_BTN_UP] = mCardZones[kCardZone_AIAvatar]; + mCardZones[kCardZone_AIEnchantmentsAndArtifacts]->mNeighbours[JGE_BTN_DOWN] = mCardZones[kCardZone_AICreatures]; + // experiment, allow round tripping from the left edge over to the right side + mCardZones[kCardZone_AIEnchantmentsAndArtifacts]->mNeighbours[JGE_BTN_LEFT] = mCardZones[kCardZone_PlayerHand]; + mCardZones[kCardZone_AIEnchantmentsAndArtifacts]->mNeighbours[JGE_BTN_RIGHT] = mCardZones[kCardZone_AICreatures]; + + mCurrentZone = mCardZones[kCardZone_PlayerAvatar]; +} + +/* +** +*/ +Navigator::~Navigator() +{ + std::map::iterator iter = mCardZones.begin(); + for (; iter != mCardZones.end(); ++iter) + { + SAFE_DELETE(iter->second); + } +} +/* +** +*/ +bool Navigator::CheckUserInput(JButton inKey) +{ + bool result = true; + + switch (inKey) + { + case JGE_BTN_SEC: + GameObserver::GetInstance()->cancelCurrentAction(); + return true; + case JGE_BTN_OK: + GameObserver::GetInstance()->ButtonPressed(GetCurrentCard()); + return true; + break; + case JGE_BTN_LEFT: + case JGE_BTN_RIGHT: + case JGE_BTN_UP: + case JGE_BTN_DOWN: + HandleKeyStroke(inKey); + break; + case JGE_BTN_CANCEL: + mDrawMode = (mDrawMode+1) % NB_BIG_MODES; + if(mDrawMode == BIG_MODE_TEXT) + options[Options::DISABLECARDS].number = 1; + else + options[Options::DISABLECARDS].number = 0; + break; + default: + result = false; + } + + return result; +} + +bool Navigator::CheckUserInput(int x, int y) +{ + // TODO - figure out what to do with mouse support + return false; +} + +/* +** reposition the selected card's draw location +*/ +void Navigator::Update(float dt) +{ + float boundary = mDuelLayers->RightBoundary(); + float position = boundary - CardGui::BigWidth / 2; + if (GetCurrentCard() != NULL) + { + if ((GetCurrentCard()->x + CardGui::Width / 2 > position - CardGui::BigWidth / 2) && + (GetCurrentCard()->x - CardGui::Width / 2 < position + CardGui::BigWidth / 2)) + { + position = CardGui::BigWidth / 2 - 10; + } + } + + if (position < CardGui::BigWidth / 2) + position = CardGui::BigWidth / 2; + + mDrawPosition.x = position; + mDrawPosition.Update(dt); +} + +/* +** +*/ +PlayGuiObject* Navigator::GetCurrentCard() +{ + return mCurrentZone ? mCurrentZone->GetCurrentCard() : NULL; +} + +/* +** +*/ +void Navigator::Render() +{ + if (GetCurrentCard() != NULL) + { + GetCurrentCard()->Render(); + + CardView* card = dynamic_cast(GetCurrentCard()); + if (card) + { + switch(mDrawMode) + { + case BIG_MODE_SHOW: + card->RenderBig(mDrawPosition); + break; + case BIG_MODE_TEXT: + card->alternateRenderBig(mDrawPosition); + break; + default: + break; + } + } + } +} + +/* +** +*/ +void Navigator::HandleKeyStroke(JButton inKey) +{ + assert(mCurrentZone); + if (mCurrentZone) + { + bool changeZone = mCurrentZone->HandleSelection(inKey); + + if (changeZone && !mLimitorEnabled) + { + mCurrentZone->LeaveZone(inKey); + mCurrentZone = mCurrentZone->GetNeighbour(inKey); + mCurrentZone = mCurrentZone->EnterZone(inKey); + } + } +} + +/* +** unused. This is CardSelector specific. +*/ +void Navigator::PopLimitor() +{ +} + +/* +** same as above. +*/ +void Navigator::PushLimitor() +{ +} + +/* +** +*/ +void Navigator::Limit(LimitorFunctor* inLimitor, CardView::SelectorZone inZone) +{ + mLimitorEnabled = (inLimitor != NULL); + if (inZone == CardView::handZone) + { + mCurrentZone->LeaveZone(JGE_BTN_NONE); + + if (mLimitorEnabled) + { + mCurrentZoneStack.push(mCurrentZone); + mCurrentZone = mCardZones[kCardZone_PlayerHand]; + } + else + { + mCurrentZone = mCurrentZoneStack.top(); + mCurrentZoneStack.pop(); + assert(mCurrentZone); + if (mCurrentZone == NULL) + { + mCurrentZone = mCardZones[kCardZone_PlayerHand]; + } + } + + mCurrentZone->EnterZone(JGE_BTN_NONE); + } +} + +/* +** +*/ +int Navigator::CardToCardZone(PlayGuiObject* inCard) +{ + int result = kCardZone_Unknown; + GuiAvatar* avatar = dynamic_cast(inCard); + if (avatar) + { + if (avatar->player->isAI()) + { + result = kCardZone_AIAvatar; + } + else + { + result = kCardZone_PlayerAvatar; + } + } + + GuiGraveyard* graveyard = dynamic_cast(inCard); + if (graveyard) + { + if (graveyard->player->isAI()) + { + result = kCardZone_AIGraveyard; + } + else + { + result = kCardZone_PlayerGraveyard; + } + } + + GuiLibrary* library = dynamic_cast(inCard); + if (library) + { + if (library->player->isAI()) + { + result = kCardZone_AILibrary; + } + else + { + result = kCardZone_PlayerLibrary; + } + } + + GuiOpponentHand* opponentHand = dynamic_cast(inCard); + if (opponentHand) + { + result = kCardZone_AIHand; + } + + CardView* card = dynamic_cast(inCard); + { + if (card) + { + if (card->owner == CardView::handZone) + { + result = kCardZone_PlayerHand; + } + else if (card->owner == CardView::playZone) + { + int isAI = card->getCard()->owner->isAI(); + + if (card->getCard()->isCreature()) + { + result = isAI ? kCardZone_AICreatures : kCardZone_PlayerCreatures; + } + else if (card->getCard()->isLand()) + { + result = isAI ? kCardZone_AILands : kCardZone_PlayerLands; + } + else if (card->getCard()->isSpell()) + { + + //if (card->getCard()->target != NULL) + // isAI = card->getCard()->target->owner->isAI(); + // nasty hack: the lines above don't always work, as when an enchantment comes into play, its ability hasn't been activated yet, + // so it doesn't yet have a target. Instead, we now look at the card's position, if it's in the top half of the screen, it goes into an AI zone + isAI = card->y < SCREEN_HEIGHT / 2; + + // enchantments that target creatures are treated as part of the creature zone + if (card->getCard()->spellTargetType.find("creature") != string::npos) + { + result = isAI ? kCardZone_AICreatures : kCardZone_PlayerCreatures; + } + else if (card->getCard()->spellTargetType.find("land") != string::npos) + { + result = isAI ? kCardZone_AILands : kCardZone_PlayerLands; + } + else + { + result = isAI ? kCardZone_AIEnchantmentsAndArtifacts : kCardZone_PlayerEnchantmentsAndArtifacts; + } + } + else assert(false); + } + else + { + assert(false); + } + } + } + + assert(result != kCardZone_Unknown); + return result; +} + +/* +** +*/ +void Navigator::Add(PlayGuiObject* card) +{ + // figure out what card's been added, add it to the appropriate pile + int zone = CardToCardZone(card); + if (zone != kCardZone_Unknown) + { + mCardZones[zone]->AddCard(card); + } +} + +/* +** +*/ +void Navigator::Remove(PlayGuiObject* card) +{ + int zone = CardToCardZone(card); + if (zone != kCardZone_Unknown) + { + mCardZones[zone]->RemoveCard(card); + } +} diff --git a/projects/mtg/template.vcxproj b/projects/mtg/template.vcxproj index 60f6f80d5..de27ee7d9 100644 --- a/projects/mtg/template.vcxproj +++ b/projects/mtg/template.vcxproj @@ -120,6 +120,7 @@ true Use PrecompiledHeader.h + true NDEBUG;%(PreprocessorDefinitions) @@ -169,6 +170,7 @@ false Use PrecompiledHeader.h + false _DEBUG;%(PreprocessorDefinitions) @@ -220,6 +222,7 @@ ProgramDatabase Use PrecompiledHeader.h + true NDEBUG;%(PreprocessorDefinitions) @@ -270,6 +273,7 @@ EditAndContinue Use PrecompiledHeader.h + true _DEBUG;%(PreprocessorDefinitions) @@ -306,6 +310,7 @@ + @@ -371,6 +376,7 @@ + @@ -431,6 +437,7 @@ + @@ -480,6 +487,7 @@ +