mapcreator git · master
SDL3 2.5D game and engine using assets, with map editor
C++ 99.3%git clone https://git.christianimmanuel.de/sdl-graphics/mapcreator.gitwget https://git.christianimmanuel.de/sdl-graphics/mapcreator/archive/mapcreator.tar.gzsrc/game/state_menu_main.cpp raw
#include "state_menu_main.hpp"
#include "defaults/ui.hpp"
#include "game/game.hpp"
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <vector>
#include <string>
StateMenuMain::StateMenuMain() {}
StateMenuMain::~StateMenuMain() {}
static SDL_FRect menu_rect{};
bool StateMenuMain::Init(Game* game) {
audio = game->audio;
game->running = game->running;
// Set up buttons
buttons.push_back({ MenuState::CONTI, "Continue", {300, 200, 200, 50}, false});
buttons.push_back({ MenuState::SAVE, "Save Game", {300, 260, 200, 50}, false});
buttons.push_back({ MenuState::LOAD, "Load Game", {300, 260, 200, 50}, false});
buttons.push_back({ MenuState::NEW, "New Game", {300, 200, 200, 50}, false});
buttons.push_back({ MenuState::QUIT, "Quit", {300, 320, 200, 50}, false});
buttons.push_back({ MenuState::NEW_CHEAT, "New Cheat", {300, 200, 200, 50}, false});
cursor = game->cursor;
cursor->set_State(CursorState::ANIMATED);
return true;
}
void runState(MenuState state, Game* game) {
switch(state) {
case MenuState::CONTI: game->set_State(GameState_e::STATE_PLAYING, NewGameState_e::NONE); break;
case MenuState::SAVE: game->Save(game->active_game_dir); break;
case MenuState::LOAD: game->set_State(GameState_e::STATE_PLAYING, NewGameState_e::LOAD); break;
case MenuState::NEW: game->is_cheating = false; game->set_State(GameState_e::STATE_PLAYING, NewGameState_e::NEW); break;
case MenuState::QUIT: game->running = false; break;
case MenuState::NEW_CHEAT: game->is_cheating = true; game->set_State(GameState_e::STATE_PLAYING, NewGameState_e::NEW); break;
}
}
void StateMenuMain::handle_Events(SDL_Event& event, Game* game) {
if(event.type == SDL_EVENT_MOUSE_MOTION) {
float mx = event.motion.x;
float my = event.motion.y;
for(auto& btn : buttons) {
SDL_FPoint p = {mx, my};
btn.hovered = SDL_PointInRectFloat(&p, &btn.rect);
}
}
else if(event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT) {
for(size_t i=0; i<buttons.size(); ++i) {
if(buttons[i].hovered) {
runState(buttons[i].state, game);
return;
}
}
}
else if(event.type == SDL_EVENT_KEY_DOWN) {
switch (event.key.scancode) {
case SDL_SCANCODE_DOWN:
selected_index = selected_index+1 % buttons.size();
break;
case SDL_SCANCODE_UP:
selected_index = (selected_index-1 + buttons.size()) % buttons.size();
break;
case SDL_SCANCODE_RETURN:
runState(buttons[selected_index].state, game);
break;
default:
break;
}
}
}
void StateMenuMain::Update(Game* game) {
// Button layout configuration
size_t button_width = 260;
size_t button_height = 70;
size_t button_gap = 25; // space between buttons
size_t padding = 40; // top and bottom padding inside the menu panel
size_t total_height =
buttons.size() * (button_height + button_gap) - button_gap
+ padding * 2;
size_t start_x = static_cast<size_t>(game->win_size.w) / 2 - button_width / 2;
size_t start_y = static_cast<size_t>(game->win_size.h) / 2 - total_height / 2 + padding;
static int last_clicked = -1;
bool any_button_hovered = false;
for(size_t i = 0; i < buttons.size(); ++i) {
if(buttons[i].hovered) {
any_button_hovered = true;
if ( (int)i != last_clicked ) {
if (audio->is_Playing("menu.hover") )
audio->Stop("menu.hover");
audio->Play("menu.hover");
last_clicked = i;
}
}
buttons[i].rect.x = start_x;
buttons[i].rect.y = start_y + i * (button_height + button_gap);
buttons[i].rect.w = button_width;
buttons[i].rect.h = button_height;
}
if (!any_button_hovered) {
last_clicked = -1;
if (audio->is_Playing("menu.hover") )
audio->Stop("menu.hover");
}
// Store menu_rect for rendering
menu_rect.x = start_x - 30;
menu_rect.y = start_y - padding;
menu_rect.w = button_width + 60;
menu_rect.h = total_height;
}
void StateMenuMain::Render(Game* game) {
SDL_Renderer* r = game->renderer;
// Fullscreen dim background
SDL_SetRenderDrawBlendMode(r, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(r, 20, 20, 35, 180);
SDL_FRect dim = {0, 0, (float)game->win_size.w, (float)game->win_size.h};
SDL_RenderFillRect(r, &dim);
// Menu background
SDL_SetRenderDrawColor(r, 40, 40, 70, 230);
SDL_RenderFillRect(r, &menu_rect);
// Menu border
SDL_SetRenderDrawColor(r, 120, 120, 170, 255);
SDL_RenderRect(r, &menu_rect);
for(int i = 1; i < 6; ++i) {
SDL_FRect inner = {
menu_rect.x + i,
menu_rect.y + i,
menu_rect.w - 2*i,
menu_rect.h - 2*i
};
SDL_RenderRect(r, &inner);
}
// Render buttons
for(size_t i = 0; i < buttons.size(); ++i) {
auto& btn = buttons[i];
// Border
float bd_w = 3;
SDL_FRect bd = {btn.rect.x-bd_w, btn.rect.y-bd_w, btn.rect.w+bd_w*2, btn.rect.h+bd_w*2 };
SDL_SetRenderDrawColor(r, 70, 70, 120, 255);
SDL_RenderFillRect(r, &bd);
// Button background
if(btn.hovered)
SDL_SetRenderDrawColor(r, 90, 170, 220, 255);
else
if(i == static_cast<size_t>(selected_index))
SDL_SetRenderDrawColor(r, 80, 120, 170, 255);
else
SDL_SetRenderDrawColor(r, 70, 70, 120, 255);
SDL_RenderFillRect(r, &btn.rect);
// Button text
SDL_Color color = {255, 255, 255, 255};
SDL_Surface* surf = TTF_RenderText_Blended(Ui::font_menu_bold, btn.text.c_str(), btn.text.length(), color);
SDL_Texture* tex = SDL_CreateTextureFromSurface(r, surf);
float tw, th;
SDL_GetTextureSize(tex, &tw, &th);
SDL_FRect dst = {
btn.rect.x + (btn.rect.w - tw) * 0.5f,
btn.rect.y + (btn.rect.h - th) * 0.5f,
tw,
th
};
SDL_RenderTexture(r, tex, nullptr, &dst);
SDL_DestroyTexture(tex);
SDL_DestroySurface(surf);
}
}