Files
coffee_crisis/source/menu.cpp

504 lines
12 KiB
C++

#include "const.h"
#include "menu.h"
// Constructor
Menu::Menu(SDL_Renderer *renderer, Text *text, Input *input, std::string *fileList)
{
mRenderer = renderer;
mText = text;
mInput = input;
mFileList = fileList;
}
Menu::~Menu()
{
mRenderer = nullptr;
mText = nullptr;
mInput = nullptr;
mFileList = nullptr;
}
// Inicializador
void Menu::init(std::string name, int x, int y, int backgroundType)
{
loadMedia();
// Inicia variables
mName = name;
mSelector.index = 0;
mTotalItems = 0;
mItemSelected = MENU_NO_OPTION;
mVerticalPadding = 1;
mPosX = x;
mPosY = y;
mRectBG.rect.x = 0;
mRectBG.rect.y = 0;
mRectBG.rect.w = 0;
mRectBG.rect.h = 0;
mRectBG.r = 0;
mRectBG.g = 0;
mRectBG.b = 0;
mSelector.rect.x = 0;
mSelector.rect.y = 0;
mSelector.rect.w = 0;
mSelector.rect.h = 0;
mSelector.r = 0;
mSelector.g = 0;
mSelector.b = 0;
mSelector.a = 255;
mBackgroundType = backgroundType;
mIsCenteredOnX = false;
mIsCenteredOnY = false;
mAreElementsCenteredOnX = false;
mCenterX = x + ((SCREEN_WIDTH - x) / 2);
mCenterY = y + ((SCREEN_HEIGHT - y) / 2);
mWidestItem = 0;
// Selector
mSelector.origin = 0;
mSelector.target = 0;
mSelector.y = 0;
mSelector.numJumps = 4;
mSelector.despY = 0;
mSelector.moving = false;
// Elementos del menu
for (int i = 0; i < MAX_ITEMS; i++)
{
mItem[i].label = "";
mItem[i].w = 0;
mItem[i].h = 0;
mItem[i].x = 0;
mItem[i].y = 0;
mItem[i].hPaddingUp = 0;
mItem[i].hPaddingDown = 0;
}
}
// Carga los recursos necesarios para la sección 'Title'
bool Menu::loadMedia()
{
// Indicador de éxito en la carga
bool success = true;
// Sonidos
mSoundMove = JA_LoadSound(mFileList[17].c_str());
mSoundAccept = JA_LoadSound(mFileList[18].c_str());
mSoundCancel = JA_LoadSound(mFileList[16].c_str());
return success;
}
// Obtiene el nombre del menu
std::string Menu::getName()
{
return mName;
}
// Obtiene el valor de la variable
Uint8 Menu::getItemSelected()
{
const int temp = mItemSelected;
mItemSelected = MENU_NO_OPTION;
return temp;
}
// Actualiza la posicion y el estado del selector
void Menu::updateSelector()
{
if (mSelector.moving)
{
mSelector.y += mSelector.despY;
if (mSelector.despY > 0) // Va hacia abajo
{
if (mSelector.y > mSelector.target) // Ha llegado al destino
{
mSelector.origin = mSelector.y = mSelector.target;
mSelector.moving = false;
}
}
if (mSelector.despY < 0) // Va hacia abajo
{
if (mSelector.y < mSelector.target) // Ha llegado al destino
{
mSelector.origin = mSelector.y = mSelector.target;
mSelector.moving = false;
}
}
mSelector.rect.y = int(mSelector.y) - 1;
}
else
{
mSelector.rect.y = int(mSelector.y) - 1;
}
}
// Establece el origen del selector
void Menu::setSelectorOrigin(int value)
{
mSelector.origin = value;
}
// Establece el destino del selector
void Menu::setSelectorTarget(int value)
{
mSelector.target = value;
}
// Coloca el selector en una posición específica
void Menu::setSelectorPos(Uint8 index)
{
if (index < mTotalItems)
{
mSelector.index = index;
mSelector.y = mSelector.origin = mSelector.target = mItem[mSelector.index].y;
mSelector.moving = false;
}
}
// Obtiene la anchura del elemento más ancho del menu
Uint16 Menu::getWidestItem()
{
Uint16 result = 0;
// Obtenemos la anchura del item mas ancho
for (int i = 0; i < mTotalItems; i++)
{
if (mItem[i].w > result)
{
result = mItem[i].w;
}
}
return result;
}
// Deja el menu apuntando al primer elemento
void Menu::reset()
{
mItemSelected = MENU_NO_OPTION;
mSelector.index = 0;
mSelector.origin = mSelector.target = mSelector.y = mItem[0].y;
mSelector.moving = false;
}
// Deja el menu sin elemento seleccionado
void Menu::deselectItem()
{
mItemSelected = MENU_NO_OPTION;
}
// Actualiza el menu para recolocarlo correctamente y establecer el tamaño
void Menu::reorganize()
{
setRectSize();
if (mIsCenteredOnX)
centerMenuOnX(mCenterX);
if (mIsCenteredOnY)
centerMenuOnY(mCenterY);
if (mAreElementsCenteredOnX)
centerMenuElementsOnX();
}
// Deja el menu apuntando al siguiente elemento
bool Menu::increaseSelectorIndex()
{
bool success = false;
mSelector.y = mSelector.origin = mItem[mSelector.index].y;
if (mSelector.index < (mTotalItems - 1))
{
mSelector.index++;
while ((!mItem[mSelector.index].selectable) && (mSelector.index < (mTotalItems - 1)))
mSelector.index++;
success = true;
}
mSelector.target = mItem[mSelector.index].y;
mSelector.despY = (mSelector.target - mSelector.origin) / mSelector.numJumps;
mSelector.moving = true;
return success;
}
// Deja el menu apuntando al elemento anterior
bool Menu::decreaseSelectorIndex()
{
bool success = false;
mSelector.y = mSelector.origin = mItem[mSelector.index].y;
if (mSelector.index > 0)
{
mSelector.index--;
while ((!mItem[mSelector.index].selectable) && (mSelector.index > 0))
mSelector.index--;
success = true;
}
mSelector.target = mItem[mSelector.index].y;
mSelector.despY = (mSelector.target - mSelector.origin) / mSelector.numJumps;
mSelector.moving = true;
return success;
}
// Actualiza la logica del menu
void Menu::update()
{
updateSelector();
}
// Pinta el menu en pantalla
void Menu::render()
{
// Rendereritza el fondo del menu
if (mBackgroundType == MENU_BACKGROUND_SOLID)
{
SDL_SetRenderDrawColor(mRenderer, mRectBG.r, mRectBG.g, mRectBG.b, mRectBG.a);
SDL_RenderFillRect(mRenderer, &mRectBG.rect);
}
// Renderiza el rectangulo del selector
SDL_SetRenderDrawColor(mRenderer, mSelector.r, mSelector.g, mSelector.b, mSelector.a);
SDL_RenderFillRect(mRenderer, &mSelector.rect);
// Renderiza el borde del fondo
if (mBackgroundType == MENU_BACKGROUND_SOLID)
{
SDL_SetRenderDrawColor(mRenderer, mRectBG.r, mRectBG.g, mRectBG.b, 255);
SDL_RenderDrawRect(mRenderer, &mRectBG.rect);
}
// Renderitza el text
for (int i = 0; i < mTotalItems; i++)
{
if (i == mSelector.index)
{
const color_t color = {mSelector.itemR, mSelector.itemG, mSelector.itemB};
mText->writeColored(mItem[i].x, mItem[i].y, mItem[i].label, color);
}
else
{
mText->write(mItem[i].x, mItem[i].y, mItem[i].label);
}
}
}
// Establece el rectangulo de fondo del menu y el selector
void Menu::setRectSize()
{
findHeight();
findWidth();
mRectBG.rect.w = mWidth;
mRectBG.rect.h = mHeight;
// La posición X es la del menú menos medio caracter
mRectBG.rect.x = mPosX - (mText->getCharacterWidth() / 2);
// La posición Y es la del menu menos la altura de medio caracter i el padding
mRectBG.rect.y = mPosY - (mText->getCharacterWidth() / 2) - mVerticalPadding;
// Establecemos los valores del rectangulo del selector a partir de los valores del rectangulo de fondo
mSelector.rect.h = (mText->getCharacterWidth() * 1) + 1;
mSelector.rect.w = mRectBG.rect.w;
mSelector.rect.x = mRectBG.rect.x;
}
// Establece el valor de la variable
void Menu::setTotalItems(int num)
{
mTotalItems = num;
mTotalItems > MAX_ITEMS ? MAX_ITEMS : mTotalItems;
}
// Establece el color del rectangulo de fondo
void Menu::setBackgroundColor(int r, int g, int b, int alpha)
{
mRectBG.r = r;
mRectBG.g = g;
mRectBG.b = b;
mRectBG.a = alpha;
}
// Establece el color del rectangulo del selector
void Menu::setSelectorColor(int r, int g, int b, int alpha)
{
mSelector.r = r;
mSelector.g = g;
mSelector.b = b;
mSelector.a = alpha;
}
// Establece el color del texto del selector
void Menu::setSelectorTextColor(int r, int g, int b)
{
mSelector.itemR = r;
mSelector.itemG = g;
mSelector.itemB = b;
}
// Centra el menu respecto un punto en el eje X
void Menu::centerMenuOnX(int value)
{
mIsCenteredOnX = true;
mCenterX = value;
// Actualiza el rectangulo de fondo para recalcular las dimensiones
setRectSize();
// Obten el acho del menu
mWidestItem = getWidestItem();
// Establece la nueva posición centrada en funcion del elemento más ancho
mPosX = (value) - (mWidestItem / 2);
// Reposiciona los elementos del menu
for (int i = 0; i < MAX_ITEMS; i++)
{
mItem[i].x = mPosX;
}
// Recalcula el rectangulo de fondo
setRectSize();
// Recoloca el selector
mSelector.origin = mSelector.target = mSelector.y = mItem[mSelector.index].y;
mSelector.moving = false;
}
// Centra el menu respecto un punto en el eje Y
void Menu::centerMenuOnY(int value)
{
mIsCenteredOnY = true;
mCenterY = value;
// Actualiza el rectangulo de fondo para recalcular las dimensiones
setRectSize();
// Obten el alto del menu
findHeight();
// Establece la nueva posición centrada en funcion del elemento más ancho
mPosY = (value) - (mHeight / 2);
// Reposiciona los elementos del menu
replaceElementsOnY();
// Recalcula el rectangulo de fondo
setRectSize();
// Recoloca el selector
mSelector.origin = mSelector.target = mSelector.y = mItem[mSelector.index].y;
mSelector.moving = false;
}
// Centra los elementos del menu en el eje X
void Menu::centerMenuElementsOnX()
{
mAreElementsCenteredOnX = true;
for (int i = 0; i < mTotalItems; i++)
mItem[i].x = (mCenterX - (mItem[i].w / 2));
}
// Añade un item al menu
void Menu::addItem(std::string text, const Uint8 hPaddingUp, const Uint8 hPaddingDown, bool selectable, bool greyed)
{
// Si es el primer item coge la posición en el eje Y del propio menu
if (mTotalItems == 0)
mItem[mTotalItems].y = mPosY;
else
// En caso contrario, coge la posición en el eje Y a partir del elemento anterior
mItem[mTotalItems].y = mItem[mTotalItems - 1].y + mItem[mTotalItems - 1].h + mItem[mTotalItems - 1].hPaddingDown;
mItem[mTotalItems].label = text;
mItem[mTotalItems].w = mText->lenght(mItem[mTotalItems].label);
mItem[mTotalItems].h = mText->getCharacterWidth() + (mVerticalPadding * 2);
mItem[mTotalItems].x = mPosX;
mItem[mTotalItems].hPaddingUp = hPaddingUp;
mItem[mTotalItems].hPaddingDown = hPaddingDown;
mItem[mTotalItems].selectable = selectable;
mItem[mTotalItems].greyed = greyed;
setTotalItems(mTotalItems + 1);
reorganize();
setSelectorPos(0);
}
// Cambia el texto de un item
void Menu::setItemCaption(Uint8 index, std::string text)
{
mItem[index].label = text;
mItem[index].w = mText->lenght(mItem[index].label);
reorganize();
}
// Establece el indice del itemm que se usará por defecto al cancelar el menu
void Menu::setDefaultActionWhenCancel(Uint8 item)
{
mDefaultActionWhenCancel = item;
}
// Gestiona la entrada de teclado y mando durante el menu
void Menu::checkInput()
{
if (mInput->checkInput(INPUT_UP, REPEAT_FALSE))
{
if (decreaseSelectorIndex())
JA_PlaySound(mSoundMove);
}
if (mInput->checkInput(INPUT_DOWN, REPEAT_FALSE))
{
if (increaseSelectorIndex())
JA_PlaySound(mSoundMove);
}
if (mInput->checkInput(INPUT_ACCEPT, REPEAT_FALSE))
{
mItemSelected = mSelector.index;
JA_PlaySound(mSoundAccept);
}
if (mInput->checkInput(INPUT_CANCEL, REPEAT_FALSE))
{
mItemSelected = mDefaultActionWhenCancel;
JA_PlaySound(mSoundCancel);
}
}
// Calcula el ancho del menu
void Menu::findWidth()
{
mWidth = 0;
// Obtenemos la anchura del item mas ancho
for (int i = 0; i < mTotalItems; i++)
if (mItem[i].w > mWidth)
mWidth = mItem[i].w;
// La anchura de la cadena más larga, mas un caracter
mWidth += (mText->getCharacterWidth() * 1);
}
// Calcula el alto del menu
void Menu::findHeight()
{
mHeight = 0;
// Obtenemos la altura de la suma de alturas de los items
for (int i = 0; i < mTotalItems; i++)
mHeight += mItem[i].h + mItem[i].hPaddingDown;
// La altura de la suma de los items mas un caracter y menos un pixel (porque el texto en realidad es de 7 pixeles)
mHeight += (mText->getCharacterWidth() * 1) - 1;
}
// Recoloca los elementos del menu en el eje Y
void Menu::replaceElementsOnY()
{
mItem[0].y = mPosY;
for (int i = 1; i < mTotalItems; i++)
{
mItem[i].y = mItem[i - 1].y + mItem[i - 1].h + mItem[i - 1].hPaddingDown;
}
}