diff --git a/game/main.c b/game/main.c index f26dafb..977b2b5 100644 --- a/game/main.c +++ b/game/main.c @@ -87,6 +87,15 @@ typedef enum { ENTITY_OBJ, } entitytag_t; +/* + * Common entity header -- this must be at the start of each type of + * entity structure. + * + * The tag is used to distinguish different types of entity when + * accessing them through the drawlist. The prev and next pointers + * allow the drawlist to be threaded through all entities as a + * doubly-linked list. + */ typedef struct entity { entitytag_t tag; struct entity *prev; @@ -559,6 +568,87 @@ static inline bool tile_passable(const map_t map, int x, int y) return true; } +static inline int dynentity_bottom(dynentity_t *e) +{ + return e->pos.y + e->ext.y / 2; +} + +static inline int objentity_bottom(objentity_t *e) +{ + return e->pos.y; +} + +static inline int entity_bottom(entity_t *e) +{ + switch (e->tag) { + case ENTITY_DYN: + return dynentity_bottom((dynentity_t *)e); + case ENTITY_OBJ: + return objentity_bottom((objentity_t *)e); + } +} + +static void update_drawlist(dynentity_t *e, entity_t **drawlist) +{ + if (e->dir.y == 0) + return; + + if (e->dir.y > 0) { + // The entity moved down -- check if it needs to be moved + // along in the drawlist. + + entity_t *n = e->e.next; + if (n == NULL) + // The entity is already at the end -- no update required. + return; + + const int y = dynentity_bottom(e); + int nexty = entity_bottom(n); + if (y <= nexty) + // The entity is still behind the next entity -- no update + // required. + return; + + // Update required -- find the earliest entity that is in + // front of e, or the end of the list. + while (n->next != NULL && y > nexty) { + n = n->next; + nexty = entity_bottom(n); + } + assert(n != NULL); + + if (y > nexty) { + // The end of the list was reached without finding a + // larger or equal y -- move the entity to the end of the + // list (after n). + e->e.prev->next = e->e.next; + e->e.next->prev = e->e.prev; + + e->e.prev = n; + e->e.next = NULL; + n->next = (entity_t *)e; + } else { + // An entity with greater or equal y was found -- move the + // entity to just before n. + + if (e->e.prev == NULL) { + // The entity is at the beginning of the list, we need + // to update the drawlist pointer. + *drawlist = e->e.next; + e->e.next->prev = NULL; + } else { + e->e.prev->next = e->e.next; + e->e.next->prev = e->e.prev; + } + + e->e.prev = n->prev; + e->e.next = n; + n->prev->next = (entity_t *)e; + n->prev = (entity_t *)e; + } + } +} + static void update_dynentity(gamestate_t *state, dynentity_t *e, double dt) { if (0 == e->speed) { @@ -602,6 +692,8 @@ static void update_dynentity(gamestate_t *state, dynentity_t *e, double dt) if (valid) { e->pos.x = nextx; e->pos.y = nexty; + + update_drawlist(e); } }