#include "on_screen_help.h" #include // for SDL_BLENDMODE_BLEND #include // for SDL_PIXELFORMAT_RGBA8888 #include // for make_unique, unique_ptr #include // for basic_string #include "asset.h" // for Asset #include "lang.h" // for getText #include "param.h" // for param #include "screen.h" // for Screen #include "sprite.h" // for Sprite #include "text.h" // for Text #include "texture.h" // for Texture #include "utils.h" // for easeInOutSine, ParamGame, Param // [SINGLETON] Hay que definir las variables estáticas, desde el .h sólo la hemos declarado OnScreenHelp *OnScreenHelp::onScreenHelp = nullptr; // Constantes evaluables en tiempo de compilación constexpr int ICONSIZE = 16; constexpr SDL_Point PADDING = {8, 8}; constexpr SDL_Point DESP = {ICONSIZE + 4, 5}; constexpr SDL_Rect CONTROLLER_UP = {16, 0, ICONSIZE, ICONSIZE}; constexpr SDL_Rect CONTROLLER_DOWN = {48, 0, ICONSIZE, ICONSIZE}; constexpr SDL_Rect CONTROLLER_LEFT = {64, 0, ICONSIZE, ICONSIZE}; constexpr SDL_Rect LEFT_BUTTON = {112, 0, ICONSIZE, ICONSIZE}; constexpr SDL_Rect TOP_BUTTON = {80, 0, ICONSIZE, ICONSIZE}; constexpr SDL_Rect RIGHT_BUTTON = {96, 0, ICONSIZE, ICONSIZE}; constexpr SDL_Rect START_BUTTON = {0, 0, ICONSIZE, ICONSIZE}; constexpr SDL_Rect CONTROLLER_UP_POS = {PADDING.x, PADDING.y + 18 * 0, ICONSIZE, ICONSIZE}; constexpr SDL_Rect CONTROLLER_DOWN_POS = {PADDING.x, PADDING.y + 18 * 1, ICONSIZE, ICONSIZE}; constexpr SDL_Rect CONTROLLER_LEFT_POS = {PADDING.x, PADDING.y + 18 * 2, ICONSIZE, ICONSIZE}; constexpr SDL_Rect LEFT_BUTTON_POS = {PADDING.x, PADDING.y + 18 * 3, ICONSIZE, ICONSIZE}; constexpr SDL_Rect TOP_BUTTON_POS = {PADDING.x, PADDING.y + 18 * 4, ICONSIZE, ICONSIZE}; constexpr SDL_Rect RIGHT_BUTTON_POS = {PADDING.x, PADDING.y + 18 * 5, ICONSIZE, ICONSIZE}; constexpr SDL_Rect START_BUTTON_POS = {PADDING.x, PADDING.y + 18 * 6, ICONSIZE, ICONSIZE}; // [SINGLETON] Crearemos el objeto onScreenHelp con esta función estática void OnScreenHelp::init() { OnScreenHelp::onScreenHelp = new OnScreenHelp(); } // [SINGLETON] Destruiremos el objeto onScreenHelp con esta función estática void OnScreenHelp::destroy() { delete OnScreenHelp::onScreenHelp; } // [SINGLETON] Con este método obtenemos el objeto onScreenHelp y podemos trabajar con él OnScreenHelp *OnScreenHelp::get() { return OnScreenHelp::onScreenHelp; } // Constructor OnScreenHelp::OnScreenHelp() : state(OnScreenHelpStatus::hidden), index(0) { setSize(); texture = SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, dest.w, dest.h); SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); fillTexture(); precalculatePath(hiddenPos, showingPos, 60); } // Destructor OnScreenHelp::~OnScreenHelp() { SDL_DestroyTexture(texture); } // Actualiza la lógica interna void OnScreenHelp::update() { // Actualiza la posición updatePosition(); } // Muestra el objeto en pantalla void OnScreenHelp::render() { if (state != OnScreenHelpStatus::hidden) { SDL_RenderCopy(Screen::get()->getRenderer(), texture, nullptr, &dest); } } // Rellena la textura con los gráficos y texto void OnScreenHelp::fillTexture() { // Cambia el renderizador a la textura auto temp = SDL_GetRenderTarget(Screen::get()->getRenderer()); SDL_SetRenderTarget(Screen::get()->getRenderer(), texture); // Crea el objeto para el texto auto text = std::make_unique(Asset::get()->get("8bithud.png"), Asset::get()->get("8bithud.txt"), Screen::get()->getRenderer()); // Crea la textura con los gráficos auto controllersTexture = std::make_shared(Screen::get()->getRenderer(), Asset::get()->get("controllers.png")); // Crea el sprite para dibujar los gráficos auto sprite = std::make_unique((SDL_Rect){0, 0, 16, 16}, controllersTexture); // Borra la textura SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0); SDL_RenderClear(Screen::get()->getRenderer()); // Pon el color de fondo con el bisel SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0x55, 0x5D, 0x77, 255); SDL_Rect rect; rect = {4, 0, dest.w - (4 * 2), dest.h}; SDL_RenderFillRect(Screen::get()->getRenderer(), &rect); rect = {4 / 2, 1, dest.w - 4, dest.h - 2}; SDL_RenderFillRect(Screen::get()->getRenderer(), &rect); rect = {1, 4 / 2, dest.w - 2, dest.h - 4}; SDL_RenderFillRect(Screen::get()->getRenderer(), &rect); rect = {0, 4, dest.w, dest.h - (4 * 2)}; SDL_RenderFillRect(Screen::get()->getRenderer(), &rect); rect = {0, 0, dest.w / 2, dest.h}; SDL_RenderFillRect(Screen::get()->getRenderer(), &rect); // Renderiza los botones y el texto renderButton(sprite.get(), text.get(), CONTROLLER_UP, CONTROLLER_UP_POS, 107); renderButton(sprite.get(), text.get(), CONTROLLER_DOWN, CONTROLLER_DOWN_POS, 108); renderButton(sprite.get(), text.get(), CONTROLLER_LEFT, CONTROLLER_LEFT_POS, 109); renderButton(sprite.get(), text.get(), LEFT_BUTTON, LEFT_BUTTON_POS, 110); renderButton(sprite.get(), text.get(), TOP_BUTTON, TOP_BUTTON_POS, 111); renderButton(sprite.get(), text.get(), RIGHT_BUTTON, RIGHT_BUTTON_POS, 112); renderButton(sprite.get(), text.get(), START_BUTTON, START_BUTTON_POS, 113); // Restaura el destino de renderizado SDL_SetRenderTarget(Screen::get()->getRenderer(), temp); } // Define el ancho y alto de la textura void OnScreenHelp::setSize() { const auto textSize = getLargestStringSize(); const auto width = PADDING.x + DESP.x + textSize + PADDING.x; const auto height = PADDING.y + (7 * 18) + PADDING.y; const auto x = 0; const auto y = (param.game.height - height) / 2; dest = (SDL_Rect){x, y, width, height}; hiddenPos = -width; showingPos = 0; } // Activa o desactiva el objeto void OnScreenHelp::toggleState() { if (state == OnScreenHelpStatus::showing || state == OnScreenHelpStatus::entering) { state = OnScreenHelpStatus::exitting; } else if (state == OnScreenHelpStatus::hidden || state == OnScreenHelpStatus::exitting) { state = OnScreenHelpStatus::entering; } } // Calcula la longitud en pixels del texto más largo auto OnScreenHelp::getLargestStringSize() -> int const { auto text = std::make_unique(Asset::get()->get("8bithud.png"), Asset::get()->get("8bithud.txt"), Screen::get()->getRenderer()); auto size = 0; for (int i = 107; i <= 113; ++i) { const auto textSize = text->lenght(lang::getText(i)); size = textSize > size ? textSize : size; } return size; } // Renderizara el boton y el texto void OnScreenHelp::renderButton(Sprite *sprite, Text *text, const SDL_Rect &buttonClip, const SDL_Rect &buttonPos, int textId) { sprite->setSpriteClip(buttonClip); sprite->setPos(buttonPos); sprite->render(); text->write(buttonPos.x + DESP.x, buttonPos.y + DESP.y, lang::getText(textId)); } // Actualiza la posición void OnScreenHelp::updatePosition() { switch (state) { case OnScreenHelpStatus::hidden: { index = 0; break; } case OnScreenHelpStatus::showing: { index = path.size() - 1; break; } case OnScreenHelpStatus::exitting: { if (index > 0) { index--; } else { state = OnScreenHelpStatus::hidden; } break; } case OnScreenHelpStatus::entering: { if (index < (int)path.size() - 1) { index++; } else { state = OnScreenHelpStatus::showing; } break; } default: break; } dest.x = path[index]; } // Rellena los puntos por donde pasa la animación void OnScreenHelp::precalculatePath(double start, double end, int steps) { path.reserve(steps); for (int i = 0; i <= steps; ++i) { double t = static_cast(i) / steps; double value = start + (end - start) * easeInOutSine(t); path.push_back(static_cast(value)); } }