diff --git a/CMakeLists.txt b/CMakeLists.txt index b570b82..060bf42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,14 +137,14 @@ if (NOT WIN32) ) endif (NOT WIN32) -if (WIN32) +if (MSVC) set_target_properties(breakhack PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") set_target_properties(breakhack PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE") set_target_properties(breakhack PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE") set_target_properties(breakhack PROPERTIES COMPILE_DEFINITIONS_RELWITHDEBINFO "_CONSOLE") set_target_properties(breakhack PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") set_target_properties(breakhack PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") -endif (WIN32) +endif (MSVC) # TESTS: IF (CHECK_FOUND AND NOT WIN32) @@ -187,7 +187,6 @@ if (NOT CMAKE_BUILD_TYPE MATCHES Debug) ) endif (NOT CMAKE_BUILD_TYPE MATCHES Debug) -SET(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) SET(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT "Release") SET(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ".") SET(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS diff --git a/src/item_builder.c b/src/item_builder.c index 6693d3a..f8f3918 100644 --- a/src/item_builder.c +++ b/src/item_builder.c @@ -95,7 +95,7 @@ static Item * create_treasure(int current_level) { double amt; - char label[50]; + char label[50] = ""; unsigned int highest_treasure; unsigned int value; @@ -109,25 +109,25 @@ create_treasure(int current_level) highest_treasure = GOLD; } - value = get_random(highest_treasure); + value = get_random(highest_treasure) - 1; SDL_Rect clip = CLIP16(0, 0); switch (value) { case COPPER: - m_sprintf(&label[0], 50, "%.0f copper", amt); + m_sprintf(label, 50, "%.0f copper", amt); amt /= 100; break; case SILVER: - m_sprintf(&label[0], 50, "%.0f silver", amt); + m_sprintf(label, 50, "%.0f silver", amt); clip.x = 48; amt /= 10; break; case GOLD: - m_sprintf(&label[0], 50, "%.0f gold", amt); + m_sprintf(label, 50, "%.0f gold", amt); clip.y = 16; break; case PLATINUM: - m_sprintf(&label[0], 50, "%.0f platinum", amt); + m_sprintf(label, 50, "%.0f platinum", amt); clip.x = 48; clip.y = 16; amt *= 10; diff --git a/src/main.c b/src/main.c index 8264ac2..7bbc60e 100644 --- a/src/main.c +++ b/src/main.c @@ -489,7 +489,7 @@ run_game(void) RIGHT_GUI_HEIGHT, &gCamera); SDL_RenderSetViewport(gRenderer, &skillBarViewport); - skillbar_render(gSkillBar, &gCamera); + skillbar_render(gSkillBar, gPlayer, &gCamera); SDL_RenderSetViewport(gRenderer, &bottomGuiViewport); gui_render_log(gGui, BOTTOM_GUI_WIDTH, diff --git a/src/particle_engine.c b/src/particle_engine.c index e1d0ee9..a80bdac 100644 --- a/src/particle_engine.c +++ b/src/particle_engine.c @@ -94,7 +94,7 @@ particle_engine_bloodspray(Position pos, Dimension dim, unsigned int count) } static void -create_explosion(Position pos, Dimension dim, size_t c_count, ...) +create_explosion(Position pos, Dimension dim, unsigned int c_count, ...) { SDL_Color *colors = ec_malloc(c_count * sizeof(SDL_Color)); diff --git a/src/player.c b/src/player.c index ca846fe..baf222e 100644 --- a/src/player.c +++ b/src/player.c @@ -113,15 +113,8 @@ has_collided(Player *player, RoomMatrix *matrix) else gui_log("You missed %s", space->monster->lclabel); - if (space->monster->stats.hp <= 0) { - unsigned int gained_xp = 5 * space->monster->stats.lvl; - player->kills += 1; + player_monster_kill_check(player, space->monster); - mixer_play_effect(DEATH); - gui_log("You killed %s and gained %d xp", - space->monster->lclabel, gained_xp); - player_gain_xp(player, gained_xp); - } } else if (collided) { mixer_play_effect(BONK); gui_log("Ouch! There is something in the way"); @@ -146,6 +139,11 @@ player_step(Player *p) p->steps++; p->missText->pos = p->sprite->pos; p->hitText->pos = p->sprite->pos; + + for (size_t i = 0; i < PLAYER_SKILL_COUNT; ++i) { + if (p->skills[i] != NULL && p->skills[i]->resetCountdown > 0) + p->skills[i]->resetCountdown--; + } } static void @@ -192,8 +190,8 @@ move_down(Player *player, RoomMatrix *matrix) player_step(player); } -static void -sip_health(Player *player) +void +player_sip_health(Player *player) { if (player->potion_sips > 0) { --player->potion_sips; @@ -244,17 +242,92 @@ handle_movement_input(Player *player, RoomMatrix *matrix, SDL_Event *event) } } +static void +check_skill_activation(Player *player, RoomMatrix *matrix, SDL_Event *event) +{ + // TODO(Linus): This could be "smarter" + unsigned int selected = 0; + if (keyboard_press(SDLK_1, event)) { + selected = 1; + } + else if (keyboard_press(SDLK_2, event)) { + selected = 2; + } + else if (keyboard_press(SDLK_3, event)) { + selected = 3; + } + else if (keyboard_press(SDLK_4, event)) { + selected = 4; + } + else if (keyboard_press(SDLK_5, event)) { + selected = 5; + } + + if (selected == 0) + return; + + for (size_t i = 0; i < PLAYER_SKILL_COUNT; ++i) { + if (!player->skills[i]) + continue; + + Skill *skill = player->skills[i]; + skill->active = (selected - 1) == i && !skill->active && skill->resetCountdown == 0; + if (skill->active && skill->instantUse) { + SkillData skillData = { player, matrix, VECTOR2D_NODIR }; + skill->active = false; + skill->use(skill, &skillData); + if (skill->actionRequired) + player_step(player); + skill->resetCountdown = skill->resetTime; + } + } +} + +static bool +check_skill_trigger(Player *player, RoomMatrix *matrix, SDL_Event *event) +{ + int activeSkill = -1; + for (size_t i = 0; i < PLAYER_SKILL_COUNT; ++i) { + if (player->skills[i] && player->skills[i]->active) { + activeSkill = i; + break; + } + } + + if (activeSkill < 0) + return false; + + Vector2d dir; + if (keyboard_direction_press(UP, event)) + dir = VECTOR2D_UP; + else if (keyboard_direction_press(DOWN, event)) + dir = VECTOR2D_DOWN; + else if (keyboard_direction_press(LEFT, event)) + dir = VECTOR2D_LEFT; + else if (keyboard_direction_press(RIGHT, event)) + dir = VECTOR2D_RIGHT; + else + return false; + + SkillData skillData = { player, matrix, dir }; + player->skills[activeSkill]->use(player->skills[activeSkill], &skillData); + player->skills[activeSkill]->active = false; + if (player->skills[activeSkill]->actionRequired) + player_step(player); + player->skills[activeSkill]->resetCountdown = player->skills[activeSkill]->resetTime; + + return true; +} + static void handle_player_input(Player *player, RoomMatrix *matrix, SDL_Event *event) { if (event->type != SDL_KEYDOWN) return; - if (keyboard_mod_press(SDLK_h, KMOD_SHIFT, event)) { - sip_health(player); - } else { + check_skill_activation(player, matrix, event); + if (!check_skill_trigger(player, matrix, event)) handle_movement_input(player, matrix, event); - } } static void @@ -282,7 +355,7 @@ player_create(class_t class, SDL_Renderer *renderer) player->sprite = sprite_create(); player->total_steps = 0; player->steps = 0; - player->xp = 0; + player->xp = 0; player->hits = 0; player->kills = 0; player->misses = 0; @@ -290,6 +363,10 @@ player_create(class_t class, SDL_Renderer *renderer) player->potion_sips = 0; player->class = class; + for (size_t i = 0; i < PLAYER_SKILL_COUNT; ++i) { + player->skills[i] = NULL; + } + char asset[100]; switch (class) { case ENGINEER: @@ -311,9 +388,12 @@ 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); break; } + player->skills[4] = skill_create(SIP_HEALTH); + sprite_load_texture(player->sprite, asset, 0, renderer); player->sprite->pos = (Position) { TILE_DIMENSION, TILE_DIMENSION }; player->sprite->dim = GAME_DIMENSION; @@ -334,6 +414,23 @@ ExperienceData player_get_xp_data(Player *p) return data; } +void +player_monster_kill_check(Player *player, Monster *monster) +{ + if (!monster) + return; + + if (monster->stats.hp <= 0) { + unsigned int gained_xp = 5 * monster->stats.lvl; + player->kills += 1; + + mixer_play_effect(DEATH); + gui_log("You killed %s and gained %d xp", + monster->lclabel, gained_xp); + player_gain_xp(player, gained_xp); + } +} + void player_hit(Player *p, unsigned int dmg) { @@ -393,5 +490,11 @@ player_destroy(Player *player) actiontext_destroy(player->hitText); actiontext_destroy(player->missText); + for (size_t i = 0; i < PLAYER_SKILL_COUNT; ++i) { + if (player->skills[i]) + skill_destroy(player->skills[i]); + player->skills[i] = NULL; + } + free(player); } diff --git a/src/player.h b/src/player.h index b51ed29..051d8c8 100644 --- a/src/player.h +++ b/src/player.h @@ -24,6 +24,9 @@ #include "stats.h" #include "actiontext.h" #include "camera.h" +#include "skill.h" + +#define PLAYER_SKILL_COUNT 5 enum PlayerClass { ENGINEER, MAGE, PALADIN, ROGUE, WARRIOR }; typedef enum PlayerClass class_t; @@ -49,6 +52,7 @@ typedef struct Player_t { double gold; unsigned int potion_sips; class_t class; + Skill *skills[PLAYER_SKILL_COUNT]; void (*handle_event)(struct Player_t*, RoomMatrix*, SDL_Event*); } Player; @@ -58,6 +62,12 @@ player_create(class_t, SDL_Renderer*); ExperienceData player_get_xp_data(Player*); +void +player_monster_kill_check(Player*, Monster*); + +void +player_sip_health(Player*); + void player_hit(Player*, unsigned int dmg); diff --git a/src/random.c b/src/random.c index a888de4..5b4e54d 100644 --- a/src/random.c +++ b/src/random.c @@ -31,4 +31,4 @@ get_random(unsigned int max) } return rand() % (max + 1); -} +} \ No newline at end of file diff --git a/src/skill.c b/src/skill.c index 7caf7f8..d467602 100644 --- a/src/skill.c +++ b/src/skill.c @@ -21,6 +21,13 @@ #include "texturecache.h" #include "skill.h" #include "util.h" +#include "player.h" +#include "roommatrix.h" +#include "stats.h" +#include "monster.h" +#include "mixer.h" +#include "gui.h" +#include "random.h" static Skill * create_default(const char *s_label, Sprite *s) @@ -30,6 +37,9 @@ create_default(const char *s_label, Sprite *s) skill->resetTime = 5; skill->resetCountdown = 0; skill->icon = s; + skill->actionRequired = true; + skill->instantUse = false; + skill->active = false; skill->use = NULL; return skill; } @@ -37,9 +47,29 @@ create_default(const char *s_label, Sprite *s) static bool skill_use_flurry(Skill *skill, SkillData *data) { - Position pos = position_to_matrix_coords(&data->player->sprite->pos); - UNUSED(pos); - UNUSED(skill); + Position playerPos = position_to_matrix_coords(&data->player->sprite->pos); + Position targetPos = playerPos; + targetPos.x += (int) data->direction.x; + targetPos.y += (int) data->direction.y; + + Monster *monster = data->matrix->spaces[targetPos.x][targetPos.y].monster; + if (monster) { + gui_log("You attack %s with a flurry of strikes", monster->lclabel); + for (size_t i = 0; i < 3; ++i) { + unsigned int dmg = stats_fight(&data->player->stats, &monster->stats); + mixer_play_effect((SWING0 - 1) + get_random(3)); + if (dmg > 0) { + gui_log("You hit for %u damage", dmg); + mixer_play_effect(SWORD_HIT); + monster_hit(monster, dmg); + } + } + + } else { + gui_log("You swing at thin air with a flurry of strikes"); + } + player_monster_kill_check(data->player, monster); + return true; } @@ -57,12 +87,37 @@ create_flurry(void) return skill; } +static bool +skill_sip_health(Skill *skill, SkillData *data) +{ + player_sip_health(data->player); + return true; +} + +static Skill * +create_sip_health() +{ + Texture *t = texturecache_add("Items/Potion.png"); + Sprite *s = sprite_create(); + sprite_set_texture(s, t, 0); + s->dim = DEFAULT_DIMENSION; + s->clip = CLIP16(0, 0); + s->fixed = true; + Skill *skill = create_default("Sip health", s); + skill->instantUse = true; + skill->use = skill_sip_health; + skill->resetTime = 0; + return skill; +} + Skill* skill_create(enum SkillType t) { switch (t) { case FLURRY: return create_flurry(); + case SIP_HEALTH: + return create_sip_health(); default: fatal("Unknown SkillType %u", (unsigned int) t); return NULL; diff --git a/src/skill.h b/src/skill.h index 2ac9de5..053cbef 100644 --- a/src/skill.h +++ b/src/skill.h @@ -20,17 +20,20 @@ #define SKILL_H_ #include -#include "player.h" #include "roommatrix.h" #include "sprite.h" #include "vector2d.h" +// Forward declaration +struct Player_t; + enum SkillType { - FLURRY + FLURRY, + SIP_HEALTH }; typedef struct SkillData_t { - Player *player; + struct Player_t *player; RoomMatrix *matrix; Vector2d direction; } SkillData; @@ -40,6 +43,8 @@ typedef struct Skill_t { Sprite *icon; unsigned int resetTime; unsigned int resetCountdown; + bool actionRequired; + bool instantUse; bool active; bool (*use)(struct Skill_t*, SkillData*); } Skill; diff --git a/src/skillbar.c b/src/skillbar.c index 478f597..477fd08 100644 --- a/src/skillbar.c +++ b/src/skillbar.c @@ -34,7 +34,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 < 4; ++i) { + for (unsigned int i = 0; i < 5; ++i) { char buffer[4]; Sprite *s = sprite_create(); s->pos = (Position) { i * 32 + 20, 20 }; @@ -113,11 +113,56 @@ render_activation_indicator(SkillBar *bar, Camera *cam) SDL_RenderDrawRect(cam->renderer, &square); } +static void +render_skills(SkillBar *bar, Player *player, Camera *cam) +{ + static SDL_Rect activeSkillBox = { 0, 0, 32, 32 }; + + for (size_t i = 0; i < PLAYER_SKILL_COUNT; ++i) { + if (!player->skills[i]) + continue; + + Skill *skill = player->skills[i]; + skill->icon->pos = (Position) { 8 + i * 32, 8 }; + sprite_render(skill->icon, cam); + + if (player->skills[i]->active) { + activeSkillBox.x = i * 32; + SDL_SetRenderDrawColor(cam->renderer, 0, 0, 255, 100); + SDL_RenderFillRect(cam->renderer, &activeSkillBox); + } + if (player->skills[i]->resetCountdown > 0) { + activeSkillBox.x = i * 32; + SDL_SetRenderDrawColor(cam->renderer, 255, 0, 0, 100); + SDL_RenderFillRect(cam->renderer, &activeSkillBox); + } + } +} + +static void +render_skill_unavailable(SkillBar *bar, Player *player, Camera *cam) +{ + static SDL_Rect unavailableSkillBox = { 0, 0, 32, 32 }; + + for (size_t i = 0; i < PLAYER_SKILL_COUNT; ++i) { + if (!player->skills[i]) + continue; + + if (player->skills[i]->resetCountdown > 0) { + unavailableSkillBox.x = i * 32; + SDL_SetRenderDrawColor(cam->renderer, 255, 0, 0, 70); + SDL_RenderFillRect(cam->renderer, &unavailableSkillBox); + } + } +} + void -skillbar_render(SkillBar *bar, Camera *cam) +skillbar_render(SkillBar *bar, Player *player, Camera *cam) { render_frame(cam); + render_skills(bar, player, cam); render_sprites(bar, cam); + render_skill_unavailable(bar, player, cam); render_activation_indicator(bar, cam); } @@ -136,6 +181,8 @@ skillbar_handle_event(SkillBar *bar, SDL_Event *event) key = 3; else if (keyboard_press(SDLK_4, event)) key = 4; + else if (keyboard_press(SDLK_5, event)) + key = 5; if (key != 0) { bar->lastActivation = key; diff --git a/src/skillbar.h b/src/skillbar.h index 6319d36..3feade5 100644 --- a/src/skillbar.h +++ b/src/skillbar.h @@ -23,6 +23,7 @@ #include "linkedlist.h" #include "camera.h" #include "timer.h" +#include "player.h" typedef struct SkillBar_t { LinkedList *sprites; @@ -34,7 +35,7 @@ SkillBar * skillbar_create(SDL_Renderer*); void -skillbar_render(SkillBar*, Camera*); +skillbar_render(SkillBar*, Player*, Camera*); void skillbar_handle_event(SkillBar*, SDL_Event*); diff --git a/src/vector2d.h b/src/vector2d.h index a1cf341..92777d3 100644 --- a/src/vector2d.h +++ b/src/vector2d.h @@ -19,6 +19,7 @@ #ifndef VECTOR2D_H_ #define VECTOR2D_H_ +#define VECTOR2D_NODIR (Vector2d) { 0, 0 } #define VECTOR2D_RIGHT (Vector2d) { 1, 0 } #define VECTOR2D_LEFT (Vector2d) { -1, 0 } #define VECTOR2D_UP (Vector2d) { 0, -1 }