RyanHub - file viewer
filename: src/thing/thing.c
branch: main
back to repo
// thing.c

#include "thing.h"
#include "physics.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

typedef struct {
    ThingTypeDef def;
    void* field_data[MAX_FIELDS];
    int count;
} ThingTypeStorage;

ThingInfo g_things[MAX_THINGS];
static ThingTypeStorage g_types[MAX_THING_TYPES];
static int g_type_count = 0;
static int g_thing_count = 0;


#if defined(DEBUG) || defined(_DEBUG)
#include <assert.h>
static inline void thing_assert_field(ThingTypeID type_id, const char* field_name, FieldType expected_type) {
    ThingTypeDef* def = &g_types[type_id].def;
    int found = 0;
    for (int i = 0; i < def->field_count; ++i) {
        if (strcmp(def->fields[i].name, field_name) == 0) {
            found = 1;
            if (def->fields[i].type != expected_type) {
                fprintf(stderr, "[Thing Assert] Field '%s' has wrong type (expected %d, got %d) in type '%s'\n",
                    field_name, expected_type, def->fields[i].type, def->name);
                assert(0 && "Field type mismatch!");
            }
            return;
        }
    }
    fprintf(stderr, "[Thing Assert] Field '%s' not found in type '%s'\n", field_name, def->name);
    assert(0 && "Field not found!");
}
#define ASSERT_VALID_THING(id) \
    do { \
        if ((id) < 0 || (id) >= MAX_THINGS || !g_things[id].alive) { \
            fprintf(stderr, "[Thing Error] Invalid or dead ThingID %d\n", id); \
            assert(0 && "Invalid ThingID access"); \
        } \
    } while(0)


#else
#define thing_assert_field(type_id, field_name, expected_type) ((void)0)
#define ASSERT_VALID_THING(id) ((void)0)
#endif



ThingTypeID thing_register_type(ThingTypeDef* def) {

    #if defined(DEBUG) || defined(_DEBUG)
        for (int i = 0; i < def->field_count; ++i) {
            for (int j = i + 1; j < def->field_count; ++j) {
                if (strcmp(def->fields[i].name, def->fields[j].name) == 0) {
                    fprintf(stderr, "[Field Error] Duplicate field '%s' in ThingType '%s'\n",
                        def->fields[i].name, def->name);
                    assert(0 && "Duplicate field name");
                }
            }
        }
        for (int i = 0; i < g_type_count; ++i) {
            if (strcmp(g_types[i].def.name, def->name) == 0) {
                fprintf(stderr, "[ThingType Error] Duplicate ThingType '%s' registered.\n", def->name);
                assert(0 && "ThingType already registered!");
            }
        }

        if (g_type_count >= MAX_THING_TYPES) {
            fprintf(stderr, "[ThingType Error] Max ThingTypes reached.\n");
            assert(0 && "Too many ThingTypes.");
        }
    #endif

    ThingTypeID id = g_type_count++;
    g_types[id].def = *def;
    g_types[id].count = 0;

    for (int i = 0; i < def->field_count; ++i) {
        FieldType t = def->fields[i].type;
        size_t size = 0;
        switch (t) {
        case FIELD_INT: size = sizeof(int); break;
        case FIELD_FLOAT: size = sizeof(float); break;
        case FIELD_VEC3: size = sizeof(float) * 3; break;
        case FIELD_STRING: size = sizeof(char*); break;
        }
        g_types[id].field_data[i] = calloc(MAX_THINGS, size);

    }

    return id;
}

ThingID thing_spawn(ThingTypeID type_id) {

    if (type_id < 0 || type_id >= g_type_count || g_thing_count >= MAX_THINGS) return -1;

    int slot = -1;
    for (int i = 0; i < MAX_THINGS; ++i) {
        if (!g_things[i].alive) {
            slot = i;
            break;
        }
    }
    if (slot == -1) return -1;

    g_things[slot].alive = 1;
    g_things[slot].type_id = type_id;
    g_things[slot].index = g_types[type_id].count++;

    #if defined(DEBUG) || defined(_DEBUG)
        printf("[Thing] Spawned ThingID %d of type '%s'\n", slot, g_types[type_id].def.name);
    #endif

    return slot;
}

