Files

860 lines
28 KiB
C++

#include "game/ui/menu.h"
#include <algorithm> // for max, min
#include <numeric> // for accumulate
#include <sstream> // for basic_stringstream
#include "core/audio/audio.hpp" // for Audio::get (playSound)
#include "core/audio/jail_audio.hpp" // for Ja::loadSound, Ja::deleteSound (propietat local)
#include "core/input/input.h" // for Input, Input::Repeat::OFF, InputAction
#include "core/rendering/text.h" // for Text
#include "core/resources/asset.h" // for Asset
#include "core/resources/resource_helper.h"
// Constructor
Menu::Menu(SDL_Renderer *renderer, const std::string &file)
: color_greyed_{128, 128, 128} {
// Copia punteros
this->renderer_ = renderer;
// Inicializa punteros
sound_move_ = nullptr;
sound_accept_ = nullptr;
sound_cancel_ = nullptr;
// Inicializa variables
selector_.index = 0;
selector_.previous_index = 0;
item_selected_ = NO_OPTION;
x_ = 0;
y_ = 0;
w_ = 0;
h_ = 0;
rect_bg_.rect = {.x = 0, .y = 0, .w = 0, .h = 0};
rect_bg_.color = {0, 0, 0};
rect_bg_.a = 0;
background_type_ = Background::SOLID;
is_centered_on_x_ = false;
is_centered_on_y_ = false;
are_elements_centered_on_x_ = false;
center_x_ = 0;
center_y_ = 0;
widest_item_ = 0;
default_action_when_cancel_ = 0;
// Selector
selector_.origin_y = 0;
selector_.target_y = 0;
selector_.desp_y = 0;
selector_.origin_h = 0;
selector_.target_h = 0;
selector_.inc_h = 0;
selector_.y = 0.0F;
selector_.h = 0.0F;
selector_.num_jumps = 8;
selector_.moving = false;
selector_.resizing = false;
selector_.rect = {.x = 0, .y = 0, .w = 0, .h = 0};
selector_.color = {0, 0, 0};
selector_.item_color = {0, 0, 0};
selector_.a = 255;
// Inicializa las variables desde un fichero. Si no se pasa fichero, el
// llamante (p.ej. Resource::preloadAll) usará loadFromBytes después —
// y ese método ya llama a setSelectorItemColors() y reset() al final.
if (!file.empty()) {
load(file);
setSelectorItemColors();
reset();
}
}
Menu::~Menu() {
renderer_ = nullptr;
if (sound_move_ != nullptr) {
Ja::deleteSound(sound_move_);
}
if (sound_accept_ != nullptr) {
Ja::deleteSound(sound_accept_);
}
if (sound_cancel_ != nullptr) {
Ja::deleteSound(sound_cancel_);
}
delete text_;
}
// Parser compartido (recibe cualquier istream)
auto Menu::parseFromStream(std::istream &file, const std::string &filename) -> bool {
bool success = true;
bool text_allocated = false;
std::string line;
(void)filename;
// Normalitza CRLF: en Windows els .men es llegeixen en binari (resource
// pack o ifstream sense flag de text) i std::getline només talla pel \n,
// deixant un \r residual. Sense això, "[item]" no fa match i el vector
// d'items queda buit → reset() accedeix a item[0] i crasheja.
auto strip_cr = [](std::string &s) {
if (!s.empty() && s.back() == '\r') {
s.pop_back();
}
};
while (std::getline(file, line)) {
strip_cr(line);
if (line == "[item]") {
Item new_item;
new_item.label = "";
new_item.h_padding_down = 1;
new_item.selectable = true;
new_item.greyed = false;
new_item.linked_down = false;
new_item.visible = true;
new_item.line = false;
do {
if (!std::getline(file, line)) {
break;
}
strip_cr(line);
int pos = line.find('=');
if (!setItem(&new_item, line.substr(0, pos), line.substr(pos + 1, line.length()))) {
success = false;
}
} while (line != "[/item]");
addItem(new_item);
} else {
int pos = line.find('=');
if (!setVars(line.substr(0, pos), line.substr(pos + 1, line.length()))) {
success = false;
}
// Crea el objeto text tan pronto como se pueda. Necesario para añadir items.
// Carga via ResourceHelper para que funcione tanto con pack como con filesystem.
if (!font_png_.empty() && !font_txt_.empty() && !text_allocated) {
auto png_bytes = ResourceHelper::loadFile(Asset::get()->get(font_png_));
auto txt_bytes = ResourceHelper::loadFile(Asset::get()->get(font_txt_));
text_ = new Text(png_bytes, txt_bytes, renderer_);
text_allocated = true;
}
}
}
return success;
}
// Carga la configuración del menu (vía ResourceHelper: pack si està inicialitzat, filesystem si no)
auto Menu::load(const std::string &file_path) -> bool {
const std::string FILE_NAME = file_path.substr(file_path.find_last_of("\\/") + 1);
auto bytes = ResourceHelper::loadFile(file_path);
if (bytes.empty()) {
return false;
}
return loadFromBytes(bytes, FILE_NAME);
}
// Carga el menu desde bytes en memoria
auto Menu::loadFromBytes(const std::vector<uint8_t> &bytes, const std::string &name_for_logs) -> bool {
if (bytes.empty()) {
return false;
}
std::string content(reinterpret_cast<const char *>(bytes.data()), bytes.size());
std::stringstream ss(content);
bool ok = parseFromStream(ss, name_for_logs);
setSelectorItemColors();
reset();
return ok;
}
// Asigna variables a partir de dos cadenas
auto Menu::setItem(Item *item, const std::string &var, const std::string &value) -> bool {
// Indicador de éxito en la asignación
bool success = true;
if (var == "text") {
item->label = value;
}
else if (var == "hPaddingDown") {
item->h_padding_down = std::stoi(value);
}
else if (var == "selectable") {
item->selectable = value == "true";
}
else if (var == "greyed") {
item->greyed = value == "true";
}
else if (var == "linkedDown") {
item->linked_down = value == "true";
}
else if (var == "visible") {
item->visible = value == "true";
}
else if (var == "line") {
item->line = value == "true";
}
else if ((var.empty()) || (var == "[/item]")) {
}
else {
success = false;
}
return success;
}
// Helper de setVars: variables de tipo sonido
auto Menu::trySetSoundVar(const std::string &var, const std::string &value) -> bool {
Ja::Sound **target = nullptr;
if (var == "sound_cancel") {
target = &sound_cancel_;
} else if (var == "sound_accept") {
target = &sound_accept_;
} else if (var == "sound_move") {
target = &sound_move_;
} else {
return false;
}
auto bytes = ResourceHelper::loadFile(Asset::get()->get(value));
if (!bytes.empty()) {
*target = Ja::loadSound(bytes.data(), (uint32_t)bytes.size());
}
return true;
}
// Helper de setVars: variables de tipo color
auto Menu::trySetColorVar(const std::string &var, const std::string &value) -> bool {
Color *color_target = nullptr;
int *alpha_target = nullptr;
if (var == "backgroundColor") {
color_target = &rect_bg_.color;
alpha_target = &rect_bg_.a;
} else if (var == "selector_color") {
color_target = &selector_.color;
alpha_target = &selector_.a;
} else if (var == "selector_text_color") {
color_target = &selector_.item_color;
} else {
return false;
}
std::stringstream ss(value);
std::string tmp;
getline(ss, tmp, ',');
color_target->r = std::stoi(tmp);
getline(ss, tmp, ',');
color_target->g = std::stoi(tmp);
getline(ss, tmp, ',');
color_target->b = std::stoi(tmp);
if (alpha_target != nullptr) {
getline(ss, tmp, ',');
*alpha_target = std::stoi(tmp);
}
return true;
}
// Asigna variables a partir de dos cadenas
auto Menu::setVars(const std::string &var, const std::string &value) -> bool {
if (trySetSoundVar(var, value)) {
return true;
}
if (trySetColorVar(var, value)) {
return true;
}
if (var == "font_png") {
font_png_ = value;
} else if (var == "font_txt") {
font_txt_ = value;
} else if (var == "name") {
name_ = value;
} else if (var == "x") {
x_ = std::stoi(value);
} else if (var == "y") {
y_ = std::stoi(value);
} else if (var == "centerX") {
center_x_ = std::stoi(value);
} else if (var == "centerY") {
center_y_ = std::stoi(value);
} else if (var == "backgroundType") {
background_type_ = static_cast<Background>(std::stoi(value));
} else if (var == "areElementsCenteredOnX") {
are_elements_centered_on_x_ = (value == "true");
} else if (var == "isCenteredOnX") {
is_centered_on_x_ = (value == "true");
} else if (var == "isCenteredOnY") {
is_centered_on_y_ = (value == "true");
} else if (var == "defaultActionWhenCancel") {
default_action_when_cancel_ = std::stoi(value);
} else if (var.empty()) {
} else {
return false;
}
return true;
}
// Carga los ficheros de audio
void Menu::loadAudioFile(const std::string &file, Sound sound) {
switch (sound) {
case Sound::ACCEPT:
sound_accept_ = Ja::loadSound(file.c_str());
break;
case Sound::CANCEL:
sound_cancel_ = Ja::loadSound(file.c_str());
break;
case Sound::MOVE:
sound_move_ = Ja::loadSound(file.c_str());
break;
default:
break;
}
}
// Obtiene el nombre del menu
auto Menu::getName() const -> const std::string & {
return name_;
}
// Obtiene el valor de la variable
auto Menu::getItemSelected() -> int {
// Al llamar a esta funcion, se obtiene el valor y se borra
const int TEMP = item_selected_;
item_selected_ = 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_.desp_y;
if (selector_.desp_y > 0) // Va hacia abajo
{
if (selector_.y > selector_.target_y) // Ha llegado al destino
{
selector_.origin_y = selector_.y = selector_.target_y;
selector_.moving = false;
}
}
else if (selector_.desp_y < 0) // Va hacia arriba
{
if (selector_.y < selector_.target_y) // Ha llegado al destino
{
selector_.origin_y = selector_.y = selector_.target_y;
selector_.moving = false;
}
}
selector_.rect.y = int(selector_.y);
// Actualiza el color del item
selector_.item_color_index++;
selector_.item_color_index = std::min(selector_.num_jumps - 1, selector_.item_color_index);
selector_.item_color = selector_.jump_item_colors[selector_.item_color_index];
selector_.previous_item_color = selector_.jump_item_colors[selector_.num_jumps - 1 - selector_.item_color_index];
} else {
selector_.rect.y = int(selector_.y);
selector_.item_color_index = 0;
selector_.item_color = selector_.jump_item_colors[selector_.num_jumps - 1];
selector_.previous_item_color = selector_.jump_item_colors[0];
}
if (selector_.resizing) {
// Calcula el incremento en H
selector_.h += selector_.inc_h;
if (selector_.inc_h > 0) // Crece
{
if (selector_.h > selector_.target_h) // Ha llegado al destino
{
// selector.originH = selector.targetH = selector.rect.h = getSelectorHeight(selector.index);
selector_.origin_h = selector_.h = selector_.target_h;
selector_.resizing = false;
}
}
else if (selector_.inc_h < 0) // Decrece
{
if (selector_.h < selector_.target_h) // Ha llegado al destino
{
// selector.originH = selector.targetH = selector.rect.h = getSelectorHeight(selector.index);
selector_.origin_h = selector_.h = selector_.target_h;
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 < (int)items_.size()) {
selector_.index = index;
selector_.rect.y = selector_.y = selector_.origin_y = selector_.target_y = items_[selector_.index].rect.y;
selector_.rect.w = rect_bg_.rect.w;
selector_.rect.x = rect_bg_.rect.x;
selector_.origin_h = selector_.target_h = selector_.rect.h = getSelectorHeight(selector_.index);
selector_.moving = false;
selector_.resizing = false;
}
}
// Obtiene la anchura del elemento más ancho del menu
auto Menu::getWidestItem() -> int {
return std::accumulate(items_.begin(), items_.end(), 0, [](int acc, const Item &i) { return std::max(acc, i.rect.w); });
}
// Deja el menu apuntando al primer elemento
void Menu::reset() {
item_selected_ = NO_OPTION;
selector_.index = 0;
if (items_.empty()) {
return;
}
selector_.origin_y = selector_.target_y = selector_.y = items_[0].rect.y;
selector_.origin_h = selector_.target_h = items_[0].rect.h;
selector_.moving = false;
selector_.resizing = false;
// Si el primer elemento no es seleccionable, incrementa el selector
if (!items_[selector_.index].selectable) {
increaseSelectorIndex();
setSelectorPos(selector_.index);
}
}
// Actualiza el menu para recolocarlo correctamente y establecer el tamaño
void Menu::reorganize() {
setRectSize();
if (is_centered_on_x_) {
centerMenuOnX(center_x_);
}
if (is_centered_on_y_) {
centerMenuOnY(center_y_);
}
if (are_elements_centered_on_x_) {
centerMenuElementsOnX();
}
}
// Deja el menu apuntando al siguiente elemento
void Menu::increaseSelectorIndex() {
// Guarda el indice actual antes de modificarlo
selector_.previous_index = selector_.index;
// Obten las coordenadas del elemento actual
selector_.y = selector_.origin_y = items_[selector_.index].rect.y;
selector_.h = selector_.origin_h = getSelectorHeight(selector_.index);
// Calcula cual es el siguiente elemento
++selector_.index %= items_.size();
while (!items_[selector_.index].selectable) {
++selector_.index %= items_.size();
}
// Establece las coordenadas y altura de destino
selector_.target_y = items_[selector_.index].rect.y;
selector_.desp_y = (selector_.target_y - selector_.origin_y) / selector_.num_jumps;
selector_.target_h = getSelectorHeight(selector_.index);
selector_.inc_h = (selector_.target_h - selector_.origin_h) / selector_.num_jumps;
selector_.moving = true;
if (selector_.inc_h != 0) {
selector_.resizing = true;
}
}
// Deja el menu apuntando al elemento anterior
void Menu::decreaseSelectorIndex() {
// Guarda el indice actual antes de modificarlo
selector_.previous_index = selector_.index;
// Obten las coordenadas del elemento actual
selector_.y = selector_.origin_y = items_[selector_.index].rect.y;
selector_.h = selector_.origin_h = getSelectorHeight(selector_.index);
// Calcula cual es el siguiente elemento
if (selector_.index == 0) {
selector_.index = items_.size() - 1;
} else {
selector_.index--;
}
while (!items_[selector_.index].selectable) {
if (selector_.index == 0) {
selector_.index = items_.size() - 1;
} else {
selector_.index--;
}
}
// Establece las coordenadas y altura de destino
selector_.target_y = items_[selector_.index].rect.y;
selector_.desp_y = (selector_.target_y - selector_.origin_y) / selector_.num_jumps;
selector_.target_h = getSelectorHeight(selector_.index);
selector_.inc_h = (selector_.target_h - selector_.origin_h) / selector_.num_jumps;
selector_.moving = true;
if (selector_.inc_h != 0) {
selector_.resizing = true;
}
}
// Actualiza la logica del menu
void Menu::update() {
checkInput();
updateSelector();
}
// Pinta el menu en pantalla
void Menu::render() {
// Rendereritza el fondo del menu
if (background_type_ == Background::SOLID) {
SDL_FRect f_bg = {(float)rect_bg_.rect.x, (float)rect_bg_.rect.y, (float)rect_bg_.rect.w, (float)rect_bg_.rect.h};
SDL_SetRenderDrawColor(renderer_, rect_bg_.color.r, rect_bg_.color.g, rect_bg_.color.b, rect_bg_.a);
SDL_RenderFillRect(renderer_, &f_bg);
}
// Renderiza el rectangulo del selector
const SDL_FRect F_TEMP = {(float)selector_.rect.x, (float)(selector_.rect.y - 1), (float)selector_.rect.w, (float)(selector_.rect.h + 1)};
SDL_SetRenderDrawColor(renderer_, selector_.color.r, selector_.color.g, selector_.color.b, selector_.a);
SDL_RenderFillRect(renderer_, &F_TEMP);
// Renderiza el borde del fondo
if (background_type_ == Background::SOLID) {
SDL_FRect f_bg_border = {(float)rect_bg_.rect.x, (float)rect_bg_.rect.y, (float)rect_bg_.rect.w, (float)rect_bg_.rect.h};
SDL_SetRenderDrawColor(renderer_, rect_bg_.color.r, rect_bg_.color.g, rect_bg_.color.b, 255);
SDL_RenderRect(renderer_, &f_bg_border);
}
// Crea una linea por si hay que dibujarla entre los items
HorizontalLine line;
line.x1 = selector_.rect.x + (selector_.rect.w / 6);
line.x2 = line.x1 + ((selector_.rect.w / 6) * 4);
// Renderiza el texto
for (int i = 0; i < (int)items_.size(); ++i) {
if (items_[i].visible) {
// Comprueba si ha de dibujar una linea en el elemento del menu
if (items_[i].line) {
line.y = items_[i].rect.y + items_[i].rect.h + (items_[i].h_padding_down / 2) - 1;
SDL_SetRenderDrawColor(renderer_, 255, 255, 255, 64);
SDL_RenderLine(renderer_, line.x1, line.y, line.x2, line.y);
}
// Dibuja el elemento
if (items_[i].greyed) { // Tiene prioridad si el elemento es gris
text_->writeColored(items_[i].rect.x, items_[i].rect.y, items_[i].label, color_greyed_);
}
else if (i == selector_.index) { // A continuación si tiene el indice
const Color COLOR = {selector_.item_color.r, selector_.item_color.g, selector_.item_color.b};
text_->writeColored(items_[i].rect.x, items_[i].rect.y, items_[i].label, COLOR);
}
else if (i == selector_.previous_index) { // O si lo ha tenido
const Color COLOR = {selector_.previous_item_color.r, selector_.previous_item_color.g, selector_.previous_item_color.b};
text_->writeColored(items_[i].rect.x, items_[i].rect.y, items_[i].label, COLOR);
}
else if (items_[i].selectable) { // O si simplemente es un elemento normal
text_->write(items_[i].rect.x, items_[i].rect.y, items_[i].label);
}
else { // Si no es seleccionable
if ((items_[i].linked_up) && (i == selector_.index + 1)) { // Si el elemento está enlazado con el elemento superior se pinta del color del selector
const Color COLOR = {selector_.item_color.r, selector_.item_color.g, selector_.item_color.b};
text_->writeColored(items_[i].rect.x, items_[i].rect.y, items_[i].label, COLOR);
} else { // Si no está enlazado con el elemento superior se pinta con el color normal
text_->write(items_[i].rect.x, items_[i].rect.y, items_[i].label);
}
}
}
}
}
// Establece el rectangulo de fondo del menu y el selector
void Menu::setRectSize(int w, int h) {
// Establece el ancho
if (w == 0) { // Si no se pasa un valor, se busca si hay uno prefijado
if (this->w_ == 0) { // Si no hay prefijado, coge el item mas ancho
rect_bg_.rect.w = findWidth() + text_->getCharacterSize();
} else { // Si hay prefijado, coge ese
rect_bg_.rect.w = this->w_;
}
} else { // Si se pasa un valor, se usa y se prefija
rect_bg_.rect.w = w;
this->w_ = w;
}
// Establece el alto
if (h == 0) { // Si no se pasa un valor, se busca de manera automatica
rect_bg_.rect.h = findHeight() + text_->getCharacterSize();
} else { // Si se pasa un valor, se aplica
rect_bg_.rect.h = h;
}
// La posición X es la del menú menos medio caracter
if (this->w_ != 0) { // Si el ancho esta prefijado, la x coinccide
rect_bg_.rect.x = x_;
} else { // Si el ancho es automatico, se le da un poco de margen
rect_bg_.rect.x = x_ - (text_->getCharacterSize() / 2);
}
// La posición Y es la del menu menos la altura de medio caracter
rect_bg_.rect.y = y_ - (text_->getCharacterSize() / 2);
// Establecemos los valores del rectangulo del selector a partir de los valores del rectangulo de fondo
setSelectorPos(selector_.index);
}
// Establece el color del rectangulo de fondo
void Menu::setBackgroundColor(Color color, int alpha) {
rect_bg_.color = color;
rect_bg_.a = alpha;
}
// Establece el color del rectangulo del selector
void Menu::setSelectorColor(Color color, int alpha) {
selector_.color = color;
selector_.a = alpha;
}
// Establece el color del texto del selector
void Menu::setSelectorTextColor(Color color) {
selector_.item_color = color;
}
// Centra el menu respecto un punto en el eje X
void Menu::centerMenuOnX(int value) {
is_centered_on_x_ = true;
if (value != 0) {
center_x_ = value;
} else if (center_x_ == 0) {
return;
}
// Establece la nueva posición centrada en funcion del elemento más ancho o del ancho fijo del menu
if (w_ != 0) { // Si se ha definido un ancho fijo
x_ = (center_x_) - (w_ / 2);
} else { // Si se actua en función del elemento más ancho
x_ = (center_x_) - (findWidth() / 2);
}
// Actualiza el rectangulo de fondo y del selector
rect_bg_.rect.x = x_;
selector_.rect.x = x_;
// Reposiciona los elementos del menu
for (auto &i : items_) {
i.rect.x = x_;
}
// Recalcula el rectangulo de fondo
setRectSize();
// Vuelve a centrar los elementos si fuera el caso
if (are_elements_centered_on_x_) {
centerMenuElementsOnX();
}
}
// Centra el menu respecto un punto en el eje Y
void Menu::centerMenuOnY(int value) {
is_centered_on_y_ = true;
center_y_ = 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() {
are_elements_centered_on_x_ = true;
for (auto &i : items_) {
i.rect.x = (center_x_ - (i.rect.w / 2));
}
}
// Añade un item al menu
void Menu::addItem(Item new_item) {
if (items_.empty()) { // Si es el primer item coge la posición en el eje Y del propio menu
new_item.rect.y = y_;
} else { // En caso contrario, coge la posición en el eje Y a partir del último elemento
new_item.rect.y = items_.back().rect.y + items_.back().rect.h + items_.back().h_padding_down;
}
new_item.rect.x = x_;
items_.push_back(new_item);
setItemCaption(items_.size() - 1, new_item.label);
if (items_.size() > 1) {
if (items_[items_.size() - 2].linked_down) {
items_.back().linked_up = true;
}
}
center_x_ = x_ + (findWidth() / 2);
reorganize();
}
// Cambia el texto de un item
void Menu::setItemCaption(int index, const std::string &text) {
items_[index].label = text;
items_[index].rect.w = this->text_->lenght(items_[index].label);
items_[index].rect.h = this->text_->getCharacterSize();
reorganize();
}
// Establece el indice del itemm que se usará por defecto al cancelar el menu
void Menu::setDefaultActionWhenCancel(int item) {
default_action_when_cancel_ = item;
}
// Gestiona la entrada de teclado y mando durante el menu
void Menu::checkInput() {
if (Input::get()->checkInput(Input::Action::UP, Input::Repeat::OFF)) {
decreaseSelectorIndex();
if (sound_move_ != nullptr) {
Audio::get()->playSound(sound_move_);
}
}
if (Input::get()->checkInput(Input::Action::DOWN, Input::Repeat::OFF)) {
increaseSelectorIndex();
if (sound_move_ != nullptr) {
Audio::get()->playSound(sound_move_);
}
}
if (Input::get()->checkInput(Input::Action::ACCEPT, Input::Repeat::OFF)) {
item_selected_ = selector_.index;
if (sound_accept_ != nullptr) {
Audio::get()->playSound(sound_accept_);
}
}
if (Input::get()->checkInput(Input::Action::CANCEL, Input::Repeat::OFF)) {
item_selected_ = default_action_when_cancel_;
if (sound_cancel_ != nullptr) {
Audio::get()->playSound(sound_cancel_);
}
}
}
// Calcula el ancho del menu
auto Menu::findWidth() -> int {
return getWidestItem();
}
// Calcula el alto del menu
auto Menu::findHeight() -> int {
const int HEIGHT = std::accumulate(items_.begin(), items_.end(), 0, [](int acc, const Item &i) { return acc + i.rect.h + i.h_padding_down; });
return HEIGHT - items_.back().h_padding_down;
}
// Recoloca los elementos del menu en el eje Y
void Menu::replaceElementsOnY() {
items_[0].rect.y = y_;
for (int i = 1; i < (int)items_.size(); i++) {
items_[i].rect.y = items_[i - 1].rect.y + items_[i - 1].rect.h + items_[i - 1].h_padding_down;
}
}
// Establece el estado seleccionable de un item
void Menu::setSelectable(int index, bool value) {
items_[index].selectable = value;
}
// Establece el estado agrisado de un item
void Menu::setGreyed(int index, bool value) {
items_[index].greyed = value;
}
// Establece el estado de enlace de un item
void Menu::setLinkedDown(int index, bool value) {
items_[index].linked_down = value;
}
// Establece el estado de visibilidad de un item
void Menu::setVisible(int index, bool value) {
items_[index].visible = value;
}
// Calcula la altura del selector
auto Menu::getSelectorHeight(int value) -> int {
if (items_[value].linked_down) {
return items_[value].rect.h + items_[value].h_padding_down + items_[value + 1].rect.h;
}
return items_[value].rect.h;
}
// Establece el nombre del menu
void Menu::setName(const std::string &name) {
this->name_ = name;
}
// Establece la posición del menu
void Menu::setPos(int x, int y) {
this->x_ = x;
this->y_ = y;
}
// Establece el tipo de fondo del menu
void Menu::setBackgroundType(Background value) {
background_type_ = value;
}
// Establece la fuente de texto que se utilizará
void Menu::setText(const std::string &font_png, const std::string &font_txt) {
if (text_ == nullptr) {
text_ = new Text(Asset::get()->get(font_png), Asset::get()->get(font_txt), renderer_);
}
}
// Calcula los colores del selector para el degradado
void Menu::setSelectorItemColors() {
const Color COLOR_FROM = {255, 255, 255};
const Color COLOR_TO = selector_.item_color;
for (int i = 0; i < selector_.num_jumps; ++i) {
const float STEP = ((float)i / (selector_.num_jumps - 1));
const int R = COLOR_FROM.r + ((COLOR_TO.r - COLOR_FROM.r) * STEP);
const int G = COLOR_FROM.g + ((COLOR_TO.g - COLOR_FROM.g) * STEP);
const int B = COLOR_FROM.b + ((COLOR_TO.b - COLOR_FROM.b) * STEP);
selector_.jump_item_colors[i].r = R;
selector_.jump_item_colors[i].g = G;
selector_.jump_item_colors[i].b = B;
}
selector_.item_color_index = 0;
}