1 /*
2  * Copyright (C) 2006-2019 Christopho, Solarus - http://www.solarus-games.org
3  *
4  * Solarus 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  * Solarus 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 along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 #include "solarus/core/Debug.h"
18 #include "solarus/core/Equipment.h"
19 #include "solarus/core/Game.h"
20 #include "solarus/core/GameCommands.h"
21 #include "solarus/core/Savegame.h"
22 #include "solarus/entities/Hero.h"
23 #include "solarus/lua/LuaContext.h"
24 #include <lua.hpp>
25 #include <sstream>
26 
27 namespace Solarus {
28 
29 /**
30  * \brief Lua name of each value of the Command enum.
31  */
32 const std::map<GameCommand, std::string> GameCommands::command_names = {
33     { GameCommand::NONE, "" },
34     { GameCommand::ACTION, "action" },
35     { GameCommand::ATTACK, "attack" },
36     { GameCommand::ITEM_1, "item_1" },
37     { GameCommand::ITEM_2, "item_2" },
38     { GameCommand::PAUSE, "pause" },
39     { GameCommand::RIGHT, "right" },
40     { GameCommand::UP, "up" },
41     { GameCommand::LEFT, "left" },
42     { GameCommand::DOWN, "down" }
43 };
44 
45 const std::string GameCommands::direction_names[4] = {
46     "right",
47     "up",
48     "left",
49     "down"
50 };
51 
52 const uint16_t GameCommands::direction_masks[4] = {
53     0x0001,
54     0x0002,
55     0x0004,
56     0x0008
57 };
58 
59 const int GameCommands::masks_to_directions8[] = {
60     -1,  // none: stop
61      0,  // right
62      2,  // up
63      1,  // right + up
64      4,  // left
65     -1,  // left + right: stop
66      3,  // left + up
67     -1,  // left + right + up: stop
68      6,  // down
69      7,  // down + right
70     -1,  // down + up: stop
71     -1,  // down + right + up: stop
72      5,  // down + left
73     -1,  // down + left + right: stop
74     -1,  // down + left + up: stop
75     -1,  // down + left + right + up: stop
76 };
77 
78 /**
79  * \brief Constructor.
80  * \param game the game
81  */
GameCommands(Game & game)82 GameCommands::GameCommands(Game& game):
83   game(game),
84   customizing(false),
85   command_to_customize(GameCommand::NONE),
86   customize_callback_ref() {
87 
88   // Load the commands from the savegame.
89   for (const auto& kvp : command_names) {
90 
91     GameCommand command = kvp.first;
92     if (command == GameCommand::NONE) {
93       continue;
94     }
95 
96     // Keyboard.
97     InputEvent::KeyboardKey keyboard_key = get_saved_keyboard_binding(command);
98     keyboard_mapping[keyboard_key] = command;
99 
100     // Joypad.
101     const std::string& joypad_string = get_saved_joypad_binding(command);
102     joypad_mapping[joypad_string] = command;
103   }
104 }
105 
106 /**
107  * \brief Returns the savegame.
108  * \return The savegame.
109  */
get_savegame()110 Savegame& GameCommands::get_savegame() {
111   return game.get_savegame();
112 }
113 
114 /**
115  * \brief Returns the savegame.
116  * \return The savegame.
117  */
get_savegame() const118 const Savegame& GameCommands::get_savegame() const {
119   return game.get_savegame();
120 }
121 
122 /**
123  * \brief Returns whether the specified game command is pressed.
124  *
125  * The command can be activated from the keyboard or the joypad.
126  *
127  * \param command A game command.
128  * \return true if this game command is currently pressed.
129  */
is_command_pressed(GameCommand command) const130 bool GameCommands::is_command_pressed(GameCommand command) const {
131   return commands_pressed.find(command) != commands_pressed.end();
132 }
133 
134 /**
135  * \brief Returns the direction corresponding to the directional commands
136  * currently pressed by the player.
137  * \return The direction (0 to 7), or -1 if no directional command is pressed
138  * or if the combination of directional command is not a valid direction.
139  */
get_wanted_direction8() const140 int GameCommands::get_wanted_direction8() const {
141 
142   uint16_t direction_mask = 0x0000;
143   if (is_command_pressed(GameCommand::RIGHT)) {
144     direction_mask |= direction_masks[0];
145   }
146   if (is_command_pressed(GameCommand::UP)) {
147     direction_mask |= direction_masks[1];
148   }
149   if (is_command_pressed(GameCommand::LEFT)) {
150     direction_mask |= direction_masks[2];
151   }
152   if (is_command_pressed(GameCommand::DOWN)) {
153     direction_mask |= direction_masks[3];
154   }
155 
156   return masks_to_directions8[direction_mask];
157 }
158 
159 /**
160  * \brief This function is called by the game when a low-level event occurs.
161  * \param event An input event.
162  */
notify_input(const InputEvent & event)163 void GameCommands::notify_input(const InputEvent& event) {
164 
165   // If no game command is being customized, we look for a binding
166   // for this input event and we ignore the event if no binding is found.
167   // If a command is being customized, we consider instead this event as
168   // the new binding for this game command.
169 
170   if (event.is_keyboard_key_pressed()) {
171     keyboard_key_pressed(event.get_keyboard_key());
172   }
173   else if (event.is_keyboard_key_released()) {
174     keyboard_key_released(event.get_keyboard_key());
175   }
176   else if (event.is_joypad_button_pressed()) {
177     joypad_button_pressed(event.get_joypad_button());
178   }
179   else if (event.is_joypad_button_released()) {
180     joypad_button_released(event.get_joypad_button());
181   }
182   else if (event.is_joypad_axis_moved()) {
183     joypad_axis_moved(event.get_joypad_axis(), event.get_joypad_axis_state());
184   }
185   else if (event.is_joypad_hat_moved()) {
186     joypad_hat_moved(event.get_joypad_hat(), event.get_joypad_hat_direction());
187   }
188 }
189 
190 /**
191  * \brief This function is called when a low-level keyboard key is pressed.
192  * \param keyboard_key_pressed The key pressed.
193  */
keyboard_key_pressed(InputEvent::KeyboardKey keyboard_key_pressed)194 void GameCommands::keyboard_key_pressed(InputEvent::KeyboardKey keyboard_key_pressed) {
195 
196   // Retrieve the game command (if any) corresponding to this keyboard key.
197   const GameCommand command_pressed = get_command_from_keyboard(keyboard_key_pressed);
198 
199   if (!customizing) {
200     // If the key is mapped, notify the game.
201     if (command_pressed != GameCommand::NONE) {
202       game_command_pressed(command_pressed);
203     }
204   }
205   else {
206     customizing = false;
207 
208     if (command_pressed != command_to_customize) {
209       // Consider this keyboard key as the new mapping for the game command being customized.
210       set_keyboard_binding(command_to_customize, keyboard_key_pressed);
211       commands_pressed.insert(command_to_customize);
212     }
213     do_customization_callback();
214   }
215 }
216 
217 /**
218  * \brief This function is called when a low-level keyboard key is released.
219  * \param keyboard_control_released The key released.
220  */
keyboard_key_released(InputEvent::KeyboardKey keyboard_key_released)221 void GameCommands::keyboard_key_released(InputEvent::KeyboardKey keyboard_key_released) {
222 
223   // Retrieve the game command (if any) corresponding to this keyboard key.
224   GameCommand command_released = get_command_from_keyboard(keyboard_key_released);
225 
226   // If the keyboard key is mapped, notify the game.
227   if (command_released != GameCommand::NONE) {
228     game_command_released(command_released);
229   }
230 }
231 
232 /**
233  * \brief This function is called when a joypad button is pressed.
234  * \param button The button pressed.
235  */
joypad_button_pressed(int button)236 void GameCommands::joypad_button_pressed(int button) {
237 
238   // Retrieve the game command (if any) corresponding to this joypad button.
239   std::ostringstream oss;
240   oss << "button " << button;
241   const std::string& joypad_string = oss.str();
242   GameCommand command_pressed = get_command_from_joypad(joypad_string);
243 
244   if (!customizing) {
245     // If the joypad button is mapped, notify the game.
246     if (command_pressed != GameCommand::NONE) {
247       game_command_pressed(command_pressed);
248     }
249   }
250   else {
251     customizing = false;
252 
253     if (command_pressed != command_to_customize) {
254       // Consider this button as the new mapping for the game command being customized.
255       set_joypad_binding(command_to_customize, joypad_string);
256       commands_pressed.insert(command_to_customize);
257     }
258     do_customization_callback();
259   }
260 }
261 
262 /**
263  * \brief This function is called when a joypad button is released.
264  * \param button The button released.
265  */
joypad_button_released(int button)266 void GameCommands::joypad_button_released(int button) {
267 
268   // Retrieve the game command (if any) corresponding to this joypad button.
269   std::ostringstream oss;
270   oss << "button " << button;
271   const std::string& joypad_string = oss.str();
272   GameCommand command_released = get_command_from_joypad(joypad_string);
273 
274   // If the key is mapped, notify the game.
275   if (command_released != GameCommand::NONE) {
276     game_command_released(command_released);
277   }
278 }
279 
280 /**
281  * \brief This function is called when a joypad axis is moved.
282  * \param axis The axis moved.
283  * \param state The new axis direction (-1: left or up, 0: centered, 1: right or down).
284  */
joypad_axis_moved(int axis,int state)285 void GameCommands::joypad_axis_moved(int axis, int state) {
286 
287   if (state == 0) {
288     // Axis in centered position.
289 
290     std::ostringstream oss;
291     oss << "axis " << axis << " +";
292     GameCommand command_released = get_command_from_joypad(oss.str());
293     if (command_released != GameCommand::NONE) {
294       game_command_released(command_released);
295     }
296 
297     oss.str("");
298     oss << "axis " << axis << " -";
299     command_released = get_command_from_joypad(oss.str());
300     if (command_released != GameCommand::NONE) {
301       game_command_released(command_released);
302     }
303   }
304   else {
305     // Axis not centered.
306 
307     std::ostringstream oss;
308     oss << "axis " << axis << ((state > 0) ? " +" : " -");
309     const std::string& joypad_string = oss.str();
310 
311     oss.str("");
312     oss << "axis " << axis << ((state > 0) ? " -" : " +");
313     const std::string& inverse_joypad_string = oss.str();
314 
315     GameCommand command_pressed = get_command_from_joypad(joypad_string);
316     GameCommand inverse_command_pressed = get_command_from_joypad(inverse_joypad_string);
317 
318     if (!customizing) {
319 
320       // If the command is mapped, notify the game.
321       if (command_pressed != GameCommand::NONE) {
322         if (is_command_pressed(inverse_command_pressed)) {
323           game_command_released(inverse_command_pressed);
324         }
325         game_command_pressed(command_pressed);
326       }
327     }
328     else {
329       customizing = false;
330 
331       if (command_pressed != command_to_customize) {
332         // Consider this axis movement as the new mapping for the game command being customized.
333         set_joypad_binding(command_to_customize, joypad_string);
334         commands_pressed.insert(command_to_customize);
335       }
336       do_customization_callback();
337     }
338   }
339 }
340 
341 /**
342  * \brief This function is called when a joypad hat is moved.
343  * \param hat The hat moved.
344  * \param direction The new hat position (-1: centered, 0 to 7: a direction).
345  */
joypad_hat_moved(int hat,int value)346 void GameCommands::joypad_hat_moved(int hat, int value) {
347 
348   if (value == -1) {
349     // Hat in centered position.
350 
351     for (int i = 0; i < 4; i++) {
352 
353       std::ostringstream oss;
354       oss << "hat " << hat << ' ' << direction_names[i];
355       GameCommand command_released = get_command_from_joypad(oss.str());
356 
357       if (command_released != GameCommand::NONE) {
358         game_command_released(command_released);
359       }
360     }
361   }
362   else {
363 
364     int direction_1 = -1;
365     int direction_2 = -1;
366 
367     switch (value) {
368 
369     case 0: // right
370       direction_1 = 0;
371       break;
372 
373     case 1: // right-up
374       direction_1 = 1;
375       direction_2 = 0;
376       break;
377 
378     case 2: // up
379       direction_1 = 1;
380       break;
381 
382     case 3: // left-up
383       direction_1 = 1;
384       direction_2 = 2;
385       break;
386 
387     case 4: // left
388       direction_1 = 2;
389       break;
390 
391     case 5: // left-down
392       direction_1 = 3;
393       direction_2 = 2;
394       break;
395 
396     case 6: // down
397       direction_1 = 3;
398       break;
399 
400     case 7: // right-down
401       direction_1 = 3;
402       direction_2 = 0;
403     }
404 
405     std::ostringstream oss;
406     oss << "hat " << hat << ' ' << direction_names[direction_1];
407     const std::string& joypad_string_1 = oss.str();
408     GameCommand command_1 = get_command_from_joypad(joypad_string_1);
409 
410     oss.str("");
411     oss << "hat " << hat << ' ' << direction_names[(direction_1 + 2) % 4];
412     const std::string& inverse_joypad_string_1 = oss.str();
413     GameCommand inverse_command_1 = get_command_from_joypad(inverse_joypad_string_1);
414 
415     GameCommand command_2 = GameCommand::NONE;
416     GameCommand inverse_command_2 = GameCommand::NONE;
417 
418     if (direction_2 != -1) {
419       oss.str("");
420       oss << "hat " << hat << ' ' << direction_names[direction_2];
421       const std::string& joypad_string_2 = oss.str();
422       command_2 = get_command_from_joypad(joypad_string_2);
423 
424       oss.str("");
425       oss << "hat " << hat << ' ' << direction_names[(direction_2 + 2) % 4];
426       const std::string& inverse_joypad_string_2 = oss.str();
427       inverse_command_2 = get_command_from_joypad(inverse_joypad_string_2);
428     }
429     else {
430       std::ostringstream oss;
431       oss << "hat " << hat << ' ' << direction_names[(direction_1 + 1) % 4];
432       const std::string& joypad_string_2 = oss.str();
433       command_2 = get_command_from_joypad(joypad_string_2);
434 
435       oss.str("");
436       oss << "hat " << hat << ' ' << direction_names[(direction_1 + 3) % 4];
437       const std::string& inverse_joypad_string_2 = oss.str();
438       inverse_command_2 = get_command_from_joypad(inverse_joypad_string_2);
439     }
440 
441     if (!customizing) {
442 
443       // If the key is mapped, notify the game.
444       if (command_1 != GameCommand::NONE) {
445 
446         if (is_command_pressed(inverse_command_1)) {
447           game_command_released(inverse_command_1);
448         }
449         game_command_pressed(command_1);
450       }
451 
452       if (direction_2 != -1) {
453         if (is_command_pressed(inverse_command_2)) {
454           game_command_released(inverse_command_2);
455         }
456         game_command_pressed(command_2);
457       }
458       else {
459         if (is_command_pressed(command_2)) {
460           game_command_released(command_2);
461         }
462         if (is_command_pressed(inverse_command_2)) {
463           game_command_released(inverse_command_2);
464         }
465       }
466     }
467     else {
468       customizing = false;
469 
470       if (command_1 != command_to_customize) {
471         // Consider this hat movement as the new mapping for the game command being customized.
472         set_joypad_binding(command_to_customize, joypad_string_1);
473         commands_pressed.insert(command_to_customize);
474       }
475       do_customization_callback();
476     }
477   }
478 }
479 
480 /**
481  * \brief This function is called when a game command is pressed.
482  *
483  * This event may come from the keyboard or the joypad.
484  *
485  * \param command The game command pressed.
486  */
game_command_pressed(GameCommand command)487 void GameCommands::game_command_pressed(GameCommand command) {
488 
489   commands_pressed.insert(command);
490   game.notify_command_pressed(command);
491 }
492 
493 /**
494  * \brief This function is called when a game command is pressed.
495  *
496  * This event may come from the keyboard or the joypad.
497  *
498  * \param command The game command released.
499  */
game_command_released(GameCommand command)500 void GameCommands::game_command_released(GameCommand command) {
501 
502   commands_pressed.erase(command);
503   game.notify_command_released(command);
504 }
505 
506 /**
507  * \brief Returns the low-level keyboard key where the specified game command
508  * is currently mapped.
509  * \param command A game command.
510  * \return The keyboard key mapped to this game command, or InputEvent::KEY_GameCommand::NONE
511  * if the command is not mapped to a keyboard key.
512  */
get_keyboard_binding(GameCommand command) const513 InputEvent::KeyboardKey GameCommands::get_keyboard_binding(GameCommand command) const {
514 
515   for (const auto& kvp: keyboard_mapping) {
516 
517     if (kvp.second == command) {
518       return kvp.first;
519     }
520   }
521 
522   return InputEvent::KeyboardKey::NONE;
523 }
524 
525 /**
526  * \brief Maps the specified keyboard key to a new game command.
527  *
528  * If this key was already mapped to a command, both commands are switched.
529  * (The old command gets the previous key of the new command.)
530  *
531  * \param command A game command.
532  * \param key The keyboard key to map to this game command, or InputEvent::KEY_NONE
533  * to unmap the command.
534  */
set_keyboard_binding(GameCommand command,InputEvent::KeyboardKey key)535 void GameCommands::set_keyboard_binding(GameCommand command, InputEvent::KeyboardKey key) {
536 
537   InputEvent::KeyboardKey previous_key = get_keyboard_binding(command);
538   GameCommand previous_command = get_command_from_keyboard(key);
539 
540   if (previous_key != InputEvent::KeyboardKey::NONE) {
541     // The command was already assigned.
542     if (previous_command != GameCommand::NONE) {
543       // This key is already mapped to a command.
544       keyboard_mapping[previous_key] = previous_command;
545       set_saved_keyboard_binding(previous_command, previous_key);
546     }
547     else {
548       keyboard_mapping.erase(previous_key);
549     }
550   }
551 
552   if (key != InputEvent::KeyboardKey::NONE) {
553       keyboard_mapping[key] = command;
554   }
555   set_saved_keyboard_binding(command, key);
556 }
557 
558 /**
559  * \brief Returns a string representing the joypad action where the specified
560  * game command is currently mapped.
561  * \param command A game command.
562  * \return The joypad action mapped to this game command, or an empty string if
563  * this game command is not mapped to a joypad action.
564  */
get_joypad_binding(GameCommand command) const565 const std::string& GameCommands::get_joypad_binding(GameCommand command) const {
566 
567   for (const auto& kvp: joypad_mapping) {
568 
569     if (kvp.second == command) {
570       return kvp.first;
571     }
572   }
573 
574   static const std::string empty_string;
575   return empty_string;
576 }
577 
578 /**
579  * \brief Maps the specified joypad action to a new game command.
580  *
581  * If this joypad action was already mapped to a command, both commands are switched.
582  * (The old command gets the previous joypad action of the new command.)
583  *
584  * \param command A game command.
585  * \param joypad_string A string describing the joypad action to map to this
586  * game command, or an empty string to unmap the command.
587  */
set_joypad_binding(GameCommand command,const std::string & joypad_string)588 void GameCommands::set_joypad_binding(GameCommand command, const std::string& joypad_string) {
589 
590   const std::string& previous_joypad_string = get_joypad_binding(command);
591   GameCommand previous_command = get_command_from_joypad(joypad_string);
592 
593   if (!previous_joypad_string.empty()) {
594     // The command was already assigned.
595     if (previous_command != GameCommand::NONE) {
596       // This joypad action is already mapped to a command.
597       joypad_mapping[previous_joypad_string] = previous_command;
598       set_saved_joypad_binding(previous_command, previous_joypad_string);
599     }
600     else {
601       joypad_mapping.erase(previous_joypad_string);
602     }
603   }
604 
605   if (!joypad_string.empty()) {
606     joypad_mapping[joypad_string] = command;
607   }
608   set_saved_joypad_binding(command, joypad_string);
609 }
610 
611 /**
612  * \brief Returns the name of the savegame variable that stores the keyboard
613  * mapping of a game command.
614  * \param command A game command.
615  * \return The savegame variable that stores the keyboard key mapped to this
616  * game command, or an empty string if this command is GameCommand::NONE.
617  */
get_keyboard_binding_savegame_variable(GameCommand command) const618 const std::string& GameCommands::get_keyboard_binding_savegame_variable(
619     GameCommand command) const {
620 
621   static const std::map<GameCommand, std::string> savegame_variables = {
622       { GameCommand::NONE, "" },
623       { GameCommand::ACTION, Savegame::KEY_KEYBOARD_ACTION },
624       { GameCommand::ATTACK, Savegame::KEY_KEYBOARD_ATTACK },
625       { GameCommand::ITEM_1, Savegame::KEY_KEYBOARD_ITEM_1 },
626       { GameCommand::ITEM_2, Savegame::KEY_KEYBOARD_ITEM_2 },
627       { GameCommand::PAUSE, Savegame::KEY_KEYBOARD_PAUSE },
628       { GameCommand::RIGHT, Savegame::KEY_KEYBOARD_RIGHT },
629       { GameCommand::UP, Savegame::KEY_KEYBOARD_UP },
630       { GameCommand::LEFT, Savegame::KEY_KEYBOARD_LEFT },
631       { GameCommand::DOWN, Savegame::KEY_KEYBOARD_DOWN }
632   };
633 
634   return savegame_variables.find(command)->second;
635 }
636 
637 /**
638  * \brief Returns the name of the savegame variable that stores the joypad
639  * mapping of a game command.
640  * \param command A game command.
641  * \return The savegame variable that stores the joypad action mapped to this
642  * game command, or an empty string if this command is GameCommand::NONE.
643  */
get_joypad_binding_savegame_variable(GameCommand command) const644 const std::string& GameCommands::get_joypad_binding_savegame_variable(
645     GameCommand command) const {
646 
647   static const std::map<GameCommand, std::string> savegame_variables = {
648       { GameCommand::NONE, "" },
649       { GameCommand::ACTION, Savegame::KEY_JOYPAD_ACTION },
650       { GameCommand::ATTACK, Savegame::KEY_JOYPAD_ATTACK },
651       { GameCommand::ITEM_1, Savegame::KEY_JOYPAD_ITEM_1 },
652       { GameCommand::ITEM_2, Savegame::KEY_JOYPAD_ITEM_2 },
653       { GameCommand::PAUSE, Savegame::KEY_JOYPAD_PAUSE },
654       { GameCommand::RIGHT, Savegame::KEY_JOYPAD_RIGHT },
655       { GameCommand::UP, Savegame::KEY_JOYPAD_UP },
656       { GameCommand::LEFT, Savegame::KEY_JOYPAD_LEFT },
657       { GameCommand::DOWN, Savegame::KEY_JOYPAD_DOWN }
658   };
659 
660   return savegame_variables.find(command)->second;
661 }
662 
663 /**
664  * \brief Determines from the savegame the low-level keyboard key where the
665  * specified game command is mapped.
666  * \param command A game command.
667  * \return The keyboard key mapped to this game command in the savegame.
668  */
get_saved_keyboard_binding(GameCommand command) const669 InputEvent::KeyboardKey GameCommands::get_saved_keyboard_binding(
670     GameCommand command) const {
671 
672   const std::string& savegame_variable = get_keyboard_binding_savegame_variable(command);
673   const std::string& keyboard_key_name = get_savegame().get_string(savegame_variable);
674   return name_to_enum(keyboard_key_name, InputEvent::KeyboardKey::NONE);
675 }
676 
677 /**
678  * \brief Saves the low-level keyboard command where the specified game key is
679  * mapped.
680  * \param command A game command.
681  * \param keyboard_key The keyboard key to map to this game command in the
682  * savegame.
683  */
set_saved_keyboard_binding(GameCommand command,InputEvent::KeyboardKey keyboard_key)684 void GameCommands::set_saved_keyboard_binding(
685     GameCommand command, InputEvent::KeyboardKey keyboard_key) {
686 
687   const std::string& savegame_variable = get_keyboard_binding_savegame_variable(command);
688   const std::string& keyboard_key_name = enum_to_name(keyboard_key);
689   get_savegame().set_string(savegame_variable, keyboard_key_name);
690 }
691 
692 /**
693  * \brief Returns the game command (if any) associated to the specified
694  * keyboard key.
695  * \param key A keyboard key.
696  * \return The game command mapped to that key or GameCommand::NONE.
697  */
get_command_from_keyboard(InputEvent::KeyboardKey key) const698 GameCommand GameCommands::get_command_from_keyboard(
699     InputEvent::KeyboardKey key) const {
700 
701   const auto& it = keyboard_mapping.find(key);
702   if (it != keyboard_mapping.end()) {
703     return it->second;
704   }
705 
706   return GameCommand::NONE;
707 }
708 
709 /**
710  * \brief Determines from the savegame the low-level joypad action where the
711  * specified game command is mapped.
712  * \param command A game command.
713  * \return The joypad action mapped to this game command in the savegame.
714  */
get_saved_joypad_binding(GameCommand command) const715 std::string GameCommands::get_saved_joypad_binding(
716     GameCommand command) const {
717 
718   const std::string& savegame_variable = get_joypad_binding_savegame_variable(command);
719   return get_savegame().get_string(savegame_variable);
720 }
721 
722 /**
723  * \brief Saves the low-level joypad action where the specified game command
724  * is mapped.
725  * \param command A game command.
726  * \return The joypad action to map to this game command in the savegame.
727  */
set_saved_joypad_binding(GameCommand command,const std::string & joypad_string)728 void GameCommands::set_saved_joypad_binding(
729     GameCommand command, const std::string& joypad_string) {
730 
731   const std::string& savegame_variable = get_joypad_binding_savegame_variable(command);
732   get_savegame().set_string(savegame_variable, joypad_string);
733 }
734 
735 /**
736  * \brief Returns the game command (if any) associated to the specified
737  * joypad action.
738  * \param joypad_string A joypad action.
739  * \return The game command mapped to that joypad action or GameCommand::NONE.
740  */
get_command_from_joypad(const std::string & joypad_string) const741 GameCommand GameCommands::get_command_from_joypad(
742     const std::string& joypad_string) const {
743 
744   const auto& it = joypad_mapping.find(joypad_string);
745   if (it != joypad_mapping.end()) {
746     return it->second;
747   }
748 
749   return GameCommand::NONE;
750 }
751 
752 // customization
753 
754 /**
755  * \brief Sets the specified command to be customized.
756  *
757  * After this function is called, the next keyboard or joypad event received will
758  * not be treated normally; it will be considered as the new keyboard or joypad
759  * binding for this game key. Then, keyboard and joypad events will be treated
760  * normally again.
761  *
762  * \param command The command to customize.
763  * \param callback_ref Lua ref to a function to call when the customization
764  * finishes, or an empty ref.
765  */
customize(GameCommand command,const ScopedLuaRef & callback_ref)766 void GameCommands::customize(
767     GameCommand command,
768     const ScopedLuaRef& callback_ref
769 ) {
770   this->customizing = true;
771   this->command_to_customize = command;
772   this->customize_callback_ref = callback_ref;
773 }
774 
775 /**
776  * \brief Returns whether the player is currently customizing a command.
777  * \return true if the player is currently customizing a command.
778  */
is_customizing() const779 bool GameCommands::is_customizing() const {
780   return customizing;
781 }
782 
783 /**
784  * \brief When the player is customizing a command, returns the command that
785  * is being customized.
786  * \return The command being customized.
787  */
get_command_to_customize() const788 GameCommand GameCommands::get_command_to_customize() const {
789 
790   Debug::check_assertion(is_customizing(),
791       "The player is not customizing a command");
792   return command_to_customize;
793 }
794 
795 /**
796  * \brief Calls the Lua function that was registered to be called after a
797  * command customization phase.
798  */
do_customization_callback()799 void GameCommands::do_customization_callback() {
800 
801   customize_callback_ref.clear_and_call("capture command callback");
802 }
803 
804 /**
805  * \brief Returns whether a string describes a valid joypad action.
806  *
807  * The string should have one of the following forms:
808  * - "button X" where X is the index of a joypad button.
809  * - "axis X +" where X is the index of a joypad axis.
810  * - "axis X -" where X is the index of a joypad axis.
811  * - "hat X Y" where X is the index of a joypad hat and Y is a direction (0 to 7).
812  *
813  * \param joypad_string The string to check.
814  * \return true if this string is a valid joypad action.
815  */
is_joypad_string_valid(const std::string &)816 bool GameCommands::is_joypad_string_valid(const std::string& /* joypad_string */) {
817 
818   // TODO
819   return true;
820 }
821 
822 /**
823  * \brief Returns the name of a game command.
824  * \param command a game command.
825  * \return The name of this command, or an empty string if the command is GameCommand::NONE.
826  */
get_command_name(GameCommand command)827 const std::string& GameCommands::get_command_name(GameCommand command) {
828 
829   return command_names.find(command)->second;
830 }
831 
832 /**
833  * \brief Returns a game command given its Lua name.
834  * \param command_name Lua name of a game command.
835  * \return The corresponding game command, or GameCommand::NONE if the name is invalid.
836  */
get_command_by_name(const std::string & command_name)837 GameCommand GameCommands::get_command_by_name(
838     const std::string& command_name) {
839 
840   for (const auto& kvp : command_names) {
841     if (kvp.second == command_name) {
842       return kvp.first;
843     }
844   }
845   return GameCommand::NONE;
846 }
847 
848 }
849 
850