ASW Lib
A.D.S. Games SDL Wrapper Library. A library targeted at Allegro4 users who want to switch to SDL3 and use modern c++.
Loading...
Searching...
No Matches
action.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <string_view>
5#include <unordered_map>
6#include <vector>
7
8namespace {
9
10struct ActionData {
11 std::vector<asw::input::ActionBinding> bindings;
12
13 bool pressed { false };
14 bool released { false };
15 bool down { false };
16 float strength { 0.0F };
17
19 bool prev_down { false };
20};
21
22// Looked up by string_view without building a std::string
23struct NameHash {
24 using is_transparent = void;
25
26 std::size_t operator()(std::string_view name) const
27 {
28 return std::hash<std::string_view> { }(name);
29 }
30};
31
32std::unordered_map<std::string, ActionData, NameHash, std::equal_to<>> action_map;
33
34// ---------------------------------------------------------------------------
35// Per-binding query helpers
36// ---------------------------------------------------------------------------
37
39bool binding_is_down(const asw::input::ActionBinding& binding, float& out_strength)
40{
41 return std::visit(
42 [&out_strength](const auto& b) -> bool {
43 using T = std::decay_t<decltype(b)>;
44
45 if constexpr (std::is_same_v<T, asw::input::KeyBinding>) {
46 if (asw::input::get_key(b.key)) {
47 out_strength = std::max(out_strength, 1.0F);
48 return true;
49 }
50 return false;
51
52 } else if constexpr (std::is_same_v<T, asw::input::MouseButtonBinding>) {
53 if (asw::input::get_mouse_button(b.button)) {
54 out_strength = std::max(out_strength, 1.0F);
55 return true;
56 }
57 return false;
58
59 } else if constexpr (std::is_same_v<T, asw::input::ControllerButtonBinding>) {
60 if (asw::input::get_controller_button(b.controller_index, b.button)) {
61 out_strength = std::max(out_strength, 1.0F);
62 return true;
63 }
64 return false;
65
66 } else if constexpr (std::is_same_v<T, asw::input::ControllerAxisBinding>) {
67 // Check each pad on its own, so one pad pushing the other way
68 // does not hide this direction on another pad
69 const auto check = [&](uint32_t index) {
70 const float val = asw::input::get_controller_axis(index, b.axis);
71 const float effective = b.positive_direction ? val : -val;
72 if (effective >= b.threshold) {
73 out_strength = std::max(out_strength, effective);
74 return true;
75 }
76 return false;
77 };
78
79 if (b.controller_index != asw::input::ANY_CONTROLLER) {
80 return check(b.controller_index);
81 }
82
83 bool any = false;
84 const auto count = static_cast<uint32_t>(asw::input::get_controller_count());
85 for (uint32_t i = 0; i < count; ++i) {
86 any |= check(i);
87 }
88 return any;
89 }
90
91 return false;
92 },
93 binding);
94}
95
99{
100 return std::visit(
101 [](const auto& b) -> bool {
102 using T = std::decay_t<decltype(b)>;
103
104 if constexpr (std::is_same_v<T, asw::input::KeyBinding>) {
105 return asw::input::get_key_down(b.key);
106
107 } else if constexpr (std::is_same_v<T, asw::input::MouseButtonBinding>) {
108 return asw::input::get_mouse_button_down(b.button);
109
110 } else if constexpr (std::is_same_v<T, asw::input::ControllerButtonBinding>) {
111 return asw::input::get_controller_button_down(b.controller_index, b.button);
112
113 } else {
114 // ControllerAxisBinding: transition handled by prev_down in update_actions.
115 return false;
116 }
117 },
118 binding);
119}
120
123{
124 return std::visit(
125 [](const auto& b) -> bool {
126 using T = std::decay_t<decltype(b)>;
127
128 if constexpr (std::is_same_v<T, asw::input::KeyBinding>) {
129 return asw::input::get_key_up(b.key);
130
131 } else if constexpr (std::is_same_v<T, asw::input::MouseButtonBinding>) {
132 return asw::input::get_mouse_button_up(b.button);
133
134 } else if constexpr (std::is_same_v<T, asw::input::ControllerButtonBinding>) {
135 return asw::input::get_controller_button_up(b.controller_index, b.button);
136
137 } else {
138 // ControllerAxisBinding: transition handled by prev_down in update_actions.
139 return false;
140 }
141 },
142 binding);
143}
144
145} // namespace
146
147// ---------------------------------------------------------------------------
148// Public API
149// ---------------------------------------------------------------------------
150
151void asw::input::bind_action(std::string_view name, asw::input::ActionBinding binding)
152{
153 action_map[std::string(name)].bindings.push_back(binding);
154}
155
156void asw::input::unbind_action(std::string_view name)
157{
158 if (auto it = action_map.find(name); it != action_map.end()) {
159 action_map.erase(it);
160 }
161}
162
164{
165 action_map.clear();
166}
167
168bool asw::input::get_action_down(std::string_view name)
169{
170 auto it = action_map.find(name);
171 return it != action_map.end() && it->second.pressed;
172}
173
174bool asw::input::get_action_up(std::string_view name)
175{
176 auto it = action_map.find(name);
177 return it != action_map.end() && it->second.released;
178}
179
180bool asw::input::get_action(std::string_view name)
181{
182 auto it = action_map.find(name);
183 return it != action_map.end() && it->second.down;
184}
185
186float asw::input::get_action_strength(std::string_view name)
187{
188 auto it = action_map.find(name);
189 return it != action_map.end() ? it->second.strength : 0.0F;
190}
191
193{
194 for (auto& [name, action] : action_map) {
195 bool any_down = false;
196 bool any_pressed = false;
197 bool any_released = false;
198 float max_strength = 0.0F;
199
200 for (const auto& binding : action.bindings) {
201 any_down |= binding_is_down(binding, max_strength);
202 any_pressed |= binding_is_pressed(binding);
203 any_released |= binding_is_released(binding);
204 }
205
206 // The action is pressed when it goes from up to down, and released
207 // when it goes from down to up. A binding's own edge still counts, so
208 // a tap within one frame is seen, but not while another binding holds
209 // the action down, e.g. pressing a second jump key does not jump again.
210 action.pressed = !action.prev_down && (any_down || any_pressed);
211 action.released = !any_down && (action.prev_down || any_released);
212 action.down = any_down;
213 action.strength = max_strength;
214 action.prev_down = any_down;
215 }
216}
Action binding system for the ASW library.
bool binding_is_pressed(const asw::input::ActionBinding &binding)
Definition action.cpp:98
bool binding_is_down(const asw::input::ActionBinding &binding, float &out_strength)
Returns true if the binding is currently active, and updates out_strength.
Definition action.cpp:39
bool binding_is_released(const asw::input::ActionBinding &binding)
Returns true if the binding was released this frame (digital sources only).
Definition action.cpp:122
std::unordered_map< std::string, ActionData, NameHash, std::equal_to<> > action_map
Definition action.cpp:32
bool get_controller_button_up(uint32_t index, asw::input::ControllerButton button)
Check if a controller button was released since the last update.
Definition input.cpp:459
bool get_controller_button_down(uint32_t index, asw::input::ControllerButton button)
Check if a controller button was pressed since the last update.
Definition input.cpp:453
bool get_key(asw::input::Key key)
Check if a key is down.
Definition input.cpp:423
bool get_action_down(std::string_view name)
Check if an action was triggered (first pressed) this frame.
Definition action.cpp:168
void bind_action(std::string_view name, ActionBinding binding)
Register a binding for a named action.
Definition action.cpp:151
bool get_controller_button(uint32_t index, asw::input::ControllerButton button)
Check if a controller button is down.
Definition input.cpp:447
bool get_key_up(asw::input::Key key)
Check if a key was released since the last update.
Definition input.cpp:439
bool get_key_down(asw::input::Key key)
Check if a key was pressed since the last update.
Definition input.cpp:428
int get_controller_count()
Get the number of controller slots.
Definition input.cpp:563
constexpr uint32_t ANY_CONTROLLER
Pass as a controller index to read every connected controller.
Definition input.h:503
bool get_mouse_button(asw::input::MouseButton button)
Check if a mouse button is down.
Definition input.cpp:282
void clear_actions()
Remove all registered actions and their bindings.
Definition action.cpp:163
void update_actions()
Update cached action states from current raw input.
Definition action.cpp:192
float get_controller_axis(uint32_t index, asw::input::ControllerAxis axis)
Get the value of a controller axis with the dead zone applied.
Definition input.cpp:465
bool get_action(std::string_view name)
Check if an action is currently held down.
Definition action.cpp:180
float get_action_strength(std::string_view name)
Get the analogue strength of an action (0.0 – 1.0).
Definition action.cpp:186
bool get_mouse_button_up(asw::input::MouseButton button)
Check if a mouse button was released since the last update.
Definition input.cpp:294
bool get_mouse_button_down(asw::input::MouseButton button)
Check if a mouse button was pressed since the last update.
Definition input.cpp:288
void unbind_action(std::string_view name)
Remove all bindings for a named action.
Definition action.cpp:156
std::variant< KeyBinding, MouseButtonBinding, ControllerButtonBinding, ControllerAxisBinding > ActionBinding
A single input binding — keyboard, mouse button, controller button, or controller axis.
Definition action.h:65
bool get_action_up(std::string_view name)
Check if an action was released this frame.
Definition action.cpp:174
std::vector< asw::input::ActionBinding > bindings
Definition action.cpp:11
std::size_t operator()(std::string_view name) const
Definition action.cpp:26