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