# Nested input in an event-driven system

I am using an event-based input handling system with events and delegates. An example:

However, I started wondering about how to deal with 'nested' input. For example in Half-Life 2 (or any Source game, really), you can pick up items with E. When you've picked up an item, you cannot fire with Left Mouse, but instead you throw the object. You can still jump with Space.

(I am saying nested input is where you press a certain key, and the actions you can do change. Not a menu.)

The three cases are:

• Being able to do the same action as before (like jumping)
• Not being able to do the same action (like firing)
• Doing a different action entirely (like in NetHack, where pressing the open door key means you do not move, but select a direction to open the door in)

My original idea was to just change it after the input had been received:

Register input 'A' to function 'Activate Secret Cloak Mode'

In 'Secret Cloak Mode' function:
Unregister input 'Fire'
Unregister input 'Sprint'
...
Register input 'Uncloak'
...

This suffers from large amounts of coupling, repetitive code, and other bad design signs.

I guess the other option is to maintain some kind of input state system - perhaps another delegate on the register function to refactor out those numerous register/deregister into a cleaner place (with some kind of stack on the input system) or maybe arrays of what to keep and what not to.

I'm sure someone here must've encountered this problem. How have you solved it?

tl;dr How can I deal with specific input received after another specific input in an event system?

two options: if "nested input" cases are at most three, four, I'd just use flags. "Holding an object? Can't fire." Anything else is overengineering it.

Otherwise, you can keep a per-input-key stack of event handlers.

Actions.Empty = () => { return; };
if(IsPressed(Keys.E)) {
keyEventHandlers[Keys.E].Push(Actions.Empty);
keyEventHandlers[Keys.LeftMouseButton].Push(Actions.Empty);
keyEventHandlers[Keys.Space].Push(Actions.Empty);
} else if (IsReleased(Keys.E)) {
keyEventHandlers[Keys.E].Pop();
keyEventHandlers[Keys.LeftMouseButton].Pop();
keyEventHandlers[Keys.Space].Pop();
}

while(GetNextKeyInBuffer(out key)) {
keyEventHandlers[key].Invoke(); // we invoke only last event handler
}

Or something to this effect :)

Edit: someone mentioned unmanageable if-else constructs. are we going to go full data-driven for an input event handling routine? You surely could, but why?

Anyway, for the heck of it:

void BuildOnKeyPressedEventHandlerTable() {
onKeyPressedHandlers[Key.E] = () => {
keyEventHandlers[Keys.E].Push(Actions.Empty);
keyEventHandlers[Keys.LeftMouseButton].Push(Actions.Empty);
keyEventHandlers[Keys.Space].Push(Actions.Empty);
};
}

void BuildOnKeyReleasedEventHandlerTable() {
onKeyReleasedHandlers[Key.E] = () => {
keyEventHandlers[Keys.E].Pop();
keyEventHandlers[Keys.LeftMouseButton].Pop();
keyEventHandlers[Keys.Space].Pop();
};
}

/* get released keys */

foreach(var releasedKey in releasedKeys)
onKeyReleasedHandlers[releasedKey].Invoke();

/* get pressed keys */
foreach(var pressedKey in pressedKeys)
onKeyPressedHandlers[pressedKey].Invoke();

keyEventHandlers[key].Invoke(); // we invoke only last event handler

Edit 2

Kylotan mentioned key mapping, which is a basic feature every game should have (think about accessibility, too). Including keymapping is a different story.

Changing behaviour depending on a key press combination or sequence is limiting. I overlooked that part.

Behaviour is related to game logic and not input. Which is fairly obvious, coming to think of it.

Therefore, I'm proposing the following solution:

// //>

void Init() {
// from config file / UI
// -something events should be set automatically
// quake 1 ftw.
// name      family         key      keystate
"+forward" "movement"   Keys.UpArrow Pressed
"-forward"              Keys.UpArrow Released
"+shoot"   "action"     Keys.LMB     Pressed
"-shoot"                Keys.LMB     Released
"jump"     "movement"   Keys.Space   Pressed
"+lstrafe" "movement"   Keys.A       Pressed
"-lstrafe"              Keys.A       Released
"cast"     "action"     Keys.RMB     Pressed
"picknose" "action"     Keys.X       Pressed
"lockpick" "action"     Keys.G       Pressed
"+crouch"  "movement"   Keys.LShift  Pressed
"-crouch"               Keys.LShift  Released
"chat"     "user"       Keys.T       Pressed
}

void ProcessInput() {
var pk = GetPressedKeys();
var rk = GetReleasedKeys();

var actions = TranslateToActions(pk, rk);
PerformActions(actions);
}

void TranslateToActions(pk, rk) {
// use what I posted above to switch actions depending
// on which keys have been pressed
// it's all about pushing and popping the right action
// depending on the "context" (it becomes a contextual action then)
}

actionHandlers["movement"] = (action, actionFamily) => {
if(player.isCasting)
InterruptCast();
};

