1 /*
2 * This file is part of the Colobot: Gold Edition source code
3 * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4 * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see http://gnu.org/licenses
18 */
19
20 #include "app/input.h"
21
22 #include "common/config_file.h"
23 #include "common/logger.h"
24 #include "common/restext.h"
25
26 #include "graphics/engine/engine.h"
27
28 #include "level/robotmain.h"
29
30
31 #include <sstream>
32 #include <boost/lexical_cast.hpp>
33 #include <SDL_system.h>
34
CInput()35 CInput::CInput()
36 : m_keyPresses()
37 {
38 m_keyTable = std::map<InputSlot, std::string>
39 {
40 { INPUT_SLOT_LEFT, "left" },
41 { INPUT_SLOT_RIGHT, "right" },
42 { INPUT_SLOT_UP, "up" },
43 { INPUT_SLOT_DOWN, "down" },
44 { INPUT_SLOT_GUP, "gup" },
45 { INPUT_SLOT_GDOWN, "gdown" },
46 { INPUT_SLOT_CAMERA, "camera" },
47 { INPUT_SLOT_DESEL, "desel" },
48 { INPUT_SLOT_ACTION, "action" },
49 { INPUT_SLOT_CAM_LEFT, "cleft" },
50 { INPUT_SLOT_CAM_RIGHT,"cright" },
51 { INPUT_SLOT_CAM_UP, "cup" },
52 { INPUT_SLOT_CAM_DOWN, "cdown" },
53 { INPUT_SLOT_CAM_NEAR, "near" },
54 { INPUT_SLOT_CAM_AWAY, "away" },
55 { INPUT_SLOT_CAM_ALT, "camalt" },
56 { INPUT_SLOT_NEXT, "next" },
57 { INPUT_SLOT_HUMAN, "human" },
58 { INPUT_SLOT_QUIT, "quit" },
59 { INPUT_SLOT_HELP, "help" },
60 { INPUT_SLOT_PROG, "prog" },
61 { INPUT_SLOT_VISIT, "visit" },
62 { INPUT_SLOT_SPEED_DEC, "speed_dec" },
63 { INPUT_SLOT_SPEED_RESET,"speed_reset" },
64 { INPUT_SLOT_SPEED_INC, "speed_inc" },
65 { INPUT_SLOT_QUICKSAVE, "quicksave" },
66 { INPUT_SLOT_QUICKLOAD, "quickload" },
67 { INPUT_SLOT_PAUSE, "pause" },
68 { INPUT_SLOT_CMDLINE, "cmdline" },
69 };
70
71 m_mousePos = Math::Point();
72 m_mouseButtonsState = 0;
73 std::fill_n(m_keyPresses, static_cast<std::size_t>(INPUT_SLOT_MAX), false);
74
75 m_joystickDeadzone = 0.2f;
76 SetDefaultInputBindings();
77 }
78
EventProcess(Event & event)79 void CInput::EventProcess(Event& event)
80 {
81 // Use the occasion to update mouse button state
82 if (event.type == EVENT_MOUSE_BUTTON_DOWN)
83 {
84 auto data = event.GetData<MouseButtonEventData>();
85 m_mouseButtonsState |= data->button;
86 }
87 if (event.type == EVENT_MOUSE_BUTTON_UP)
88 {
89 auto data = event.GetData<MouseButtonEventData>();
90 m_mouseButtonsState &= ~data->button;
91 }
92
93
94 if (event.type == EVENT_KEY_DOWN ||
95 event.type == EVENT_KEY_UP)
96 {
97 auto data = event.GetData<KeyEventData>();
98 data->slot = FindBinding(data->key);
99 }
100
101 event.kmodState = SDL_GetModState();
102 event.mousePos = m_mousePos;
103 event.mouseButtonsState = m_mouseButtonsState;
104
105
106 if (event.type == EVENT_KEY_DOWN ||
107 event.type == EVENT_KEY_UP)
108 {
109 auto data = event.GetData<KeyEventData>();
110 m_keyPresses[data->slot] = (event.type == EVENT_KEY_DOWN);
111 }
112
113
114
115 /* Motion vector management */
116
117 if (event.type == EVENT_KEY_DOWN && !(event.kmodState & KEY_MOD(ALT) ) )
118 {
119 auto data = event.GetData<KeyEventData>();
120
121 if (data->slot == INPUT_SLOT_UP ) m_keyMotion.y = 1.0f;
122 if (data->slot == INPUT_SLOT_DOWN ) m_keyMotion.y = -1.0f;
123 if (data->slot == INPUT_SLOT_LEFT ) m_keyMotion.x = -1.0f;
124 if (data->slot == INPUT_SLOT_RIGHT) m_keyMotion.x = 1.0f;
125 if (data->slot == INPUT_SLOT_GUP ) m_keyMotion.z = 1.0f;
126 if (data->slot == INPUT_SLOT_GDOWN) m_keyMotion.z = -1.0f;
127
128 if (data->slot == INPUT_SLOT_CAM_LEFT ) m_cameraKeyMotion.x = -1.0f;
129 if (data->slot == INPUT_SLOT_CAM_RIGHT) m_cameraKeyMotion.x = 1.0f;
130 if (data->slot == INPUT_SLOT_CAM_UP ) m_cameraKeyMotion.y = 1.0f;
131 if (data->slot == INPUT_SLOT_CAM_DOWN ) m_cameraKeyMotion.y = -1.0f;
132 if (data->slot == INPUT_SLOT_CAM_NEAR ) m_cameraKeyMotion.z = -1.0f;
133 if (data->slot == INPUT_SLOT_CAM_AWAY ) m_cameraKeyMotion.z = 1.0f;
134 }
135 else if (event.type == EVENT_KEY_UP)
136 {
137 auto data = event.GetData<KeyEventData>();
138
139 if (data->slot == INPUT_SLOT_UP ) m_keyMotion.y = 0.0f;
140 if (data->slot == INPUT_SLOT_DOWN ) m_keyMotion.y = 0.0f;
141 if (data->slot == INPUT_SLOT_LEFT ) m_keyMotion.x = 0.0f;
142 if (data->slot == INPUT_SLOT_RIGHT) m_keyMotion.x = 0.0f;
143 if (data->slot == INPUT_SLOT_GUP ) m_keyMotion.z = 0.0f;
144 if (data->slot == INPUT_SLOT_GDOWN) m_keyMotion.z = 0.0f;
145
146 if (data->slot == INPUT_SLOT_CAM_LEFT ) m_cameraKeyMotion.x = 0.0f;
147 if (data->slot == INPUT_SLOT_CAM_RIGHT) m_cameraKeyMotion.x = 0.0f;
148 if (data->slot == INPUT_SLOT_CAM_UP ) m_cameraKeyMotion.y = 0.0f;
149 if (data->slot == INPUT_SLOT_CAM_DOWN ) m_cameraKeyMotion.y = 0.0f;
150 if (data->slot == INPUT_SLOT_CAM_NEAR ) m_cameraKeyMotion.z = 0.0f;
151 if (data->slot == INPUT_SLOT_CAM_AWAY ) m_cameraKeyMotion.z = 0.0f;
152 }
153 else if (event.type == EVENT_JOY_AXIS)
154 {
155 auto data = event.GetData<JoyAxisEventData>();
156
157 if (data->axis == GetJoyAxisBinding(JOY_AXIS_SLOT_X).axis)
158 {
159 m_joyMotion.x = Math::Neutral(data->value / 32768.0f, m_joystickDeadzone);
160 if (GetJoyAxisBinding(JOY_AXIS_SLOT_X).invert)
161 m_joyMotion.x *= -1.0f;
162 }
163
164 if (data->axis == GetJoyAxisBinding(JOY_AXIS_SLOT_Y).axis)
165 {
166 m_joyMotion.y = -Math::Neutral(data->value / 32768.0f, m_joystickDeadzone);
167 if (GetJoyAxisBinding(JOY_AXIS_SLOT_Y).invert)
168 m_joyMotion.y *= -1.0f;
169 }
170
171 if (data->axis == GetJoyAxisBinding(JOY_AXIS_SLOT_Z).axis)
172 {
173 m_joyMotion.z = -Math::Neutral(data->value / 32768.0f, m_joystickDeadzone);
174 if (GetJoyAxisBinding(JOY_AXIS_SLOT_Z).invert)
175 m_joyMotion.z *= -1.0f;
176 }
177
178 if (data->axis == GetJoyAxisBinding(JOY_AXIS_SLOT_CAM_X).axis)
179 {
180 m_joyMotionCam.x = -Math::Neutral(data->value / 32768.0f, m_joystickDeadzone);
181 if (GetJoyAxisBinding(JOY_AXIS_SLOT_CAM_X).invert)
182 m_joyMotionCam.x *= -1.0f;
183 }
184
185 if (data->axis == GetJoyAxisBinding(JOY_AXIS_SLOT_CAM_Y).axis)
186 {
187 m_joyMotionCam.y = -Math::Neutral(data->value / 32768.0f, m_joystickDeadzone);
188 if (GetJoyAxisBinding(JOY_AXIS_SLOT_CAM_Y).invert)
189 m_joyMotionCam.y *= -1.0f;
190 }
191
192 if (data->axis == GetJoyAxisBinding(JOY_AXIS_SLOT_CAM_Z).axis)
193 {
194 m_joyMotionCam.z = -Math::Neutral(data->value / 32768.0f, m_joystickDeadzone);
195 if (GetJoyAxisBinding(JOY_AXIS_SLOT_CAM_Z).invert)
196 m_joyMotionCam.z *= -1.0f;
197 }
198 }
199
200 event.motionInput = Math::Clamp(m_joyMotion + m_keyMotion, Math::Vector(-1.0f, -1.0f, -1.0f), Math::Vector(1.0f, 1.0f, 1.0f));
201 event.cameraInput = Math::Clamp(m_joyMotionCam + m_cameraKeyMotion, Math::Vector(-1.0f, -1.0f, -1.0f), Math::Vector(1.0f, 1.0f, 1.0f));
202 }
203
MouseMove(Math::IntPoint pos)204 void CInput::MouseMove(Math::IntPoint pos)
205 {
206 m_mousePos = Gfx::CEngine::GetInstancePointer()->WindowToInterfaceCoords(pos);
207 }
208
GetKeyState(InputSlot key) const209 bool CInput::GetKeyState(InputSlot key) const
210 {
211 return m_keyPresses[key];
212 }
213
GetMouseButtonState(int index) const214 bool CInput::GetMouseButtonState(int index) const
215 {
216 return (m_mouseButtonsState & (1<<index)) != 0;
217 }
218
ResetKeyStates()219 void CInput::ResetKeyStates()
220 {
221 GetLogger()->Trace("Reset key states\n");
222 m_keyMotion = Math::Vector(0.0f, 0.0f, 0.0f);
223 m_joyMotion = Math::Vector(0.0f, 0.0f, 0.0f);
224 m_cameraKeyMotion = Math::Vector(0.0f, 0.0f, 0.0f);
225 m_joyMotionCam = Math::Vector(0.0f, 0.0f, 0.0f);
226 for(int i=0; i<INPUT_SLOT_MAX; i++)
227 m_keyPresses[i] = false;
228 }
229
GetMousePos() const230 Math::Point CInput::GetMousePos() const
231 {
232 return m_mousePos;
233 }
234
SetDefaultInputBindings()235 void CInput::SetDefaultInputBindings()
236 {
237 for (int i = 0; i < INPUT_SLOT_MAX; i++)
238 {
239 m_inputBindings[i].primary = m_inputBindings[i].secondary = KEY_INVALID;
240 }
241
242 for (int i = 0; i < JOY_AXIS_SLOT_MAX; i++)
243 {
244 m_joyAxisBindings[i].axis = AXIS_INVALID;
245 m_joyAxisBindings[i].invert = false;
246 }
247
248 m_inputBindings[INPUT_SLOT_LEFT ].primary = KEY(LEFT);
249 m_inputBindings[INPUT_SLOT_RIGHT ].primary = KEY(RIGHT);
250 m_inputBindings[INPUT_SLOT_UP ].primary = KEY(UP);
251 m_inputBindings[INPUT_SLOT_DOWN ].primary = KEY(DOWN);
252 m_inputBindings[INPUT_SLOT_LEFT ].secondary = KEY(a);
253 m_inputBindings[INPUT_SLOT_RIGHT ].secondary = KEY(d);
254 m_inputBindings[INPUT_SLOT_UP ].secondary = KEY(w);
255 m_inputBindings[INPUT_SLOT_DOWN ].secondary = KEY(s);
256 m_inputBindings[INPUT_SLOT_GUP ].primary = VIRTUAL_KMOD(SHIFT);
257 m_inputBindings[INPUT_SLOT_GDOWN ].primary = VIRTUAL_KMOD(CTRL);
258 m_inputBindings[INPUT_SLOT_CAMERA ].primary = KEY(SPACE);
259 m_inputBindings[INPUT_SLOT_DESEL ].primary = KEY(KP_0);
260 m_inputBindings[INPUT_SLOT_ACTION ].primary = KEY(RETURN);
261 m_inputBindings[INPUT_SLOT_ACTION ].secondary = KEY(e);
262 m_inputBindings[INPUT_SLOT_CAM_LEFT ].primary = KEY(KP_4);
263 m_inputBindings[INPUT_SLOT_CAM_RIGHT].primary = KEY(KP_6);
264 m_inputBindings[INPUT_SLOT_CAM_UP ].primary = KEY(KP_8);
265 m_inputBindings[INPUT_SLOT_CAM_DOWN ].primary = KEY(KP_2);
266 m_inputBindings[INPUT_SLOT_CAM_NEAR ].primary = KEY(KP_PLUS);
267 m_inputBindings[INPUT_SLOT_CAM_AWAY ].primary = KEY(KP_MINUS);
268 m_inputBindings[INPUT_SLOT_CAM_ALT ].primary = VIRTUAL_KMOD(ALT);
269 m_inputBindings[INPUT_SLOT_NEXT ].primary = KEY(TAB);
270 m_inputBindings[INPUT_SLOT_HUMAN ].primary = KEY(HOME);
271 m_inputBindings[INPUT_SLOT_QUIT ].primary = KEY(ESCAPE);
272 m_inputBindings[INPUT_SLOT_HELP ].primary = KEY(F1);
273 m_inputBindings[INPUT_SLOT_PROG ].primary = KEY(F2);
274 m_inputBindings[INPUT_SLOT_VISIT ].primary = KEY(KP_PERIOD);
275 m_inputBindings[INPUT_SLOT_QUICKSAVE].primary = KEY(F5);
276 m_inputBindings[INPUT_SLOT_SPEED_DEC].primary = KEY(F6);
277 m_inputBindings[INPUT_SLOT_SPEED_RESET].primary = KEY(F7);
278 m_inputBindings[INPUT_SLOT_SPEED_INC].primary = KEY(F8);
279 m_inputBindings[INPUT_SLOT_QUICKLOAD].primary = KEY(F9);
280 m_inputBindings[INPUT_SLOT_PAUSE].primary = KEY(PAUSE);
281 m_inputBindings[INPUT_SLOT_PAUSE].secondary = KEY(p);
282 m_inputBindings[INPUT_SLOT_CMDLINE].primary = KEY(BACKQUOTE);
283
284 m_joyAxisBindings[JOY_AXIS_SLOT_X].axis = 0;
285 m_joyAxisBindings[JOY_AXIS_SLOT_Y].axis = 1;
286 m_joyAxisBindings[JOY_AXIS_SLOT_Z].axis = 2;
287 m_joyAxisBindings[JOY_AXIS_SLOT_CAM_X].axis = -1;
288 m_joyAxisBindings[JOY_AXIS_SLOT_CAM_Y].axis = -1;
289 m_joyAxisBindings[JOY_AXIS_SLOT_CAM_Z].axis = -1;
290 }
291
SetInputBinding(InputSlot slot,InputBinding binding)292 void CInput::SetInputBinding(InputSlot slot, InputBinding binding)
293 {
294 unsigned int index = static_cast<unsigned int>(slot);
295 m_inputBindings[index] = binding;
296 }
297
GetInputBinding(InputSlot slot)298 const InputBinding& CInput::GetInputBinding(InputSlot slot)
299 {
300 unsigned int index = static_cast<unsigned int>(slot);
301 return m_inputBindings[index];
302 }
303
SetJoyAxisBinding(JoyAxisSlot slot,JoyAxisBinding binding)304 void CInput::SetJoyAxisBinding(JoyAxisSlot slot, JoyAxisBinding binding)
305 {
306 unsigned int index = static_cast<unsigned int>(slot);
307 m_joyAxisBindings[index] = binding;
308 }
309
GetJoyAxisBinding(JoyAxisSlot slot)310 const JoyAxisBinding& CInput::GetJoyAxisBinding(JoyAxisSlot slot)
311 {
312 unsigned int index = static_cast<unsigned int>(slot);
313 return m_joyAxisBindings[index];
314 }
315
SetJoystickDeadzone(float zone)316 void CInput::SetJoystickDeadzone(float zone)
317 {
318 m_joystickDeadzone = zone;
319 }
320
GetJoystickDeadzone()321 float CInput::GetJoystickDeadzone()
322 {
323 return m_joystickDeadzone;
324 }
325
FindBinding(unsigned int key)326 InputSlot CInput::FindBinding(unsigned int key)
327 {
328 for (int i = 0; i < INPUT_SLOT_MAX; i++)
329 {
330 InputSlot slot = static_cast<InputSlot>(i);
331 InputBinding b = GetInputBinding(slot);
332 if(b.primary == key || b.secondary == key)
333 return slot;
334 }
335 return INPUT_SLOT_MAX;
336 }
337
SaveKeyBindings()338 void CInput::SaveKeyBindings()
339 {
340 std::stringstream key;
341 GetConfigFile().SetStringProperty("Keybindings", "_Version", "SDL2");
342 for (int i = 0; i < INPUT_SLOT_MAX; i++)
343 {
344 InputBinding b = GetInputBinding(static_cast<InputSlot>(i));
345
346 key.clear();
347 key.str("");
348 key << b.primary << " " << b.secondary;
349
350 GetConfigFile().SetStringProperty("Keybindings", m_keyTable[static_cast<InputSlot>(i)], key.str());
351 }
352
353 for (int i = 0; i < JOY_AXIS_SLOT_MAX; i++)
354 {
355 JoyAxisBinding b = GetJoyAxisBinding(static_cast<JoyAxisSlot>(i));
356
357 GetConfigFile().SetIntProperty("Setup", "JoystickAxisBinding"+boost::lexical_cast<std::string>(i), b.axis);
358 GetConfigFile().SetIntProperty("Setup", "JoystickAxisInvert"+boost::lexical_cast<std::string>(i), b.invert);
359 }
360 GetConfigFile().SetFloatProperty("Setup", "JoystickDeadzone", GetJoystickDeadzone());
361 }
362
LoadKeyBindings()363 void CInput::LoadKeyBindings()
364 {
365 std::stringstream skey;
366 std::string keys;
367 if (GetConfigFile().GetStringProperty("Keybindings", "_Version", keys) && keys == "SDL2") // Keybindings from SDL1.2 are incompatible with SDL2 !!
368 {
369 for (int i = 0; i < INPUT_SLOT_MAX; i++)
370 {
371 InputBinding b;
372
373 if (!GetConfigFile().GetStringProperty("Keybindings", m_keyTable[static_cast<InputSlot>(i)], keys))
374 continue;
375 skey.clear();
376 skey.str(keys);
377
378 skey >> b.primary;
379 skey >> b.secondary;
380
381 SetInputBinding(static_cast<InputSlot>(i), b);
382 }
383 }
384
385 for (int i = 0; i < JOY_AXIS_SLOT_MAX; i++)
386 {
387 JoyAxisBinding b;
388
389 if (!GetConfigFile().GetIntProperty("Setup", "JoystickAxisBinding"+boost::lexical_cast<std::string>(i), b.axis))
390 continue;
391
392 int x = 0;
393 GetConfigFile().GetIntProperty("Setup", "JoystickAxisInvert"+boost::lexical_cast<std::string>(i), x); // If doesn't exist, use default (0)
394 b.invert = (x != 0);
395
396 SetJoyAxisBinding(static_cast<JoyAxisSlot>(i), b);
397 }
398 float deadzone;
399 if (GetConfigFile().GetFloatProperty("Setup", "JoystickDeadzone", deadzone))
400 SetJoystickDeadzone(deadzone);
401 }
402
SearchKeyById(std::string id)403 InputSlot CInput::SearchKeyById(std::string id)
404 {
405 for(auto& key : m_keyTable)
406 {
407 if ( id == key.second )
408 {
409 return key.first;
410 }
411 }
412 return INPUT_SLOT_MAX;
413 }
414
GetKeysString(InputBinding binding)415 std::string CInput::GetKeysString(InputBinding binding)
416 {
417 std::ostringstream ss;
418 if ( binding.primary != KEY_INVALID )
419 {
420 std::string iNameStr;
421 if ( GetResource(RES_KEY, binding.primary, iNameStr) )
422 {
423 ss << iNameStr;
424
425 if ( binding.secondary != KEY_INVALID )
426 {
427 if ( GetResource(RES_KEY, binding.secondary, iNameStr) )
428 {
429 std::string textStr;
430 GetResource(RES_TEXT, RT_KEY_OR, textStr);
431
432 ss << textStr << iNameStr;
433 }
434 }
435 }
436 }
437 else
438 {
439 return "?";
440 }
441 return ss.str();
442 }
443
444
GetKeysString(InputSlot slot)445 std::string CInput::GetKeysString(InputSlot slot)
446 {
447 InputBinding b = GetInputBinding(slot);
448 return GetKeysString(b);
449 }
450