Nimbin[12]?SDL & Graphics / mapcreator / src/building/research.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/building/research.cpp 16 KB · 434 lines raw
#include "building/research.hpp"

#include "defaults/ui.hpp"
#include "defaults/icons.hpp"
#include "building/building.hpp"


namespace Research {
    const int MENU_BG_COLOR_R = 0;
    const int MENU_BG_COLOR_G = 0;
    const int MENU_BG_COLOR_B = 0;
    const int MENU_BG_COLOR_A = 182;

    const int   MENU_PADDING = 16;
    const float MENU_INNER_W = 800;
    const float MENU_INNER_H = 600;
    const float MENU_W = MENU_PADDING * 2 + MENU_INNER_W;
    const float MENU_H = MENU_PADDING * 2 + MENU_INNER_H;
    const int   MENU_ITEM_SPACING = 6;
    const float MENU_TILE_SIZE = 48.0f;
    const float MENU_INFO_W    = 300.0f;

    const float MENU_BORDER_OUTER     = 4.0f;
    const float MENU_BORDER_SEPARATOR = 4.0f;
    const float MENU_TILE_BORDER      = 2.0f;
    const int MENU_COLS = 6;
    const int MENU_ROWS = 8;

    SDL_FRect MENU_RESEARCH_BUTTON = {0,0,0,0};
    SDL_FRect MENU_INFO_PANEL      = {0,0,0,0};
    SDL_Color MENU_HOVER_BORDER_C = Default::menu_hovering_color;

    const float RESEARCH_DURATION_MAX = 2.2f;




    bool is_researching = false;
    Researching research_item;


    std::vector<BuildingType> research_buildings;
    std::vector<ItemType>     research_items;

    void
    draw_ReserachMenu(SDL_Renderer* renderer, SDL_Rect win_size, BuildingType type, std::vector<bool>& researched_buildings, std::vector<bool>& researched_items, bool hovering_research_button) {
        BuildingData_s building_s = BuildingData::building_data[static_cast<size_t>(type)];
        const float menu_x = static_cast<float>(win_size.w/2) - MENU_W/2;
        const float menu_y = static_cast<float>(win_size.h/2) - MENU_H/2;

        const float inner_x = menu_x + MENU_BORDER_OUTER;
        const float inner_y = menu_y + MENU_BORDER_OUTER;
        const float inner_w = MENU_W - MENU_BORDER_OUTER * 2;
        const float inner_h = MENU_H - MENU_BORDER_OUTER * 2;

        const float info_x = inner_x + inner_w - MENU_INFO_W;

        const float sep_x = info_x - MENU_BORDER_SEPARATOR;

        const float grid_x = inner_x;
        const float grid_y = inner_y;
        const float grid_w = sep_x - inner_x;
        const float grid_h = inner_h;

        // MENU BACKGROUND
        SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
        SDL_SetRenderDrawColor(renderer,
            MENU_BG_COLOR_R, MENU_BG_COLOR_G,
            MENU_BG_COLOR_B, MENU_BG_COLOR_A
        );

        SDL_FRect bg = { menu_x, menu_y, MENU_W, MENU_H };
        SDL_RenderFillRect(renderer, &bg);

        // LEFT RIGHT SEPARATOR
        SDL_FRect separator_bar = { sep_x, inner_y, MENU_BORDER_SEPARATOR, inner_h };

        SDL_SetRenderDrawColor(renderer, 80, 80, 80, 255);
        SDL_RenderFillRect(renderer, &separator_bar);

        // HEADER BAR
        const float header_h = 40.0f;
        SDL_FRect header_rect = { grid_x, grid_y, grid_w, header_h };

        SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);
        SDL_RenderFillRect(renderer, &header_rect);

        const std::string header_text =
            building_s.title +
            std::string(" | Level ") +
            std::to_string(BuildingData::get_WorkbenchLevel(building_s.type));

        int text_w = 0;
        int text_h = 0;
        TTF_GetStringSize(Ui::font_menu, header_text.c_str(), header_text.size(), &text_w, &text_h);

        float text_x = header_rect.x + (header_rect.w - text_w) * 0.5f;
        float text_y = header_rect.y + (header_h - text_h) * 0.5f;

        SDL_FRect text_dest = { text_x, text_y, static_cast<float>(text_w), static_cast<float>(text_h) };

        SDL_Color text_color = { 230, 230, 230, 255 };
        SDL_Surface* surface_text = TTF_RenderText_Blended(Ui::font_menu, header_text.c_str(), header_text.size(), text_color);
        if (!surface_text) {
            SDL_DestroySurface(surface_text);
            return;
        }

