diff --git a/CMakeLists.txt b/CMakeLists.txt index 473f8a5..3515bce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ project(breakhack C) set(breakhack_GAME_TITLE "BreakHack") set(breakhack_MAJOR_VERSION 0) set(breakhack_MINOR_VERSION 2) -set(breakhack_PATCH_VERSION 1) +set(breakhack_PATCH_VERSION 0) set(breakhack_RELEASE_TYPE "(beta)") include(FindLua) @@ -100,6 +100,7 @@ include_directories( ${SDL2_IMAGE_INCLUDE_DIR} ${SDL2_TTF_INCLUDE_DIR} ${SDL2_MIXER_INCLUDE_DIR} + sqlite3 ) if (CMOCKA_FOUND) @@ -167,7 +168,7 @@ add_executable(breakhack src/projectile src/vector2d src/map_room_modifiers - src/sqlite3 + sqlite3/sqlite3 src/db src/settings src/actiontextbuilder @@ -177,10 +178,12 @@ add_executable(breakhack src/screen src/hiscore src/object + src/gui_util + src/tooltip ) # Sqlite has some warnings that I we don't need to see -set_source_files_properties(src/sqlite3.c COMPILE_FLAGS -w) +set_source_files_properties(sqlite3/sqlite3.c COMPILE_FLAGS -w) target_link_libraries(breakhack ${CMAKE_DL_LIBS} # Sqlite needs DL libs @@ -241,7 +244,7 @@ ENDIF () # LINT: if (CPPCHECK_FOUND) add_custom_target(lint - COMMAND ${CPPCHECK_EXECUTABLE} --force --language=c --template=gcc --error-exitcode=1 --quiet --enable=warning,style,performance,portability,information,missingInclude --suppress=*:src/sqlite3.? -isrc/sqlite3.c src/ + COMMAND ${CPPCHECK_EXECUTABLE} --force --language=c --template=gcc --error-exitcode=1 --quiet --enable=warning,style,performance,portability,information,missingInclude src/ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMENT "Run cppcheck" ) diff --git a/build/releasenotes/beta2.txt b/build/releasenotes/beta2.txt new file mode 100644 index 0000000..854a7f1 --- /dev/null +++ b/build/releasenotes/beta2.txt @@ -0,0 +1,20 @@ +f5a88fc Introduces texture blending +52913af Completes #42 Add boss 2 & 3 +449cc36 Fixes some minor buggs +f67aab0 Completes #43 Add win state +8c261fd Added the undead monsters for level > 10 +6326a64 Begins #42 Add boss 2 & 3 +f1b0045 Introduces the "orc levels" +ffe1736 Minor buggfixes and typos +8bf9329 Version bump and badges +8c25693 Moves sqlite3 files to separate dir +bec9eb4 Fixed a typo in skill tooltip +43f021a Created an FPS counter for debug mode +dbc36aa Fixes #41, Minimap +c390c02 Fixes #40, Prevent adjecant traps before lvl 4 +9ef97c0 Fixes #38, Artifact inventory +358c0c7 Fixes #37 and #39 Adds tooltips to everything +5f754d5 Merge branch 'master' into dev +30058ea Creates gui_util and moves some code out from gui.c +854f2c0 Prevent levels > 10 from crashing the game. +549f47a Minor code fixes and a slight tweak to the blue color diff --git a/data/monstergen.lua b/data/monstergen.lua index bd28aba..719d705 100644 --- a/data/monstergen.lua +++ b/data/monstergen.lua @@ -45,7 +45,9 @@ local behaviour = { guerilla = 3, coward = 4, sentinel = 5, - fire_demon = 6 + fire_demon = 6, + sorcerer = 7, + assassin = 8 } local stats = { @@ -84,19 +86,26 @@ local stats = { def = 0, speed = 1 }, + orc = { + hp = 20, + dmg = 2, + atk = 0, + def = 0, + speed = 1 + }, boss = { hp = 60, - dmg = 4, + dmg = 3, atk = 1, def = 0, speed = 1 }, platino = { - hp = 60, - dmg = 60, - atk = 60, - def = 60, - speed = 3 + hp = 90, + dmg = 1, + atk = 0, + def = 0, + speed = 1 } } @@ -134,6 +143,16 @@ for i=1,#pests do pests[i] = concat({ texturePaths.pest0, texturePaths.pest1 }, pests[i]) end +local avian = { + { stats.default, 0, 11*16, "A Small Brown Bat", behaviour.pacifist }, + { stats.default, 16, 11*16, "A Big Brown Bat", behaviour.normal }, + { stats.default, 32, 11*16, "A Vampire Bat", behaviour.guerilla }, + { stats.default, 48, 11*16, "A Rabid Bat", behaviour.hostile }, +} +for i=1,#avian do + avian[i] = concat({ texturePaths.avian0, texturePaths.avian1 }, avian[i]) +end + local misc = { { stats.misc, 0, 0, "A Giant Black Rat", behaviour.sentinel }, { stats.misc, 16, 0, "A Giant White Rat", behaviour.sentinel }, @@ -143,13 +162,33 @@ for i=1,#misc do misc[i] = concat({ texturePaths.misc0, texturePaths.misc1 }, misc[i]) end -local undead = { - -- UNDEAD +local reanimated = { { stats.undead, 0, 32, "A Skeleton", behaviour.normal }, { stats.undead, 48, 32, "A Black Skeleton", behaviour.normal }, { stats.undead, 64, 32, "A Zombie", behaviour.normal }, { stats.undead, 80, 32, "A Zombie", behaviour.normal } } +for i=1,#reanimated do + reanimated[i] = concat({ texturePaths.undead0, texturePaths.undead1 }, reanimated[i]) +end + +local undead = { + { stats.undead, 5*16, 16, "A Mummy", behaviour.normal }, + { stats.undead, 6*16, 16, "A Two Headed Mummy", behaviour.sentinel }, + { stats.undead, 0*16, 32, "A Skeleton", behaviour.normal }, + { stats.misc, 1*16, 32, "A Burning Skeleton", behaviour.fire_demon }, + { stats.misc, 2*16, 32, "An Eldritch Skeleton", behaviour.sorcerer }, + { stats.misc, 3*16, 32, "A Black Skeleton", behaviour.guerilla }, + { stats.misc, 4*16, 32, "A Zombie", behaviour.coward }, + { stats.misc, 5*16, 32, "A Pale Zombie", behaviour.coward }, + { stats.misc, 7*16, 32, "A Scorched Zombie", behaviour.fire_demon }, + { stats.undead, 0*16, 4*16, "A Whight", behaviour.coward }, + { stats.undead, 1*16, 4*16, "A Ghast", behaviour.sentinel }, + { stats.misc, 1*16, 4*16, "A Ghost", behaviour.guerilla }, + { stats.misc, 0*16, 5*16, "A Spectre", behaviour.sentinel }, + { stats.undead, 1*16, 5*16, "An Eldritch Spectre", behaviour.sorcerer }, + { stats.undead, 2*16, 5*16, "A Scorched Spectre", behaviour.fire_demon }, +} for i=1,#undead do undead[i] = concat({ texturePaths.undead0, texturePaths.undead1 }, undead[i]) end @@ -188,10 +227,36 @@ for i=1,#demon do demon[i] = concat({ texturePaths.demon0, texturePaths.demon1 }, demon[i]) end +local orcs = { + { stats.orc, 0, 4*16, "An Orc Guard", behaviour.normal }, + { stats.orc, 16, 4*16, "An Orc Seargeant", behaviour.coward }, + { stats.orc, 32, 4*16, "An Orc Militia", behaviour.hostile }, + { stats.orc, 48, 4*16, "An Orc Sentry", behaviour.sentinel }, + { stats.orc, 64, 4*16, "An Orc Brute", behaviour.guerilla }, + { stats.orc, 80, 4*16, "An Orc Captain", behaviour.hostile }, + { stats.orc, 96, 4*16, "An Orc Pyro", behaviour.fire_demon }, +} +for i=1,#orcs do + orcs[i] = concat({ texturePaths.humanoid0, texturePaths.humanoid1 }, orcs[i]) +end + +local assassins = { + { stats.misc, 1*16, 6*16, "A Reaper", behaviour.assassin }, + { stats.misc, 0*16, 7*16, "An Assassin", behaviour.assassin }, + { stats.misc, 1*16, 7*16, "A Royal Assassin", behaviour.assassin }, +} +for i=1,#assassins do + assassins[i] = concat({ texturePaths.undead0, texturePaths.undead1 }, assassins[i]) +end + local bosses = { - { stats.boss, 16, 5*16, "The Hell Hound", behaviour.fire_demon, true } + { stats.boss, 16, 5*16, "The Hell Hound", behaviour.fire_demon, true }, + { stats.boss, 16, 23*16, "The Cleric", behaviour.sorcerer, true }, + { stats.boss, 16, 8*16, "The Shadow", behaviour.assassin, true }, } bosses[1] = concat({ texturePaths.dog0, texturePaths.dog1 }, bosses[1]) +bosses[2] = concat({ texturePaths.humanoid0, texturePaths.humanoid1 }, bosses[2]) +bosses[3] = concat({ texturePaths.undead0, texturePaths.undead1 }, bosses[3]) local platino = { { @@ -201,7 +266,8 @@ local platino = { 48, 12*16, "Platino", - behaviour.sentinel + behaviour.sentinel, + true } } @@ -224,29 +290,24 @@ if(CURRENT_LEVEL > 0) then if (CURRENT_LEVEL == 1) then enemies = concat(enemies, pests) enemies = concat(enemies, misc) - enemies = concat(enemies, dogs) + elseif (CURRENT_LEVEL > 15) then + enemies = {} + enemies = concat(enemies, undead) + enemies = concat(enemies, orcs) + enemies = concat(enemies, assassins) + elseif (CURRENT_LEVEL > 10) then + enemies = {} + enemies = concat(enemies, undead) + enemies = concat(enemies, avian) elseif (CURRENT_LEVEL > 5) then enemies = {} - enemies = concat(enemies, demon) - enemies = concat(enemies, undead) - enemies = concat(enemies, reptile) - enemies = concat(enemies, misc) - elseif (CURRENT_LEVEL > 3) then - enemies = {} - enemies = concat(enemies, undead) - enemies = concat(enemies, reptile) - enemies = concat(enemies, misc) - enemies = concat(enemies, dogs) - elseif (CURRENT_LEVEL > 2) then - enemies = {} - enemies = concat(enemies, undead) - enemies = concat(enemies, reptile) - enemies = concat(enemies, misc) - enemies = concat(enemies, dogs) + enemies = concat(enemies, orcs) + enemies = concat(enemies, avian) elseif (CURRENT_LEVEL > 1) then enemies = {} - enemies = concat(enemies, undead) + enemies = concat(enemies, reanimated) enemies = concat(enemies, reptile) + enemies = concat(enemies, avian) enemies = concat(enemies, misc) enemies = concat(enemies, dogs) end @@ -279,7 +340,7 @@ function module.add_monsters_to_room(room, roomx, roomy) end function module.add_boss_to_room(room, roomx, roomy) - local boss = bosses[1] + local boss = bosses[CURRENT_LEVEL / 5] local success = false while not success do local rx = random(13) + 1 @@ -309,3 +370,4 @@ function module.load_monsters(map, monsters) end return module + diff --git a/data/trapgen.lua b/data/trapgen.lua index 2948d75..8edbd1c 100644 --- a/data/trapgen.lua +++ b/data/trapgen.lua @@ -35,9 +35,25 @@ function module.add_traps_to_room(room) while i < count do local rx = random(13) + 1 local ry = random(9) + 1 - if room_builder.is_tile_avilable(room, rx, ry) then - room.traps[rx][ry] = traps[random(#traps)] - i = i + 1 + if CURRENT_LEVEL < 4 then + if room_builder.is_tile_avilable(room, rx, ry) + and not room.traps[rx+1][ry] + and not room.traps[rx-1][ry] + and not room.traps[rx][ry+1] + and not room.traps[rx][ry-1] + and not room.traps[rx+1][ry+1] + and not room.traps[rx+1][ry-1] + and not room.traps[rx-1][ry+1] + and not room.traps[rx-1][ry-1] + then + room.traps[rx][ry] = traps[random(#traps)] + i = i + 1 + end + else + if room_builder.is_tile_avilable(room, rx, ry) then + room.traps[rx][ry] = traps[random(#traps)] + i = i + 1 + end end end end diff --git a/src/sqlite3.c b/sqlite3/sqlite3.c similarity index 100% rename from src/sqlite3.c rename to sqlite3/sqlite3.c diff --git a/src/sqlite3.h b/sqlite3/sqlite3.h similarity index 100% rename from src/sqlite3.h rename to sqlite3/sqlite3.h diff --git a/src/artifact.c b/src/artifact.c index d4f6e2e..99022d9 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -26,65 +26,39 @@ static void artifact_set_effect(Artifact *a, MagicalEffect effect) { - Texture *t; - a->effect = effect; switch (effect) { case IMPROVED_HEARING: a->info.name = "Potion of ear juice"; a->info.desc = "Your hearing is slightly improved"; - t = texturecache_add("Items/Potion.png"); - sprite_set_texture(a->sprite, t, 0); - a->sprite->clip = CLIP16(7*16, 4*16); break; case TRAP_AVOIDANCE: a->info.name = "Boot with nails inside"; a->info.desc = "You are lighter on your feet"; - t = texturecache_add("Items/Boot.png"); - sprite_set_texture(a->sprite, t, 0); - a->sprite->clip = CLIP16(5*16, 0); break; case PIERCING_DAGGERS: a->info.name = "Whetstone"; a->info.desc = "Your daggers are sharper"; - t = texturecache_add("Items/Rock.png"); - sprite_set_texture(a->sprite, t, 0); - a->sprite->clip = CLIP16(0, 0); break; case CHARGE_THROUGH: a->info.name = "Greasy shield"; a->info.desc = "You glide through obstructions"; - t = texturecache_add("Items/Shield.png"); - sprite_set_texture(a->sprite, t, 0); - a->sprite->clip = CLIP16(16, 0); break; case PUSH_BACK: a->info.name = "Glove of strength"; a->info.desc = "Your arm is stronger"; - t = texturecache_add("Items/Glove.png"); - sprite_set_texture(a->sprite, t, 0); - a->sprite->clip = CLIP16(0, 0); break; case DAGGER_RECOVERY: a->info.name = "Forging hammer"; a->info.desc = "Your daggers are more durable"; - t = texturecache_add("Items/LongWep.png"); - sprite_set_texture(a->sprite, t, 0); - a->sprite->clip = CLIP16(0, 6*16); break; case INCREASED_STUN: a->info.name = "Solid shield"; a->info.desc = "Your shield is harder"; - t = texturecache_add("Items/Shield.png"); - sprite_set_texture(a->sprite, t, 0); - a->sprite->clip = CLIP16(4*16, 0); break; case FEAR_INDUCING: a->info.name = "Ugly shirt"; a->info.desc = "You look disgusting"; - t = texturecache_add("Items/Armor.png"); - sprite_set_texture(a->sprite, t, 0); - a->sprite->clip = CLIP16(6*16, 8*16); break; default: break; @@ -107,11 +81,63 @@ artifact_create_random(Player *p, Uint8 level) return a; } +Sprite * +artifact_sprite_for(MagicalEffect effect) +{ + Sprite *sprite = sprite_create(); + Texture *t; + switch (effect) { + case IMPROVED_HEARING: + t = texturecache_add("Items/Potion.png"); + sprite_set_texture(sprite, t, 0); + sprite->clip = CLIP16(7*16, 4*16); + break; + case TRAP_AVOIDANCE: + t = texturecache_add("Items/Boot.png"); + sprite_set_texture(sprite, t, 0); + sprite->clip = CLIP16(5*16, 0); + break; + case PIERCING_DAGGERS: + t = texturecache_add("Items/Rock.png"); + sprite_set_texture(sprite, t, 0); + sprite->clip = CLIP16(0, 0); + break; + case CHARGE_THROUGH: + t = texturecache_add("Items/Shield.png"); + sprite_set_texture(sprite, t, 0); + sprite->clip = CLIP16(16, 0); + break; + case PUSH_BACK: + t = texturecache_add("Items/Glove.png"); + sprite_set_texture(sprite, t, 0); + sprite->clip = CLIP16(0, 0); + break; + case DAGGER_RECOVERY: + t = texturecache_add("Items/LongWep.png"); + sprite_set_texture(sprite, t, 0); + sprite->clip = CLIP16(0, 6*16); + break; + case INCREASED_STUN: + t = texturecache_add("Items/Shield.png"); + sprite_set_texture(sprite, t, 0); + sprite->clip = CLIP16(4*16, 0); + break; + case FEAR_INDUCING: + t = texturecache_add("Items/Armor.png"); + sprite_set_texture(sprite, t, 0); + sprite->clip = CLIP16(6*16, 8*16); + break; + default: + break; + } + return sprite; +} + Artifact * artifact_create(MagicalEffect effect) { Artifact *a = ec_malloc(sizeof(Artifact)); - a->sprite = sprite_create(); + a->sprite = artifact_sprite_for(effect); a->sprite->dim = GAME_DIMENSION; a->collected = false; a->level = 1; diff --git a/src/artifact.h b/src/artifact.h index eeb2471..689b77c 100644 --- a/src/artifact.h +++ b/src/artifact.h @@ -45,6 +45,9 @@ typedef struct Artifact { int level; } Artifact; +Sprite * +artifact_sprite_for(MagicalEffect); + Artifact * artifact_create_random(Player*, Uint8 level); diff --git a/src/defines.h b/src/defines.h index c190d64..d7db9f7 100644 --- a/src/defines.h +++ b/src/defines.h @@ -33,20 +33,21 @@ #define SPRITE_DIMENSION 16 /* Display stuff */ -#define GAME_VIEW_WIDTH (MAP_ROOM_WIDTH * TILE_DIMENSION) -#define GAME_VIEW_HEIGHT (MAP_ROOM_HEIGHT * TILE_DIMENSION) +#define GAME_VIEW_WIDTH (MAP_ROOM_WIDTH * TILE_DIMENSION) // 16 * 32 +#define GAME_VIEW_HEIGHT (MAP_ROOM_HEIGHT * TILE_DIMENSION) // 12 * 32 #define SKILL_BAR_WIDTH GAME_VIEW_WIDTH #define SKILL_BAR_HEIGHT 32 -#define RIGHT_GUI_WIDTH (10 * SPRITE_DIMENSION) -#define RIGHT_GUI_HEIGHT (GAME_VIEW_HEIGHT + SKILL_BAR_HEIGHT) +#define RIGHT_GUI_WIDTH (10 * SPRITE_DIMENSION) // 10 * 16 +#define MINIMAP_GUI_HEIGHT 128 +#define STATS_GUI_HEIGHT (GAME_VIEW_HEIGHT + SKILL_BAR_HEIGHT - MINIMAP_GUI_HEIGHT) #define BOTTOM_GUI_HEIGHT (10 * SPRITE_DIMENSION) #define BOTTOM_GUI_WIDTH (GAME_VIEW_WIDTH + RIGHT_GUI_WIDTH) #define SCREEN_WIDTH (GAME_VIEW_WIDTH + RIGHT_GUI_WIDTH) -#define SCREEN_HEIGHT (RIGHT_GUI_HEIGHT + BOTTOM_GUI_HEIGHT) +#define SCREEN_HEIGHT (GAME_VIEW_HEIGHT + SKILL_BAR_HEIGHT + BOTTOM_GUI_HEIGHT) /* Quality of life stuff */ #define DEFAULT_DIMENSION (Dimension) { 16, 16 } @@ -65,7 +66,7 @@ #define C_WHITE (SDL_Color) { 255, 255, 255, 255 } #define C_RED (SDL_Color) { 255, 0, 0, 255 } #define C_GREEN (SDL_Color) { 0, 255, 0, 255 } -#define C_BLUE (SDL_Color) { 0, 0, 255, 255 } +#define C_BLUE (SDL_Color) { 60, 134, 252, 255 } #define C_YELLOW (SDL_Color) { 255, 255, 0, 255 } #define C_BLACK (SDL_Color) { 0, 0, 0, 255 } #define C_PURPLE (SDL_Color) { 137, 16, 229, 255 } diff --git a/src/dimension.h b/src/dimension.h index ddbc1b0..7b1b8d1 100644 --- a/src/dimension.h +++ b/src/dimension.h @@ -19,6 +19,8 @@ #ifndef DIMENSION_H_ #define DIMENSION_H_ +#define DIM(x, y) (Dimension) { x, y } + typedef struct { unsigned int width; unsigned int height; diff --git a/src/gamestate.h b/src/gamestate.h index 5e8a52f..ae564aa 100644 --- a/src/gamestate.h +++ b/src/gamestate.h @@ -26,6 +26,7 @@ typedef enum GameState_t { PLAYING, IN_GAME_MENU, GAME_OVER, + COMPLETED, QUIT } GameState; diff --git a/src/gui.c b/src/gui.c index 63b15cf..b70ec5e 100644 --- a/src/gui.c +++ b/src/gui.c @@ -26,6 +26,8 @@ #include "util.h" #include "map.h" #include "texturecache.h" +#include "gui_util.h" +#include "tooltip.h" #define DEFAULT_LOG { NULL, LOG_LINES_COUNT, 0, 200 } #define DEFAULT_EVENT_MESSAGES { NULL, 5, 0, 200 } @@ -33,16 +35,6 @@ #define POS_Y_COLLECTABLES 64 #define POS_Y_XPBAR 128 -static SDL_Rect frame_top_left = { 16, 160, 16, 16 }; -static SDL_Rect frame_top_right = { 48, 160, 16, 16 }; -static SDL_Rect frame_bottom_left = { 16, 192, 16, 16 }; -static SDL_Rect frame_bottom_right = { 48, 192, 16, 16 }; -static SDL_Rect frame_top = { 32, 160, 16, 16 }; -static SDL_Rect frame_bottom = { 32, 192, 16, 16 }; -static SDL_Rect frame_center = { 32, 176, 16, 16 }; -static SDL_Rect frame_left = { 16, 176, 16, 16 }; -static SDL_Rect frame_right = { 48, 176, 16, 16 }; - static struct LogData_t { char **log; unsigned int len; @@ -50,16 +42,13 @@ static struct LogData_t { unsigned int strlen; } log_data = DEFAULT_LOG; -static struct GuiEventMsgData_t { +static struct GuiEventMsgs { char **messages; unsigned int len; unsigned int count; unsigned int strlen; } event_messages = DEFAULT_EVENT_MESSAGES; -static Sprite* -gui_create_frame(unsigned int width, unsigned int height, Camera *cam); - static void gui_malloc_log(void) { @@ -174,12 +163,16 @@ init_sprites(Gui *gui, Camera *cam) s->pos = (Position) { 16, POS_Y_COLLECTABLES + 32 }; linkedlist_append(&gui->sprites, s); - gui->rightFrame = gui_create_frame(RIGHT_GUI_WIDTH/16, - RIGHT_GUI_HEIGHT/16, - cam); - gui->bottomFrame = gui_create_frame(BOTTOM_GUI_WIDTH/16, - BOTTOM_GUI_HEIGHT/16, - cam); + gui->statsFrame = gui_util_create_frame_sprite(RIGHT_GUI_WIDTH/16, + STATS_GUI_HEIGHT/16, + cam); + gui->bottomFrame = gui_util_create_frame_sprite(BOTTOM_GUI_WIDTH/16, + BOTTOM_GUI_HEIGHT/16, + cam); + + gui->miniMapFrame = gui_util_create_frame_sprite(RIGHT_GUI_WIDTH/16, + MINIMAP_GUI_HEIGHT/16, + cam); } Gui* @@ -192,6 +185,7 @@ gui_create(Camera *cam) gui->sprites = linkedlist_create(); gui->health = linkedlist_create(); gui->xp_bar = linkedlist_create(); + gui->activeTooltip = NULL; for (i = 0; i < LOG_LINES_COUNT; ++i) { t = texture_create(); @@ -400,91 +394,10 @@ gui_update_player_stats(Gui *gui, Player *player, Map *map, SDL_Renderer *render } } -static Sprite* -gui_create_frame(unsigned int width, unsigned int height, Camera *cam) -{ - Sprite *frame = sprite_create(); - Texture *texture = texture_create(); - texture->dim = (Dimension) { - width * 16, - height * 16 - }; - frame->textures[0] = texture; - frame->destroyTextures = true; - frame->pos = (Position) { 0, 0 }; - frame->dim = (Dimension) { width*16, height*16 }; - frame->fixed = true; - texture_create_blank(texture, - SDL_TEXTUREACCESS_TARGET, - cam->renderer); - Texture *source = texturecache_get("GUI/GUI0.png"); - - SDL_SetRenderTarget(cam->renderer, texture->texture); - SDL_RenderClear(cam->renderer); - - SDL_Rect box = { 0, 0, 16, 16 }; - unsigned int i, j; - for (i = 0; i < width; ++i) { - for (j = 0; j < height; ++j) { - box.x = i * 16; - box.y = j * 16; - - if (i == 0 && j == 0) { - texture_render_clip(source, - &box, - &frame_top_left, - cam); - } else if (i == (width - 1) && j == 0) { - texture_render_clip(source, - &box, - &frame_top_right, - cam); - } else if (i == 0 && j == (height - 1)) { - texture_render_clip(source, - &box, - &frame_bottom_left, - cam); - } else if (i == (width - 1) && j == (height - 1)) { - texture_render_clip(source, - &box, - &frame_bottom_right, - cam); - } else if (i == 0) { - texture_render_clip(source, - &box, - &frame_left, - cam); - } else if (i == (width - 1)) { - texture_render_clip(source, - &box, - &frame_right, - cam); - } else if (j == 0) { - texture_render_clip(source, - &box, - &frame_top, - cam); - } else if (j == (height - 1)) { - texture_render_clip(source, - &box, - &frame_bottom, - cam); - } else { - texture_render_clip(source, - &box, - &frame_center, - cam); - } - } - } - SDL_SetRenderTarget(cam->renderer, NULL); - return frame; -} - void gui_render_panel(Gui *gui, Camera *cam) { - sprite_render(gui->rightFrame, cam); + sprite_render(gui->statsFrame, cam); LinkedList *item = gui->health; while (item != NULL) { Sprite *s = item->data; @@ -508,6 +421,30 @@ gui_render_panel(Gui *gui, Camera *cam) sprite_render(gui->labels[i], cam); } +void +gui_render_minimap(Gui *gui, Map *map, Camera *cam) +{ + sprite_render(gui->miniMapFrame, cam); + + SDL_Rect box = { 0, 0, 12, 8 }; + for (Uint8 i = 0; i < MAP_H_ROOM_COUNT; ++i) { + for (Uint8 j = 0; j < MAP_V_ROOM_COUNT; ++j) { + Room *room = map->rooms[i][j]; + box.x = i*14 + 10; + box.y = j*10 + 14; + if (room && room->visited) { + if (map->currentRoom.x == i && map->currentRoom.y == j) + SDL_SetRenderDrawColor(cam->renderer, 0, 255, 255, 255); + else + SDL_SetRenderDrawColor(cam->renderer, 255, 255, 255, 255); + SDL_RenderFillRect(cam->renderer, &box); + SDL_SetRenderDrawColor(cam->renderer, 60, 134, 252, 255); + SDL_RenderDrawRect(cam->renderer, &box); + } + } + } +} + void gui_log(const char *fmt, ...) { @@ -557,6 +494,14 @@ gui_event_message(const char *fmt, ...) event_messages.count++; } +void +gui_render_tooltip(Gui *gui, Camera *cam) +{ + if (gui->activeTooltip) { + sprite_render(gui->activeTooltip, cam); + } +} + void gui_render_log(Gui *gui, Camera *cam) { @@ -613,8 +558,9 @@ gui_render_event_message(Gui *gui, Camera *cam) void gui_clear_message_log(void) { - for (size_t i = 0; i < event_messages.count; ++i) + for (size_t i = 0; i < event_messages.count; ++i) { free(event_messages.messages[i]); + } event_messages.count = 0; for (size_t i = 0; i < log_data.count; ++i) @@ -642,8 +588,9 @@ destroy_event_messages(void) if (event_messages.messages == NULL) return; - for (unsigned int i = 0; i < event_messages.count; ++i) + for (unsigned int i = 0; i < event_messages.count; ++i) { free(event_messages.messages[i]); + } free(event_messages.messages); event_messages.messages = NULL; @@ -659,7 +606,8 @@ gui_destroy(Gui *gui) texture_destroy(gui->event_message); sprite_destroy(gui->bottomFrame); - sprite_destroy(gui->rightFrame); + sprite_destroy(gui->statsFrame); + sprite_destroy(gui->miniMapFrame); while (gui->sprites != NULL) sprite_destroy(linkedlist_pop(&gui->sprites)); diff --git a/src/gui.h b/src/gui.h index 91d5791..f5a0d09 100644 --- a/src/gui.h +++ b/src/gui.h @@ -42,13 +42,15 @@ typedef enum Label_e { LABEL_COUNT } LabelIndex; -typedef struct { +typedef struct Gui { LinkedList *sprites; LinkedList *health; LinkedList *xp_bar; Sprite *bottomFrame; - Sprite *rightFrame; + Sprite *statsFrame; + Sprite *miniMapFrame; Sprite *labels[LABEL_COUNT]; + Sprite *activeTooltip; Texture *log_lines[LOG_LINES_COUNT]; Texture *event_message; Timer *event_message_timer; @@ -63,12 +65,18 @@ gui_update_player_stats(Gui*, Player*, Map*, SDL_Renderer*); void gui_render_panel(Gui*, Camera*); +void +gui_render_minimap(Gui*, Map*, Camera*); + void gui_render_log(Gui*, Camera*); void gui_render_event_message(Gui*, Camera*); +void +gui_render_tooltip(Gui*, Camera*); + void gui_log(const char *fmt, ...); diff --git a/src/gui_util.c b/src/gui_util.c new file mode 100644 index 0000000..5908341 --- /dev/null +++ b/src/gui_util.c @@ -0,0 +1,132 @@ +/* + * BreakHack - A dungeone crawler RPG + * Copyright (C) 2018 Linus Probert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "texturecache.h" +#include "gui_util.h" + +static Sprite * +render_frame_on_texture(Uint32 width, + Uint32 height, + Position offset, + Camera *cam) +{ + Sprite *frame = sprite_create(); + Texture *texture = texture_create(); + texture->dim = (Dimension) { + width * 16, + height * 16 + }; + frame->textures[0] = texture; + frame->destroyTextures = true; + frame->pos = (Position) { 0, 0 }; + frame->dim = (Dimension) { width*16, height*16 }; + frame->fixed = true; + texture_create_blank(texture, + SDL_TEXTUREACCESS_TARGET, + cam->renderer); + + SDL_SetRenderTarget(cam->renderer, texture->texture); + SDL_RenderClear(cam->renderer); + + SDL_Rect frame_top_left = CLIP16(offset.x, offset.y); + SDL_Rect frame_top_right = CLIP16(offset.x + 32, offset.y); + SDL_Rect frame_bottom_left = CLIP16(offset.x, offset.y + 32); + SDL_Rect frame_bottom_right = CLIP16(offset.x + 32, offset.y + 32); + SDL_Rect frame_top = CLIP16(offset.x + 16, offset.y); + SDL_Rect frame_bottom = CLIP16(offset.x + 16, offset.y + 32); + SDL_Rect frame_center = CLIP16(offset.x + 16, offset.y + 16); + SDL_Rect frame_left = CLIP16(offset.x, offset.y + 16); + SDL_Rect frame_right = CLIP16(offset.x + 32, offset.y + 16); + + Texture *source = texturecache_get("GUI/GUI0.png"); + SDL_Rect box = { 0, 0, 16, 16 }; + unsigned int i, j; + for (i = 0; i < width; ++i) { + for (j = 0; j < height; ++j) { + box.x = i * 16; + box.y = j * 16; + + if (i == 0 && j == 0) { + texture_render_clip(source, + &box, + &frame_top_left, + cam); + } else if (i == (width - 1) && j == 0) { + texture_render_clip(source, + &box, + &frame_top_right, + cam); + } else if (i == 0 && j == (height - 1)) { + texture_render_clip(source, + &box, + &frame_bottom_left, + cam); + } else if (i == (width - 1) && j == (height - 1)) { + texture_render_clip(source, + &box, + &frame_bottom_right, + cam); + } else if (i == 0) { + texture_render_clip(source, + &box, + &frame_left, + cam); + } else if (i == (width - 1)) { + texture_render_clip(source, + &box, + &frame_right, + cam); + } else if (j == 0) { + texture_render_clip(source, + &box, + &frame_top, + cam); + } else if (j == (height - 1)) { + texture_render_clip(source, + &box, + &frame_bottom, + cam); + } else { + texture_render_clip(source, + &box, + &frame_center, + cam); + } + } + } + + SDL_SetRenderTarget(cam->renderer, NULL); + + return frame; +} + +Sprite * +gui_util_create_frame_sprite(Uint32 width, + Uint32 height, + Camera *cam) +{ + return render_frame_on_texture(width, height, POS(16, 16*10), cam); +} + +Sprite * +gui_util_create_tooltip_frame_sprite(Uint32 width, + Uint32 height, + Camera *cam) +{ + return render_frame_on_texture(width, height, POS(16*13, 16*13), cam); +} diff --git a/src/gui_util.h b/src/gui_util.h new file mode 100644 index 0000000..6698fef --- /dev/null +++ b/src/gui_util.h @@ -0,0 +1,32 @@ +/* + * BreakHack - A dungeone crawler RPG + * Copyright (C) 2018 Linus Probert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "sprite.h" +#include "camera.h" + +Sprite * +gui_util_create_frame_sprite(Uint32 width, + Uint32 height, + Camera*); + +Sprite * +gui_util_create_tooltip_frame_sprite(Uint32 width, + Uint32 height, + Camera*); diff --git a/src/input.c b/src/input.c index a53db62..cb8182d 100644 --- a/src/input.c +++ b/src/input.c @@ -107,6 +107,21 @@ get_event_modkey(SDL_Event *event) key = KEY_CTRL_S; break; case SDLK_m: key = KEY_CTRL_M; break; + case SDLK_d: + key = KEY_CTRL_D; break; + } + } else if (event->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) { + switch (event->key.keysym.sym) { + case SDLK_1: + key = KEY_SHIFT_NUM1; break; + case SDLK_2: + key = KEY_SHIFT_NUM2; break; + case SDLK_3: + key = KEY_SHIFT_NUM3; break; + case SDLK_4: + key = KEY_SHIFT_NUM4; break; + case SDLK_5: + key = KEY_SHIFT_NUM5; break; default: key = 0; break; } diff --git a/src/input.h b/src/input.h index c2a39fb..78e3f1b 100644 --- a/src/input.h +++ b/src/input.h @@ -39,8 +39,14 @@ #define KEY_ESC 16384 #define KEY_ENTER 32768 -#define KEY_CTRL_M 1 -#define KEY_CTRL_S 2 +#define KEY_CTRL_M 0x1 +#define KEY_CTRL_S 0x2 +#define KEY_CTRL_D 0x4 +#define KEY_SHIFT_NUM1 0x8 +#define KEY_SHIFT_NUM2 0x10 +#define KEY_SHIFT_NUM3 0x20 +#define KEY_SHIFT_NUM4 0x40 +#define KEY_SHIFT_NUM5 0x80 #define MBUTTON_LEFT 1 #define MBUTTON_MIDDLE 2 diff --git a/src/main.c b/src/main.c index 39876fc..a6264db 100644 --- a/src/main.c +++ b/src/main.c @@ -52,38 +52,116 @@ #include "screen.h" #include "hiscore.h" #include "io_util.h" +#include "tooltip.h" + +static char *artifacts_tooltip[] = { + "CONGRATULATIONS!", + "", + " You just picked up your first artifact!", + "", + " Your current artifacts and corresponding level are", + " listed next to your skills." + "", + "", + " Artifacts have mystical effects that improve your offensive", + " or defensive advantage in the dungeon. However it is sometimes", + " hard to know what effect an artifact has.", + "", + "", + " Perhaps an experienced dungeoner will know more?", + "", + "", + "Press ESC to close", + NULL +}; + +static char *skills_tooltip[] = { + "CONGRATULATIONS!", + "", + " You have aquired a new level and a new skill!", + "", + " Skills are listed in the bar below the game screen.", + "", + "", + " SKILL INFO: SHIFT + ", + " Where is the skill number (1-5)", + "", + " DISABLE TOOLTIPS: CTRL + D", + "", + "", + "Press ESC to close", + NULL +}; + +static char *how_to_play_tooltip[] = { + "HOW TO PLAY", + "", + " NAVIGATION: Use ARROWS or WASD or HJKL to move", + "", + " ATTACK: Walk into a monster to attack it", + "", + " THROW DAGGER: Press 4 then chose a direction (navigation keys)", + "", + " DRINK HEALTH: Press 5 (if you need health and have potions)", + "", + " TOGGLE MUSIC: CTRL + M", + "", + " TOGGLE SOUND: CTRL + S", + "", + " TOGGLE MENU: ESC", + "", + " Your stats and inventory are listed in the right panel", + "", + "", + " GOOD LUCK!", + " May your death be quick and painless...", + "", + "", + "", + "Press ESC to close", + NULL +}; + typedef enum Turn_t { PLAYER, MONSTER } Turn; -static SDL_Window *gWindow = NULL; -static SDL_Renderer *gRenderer = NULL; -static Player *gPlayer = NULL; -static Map *gMap = NULL; -static RoomMatrix *gRoomMatrix = NULL; -static Gui *gGui = NULL; -static SkillBar *gSkillBar = NULL; -static Pointer *gPointer = NULL; -static Menu *mainMenu = NULL; -static Menu *inGameMenu = NULL; -static Timer *menuTimer = NULL; -static Camera *gCamera = NULL; -static Screen *creditsScreen = NULL; -static Screen *scoreScreen = NULL; -static unsigned int cLevel = 1; -static float deltaTime = 1.0; -static double renderScale = 1.0; +static SDL_Window *gWindow = NULL; +static SDL_Renderer *gRenderer = NULL; +static Player *gPlayer = NULL; +static Map *gMap = NULL; +static RoomMatrix *gRoomMatrix = NULL; +static Gui *gGui = NULL; +static SkillBar *gSkillBar = NULL; +static Menu *mainMenu = NULL; +static Menu *inGameMenu = NULL; +static Timer *menuTimer = NULL; +static Camera *gCamera = NULL; +static Screen *creditsScreen = NULL; +static Screen *scoreScreen = NULL; +static Sprite *new_skill_tooltip = NULL; +static Sprite *howto_tooltip = NULL; +static Sprite *new_artifact_tooltip = NULL; +static unsigned int cLevel = 1; +static float deltaTime = 1.0; +static double renderScale = 1.0; +static Turn currentTurn = PLAYER; static GameState gGameState; static SDL_Rect gameViewport; static SDL_Rect skillBarViewport; static SDL_Rect bottomGuiViewport; -static SDL_Rect rightGuiViewport; +static SDL_Rect statsGuiViewport; +static SDL_Rect minimapViewport; static SDL_Rect menuViewport; -static Turn currentTurn = PLAYER; static Input input; +#ifdef DEBUG +static Sprite *fpsSprite = NULL; +static Pointer *gPointer = NULL; +#endif // DEBUG + static SDL_Color C_MENU_DEFAULT = { 255, 255, 0, 255 }; static SDL_Color C_MENU_OUTLINE_DEFAULT = { 0, 0, 0, 255 }; static SDL_Color C_MENU_HOVER = { 255, 0, 0, 255 }; @@ -180,8 +258,11 @@ initViewports(void) bottomGuiViewport = (SDL_Rect) { 0, GAME_VIEW_HEIGHT + SKILL_BAR_HEIGHT, BOTTOM_GUI_WIDTH, BOTTOM_GUI_WIDTH }; - rightGuiViewport = (SDL_Rect) { GAME_VIEW_WIDTH, 0, - RIGHT_GUI_WIDTH, RIGHT_GUI_HEIGHT }; + statsGuiViewport = (SDL_Rect) { GAME_VIEW_WIDTH, 0, + RIGHT_GUI_WIDTH, STATS_GUI_HEIGHT }; + + minimapViewport = (SDL_Rect) { GAME_VIEW_WIDTH, STATS_GUI_HEIGHT, + RIGHT_GUI_WIDTH, MINIMAP_GUI_HEIGHT }; menuViewport = (SDL_Rect) { (SCREEN_WIDTH - GAME_VIEW_WIDTH)/2, @@ -200,13 +281,22 @@ initGame(void) gCamera = camera_create(gRenderer); gRoomMatrix = roommatrix_create(); gGui = gui_create(gCamera); - gSkillBar = skillbar_create(gRenderer); + gSkillBar = skillbar_create(gCamera); item_builder_init(gRenderer); +#ifdef DEBUG gPointer = pointer_create(gRenderer); +#endif // DEBUG particle_engine_init(); menuTimer = timer_create(); actiontextbuilder_init(gRenderer); +#ifdef DEBUG + fpsSprite = sprite_create(); + sprite_load_text_texture(fpsSprite, "GUI/SDS_8x8.ttf", 0, 14, 1); + fpsSprite->pos = POS(16, 16); + fpsSprite->fixed = true; +#endif // DEBUG + return true; } @@ -218,20 +308,18 @@ startGame(void *unused) gGameState = PLAYING; if (gPlayer) player_destroy(gPlayer); - gPlayer = player_create(WARRIOR, gRenderer); + gPlayer = player_create(WARRIOR, gCamera); mixer_play_music(GAME_MUSIC0 + get_random(2)); -#ifdef DEBUG - // This block is for testing - cLevel = 1; - if (cLevel % 5 == 0) - mixer_play_music(BOSS_MUSIC0); - for (size_t i = 1; i < cLevel; ++i) - player_levelup(gPlayer); -#endif // DEBUG resetGame(); + skillbar_reset(gSkillBar); gui_clear_message_log(); gui_log("The Dungeon Crawl begins!"); gui_event_message("Welcome to the dungeon!"); + + Settings *settings = settings_get(); + if (!settings->howto_tooltip_shown) + gGui->activeTooltip = howto_tooltip; + settings->howto_tooltip_shown = true; } static void @@ -245,10 +333,14 @@ static void toggleInGameMenu(void *unused) { UNUSED(unused); - if (gGameState == PLAYING || gGameState == GAME_OVER) + if (gGameState == PLAYING || + gGameState == GAME_OVER || + gGameState == COMPLETED) gGameState = IN_GAME_MENU; else if (is_player_dead()) gGameState = GAME_OVER; + else if (cLevel >= 20) + gGameState = COMPLETED; else gGameState = PLAYING; } @@ -257,6 +349,7 @@ static void goToMainMenu(void *unused) { UNUSED(unused); + gui_clear_message_log(); gGameState = MENU; menu_destroy(inGameMenu); inGameMenu = NULL; @@ -299,16 +392,25 @@ createMenu(Menu **menu, struct MENU_ITEM menu_items[], unsigned int size) } } +static void +showHowToTooltip(void *unused) +{ + UNUSED(unused); + toggleInGameMenu(NULL); + gGui->activeTooltip = howto_tooltip; +} + static void initInGameMenu(void) { struct MENU_ITEM menu_items[] = { { "RESUME", toggleInGameMenu }, + { "HOW TO PLAY", showHowToTooltip }, { "MAIN MENU", goToMainMenu }, { "QUIT", exitGame }, }; - createMenu(&inGameMenu, menu_items, 3); + createMenu(&inGameMenu, menu_items, 4); } static void @@ -388,8 +490,10 @@ resetGame(void) screen_destroy(scoreScreen); scoreScreen = NULL; - if (!inGameMenu) - initInGameMenu(); + if (inGameMenu) + menu_destroy(inGameMenu); + inGameMenu = NULL; + initInGameMenu(); if (gMap) map_destroy(gMap); @@ -420,6 +524,10 @@ init(void) hiscore_init(); initMainMenu(); + howto_tooltip = tooltip_create(how_to_play_tooltip, gCamera); + new_skill_tooltip = tooltip_create(skills_tooltip, gCamera); + new_artifact_tooltip = tooltip_create(artifacts_tooltip, gCamera); + gCamera->pos = (Position) { 0, 0 }; gGameState = MENU; @@ -432,9 +540,10 @@ handle_main_input(void) { if (gGameState == PLAYING || gGameState == IN_GAME_MENU - || gGameState == GAME_OVER) + || gGameState == GAME_OVER + || gGameState == COMPLETED) { - if (input_key_is_pressed(&input, KEY_ESC)) + if (!gGui->activeTooltip && input_key_is_pressed(&input, KEY_ESC)) toggleInGameMenu(NULL); } @@ -444,6 +553,8 @@ handle_main_input(void) gGameState = MENU; else if (gGameState == MENU && input_key_is_pressed(&input, KEY_ESC)) gGameState = QUIT; + else if (gGui->activeTooltip && input_key_is_pressed(&input, KEY_ESC)) + gGui->activeTooltip = NULL; if (input_modkey_is_pressed(&input, KEY_CTRL_M)) { if (mixer_toggle_music(&gGameState)) @@ -458,6 +569,15 @@ handle_main_input(void) else gui_log("Sound disabled"); } + + if (input_modkey_is_pressed(&input, KEY_CTRL_D)) { + Settings *s = settings_get(); + s->tooltips_enabled = !s->tooltips_enabled; + if (s->tooltips_enabled) + gui_log("Tooltips enabled"); + else + gui_log("Tooltips disabled"); + } } static bool @@ -491,15 +611,33 @@ handle_events(void) static bool is_player_dead(void) { +#ifdef DEBUG + gPlayer->stats.hp = gPlayer->stats.hp > 0 ? gPlayer->stats.hp : 1; +#endif // DEBUG if (gPlayer->stats.hp <= 0) { return true; } return false; } +static void +end_game_details(void) +{ + gui_log("You earned %.2f gold", gPlayer->gold); + gui_event_message("You earned %.2f gold", gPlayer->gold); + if (hiscore_get_top_gold() < gPlayer->gold) { + gui_event_message("NEW HIGHSCORE"); + gui_log("NEW HIGHSCORE"); + } +} + static void check_next_level(void) { + if (cLevel >= 20) { + return; + } + Room *room = gMap->rooms[gMap->currentRoom.x][gMap->currentRoom.y]; Position pos = position_to_matrix_coords(&gPlayer->sprite->pos); @@ -511,11 +649,18 @@ check_next_level(void) if (tile->levelExit) { mixer_play_effect(NEXT_LEVEL); ++cLevel; - if (cLevel % 5 == 0) + if (cLevel > 19) { mixer_play_music(BOSS_MUSIC0); - else + } else if (cLevel % 5 == 0) { + gui_log("You sense something powerful in the vicinity"); + mixer_play_music(BOSS_MUSIC0); + } else { mixer_play_music(GAME_MUSIC0 + get_random(2)); - resetGame(); + } + + if (cLevel < 20) { + resetGame(); + } } } @@ -526,7 +671,9 @@ populateUpdateData(UpdateData *data, float deltatime) data->map = gMap; data->matrix = gRoomMatrix; data->input = &input; + data->gui = gGui; data->deltatime = deltatime; + data->cam = gCamera; } static void @@ -534,17 +681,28 @@ run_game_update(void) { static UpdateData updateData; static unsigned int playerLevel = 1; + static bool artifactTooltipShown = false; if (gGameState == IN_GAME_MENU) menu_update(inGameMenu, &input); populateUpdateData(&updateData, deltaTime); + bool skillActivated = false; if (playerLevel != gPlayer->stats.lvl) { playerLevel = gPlayer->stats.lvl; - skillbar_check_skill_activation(gSkillBar, gPlayer); + skillActivated = skillbar_check_skill_activation(gSkillBar, + gPlayer); + } + + Settings *settings = settings_get(); + if (skillActivated && settings->tooltips_enabled && playerLevel < 5) { + gGui->activeTooltip = new_skill_tooltip; + } + if (!artifactTooltipShown && gPlayer->equipment.hasArtifacts && settings->tooltips_enabled) { + artifactTooltipShown = true; + gGui->activeTooltip = new_artifact_tooltip; } - map_clear_expired_entities(gMap, gPlayer); if (gGameState == PLAYING && currentTurn == PLAYER) player_update(&updateData); @@ -558,19 +716,85 @@ run_game_update(void) map_set_current_room(gMap, &gPlayer->sprite->pos); map_update(&updateData); + bool turnSwitch = false; if (currentTurn == PLAYER) { if (player_turn_over(gPlayer)) { currentTurn = MONSTER; player_reset_steps(gPlayer); map_on_new_turn(gMap); - repopulate_roommatrix(); + turnSwitch = true; } } else if (currentTurn == MONSTER) { if (map_move_monsters(gMap, gRoomMatrix)) { currentTurn = PLAYER; - repopulate_roommatrix(); + turnSwitch = true; } } + + map_clear_expired_entities(gMap, gPlayer); + if (turnSwitch) + repopulate_roommatrix(); +} + +static void +render_gui(void) +{ + SDL_RenderSetViewport(gRenderer, &statsGuiViewport); + gui_render_panel(gGui, gCamera); + SDL_RenderSetViewport(gRenderer, &minimapViewport); + gui_render_minimap(gGui, gMap, gCamera); + SDL_RenderSetViewport(gRenderer, &skillBarViewport); + skillbar_render(gSkillBar, gPlayer, gCamera); + SDL_RenderSetViewport(gRenderer, &bottomGuiViewport); + gui_render_log(gGui, gCamera); + SDL_RenderSetViewport(gRenderer, NULL); +} + +static void +render_game_completed(void) +{ + SDL_RenderSetViewport(gRenderer, &gameViewport); + if (!is_player_dead()) { + player_render(gPlayer, gCamera); + player_render_toplayer(gPlayer, gCamera); + } + actiontextbuilder_render(gCamera); + gui_render_event_message(gGui, gCamera); + + if (gGameState == IN_GAME_MENU) { + SDL_Rect dimmer = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT }; + SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 150); + SDL_RenderFillRect(gRenderer, &dimmer); + menu_render(inGameMenu, gCamera); + } +#ifdef DEBUG + sprite_render(fpsSprite, gCamera); + pointer_render(gPointer, gCamera); +#endif // DEBUG +} + +static void +render_game(void) +{ + SDL_RenderSetViewport(gRenderer, &gameViewport); + map_render(gMap, gCamera); + particle_engine_render_game(gCamera); + + map_render_mid_layer(gMap, gCamera); + + if (!is_player_dead()) { + player_render(gPlayer, gCamera); + player_render_toplayer(gPlayer, gCamera); + } + + map_render_top_layer(gMap, gRoomMatrix, gCamera); + + if (gPlayer->class == MAGE || gPlayer->class == PALADIN) + roommatrix_render_mouse_square(gRoomMatrix, gCamera); + + roommatrix_render_lightmap(gRoomMatrix, gCamera); + actiontextbuilder_render(gCamera); + gui_render_event_message(gGui, gCamera); } static void @@ -579,36 +803,11 @@ run_game_render(void) SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0); SDL_RenderClear(gRenderer); - SDL_RenderSetViewport(gRenderer, &gameViewport); - map_render(gMap, gCamera); - particle_engine_render_game(gCamera); - map_render_mid_layer(gMap, gCamera); + render_game(); + render_gui(); - if (!is_player_dead()) { - player_render(gPlayer, gCamera); - player_render_toplayer(gPlayer, gCamera); - } - - map_render_top_layer(gMap, gCamera); - - if (gPlayer->class == MAGE || gPlayer->class == PALADIN) - roommatrix_render_mouse_square(gRoomMatrix, gCamera); - - roommatrix_render_lightmap(gRoomMatrix, gCamera); - actiontextbuilder_render(gCamera); - gui_render_event_message(gGui, gCamera); - - SDL_RenderSetViewport(gRenderer, &rightGuiViewport); - gui_render_panel(gGui, gCamera); - - SDL_RenderSetViewport(gRenderer, &skillBarViewport); - skillbar_render(gSkillBar, gPlayer, gCamera); - - SDL_RenderSetViewport(gRenderer, &bottomGuiViewport); - gui_render_log(gGui, gCamera); - - SDL_RenderSetViewport(gRenderer, NULL); particle_engine_render_global(gCamera); + gui_render_tooltip(gGui, gCamera); if (gGameState == IN_GAME_MENU) { SDL_Rect dimmer = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT }; @@ -616,7 +815,10 @@ run_game_render(void) SDL_RenderFillRect(gRenderer, &dimmer); menu_render(inGameMenu, gCamera); } +#ifdef DEBUG + sprite_render(fpsSprite, gCamera); pointer_render(gPointer, gCamera); +#endif // DEBUG SDL_RenderPresent(gRenderer); } @@ -626,19 +828,21 @@ run_game(void) { run_game_update(); - run_game_render(); + if (cLevel >= 20) { + SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 255); + SDL_RenderClear(gRenderer); + render_game_completed(); + render_gui(); + SDL_RenderPresent(gRenderer); + } else { + run_game_render(); + } if (gGameState == PLAYING && is_player_dead()) { camera_shake(VECTOR2D_RIGHT, 800); gui_log("The dungeon consumed you"); - gui_log("You earned %.2f gold", gPlayer->gold); gui_event_message("You died!"); - gui_event_message("You earned %.2f gold", gPlayer->gold); - if (hiscore_get_top_gold() < gPlayer->gold) { - gui_event_message("NEW HIGHSCORE"); - gui_log("NEW HIGHSCORE"); - } - gui_event_message("Press ESC to open menu"); + end_game_details(); mixer_play_effect(SPLAT); gGameState = GAME_OVER; createInGameGameOverMenu(); @@ -647,6 +851,15 @@ run_game(void) } else { check_next_level(); } + + if (gGameState == PLAYING && cLevel >= 20) { + gGameState = COMPLETED; + createInGameGameOverMenu(); + gui_event_message("Your break is over!"); + gui_log("Your break is over!"); + gui_event_message("Well done!"); + end_game_details(); + } } static void @@ -672,7 +885,7 @@ run_menu(void) SDL_RenderSetViewport(gRenderer, &menuViewport); map_render(gMap, gCamera); map_render_mid_layer(gMap, gCamera); - map_render_top_layer(gMap, gCamera); + map_render_top_layer(gMap, gRoomMatrix, gCamera); roommatrix_render_lightmap(gRoomMatrix, gCamera); SDL_RenderSetViewport(gRenderer, NULL); @@ -684,19 +897,31 @@ run_menu(void) else if (gGameState == SCORE_SCREEN) screen_render(scoreScreen, gCamera); +#ifdef DEBUG + sprite_render(fpsSprite, gCamera); pointer_render(gPointer, gCamera); +#endif // DEBUG SDL_RenderPresent(gRenderer); } -static -void run(void) +static void +run(void) { static int oldTime = 0; static int currentTime = 0; bool quit = false; - Timer* fpsTimer = timer_create(); + +#ifdef DEBUG + Uint32 frame = 0; + Timer *fpsTime = timer_create(); + Timer *updateTimer = timer_create(); + timer_start(fpsTime); + timer_start(updateTimer); +#endif // DEBUG + + Timer *fpsTimer = timer_create(); while (!quit) { @@ -704,12 +929,15 @@ void run(void) quit = handle_events(); handle_main_input(); +#ifdef DEBUG pointer_handle_input(gPointer, &input); +#endif // DEBUG switch (gGameState) { case PLAYING: case IN_GAME_MENU: case GAME_OVER: + case COMPLETED: run_game(); break; case MENU: @@ -736,9 +964,23 @@ void run(void) currentTime = SDL_GetTicks(); deltaTime = (float) ((currentTime - oldTime) / 1000.0); } +#ifdef DEBUG + frame++; + if (timer_get_ticks(updateTimer) > 1000) { + char buffer[20]; + m_sprintf(buffer, 20, "FPS: %u", frame / (timer_get_ticks(fpsTime) / 1000)); + texture_load_from_text(fpsSprite->textures[0], buffer, C_RED, C_WHITE, gRenderer); + fpsSprite->dim = fpsSprite->textures[0]->dim; + timer_start(updateTimer); + } +#endif // DEBUG } timer_destroy(fpsTimer); +#ifdef DEBUG + timer_destroy(fpsTime); + timer_destroy(updateTimer); +#endif // DEBUG } static @@ -758,11 +1000,16 @@ void close(void) if (inGameMenu) menu_destroy(inGameMenu); + sprite_destroy(howto_tooltip); + sprite_destroy(new_skill_tooltip); camera_destroy(gCamera); roommatrix_destroy(gRoomMatrix); gui_destroy(gGui); skillbar_destroy(gSkillBar); +#ifdef DEBUG pointer_destroy(gPointer); + sprite_destroy(fpsSprite); +#endif // DEBUG actiontextbuilder_close(); item_builder_close(); particle_engine_close(); diff --git a/src/map.c b/src/map.c index e19044c..04b45e2 100644 --- a/src/map.c +++ b/src/map.c @@ -27,8 +27,8 @@ #include "update_data.h" #include "trap.h" -static -Room* create_room(void) +static Room* +create_room(void) { int i, j; Room *room; @@ -40,6 +40,7 @@ Room* create_room(void) room->tiles[i][j] = NULL; room->decorations[i][j] = NULL; room->traps[i][j] = NULL; + room->visited = false; } } return room; @@ -205,14 +206,18 @@ map_move_monsters(Map *map, RoomMatrix *rm) if (monster->state.current == PASSIVE && position_proximity(1, &rm->playerRoomPos, &pos)) continue; + if (monster->steps >= monster->stats.speed) + continue; allDone = allDone && monster_move(monster, rm, map); } - if (allDone) + if (allDone) { timer_stop(map->monsterMoveTimer); - else + linkedlist_each(&map->monsters, (void (*)(void*)) monster_reset_steps); + } else { timer_start(map->monsterMoveTimer); + } return allDone; } @@ -353,13 +358,13 @@ map_render_mid_layer(Map *map, Camera *cam) } void -map_render_top_layer(Map *map, Camera *cam) +map_render_top_layer(Map *map, RoomMatrix *rm, Camera *cam) { LinkedList *monsterItem = map->monsters; while (monsterItem != NULL) { Monster *monster = monsterItem->data; monsterItem = monsterItem->next; - monster_render_top_layer(monster, cam); + monster_render_top_layer(monster, rm, cam); } } @@ -388,6 +393,8 @@ void map_set_current_room(Map *map, Position *pos) map->currentRoom.x = MAP_H_ROOM_COUNT - 1; if (map->currentRoom.y >= MAP_V_ROOM_COUNT) map->currentRoom.y = MAP_V_ROOM_COUNT - 1; + + map->rooms[map->currentRoom.x][map->currentRoom.y]->visited = true; } static diff --git a/src/map.h b/src/map.h index a769607..f4be227 100644 --- a/src/map.h +++ b/src/map.h @@ -51,6 +51,7 @@ typedef struct Room_t { MapTile* decorations[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT]; Trap* traps[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT]; RoomModifierData modifier; + bool visited; } Room; typedef struct Map_t { @@ -106,7 +107,7 @@ void map_render_mid_layer(Map*, Camera*); void -map_render_top_layer(Map*, Camera*); +map_render_top_layer(Map*, RoomMatrix*, Camera*); void map_set_current_room(Map*, Position*); diff --git a/src/monster.c b/src/monster.c index 6d27f87..8334857 100644 --- a/src/monster.c +++ b/src/monster.c @@ -106,6 +106,8 @@ monster_behaviour_check_post_hit(Monster *m) monster_state_change(m, SCARED); break; case GUERILLA: + case ASSASSIN: + case SORCERER: case FIRE_DEMON: break; default: @@ -114,14 +116,61 @@ monster_behaviour_check_post_hit(Monster *m) } } +static void +damage_surroundings(Monster *m, RoomMatrix *rm) +{ + Position roomPos = position_to_matrix_coords(&m->sprite->pos); + for (Sint32 i = -1; i <= 1; ++i) { + for (Sint32 j = -1; j <= 1; ++j) { + if (i == 0 && j == 0) + continue; + RoomSpace *r = &rm->spaces[roomPos.x + i][roomPos.y + j]; + if (r->monster) { + int dmg = stats_fight(&m->stats, &r->monster->stats); + monster_hit(r->monster, dmg); + gui_log("%s takes %d damage from the explosion", r->monster->label, dmg); + } else if (r->player) { + int dmg = stats_fight(&m->stats, &r->player->stats); + player_hit(r->player, dmg); + gui_log("You take %d damage from the explosion", dmg); + } + } + } +} + +static void +sorcerer_blast(Monster *m, RoomMatrix *rm) +{ + gui_log("%s creates a magical explosion", m->label); + particle_engine_eldritch_explosion(m->sprite->pos, DIM(TILE_DIMENSION, TILE_DIMENSION)); + + damage_surroundings(m, rm); +} + +static void +assassin_cloak_effect(Monster *m, bool cloak) +{ + if (cloak) + gui_log("%s dissappears from sight", m->label); + else + gui_log("%s reappears, filled with rage", m->label); + particle_engine_fire_explosion(m->sprite->pos, DIM(TILE_DIMENSION, TILE_DIMENSION)); +} + + static void monster_behaviour_check_post_attack(Monster *m) { switch (m->behaviour) { case GUERILLA: + case SORCERER: case FIRE_DEMON: monster_state_change(m, SCARED); break; + case ASSASSIN: + assassin_cloak_effect(m, true); + monster_state_change(m, SCARED); + break; default: break; } @@ -154,12 +203,20 @@ monster_behaviour_check(Monster *m, RoomMatrix *rm) { switch (m->behaviour) { case GUERILLA: + case SORCERER: case FIRE_DEMON: - if (m->state.stepsSinceChange > 8 + if (m->state.stepsSinceChange > 5 && m->state.current == SCARED) { monster_state_change(m, AGRESSIVE); } break; + case ASSASSIN: + if (m->state.stepsSinceChange > 5 + && m->state.current == SCARED) { + assassin_cloak_effect(m, false); + monster_state_change(m, AGRESSIVE); + } + break; case SENTINEL: handle_sentinel_behaviour(m, rm); break; @@ -395,10 +452,18 @@ monster_move(Monster *m, RoomMatrix *rm, Map *map) } monster_behaviour_check(m, rm); - Position origPos = m->sprite->pos; Position originalMPos = position_to_matrix_coords(&m->sprite->pos); + + if (m->state.current == AGRESSIVE && m->behaviour == SORCERER) { + if (position_proximity(1, &originalMPos, &rm->playerRoomPos)) { + sorcerer_blast(m, rm); + monster_behaviour_check_post_attack(m); + return true; + } + } + rm->spaces[originalMPos.x][originalMPos.y].occupied = false; rm->spaces[originalMPos.x][originalMPos.y].monster = NULL; @@ -436,18 +501,28 @@ monster_move(Monster *m, RoomMatrix *rm, Map *map) RoomSpace *space = &rm->spaces[newPos.x][newPos.y]; if (space->light < 100 && withinHearingDist) { - actiontextbuilder_create_text("!", C_WHITE, &m->sprite->pos); + Position alertPos = m->sprite->pos; + alertPos.x += TILE_DIMENSION >> 1; + alertPos.y += TILE_DIMENSION >> 1; + actiontextbuilder_create_text("!", C_WHITE, &alertPos); } } } - if (!position_equals(&origPos, &m->sprite->pos) - && (rm->modifier->type == RMOD_TYPE_FIRE || m->behaviour == FIRE_DEMON)) { - Object *o = object_create_fire(); - o->sprite->pos = origPos; - o->damage *= m->stats.lvl; - linkedlist_push(&map->objects, o); + if (!position_equals(&origPos, &m->sprite->pos)) { + if (rm->modifier->type == RMOD_TYPE_FIRE || m->behaviour == FIRE_DEMON) { + Object *o = object_create_fire(); + o->sprite->pos = origPos; + o->damage *= m->stats.lvl; + linkedlist_push(&map->objects, o); + } + if (m->behaviour == SORCERER) { + Object *o = object_create_green_gas(); + o->sprite->pos = origPos; + o->damage *= m->stats.lvl; + linkedlist_push(&map->objects, o); + } } m->steps++; @@ -455,13 +530,18 @@ monster_move(Monster *m, RoomMatrix *rm, Map *map) if (m->stateIndicator.displayCount > 0) m->stateIndicator.displayCount -= 1; m->state.stepsSinceChange += 1; - m->steps = 0; return true; } return false; } +void +monster_reset_steps(Monster *m) +{ + m->steps = 0; +} + void monster_update(Monster *m, UpdateData *data) { @@ -542,7 +622,7 @@ monster_drop_loot(Monster *monster, Map *map, Player *player) linkedlist_append(&map->items, treasure); } - if (monster->stats.lvl > 2 && get_random(19) == 0) { + if (monster->stats.lvl > 2 && get_random(29) == 0) { Artifact *a = artifact_create_random(player, 1); a->sprite->pos = monster->sprite->pos; linkedlist_append(&map->artifacts, a); @@ -593,17 +673,31 @@ monster_render(Monster *m, Camera *cam) if (m->stats.hp <= 0) return; + if (m->behaviour == ASSASSIN && m->state.current != AGRESSIVE) + return; + sprite_render(m->sprite, cam); } void -monster_render_top_layer(Monster *m, Camera *cam) +monster_render_top_layer(Monster *m, RoomMatrix *rm, Camera *cam) { if (m->stats.hp <= 0) return; + if (m->behaviour == ASSASSIN && m->state.current != AGRESSIVE) + return; + + Position mPos = position_to_matrix_coords(&m->sprite->pos); + mPos.y -= 1; + if (rm->spaces[mPos.x][mPos.y].player) { + sprite_set_alpha(m->stateIndicator.sprite, 110); + } if (m->stateIndicator.displayCount != 0) sprite_render(m->stateIndicator.sprite, cam); + if (rm->spaces[mPos.x][mPos.y].player) { + sprite_set_alpha(m->stateIndicator.sprite, 255); + } } void @@ -613,6 +707,8 @@ monster_set_behaviour(Monster *m, MonsterBehaviour behaviour) switch (behaviour) { case HOSTILE: case GUERILLA: + case ASSASSIN: + case SORCERER: case COWARD: case FIRE_DEMON: m->state.current = AGRESSIVE; diff --git a/src/monster.h b/src/monster.h index 91ebd1b..5faf665 100644 --- a/src/monster.h +++ b/src/monster.h @@ -35,7 +35,9 @@ typedef enum { GUERILLA, COWARD, SENTINEL, - FIRE_DEMON + FIRE_DEMON, + SORCERER, + ASSASSIN } MonsterBehaviour; typedef enum { @@ -85,7 +87,7 @@ void monster_render(Monster*, Camera*); void -monster_render_top_layer(Monster*, Camera*); +monster_render_top_layer(Monster*, RoomMatrix*, Camera*); void monster_hit(Monster*, unsigned int dmg); @@ -108,6 +110,9 @@ monster_set_state(Monster *m, StateType state, Uint8 forceCount); void monster_push(Monster *, RoomMatrix*, Vector2d dir); +void +monster_reset_steps(Monster *m); + void monster_destroy(Monster*); diff --git a/src/object.c b/src/object.c index 3cd9098..c566432 100644 --- a/src/object.c +++ b/src/object.c @@ -49,6 +49,21 @@ object_create_fire() return o; } +Object * +object_create_green_gas() +{ + Object *o = object_create(); + Texture *t0 = texturecache_add("Objects/Effect0.png"); + Texture *t1 = texturecache_add("Objects/Effect1.png"); + sprite_set_texture(o->sprite, t0, 0); + sprite_set_texture(o->sprite, t1, 1); + o->sprite->dim = GAME_DIMENSION; + o->sprite->clip = CLIP16(32, 24*16); + o->damage = 3; + o->timeout = 3; + return o; +} + void object_render(Object *o, Camera *cam) { diff --git a/src/object.h b/src/object.h index 4ca2190..9011151 100644 --- a/src/object.h +++ b/src/object.h @@ -38,6 +38,9 @@ object_create(void); Object * object_create_fire(void); +Object * +object_create_green_gas(void); + void object_render(Object*, Camera*); diff --git a/src/particle_engine.c b/src/particle_engine.c index b86ffb2..40d1d66 100644 --- a/src/particle_engine.c +++ b/src/particle_engine.c @@ -156,10 +156,10 @@ create_explosion(Position pos, Dimension dim, unsigned int c_count, ...) x = get_random(dim.width) + pos.x; y = get_random(dim.height) + pos.y; - xv = get_random(600) - 300; - yv = get_random(600) - 300; + xv = get_random(500) - 300; + yv = get_random(500) - 300; - lt = get_random(10); + lt = get_random(20); p = create_rect_particle(); p->particle.rect.pos = (Position) { x, y }; @@ -167,6 +167,7 @@ create_explosion(Position pos, Dimension dim, unsigned int c_count, ...) p->velocity = (Vector2d) { (float) xv, (float) yv }; p->movetime = lt; p->lifetime = lt; + p->blend_mode = SDL_BLENDMODE_BLEND; p->color = colors[get_random((unsigned int) c_count-1)]; linkedlist_append(&engine->game_particles, p); } @@ -318,7 +319,7 @@ particle_engine_heat() h = get_random(2) + 2; yvel = get_random(50) - 200; - xvel = get_random(100) * -get_random(1); + xvel = get_random(100) * -((int) get_random(1)); lt = get_random(500); diff --git a/src/player.c b/src/player.c index 8af18d4..1e9f6e3 100644 --- a/src/player.c +++ b/src/player.c @@ -428,7 +428,7 @@ build_sword_animation(Player *p, SDL_Renderer *renderer) } Player* -player_create(class_t class, SDL_Renderer *renderer) +player_create(class_t class, Camera *cam) { Player *player = malloc(sizeof(Player)); player->sprite = sprite_create(); @@ -450,8 +450,9 @@ player_create(class_t class, SDL_Renderer *renderer) player->projectiles = linkedlist_create(); player->animationTimer = timer_create(); player->swordAnimation = animation_create(5); + player->equipment.hasArtifacts = false; - build_sword_animation(player, renderer); + build_sword_animation(player, cam->renderer); memset(&player->skills, 0, PLAYER_SKILL_COUNT * sizeof(Skill*)); @@ -480,16 +481,16 @@ player_create(class_t class, SDL_Renderer *renderer) case WARRIOR: m_strcpy(asset, 100, "Commissions/Warrior.png"); player->stats = (Stats) WARRIOR_STATS; - player->skills[0] = skill_create(FLURRY); - player->skills[1] = skill_create(BASH); - player->skills[2] = skill_create(CHARGE); - player->skills[3] = skill_create(DAGGER_THROW); + player->skills[0] = skill_create(FLURRY, cam); + player->skills[1] = skill_create(BASH, cam); + player->skills[2] = skill_create(CHARGE, cam); + player->skills[3] = skill_create(DAGGER_THROW, cam); break; } - player->skills[4] = skill_create(SIP_HEALTH); + player->skills[4] = skill_create(SIP_HEALTH, cam); - sprite_load_texture(player->sprite, asset, 0, renderer); + sprite_load_texture(player->sprite, asset, 0, cam->renderer); player->sprite->pos = (Position) { TILE_DIMENSION, TILE_DIMENSION }; player->sprite->dim = GAME_DIMENSION; player->sprite->clip = (SDL_Rect) { 0, 0, 16, 16 }; @@ -530,7 +531,7 @@ player_hit(Player *p, unsigned int dmg) Position pos = p->sprite->pos; pos.x += 8; pos.y += 8; - particle_engine_bloodspray(pos, (Dimension) { 8, 8 }, dmg); + particle_engine_bloodspray(pos, DIM(8, 8), dmg); mixer_play_effect(PLAYER_HIT0 + get_random(2)); char msg[5]; m_sprintf(msg, 5, "-%d", dmg); @@ -624,6 +625,7 @@ player_reset(Player *player) { for (size_t i = 0; i < LAST_ARTIFACT_EFFECT; ++i) player->equipment.artifacts[i].level = 0; + player->equipment.hasArtifacts = false; while (player->projectiles) projectile_destroy(linkedlist_pop(&player->projectiles)); @@ -677,4 +679,5 @@ player_add_artifact(Player *p, Artifact *a) gui_log("You pick an ancient %s", ad->name); gui_log("%s (%u)", ad->desc, ad->level); + p->equipment.hasArtifacts = true; } diff --git a/src/player.h b/src/player.h index e8af448..07d970d 100644 --- a/src/player.h +++ b/src/player.h @@ -59,6 +59,7 @@ typedef struct ArtifactData { typedef struct PlayerEquipment { ArtifactData artifacts[LAST_ARTIFACT_EFFECT]; + bool hasArtifacts; } PlayerEquipment; typedef struct Player { @@ -79,7 +80,7 @@ typedef struct Player { } Player; Player* -player_create(class_t, SDL_Renderer*); +player_create(class_t, Camera*); ExperienceData player_get_xp_data(Player*); diff --git a/src/position.h b/src/position.h index 421a860..e50d1b0 100644 --- a/src/position.h +++ b/src/position.h @@ -21,6 +21,8 @@ #include +#define POS(x, y) (Position) { x, y } + typedef struct { int x; int y; diff --git a/src/roommatrix.c b/src/roommatrix.c index 7c5f9d0..519b37e 100644 --- a/src/roommatrix.c +++ b/src/roommatrix.c @@ -295,6 +295,8 @@ void roommatrix_destroy(RoomMatrix *m) linkedlist_pop(&space->items); while (space->artifacts) linkedlist_pop(&space->artifacts); + while (space->objects) + linkedlist_pop(&space->objects); } } diff --git a/src/settings.c b/src/settings.c index fdce103..489e1b1 100644 --- a/src/settings.c +++ b/src/settings.c @@ -27,8 +27,10 @@ static sqlite3 *db = NULL; static Settings settings; -static const char *KEY_MUSIC_ENABLED = "music_enabled"; -static const char *KEY_SOUND_ENABLED = "sound_enabled"; +static const char *KEY_MUSIC_ENABLED = "music_enabled"; +static const char *KEY_SOUND_ENABLED = "sound_enabled"; +static const char *KEY_TOOLTIPS_ENABLED = "tooltips_enabled"; +static const char *KEY_HOW_TO_PLAY_SHOWN = "how_to_play_shown"; static DbQuery MIGRATE_COMMANDS[] = { @@ -49,6 +51,8 @@ set_default_settings(void) { settings.music_enabled = true; settings.sound_enabled = true; + settings.tooltips_enabled = true; + settings.howto_tooltip_shown = false; } static void @@ -82,6 +86,14 @@ load_settings_cb(void *unused, int count, char **values, char **colNames) settings.music_enabled = (bool)atoi(values[i + 1]); i += 2; } + else if (!strcmp(KEY_HOW_TO_PLAY_SHOWN, values[i])) { + settings.howto_tooltip_shown = (bool)atoi(values[i + 1]); + i += 2; + } + else if (!strcmp(KEY_TOOLTIPS_ENABLED, values[i])) { + settings.tooltips_enabled = (bool)atoi(values[i + 1]); + i += 2; + } } return 0; } @@ -127,6 +139,8 @@ settings_save(void) { save_setting_int(KEY_SOUND_ENABLED, settings.sound_enabled); save_setting_int(KEY_MUSIC_ENABLED, settings.music_enabled); + save_setting_int(KEY_TOOLTIPS_ENABLED, settings.tooltips_enabled); + save_setting_int(KEY_HOW_TO_PLAY_SHOWN, settings.howto_tooltip_shown); } Settings * diff --git a/src/settings.h b/src/settings.h index 99a7857..91d5a25 100644 --- a/src/settings.h +++ b/src/settings.h @@ -24,6 +24,8 @@ typedef struct Settings { bool music_enabled; bool sound_enabled; + bool tooltips_enabled; + bool howto_tooltip_shown; } Settings; void diff --git a/src/skill.c b/src/skill.c index c303ea1..b512d0b 100644 --- a/src/skill.c +++ b/src/skill.c @@ -35,6 +35,106 @@ #include "animation.h" #include "artifact.h" #include "trap.h" +#include "tooltip.h" + +static char *flurry_tooltip[] = { + "FLURRY", + "", + " Hits an adjecant enemy with a flurry of three strikes.", + " Each strike has the same odds of hitting as a regular attack", + "", + "COOLDOWN:", + " 5 turns", + "", + "USAGE:", + " activate the skill (press 1)", + " followed by a direction (left, right, up or down)", + "", + "", + "Press ESC to close", + NULL +}; + +static char *bash_tooltip[] = { + "BASH", + "", + " Bashes an adjecant enemy with your shield", + " On a successful hit the target will be stunned for 2 turns", + "", + "COOLDOWN:", + " 2 turns", + "", + "USAGE:", + " activate the skill (press 2)", + " followed by a direction (left, right, up or down)", + "", + "", + "Press ESC to close", + NULL +}; + +static char *charge_tooltip[] = { + "CHARGE", + "", + " You charge in a chosen direction into the first obstructing", + " object. Charging into an enemy can deliver massive damage.", + "", + " Damage is affected by charge distance.", + " Longer distance, more damage.", + "", + "COOLDOWN:", + " 5 turns", + "", + "USAGE:", + " activate the skill (press 3)", + " followed by a direction (left, right, up or down)", + "", + "", + "Press ESC to close", + NULL +}; + +static char *dagger_tooltip[] = { + "THROW DAGGER", + "", + " You throw a dagger in the chosen direction.", + "", + " Damage is affected by throwing distance.", + " Longer distance, more damage.", + "", + " Dagger supply is not infinite, your current dagger", + " inventory is displayed in the panel to the right.", + "", + "COOLDOWN:", + " 0 turns", + "", + "USAGE:", + " activate the skill (press 4)", + " followed by a direction (left, right, up or down)", + "", + "", + "Press ESC to close", + NULL +}; + +static char *health_tooltip[] = { + "DRINK HEALTH", + "", + " You take a sip from your health vial", + "", + " The current amount of sips in your vials is", + " dsplayed in the panel to the right.", + "", + "COOLDOWN:", + " 0 turns", + "", + "USAGE:", + " Sip health (press 5)", + "", + "", + "Press ESC to close", + NULL +}; static Skill * create_default(const char *s_label, Sprite *s) @@ -50,6 +150,7 @@ create_default(const char *s_label, Sprite *s) skill->available = NULL; skill->use = NULL; skill->levelcap = 1; + skill->tooltip = NULL; return skill; } @@ -410,24 +511,29 @@ create_charge(void) } Skill* -skill_create(enum SkillType t) +skill_create(enum SkillType t, Camera *cam) { Skill *skill; switch (t) { case FLURRY: skill = create_flurry(); + skill->tooltip = tooltip_create(flurry_tooltip, cam); break; case SIP_HEALTH: skill = create_sip_health(); + skill->tooltip = tooltip_create(health_tooltip, cam); break; case CHARGE: skill = create_charge(); + skill->tooltip = tooltip_create(charge_tooltip, cam); break; case DAGGER_THROW: skill = create_throw_dagger(); + skill->tooltip = tooltip_create(dagger_tooltip, cam); break; case BASH: skill = create_bash(); + skill->tooltip = tooltip_create(bash_tooltip, cam); break; default: fatal("Unknown SkillType %u", (unsigned int) t); @@ -444,5 +550,7 @@ void skill_destroy(Skill *skill) { sprite_destroy(skill->icon); + if (skill->tooltip) + sprite_destroy(skill->tooltip); free(skill); } diff --git a/src/skill.h b/src/skill.h index a027cea..df42675 100644 --- a/src/skill.h +++ b/src/skill.h @@ -52,10 +52,11 @@ typedef struct Skill_t { bool active; bool (*available)(Player*); bool (*use)(struct Skill_t*, SkillData*); + Sprite *tooltip; } Skill; Skill* -skill_create(enum SkillType); +skill_create(enum SkillType, Camera *cam); void skill_destroy(Skill*); diff --git a/src/skillbar.c b/src/skillbar.c index 0ef70e3..167a3a5 100644 --- a/src/skillbar.c +++ b/src/skillbar.c @@ -26,6 +26,7 @@ #include "texturecache.h" #include "particle_engine.h" #include "update_data.h" +#include "gui.h" static void load_texture(SkillBar *bar, const char *path, SDL_Renderer *renderer) @@ -34,7 +35,7 @@ load_texture(SkillBar *bar, const char *path, SDL_Renderer *renderer) t->dim.width = 16; t->dim.height = 16; - for (unsigned int i = 0; i < 10; ++i) { + for (unsigned int i = 0; i < 5; ++i) { char buffer[4]; Sprite *s = sprite_create(); s->pos = (Position) { i * 32 + 20, 20 }; @@ -52,7 +53,7 @@ load_countdown_sprites(SkillBar *bar) { for (int i = 0; i < PLAYER_SKILL_COUNT; ++i) { Sprite *s = sprite_create(); - sprite_load_text_texture(s, "GUI/SDS_8x8.ttf", 0, 16, 0); + sprite_load_text_texture(s, "GUI/SDS_8x8.ttf", 0, 14, 1); s->fixed = true; s->pos = (Position) { 8 + (32 * i), 8 }; s->dim = (Dimension) { 16, 16 }; @@ -60,45 +61,36 @@ load_countdown_sprites(SkillBar *bar) } } -SkillBar * -skillbar_create(SDL_Renderer *renderer) -{ - SkillBar *bar = ec_malloc(sizeof(SkillBar)); - bar->sprites = linkedlist_create(); - bar->activationTimer = timer_create(); - bar->skillSparkleTimer = timer_create(); - bar->lastActivation = 0; - load_texture(bar, "GUI/GUI0.png", renderer); - load_countdown_sprites(bar); - return bar; -} - -void -skillbar_check_skill_activation(SkillBar *bar, Player *player) -{ - for (int i = 0; i < PLAYER_SKILL_COUNT; ++i) { - if (!player->skills[i]) - continue; - - if (player->skills[i]->levelcap != player->stats.lvl) - continue; - - timer_start(bar->skillSparkleTimer); - } -} - -static void -render_frame(Camera *cam) +static Sprite* +create_frame_sprite(Camera *cam) { static SDL_Rect c_top_left = { 1*16, 10*16, 16, 16 }; static SDL_Rect c_top_right = { 3*16, 10*16, 16, 16 }; + static SDL_Rect c_center_top = { 2*16, 10*16, 16, 16 }; + static SDL_Rect c_center_bottom = { 2*16, 12*16, 16, 16 }; static SDL_Rect c_bottom_left = { 1*16, 12*16, 16, 16 }; static SDL_Rect c_bottom_right = { 3*16, 12*16, 16, 16 }; + Sprite *frame = sprite_create(); + Texture *texture = texture_create(); + texture->dim = (Dimension) { GAME_VIEW_WIDTH, 32 }; + frame->textures[0] = texture; + frame->destroyTextures = true; + frame->pos = (Position) { 0, 0 }; + frame->dim = (Dimension) { GAME_VIEW_WIDTH, 32 }; + frame->fixed = true; + texture_create_blank(texture, + SDL_TEXTUREACCESS_TARGET, + cam->renderer); + + SDL_SetRenderTarget(cam->renderer, texture->texture); + SDL_RenderClear(cam->renderer); + Texture *t = texturecache_get("GUI/GUI0.png"); SDL_Rect box = { 0, 0, 16, 16 }; - for (unsigned int i = 0; i < MAP_ROOM_WIDTH; ++i) { + // Render skill squares + for (Uint32 i = 0; i < 5; ++i) { box.x = i*32; box.y = 0; texture_render_clip(t, &box, &c_top_left, cam); @@ -111,6 +103,93 @@ render_frame(Camera *cam) box.y = 16; texture_render_clip(t, &box, &c_bottom_right, cam); } + + // Render inventory box + box.x = 5 * 32; + box.y = 0; + texture_render_clip(t, &box, &c_top_left, cam); + box.y = 16; + texture_render_clip(t, &box, &c_bottom_left, cam); + box.x = 5 * 32 + 16; + box.y = 0; + texture_render_clip(t, &box, &c_center_top, cam); + box.y = 16; + texture_render_clip(t, &box, &c_center_bottom, cam); + + for (Uint32 i = 6; i < MAP_ROOM_WIDTH - 1; ++i) { + box.x = i*32; + box.y = 0; + texture_render_clip(t, &box, &c_center_top, cam); + box.y = 16; + texture_render_clip(t, &box, &c_center_bottom, cam); + + box.x = i * 32 + 16; + box.y = 0; + texture_render_clip(t, &box, &c_center_top, cam); + box.y = 16; + texture_render_clip(t, &box, &c_center_bottom, cam); + } + + box.x = (MAP_ROOM_WIDTH - 1) * 32; + box.y = 0; + texture_render_clip(t, &box, &c_center_top, cam); + box.y = 16; + texture_render_clip(t, &box, &c_center_bottom, cam); + box.x = (MAP_ROOM_WIDTH - 1) * 32 + 16; + box.y = 0; + texture_render_clip(t, &box, &c_top_right, cam); + box.y = 16; + texture_render_clip(t, &box, &c_bottom_right, cam); + + SDL_SetRenderTarget(cam->renderer, NULL); + + return frame; +} + +SkillBar * +skillbar_create(Camera *cam) +{ + SkillBar *bar = ec_malloc(sizeof(SkillBar)); + bar->sprites = linkedlist_create(); + bar->activationTimer = timer_create(); + bar->skillSparkleTimer = timer_create(); + bar->lastActivation = 0; + bar->frame = create_frame_sprite(cam); + bar->artifactDisplayOffset = 5 * 32 + 8; + load_texture(bar, "GUI/GUI0.png", cam->renderer); + load_countdown_sprites(bar); + + for (Uint32 i = 0; i < LAST_ARTIFACT_EFFECT; ++i) { + bar->artifacts[i].aSprite = artifact_sprite_for(i); + bar->artifacts[i].aSprite->fixed = true; + bar->artifacts[i].lvl = 0; + + Sprite *lvlSprite = sprite_create(); + lvlSprite->fixed = true; + lvlSprite->dim = DIM(9, 9); + sprite_load_text_texture(lvlSprite, "GUI/SDS_8x8.ttf", 0, 9, 0); + bar->artifacts[i].lvlSprite = lvlSprite; + } + return bar; +} + +bool +skillbar_check_skill_activation(SkillBar *bar, Player *player) +{ + if (player->stats.lvl == 1) + return false; + + for (int i = 0; i < PLAYER_SKILL_COUNT; ++i) { + if (!player->skills[i]) + continue; + + if (player->skills[i]->levelcap != player->stats.lvl) + continue; + + timer_start(bar->skillSparkleTimer); + } + + return timer_started(bar->skillSparkleTimer); } static void @@ -177,6 +256,18 @@ render_skills(Player *player, Camera *cam) } } +static void +render_artifacts(SkillBar *bar, Camera *cam) +{ + UNUSED(bar); + for (size_t i = 0; i < LAST_ARTIFACT_EFFECT; ++i) { + if (bar->artifacts[i].lvl == 0) + continue; + sprite_render(bar->artifacts[i].aSprite, cam); + sprite_render(bar->artifacts[i].lvlSprite, cam); + } +} + static void render_skill_unavailable(SkillBar *bar, Player *player, Camera *cam) { @@ -242,8 +333,9 @@ render_skill_sparkles(SkillBar *bar, Player *player) void skillbar_render(SkillBar *bar, Player *player, Camera *cam) { - render_frame(cam); + sprite_render(bar->frame, cam); render_skills(player, cam); + render_artifacts(bar, cam); render_sprites(bar, cam); render_skill_unavailable(bar, player, cam); render_activation_indicator(bar, cam); @@ -256,8 +348,17 @@ skillbar_update(SkillBar *bar, UpdateData *data) { Input *input = data->input; - unsigned int key = 0; - for (int i = 0; i < 10; ++i) { + for (int i = 0; i < 5; ++i) { + if (!data->player->skills[i]) + continue; + if (input_modkey_is_pressed(input, KEY_SHIFT_NUM1 << i)) { + data->gui->activeTooltip = data->player->skills[i]->tooltip; + return; + } + } + + Uint32 key = 0; + for (int i = 0; i < 5; ++i) { if (!input_key_is_pressed(input, KEY_NUM0 << i)) continue; key = i; @@ -268,6 +369,35 @@ skillbar_update(SkillBar *bar, UpdateData *data) bar->lastActivation = key; timer_start(bar->activationTimer); } + + for (size_t i = 0; i < LAST_ARTIFACT_EFFECT; ++i) { + if (data->player->equipment.artifacts[i].level == bar->artifacts[i].lvl) + continue; + + Uint32 origLevel = bar->artifacts[i].lvl; + bar->artifacts[i].lvl = data->player->equipment.artifacts[i].level; + + char lvl[4]; + m_sprintf(lvl, 4, "%u", bar->artifacts[i].lvl); + + texture_load_from_text(bar->artifacts[i].lvlSprite->textures[0], + lvl, C_PURPLE, C_WHITE, data->cam->renderer); + + // Only update position if this is the first pickup + if (origLevel == 0) { + bar->artifacts[i].lvlSprite->pos.x = bar->artifactDisplayOffset + 12; + bar->artifacts[i].lvlSprite->pos.y = 16; + bar->artifacts[i].aSprite->pos.x = bar->artifactDisplayOffset; + bar->artifacts[i].aSprite->pos.y = 8; + bar->artifactDisplayOffset += 32; + } + } +} + +void +skillbar_reset(SkillBar *bar) +{ + bar->artifactDisplayOffset = 5 * 32 + 8; } void @@ -275,9 +405,14 @@ skillbar_destroy(SkillBar *bar) { while (bar->sprites) sprite_destroy(linkedlist_pop(&bar->sprites)); - for (unsigned int i = 0; i < PLAYER_SKILL_COUNT; ++i) + for (Uint32 i = 0; i < PLAYER_SKILL_COUNT; ++i) if (bar->countdowns[i]) sprite_destroy(bar->countdowns[i]); + for (Uint32 i = 0; i < LAST_ARTIFACT_EFFECT; ++i) { + sprite_destroy(bar->artifacts[i].aSprite); + sprite_destroy(bar->artifacts[i].lvlSprite); + } + sprite_destroy(bar->frame); timer_destroy(bar->activationTimer); timer_destroy(bar->skillSparkleTimer); free(bar); diff --git a/src/skillbar.h b/src/skillbar.h index 89aaef4..2928faf 100644 --- a/src/skillbar.h +++ b/src/skillbar.h @@ -28,18 +28,27 @@ struct UpdateData; -typedef struct SkillBar_t { +typedef struct ArtifactDisplay { + Sprite *aSprite; + Sprite *lvlSprite; + Uint32 lvl; +} ArtifactDisplay; + +typedef struct SkillBar { LinkedList *sprites; + ArtifactDisplay artifacts[LAST_ARTIFACT_EFFECT]; + Uint32 artifactDisplayOffset; Sprite *countdowns[PLAYER_SKILL_COUNT]; + Sprite *frame; Timer *activationTimer; Timer *skillSparkleTimer; - unsigned int lastActivation; + Uint32 lastActivation; } SkillBar; SkillBar * -skillbar_create(SDL_Renderer*); +skillbar_create(Camera*); -void +bool skillbar_check_skill_activation(SkillBar*, Player*); void @@ -48,6 +57,9 @@ skillbar_render(SkillBar*, Player*, Camera*); void skillbar_update(SkillBar*, struct UpdateData*); +void +skillbar_reset(SkillBar*); + void skillbar_destroy(SkillBar*); diff --git a/src/sprite.c b/src/sprite.c index e1a4040..6a1a1a6 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -94,6 +94,24 @@ sprite_set_texture(Sprite *s, Texture *t, int index) s->textures[index] = t; } +void +sprite_set_blend_mode(Sprite *s, SDL_BlendMode mode) +{ + if (s->textures[0]) + texture_set_blend_mode(s->textures[0], mode); + if (s->textures[1]) + texture_set_blend_mode(s->textures[1], mode); +} + +void +sprite_set_alpha(Sprite *s, Uint8 alpha) +{ + if (s->textures[0]) + texture_set_alpha(s->textures[0], alpha); + if (s->textures[1]) + texture_set_alpha(s->textures[1], alpha); +} + void sprite_render(Sprite *s, Camera *cam) { diff --git a/src/sprite.h b/src/sprite.h index 87c393d..045ebea 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -59,6 +59,12 @@ sprite_set_texture(Sprite *, Texture *, int index); void sprite_render(Sprite*, Camera*); +void +sprite_set_blend_mode(Sprite*, SDL_BlendMode); + +void +sprite_set_alpha(Sprite*, Uint8); + void sprite_destroy(Sprite *); diff --git a/src/stats.c b/src/stats.c index f713abc..1da5b88 100644 --- a/src/stats.c +++ b/src/stats.c @@ -67,16 +67,17 @@ stats_fight(Stats *attacker, Stats *defender) bool critical = false; int atkRoll = get_attack_roll(attacker); - if (atkRoll - attacker->atk == 20) - critical = true; int defRoll = get_defence_roll(defender); + if (atkRoll - attacker->atk == 20) + critical = get_attack_roll(attacker) > defRoll; + int dmgRoll = 0; if (atkRoll >= defRoll) { if (attacker->dmg > 0) - dmgRoll = get_random(attacker->dmg) + 1; - else dmgRoll = get_random(attacker->dmg - 1) + 1; + else + dmgRoll = 1; if (critical) { dmgRoll = dmgRoll * 2; diff --git a/src/texture.c b/src/texture.c index b0b1126..114e41d 100644 --- a/src/texture.c +++ b/src/texture.c @@ -223,6 +223,20 @@ texture_load_from_text_blended(Texture *t, const char * text, SDL_Color fg, SDL_ load_from_surface(t, surface, renderer); } +void +texture_set_blend_mode(Texture *t, SDL_BlendMode mode) +{ + assert(t->texture); + SDL_SetTextureBlendMode(t->texture, mode); +} + +void +texture_set_alpha(Texture *t, Uint8 alpha) +{ + assert(t->texture); + SDL_SetTextureAlphaMod(t->texture, alpha); +} + void texture_render(Texture *texture, SDL_Rect *box, Camera *cam) { diff --git a/src/texture.h b/src/texture.h index e0a8f8c..b3f038f 100644 --- a/src/texture.h +++ b/src/texture.h @@ -25,7 +25,7 @@ #include "position.h" #include "camera.h" -typedef struct { +typedef struct Texture { SDL_Texture *texture; TTF_Font *font; TTF_Font *outlineFont; @@ -76,6 +76,12 @@ texture_load_from_text_blended(Texture*, SDL_Color, SDL_Renderer*); +void +texture_set_blend_mode(Texture*, SDL_BlendMode); + +void +texture_set_alpha(Texture*, Uint8); + void texture_render(Texture*, SDL_Rect*, Camera*); diff --git a/src/tooltip.c b/src/tooltip.c new file mode 100644 index 0000000..0957426 --- /dev/null +++ b/src/tooltip.c @@ -0,0 +1,65 @@ +/* + * BreakHack - A dungeone crawler RPG + * Copyright (C) 2018 Linus Probert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "tooltip.h" +#include "gui_util.h" +#include "defines.h" +#include "gui.h" + +Sprite * +tooltip_create(char **content, Camera *cam) +{ + int rowCount = 0; + char **contentIndex = content; + while (*contentIndex) { + rowCount++; + contentIndex++; + } + + Sprite *sprite = gui_util_create_tooltip_frame_sprite(BOTTOM_GUI_WIDTH/16 - 6, + (Uint32) ((rowCount * 10 + 48)/16), + cam); + sprite->pos.x = 48; + sprite->pos.y = 96; + Texture *texture = sprite->textures[0]; + Texture *text = texture_create(); + texture_load_font(text, "GUI/SDS_8x8.ttf", LOG_FONT_SIZE, 0); + SDL_SetRenderTarget(cam->renderer, texture->texture); + SDL_Rect renderBox = { 16, 16, 0, 0 }; + + while (*content) { + if (strlen(*content) > 0) { + texture_load_from_text(text, + *content, + C_WHITE, + C_WHITE, + cam->renderer); + renderBox.w = text->dim.width; + renderBox.h = text->dim.height; + texture_render(text, &renderBox, cam); + } + + renderBox.y += 10; + content++; + } + SDL_SetRenderTarget(cam->renderer, NULL); + texture_destroy(text); + + return sprite; +} diff --git a/src/tooltip.h b/src/tooltip.h new file mode 100644 index 0000000..136729d --- /dev/null +++ b/src/tooltip.h @@ -0,0 +1,25 @@ +/* + * BreakHack - A dungeone crawler RPG + * Copyright (C) 2018 Linus Probert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "camera.h" +#include "sprite.h" + +Sprite * +tooltip_create(char **content, Camera*); diff --git a/src/update_data.h b/src/update_data.h index c3a5164..a0c00d9 100644 --- a/src/update_data.h +++ b/src/update_data.h @@ -22,11 +22,15 @@ #include "player.h" #include "map.h" #include "roommatrix.h" +#include "gui.h" +#include "camera.h" typedef struct UpdateData { Player *player; Map *map; RoomMatrix *matrix; + Gui *gui; + Camera *cam; Input *input; float deltatime; } UpdateData; diff --git a/src/util.h b/src/util.h index 5e0fcd3..accefaa 100644 --- a/src/util.h +++ b/src/util.h @@ -34,8 +34,8 @@ #define debug(...) do {} while(0) #define info(...) do {} while(0) #endif // DEBUG -#define error(...) log_print(stderr, "ERROR", __FNAME__, __LINE__, __func__, __VA_ARGS__) +#define error(...) log_print(stderr, "ERROR", __FNAME__, __LINE__, __func__, __VA_ARGS__) #ifdef DEBUG #define fatal(...) \ { \ @@ -44,7 +44,7 @@ getchar(); \ exit(-1); \ } -#else +#else // DEBUG #define fatal(...) \ { \ log_print(stderr, "FATAL", __FNAME__, __LINE__, __func__, __VA_ARGS__); \