Nimbin[12]?SDL & Graphics / mapcreator / src/game/state_menu_main.cpp

mapcreator git · master

SDL3 2.5D game and engine using assets, with map editor

sdl3 c++ game engine map-editor cmake · first commit 2025-10-31 · last commit 2026-09-07 (1 month ago) · synced 3 days ago · upstream: git.ide3.de/hsnr/sdl-spieleentwicklung/map_creator

C++ 99.3%
git clone https://git.christianimmanuel.de/sdl-graphics/mapcreator.gitwget https://git.christianimmanuel.de/sdl-graphics/mapcreator/archive/mapcreator.tar.gz
src/game/state_menu_main.cpp 6.3 KB · 190 lines 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);
    }
}