        SDL_Texture* texture_text = SDL_CreateTextureFromSurface(renderer, surface_text);
        if (!texture_text) {
            SDL_DestroySurface(surface_text);
            return;
        }
        SDL_RenderTexture(renderer, texture_text, nullptr, &text_dest);

        SDL_DestroyTexture(texture_text);


        // GRID
        const float tile_total = MENU_TILE_SIZE + MENU_TILE_BORDER * 2;

        const float grid_required_w = MENU_COLS * tile_total + (MENU_COLS - 1) * MENU_ITEM_SPACING;
        const float grid_required_h = MENU_ROWS * tile_total + (MENU_ROWS - 1) * MENU_ITEM_SPACING;

        const float grid_content_y = grid_y + header_h;
        const float grid_start_x   = grid_x + (grid_w - grid_required_w) * 0.5f;
        const float grid_start_y   = grid_content_y + (grid_h - header_h - grid_required_h) * 0.5f;

        size_t b_count = research_buildings.size();
        size_t i_count = research_items.size();
        size_t slot_index = 0;
        //std::cout << "BC " << b_count << " IC " << i_count << std::endl;

        int active_b_el = -1;
        int active_i_el = -1;

        auto draw_checkmark = [&](SDL_Renderer* renderer, const SDL_FRect& rect) {
            auto it = Icon::icon_pos.find(IconType::CHECKMARK);
            SDL_FRect src{ it->second.x * 16.f, it->second.y * 16.f, 16.f, 16.f };
            SDL_RenderTexture(renderer, Icon::icons_texture, &src, &rect);
        };

        for (int r = 0; r < MENU_ROWS; ++r) {
            for (int c = 0; c < MENU_COLS; ++c) {
                if (Building::draw_menu_curser_grid_active == r*MENU_COLS+c) {
                    if (slot_index < b_count)
                        active_b_el = slot_index;
                    else
                        active_i_el = slot_index - b_count;
                }

                float x = grid_start_x + c * (tile_total + MENU_ITEM_SPACING);
                float y = grid_start_y + r * (tile_total + MENU_ITEM_SPACING);

                SDL_FRect border_rect{ x, y, tile_total, tile_total };
                SDL_FRect tile_rect{
                    x + MENU_TILE_BORDER,
                      y + MENU_TILE_BORDER,
                      MENU_TILE_SIZE,
                      MENU_TILE_SIZE
                };

                if (Building::draw_menu_curser_grid_active == static_cast<int>(slot_index))
                    SDL_SetRenderDrawColor(renderer, 20, 215, 0, 255);
                else if (Building::draw_menu_curser_grid == static_cast<int>(slot_index))
                    SDL_SetRenderDrawColor(renderer, MENU_HOVER_BORDER_C.r, MENU_HOVER_BORDER_C.g, MENU_HOVER_BORDER_C.b, MENU_HOVER_BORDER_C.a);
                else
                    SDL_SetRenderDrawColor(renderer, 20, 20, 20, 255);
                SDL_RenderFillRect(renderer, &border_rect);

                SDL_SetRenderDrawColor(renderer, 60, 60, 60, 255);
                SDL_RenderFillRect(renderer, &tile_rect);

                if (slot_index < b_count) {
                    BuildingType bt = research_buildings[slot_index];
                    auto it = Icon::building_pos.find(bt);
                    if (it != Icon::building_pos.end()) {
                        SDL_FRect src{ it->second.x * 16.f, it->second.y * 16.f, 16.f, 16.f };
                        SDL_RenderTexture(renderer, Icon::icons_texture, &src, &tile_rect);
                    }
                    if (researched_buildings[static_cast<size_t>(bt)]) {
                        draw_checkmark(renderer, tile_rect);
                    }

                } else if (slot_index < b_count + i_count) {
                    ItemType itype = research_items[slot_index - b_count];
                    auto it2 = Icon::item_pos.find(itype);
                    if (it2 != Icon::item_pos.end()) {
                        SDL_FRect src{ it2->second.x * 16.f, it2->second.y * 16.f, 16.f, 16.f };
                        SDL_RenderTexture(renderer, Icon::icons_texture, &src, &tile_rect);
                    }
                    if (researched_items[static_cast<size_t>(itype)]) {
                        draw_checkmark(renderer, tile_rect);
                    }
                }

                slot_index++;
            }
        }

