diff --git a/assets/Extras/Skills.png b/assets/Extras/Skills.png index 9738d59..71d1f8a 100644 Binary files a/assets/Extras/Skills.png and b/assets/Extras/Skills.png differ diff --git a/src/defines.h b/src/defines.h index 0ceaac1..aabe8a2 100644 --- a/src/defines.h +++ b/src/defines.h @@ -68,6 +68,9 @@ #define C_YELLOW (SDL_Color) { 255, 255, 0, 255 } #define C_BLACK (SDL_Color) { 0, 0, 0, 255 } +#define max(a, b) (a > b ? a : b) +#define min(a, b) (a < b ? a : b) + typedef enum Direction_t { UP, DOWN, LEFT, RIGHT } Direction; diff --git a/src/map_lua.c b/src/map_lua.c index 53fa427..7db049f 100644 --- a/src/map_lua.c +++ b/src/map_lua.c @@ -231,7 +231,7 @@ lua_checkstats(lua_State *L, int index) // Reset the stack lua_pop(L, 6); - Stats stats = { hp, hp, dmg, atk, def, speed, 1 }; + Stats stats = { hp, hp, dmg, atk, def, speed, 1, false, false }; return stats; } diff --git a/src/monster.c b/src/monster.c index 3c5c7c6..e41784d 100644 --- a/src/monster.c +++ b/src/monster.c @@ -51,6 +51,9 @@ monster_set_sprite_clip_for_current_state(Monster *m) case SLEEPING: m->stateIndicator.sprite->clip = CLIP16(16 * 10, 16 * 4); break; + case STUNNED: + m->stateIndicator.sprite->clip = CLIP16(16 * 13, 16 * 3); + break; case SCANNING: m->stateIndicator.sprite->clip = CLIP16(16 * 13, 16 * 4); default: @@ -64,6 +67,7 @@ monster_state_change(Monster *m, StateType newState) if (m->state.current == newState) return; + m->state.last = m->state.current; m->state.current = newState; m->state.stepsSinceChange = 0; @@ -72,12 +76,19 @@ monster_state_change(Monster *m, StateType newState) else m->stateIndicator.displayCount = 5; + if (newState == STUNNED) + m->stats.disadvantage = true; + else if (m->state.last == STUNNED) + m->stats.disadvantage = false; + monster_set_sprite_clip_for_current_state(m); } static void monster_behaviour_check_post_hit(Monster *m) { + if (m->state.current == STUNNED) + return; switch (m->behaviour) { case PACIFIST: case COWARD: @@ -156,7 +167,9 @@ monster_create(void) 0, // atk 0, // def 1, // speed - 1 // lvl + 1, // lvl + false, // advantage + false // disadvantage }; m->label = NULL; @@ -350,6 +363,16 @@ monster_move(Monster *m, RoomMatrix *rm) { Position monsterRoomPos; + if (m->state.current == STUNNED) { + if (m->state.stepsSinceChange < 3) { + m->state.stepsSinceChange += 1; + return true; + } else { + monster_state_change(m, m->state.last); + monster_behaviour_check_post_hit(m); + } + } + monster_behaviour_check(m, rm); monsterRoomPos = position_to_matrix_coords(&m->sprite->pos); @@ -525,6 +548,12 @@ monster_set_behaviour(Monster *m, MonsterBehaviour behaviour) monster_set_sprite_clip_for_current_state(m); } +void +monster_set_stunned(Monster *m) +{ + monster_state_change(m, STUNNED); +} + void monster_destroy(Monster *m) { diff --git a/src/monster.h b/src/monster.h index 6930d5e..bd4bfc2 100644 --- a/src/monster.h +++ b/src/monster.h @@ -43,11 +43,13 @@ typedef enum { SCARED, STATIONARY, SLEEPING, - SCANNING + SCANNING, + STUNNED } StateType; -typedef struct { +typedef struct State { StateType current; + StateType last; unsigned int stepsSinceChange; } State; @@ -94,6 +96,9 @@ monster_drop_loot(Monster*, Map*, Player*); void monster_set_behaviour(Monster *, MonsterBehaviour behaviour); +void +monster_set_stunned(Monster *m); + void monster_destroy(Monster*); diff --git a/src/player.c b/src/player.c index a421032..4397a63 100644 --- a/src/player.c +++ b/src/player.c @@ -35,11 +35,11 @@ #include "actiontextbuilder.h" #include "animation.h" -#define ENGINEER_STATS { 12, 12, 5, 7, 2, 2, 1 } -#define MAGE_STATS { 12, 12, 5, 7, 1, 2, 1 } -#define PALADIN_STATS { 12, 12, 8, 9, 3, 1, 1 } -#define ROGUE_STATS { 12, 12, 5, 7, 1, 2, 1 } -#define WARRIOR_STATS { 12, 12, 8, 9, 3, 1, 1 } +#define ENGINEER_STATS { 12, 12, 5, 7, 2, 2, 1, false, false } +#define MAGE_STATS { 12, 12, 5, 7, 1, 2, 1, false, false } +#define PALADIN_STATS { 12, 12, 8, 9, 3, 1, 1, false, false } +#define ROGUE_STATS { 12, 12, 5, 7, 1, 2, 1, false, false } +#define WARRIOR_STATS { 12, 12, 8, 9, 3, 1, 1, false, false } static void player_levelup(Player *player) @@ -428,8 +428,9 @@ player_create(class_t class, SDL_Renderer *renderer) m_strcpy(asset, 100, "Commissions/Warrior.png"); player->stats = (Stats) WARRIOR_STATS; player->skills[0] = skill_create(FLURRY); - player->skills[1] = skill_create(CHARGE); - player->skills[2] = skill_create(DAGGER_THROW); + player->skills[1] = skill_create(BASH); + player->skills[2] = skill_create(CHARGE); + player->skills[3] = skill_create(DAGGER_THROW); break; } diff --git a/src/roommatrix.c b/src/roommatrix.c index b16807d..d53ee4e 100644 --- a/src/roommatrix.c +++ b/src/roommatrix.c @@ -25,6 +25,7 @@ #include "player.h" #include "item.h" #include "update_data.h" +#include "defines.h" static void roommatrix_reset(RoomMatrix *m) @@ -152,25 +153,6 @@ void roommatrix_populate_from_map(RoomMatrix *rm, Map *m) } } -// TODO(Linus): These should probably be macros -#undef min -#ifndef min -static int -min(int a, int b) -{ - return a > b ? b : a; -} -#endif // min - -#undef max -#ifndef max -static int -max(int a, int b) -{ - return a > b ? a : b; -} -#endif // max - void roommatrix_add_lightsource(RoomMatrix *matrix, Position *pos) { diff --git a/src/skill.c b/src/skill.c index 3806c96..4075205 100644 --- a/src/skill.c +++ b/src/skill.c @@ -177,6 +177,67 @@ create_throw_dagger(void) return skill; } +static bool +skill_bash(Skill *skill, SkillData *data) +{ + 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; + + player_turn(data->player, &data->direction); + + if (!position_in_roommatrix(&targetPos)) { + return false; + } + + animation_run(data->player->swordAnimation); + Monster *monster = data->matrix->spaces[targetPos.x][targetPos.y].monster; + mixer_play_effect(SWING1); + if (monster) { + gui_log("You bash %s with your shield", monster->lclabel); + unsigned int dmg = stats_fight(&data->player->stats, &monster->stats); + if (dmg > 0) { + gui_log("You hit for %u damage", dmg); + if (monster->stats.hp > 0) { + gui_log("%s seems dazed and confused", monster->label); + monster_set_stunned(monster); + } + mixer_play_effect(SWORD_HIT); + data->player->stat_data.hits += 1; + } else { + gui_log("You missed %s", monster->lclabel); + } + monster_hit(monster, dmg); + } else { + gui_log("You bash your shield at nothing"); + } + player_monster_kill_check(data->player, monster); + + return true; +} + +static Skill * +create_bash(void) +{ + Texture *t = texturecache_add("Extras/Skills.png"); + Sprite *s = sprite_create(); + sprite_set_texture(s, t, 0); + s->dim = GAME_DIMENSION; + s->clip = CLIP32(96, 0); + s->fixed = true; + Skill *skill = create_default("Bash", s); + skill->levelcap = 3; + skill->instantUse = false; + skill->resetTime = 2; + skill->available = NULL; + skill->use = skill_bash; + skill->actionRequired = true; + return skill; +} + static bool skill_sip_health_available(Player *player) { @@ -316,6 +377,8 @@ skill_create(enum SkillType t) return create_charge(); case DAGGER_THROW: return create_throw_dagger(); + case BASH: + return create_bash(); default: fatal("Unknown SkillType %u", (unsigned int) t); return NULL; diff --git a/src/skill.h b/src/skill.h index b5421a0..6ad2644 100644 --- a/src/skill.h +++ b/src/skill.h @@ -29,6 +29,7 @@ struct Player_t; enum SkillType { FLURRY, + BASH, CHARGE, DAGGER_THROW, SIP_HEALTH diff --git a/src/stats.c b/src/stats.c index de2801a..263e0f9 100644 --- a/src/stats.c +++ b/src/stats.c @@ -25,6 +25,7 @@ #include "stats.h" #include "random.h" #include "util.h" +#include "defines.h" unsigned int stats_fight(Stats *attacker, Stats *defender) @@ -32,11 +33,23 @@ stats_fight(Stats *attacker, Stats *defender) int atkRoll, defRoll, dmgRoll; bool critical = false; - atkRoll = get_random(19) + 1; + if (attacker->advantage) + atkRoll = max(get_random(19), get_random(19)) + 1; + else if (attacker->disadvantage) + atkRoll = min(get_random(19), get_random(19)) + 1; + else + atkRoll = get_random(19) + 1; + if (atkRoll == 20) critical = true; atkRoll += attacker->atk; - defRoll = (get_random(19) + 1) + defender->def; + + if (defender->advantage) + defRoll = max(get_random(19), get_random(19)) + 1 + defender->def; + else if (defender->disadvantage) + defRoll = min(get_random(19), get_random(19)) + 1 + defender->def; + else + defRoll = get_random(19) + 1 + defender->def; dmgRoll = 0; if (atkRoll >= defRoll) { diff --git a/src/stats.h b/src/stats.h index 53ebea3..9717216 100644 --- a/src/stats.h +++ b/src/stats.h @@ -27,6 +27,8 @@ typedef struct Stats_t { int def; /* Defence rating */ unsigned int speed; /* Speed */ unsigned int lvl; /* Level */ + bool advantage; + bool disadvantage; } Stats; unsigned int