Skip to content

Commit a3426e8

Browse files
more keyboard scancode mappings
start menu looks more identical now
1 parent 9afda5a commit a3426e8

File tree

10 files changed

+158
-51
lines changed

10 files changed

+158
-51
lines changed

game/src/engine.cpp

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -569,19 +569,16 @@ bool Engine::is_hosting() {
569569
}
570570

571571
void Engine::set_background(io::DrsId id) {
572+
if (id == (io::DrsId)0)
573+
return;
574+
572575
assert(assets.get());
573576
Assets &a = *assets.get();
574577

575578
const gfx::ImageRef &r = a.at(id);
576579
SetBackground(r, vbo);
577580
}
578581

579-
void Engine::set_background(MenuState s) {
580-
const MenuInfo &mi = GetMenuInfo(s);
581-
if (mi.border_col != (io::DrsId)0)
582-
set_background(mi.border_col);
583-
}
584-
585582
ImVec2 Engine::tilepos(float x, float y, float left, float top, int h) {
586583
float x0 = left + tw / 2 * y + tw / 2 * x;
587584
float y0 = top - th / 2 * y + th / 2 * x - th / 2 * h;
@@ -629,8 +626,11 @@ void Engine::cam_reset() {
629626

630627
void Engine::goto_menu(MenuState state) {
631628
menu_state = state;
632-
keyctl.clear();
633-
set_background(menu_state);
629+
630+
const MenuInfo &mi = GetMenuInfo(state);
631+
keyctl.clear(mi.keyboard_mode);
632+
set_background(mi.border_col);
633+
634634
sdl->window.set_clipping(false);
635635

636636
switch (menu_state) {
@@ -775,7 +775,7 @@ void Engine::start_multiplayer_game() {
775775
show_timeline = false;
776776
show_diplomacy = false;
777777
multiplayer_ready = false;
778-
keyctl.clear();
778+
keyctl.clear(KeyboardMode::gameplay);
779779

780780
cam_reset();
781781
}
@@ -1116,6 +1116,9 @@ void Engine::key_tapped(SDL &sdl, GameKey k) {
11161116

11171117
// menu specific
11181118
switch (menu_state) {
1119+
case MenuState::start:
1120+
mainMenu.key_tapped(k);
1121+
break;
11191122
case MenuState::singleplayer_game:
11201123
case MenuState::multiplayer_game:
11211124
kbp_game(k);
@@ -1131,7 +1134,7 @@ void Engine::kbp_down(GameKey k) {
11311134
return;
11321135

11331136
if (menu_state == MenuState::start)
1134-
mainMenu.kbp_down(k);
1137+
mainMenu.key_down(k, keyctl, sfx);
11351138
}
11361139

11371140
void Engine::eventloop(SDL &sdl, gfx::GLprogram &prog, GLuint vao) {
@@ -1181,7 +1184,7 @@ void Engine::eventloop(SDL &sdl, gfx::GLprogram &prog, GLuint vao) {
11811184

11821185
if (menu_state == MenuState::start) {
11831186
if (event.button.button = SDL_BUTTON_LEFT)
1184-
mainMenu.mouse_up(event.button.x, event.button.y, menu_state);
1187+
mainMenu.mouse_up(event.button.x, event.button.y);
11851188
}
11861189
break;
11871190
default:

game/src/engine.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ class Engine final {
228228
void goto_multiplayer_menu();
229229
void start_multiplayer_game();
230230

231-
void set_background(MenuState);
232231
void set_background(io::DrsId);
233232

234233
void draw_background_border();

game/src/engine/keyboard_mode.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#ifndef AOE_KEYBOARD_MODE_HPP
2+
#define AOE_KEYBOARD_MODE_HPP 1
3+
4+
namespace aoe {
5+
6+
enum KeyboardMode {
7+
configure, // change settings
8+
fullscreen_menu,
9+
gameplay,
10+
other, // gameplay, scenario editor, etc.
11+
};
12+
13+
}
14+
15+
#endif

game/src/engine/keyctl.cpp

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,35 @@
11
#include "keyctl.hpp"
22

3+
#include <algorithm> // fill
4+
35
namespace aoe {
46

57
KeyboardController keyctl;
68

7-
KeyboardController::KeyboardController() : state((size_t)GameKey::max, false), state_tapped(state.size(), false), keys() {
9+
/*
10+
https://discourse.libsdl.org/t/scancode-vs-keycode/32860/5
11+
12+
the author of SDL says about key-/scancodes:
13+
14+
SDL is trying to support essentially 3 different types of keyboard input:
15+
16+
SDL scancodes are used for games that require position independent key input, e.g. an FPS that uses WASD for movement should use scancodes because the position is important (i.e. the key position shouldn't change on a French AZERTY keyboard, etc.)
17+
SDL keycodes are used for games that use symbolic key input, e.g. `I' for inventory, `B' for bag, etc. This is useful for MMOs and other games where the label on the key is important for understanding what the action is. Typically shift or control modifiers on those keys do something related to the original function and aren't related to another letter that might be mapped there (e.g. Shift-B closes all bags, etc.)
18+
SDL text input is used for games that have a text field for chat, or character names, etc.
19+
Games don't always follow these usage patterns, but this is the way it was originally designed.
20+
*/
21+
22+
enum class KeyType {
23+
scan, // position independent
24+
key , // symbolic key
25+
text, // input fields
26+
};
27+
28+
KeyboardController::KeyboardController()
29+
: state((size_t)GameKey::max, false), state_tapped(state.size(), false)
30+
, scan_keys()
31+
, keys(), mode(KeyboardMode::fullscreen_menu)
32+
{
833
// NOTE in theory, you can have multiple keys mapped to the same GameKey, but this can lead to unreliable results with key down state.
934
keys[SDLK_UP] = GameKey::ui_prev;
1035
keys[SDLK_DOWN] = GameKey::ui_next;
@@ -27,17 +52,35 @@ KeyboardController::KeyboardController() : state((size_t)GameKey::max, false), s
2752
keys['t'] = keys['T'] = GameKey::train_melee1;
2853
keys['.'] = GameKey::focus_idle_villager;
2954
keys[' '] = GameKey::focus_entity;
55+
56+
scan_keys[SDL_SCANCODE_UP] = GameKey::ui_prev;
57+
scan_keys[SDL_SCANCODE_DOWN] = GameKey::ui_next;
58+
scan_keys[SDL_SCANCODE_SPACE] = GameKey::ui_select;
59+
scan_keys[SDL_SCANCODE_GRAVE] = GameKey::toggle_debug_window;
60+
scan_keys[SDL_SCANCODE_F1] = GameKey::open_help;
61+
scan_keys[SDL_SCANCODE_F11] = GameKey::toggle_fullscreen;
3062
}
3163

32-
void KeyboardController::clear() {
33-
state.clear();
34-
state_tapped.clear();
35-
size_t end = (size_t)GameKey::max;
36-
state.resize(end, false);
37-
state_tapped.resize(end, false);
64+
void KeyboardController::clear(KeyboardMode mode) {
65+
this->mode = mode;
66+
std::fill(state.begin(), state.end(), false);
67+
std::fill(state_tapped.begin(), state_tapped.end(), false);
3868
}
3969

4070
GameKey KeyboardController::down(const SDL_KeyboardEvent &e) {
71+
if (mode == KeyboardMode::fullscreen_menu) {
72+
auto it = scan_keys.find(e.keysym.scancode);
73+
74+
if (it == scan_keys.end())
75+
return GameKey::max;
76+
77+
GameKey k = it->second;
78+
size_t idx = (size_t)k;
79+
state[idx] = true;
80+
state_tapped[idx] = false;
81+
return k;
82+
}
83+
4184
auto it = keys.find(e.keysym.sym);
4285

4386
if (it == keys.end())
@@ -52,6 +95,24 @@ GameKey KeyboardController::down(const SDL_KeyboardEvent &e) {
5295
}
5396

5497
GameKey KeyboardController::up(const SDL_KeyboardEvent &e) {
98+
if (mode == KeyboardMode::fullscreen_menu) {
99+
auto it = scan_keys.find(e.keysym.scancode);
100+
101+
if (it == scan_keys.end())
102+
return GameKey::max;
103+
104+
GameKey k = it->second;
105+
size_t idx = (size_t)k;
106+
107+
if (!state[idx]) {
108+
state[idx] = false;
109+
return GameKey::max;
110+
}
111+
state_tapped[idx] = true;
112+
state[idx] = false;
113+
return k;
114+
}
115+
55116
auto it = keys.find(e.keysym.sym);
56117

57118
if (it == keys.end())
@@ -60,6 +121,10 @@ GameKey KeyboardController::up(const SDL_KeyboardEvent &e) {
60121
GameKey k = it->second;
61122
size_t idx = (size_t)k;
62123

124+
if (!state[idx]) {
125+
state[idx] = false;
126+
return GameKey::max;
127+
}
63128
state[idx] = false;
64129
state_tapped[idx] = true;
65130
return k;

game/src/engine/keyctl.hpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22
#define AOE_KEYCTL_HPP 1
33

44
#include <map>
5+
#include <set>
56
#include <vector>
67

78
#include <SDL2/SDL_events.h>
89
#include <SDL2/SDL_keyboard.h>
910

11+
#include "keyboard_mode.hpp"
12+
1013
namespace aoe {
1114

1215
enum class GameKey {
1316
ui_prev,
1417
ui_next,
18+
ui_select,
1519
// gameplay
1620
key_left,
1721
key_right,
@@ -37,19 +41,15 @@ enum class GameKey {
3741
max,
3842
};
3943

40-
enum KeyboardMode {
41-
configure, // change settings
42-
fullscreen_menu,
43-
other, // gameplay, scenario editor, etc.
44-
};
45-
4644
class KeyboardController final {
4745
std::vector<bool> state, state_tapped;
46+
std::map<SDL_Scancode, GameKey> scan_keys;
4847
std::map<SDL_Keycode, GameKey> keys;
48+
KeyboardMode mode;
4949
public:
5050
KeyboardController();
5151

52-
void clear();
52+
void clear(KeyboardMode mode);
5353

5454
GameKey down(const SDL_KeyboardEvent&);
5555
GameKey up(const SDL_KeyboardEvent&);

game/src/engine/menu.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@ namespace aoe {
66
using namespace io;
77

88
const MenuInfo menu_info[] = {
9-
{ false, DrsId::bkg_main_menu }, // init
10-
{ true, DrsId::bkg_main_menu }, // start
11-
{ true, DrsId::bkg_singleplayer },
12-
{ true, DrsId::bkg_singleplayer },
13-
{ false, (DrsId)0 }, // sp game
14-
{ true, DrsId::bkg_multiplayer },
15-
{ true, DrsId::bkg_multiplayer },
16-
{ false, (DrsId)0 }, // mp game
17-
{ false, (DrsId)0 }, // mp settings
18-
{ true, DrsId::bkg_defeat },
19-
{ true, DrsId::bkg_victory },
20-
{ true, DrsId::bkg_editor_menu }, // edit menu
21-
{ false, (DrsId)0 }, // edit scn
9+
{ false, KeyboardMode::configure, DrsId::bkg_main_menu }, // init
10+
{ true, KeyboardMode::fullscreen_menu, DrsId::bkg_main_menu }, // start
11+
{ true, KeyboardMode::other, DrsId::bkg_singleplayer },
12+
{ true, KeyboardMode::other, DrsId::bkg_singleplayer },
13+
{ false, KeyboardMode::other, (DrsId)0 }, // sp game
14+
{ true, KeyboardMode::other, DrsId::bkg_multiplayer },
15+
{ true, KeyboardMode::other, DrsId::bkg_multiplayer },
16+
{ false, KeyboardMode::other, (DrsId)0 }, // mp game
17+
{ false, KeyboardMode::other, (DrsId)0 }, // mp settings
18+
{ true, KeyboardMode::other, DrsId::bkg_defeat },
19+
{ true, KeyboardMode::other, DrsId::bkg_victory },
20+
{ true, KeyboardMode::other, DrsId::bkg_editor_menu }, // edit menu
21+
{ false, KeyboardMode::other, (DrsId)0 }, // edit scn
2222
};
2323

2424
const MenuInfo &GetMenuInfo(MenuState state) {

game/src/engine/menu.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include "../legacy/legacy.hpp"
4+
#include "keyboard_mode.hpp"
45

56
namespace aoe {
67

@@ -23,6 +24,7 @@ enum class MenuState {
2324

2425
struct MenuInfo final {
2526
bool draw_border;
27+
KeyboardMode keyboard_mode;
2628
io::DrsId border_col;
2729
};
2830

game/src/ui.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,7 @@ MenuButton mainMenuButtons[] = {
10511051

10521052
static void DrawMainMenu(Frame &f, Audio &sfx, Assets &ass);
10531053

1054-
FullscreenMenu mainMenu((unsigned)MenuState::start, mainMenuButtons, ARRAY_SIZE(mainMenuButtons), DrawMainMenu);
1054+
FullscreenMenu mainMenu(MenuState::start, mainMenuButtons, ARRAY_SIZE(mainMenuButtons), DrawMainMenu);
10551055

10561056
static void DrawMainMenu(Frame &f, Audio &sfx, Assets &ass)
10571057
{

game/src/ui/fullscreenmenu.cpp

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ void FullscreenMenu::reshape(ImGuiViewport *vp) {
1111
buttons[i].reshape(vp);
1212
}
1313

14-
FullscreenMenu::FullscreenMenu(unsigned menuState, MenuButton *buttons, unsigned buttonCount,
14+
FullscreenMenu::FullscreenMenu(MenuState menuState, MenuButton *buttons, unsigned buttonCount,
1515
void (*fn)(Frame &, Audio &, Assets &))
16-
: menuState(menuState), buttons(buttons), buttonCount(buttonCount), selected(0), draw(fn)
16+
: menuState(menuState), buttons(buttons), buttonCount(buttonCount), activated(false), selected(0), draw(fn)
1717
{
1818
assert(buttonCount);
1919
buttons[selected].state |= (unsigned)MenuButtonState::selected;
@@ -30,7 +30,7 @@ static inline bool PointInMenuButton(const MenuButton *btn, int mx, int my)
3030

3131
void FullscreenMenu::mouse_down(int mx, int my, Audio &sfx) {
3232
unsigned mask = (unsigned)MenuButtonState::active | (unsigned)MenuButtonState::selected;
33-
bool activated = false;
33+
activated = false;
3434

3535
for (unsigned i = 0; i < buttonCount; ++i) {
3636
MenuButton *btn = &buttons[i];
@@ -44,27 +44,48 @@ void FullscreenMenu::mouse_down(int mx, int my, Audio &sfx) {
4444
btn->state &= ~mask;
4545
}
4646
}
47+
48+
if (!activated)
49+
buttons[selected].state |= (unsigned)MenuButtonState::selected;
4750
}
4851

49-
void FullscreenMenu::mouse_up(int mx, int my, MenuState state) {
50-
if (selected >= buttonCount)
52+
void FullscreenMenu::mouse_up(int mx, int my) {
53+
if (!activated || selected >= buttonCount)
5154
return;
5255

5356
MenuButton *btn = &buttons[selected];
5457
unsigned mselected = (unsigned)MenuButtonState::selected;
5558
unsigned mactive = (unsigned)MenuButtonState::active;
5659

60+
activated = false;
61+
5762
if (PointInMenuButton(btn, mx, my)) {
5863
btn->state = (btn->state | mselected) & ~mactive;
59-
MenuButtonActivate(state, selected);
64+
MenuButtonActivate(menuState, selected);
6065
} else {
6166
btn->state &= ~mactive;
6267
}
6368
}
6469

65-
void FullscreenMenu::kbp_down(GameKey key) {
70+
void FullscreenMenu::key_tapped(GameKey key) {
71+
if (key == GameKey::ui_select) {
72+
buttons[selected].state &= ~(unsigned)MenuButtonState::active;
73+
MenuButtonActivate(menuState, selected);
74+
}
75+
}
76+
77+
void FullscreenMenu::key_down(GameKey key, KeyboardController &keyctl, Audio &sfx) {
6678
unsigned old = selected;
6779

80+
if (keyctl.is_down(GameKey::ui_select)) {
81+
// only do this if we just selected
82+
if (key == GameKey::ui_select) {
83+
buttons[selected].state |= (unsigned)MenuButtonState::active;
84+
sfx.play_sfx(SfxId::ui_click);
85+
}
86+
return;
87+
}
88+
6889
if (key == GameKey::ui_prev)
6990
dec(selected);
7091
else if (key == GameKey::ui_next)

0 commit comments

Comments
 (0)