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.gzPropper starting screen
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); +} +