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

#include <iostream>
#include <algorithm>

#include "map/tile.hpp"
#include "map/map.hpp"



std::vector<Object*> Object::objects;


Object::~Object() {
    for (int y = 0; y < dimension.y; y++) {
        for (int x = 0; x < dimension.x; x++) {
            size_t m_id = static_cast<size_t>((y+world_tile_position.y)*Map::size.x+(x+world_tile_position.x));
            if (m_id >= static_cast<size_t>(Map::size.x*Map::size.y))
                continue;
            std::vector<TileObject>& t_objects = Map::tile_map[m_id].objects;

            for (auto it = t_objects.begin(); it != t_objects.end(); ) {
                if (it->object_id == id)
                    it = t_objects.erase(it);
                else ++it;
            }
        }
    }
};

const char* Object::to_String(ObjectType type) {
        switch (type) {
            case ObjectType::NONE:   return "NONE";
            case ObjectType::ROCK:   return "ROCK";
            case ObjectType::TREE:   return "TREE";
            case ObjectType::STICK:  return "STICK";
            case ObjectType::BUSH:   return "BUSH";
            case ObjectType::FLOWER: return "FLOWER";
            case ObjectType::FOOD:   return "FOOD";
            case ObjectType::FURNITURE:   return "FURNITURE";
            case ObjectType::ROOF:   return "ROOF";
            case ObjectType::FLOOR:  return "FLOOR";
            case ObjectType::FURNISHING:  return "FURNISHING";
            case ObjectType::WORKBENCH:   return "WORKBENCH";
            default:                 return "UNKNOWN";
        }
}

std::vector<Object*>
Object::get_ObjectsByWorldPosition(SDL_Point position) {
    std::vector<Object*> objects;
    for (Object* object : Object::objects) {
        if (object->world_tile_position.x == position.x && object->world_tile_position.y == position.y) {
            objects.push_back(object);
        }
    }
    return objects;
}

Object*
Object::get_ObjectByWorldPositionAndTilesetObjectID(SDL_Point position, size_t ts_object_id) {
    std::vector<Object*> os = Object::get_ObjectsByWorldPosition(position);
    for (Object* o : os) {
        if (o->tileset_object_id == ts_object_id)
            return o;
    }
    return nullptr;
}

Object*
Object::get_ObjectById(size_t id) {
    //std::cout << "GETTT " << id << std::endl;
    for (Object* object : Object::objects)
        if (object->id == id) return object;
    return nullptr;
}

SDL_Point
Object::get_SetPosition() {
        return Tile::tilesets[tileset_type][tileset_id].objects[tileset_object_id].tileset_info.set_position;
}

