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


#include <iostream>
#include <cmath>
#include <vector>
#include <unordered_map>
#include <fstream>

#include "game_state.hpp"
#include "state_modify.hpp"
#include "state_playing.hpp"

#include "defaults/defaults.hpp"
#include "character/character.hpp"
#include "defaults/ui.hpp"
#include "map/tile.hpp"
#include "map/map.hpp"
#include "inventory/weapon.hpp"
#include "building/building.hpp"
#include "defaults/icons.hpp"
#include "inventory/inventory.hpp"
#include "resource/resource_data.hpp"
#include "building/research.hpp"
#include "defaults/input.hpp"
#include "start_screen/start_screen.hpp"
#include "cursor/animated_cursor.hpp"
#include "end_screen/end_screen.hpp"


// Game class encapsulating SDL initialization and loop
Game::Game() : running(true) {
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        std::cerr << "SDL_Init Error: " << SDL_GetError() << "\n";
        running = false;
    }
    Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
    if (Default::is_fullscreen) {
        window_flags |= SDL_WINDOW_FULLSCREEN;
    }

    window = SDL_CreateWindow("Map creator",
                         win_size.w, win_size.h,
                         window_flags
        );
    if (!window) {
        std::cout << "Fialed to create window\n";
        running = false;
        return;
    }



    SDL_SetHint(SDL_HINT_RENDER_VSYNC, "0");
    renderer = SDL_CreateRenderer( window, "opengl" );
    if (!renderer) {
        std::cout << "Fialed to create Renderer\n";
        running = false;
        return;
    }
    SDL_SetRenderVSync(renderer, 1);
    //SDL_SetRenderVSync(renderer, SDL_RENDERER_VSYNC_DISABLED);

    std::cout << "INIT DONE sdl\n";

    lastTicks = SDL_GetTicks();

    // INIT Cursor
    SDL_HideCursor();
    cursor = input.cursor = new AnimatedCursor();
    cursor->Init(renderer, "Pixel_Crawler/Entities/Characters/Body_A/Animations/Slice_Base/Slice_Up.png");


    // Music
    audio = new Audio();
    Animation::audio = audio;

    /*
    audio->RegisterSound("character.walk", "music/character.walk.wav");
    audio->RegisterSound("character.hit",  "music/character.hit.wav");
    */
    audio->RegisterSound("bird.sing",      "music/bird.sing.wav");
    audio->RegisterSound("menu.hover",     "music/menu.hover.wav");
    audio->RegisterSound("character.hae",  "music/character.hae.wav");

    // INIT Font
    Ui::init_Font();

    // INIT TileSets
    Tile::Init(renderer);

    // Icons
    Icon::Init(renderer);




    Default::now = SDL_GetPerformanceCounter();
    Default::last = 0;
    Default::delta_time = 0.0;

    set_State(GameState_e::STATE_PLAYING, NewGameState_e::LOAD);

    set_State(GameState_e::STATE_MENU_MAIN);
}

Game::Game(Game&&) {
    std::cout << "[Game] Move constructor called!" << std::endl;
}

