#include "const.h" #include "menu.h" // Constructor Menu::Menu(SDL_Renderer *renderer, Text *text, Input *input, Asset *asset) { this->renderer = renderer; this->text = text; this->input = input; this->asset = asset; } Menu::~Menu() { renderer = nullptr; text = nullptr; input = nullptr; asset = nullptr; } // Inicializador void Menu::init(std::string name, int x, int y, int backgroundType) { loadMedia(); // Inicia variables this->name = name; selector.index = 0; totalItems = 0; itemSelected = MENU_NO_OPTION; this->x = x; this->y = y; rectBG.rect = {0,0,0,0}; rectBG.color = {0,0,0}; rectBG.a = 0; this->backgroundType = backgroundType; isCenteredOnX = false; isCenteredOnY = false; areElementsCenteredOnX = false; centerX = 0; centerY = 0; widestItem = 0; colorGreyed = {128, 128, 128}; // Selector selector.originY = 0; selector.targetY = 0; selector.despY = 0; selector.originH = 0; selector.targetH = 0; selector.incH = 0; selector.y = 0.0f; selector.h = 0.0f; selector.numJumps = 8; selector.moving = false; selector.resizing = false; selector.rect = {0, 0, 0, 0}; selector.color = {0,0,0}; selector.itemColor = {0,0,0}; selector.a = 255; // Elementos del menu for (int i = 0; i < MENU_MAX_ITEMS; i++) { item[i].label = ""; item[i].rect = {0, 0, 0, 0}; item[i].hPaddingDown = 0; item[i].selectable = false; item[i].greyed = false; item[i].linkedDown = false; item[i].linkedUp = false; } } // Carga los recursos necesarios para la sección 'Title' bool Menu::loadMedia() { // Indicador de éxito en la carga bool success = true; // Sonidos soundMove = JA_LoadSound(asset->get("").c_str()); soundAccept = JA_LoadSound(asset->get("").c_str()); soundCancel = JA_LoadSound(asset->get("").c_str()); return success; } // Obtiene el nombre del menu std::string Menu::getName() { return name; } // Obtiene el valor de la variable int Menu::getItemSelected() { // Al llamar a esta funcion, se obtiene el valor y se borra const int temp = itemSelected; itemSelected = MENU_NO_OPTION; return temp; } // Actualiza la posicion y el estado del selector void Menu::updateSelector() { if (selector.moving) { // Calcula el desplazamiento en Y selector.y += selector.despY; if (selector.despY > 0) // Va hacia abajo { if (selector.y > selector.targetY) // Ha llegado al destino { selector.originY = selector.y = selector.targetY; selector.moving = false; } } if (selector.despY < 0) // Va hacia arriba { if (selector.y < selector.targetY) // Ha llegado al destino { selector.originY = selector.y = selector.targetY; selector.moving = false; } } selector.rect.y = int(selector.y); } else { selector.rect.y = int(selector.y); } if (selector.resizing) { // Calcula el incremento en H selector.h += selector.incH; if (selector.incH > 0) // Crece { if (selector.h > selector.targetH) // Ha llegado al destino { //selector.originH = selector.targetH = selector.rect.h = getSelectorHeight(selector.index); selector.originH = selector.h = selector.targetH; selector.resizing = false; } } if (selector.incH < 0) // Decrece { if (selector.h < selector.targetH) // Ha llegado al destino { //selector.originH = selector.targetH = selector.rect.h = getSelectorHeight(selector.index); selector.originH = selector.h = selector.targetH; selector.resizing = false; } } selector.rect.h = int(selector.h); } else { selector.rect.h = getSelectorHeight(selector.index); } } // Coloca el selector en una posición específica void Menu::setSelectorPos(int index) { if (index < totalItems) { selector.index = index; selector.rect.y = selector.y = selector.originY = selector.targetY = item[selector.index].rect.y; selector.rect.w = rectBG.rect.w; selector.rect.x = rectBG.rect.x; selector.originH = selector.targetH = selector.rect.h = getSelectorHeight(selector.index); selector.moving = false; selector.resizing = false; } } // Obtiene la anchura del elemento más ancho del menu int Menu::getWidestItem() { int result = 0; // Obtenemos la anchura del item mas ancho for (int i = 0; i < totalItems; i++) if (item[i].rect.w > result) result = item[i].rect.w; return result; } // Deja el menu apuntando al primer elemento void Menu::reset() { itemSelected = MENU_NO_OPTION; selector.index = 0; selector.originY = selector.targetY = selector.y = item[0].rect.y; selector.originH = selector.targetH = item[0].rect.h; selector.moving = false; selector.resizing = false; } // Actualiza el menu para recolocarlo correctamente y establecer el tamaño void Menu::reorganize() { setRectSize(); if (isCenteredOnX) centerMenuOnX(centerX); if (isCenteredOnY) centerMenuOnY(centerY); if (areElementsCenteredOnX) centerMenuElementsOnX(); } // Deja el menu apuntando al siguiente elemento bool Menu::increaseSelectorIndex() { bool success = false; // Obten las coordenadas del elemento actual selector.y = selector.originY = item[selector.index].rect.y; selector.h = selector.originH = getSelectorHeight(selector.index); // Calcula cual es el siguiente elemento ++selector.index %= totalItems; while (!item[selector.index].selectable) //selector.index++; ++selector.index %= totalItems; success = true; // Establece las coordenadas y altura de destino if (success) { selector.targetY = item[selector.index].rect.y; selector.despY = (selector.targetY - selector.originY) / selector.numJumps; selector.targetH = getSelectorHeight(selector.index); selector.incH = (selector.targetH - selector.originH) / selector.numJumps; selector.moving = true; if (selector.incH != 0) selector.resizing = true; } return success; } // Deja el menu apuntando al elemento anterior bool Menu::decreaseSelectorIndex() { bool success = false; // Obten las coordenadas del elemento actual selector.y = selector.originY = item[selector.index].rect.y; selector.h = selector.originH = getSelectorHeight(selector.index); // Calcula cual es el siguiente elemento if (selector.index == 0) selector.index = totalItems; else selector.index--; while (!item[selector.index].selectable) { if (selector.index == 0) selector.index = totalItems; else selector.index--; } success = true; // Establece las coordenadas y altura de destino if (success) { selector.targetY = item[selector.index].rect.y; selector.despY = (selector.targetY - selector.originY) / selector.numJumps; selector.targetH = getSelectorHeight(selector.index); selector.incH = (selector.targetH - selector.originH) / selector.numJumps; selector.moving = true; if (selector.incH != 0) selector.resizing = 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 (backgroundType == MENU_BACKGROUND_SOLID) { SDL_SetRenderDrawColor(renderer, rectBG.color.r, rectBG.color.g, rectBG.color.b, rectBG.a); SDL_RenderFillRect(renderer, &rectBG.rect); } // Renderiza el rectangulo del selector SDL_Rect temp = selector.rect; temp.y--; temp.h++; SDL_SetRenderDrawColor(renderer, selector.color.r, selector.color.g, selector.color.b, selector.a); SDL_RenderFillRect(renderer, &temp); // Renderiza el borde del fondo if (backgroundType == MENU_BACKGROUND_SOLID) { SDL_SetRenderDrawColor(renderer, rectBG.color.r, rectBG.color.g, rectBG.color.b, 255); SDL_RenderDrawRect(renderer, &rectBG.rect); } // Renderitza el texto for (int i = 0; i < totalItems; i++) { if (i == selector.index) { const color_t color = {selector.itemColor.r, selector.itemColor.g, selector.itemColor.b}; text->writeColored(item[i].rect.x, item[i].rect.y, item[i].label, color); } else if (item[i].selectable) { text->write(item[i].rect.x, item[i].rect.y, item[i].label); } else if (item[i].greyed) { text->writeColored(item[i].rect.x, item[i].rect.y, item[i].label, colorGreyed); } else // No seleccionable { if ((item[i].linkedUp) && (i == selector.index + 1)) { const color_t color = {selector.itemColor.r, selector.itemColor.g, selector.itemColor.b}; text->writeColored(item[i].rect.x, item[i].rect.y, item[i].label, color); } else // No enlazado con el de arriba { text->write(item[i].rect.x, item[i].rect.y, item[i].label); } } } } // Establece el rectangulo de fondo del menu y el selector void Menu::setRectSize() { rectBG.rect.w = findWidth() + text->getCharacterWidth(); rectBG.rect.h = findHeight() + text->getCharacterWidth(); // La posición X es la del menú menos medio caracter rectBG.rect.x = x - (text->getCharacterWidth() / 2); // La posición Y es la del menu menos la altura de medio caracter rectBG.rect.y = y - (text->getCharacterWidth() / 2); // Establecemos los valores del rectangulo del selector a partir de los valores del rectangulo de fondo setSelectorPos(selector.index); } // Establece el valor de la variable void Menu::setTotalItems(int num) { totalItems = num; if (totalItems > MENU_MAX_ITEMS) totalItems = MENU_MAX_ITEMS; } // Establece el color del rectangulo de fondo void Menu::setBackgroundColor(color_t color, int alpha) { rectBG.color = color; rectBG.a = alpha; } // Establece el color del rectangulo del selector void Menu::setSelectorColor(color_t color, int alpha) { selector.color = color; selector.a = alpha; } // Establece el color del texto del selector void Menu::setSelectorTextColor(color_t color) { selector.itemColor = color; } // Centra el menu respecto un punto en el eje X void Menu::centerMenuOnX(int value) { isCenteredOnX = true; centerX = value; // Establece la nueva posición centrada en funcion del elemento más ancho x = (value) - (findWidth() / 2); // Reposiciona los elementos del menu for (int i = 0; i < MENU_MAX_ITEMS; i++) item[i].rect.x = x; // Recalcula el rectangulo de fondo setRectSize(); } // Centra el menu respecto un punto en el eje Y void Menu::centerMenuOnY(int value) { isCenteredOnY = true; centerY = value; // Establece la nueva posición centrada en funcion del elemento más ancho y = (value) - (findHeight() / 2); // Reposiciona los elementos del menu replaceElementsOnY(); // Recalcula el rectangulo de fondo setRectSize(); } // Centra los elementos del menu en el eje X void Menu::centerMenuElementsOnX() { areElementsCenteredOnX = true; for (int i = 0; i < totalItems; i++) item[i].rect.x = (centerX - (item[i].rect.w / 2)); } // Añade un item al menu void Menu::addItem(std::string text, int hPaddingDown, bool selectable, bool greyed, bool linkedDown) { // Si es el primer item coge la posición en el eje Y del propio menu if (totalItems == 0) item[totalItems].rect.y = y; else // En caso contrario, coge la posición en el eje Y a partir del elemento anterior item[totalItems].rect.y = item[totalItems - 1].rect.y + item[totalItems - 1].rect.h + item[totalItems - 1].hPaddingDown; setItemCaption(totalItems, text); item[totalItems].rect.x = x; item[totalItems].hPaddingDown = hPaddingDown; item[totalItems].selectable = selectable; item[totalItems].greyed = greyed; item[totalItems].linkedDown = linkedDown; if (totalItems > 0) if (item[totalItems - 1].linkedDown) item[totalItems].linkedUp = true; setTotalItems(totalItems + 1); centerX = x + (findWidth() / 2); reorganize(); } // Cambia el texto de un item void Menu::setItemCaption(int index, std::string text) { item[index].label = text; item[index].rect.w = this->text->lenght(item[index].label); item[index].rect.h = this->text->getCharacterWidth(); reorganize(); } // Establece el indice del itemm que se usará por defecto al cancelar el menu void Menu::setDefaultActionWhenCancel(int item) { defaultActionWhenCancel = item; } // Gestiona la entrada de teclado y mando durante el menu void Menu::checkInput() { if (input->checkInput(INPUT_UP, REPEAT_FALSE)) if (decreaseSelectorIndex()) JA_PlaySound(soundMove); if (input->checkInput(INPUT_DOWN, REPEAT_FALSE)) if (increaseSelectorIndex()) JA_PlaySound(soundMove); if (input->checkInput(INPUT_ACCEPT, REPEAT_FALSE)) { itemSelected = selector.index; JA_PlaySound(soundAccept); } if (input->checkInput(INPUT_CANCEL, REPEAT_FALSE)) { itemSelected = defaultActionWhenCancel; JA_PlaySound(soundCancel); } } // Calcula el ancho del menu int Menu::findWidth() { return getWidestItem(); } // Calcula el alto del menu int Menu::findHeight() { int height = 0; // Obtenemos la altura de la suma de alturas de los items for (int i = 0; i < totalItems; i++) height += item[i].rect.h + item[i].hPaddingDown; return height - item[totalItems - 1].hPaddingDown; } // Recoloca los elementos del menu en el eje Y void Menu::replaceElementsOnY() { item[0].rect.y = y; for (int i = 1; i < totalItems; i++) item[i].rect.y = item[i - 1].rect.y + item[i - 1].rect.h + item[i - 1].hPaddingDown; } // Establece el estado seleccionable de un item void Menu::setSelectable(int index, bool value) { item[index].selectable = value; } // Establece el estado agrisado de un item void Menu::setGreyed(int index, bool value) { item[index].greyed = value; } // Establece el estado de enlace de un item void Menu::setLinkedDown(int index, bool value) { item[index].linkedDown = value; } // Calcula la altura del selector int Menu::getSelectorHeight(int value) { if (item[value].linkedDown) return item[value].rect.h + item[value].hPaddingDown + item[value + 1].rect.h; else return item[value].rect.h; }