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.
This commit is contained in:
wrenczes@gmail.com
2010-10-31 07:50:53 +00:00
parent dfb9d76829
commit 5a1e8e6ffe
22 changed files with 1041 additions and 154 deletions
+10 -9
View File
@@ -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 <typeinfo>
@@ -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();
+3 -2
View File
@@ -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<float>(x + 20 + (mCount - start_item) * 30), static_cast<float>(y + 25));
CardGui * card = NEW CardView(CardView::nullZone, _card, static_cast<float>(x + 20 + (mCount - start_item) * 30), static_cast<float>(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;
}
+2 -2
View File
@@ -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)
{
+41 -73
View File
@@ -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<Target*>::iterator it = cards.begin(); it != cards.end(); ++it)
if (card == *it)
{
if (active == *it)
{
if (active == *it)
{
CardView* c = dynamic_cast<CardView*>(active); if (c) c->zoom = 1.0f;
active = closest<Diff>(cards, limitor, active);
c = dynamic_cast<CardView*>(active); if (c) c->zoom = 1.4f;
}
if (active == *it) active = NULL;
cards.erase(it);
return;
CardView* c = dynamic_cast<CardView*>(active); if (c) c->zoom = 1.0f;
active = closest<Diff>(cards, limitor, active);
c = dynamic_cast<CardView*>(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<Target*>::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<True>(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<CardView*>(oldactive)) oldowner = q->owner; else oldowner = nullZone;
if (nullZone != oldowner) lasts[oldowner] = SelectorMemory(oldactive);
CardView::SelectorZone oldowner;
if (CardView *q = dynamic_cast<CardView*>(oldactive)) oldowner = q->owner; else oldowner = CardView::nullZone;
if (CardView::nullZone != oldowner) lasts[oldowner] = SelectorMemory(oldactive);
}
if (active != oldactive) {
{ CardView* c = dynamic_cast<CardView*>(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<Down>(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<CardView*>(oldactive)) oldowner = q->owner; else oldowner = nullZone;
if (CardView *q = dynamic_cast<CardView*>(active)) owner = q->owner; else owner = nullZone;
CardView::SelectorZone oldowner, owner;
if (CardView *q = dynamic_cast<CardView*>(oldactive)) oldowner = q->owner; else oldowner = CardView::nullZone;
if (CardView *q = dynamic_cast<CardView*>(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<True>(cards, limitor, x, y);
if (active != oldactive) {
SelectorZone oldowner, owner;
if (CardView *q = dynamic_cast<CardView*>(oldactive)) oldowner = q->owner; else oldowner = nullZone;
if (CardView *q = dynamic_cast<CardView*>(active)) owner = q->owner; else owner = nullZone;
CardView::SelectorZone oldowner, owner;
if (CardView *q = dynamic_cast<CardView*>(oldactive)) oldowner = q->owner; else oldowner = CardView::nullZone;
if (CardView *q = dynamic_cast<CardView*>(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<CardView*>(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<CardView*>(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<Target>* limitor, SelectorZone destzone) {
void CardSelector::Limit(LimitorFunctor<PlayGuiObject>* limitor, CardView::SelectorZone destzone) {
this->limitor = limitor;
if (limitor && !limitor->select(active)) {
Target* oldactive = active;
SelectorZone oldowner;
if (CardView *q = dynamic_cast<CardView*>(oldactive)) oldowner = q->owner; else oldowner = nullZone;
PlayGuiObject* oldactive = active;
CardView::SelectorZone oldowner;
if (CardView *q = dynamic_cast<CardView*>(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<Target>* limitor, SelectorZone destzone)
if (limitor && !limitor->select(active)) {
active = NULL;
for (vector<Target*>::iterator it = cards.begin(); it != cards.end(); ++it)
for (vector<PlayGuiObject*>::iterator it = cards.begin(); it != cards.end(); ++it)
if (limitor->select(*it)) {
active = *it;
break;
@@ -286,43 +282,15 @@ void CardSelector::Limit(LimitorFunctor<Target>* limitor, SelectorZone destzone)
}
}
template<>
void CardSelector::PushLimitor() {
if (NULL == limitor) return;
SelectorZone owner;
if (CardView *q = dynamic_cast<CardView*>(active)) owner = q->owner; else owner = nullZone;
CardView::SelectorZone owner;
if (CardView *q = dynamic_cast<CardView*>(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;
}
}
@@ -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;
}
}
+1
View File
@@ -1,6 +1,7 @@
#include "PrecompiledHeader.h"
#include "MTGRules.h"
#include "CardSelectorSingleton.h"
#include "GuiCombat.h"
#include "GuiBackground.h"
#include "GuiFrame.h"
+1
View File
@@ -1,5 +1,6 @@
#include "PrecompiledHeader.h"
#include "CardSelectorSingleton.h"
#include "GameApp.h"
#include "GuiAvatars.h"
#include "GameObserver.h"
+1
View File
@@ -5,6 +5,7 @@
#include "AIPlayer.h"
#include "GameObserver.h"
#include "Trash.h"
#include "CardSelector.h"
#include "Closest.cpp"
static const float MARGIN = 70;
+7 -5
View File
@@ -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;
+7 -3
View File
@@ -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;
}
}
+4 -4
View File
@@ -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;
+3 -2
View File
@@ -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();
+773
View File
@@ -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<PlayGuiObject*> mCards;
size_t mCurrentCard;
// you'll typically have up to 4 neighbours, ie left/right/up/down
std::map<JButton, CardZone*> 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<LandCardZone*>(mNeighbours[JGE_BTN_DOWN]);
if (landZone == NULL)
{
landZone = dynamic_cast<LandCardZone*>(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<CreatureCardZone*>(mNeighbours[JGE_BTN_DOWN]);
if (creatureZone == NULL)
{
creatureZone = dynamic_cast<CreatureCardZone*>(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<int, CardZone*>(kCardZone_PlayerHand, NEW HandCardZone()));
mCardZones.insert(std::pair<int, CardZone*>(kCardZone_PlayerAvatar, NEW CardZone()));
mCardZones.insert(std::pair<int, CardZone*>(kCardZone_PlayerLibrary, NEW CardZone()));
mCardZones.insert(std::pair<int, CardZone*>(kCardZone_PlayerGraveyard, NEW CardZone()));
mCardZones.insert(std::pair<int, CardZone*>(kCardZone_PlayerLands, NEW LandCardZone()));
mCardZones.insert(std::pair<int, CardZone*>(kCardZone_PlayerCreatures, NEW CreatureCardZone()));
mCardZones.insert(std::pair<int, CardZone*>(kCardZone_PlayerEnchantmentsAndArtifacts, NEW GridCardZone()));
mCardZones.insert(std::pair<int, CardZone*>(kCardZone_AIHand, NEW CardZone()));
mCardZones.insert(std::pair<int, CardZone*>(kCardZone_AIAvatar, NEW CardZone()));
mCardZones.insert(std::pair<int, CardZone*>(kCardZone_AILibrary, NEW CardZone()));
mCardZones.insert(std::pair<int, CardZone*>(kCardZone_AIGraveyard, NEW CardZone()));
mCardZones.insert(std::pair<int, CardZone*>(kCardZone_AILands, NEW LandCardZone()));
mCardZones.insert(std::pair<int, CardZone*>(kCardZone_AICreatures, NEW CreatureCardZone()));
mCardZones.insert(std::pair<int, CardZone*>(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<int, CardZone*>::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<CardView*>(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<PlayGuiObject>* 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<GuiAvatar*>(inCard);
if (avatar)
{
if (avatar->player->isAI())
{
result = kCardZone_AIAvatar;
}
else
{
result = kCardZone_PlayerAvatar;
}
}
GuiGraveyard* graveyard = dynamic_cast<GuiGraveyard*>(inCard);
if (graveyard)
{
if (graveyard->player->isAI())
{
result = kCardZone_AIGraveyard;
}
else
{
result = kCardZone_PlayerGraveyard;
}
}
GuiLibrary* library = dynamic_cast<GuiLibrary*>(inCard);
if (library)
{
if (library->player->isAI())
{
result = kCardZone_AILibrary;
}
else
{
result = kCardZone_PlayerLibrary;
}
}
GuiOpponentHand* opponentHand = dynamic_cast<GuiOpponentHand*>(inCard);
if (opponentHand)
{
result = kCardZone_AIHand;
}
CardView* card = dynamic_cast<CardView*>(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);
}
}