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