Nimbin[12]?SDL & Graphics / mapcreator / src/map/tile.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/map/tile.cpp 67.9 KB · 1569 lines raw
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>

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

#include "map/tile.hpp"
#include "defaults/input.hpp"
#include "map/map.hpp"
#include "defaults/ui.hpp"

namespace Tile {
    size_t active_tileset_id = 0;
    TileType active_tileset_type = TILE_BACKGROUND;
    TileSet* active_tileset = nullptr;

    bool draw_tile_set = true;
    bool tile_selected = false;

    bool object_selected = false;
    size_t selected_object_id = SIZE_MAX;
    SDL_Point selected_position = {0,0};

    TileObjectPropSettingStates object_props_setting_state = TileObjectPropSettingStates::OBJECT_SETTING_STATE_TYPE;

    std::unordered_map<TileType, std::vector<TileSet>> tilesets;
    std::vector<SDL_Point> selected_tiles;
    SDL_FPoint tileset_offset = {0, Ui::top_bar_height};
    SDL_FPoint tileset_offset_to_set = {0, 0};
    bool setting_tileset_offset = false;

    float tileset_scale = 0;
    float tileset_scale_to_set = 0;
    bool setting_tileset_scale = false;

    bool hovering_set = false;

    SDL_Point selection_tile_dimension = {1, 1};
    SDL_Point selected_tile_dimension = {1, 1};

    std::string save_tileset_collission_path = "saves/save.tileset_collissions";
    std::string save_tileset_objects_path = "saves/save.tileset_objects";
    std::string save_tileset_floortype_path = "saves/save.tileset_floortype";

    std::vector<TileSettingHistory> tile_setting_history;

    std::vector<FloorProps> floor_props;

    std::unordered_map<TileCategory, std::unordered_map<size_t,TilesetFloorProps>> tileset_floor_props;

    const char* to_String(TileTypeFloors type) {
        switch (type) {
            case NONE:    return "NONE";
            case MEADOW:  return "MEADOW";
            case CLAY:    return "CLAY";
            case SOIL:    return "SOIL";
            case BRICKS:  return "BRICKS";
            case ICE:     return "ICE";
            case DESERT:  return "DESERT";
            case WATER:   return "WATER";
            default:      return "UNKNOWN";
        }
    }

