Revamped Deck Selection Screen using abrasax's design as a template.

TODO:  
     change literals to use constants, 
     refactor the rendering code for the menu to have be leaner.
     add text scroller to list all the tasks. 
         * 1st implementation will list all the tasks.dat  
         * 2nd round will try to get the scroller to only display relevant tasks to ai

Special thanks to wololo and MootPoint for helping me hammer this out.  To abrasax, for the initial design of the layout.
This commit is contained in:
techdragon.nguyen@gmail.com
2010-11-01 08:22:55 +00:00
parent 63eca8ebfe
commit 6c1497bbd5
13 changed files with 507 additions and 17 deletions
+208
View File
@@ -0,0 +1,208 @@
#include "PrecompiledHeader.h"
#include "DeckMenu.h"
#include "DeckMenuItem.h"
#include "DeckMetaData.h"
#include "JTypes.h"
#include "GameApp.h"
#include "Translate.h"
namespace
{
const unsigned int kPoleWidth = 7;
const unsigned int kVerticalMargin = 16;
const unsigned int kHorizontalMargin = 30;
const signed int kLineHeight = 20;
}
WFont* DeckMenu::titleFont = NULL;
hgeParticleSystem* DeckMenu::stars = NULL;
unsigned int DeckMenu::refCount = 0;
// Here comes the magic of jewel graphics
PIXEL_TYPE DeckMenu::jewelGraphics[9] = {0x3FFFFFFF,0x63645AEA,0x610D0D98,
0x63645AEA,0xFF635AD5,0xFF110F67,
0x610D0D98,0xFF110F67,0xFD030330};
//
// For the additional info window, maximum characters per line is roughly 30 characters across.
// TODO: figure a way to get incoming text to wrap.
//
// used fixed locations where the menu, title and descriptive text are located.
// * menu at (125, 60 )
// * descriptive information 125
DeckMenu::DeckMenu(int id, JGuiListener* listener, int fontId, const char * _title)
: JGuiController(id, listener),
fontId(fontId) {
background = NULL;
autoTranslate = true;
maxItems = 7;
mHeight = 2 * kVerticalMargin + ( maxItems * kLineHeight );
mWidth = 0;
mX = 125;
mY = 60;
// where to place the title of the menu
titleX = mX;
titleY = mY - 30;
title = _(_title);
// where stats information goes
statsX = 280;
statsY = 8 + kVerticalMargin;
statsHeight = 50;
statsWidth = SCREEN_WIDTH / 2 - 40; // 40 is the width of the right border
// where to place the descripiton information
descX = 229;
descY = 70;
startId = 0;
selectionT = 0;
timeOpen = 0;
closed = false;
++refCount;
selectionTargetY = selectionY = kVerticalMargin;
if (NULL == stars)
stars = NEW hgeParticleSystem(resources.RetrievePSI("stars.psi", resources.GetQuad("stars")));
stars->FireAt(mX, mY);
}
// TODO: Make this configurable, perhaps by user as part of the theme options.
JQuad* getBackground()
{
resources.RetrieveTexture("DeckMenuBackdrop.png", RETRIEVE_MANAGE );
return resources.RetrieveQuad("DeckMenuBackdrop.png", 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, "DualPaneBG" );
}
void DeckMenu::Render() {
JRenderer * renderer = JRenderer::GetInstance();
WFont * titleFont = resources.GetWFont(Fonts::SMALLFACE_FONT);
WFont * mFont = resources.GetWFont(fontId);
// figure out where to place the stars initially
if (0 == mWidth) {
float sY = mY + kVerticalMargin;
for (int i = startId; i < startId + mCount; ++i) {
DeckMenuItem *menuItem = static_cast<DeckMenuItem *> (mObjects[i]);
int width = menuItem->GetWidth();
if (mWidth < width) mWidth = width;
}
if ((!title.empty()) && (mWidth < titleFont->GetStringWidth(title.c_str())))
mWidth = titleFont->GetStringWidth(title.c_str());
mWidth += 2*kHorizontalMargin;
for (int i = startId; i < startId + mCount; ++i) {
float y = mY + kVerticalMargin + i * kLineHeight;
DeckMenuItem * currentMenuItem = static_cast<DeckMenuItem*>(mObjects[i]);
currentMenuItem->Relocate( mX, y);
if (currentMenuItem->hasFocus()) sY = y;
}
stars->Fire();
selectionTargetY = selectionY = sY;
timeOpen = 0;
}
renderer->RenderQuad(getBackground(), 0, 0 );
float height = mHeight;
if (timeOpen < 1) height *= timeOpen > 0 ? timeOpen : -timeOpen;
renderer->SetTexBlend(BLEND_SRC_ALPHA, BLEND_ONE);
stars->Render();
renderer->SetTexBlend(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
mFont->SetScale(1.0f);
for (int i = startId; i < startId + maxItems ; i++){
if (i > mCount-1) break;
if ( static_cast<DeckMenuItem*>(mObjects[i])->mY - kLineHeight * startId < mY + height - kLineHeight + 7) {
DeckMenuItem *currentMenuItem = static_cast<DeckMenuItem*>(mObjects[i]);
if ( currentMenuItem->hasFocus()){
// display the avatar image
if ( currentMenuItem->imageFilename.size() > 0 )
{
JQuad * quad = resources.RetrieveTempQuad( currentMenuItem->imageFilename, TEXTURE_SUB_AVATAR );
if (quad) {
renderer->RenderQuad(quad, 232, 10, 0, 0.9f, 0.9f);
}
}
// fill in the description part of the screen
string text = currentMenuItem->desc;
WFont *mainFont = resources.GetWFont(Fonts::MAIN_FONT);
mainFont->DrawString(text.c_str(), descX, descY);
mFont->SetColor(ARGB(255,255,255,0));
// fill in the statistical portion
if ( currentMenuItem->meta )
{
ostringstream oss;
oss << "Deck: " << currentMenuItem->meta->getName() << endl;
oss << currentMenuItem->meta->getStatsSummary();
mainFont->DrawString( oss.str(), statsX, statsY - kVerticalMargin );
}
// fill in the bottom section of the screen.
// TODO: add text scroller of Tasks.
}
else {
mFont->SetColor(ARGB(150,255,255,255));
}
currentMenuItem->RenderWithOffset(-kLineHeight*startId);
}
if (!title.empty())
titleFont->DrawString(title.c_str(), titleX, titleY, JGETEXT_CENTER);
}
}
void DeckMenu::Update(float dt){
JGuiController::Update(dt);
if (mCurr > startId + maxItems-1)
startId = mCurr - maxItems +1;
else if (mCurr < startId)
startId = mCurr;
stars->Update(dt);
selectionT += 3*dt;
selectionY += (selectionTargetY - selectionY) * 8 * dt;
stars->MoveTo( 40 + ((mWidth-2*kHorizontalMargin)*(1+cos(selectionT))/2), selectionY + 5 * cos(selectionT*2.35) + kLineHeight / 2 - kLineHeight * startId);
if (timeOpen < 0) {
timeOpen += dt * 10;
if (timeOpen >= 0) { timeOpen = 0; closed = true; stars->FireAt(mX, mY); }
} else {
closed = false;
timeOpen += dt * 10;
}
}
void DeckMenu::Add(int id, const char * text,string desc, bool forceFocus, DeckMetaData * deckMetaData) {
DeckMenuItem * menuItem = NEW DeckMenuItem(this, id, fontId, text, 0, mY + kVerticalMargin + mCount*kLineHeight, (mCount == 0), autoTranslate, deckMetaData);
menuItem->desc = deckMetaData ? deckMetaData->getDescription() : desc;
JGuiController::Add(menuItem);
if (mCount <= maxItems) mHeight += kLineHeight;
if (forceFocus){
mObjects[mCurr]->Leaving(JGE_BTN_DOWN);
mCurr = mCount-1;
menuItem->Entering();
}
}
void DeckMenu::Close()
{
timeOpen = -1.0;
stars->Stop(true);
}
void DeckMenu::destroy(){
SAFE_DELETE(DeckMenu::stars);
}
+109
View File
@@ -0,0 +1,109 @@
#include "PrecompiledHeader.h"
#include "DeckMenuItem.h"
#include "Translate.h"
#include "WResourceManager.h"
DeckMenuItem::DeckMenuItem(DeckMenu* _parent, int id, int fontId, string text, int x, int y, bool hasFocus, bool autoTranslate, DeckMetaData *deckMetaData): JGuiObject(id), parent(_parent), fontId(fontId), mX(x), mY(y)
{
if (autoTranslate)
mText = _(text);
else
mText = text;
mHasFocus = hasFocus;
mScale = 1.0f;
mTargetScale = 1.0f;
if (hasFocus)
Entering();
meta = deckMetaData;
if ( meta && meta->getAvatarFilename().size() > 0 )
this->imageFilename = meta->getAvatarFilename();
else
this->imageFilename = "avatar.jpg";
}
void DeckMenuItem::RenderWithOffset(float yOffset)
{
WFont * mFont = resources.GetWFont(fontId);
mFont->DrawString(mText.c_str(), mX, mY + yOffset, JGETEXT_CENTER);
}
void DeckMenuItem::Render()
{
RenderWithOffset(0);
}
void DeckMenuItem::Update(float dt)
{
if (mScale < mTargetScale)
{
mScale += 8.0f*dt;
if (mScale > mTargetScale)
mScale = mTargetScale;
}
else if (mScale > mTargetScale)
{
mScale -= 8.0f*dt;
if (mScale < mTargetScale)
mScale = mTargetScale;
}
}
void DeckMenuItem::Entering()
{
mHasFocus = true;
parent->selectionTargetY = mY;
}
bool DeckMenuItem::Leaving(JButton key)
{
mHasFocus = false;
return true;
}
bool DeckMenuItem::ButtonPressed()
{
return true;
}
void DeckMenuItem::Relocate(int x, int y)
{
mX = x;
mY = y;
}
int DeckMenuItem::GetWidth()
{
WFont * mFont = resources.GetWFont(fontId);
mFont->SetScale(1.0);
return mFont->GetStringWidth(mText.c_str());
}
bool DeckMenuItem::hasFocus()
{
return mHasFocus;
}
ostream& DeckMenuItem::toString(ostream& out) const
{
return out << "DeckMenuItem ::: mHasFocus : " << mHasFocus
<< " ; parent : " << parent
<< " ; mText : " << mText
<< " ; mScale : " << mScale
<< " ; mTargetScale : " << mTargetScale
<< " ; mX,mY : " << mX << "," << mY;
}
DeckMenuItem::~DeckMenuItem()
{
meta = NULL;
}
+32 -10
View File
@@ -33,6 +33,9 @@ void DeckMetaData::loadStatsForPlayer( Player * statsPlayer, string deckStatsFil
_percentVictories = stats->percentVictories(deckStatsFileName);
_victories = opponentDeckStats->victories;
_nbGamesPlayed = opponentDeckStats->nbgames;
ostringstream oss;
oss << "avatar" << deckStatsFileName.substr( deckStatsFileName.find("deck") + 4, deckStatsFileName.find_last_of(".") -1 ) << ".jpg";
_avatarFilename = oss.str();
if (_percentVictories < 34)
{
_difficulty = HARD;
@@ -46,6 +49,12 @@ void DeckMetaData::loadStatsForPlayer( Player * statsPlayer, string deckStatsFil
_difficulty = EASY;
}
}
else
{
ostringstream oss;
oss << "avatar" << this->getDeckId() << ".jpg";
_avatarFilename = oss.str();
}
}
else
{
@@ -114,6 +123,10 @@ int DeckMetaData::getDeckId()
return _deckid;
}
string DeckMetaData::getAvatarFilename()
{
return _avatarFilename;
}
int DeckMetaData::getGamesPlayed()
{
@@ -136,10 +149,9 @@ int DeckMetaData::getDifficulty()
return _difficulty;
}
string DeckMetaData::getDescription()
string DeckMetaData::getDifficultyString()
{
char deckDesc[512];
string difficultyString = "";
string difficultyString = "Normal";
switch( _difficulty )
{
case HARD:
@@ -149,12 +161,22 @@ string DeckMetaData::getDescription()
difficultyString = "Easy";
break;
}
if ( _nbGamesPlayed > 0 && difficultyString != "")
sprintf(deckDesc, "Deck: %s\nDifficulty: %s\nVictory %%: %i\nGames Played: %i\n\n%s", _name.c_str(), difficultyString.c_str(), _percentVictories, _nbGamesPlayed, _desc.c_str() );
else if ( _nbGamesPlayed > 0 )
sprintf(deckDesc, "Deck: %s\nVictory %%: %i\nGames Played: %i\n\n%s", _name.c_str(), _percentVictories, _nbGamesPlayed, _desc.c_str() );
else
sprintf(deckDesc, "Deck: %s\n\n%s", _name.c_str(), _desc.c_str() );
return deckDesc;
return difficultyString;
}
string DeckMetaData::getDescription()
{
return _desc;
}
string DeckMetaData::getStatsSummary()
{
ostringstream statsSummary;
statsSummary << "Difficulty: " << getDifficultyString() << endl
<< "Victory %: " << getVictoryPercentage() << endl
<< "Games Played: " << getGamesPlayed() << endl;
return statsSummary.str();
}
+1
View File
@@ -242,6 +242,7 @@ void GameApp::Destroy()
Translator::EndInstance();
WCFilterFactory::Destroy();
SimpleMenu::destroy();
DeckMenu::destroy();
options.theGame = NULL;
LOG("==Destroying GameApp Successful==");
+32
View File
@@ -23,6 +23,16 @@ vector<DeckMetaData *> GameState::fillDeckMenu( SimpleMenu * _menu, string path,
return deckMetaDataVector;
}
vector<DeckMetaData *> GameState::fillDeckMenu( DeckMenu * _menu, string path, string smallDeckPrefix, Player * statsPlayer){
bool translate = _menu->autoTranslate;
_menu->autoTranslate = false;
vector<DeckMetaData *> deckMetaDataVector = getValidDeckMetaData( path, smallDeckPrefix, statsPlayer );
renderDeckMenu( _menu, deckMetaDataVector);
_menu->autoTranslate = translate;
return deckMetaDataVector;
}
vector<DeckMetaData *> GameState::getValidDeckMetaData( string path, string smallDeckPrefix, Player * statsPlayer)
{
@@ -91,6 +101,28 @@ void GameState::renderDeckMenu ( SimpleMenu * _menu, vector<DeckMetaData *> deck
}
// build a menu with the given deck list and return a vector of the deck ids created.
void GameState::renderDeckMenu ( DeckMenu * _menu, vector<DeckMetaData *> deckMetaDataList )
{
int deckNumber = 1;
Translator * t = Translator::GetInstance();
map<string,string>::iterator it;
for (vector<DeckMetaData *>::iterator i = deckMetaDataList.begin(); i != deckMetaDataList.end(); i++)
{
DeckMetaData * deckMetaData = *i;
string deckName = deckMetaData -> getName();
string deckDescription = deckMetaData -> getDescription();
int deckId = deckMetaData -> getDeckId();
//translate decks desc
it = t->deckValues.find(deckName);
if (it != t->deckValues.end())
_menu->Add(deckNumber++, deckName.c_str(), it->second, false, deckMetaData);
else
_menu->Add( deckNumber++ ,deckName.c_str(), deckDescription.c_str(), false, deckMetaData);
}
}
// deck sorting routines
bool sortByName( DeckMetaData * d1, DeckMetaData * d2 )
{
+5 -4
View File
@@ -1,5 +1,6 @@
#include "PrecompiledHeader.h"
#include "DeckMenu.h"
#include "GameStateDuel.h"
#include "GameOptions.h"
#include "utils.h"
@@ -87,11 +88,11 @@ void GameStateDuel::Start()
menu = NULL;
int decksneeded = 0;
for (int i = 0; i<2; i ++){
if (mParent->players[i] == PLAYER_TYPE_HUMAN){
decksneeded = 1;
deckmenu = NEW SimpleMenu(DUEL_MENU_CHOOSE_DECK, this, Fonts::MENU_FONT, 35, 25, "Choose a Deck");
deckmenu = NEW DeckMenu(DUEL_MENU_CHOOSE_DECK, this, Fonts::MENU_FONT, "Choose a Deck");
DeckManager *deckManager = DeckManager::GetInstance();
vector<DeckMetaData *> playerDeckList = getValidDeckMetaData( options.profileFile() );
@@ -196,7 +197,6 @@ void GameStateDuel::loadTestSuitePlayers(){
void GameStateDuel::End()
{
DebugTrace("Ending GameStateDuel");
SAFE_DELETE(deckmenu);
JRenderer::GetInstance()->EnableVSync(false);
if (mPlayers[0] && mPlayers[1]) mPlayers[0]->End();
@@ -215,6 +215,7 @@ void GameStateDuel::End()
SAFE_DELETE(menu);
SAFE_DELETE(opponentMenu);
SAFE_DELETE(deckmenu);
#ifdef TESTSUITE
SAFE_DELETE(testSuite);
#endif
@@ -236,7 +237,7 @@ bool GameStateDuel::MusicExist(string FileName){
void GameStateDuel::ensureOpponentMenu(){
if (!opponentMenu){
opponentMenu = NEW SimpleMenu(DUEL_MENU_CHOOSE_OPPONENT, this, Fonts::MENU_FONT, 35, 25, "Choose Opponent");
opponentMenu = NEW DeckMenu(DUEL_MENU_CHOOSE_OPPONENT, this, Fonts::MENU_FONT, "Choose Your Opponent");
opponentMenu->Add( MENUITEM_RANDOM_AI, "Random");
if (options[Options::EVILTWIN_MODE_UNLOCKED].number)
opponentMenu->Add( MENUITEM_EVIL_TWIN, "Evil Twin", _("Can you play against yourself?").c_str());