Sorry, I was a bit rambly last night. I’ll see if I can be more clear. Being more clear means a wall of text, so apologies. If you skip this I won’t feel bad. I’ll put a TL;DR toward the bottom.
If I press A on my keyboard, my current InputManager class reads that key press(The Keys enum in Monogame), and matches the Keys enum to a more generic string (“A”). I have another class I’m calling the SequenceParser… it basically looks at a list of all the keys which were pressed on a given frame and decides if a valid sequence can be constructed from it. A valid sequence would be some combination of keys which include a modifier key, like Shift, Control, etc… It’s not for mapping things like combos in a game or anything like that. I’m not there yet.
So LControl+C is a valid sequence, but X+X+X… is not. That’s not what the class is trying to build.
Anyway once any valid sequences are found a final list containing every valid input for the frame is made. If I pressed Left Control and C that frame, then the list would contain { “LControl+C”, “LControl”, “C” } . Basically every individual key press, plus a constructed sequence if valid modifier keys were included.
The next step is to match those individual presses and sequences to what I was calling ‘commands’, but ‘input actions’ might also be another common word. Or hotkey… etc. Using your fighting game example, I might have a command/action for:
Low Attack
High Attack
Up, Down, Left, Right
Etc
Low Attack could be arbitrarily bound to something simple like X, High Attack to C, and the directions to WASD, or Arrow Keys. But, crucially, I could also bind them to Left and Right mouse buttons and WASD. Or to GamePad A, GamePad X, and the D-Pad directions. So to maintain device independence, its important that DpadUp, ArrowKeyUp, and W, are all possible assignments for the ‘Up’ command/action.
The fact that pressing W on my keyboard 3 times and then The C key twice maps to some kind of special move (Up, Up, Up, Heavy Attack, Heavy Attack), is something an entirely different part of my code will care about later, when I get there.
So where the entry point of my real question comes from is here.
I’ll just use my current worst-case scenario as an example. I have a (very shoddy, half-assed) editor in my setup right now for editing levels in my game. Ideally, I should be able to transition from the editor to the game, then back to the editor(Like in Mario Maker, for example). That’s all handled fairly easily with a state manager class. So far so good.
Where my uncertainty comes in is with the inputs. If I used events in the traditional sense, I might have a published event for every command/action. That way any code that cares about knowing when ‘High Attack’(“C”) has been pressed by the user will know about it and can react to it.
Of course the trouble with this simplistic publisher/subscriber model is that if I bring up my menu or editor, and my game character is still listening for ‘High Attack’ and other keys, those events will still fire and the state of my game character changes even while I’m in the menu, as you noted in your flying kick example.
TL;DR
So really what I’m asking is, what pattern will allow me to create delegates/events which my input manager can invoke when raw inputs come in, which will inform only the currently active state. Or maybe put another way, how I can be sure the InputManager isn’t invoking commands/actions for input domains(menus, game screen, text entry, etc) that shouldn’t currently be receiving inputs.
This question is further compounded by the added detail that some of my game states could reasonably be listening for events from more than one domain. My editor state for instance, would want to listen for menu/UI related inputs, but it probably also has its own editor-specific domain for actions like ‘Pan Camera Up, Left, Down, Right’ or ‘Place Terrain’ or whatever.
The comments you and willmotil left I feel have gotten me 85% of the way towards figuring out something robust to handle this, so I’m really appreciate of that. I’m just missing some of the more specific class/implementation details to make it all click in my head.