Completely separates input handling and game logic.

This commit disables mouse support in menus. This needs to be rethought
to be more stable.
This commit is contained in:
Linus Probert 2018-05-20 00:02:39 +02:00
parent 186cc7b514
commit 821cac2fbd
23 changed files with 461 additions and 421 deletions

View File

@ -157,7 +157,7 @@ add_executable(breakhack
src/menu
src/collisions
src/keyboard
src/keyboardinput
src/input
src/mixer
src/io_util
src/physfsrwops
@ -221,15 +221,15 @@ IF (CMOCKA_FOUND AND NOT OSX AND NOT CLANG)
target_link_libraries(test_hashtable ${CMOCKA_LIBRARY})
add_test(test_hashtable test_hashtable)
add_executable(test_keyboardinput test/test_keyboardinput src/keyboardinput src/keyboard)
target_link_libraries(test_keyboardinput
add_executable(test_input test/test_input src/input src/keyboard)
target_link_libraries(test_input
${CMOCKA_LIBRARY}
${SDL2_LIBRARY}
${SDL2MAIN_LIBRARY}
)
set_target_properties(test_keyboardinput PROPERTIES
LINK_FLAGS "-Wl,--wrap,keyboard_direction_press -Wl,--wrap,keyboard_press")
add_test(test_keyboardinput test_keyboardinput)
#set_target_properties(test_input PROPERTIES
#LINK_FLAGS "-Wl,--wrap,keyboard_direction_press -Wl,--wrap,keyboard_press")
add_test(test_input test_input)
ENDIF ()
# LINT:

View File

@ -27,7 +27,7 @@
#include "timer.h"
#include "vector2d.h"
struct UpdateData_t;
struct UpdateData;
typedef struct {
Position pos;
@ -40,7 +40,7 @@ typedef struct {
ActionText* actiontext_create(Sprite*);
void actiontext_update(ActionText*, struct UpdateData_t*);
void actiontext_update(ActionText*, struct UpdateData*);
void actiontext_render(ActionText*, Camera*);

View File

@ -40,20 +40,14 @@ gui_button_check_pointer(GuiButton *button, Pointer *pointer)
}
void
gui_button_handle_event(GuiButton *button, SDL_Event *event)
gui_button_update(GuiButton *button, Input *input)
{
if (event->type == SDL_MOUSEBUTTONDOWN) {
Position p = { input->mouseX, input->mouseY };
button->hover = position_in_rect(&p, &button->area);
if (event->button.button != SDL_BUTTON_LEFT)
return;
Position p = { event->button.x, event->button.y };
if (input_mousebutton_is_pressed(input, MBUTTON_LEFT)) {
if (position_in_rect(&p, &button->area) && button->event)
button->event(button->usrdata);
} else if (event->type == SDL_MOUSEMOTION) {
Position p = { event->motion.x, event->motion.y };
button->hover = position_in_rect(&p, &button->area);
}
}

View File

@ -24,8 +24,9 @@
#include "sprite.h"
#include "linkedlist.h"
#include "camera.h"
#include "input.h"
typedef struct GuiButton_t {
typedef struct GuiButton {
SDL_Rect area;
bool hover;
void *usrdata;
@ -39,7 +40,7 @@ void
gui_button_check_pointer(GuiButton*, Pointer*);
void
gui_button_handle_event(GuiButton*, SDL_Event*);
gui_button_update(GuiButton*, Input*);
void
gui_button_destroy(GuiButton*);

154
src/input.c Normal file
View File

@ -0,0 +1,154 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "input.h"
#include "vector2d.h"
void
input_init(Input *input)
{
input->keyState = 0;
input->lastKeyState = 0;
input->mouseButtonState = 0;
input->lastMouseButtonState = 0;
input->mouseX = 0;
input->mouseY = 0;
}
void input_reset(Input *input)
{
input->lastKeyState = input->keyState;
input->lastMouseButtonState = input->mouseButtonState;
input->keyState = 0;
input->mouseButtonState = 0;
}
static Uint64
get_event_key(SDL_Event *event)
{
switch (event->key.keysym.sym) {
case SDLK_UP:
case SDLK_k:
case SDLK_w:
return KEY_UP;
case SDLK_DOWN:
case SDLK_j:
return KEY_DOWN;
case SDLK_s:
return KEY_DOWN | KEY_S;
case SDLK_LEFT:
case SDLK_h:
case SDLK_a:
return KEY_LEFT;
case SDLK_RIGHT:
case SDLK_l:
case SDLK_d:
return KEY_RIGHT;
case SDLK_0:
return KEY_NUM0;
case SDLK_1:
return KEY_NUM1;
case SDLK_2:
return KEY_NUM2;
case SDLK_3:
return KEY_NUM3;
case SDLK_4:
return KEY_NUM4;
case SDLK_5:
return KEY_NUM5;
case SDLK_6:
return KEY_NUM6;
case SDLK_7:
return KEY_NUM7;
case SDLK_8:
return KEY_NUM8;
case SDLK_9:
return KEY_NUM9;
case SDLK_ESCAPE:
return KEY_ESC;
case SDLK_RETURN:
return KEY_ENTER;
case SDLK_LALT:
case SDLK_RALT:
return KEY_ALT;
case SDLK_LCTRL:
case SDLK_RCTRL:
return KEY_CTRL;
case SDLK_m:
return KEY_M;
default:
return 0;
}
}
static Uint32
get_event_mousebutton(SDL_Event *event)
{
switch (event->button.button) {
case SDL_BUTTON_LEFT:
return MBUTTON_LEFT;
case SDL_BUTTON_MIDDLE:
return MBUTTON_MIDDLE;
case SDL_BUTTON_RIGHT:
return MBUTTON_RIGHT;
default:
return 0;
}
}
void
input_handle_event(Input *input, SDL_Event *event)
{
if (event->type == SDL_KEYDOWN)
input->keyState |= get_event_key(event);
else if (event->type == SDL_KEYUP)
input->keyState &= ~get_event_key(event);
else if (event->type == SDL_MOUSEBUTTONDOWN)
input->mouseButtonState |= get_event_mousebutton(event);
else if (event->type == SDL_MOUSEBUTTONUP)
input->mouseButtonState &= ~get_event_mousebutton(event);
else if (event->type == SDL_MOUSEMOTION) {
input->mouseX = event->motion.x;
input->mouseY = event->motion.y;
}
}
bool
input_key_is_pressed(Input *input, Uint64 key)
{
return (input->keyState & key) && !(input->lastKeyState & key);
}
bool
input_key_is_released(Input *input, Uint64 key)
{
return (input->lastKeyState & key) && !(input->keyState & key);
}
bool
input_key_is_down(Input *input, Uint64 key)
{
return input->keyState & key;
}
bool
input_mousebutton_is_pressed(Input *input, Uint8 button)
{
return (input->mouseButtonState & button)
&& !(input->lastMouseButtonState & button);
}

View File

@ -16,8 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KEYBOARDINPUT_H_
#define KEYBOARDINPUT_H_
#ifndef INPUT_H_
#define INPUT_H_
#include <SDL.h>
#include <stdbool.h>
@ -36,33 +36,46 @@
#define KEY_NUM7 2048
#define KEY_NUM8 4096
#define KEY_NUM9 8192
#define KEY_ESC 16384
#define KEY_ENTER 32768
#define KEY_CTRL 65536
#define KEY_ALT 131072
#define KEY_M 262144
#define KEY_S 524288
typedef struct KeyboardState {
bool dir_left;
bool dir_right;
bool dir_up;
bool dir_down;
} KeyboardState;
#define MBUTTON_LEFT 1
#define MBUTTON_MIDDLE 2
#define MBUTTON_RIGHT 4
typedef struct KeyboardInput {
Uint64 currentState;
Uint64 lastState;
} KeyboardInput;
typedef struct Input {
Uint64 keyState;
Uint64 lastKeyState;
Uint32 mouseButtonState;
Uint32 lastMouseButtonState;
Uint32 mouseX;
Uint32 mouseY;
} Input;
void
keyboardinput_init(KeyboardInput *);
input_init(Input *);
void
keyboardinput_handle_event(KeyboardInput *, SDL_Event*);
input_reset(Input *);
void
input_handle_event(Input *, SDL_Event*);
bool
key_is_pressed(KeyboardInput *, Uint64 key);
input_key_is_pressed(Input *, Uint64 key);
bool
key_is_released(KeyboardInput *, Uint64 key);
input_key_is_released(Input *, Uint64 key);
bool
key_is_down(KeyboardInput *, Uint64 key);
input_key_is_down(Input *, Uint64 key);
#endif // KEYBOARDINPUT_H_
bool
input_mousebutton_is_pressed(Input *, Uint8 button);
#endif // INPUT_H_

View File

@ -1,83 +0,0 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "keyboardinput.h"
#include "keyboard.h"
#include "vector2d.h"
void
keyboardinput_init(KeyboardInput *input)
{
input->currentState = 0;
input->lastState = 0;
}
void
keyboardinput_handle_event(KeyboardInput *input, SDL_Event *event)
{
if (keyboard_direction_press(UP, event)) {
input->currentState |= KEY_UP;
} else if (keyboard_direction_press(DOWN, event)) {
input->currentState |= KEY_DOWN;
} else if (keyboard_direction_press(LEFT, event)) {
input->currentState |= KEY_LEFT;
} else if (keyboard_direction_press(RIGHT, event)) {
input->currentState |= KEY_RIGHT;
} else if (keyboard_press(SDLK_0, event)) {
input->currentState |= KEY_NUM1;
}
for (int i = SDLK_0; i <= SDLK_9; ++i) {
if (keyboard_press(i, event))
input->currentState |= (KEY_NUM0 << (i - SDLK_0));
}
if (keyboard_direction_release(UP, event)) {
input->currentState &= ~KEY_UP;
} else if (keyboard_direction_release(DOWN, event)) {
input->currentState &= ~KEY_DOWN;
} else if (keyboard_direction_release(LEFT, event)) {
input->currentState &= ~KEY_LEFT;
} else if (keyboard_direction_release(RIGHT, event)) {
input->currentState &= ~KEY_RIGHT;
} else if (keyboard_release(SDLK_0, event)) {
input->currentState &= ~KEY_NUM1;
}
for (int i = SDLK_0; i <= SDLK_9; ++i) {
if (keyboard_release(i, event))
input->currentState &= ~(KEY_NUM0 << (i - SDLK_0));
}
}
bool
key_is_pressed(KeyboardInput *input, Uint64 key)
{
return (input->currentState & key) && !(input->lastState & key);
}
bool
key_is_released(KeyboardInput *input, Uint64 key)
{
return (input->lastState & key) && !(input->currentState & key);
}
bool
key_is_down(KeyboardInput *input, Uint64 key)
{
return input->currentState & key;
}

View File

@ -48,6 +48,7 @@
#include "update_data.h"
#include "settings.h"
#include "actiontextbuilder.h"
#include "input.h"
typedef enum Turn_t {
PLAYER,
@ -61,7 +62,6 @@ static Map *gMap = NULL;
static RoomMatrix *gRoomMatrix = NULL;
static Gui *gGui = NULL;
static SkillBar *gSkillBar = NULL;
static Pointer *gPointer = NULL;
static unsigned int cLevel = 1;
static float deltaTime = 1.0;
static double renderScale = 1.0;
@ -76,6 +76,7 @@ static SDL_Rect bottomGuiViewport;
static SDL_Rect rightGuiViewport;
static SDL_Rect menuViewport;
static Turn currentTurn = PLAYER;
static Input input;
static SDL_Color C_MENU_DEFAULT = { 255, 255, 0, 255 };
static SDL_Color C_MENU_OUTLINE_DEFAULT = { 0, 0, 0, 255 };
@ -189,13 +190,13 @@ static bool
initGame(void)
{
initViewports();
input_init(&input);
texturecache_init(gRenderer);
gCamera = camera_create(gRenderer);
gRoomMatrix = roommatrix_create();
gGui = gui_create(gCamera);
gSkillBar = skillbar_create(gRenderer);
item_builder_init(gRenderer);
gPointer = pointer_create(gRenderer);
particle_engine_init();
menuTimer = timer_create();
actiontextbuilder_init(gRenderer);
@ -360,19 +361,20 @@ init(void)
}
static bool
handle_main_events(SDL_Event *event)
handle_main_input(void)
{
if (gGameState == PLAYING
|| gGameState == IN_GAME_MENU
|| gGameState == GAME_OVER)
{
if (keyboard_press(SDLK_ESCAPE, event)) {
if (input_key_is_pressed(&input, KEY_ESC)) {
toggleInGameMenu(NULL);
return true;
}
}
if (keyboard_mod_press(SDLK_m, KMOD_CTRL, event)) {
if (input_key_is_down(&input, KEY_CTRL)
&& input_key_is_pressed(&input, SDLK_m)) {
if (mixer_toggle_music(&gGameState))
gui_log("Music enabled");
else
@ -380,7 +382,8 @@ handle_main_events(SDL_Event *event)
return true;
}
if (keyboard_mod_press(SDLK_s, KMOD_CTRL, event)) {
if (input_key_is_down(&input, KEY_CTRL)
&& input_key_is_pressed(&input, SDLK_s)) {
if (mixer_toggle_sound())
gui_log("Sound enabled");
else
@ -398,31 +401,17 @@ handle_events(void)
bool quit = false;
int handleCount = 0;
input_reset(&input);
while (SDL_PollEvent(&event) != 0) {
if (event.type == SDL_QUIT) {
quit = true;
continue;
}
if (handle_main_events(&event))
continue;
if (gGameState == PLAYING) {
if (currentTurn == PLAYER && !player_turn_over(gPlayer))
gPlayer->handle_event(gPlayer,
gRoomMatrix,
&event);
roommatrix_handle_event(gRoomMatrix, &event);
skillbar_handle_event(gSkillBar, &event);
} else if (gGameState == MENU) {
menu_handle_event(mainMenu, &event);
} else if (gGameState == IN_GAME_MENU) {
menu_handle_event(inGameMenu, &event);
}
pointer_handle_event(gPointer, &event);
input_handle_event(&input, &event);
handleCount++;
if (handleCount >= 5) {
if (handleCount >= 20) {
debug("Flushing event queue");
SDL_PumpEvents();
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
@ -467,6 +456,7 @@ populateUpdateData(UpdateData *data, float deltatime)
data->player = gPlayer;
data->map = gMap;
data->matrix = gRoomMatrix;
data->input = &input;
data->deltatime = deltatime;
}
@ -476,6 +466,11 @@ run_game(void)
static UpdateData updateData;
static unsigned int playerLevel = 1;
if (gGameState == IN_GAME_MENU)
menu_update(inGameMenu, &input);
if (gGameState != PLAYING && gGameState != IN_GAME_MENU)
return;
map_clear_dead_monsters(gMap, gPlayer);
map_clear_collected_items(gMap);
roommatrix_populate_from_map(gRoomMatrix, gMap);
@ -489,17 +484,20 @@ run_game(void)
playerLevel = gPlayer->stats.lvl;
skillbar_check_skill_activation(gSkillBar, gPlayer);
}
if (gGameState == PLAYING && currentTurn == PLAYER)
player_update(&updateData);
gui_update_player_stats(gGui, gPlayer, gMap, gRenderer);
camera_update(gCamera, updateData.deltatime);
particle_engine_update(deltaTime);
roommatrix_update(&updateData);
actiontextbuilder_update(&updateData);
player_update(&updateData);
skillbar_update(gSkillBar, &updateData);
camera_follow_position(gCamera, &gPlayer->sprite->pos);
map_set_current_room(gMap, &gPlayer->sprite->pos);
map_update(&updateData);
roommatrix_update_with_player(gRoomMatrix, gPlayer);
if (currentTurn == PLAYER) {
if (player_turn_over(gPlayer)) {
currentTurn = MONSTER;
@ -541,16 +539,13 @@ run_game(void)
SDL_RenderSetViewport(gRenderer, NULL);
particle_engine_render_global(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);
}
if (gGameState == GAME_OVER) {
// TODO(Linus): Render game over?
}
pointer_render(gPointer, gCamera);
SDL_RenderPresent(gRenderer);
@ -579,6 +574,10 @@ run_menu(void)
map_move_monsters(gMap, gRoomMatrix);
}
menu_update(mainMenu, &input);
if (gGameState != MENU)
return;
SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0);
SDL_RenderClear(gRenderer);
SDL_RenderSetViewport(gRenderer, &menuViewport);
@ -587,7 +586,6 @@ run_menu(void)
SDL_RenderSetViewport(gRenderer, NULL);
menu_render(mainMenu, gCamera);
pointer_render(gPointer, gCamera);
SDL_RenderPresent(gRenderer);
}
@ -606,6 +604,7 @@ void run(void)
timer_start(fpsTimer);
quit = handle_events();
handle_main_input();
switch (gGameState) {
case PLAYING:
@ -656,7 +655,6 @@ void close(void)
roommatrix_destroy(gRoomMatrix);
gui_destroy(gGui);
skillbar_destroy(gSkillBar);
pointer_destroy(gPointer);
actiontextbuilder_close();
item_builder_close();
particle_engine_close();

View File

@ -32,7 +32,7 @@
#include "player.h"
#include "map_room_modifiers.h"
struct UpdateData_t;
struct UpdateData;
typedef struct MapTile_t {
int textureIndex0;
@ -89,7 +89,7 @@ void
map_clear_collected_items(Map*);
void
map_update(struct UpdateData_t*);
map_update(struct UpdateData*);
void
map_render(Map*, Camera*);

View File

@ -42,17 +42,17 @@ menu_create(void)
}
static void
handle_keyboard_event(Menu *m, SDL_Event *event)
handle_keyboard_input(Menu *m, Input *input)
{
int lastSelect = -1;
if (keyboard_direction_press(UP, event)) {
if (input_key_is_pressed(input, KEY_UP)) {
lastSelect = m->selected;
m->selected--;
} else if (keyboard_direction_press(DOWN, event)) {
} else if (input_key_is_pressed(input, KEY_DOWN)) {
lastSelect = m->selected;
m->selected++;
} else if (keyboard_press(SDLK_RETURN, event)) {
} else if (input_key_is_pressed(input, KEY_ENTER)) {
MenuItem *item = linkedlist_get(&m->items, m->selected);
if (item->button->event)
item->button->event(item->button->usrdata);
@ -69,55 +69,10 @@ handle_keyboard_event(Menu *m, SDL_Event *event)
((MenuItem*) linkedlist_get(&m->items, m->selected))->button->hover = true;
}
static void
handle_mouse_motion_event(Menu *m, SDL_Event *event)
{
LinkedList *items;
int current_select;
bool activeItemFound = false;
items = m->items;
current_select = 0;
while (items) {
MenuItem *item = items->data;
items = items->next;
item->button->hover = false;
gui_button_handle_event(item->button, event);
if (item->button->hover) {
if (current_select != m->selected) {
mixer_play_effect(CLICK);
m->selected = current_select;
}
activeItemFound = true;
}
current_select++;
}
if (!activeItemFound)
((MenuItem*) linkedlist_get(&m->items, m->selected))->button->hover = true;
}
static void
handle_mouse_button_event(Menu *m, SDL_Event *event)
{
/* NOTE: In some cases the button/item is destroyed by the click action
* make sure you don't 'use' items after a click event has fired. It
* might break. */
MenuItem *item = linkedlist_get(&m->items, m->selected);
gui_button_handle_event(item->button, event);
}
void
menu_handle_event(Menu *m, SDL_Event *event)
menu_update(Menu *m, Input *input)
{
if (event->type == SDL_KEYDOWN)
handle_keyboard_event(m, event);
else if (event->type == SDL_MOUSEMOTION)
handle_mouse_motion_event(m, event);
else if (event->type == SDL_MOUSEBUTTONDOWN)
handle_mouse_button_event(m, event);
handle_keyboard_input(m, input);
}
void

View File

@ -34,7 +34,7 @@ Menu *
menu_create(void);
void
menu_handle_event(Menu*, SDL_Event*);
menu_update(Menu*, Input*);
void
menu_item_add(Menu*, Sprite*, Sprite*, void (*)(void*));

View File

@ -26,7 +26,7 @@
#include "player.h"
#include "linkedlist.h"
struct UpdateData_t;
struct UpdateData;
typedef enum {
PACIFIST,
@ -83,13 +83,13 @@ void
monster_update_stats_for_level(Monster*, unsigned int level);
void
monster_update(Monster*, struct UpdateData_t*);
monster_update(Monster*, struct UpdateData*);
void
monster_drop_loot(Monster*, Map*, Player*);
void
monster_set_behaviour(Monster *, MonsterBehaviour m);
monster_set_behaviour(Monster *, MonsterBehaviour behaviour);
void
monster_destroy(Monster*);

View File

@ -208,22 +208,39 @@ player_sip_health(Player *player)
}
}
static Vector2d
read_direction_from(Input *input)
{
if (input_key_is_pressed(input, KEY_LEFT))
return VECTOR2D_LEFT;
else if (input_key_is_pressed(input, KEY_RIGHT))
return VECTOR2D_RIGHT;
else if (input_key_is_pressed(input, KEY_UP))
return VECTOR2D_UP;
else if (input_key_is_pressed(input, KEY_DOWN))
return VECTOR2D_DOWN;
else
return VECTOR2D_NODIR;
}
static void
handle_next_move(Player *player, RoomMatrix *matrix)
handle_next_move(UpdateData *data)
{
static unsigned int step = 1;
if (!vector2d_equals(player->nextDirection, VECTOR2D_NODIR))
move(player, matrix, player->nextDirection);
map_room_modifier_player_effect(player, matrix, &player->nextDirection, move);
Player *player = data->player;
RoomMatrix *matrix = data->matrix;
Vector2d nextDir = read_direction_from(data->input);
if (!vector2d_equals(nextDir, VECTOR2D_NODIR))
move(player, matrix, nextDir);
if (!vector2d_equals(VECTOR2D_NODIR, player->nextDirection)) {
map_room_modifier_player_effect(player, matrix, &nextDir, move);
if (!vector2d_equals(VECTOR2D_NODIR, nextDir)) {
player->sprite->clip.x = 16*step;
++step;
step = step % 4;
}
player->nextDirection = VECTOR2D_NODIR;
}
static void
@ -237,19 +254,23 @@ use_skill(Skill *skill, SkillData *skillData)
}
static void
check_skill_activation(Player *player, RoomMatrix *matrix, SDL_Event *event)
check_skill_activation(UpdateData *data)
{
// TODO(Linus): This could be "smarter"
Player *player = data->player;
Input *input = data->input;
RoomMatrix *matrix = data->matrix;
unsigned int selected = 0;
if (keyboard_press(SDLK_1, event)) {
if (input_key_is_pressed(input, KEY_NUM1)) {
selected = 1;
} else if (keyboard_press(SDLK_2, event)) {
} else if (input_key_is_pressed(input, KEY_NUM2)) {
selected = 2;
} else if (keyboard_press(SDLK_3, event)) {
} else if (input_key_is_pressed(input, KEY_NUM3)) {
selected = 3;
} else if (keyboard_press(SDLK_4, event)) {
} else if (input_key_is_pressed(input, KEY_NUM4)) {
selected = 4;
} else if (keyboard_press(SDLK_5, event)) {
} else if (input_key_is_pressed(input, KEY_NUM5)) {
selected = 5;
}
@ -273,9 +294,13 @@ check_skill_activation(Player *player, RoomMatrix *matrix, SDL_Event *event)
}
}
static void
check_skill_trigger(Player *player, RoomMatrix *matrix)
static bool
check_skill_trigger(UpdateData *data)
{
Player *player = data->player;
RoomMatrix *matrix = data->matrix;
Input *input = data->input;
int activeSkill = -1;
for (int i = 0; i < PLAYER_SKILL_COUNT; ++i) {
if (player->skills[i] && player->skills[i]->active) {
@ -285,48 +310,16 @@ check_skill_trigger(Player *player, RoomMatrix *matrix)
}
if (activeSkill < 0)
return;
return false;
Vector2d nextDir = read_direction_from(input);
if (vector2d_equals(nextDir, VECTOR2D_NODIR))
return false;
if (vector2d_equals(player->nextDirection, VECTOR2D_NODIR))
return;
SkillData skillData = { player, matrix, player->nextDirection };
SkillData skillData = { player, matrix, nextDir };
use_skill(player->skills[activeSkill], &skillData);
player->nextDirection = VECTOR2D_NODIR;
}
static void
read_player_next_direction(Player *player, SDL_Event *event)
{
player->nextDirection = VECTOR2D_NODIR;
if (keyboard_direction_press(LEFT, event))
player->nextDirection = VECTOR2D_LEFT;
if (keyboard_direction_press(RIGHT, event))
player->nextDirection = VECTOR2D_RIGHT;
if (keyboard_direction_press(UP, event))
player->nextDirection = VECTOR2D_UP;
if (keyboard_direction_press(DOWN, event))
player->nextDirection = VECTOR2D_DOWN;
}
static void
handle_player_input(Player *player, RoomMatrix *matrix, SDL_Event *event)
{
if (player->state != ALIVE)
return;
if (event->type != SDL_KEYDOWN)
return;
if (player->projectiles)
return;
check_skill_activation(player, matrix, event);
read_player_next_direction(player, event);
return true;
}
Player*
@ -347,7 +340,6 @@ player_create(class_t class, SDL_Renderer *renderer)
player->state = ALIVE;
player->projectiles = linkedlist_create();
player->animationTimer = timer_create();
player->nextDirection = VECTOR2D_NODIR;
for (size_t i = 0; i < PLAYER_SKILL_COUNT; ++i) {
player->skills[i] = NULL;
@ -386,7 +378,6 @@ player_create(class_t class, SDL_Renderer *renderer)
player->sprite->pos = (Position) { TILE_DIMENSION, TILE_DIMENSION };
player->sprite->dim = GAME_DIMENSION;
player->sprite->clip = (SDL_Rect) { 0, 0, 16, 16 };
player->handle_event = &handle_player_input;
return player;
}
@ -465,8 +456,9 @@ void player_update(UpdateData *data)
{
Player *player = data->player;
check_skill_trigger(player, data->matrix);
handle_next_move(player, data->matrix);
check_skill_activation(data);
if (!check_skill_trigger(data))
handle_next_move(data);
if (player->state == FALLING && player->stats.hp > 0) {
if (!timer_started(player->animationTimer)) {

View File

@ -26,11 +26,12 @@
#include "camera.h"
#include "skill.h"
#include "linkedlist.h"
#include "input.h"
#define PLAYER_SKILL_COUNT 5
// Foward declare
struct UpdateData_t;
struct UpdateData;
typedef enum PlayerClass { ENGINEER, MAGE, PALADIN, ROGUE, WARRIOR } class_t;
typedef enum PlayerState { ALIVE, DEAD, FALLING } state_t;
@ -63,8 +64,6 @@ typedef struct Player_t {
state_t state;
Skill *skills[PLAYER_SKILL_COUNT];
Timer *animationTimer;
Vector2d nextDirection;
void (*handle_event)(struct Player_t*, RoomMatrix*, SDL_Event*);
} Player;
Player*
@ -86,7 +85,7 @@ void
player_reset_steps(Player*);
void
player_update(struct UpdateData_t *);
player_update(struct UpdateData *);
void
player_render(Player*, Camera*);

View File

@ -38,13 +38,11 @@ pointer_create(SDL_Renderer *renderer)
}
void
pointer_handle_event(Pointer *p, SDL_Event *event)
pointer_handle_input(Pointer *p, Input *input)
{
if (event->type == SDL_MOUSEMOTION) {
// Compensate for a small offset in the sprite
p->sprite->pos.x = event->motion.x - 6;
p->sprite->pos.y = event->motion.y - 6;
}
// Compensate for a small offset in the sprite
p->sprite->pos.x = input->mouseX - 6;
p->sprite->pos.y = input->mouseY - 6;
}
void

View File

@ -22,6 +22,7 @@
#include <SDL.h>
#include "sprite.h"
#include "camera.h"
#include "input.h"
typedef struct Pointer_t {
Sprite *sprite;
@ -31,7 +32,7 @@ Pointer *
pointer_create(SDL_Renderer *renderer);
void
pointer_handle_event(Pointer*, SDL_Event *event);
pointer_handle_input(Pointer*, Input *);
void
pointer_toggle_clickable_pointer(Pointer *, bool clickable);

View File

@ -23,6 +23,7 @@
#include "map.h"
#include "player.h"
#include "item.h"
#include "update_data.h"
RoomMatrix* roommatrix_create(void)
{
@ -37,18 +38,28 @@ RoomMatrix* roommatrix_create(void)
return m;
}
void
roommatrix_handle_event(RoomMatrix *matrix, SDL_Event *event)
static void
roommatrix_update_with_player(RoomMatrix *rm, Player *p)
{
if (event->type != SDL_MOUSEMOTION)
return;
Position rp = position_to_matrix_coords(&p->sprite->pos);
rm->spaces[rp.x][rp.y].occupied = true;
rm->spaces[rp.x][rp.y].player = p;
rm->playerRoomPos = rp;
}
if (event->motion.x < GAME_VIEW_WIDTH
&& event->motion.y < GAME_VIEW_HEIGHT)
void
roommatrix_update(UpdateData *data)
{
RoomMatrix *matrix = data->matrix;
Input *input = data->input;
if (input->mouseX < GAME_VIEW_WIDTH
&& input->mouseY < GAME_VIEW_HEIGHT)
{
matrix->mousePos.x = event->motion.x;
matrix->mousePos.y = event->motion.y;
matrix->mousePos.x = input->mouseX;
matrix->mousePos.y = input->mouseY;
}
roommatrix_update_with_player(matrix, data->player);
}
void roommatrix_populate_from_map(RoomMatrix *rm, Map *m)
@ -136,15 +147,6 @@ max(int a, int b)
}
#endif // max
void
roommatrix_update_with_player(RoomMatrix *rm, Player *p)
{
Position rp = position_to_matrix_coords(&p->sprite->pos);
rm->spaces[rp.x][rp.y].occupied = true;
rm->spaces[rp.x][rp.y].player = p;
rm->playerRoomPos = rp;
}
void
roommatrix_add_lightsource(RoomMatrix *matrix, Position *pos)
{

View File

@ -24,6 +24,7 @@
#include "position.h"
#include "camera.h"
#include "map_room_modifiers.h"
#include "input.h"
typedef struct Sprite_t Sprite;
typedef struct Map_t Map;
@ -32,6 +33,8 @@ typedef struct Player_t Player;
typedef struct Item_t Item;
typedef struct Node LinkedList;
struct UpdateData;
typedef struct {
bool occupied;
bool lethal;
@ -52,12 +55,10 @@ typedef struct RoomMatrix_t {
RoomMatrix* roommatrix_create(void);
void roommatrix_handle_event(RoomMatrix*, SDL_Event*);
void roommatrix_update(struct UpdateData*);
void roommatrix_populate_from_map(RoomMatrix*, Map*);
void roommatrix_update_with_player(RoomMatrix*, Player*);
void roommatrix_add_lightsource(RoomMatrix*, Position*);
void roommatrix_build_lightmap(RoomMatrix*);

View File

@ -25,6 +25,7 @@
#include "keyboard.h"
#include "texturecache.h"
#include "particle_engine.h"
#include "update_data.h"
static void
load_texture(SkillBar *bar, const char *path, SDL_Renderer *renderer)
@ -250,17 +251,15 @@ skillbar_render(SkillBar *bar, Player *player, Camera *cam)
}
void
skillbar_handle_event(SkillBar *bar, SDL_Event *event)
skillbar_update(SkillBar *bar, UpdateData *data)
{
if (event->type != SDL_KEYDOWN)
return;
Input *input = data->input;
unsigned int key = 0;
for (SDL_Keycode keysym = SDLK_0; keysym <= SDLK_9; ++keysym) {
if (!keyboard_press(keysym, event))
for (int i = 0; i < 10; ++i) {
if (!input_key_is_pressed(input, KEY_NUM0 << i))
continue;
key = (int)(keysym - SDLK_0);
if (key == 0) key = 10;
key = i;
break;
}

View File

@ -24,6 +24,9 @@
#include "camera.h"
#include "timer.h"
#include "player.h"
#include "input.h"
struct UpdateData;
typedef struct SkillBar_t {
LinkedList *sprites;
@ -43,7 +46,7 @@ void
skillbar_render(SkillBar*, Player*, Camera*);
void
skillbar_handle_event(SkillBar*, SDL_Event*);
skillbar_update(SkillBar*, struct UpdateData*);
void
skillbar_destroy(SkillBar*);

View File

@ -23,10 +23,11 @@
#include "map.h"
#include "roommatrix.h"
typedef struct UpdateData_t {
typedef struct UpdateData {
Player *player;
Map *map;
RoomMatrix *matrix;
Input *input;
float deltatime;
} UpdateData;

126
test/test_input.c Normal file
View File

@ -0,0 +1,126 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <SDL.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <setjmp.h>
#include <cmocka.h>
#include "../src/input.h"
static void
test_event_parse(void **state)
{
(void) state;
Input input;
input_init(&input);
SDL_KeyboardEvent event;
event.type = SDL_KEYDOWN;
event.keysym = (SDL_Keysym) { SDL_SCANCODE_W, SDLK_w, KMOD_NONE, 0 };
input_handle_event(&input, (SDL_Event*) &event);
event.keysym = (SDL_Keysym) { SDL_SCANCODE_0, SDLK_0, KMOD_NONE, 0 };
input_handle_event(&input, (SDL_Event*) &event);
assert_true(input_key_is_pressed(&input, KEY_UP));
assert_true(input_key_is_pressed(&input, KEY_NUM0));
event.type = SDL_KEYUP;
event.keysym = (SDL_Keysym) { SDL_SCANCODE_0, SDLK_0, KMOD_NONE, 0 };
input_handle_event(&input, (SDL_Event*) &event);
assert_true(input_key_is_pressed(&input, KEY_UP));
assert_true(!input_key_is_pressed(&input, KEY_NUM0));
input_reset(&input);
assert_true(input_key_is_released(&input, KEY_UP));
assert_true(!input_key_is_released(&input, KEY_NUM0));
}
static void
test_keypress(void **state)
{
(void) state;
Input input;
input_init(&input);
input.lastKeyState = 0;
input.keyState = KEY_UP;
assert_true(input_key_is_pressed(&input, KEY_UP));
}
static void
test_keyrelease(void **state)
{
(void) state;
Input input;
input_init(&input);
input.lastKeyState = KEY_UP;
input.keyState = 0;
assert_true(input_key_is_released(&input, KEY_UP));
}
static void
test_keydown(void **state)
{
(void) state;
Input input;
input_init(&input);
input.keyState = KEY_UP;
assert_true(input_key_is_down(&input, KEY_UP));
}
static void
test_mousebuttons(void **state)
{
(void) state;
Input input;
input_init(&input);
SDL_MouseButtonEvent event;
event.type = SDL_MOUSEBUTTONDOWN;
event.button = SDL_BUTTON_LEFT;
input_handle_event(&input, (SDL_Event*) &event);
assert_true(input_mousebutton_is_pressed(&input, MBUTTON_LEFT));
input_reset(&input);
event.button = SDL_BUTTON_RIGHT;
input_handle_event(&input, (SDL_Event*) &event);
assert_true(!input_mousebutton_is_pressed(&input, MBUTTON_LEFT));
assert_true(input_mousebutton_is_pressed(&input, MBUTTON_RIGHT));
}
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_keypress),
cmocka_unit_test(test_keyrelease),
cmocka_unit_test(test_keydown),
cmocka_unit_test(test_event_parse),
cmocka_unit_test(test_mousebuttons),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

View File

@ -1,114 +0,0 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <setjmp.h>
#include <cmocka.h>
#include "../src/keyboardinput.h"
#include "../src/keyboard.h"
bool __wrap_keyboard_direction_press(Direction, SDL_Event*);
bool __wrap_keyboard_press(Uint64, SDL_Event*);
bool
__wrap_keyboard_direction_press(Direction d, SDL_Event *event)
{
(void) d;
(void) event;
return mock_type(bool);
}
bool
__wrap_keyboard_press(Uint64 key, SDL_Event *event)
{
(void) key;
(void) event;
return mock_type(bool);
}
static void
test_event_parse(void **state)
{
(void) state;
KeyboardInput input = { 0, 0 };
will_return(__wrap_keyboard_direction_press, true); // KEY_UP
will_return(__wrap_keyboard_press, true); // NUM0
will_return(__wrap_keyboard_press, false); // NUM1
will_return(__wrap_keyboard_press, false); // NUM2
will_return(__wrap_keyboard_press, false); // NUM3
will_return(__wrap_keyboard_press, false); // NUM4
will_return(__wrap_keyboard_press, false); // NUM5
will_return(__wrap_keyboard_press, false); // NUM6
will_return(__wrap_keyboard_press, false); // NUM7
will_return(__wrap_keyboard_press, false); // NUM8
will_return(__wrap_keyboard_press, false); // NUM9
SDL_Event event;
keyboardinput_handle_event(&input, &event);
assert_true(key_is_pressed(&input, KEY_UP));
assert_true(key_is_pressed(&input, KEY_NUM0));
}
static void
test_keypress(void **state)
{
(void) state;
KeyboardInput input = { 0, 0 };
input.lastState = 0;
input.currentState = KEY_UP;
assert_true(key_is_pressed(&input, KEY_UP));
}
static void
test_keyrelease(void **state)
{
(void) state;
KeyboardInput input = { 0, 0 };
input.lastState = KEY_UP;
input.currentState = 0;
assert_true(key_is_released(&input, KEY_UP));
}
static void
test_keydown(void **state)
{
(void) state;
KeyboardInput input = { 0, 0 };
input.currentState = KEY_UP;
assert_true(key_is_down(&input, KEY_UP));
}
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_keypress),
cmocka_unit_test(test_keyrelease),
cmocka_unit_test(test_keydown),
cmocka_unit_test(test_event_parse),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}