Game::~Game() {
    audio->Quit();
    Ui::delete_Font();
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

void Game::set_State(GameState_e new_state, NewGameState_e game_state_e) {
    // STATE_PLAYING

    // START CURSOR
    switch (new_state) {
        case GameState_e::STATE_MODIFY:
            cursor->set_State(CursorState::NORMAL);
            break;
        case GameState_e::STATE_MENU_MAIN:
            cursor->set_State(CursorState::ANIMATED);
            break;
        case GameState_e::STATE_PLAYING:
            cursor->set_State(CursorState::HIDDEN);
            break;
    }
    // END CURSOR

    state_changed = true;
    if (new_state == GameState_e::STATE_PLAYING) {

        if (Default::game_time_pause_start != 0) {
            Default::game_time_paused += (SDL_GetTicksNS() - Default::game_time_pause_start) * 1e-9;
            Default::game_time_pause_start = 0;
        }

        state_temp.reset();

        switch (game_state_e) {
            case NewGameState_e::LOAD:
            case NewGameState_e::NEW:
                input.game_restart = false;

                state_playing = std::make_unique<StatePlaying>();
                state_playing->Init(this);
                break;
            default: break;
        }

        switch (game_state_e) {
            case NewGameState_e::LOAD:
                Load(active_game_dir);
                break;
            case NewGameState_e::NEW:
                Save(active_game_dir);
                break;
            default: break;
        }

        state_current = state_playing.get();

        state_current_e = new_state;
        return;
    }

    if (state_current_e == GameState_e::STATE_PLAYING && Default::game_time_pause_start == 0)
        Default::game_time_pause_start = SDL_GetTicksNS();

    // STATE MODIFY AND MENU
    switch (new_state) {
        case GameState_e::STATE_MODIFY:
            SDL_ShowCursor();
            state_temp = std::make_unique<StateModify>();
            break;
        case GameState_e::STATE_MENU_MAIN:
            SDL_ShowCursor();
            state_temp = std::make_unique<StateMenuMain>();
            break;
        default: break;
    }
    bool is_set = false;
    switch (new_state) {
        case GameState_e::STATE_MODIFY:
        case GameState_e::STATE_MENU_MAIN:
            state_current = state_temp.get();
            state_current->Init(this);
            is_set = true;
            break;
        default: break;
    }
    if (is_set)
        state_current_e = new_state;
}

void Game::Run() {
    if (Default::is_fullscreen)
        StartScreen_Event::event_Loop(renderer);

    SDL_GetRenderOutputSize(renderer, &win_size.w, &win_size.h);

    while (running) {
        Default::set_DeltaTime();
        handle_Events();
        if (state_changed) {
            continue;
        }
        //std::cout << "IC " << (int)this->input.click_left << std::endl;
        Update();
        Render();

        // FPS calculation
        Default::fps_counter++;
        Default::fps_timer += Default::delta_time;

        if (Default::fps_timer >= 1.0f) {
            Default::fps = Default::fps_counter;

            Default::fps_counter = 0;
            Default::fps_timer -= 1.0f;
        }
    }

    Enemy::shutdown_Workers();
    EndScreen_Event::event_Loop(renderer);
}

void Game::handle_Events() {
    SDL_Event event;

    //SDL_GetWindowSize(window, &win_size.w, &win_size.h);


    Uint32 mouse_buttons = SDL_GetMouseState(&input.mouse_state_raw.x, &input.mouse_state_raw.y);

    input.mouse_state.x = input.mouse_state_raw.x/Default::game_scale;
    input.mouse_state.y = input.mouse_state_raw.y/Default::game_scale;

    input.click_left  = (mouse_buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT))  != 0;
    input.click_right = (mouse_buttons & SDL_BUTTON_MASK(SDL_BUTTON_RIGHT)) != 0;

    input.click_left_released        = false;
    input.click_right_released       = false;
    input.pressed_return_released    = false;
    input.pressed_arrowdown_released = false;
    input.pressed_arrowup_released   = false;
    input.pressed_c_released         = false;

    state_changed = false;

    input.mouse_game_offset = {Map::offset.x,
                               Map::offset.y };

    // START set input.mouse_tile_position
    Tile::set_HoveringTileset(input.mouse_state);
    input.cursor_over_tileset = Tile::hovering_set;

    // END set input.mouse_tile_position
    if (input.cursor_over_tileset) {
        input.mouse_game_offset = {(int)Tile::tileset_offset.x,
                                   (int)Tile::tileset_offset.y };
    }


    while (SDL_PollEvent(&event)) {
        switch (event.type) {
            case SDL_EVENT_QUIT:
                running = false;
                break;
            case SDL_EVENT_WINDOW_RESIZED:
            case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: {
                int w, h;
                SDL_GetWindowSize(window, &w, &h);
                win_size.w = w;
                win_size.h = h;
                break;
            }
            case SDL_EVENT_MOUSE_BUTTON_DOWN:
                cursor->mouse_OnClick();
                break;
            case SDL_EVENT_MOUSE_BUTTON_UP:
                input.click_left_released = true;
                input.click_left = false;
                input.click_right_released = true;
                input.click_right = false;
                break;
            case SDL_EVENT_MOUSE_WHEEL:
                if (event.wheel.y > 0)
                    Default::game_scale += 0.2f;
                else if (event.wheel.y < 0)
                    Default::game_scale -= 0.2f;

                if (Default::game_scale < 0.5)
                    Default::game_scale = 0.5;
                if (Default::game_scale > 4.2f)
                    Default::game_scale = 4.2f;
                break;

            case SDL_EVENT_KEY_DOWN:
                switch (event.key.scancode) {
                    case SDL_SCANCODE_ESCAPE:
                        if (state_current_e == GameState_e::STATE_MENU_MAIN)
                            set_State(GameState_e::STATE_PLAYING, NewGameState_e::NONE);
                        else
                            set_State(GameState_e::STATE_MENU_MAIN);
                        return;
                    case SDL_SCANCODE_F9:
                        set_State(GameState_e::STATE_PLAYING, NewGameState_e::NONE);
                        return;
                    case SDL_SCANCODE_F10:
                        set_State(GameState_e::STATE_MODIFY);
                        return;
                    case SDL_SCANCODE_F12:
                        draw_frames = !draw_frames;
                        return;
                    case SDL_SCANCODE_LSHIFT:
                        input.pressed_shift = true;
                        break;
                    case SDL_SCANCODE_LCTRL:
                        input.pressed_ctrl = true;
                        break;
                    case SDL_SCANCODE_RETURN:
                        input.pressed_return = true;
                        break;
                    case SDL_SCANCODE_I:
                        Ui::draw_key_info = !Ui::draw_key_info;
                        break;
                    case SDL_SCANCODE_C:
                        input.pressed_c = true;
                        break;
                    case SDL_SCANCODE_Q:
                        if (input.pressed_ctrl)
                            running = false;
                        break;
                    default:
                        break;
                }
                break;

            case SDL_EVENT_KEY_UP:
                switch (event.key.scancode) {
                    case SDL_SCANCODE_LCTRL:
                        input.pressed_ctrl = false;
                        break;
                    case SDL_SCANCODE_LSHIFT:
                        input.pressed_shift = false;
                        break;
                    case SDL_SCANCODE_C:
                        input.pressed_c = false;
                        input.pressed_c_released = true;
                        break;
                    case SDL_SCANCODE_RETURN:
                        input.pressed_return = false;
                        input.pressed_return_released = true;
                        break;
                    case SDL_SCANCODE_DOWN:
                        input.pressed_arrowdown_released = true;
                        break;
                    case SDL_SCANCODE_UP:
                        input.pressed_arrowup_released = true;
                        break;
                    default:
                        break;
                }
            default:
                break;
        }

        state_current->handle_Events(event, this);
    }

    if (!input.game_running) running = false;

    input.update_Mousetileposition();
}

