Antkeeper  0.0.1
gamepad-config-menu-state.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code 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  * Antkeeper source code 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. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
22 #include "game/controls.hpp"
23 #include "game/game.hpp"
24 #include <engine/scene/text.hpp>
25 #include <engine/debug/log.hpp>
27 #include "game/menu.hpp"
28 #include "game/controls.hpp"
29 #include "game/strings.hpp"
31 
32 using namespace hash::literals;
33 
34 
36  game_state(ctx),
37  action_remapped(false)
38 {
39  debug::log_trace("Entering gamepad config menu state...");
40 
41  // Add control menu items
42  add_control_item(ctx.movement_action_map, ctx.move_forward_action, "control_move_forward");
43  add_control_item(ctx.movement_action_map, ctx.move_back_action, "control_move_back");
44  add_control_item(ctx.movement_action_map, ctx.move_left_action, "control_move_left");
45  add_control_item(ctx.movement_action_map, ctx.move_right_action, "control_move_right");
46  add_control_item(ctx.movement_action_map, ctx.move_up_action, "control_move_up");
47  add_control_item(ctx.movement_action_map, ctx.move_down_action, "control_move_down");
48  add_control_item(ctx.movement_action_map, ctx.pause_action, "control_pause");
49 
50  // Construct menu item texts
51  back_text = std::make_unique<scene::text>();
52 
53  // Build list of menu item texts
54  ctx.menu_item_texts.push_back({back_text.get(), nullptr});
55 
56  // Set content of menu item texts
57  back_text->set_content(get_string(ctx, "back"));
58 
59  // Init menu item index
60  ::menu::init_menu_item_index(ctx, "gamepad_config");
61 
67 
68  // Construct menu item callbacks
69  auto select_back_callback = [&ctx]()
70  {
71  // Disable menu controls
72  ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx)));
73 
75  (
76  ctx,
77  [&ctx]()
78  {
79  // Queue change to controls menu state
80  ctx.function_queue.push
81  (
82  [&ctx]()
83  {
84  ctx.state_machine.pop();
85  ctx.state_machine.emplace(std::make_unique<controls_menu_state>(ctx));
86  }
87  );
88  }
89  );
90  };
91 
92  // Build list of menu select callbacks
93  ctx.menu_select_callbacks.push_back(select_back_callback);
94 
95  // Build list of menu left callbacks
96  ctx.menu_left_callbacks.push_back(nullptr);
97 
98  // Build list of menu right callbacks
99  ctx.menu_right_callbacks.push_back(nullptr);
100 
101  // Set menu back callback
102  ctx.menu_back_callback = select_back_callback;
103 
104  // Queue menu control setup
105  ctx.function_queue.push(std::bind(::enable_menu_controls, std::ref(ctx)));
106 
107  // Fade in menu
108  ::menu::fade_in(ctx, nullptr);
109 
110  debug::log_trace("Entered gamepad config menu state");
111 }
112 
114 {
115  debug::log_trace("Exiting gamepad config menu state...");
116 
117  // Destruct menu
123 
124  if (action_remapped)
125  {
126  // Update control profile
128 
129  // Save control profile
130  ctx.resource_manager->set_write_path(ctx.controls_path);
132  }
133 
134  debug::log_trace("Exited gamepad config menu state");
135 }
136 
137 std::string gamepad_config_menu_state::get_mapping_string(const input::action_map& action_map, const input::action& control)
138 {
139  std::string mapping_string;
140 
141  if (auto gamepad_axis_mappings = action_map.get_gamepad_axis_mappings(control); !gamepad_axis_mappings.empty())
142  {
143  const auto& gamepad_axis_mapping = gamepad_axis_mappings.front();
144 
145  switch (gamepad_axis_mapping.axis)
146  {
148  if (gamepad_axis_mapping.direction)
149  {
150  mapping_string = get_string(ctx, "gamepad_left_stick_left");
151  }
152  else
153  {
154  mapping_string = get_string(ctx, "gamepad_left_stick_right");
155  }
156  break;
157 
159  if (gamepad_axis_mapping.direction)
160  {
161  mapping_string = get_string(ctx, "gamepad_left_stick_up");
162  }
163  else
164  {
165  mapping_string = get_string(ctx, "gamepad_left_stick_down");
166  }
167  break;
168 
170  if (gamepad_axis_mapping.direction)
171  {
172  mapping_string = get_string(ctx, "gamepad_right_stick_left");
173  }
174  else
175  {
176  mapping_string = get_string(ctx, "gamepad_right_stick_right");
177  }
178  break;
179 
181  if (gamepad_axis_mapping.direction)
182  {
183  mapping_string = get_string(ctx, "gamepad_right_stick_up");
184  }
185  else
186  {
187  mapping_string = get_string(ctx, "gamepad_right_stick_down");
188  }
189  break;
190 
192  mapping_string = get_string(ctx, "gamepad_left_trigger");
193  break;
194 
196  mapping_string = get_string(ctx, "gamepad_right_trigger");
197  break;
198 
199  default:
200  {
201  const char sign = (gamepad_axis_mapping.direction) ? '-' : '+';
202  const std::string format_string = get_string(ctx, "gamepad_axis_n_format");
203  mapping_string = std::vformat(format_string, std::make_format_args(std::to_underlying(gamepad_axis_mapping.axis), sign));
204  break;
205  }
206  }
207  }
208  else if (auto gamepad_button_mappings = action_map.get_gamepad_button_mappings(control); !gamepad_button_mappings.empty())
209  {
210  const auto& gamepad_button_mapping = gamepad_button_mappings.front();
211  switch (gamepad_button_mapping.button)
212  {
214  mapping_string = get_string(ctx, "gamepad_button_a");
215  break;
216 
218  mapping_string = get_string(ctx, "gamepad_button_b");
219  break;
220 
222  mapping_string = get_string(ctx, "gamepad_button_x");
223  break;
224 
226  mapping_string = get_string(ctx, "gamepad_button_y");
227  break;
228 
230  mapping_string = get_string(ctx, "gamepad_button_back");
231  break;
232 
234  mapping_string = get_string(ctx, "gamepad_button_guide");
235  break;
236 
238  mapping_string = get_string(ctx, "gamepad_button_start");
239  break;
240 
242  mapping_string = get_string(ctx, "gamepad_button_left_stick");
243  break;
244 
246  mapping_string = get_string(ctx, "gamepad_button_right_stick");
247  break;
248 
250  mapping_string = get_string(ctx, "gamepad_button_left_shoulder");
251  break;
252 
254  mapping_string = get_string(ctx, "gamepad_button_right_shoulder");
255  break;
256 
258  mapping_string = get_string(ctx, "gamepad_button_dpad_up");
259  break;
260 
262  mapping_string = get_string(ctx, "gamepad_button_dpad_down");
263  break;
264 
266  mapping_string = get_string(ctx, "gamepad_button_dpad_left");
267  break;
268 
270  mapping_string = get_string(ctx, "gamepad_button_dpad_right");
271  break;
272 
273  default:
274  {
275  const std::string format_string = get_string(ctx, "gamepad_button_n_format");
276  mapping_string = std::vformat(format_string, std::make_format_args(std::to_underlying(gamepad_button_mapping.button)));
277  break;
278  }
279  }
280  }
281  else
282  {
283  mapping_string = get_string(ctx, "control_unmapped");
284  }
285 
286  return mapping_string;
287 }
288 
289 void gamepad_config_menu_state::add_control_item(input::action_map& action_map, input::action& control, hash::fnv1a32_t control_name_hash)
290 {
291  // Construct texts
292  auto name_text = std::make_unique<scene::text>();
293  auto value_text = std::make_unique<scene::text>();
294 
295  // Add texts to list of menu item texts
296  ctx.menu_item_texts.push_back({name_text.get(), value_text.get()});
297 
298  // Set control name and mapping texts
299  name_text->set_content(get_string(ctx, control_name_hash));
300  value_text->set_content(get_mapping_string(action_map, control));
301 
302  // Callback invoked when an input has been mapped to the control
303  auto input_mapped_callback = [this, &ctx = this->ctx, action_map = &action_map, control = &control, value_text = value_text.get()](const auto& event)
304  {
305  if (event.mapping.get_mapping_type() != input::mapping_type::key)
306  {
307  this->action_remapped = true;
308 
309  // Remove gamepad axis mappings and gamepad button mappings mapped to the control
310  action_map->remove_mappings(*control, input::mapping_type::gamepad_axis);
311  action_map->remove_mappings(*control, input::mapping_type::gamepad_button);
312 
313  action_map->add_mapping(*control, event.mapping);
314  }
315 
316  // Update control mapping text
317  value_text->set_content(this->get_mapping_string(*action_map, *control));
319 
320  // Queue disabling of input mapper re-enabling of menu controls
321  ctx.function_queue.push
322  (
323  [&ctx]()
324  {
327  }
328  );
329  };
330 
331  // Callback invoked when the control menu item has been selected
332  auto select_callback = [this, &ctx = this->ctx, action_map = &action_map, control = &control, value_text = value_text.get(), input_mapped_callback]()
333  {
334  // Set control mapping text to "..."
335  value_text->set_content(get_string(ctx, "control_mapping"));
337 
338  // Setup input mapped callbacks
339  gamepad_axis_mapped_subscription = ctx.input_mapper.get_gamepad_axis_mapped_channel().subscribe
340  (
341  input_mapped_callback
342  );
343  gamepad_button_mapped_subscription = ctx.input_mapper.get_gamepad_button_mapped_channel().subscribe
344  (
345  input_mapped_callback
346  );
347  key_mapped_subscription = ctx.input_mapper.get_key_mapped_channel().subscribe
348  (
349  input_mapped_callback
350  );
351 
352  // Queue disabling of menu controls and enabling of input mapper
353  ctx.function_queue.push
354  (
355  [&]()
356  {
358  ctx.input_mapper.connect(ctx.input_manager->get_event_dispatcher());
359  }
360  );
361  };
362 
363  control_item_texts.emplace_back(std::move(name_text));
364  control_item_texts.emplace_back(std::move(value_text));
365 
366  // Register menu item callbacks
367  ctx.menu_select_callbacks.push_back(select_callback);
368  ctx.menu_left_callbacks.push_back(nullptr);
369  ctx.menu_right_callbacks.push_back(nullptr);
370 }
Abstract base class for game states.
Definition: game-state.hpp:29
::game & ctx
Definition: game-state.hpp:44
Definition: game.hpp:121
std::vector< std::function< void()> > menu_left_callbacks
Definition: game.hpp:348
input::action move_up_action
Definition: game.hpp:220
input::action move_down_action
Definition: game.hpp:221
std::vector< std::function< void()> > menu_right_callbacks
Definition: game.hpp:349
hsm::state_machine< game_state > state_machine
Definition: game.hpp:299
input::action move_right_action
Definition: game.hpp:219
input::action pause_action
Definition: game.hpp:224
std::vector< std::function< void()> > menu_select_callbacks
Definition: game.hpp:347
std::filesystem::path controls_path
Definition: game.hpp:159
std::string control_profile_filename
Definition: game.hpp:195
input::action move_left_action
Definition: game.hpp:218
input::action move_forward_action
Definition: game.hpp:216
std::vector< std::tuple< scene::text *, scene::text * > > menu_item_texts
Definition: game.hpp:351
std::unique_ptr< resource_manager > resource_manager
Definition: game.hpp:152
std::queue< std::function< void()> > function_queue
Definition: game.hpp:303
input::action move_back_action
Definition: game.hpp:217
std::function< void()> menu_back_callback
Definition: game.hpp:350
input::action_map movement_action_map
Definition: game.hpp:213
input::mapper input_mapper
Definition: game.hpp:214
std::unique_ptr< app::input_manager > input_manager
Definition: game.hpp:172
std::shared_ptr<::control_profile > control_profile
Definition: game.hpp:196
Maps input to a set of contextually-related actions.
Definition: action-map.hpp:43
std::vector< gamepad_button_mapping > get_gamepad_button_mappings(const action &action) const
Returns all of the gamepad button mappings associated with an action.
Definition: action-map.cpp:427
std::vector< gamepad_axis_mapping > get_gamepad_axis_mappings(const action &action) const
Returns all of the gamepad axis mappings associated with an action.
Definition: action-map.cpp:412
Evaluates an activation state given input values and publishes events on activation state changes.
Definition: action.hpp:33
::event::channel< key_mapped_event > & get_key_mapped_channel() noexcept
Returns the channel through which key mapped events are published.
Definition: mapper.hpp:66
void disconnect()
Disconnects all input event signals from the mapper.
Definition: mapper.cpp:35
::event::channel< gamepad_button_mapped_event > & get_gamepad_button_mapped_channel() noexcept
Returns the channel through which gamepad button mapped events are published.
Definition: mapper.hpp:60
::event::channel< gamepad_axis_mapped_event > & get_gamepad_axis_mapped_channel() noexcept
Returns the channel through which gamepad axis mapped events are published.
Definition: mapper.hpp:54
void connect(::event::dispatcher &dispatcher)
Connects the input event signals of an event dispatcher to the mapper.
Definition: mapper.cpp:25
void update_control_profile(::game &ctx, ::control_profile &profile)
Updates a control profile after actions have been remapped.
Definition: controls.cpp:273
void enable_menu_controls(::game &ctx)
void disable_menu_controls(::game &ctx)
log_message< log_message_severity::trace, Args... > log_trace
Formats and logs a trace message.
Definition: log.hpp:88
Publish-subscribe messaging.
Definition: channel.hpp:32
User-defined literals for compile-time string hashing.
Definition: fnv1a.hpp:232
@ dpad_down
D-pad down button.
@ dpad_up
D-pad up button.
@ left_shoulder
Left shoulder button.
@ back
Back button.
@ dpad_left
D-pad left button.
@ right_stick
Right stick button.
@ guide
Guide button.
@ dpad_right
D-pad right button.
@ left_stick
Left stick button.
@ right_shoulder
Right shoulder button.
@ start
Start button.
@ gamepad_axis
Gamepad axis mapping.
@ key
Key mapping.
@ gamepad_button
Gamepad button mapping.
@ left_trigger
Left trigger.
@ right_trigger
Right trigger.
@ right_stick_y
Right stick Y-axis.
@ left_stick_y
Left stick Y-axis.
@ left_stick_x
Left stick X-axis.
@ right_stick_x
Right stick X-axis.
constexpr vector< T, N > sign(const vector< T, N > &x)
Returns a vector containing the signs of each element.
Definition: vector.hpp:1502
void init_menu_item_index(::game &ctx, hash::fnv1a32_t menu_name)
Definition: menu.cpp:31
void update_text_color(::game &ctx)
Definition: menu.cpp:59
void clear_callbacks(::game &ctx)
Definition: menu.cpp:197
void delete_animations(::game &ctx)
Definition: menu.cpp:191
void align_text(::game &ctx, bool center, bool has_back, float anchor_y)
Definition: menu.cpp:73
void setup_animations(::game &ctx)
Definition: menu.cpp:206
void add_text_to_ui(::game &ctx)
Definition: menu.cpp:166
void fade_out(::game &ctx, const std::function< void()> &end_callback)
Definition: menu.cpp:263
void update_text_font(::game &ctx)
Definition: menu.cpp:44
void fade_in(::game &ctx, const std::function< void()> &end_callback)
Definition: menu.cpp:233
void remove_text_from_ui(::game &ctx)
Definition: menu.cpp:176
void delete_text(::game &ctx)
Definition: menu.cpp:186
std::string get_string(const ::game &ctx, hash::fnv1a32_t key)
Returns a localized string.
Definition: strings.cpp:23
32-bit FNV-1a hash value.
Definition: fnv1a.hpp:117