diff --git a/CMakeLists.txt b/CMakeLists.txt index eff5630..3b77b8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,8 @@ add_executable(breakhack src/linkedlist src/hashtable src/gui + src/item + src/item_builder ) target_link_libraries(breakhack diff --git a/src/hashtable.c b/src/hashtable.c index a8745a5..cbbf6cb 100644 --- a/src/hashtable.c +++ b/src/hashtable.c @@ -35,8 +35,8 @@ hash(Hashtable *table, const char *key) // TODO(Linus): This isn't very good, // bad distribution on similar strings while (hashval < ULONG_MAX && i < strlen(key)) { - hashval = hashval << 8; hashval += key[i++]; + hashval = hashval << 8; } return hashval % table->size; diff --git a/src/item.c b/src/item.c new file mode 100644 index 0000000..e600e3f --- /dev/null +++ b/src/item.c @@ -0,0 +1,25 @@ +#include "item.h" +#include "util.h" + +Item * +item_create(void) +{ + Item *item = ec_malloc(sizeof(Item)); + item->sprite = NULL; + item->collected = false; + item->effect = NULL; + return item; +} + +void item_render(Item *item, Camera *cam) +{ + sprite_render(item->sprite, cam); +} + +void +item_destroy(Item *item) +{ + if (item->sprite) + sprite_destroy(item->sprite); + free(item); +} \ No newline at end of file diff --git a/src/item.h b/src/item.h new file mode 100644 index 0000000..f780e69 --- /dev/null +++ b/src/item.h @@ -0,0 +1,26 @@ +#ifndef ITEM_H_ +#define ITEM_H_ + +#include + +#include "sprite.h" +#include "position.h" +#include "player.h" +#include "camera.h" + +typedef struct Item_t { + Sprite *sprite; + bool collected; + void (*effect)(struct Item_t *, Player *); +} Item; + +Item * +item_create(void); + +void +item_render(Item*, Camera*); + +void +item_destroy(Item*); + +#endif // ITEM_H_ \ No newline at end of file diff --git a/src/item_builder.c b/src/item_builder.c new file mode 100644 index 0000000..7042fb6 --- /dev/null +++ b/src/item_builder.c @@ -0,0 +1,88 @@ +#include + +#include "item_builder.h" +#include "texture.h" +#include "util.h" +#include "gui.h" + +static ItemBuilder *builder = NULL; + +void +item_builder_init(SDL_Renderer *renderer) +{ + builder = ec_malloc(sizeof(ItemBuilder)); + builder->textures = ht_create(20); + builder->renderer = renderer; +} + +static void +check_builder() +{ + if (!builder) + fatal("item_builder_init() not run"); +} + +static Texture * +load_texture(const char *path) +{ + Texture *t = ht_get(builder->textures, path); + if (!t) { + t = texture_create(); + texture_load_from_file(t, path, builder->renderer); + t->dim = (Dimension) { 32, 32 }; + } + return t; +} + +static void +add_health(Item *item, Player *player) +{ + int original_hp = player->stats.hp; + player->stats.hp += 2; + if (player->stats.hp > player->stats.maxhp) + player->stats.hp = player->stats.maxhp; + + gui_log("You gained %d health", player->stats.hp - original_hp); +} + +static Item * +create_health() +{ + Texture *t; + Item *item; + + item = item_create(); + t = load_texture("assets/Items/Potion.png"); + + item->sprite = sprite_create(); + sprite_set_texture(item->sprite, t, 0); + item->sprite->clip = (SDL_Rect) { 0, 0, 16, 16 }; + item->effect = &add_health; + + return item; +} + +Item * +item_builder_build_item(ItemKey key) +{ + check_builder(); + + Item *item = NULL; + switch (key) { + case HEALTH: + item = create_health(builder); + break; + default: + fatal("in item_builder_build() : Unhandled item key %d", key); + break; + } + + return item; +} + +void +item_builder_close() +{ + ht_destroy_custom(builder->textures, (void (*)(void*)) &texture_destroy); + free(builder); +} \ No newline at end of file diff --git a/src/item_builder.h b/src/item_builder.h new file mode 100644 index 0000000..f55b41b --- /dev/null +++ b/src/item_builder.h @@ -0,0 +1,25 @@ +#ifndef ITEMBUILDER_H_ +#define ITEMBUILDER_H_ + +#include "item.h" +#include "hashtable.h" + +typedef struct { + Hashtable *textures; + SDL_Renderer *renderer; +} ItemBuilder; + +typedef enum { + HEALTH +} ItemKey; + +void +item_builder_init(SDL_Renderer *); + +Item * +item_builder_build_item(ItemKey key); + +void +item_builder_close(); + +#endif // ITEMBUILDER_H_ \ No newline at end of file diff --git a/src/main.c b/src/main.c index 7c27b39..e8fb1e2 100644 --- a/src/main.c +++ b/src/main.c @@ -15,6 +15,7 @@ #include "gamestate.h" #include "gui.h" #include "util.h" +#include "item_builder.h" static SDL_Window *gWindow = NULL; static SDL_Renderer *gRenderer = NULL; @@ -36,7 +37,6 @@ bool initSDL(void) { int imgFlags = IMG_INIT_PNG; Dimension dim = getScreenDimensions(); - //Dimension dim = (Dimension) { 1920, 1080 }; if (dim.height > 1080) { info("Hi resolution screen detected (%u x %u)", dim.width, dim.height); @@ -131,6 +131,7 @@ bool init(void) gCamera.renderer = gRenderer; gRoomMatrix = roommatrix_create(); gGui = gui_create(); + item_builder_init(gRenderer); } gGameState = PLAYING; @@ -188,6 +189,7 @@ static void run_game(void) { map_clear_dead_monsters(gMap); + map_clear_collected_items(gMap); roommatrix_populate_from_map(gRoomMatrix, gMap); roommatrix_add_lightsource(gRoomMatrix, &gPlayer->sprite->pos); @@ -266,6 +268,7 @@ void close(void) map_destroy(gMap); roommatrix_destroy(gRoomMatrix); gui_destroy(gGui); + item_builder_close(); SDL_DestroyRenderer(gRenderer); SDL_DestroyWindow(gWindow); gWindow = NULL; diff --git a/src/map.c b/src/map.c index 54146ba..0754e6b 100644 --- a/src/map.c +++ b/src/map.c @@ -2,6 +2,9 @@ #include "map.h" #include "map_lua.h" #include "util.h" +#include "item.h" +#include "item_builder.h" +#include "gui.h" static Room* create_room(void) @@ -27,6 +30,7 @@ Map* map_create() map->textures = linkedlist_create(); map->monsterTextures = ht_create(30); map->monsters = linkedlist_create(); + map->items = linkedlist_create(); map->currentRoom = (Position) { 0, 0 }; map->renderTimer = timer_create(); map->level = 1; @@ -102,7 +106,43 @@ map_clear_dead_monsters(Map *map) else last->next = current->next; - monster_destroy(current->data); + // TODO(Linus): We should really move this code somewhere else, perhaps to monster.c? + // Create a health drop + Monster *monster = current->data; + Item *item = item_builder_build_item(HEALTH); + item->sprite->pos = monster->sprite->pos; + linkedlist_append(&map->items, item); + gui_log("%s dropped a health potion", monster->label); + + monster_destroy(monster); + current->data = NULL; + next = current->next; + current->next = NULL; + linkedlist_destroy(¤t); + current = next; + continue; + } + last = current; + current = current->next; + } +} + +void +map_clear_collected_items(Map *map) +{ + LinkedList *last, *current, *next; + + last = NULL; + current = map->items; + + while (current != NULL) { + if (((Item*) current->data)->collected) { + if (last == NULL) + map->items = current->next; + else + last->next = current->next; + + item_destroy(current->data); current->data = NULL; next = current->next; current->next = NULL; @@ -182,6 +222,7 @@ void map_render(Map *map, Camera *cam) { unsigned int i, j; LinkedList *monsterItem; + LinkedList *items; Room *room; if (!timer_started(map->renderTimer)) { @@ -215,6 +256,13 @@ void map_render(Map *map, Camera *cam) monsterItem = monsterItem->next; monster_render(monster, cam); } + + items = map->items; + while (items != NULL) { + Item *item = items->data; + items = items->next; + item_render(item, cam); + } } void map_set_current_room(Map *map, Position *pos) @@ -269,12 +317,16 @@ void map_destroy(Map *map) map_room_destroy(map->rooms[i][j]); } } - while (map->textures != NULL) { + + while (map->textures != NULL) texture_destroy(linkedlist_pop(&map->textures)); - } - while (map->monsters != NULL) { + + while (map->monsters != NULL) monster_destroy(linkedlist_pop(&map->monsters)); - } + + while (map->items != NULL) + item_destroy(linkedlist_pop(&map->items)); + ht_destroy_custom(map->monsterTextures, (void (*)(void*)) texture_destroy); timer_destroy(map->renderTimer); free(map); diff --git a/src/map.h b/src/map.h index f9b506a..49fd93a 100644 --- a/src/map.h +++ b/src/map.h @@ -33,6 +33,7 @@ typedef struct Map_t { LinkedList *textures; Hashtable *monsterTextures; LinkedList *monsters; + LinkedList *items; Position currentRoom; Timer *renderTimer; int level; @@ -54,6 +55,8 @@ void map_move_monsters(Map*, RoomMatrix*); void map_clear_dead_monsters(Map*); +void map_clear_collected_items(Map*); + void map_render(Map*, Camera*); void map_set_current_room(Map*, Position*); diff --git a/src/player.c b/src/player.c index 10909a6..9db45e4 100644 --- a/src/player.c +++ b/src/player.c @@ -6,6 +6,7 @@ #include "monster.h" #include "util.h" #include "gui.h" +#include "item.h" #define ENGINEER_STATS { 12, 12, 5, 7, 2, 1, 1 } #define MAGE_STATS { 12, 12, 5, 7, 2, 1, 1 } @@ -53,6 +54,12 @@ has_collided(Player *player, RoomMatrix *matrix) gui_log("Ouch! There is something in the way"); } + if (space->item != NULL) { + if (space->item->effect) + space->item->effect(space->item, player); + space->item->collected = true; + } + return collided; } diff --git a/src/roommatrix.c b/src/roommatrix.c index 7a21faa..686404e 100644 --- a/src/roommatrix.c +++ b/src/roommatrix.c @@ -4,6 +4,7 @@ #include "util.h" #include "map.h" #include "player.h" +#include "item.h" RoomMatrix* roommatrix_create() { @@ -15,10 +16,12 @@ RoomMatrix* roommatrix_create() void roommatrix_populate_from_map(RoomMatrix *rm, Map *m) { int i, j; - Position monster_matrix_pos; + Position position; Room *r; Monster *monster; LinkedList *monsterItem; + Item *item; + LinkedList *items; roommatrix_reset(rm); @@ -51,14 +54,26 @@ void roommatrix_populate_from_map(RoomMatrix *rm, Map *m) if (!position_in_room(&monster->sprite->pos, &m->currentRoom)) continue; - monster_matrix_pos = + position = position_to_matrix_coords(&monster->sprite->pos); - rm->spaces[monster_matrix_pos.x][monster_matrix_pos.y] + rm->spaces[position.x][position.y] .occupied = true; - rm->spaces[monster_matrix_pos.x][monster_matrix_pos.y] + rm->spaces[position.x][position.y] .monster = monster; } + + items = m->items; + while (items) { + item = items->data; + items = items->next; + + if (!position_in_room(&item->sprite->pos, &m->currentRoom)) + continue; + + position = position_to_matrix_coords(&item->sprite->pos); + rm->spaces[position.x][position.y].item = item; + } } // TODO(Linus): These should probably be macros @@ -172,6 +187,7 @@ void roommatrix_reset(RoomMatrix *m) m->spaces[i][j].lightsource = false; m->spaces[i][j].light = 0; m->spaces[i][j].monster = NULL; + m->spaces[i][j].item = NULL; m->spaces[i][j].player = NULL; } } diff --git a/src/roommatrix.h b/src/roommatrix.h index 62d5458..c1b06d6 100644 --- a/src/roommatrix.h +++ b/src/roommatrix.h @@ -10,13 +10,15 @@ typedef struct Sprite_t Sprite; typedef struct Map_t Map; typedef struct Monster_t Monster; typedef struct Player_t Player; +typedef struct Item_t Item; typedef struct { bool occupied; bool lightsource; unsigned int light; - Monster* monster; - Player* player; + Monster *monster; + Player *player; + Item *item; } RoomSpace; typedef struct {