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