Nimbin[12]?SDL & Graphics / mapcreator / commits / be5d861

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

Propper starting screen

Christian Immanuel · 2026-02-09 18:13 · be5d8617eb2a9d7c55bef4c4405daace21028743

 src/character/character.cpp       |   2 +-
 src/defaults/ui.cpp               |   1 +
 src/game/game.cpp                 |   4 +
 src/start_screen/start_screen.cpp | 260 ++++++++++++++++++++++++++++++++++++++
 src/start_screen/start_screen.hpp |  87 +++++++++++++
 5 files changed, 353 insertions(+), 1 deletion(-)

diff --git a/src/character/character.cpp b/src/character/character.cpp
index 96f1b2a..8b9cbe1 100644
--- a/src/character/character.cpp
+++ b/src/character/character.cpp
@@ -801,7 +801,7 @@ Character::drink_River() {
         consuming_logic.duration += Default::delta_time;
         float amount_per_second = 25.0f / consuming_logic.duration_max;
         stats.thirst += amount_per_second * Default::delta_time;
-        float stamina_per_second = 0.06f / consuming_logic.duration_max;
+        float stamina_per_second = 0.01f / consuming_logic.duration_max;
         stats.stamina -= stamina_per_second;
         if (stats.stamina <= 0) {
             stamina_StopReg();
diff --git a/src/defaults/ui.cpp b/src/defaults/ui.cpp
index e590b5c..0e14ad6 100644
--- a/src/defaults/ui.cpp
+++ b/src/defaults/ui.cpp
@@ -43,6 +43,7 @@ keyboard_layout_playing = {
     {               "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"}
 };
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 7eff0c1..64677d4 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -23,6 +23,7 @@
 #include "resource/resource_data.hpp"
 #include "building/research.hpp"
 #include "defaults/input.hpp"
+#include "start_screen/start_screen.hpp"
 
 
 // Game class encapsulating SDL initialization and loop
@@ -162,6 +163,9 @@ void Game::set_State(GameState_e new_state, NewGameState_e game_state_e) {
 }
 
 void Game::Run() {
+    StartScreen_Event::event_Loop(renderer);
+    SDL_GetRenderOutputSize(renderer, &win_size.w, &win_size.h);
+
     while (running) {
         Default::set_DeltaTime();
         handle_Events();
diff --git a/src/start_screen/start_screen.cpp b/src/start_screen/start_screen.cpp
new file mode 100644
index 0000000..5842daf
--- /dev/null
+++ b/src/start_screen/start_screen.cpp
@@ -0,0 +1,260 @@
+#include "start_screen.hpp"
+
+StartScreen::StartScreen(SDL_Renderer* r) : renderer(r) { Init(); }
+StartScreen::~StartScreen() {
+        if (char_texture) SDL_DestroyTexture(char_texture);
+        if (logo_surface) SDL_DestroySurface(logo_surface);
+    }
+
+void StartScreen::Update() {
+    Uint64 now = SDL_GetTicks();
+    float dt = static_cast<float>(now - last_time) / 1000.0f;
+    last_time = now;
+
+    switch (phase) {
+        case Phase::DOT:
+            if (now - phase_start > 500) StartSpreadToLine();
+            break;
+
+        case Phase::SPREAD_TO_LINE: {
+            float p = (now - phase_start) /  280.0f;
+            if (p >= 1.0f) { p = 1.0f; phase = Phase::LINE_WAIT; phase_start = now; }
+            for (auto& pt : particles) {
+                SDL_FPoint from = { center_dot.x, center_dot.y };
+                pt.current.x = Lerp(from.x, pt.target.x, p);
+                pt.current.y = Lerp(from.y, pt.target.y, p);
+            }
+            break;
+        }
+
+        case Phase::LINE_WAIT:
+                                    if (now - phase_start > 300) {
+                                        phase = Phase::RUNNING;
+                                        run_start_time = now;
+                                        char_x = -300.0f;
+
+                                        // Re-apply dynamic speed (in case resized just before running)
+                                        char_speed = (static_cast<float>(screen_w) + extra_distance) / desired_cross_time;
+                                    }
+                                    break;
+
+        case Phase::RUNNING: {
+                                 char_x += char_speed * dt;
+                                 Uint64 frame_ticks = now - run_start_time;
+                                 char_frame = static_cast<int>((frame_ticks / 80ULL) % 6ULL);
+
+                                 // End when character is fully off-screen right
+                                 if (char_x > static_cast<float>(screen_w) + 1420.0f) {
+                                     StartFadeToLogo();
+                                 }
+                                 break;
+                             }
+
+        case Phase::FADE_TO_LOGO: {
+            float p = (now - phase_start) /  420.0f;
+            if (p >= 1.0f) { p = 1.0f; phase = Phase::SHOW_LOGO; phase_start = now; }
+            for (auto& pt : particles) {
+                SDL_FPoint logo_screen = GetScreenOriginal(pt);
+                pt.current.x = Lerp(pt.target.x, logo_screen.x, p);
+                pt.current.y = Lerp(pt.target.y, logo_screen.y, p);
+            }
+            break;
+        }
+        case Phase::SHOW_LOGO: {
+                                   if (now - phase_start > static_cast<Uint64>(logo_display_time * 1000.0f)) {
+                                       // Time's up → quit the application
+                                       running = false;           // will break main loop
+                                                                  // or: SDL_Quit(); return; // but better to let main clean up
+                                   }
+                                   break;
+                               }
+    }
+}
+
+void StartScreen::Render() {
+    SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
+    SDL_RenderClear(renderer);
+
+    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
+    for (const auto& p : particles) {
+        SDL_FRect r { p.current.x, p.current.y, 1.0f, 1.0f };
+        SDL_RenderFillRect(renderer, &r);
+    }
+
+    if (phase == Phase::RUNNING) {
+        float scale = 4.0f;                               // double size as requested
+        float sw = char_frame_width * scale;
+        float sh = char_height * scale;
+        std::print("{0} {1} {2}\n", "FEET: ", bottom_y, sh);
+        float dy = static_cast<float>(bottom_y) - sh + 46.0f; // feet on line
+
+        SDL_FRect src { static_cast<float>(char_frame * char_frame_width), 0.0f,
+                        static_cast<float>(char_frame_width), static_cast<float>(char_height) };
+        SDL_FRect dst { char_x - (sw - char_frame_width) / 2.0f, dy, sw, sh };
+
+        SDL_RenderTexture(renderer, char_texture, &src, &dst);
+    }
+
+    SDL_RenderPresent(renderer);
+}
+
+void StartScreen::UpdateScreen() {
+    if (!logo_surface) return;
+    SDL_GetRenderOutputSize(renderer, &screen_w, &screen_h);
+
+    // Logo centered
+    int lw = logo_surface->w;
+    int lh = logo_surface->h;
+    logo_dest.x = (screen_w - lw) / 2;
+    logo_dest.y = (screen_h - lh) / 2 - 100;
+    logo_offset.x = static_cast<float>(logo_dest.x);
+    logo_offset.y = static_cast<float>(logo_dest.y);
+
+    // Central dot
+    center_dot.x = static_cast<float>(screen_w) / 2.0f;
+    center_dot.y = static_cast<float>(screen_h) * 0.65f;
+
+    // Ground bar — edge-to-edge friendly
+    int margin = 0;
+    int usable = screen_w - 2 * margin;
+    int pixels_per_full_row = usable / ground_row_height;
+    int total = static_cast<int>(particles.size());
+    int full_rows = total / pixels_per_full_row;
+    int rem = total % pixels_per_full_row;
+
+    std::vector<SDL_FPoint> line_targets;
+    line_targets.reserve(static_cast<size_t>(total));
+
+    bottom_y = static_cast<float>(screen_h) * 0.68f;
+
+    // Full rows — packed densely (as before)
+    for (int r = 0; r < full_rows; ++r) {
+        float y = bottom_y - static_cast<float>(r) * static_cast<float>(ground_row_height);
+        float row_start_x = (screen_w - pixels_per_full_row * ground_row_height) / 2.0f;
+        for (int c = 0; c < pixels_per_full_row; ++c) {
+            float x = row_start_x + static_cast<float>(c) * static_cast<float>(ground_row_height);
+            line_targets.push_back({x, y});
+        }
+    }
+
+    // Remainder row — evenly spaced across full width
+    if (rem > 0) {
+        float y = bottom_y - static_cast<float>(full_rows) * static_cast<float>(ground_row_height);
+
+        // Spread evenly with equal gaps
+        float spacing = static_cast<float>(usable) / static_cast<float>(rem + 1);
+        float first_x = static_cast<float>(margin) + spacing / 2.0f;
+
+        for (int c = 0; c < rem; ++c) {
+            float x = first_x + static_cast<float>(c) * spacing;
+            line_targets.push_back({x, y});
+        }
+    }
+
+    line_positions = std::move(line_targets);
+
+    // Character position above top of bar
+    float top_bar = bottom_y - static_cast<float>(full_rows + (rem > 0 ? 1 : 0)) * static_cast<float>(ground_row_height);
+    line_y = static_cast<int>(top_bar - static_cast<float>(char_height) * 2.0f - 20.0f);
+}
+
+
+void StartScreen::Init() {
+    logo_surface = IMG_Load("logo/logo.png");
+    char_texture = IMG_LoadTexture(renderer, "logo/Run_Side-Sheet.png");
+
+    SDL_GetRenderOutputSize(renderer, &screen_w, &screen_h);
+
+    // Extract black pixels (local coords)
+    const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(logo_surface->format);
+    Uint32* pixels = static_cast<Uint32*>(logo_surface->pixels);
+    int pitch = logo_surface->pitch / (int)sizeof(Uint32);
+    int w = logo_surface->w, h = logo_surface->h;
+
+    particles.clear();
+    for (int y = 0; y < h; ++y) {
+        for (int x = 0; x < w; ++x) {
+            Uint32 p = pixels[y * pitch + x];
+            Uint8 r,g,b,a;
+            SDL_GetRGBA(p, details, NULL, &r, &g, &b, &a);
+            if (r < 20 && g < 20 && b < 20 && a > 180) {
+                particles.push_back({ {static_cast<float>(x), static_cast<float>(y)},
+                                      {0,0}, {0,0} });
+            }
+        }
+    }
+
+    UpdateScreen();                     // compute dot + line positions
+
+    // Set all particles to center dot for first phase
+    for (auto& p : particles) {
+        p.current.x = center_dot.x;
+        p.current.y = center_dot.y;
+        p.target    = center_dot;
+    }
+
+    rng.seed(std::random_device{}());
+    last_time = SDL_GetTicks();
+    phase_start = last_time;
+    phase = Phase::DOT;
+
+    char_speed = (static_cast<float>(screen_w) + extra_distance) / desired_cross_time;
+    //char_speed = 520.0f;
+    char_frame_width = 64;
+    char_height = 64;
+}
+
+void StartScreen::StartSpreadToLine() {
+    phase = Phase::SPREAD_TO_LINE;
+    phase_start = SDL_GetTicks();
+
+    int N = static_cast<int>(particles.size());
+    std::vector<size_t> perm((size_t)N);
+    std::iota(perm.begin(), perm.end(), 0);
+    std::shuffle(perm.begin(), perm.end(), rng);
+
+    for (size_t i = 0; i < (size_t)N; ++i) {
+        particles[i].target = line_positions[perm[i]];
+    }
+}
+
+void StartScreen::StartFadeToLogo() {
+    phase = Phase::FADE_TO_LOGO;
+    phase_start = SDL_GetTicks();
+
+    // targets stay at line positions → lerp back to logo in Update()
+}
+
+SDL_FPoint StartScreen::GetScreenOriginal(const Particle& p) const {
+    return { p.original.x + logo_offset.x, p.original.y + logo_offset.y };
+}
+
+float StartScreen::Lerp(float a, float b, float t) {
+    return a + (b - a) * t;
+}
+
+namespace StartScreen_Event {
+    void event_Loop(SDL_Renderer* renderer) {
+    StartScreen* scene = new StartScreen(renderer);
+    while (scene->running) {
+        SDL_Event e;
+        while (SDL_PollEvent(&e)) {
+            if (e.type == SDL_EVENT_QUIT) scene->running = false;
+
+            if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_SPACE) {
+                scene->~StartScreen();
+                scene = new StartScreen(renderer);   // restart from DOT
+            }
+
+            if (e.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED ||
+                e.type == SDL_EVENT_WINDOW_RESIZED) {
+                scene->UpdateScreen();
+            }
+        }
+
+        scene->Update();
+        scene->Render();
+    }
+}
+}
+
diff --git a/src/start_screen/start_screen.hpp b/src/start_screen/start_screen.hpp
new file mode 100644
index 0000000..208c5a4
--- /dev/null
+++ b/src/start_screen/start_screen.hpp
@@ -0,0 +1,87 @@
+// StartScreen.cpp (single-file SDL3 example)
+// Compile: g++ -std=c++20 StartScreen.cpp -o startscreen $(pkg-config --cflags --libs sdl3 SDL3_image)
+
+#pragma once
+
+#include <SDL3/SDL.h>
+#include <SDL3_image/SDL_image.h>
+#include <vector>
+#include <random>
+#include <numeric>
+#include <algorithm>
+#include <cstdint>
+#include <print>
+
+struct Particle {
+    SDL_FPoint original;   // local logo coords (0..w, 0..h) — never change
+    SDL_FPoint current;
+    SDL_FPoint target;
+};
+
+
+class StartScreen {
+public:
+    bool running = true;
+    StartScreen(SDL_Renderer* r);
+    ~StartScreen();
+
+    void Update();
+
+    void Render();
+
+    void UpdateScreen();
+
+private:
+    enum class Phase { SHOW_LOGO, DOT, SPREAD_TO_LINE, LINE_WAIT, RUNNING, FADE_TO_LOGO };
+    float desired_cross_time = 1.8f;  // seconds
+    float extra_distance     = 600.0f; // px left + right
+    float logo_display_time = 3.2f;
+
+    void Init();
+
+    void StartSpreadToLine();
+
+    void StartFadeToLogo();
+
+    SDL_FPoint GetScreenOriginal(const Particle& p) const;
+
+    static float Lerp(float a, float b, float t);
+
+private:
+    SDL_Renderer* renderer = nullptr;
+    SDL_Surface*  logo_surface = nullptr;
+    SDL_Texture*  char_texture = nullptr;
+
+    std::vector<Particle> particles;
+    std::vector<SDL_FPoint> line_positions;
+
+    int screen_w = 0, screen_h = 0;
+    SDL_Rect logo_dest{};
+    SDL_FPoint logo_offset{0,0};
+    SDL_FPoint center_dot{0,0};
+
+    int line_y = 0;
+
+    Phase phase = Phase::DOT;
+    Uint64 phase_start = 0;
+    Uint64 run_start_time = 0;
+    Uint64 last_time = 0;
+
+    float char_x = 0.0f;
+    int   char_frame = 0;
+
+    // tunable
+    float char_speed = 520.0f;
+    int char_frame_width = 64;
+    int char_height = 64;
+    int ground_row_height = 1;          // thicker = denser line
+
+    float bottom_y = 0;
+
+    std::mt19937 rng;
+};
+
+namespace StartScreen_Event {
+    void event_Loop(SDL_Renderer* renderer);
+}
+