void Game::Update() {
    state_current->Update(this);
    cursor->Update(Default::delta_time, input.mouse_state_raw.x, input.mouse_state_raw.y);
}

void Game::Render() {
    SDL_SetRenderDrawColor(renderer, 62, 146, 209, 255);
    SDL_RenderClear(renderer);

    Map::draw_MapTiles(this);

    state_current->Render(this);

    if (draw_frames)
        Ui::draw_Frames(renderer, win_size);

    if (Ui::draw_key_info) Ui::draw_KeyInfo(this);

    cursor->Render(renderer);

    SDL_RenderPresent(renderer);
}




bool Game::Save(const std::string &path) const {
    std::ofstream file(path + "/game", std::ios::binary | std::ios::trunc);
    std::cout << "Save Game\n";
    if (!file) {
        std::cout << "No file found at: " << path+"/game" << std::endl;
        return false;
    }

    // Default namespace
    file.write(reinterpret_cast<const char*>(&Default::game_scale), sizeof(Default::game_scale));

    // Map namespace
    file.write(reinterpret_cast<const char*>(&Map::offset.x), sizeof(Map::offset.x));
    file.write(reinterpret_cast<const char*>(&Map::offset.y), sizeof(Map::offset.y));

    state_playing->Save(path);
    return file.good();
}

bool Game::Load(const std::string &path) {
    std::ifstream file(path + "/game", std::ios::binary);
    std::cout << "Load Game\n";
    if (!file) {
        std::cout << "No file found at: " << path+"/game" << std::endl;
        return false;
    }

    // Default namespace
    file.read(reinterpret_cast<char*>(&Default::game_scale), sizeof(Default::game_scale));

    // Map namespace
    file.read(reinterpret_cast<char*>(&Map::offset.x), sizeof(Map::offset.x));
    file.read(reinterpret_cast<char*>(&Map::offset.y), sizeof(Map::offset.y));

    state_playing->Load(renderer, path);

    return file.good();
}