From 549f47ae0b3c4fc9efd2fb65d71787edda23308f Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Thu, 16 Aug 2018 14:09:54 +0200 Subject: [PATCH 01/20] Minor code fixes and a slight tweak to the blue color --- src/defines.h | 2 +- src/main.c | 8 -------- src/particle_engine.c | 2 +- src/util.h | 4 ++-- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/defines.h b/src/defines.h index c190d64..41e791f 100644 --- a/src/defines.h +++ b/src/defines.h @@ -65,7 +65,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/main.c b/src/main.c index 7291ef5..5b6839f 100644 --- a/src/main.c +++ b/src/main.c @@ -216,14 +216,6 @@ startGame(void *unused) player_destroy(gPlayer); gPlayer = player_create(WARRIOR, gRenderer); 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(); gui_clear_message_log(); gui_log("The Dungeon Crawl begins!"); diff --git a/src/particle_engine.c b/src/particle_engine.c index b86ffb2..f776bd4 100644 --- a/src/particle_engine.c +++ b/src/particle_engine.c @@ -318,7 +318,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/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__); \ From 854f2c09187586a7b7de87ea0ad17d15f92e4e6d Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Sat, 18 Aug 2018 20:53:13 +0200 Subject: [PATCH 02/20] Prevent levels > 10 from crashing the game. --- data/monstergen.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/monstergen.lua b/data/monstergen.lua index de98692..bd28aba 100644 --- a/data/monstergen.lua +++ b/data/monstergen.lua @@ -220,7 +220,7 @@ end -- Begin script local enemies = {} -if(CURRENT_LEVEL > 0 and CURRENT_LEVEL < 10) then +if(CURRENT_LEVEL > 0) then if (CURRENT_LEVEL == 1) then enemies = concat(enemies, pests) enemies = concat(enemies, misc) From 30058ea4e0d5568972b0ad80dfa073d0c4a48eeb Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Sun, 19 Aug 2018 20:25:31 +0200 Subject: [PATCH 03/20] Creates gui_util and moves some code out from gui.c --- CMakeLists.txt | 1 + src/gui.c | 107 +++------------------------------------------- src/gui_util.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++ src/gui_util.h | 27 ++++++++++++ 4 files changed, 148 insertions(+), 100 deletions(-) create mode 100644 src/gui_util.c create mode 100644 src/gui_util.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 750b7cc..25b436f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,6 +177,7 @@ add_executable(breakhack src/screen src/hiscore src/object + src/gui_util ) # Sqlite has some warnings that I we don't need to see diff --git a/src/gui.c b/src/gui.c index 63b15cf..0aaa162 100644 --- a/src/gui.c +++ b/src/gui.c @@ -26,6 +26,7 @@ #include "util.h" #include "map.h" #include "texturecache.h" +#include "gui_util.h" #define DEFAULT_LOG { NULL, LOG_LINES_COUNT, 0, 200 } #define DEFAULT_EVENT_MESSAGES { NULL, 5, 0, 200 } @@ -33,16 +34,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; @@ -57,9 +48,6 @@ static struct GuiEventMsgData_t { 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 +162,12 @@ 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->rightFrame = gui_util_create_frame_sprite(RIGHT_GUI_WIDTH/16, + RIGHT_GUI_HEIGHT/16, + cam); + gui->bottomFrame = gui_util_create_frame_sprite(BOTTOM_GUI_WIDTH/16, + BOTTOM_GUI_HEIGHT/16, + cam); } Gui* @@ -400,87 +388,6 @@ 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) { diff --git a/src/gui_util.c b/src/gui_util.c new file mode 100644 index 0000000..2e7c9cb --- /dev/null +++ b/src/gui_util.c @@ -0,0 +1,113 @@ +/* + * 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 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 }; + +Sprite * +gui_util_create_frame_sprite(Uint32 width, + Uint32 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; +} diff --git a/src/gui_util.h b/src/gui_util.h new file mode 100644 index 0000000..52e4d7f --- /dev/null +++ b/src/gui_util.h @@ -0,0 +1,27 @@ +/* + * 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*); From 358c0c7ddcd6d5152ca5458cfa3af14db1c1a3fa Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Mon, 20 Aug 2018 14:30:31 +0200 Subject: [PATCH 04/20] Fixes #37 and #39 Adds tooltips to everything - A tooltip on first play will explain how it works - A tooltip on levelup will display skill info - A setting is introduced to disable tooltips --- CMakeLists.txt | 1 + src/gui.c | 18 +++++-- src/gui.h | 6 ++- src/gui_util.c | 49 +++++++++++------ src/gui_util.h | 5 ++ src/input.c | 15 ++++++ src/input.h | 10 +++- src/main.c | 132 ++++++++++++++++++++++++++++++++++++++-------- src/player.c | 16 +++--- src/player.h | 2 +- src/position.h | 2 + src/settings.c | 18 ++++++- src/settings.h | 2 + src/skill.c | 110 +++++++++++++++++++++++++++++++++++++- src/skill.h | 3 +- src/skillbar.c | 20 +++++-- src/skillbar.h | 2 +- src/tooltip.c | 64 ++++++++++++++++++++++ src/tooltip.h | 25 +++++++++ src/update_data.h | 2 + 20 files changed, 441 insertions(+), 61 deletions(-) create mode 100644 src/tooltip.c create mode 100644 src/tooltip.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c9c152..86099c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,6 +178,7 @@ add_executable(breakhack src/hiscore src/object src/gui_util + src/tooltip ) # Sqlite has some warnings that I we don't need to see diff --git a/src/gui.c b/src/gui.c index 0aaa162..479e7fb 100644 --- a/src/gui.c +++ b/src/gui.c @@ -27,6 +27,7 @@ #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 } @@ -41,7 +42,7 @@ 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; @@ -180,6 +181,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(); @@ -464,6 +466,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) { @@ -520,8 +530,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) @@ -549,8 +560,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; diff --git a/src/gui.h b/src/gui.h index 91d5791..e70f563 100644 --- a/src/gui.h +++ b/src/gui.h @@ -42,13 +42,14 @@ typedef enum Label_e { LABEL_COUNT } LabelIndex; -typedef struct { +typedef struct Gui { LinkedList *sprites; LinkedList *health; LinkedList *xp_bar; Sprite *bottomFrame; Sprite *rightFrame; Sprite *labels[LABEL_COUNT]; + Sprite *activeTooltip; Texture *log_lines[LOG_LINES_COUNT]; Texture *event_message; Timer *event_message_timer; @@ -69,6 +70,9 @@ 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 index 2e7c9cb..5908341 100644 --- a/src/gui_util.c +++ b/src/gui_util.c @@ -19,20 +19,11 @@ #include "texturecache.h" #include "gui_util.h" -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 }; - -Sprite * -gui_util_create_frame_sprite(Uint32 width, - Uint32 height, - Camera *cam) +static Sprite * +render_frame_on_texture(Uint32 width, + Uint32 height, + Position offset, + Camera *cam) { Sprite *frame = sprite_create(); Texture *texture = texture_create(); @@ -48,11 +39,21 @@ gui_util_create_frame_sprite(Uint32 width, 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 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) { @@ -108,6 +109,24 @@ gui_util_create_frame_sprite(Uint32 width, } } } + 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 index 52e4d7f..6698fef 100644 --- a/src/gui_util.h +++ b/src/gui_util.h @@ -25,3 +25,8 @@ 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 b81f781..ca9389e 100644 --- a/src/main.c +++ b/src/main.c @@ -52,29 +52,80 @@ #include "screen.h" #include "hiscore.h" #include "io_util.h" +#include "tooltip.h" + +static char *skills_tooltip[] = { + "CONGRATULATIONS!", + "", + " You have aquired a new skill!", + "", + " Skills are listed in the bar below the game screen.", + "", + "", + " SKILL INFO: CTRL + ", + " 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 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 Sprite *new_skill_tooltip = NULL; +static Sprite *howto_tooltip = NULL; +static unsigned int cLevel = 1; +static float deltaTime = 1.0; +static double renderScale = 1.0; static GameState gGameState; static SDL_Rect gameViewport; static SDL_Rect skillBarViewport; @@ -218,12 +269,17 @@ 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)); resetGame(); 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 @@ -249,6 +305,7 @@ static void goToMainMenu(void *unused) { UNUSED(unused); + gui_clear_message_log(); gGameState = MENU; menu_destroy(inGameMenu); inGameMenu = NULL; @@ -291,16 +348,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 @@ -412,6 +478,9 @@ init(void) hiscore_init(); initMainMenu(); + howto_tooltip = tooltip_create(how_to_play_tooltip, gCamera); + new_skill_tooltip = tooltip_create(skills_tooltip, gCamera); + gCamera->pos = (Position) { 0, 0 }; gGameState = MENU; @@ -426,7 +495,7 @@ handle_main_input(void) || gGameState == IN_GAME_MENU || gGameState == GAME_OVER) { - if (input_key_is_pressed(&input, KEY_ESC)) + if (!gGui->activeTooltip && input_key_is_pressed(&input, KEY_ESC)) toggleInGameMenu(NULL); } @@ -436,6 +505,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)) @@ -450,6 +521,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 @@ -518,6 +598,7 @@ populateUpdateData(UpdateData *data, float deltatime) data->map = gMap; data->matrix = gRoomMatrix; data->input = &input; + data->gui = gGui; data->deltatime = deltatime; } @@ -531,9 +612,14 @@ run_game_update(void) 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); + } + if (skillActivated && settings_get()->tooltips_enabled && playerLevel < 5) { + gGui->activeTooltip = new_skill_tooltip; } map_clear_expired_entities(gMap, gPlayer); @@ -601,6 +687,7 @@ run_game_render(void) 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 }; @@ -630,7 +717,6 @@ run_game(void) gui_event_message("NEW HIGHSCORE"); gui_log("NEW HIGHSCORE"); } - gui_event_message("Press ESC to open menu"); mixer_play_effect(SPLAT); gGameState = GAME_OVER; createInGameGameOverMenu(); @@ -750,6 +836,8 @@ 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); diff --git a/src/player.c b/src/player.c index 8af18d4..3225da3 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(); @@ -451,7 +451,7 @@ player_create(class_t class, SDL_Renderer *renderer) player->animationTimer = timer_create(); player->swordAnimation = animation_create(5); - build_sword_animation(player, renderer); + build_sword_animation(player, cam->renderer); memset(&player->skills, 0, PLAYER_SKILL_COUNT * sizeof(Skill*)); @@ -480,16 +480,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 }; diff --git a/src/player.h b/src/player.h index e8af448..2e921a2 100644 --- a/src/player.h +++ b/src/player.h @@ -79,7 +79,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/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..709ec5b 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 }; @@ -73,7 +74,7 @@ skillbar_create(SDL_Renderer *renderer) return bar; } -void +bool skillbar_check_skill_activation(SkillBar *bar, Player *player) { for (int i = 0; i < PLAYER_SKILL_COUNT; ++i) { @@ -85,6 +86,8 @@ skillbar_check_skill_activation(SkillBar *bar, Player *player) timer_start(bar->skillSparkleTimer); } + + return timer_started(bar->skillSparkleTimer); } static void @@ -256,8 +259,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; diff --git a/src/skillbar.h b/src/skillbar.h index 89aaef4..9cb24ff 100644 --- a/src/skillbar.h +++ b/src/skillbar.h @@ -39,7 +39,7 @@ typedef struct SkillBar_t { SkillBar * skillbar_create(SDL_Renderer*); -void +bool skillbar_check_skill_activation(SkillBar*, Player*); void diff --git a/src/tooltip.c b/src/tooltip.c new file mode 100644 index 0000000..4c46740 --- /dev/null +++ b/src/tooltip.c @@ -0,0 +1,64 @@ +/* + * 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 "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..ca15601 100644 --- a/src/update_data.h +++ b/src/update_data.h @@ -22,11 +22,13 @@ #include "player.h" #include "map.h" #include "roommatrix.h" +#include "gui.h" typedef struct UpdateData { Player *player; Map *map; RoomMatrix *matrix; + Gui *gui; Input *input; float deltatime; } UpdateData; From 9ef97c0897ea22079bb1f680889a49abfda79fee Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Tue, 21 Aug 2018 12:30:44 +0200 Subject: [PATCH 05/20] Fixes #38, Artifact inventory --- src/artifact.c | 80 ++++++++++++------- src/artifact.h | 3 + src/dimension.h | 2 + src/main.c | 33 +++++++- src/player.c | 3 + src/player.h | 1 + src/skillbar.c | 192 +++++++++++++++++++++++++++++++++++++--------- src/skillbar.h | 18 ++++- src/update_data.h | 2 + 9 files changed, 268 insertions(+), 66 deletions(-) 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/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/main.c b/src/main.c index ca9389e..86c3e94 100644 --- a/src/main.c +++ b/src/main.c @@ -54,6 +54,27 @@ #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!", "", @@ -123,6 +144,7 @@ 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; @@ -251,7 +273,7 @@ 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); gPointer = pointer_create(gRenderer); particle_engine_init(); @@ -452,6 +474,8 @@ resetGame(void) if (gMap) map_destroy(gMap); + skillbar_reset(gSkillBar); + particle_engine_clear(); info("Building new map"); @@ -480,6 +504,7 @@ init(void) 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 }; @@ -600,6 +625,7 @@ populateUpdateData(UpdateData *data, float deltatime) data->input = &input; data->gui = gGui; data->deltatime = deltatime; + data->cam = gCamera; } static void @@ -607,6 +633,7 @@ 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); @@ -621,6 +648,10 @@ run_game_update(void) if (skillActivated && settings_get()->tooltips_enabled && playerLevel < 5) { gGui->activeTooltip = new_skill_tooltip; } + if (!artifactTooltipShown && gPlayer->equipment.hasArtifacts) { + artifactTooltipShown = true; + gGui->activeTooltip = new_artifact_tooltip; + } map_clear_expired_entities(gMap, gPlayer); if (gGameState == PLAYING && currentTurn == PLAYER) diff --git a/src/player.c b/src/player.c index 3225da3..41f8558 100644 --- a/src/player.c +++ b/src/player.c @@ -450,6 +450,7 @@ player_create(class_t class, Camera *cam) player->projectiles = linkedlist_create(); player->animationTimer = timer_create(); player->swordAnimation = animation_create(5); + player->equipment.hasArtifacts = false; build_sword_animation(player, cam->renderer); @@ -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 2e921a2..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 { diff --git a/src/skillbar.c b/src/skillbar.c index 709ec5b..cd01a9c 100644 --- a/src/skillbar.c +++ b/src/skillbar.c @@ -53,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 }; @@ -61,47 +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; -} - -bool -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); - } - - return timer_started(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); @@ -114,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 @@ -180,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) { @@ -245,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); @@ -280,6 +369,34 @@ 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; + + 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_BLUE, C_WHITE, data->cam->renderer); + + // Only update position if this is the first pickup + if (bar->artifacts[i].lvl == 1) { + 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 @@ -287,9 +404,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 9cb24ff..2928faf 100644 --- a/src/skillbar.h +++ b/src/skillbar.h @@ -28,16 +28,25 @@ 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*); bool skillbar_check_skill_activation(SkillBar*, Player*); @@ -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/update_data.h b/src/update_data.h index ca15601..a0c00d9 100644 --- a/src/update_data.h +++ b/src/update_data.h @@ -23,12 +23,14 @@ #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; From c390c024f6bed8a1abbec40a320e1fc39f2f5468 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Tue, 21 Aug 2018 12:56:02 +0200 Subject: [PATCH 06/20] Fixes #40, Prevent adjecant traps before lvl 4 Made the simplest solution possible here. An A* start algo to check that we didn't block paths seemed overkill. --- data/trapgen.lua | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) 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 From dbc36aab9f252b915d76d3bf82f1743abb8bacb9 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Tue, 21 Aug 2018 15:44:12 +0200 Subject: [PATCH 07/20] Fixes #41, Minimap --- src/defines.h | 11 ++++++----- src/gui.c | 37 +++++++++++++++++++++++++++++++++---- src/gui.h | 6 +++++- src/main.c | 15 +++++++++++---- src/map.c | 7 +++++-- src/map.h | 1 + 6 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/defines.h b/src/defines.h index 41e791f..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 } diff --git a/src/gui.c b/src/gui.c index 479e7fb..b70ec5e 100644 --- a/src/gui.c +++ b/src/gui.c @@ -163,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_util_create_frame_sprite(RIGHT_GUI_WIDTH/16, - RIGHT_GUI_HEIGHT/16, + 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* @@ -393,7 +397,7 @@ gui_update_player_stats(Gui *gui, Player *player, Map *map, SDL_Renderer *render 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; @@ -417,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, ...) { @@ -578,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 e70f563..f5a0d09 100644 --- a/src/gui.h +++ b/src/gui.h @@ -47,7 +47,8 @@ typedef struct Gui { 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]; @@ -64,6 +65,9 @@ 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*); diff --git a/src/main.c b/src/main.c index 86c3e94..80c5f00 100644 --- a/src/main.c +++ b/src/main.c @@ -152,7 +152,8 @@ 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; @@ -253,8 +254,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, @@ -707,9 +711,12 @@ run_game_render(void) actiontextbuilder_render(gCamera); gui_render_event_message(gGui, gCamera); - SDL_RenderSetViewport(gRenderer, &rightGuiViewport); + 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); diff --git a/src/map.c b/src/map.c index e19044c..a9d166f 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; @@ -388,6 +389,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..d4f2c5a 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 { From 43f021a9979d0c5c27ac2d4d324ebb7ae53846c7 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Tue, 21 Aug 2018 16:18:11 +0200 Subject: [PATCH 08/20] Created an FPS counter for debug mode - Also moves the "sword pointer" to debug mode only. --- src/main.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/src/main.c b/src/main.c index 80c5f00..3f23105 100644 --- a/src/main.c +++ b/src/main.c @@ -135,7 +135,6 @@ 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; @@ -148,6 +147,7 @@ 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; @@ -155,9 +155,13 @@ static SDL_Rect bottomGuiViewport; 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 }; @@ -279,11 +283,20 @@ initGame(void) gGui = gui_create(gCamera); 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; } @@ -400,6 +413,7 @@ createInGameGameOverMenu(void) { struct MENU_ITEM menu_items[] = { { "NEW GAME", startGame }, + { "HOW TO PLAY", showHowToTooltip }, { "MAIN MENU", goToMainMenu }, { "QUIT", exitGame }, }; @@ -408,7 +422,7 @@ createInGameGameOverMenu(void) menu_destroy(inGameMenu); inGameMenu = NULL; } - createMenu(&inGameMenu, menu_items, 3); + createMenu(&inGameMenu, menu_items, 4); } static void @@ -733,7 +747,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); } @@ -800,7 +817,10 @@ 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); } @@ -812,7 +832,16 @@ void run(void) 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) { @@ -820,7 +849,9 @@ void run(void) quit = handle_events(); handle_main_input(); +#ifdef DEBUG pointer_handle_input(gPointer, &input); +#endif // DEBUG switch (gGameState) { case PLAYING: @@ -852,9 +883,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 @@ -880,7 +925,10 @@ void close(void) 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(); From bec9eb429df1e9b19b298f1a3dcd61d10d43d408 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Tue, 21 Aug 2018 16:21:22 +0200 Subject: [PATCH 09/20] Fixed a typo in skill tooltip --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 3f23105..5b0be05 100644 --- a/src/main.c +++ b/src/main.c @@ -83,7 +83,7 @@ static char *skills_tooltip[] = { " Skills are listed in the bar below the game screen.", "", "", - " SKILL INFO: CTRL + ", + " SKILL INFO: SHIFT + ", " Where is the skill number (1-5)", "", " DISABLE TOOLTIPS: CTRL + D", From 8c25693d6d5f4be2594844993ae8c1a52d930e89 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Tue, 21 Aug 2018 16:32:26 +0200 Subject: [PATCH 10/20] Moves sqlite3 files to separate dir Only intention is to separate my code from 3rd party code. --- CMakeLists.txt | 5 +++-- {src => sqlite3}/sqlite3.c | 0 {src => sqlite3}/sqlite3.h | 0 3 files changed, 3 insertions(+), 2 deletions(-) rename {src => sqlite3}/sqlite3.c (100%) rename {src => sqlite3}/sqlite3.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 86099c8..5546483 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 @@ -182,7 +183,7 @@ add_executable(breakhack ) # 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 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 From 8bf9329b8ca5ea9b88cf858371fae63e841648f0 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Tue, 21 Aug 2018 17:00:33 +0200 Subject: [PATCH 11/20] Version bump and badges --- CMakeLists.txt | 6 +++--- README.md | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5546483..fbf024a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,9 +6,9 @@ project(breakhack C) set(breakhack_GAME_TITLE "BreakHack") set(breakhack_MAJOR_VERSION 0) -set(breakhack_MINOR_VERSION 1) -set(breakhack_PATCH_VERSION 13) -set(breakhack_RELEASE_TYPE "(early access)") +set(breakhack_MINOR_VERSION 2) +set(breakhack_PATCH_VERSION 1) +set(breakhack_RELEASE_TYPE "(beta)") include(FindLua) include(FindPhysFS) diff --git a/README.md b/README.md index 42c69bb..db52a82 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,11 @@ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/fc02d56fa7194e61b2c7d260fd2e4186)](https://www.codacy.com/app/LiquidityC/breakhack?utm_source=github.com&utm_medium=referral&utm_content=LiquidityC/breakhack&utm_campaign=Badge_Grade) [![CodeFactor](https://www.codefactor.io/repository/github/liquidityc/breakhack/badge/master)](https://www.codefactor.io/repository/github/liquidityc/breakhack/overview/master) +[![GitHub issues](https://img.shields.io/github/issues/liquidityc/breakhack.svg)](https://github.com/liquidityc/breakhack/issues) +[![GitHub closed issues](https://img.shields.io/github/issues-closed/liquidityc/breakhack.svg)](https://github.com/liquidityc/breakhack/issues) +![GitHub (pre-)release](https://img.shields.io/github/release/liquidityc/breakhack/all.svg) +[![Github All Releases](https://img.shields.io/github/downloads/liquidityc/breakhack/total.svg)](https://github.com/liquidityc/breakhack/releases) +[![HitCount](http://hits.dwyl.io/liquidityc/breakhack.svg)](http://hits.dwyl.io/liquidityc/breakhack) Something in the ways of a roguelike ================================ From ffe1736792f0a0df3f0be30413847e85e670d3f3 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Tue, 21 Aug 2018 22:27:45 +0200 Subject: [PATCH 12/20] Minor buggfixes and typos - Mention level in "new skill tooltip" - Fix a msvc compiler warning - Prevent level 2 artifact first pickup from bugging rendering --- src/main.c | 2 +- src/skillbar.c | 5 ++--- src/tooltip.c | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.c b/src/main.c index 5b0be05..547e848 100644 --- a/src/main.c +++ b/src/main.c @@ -78,7 +78,7 @@ static char *artifacts_tooltip[] = { static char *skills_tooltip[] = { "CONGRATULATIONS!", "", - " You have aquired a new skill!", + " You have aquired a new level and a new skill!", "", " Skills are listed in the bar below the game screen.", "", diff --git a/src/skillbar.c b/src/skillbar.c index cd01a9c..9f91a72 100644 --- a/src/skillbar.c +++ b/src/skillbar.c @@ -374,8 +374,6 @@ skillbar_update(SkillBar *bar, UpdateData *data) if (data->player->equipment.artifacts[i].level == bar->artifacts[i].lvl) continue; - bar->artifacts[i].lvl = data->player->equipment.artifacts[i].level; - char lvl[4]; m_sprintf(lvl, 4, "%u", bar->artifacts[i].lvl); @@ -383,13 +381,14 @@ skillbar_update(SkillBar *bar, UpdateData *data) lvl, C_BLUE, C_WHITE, data->cam->renderer); // Only update position if this is the first pickup - if (bar->artifacts[i].lvl == 1) { + if (bar->artifacts[i].lvl == 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; } + bar->artifacts[i].lvl = data->player->equipment.artifacts[i].level; } } diff --git a/src/tooltip.c b/src/tooltip.c index 4c46740..0957426 100644 --- a/src/tooltip.c +++ b/src/tooltip.c @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include "tooltip.h" #include "gui_util.h" #include "defines.h" From f1b0045829d5761bc3687454d01fb51d77b41fee Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Wed, 22 Aug 2018 11:05:12 +0200 Subject: [PATCH 13/20] Introduces the "orc levels" Also fixes some minor buggs. --- data/monstergen.lua | 56 ++++++++++++++++++++++++++++++++------------- src/main.c | 6 +++-- src/skillbar.c | 8 ++++--- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/data/monstergen.lua b/data/monstergen.lua index bd28aba..6331efe 100644 --- a/data/monstergen.lua +++ b/data/monstergen.lua @@ -84,6 +84,13 @@ local stats = { def = 0, speed = 1 }, + orc = { + hp = 20, + dmg = 2, + atk = 0, + def = 0, + speed = 1 + }, boss = { hp = 60, dmg = 4, @@ -134,6 +141,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 }, @@ -188,10 +205,25 @@ 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, 80, 4*16, "An Orc Pyro", behaviour.fire_demon }, +} +for i=1,#orcs do + orcs[i] = concat({ texturePaths.humanoid0, texturePaths.humanoid1 }, orcs[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, 32, 23*16, "The Cleric", behaviour.sentinel, true }, } bosses[1] = concat({ texturePaths.dog0, texturePaths.dog1 }, bosses[1]) +bosses[2] = concat({ texturePaths.humanoid0, texturePaths.humanoid1 }, bosses[2]) local platino = { { @@ -224,29 +256,21 @@ 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 > 5) then + elseif (CURRENT_LEVEL > 15) then enemies = {} enemies = concat(enemies, demon) - enemies = concat(enemies, undead) - enemies = concat(enemies, reptile) - enemies = concat(enemies, misc) - elseif (CURRENT_LEVEL > 3) then + elseif (CURRENT_LEVEL > 10) then enemies = {} - enemies = concat(enemies, undead) - enemies = concat(enemies, reptile) - enemies = concat(enemies, misc) - enemies = concat(enemies, dogs) - elseif (CURRENT_LEVEL > 2) then + enemies = concat(enemies, demon) + elseif (CURRENT_LEVEL > 5) 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, reptile) + enemies = concat(enemies, avian) enemies = concat(enemies, misc) enemies = concat(enemies, dogs) end diff --git a/src/main.c b/src/main.c index 547e848..1aff808 100644 --- a/src/main.c +++ b/src/main.c @@ -663,10 +663,12 @@ run_game_update(void) skillActivated = skillbar_check_skill_activation(gSkillBar, gPlayer); } - if (skillActivated && settings_get()->tooltips_enabled && playerLevel < 5) { + + Settings *settings = settings_get(); + if (skillActivated && settings->tooltips_enabled && playerLevel < 5) { gGui->activeTooltip = new_skill_tooltip; } - if (!artifactTooltipShown && gPlayer->equipment.hasArtifacts) { + if (!artifactTooltipShown && gPlayer->equipment.hasArtifacts && settings->tooltips_enabled) { artifactTooltipShown = true; gGui->activeTooltip = new_artifact_tooltip; } diff --git a/src/skillbar.c b/src/skillbar.c index 9f91a72..167a3a5 100644 --- a/src/skillbar.c +++ b/src/skillbar.c @@ -374,21 +374,23 @@ skillbar_update(SkillBar *bar, UpdateData *data) 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_BLUE, C_WHITE, data->cam->renderer); + lvl, C_PURPLE, C_WHITE, data->cam->renderer); // Only update position if this is the first pickup - if (bar->artifacts[i].lvl == 0) { + 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; } - bar->artifacts[i].lvl = data->player->equipment.artifacts[i].level; } } From 6326a641117a23f8ad4e339e5a1eeec7b3da402b Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Wed, 22 Aug 2018 13:13:54 +0200 Subject: [PATCH 14/20] Begins #42 Add boss 2 & 3 - Added "The cleric" for level 10. --- data/monstergen.lua | 24 +++++++++-------- src/main.c | 9 ++++--- src/monster.c | 62 +++++++++++++++++++++++++++++++++++++------ src/monster.h | 3 ++- src/object.c | 15 +++++++++++ src/object.h | 3 +++ src/particle_engine.c | 7 ++--- src/player.c | 2 +- src/stats.c | 5 ++-- 9 files changed, 101 insertions(+), 29 deletions(-) diff --git a/data/monstergen.lua b/data/monstergen.lua index 6331efe..b0f49d5 100644 --- a/data/monstergen.lua +++ b/data/monstergen.lua @@ -45,7 +45,8 @@ local behaviour = { guerilla = 3, coward = 4, sentinel = 5, - fire_demon = 6 + fire_demon = 6, + sorcerer = 7 } local stats = { @@ -93,17 +94,17 @@ local stats = { }, 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 = 30, + dmg = 0, + atk = 0, + def = 0, + speed = 2 } } @@ -212,7 +213,7 @@ local orcs = { { 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, 80, 4*16, "An Orc Pyro", behaviour.fire_demon }, + { 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]) @@ -220,7 +221,7 @@ end local bosses = { { stats.boss, 16, 5*16, "The Hell Hound", behaviour.fire_demon, true }, - { stats.boss, 32, 23*16, "The Cleric", behaviour.sentinel, true }, + { stats.boss, 16, 23*16, "The Cleric", behaviour.sorcerer, true }, } bosses[1] = concat({ texturePaths.dog0, texturePaths.dog1 }, bosses[1]) bosses[2] = concat({ texturePaths.humanoid0, texturePaths.humanoid1 }, bosses[2]) @@ -233,7 +234,8 @@ local platino = { 48, 12*16, "Platino", - behaviour.sentinel + behaviour.sentinel, + true } } @@ -303,7 +305,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 diff --git a/src/main.c b/src/main.c index 1aff808..d9a3102 100644 --- a/src/main.c +++ b/src/main.c @@ -304,7 +304,7 @@ static void startGame(void *unused) { UNUSED(unused); - cLevel = 1; + cLevel = 5; gGameState = PLAYING; if (gPlayer) player_destroy(gPlayer); @@ -626,10 +626,13 @@ check_next_level(void) if (tile->levelExit) { mixer_play_effect(NEXT_LEVEL); ++cLevel; - if (cLevel % 5 == 0) + if (cLevel % 5 == 0) { + gui_log("You sense something powerful in the vicinity"); + gui_event_message("Something powerful lurks in the dark"); mixer_play_music(BOSS_MUSIC0); - else + } else { mixer_play_music(GAME_MUSIC0 + get_random(2)); + } resetGame(); } } diff --git a/src/monster.c b/src/monster.c index 6d27f87..264a3d9 100644 --- a/src/monster.c +++ b/src/monster.c @@ -106,6 +106,7 @@ monster_behaviour_check_post_hit(Monster *m) monster_state_change(m, SCARED); break; case GUERILLA: + case SORCERER: case FIRE_DEMON: break; default: @@ -119,6 +120,7 @@ monster_behaviour_check_post_attack(Monster *m) { switch (m->behaviour) { case GUERILLA: + case SORCERER: case FIRE_DEMON: monster_state_change(m, SCARED); break; @@ -154,6 +156,7 @@ monster_behaviour_check(Monster *m, RoomMatrix *rm) { switch (m->behaviour) { case GUERILLA: + case SORCERER: case FIRE_DEMON: if (m->state.stepsSinceChange > 8 && m->state.current == SCARED) { @@ -379,6 +382,33 @@ monster_coward_walk(Monster *m, RoomMatrix *rm) } } +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)); + + 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); + r->monster->stats.hp -= 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); + r->player->stats.hp -= dmg; + gui_log("You take %d damage from the explosion", dmg); + } + } + } +} + bool monster_move(Monster *m, RoomMatrix *rm, Map *map) { @@ -395,10 +425,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; @@ -442,12 +480,19 @@ monster_move(Monster *m, RoomMatrix *rm, Map *map) } - 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++; @@ -542,7 +587,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); @@ -613,6 +658,7 @@ monster_set_behaviour(Monster *m, MonsterBehaviour behaviour) switch (behaviour) { case HOSTILE: case GUERILLA: + case SORCERER: case COWARD: case FIRE_DEMON: m->state.current = AGRESSIVE; diff --git a/src/monster.h b/src/monster.h index 91ebd1b..e99a0fb 100644 --- a/src/monster.h +++ b/src/monster.h @@ -35,7 +35,8 @@ typedef enum { GUERILLA, COWARD, SENTINEL, - FIRE_DEMON + FIRE_DEMON, + SORCERER } MonsterBehaviour; typedef enum { 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 f776bd4..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); } diff --git a/src/player.c b/src/player.c index 41f8558..1e9f6e3 100644 --- a/src/player.c +++ b/src/player.c @@ -531,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); diff --git a/src/stats.c b/src/stats.c index f713abc..8a5e393 100644 --- a/src/stats.c +++ b/src/stats.c @@ -67,10 +67,11 @@ 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) From 8c261fd59f1f489461c31ad5a3f3b7567c807a34 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Wed, 22 Aug 2018 14:02:55 +0200 Subject: [PATCH 15/20] Added the undead monsters for level > 10 - Fixed a double damage bug - Prevent player from getting killed when in DEBUG mode - This can cause really weird behaviour if you fall into a pit. --- data/monstergen.lua | 35 ++++++++++++++++++++++++++++++----- src/main.c | 5 ++++- src/monster.c | 6 ++---- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/data/monstergen.lua b/data/monstergen.lua index b0f49d5..30951f3 100644 --- a/data/monstergen.lua +++ b/data/monstergen.lua @@ -161,13 +161,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 @@ -260,17 +280,21 @@ if(CURRENT_LEVEL > 0) then enemies = concat(enemies, misc) elseif (CURRENT_LEVEL > 15) then enemies = {} - enemies = concat(enemies, demon) + enemies = concat(enemies, undead) + enemies = concat(enemies, orcs) + enemies = concat(enemies, reptile) + enemies = concat(enemies, avian) elseif (CURRENT_LEVEL > 10) then enemies = {} - enemies = concat(enemies, demon) + enemies = concat(enemies, undead) + enemies = concat(enemies, avian) elseif (CURRENT_LEVEL > 5) then enemies = {} 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) @@ -335,3 +359,4 @@ function module.load_monsters(map, monsters) end return module + diff --git a/src/main.c b/src/main.c index d9a3102..412eef4 100644 --- a/src/main.c +++ b/src/main.c @@ -304,7 +304,7 @@ static void startGame(void *unused) { UNUSED(unused); - cLevel = 5; + cLevel = 1; gGameState = PLAYING; if (gPlayer) player_destroy(gPlayer); @@ -606,6 +606,9 @@ 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; } diff --git a/src/monster.c b/src/monster.c index 264a3d9..b14f27d 100644 --- a/src/monster.c +++ b/src/monster.c @@ -158,7 +158,7 @@ monster_behaviour_check(Monster *m, RoomMatrix *rm) 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); } @@ -397,12 +397,10 @@ sorcerer_blast(Monster *m, RoomMatrix *rm) if (r->monster) { int dmg = stats_fight(&m->stats, &r->monster->stats); monster_hit(r->monster, dmg); - r->monster->stats.hp -= dmg; gui_log("%s takes %d damage from the explosion", r->monster->label, dmg); - }else if (r->player) { + } else if (r->player) { int dmg = stats_fight(&m->stats, &r->player->stats); player_hit(r->player, dmg); - r->player->stats.hp -= dmg; gui_log("You take %d damage from the explosion", dmg); } } From f67aab0b37d877b9fb2a60b6b7f032e0b1d39d8d Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Wed, 22 Aug 2018 20:54:39 +0200 Subject: [PATCH 16/20] Completes #43 Add win state Game now ends if you reach depth level 20 Also fixed: - Funky bug with stats with 0 dmg - Wonky speed = 2 on monsters behaviour - Some other minor stuff --- CMakeLists.txt | 2 +- data/monstergen.lua | 8 +-- src/gamestate.h | 1 + src/main.c | 137 +++++++++++++++++++++++++++++++++----------- src/map.c | 8 ++- src/monster.c | 12 +++- src/monster.h | 3 + src/roommatrix.c | 2 + src/stats.c | 4 +- 9 files changed, 132 insertions(+), 45 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fbf024a..4c18097 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -244,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/data/monstergen.lua b/data/monstergen.lua index 30951f3..542883f 100644 --- a/data/monstergen.lua +++ b/data/monstergen.lua @@ -100,11 +100,11 @@ local stats = { speed = 1 }, platino = { - hp = 30, - dmg = 0, + hp = 90, + dmg = 1, atk = 0, def = 0, - speed = 2 + speed = 1 } } @@ -282,8 +282,6 @@ if(CURRENT_LEVEL > 0) then enemies = {} enemies = concat(enemies, undead) enemies = concat(enemies, orcs) - enemies = concat(enemies, reptile) - enemies = concat(enemies, avian) elseif (CURRENT_LEVEL > 10) then enemies = {} enemies = concat(enemies, undead) 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/main.c b/src/main.c index 412eef4..e9d8c35 100644 --- a/src/main.c +++ b/src/main.c @@ -332,10 +332,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; } @@ -413,7 +417,6 @@ createInGameGameOverMenu(void) { struct MENU_ITEM menu_items[] = { { "NEW GAME", startGame }, - { "HOW TO PLAY", showHowToTooltip }, { "MAIN MENU", goToMainMenu }, { "QUIT", exitGame }, }; @@ -422,7 +425,7 @@ createInGameGameOverMenu(void) menu_destroy(inGameMenu); inGameMenu = NULL; } - createMenu(&inGameMenu, menu_items, 4); + createMenu(&inGameMenu, menu_items, 3); } static void @@ -486,8 +489,10 @@ resetGame(void) screen_destroy(scoreScreen); scoreScreen = NULL; - if (!inGameMenu) - initInGameMenu(); + if (inGameMenu) + menu_destroy(inGameMenu); + inGameMenu = NULL; + initInGameMenu(); if (gMap) map_destroy(gMap); @@ -536,7 +541,8 @@ handle_main_input(void) { if (gGameState == PLAYING || gGameState == IN_GAME_MENU - || gGameState == GAME_OVER) + || gGameState == GAME_OVER + || gGameState == COMPLETED) { if (!gGui->activeTooltip && input_key_is_pressed(&input, KEY_ESC)) toggleInGameMenu(NULL); @@ -615,9 +621,24 @@ is_player_dead(void) 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); @@ -629,14 +650,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 if (cLevel % 5 == 0) { gui_log("You sense something powerful in the vicinity"); - gui_event_message("Something powerful lurks in the dark"); mixer_play_music(BOSS_MUSIC0); } else { mixer_play_music(GAME_MUSIC0 + get_random(2)); } - resetGame(); + + if (cLevel < 20) { + resetGame(); + } } } @@ -679,7 +704,6 @@ run_game_update(void) gGui->activeTooltip = new_artifact_tooltip; } - map_clear_expired_entities(gMap, gPlayer); if (gGameState == PLAYING && currentTurn == PLAYER) player_update(&updateData); @@ -698,25 +722,62 @@ run_game_update(void) currentTurn = MONSTER; player_reset_steps(gPlayer); map_on_new_turn(gMap); + map_clear_expired_entities(gMap, gPlayer); repopulate_roommatrix(); } } else if (currentTurn == MONSTER) { if (map_move_monsters(gMap, gRoomMatrix)) { currentTurn = PLAYER; + map_clear_expired_entities(gMap, gPlayer); repopulate_roommatrix(); } } } static void -run_game_render(void) +render_gui(void) { - SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0); - SDL_RenderClear(gRenderer); + 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()) { @@ -732,20 +793,17 @@ run_game_render(void) roommatrix_render_lightmap(gRoomMatrix, gCamera); actiontextbuilder_render(gCamera); gui_render_event_message(gGui, gCamera); +} - SDL_RenderSetViewport(gRenderer, &statsGuiViewport); - gui_render_panel(gGui, gCamera); +static void +run_game_render(void) +{ + SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0); + SDL_RenderClear(gRenderer); - SDL_RenderSetViewport(gRenderer, &minimapViewport); - gui_render_minimap(gGui, gMap, gCamera); + render_game(); + render_gui(); - 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); @@ -768,18 +826,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"); - } + end_game_details(); mixer_play_effect(SPLAT); gGameState = GAME_OVER; createInGameGameOverMenu(); @@ -788,6 +849,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 @@ -833,8 +903,8 @@ run_menu(void) SDL_RenderPresent(gRenderer); } -static -void run(void) +static void +run(void) { static int oldTime = 0; static int currentTime = 0; @@ -865,6 +935,7 @@ void run(void) case PLAYING: case IN_GAME_MENU: case GAME_OVER: + case COMPLETED: run_game(); break; case MENU: diff --git a/src/map.c b/src/map.c index a9d166f..b67d6ec 100644 --- a/src/map.c +++ b/src/map.c @@ -206,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; } diff --git a/src/monster.c b/src/monster.c index b14f27d..82460d8 100644 --- a/src/monster.c +++ b/src/monster.c @@ -472,7 +472,10 @@ 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); } } @@ -498,13 +501,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) { diff --git a/src/monster.h b/src/monster.h index e99a0fb..e35d09c 100644 --- a/src/monster.h +++ b/src/monster.h @@ -109,6 +109,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/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/stats.c b/src/stats.c index 8a5e393..1da5b88 100644 --- a/src/stats.c +++ b/src/stats.c @@ -75,9 +75,9 @@ stats_fight(Stats *attacker, Stats *defender) 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; From 449cc362a0a56fdc8d8869f13c2556816da15672 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Thu, 23 Aug 2018 08:41:47 +0200 Subject: [PATCH 17/20] Fixes some minor buggs - Monsters now drop loot as soon as they die (not after the dagger has stopped moving) - Artifacts keep correct alignment even after a level switch. --- src/main.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main.c b/src/main.c index e9d8c35..00b776c 100644 --- a/src/main.c +++ b/src/main.c @@ -311,6 +311,7 @@ startGame(void *unused) gPlayer = player_create(WARRIOR, gCamera); mixer_play_music(GAME_MUSIC0 + get_random(2)); resetGame(); + skillbar_reset(gSkillBar); gui_clear_message_log(); gui_log("The Dungeon Crawl begins!"); gui_event_message("Welcome to the dungeon!"); @@ -497,8 +498,6 @@ resetGame(void) if (gMap) map_destroy(gMap); - skillbar_reset(gSkillBar); - particle_engine_clear(); info("Building new map"); @@ -717,21 +716,24 @@ 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); - map_clear_expired_entities(gMap, gPlayer); - repopulate_roommatrix(); + turnSwitch = true; } } else if (currentTurn == MONSTER) { if (map_move_monsters(gMap, gRoomMatrix)) { currentTurn = PLAYER; - map_clear_expired_entities(gMap, gPlayer); - repopulate_roommatrix(); + turnSwitch = true; } } + + map_clear_expired_entities(gMap, gPlayer); + if (turnSwitch) + repopulate_roommatrix(); } static void From 52913af237991ff751a4bea4602b200c1628f770 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Thu, 23 Aug 2018 09:11:13 +0200 Subject: [PATCH 18/20] Completes #42 Add boss 2 & 3 - Adds "The Shadow" - Adds the "assassin" behaviour - Adds some assassins to levels > 15 --- data/monstergen.lua | 15 +++++++- src/monster.c | 86 ++++++++++++++++++++++++++++++++------------- src/monster.h | 3 +- 3 files changed, 77 insertions(+), 27 deletions(-) diff --git a/data/monstergen.lua b/data/monstergen.lua index 542883f..719d705 100644 --- a/data/monstergen.lua +++ b/data/monstergen.lua @@ -46,7 +46,8 @@ local behaviour = { coward = 4, sentinel = 5, fire_demon = 6, - sorcerer = 7 + sorcerer = 7, + assassin = 8 } local stats = { @@ -239,12 +240,23 @@ 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, 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 = { { @@ -282,6 +294,7 @@ if(CURRENT_LEVEL > 0) then enemies = {} enemies = concat(enemies, undead) enemies = concat(enemies, orcs) + enemies = concat(enemies, assassins) elseif (CURRENT_LEVEL > 10) then enemies = {} enemies = concat(enemies, undead) diff --git a/src/monster.c b/src/monster.c index 82460d8..936a158 100644 --- a/src/monster.c +++ b/src/monster.c @@ -106,6 +106,7 @@ monster_behaviour_check_post_hit(Monster *m) monster_state_change(m, SCARED); break; case GUERILLA: + case ASSASSIN: case SORCERER: case FIRE_DEMON: break; @@ -115,6 +116,48 @@ 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) { @@ -124,6 +167,10 @@ monster_behaviour_check_post_attack(Monster *m) case FIRE_DEMON: monster_state_change(m, SCARED); break; + case ASSASSIN: + assassin_cloak_effect(m, true); + monster_state_change(m, SCARED); + break; default: break; } @@ -163,6 +210,13 @@ monster_behaviour_check(Monster *m, RoomMatrix *rm) 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; @@ -382,31 +436,6 @@ monster_coward_walk(Monster *m, RoomMatrix *rm) } } -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)); - - 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); - } - } - } -} - bool monster_move(Monster *m, RoomMatrix *rm, Map *map) { @@ -644,6 +673,9 @@ 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); } @@ -653,6 +685,9 @@ monster_render_top_layer(Monster *m, Camera *cam) if (m->stats.hp <= 0) return; + if (m->behaviour == ASSASSIN && m->state.current != AGRESSIVE) + return; + if (m->stateIndicator.displayCount != 0) sprite_render(m->stateIndicator.sprite, cam); } @@ -664,6 +699,7 @@ monster_set_behaviour(Monster *m, MonsterBehaviour behaviour) switch (behaviour) { case HOSTILE: case GUERILLA: + case ASSASSIN: case SORCERER: case COWARD: case FIRE_DEMON: diff --git a/src/monster.h b/src/monster.h index e35d09c..da201fa 100644 --- a/src/monster.h +++ b/src/monster.h @@ -36,7 +36,8 @@ typedef enum { COWARD, SENTINEL, FIRE_DEMON, - SORCERER + SORCERER, + ASSASSIN } MonsterBehaviour; typedef enum { From f5a88fc10ea2d7d25d4c7cd8940f974c449500d1 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Thu, 23 Aug 2018 09:41:10 +0200 Subject: [PATCH 19/20] Introduces texture blending - Makes indicator sprites transparent when the player is under them --- src/main.c | 4 ++-- src/map.c | 4 ++-- src/map.h | 2 +- src/monster.c | 10 +++++++++- src/monster.h | 2 +- src/sprite.c | 18 ++++++++++++++++++ src/sprite.h | 6 ++++++ src/texture.c | 14 ++++++++++++++ src/texture.h | 8 +++++++- 9 files changed, 60 insertions(+), 8 deletions(-) diff --git a/src/main.c b/src/main.c index 00b776c..a6264db 100644 --- a/src/main.c +++ b/src/main.c @@ -787,7 +787,7 @@ render_game(void) player_render_toplayer(gPlayer, gCamera); } - map_render_top_layer(gMap, gCamera); + map_render_top_layer(gMap, gRoomMatrix, gCamera); if (gPlayer->class == MAGE || gPlayer->class == PALADIN) roommatrix_render_mouse_square(gRoomMatrix, gCamera); @@ -885,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); diff --git a/src/map.c b/src/map.c index b67d6ec..04b45e2 100644 --- a/src/map.c +++ b/src/map.c @@ -358,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); } } diff --git a/src/map.h b/src/map.h index d4f2c5a..f4be227 100644 --- a/src/map.h +++ b/src/map.h @@ -107,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 936a158..8334857 100644 --- a/src/monster.c +++ b/src/monster.c @@ -680,7 +680,7 @@ monster_render(Monster *m, Camera *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; @@ -688,8 +688,16 @@ monster_render_top_layer(Monster *m, Camera *cam) 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 diff --git a/src/monster.h b/src/monster.h index da201fa..5faf665 100644 --- a/src/monster.h +++ b/src/monster.h @@ -87,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); 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/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*); From cc1ae5d99dc0e4bb15b8065aef317e1ee72741ae Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Thu, 23 Aug 2018 09:44:19 +0200 Subject: [PATCH 20/20] Beta 2 releasenotes and version --- CMakeLists.txt | 2 +- build/releasenotes/beta2.txt | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 build/releasenotes/beta2.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c18097..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) 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