int
Object::is_InFrontOf(SDL_FPoint world_position, SDL_FRect rect) {
    const int scale = Tile::tile_size / Tile::tile_collider_size;

    const int obj_w = dimension.x * scale;
    const int obj_h = dimension.y * scale;
    const int obj_x = world_position.x / scale;
    const int obj_y = world_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;
    const int rect_h = (rect.h + Tile::tile_collider_size - 1) / Tile::tile_collider_size;

    /*
    std::cout << "WP: " << world_position.x << " " << world_position.y << std::endl;
    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;

    // object is left or right of rect
    if (rect_x + rect_w <= obj_x || rect_x >= obj_x + obj_w)
        return false;

    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;

    // is above object
    if (rect_y+rect_h < obj_y)
        return 2;

    // is below object
    if (obj_y + obj_h <= rect_y)
        return false;

    //
    int rect_row = rect_y - obj_y;
    if (rect_row >= obj_h)
        return 2;

    bool collission_below  = false;
    bool anywhere_above  = false;
    bool anywhere_below  = false;
    std::vector<bool> collission_map = get_CollisionMap();
    bool has_collision = false;
    for (int y = 0; y < obj_h; ++y) {
        int base = y * row_width;
        for (int x = 0; x < obj_w; ++x) {
            if (collission_map[static_cast<size_t>(base + x)]) {
                has_collision = true;
                if (y < rect_row) {
                    //std::cout << "ABOVE \n";
                    anywhere_above = true;
                    if (x > start_x && x < end_x) {
                        //std::cout << "SUPABOVE \n";
                        //collission_above = true;
                        return false;
                    }
                } else {
                    //std::cout << "BELOW\n";
                    anywhere_below = true;
                    if (x > start_x && x < end_x) {
                        //std::cout << "SUPBELOW\n";
                        collission_below = true;
                    }
                }
            }
        }
    }
    if ( !has_collision ) {
        //std::cout << "NO COLL\n";
        SDL_FRect temp_rect = { world_position.x, world_position.y, static_cast<float>(dimension.x*Tile::tile_size), static_cast<float>(dimension.y*Tile::tile_size) };
        //std::cout << "COLL " << SDL_HasRectIntersectionFloat(&temp_rect, &rect) <<  " " << world_position.x << " " << world_position.y << " " << rect.x << " " << rect.y <<std::endl;
        //std::cout << "WIDH " << dimension.x *Tile::tile_size<< " " << dimension.y *Tile::tile_size<< " RE " << rect.w << " " << rect.h << std::endl;
        if (z_index <= 0 ) return false;
        return !SDL_HasRectIntersectionFloat(&temp_rect, &rect);
    }

    //std::cout << "COL " << collission_below << " " << anywhere_below << " " << anywhere_above << std::endl;
    if ((collission_below || anywhere_below) && !anywhere_above)
        return true;
    if (!anywhere_above && !collission_below)
        return false;
    return false;
}

std::vector<bool> Object::get_CollisionMap() {
    std::vector<bool> map =
        Tile::get_ObjectCollisionsFromTileset(
            tileset_id,
            tileset_object_id,
            tileset_type);

    if (!map.empty())
        return map;

    const size_t n = Tile::tile_collider_size * Tile::tile_collider_size;
    return std::vector<bool>(n, false);
}

bool
Object::has_Collision() {
    for (bool collission : get_CollisionMap()) {
        if (collission) return true;
    }
    return false;
}

std::vector<bool>
Object::get_CollissionsOfTile(SDL_Point tile) {
    std::vector<bool> collision_map = get_CollisionMap();
    const size_t scale = Tile::tile_size / Tile::tile_collider_size;

    int y_start = tile.y * static_cast<int>(Tile::tile_size/Tile::tile_collider_size);
    int y_end   = y_start + static_cast<int>(Tile::tile_size/Tile::tile_collider_size);
    int x_start = tile.x * static_cast<int>(Tile::tile_size/Tile::tile_collider_size);
    int x_end   = x_start + static_cast<int>(Tile::tile_size/Tile::tile_collider_size);
    int obj_w = static_cast<size_t>(dimension.x) * scale;

    std::vector<bool> collisions;
    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);
            collisions.push_back(collision_map[index]);
        }
    }
    return collisions;
}

void
Object::print_CollisionMap() {
    size_t collider_w = static_cast<size_t>(dimension.x) * Tile::tile_size / Tile::tile_collider_size;
    size_t collider_h = static_cast<size_t>(dimension.y) * Tile::tile_size / Tile::tile_collider_size;

    std::cout << "Collision Map for object "
              << tileset_object_id
              << " (collider size: " << collider_w << "x" << collider_h << "):\n";

    std::vector<bool> collision_map = get_CollisionMap();

    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 << "? ";
            } else {
                std::cout << (collision_map[index] ? "1 " : "0 ");
            }
        }
        std::cout << "\n";
    }
}

void
Object::Draw(SDL_Renderer* renderer, SDL_FPoint position, bool is_transparent, int transparency) {
    SDL_Point obj_set_pos = get_SetPosition();
    SDL_FRect obj_src {
        static_cast<float>(obj_set_pos.x) * static_cast<float>(Tile::tile_size),
        static_cast<float>(obj_set_pos.y) * static_cast<float>(Tile::tile_size),
        static_cast<float>(dimension.x * static_cast<int>(Tile::tile_size)),
        static_cast<float>(dimension.y * static_cast<int>(Tile::tile_size))
    };

    int size = Tile::get_Size();
    SDL_FRect dst;
    if (object_type == ObjectType::VEGETABLE) {
        dst = { Scale(position.x + Map::offset.x),
                      Scale(position.y + Map::offset.y-7),
                      Scale(dimension.x * size),
                      Scale(dimension.y * size) };
    } else {
        dst = { Scale(position.x + Map::offset.x),
                      Scale(position.y + Map::offset.y),
                      Scale(dimension.x * size),
                      Scale(dimension.y * size) };
    }


    SDL_Texture* tex = Tile::tilesets[tileset_type][tileset_id].texture;

    if (is_transparent)
        SDL_SetTextureAlphaMod(tex, transparency);

    SDL_RenderTexture(renderer, tex, &obj_src, &dst);

    if (is_transparent)
        SDL_SetTextureAlphaMod(tex, 255);

#ifdef DEBUG_BUILD
    std::vector<bool> collision_map = get_CollisionMap();
    size_t scale = Tile::tile_size / Tile::tile_collider_size;
    size_t collider_w = static_cast<size_t>(dimension.x) * scale;
    size_t collider_h = static_cast<size_t>(dimension.y) * scale;
    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()) continue;

            if (collision_map[index]) {
                SDL_FRect col_rect = {
                    Scale(static_cast<float>(position.x + (x * Tile::tile_collider_size)) + Map::offset.x),
                    Scale(static_cast<float>(position.y + (y * Tile::tile_collider_size)) + Map::offset.y),
                    Scale(static_cast<float>(Tile::tile_collider_size)),
                    Scale(static_cast<float>(Tile::tile_collider_size))
                };

                SDL_SetRenderDrawColor(renderer, 255, 0, 0, 120);
                SDL_RenderFillRect(renderer, &col_rect);
            }
        }
    }
#endif
}

void
Object::Remove(Object* obj) {
    if (!obj) return;

    int start_x = obj->world_tile_position.x;
    int start_y = obj->world_tile_position.y;

    int end_x = start_x + obj->dimension.x;
    int end_y = start_y + obj->dimension.y;

    for (int y = start_y; y < end_y; ++y) {
        for (int x = start_x; x < end_x; ++x) {
            size_t index = static_cast<size_t>(y * Map::size.x + x);
            if (index >= Map::tile_map.size()) continue;

            auto& tile_objects = Map::tile_map[index].objects;

            tile_objects.erase(
                std::remove_if(tile_objects.begin(), tile_objects.end(),
                               [obj](const TileObject& to) { return to.object_id == obj->id; }),
                tile_objects.end());
            Map::set_TileCollissionForObjects({x, y});
        }
    }

    Object::objects.erase(
            std::remove(Object::objects.begin(),
                Object::objects.end(),
                obj),
            Object::objects.end()
            );


    delete obj;
}