]> icculus.org git repositories - taylor/freespace2.git/blob - src/controlconfig/controlsconfig.cpp
The Great Newline Fix
[taylor/freespace2.git] / src / controlconfig / controlsconfig.cpp
1 /*
2  * $Logfile: /Freespace2/code/ControlConfig/ControlsConfig.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C module for keyboard, joystick and mouse configuration
8  *
9  * $Log$
10  * Revision 1.2  2002/05/07 03:16:43  theoddone33
11  * The Great Newline Fix
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:08  root
14  * Initial import.
15  *
16  * 
17  * 13    10/14/99 2:50p Jefff
18  * localization fixes
19  * 
20  * 12    8/16/99 9:50a Jefff
21  * fixed loading of tab bitmaps
22  * 
23  * 11    8/11/99 3:21p Jefff
24  * added tab highlights on conflict
25  * 
26  * 10    7/26/99 5:25p Jefff
27  * removed invalidation of key binding for demo builds
28  * 
29  * 9     7/19/99 2:13p Dave
30  * Added some new strings for Heiko.
31  * 
32  * 8     7/15/99 9:20a Andsager
33  * FS2_DEMO initial checkin
34  * 
35  * 7     6/19/99 2:46p Dave
36  * New control config screen.
37  * 
38  * 6     1/30/99 5:08p Dave
39  * More new hi-res stuff.Support for nice D3D textures.
40  * 
41  * 5     1/15/99 11:29a Neilk
42  * Fixed D3D screen/texture pixel formatting problem. 
43  * 
44  * 4     11/05/98 4:18p Dave
45  * First run nebula support. Beefed up localization a bit. Removed all
46  * conditional compiles for foreign versions. Modified mission file
47  * format.
48  * 
49  * 3     10/13/98 9:28a Dave
50  * Started neatening up freespace.h. Many variables renamed and
51  * reorganized. Added AlphaColors.[h,cpp]
52  * 
53  * 2     10/07/98 10:52a Dave
54  * Initial checkin.
55  * 
56  * 1     10/07/98 10:48a Dave
57  * 
58  * 61    8/09/98 11:55a Lawrance
59  * if GRAVIS_OEM is defined, map the throttle axis by default
60  * 
61  * 60    6/19/98 3:51p Lawrance
62  * localize control text
63  * 
64  * 59    6/17/98 11:04a Lawrance
65  * localize the control config strings
66  * 
67  * 58    6/13/98 5:19p Hoffoss
68  * externalized control config texts.
69  * 
70  * 57    6/09/98 5:15p Lawrance
71  * French/German localization
72  * 
73  * 56    6/09/98 10:31a Hoffoss
74  * Created index numbers for all xstr() references.  Any new xstr() stuff
75  * added from here on out should be added to the end if the list.  The
76  * current list count can be found in FreeSpace.cpp (search for
77  * XSTR_SIZE).
78  * 
79  * 55    6/01/98 11:43a John
80  * JAS & MK:  Classified all strings for localization.
81  * 
82  * 54    5/26/98 11:10a Lawrance
83  * Fix bug where window controls get disabled when F1 pressed twice
84  * 
85  * 53    5/20/98 10:35p Hoffoss
86  * Fixed bug with mouse buttons not working when action isn't continuous.
87  * 
88  * 52    5/19/98 4:08p Allender
89  * kill default binding for Z axis
90  * 
91  * 51    5/19/98 12:56p Hoffoss
92  * Added some code to help prevent triple-clicking for binding the mouse
93  * button to an action (why the hell people triple click is beyond me,
94  * though).
95  * 
96  * 50    5/19/98 11:11a Lawrance
97  * Ensure X and Y axis have defaults!
98  * 
99  * 49    5/18/98 4:53p Hoffoss
100  * Some force feedback tweaks and pilot initializations there should have
101  * been happening, but weren't, and not are!
102  * 
103  * 48    5/18/98 10:15a Lawrance
104  * Only do hud squad msg key check when necessary
105  * 
106  * 47    5/18/98 10:08a Lawrance
107  * deal with overlap between hud squad msg number keys and CC_CONTINUOUS
108  * keys
109  * 
110  * 46    5/17/98 5:44p Hoffoss
111  * Made throttle never bound by default (ask Sandeep why if interested).
112  * 
113  * 45    5/14/98 5:32p Hoffoss
114  * Improved axis binding code some more.
115  * 
116  * 44    5/13/98 7:15p Hoffoss
117  * Fixed remaining bugs with axis binding.
118  * 
119  * 43    5/13/98 1:17a Hoffoss
120  * Added joystick axes configurability.
121  * 
122  * 42    5/12/98 3:49p Hoffoss
123  * Fixed bug where double mouse click would bind mouse button right away.
124  * 
125  * 41    5/11/98 5:43p Hoffoss
126  * Made num lock not bindable.
127  * 
128  * 40    5/11/98 5:29p Hoffoss
129  * Added mouse button mapped to joystick button support.
130  * 
131  * 39    5/07/98 6:25p Dave
132  * Fix strange boundary conditions which arise when players die/respawn
133  * while the game is being ended. Spiff up the chatbox doskey thing a bit.
134  * 
135  * 38    5/05/98 1:48a Lawrance
136  * Add in missing help overlays
137  * 
138  * 37    4/27/98 10:11a Lawrance
139  * Add in disabled beep for missing buttons
140  * 
141  * 36    4/25/98 2:59p Hoffoss
142  * Fixed typo that was causing a bug.
143  * 
144  * 35    4/22/98 1:51a Lawrance
145  * Take out multiplayer key from demo key config
146  * 
147  * 34    4/16/98 4:29p Hoffoss
148  * Fixed bank_when_pressed functionality when using alt or shift for it.
149  * 
150  * 33    4/15/98 11:06a Lawrance
151  * fix bug with a multi key showing up in demo, remove obsolete bindings
152  * from demo and full version
153  * 
154  * 32    4/14/98 2:45p Hoffoss
155  * Made hitting escape to exit screen not play failed sound.
156  * 
157  * 31    4/14/98 2:27p Hoffoss
158  * Made certain actions be hidden in demo build.
159  * 
160  * 30    4/13/98 2:38p Hoffoss
161  * Added a tooltip handler and make binding attempts with illegal keys
162  * show a popup.
163  * 
164  * 29    4/11/98 7:59p Lawrance
165  * Add support for help overlays
166  * 
167  * 28    4/09/98 4:12p Hoffoss
168  * Changed check_control() to automatically register a control as used if
169  * it detects it being used.
170  * 
171  * 27    4/08/98 11:11a Hoffoss
172  * Fixed some bugs that showed up due to fixing other bugs the other day
173  * with controls.
174  * 
175  * 26    4/07/98 3:47p Hoffoss
176  * Fixed continuous controls checking with respect to modifiers.
177  * 
178  * 25    4/06/98 11:17a Hoffoss
179  * Fixed num lock/pause interplay bug.
180  * 
181  * 24    4/03/98 3:51p Hoffoss
182  * Fixed some bugs, and made changed Interplay requested regarding search
183  * mode.
184  * 
185  * 23    3/31/98 4:12p Hoffoss
186  * Made control used status clear at mission init time.
187  * 
188  * 22    3/23/98 11:28a Hoffoss
189  * Fixed flashing question mark bug.
190  * 
191  * 21    3/21/98 11:30a John
192  * Fixed bug where joymouse caused you to stay in binding mode when
193  * binding joystick button 1 to something.
194  * 
195  * 20    3/20/98 3:37p Hoffoss
196  * Tried to fix mitri's bug, failed miserably.
197  * 
198  * 19    3/19/98 5:04p Dave
199  * Put in support for targeted multiplayer text and voice messaging (all,
200  * friendly, hostile, individual).
201  * 
202  * 18    3/18/98 12:03p John
203  * Marked all the new strings as externalized or not.
204  * 
205  * 17    3/18/98 10:16a Hoffoss
206  * Fixed warning.
207  * 
208  * 16    3/17/98 11:15a Hoffoss
209  * Made question mark that appears when you are in bind mode flash.
210  * 
211  * 15    3/17/98 10:48a Hoffoss
212  * Allowed a special hack for "bank while pressed" action to use alt and
213  * shift keys standalone.
214  * 
215  * 14    3/12/98 3:22p Hoffoss
216  * Fixed 2 bugs with one solution!  Yay!  Failed sound on bind fixed and
217  * pad enter now not translated to enter.
218  * 
219  * 13    3/11/98 5:28p Hoffoss
220  * Added control config debug display info to possibly aid in tracking
221  * down a bug.
222  * 
223  * 12    2/26/98 10:07p Hoffoss
224  * Rewrote state saving and restoring to fix bugs and simplify the code.
225  * 
226  * 11    2/22/98 12:19p John
227  * Externalized some strings
228  * 
229  * 10    2/20/98 3:39p Hoffoss
230  * Updated code for new control config screen artwork.
231  * 
232  * 9     2/09/98 2:50p Hoffoss
233  * Made 'none' show up as gray instead of normal color, to distinguish it
234  * from actions with bound keys.
235  * 
236  * 8     2/07/98 10:04p Hoffoss
237  * Changed color and placement of "more" indicator.
238  * 
239  * 7     2/05/98 10:42a Hoffoss
240  * Fixed bug where while in bind mode, you could change the line selected
241  * using the mouse, and binding would work on the new line instead.
242  * 
243  * 6     2/03/98 5:05p Hoffoss
244  * Added "clear other" button to clear all conflicting bindings with
245  * selected action.
246  * 
247  * 5     1/22/98 4:53p Hoffoss
248  * Made training messages/directives display a joystick button in place of
249  * a keypress if there is no keypress bound to the action.
250  * 
251  * 4     1/20/98 4:20p Hoffoss
252  * Removed confusing behavior of clear button clearing out the other
253  * binding in a conflict.
254  * 
255  * 3     1/08/98 12:11p Hoffoss
256  * Changed Rudder axis to Roll axis, added new function we can use to
257  * check what joystick axes are valid with.
258  * 
259  * 2     12/24/97 3:37p Hoffoss
260  * Moved control config stuff to seperate library to Fred can access it as
261  * well.
262  * 
263  * 1     12/24/97 11:58a Hoffoss
264  * 
265  * 98    12/22/97 2:15p Hoffoss
266  * Fixed bug where joystick axis lines weren't being displayed.
267  * 
268  * 97    12/16/97 2:44p Hoffoss
269  * Added clear button to control config screen.
270  * 
271  * 96    12/12/97 3:07p Hoffoss
272  * Changed how deleting bindings work.  Each control of an action can be
273  * deleted independently or both at once.
274  * 
275  * 95    12/07/97 2:36p John
276  * Made warp out be Alt+J instead of J
277  * 
278  * 94    12/03/97 4:59p Hoffoss
279  * Added reset sound and change control config sounds around.
280  * 
281  * 93    12/03/97 4:16p Hoffoss
282  * Changed sound stuff used in interface screens for interface purposes.
283  *
284  * $NoKeywords: $
285  *
286 */
287
288 #include "freespace.h"
289 #include "controlsconfig.h"
290 #include "gamesequence.h"
291 #include "player.h"
292 #include "2d.h"
293 #include "hudsquadmsg.h"
294 #include "key.h"
295 #include "timer.h"
296 #include "math.h"
297 #include "mouse.h"
298 #include "ui.h"
299 #include "joy.h"
300 #include "bmpman.h"
301 #include "sound.h"
302 #include "gamesnd.h"
303 #include "missionscreencommon.h"
304 #include "font.h"
305 #include "hud.h"
306 #include "managepilot.h"
307 #include "multi_pmsg.h"
308 #include "contexthelp.h"
309 #include "popup.h"
310 #include "uidefs.h"
311 #include "multiutil.h"
312 #include "alphacolors.h"
313
314 #define NUM_SYSTEM_KEYS                 14
315 #define NUM_BUTTONS                             19
316 #define NUM_TABS                                        4
317
318 // coordinate indicies
319 #define CONTROL_X_COORD 0
320 #define CONTROL_Y_COORD 1
321 #define CONTROL_W_COORD 2
322 #define CONTROL_H_COORD 3
323
324 char* Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS] = {
325         "ControlConfig",                // GR_640
326         "2_ControlConfig"               // GR_1024
327 };
328
329 char* Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
330         "ControlConfig-m",              // GR_640
331         "2_ControlConfig-m"             // GR_1024
332 };
333
334 // control list area
335 int Control_list_coords[GR_NUM_RESOLUTIONS][4] = {
336         {
337                 32, 58, 198, 259                        // GR_640
338         },
339         {
340                 32, 94, 904, 424                        // GR_1024
341         }
342 };
343
344 // width of the control name section of the list
345 int Control_list_ctrl_w[GR_NUM_RESOLUTIONS] = {
346         350,            // GR_640
347         600             // GR_1024
348 };
349
350 // x start position of the binding area section of the list
351 int Control_list_key_x[GR_NUM_RESOLUTIONS] = {
352         397,            // GR_640
353         712             // GR_1024
354 };
355
356 // width of the binding area section of the list
357 int Control_list_key_w[GR_NUM_RESOLUTIONS] = {
358         198,            // GR_640
359         230             // GR_1024
360 };
361
362 // display the "more..." text under the control list
363 int Control_more_coords[GR_NUM_RESOLUTIONS][2] = {
364         {
365                 320, 326                        // GR_640
366         },
367         {
368                 500, 542                        // GR_1024
369         }
370 };
371
372 // area to display "conflicts with..." text
373 int Conflict_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
374         {
375                 32, 313, 250, 32        // GR_640
376         },
377         {
378                 48, 508, 354, 46        // GR_1024
379         }
380 };
381
382 // conflict warning anim coords
383 int Conflict_warning_coords[GR_NUM_RESOLUTIONS][2] = {
384         {
385                 -1, 420                 // GR_640
386         },
387         {
388                 -1, 669                 // GR_1024
389         }
390 };
391
392 // for flashing the conflict text
393 #define CONFLICT_FLASH_TIME     250
394 int Conflict_stamp = -1;
395 int Conflict_bright = 0;
396
397 #define LIST_BUTTONS_MAX        40
398 #define JOY_AXIS                                0x80000
399
400 static int Num_cc_lines;
401 static struct {
402         char *label;
403         int cc_index;  // index into Control_config of item
404         int y;  // Y coordinate of line
405         int kx, kw, jx, jw;  // x start and width of keyboard and joystick bound text
406 } Cc_lines[CCFG_MAX];
407
408 // struct to hold backup config_item elements so we can undo them
409 struct config_item_undo {
410         int size;
411         int *index;  // array (size) of Control_config indices of replaced elements
412         config_item *list;  // array (size) of original elements
413         config_item_undo *next;
414 };
415
416 config_item Control_config_backup[CCFG_MAX];
417
418 #ifdef GRAVIS_OEM
419 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
420 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
421 #else
422 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
423 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
424 #endif
425
426 // all this stuff is localized/externalized
427 #define NUM_AXIS_TEXT                   6
428 #define NUM_MOUSE_TEXT                  5
429 #define NUM_MOUSE_AXIS_TEXT     2
430 #define NUM_INVERT_TEXT                 2       
431 char *Joy_axis_action_text[NUM_JOY_AXIS_ACTIONS];
432 char *Joy_axis_text[NUM_AXIS_TEXT];
433 char *Mouse_button_text[NUM_MOUSE_TEXT];
434 char *Mouse_axis_text[NUM_MOUSE_AXIS_TEXT];
435 char *Invert_text[NUM_INVERT_TEXT];
436
437 ubyte System_keys[NUM_SYSTEM_KEYS] = {
438         KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,
439         KEY_F11, KEY_F12, KEY_PRINT_SCRN
440 };
441
442 int Control_check_count = 0;
443
444 static int Tab;  // which tab we are currently in
445 static int Binding_mode = 0;  // are we waiting for a key to bind it?
446 static int Bind_time = 0;
447 static int Search_mode = 0;  // are we waiting for a key to bind it?
448 static int Last_key = -1;
449 static int Selected_line = 0;  // line that is currently selected for binding
450 static int Selected_item = -1;  // -1 = none, 0 = key, 1 = button
451 static int Scroll_offset;
452 static int Axis_override = -1;
453 static int Background_bitmap;
454 static int Conflicts_tabs[NUM_TABS];
455 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX];  // buttons for each line of text in list
456 static UI_WINDOW Ui_window;
457
458 static struct {
459         int key;  // index of other control in conflict with this one
460         int joy;  // index of other control in conflict with this one
461 } Conflicts[CCFG_MAX];
462
463 int Conflicts_axes[NUM_JOY_AXIS_ACTIONS];
464
465 #define TARGET_TAB                              0
466 #define SHIP_TAB                                        1
467 #define WEAPON_TAB                              2
468 #define COMPUTER_TAB                            3
469 #define SCROLL_UP_BUTTON                4
470 #define SCROLL_DOWN_BUTTON              5
471 #define ALT_TOGGLE                              6
472 #define SHIFT_TOGGLE                            7
473 #define INVERT_AXIS                             8
474 #define CANCEL_BUTTON                   9
475 #define UNDO_BUTTON                             10
476 #define RESET_BUTTON                            11
477 #define SEARCH_MODE                             12
478 #define BIND_BUTTON                             13
479 #define HELP_BUTTON                             14
480 #define ACCEPT_BUTTON                   15
481 #define CLEAR_OTHER_BUTTON              16
482 #define CLEAR_ALL_BUTTON                17
483 #define CLEAR_BUTTON                            18
484
485 ui_button_info CC_Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
486         { // GR_640
487                 ui_button_info("CCB_00",        32,     348,    17,     384,    0),     // target tab
488                 ui_button_info("CCB_01",        101,    348,    103,    384,    1),     // ship tab
489                 ui_button_info("CCB_02",        173,    352,    154,    384,    2),     // weapon tab
490                 ui_button_info("CCB_03",        242,    347,    244,    384,    3),     // computer/misc tab
491                 ui_button_info("CCB_04",        614,    73,     -1,     -1,     4),     // scroll up
492                 ui_button_info("CCB_05",        614,    296,    -1,     -1,     5),     // scroll down
493                 ui_button_info("CCB_06",        17,     452,    12,     440,    6),     // alt toggle
494                 ui_button_info("CCB_07",        56,     452,    50,     440,    7),     // shift toggle
495                 ui_button_info("CCB_09",        162,    452,    155,    440,    9),     // invert
496                 ui_button_info("CCB_10",        404,    1,              397,    45,     10),    // cancel
497                 ui_button_info("CCB_11",        582,    347,    586,    386,    11),    // undo
498                 ui_button_info("CCB_12",        576,    1,              578,    45,     12),    // default
499                 ui_button_info("CCB_13",        457,    4,              453,    45,     13),    // search
500                 ui_button_info("CCB_14",        516,    4,              519,    45,     14),    // bind
501                 ui_button_info("CCB_15",        540,    428,    500,    440,    15),    // help
502                 ui_button_info("CCB_16",        574,    432,    571,    412,    16),    // accept
503                 ui_button_info("CCB_18",        420,    346,    417,    386,    18),    // clear other 
504                 ui_button_info("CCB_19",        476,    346,    474,    386,    19),    // clear all
505                 ui_button_info("CCB_20",        524,    346,    529,    386,    20),    // clear button
506         },
507         { // GR_1024
508                 ui_button_info("2_CCB_00",      51,     557,    27,     615,    0),     // target tab
509                 ui_button_info("2_CCB_01",      162,    557,    166,    615,    1),     // ship tab
510                 ui_button_info("2_CCB_02",      277,    563,    246,    615,    2),     // weapon tab
511                 ui_button_info("2_CCB_03",      388,    555,    391,    615,    3),     // computer/misc tab
512                 ui_button_info("2_CCB_04",      982,    117,    -1,     -1,     4),     // scroll up
513                 ui_button_info("2_CCB_05",      982,    474,    -1,     -1,     5),     // scroll down
514                 ui_button_info("2_CCB_06",      28,     723,    24,     704,    6),     // alt toggle
515                 ui_button_info("2_CCB_07",      89,     723,    80,     704,    7),     // shift toggle
516                 ui_button_info("2_CCB_09",      260,    723,    249,    704,    9),     // invert
517                 ui_button_info("2_CCB_10",      646,    2,              635,    71,     10),    // cancel
518                 ui_button_info("2_CCB_11",      932,    555,    938,    619,    11),    // undo
519                 ui_button_info("2_CCB_12",      921,    1,              923,    71,     12),    // default
520                 ui_button_info("2_CCB_13",      732,    6,              726,    71,     13),    // search
521                 ui_button_info("2_CCB_14",      825,    6,              831,    71,     14),    // bind
522                 ui_button_info("2_CCB_15",      864,    685,    800,    704,    15),    // help
523                 ui_button_info("2_CCB_16",      919,    692,    914,    660,    16),    // accept
524                 ui_button_info("2_CCB_18",      672,    553,    668,    619,    18),    // clear other 
525                 ui_button_info("2_CCB_19",      761,    553,    749,    619,    19),    // clear all
526                 ui_button_info("2_CCB_20",      838,    553,    846,    619,    20),    // clear button
527         }
528 };
529
530 // strings
531 #define CC_NUM_TEXT             20
532 UI_XSTR CC_text[GR_NUM_RESOLUTIONS][CC_NUM_TEXT] = {
533         { // GR_640
534                 { "Targeting",          1340,           17,     384,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][TARGET_TAB].button },
535                 { "Ship",                       1341,           103,    384,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIP_TAB].button },
536                 { "Weapons",            1065,           154,    384,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][WEAPON_TAB].button },
537                 { "Misc",                       1411,           244,    384,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][COMPUTER_TAB].button },         
538                 { "Alt",                                1510,           12,     440,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][ALT_TOGGLE].button },
539                 { "Shift",                      1511,           50,     440,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIFT_TOGGLE].button },
540                 { "Invert",                     1342,           155,    440,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][INVERT_AXIS].button },
541                 { "Cancel",                     641,            397,    45,     UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CANCEL_BUTTON].button },
542                 { "Undo",                       1343,           586,    386,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][UNDO_BUTTON].button },
543                 { "Defaults",           1344,           568,    45,     UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][RESET_BUTTON].button },
544                 { "Search",                     1345,           453,    45,     UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SEARCH_MODE].button },
545                 { "Bind",                       1346,           519,    45,     UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][BIND_BUTTON].button },
546                 { "Help",                       928,            500,    440,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][HELP_BUTTON].button },
547                 { "Accept",                     1035,           571,    412,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][ACCEPT_BUTTON].button },
548                 { "Clear",                      1347,           417,    386,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
549                 { "Conflict",           1348,           406,    396,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
550                 { "Clear",                      1413,           474,    386,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
551                 { "All",                                1349,           483,    396,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
552                 { "Clear",                      1414,           529,    388,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
553                 { "Selected",           1350,           517,    396,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
554         },
555         { // GR_1024
556                 { "Targeting",          1340,           47,     615,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][TARGET_TAB].button },
557                 { "Ship",                       1341,           176,    615,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIP_TAB].button },
558                 { "Weapons",            1065,           266,    615,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][WEAPON_TAB].button },
559                 { "Misc",                       1411,           401,    615,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][COMPUTER_TAB].button },         
560                 { "Alt",                                1510,           29,     704,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][ALT_TOGGLE].button },
561                 { "Shift",                      1511,           85,     704,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIFT_TOGGLE].button },
562                 { "Invert",                     1342,           254,    704,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][INVERT_AXIS].button },
563                 { "Cancel",                     641,            655,    71,     UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CANCEL_BUTTON].button },
564                 { "Undo",                       1343,           938,    619,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][UNDO_BUTTON].button },
565                 { "Defaults",           1344,           923,    71,     UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][RESET_BUTTON].button },
566                 { "Search",                     1345,           746,    71,     UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SEARCH_MODE].button },
567                 { "Bind",                       1346,           846,    71,     UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][BIND_BUTTON].button },
568                 { "Help",                       928,            800,    704,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][HELP_BUTTON].button },
569                 { "Accept",                     1035,           914,    660,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][ACCEPT_BUTTON].button },
570                 { "Clear",                      1347,           683,    619,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
571                 { "Conflict",           1348,           666,    634,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
572                 { "Clear",                      1413,           759,    619,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
573                 { "All",                                1349,           772,    634,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
574                 { "Clear",                      1414,           871,    619,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
575                 { "Selected",           1350,           852,    634,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
576         }
577 };
578
579 // linked list head of undo items
580 config_item_undo *Config_item_undo;
581
582 // same indices as Scan_code_text[].  Indicates if a scancode is allowed to be bound.
583 int Config_allowed[] = {
584         0, 0, 1, 1, 1, 1, 1, 1,
585         1, 1, 1, 1, 1, 1, 1, 1,
586         1, 1, 1, 1, 1, 1, 1, 1,
587         1, 1, 1, 1, 1, 1, 1, 1,
588
589         1, 1, 1, 1, 1, 1, 1, 1,
590         1, 0, 1, 1, 1, 1, 1, 1,
591         1, 1, 1, 1, 1, 1, 1, 1,
592         1, 1, 1, 0, 0, 0, 0, 0,
593
594         0, 0, 0, 0, 0, 0, 1, 1,
595         1, 1, 1, 1, 1, 1, 1, 1,
596         1, 1, 1, 1, 0, 0, 0, 0,
597         0, 0, 0, 0, 0, 0, 0, 0,
598
599         0, 0, 0, 0, 0, 0, 0, 0,
600         0, 0, 0, 0, 0, 0, 0, 0,
601         0, 0, 0, 0, 0, 0, 0, 0,
602         0, 0, 0, 0, 0, 0, 0, 0,
603
604         0, 0, 0, 0, 0, 0, 0, 0,
605         0, 0, 0, 0, 0, 0, 0, 0,
606         0, 0, 0, 0, 0, 0, 0, 0,
607         0, 0, 0, 0, 1, 1, 0, 0,
608
609         0, 0, 0, 0, 0, 0, 0, 0,
610         0, 0, 0, 0, 0, 0, 0, 0,
611         0, 0, 0, 0, 0, 1, 0, 0,
612         1, 0, 0, 0, 0, 0, 0, 0,
613
614         0, 0, 0, 0, 0, 0, 0, 1,
615         1, 1, 0, 1, 0, 1, 0, 1,
616         1, 1, 1, 1, 0, 0, 0, 0,
617         0, 0, 0, 0, 0, 0, 0, 0,
618
619         0, 0, 0, 0, 0, 0, 0, 0,
620         0, 0, 0, 0, 0, 0, 0, 0,
621         0, 0, 0, 0, 0, 0, 0, 0,
622         0, 0, 0, 0, 0, 0, 0, 0,
623 };
624
625
626 /*
627 // old invalid demo keys
628 #define INVALID_DEMO_KEYS_MAX   14
629 int Invalid_demo_keys[] = {
630         INCREASE_SHIELD,
631         DECREASE_SHIELD,
632         SHIELD_EQUALIZE,
633         SHIELD_XFER_TOP,
634         SHIELD_XFER_BOTTOM,
635         SHIELD_XFER_LEFT,
636         SHIELD_XFER_RIGHT,
637         XFER_SHIELD,
638         XFER_LASER,
639         MULTI_MESSAGE_ALL,
640         MULTI_MESSAGE_FRIENDLY,
641         MULTI_MESSAGE_HOSTILE,
642         MULTI_MESSAGE_TARGET,
643         MULTI_OBSERVER_ZOOM_TO
644 };
645 */
646 #define INVALID_DEMO_KEYS_MAX   0
647 int Invalid_demo_keys[INVALID_DEMO_KEYS_MAX+1];         // +1 is only to prevent a 0-size array;
648
649
650 #ifndef NDEBUG
651 int Show_controls_info = 0;
652
653 DCF_BOOL(show_controls_info, Show_controls_info)
654 #endif
655
656 static int Axes_origin[JOY_NUM_AXES];
657
658 void control_config_detect_axis_reset()
659 {
660         joystick_read_raw_axis(JOY_NUM_AXES, Axes_origin);
661 }
662
663 int control_config_detect_axis()
664 {
665         int i, d, axis = -1, delta = 16384;
666         int axes_values[JOY_NUM_AXES];
667
668         joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
669         for (i=0; i<JOY_NUM_AXES; i++) {
670                 d = abs(axes_values[i] - Axes_origin[i]);
671                 if (d > delta) {
672                         axis = i;
673                         delta = d;
674                 }
675         }
676
677         return axis;
678 }
679
680 int control_config_valid_action(int n)
681 {
682 #ifdef FS2_DEMO
683         int i;
684
685         for (i=0; i<INVALID_DEMO_KEYS_MAX; i++)
686                 if (n == Invalid_demo_keys[i])
687                         return 0;
688 #endif
689
690         return 1;
691 }
692
693 void control_config_conflict_check()
694 {
695         int i, j, a, b, c, shift = -1, alt = -1;
696
697         for (i=0; i<CCFG_MAX; i++) {
698                 Conflicts[i].key = Conflicts[i].joy = -1;
699                 switch (Control_config[i].key_id) {
700                         case KEY_LSHIFT:
701                         case KEY_RSHIFT:
702                                 shift = i;
703                                 break;
704
705                         case KEY_LALT:
706                         case KEY_RALT:
707                                 alt = i;
708                                 break;
709                 }
710         }
711
712         for (i=0; i<NUM_TABS; i++)
713                 Conflicts_tabs[i] = 0;
714
715         for (i=0; i<CCFG_MAX-1; i++) {
716                 if (control_config_valid_action(i)) {
717                         for (j=i+1; j<CCFG_MAX; j++) {
718                                 if (control_config_valid_action(j)) {
719                                         if (Control_config[i].key_id >= 0) {
720                                                 c = 0;
721                                                 a = Control_config[i].key_id;
722                                                 b = Control_config[j].key_id;
723                                                 if (a == b) {
724                                                         Conflicts[i].key = j;
725                                                         Conflicts[j].key = i;
726                                                         Conflicts_tabs[ Control_config[i].tab ] = 1;
727                                                         Conflicts_tabs[ Control_config[j].tab ] = 1;
728                                                 }
729
730                 /*                              if ((a >= 0) && (a & KEY_SHIFTED) && (shift >= 0)) {
731                                                         Conflicts[i].key = shift;
732                                                         Conflicts[shift].key = i;
733                                                         Conflicts_tabs[ Control_config[i].tab ] = 1;
734                                                         Conflicts_tabs[ Control_config[shift].tab ] = 1;
735                                                 }
736
737                                                 if ((b >= 0) && (b & KEY_SHIFTED) && (shift >= 0)) {
738                                                         Conflicts[j].key = shift;
739                                                         Conflicts[shift].key = j;
740                                                         Conflicts_tabs[ Control_config[j].tab ] = 1;
741                                                         Conflicts_tabs[ Control_config[shift].tab ] = 1;
742                                                 }
743
744                                                 if ((a >= 0) && (a & KEY_ALTED) && (alt >= 0)) {
745                                                         Conflicts[i].key = alt;
746                                                         Conflicts[alt].key = i;
747                                                         Conflicts_tabs[ Control_config[i].tab ] = 1;
748                                                         Conflicts_tabs[ Control_config[alt].tab ] = 1;
749                                                 }
750
751                                                 if ((b >= 0) && (b & KEY_ALTED) && (alt >= 0)) {
752                                                         Conflicts[j].key = alt;
753                                                         Conflicts[alt].key = j;
754                                                         Conflicts_tabs[ Control_config[j].tab ] = 1;
755                                                         Conflicts_tabs[ Control_config[alt].tab ] = 1;
756                                                 }*/
757                                         }
758
759                                         if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) {
760                                                 Conflicts[i].joy = j;
761                                                 Conflicts[j].joy = i;
762                                                 Conflicts_tabs[ Control_config[i].tab ] = 1;
763                                                 Conflicts_tabs[ Control_config[j].tab ] = 1;
764                                         }
765                                 }
766                         }
767                 }
768         }
769
770         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
771                 Conflicts_axes[i] = -1;
772
773         for (i=0; i<NUM_JOY_AXIS_ACTIONS-1; i++) {
774                 for (j=i+1; j<NUM_JOY_AXIS_ACTIONS; j++) {
775                         if ((Axis_map_to[i] >= 0) && (Axis_map_to[i] == Axis_map_to[j])) {
776                                 Conflicts_axes[i] = j;
777                                 Conflicts_axes[j] = i;
778                                 Conflicts_tabs[SHIP_TAB] = 1;
779                         }
780                 }
781         }
782 }
783
784 // do list setup required prior to rendering and checking for the controls listing.  Called when list changes
785 void control_config_list_prepare()
786 {
787         int j, k, y, z;
788         int font_height = gr_get_font_height();
789
790         Num_cc_lines = y = z = 0;
791         while (z < CCFG_MAX) {
792                 if ((Control_config[z].tab == Tab) && control_config_valid_action(z)) {
793                         k = Control_config[z].key_id;
794                         j = Control_config[z].joy_id;
795                         Cc_lines[Num_cc_lines].label = XSTR(Control_config[z].text, CONTROL_CONFIG_XSTR + z);
796                         Cc_lines[Num_cc_lines].cc_index = z;
797                         Cc_lines[Num_cc_lines++].y = y;
798                         y += font_height + 2;
799                 }
800
801                 z++;
802         }
803
804         if (Tab == SHIP_TAB) {
805                 for (j=0; j<NUM_JOY_AXIS_ACTIONS; j++) {
806                         Cc_lines[Num_cc_lines].label = Joy_axis_action_text[j];
807                         Cc_lines[Num_cc_lines].cc_index = j | JOY_AXIS;
808                         Cc_lines[Num_cc_lines++].y = y;
809                         y += font_height + 2;
810                 }
811         }
812 }
813
814 int cc_line_query_visible(int n)
815 {
816         int y;
817
818         if ((n < 0) || (n >= Num_cc_lines))
819                 return 0;
820         
821         y = Cc_lines[n].y - Cc_lines[Scroll_offset].y;
822         if ((y < 0) || (y + gr_get_font_height() > Control_list_coords[gr_screen.res][CONTROL_H_COORD])){
823                 return 0;
824         }
825
826         return 1;
827 }
828
829 // allocates the required space for one undo block and put it in the beginning of the linked list (top of a stack).
830 // Returns a pointer to this newly allocated block
831 config_item_undo *get_undo_block(int size)
832 {
833         config_item_undo *ptr;
834
835         ptr = (config_item_undo *) malloc( sizeof(config_item_undo) );
836         Assert(ptr);
837         ptr->next = Config_item_undo;
838         Config_item_undo = ptr;
839
840         ptr->size = size;
841         if (size) {
842                 ptr->index = (int *) malloc( sizeof(int) * size );
843                 Assert(ptr->index);
844                 ptr->list = (config_item *) malloc( sizeof(config_item) * size );
845                 Assert(ptr->list);
846
847         } else {
848                 ptr->index = NULL;
849                 ptr->list = NULL;
850         }
851
852         return ptr;
853 }
854
855 // frees one undo block.  The first one in the list (top of the stack) to be precise.
856 void free_undo_block()
857 {
858         config_item_undo *ptr;
859
860         ptr = Config_item_undo;
861         if (!ptr)
862                 return;
863
864         Config_item_undo = ptr->next;
865         if (ptr->size) {
866                 free(ptr->list);
867                 free(ptr->index);
868         }
869
870         free(ptr);
871 }
872
873 // undo the most recent binding changes
874 int control_config_undo_last()
875 {
876         int i, z, tab;
877
878         if (!Config_item_undo) {
879                 gamesnd_play_iface(SND_GENERAL_FAIL);
880                 return -1;
881         }
882
883         if (Config_item_undo->index[0] & JOY_AXIS)
884                 tab = SHIP_TAB;
885         else
886                 tab = Control_config[Config_item_undo->index[0]].tab;
887
888         for (i=1; i<Config_item_undo->size; i++) {
889                 if (Config_item_undo->index[i] & JOY_AXIS) {
890                         if (tab != SHIP_TAB)
891                                 tab = -1;
892
893                 } else {
894                         if (Control_config[Config_item_undo->index[i]].tab != tab)
895                                 tab = -1;
896                 }
897         }
898
899         if (tab >= 0)
900                 Tab = tab;
901
902         for (i=0; i<Config_item_undo->size; i++) {
903                 z = Config_item_undo->index[i];
904                 if (z & JOY_AXIS) {
905                         config_item *ptr;
906
907                         z &= ~JOY_AXIS;
908                         ptr = &Config_item_undo->list[i];
909                         Axis_map_to[z] = ptr->joy_id;
910                         Invert_axis[z] = ptr->used;
911
912                 } else {
913                         Control_config[z] = Config_item_undo->list[i];
914                 }
915         }
916
917         free_undo_block();
918         control_config_conflict_check();
919         control_config_list_prepare();
920         gamesnd_play_iface(SND_USER_SELECT);
921         return 0;
922 }
923
924 void control_config_save_axis_undo(int axis)
925 {
926         config_item_undo *ptr;
927         config_item item;
928
929         item.joy_id = (short) Axis_map_to[axis];
930         item.used = 0;
931         item.used = Invert_axis[axis];
932
933         ptr = get_undo_block(1);
934         ptr->index[0] = axis | JOY_AXIS;
935         ptr->list[0] = item;
936 }
937
938 void control_config_bind_key(int i, int key)
939 {
940         config_item_undo *ptr;
941
942         ptr = get_undo_block(1);
943         ptr->index[0] = i;
944         ptr->list[0] = Control_config[i];
945         Control_config[i].key_id = (short) key;
946 }
947
948 void control_config_bind_joy(int i, int joy)
949 {
950         config_item_undo *ptr;
951
952         ptr = get_undo_block(1);
953         ptr->index[0] = i;
954         ptr->list[0] = Control_config[i];
955         Control_config[i].joy_id = (short) joy;
956 }
957
958 void control_config_bind_axis(int i, int axis)
959 {
960         control_config_save_axis_undo(i);
961         Axis_map_to[i] = axis;
962 }
963
964 int control_config_remove_binding()
965 {
966         int z, j, k;
967         config_item_undo *ptr;
968
969         if (Selected_line < 0) {
970                 gamesnd_play_iface(SND_GENERAL_FAIL);
971                 return -1;
972         }
973
974         z = Cc_lines[Selected_line].cc_index;
975         if (z & JOY_AXIS) {
976                 z &= ~JOY_AXIS;
977                 if (Axis_map_to[z] < 0) {
978                         gamesnd_play_iface(SND_GENERAL_FAIL);
979                         return -1;
980                 }
981
982                 control_config_save_axis_undo(z);
983                 Axis_map_to[z] = -1;
984                 control_config_conflict_check();
985                 control_config_list_prepare();
986                 gamesnd_play_iface(SND_USER_SELECT);
987                 Selected_item = -1;
988                 return 0;
989         }
990
991         if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
992                 gamesnd_play_iface(SND_GENERAL_FAIL);
993                 return -1;
994         }
995
996         j = k = -1;
997         ptr = get_undo_block(1);
998         ptr->index[0] = z;
999         ptr->list[0] = Control_config[z];
1000
1001         if (Selected_item && (Control_config[z].joy_id >= 0))  // if not just key selected (which would be 0)
1002                 Control_config[z].joy_id = (short) -1;
1003
1004         if ((Selected_item != 1) && (Control_config[z].key_id >= 0))  // if not just joy button selected (1)
1005                 Control_config[z].key_id = (short) -1;
1006
1007         control_config_conflict_check();
1008         control_config_list_prepare();
1009         gamesnd_play_iface(SND_USER_SELECT);
1010         Selected_item = -1;
1011         return 0;
1012 }
1013
1014 int control_config_clear_other()
1015 {
1016         int z, i, j, total = 0;
1017         config_item_undo *ptr;
1018
1019         if (Selected_line < 0) {
1020                 gamesnd_play_iface(SND_GENERAL_FAIL);
1021                 return -1;
1022         }
1023
1024         z = Cc_lines[Selected_line].cc_index;
1025         if (z & JOY_AXIS) {
1026                 config_item item;
1027
1028                 z &= ~JOY_AXIS;
1029                 if (Axis_map_to[z] < 0) {
1030                         gamesnd_play_iface(SND_GENERAL_FAIL);
1031                         return -1;
1032                 }
1033
1034                 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1035                         if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z))
1036                                 total++;
1037
1038                 if (!total) {
1039                         gamesnd_play_iface(SND_GENERAL_FAIL);
1040                         return -1;
1041                 }
1042
1043                 ptr = get_undo_block(total);
1044                 for (i=j=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1045                         if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
1046                                 item.joy_id = (short) Axis_map_to[i];
1047                                 item.used = Invert_axis[i];
1048
1049                                 ptr->index[j] = i | JOY_AXIS;
1050                                 ptr->list[j] = item;
1051                                 j++;
1052
1053                                 Axis_map_to[i] = -1;
1054                         }
1055
1056                 control_config_conflict_check();
1057                 control_config_list_prepare();
1058                 gamesnd_play_iface(SND_USER_SELECT);
1059                 return 0;
1060         }
1061
1062         for (i=0; i<CCFG_MAX; i++)
1063                 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1064                         if (i != z)
1065                                 total++;
1066
1067         if (!total) {
1068                 gamesnd_play_iface(SND_GENERAL_FAIL);
1069                 return -1;
1070         }
1071
1072         if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1073                 gamesnd_play_iface(SND_GENERAL_FAIL);
1074                 return -1;
1075         }
1076
1077         // now, back up the old bindings so we can undo if we want to
1078         ptr = get_undo_block(total);
1079         for (i=j=0; i<CCFG_MAX; i++)
1080                 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1081                         if (i != z) {
1082                                 ptr->index[j] = i;
1083                                 ptr->list[j] = Control_config[i];
1084                                 j++;
1085
1086                                 if (Control_config[i].key_id == Control_config[z].key_id)
1087                                         Control_config[i].key_id = (short) -1;
1088                                 if (Control_config[i].joy_id == Control_config[z].joy_id)
1089                                         Control_config[i].joy_id = (short) -1;
1090                         }
1091
1092         control_config_conflict_check();
1093         control_config_list_prepare();
1094         gamesnd_play_iface(SND_USER_SELECT);
1095         return 0;
1096 }
1097
1098 int control_config_clear_all()
1099 {
1100         int i, j, total = 0;
1101         config_item_undo *ptr;
1102
1103         // first, determine how many bindings need to be changed
1104         for (i=0; i<CCFG_MAX; i++)
1105                 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0))
1106                         total++;
1107
1108         if (!total) {
1109                 gamesnd_play_iface(SND_GENERAL_FAIL);
1110                 return -1;
1111         }
1112
1113         // now, back up the old bindings so we can undo if we want to
1114         ptr = get_undo_block(total);
1115         for (i=j=0; i<CCFG_MAX; i++) {
1116                 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
1117                         ptr->index[j] = i;
1118                         ptr->list[j] = Control_config[i];
1119                         j++;
1120                 }
1121         }
1122
1123         Assert(j == total);
1124         for (i=0; i<CCFG_MAX; i++) {
1125                 Control_config[i].key_id = Control_config[i].joy_id = -1;
1126         }
1127
1128         control_config_conflict_check();
1129         control_config_list_prepare();
1130         gamesnd_play_iface(SND_RESET_PRESSED);
1131         return 0;
1132 }
1133
1134 extern Joy_info joystick;
1135
1136 int control_config_axis_default(int axis)
1137 {
1138         Assert(axis >= 0);
1139
1140         if ( axis > 1 ) {
1141                 if (Axis_map_to_defaults[axis] < 0)
1142                         return -1;
1143
1144                 if (!joystick.axis_valid[Axis_map_to_defaults[axis]])
1145                         return -1;
1146         }
1147
1148         return Axis_map_to_defaults[axis];
1149 }
1150
1151 int control_config_do_reset()
1152 {
1153         int i, j, total = 0;
1154         config_item_undo *ptr;
1155         config_item item;
1156
1157         // first, determine how many bindings need to be changed
1158         for (i=0; i<CCFG_MAX; i++)
1159                 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default))
1160                         total++;
1161
1162         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1163                 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i]))
1164                         total++;
1165
1166         if (!total) {
1167                 gamesnd_play_iface(SND_GENERAL_FAIL);
1168                 return -1;
1169         }
1170
1171         // now, back up the old bindings so we can undo if we want to
1172         ptr = get_undo_block(total);
1173         for (i=j=0; i<CCFG_MAX; i++) {
1174                 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
1175                         ptr->index[j] = i;
1176                         ptr->list[j] = Control_config[i];
1177                         j++;
1178                 }
1179         }
1180
1181         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1182                 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
1183                         item.joy_id = (short) Axis_map_to[i];
1184                         item.used = Invert_axis[i];
1185
1186                         ptr->index[j] = i | JOY_AXIS;
1187                         ptr->list[j] = item;
1188                         j++;
1189                 }
1190
1191         Assert(j == total);
1192         control_config_reset_defaults();
1193         control_config_conflict_check();
1194         control_config_list_prepare();
1195         gamesnd_play_iface(SND_RESET_PRESSED);
1196         return 0;
1197 }
1198
1199 // This sets all the controls to their default values
1200 void control_config_reset_defaults()
1201 {
1202         int i;
1203
1204         // Reset keyboard defaults
1205         for (i=0; i<CCFG_MAX; i++) {
1206                 Control_config[i].key_id = Control_config[i].key_default;
1207                 Control_config[i].joy_id = Control_config[i].joy_default;
1208         }
1209
1210         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1211                 Axis_map_to[i] = control_config_axis_default(i);
1212                 Invert_axis[i] = Invert_axis_defaults[i];
1213         }
1214 }
1215
1216 void control_config_scroll_screen_up()
1217 {
1218         if (Scroll_offset) {
1219                 Scroll_offset--;
1220                 Assert(Selected_line > Scroll_offset);
1221                 while (!cc_line_query_visible(Selected_line))
1222                         Selected_line--;
1223
1224                 Selected_item = -1;
1225                 gamesnd_play_iface(SND_SCROLL);
1226
1227         } else
1228                 gamesnd_play_iface(SND_GENERAL_FAIL);
1229 }
1230
1231 void control_config_scroll_line_up()
1232 {
1233         if (Selected_line) {
1234                 Selected_line--;
1235                 if (Selected_line < Scroll_offset)
1236                         Scroll_offset = Selected_line;
1237
1238                 Selected_item = -1;
1239                 gamesnd_play_iface(SND_SCROLL);
1240
1241         } else
1242                 gamesnd_play_iface(SND_GENERAL_FAIL);
1243 }
1244
1245 void control_config_scroll_screen_down()
1246 {
1247         if (Cc_lines[Num_cc_lines - 1].y + gr_get_font_height() > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
1248                 Scroll_offset++;
1249                 while (!cc_line_query_visible(Selected_line)) {
1250                         Selected_line++;
1251                         Assert(Selected_line < Num_cc_lines);
1252                 }
1253
1254                 Selected_item = -1;
1255                 gamesnd_play_iface(SND_SCROLL);
1256
1257         } else
1258                 gamesnd_play_iface(SND_GENERAL_FAIL);
1259 }
1260
1261 void control_config_scroll_line_down()
1262 {
1263         if (Selected_line < Num_cc_lines - 1) {
1264                 Selected_line++;
1265                 Assert(Selected_line > Scroll_offset);
1266                 while (!cc_line_query_visible(Selected_line))
1267                         Scroll_offset++;
1268
1269                 Selected_item = -1;
1270                 gamesnd_play_iface(SND_SCROLL);
1271
1272         } else
1273                 gamesnd_play_iface(SND_GENERAL_FAIL);
1274 }
1275
1276 void control_config_toggle_modifier(int bit)
1277 {
1278         int k, z;
1279
1280         z = Cc_lines[Selected_line].cc_index;
1281         Assert(!(z & JOY_AXIS));
1282         k = Control_config[z].key_id;
1283         if (k < 0) {
1284                 gamesnd_play_iface(SND_GENERAL_FAIL);
1285                 return;
1286         }
1287
1288         control_config_bind_key(z, k ^ bit);
1289         control_config_conflict_check();
1290         gamesnd_play_iface(SND_USER_SELECT);
1291 }
1292
1293 void control_config_toggle_invert()
1294 {
1295         int z;
1296
1297         z = Cc_lines[Selected_line].cc_index;
1298         Assert(z & JOY_AXIS);
1299         z &= ~JOY_AXIS;
1300         control_config_save_axis_undo(z);
1301         Invert_axis[z] = !Invert_axis[z];
1302 }
1303
1304 void control_config_do_bind()
1305 {
1306         int i;
1307
1308         game_flush();
1309 //      if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
1310         if (Selected_line < 0) {
1311                 gamesnd_play_iface(SND_GENERAL_FAIL);
1312                 return;
1313         }
1314
1315         for (i=0; i<NUM_BUTTONS; i++)
1316                 if (i != CANCEL_BUTTON) {
1317                         CC_Buttons[gr_screen.res][i].button.reset_status();
1318                         CC_Buttons[gr_screen.res][i].button.disable();
1319                 }
1320
1321         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1322         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1323
1324         for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1325                 joy_down_count(i);  // clear checking status of all joystick buttons
1326         }
1327
1328         control_config_detect_axis_reset();
1329
1330         Binding_mode = 1;
1331         Bind_time = timer_get_milliseconds();
1332         Search_mode = 0;
1333         Last_key = -1;
1334         Axis_override = -1;
1335         gamesnd_play_iface(SND_USER_SELECT);
1336 }
1337
1338 void control_config_do_search()
1339 {
1340         int i;
1341
1342         for (i=0; i<NUM_BUTTONS; i++){
1343                 if (i != CANCEL_BUTTON) {
1344                         CC_Buttons[gr_screen.res][i].button.reset_status();
1345                         CC_Buttons[gr_screen.res][i].button.disable();
1346                 }
1347         }
1348
1349         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1350         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1351
1352         for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1353                 joy_down_count(i);  // clear checking status of all joystick buttons
1354         }
1355
1356         Binding_mode = 0;
1357         Search_mode = 1;
1358         Last_key = -1;
1359         gamesnd_play_iface(SND_USER_SELECT);
1360 }
1361
1362 void control_config_do_cancel(int fail = 0)
1363 {
1364         int i;
1365
1366         game_flush();
1367
1368         for (i=0; i<NUM_BUTTONS; i++){
1369                 if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
1370                         CC_Buttons[gr_screen.res][i].button.enable();
1371                 }
1372         }
1373
1374         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.reset_status();
1375         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1376         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
1377         CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
1378         CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
1379
1380         Binding_mode = Search_mode = 0;
1381         if (fail){
1382                 gamesnd_play_iface(SND_GENERAL_FAIL);
1383         } else {
1384                 gamesnd_play_iface(SND_USER_SELECT);
1385         }
1386 }
1387
1388 int control_config_accept()
1389 {
1390         int i;
1391
1392         for (i=0; i<NUM_TABS; i++)
1393                 if (Conflicts_tabs[i])
1394                         break;
1395
1396         if (i < NUM_TABS) {
1397                 gamesnd_play_iface(SND_GENERAL_FAIL);
1398                 return -1;
1399         }
1400
1401         hud_squadmsg_save_keys();  // rebuild map for saving/restoring keys in squadmsg mode
1402         gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1403         gamesnd_play_iface(SND_COMMIT_PRESSED);
1404         return 0;
1405 }
1406
1407 void control_config_cancel_exit()
1408 {
1409         int i;
1410
1411         for (i=0; i<CCFG_MAX; i++)
1412                 Control_config[i] = Control_config_backup[i];
1413
1414         gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1415 }
1416
1417 void control_config_button_pressed(int n)
1418 {
1419         switch (n) {
1420                 case TARGET_TAB:
1421                 case SHIP_TAB:
1422                 case WEAPON_TAB:
1423                 case COMPUTER_TAB:
1424                         Tab = n;
1425                         Scroll_offset = Selected_line = 0;
1426                         control_config_list_prepare();
1427                         gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1428                         break;
1429
1430                 case BIND_BUTTON:
1431                         control_config_do_bind();
1432                         break;
1433
1434                 case SEARCH_MODE:
1435                         control_config_do_search();
1436                         break;
1437
1438                 case SHIFT_TOGGLE:
1439                         control_config_toggle_modifier(KEY_SHIFTED);
1440                         gamesnd_play_iface(SND_USER_SELECT);
1441                         break;
1442
1443                 case ALT_TOGGLE:
1444                         control_config_toggle_modifier(KEY_ALTED);
1445                         gamesnd_play_iface(SND_USER_SELECT);
1446                         break;
1447
1448                 case INVERT_AXIS:
1449                         control_config_toggle_invert();
1450                         gamesnd_play_iface(SND_USER_SELECT);
1451                         break;
1452
1453                 case SCROLL_UP_BUTTON:
1454                         control_config_scroll_screen_up();
1455                         break;
1456
1457                 case SCROLL_DOWN_BUTTON:
1458                         control_config_scroll_screen_down();
1459                         break;
1460
1461                 case ACCEPT_BUTTON:
1462                         control_config_accept();
1463                         break;
1464
1465                 case CLEAR_BUTTON:
1466                         control_config_remove_binding();
1467                         break;
1468
1469                 case HELP_BUTTON:
1470                         launch_context_help();
1471                         gamesnd_play_iface(SND_HELP_PRESSED);
1472                         break;
1473
1474                 case RESET_BUTTON:
1475                         control_config_do_reset();
1476                         break;
1477
1478                 case UNDO_BUTTON:
1479                         control_config_undo_last();
1480                         break;
1481
1482                 case CANCEL_BUTTON:
1483                         control_config_do_cancel();
1484                         break;
1485
1486                 case CLEAR_OTHER_BUTTON:
1487                         control_config_clear_other();
1488                         break;
1489
1490                 case CLEAR_ALL_BUTTON:
1491                         control_config_clear_all();
1492                         break;          
1493         }
1494 }
1495
1496 char *control_config_tooltip_handler(char *str)
1497 {
1498         int i;
1499
1500         if (!stricmp(str, NOX("@conflict"))) {
1501                 for (i=0; i<NUM_TABS; i++) {
1502                         if (Conflicts_tabs[i])
1503                                 return XSTR( "Conflict!", 205);
1504                 }
1505         }
1506
1507         return NULL;
1508 }
1509
1510 void control_config_init()
1511 {
1512         int i;
1513         ui_button_info *b;
1514
1515         // make backup of all controls
1516         for (i=0; i<CCFG_MAX; i++)
1517                 Control_config_backup[i] = Control_config[i];
1518
1519         common_set_interface_palette(NOX("ControlConfigPalette"));  // set the interface palette
1520         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1521         Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
1522         Ui_window.tooltip_handler = control_config_tooltip_handler;
1523
1524         // load in help overlay bitmap  
1525         help_overlay_load(CONTROL_CONFIG_OVERLAY);
1526         help_overlay_set_state(CONTROL_CONFIG_OVERLAY,0);
1527
1528         // reset conflict flashing
1529         Conflict_stamp = -1;
1530
1531         for (i=0; i<NUM_BUTTONS; i++) {
1532                 b = &CC_Buttons[gr_screen.res][i];
1533
1534                 if (b->hotspot < 0) {  // temporary
1535                         b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1);  // temporary
1536                         b->button.set_highlight_action(common_play_highlight_sound);
1537                         continue;
1538                 }
1539
1540                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1541
1542                 // set up callback for when a mouse first goes over a button
1543                 b->button.set_highlight_action(common_play_highlight_sound);            
1544                 if (i<4) {
1545                         b->button.set_bmaps(b->filename, 5, 1);         // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1546                 } else {
1547                         b->button.set_bmaps(b->filename);
1548                 }
1549                 b->button.link_hotspot(b->hotspot);
1550         }       
1551
1552         // create all text
1553         for(i=0; i<CC_NUM_TEXT; i++){
1554                 Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
1555         }
1556
1557         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1558                 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1559                 List_buttons[i].hide();
1560                 List_buttons[i].disable();
1561         }
1562
1563         // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1564         CC_Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
1565         CC_Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
1566         CC_Buttons[gr_screen.res][BIND_BUTTON].button.set_hotkey(KEY_ENTER);
1567         CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_DELETE);
1568         CC_Buttons[gr_screen.res][UNDO_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_Z);
1569         CC_Buttons[gr_screen.res][CLEAR_BUTTON].button.set_hotkey(KEY_DELETE);
1570         CC_Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
1571         CC_Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(KEY_F1);
1572         CC_Buttons[gr_screen.res][RESET_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_R);
1573         CC_Buttons[gr_screen.res][INVERT_AXIS].button.set_hotkey(KEY_I);
1574
1575         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1576         CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
1577
1578         Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);   
1579
1580         Scroll_offset = Selected_line = 0;
1581         Config_item_undo = NULL;
1582         control_config_conflict_check();
1583
1584         // setup strings                                        
1585         Joy_axis_action_text[0] = strdup(XSTR("Turn (Yaw) Axis", 1016));
1586         Joy_axis_action_text[1] = strdup(XSTR("Pitch Axis", 1017));
1587         Joy_axis_action_text[2] = strdup(XSTR("Bank Axis", 1018));
1588         Joy_axis_action_text[3] = strdup(XSTR("Absolute Throttle Axis", 1019));
1589         Joy_axis_action_text[4] = strdup(XSTR("Relative Throttle Axis", 1020));
1590         Joy_axis_text[0] = strdup(XSTR("Joystick/Mouse X Axis", 1021));
1591         Joy_axis_text[1] = strdup(XSTR("Joystick/Mouse Y Axis", 1022));
1592         Joy_axis_text[2] = strdup(XSTR("Joystick Z Axis", 1023));
1593         Joy_axis_text[3] = strdup(XSTR("Joystick rX Axis", 1024));
1594         Joy_axis_text[4] = strdup(XSTR("Joystick rY Axis", 1025));
1595         Joy_axis_text[5] = strdup(XSTR("Joystick rZ Axis", 1026));
1596         Mouse_button_text[0] = strdup("");
1597         Mouse_button_text[1] = strdup(XSTR("Left Button", 1027));
1598         Mouse_button_text[2] = strdup(XSTR("Right Button", 1028));
1599         Mouse_button_text[3] = strdup(XSTR("Mid Button", 1029));
1600         Mouse_button_text[4] = strdup("");
1601         Mouse_axis_text[0] = strdup(XSTR("L/R", 1030));
1602         Mouse_axis_text[1] = strdup(XSTR("U/B", 1031));
1603         Invert_text[0] = strdup(XSTR("N", 1032));
1604         Invert_text[1] = strdup(XSTR("Y", 1033));
1605
1606         control_config_list_prepare();
1607 }
1608
1609 void control_config_close()
1610 {
1611         int idx;
1612         
1613         while (Config_item_undo){
1614                 free_undo_block();
1615         }
1616
1617         // unload the overlay bitmap
1618         help_overlay_unload(CONTROL_CONFIG_OVERLAY);
1619         
1620         if (Background_bitmap){
1621                 bm_unload(Background_bitmap);
1622         }
1623
1624         Ui_window.destroy();
1625         common_free_interface_palette();                // restore game palette
1626         hud_squadmsg_save_keys();                               // rebuild map for saving/restoring keys in squadmsg mode
1627         game_flush();
1628         write_pilot_file();
1629
1630         // free strings 
1631         for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
1632                 if(Joy_axis_action_text[idx] != NULL){
1633                         free(Joy_axis_action_text[idx]);
1634                         Joy_axis_action_text[idx] = NULL;
1635                 }
1636         }
1637         for(idx=0; idx<NUM_AXIS_TEXT; idx++){
1638                 if(Joy_axis_text[idx] != NULL){
1639                         free(Joy_axis_text[idx]);
1640                         Joy_axis_text[idx] = NULL;
1641                 }
1642         }
1643         for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
1644                 if(Mouse_button_text[idx] != NULL){
1645                         free(Mouse_button_text[idx]);
1646                         Mouse_button_text[idx] = NULL;
1647                 }
1648         }
1649         for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
1650                 if(Mouse_axis_text[idx] != NULL){
1651                         free(Mouse_axis_text[idx]);
1652                         Mouse_axis_text[idx] = NULL;
1653                 }
1654         }
1655         for(idx=0; idx<NUM_INVERT_TEXT; idx++){
1656                 if(Invert_text[idx] != NULL){
1657                         free(Invert_text[idx]);
1658                         Invert_text[idx] = NULL;
1659                 }
1660         }
1661 }
1662
1663 void control_config_do_frame(float frametime)
1664 {
1665         char buf[256], *str, *jptr;
1666         int i, j, k, w, x, y, z, len, line, conflict;
1667         int font_height = gr_get_font_height();
1668         int select_tease_line = -1;  // line mouse is down on, but won't be selected until button released
1669         static float timer = 0.0f;
1670         color *c;
1671         static int bound_timestamp = 0;
1672         static char bound_string[40];
1673         
1674         timer += frametime;
1675
1676         if (Binding_mode) {
1677                 if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1678                         int bind = 0;
1679
1680                         z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1681                         i = control_config_detect_axis();
1682                         if (i >= 0) {
1683                                 Axis_override = i;
1684                                 bind = 1;
1685                         }
1686
1687                         k = game_poll();
1688                         Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1689                         Ui_window.process(0);
1690
1691                         if (k == KEY_ESC) {
1692                                 strcpy(bound_string, XSTR( "Canceled", 206));
1693                                 bound_timestamp = timestamp(2500);
1694                                 control_config_do_cancel();
1695
1696                         } else {
1697                                 if (k == KEY_ENTER)
1698                                         bind = 1;
1699
1700                                 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1701                                         if (joy_down_count(i))
1702                                                 bind = 1;
1703
1704                                 if (bind) {
1705                                         if (Axis_override >= 0) {
1706                                                 control_config_bind_axis(z, Axis_override);
1707                                                 strcpy(bound_string, Joy_axis_text[Axis_override]);
1708                                                 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1709                                                 bound_timestamp = timestamp(2500);
1710                                                 control_config_conflict_check();
1711                                                 control_config_list_prepare();
1712                                                 control_config_do_cancel();
1713
1714                                         } else {
1715                                                 control_config_do_cancel(1);
1716                                         }
1717                                 }
1718                         }
1719
1720                 } else {
1721                         if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1722                                 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1723                                 Ui_window.set_ignore_gadgets(1);
1724                         }
1725
1726                         k = game_poll();
1727                         Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1728                         Ui_window.process(0);
1729
1730                         if ( (k > 0) || B1_JUST_RELEASED ) {
1731                                 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1732                                         help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1733                                         Ui_window.set_ignore_gadgets(0);
1734                                         k = 0;
1735                                 }
1736                         }
1737
1738                         if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1739                                 Ui_window.set_ignore_gadgets(0);
1740                         }
1741
1742                         if (k == KEY_ESC) {
1743                                 strcpy(bound_string, XSTR( "Canceled", 206));
1744                                 bound_timestamp = timestamp(2500);
1745                                 control_config_do_cancel();
1746
1747                         } else {
1748                                 switch (k & KEY_MASK) {
1749                                         case KEY_LSHIFT:
1750                                         case KEY_RSHIFT:
1751                                         case KEY_LALT:
1752                                         case KEY_RALT:
1753                                                 Last_key = k & KEY_MASK;
1754                                                 k = 0;
1755                                                 break;
1756                                 }
1757
1758                                 if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED)  // a special hack just for Mike K.
1759                                         if ( (Last_key >= 0) && (k <= 0) && !keyd_pressed[Last_key] )
1760                                                 k = Last_key;
1761
1762                                 if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1763                                         popup(0, 1, POPUP_OK, XSTR( "That is a non-bindable key.  Please try again.", 207));
1764                                         k = 0;
1765                                 }
1766
1767                                 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1768                                 if (k > 0) {
1769                                         z = Cc_lines[Selected_line].cc_index;
1770                                         Assert(!(z & JOY_AXIS));
1771                                         control_config_bind_key(z, k);
1772
1773                                         strcpy(bound_string, textify_scancode(k));
1774                                         gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1775                                         bound_timestamp = timestamp(2500);
1776                                         control_config_conflict_check();
1777                                         control_config_list_prepare();
1778                                         control_config_do_cancel();
1779                                 }
1780
1781                                 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1782                                         if (joy_down_count(i)) {
1783                                                 z = Cc_lines[Selected_line].cc_index;
1784                                                 Assert(!(z & JOY_AXIS));
1785                                                 control_config_bind_joy(z, i);
1786
1787                                                 strcpy(bound_string, Joy_button_text[i]);
1788                                                 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1789                                                 bound_timestamp = timestamp(2500);
1790                                                 control_config_conflict_check();
1791                                                 control_config_list_prepare();
1792                                                 control_config_do_cancel();
1793                                                 break;
1794                                         }
1795
1796                                 if (Bind_time + 375 < timer_get_milliseconds()) {
1797                                         for (i=0; i<NUM_BUTTONS; i++){
1798                                                 if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
1799                                                         break;
1800                                                 }
1801                                         }
1802
1803                                         if (i == NUM_BUTTONS) {  // no buttons pressed
1804                                                 for (i=0; i<MOUSE_NUM_BUTTONS; i++)
1805                                                         if (mouse_down(1 << i)) {
1806                                                                 z = Cc_lines[Selected_line].cc_index;
1807                                                                 Assert(!(z & JOY_AXIS));
1808                                                                 control_config_bind_joy(z, i);
1809
1810                                                                 strcpy(bound_string, Joy_button_text[i]);
1811                                                                 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1812                                                                 bound_timestamp = timestamp(2500);
1813                                                                 control_config_conflict_check();
1814                                                                 control_config_list_prepare();
1815                                                                 control_config_do_cancel();
1816                                                                 for (j=0; j<NUM_BUTTONS; j++){
1817                                                                         CC_Buttons[gr_screen.res][j].button.reset();
1818                                                                 }
1819
1820                                                                 break;
1821                                                         }
1822                                         }
1823                                 }
1824                         }
1825                 }
1826
1827         } else if (Search_mode) {
1828                 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1829                         CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1830                         Ui_window.set_ignore_gadgets(1);
1831                 }
1832
1833                 k = game_poll();
1834                 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1835                 Ui_window.process(0);
1836
1837                 if ( (k > 0) || B1_JUST_RELEASED ) {
1838                         if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1839                                 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1840                                 Ui_window.set_ignore_gadgets(0);
1841                                 k = 0;
1842                         }
1843                 }
1844
1845                 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1846                         Ui_window.set_ignore_gadgets(0);
1847                 }
1848
1849                 if (k == KEY_ESC) {
1850                         control_config_do_cancel();
1851
1852                 } else {
1853                         if ((k > 0) && !Config_allowed[k & KEY_MASK])
1854                                 k = 0;
1855
1856                         k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1857                         z = -1;
1858                         if (k > 0) {
1859                                 for (i=0; i<CCFG_MAX; i++)
1860                                         if (Control_config[i].key_id == k) {
1861                                                 z = i;
1862                                                 break;
1863                                         }
1864                         }
1865
1866                         for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1867                                 if (joy_down_count(i)) {
1868                                         j = i;
1869                                         for (i=0; i<CCFG_MAX; i++)
1870                                                 if (Control_config[i].joy_id == j) {
1871                                                         z = i;
1872                                                         break;
1873                                                 }
1874
1875                                         break;
1876                                 }
1877
1878                         // check if not on enabled button
1879                         for (j=0; j<NUM_BUTTONS; j++){
1880                                 if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
1881                                         break;
1882                                 }
1883                         }
1884
1885                         if (j == NUM_BUTTONS) {  // no buttons pressed
1886                                 for (j=0; j<MOUSE_NUM_BUTTONS; j++)
1887                                         if (mouse_down(1 << j)) {
1888                                                 for (i=0; i<CCFG_MAX; i++)
1889                                                         if (Control_config[i].joy_id == j) {
1890                                                                 z = i;
1891                                                                 for (j=0; j<NUM_BUTTONS; j++){
1892                                                                         CC_Buttons[gr_screen.res][j].button.reset();
1893                                                                 }
1894                                                                 break;
1895                                                         }
1896
1897                                                 break;
1898                                         }
1899                         }
1900
1901                         if (z >= 0) {
1902                                 Tab = Control_config[z].tab;
1903                                 control_config_list_prepare();
1904                                 Selected_line = Scroll_offset = 0;
1905                                 for (i=0; i<Num_cc_lines; i++)
1906                                         if (Cc_lines[i].cc_index == z) {
1907                                                 Selected_line = i;
1908                                                 break;
1909                                         }
1910
1911                                 while (!cc_line_query_visible(Selected_line)) {
1912                                         Scroll_offset++;
1913                                         Assert(Scroll_offset < Num_cc_lines);
1914                                 }
1915                         }
1916                 }
1917
1918         } else {
1919                 z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
1920                 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
1921                 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
1922                 CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
1923
1924                 if (!z) {
1925                         z = Cc_lines[Selected_line].cc_index;
1926                         k = Control_config[z].key_id;
1927                         if ( (k == KEY_LALT) || (k == KEY_RALT) || (k == KEY_LSHIFT) || (k == KEY_RSHIFT) ) {
1928                                 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
1929                                 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
1930                         }
1931                 }
1932
1933                 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
1934
1935                 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1936                         CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1937                         Ui_window.set_ignore_gadgets(1);
1938                 }
1939
1940                 k = Ui_window.process();
1941
1942                 if ( (k > 0) || B1_JUST_RELEASED ) {
1943                         if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1944                                 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1945                                 Ui_window.set_ignore_gadgets(0);
1946                                 k = 0;
1947                         }
1948                 }
1949
1950                 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1951                         Ui_window.set_ignore_gadgets(0);
1952                 }
1953
1954                 switch (k) {
1955                         case KEY_DOWN:  // select next line
1956                                 control_config_scroll_line_down();
1957                                 break;
1958
1959                         case KEY_UP:  // select previous line
1960                                 control_config_scroll_line_up();
1961                                 break;
1962
1963                         case KEY_SHIFTED | KEY_TAB:  // activate previous tab
1964                                 Tab--;
1965                                 if (Tab < 0)
1966                                         Tab = NUM_TABS - 1;
1967
1968                                 Scroll_offset = Selected_line = 0;
1969                                 control_config_list_prepare();
1970                                 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1971                                 break;
1972
1973                         case KEY_TAB:  // activate next tab
1974                                 Tab++;
1975                                 if (Tab >= NUM_TABS)
1976                                         Tab = 0;
1977
1978                                 Scroll_offset = Selected_line = 0;
1979                                 control_config_list_prepare();
1980                                 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1981                                 break;
1982
1983                         case KEY_LEFT:
1984                                 Selected_item--;
1985                                 if (Selected_item == -2) {
1986                                         Selected_item = 1;
1987                                         if (Cc_lines[Selected_line].jw < 1) {
1988                                                 Selected_item = 0;
1989                                                 if (Cc_lines[Selected_line].kw < 1)
1990                                                         Selected_item = -1;
1991                                         }
1992                                 }
1993
1994                                 gamesnd_play_iface(SND_SCROLL);
1995                                 break;
1996
1997                         case KEY_RIGHT:
1998                                 Selected_item++;
1999                                 if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1))
2000                                         Selected_item = -1;
2001                                 else if (!Selected_item && (Cc_lines[Selected_line].kw < 1))
2002                                         Selected_item = -1;
2003                                 else if (Selected_item > 1)
2004                                         Selected_item = -1;
2005
2006                                 gamesnd_play_iface(SND_SCROLL);
2007                                 break;
2008
2009                         case KEY_BACKSP:  // undo
2010                                 control_config_undo_last();
2011                                 break;
2012
2013                         case KEY_ESC:
2014                                 control_config_cancel_exit();
2015                                 break;
2016                 }       // end switch
2017         }
2018
2019         for (i=0; i<NUM_BUTTONS; i++){
2020                 if (CC_Buttons[gr_screen.res][i].button.pressed()){
2021                         control_config_button_pressed(i);
2022                 }
2023         }
2024
2025         for (i=0; i<LIST_BUTTONS_MAX; i++) {
2026                 if (List_buttons[i].is_mouse_on())
2027                         select_tease_line = i + Scroll_offset;
2028         
2029                 if (List_buttons[i].pressed()) {
2030                         Selected_line = i + Scroll_offset;
2031                         Selected_item = -1;
2032                         List_buttons[i].get_mouse_pos(&x, &y);
2033                         if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw))
2034                                 Selected_item = 0;
2035
2036                         if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw))
2037                                 Selected_item = 1;
2038
2039                         gamesnd_play_iface(SND_USER_SELECT);
2040                 }
2041
2042                 if (List_buttons[i].double_clicked())
2043                         control_config_do_bind();
2044         }
2045
2046         GR_MAYBE_CLEAR_RES(Background_bitmap);
2047         if (Background_bitmap >= 0) {
2048                 gr_set_bitmap(Background_bitmap);
2049                 gr_bitmap(0, 0);
2050         } 
2051
2052         // highlight tab with conflict
2053         Ui_window.draw();
2054         for (i=z=0; i<NUM_TABS; i++) {
2055                 if (Conflicts_tabs[i]) {
2056                         CC_Buttons[gr_screen.res][i].button.draw_forced(4);
2057                         z++;
2058                 }
2059         }
2060
2061         if (z) {
2062                 // maybe switch from bright to normal
2063                 if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
2064                         Conflict_bright = !Conflict_bright;
2065
2066                         Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
2067                 }
2068
2069                 // set color and font
2070                 gr_set_font(FONT2);
2071                 if(Conflict_bright){
2072                         gr_set_color_fast(&Color_bright_red);
2073                 } else {
2074                         gr_set_color_fast(&Color_red);
2075                 }
2076
2077                 // setup the conflict string
2078                 char conflict_str[512] = "";
2079                 strncpy(conflict_str, XSTR("Conflict!", 205), 511);
2080                 int sw, sh;
2081                 gr_get_string_size(&sw, &sh, conflict_str);
2082
2083                 gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str);
2084
2085                 gr_set_font(FONT1);
2086         } else {
2087                 // might as well always reset the conflict stamp
2088                 Conflict_stamp = -1;
2089         }
2090
2091         for (i=0; i<NUM_TABS; i++) {
2092                 if (CC_Buttons[gr_screen.res][i].button.button_down()) {
2093                         break;
2094                 }
2095         }
2096
2097         if (i == NUM_TABS) {
2098                 CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
2099         }
2100
2101         if (Search_mode) {
2102                 CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
2103         }
2104
2105         if (Selected_line >= 0) {
2106                 z = Cc_lines[Selected_line].cc_index;
2107                 if (z & JOY_AXIS) {
2108                         if (Invert_axis[z & ~JOY_AXIS]) {
2109                                 CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
2110                         }
2111
2112                 } else {
2113                         z = Control_config[z].key_id;
2114                         if (z >= 0) {
2115                                 if (z & KEY_SHIFTED) {
2116                                         CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
2117                                 }
2118                                 if (z & KEY_ALTED) {
2119                                         CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
2120                                 }
2121                         }
2122                 }
2123         }
2124
2125         if (Binding_mode) {
2126                 CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
2127         }
2128
2129         z = Cc_lines[Selected_line].cc_index;
2130         x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2;
2131         y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2;
2132         if (Binding_mode) {
2133                 int t;
2134
2135                 t = (int) (timer * 3);
2136                 if (t % 2) {
2137                         gr_set_color_fast(&Color_text_normal);
2138                         gr_get_string_size(&w, NULL, XSTR( "?", 208));
2139                         gr_printf(x - w / 2, y - font_height / 2, XSTR( "?", 208));
2140                 }
2141
2142         } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
2143                 i = Conflicts[z].key;
2144                 if (i < 0)
2145                         i = Conflicts[z].joy;
2146
2147                 gr_set_color_fast(&Color_text_normal);
2148                 str = XSTR( "Control conflicts with:", 209);
2149                 gr_get_string_size(&w, NULL, str);
2150                 gr_printf(x - w / 2, y - font_height, str);
2151
2152                 strcpy(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
2153                 gr_force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
2154                 gr_get_string_size(&w, NULL, buf);
2155                 gr_printf(x - w / 2, y, buf);
2156
2157         } else if (*bound_string) {
2158                 gr_set_color_fast(&Color_text_normal);
2159                 gr_get_string_size(&w, NULL, bound_string);
2160                 gr_printf(x - w / 2, y - font_height / 2, bound_string);
2161                 if (timestamp_elapsed(bound_timestamp))
2162                         *bound_string = 0;
2163         }
2164
2165 //      gr_set_color_fast(&Color_text_heading);
2166 //      gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]);
2167
2168 //      gr_get_string_size(&w, &h, Heading[Tab]);
2169 //      y = HEADING_Y + h / 2 - 1;
2170 //      gr_line(LIST_X, y, LIST_X + 18, y);
2171 //      gr_line(LIST_X + w + 21, y, LIST_X + LIST_W, y);
2172
2173         if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
2174                 gr_set_color_fast(&Color_white);
2175                 gr_printf(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210));
2176         }
2177
2178         conflict = 0;
2179         line = Scroll_offset;
2180         while (cc_line_query_visible(line)) {
2181                 z = Cc_lines[line].cc_index;
2182                 y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
2183
2184                 List_buttons[line - Scroll_offset].update_dimensions(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, Control_list_coords[gr_screen.res][CONTROL_W_COORD], font_height);
2185                 List_buttons[line - Scroll_offset].enable(!Binding_mode);
2186
2187                 Cc_lines[line].kw = Cc_lines[line].jw = 0;
2188
2189                 if (line == Selected_line){
2190                         c = &Color_text_selected;
2191                 } else if (line == select_tease_line) {
2192                         c = &Color_text_subselected;
2193                 } else {
2194                         c = &Color_text_normal;
2195                 }
2196
2197                 gr_set_color_fast(c);
2198                 if (Cc_lines[line].label) {
2199                         strcpy(buf, Cc_lines[line].label);
2200                         gr_force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
2201                         gr_printf(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
2202                 }
2203
2204                 if (!(z & JOY_AXIS)) {
2205                         k = Control_config[z].key_id;
2206                         j = Control_config[z].joy_id;
2207                         x = Control_list_key_x[gr_screen.res];
2208                         jptr = NULL;
2209                         *buf = 0;
2210
2211                         if ((k < 0) && (j < 0)) {
2212                                 gr_set_color_fast(&Color_grey);
2213                                 gr_printf(x, y, XSTR( "None", 211));
2214
2215                         } else {
2216                                 if (k >= 0) {
2217                                         strcpy(buf, textify_scancode(k));
2218                                         if (Conflicts[z].key >= 0) {
2219                                                 if (c == &Color_text_normal)
2220                                                         gr_set_color_fast(&Color_text_error);
2221                                                 else {
2222                                                         gr_set_color_fast(&Color_text_error_hi);
2223                                                         conflict++;
2224                                                 }
2225
2226                                         } else if (Selected_item == 1) {
2227                                                 gr_set_color_fast(&Color_text_normal);
2228
2229                                         } else
2230                                                 gr_set_color_fast(c);
2231
2232                                         gr_printf(x, y, buf);
2233
2234                                         len = strlen(buf);
2235                                         Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2236                                         gr_get_string_size(&w, NULL, buf);
2237                                         Cc_lines[line].kw = w;
2238                                         x += w;
2239
2240                                         if (j >= 0) {
2241                                                 gr_set_color_fast(&Color_text_normal);
2242                                                 gr_printf(x, y, XSTR( ", ", 212));
2243                                                 gr_get_string_size(&w, NULL, XSTR( ", ", 212));
2244                                                 x += w;
2245                                         }
2246                                 }
2247
2248                                 if (j >= 0) {
2249                                         strcpy(buf, Joy_button_text[j]);
2250                                         if (Conflicts[z].joy >= 0) {
2251                                                 if (c == &Color_text_normal)
2252                                                         gr_set_color_fast(&Color_text_error);
2253                                                 else {
2254                                                         gr_set_color_fast(&Color_text_error_hi);
2255                                                         conflict++;
2256                                                 }
2257
2258                                         } else if (!Selected_item) {
2259                                                 gr_set_color_fast(&Color_text_normal);
2260
2261                                         } else
2262                                                 gr_set_color_fast(c);
2263
2264                                         gr_force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x);
2265                                         gr_printf(x, y, buf);
2266
2267                                         Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2268                                         gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
2269                                 }
2270                         }
2271
2272                 } else {
2273                         x = Control_list_key_x[gr_screen.res];
2274                         j = Axis_map_to[z & ~JOY_AXIS];
2275                         if (Binding_mode && (line == Selected_line))
2276                                 j = Axis_override;
2277
2278                         if (j < 0) {
2279                                 gr_set_color_fast(&Color_grey);
2280                                 gr_printf(x, y, XSTR( "None", 211));
2281
2282                         } else {
2283                                 if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
2284                                         if (c == &Color_text_normal)
2285                                                 gr_set_color_fast(&Color_text_error);
2286
2287                                         else {
2288                                                 gr_set_color_fast(&Color_text_error_hi);
2289                                                 conflict++;
2290                                         }
2291
2292                                 } else if (!Selected_item) {
2293                                         gr_set_color_fast(&Color_text_normal);
2294
2295                                 } else
2296                                         gr_set_color_fast(c);
2297
2298                                 gr_string(x, y, Joy_axis_text[j]);
2299                         }
2300                 }
2301
2302                 line++;
2303         }
2304
2305         CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
2306
2307         i = line - Scroll_offset;
2308         while (i < LIST_BUTTONS_MAX)
2309                 List_buttons[i++].disable();
2310
2311         // blit help overlay if active
2312         help_overlay_maybe_blit(CONTROL_CONFIG_OVERLAY);
2313
2314         gr_flip();
2315 }
2316
2317 void clear_key_binding(short key)
2318 {
2319         int i;
2320
2321         for (i=0; i<CCFG_MAX; i++) {
2322                 if (Control_config[i].key_id == key)
2323                         Control_config[i].key_id = -1;
2324         }
2325 }
2326
2327 float check_control_timef(int id)
2328 {
2329         float t1, t2;
2330
2331         // if type isn't continuous, we shouldn't be using this function, cause it won't work.
2332         Assert(Control_config[id].type == CC_TYPE_CONTINUOUS);
2333
2334         // first, see if control actually used (makes sure modifiers match as well)
2335         if (!check_control(id))
2336                 return 0.0f;
2337
2338         t1 = key_down_timef(Control_config[id].key_id);
2339         if (t1)
2340                 control_used(id);
2341
2342         t2 = joy_down_time(Control_config[id].joy_id);
2343         if (t2)
2344                 control_used(id);
2345
2346         if (t1 + t2)
2347                 return t1 + t2;
2348
2349         return 1.0f;
2350 }
2351
2352 void control_check_indicate()
2353 {
2354 #ifndef NDEBUG
2355         if (Show_controls_info) {
2356                 gr_set_color_fast(&HUD_color_debug);
2357                 gr_printf(490, 15, NOX("Ctrls checked: %d"), Control_check_count);
2358         }
2359 #endif
2360
2361         Control_check_count = 0;
2362 }
2363
2364 int check_control(int id, int key)
2365 {
2366         int z, mask;
2367         static int last_key = 0;
2368
2369         Control_check_count++;
2370         if (key < 0)
2371                 key = last_key;
2372
2373         last_key = key;
2374
2375         // if we're in multiplayer text enter (for chat) mode, check to see if we should ignore controls
2376         if ((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls()){
2377                 return 0;
2378         }
2379
2380         if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
2381                 if (joy_down(Control_config[id].joy_id) || joy_down_count(Control_config[id].joy_id)) {
2382                         control_used(id);
2383                         return 1;
2384                 }
2385
2386                 if ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS))
2387                         if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2388                                 control_used(id);
2389                                 return 1;
2390                         }
2391
2392                 // check what current modifiers are pressed
2393                 mask = 0;
2394                 if (keyd_pressed[KEY_LSHIFT] || key_down_count(KEY_LSHIFT) || keyd_pressed[KEY_RSHIFT] || key_down_count(KEY_RSHIFT))
2395                         mask |= KEY_SHIFTED;
2396
2397                 if (keyd_pressed[KEY_LALT] || key_down_count(KEY_LALT) || keyd_pressed[KEY_RALT] || key_down_count(KEY_RALT))
2398                         mask |= KEY_ALTED;
2399
2400                 z = Control_config[id].key_id;
2401                 if (z >= 0) {
2402                         if ( (z != KEY_LALT) && (z != KEY_RALT) && (z != KEY_LSHIFT) && (z != KEY_RSHIFT) ) {
2403                                 // if current modifiers don't match action's modifiers, don't register control active.
2404                                 if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask)
2405                                         return 0;
2406                         }
2407
2408                         z &= KEY_MASK;
2409
2410                         if (keyd_pressed[z] || key_down_count(z)) {
2411                                 if ( !hud_squadmsg_read_key(z) ) {
2412                                         control_used(id);
2413                                         return 1;
2414                                 }
2415                         }
2416                 }
2417
2418                 return 0;
2419         }
2420
2421         if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2422                 control_used(id);
2423                 return 1;
2424         }
2425
2426         return 0;
2427 }
2428
2429 // get heading, pitch, bank, throttle abs. and throttle rel. values.
2430 void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
2431 {
2432         int axes_values[JOY_NUM_AXES];
2433
2434         joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
2435
2436         //      joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
2437         *h = 0;
2438         if (Axis_map_to[0] >= 0)
2439                 *h = joy_get_scaled_reading(axes_values[Axis_map_to[0]], Axis_map_to[0]);
2440
2441         *p = 0;
2442         if (Axis_map_to[1] >= 0)
2443                 *p = joy_get_scaled_reading(axes_values[Axis_map_to[1]], Axis_map_to[1]);
2444
2445         *b = 0;
2446         if (Axis_map_to[2] >= 0)
2447                 *b = joy_get_scaled_reading(axes_values[Axis_map_to[2]], Axis_map_to[2]);
2448
2449         *ta = 0;
2450         if (Axis_map_to[3] >= 0)
2451                 *ta = joy_get_unscaled_reading(axes_values[Axis_map_to[3]], Axis_map_to[3]);
2452
2453         *tr = 0;
2454         if (Axis_map_to[4] >= 0)
2455                 *tr = joy_get_scaled_reading(axes_values[Axis_map_to[4]], Axis_map_to[4]);
2456
2457         if (Invert_axis[0])
2458                 *h = -(*h);
2459         if (Invert_axis[1])
2460                 *p = -(*p);
2461         if (Invert_axis[2])
2462                 *b = -(*b);
2463         if (Invert_axis[3])
2464                 *ta = F1_0 - *ta;
2465         if (Invert_axis[4])
2466                 *tr = -(*tr);
2467
2468         return;
2469 }
2470
2471 void control_used(int id)
2472 {
2473         Control_config[id].used = timestamp();
2474 }
2475
2476 void control_config_clear_used_status()
2477 {
2478         int i;
2479
2480         for (i=0; i<CCFG_MAX; i++)
2481                 Control_config[i].used = 0;
2482 }
2483
2484 void control_config_clear()
2485 {
2486         int i;
2487
2488         // Reset keyboard defaults
2489         for (i=0; i<CCFG_MAX; i++)
2490                 Control_config[i].key_id = Control_config[i].joy_id = -1;
2491 }
2492
2493 int control_config_handle_conflict()
2494 {/*
2495         if ((Selected_item == -1) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {  // we are deleting a conflict
2496                 j = Conflicts[z].joy;
2497                 if ((j >= 0) && (Control_config[j].joy_id < 0))
2498                         j = -1;
2499
2500                 k = Conflicts[z].key;
2501                 if ((k >= 0) && (Control_config[k].key_id < 0))
2502                         k = -1;
2503
2504                 if ((j >= 0) && (k >= 0) && (j != k)) {  // deleting 2 conflicts, each in different actions
2505                         ptr = get_undo_block(2);
2506                         ptr->index[0] = j;
2507                         ptr->list[0] = Control_config[j];
2508                         Control_config[j].joy_id = (short) -1;
2509
2510                         ptr->index[1] = k;
2511                         ptr->list[1] = Control_config[k];
2512                         Control_config[k].key_id = (short) -1;
2513
2514                 } else {  // only 1 action in conflict with selected action (might be both controls, though)
2515                         z = j;
2516                         if (j < 0)
2517                                 z = k;
2518
2519                         Assert(z >= 0);
2520                         ptr = get_undo_block(1);
2521                         ptr->index[0] = z;
2522                         ptr->list[0] = Control_config[z];
2523
2524                         if (j >= 0)
2525                                 Control_config[z].joy_id = (short) -1;
2526
2527                         if (k >= 0)
2528                                 Control_config[z].key_id = (short) -1;
2529                 }*/
2530
2531         return 0;
2532 }