// 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;
}