Purple Martians
Technical Code Descriptions
Input
Overview
Keyboard input
Hack to make PRINTSCREEN work
Mouse input
Joystick input
Key bindings vs controls
Overview
I really missed the way that Allegro 4 handled input, so I recreated parts of it.
Allegro 4 had global variables that were updated asynchronously:
extern volatile char key[KEY_MAX];
extern volatile int mouse_x;
extern volatile int mouse_y;
extern volatile int mouse_z;
extern volatile int mouse_b;
extern JOYSTICK_INFO joy[n];
I recreated most of these, or similar equivalents, and update them when I get events.
This does not happen asynchronously, I have to process the event queue.
Every frame, 'proc_controllers()' is called, which processes all events in the event queue.
Keyboard input
In Allegro 4, there is an array of keys that is updated asynchronously:
extern volatile char key[KEY_MAX];
You could do stuff like:
if (key[KEY_I])
{
while (key[KEY_I]); // wait for key to be released
// do something
}
So in Allegro 5, I made my own key array and set it with keyboard events like this:
extern bool key[ALLEGRO_KEY_MAX]; // global variable
if (ev.type == ALLEGRO_EVENT_KEY_DOWN) key[ev.keyboard.keycode] = true;
if (ev.type == ALLEGRO_EVENT_KEY_UP) key[ev.keyboard.keycode] = false;
Now in Allegro 5, I can do something similar to what I used to do in Allegro 4:
proc_controllers();
if (key[ALLEGRO_KEY_I])
{
while (key[ALLEGRO_KEY_I]) proc_controllers(); // wait for key to be released
// do something
}
Hack to make PRINTSCREEN work
There is a known bug with the PRINTSCREEN key in windows.
It does not generate ALLEGRO_EVENT_KEY_DOWN events, only ALLEGRO_EVENT_KEY_UP.
I made the following work around so I could use PRINTSCREEN for my screenshot function.
At the start of my event processing I clear the key like this:
key[ALLEGRO_KEY_PRINTSCREEN] = 0; // hack to make PRINTSCREEN work
Then I add an exception to my key up section like this:
if (ev.type == ALLEGRO_EVENT_KEY_DOWN)
{
int k = ev.keyboard.keycode;
key[k] = true;
}
if (ev.type == ALLEGRO_EVENT_KEY_UP)
{
int k = ev.keyboard.keycode;
key[k] = false;
if (k == ALLEGRO_KEY_PRINTSCREEN) key[k] = true; // hack to make PRINTSCREEN work
}
Mouse input
For mouse input, I do something similar to what I do for keyboard.
Allegro 4 has external variables for mouse position and button state that are asynchronously updated.
In Allegro 5, I re-implement them and set them with mouse events:
// global variables
int mouse_x = 0;
int mouse_y = 0;
int mouse_z = 0;
int mouse_dx = 0;
int mouse_dy = 0;
int mouse_dz = 0;
int mouse_b1 = 0;
int mouse_b2 = 0;
int mouse_b3 = 0;
int mouse_b4 = 0;
if (ev.type == ALLEGRO_EVENT_MOUSE_WARPED)
{
mouse_x = ev.mouse.x;
mouse_y = ev.mouse.y;
}
if (ev.type == ALLEGRO_EVENT_MOUSE_AXES)
{
mouse_x = ev.mouse.x;
mouse_y = ev.mouse.y;
mouse_z = ev.mouse.z;
mouse_dx = ev.mouse.dx;
mouse_dy = ev.mouse.dy;
mouse_dz = ev.mouse.dz;
}
if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)
{
if (ev.mouse.button == 1) mouse_b1 = 1;
if (ev.mouse.button == 2) mouse_b2 = 1;
if (ev.mouse.button == 3) mouse_b3 = 1;
if (ev.mouse.button == 4) mouse_b4 = 1;
}
if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP)
{
if (ev.mouse.button == 1) mouse_b1 = 0;
if (ev.mouse.button == 2) mouse_b2 = 0;
if (ev.mouse.button == 3) mouse_b3 = 0;
if (ev.mouse.button == 4) mouse_b4 = 0;
}
Joystick input
I do something really weird for the joystick. I've done it for many years.
I take a section of usused key codes (I use from 128 to 167) and map them to to joystick inputs:
char *key_names[] =
{
(char*) "(none)", (char*) "A", (char*) "B", (char*) "C",
(char*) "D", (char*) "E", (char*) "F", (char*) "G",
(char*) "H", (char*) "I", (char*) "J", (char*) "K",
(char*) "L", (char*) "M", (char*) "N", (char*) "O",
(char*) "P", (char*) "Q", (char*) "R", (char*) "S",
(char*) "T", (char*) "U", (char*) "V", (char*) "W",
(char*) "X", (char*) "Y", (char*) "Z", (char*) "0",
(char*) "1", (char*) "2", (char*) "3", (char*) "4",
(char*) "5", (char*) "6", (char*) "7", (char*) "8",
(char*) "9", (char*) "PAD 0", (char*) "PAD 1", (char*) "PAD 2",
(char*) "PAD 3", (char*) "PAD 4", (char*) "PAD 5", (char*) "PAD 6",
(char*) "PAD 7", (char*) "PAD 8", (char*) "PAD 9", (char*) "F1",
(char*) "F2", (char*) "F3", (char*) "F4", (char*) "F5",
(char*) "F6", (char*) "F7", (char*) "F8", (char*) "F9",
(char*) "F10", (char*) "F11", (char*) "F12", (char*) "ESCAPE",
(char*) "KEY60", (char*) "KEY61", (char*) "KEY62", (char*) "BACKSPACE",
(char*) "TAB", (char*) "KEY65", (char*) "KEY66", (char*) "ENTER",
(char*) "KEY68", (char*) "KEY69", (char*) "BACKSLASH", (char*) "KEY71",
(char*) "KEY72", (char*) "KEY73", (char*) "KEY74", (char*) "SPACE",
(char*) "INSERT", (char*) "DELETE", (char*) "HOME", (char*) "END",
(char*) "PGUP", (char*) "PGDN", (char*) "LEFT", (char*) "RIGHT",
(char*) "UP", (char*) "DOWN", (char*) "PAD /", (char*) "PAD *",
(char*) "PAD -", (char*) "PAD +", (char*) "PAD DELETE",(char*) "PAD ENTER",
(char*) "PRINTSCREEN",(char*) "PAUSE", (char*) "KEY94", (char*) "KEY95",
(char*) "KEY96", (char*) "KEY97", (char*) "KEY98", (char*) "KEY99",
(char*) "KEY100", (char*) "KEY101", (char*) "KEY102", (char*) "PAD =",
(char*) "KEY104", (char*) "KEY105", (char*) "KEY106", (char*) "KEY107",
(char*) "KEY108", (char*) "KEY109", (char*) "KEY110", (char*) "KEY111",
(char*) "KEY112", (char*) "KEY113", (char*) "KEY114", (char*) "KEY115",
(char*) "KEY116", (char*) "KEY117", (char*) "KEY118", (char*) "KEY119",
(char*) "KEY120", (char*) "KEY121", (char*) "KEY122", (char*) "KEY123",
(char*) "KEY124", (char*) "KEY125", (char*) "KEY126", (char*) "KEY127",
// joystick equivalents
// 128-
(char*) "joy1-up", (char*) "joy1-down",(char*) "joy1-left",(char*) "joy1-right",
(char*) "joy1-b0", (char*) "joy1-b1", (char*) "joy1-b2", (char*) "joy1-b3",
(char*) "joy1-b4", (char*) "joy1-b5", (char*) "joy1-b6", (char*) "joy1-b7",
(char*) "joy1-b8", (char*) "joy1-b9", (char*) "joy1-b10", (char*) "joy1-b11",
(char*) "joy1-b12",(char*) "joy1-b13", (char*) "joy1-b14", (char*) "joy1-b15",
// 148-
(char*) "joy2-up", (char*) "joy2-down",(char*) "joy2-left",(char*) "joy2-right",
(char*) "joy2-b0", (char*) "joy2-b1", (char*) "joy2-b2", (char*) "joy2-b3",
(char*) "joy2-b4", (char*) "joy2-b5", (char*) "joy2-b6", (char*) "joy2-b7",
(char*) "joy2-b8", (char*) "joy2-b9", (char*) "joy2-b10", (char*) "joy2-b11",
(char*) "joy2-b12",(char*) "joy2-b13", (char*) "joy2-b14", (char*) "joy2-b15",
// (char*) "KEY128", (char*) "KEY129", (char*) "KEY130", (char*) "KEY131",
// (char*) "KEY132", (char*) "KEY133", (char*) "KEY134", (char*) "KEY135",
// (char*) "KEY136", (char*) "KEY137", (char*) "KEY138", (char*) "KEY139",
// (char*) "KEY140", (char*) "KEY141", (char*) "KEY142", (char*) "KEY143",
// (char*) "KEY144", (char*) "KEY145", (char*) "KEY146", (char*) "KEY147",
//
// (char*) "KEY148", (char*) "KEY149", (char*) "KEY150", (char*) "KEY151",
// (char*) "KEY152", (char*) "KEY153", (char*) "KEY154", (char*) "KEY155",
// (char*) "KEY156", (char*) "KEY157", (char*) "KEY158", (char*) "KEY159",
// (char*) "KEY160", (char*) "KEY161", (char*) "KEY162", (char*) "KEY163",
// (char*) "KEY164", (char*) "KEY165", (char*) "KEY166", (char*) "KEY167",
(char*) "KEY168", (char*) "KEY169", (char*) "KEY170", (char*) "KEY171",
(char*) "KEY172", (char*) "KEY173", (char*) "KEY174", (char*) "KEY175",
(char*) "KEY176", (char*) "KEY177", (char*) "KEY178", (char*) "KEY179",
(char*) "KEY180", (char*) "KEY181", (char*) "KEY182", (char*) "KEY183",
(char*) "KEY184", (char*) "KEY185", (char*) "KEY186", (char*) "KEY187",
(char*) "KEY188", (char*) "KEY189", (char*) "KEY190", (char*) "KEY191",
(char*) "KEY192", (char*) "KEY193", (char*) "KEY194", (char*) "KEY195",
(char*) "KEY196", (char*) "KEY197", (char*) "KEY198", (char*) "KEY199",
(char*) "KEY200", (char*) "KEY201", (char*) "KEY202", (char*) "KEY203",
(char*) "KEY204", (char*) "KEY205", (char*) "KEY206", (char*) "KEY207",
(char*) "KEY208", (char*) "KEY209", (char*) "KEY210", (char*) "KEY211",
(char*) "KEY212", (char*) "KEY213", (char*) "KEY214", (char*) "LSHIFT",
(char*) "RSHIFT", (char*) "LCTRL", (char*) "RCTRL", (char*) "ALT",
(char*) "ALTGR", (char*) "LWIN", (char*) "RWIN", (char*) "MENU",
(char*) "SCROLLLOCK",(char*) "NUMLOCK", (char*) "CAPSLOCK"
};
In my key array, when a key is pressed, its index in the array is set TRUE and when the key is released it's set FALSE.
I do the exact same thing with joystick inputs.
When a joystick control is pressed, its index in the key array is set TRUE, and when released, FALSE.
This method makes it easier to set up the controller bindings for the game.
For example, the game control [FIRE] could be mapped to a keyboard key like "C" or a joystick control like "joy-b5", and the code to detect that would be the same. I just check if that key array index is set.
When I display or change the key bindings I use the char array above and both key and joystick bindings show a text description.
Here is how I use joystick events to set joystick controls in the key array:
if (ev.type == ALLEGRO_EVENT_JOYSTICK_AXIS)
{
int jy = getJoystickNum(ev.joystick.id);
int jo = 0; // offset
if (jy == 0) jo = 0;
if (jy == 1) jo = 20;
int ax = ev.joystick.axis;
float pos = ev.joystick.pos;
if (ax == 0) // x axis
{
key[130+jo] = false;
key[131+jo] = false;
if (pos > 0) key[131+jo] = true;
if (pos < 0) key[130+jo] = true;
}
if (ax == 1) // y axis
{
key[128+jo] = false;
key[129+jo] = false;
if (pos > 0) key[129+jo] = true;
if (pos < 0) key[128+jo] = true;
}
}
if (ev.type == ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN)
{
int jy = getJoystickNum(ev.joystick.id);
int sc = get_scan_code_from_joystick(jy, 1, ev.joystick.button);
key[sc] = true;
}
if (ev.type == ALLEGRO_EVENT_JOYSTICK_BUTTON_UP)
{
int jy = getJoystickNum(ev.joystick.id);
int sc = get_scan_code_from_joystick(jy, 1, ev.joystick.button);
key[sc] = false;
}
int getJoystickNum(ALLEGRO_JOYSTICK* joy) // Thanks Edgar Reynaldo!
{
for (int i=0; i < al_get_num_joysticks(); ++i)
{
if (joy == al_get_joystick(i)) {return i;}
}
return -1;
}
int get_scan_code_from_joystick(int joy, int button, int num)
{
int ret = 0;
int base = 128;
if (joy == 0) base = 128;
if (joy == 1) base = 148;
if (button) ret = num + 4;
return base + ret;
}
Key bindings vs controls
There is a clear distinction between the key bindings and the controls.
In the player struct, for each player we have the key bindings:
int up_key, down_key, left_key, right_key, jump_key, fire_key, menu_key;
And then we have variables to indicate if a particular control is active for that frame:
int up, down, left, right, jump, fire, menu;
The game used to have a very simple method of checking if a key was pressed and then setting the corresponding control.
That has been superceded by the game moves array control method.
For an in depth description of how controls are set, see: Game Moves Array