diff --git a/data/blackboard.art b/data/blackboard.art index 093cf49..6f7d9e0 100644 Binary files a/data/blackboard.art and b/data/blackboard.art differ diff --git a/src/WorldTraveller.c b/src/WorldTraveller.c index 179c41e..572a878 100644 --- a/src/WorldTraveller.c +++ b/src/WorldTraveller.c @@ -352,6 +352,42 @@ INTERNAL inline v4 getEntityScreenRect(Entity entity) return result; } +enum ReadKeyType +{ + readkeytype_oneShot, + readkeytype_delayedRepeat, + readkeytype_repeat, + readkeytype_count, +}; + +INTERNAL b32 getKeyStatus(KeyState key, enum ReadKeyType readType) +{ + switch(readType) + { + case readkeytype_oneShot: + { + if (key.endedDown && + (key.newHalfTransitionCount > key.oldHalfTransitionCount)) + return TRUE; + break; + } + case readkeytype_repeat: + { + if (key.endedDown) return TRUE; + break; + } + case readkeytype_delayedRepeat: + default: + // TODO(doyle): Add delayed repeat of keys +#ifdef DENGINE_DEBUG + ASSERT(INVALID_CODE_PATH); +#endif + break; + } + + return FALSE; +} + INTERNAL void parseInput(GameState *state, const f32 dt) { /* @@ -370,31 +406,52 @@ INTERNAL void parseInput(GameState *state, const f32 dt) Entity *hero = &world->entities[entity_getIndex(world, world->heroId)]; v2 ddPos = V2(0, 0); + KeyState *keys = state->input.keys; + for (i32 i = 0; i < keycode_count; i++) + { + KeyState *currKey = &keys[i]; + if (currKey->newHalfTransitionCount > currKey->oldHalfTransitionCount) + { + i32 numTransitions = currKey->newHalfTransitionCount - + currKey->oldHalfTransitionCount; + + if ((numTransitions & 1) == 1) + { + currKey->endedDown = ~currKey->endedDown; + } + + // TODO(doyle): Multi press within frame override UI input parsing + if ((i >= keycode_A && i <= keycode_Z) || + (i >= keycode_a && i <= keycode_z)) + { + if (getKeyStatus(*currKey, readkeytype_oneShot)) + { + state->uiState.keyChar = i; + } + } + } + } + if (hero->stats->busyDuration <= 0) { - // TODO(doyle): As we need to handle more key spam input, we want to - // track - // if a button ended down - LOCAL_PERSIST b32 spaceBarWasDown = FALSE; - - if (state->input.keys[keycode_right]) + if (getKeyStatus(keys[keycode_right], readkeytype_repeat)) { ddPos.x = 1.0f; hero->direction = direction_east; } - if (state->input.keys[keycode_left]) + if (getKeyStatus(keys[keycode_left], readkeytype_repeat)) { ddPos.x = -1.0f; hero->direction = direction_west; } - if (state->input.keys[keycode_up]) + if (getKeyStatus(keys[keycode_up], readkeytype_repeat)) { ddPos.y = 1.0f; } - if (state->input.keys[keycode_down]) + if (getKeyStatus(keys[keycode_down], readkeytype_repeat)) { ddPos.y = -1.0f; } @@ -408,18 +465,16 @@ INTERNAL void parseInput(GameState *state, const f32 dt) ddPos = v2_scale(ddPos, 0.70710678118f); } - if (state->input.keys[keycode_enter]) + if (getKeyStatus(keys[keycode_enter], readkeytype_oneShot)) { state->uiState.keyEntered = keycode_enter; } - LOCAL_PERSIST b32 toggleFlag = TRUE; - // TODO(doyle): Revisit key input with state checking for last ended down - if (state->input.keys[keycode_space] && spaceBarWasDown == FALSE) + if (getKeyStatus(keys[keycode_space], readkeytype_oneShot)) { state->uiState.keyEntered = keycode_space; + DEBUG_LOG("push space"); -#if 0 Renderer *renderer = &state->renderer; f32 yPos = CAST(f32)(rand() % CAST(i32)renderer->size.h); f32 xModifier = 5.0f - CAST(f32)(rand() % 3); @@ -427,12 +482,6 @@ INTERNAL void parseInput(GameState *state, const f32 dt) v2 pos = V2(renderer->size.w - (renderer->size.w / xModifier), yPos); entity_addGenericMob(&state->arena, &state->assetManager, world, pos); -#endif - spaceBarWasDown = TRUE; - } - else if (!state->input.keys[keycode_space]) - { - spaceBarWasDown = FALSE; } } @@ -455,7 +504,7 @@ INTERNAL void parseInput(GameState *state, const f32 dt) } f32 heroSpeed = 6.2f * METERS_TO_PIXEL; - if (state->input.keys[keycode_leftShift]) + if (state->input.keys[keycode_leftShift].endedDown) { // TODO: Context sensitive command separation state->uiState.keyMod = keycode_leftShift; @@ -898,7 +947,7 @@ INTERNAL i32 button(UiState *uiState, AssetManager *assetManager, if (math_pointInRect(rect, input.mouseP)) { uiState->hotItem = id; - if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft]) + if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown) uiState->activeItem = id; } @@ -963,7 +1012,7 @@ INTERNAL i32 button(UiState *uiState, AssetManager *assetManager, uiState->lastWidget = id; - if (!input.keys[keycode_mouseLeft] && + if (!input.keys[keycode_mouseLeft].endedDown && uiState->hotItem == id && uiState->activeItem == id) { @@ -984,7 +1033,7 @@ INTERNAL i32 scrollBar(UiState *uiState, AssetManager *assetManager, if (math_pointInRect(scrollBarRect, input.mouseP)) { uiState->hotItem = id; - if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft]) + if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown) uiState->activeItem = id; } @@ -1095,7 +1144,7 @@ INTERNAL i32 textField(UiState *const uiState, MemoryArena *arena, if (math_pointInRect(textRect, input.mouseP)) { uiState->hotItem = id; - if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft]) + if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown) { uiState->activeItem = id; } @@ -1158,15 +1207,15 @@ INTERNAL i32 textField(UiState *const uiState, MemoryArena *arena, } if (uiState->keyChar >= keycode_space && - uiState->keyChar <= keycode_Z && strLen < 30) + uiState->keyChar <= keycode_tilda && strLen < 30) { - string[strLen++] = uiState->keyChar; + string[strLen++] = uiState->keyChar + ' '; string[strLen] = 0; changed = TRUE; } } - if (!input.keys[keycode_mouseLeft] && + if (!input.keys[keycode_mouseLeft].endedDown && uiState->hotItem == id && uiState->activeItem == id) { @@ -1449,7 +1498,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) state->input, 5, V2(1000, 500), fieldString); // RESET IMGUI - if (!state->input.keys[keycode_mouseLeft]) + if (!state->input.keys[keycode_mouseLeft].endedDown) state->uiState.activeItem = 0; else if (state->uiState.activeItem == 0) state->uiState.activeItem = -1; @@ -1500,6 +1549,12 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) } } + for (i32 i = 0; i < keycode_count; i++) + { + state->input.keys[i].oldHalfTransitionCount = + state->input.keys[i].newHalfTransitionCount; + } + sortWorldEntityList(world, numDeadEntities); #ifdef DENGINE_DEBUG diff --git a/src/dengine.c b/src/dengine.c index 52e3d01..ded0d64 100644 --- a/src/dengine.c +++ b/src/dengine.c @@ -5,10 +5,13 @@ #include "Dengine/OpenGL.h" #include "Dengine/WorldTraveller.h" -INTERNAL inline void processKey(b32 *currState, int key, int action) +INTERNAL inline void processKey(KeyState *state, int action) { - if (action == GLFW_PRESS) *currState = TRUE; - else if (action == GLFW_RELEASE) *currState = FALSE; + // TODO(doyle): Handle GLFW_REPEAT, and probably remove this function + if (action == GLFW_PRESS || action == GLFW_RELEASE) + { + state->newHalfTransitionCount++; + } } INTERNAL void keyCallback(GLFWwindow *window, int key, int scancode, int action, @@ -24,32 +27,38 @@ INTERNAL void keyCallback(GLFWwindow *window, int key, int scancode, int action, switch (key) { case GLFW_KEY_UP: - processKey(&game->input.keys[keycode_up], key, action); + processKey(&game->input.keys[keycode_up], action); break; case GLFW_KEY_DOWN: - processKey(&game->input.keys[keycode_down], key, action); + processKey(&game->input.keys[keycode_down], action); break; case GLFW_KEY_LEFT: - processKey(&game->input.keys[keycode_left], key, action); + processKey(&game->input.keys[keycode_left], action); break; case GLFW_KEY_RIGHT: - processKey(&game->input.keys[keycode_right], key, action); + processKey(&game->input.keys[keycode_right], action); break; case GLFW_KEY_LEFT_SHIFT: - processKey(&game->input.keys[keycode_leftShift], key, action); + processKey(&game->input.keys[keycode_leftShift], action); break; case GLFW_KEY_ENTER: - processKey(&game->input.keys[keycode_enter], key, action); + processKey(&game->input.keys[keycode_enter], action); break; case GLFW_KEY_BACKSPACE: - processKey(&game->input.keys[keycode_backspace], key, action); + processKey(&game->input.keys[keycode_backspace], action); break; default: if (key >= ' ' && key <= '~') { - processKey(&game->input.keys[key - ' '], key, action); - // TODO(doyle): Temporary - game->uiState.keyChar = key - ' '; + i32 offset = 0; + if (key >= 'A' && key <= 'Z') + { + if (!game->input.keys[keycode_leftShift].endedDown) + offset = 'a' - 'A'; + } + + i32 glfwCodeToPlatformCode = (key - ' ') + offset; + processKey(&game->input.keys[glfwCodeToPlatformCode], action); } break; } @@ -73,7 +82,7 @@ INTERNAL void mouseButtonCallback(GLFWwindow *window, int button, int action, switch(button) { case GLFW_MOUSE_BUTTON_LEFT: - processKey(&game->input.keys[keycode_mouseLeft], button, action); + processKey(&game->input.keys[keycode_mouseLeft], action); break; default: break; diff --git a/src/include/Dengine/Platform.h b/src/include/Dengine/Platform.h index 6157d73..951a63f 100644 --- a/src/include/Dengine/Platform.h +++ b/src/include/Dengine/Platform.h @@ -11,7 +11,7 @@ typedef struct MemoryArena MemoryArena; // have a mapping function to map it back to ascii? enum KeyCode { - keycode_space = 0, + keycode_space, keycode_exclamation, keycode_dbl_quotes, keycode_hash, @@ -24,7 +24,6 @@ enum KeyCode keycode_star, keycode_plus, keycode_comma, - keycode_minus, keycode_hyphen, keycode_dot, keycode_forward_slash, @@ -114,16 +113,22 @@ enum KeyCode keycode_leftShift, keycode_mouseLeft, keycode_enter, - keycode_count, keycode_backspace, - keycode_tab, + keycode_count, keycode_null, }; +typedef struct KeyState +{ + u32 oldHalfTransitionCount; + u32 newHalfTransitionCount; + b32 endedDown; +} KeyState; + typedef struct KeyInput { v2 mouseP; - b32 keys[keycode_count - 1]; + KeyState keys[keycode_count]; } KeyInput; typedef struct PlatformFileRead