Adds skills, 'flurry' skill and sip_potion skill.

Removes shift-h sipping.
This commit is contained in:
Linus Probert 2018-02-28 22:31:38 +01:00
parent 20cb94b529
commit 2253479532
12 changed files with 257 additions and 36 deletions

View File

@ -137,14 +137,14 @@ if (NOT WIN32)
) )
endif (NOT WIN32) endif (NOT WIN32)
if (WIN32) if (MSVC)
set_target_properties(breakhack PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") set_target_properties(breakhack PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(breakhack PROPERTIES COMPILE_DEFINITIONS_DEBUG "_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 LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
set_target_properties(breakhack PROPERTIES COMPILE_DEFINITIONS_RELWITHDEBINFO "_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_RELEASE "/SUBSYSTEM:WINDOWS")
set_target_properties(breakhack PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") set_target_properties(breakhack PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
endif (WIN32) endif (MSVC)
# TESTS: # TESTS:
IF (CHECK_FOUND AND NOT WIN32) IF (CHECK_FOUND AND NOT WIN32)
@ -187,7 +187,6 @@ if (NOT CMAKE_BUILD_TYPE MATCHES Debug)
) )
endif (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_COMPONENT "Release")
SET(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ".") SET(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ".")
SET(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS SET(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS

View File

@ -95,7 +95,7 @@ static Item *
create_treasure(int current_level) create_treasure(int current_level)
{ {
double amt; double amt;
char label[50]; char label[50] = "";
unsigned int highest_treasure; unsigned int highest_treasure;
unsigned int value; unsigned int value;
@ -109,25 +109,25 @@ create_treasure(int current_level)
highest_treasure = GOLD; highest_treasure = GOLD;
} }
value = get_random(highest_treasure); value = get_random(highest_treasure) - 1;
SDL_Rect clip = CLIP16(0, 0); SDL_Rect clip = CLIP16(0, 0);
switch (value) { switch (value) {
case COPPER: case COPPER:
m_sprintf(&label[0], 50, "%.0f copper", amt); m_sprintf(label, 50, "%.0f copper", amt);
amt /= 100; amt /= 100;
break; break;
case SILVER: case SILVER:
m_sprintf(&label[0], 50, "%.0f silver", amt); m_sprintf(label, 50, "%.0f silver", amt);
clip.x = 48; clip.x = 48;
amt /= 10; amt /= 10;
break; break;
case GOLD: case GOLD:
m_sprintf(&label[0], 50, "%.0f gold", amt); m_sprintf(label, 50, "%.0f gold", amt);
clip.y = 16; clip.y = 16;
break; break;
case PLATINUM: case PLATINUM:
m_sprintf(&label[0], 50, "%.0f platinum", amt); m_sprintf(label, 50, "%.0f platinum", amt);
clip.x = 48; clip.x = 48;
clip.y = 16; clip.y = 16;
amt *= 10; amt *= 10;

View File

@ -489,7 +489,7 @@ run_game(void)
RIGHT_GUI_HEIGHT, &gCamera); RIGHT_GUI_HEIGHT, &gCamera);
SDL_RenderSetViewport(gRenderer, &skillBarViewport); SDL_RenderSetViewport(gRenderer, &skillBarViewport);
skillbar_render(gSkillBar, &gCamera); skillbar_render(gSkillBar, gPlayer, &gCamera);
SDL_RenderSetViewport(gRenderer, &bottomGuiViewport); SDL_RenderSetViewport(gRenderer, &bottomGuiViewport);
gui_render_log(gGui, BOTTOM_GUI_WIDTH, gui_render_log(gGui, BOTTOM_GUI_WIDTH,

View File

@ -94,7 +94,7 @@ particle_engine_bloodspray(Position pos, Dimension dim, unsigned int count)
} }
static void 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)); SDL_Color *colors = ec_malloc(c_count * sizeof(SDL_Color));

View File

@ -113,15 +113,8 @@ has_collided(Player *player, RoomMatrix *matrix)
else else
gui_log("You missed %s", space->monster->lclabel); gui_log("You missed %s", space->monster->lclabel);
if (space->monster->stats.hp <= 0) { player_monster_kill_check(player, space->monster);
unsigned int gained_xp = 5 * space->monster->stats.lvl;
player->kills += 1;
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) { } else if (collided) {
mixer_play_effect(BONK); mixer_play_effect(BONK);
gui_log("Ouch! There is something in the way"); gui_log("Ouch! There is something in the way");
@ -146,6 +139,11 @@ player_step(Player *p)
p->steps++; p->steps++;
p->missText->pos = p->sprite->pos; p->missText->pos = p->sprite->pos;
p->hitText->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 static void
@ -192,8 +190,8 @@ move_down(Player *player, RoomMatrix *matrix)
player_step(player); player_step(player);
} }
static void void
sip_health(Player *player) player_sip_health(Player *player)
{ {
if (player->potion_sips > 0) { if (player->potion_sips > 0) {
--player->potion_sips; --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 static void
handle_player_input(Player *player, RoomMatrix *matrix, SDL_Event *event) handle_player_input(Player *player, RoomMatrix *matrix, SDL_Event *event)
{ {
if (event->type != SDL_KEYDOWN) if (event->type != SDL_KEYDOWN)
return; return;
if (keyboard_mod_press(SDLK_h, KMOD_SHIFT, event)) { check_skill_activation(player, matrix, event);
sip_health(player); if (!check_skill_trigger(player, matrix, event))
} else {
handle_movement_input(player, matrix, event); handle_movement_input(player, matrix, event);
}
} }
static void static void
@ -282,7 +355,7 @@ player_create(class_t class, SDL_Renderer *renderer)
player->sprite = sprite_create(); player->sprite = sprite_create();
player->total_steps = 0; player->total_steps = 0;
player->steps = 0; player->steps = 0;
player->xp = 0; player->xp = 0;
player->hits = 0; player->hits = 0;
player->kills = 0; player->kills = 0;
player->misses = 0; player->misses = 0;
@ -290,6 +363,10 @@ player_create(class_t class, SDL_Renderer *renderer)
player->potion_sips = 0; player->potion_sips = 0;
player->class = class; player->class = class;
for (size_t i = 0; i < PLAYER_SKILL_COUNT; ++i) {
player->skills[i] = NULL;
}
char asset[100]; char asset[100];
switch (class) { switch (class) {
case ENGINEER: case ENGINEER:
@ -311,9 +388,12 @@ player_create(class_t class, SDL_Renderer *renderer)
case WARRIOR: case WARRIOR:
m_strcpy(asset, 100, "Commissions/Warrior.png"); m_strcpy(asset, 100, "Commissions/Warrior.png");
player->stats = (Stats) WARRIOR_STATS; player->stats = (Stats) WARRIOR_STATS;
player->skills[0] = skill_create(FLURRY);
break; break;
} }
player->skills[4] = skill_create(SIP_HEALTH);
sprite_load_texture(player->sprite, asset, 0, renderer); sprite_load_texture(player->sprite, asset, 0, renderer);
player->sprite->pos = (Position) { TILE_DIMENSION, TILE_DIMENSION }; player->sprite->pos = (Position) { TILE_DIMENSION, TILE_DIMENSION };
player->sprite->dim = GAME_DIMENSION; player->sprite->dim = GAME_DIMENSION;
@ -334,6 +414,23 @@ ExperienceData player_get_xp_data(Player *p)
return data; 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 void
player_hit(Player *p, unsigned int dmg) player_hit(Player *p, unsigned int dmg)
{ {
@ -393,5 +490,11 @@ player_destroy(Player *player)
actiontext_destroy(player->hitText); actiontext_destroy(player->hitText);
actiontext_destroy(player->missText); 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); free(player);
} }

View File

@ -24,6 +24,9 @@
#include "stats.h" #include "stats.h"
#include "actiontext.h" #include "actiontext.h"
#include "camera.h" #include "camera.h"
#include "skill.h"
#define PLAYER_SKILL_COUNT 5
enum PlayerClass { ENGINEER, MAGE, PALADIN, ROGUE, WARRIOR }; enum PlayerClass { ENGINEER, MAGE, PALADIN, ROGUE, WARRIOR };
typedef enum PlayerClass class_t; typedef enum PlayerClass class_t;
@ -49,6 +52,7 @@ typedef struct Player_t {
double gold; double gold;
unsigned int potion_sips; unsigned int potion_sips;
class_t class; class_t class;
Skill *skills[PLAYER_SKILL_COUNT];
void (*handle_event)(struct Player_t*, RoomMatrix*, SDL_Event*); void (*handle_event)(struct Player_t*, RoomMatrix*, SDL_Event*);
} Player; } Player;
@ -58,6 +62,12 @@ player_create(class_t, SDL_Renderer*);
ExperienceData ExperienceData
player_get_xp_data(Player*); player_get_xp_data(Player*);
void
player_monster_kill_check(Player*, Monster*);
void
player_sip_health(Player*);
void void
player_hit(Player*, unsigned int dmg); player_hit(Player*, unsigned int dmg);

View File

@ -31,4 +31,4 @@ get_random(unsigned int max)
} }
return rand() % (max + 1); return rand() % (max + 1);
} }

