diff --git a/src/character.c b/src/character.c index 4a28ec5..05341b1 100644 --- a/src/character.c +++ b/src/character.c @@ -105,8 +105,13 @@ SYMBOL_EXPORT void LoadSpritesheets(struct Game* game, struct Character* charact PrintConsole(game, "- %s", tmp->name); if ((!tmp->bitmap) && (tmp->file)) { char filename[255] = {0}; - snprintf(filename, 255, "sprites/%s/%s", character->name, tmp->file); - tmp->bitmap = al_load_bitmap(GetDataFilePath(game, filename)); + if (strstr(tmp->file, "../") == tmp->file) { + snprintf(filename, 255, "sprites/%s", tmp->file + 3); + } else { + snprintf(filename, 255, "sprites/%s/%s", character->name, tmp->file); + } + tmp->bitmap = AddBitmap(game, filename); + tmp->filepath = strdup(filename); tmp->width = al_get_bitmap_width(tmp->bitmap) / tmp->cols; tmp->height = al_get_bitmap_height(tmp->bitmap) / tmp->rows; } @@ -116,8 +121,13 @@ SYMBOL_EXPORT void LoadSpritesheets(struct Game* game, struct Character* charact PrintConsole(game, " - %s", tmp->frames[i].file); } char filename[255] = {0}; - snprintf(filename, 255, "sprites/%s/%s", character->name, tmp->frames[i].file); - tmp->frames[i].bitmap = al_load_bitmap(GetDataFilePath(game, filename)); + if (strstr(tmp->frames[i].file, "../") == tmp->frames[i].file) { + snprintf(filename, 255, "sprites/%s", tmp->frames[i].file + 3); + } else { + snprintf(filename, 255, "sprites/%s/%s", character->name, tmp->frames[i].file); + } + tmp->frames[i].bitmap = AddBitmap(game, filename); + tmp->frames[i].filepath = strdup(filename); int width = al_get_bitmap_width(tmp->frames[i].bitmap) + tmp->frames[i].x; if (width > tmp->width) { tmp->width = width; @@ -146,12 +156,14 @@ SYMBOL_EXPORT void UnloadSpritesheets(struct Game* game, struct Character* chara struct Spritesheet* tmp = character->spritesheets; while (tmp) { for (int i = 0; i < tmp->frameCount; i++) { - if (tmp->frames[i].bitmap) { + if (tmp->frames[i].filepath) { + RemoveBitmap(game, tmp->frames[i].filepath); + } else { al_destroy_bitmap(tmp->frames[i].bitmap); } } if (tmp->bitmap) { - al_destroy_bitmap(tmp->bitmap); + RemoveBitmap(game, tmp->filepath); } tmp->bitmap = NULL; tmp = tmp->next; @@ -227,6 +239,8 @@ SYMBOL_EXPORT void RegisterSpritesheet(struct Game* game, struct Character* char strncpy(s->predecessor, predecessor, len); } + s->filepath = NULL; + { s->file = NULL; const char* file = al_get_config_value(config, "animation", "file"); @@ -263,6 +277,7 @@ SYMBOL_EXPORT void RegisterSpritesheet(struct Game* game, struct Character* char s->frames[i].file = malloc(len * sizeof(char)); strncpy(s->frames[i].file, file, len); } + s->frames[i].filepath = NULL; if (!file) { s->frames[i].col = i % s->cols; @@ -342,15 +357,23 @@ SYMBOL_EXPORT void DestroyCharacter(struct Game* game, struct Character* charact free(tmp->file); } for (int i = 0; i < tmp->frameCount; i++) { - if (tmp->frames[i].bitmap) { + if (tmp->frames[i].filepath) { + RemoveBitmap(game, tmp->frames[i].filepath); + } else { al_destroy_bitmap(tmp->frames[i].bitmap); } if (tmp->frames[i].file) { free(tmp->frames[i].file); } + if (tmp->frames[i].filepath) { + free(tmp->frames[i].filepath); + } } if (tmp->bitmap) { - al_destroy_bitmap(tmp->bitmap); + RemoveBitmap(game, tmp->filepath); + } + if (tmp->filepath) { + free(tmp->filepath); } free(tmp->frames); free(tmp->name); @@ -475,6 +498,7 @@ SYMBOL_EXPORT ALLEGRO_TRANSFORM GetCharacterTransform(struct Game* game, struct ALLEGRO_TRANSFORM transform; int w = character->spritesheet->width, h = character->spritesheet->height; al_identity_transform(&transform); + al_translate_transform(&transform, -w / 2.0, -h / 2.0); al_scale_transform(&transform, ((character->flipX ^ character->spritesheet->flipX ^ character->frame->flipX) ? -1 : 1), ((character->flipY ^ character->spritesheet->flipY ^ character->frame->flipY) ? -1 : 1)); // flipping; FIXME: should it be here or later? al_translate_transform(&transform, w / 2.0, h / 2.0); @@ -483,10 +507,13 @@ SYMBOL_EXPORT ALLEGRO_TRANSFORM GetCharacterTransform(struct Game* game, struct al_translate_transform(&transform, -w * character->spritesheet->pivotX, -h * character->spritesheet->pivotY); // pivot al_scale_transform(&transform, character->scaleX, character->scaleY); al_rotate_transform(&transform, character->angle); + al_translate_transform(&transform, GetCharacterX(game, character), GetCharacterY(game, character)); // position + if (character->parent) { ALLEGRO_TRANSFORM parent = GetCharacterTransform(game, character->parent); al_compose_transform(&transform, &parent); + // FIXME: position should be calculated in relation to parents pivot point } return transform; } diff --git a/src/character.h b/src/character.h index 2609759..59c4879 100644 --- a/src/character.h +++ b/src/character.h @@ -27,6 +27,7 @@ struct SpritesheetFrame { char* file; + char* filepath; ALLEGRO_BITMAP* bitmap; double duration; int row; @@ -46,6 +47,7 @@ struct Spritesheet { int cols; /*!< Number of columns in the spritesheet. */ double duration; char* file; + char* filepath; int repeats; /*!< Number of repeats to make before the spritesheet is changed to its successor. */ char* successor; /*!< Name of animation successor. If it's not blank, then animation will be played only once. */ char* predecessor; diff --git a/src/internal.c b/src/internal.c index 7c4970c..1d872a5 100644 --- a/src/internal.c +++ b/src/internal.c @@ -359,13 +359,13 @@ SYMBOL_INTERNAL void CloseGamestate(struct Game* game, struct Gamestate* gamesta al_destroy_bitmap(gamestate->fb); } -SYMBOL_INTERNAL struct libsuperderpy_list* AddToList(struct libsuperderpy_list* list, void* data) { +SYMBOL_INTERNAL struct List* AddToList(struct List* list, void* data) { if (!list) { - list = malloc(sizeof(struct libsuperderpy_list)); + list = malloc(sizeof(struct List)); list->data = data; list->next = NULL; } else { - struct libsuperderpy_list* elem = malloc(sizeof(struct libsuperderpy_list)); + struct List* elem = malloc(sizeof(struct List)); elem->next = list; elem->data = data; list = elem; @@ -373,12 +373,12 @@ SYMBOL_INTERNAL struct libsuperderpy_list* AddToList(struct libsuperderpy_list* return list; } -static bool Identity(struct libsuperderpy_list* elem, void* data) { +static bool Identity(struct List* elem, void* data) { return elem->data == data; } -SYMBOL_INTERNAL struct libsuperderpy_list* FindInList(struct libsuperderpy_list* list, void* data, bool (*identity)(struct libsuperderpy_list* elem, void* data)) { - struct libsuperderpy_list* tmp = list; +SYMBOL_INTERNAL struct List* FindInList(struct List* list, void* data, bool (*identity)(struct List* elem, void* data)) { + struct List* tmp = list; if (!identity) { identity = Identity; } @@ -391,8 +391,8 @@ SYMBOL_INTERNAL struct libsuperderpy_list* FindInList(struct libsuperderpy_list* return NULL; } -SYMBOL_INTERNAL void* RemoveFromList(struct libsuperderpy_list** list, void* data, bool (*identity)(struct libsuperderpy_list* elem, void* data)) { - struct libsuperderpy_list *prev = NULL, *tmp = *list, *start; +SYMBOL_INTERNAL void* RemoveFromList(struct List** list, void* data, bool (*identity)(struct List* elem, void* data)) { + struct List *prev = NULL, *tmp = *list, *start; void* d = NULL; if (!identity) { identity = Identity; @@ -424,7 +424,7 @@ SYMBOL_INTERNAL void* AddGarbage(struct Game* game, void* data) { } SYMBOL_INTERNAL void ClearGarbage(struct Game* game) { - struct libsuperderpy_list* tmp; + struct List* tmp; while (game->_priv.garbage) { free(game->_priv.garbage->data); tmp = game->_priv.garbage->next; @@ -438,16 +438,16 @@ SYMBOL_INTERNAL void AddTimeline(struct Game* game, struct Timeline* timeline) { } SYMBOL_INTERNAL void RemoveTimeline(struct Game* game, struct Timeline* timeline) { - struct libsuperderpy_list* tmp = game->_priv.timelines; + struct List* tmp = game->_priv.timelines; if (tmp->data == timeline) { - struct libsuperderpy_list* next = tmp->next; + struct List* next = tmp->next; free(tmp); game->_priv.timelines = next; return; } while (tmp->next) { if (tmp->next->data == timeline) { - struct libsuperderpy_list* next = tmp->next->next; + struct List* next = tmp->next->next; free(tmp->next); tmp->next = next; return; @@ -500,7 +500,7 @@ SYMBOL_INTERNAL void DrawTimelines(struct Game* game) { if (!game->_priv.showtimeline) { return; } - struct libsuperderpy_list* tmp = game->_priv.timelines; + struct List* tmp = game->_priv.timelines; int i = 0; while (tmp) { DrawTimeline(game, tmp->data, i); @@ -574,3 +574,54 @@ SYMBOL_INTERNAL char* GetGameName(struct Game* game, const char* format) { SUPPRESS_END return AddGarbage(game, result); } + +static int HashString(struct Game* game, const char* str) { + unsigned long hash = 5381; + int c; + + while ((c = *str++)) { + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + } + + //PrintConsole(game, "sum %d, bucket %d", hash, hash % LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS); + return hash % LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS; +} + +static bool RefCountIdentity(struct List* elem, void* data) { + struct RefCount* item = elem->data; + return strcmp(data, item->id) == 0; +} + +SYMBOL_INTERNAL ALLEGRO_BITMAP* AddBitmap(struct Game* game, char* filename) { + int bucket = HashString(game, filename); + struct List* item = FindInList(game->_priv.bitmaps[bucket], filename, RefCountIdentity); + struct RefCount* rc; + if (item) { + rc = item->data; + rc->counter++; + } else { + rc = malloc(sizeof(struct RefCount)); + rc->counter = 1; + rc->id = strdup(filename); + rc->data = al_load_bitmap(GetDataFilePath(game, filename)); + game->_priv.bitmaps[bucket] = AddToList(game->_priv.bitmaps[bucket], rc); + } + return rc->data; +} + +SYMBOL_INTERNAL void RemoveBitmap(struct Game* game, char* filename) { + int bucket = HashString(game, filename); + struct List* item = FindInList(game->_priv.bitmaps[bucket], filename, RefCountIdentity); + if (item) { + struct RefCount* rc = item->data; + rc->counter--; + if (rc->counter == 0) { + RemoveFromList(&game->_priv.bitmaps[bucket], filename, RefCountIdentity); + al_destroy_bitmap(rc->data); + free(rc->id); + free(rc); + } + } else { + PrintConsole(game, "Tried to remove non-existent bitmap %s!", filename); + } +} diff --git a/src/internal.h b/src/internal.h index c089845..eb78bf8 100644 --- a/src/internal.h +++ b/src/internal.h @@ -46,9 +46,15 @@ #define SUPPRESS_END #endif -struct libsuperderpy_list { +struct RefCount { + int counter; + char* id; void* data; - struct libsuperderpy_list* next; +}; + +struct List { + void* data; + struct List* next; }; struct GamestateLoadingThreadData { @@ -80,9 +86,9 @@ void GamestateProgress(struct Game* game); void* AddGarbage(struct Game* game, void* data); void ClearGarbage(struct Game* game); void ClearScreen(struct Game* game); -struct libsuperderpy_list* AddToList(struct libsuperderpy_list* list, void* data); -struct libsuperderpy_list* FindInList(struct libsuperderpy_list* list, void* data, bool (*identity)(struct libsuperderpy_list* elem, void* data)); -void* RemoveFromList(struct libsuperderpy_list** list, void* data, bool (*identity)(struct libsuperderpy_list* elem, void* data)); +struct List* AddToList(struct List* list, void* data); +struct List* FindInList(struct List* list, void* data, bool (*identity)(struct List* elem, void* data)); +void* RemoveFromList(struct List** list, void* data, bool (*identity)(struct List* elem, void* data)); void AddTimeline(struct Game* game, struct Timeline* timeline); void RemoveTimeline(struct Game* game, struct Timeline* timeline); void DrawTimelines(struct Game* game); @@ -96,5 +102,7 @@ void ResumeExecution(struct Game* game); void ReloadShaders(struct Game* game, bool force); void DestroyShaders(struct Game* game); char* GetGameName(struct Game* game, const char* format); +ALLEGRO_BITMAP* AddBitmap(struct Game* game, char* filename); +void RemoveBitmap(struct Game* game, char* filename); #endif diff --git a/src/libsuperderpy.h b/src/libsuperderpy.h index 1917e7e..9a50551 100644 --- a/src/libsuperderpy.h +++ b/src/libsuperderpy.h @@ -57,6 +57,8 @@ struct GamestateResources; #include #include +#define LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS 16 + struct Gamestate; struct Viewport { @@ -129,7 +131,7 @@ struct Game { struct Gamestate* current_gamestate; - struct libsuperderpy_list *garbage, *timelines, *shaders; + struct List *garbage, *timelines, *shaders, *bitmaps[LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS]; bool draw; diff --git a/src/shader.c b/src/shader.c index ef4661b..826b08c 100644 --- a/src/shader.c +++ b/src/shader.c @@ -96,7 +96,7 @@ SYMBOL_EXPORT ALLEGRO_SHADER* CreateShader(struct Game* game, const char* vertex return shader; } -static bool ShaderIdentity(struct libsuperderpy_list* item, void* shader) { +static bool ShaderIdentity(struct List* item, void* shader) { return ((struct ShaderListItem*)item->data)->shader == shader; } @@ -118,7 +118,7 @@ SYMBOL_EXPORT void DestroyShader(struct Game* game, ALLEGRO_SHADER* shader) { } SYMBOL_INTERNAL void ReloadShaders(struct Game* game, bool force) { - struct libsuperderpy_list* list = game->_priv.shaders; + struct List* list = game->_priv.shaders; PrintConsole(game, "Reloading shaders..."); while (list) { struct ShaderListItem* item = list->data; @@ -150,7 +150,7 @@ SYMBOL_INTERNAL void DestroyShaders(struct Game* game) { if (item->fragment) { free(item->fragment); } - struct libsuperderpy_list* prev = game->_priv.shaders; + struct List* prev = game->_priv.shaders; game->_priv.shaders = game->_priv.shaders->next; free(prev); }