    bool
    Init(SDL_Renderer* renderer) {
        Tile::tilesets[TILE_BACKGROUND].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Tilesets/Floors_Tiles.png"),
                {25, 26},
                TILE_GROUND, {}, {}, {}
        });
        Tile::tilesets[TILE_BACKGROUND].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Tilesets/Water_tiles.png"),
                {25, 25},
                TILE_WATER, {}, {}, {}
        });
        Tile::tilesets[TILE_BACKGROUND].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Tilesets/Dungeon_Tiles.png"),
                {25, 25},
                TILE_DUNGEON, {}, {}, {}
        });
        Tile::tilesets[TILE_BACKGROUND].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Tilesets/Wall_Tiles.png"),
                {25, 25},
                TILE_WALL, {}, {}, {}
        });
        Tile::tilesets[TILE_BACKGROUND].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Tilesets/Wall_Variations.png"),
                {16, 30},
                TILE_WALL_VARIATION, {}, {}, {}
        });
        Tile::tilesets[TILE_OVERLAY].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Tilesets/Overlay_tiles.png"),
                {25,25},
                TILE_OVERLAY_FARM, {}, {}, {}
        });
        Tile::tilesets[TILE_BUILDING].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Structures/Buildings/Floors.png"),
                {25, 25},
                TILE_FLOORS, {}, {}, {}
        });
        Tile::tilesets[TILE_BUILDING].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Structures/Buildings/Props.png"),
                {25, 25},
                TILE_PROPS, {}, {}, {}
        });
        Tile::tilesets[TILE_BUILDING].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Structures/Buildings/Roofs.png"),
                {25, 25},
                TILE_ROOFS, {}, {}, {}
        });
        Tile::tilesets[TILE_BUILDING].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Structures/Buildings/Walls.png"),
                {42, 50},
                TILE_WALLS, {}, {}, {}
        });
        Tile::tilesets[TILE_BUILDING].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Props/Static/Furniture.png"),
                {50, 54},
                TILE_FURNITURE, {}, {}, {}
        });
        Tile::tilesets[TILE_BUILDING].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Structures/Stations/Workbench/Workbench.png"),
                {12, 22},
                TILE_WORKBENCH, {}, {}, {}
        });
        Tile::tilesets[TILE_OBJECT].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Props/Static/Vegetation.png"),
                {25, 27},
                TILE_VEGETATION, {}, {}, {}
        });
        Tile::tilesets[TILE_OBJECT].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Props/Static/Rocks.png"),
                {13, 19},
                TILE_ROCK, {}, {}, {}
        });
        Tile::tilesets[TILE_OBJECT].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Props/Static/Trees/Model_01/Size_02.png"),
                {16, 8},
                TILE_TREE, {}, {}, {}
        });
        Tile::tilesets[TILE_OBJECT].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Props/Static/Trees/Model_01/Size_03.png"),
                {13, 12},
                TILE_TREE, {}, {}, {}
        });
        Tile::tilesets[TILE_OBJECT].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Props/Static/Trees/Model_01/Size_04.png"),
                {23, 16},
                TILE_TREE, {}, {}, {}
        });
        Tile::tilesets[TILE_OBJECT].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Props/Static/Trees/Model_01/Size_05.png"),
                {28, 23},
                TILE_TREE, {}, {}, {}
        });
        Tile::tilesets[TILE_FARM].push_back(
                {IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Props/Static/Farm.png"),
                {25, 25},
                TILE_FARMING, {}, {}, {}
        });

        for (auto& [tile_type, tileset_vector] : Tile::tilesets) {
            for (auto& tileset : tileset_vector) {
                SDL_Texture* tex = tileset.texture;
                if (tex) {
                    SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_NEAREST);
                }
            }
        }

        if (!Tile::tilesets[TILE_BACKGROUND][0].texture) {
            SDL_Log("Couldn't load texture: %s\n", SDL_GetError());
            return false;
        }
        const int cells_per_tile = Tile::get_Size() / Tile::tile_collider_size;
        for (auto& tileset : Tile::tilesets[TILE_BACKGROUND]) {
            if (tileset.texture) {
                SDL_SetTextureScaleMode(tileset.texture, SDL_SCALEMODE_NEAREST);
            }
        }

        for (auto &pair : Tile::tilesets) {
            for (auto &tile : pair.second) {
                size_t total_cells = static_cast<size_t>(tile.size.x * cells_per_tile * tile.size.y * cells_per_tile);
                tile.collission_map.assign(total_cells, false);
            }
        }

        Tile::load_Collissions();
        Tile::load_Objects();
        ResourceData::load_Resources();
        Tile::load_TilesetFloorProps();

        Tile::active_tileset = &Tile::tilesets[TILE_BACKGROUND][0];


        floor_props.push_back({NONE,   true,  false});
        floor_props.push_back({MEADOW, true,  false});
        floor_props.push_back({CLAY,   true,  false});
        floor_props.push_back({SOIL,   true,  true});
        floor_props.push_back({BRICKS, true,  false});
        floor_props.push_back({ICE,    false, false});
        floor_props.push_back({DESERT, false, false});
        floor_props.push_back({WATER,  false, false});
        return true;
    }

    TileType
    get_Type(TileCategory category) {
        switch (category) {
            case TILE_GROUND:
            case TILE_WATER:
            case TILE_WALL:
            case TILE_WALL_VARIATION:
            case TILE_DUNGEON:
                return TILE_BACKGROUND;
            case TILE_OVERLAY_FARM:
                return TILE_OVERLAY;
            case TILE_FLOORS:
            case TILE_PROPS:
            case TILE_ROOFS:
            case TILE_WALLS:
            case TILE_WORKBENCH:
            case TILE_FURNITURE:
                return TILE_BUILDING;

            case TILE_TREE:
            case TILE_ROCK:
            case TILE_VEGETATION:
            case TILE_FARMING:
                return TILE_OBJECT;

        }
        return TILE_TYPE_LAST;
    }

    size_t
    get_Size() {
        return (tile_size);
    }
    size_t
    get_SelectionSize(ModifyState_s modify_state) {
        if (modify_state == MODIFY_COLLISSION)
            return (tile_collider_size);

        return (tile_size);
    }

    size_t
    get_ObjectIdFromTileset(SDL_Point selected_tile, TileSet tileset) {
        for (const auto& pair : tileset.objects) {
            SDL_Rect position = pair.second.position;
            if (position.x <= static_cast<int>(selected_tile.x) && 
                position.y <= static_cast<int>(selected_tile.y) &&
                position.x+pair.second.position.w > static_cast<int>(selected_tile.x) &&
                position.y+pair.second.position.h > static_cast<int>(selected_tile.y)) {
                return pair.first;
            }
        }
        return SIZE_MAX;
    }

    void
    set_HoveringTileset(SDL_FPoint mouse_state) {
        size_t selection_size = get_Size();

        float logical_x = mouse_state.x - tileset_offset.x;
        float logical_y = mouse_state.y - tileset_offset.y;

        int tile_x = static_cast<int>(logical_x / selection_size);
        int tile_y = static_cast<int>(logical_y / selection_size);

        hovering_set = (tile_x >= 0 && tile_x < static_cast<int>(active_tileset->size.x) &&
                tile_y >= 0 && tile_y < static_cast<int>(active_tileset->size.y));
    }


    TileSet&
    get_TilesetFromMapTile(Tile_s tile) {
            return tilesets[get_Type(tile.set_category)][tile.set_id];
    }


    void
    save_Objects() {
        std::ofstream file(save_tileset_objects_path, std::ios::binary);
        if (!file) return;

        uint32_t types_count = tilesets.size();
        file.write(reinterpret_cast<const char*>(&types_count), sizeof(types_count));

        for (const auto& [type, vec] : tilesets) {
            uint32_t t = static_cast<uint32_t>(type);
            file.write(reinterpret_cast<const char*>(&t), sizeof(t));

            uint32_t set_count = vec.size();
            file.write(reinterpret_cast<const char*>(&set_count), sizeof(set_count));

            for (const auto& tile : vec) {
                uint64_t obj_count = tile.objects.size();
                file.write(reinterpret_cast<const char*>(&obj_count), sizeof(obj_count));

                for (const auto& [key, obj] : tile.objects) {
                    file.write(reinterpret_cast<const char*>(&key), sizeof(key));

                    // --- TilesetObject serialization ---
                    file.write(reinterpret_cast<const char*>(&obj.position.x), sizeof(obj.position.x));
                    file.write(reinterpret_cast<const char*>(&obj.position.y), sizeof(obj.position.y));
                    file.write(reinterpret_cast<const char*>(&obj.position.w), sizeof(obj.position.w));
                    file.write(reinterpret_cast<const char*>(&obj.position.h), sizeof(obj.position.h));

                    file.write(reinterpret_cast<const char*>(&obj.draw_from_this_tile), sizeof(obj.draw_from_this_tile));

                    int type_value = static_cast<int>(obj.object_type);
                    file.write(reinterpret_cast<const char*>(&type_value), sizeof(type_value));

                    file.write(reinterpret_cast<const char*>(&obj.collectable), sizeof(obj.collectable));
                    //int building_status = static_cast<int>(obj.building_status);
                    // file.write(reinterpret_cast<const char*>(&building_status), sizeof(building_status));
                    file.write(reinterpret_cast<const char*>(&obj.z_index), sizeof(obj.z_index));
                    // --- Tile_s serialization ---
                    file.write(reinterpret_cast<const char*>(&obj.tileset_info.set_id), sizeof(obj.tileset_info.set_id));
                    file.write(reinterpret_cast<const char*>(&obj.tileset_info.object_id), sizeof(obj.tileset_info.object_id));

                    int category = static_cast<int>(obj.tileset_info.set_category);
                    file.write(reinterpret_cast<const char*>(&category), sizeof(category));

                    file.write(reinterpret_cast<const char*>(&obj.tileset_info.set_position.x), sizeof(obj.tileset_info.set_position.x));
                    file.write(reinterpret_cast<const char*>(&obj.tileset_info.set_position.y), sizeof(obj.tileset_info.set_position.y));

                }
            }
        }
    }
    void
    load_Objects() {
        std::ifstream file(save_tileset_objects_path, std::ios::binary);
        if (!file) return;

        uint32_t types_count = 0;
        file.read(reinterpret_cast<char*>(&types_count), sizeof(types_count));

        for (uint32_t i = 0; i < types_count; ++i) {
            uint32_t t = 0;
            file.read(reinterpret_cast<char*>(&t), sizeof(t));
            TileType type = static_cast<TileType>(t);

            uint32_t set_count = 0;
            file.read(reinterpret_cast<char*>(&set_count), sizeof(set_count));

            auto& vec = tilesets[type];
            for (uint32_t j = 0; j < set_count && j < vec.size(); ++j) {
                uint64_t obj_count = 0;
                file.read(reinterpret_cast<char*>(&obj_count), sizeof(obj_count));

                vec[j].objects.clear();
                for (uint64_t k = 0; k < obj_count; ++k) {
                    size_t key = 0;
                    file.read(reinterpret_cast<char*>(&key), sizeof(key));

                    TilesetObject obj{};


                    file.read(reinterpret_cast<char*>(&obj.position.x), sizeof(obj.position.x));
                    file.read(reinterpret_cast<char*>(&obj.position.y), sizeof(obj.position.y));
                    file.read(reinterpret_cast<char*>(&obj.position.w), sizeof(obj.position.w));
                    file.read(reinterpret_cast<char*>(&obj.position.h), sizeof(obj.position.h));


                    file.read(reinterpret_cast<char*>(&obj.draw_from_this_tile), sizeof(obj.draw_from_this_tile));


                    int type_value = 0;
                    file.read(reinterpret_cast<char*>(&type_value), sizeof(type_value));
                    obj.object_type = static_cast<ObjectType>(type_value);

                    file.read(reinterpret_cast<char*>(&obj.collectable), sizeof(obj.collectable));
                    file.read(reinterpret_cast<char*>(&obj.z_index), sizeof(obj.z_index));

                    // --- Tile_s deserialization ---
                    file.read(reinterpret_cast<char*>(&obj.tileset_info.set_id), sizeof(obj.tileset_info.set_id));
                    file.read(reinterpret_cast<char*>(&obj.tileset_info.object_id), sizeof(obj.tileset_info.object_id));

                    int category = 0;
                    file.read(reinterpret_cast<char*>(&category), sizeof(category));
                    obj.tileset_info.set_category = static_cast<TileCategory>(category);

                    file.read(reinterpret_cast<char*>(&obj.tileset_info.set_position.x), sizeof(obj.tileset_info.set_position.x));
                    file.read(reinterpret_cast<char*>(&obj.tileset_info.set_position.y), sizeof(obj.tileset_info.set_position.y));

                    vec[j].objects.emplace(key, obj);
                }
            }
        }
    }

    void
    save_Collissions() {
        std::ofstream file(save_tileset_collission_path, std::ios::binary);
        if (!file) return;

        uint32_t types_count = tilesets.size();
        file.write(reinterpret_cast<char*>(&types_count), sizeof(types_count));

        for (const auto &[type, vec] : tilesets) {
            uint32_t t = static_cast<uint32_t>(type);
            file.write(reinterpret_cast<char*>(&t), sizeof(t));

            uint32_t set_count = vec.size();
            file.write(reinterpret_cast<char*>(&set_count), sizeof(set_count));

            for (const auto &tile : vec) {
                uint64_t map_size = tile.collission_map.size();
                file.write(reinterpret_cast<char*>(&map_size), sizeof(map_size));

                for (bool v : tile.collission_map) {
                    uint8_t b = v ? 1 : 0;
                    file.write(reinterpret_cast<char*>(&b), 1);
                }
            }
        }
    }


    void
    load_Collissions() {
        std::ifstream file(save_tileset_collission_path, std::ios::binary);
        if (!file) return;

        uint32_t types_count = 0;
        file.read(reinterpret_cast<char*>(&types_count), sizeof(types_count));

        for (uint32_t i = 0; i < types_count; ++i) {
            uint32_t t = 0;
            file.read(reinterpret_cast<char*>(&t), sizeof(t));
            TileType type = static_cast<TileType>(t);

            uint32_t set_count = 0;
            file.read(reinterpret_cast<char*>(&set_count), sizeof(set_count));

            auto &vec = tilesets[type];
            for (uint32_t j = 0; j < set_count && j < vec.size(); ++j) {
                uint64_t map_size = 0;
                file.read(reinterpret_cast<char*>(&map_size), sizeof(map_size));

                vec[j].collission_map.resize(map_size);
                for (uint64_t k = 0; k < map_size; ++k) {
                    uint8_t b = 0;
                    file.read(reinterpret_cast<char*>(&b), 1);
                    vec[j].collission_map[k] = (b != 0);
                }
            }
        }
    }


    inline int TileCategoryToInt(TileCategory t) { return static_cast<int>(t); }
    inline int TileTypeFloorsToInt(TileTypeFloors f) { return static_cast<int>(f); }

    inline TileCategory IntToTileCategory(int i) { return static_cast<TileCategory>(i); }
    inline TileTypeFloors IntToTileTypeFloors(int i) { return static_cast<TileTypeFloors>(i); }

    void
    save_TilesetFloorProps() {
        std::ofstream out(save_tileset_floortype_path, std::ios::binary);
        if (!out) {
            std::cerr << "Failed to open file for saving: " << save_tileset_floortype_path << "\n";
            return;
        }

        size_t map_size = tileset_floor_props.size();
        out.write(reinterpret_cast<const char*>(&map_size), sizeof(map_size));

        for (auto& [tile_type, inner_map] : tileset_floor_props) {
            int tt = TileCategoryToInt(tile_type);
            out.write(reinterpret_cast<const char*>(&tt), sizeof(tt));

            size_t inner_size = inner_map.size();
            out.write(reinterpret_cast<const char*>(&inner_size), sizeof(inner_size));

            for (auto& [index, props] : inner_map) {
                out.write(reinterpret_cast<const char*>(&index), sizeof(index));

                int floor = TileTypeFloorsToInt(props.tiletype);
                out.write(reinterpret_cast<const char*>(&floor), sizeof(floor));

                out.write(reinterpret_cast<const char*>(&props.z_index), sizeof(props.z_index));
            }
        }
    }


    void
    load_TilesetFloorProps() {
        std::ifstream in(save_tileset_floortype_path, std::ios::binary);
        if (!in) {
            std::cerr << "Failed to open file for loading: " << save_tileset_floortype_path << "\n";
            return;
        }

        tileset_floor_props.clear();

        size_t map_size = 0;
        in.read(reinterpret_cast<char*>(&map_size), sizeof(map_size));

        for (size_t i = 0; i < map_size; ++i) {
            int tt_int;
            in.read(reinterpret_cast<char*>(&tt_int), sizeof(tt_int));
            TileCategory tt = IntToTileCategory(tt_int);

            size_t inner_size = 0;
            in.read(reinterpret_cast<char*>(&inner_size), sizeof(inner_size));

            std::unordered_map<size_t, TilesetFloorProps> inner_map;
            for (size_t j = 0; j < inner_size; ++j) {
                size_t index;
                int floor_int;
                int z_index;

                in.read(reinterpret_cast<char*>(&index), sizeof(index));
                in.read(reinterpret_cast<char*>(&floor_int), sizeof(floor_int));
                in.read(reinterpret_cast<char*>(&z_index), sizeof(z_index));

                inner_map[index] = TilesetFloorProps{IntToTileTypeFloors(floor_int), z_index};
            }

            tileset_floor_props[tt] = std::move(inner_map);
        }
    }


    void
    draw_SelectedObject(SDL_Renderer* renderer, Input input) {
        // render selected object
        if (Tile::object_selected && Default::modify_state == MODIFY_TILES) {
                TilesetObject object = active_tileset->objects[selected_object_id];
                SDL_FRect obj_src = {
                    object.position.x * static_cast<float>(Tile::tile_size),
                    object.position.y * static_cast<float>(Tile::tile_size),
                    object.position.w * static_cast<float>(Tile::tile_size),
                    object.position.h * static_cast<float>(Tile::tile_size)
                };

                SDL_FPoint tile_offset = {
                    (static_cast<float>(selected_position.x) - object.position.x) * static_cast<float>(Tile::tile_size),
                    (static_cast<float>(selected_position.y) - object.position.y) * static_cast<float>(Tile::tile_size)
                };

                SDL_FPoint offset = {(float)tileset_offset.x, (float)tileset_offset.y};
                if (!hovering_set) {
                    offset.x = Map::offset.x;
                    offset.y = Map::offset.y;
                }
                SDL_FRect dst {Scale(input.mouse_tile_position.x* static_cast<int>(Tile::tile_size) + offset.x - tile_offset.x),
                               Scale(input.mouse_tile_position.y* static_cast<int>(Tile::tile_size) + offset.y - tile_offset.y),
                               Scale(object.position.w * static_cast<float>(Tile::tile_size)),
                               Scale(object.position.h * static_cast<float>(Tile::tile_size))};

                SDL_Texture* tex = Tile::tilesets[active_tileset_type][active_tileset_id].texture;
                SDL_SetTextureAlphaMod(tex, 100);
                SDL_RenderTexture(renderer, tex, &obj_src, &dst);
                SDL_SetTextureAlphaMod(tex, 255);

        }
    }

    void
    draw_Tileset(SDL_Renderer* renderer, Input input) {
        // render Set
        float cell_size = Tile::get_SelectionSize(Default::modify_state);
        SDL_FRect dst = {
            0.0f + tileset_offset.x * Default::game_scale,
            0.0f + tileset_offset.y * Default::game_scale,
            Scale(Tile::active_tileset->size.x * tile_size),
            Scale(Tile::active_tileset->size.y * tile_size)
        };

        SDL_FRect bg_rect = dst;
        SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
        SDL_RenderFillRect(renderer, &bg_rect);

        SDL_RenderTexture(renderer, Tile::active_tileset->texture, NULL, &dst);

        // Render Set-Border
        float border_thickness = 4.0f;
        SDL_FRect top = { dst.x-border_thickness, dst.y-border_thickness, dst.w+border_thickness*2, border_thickness };
        SDL_FRect bottom = { dst.x-border_thickness, dst.y + dst.h, dst.w+border_thickness*2, border_thickness };
        SDL_FRect left = { dst.x-border_thickness, dst.y-border_thickness, border_thickness, dst.h+border_thickness*2 };
        SDL_FRect right = { dst.x + dst.w, dst.y-border_thickness, border_thickness, dst.h+border_thickness*2 };
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderFillRect(renderer, &top);
        SDL_RenderFillRect(renderer, &bottom);
        SDL_RenderFillRect(renderer, &left);
        SDL_RenderFillRect(renderer, &right);


        // render object cells
        if (Default::modify_state == MODIFY_FLOOR_TYPE) {
            SDL_SetRenderDrawColor(renderer, 0, 160, 255, 255);
            SDL_Point tileset_size = active_tileset->size;
            for (const auto& pair : tileset_floor_props[active_tileset->category]) {
                if (pair.second.tiletype == NONE) continue;
                int x = static_cast<int>(pair.first) % tileset_size.x;
                int y = static_cast<int>(pair.first) / tileset_size.x;
                SDL_FRect cell_rect = {
                    Scale(x * tile_size + tileset_offset.x),
                    Scale(y * tile_size + tileset_offset.y),
                    Scale(tile_size),
                    Scale(tile_size)
                };

                // draw cell outline
                SDL_RenderRect(renderer, &cell_rect);

                // draw cross
                SDL_RenderLine(renderer,
                        cell_rect.x,
                        cell_rect.y,
                        cell_rect.x + cell_rect.w, cell_rect.y + cell_rect.h);
                SDL_RenderLine(renderer,
                        cell_rect.x + cell_rect.w,
                        cell_rect.y,
                        cell_rect.x, cell_rect.y + cell_rect.h);
            }

        } else if (Default::modify_state == MODIFY_OBJECTS || Default::modify_state == MODIFY_OBJECTS_PROPS || Default::modify_state == MODIFY_OBJECTS_PROPS_RESOURCE) {

            SDL_SetRenderDrawColor(renderer, 0, 255, Default::modify_state == MODIFY_OBJECTS_PROPS ? 160 : 255, 255);
            for (const auto& pair : active_tileset->objects) {
                if (Default::modify_state == MODIFY_OBJECTS_PROPS_RESOURCE && !pair.second.collectable) continue;
                if (pair.second.object_type == ObjectType::NONE)
                    SDL_SetRenderDrawColor(renderer, 200, 0, 0, 255);
                else
                    SDL_SetRenderDrawColor(renderer, 0, 255, Default::modify_state == MODIFY_OBJECTS_PROPS ? 160 : 255, 255);

                SDL_FRect cell_rect = {
                    Scale(pair.second.position.x * cell_size + tileset_offset.x),
                    Scale(pair.second.position.y * cell_size + tileset_offset.y),
                    Scale(pair.second.position.w * cell_size),
                    Scale(pair.second.position.h * cell_size)
                };

                // draw cell outline
                SDL_RenderRect(renderer, &cell_rect);

                // draw cross
                SDL_RenderLine(renderer,
                        cell_rect.x,
                        cell_rect.y,
                        cell_rect.x + cell_rect.w, cell_rect.y + cell_rect.h);
                SDL_RenderLine(renderer,
                        cell_rect.x + cell_rect.w,
                        cell_rect.y,
                        cell_rect.x, cell_rect.y + cell_rect.h);
            }
        // render collision cells
        } else if (Default::modify_state == MODIFY_COLLISSION) {
            size_t map_width  = static_cast<size_t>(Tile::active_tileset->size.x);
            size_t map_height = static_cast<size_t>(Tile::active_tileset->size.y);

            SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
            SDL_SetRenderDrawColor(renderer, 255, 0, 0, 100);

            size_t cells_per_tile = tile_size/tile_collider_size;
            for (size_t y = 0; y < map_height*cells_per_tile; ++y) {
                for (size_t x = 0; x < map_width*cells_per_tile; ++x) {
                    size_t index = y * map_width*cells_per_tile + x;
                    if (!Tile::active_tileset->collission_map[index])
                        continue;

                    SDL_FRect cell_rect = {
                        Scale(x * cell_size + tileset_offset.x),
                        Scale(y * cell_size + tileset_offset.y),
                        Scale(cell_size),
                        Scale(cell_size)
                    };

                    // draw cell outline
                    SDL_RenderRect(renderer, &cell_rect);

                    // draw cross
                    SDL_RenderLine(renderer,
                            cell_rect.x,
                            cell_rect.y,
                            cell_rect.x + cell_rect.w, cell_rect.y + cell_rect.h);
                    SDL_RenderLine(renderer,
                            cell_rect.x + cell_rect.w,
                            cell_rect.y,
                            cell_rect.x, cell_rect.y + cell_rect.h);
                }
            }
        }

        // render selection_border
        if (hovering_set) {
            SDL_FRect tile_rect {Scale(input.mouse_tile_position.x*cell_size + tileset_offset.x),
                                 Scale(input.mouse_tile_position.y*cell_size + tileset_offset.y),
                                 Scale(cell_size*Tile::selection_tile_dimension.x),
                                 Scale(cell_size*Tile::selection_tile_dimension.y)};

            SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
            SDL_RenderRect(renderer, &tile_rect);
        }
        // render selected border
        if (Tile::tile_selected) {
            SDL_FRect tile_rect_selected
                                {Scale(Tile::selected_tiles[0].x*tile_size + tileset_offset.x),
                                 Scale(Tile::selected_tiles[0].y*tile_size + tileset_offset.y),
                                 Scale(tile_size*Tile::selected_tile_dimension.x),
                                 Scale(tile_size*Tile::selected_tile_dimension.y)};

            SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
            SDL_RenderRect(renderer, &tile_rect_selected);
        } else if (Tile::object_selected) {
            TilesetObject object = active_tileset->objects[selected_object_id];
            SDL_FRect tile_rect_selected
                                {Scale(object.position.x*static_cast<int>(tile_size) + tileset_offset.x),
                                 Scale(object.position.y*static_cast<int>(tile_size) + tileset_offset.y),
                                 Scale(object.position.w*static_cast<int>(tile_size)),
                                 Scale(object.position.h*static_cast<int>(tile_size))};

            SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
            SDL_RenderRect(renderer, &tile_rect_selected);

            SDL_FRect tile_rect_selected_position
                                {Scale(selected_position.x * static_cast<int>(tile_size) + tileset_offset.x),
                                 Scale(selected_position.y * static_cast<int>(tile_size) + tileset_offset.y),
                                 Scale(tile_size),
                                 Scale(tile_size)};
            SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
            SDL_SetRenderDrawColor(renderer, 0, 255, 0, 60);
            SDL_RenderFillRect(renderer, &tile_rect_selected_position);
            SDL_SetRenderDrawColor(renderer, 0, 255, 255, 60);
            SDL_RenderRect(renderer, &tile_rect_selected_position);
        }
    }

    void
    clear_Objects(Input& input) {
        for (int y = 0; y < Tile::selection_tile_dimension.y; y++) {
            for (int x = 0; x < Tile::selection_tile_dimension.x; x++) {

                int map_x = input.mouse_tile_position.x + x;
                int map_y = input.mouse_tile_position.y + y;

                if (map_x >= Map::size.x || map_y >= Map::size.y)
                    continue;

                size_t index = static_cast<size_t>(map_y * Map::size.x + map_x);
                auto& tile_objects = Map::tile_map[index].objects;
                if (!input.pressed_c || tile_objects.empty())
                    continue;

                TileObject &obj = tile_objects[0];
                Object* object = Object::get_ObjectById(obj.object_id);
                if (!object) continue;

                int world_tile_x = map_x - obj.position.x;
                int world_tile_y = map_y - obj.position.y;

                for (int wx = world_tile_x; wx < world_tile_x + object->dimension.x; ++wx) {

                    for (int wy = world_tile_y; wy < world_tile_y + object->dimension.y; ++wy) {

                        if (wx < 0 || wy < 0 || wx >= Map::size.x || wy >= Map::size.y)
                            continue;


                        size_t i = static_cast<size_t>(wy * Map::size.x + wx);
                        auto &objects = Map::tile_map[i].objects;

                        for (auto it = objects.begin(); it != objects.end();) {

                            Object* oo = Object::get_ObjectById(it->object_id);

                            if (!oo || oo->id == object->id) {
                                it = objects.erase(it);
                            } else ++it;
                        }

                        Map::set_TileCollissionForObjects({wx, wy});
                    }
                }

                delete object;
            }
        }
    }

    void
    set_Collission(Input& input) {
        const int map_width = Tile::active_tileset->size.x * (tile_size/tile_collider_size);

        for (int y = 0; y < Tile::selection_tile_dimension.y; ++y) {
            for (int x = 0; x < Tile::selection_tile_dimension.x; ++x) {

                int cell_x = input.mouse_tile_position.x + x;
                int cell_y = input.mouse_tile_position.y + y;
                size_t id = static_cast<size_t>(cell_y * map_width + cell_x);

                if (id >= Tile::active_tileset->collission_map.size())
                    continue;

                Tile::active_tileset->collission_map[id] = !Tile::active_tileset->collission_map[id];
            }
        }
    }

    bool
    objec_HasCollission(TilesetObject& object) {
        TileType type = get_Type( object.tileset_info.set_category);
        std::vector<bool> collision_map =
            Tile::get_ObjectCollisionsFromTileset(object.tileset_info.set_id,
                                                  object.tileset_info.object_id, type);

        for (bool collission : collision_map) {
            if (collission) return true;
        }
        return false;
    }

    std::vector<bool>
    get_CollissionsOfTileFromObject(TilesetObject& obj, SDL_Point tile) {
        const size_t scale = Tile::tile_size / Tile::tile_collider_size;

        // convert rectangle to collider coordinates

        // 0 2
        TileType type = get_Type( obj.tileset_info.set_category);
        std::vector<bool> collision_map =
            Tile::get_ObjectCollisionsFromTileset(obj.tileset_info.set_id,
                                                  obj.tileset_info.object_id, type);

        int y_start = tile.y * (tile_size/tile_collider_size);
        int y_end   = y_start + static_cast<int>(tile_size/tile_collider_size);
        int x_start = tile.x * (tile_size/tile_collider_size);
        int x_end   = x_start + static_cast<int>(tile_size/tile_collider_size);
        int obj_w = static_cast<size_t>(obj.position.w) * scale;
        //int obj_h = static_cast<size_t>(obj.position.h) * scale;
        std::vector<bool> collissions;
        for (int y = y_start; y < y_end; y++) {
            for (int x = x_start; x < x_end; x++) {
                size_t index = static_cast<size_t>(y * obj_w + x);
                bool cell_value = index < collision_map.size() ? collision_map[index] : false;
                collissions.push_back(cell_value);
            }
        }
        /*
        int obj_abs_x = static_cast<int>(tile.x) - obj.position.x;
        int obj_abs_y = static_cast<int>(tile.y) - obj.position.y;

        int obj_cx = obj_abs_x * static_cast<int>(scale);
        int obj_cy = obj_abs_y * static_cast<int>(scale);
        size_t obj_cw = static_cast<size_t>(obj.position.w) * scale;
        size_t obj_ch = static_cast<size_t>(obj.position.h) * scale;

        int start_cx = obj_cx;
        int end_cx   = obj_cx + static_cast<int>(Tile::tile_size) / static_cast<int>(Tile::tile_collider_size);
        int start_cy = obj_cy;
        int end_cy   = obj_cy + static_cast<int>(Tile::tile_size) / static_cast<int>(Tile::tile_collider_size);

        for (int cy = start_cy; cy < end_cy; ++cy) {
            for (int cx = start_cx; cx < end_cx; ++cx) {
                int rel_x = cx - obj_cx;
                int rel_y = cy - obj_cy;

                if (rel_x < 0 || rel_y < 0 || static_cast<size_t>(rel_x) >= obj_cw || static_cast<size_t>(rel_y) >= obj_ch) {
                    continue;
                }

                size_t obj_index = static_cast<size_t>(rel_y) * obj_cw + static_cast<size_t>(rel_x);
                bool cell_value = obj_index < obj.collission_map.size() ? obj.collission_map[obj_index] : false;

                std::cout << (int)cell_value;
                collissions.push_back(cell_value);
            }
            std::cout << std::endl;
        }
        */
        return collissions;
    }

    void
    set_Object(Input& input) {
        std::unordered_map<size_t, TilesetObject>& objects = active_tileset->objects;

        size_t object_id = objects.size();

        size_t mouse_tile_x = static_cast<size_t>(input.mouse_tile_position.x);
        size_t mouse_tile_y = static_cast<size_t>(input.mouse_tile_position.y);

        // check if any tile already in objects map
        bool removed_object = false;
        for (size_t y = 0; y < static_cast<size_t>(Tile::selection_tile_dimension.y); ++y) {
            for (size_t x = 0; x < static_cast<size_t>(Tile::selection_tile_dimension.x); ++x) {
                size_t cell_x = mouse_tile_x + x;
                size_t cell_y = mouse_tile_y + y;

                for (size_t i = 0; i < object_id; i++) {
                    if (objects[i].position.x == static_cast<int>(cell_x) && objects[i].position.y == static_cast<int>(cell_y)) {
                        objects.erase(i);
                        removed_object = true;
                        break;
                    }
                }
            }
        }
        std::cout<< "NEW OBJ ID " << object_id << " " << selected_object_id << std::endl;
        if (removed_object) return;

        // add objecz to map
        objects[object_id].position.x = static_cast<int>(mouse_tile_x);
        objects[object_id].position.y = static_cast<int>(mouse_tile_y);
        objects[object_id].position.w = Tile::selection_tile_dimension.x,
        objects[object_id].position.h = Tile::selection_tile_dimension.y,
        objects[object_id].object_type = ObjectType::NONE,
        objects[object_id].collectable = false;
        //objects[object_id].building_status = TileBuildingStatus::NONE;
        Tile_s tileset_info = {Tile::active_tileset_id,
                        object_id,
                        Tile::tilesets[Tile::active_tileset_type][Tile::active_tileset_id].category ,
                        {static_cast<int>(objects[object_id].position.x),
                         static_cast<int>(objects[object_id].position.y)}};
        objects[object_id].tileset_info = tileset_info;
        // std::cout << mouse_tile_x << " " << mouse_tile_y << " " << Tile::selection_tile_dimension.x << " " << Tile::selection_tile_dimension.x << std::endl;
    }

    void
    set_ObjectProps(Input& input) {
        static ObjectType set_type  = ObjectType::NONE;
        static bool set_collectable = 0;
        static int  set_zindex      = 1;
        if (input.click_left_released) {
            object_props_setting_state = TileObjectPropSettingStates::OBJECT_SETTING_STATE_TYPE;

            size_t id = get_ObjectIdFromTileset({input.mouse_tile_position.x, 
                                                input.mouse_tile_position.y});
            if (id == SIZE_MAX) return;

            Tile::object_selected    = true;
            Tile::selected_object_id = id;
            Tile::selected_position  = {input.mouse_tile_position.x, input.mouse_tile_position.y};

            Tile::tile_selected = false;

            TilesetObject object = active_tileset->objects[selected_object_id];

            if (object.object_type != ObjectType::NONE) {
                set_type        = object.object_type;
                set_collectable = object.collectable;
                set_zindex      = object.z_index;
            }

            std::cout << "Type: " << Object::to_String(set_type)
                      << ", Collectable: " << set_collectable << ", Z-Index: " << set_zindex
                      << std::endl;
            std::cout << "Setting: ";
        } else if (object_selected) {
            if (input.pressed_return_released) {
                // get to next
                object_props_setting_state = static_cast<TileObjectPropSettingStates>(static_cast<int>(object_props_setting_state) + 1);

                std::cout << "Setting: ";
                switch ( object_props_setting_state ) {
                    case TileObjectPropSettingStates::OBJECT_SETTING_STATE_TYPE:
                        std::cout << "-> Type\n   active: " << Object::to_String(set_type) << std::endl; 
                        break;
                    case TileObjectPropSettingStates::OBJECT_SETTING_STATE_COLLECTING_METHODE:
                        std::cout << "-> Collecting Methode\n   active: " << set_collectable << std::endl; 
                        break;
                    case TileObjectPropSettingStates::OBJECT_SETTING_STATE_ZINDEX_METHODE:
                        std::cout << "-> Z-Index\n   active: " << set_zindex << std::endl; 
                        break;
                    case TileObjectPropSettingStates::OBJECT_SETTING_STATE_SAVE:
                        std::cout << "Save ?\n"; 
                        break;
                    case TileObjectPropSettingStates::OBJECT_SETTING_STATE_LAST: {
                        // Save current
                        TilesetObject& object = active_tileset->objects[selected_object_id];
                        object.object_type = set_type;
                        object.collectable = set_collectable;
                        object.z_index     = set_zindex;
                        save_Objects();

                        std::cout << "Saving!"
                                  << "\n  Type        :" << Object::to_String(object.object_type)
                                  << "\n  Collectable :" << object.collectable
                                  << "\n  Z-Index     :" << object.z_index 
                                  << std::endl;
                        object_selected = false;

                        break; }
                    default:
                        break;
                }
            } else if (input.pressed_arrowdown_released) {
                switch ( object_props_setting_state ) {
                    case TileObjectPropSettingStates::OBJECT_SETTING_STATE_TYPE:
                        set_type = static_cast<ObjectType>(
                                (static_cast<int>(set_type) + 1) % static_cast<int>(ObjectType::LAST)
                                );

                        std::cout << "Type: " << Object::to_String(set_type) << std::endl;
                        break;
                    case TileObjectPropSettingStates::OBJECT_SETTING_STATE_COLLECTING_METHODE:
                        set_collectable = !set_collectable;
                        std::cout << "Collectable: " << set_collectable << "\n"; 
                        break;
                    case TileObjectPropSettingStates::OBJECT_SETTING_STATE_ZINDEX_METHODE:
                        set_zindex -= 1;
                        std::cout << "Z-Index " << set_zindex << "\n"; 
                        break;
                    default:
                        break;
                }
                std::cout << "Type: " << Object::to_String(set_type)
                          << ", Collectable: " << set_collectable
                          << ", Z-Index: "<< set_zindex 
                          << std::endl;
            } else if (input.pressed_arrowup_released) {
                switch ( object_props_setting_state ) {
                    case TileObjectPropSettingStates::OBJECT_SETTING_STATE_TYPE:
                        {
                            int type = static_cast<int>(set_type);
                            int last = static_cast<int>(ObjectType::LAST);
                            type = (type - 1 + last) % last;
                            set_type = static_cast<ObjectType>(type);
                        }
                        std::cout << "Type: " << Object::to_String(set_type) << std::endl;
                        break;
                    case TileObjectPropSettingStates::OBJECT_SETTING_STATE_COLLECTING_METHODE:
                        set_collectable = !set_collectable;
                        std::cout << "Collectable: " << set_collectable << "\n"; 
                        break;
                    case TileObjectPropSettingStates::OBJECT_SETTING_STATE_ZINDEX_METHODE:
                        set_zindex += 1;
                        std::cout << "Z-Index " << set_zindex << "\n"; 
                        break;
                    default:
                        break;
                }
                std::cout << "Type: " << Object::to_String(set_type)
                          << ", Collectable: " << set_collectable
                          << ", Z-Index: "<< set_zindex 
                          << std::endl;
            }
        }
    };

    void
    set_FloorType(Input& input) {
        static TileTypeFloors set_type  = TileTypeFloors::NONE;
        static size_t set_state  = 0;
        static int    set_zindex = 0;
        static TilesetFloorProps* props = nullptr;
        if (input.click_left_released) {
            size_t id = static_cast<size_t>(input.mouse_tile_position.x +
                                            input.mouse_tile_position.y * active_tileset->size.x);
            if (id == SIZE_MAX) return;

            Tile::tile_selected      = false;
            Tile::object_selected    = true;
            Tile::selected_object_id = id;
            Tile::selected_position  = {input.mouse_tile_position.x, input.mouse_tile_position.y};
            set_state = 0;
            props = nullptr;


            props = &tileset_floor_props[active_tileset->category][id];

            if (props->tiletype != NONE) {
                set_type   = props->tiletype;
                set_zindex = props->z_index;
            }

            std::cout << "Type: " << to_String(set_type) << std::endl;
            std::cout << "Z-Index: " << set_zindex << std::endl;

            if (set_state == 2) std::cout << "Save?\n";
        } else if (object_selected) {
            if ( input.pressed_return_released ) {
                set_state++;
                if (set_state > 2) {
                    std::cout << "Saving...\n";
                    props->tiletype = set_type;
                    props->z_index  = set_zindex;
                    save_TilesetFloorProps();
                    set_state = 0;
                } else
                    std::cout << "Setting: \n";
                std::cout << "Type: " << to_String(set_type) << std::endl;
                std::cout << "Z-Index: " << set_zindex << std::endl;
            } else if (set_state == 0) {
                if (input.pressed_arrowup_released) {
                    int type = static_cast<int>(set_type);
                    int last = static_cast<int>(TileTypeFloors::LAST);
                    type = (type - 1 + last) % last;
                    set_type = static_cast<TileTypeFloors>(type);
                } else if (input.pressed_arrowdown_released) {
                    set_type = static_cast<TileTypeFloors>(
                            (static_cast<int>(set_type) + 1) % static_cast<int>(TileTypeFloors::LAST)
                            );
                }
                if ( input.pressed_arrowdown_released || input.pressed_arrowup_released || input.pressed_return_released) {
                    std::cout << "Type: " << to_String(set_type) << std::endl;
                }
            } else if (set_state == 1) {
                if (input.pressed_arrowup_released) {
                    set_zindex++;
                } else if (input.pressed_arrowdown_released) {
                    set_zindex--;
                }
                if ( input.pressed_arrowdown_released || input.pressed_arrowup_released) {
                    std::cout << "Z-Index: " << set_zindex << std::endl;
                }
            }
        }
    }

    TileTypeFloors
    get_FloorType(Tile_s& tile_s) {
        SDL_Point& position = tile_s.set_position;
        TileSet& tileset = get_TilesetFromMapTile(tile_s);
        size_t id = static_cast<size_t>(position.y * tileset.size.x + position.x);
        return tileset_floor_props[tile_s.set_category][id].tiletype;
    }
    TileTypeFloors
    get_FloorType(SDL_Point& world_tile) {
        if (Map::tile_map[Map::get_IdFromTilePoint(world_tile)].overlays.size() > 0) {
            for (Tile_s t : Map::tile_map[Map::get_IdFromTilePoint(world_tile)].overlays) {
                if (t.set_position.x == 0 && t.set_position.y == 0)
                    return TileTypeFloors::SOIL;
            }
        }
        Tile_s& tile_s = Map::tile_map[Map::get_IdFromTilePoint(world_tile)].background;
        SDL_Point& position = tile_s.set_position;
        TileSet& tileset = get_TilesetFromMapTile(tile_s);
        size_t id = static_cast<size_t>(position.y * tileset.size.x + position.x);
        return tileset_floor_props[tile_s.set_category][id].tiletype;
    }

    void
    transfer_ToMap(Input& input) {
        // If pressing on tileset
        if (hovering_set) {
            if (!input.click_left) {
                if (active_tileset_type == TILE_BACKGROUND || input.pressed_shift) {

                    Tile::selected_tiles.clear();
                    for (int y = 0; y < Tile::selection_tile_dimension.y; y++) {
                        for (int x = 0; x < Tile::selection_tile_dimension.x; x++) {
                            Tile::selected_tiles.push_back({input.mouse_tile_position.x+x,
                                    input.mouse_tile_position.y+y
                                    });
                        }
                    }
                    Tile::selected_tile_dimension.x = Tile::selection_tile_dimension.x;
                    Tile::selected_tile_dimension.y = Tile::selection_tile_dimension.y;
                    Tile::tile_selected = true;
                    return;
                }

                size_t id = get_ObjectIdFromTileset({input.mouse_tile_position.x, 
                                                    input.mouse_tile_position.y});
                if (id == SIZE_MAX) return;
                Tile::object_selected = true;
                Tile::selected_object_id = id;
                Tile::selected_position = {input.mouse_tile_position.x, input.mouse_tile_position.y};

                Tile::tile_selected = false;
                print_TileObject(active_tileset->objects[selected_object_id]);
            }
        }

        // If pressing on map
        else if ( input.click_left_released && !input.pressed_c && (Tile::tile_selected || Tile::object_selected) && !Map::setting_offset &&
                ( input.mouse_tile_position.x >= 0 && input.mouse_tile_position.y >= 0 ) &&
                ( input.mouse_tile_position.x < Map::size.x ) &&
                ( input.mouse_tile_position.y < Map::size.y )
                ) {
            std::cout << "MOUS: " << input.mouse_tile_position.x << " " << input.mouse_tile_position.y << std::endl;

            // Set selected tile in map
            if (Tile::active_tileset_type == TILE_OBJECT) {
                TilesetObject object = active_tileset->objects[selected_object_id];
                std::vector<bool> collision_map = get_ObjectCollisionsFromTileset(object.tileset_info.set_id, object.tileset_info.object_id, get_Type(object.tileset_info.set_category));

                bool has_collission = objec_HasCollission(object);
                SDL_Point tile_offset = {
                    selected_position.x - object.position.x,
                    selected_position.y - object.position.y
                };
                int map_origin_x = input.mouse_tile_position.x - tile_offset.x;
                int map_origin_y = input.mouse_tile_position.y - tile_offset.y;
                std::cout << "WP " << map_origin_x << " " << map_origin_y << std::endl;

                TileType type = get_Type(object.tileset_info.set_category);

                Resource_s res;
                if (object.collectable) {
                    res = get_ResourceFromTileSet(object.tileset_info.set_id, object.tileset_info.object_id);
                }
                Object* new_object = !object.collectable ?
                    new Object(object.object_type,
                            object.collectable ?
                            ObjectCategory::RESOURCE :
                            ObjectCategory::STATIC,
                            type,
                            object.tileset_info.set_id,
                            object.tileset_info.object_id,
                            {map_origin_x, map_origin_y},
                            {object.position.w, object.position.h},
                            object.z_index)
                    :
                        new Resource(object.object_type,
                                ObjectCategory::RESOURCE,
                                type,
                                object.tileset_info.set_id,
                                object.tileset_info.object_id,
                                {map_origin_x, map_origin_y},
                                {object.position.w, object.position.h},
                                object.z_index,
                                res.resource,
                                res.quantity,
                                res.collecting_methode,
                                res.collecting_duration
                                );
                Object::objects.push_back(new_object);
                TileObject o ={ {0,0}, false, new_object->id };
                bool tile_exists     = false;
                bool check_existence = true;
                bool draw_is_set = false;
                std::cout << "##################################\n";

                for (int y = 0; y < object.position.h && !tile_exists; y++) {
                    for (int x = 0; x < object.position.w && !tile_exists; x++) {
                        //bool shall_set_on_tile = true;
                        o.draw_from_this_tile = false;

                        o.position.x = x; o.position.y = y;
                        int map_x = map_origin_x + x ;
                        int map_y = map_origin_y + y ;
                        if (map_x < 0 || map_y < 0 ||
                                map_x >= static_cast<int>(Map::size.x) ||
                                map_y >= static_cast<int>(Map::size.y))
                            continue;
                        int index = map_y * static_cast<int>(Map::size.x) + map_x;
                        if (has_collission) {
                            size_t s = Tile::tile_size / Tile::tile_collider_size;
                            size_t width_c = static_cast<size_t>(object.position.w) * s;

                            bool check = false;
                            for (size_t yc = static_cast<size_t>(y) * s; yc < static_cast<size_t>(y + 1) * s && !check; yc++) {
                                for (size_t xc = static_cast<size_t>(x) * s; xc < static_cast<size_t>(x + 1) * s && !check; xc++) {
                                    size_t i = yc * width_c + xc;
                                    if (collision_map[i]) {
                                        check = true;
                                        if (!draw_is_set) {
                                            o.draw_from_this_tile = true;
                                            draw_is_set = true;
                                        }
                                    }
                                    std::cout << (collision_map[i] ? '#' : '.') << ' ';
                                }
                                std::cout << '\n';
                            }
                            //shall_set_on_tile = check;

                            std::cout << "---- tile (" << x << "," << y << ") ----\n";
                        } else {
                            if (!draw_is_set)
                                o.draw_from_this_tile = true;
                            draw_is_set = true;
                        }
                        //if (!shall_set_on_tile) continue;
                        
                        // make sure we dont get dublicates
                        if (check_existence) {
                            for (const TileObject& t : Map::tile_map[static_cast<size_t>(index)].objects) {
                                Object* map_o = Object::get_ObjectById(t.object_id);
                                if (t.position.x == x && t.position.y == y && map_o->tileset_object_id == selected_object_id) {
                                    std::cout << "Object already set!\n";
                                    tile_exists = true; 
                                    break;
                                }
                            }
                            check_existence = false;
                            if (tile_exists) break;
                        }

                        Map::tile_map[static_cast<size_t>(index)].objects.push_back(o);

                        std::sort(Map::tile_map[static_cast<size_t>(index)].objects.begin(),
                                Map::tile_map[static_cast<size_t>(index)].objects.end(),
                                [](const TileObject& a, const TileObject& b) {
                                return Object::get_ObjectById(a.object_id)->z_index < Object::get_ObjectById(b.object_id)->z_index;
                                });
                        //print_TileObject(o);
                    }
                }
                // SET OBJECT COLLISION ON TILE_MAP

                if (tile_exists) {
                    delete Object::objects.back();
                    return;
                }
                if (!has_collission) return;

                Map::set_ObjectCollisionOnTilemap(new_object->id, {map_origin_x, map_origin_y});

                return;
            }
            else if (Tile::active_tileset_type == TILE_BACKGROUND) {
                for (int y = 0; y < Tile::selection_tile_dimension.y; y++) {
                    for (int x = 0; x < Tile::selection_tile_dimension.x; x++) {

                        int map_x = input.mouse_tile_position.x + x;
                        int map_y = input.mouse_tile_position.y + y;

                        if (map_x < Map::size.x && map_y < Map::size.y) {
                            size_t index = static_cast<size_t>(map_y * Map::size.x + map_x);
                            // Repeat selection if placing is bigger than original
                            int wrapped_x = x % Tile::selected_tile_dimension.x;
                            int wrapped_y = y % Tile::selected_tile_dimension.y;
                            size_t t_id = static_cast<size_t>(wrapped_y * Tile::selected_tile_dimension.x + wrapped_x);

                            Tile_s& background = Map::tile_map[index].background;
                            TileSet tileset = get_TilesetFromMapTile(background);
                            /*
                            tile_setting_history.push_back({
                                    TILE_BACKGROUND,
                                    index,
                                    {background.set_id,
                                     background.set_category,
                                     {background.set_position.x,
                                      background.set_position.y
                                     }
                                    }
                            });
                            */
                            background.set_id = Tile::active_tileset_id;
                            background.set_position.x = Tile::selected_tiles[t_id].x;
                            background.set_position.y = Tile::selected_tiles[t_id].y;
                            background.object_id = static_cast<size_t>(map_y*tileset.size.x + map_x);
                            background.set_category = Tile::tilesets[TILE_BACKGROUND][background.set_id].category;
                        }
                    }
                }
            }

        }
    }
    void
    set_TilesetOffset(Input& input) {
        // START UNUSED FUNCTION
        // reset scale
        if (!input.click_left && tileset_scale_to_set != 0) {
            tileset_scale_to_set = 0;
            setting_tileset_scale = false;
        }
        // Set scale
        else if (tileset_scale_to_set != 0) {
            tileset_scale = input.mouse_state.y - tileset_scale_to_set;
        }
        // Update scale
        if (input.click_left && input.pressed_shift &&
                (tileset_scale_to_set == 0)) {
            tileset_scale_to_set = input.mouse_state.y - tileset_scale;
            setting_tileset_scale = true;
        }
        // END UNUSED FUNCTION


        // Reset tileset offset
        if (!input.click_left && tileset_offset_to_set.x != 0 && tileset_offset_to_set.y != 0) {
            tileset_offset_to_set.x = 0;
            tileset_offset_to_set.y = 0;
            setting_tileset_offset = false;
        }
        // Set tileset offset
        else if (tileset_offset_to_set.x != 0 && tileset_offset_to_set.y != 0) {
            tileset_offset.x = input.mouse_state.x - tileset_offset_to_set.x;
            tileset_offset.y = input.mouse_state.y - tileset_offset_to_set.y;
        }


        // Update tileset_offset set
        if (input.click_left && input.pressed_ctrl &&
                (tileset_offset_to_set.x == 0 && tileset_offset_to_set.y == 0)) {
            tileset_offset_to_set.x = input.mouse_state.x - tileset_offset.x;
            tileset_offset_to_set.y = input.mouse_state.y - tileset_offset.y;
            setting_tileset_offset = true;
        }
    }

    Resource_s
    get_ResourceFromTileSet(size_t tileset_id, size_t object_id) {
        return tilesets[TILE_OBJECT][tileset_id].resources[object_id];
    }

    TilesetObject
    get_ObjectFromTileSet(size_t tileset_id, size_t object_id, TileType type) {
        return tilesets[type][tileset_id].objects[object_id];
    }

    std::vector<bool>
    get_ObjectCollisionsFromTileset(size_t tileset_id, size_t object_id, TileType type) {
        TileSet& ts = tilesets[type][tileset_id];
        auto it = ts.objects.find(object_id);
        if (it == ts.objects.end()) {
            return {};
        }

        TilesetObject& obj = it->second;

        std::vector<bool> vec;

        int scale = Tile::tile_size / Tile::tile_collider_size;
        int collider_w = obj.position.w * scale;
        int collider_h = obj.position.h * scale;
        int collider_x = obj.position.x * scale;
        int collider_y = obj.position.y * scale;

        int row_width = ts.size.x * scale;

        if (collider_x + collider_w > row_width ||
            collider_y + collider_h > ts.size.y * scale) {
            std::cerr << "Object rectangle out of collider bounds!\n";
            return vec;
        }

        vec.reserve(static_cast<size_t>(collider_w * collider_h));

        for (int y = 0; y < collider_h; ++y) {
            for (int x = 0; x < collider_w; ++x) {
                size_t index = static_cast<size_t>((collider_y + y) * row_width + (collider_x + x));
                vec.push_back(ts.collission_map[index]);
            }
        }

        return vec;
    }

    void
    print_CollisionMap(const TilesetObject& obj) {
        size_t collider_w = static_cast<size_t>(obj.position.w) * Tile::tile_size / Tile::tile_collider_size;
        size_t collider_h = static_cast<size_t>(obj.position.h) * Tile::tile_size / Tile::tile_collider_size;

        std::cout << "Collision Map for object "
                  << obj.tileset_info.object_id
                  << " (collider size: " << collider_w << "x" << collider_h << "):\n";

        std::vector<bool> collision_map = get_ObjectCollisionsFromTileset(obj.tileset_info.set_id, obj.tileset_info.object_id, get_Type(obj.tileset_info.set_category));

        for (size_t y = 0; y < collider_h; ++y) {
            for (size_t x = 0; x < collider_w; ++x) {
                size_t index = y * collider_w + x;
                if (index >= collision_map.size()) {
                    std::cout << "? "; // safety: missing data
                } else {
                    std::cout << (collision_map[index] ? "1 " : "0 ");
                }
            }
            std::cout << "\n";
        }
    }

    void
    print_TileObject(const TilesetObject& obj) {
        std::cout << "TilesetObject {\n";
        std::cout << "    position: { x: " << obj.position.x
                  << ", y: " << obj.position.y
                  << ", w: " << obj.position.w
                  << ", h: " << obj.position.h << " }\n";
        std::cout << "    object_type: " << static_cast<int>(obj.object_type) << "\n";
        std::cout << "    collectable: " << std::boolalpha << obj.collectable << "\n";
        std::cout << "    z_index: " << obj.z_index << "\n";
        std::cout << "    tileset_info: {\n";
        std::cout << "        set_id: " << obj.tileset_info.set_id << "\n";
        std::cout << "        object_id: " << obj.tileset_info.object_id << "\n";
        std::cout << "        set_category: " << static_cast<int>(obj.tileset_info.set_category) << "\n";
        std::cout << "        set_position: { x: " << obj.tileset_info.set_position.x
                  << ", y: " << obj.tileset_info.set_position.y << " }\n";
        std::cout << "    }\n";
        std::cout << "}\n";
        print_CollisionMap(obj);
    }


    /*
    bool
    is_BehindTileObject(TilesetObject object, SDL_FPoint position, SDL_FRect rect) {
        const int scale = Tile::tile_size / Tile::tile_collider_size;

        const int obj_w = object.position.w * scale;
        const int obj_h = object.position.h * scale;
        const int obj_x = position.x / scale;
        const int obj_y = position.y / scale;

        const int rect_x = rect.x / Tile::tile_collider_size;
        const int rect_y = rect.y / Tile::tile_collider_size;
        const int rect_w =
            (rect.w + Tile::tile_collider_size - 1) / Tile::tile_collider_size;

        //std::cout << "OB " << obj_x << " " << obj_y << " " << obj_w << " " << obj_h << std::endl;
        //std::cout << "RE " << rect_x << " " << rect_y << " " << rect_w << std::endl;
        const int row_width = obj_w;

        // horizontal overlap?
        if (rect_x + rect_w <= obj_x || rect_x >= obj_x + obj_w)
            return true;

        int start_x = std::max(rect_x, obj_x) - obj_x;
        int end_x   = std::min(rect_x + rect_w, obj_x + obj_w) - obj_x;

        // rect completely above object
        if (rect_y < obj_y)
            return true;

        int rect_row = rect_y - obj_y;

        // rect completely below object
        if (rect_row >= obj_h)
            return true;

        // --------------------------------------------------------
        // SCAN COLLIDER ROWS BELOW THE RECT
        // --------------------------------------------------------
        //bool collission_above  = false;
        bool collission_below  = false;
        bool anywhere_above  = false;
        bool anywhere_below  = false;
        for (int y = 0; y < obj_h; ++y) {
            int base = y * row_width;
            for (int x = 0; x < obj_w; ++x) {
                if (object.collission_map[static_cast<size_t>(base + x)]) {
                    if (y < rect_row) {
                        //std::cout << "ABOVE ";
                        anywhere_above = true;
                        if (x > start_x && x < end_x) {
                            //collission_above = true;
                            return false;
                        }
                    } else {
                        //std::cout << "BELOW\n";
                        anywhere_below = true;
                        if (x > start_x && x < end_x) {
                            //std::cout << "BELOW";
                            collission_below = true;
                        }
                    }
                }
            }
        }
        if (anywhere_below && !collission_below)
            return true;
        if (collission_below)
            return true;
        if (!anywhere_above && !collission_below)
            return false;
        return false;

    }
*/
}