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)
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

View File

@ -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;

View File

@ -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,

View File

@ -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));

View File

@ -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);
}

View File

@ -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);

View File

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

View File

@ -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;

View File

@ -20,17 +20,20 @@
#define SKILL_H_
#include <stdbool.h>
#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;

View File

@ -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;

View File

@ -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*);

View File

@ -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 }