void thing_destroy(ThingID id) {
    if (id < 0 || id >= MAX_THINGS || !g_things[id].alive) return;
    g_things[id].alive = 0;

    #if defined(DEBUG) || defined(_DEBUG)
        printf("[Thing] Destroyed ID: %d\n", id);
    #endif

    // this doesnt actually deallocate, we will do that seperately
}

static int get_field_index(ThingTypeID type_id, const char* name) {
    ThingTypeDef* def = &g_types[type_id].def;
    for (int i = 0; i < def->field_count; ++i) {
        if (strcmp(def->fields[i].name, name) == 0) return i;
    }
    return -1;
}

ThingTypeDef* thing_get_typedef(ThingID id) {
    if (id < 0 || id >= MAX_THINGS || !g_things[id].alive) return NULL;
    int type_id = g_things[id].type_id;
    if (type_id < 0 || type_id >= g_type_count) return NULL;
    return &g_types[type_id].def;
}

int thing_has_field(ThingID id, const char* field_name) {
    ThingTypeDef* def = thing_get_typedef(id);
    if (!def) return 0;
    for (int i = 0; i < def->field_count; ++i) {
        if (strcmp(def->fields[i].name, field_name) == 0)
            return 1;
    }
    return 0;
}

float thing_get_float(ThingID id, const char* name) {
    if (!g_things[id].alive) return 0.0f;
    int type_id = g_things[id].type_id;

    #if defined(DEBUG) || defined(_DEBUG)
        ASSERT_VALID_THING(id);
        thing_assert_field(type_id, name, FIELD_FLOAT);
    #endif

    int field_index = get_field_index(type_id, name);
    if (field_index < 0 || g_types[type_id].def.fields[field_index].type != FIELD_FLOAT) return 0.0f;
    return ((float*)g_types[type_id].field_data[field_index])[g_things[id].index];
}

int thing_get_int(ThingID id, const char* name) {
    if (!g_things[id].alive) return 0;
    int type_id = g_things[id].type_id;

    #if defined(DEBUG) || defined(_DEBUG)
        ASSERT_VALID_THING(id);
        thing_assert_field(type_id, name, FIELD_INT);
    #endif

    int field_index = get_field_index(type_id, name);
    if (field_index < 0 || g_types[type_id].def.fields[field_index].type != FIELD_INT) return 0;
    return ((int*)g_types[type_id].field_data[field_index])[g_things[id].index];
}

void thing_get_vec3(ThingID id, const char* name, vec3 out) {
    glm_vec3_zero(out);
    if (!g_things[id].alive) return;
    int type_id = g_things[id].type_id;

    #if defined(DEBUG) || defined(_DEBUG)
        ASSERT_VALID_THING(id);
        thing_assert_field(type_id, name, FIELD_VEC3);
    #endif

    int field_index = get_field_index(type_id, name);
    if (field_index < 0 || g_types[type_id].def.fields[field_index].type != FIELD_VEC3) return;

    float* data = &((float*)g_types[type_id].field_data[field_index])[g_things[id].index * 3];

    glm_vec3_copy(data, out);
}

const char* thing_get_string(ThingID id, const char* name) {
    if (!g_things[id].alive) return NULL;
    int type_id = g_things[id].type_id;

    #if defined(DEBUG) || defined(_DEBUG)
        ASSERT_VALID_THING(id);
        thing_assert_field(type_id, name, FIELD_STRING);
    #endif

    int field_index = get_field_index(type_id, name);
    if (field_index < 0 || g_types[type_id].def.fields[field_index].type != FIELD_STRING) return NULL;
    return ((const char**)g_types[type_id].field_data[field_index])[g_things[id].index];
}

