1 /*
2 * This file is part of EasyRPG Player.
3 *
4 * EasyRPG Player is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * EasyRPG Player is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 // Headers
19 #include "input.h"
20 #include "input_source.h"
21 #include "output.h"
22 #include "player.h"
23 #include "system.h"
24 #include "baseui.h"
25
26 #include <algorithm>
27 #include <array>
28 #include <fstream>
29 #include <utility>
30 #include <cassert>
31
32 namespace Input {
33 /**
34 * Start repeat time (in frames) a key has
35 * to be maintained pressed before being
36 * repeated for fist time.
37 */
38 constexpr int start_repeat_time = 23;
39
40 /**
41 * Repeat time (in frames) a key has to be
42 * maintained pressed after the start repeat time
43 * has passed for being repeated again.
44 */
45 constexpr int repeat_time = 4;
46
47 std::array<int, BUTTON_COUNT> press_time;
48 std::bitset<BUTTON_COUNT> triggered, repeated, released;
49 Input::KeyStatus raw_triggered, raw_pressed, raw_released;
50 int dir4;
51 int dir8;
52 std::unique_ptr<Source> source;
53
54 bool wait_input = false;
55 }
56
IsWaitingInput()57 bool Input::IsWaitingInput() { return wait_input; }
WaitInput(bool v)58 void Input::WaitInput(bool v) { wait_input = v; }
59
Init(ButtonMappingArray buttons,DirectionMappingArray directions,const std::string & replay_from_path,const std::string & record_to_path)60 void Input::Init(
61 ButtonMappingArray buttons,
62 DirectionMappingArray directions,
63 const std::string& replay_from_path,
64 const std::string& record_to_path
65 ) {
66 std::fill(press_time.begin(), press_time.end(), 0);
67 triggered.reset();
68 repeated.reset();
69 released.reset();
70 raw_triggered.reset();
71 raw_pressed.reset();
72 raw_released.reset();
73
74 source = Source::Create(std::move(buttons), std::move(directions), replay_from_path);
75 source->InitRecording(record_to_path);
76
77 ResetMask();
78 }
79
UpdateButton(int i,bool pressed)80 static void UpdateButton(int i, bool pressed) {
81 using namespace Input;
82
83 if (pressed) {
84 released[i] = false;
85 press_time[i] += 1;
86 } else {
87 released[i] = press_time[i] > 0;
88 press_time[i] = 0;
89 }
90
91 if (press_time[i] > 0) {
92 triggered[i] = press_time[i] == 1;
93 repeated[i] = press_time[i] == 1 || (press_time[i] >= start_repeat_time && press_time[i] % repeat_time == 0);
94 } else {
95 triggered[i] = false;
96 repeated[i] = false;
97 }
98 }
99
Update()100 void Input::Update() {
101 wait_input = false; // clear each frame
102
103 source->Update();
104 auto& pressed_buttons = source->GetPressedButtons();
105
106 // Check button states
107 for (unsigned i = 0; i < BUTTON_COUNT; ++i) {
108 bool pressed = pressed_buttons[i];
109 UpdateButton(i, pressed);
110 }
111
112 auto& directions = source->GetDirectionMappings();
113
114 // Press time for directional buttons, the less they have been pressed, the higher their priority will be
115 int dirpress[Direction::NUM_DIRECTIONS] = {};
116
117 // Get max pressed time for each directional button
118 for (auto& dm: directions) {
119 if (dirpress[dm.first] < press_time[dm.second]) {
120 dirpress[dm.first] = press_time[dm.second];
121 };
122 }
123
124 // Calculate diagonal directions pressed time by dir4 combinations
125 dirpress[Direction::DOWNLEFT] += (dirpress[Direction::DOWN] > 0 && dirpress[Direction::LEFT] > 0) ? dirpress[Direction::DOWN] + dirpress[Direction::LEFT] : 0;
126 dirpress[Direction::DOWNRIGHT] += (dirpress[Direction::DOWN] > 0 && dirpress[Direction::RIGHT] > 0) ? dirpress[Direction::DOWN] + dirpress[Direction::RIGHT] : 0;
127 dirpress[Direction::UPLEFT] += (dirpress[Direction::UP] > 0 && dirpress[Direction::LEFT] > 0) ? dirpress[Direction::UP] + dirpress[Direction::LEFT] : 0;
128 dirpress[Direction::UPRIGHT] += (dirpress[Direction::UP] > 0 && dirpress[Direction::RIGHT] > 0) ? dirpress[Direction::UP] + dirpress[Direction::RIGHT] : 0;
129
130 dir4 = Direction::NONE;
131 dir8 = Direction::NONE;
132
133 // Check if no opposed keys are being pressed at the same time
134 if (!(dirpress[Direction::DOWN] > 0 && dirpress[Direction::UP] > 0) && !(dirpress[Direction::LEFT] > 0 && dirpress[Direction::RIGHT] > 0)) {
135
136 // Get dir4 by the with lowest press time (besides 0 frames)
137 int min_press_time = 0;
138 for (int i = 2; i <= 8; i += 2) {
139 if (dirpress[i] > 0) {
140 if (min_press_time == 0 || dirpress[i] < min_press_time) {
141 dir4 = i;
142 min_press_time = dirpress[i];
143 }
144 }
145 }
146
147 // Dir8 will be at least equal to Dir4
148 dir8 = dir4;
149
150 // Check diagonal directions (There is a priority order)
151 if (dirpress[Direction::UPRIGHT] > 0) dir8 = Direction::UPRIGHT;
152 else if (dirpress[Direction::UPLEFT] > 0) dir8 = Direction::UPLEFT;
153 else if (dirpress[Direction::DOWNRIGHT] > 0) dir8 = Direction::DOWNRIGHT;
154 else if (dirpress[Direction::DOWNLEFT] > 0) dir8 = Direction::DOWNLEFT;
155 }
156
157 // Determine pressed & released keys from raw keystate
158 const auto& raw_pressed_now = source->GetPressedKeys();
159 for (unsigned i = 0; i < Input::Keys::KEYS_COUNT; ++i) {
160 raw_triggered[i] = raw_pressed_now[i] && !raw_pressed[i];
161 raw_released[i] = !raw_pressed_now[i] && raw_pressed[i];
162 }
163 raw_pressed = raw_pressed_now;
164 }
165
UpdateSystem()166 void Input::UpdateSystem() {
167 source->UpdateSystem();
168 auto& pressed_buttons = source->GetPressedButtons();
169
170 // Check button states
171 for (unsigned i = 0; i < BUTTON_COUNT; ++i) {
172 if (IsSystemButton(static_cast<InputButton>(i))) {
173 bool pressed = pressed_buttons[i];
174 UpdateButton(i, pressed);
175 }
176 }
177 }
178
ResetKeys()179 void Input::ResetKeys() {
180 triggered.reset();
181 repeated.reset();
182 released.reset();
183 for (unsigned i = 0; i < BUTTON_COUNT; i++) {
184 press_time[i] = 0;
185 }
186 dir4 = Direction::NONE;
187 dir8 = Direction::NONE;
188
189 // TODO: we want Input to be agnostic to where the button
190 // presses are coming from, and if there's a UI at all.
191 // Move this into the callers?
192 if (DisplayUi) {
193 DisplayUi->GetKeyStates().reset();
194 }
195 }
196
ResetTriggerKeys()197 void Input::ResetTriggerKeys() {
198 triggered.reset();
199 }
200
IsPressed(InputButton button)201 bool Input::IsPressed(InputButton button) {
202 assert(!IsSystemButton(button));
203 WaitInput(true);
204 return press_time[button] > 0;
205 }
206
IsTriggered(InputButton button)207 bool Input::IsTriggered(InputButton button) {
208 assert(!IsSystemButton(button));
209 WaitInput(true);
210 return triggered[button];
211 }
212
IsRepeated(InputButton button)213 bool Input::IsRepeated(InputButton button) {
214 assert(!IsSystemButton(button));
215 WaitInput(true);
216 return repeated[button];
217 }
218
IsReleased(InputButton button)219 bool Input::IsReleased(InputButton button) {
220 assert(!IsSystemButton(button));
221 WaitInput(false);
222 return released[button];
223 }
224
IsSystemPressed(InputButton button)225 bool Input::IsSystemPressed(InputButton button) {
226 assert(IsSystemButton(button));
227 return press_time[button] > 0;
228 }
229
IsSystemTriggered(InputButton button)230 bool Input::IsSystemTriggered(InputButton button) {
231 assert(IsSystemButton(button));
232 return triggered[button];
233 }
234
IsSystemRepeated(InputButton button)235 bool Input::IsSystemRepeated(InputButton button) {
236 assert(IsSystemButton(button));
237 return repeated[button];
238 }
239
IsSystemReleased(InputButton button)240 bool Input::IsSystemReleased(InputButton button) {
241 assert(IsSystemButton(button));
242 return released[button];
243 }
244
IsAnyPressed()245 bool Input::IsAnyPressed() {
246 WaitInput(true);
247 return std::find_if(press_time.begin(), press_time.end(),
248 [](int t) {return t > 0;}) != press_time.end();
249 }
250
IsAnyTriggered()251 bool Input::IsAnyTriggered() {
252 WaitInput(true);
253 return triggered.any();
254 }
255
IsAnyRepeated()256 bool Input::IsAnyRepeated() {
257 WaitInput(true);
258 return repeated.any();
259 }
260
IsAnyReleased()261 bool Input::IsAnyReleased() {
262 WaitInput(false);
263 return released.any();
264 }
265
GetAllPressed()266 std::vector<Input::InputButton> Input::GetAllPressed() {
267 WaitInput(true);
268 std::vector<InputButton> vector;
269 for (unsigned i = 0; i < BUTTON_COUNT; i++) {
270 if (press_time[i] > 0)
271 vector.push_back((InputButton)i);
272 }
273 return vector;
274 }
275
GetAllTriggered()276 std::vector<Input::InputButton> Input::GetAllTriggered() {
277 WaitInput(true);
278 std::vector<InputButton> vector;
279 for (unsigned i = 0; i < BUTTON_COUNT; i++) {
280 if (triggered[i])
281 vector.push_back((InputButton)i);
282 }
283 return vector;
284 }
285
GetAllRepeated()286 std::vector<Input::InputButton> Input::GetAllRepeated() {
287 WaitInput(true);
288 std::vector<InputButton> vector;
289 for (unsigned i = 0; i < BUTTON_COUNT; i++) {
290 if (repeated[i])
291 vector.push_back((InputButton)i);
292 }
293 return vector;
294 }
295
GetAllReleased()296 std::vector<Input::InputButton> Input::GetAllReleased() {
297 WaitInput(false);
298 std::vector<InputButton> vector;
299 for (unsigned i = 0; i < BUTTON_COUNT; i++) {
300 if (released[i])
301 vector.push_back((InputButton)i);
302 }
303 return vector;
304 }
305
IsRawKeyPressed(Input::Keys::InputKey key)306 bool Input::IsRawKeyPressed(Input::Keys::InputKey key) {
307 return raw_pressed[key];
308 }
309
IsRawKeyTriggered(Input::Keys::InputKey key)310 bool Input::IsRawKeyTriggered(Input::Keys::InputKey key) {
311 return raw_triggered[key];
312 }
313
IsRawKeyReleased(Input::Keys::InputKey key)314 bool Input::IsRawKeyReleased(Input::Keys::InputKey key) {
315 return raw_released[key];
316 }
317
GetAllRawPressed()318 const Input::KeyStatus& Input::GetAllRawPressed() {
319 return raw_pressed;
320 }
321
GetAllRawTriggered()322 const Input::KeyStatus& Input::GetAllRawTriggered() {
323 return raw_triggered;
324 }
325
GetAllRawReleased()326 const Input::KeyStatus& Input::GetAllRawReleased() {
327 return raw_released;
328 }
329
GetMousePosition()330 Point Input::GetMousePosition() {
331 return source->GetMousePosition();
332 }
333
AddRecordingData(Input::RecordingData type,StringView data)334 void Input::AddRecordingData(Input::RecordingData type, StringView data) {
335 assert(source);
336 source->AddRecordingData(type, data);
337 }
338
IsRecording()339 bool Input::IsRecording() {
340 assert(source);
341 return source->IsRecording();
342 }
343
GetMask()344 Input::KeyStatus Input::GetMask() {
345 assert(source);
346 return source->GetMask();
347 }
348
SetMask(Input::KeyStatus new_mask)349 void Input::SetMask(Input::KeyStatus new_mask) {
350 auto& old_mask = source->GetMask();
351
352 #if defined(USE_MOUSE) && defined(SUPPORT_MOUSE)
353 if (!Player::mouse_flag) {
354 // Mask mouse input when mouse input is not enabled
355 constexpr std::array<Input::Keys::InputKey, 7> mouse_keys = {
356 Input::Keys::MOUSE_LEFT,
357 Input::Keys::MOUSE_RIGHT,
358 Input::Keys::MOUSE_MIDDLE,
359 Input::Keys::MOUSE_XBUTTON1,
360 Input::Keys::MOUSE_XBUTTON2,
361 Input::Keys::MOUSE_SCROLLUP,
362 Input::Keys::MOUSE_SCROLLDOWN
363 };
364 for (auto k: mouse_keys) {
365 new_mask[k] = true;
366 }
367 }
368 #endif
369
370 old_mask = new_mask;
371 }
372
ResetMask()373 void Input::ResetMask() {
374 assert(source);
375 SetMask(source->GetMask());
376 }
377