diff --git a/app/main.c b/app/main.c index c132988..fa4b8c4 100644 --- a/app/main.c +++ b/app/main.c @@ -48,6 +48,9 @@ #define BASEANIMPERIOD 100 +#define MAXOBJTYPES 8 +#define INITOBJCOLCAP 16 + #define NELEMS(a) (sizeof(a) / sizeof(a[0])) typedef enum { @@ -85,6 +88,22 @@ typedef struct { SDL_Rect src; } entity_t; +typedef struct { + unsigned animframes; + SDL_Rect src; + SDL_Texture *tex; +} objtype_t; + +typedef struct { + unsigned type; + ivec_t pos; +} obj_t; + +typedef struct { + size_t n, cap; + obj_t *buf; +} objcol_t; + static const char *assetdir; static SDL_Window *window; static SDL_Renderer *renderer; @@ -96,6 +115,8 @@ static const unsigned impassable[] = { 284, 485, 486, 525, 527, 566, 567, 731, 768, 770, 771, 804, 805, 806, 808, 845, }; +static objtype_t objtypes[MAXOBJTYPES]; +static objcol_t objcol; static inline double mag(dvec_t v) { @@ -188,6 +209,101 @@ static void maploadlayer(xmlNodePtr layernode, int layeridx) } } +static unsigned objtypeload(const char *templ) +{ + static char buf[MAX_PATH_LEN]; + assert(strlen(assetdir) + strlen(templ) + 1 < MAX_PATH_LEN); + strcpy(buf, assetdir); + strcat(buf, "/"); + strcat(buf, templ); + + // Identify object type ID + xmlDocPtr doc = xmlParseFile(buf); + assert(NULL != doc); + xmlNodePtr node = xmlDocGetRootElement(doc); + assert(NULL != node); + assert(xmlStrcmp(node->name, (const xmlChar *)"template") == 0); + node = node->xmlChildrenNode; + while (node != NULL + && xmlStrcmp(node->name, (const xmlChar *)"object") != 0) + node = node->next; + assert(NULL != node); + unsigned id + = atoi((const char *)xmlGetProp(node, (const xmlChar *)"gid")); + assert(id < MAXOBJTYPES); + + // Populate objtype struct if not already loaded + if (NULL == objtypes[id].tex) { + objtypes[id].src.x = 0; + objtypes[id].src.y = 0; + objtypes[id].src.w + = atoi((const char *)xmlGetProp(node, (const xmlChar *)"width")); + objtypes[id].src.h = atoi( + (const char *)xmlGetProp(node, (const xmlChar *)"height")); + + node = node->xmlChildrenNode; + while (node != NULL + && xmlStrcmp(node->name, (const xmlChar *)"properties") != 0) + node = node->next; + assert(NULL != node); + for (node = node->xmlChildrenNode; NULL != node; node = node->next) { + if (xmlStrcmp(node->name, (const xmlChar *)"property") != 0) + continue; + const char *key + = (const char *)xmlGetProp(node, (const xmlChar *)"name"); + const char *val + = (const char *)xmlGetProp(node, (const xmlChar *)"value"); + if (strcmp(key, "animframes") == 0) { + objtypes[id].animframes = atoi(val); + } else if (strcmp(key, "assetpath") == 0) { + assert(strlen(assetdir) + strlen(val) < MAX_PATH_LEN); + strcpy(buf, assetdir); + strcat(buf, val); + objtypes[id].tex = IMG_LoadTexture(renderer, buf); + assert(NULL != objtypes[id].tex); + } else { + assert(false); + } + } + } + + return id; +} + +static void maploadobjects(xmlNodePtr node) +{ + objcol.n = 0; + objcol.cap = INITOBJCOLCAP; + objcol.buf = malloc(sizeof(obj_t) * objcol.cap); + assert(objcol.buf); + + node = node->xmlChildrenNode; + assert(NULL != node); + do { + if (xmlStrcmp(node->name, (const xmlChar *)"object") != 0) + continue; + + // Get slot for object, growing buffer if needed. + if (objcol.n == objcol.cap) { + objcol.cap *= 2; + objcol.buf = realloc(objcol.buf, sizeof(obj_t) * objcol.cap); + assert(objcol.buf); + } + obj_t *o = &objcol.buf[objcol.n++]; + + // Load object type + const xmlChar *templ = xmlGetProp(node, (const xmlChar *)"template"); + assert(NULL != templ); + o->type = objtypeload((const char *)templ); + + // Load object location and set size from objtype + o->pos.x + = atoi((const char *)xmlGetProp(node, (const xmlChar *)"x")); + o->pos.y + = atoi((const char *)xmlGetProp(node, (const xmlChar *)"y")); + } while ((node = node->next) != NULL); +} + static void mapload(const char *path) { xmlDocPtr doc; @@ -197,14 +313,14 @@ static void mapload(const char *path) node = xmlDocGetRootElement(doc); assert(0 == xmlStrcmp(node->name, (const xmlChar *)"map")); node = node->xmlChildrenNode; - for (int l = 0; l < MAPNLAYERS; ++l) { - do - node = node->next; - while (NULL != node - && xmlStrcmp(node->name, (const xmlChar *)"layer") != 0); - assert(NULL != node); - maploadlayer(node, l); - } + int layer = 0; + assert(NULL != node); + do { + if (xmlStrcmp(node->name, (const xmlChar *)"layer") == 0) + maploadlayer(node, layer++); + else if (xmlStrcmp(node->name, (const xmlChar *)"objectgroup") == 0) + maploadobjects(node); + } while ((node = node->next) != NULL); } static void mapdraw(void) @@ -279,14 +395,33 @@ static void entitydraw(entity_t *e, uint64_t t) e->src.x = e->animstep.x * frame + e->svarstep.x * e->svar; e->src.y = e->animstep.y * frame + e->svarstep.y * e->svar; SDL_Rect dest = { - .x = SCALE * (int)(e->pos.x - vpos.x - e->src.w / 2), - .y = SCALE * (int)(e->pos.y - vpos.y - e->src.h / 2), + .x = SCALE * (e->pos.x - vpos.x - e->src.w / 2), + .y = SCALE * (e->pos.y - vpos.y - e->src.h / 2), .w = SCALE * e->src.w, .h = SCALE * e->src.h, }; SDL_RenderCopy(renderer, e->tex, &e->src, &dest); } +static void objsdraw(uint64_t t) +{ + for (unsigned i = 0; i < objcol.n; ++i) { + const obj_t *obj = &objcol.buf[i]; + assert(obj->type < MAXOBJTYPES); + const objtype_t *type = &objtypes[obj->type]; + assert(NULL != type->tex); + SDL_Rect src = type->src; + src.x += type->src.w * ((t / BASEANIMPERIOD) % type->animframes); + SDL_Rect dest = { + .x = SCALE * (obj->pos.x - vpos.x), + .y = SCALE * (obj->pos.y - vpos.y - type->src.h), + .w = SCALE * type->src.w, + .h = SCALE * type->src.h, + }; + SDL_RenderCopy(renderer, type->tex, &src, &dest); + } +} + int main(int argc, char *argv[]) { char path[MAX_PATH_LEN]; @@ -436,6 +571,7 @@ int main(int argc, char *argv[]) SDL_RenderClear(renderer); mapdraw(); + objsdraw(t); entitydraw(&p, t); SDL_RenderPresent(renderer); }