        // INFO PANEL
        std::string info_title;
        if (active_b_el > -1 && active_b_el < static_cast<int>(research_buildings.size())) {
            info_title = BuildingData::building_data[static_cast<size_t>(research_buildings[static_cast<size_t>(active_b_el)])].title;
        } else if (active_i_el > -1 && active_i_el < static_cast<int>(research_items.size())) {
            info_title = Item::stats[static_cast<size_t>(research_items[static_cast<size_t>(active_i_el)])].title;
        }


        SDL_FRect info_panel = { info_x, inner_y, MENU_INFO_W, inner_h };
        MENU_INFO_PANEL = info_panel;
        SDL_SetRenderDrawColor(renderer, 40, 40, 40, 255);
        SDL_RenderFillRect(renderer, &info_panel);



        // INFO TEXT
        int tw = 0;
        int th = 0;
        TTF_GetStringSize(Ui::font_menu, info_title.c_str(), info_title.size(), &tw, &th);

        const float t_x = info_panel.x + (info_panel.w - tw) * 0.5f;
        const float t_y = info_panel.y + MENU_BORDER_OUTER;

        SDL_Color t_color = { 230, 230, 230, 255 };

        SDL_Surface* s = TTF_RenderText_Blended(Ui::font_menu, info_title.c_str(), info_title.size(), t_color);
        if (!s) return;

        SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, s);
        if (!tex) {
            SDL_DestroySurface(s);
            return;
        }

        SDL_FRect dst = { t_x, t_y, static_cast<float>(tw), static_cast<float>(th) };
        SDL_RenderTexture(renderer, tex, nullptr, &dst);

        SDL_DestroyTexture(tex);

        // RESEARCH BUTTON
        float btn_h = 36.0f;
        float btn_w = MENU_INFO_W - MENU_BORDER_OUTER * 2;
        float btn_x = info_x + MENU_BORDER_OUTER;
        float btn_y = inner_y + inner_h - btn_h - MENU_BORDER_OUTER;

        SDL_FRect btn_rect = { btn_x, btn_y, btn_w, btn_h };
        SDL_FRect btn_rect_bg = { btn_x-2, btn_y-2, btn_w+4, btn_h+4 };
        MENU_RESEARCH_BUTTON = btn_rect;

        if (hovering_research_button)
            SDL_SetRenderDrawColor(renderer, MENU_HOVER_BORDER_C.r, MENU_HOVER_BORDER_C.g, MENU_HOVER_BORDER_C.b, MENU_HOVER_BORDER_C.a);
        else
            SDL_SetRenderDrawColor(renderer, 0,0,0, 255);
        SDL_RenderFillRect(renderer, &btn_rect_bg);

        SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);
        SDL_RenderFillRect(renderer, &btn_rect);

        // Draw progress
        if (Research::research_item.is_researching) {
            float progress = Research::research_item.duration / RESEARCH_DURATION_MAX;
            SDL_FRect progress_rect = { btn_x, btn_y, btn_w * progress, btn_h };
            SDL_SetRenderDrawColor(renderer, 100, 200, 100, 255);
            SDL_RenderFillRect(renderer, &progress_rect);
        }

        // Button label
        const std::string btn_text = "Research";

        int r_w = 0;
        int r_h = 0;
        TTF_GetStringSize(Ui::font_menu, btn_text.c_str(), btn_text.size(), &r_w, &r_h);

        float r_x = btn_x + (btn_w - r_w) * 0.5f;
        float r_y = btn_y + (btn_h - r_h) * 0.5f;

        SDL_FRect r_dest = { r_x, r_y, static_cast<float>(r_w), static_cast<float>(r_h) };

        SDL_Color r_color = { 230, 230, 230, 255 };
        SDL_Surface* r_surface = TTF_RenderText_Blended(Ui::font_menu, btn_text.c_str(), btn_text.size(), r_color);
        if (!r_surface) {
            SDL_DestroySurface(r_surface);
            return;
        }

        SDL_Texture* r_texture = SDL_CreateTextureFromSurface(renderer, r_surface);
        if (!r_texture) {
            SDL_DestroySurface(r_surface);
            return;
        }

        SDL_RenderTexture(renderer, r_texture, nullptr, &r_dest);

        SDL_DestroyTexture(r_texture);
    }

    bool
    is_CursorOverMenuInfoPanel(const SDL_FPoint mouse_pos) {
        return (
            mouse_pos.x >= MENU_INFO_PANEL.x &&
            mouse_pos.x <= MENU_INFO_PANEL.x + MENU_INFO_PANEL.w &&
            mouse_pos.y >= MENU_INFO_PANEL.y &&
            mouse_pos.y <= MENU_INFO_PANEL.y + MENU_INFO_PANEL.h
        );
    }


    bool
    cursor_OverResearchButton(const SDL_FPoint mouse_pos) {
        return (
                mouse_pos.x >= MENU_RESEARCH_BUTTON.x &&
                mouse_pos.x <= MENU_RESEARCH_BUTTON.x + MENU_RESEARCH_BUTTON.w &&
                mouse_pos.y >= MENU_RESEARCH_BUTTON.y &&
                mouse_pos.y <= MENU_RESEARCH_BUTTON.y + MENU_RESEARCH_BUTTON.h
               );
    }

    int
    cursor_ResearchGridTileIndex(SDL_FPoint mouse, SDL_Rect win_size) {
        const float MENU_W = MENU_PADDING * 2 + MENU_INNER_W;
        const float MENU_H = MENU_PADDING * 2 + MENU_INNER_H;
        const float menu_x = static_cast<float>(win_size.w / 2) - MENU_W / 2;
        const float menu_y = static_cast<float>(win_size.h / 2) - MENU_H / 2;

        const float inner_x = menu_x + MENU_BORDER_OUTER;
        const float inner_y = menu_y + MENU_BORDER_OUTER;
        const float inner_w = MENU_W - MENU_BORDER_OUTER * 2;
        const float inner_h = MENU_H - MENU_BORDER_OUTER * 2;

        const float info_x = inner_x + inner_w - MENU_INFO_W;
        const float sep_x = info_x - MENU_BORDER_SEPARATOR;

        const float grid_x = inner_x;
        const float grid_y = inner_y;
        const float grid_w = sep_x - inner_x;
        const float grid_h = inner_h;

        const float header_h = 40.0f;

        const float tile_total = MENU_TILE_SIZE + MENU_TILE_BORDER * 2;
        const float grid_required_w = MENU_COLS * tile_total + (MENU_COLS - 1) * MENU_ITEM_SPACING;
        const float grid_required_h = MENU_ROWS * tile_total + (MENU_ROWS - 1) * MENU_ITEM_SPACING;

        const float grid_content_y = grid_y + header_h;
        const float grid_start_x = grid_x + (grid_w - grid_required_w) * 0.5f;
        const float grid_start_y = grid_content_y + (grid_h - header_h - grid_required_h) * 0.5f;

        if (mouse.x < grid_start_x ||
            mouse.y < grid_start_y ||
            mouse.x > grid_start_x + grid_required_w ||
            mouse.y > grid_start_y + grid_required_h) {
            return -1;
        }

        float rel_x = mouse.x - grid_start_x;
        float rel_y = mouse.y - grid_start_y;

        const float step = tile_total + MENU_ITEM_SPACING;

        int c = static_cast<int>(rel_x / step);
        int r = static_cast<int>(rel_y / step);

        if (c < 0 || c >= MENU_COLS || r < 0 || r >= MENU_ROWS) {
            return -1;
        }

        float cx = rel_x - c * step;
        float cy = rel_y - r * step;

        if (cx < MENU_TILE_BORDER || cx > MENU_TILE_BORDER + MENU_TILE_SIZE ||
            cy < MENU_TILE_BORDER || cy > MENU_TILE_BORDER + MENU_TILE_SIZE) {
            return -1;
        }

        return r * MENU_COLS + c;
    }




    bool
    research_Start() {
        if (!is_researching)
            return false;
        Researching item;

        size_t active_index = static_cast<size_t>(Building::draw_menu_curser_grid_active);

        size_t b_count = research_buildings.size();

        size_t index = active_index;
        if (active_index >= b_count) {
            // RESEACHING ITEM
            index = active_index - b_count;
            item.i_type  = Research::research_items[index];
            item.i_stats = Item::stats[static_cast<size_t>(item.i_type)];
        } else {
            item.b_type = Research::research_buildings[index];
            item.b_stats = BuildingData::building_data[static_cast<size_t>(item.b_type)];
        }

        item.is_researching = true;

        research_item = item;
        return true;
    }

    void
    research_Stop() {
        Researching item;
        research_item = item;
        is_researching = false;
    }

    bool
    research_Continue(std::vector<bool>& researched_buildings, std::vector<bool>& researched_items) {
        research_item.duration += Default::delta_time;
        if (research_item.duration < RESEARCH_DURATION_MAX)
            return false;

        std::cout << "Researched something!\n";
        if (research_item.i_type != ItemType::NONE) {
            researched_items[static_cast<size_t>(research_item.i_type)] = true;
        } else
        if (research_item.b_type != BuildingType::NONE) {
            researched_buildings[static_cast<size_t>(research_item.b_type)] = true;
        }

        research_Stop();
        return true;
    }

}