actionHandlers["cast"] = (action, actionFamily) => {
if(player.isSilenced) {
Message("Cannot do that when silenced.");
}
};

actionHandlers["picknose"] = (action, actionFamily) => {
if(!player.canPickNose) {
}
};

actionHandlers["chat"] = (action, actionFamily) => {
if(player.isSilenced) {
Message("Cannot chat when silenced!");
}
};

actionHandlers["jump"] = (action, actionFamily) => {
if(player.canJump && !player.isJumping)
player.PerformJump();

if(player.isJumping) {
if(player.CanDoubleJump())
player.PerformDoubleJump();
}

player.canPickNose = false; // it's dangerous while jumping
};

void PerformActions(IList<ActionEntry> actions) {
foreach(var action in actions) {
// we pass both action name and family
// if we find no action handler, we look for an "action family" handler
// otherwise call an empty delegate
actionHandlers[action.Name, action.Family]();
}
}

// //<

This could be improved in many ways by people smarter than me, but I believe it's also a good starting point.

• Works fine for simple games - now how are you going to code an key-mapper for this? :) That alone can be a good enough reason to go data-driven for your input. Jun 8 '11 at 19:11
• @Kylotan, that's a good observation indeed, I'm going to edit my answer. Jun 8 '11 at 19:54
• This is a great answer. Here, have the bounty :P Jun 14 '11 at 10:43
• @The Communist Duck - thanks chap, hope this helps. Jun 14 '11 at 19:27

We used a state system, as you mentioned before.

We would create a map that would contain all the keys for a specific state with a flag that would allow pass through of previously mapped keys or not. When we changed states the new map would be pushed on or a previous map would be popped off.

What causes the maps to be pushed and popped is up to you, and I also will honestly say is we had certain 'clear' events to make sure the map stack was kept clean, level loading being the most obvious time (Cutscenes as well at times).

Hope this helps.

TL;DR - Use states and an input map that you can push and/or pop. Include a flag to say whether or not the map completely removes previous input or not.

• THIS. Pages and pages of nested if statements are the devil. Jun 6 '11 at 5:37
• +1. When I think on input, I always have Acrobat Reader in mind - Select Tool, Hand Tool, Marquee Zoom. IMO, using a stack might be overkill at times. GEF hides this via AbstractTool. JHotDraw has a nice hierarchy view of their Tool implementations. Jan 28 '14 at 9:37

This looks like a case where inheritance could solve your problem. You could have a base class with a bunch of methods that implement the default behavior. You could then extends this class and override some methods. Switching mode is then just a matter of switching the current implementation.

Here's some pseudo-code

class DefaultMode
function handle(key) {/* call the right method based on the given key. */}
function run() { ... }
function pickup() { ... }
function fire() { ... }

class CarryingMode extends DefaultMode
function pickup() {} //empty method, so no pickup action in this mode
function fire() { /*throw object and switch to DefaultMode. */ }

This is similar to what James proposed.

I'm not writing the exact code in any particular language. I'm giving you the idea.

(Keys.LeftMouseButton, left_click_event), (Keys.E, e_key_event), (Keys.Space, space_key_event)

2) Assign/Modify your events as given below

def left_click_event = fire();
def e_key_event = pick_item();
def space_key_event = jump();

pick_item() {
.....
left_click_action = throw_object();
}

throw_object() {
....
left_click_action = fire();
}

fire() {
....
}

jump() {
....
}

Let your jump action remain decoupled with other events like jump and fire.

Avoid if..else.. conditional checks here as it'd lead to unmanageable code.

• This doesn't seem to aid me at all. It has the problem of high levels of coupling and seems to have repetitive code. Jun 4 '11 at 14:01
• Just to get a better understanding - Can you explain what is "repetitive" in this and where do you see "high levels of coupling". Jun 4 '11 at 15:27
• If you see my code example, I have to remove all the actions in the function itself. I am hardcoding it all to the function itself - what if two functions want to share the same register/unregisters? I will either have to duplicate code OR have to couple them. Also there would be a large amount of code to remove all the unwanted actions. Lastly, I would have to have some place that 'remembers' all the original actions to replace them. Jun 4 '11 at 15:30
• Can you give me two of the actual functions from your code (like the secret cloak function) with the complete register/deregister statements. Jun 4 '11 at 15:49
• I don't actually have code for those actions at this time. However, the secret cloak would require things such as fire, sprint, walk, and change weapon to be unregistered, and uncloak to be registered. Jun 4 '11 at 15:58

Instead of unregistering, just make a state and then re-register.

In 'Secret Cloak Mode' function:
Grab the state of all bound keys- save somewhere
Unbind all keys
Re-register the keys/etc that we want to do.

In Unsecret Cloak Mode:
Unbind all keys
Rebind the state that we saved earlier.

Of course, extending this simple idea would be that you might have separate states for moving, and such, and then instead of dictating "Well, here's all the things I can't do whilst in Secret Cloak Mode, here's all the things I can do in Secret Cloak Mode.".