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/defaults/ui.cpp raw
#include "defaults/ui.hpp"
#include "inventory/inventory.hpp"
#include "game/game.hpp"
#include "character/character.hpp"
#include <iostream>
// Key description pairs
std::vector<std::pair<std::string, std::string>>
keyboard_layout = {
{ "9", "Game mode: play"},
{ "Scroll", "Zoom in or out"},
{ "ctrl+s", "Map save to file"},
{ "s", "Map scale"},
{ "r", "Map clear"},
{ "shift+k", "Map clear colliders"},
{ "ctrl+LeftClick", "Map move"},
{ "i", "Info toggle"},
{ "h", "Tileset toggle hide"},
{ "[ctrl,shift]m", "Tileset toggle"},
{ "k", "Toggle set collission"},
{ "o", "Toggle set object or floor"},
{ "ctrl+o", "Set properties object"},
{ "shift+o", "Set properites resource"},
{ "ctrl+k", "Collider transfer to map"},
{ "c+LeftClick", "Object clear"},
{ "shift+LeftClick", "Object select"},
{ "[ctrl,shift]+", "Selector increase"},
{ "[ctrl,shift]- ", "Selector decrease"},
{ "ctrl+q", "Exit game"}
};
std::vector<std::pair<std::string, std::string>>
keyboard_layout_playing = {
{ "f10", "Game mode: modify"},
{ "Scroll", "Zoom in or out"},
{ "0", "Zoom reset"},
{ "[w,a,s,d]", "Move in direction"},
{ "c", "Collect item | interact | plant"},
{ "i", "Info toggle"},
{ "e", "Inventory toggle"},
{ "b", "Build menu toggle"},
{ "k", "Building set deconstruct"},
{ "[1-6]", "Equipement set or select"},
{ "Left-Click", "Attack | Build | Deconstruct"},
{ "ctrl+q", "Exit game"}
};
namespace Ui {
bool draw_key_info = false;
// Font
TTF_Font* font_big = nullptr;
TTF_Font* font_small = nullptr;
TTF_Font* font_tiny = nullptr;
TTF_Font* font_menu = nullptr;
TTF_Font* font_top_bar = nullptr;
TTF_Font* font_menu_bold = nullptr;
TTF_Font* font_message = nullptr;
float top_bar_height = 40;
bool
init_Font() {
if (!TTF_Init()) {
std::cerr << "SDL_ttf initialization failed: " << SDL_GetError() << std::endl;
return false;
}
font_big = TTF_OpenFont("fonts/DejaVuSans-Bold.ttf", 28);
font_small = TTF_OpenFont("fonts/DejaVuSansMono.ttf", 30);
font_tiny = TTF_OpenFont("fonts/DejaVuSansMono.ttf", 12);
font_menu = TTF_OpenFont("fonts/DejaVuSansMono.ttf", 18);
font_menu_bold = TTF_OpenFont("fonts/DejaVuSansMono-Bold.ttf", 18);
font_top_bar = TTF_OpenFont("fonts/DejaVuSansMono-Bold.ttf", 28);
font_message = TTF_OpenFont("fonts/DejaVuSansMono.ttf", 22);
if (!font_big || !font_small) {
std::cerr << "Failed to load font: " << SDL_GetError() << std::endl;
return false;
}
return true;
}
void
delete_Font() {
TTF_CloseFont(font_big);
TTF_CloseFont(font_small);
TTF_CloseFont(font_top_bar);
}
void
draw_KeyInfo(Game* game) {
if (!game->renderer || !font_small || !font_big) return;
SDL_FRect rect_background_window = {0, 0, static_cast<float>(game->win_size.w), static_cast<float>(game->win_size.h)};
SDL_SetRenderDrawBlendMode(game->renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(game->renderer, 0, 0, 0, 200);
SDL_RenderFillRect(game->renderer, &rect_background_window);
SDL_Color text_color = {255, 255, 255, 255};
float line_spacing = 8.0f;
float total_height = 0.0f;
float max_key_width = 0.0f;
float max_desc_width = 0.0f;
std::vector<std::pair<std::string, std::string>>& layout =
(dynamic_cast<StatePlaying*>(game->state_current) != nullptr)
? keyboard_layout_playing
: keyboard_layout;
for (const auto& [key, desc] : layout) {
SDL_Surface* surface_key = TTF_RenderText_Blended(font_big, key.c_str(), key.length(), text_color);
SDL_Surface* surface_desc = TTF_RenderText_Blended(font_small, desc.c_str(), desc.length(), text_color);
if (!surface_key || !surface_desc) {
if (surface_key) SDL_DestroySurface(surface_key);
if (surface_desc) SDL_DestroySurface(surface_desc);
continue;
}
max_key_width = std::max(max_key_width, static_cast<float>(surface_key->w));
max_desc_width = std::max(max_desc_width, static_cast<float>(surface_desc->w));
total_height += std::max(surface_key->h, surface_desc->h) + line_spacing;
SDL_DestroySurface(surface_key);
SDL_DestroySurface(surface_desc);
}
total_height -= line_spacing;
// Center coordinates
float col_spacing = 50.0f;
float x_key = (game->win_size.w - (max_key_width + max_desc_width + col_spacing)) / 2.0f;
float x_desc = x_key + max_key_width + col_spacing;
float y = (game->win_size.h - total_height) / 2.0f;
// background
SDL_FPoint padding = {120.0f, 30.0f};
SDL_FRect rect_background = {x_key - padding.x, y - padding.y,
(max_key_width + max_desc_width + 20.0f) + 2*padding.x,
total_height + 2*padding.y};
SDL_SetRenderDrawBlendMode(game->renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(game->renderer, 0, 0, 0, 182);
SDL_RenderFillRect(game->renderer, &rect_background);
// Text
for (const auto& [key, desc] : layout) {
SDL_Surface* surface_key = TTF_RenderText_Blended(font_big, key.c_str(), key.length(), text_color);
if (!surface_key) continue;
SDL_Texture* texture_key = SDL_CreateTextureFromSurface(game->renderer, surface_key);
if (!texture_key) {
SDL_DestroySurface(surface_key);
continue;
}
float h = static_cast<float>(surface_key->h);
// Right-align the key
SDL_FRect dst_key = {x_key + max_key_width - static_cast<float>(surface_key->w), y,
static_cast<float>(surface_key->w), h};
SDL_RenderTexture(game->renderer, texture_key, nullptr, &dst_key);
SDL_DestroySurface(surface_key);
SDL_DestroyTexture(texture_key);
SDL_Surface* surface_desc = TTF_RenderText_Blended(font_small, desc.c_str(), desc.length(), text_color);
if (!surface_desc) continue;
SDL_Texture* texture_desc = SDL_CreateTextureFromSurface(game->renderer, surface_desc);
if (!texture_desc) {
SDL_DestroySurface(surface_desc);
continue;
}
SDL_FRect dst_desc = {x_desc, y, static_cast<float>(surface_desc->w), static_cast<float>(surface_desc->h)};
SDL_RenderTexture(game->renderer, texture_desc, nullptr, &dst_desc);
SDL_DestroySurface(surface_desc);
SDL_DestroyTexture(texture_desc);
y += h + line_spacing;
}
}
void draw_Frames(SDL_Renderer* renderer, SDL_Rect win_size) {
SDL_Color text_color = {255, 255, 255, 255};
// --- RIGHT SIDE: FRAMES ---
std::string res_text = "FPS: " + std::to_string(Default::fps);
SDL_Surface* surf = TTF_RenderText_Blended(font_top_bar, res_text.c_str(), res_text.length(), text_color);
if (!surf) return;
SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf);
if (!tex) {
SDL_DestroySurface(surf);
return;
}
float padding = 20.0f;
float x = static_cast<float>(win_size.w) - static_cast<float>(surf->w) - padding;
SDL_FRect rect = {
x,
6.0f,
static_cast<float>(surf->w),
static_cast<float>(surf->h)
};
SDL_RenderTexture(renderer, tex, nullptr, &rect);
SDL_DestroyTexture(tex);
SDL_DestroySurface(surf);
}
void draw_TopBarTileInfo(SDL_Renderer* renderer, SDL_Rect win_size) {
SDL_Color text_color = {255, 255, 255, 255};
std::string text;
text = std::string("Mode: ") + Default::get_ModifyStateName();
// background
SDL_FRect top_bar_rect = {
0.0f,
0.0f,
static_cast<float>(win_size.w),
static_cast<float>(top_bar_height)
};
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 120);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_RenderFillRect(renderer, &top_bar_rect);
// --- LEFT TEXT ---
SDL_Surface* surface_text = TTF_RenderText_Blended(font_top_bar, text.c_str(), text.length(), text_color);
if (!surface_text) return;
SDL_Texture* texture_text = SDL_CreateTextureFromSurface(renderer, surface_text);
if (!texture_text) {
SDL_DestroySurface(surface_text);
return;
}
SDL_FRect dst_text = {20.0f, 6.0f, static_cast<float>(surface_text->w), static_cast<float>(surface_text->h)};
SDL_RenderTexture(renderer, texture_text, nullptr, &dst_text);
SDL_DestroyTexture(texture_text);
SDL_DestroySurface(surface_text);
}
void
draw_BottomLeftBars(SDL_Renderer* renderer, SDL_Rect win_size, Character& player) {
const float health_w = 20.0f;
const float energy_w = health_w * 0.5f;
const float thirst_w = health_w * 0.4f;
const float bar_h = 120.0f;
const float spacing = 4.0f;
float start_x = 20.0f;
float start_y = static_cast<float>(win_size.h) - bar_h - 20.0f;
auto clamp01 = [](float v) {
return v < 0.0f ? 0.0f : (v > 1.0f ? 1.0f : v);
};
float thirst_max = player.stats.thirst_max;
if (thirst_max <= 0.0f) thirst_max = 1.0f;
float thirst_ratio = clamp01(player.stats.thirst / thirst_max);
// ---------------- HEALTH ----------------
float health_max = player.health_Max();
float health_bonus = player.health_MaxBonus();
float health_base_max = health_max - health_bonus;
if (health_base_max < 0.0f) health_base_max = 0.0f;
float health_capacity_ratio = clamp01(health_max / thirst_max);
float health_bar_max_h = bar_h * health_capacity_ratio;
float health_base_capacity_ratio =
health_max > 0.0f ? clamp01(health_base_max / health_max) : 0.0f;
float health_base_cap_h = health_bar_max_h * health_base_capacity_ratio;
float health_bonus_cap_h = health_bar_max_h - health_base_cap_h;
float health_val = player.health_Get();
float health_base_ratio =
health_base_max > 0.0f ? clamp01(health_val / health_base_max) : 0.0f;
float health_bonus_ratio =
health_bonus > 0.0f
? clamp01((health_val - health_base_max) / health_bonus)
: 0.0f;
float health_bottom_y = start_y + (bar_h - health_bar_max_h);
// ---------------- STAMINA ----------------
float stamina_max = player.stamina_Max();
float stamina_bonus = player.stamina_MaxBonus();
float stamina_base_max = stamina_max - stamina_bonus;
if (stamina_base_max < 0.0f) stamina_base_max = 0.0f;
float stamina_capacity_ratio = clamp01(stamina_max / thirst_max);
float stamina_bar_max_h = bar_h * stamina_capacity_ratio;
float stamina_base_capacity_ratio =
stamina_max > 0.0f ? clamp01(stamina_base_max / stamina_max) : 0.0f;
float stamina_base_cap_h = stamina_bar_max_h * stamina_base_capacity_ratio;
float stamina_bonus_cap_h = stamina_bar_max_h - stamina_base_cap_h;
float stamina_val = player.stamina_Get();
float stamina_base_ratio =
stamina_base_max > 0.0f ? clamp01(stamina_val / stamina_base_max) : 0.0f;
float stamina_bonus_ratio =
stamina_bonus > 0.0f
? clamp01((stamina_val - stamina_base_max) / stamina_bonus)
: 0.0f;
float stamina_bottom_y = start_y + (bar_h - stamina_bar_max_h);
// ---------------- RECTS ----------------
SDL_FRect thirst_bg = { start_x, start_y, thirst_w, bar_h };
SDL_FRect energy_bg = {
thirst_bg.x + thirst_w + spacing,
stamina_bottom_y,
energy_w,
stamina_bar_max_h
};
SDL_FRect health_bg = {
energy_bg.x + energy_w + spacing,
health_bottom_y,
health_w,
health_bar_max_h
};
// ---------------- BACKGROUND ----------------
// thirst background
SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255);
SDL_RenderFillRect(renderer, &thirst_bg);
// stamina background base
SDL_FRect stamina_bg_base = {
energy_bg.x,
stamina_bottom_y + stamina_bonus_cap_h,
energy_w,
stamina_base_cap_h
};
SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255);
SDL_RenderFillRect(renderer, &stamina_bg_base);
// stamina background bonus (slightly lighter)
SDL_FRect stamina_bg_bonus = {
energy_bg.x,
stamina_bottom_y,
energy_w,
stamina_bonus_cap_h
};
SDL_SetRenderDrawColor(renderer, 45, 45, 45, 255);
SDL_RenderFillRect(renderer, &stamina_bg_bonus);
// health background base
SDL_FRect health_bg_base = {
health_bg.x,
health_bottom_y + health_bonus_cap_h,
health_w,
health_base_cap_h
};
SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255);
SDL_RenderFillRect(renderer, &health_bg_base);
// health background bonus (slightly lighter)
SDL_FRect health_bg_bonus = {
health_bg.x,
health_bottom_y,
health_w,
health_bonus_cap_h
};
SDL_SetRenderDrawColor(renderer, 45, 45, 45, 255);
SDL_RenderFillRect(renderer, &health_bg_bonus);
// ---------------- FOREGROUND ----------------
// health base fill
SDL_FRect health_fill_base = {
health_bg.x,
health_bottom_y + health_bonus_cap_h
+ health_base_cap_h * (1.0f - health_base_ratio),
health_w,
health_base_cap_h * health_base_ratio
};
SDL_SetRenderDrawColor(renderer, 200, 40, 40, 255);
SDL_RenderFillRect(renderer, &health_fill_base);
// health bonus fill (lighter red)
SDL_FRect health_fill_bonus = {
health_bg.x,
health_bottom_y
+ health_bonus_cap_h * (1.0f - health_bonus_ratio),
health_w,
health_bonus_cap_h * health_bonus_ratio
};
SDL_SetRenderDrawColor(renderer, 255, 100, 100, 255);
SDL_RenderFillRect(renderer, &health_fill_bonus);
// stamina base fill
SDL_FRect stamina_fill_base = {
energy_bg.x,
stamina_bottom_y + stamina_bonus_cap_h
+ stamina_base_cap_h * (1.0f - stamina_base_ratio),
energy_w,
stamina_base_cap_h * stamina_base_ratio
};
SDL_SetRenderDrawColor(renderer, 200, 200, 40, 255);
SDL_RenderFillRect(renderer, &stamina_fill_base);
// stamina bonus fill (lighter yellow)
SDL_FRect stamina_fill_bonus = {
energy_bg.x,
stamina_bottom_y
+ stamina_bonus_cap_h * (1.0f - stamina_bonus_ratio),
energy_w,
stamina_bonus_cap_h * stamina_bonus_ratio
};
SDL_SetRenderDrawColor(renderer, 255, 255, 120, 255);
SDL_RenderFillRect(renderer, &stamina_fill_bonus);
// thirst fill
SDL_FRect thirst_fill = {
thirst_bg.x,
start_y + bar_h * (1.0f - thirst_ratio),
thirst_w,
bar_h * thirst_ratio
};
SDL_SetRenderDrawColor(renderer, 40, 120, 200, 255);
SDL_RenderFillRect(renderer, &thirst_fill);
// ---------------- BORDERS ----------------
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderRect(renderer, &thirst_bg);
SDL_RenderRect(renderer, &energy_bg);
SDL_RenderRect(renderer, &health_bg);
const float icon_size = 32.0f;
const float icon_spacing = 6.0f;
const float icons_y = start_y - icon_size - 8.0f;
const float max_duration = 300.0f;
for (size_t n = 0; n < player.stats_bonus.size(); ++n) {
const CharacterStatsBonus& bonus = player.stats_bonus[n];
const VegetableData::CropDefinition* data =
VegetableData::get_DataByType(
static_cast<VegetableData::Type>(bonus.type)
);
if (!data)
continue;
SDL_FRect dst = {
thirst_bg.x + n * (icon_size + icon_spacing),
icons_y,
icon_size,
icon_size
};
SDL_FRect src = {
static_cast<float>(data->icon_tile_x * 16),
static_cast<float>(data->icon_tile_y * 16),
16.0f,
16.0f
};
SDL_RenderTexture(renderer, Icon::icons_texture, &src, &dst);
// -------- duration overlay --------
float ratio = bonus.duration / max_duration;
if (ratio < 0.0f) ratio = 0.0f;
if (ratio > 1.0f) ratio = 1.0f;
float overlay_h = dst.h * ratio;
SDL_FRect overlay = {
dst.x,
dst.y + (dst.h - overlay_h),
dst.w,
overlay_h
};
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 140);
SDL_RenderFillRect(renderer, &overlay);
// border
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderRect(renderer, &dst);
}
}
}