View File

@ -21,6 +21,13 @@
#include "texturecache.h" #include "texturecache.h"
#include "skill.h" #include "skill.h"
#include "util.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 * static Skill *
create_default(const char *s_label, Sprite *s) create_default(const char *s_label, Sprite *s)
@ -30,6 +37,9 @@ create_default(const char *s_label, Sprite *s)
skill->resetTime = 5; skill->resetTime = 5;
skill->resetCountdown = 0; skill->resetCountdown = 0;
skill->icon = s; skill->icon = s;
skill->actionRequired = true;
skill->instantUse = false;
skill->active = false;
skill->use = NULL; skill->use = NULL;
return skill; return skill;
} }
@ -37,9 +47,29 @@ create_default(const char *s_label, Sprite *s)
static bool static bool
skill_use_flurry(Skill *skill, SkillData *data) skill_use_flurry(Skill *skill, SkillData *data)
{ {
Position pos = position_to_matrix_coords(&data->player->sprite->pos); Position playerPos = position_to_matrix_coords(&data->player->sprite->pos);
UNUSED(pos); Position targetPos = playerPos;
UNUSED(skill); 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; return true;
} }
@ -57,12 +87,37 @@ create_flurry(void)
return skill; 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*
skill_create(enum SkillType t) skill_create(enum SkillType t)
{ {
switch (t) { switch (t) {
case FLURRY: case FLURRY:
return create_flurry(); return create_flurry();
case SIP_HEALTH:
return create_sip_health();
default: default:
fatal("Unknown SkillType %u", (unsigned int) t); fatal("Unknown SkillType %u", (unsigned int) t);
return NULL; return NULL;

View File

@ -20,17 +20,20 @@
#define SKILL_H_ #define SKILL_H_
#include <stdbool.h> #include <stdbool.h>
#include "player.h"
#include "roommatrix.h" #include "roommatrix.h"
#include "sprite.h" #include "sprite.h"
#include "vector2d.h" #include "vector2d.h"
// Forward declaration
struct Player_t;
enum SkillType { enum SkillType {
FLURRY FLURRY,
SIP_HEALTH
}; };
typedef struct SkillData_t { typedef struct SkillData_t {
Player *player; struct Player_t *player;
RoomMatrix *matrix; RoomMatrix *matrix;
Vector2d direction; Vector2d direction;
} SkillData; } SkillData;
@ -40,6 +43,8 @@ typedef struct Skill_t {
Sprite *icon; Sprite *icon;
unsigned int resetTime; unsigned int resetTime;
unsigned int resetCountdown; unsigned int resetCountdown;
bool actionRequired;
bool instantUse;
bool active; bool active;
bool (*use)(struct Skill_t*, SkillData*); bool (*use)(struct Skill_t*, SkillData*);
} Skill; } Skill;

View File

@ -34,7 +34,7 @@ load_texture(SkillBar *bar, const char *path, SDL_Renderer *renderer)
t->dim.width = 16; t->dim.width = 16;
t->dim.height = 16; t->dim.height = 16;
for (unsigned int i = 0; i < 4; ++i) { for (unsigned int i = 0; i < 5; ++i) {
char buffer[4]; char buffer[4];
Sprite *s = sprite_create(); Sprite *s = sprite_create();
s->pos = (Position) { i * 32 + 20, 20 }; s->pos = (Position) { i * 32 + 20, 20 };
@ -113,11 +113,56 @@ render_activation_indicator(SkillBar *bar, Camera *cam)
SDL_RenderDrawRect(cam->renderer, &square); 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 void
skillbar_render(SkillBar *bar, Camera *cam) skillbar_render(SkillBar *bar, Player *player, Camera *cam)
{ {
render_frame(cam); render_frame(cam);
render_skills(bar, player, cam);
render_sprites(bar, cam); render_sprites(bar, cam);
render_skill_unavailable(bar, player, cam);
render_activation_indicator(bar, cam); render_activation_indicator(bar, cam);
} }
@ -136,6 +181,8 @@ skillbar_handle_event(SkillBar *bar, SDL_Event *event)
key = 3; key = 3;
else if (keyboard_press(SDLK_4, event)) else if (keyboard_press(SDLK_4, event))
key = 4; key = 4;
else if (keyboard_press(SDLK_5, event))
key = 5;
if (key != 0) { if (key != 0) {
bar->lastActivation = key; bar->lastActivation = key;

View File

@ -23,6 +23,7 @@
#include "linkedlist.h" #include "linkedlist.h"
#include "camera.h" #include "camera.h"
#include "timer.h" #include "timer.h"
#include "player.h"
typedef struct SkillBar_t { typedef struct SkillBar_t {
LinkedList *sprites; LinkedList *sprites;
@ -34,7 +35,7 @@ SkillBar *
skillbar_create(SDL_Renderer*); skillbar_create(SDL_Renderer*);
void void
skillbar_render(SkillBar*, Camera*); skillbar_render(SkillBar*, Player*, Camera*);
void void
skillbar_handle_event(SkillBar*, SDL_Event*); skillbar_handle_event(SkillBar*, SDL_Event*);

View File

@ -19,6 +19,7 @@
#ifndef VECTOR2D_H_ #ifndef VECTOR2D_H_
#define VECTOR2D_H_ #define VECTOR2D_H_
#define VECTOR2D_NODIR (Vector2d) { 0, 0 }
#define VECTOR2D_RIGHT (Vector2d) { 1, 0 } #define VECTOR2D_RIGHT (Vector2d) { 1, 0 }
#define VECTOR2D_LEFT (Vector2d) { -1, 0 } #define VECTOR2D_LEFT (Vector2d) { -1, 0 }
#define VECTOR2D_UP (Vector2d) { 0, -1 } #define VECTOR2D_UP (Vector2d) { 0, -1 }