Your friend is a diehard rogue player, the VI keyboard mapping
is second nature to her.
You have just finished your whizzbang roguelike game, only it doesnt
support the VI mapping as you detest it with a passion!
If you dont want to alienate some players, you need to keep them
happy. You need redefinable keyboard bindings.
This requires a two-tier representation of the key. An internal
(to your game) representation and an external (eg: ncurses, dos, etc)
representation.
In my roguelike I maintained an enumeration of valid keys.
enum InternalKeyBindings
{
ik_NullKey=0,
ik_KeyDown,
ik_KeyLeft,
ik_KeyRight,
ik_KeyUp,
ik_MAX_KEYS
};
external key values are then mapped to the internal key values.
#define _ALIAS_COUNT 2
unsigned long lngKeyBinding[ik_MAX_KEYS][_ALIAS_COUNT];
The _ALIAS_COUNT in the array definition shows that we can store
up to two different key values per internal binding, for example,
Open Door and Bash Door, say 'o' and 'b', could be an alias for the same
key.
For this to work properly we must translate our keypresses from external
to internal.
For something like NCurses,
unsigned long ncurses_GetKey()
{
return getch();
}
unsigned long TranslateKey()
{
unsigned long lngKey;
unsigned long lngReturnKey;
unsigned long lngLoopCount;
unsigned long lngAliasCount;
lngKey=ncurses_GetKey();
lngReturnKey=ik_NullKey;
for(lngLoopCount=1; lngLoopCount<ik_MAX_KEYS; lngLoopCount++)
{
for(lngAliasCount=1; lngAliasCount<_ALIAS_COUNT;
lngAliasCount++)
{
if(lngKeyBinding[lngLoopCount][lngAliasCount]=lngKey)
{
lngReturnKey=lngLoopCount;
/* break out of the loop faster/nicer */
lngAliasCount=_ALIAS_COUNT;
lngLoopCount=ik_MAX_KEYS;
}
}
}
return lngReturnKey;
}
Now we have our translation working, we can assign keys for our
VI key fans and our VI key haters!
void BindKeys(void)
{
/* binds VI only */
lngKeyBidning[ik_KeyUp][0]='i';
lngKeyBinding[ik_KeyUp][1]=0;
lngKeyBidning[ik_KeyDown][0]='k';
lngKeyBinding[ik_KeyDown][1]=0;
lngKeyBidning[ik_KeyLeft][0]='j';
lngKeyBinding[ik_KeyLeft][1]=0;
lngKeyBidning[ik_KeyRight][0]='l';
lngKeyBinding[ik_KeyRight][1]=0;
/* the uppercase KEY_* are NCurses constants */
/* binds non VI arrows + number movement */
lngKeyBidning[ik_KeyUp][0]=KEY_UP;
lngKeyBinding[ik_KeyUp][1]='8';
lngKeyBidning[ik_KeyDown][0]=KEY_DOWN;
lngKeyBinding[ik_KeyDown][1]='2';
lngKeyBidning[ik_KeyLeft][0]=KEY_LEFT;
lngKeyBinding[ik_KeyLeft][1]='4';
lngKeyBidning[ik_KeyRight][0]=KEY_RIGHT;
lngKeyBinding[ik_KeyRight][1]='6';
/* binds both VI and non VI together */
lngKeyBidning[ik_KeyUp][0]='i';
lngKeyBinding[ik_KeyUp][1]=KEY_UP;
lngKeyBidning[ik_KeyDown][0]='k';
lngKeyBinding[ik_KeyDown][1]=KEY_DOWN;
lngKeyBidning[ik_KeyLeft][0]='j';
lngKeyBinding[ik_KeyLeft][1]=KEY_LEFT;
lngKeyBidning[ik_KeyRight][0]='l';
lngKeyBinding[ik_KeyRight][1]=KEY_RIGHT;
}
Now you just have to remember to work with internal keys instead
of external.
if(keypress==ik_KeyDown) instead of a hardcoded keyboard scancode
or a hardcoded NCurses constant.
There are other things you can add to this, such as checking for
a key that is bound two or more times.
Ultimatly the next step is to have all your key bindings in a config
file that your roguelike reads in on startup, that way the user can
bind whatever keys they like however they like them.
For my roguelike I allowed the player to bind NCurses constants into
the config file (KEY_UP, etc) isntead of making them hunt down
obscure scancodes for keyup \0\P etc.
Adding Meta key / CTRL / ALT key support is also another issue worth
considering.
(*nb* the above code was written off the top of my head at work,
but should be pretty much correct. Enough to get you started anyway)
|