I recently redesigned our game's input system based on the robust input handling article, and it works perfectly. However something I've noticed -- not just with this design but with others I've used as well -- is that modifier key behavior can be tricky to handle correctly, and it's not something I've seen covered in detail by resources on the subject.
The issue with modifier keys (Ctrl, Alt, and Shift for the purpose of this post) is that they can either be bound individually to game actions ('Shift' -> 'Action_Jump'), or in combination with a non-modifier key ('Ctrl+A' -> 'Select_All'). As you can imagine, this results in some interesting behavior if you have a modifier key that is both bound to an action on its own and modifiers another key bound to another action. If you want to trigger the latter action, you'll necessarily trigger the former when you first press the modifier. I don't have a problem with this though, since I'd say it's expected behavior when the user binds their actions in such a way (where an 'action' is a single-shot event).
However when the modifier key is bound to a state, the expected behavior is not as clear, especially if that state is designed to modify other actions that are triggered when that state is active. To take a specific example, in our game the user can press the 'Q' key to queue a single unit for production at a factory. If they're holding down the 'Shift' key when they do this, the unit is queued in a special mode that tells it to autonomously re-queue itself upon completion. At least this is what's supposed to happen, however if the user is holding down the 'Shift' key when they press the 'Q' key, the input system doesn't see a 'Q' press, it sees a 'Shift+Q' press, and no action is triggered.
The solution that made the most sense to me was to add some special logic for modifier keys bound to states. When the user presses a key, it first attempts to map that key with currently held modifiers. If it doesn't find any results, it then checks to see if there are any active states mapped to modifier keys (under the assumption that this could be why the original mapping failed). If there are, it re-maps the key without those modifier states, and uses those results instead. My reasoning behind this approach is that the user wouldn't expect a modifier key bound to a state, to interfere with any actions meant to be triggered while that state is active. The only side-effect is that if the user actually had another action bound to the modified key, like 'Shift+Q', that action would trigger instead of the 'Q' action. However I can live with that.
I'm curious as to whether or not this approach makes sense to anyone else, or whether they've found another way to deal with modifier key ambiguity? My above solution fixes the cases I need it to fix, however that doesn't mean there aren't some other nasty side-effects I haven't thought of yet that could appear with the right combination of key bindings. If possible, I'd like to move the special case handling out of code and somehow make it work by adding more options to the key binding data, but I'm not sure what that would look like...