void thing_set_float(ThingID id, const char* name, float f) {
    if (!g_things[id].alive) return;
    int type_id = g_things[id].type_id;

    #if defined(DEBUG) || defined(_DEBUG)
        ASSERT_VALID_THING(id);
        thing_assert_field(type_id, name, FIELD_FLOAT);
    #endif

    int field_index = get_field_index(type_id, name);
    if (field_index < 0 || g_types[type_id].def.fields[field_index].type != FIELD_FLOAT) return;
    ((float*)g_types[type_id].field_data[field_index])[g_things[id].index] = f;
}

void thing_set_int(ThingID id, const char* name, int i) {
    if (!g_things[id].alive) return;
    int type_id = g_things[id].type_id;

    #if defined(DEBUG) || defined(_DEBUG)
        ASSERT_VALID_THING(id);
        thing_assert_field(type_id, name, FIELD_INT);
    #endif

    int field_index = get_field_index(type_id, name);
    if (field_index < 0 || g_types[type_id].def.fields[field_index].type != FIELD_INT) return;
    ((int*)g_types[type_id].field_data[field_index])[g_things[id].index] = i;
}

void thing_set_vec3(ThingID id, const char* name, vec3 in) {

    if (!g_things[id].alive) return;
    int type_id = g_things[id].type_id;

    #if defined(DEBUG) || defined(_DEBUG)
        ASSERT_VALID_THING(id);
        thing_assert_field(type_id, name, FIELD_VEC3);
    #endif

    int field_index = get_field_index(type_id, name);
    if (field_index < 0 || g_types[type_id].def.fields[field_index].type != FIELD_VEC3) return;
    float* ptr = &((float*)g_types[type_id].field_data[field_index])[g_things[id].index * 3];
    glm_vec3_copy(in, ptr);
}

void thing_set_string(ThingID id, const char* name, const char* s) {
    if (!g_things[id].alive) return;
    int type_id = g_things[id].type_id;

    #if defined(DEBUG) || defined(_DEBUG)
        ASSERT_VALID_THING(id);
        thing_assert_field(type_id, name, FIELD_STRING);
    #endif

    int field_index = get_field_index(type_id, name);
    if (field_index < 0 || g_types[type_id].def.fields[field_index].type != FIELD_STRING) return;
    ((const char**)g_types[type_id].field_data[field_index])[g_things[id].index] = s;
}

void thing_update_all() {
    //printf("update all: \n");

    for (int i = 0; i < MAX_THINGS; ++i) {

        if (!g_things[i].alive) continue;


        ThingTypeID type_id = g_things[i].type_id;
        int index = g_things[i].index;

        if (g_types[type_id].def.update) {
            g_types[type_id].def.update(i);

        }
    }
}

void thing_render_all() {
    //printf("render all \n");

    for (int i = 0; i < MAX_THINGS; ++i) {
        if (!g_things[i].alive) continue;

        ThingTypeID type_id = g_things[i].type_id;

        if (g_types[type_id].def.render) {
            g_types[type_id].def.render(i);
        }
    }
}

void thing_physics_all() {
    // iterations to prevent tunneling
    for (int solver = 0; solver < 6; ++solver) {
        for (int i = 0; i < MAX_THINGS; ++i) {
            if (!g_things[i].alive) continue;
            if (thing_has_field(i, "vel")) {
                integrate_physics(i);
            }
        }
        for (int i = 0; i < MAX_THINGS; ++i) {
            if (!g_things[i].alive) continue;
            ThingTypeID type_id = g_things[i].type_id;
            if (g_types[type_id].def.physics) {
                g_types[type_id].def.physics(i);
            }
        }
    }
}


void thing_clear_all() {
    for (int tid = 0; tid < g_type_count; ++tid) {
        ThingTypeStorage* type = &g_types[tid];
        for (int f = 0; f < type->def.field_count; ++f) {
            if (type->field_data[f]) {
                free(type->field_data[f]);
                type->field_data[f] = NULL;
            }
        }
        type->count = 0;
    }

    for (int i = 0; i < MAX_THINGS; ++i) {
        g_things[i].alive = 0;
        g_things[i].type_id = 0;
        g_things[i].index = 0;
    }

    g_thing_count = 0;
    g_type_count = 0;
}