2 * $Logfile: /Freespace2/code/ControlConfig/ControlsConfig.cpp $
7 * C module for keyboard, joystick and mouse configuration
10 * Revision 1.1 2002/05/03 03:28:08 root
14 * 13 10/14/99 2:50p Jefff
17 * 12 8/16/99 9:50a Jefff
18 * fixed loading of tab bitmaps
20 * 11 8/11/99 3:21p Jefff
21 * added tab highlights on conflict
23 * 10 7/26/99 5:25p Jefff
24 * removed invalidation of key binding for demo builds
26 * 9 7/19/99 2:13p Dave
27 * Added some new strings for Heiko.
29 * 8 7/15/99 9:20a Andsager
30 * FS2_DEMO initial checkin
32 * 7 6/19/99 2:46p Dave
33 * New control config screen.
35 * 6 1/30/99 5:08p Dave
36 * More new hi-res stuff.Support for nice D3D textures.
38 * 5 1/15/99 11:29a Neilk
39 * Fixed D3D screen/texture pixel formatting problem.
41 * 4 11/05/98 4:18p Dave
42 * First run nebula support. Beefed up localization a bit. Removed all
43 * conditional compiles for foreign versions. Modified mission file
46 * 3 10/13/98 9:28a Dave
47 * Started neatening up freespace.h. Many variables renamed and
48 * reorganized. Added AlphaColors.[h,cpp]
50 * 2 10/07/98 10:52a Dave
53 * 1 10/07/98 10:48a Dave
55 * 61 8/09/98 11:55a Lawrance
56 * if GRAVIS_OEM is defined, map the throttle axis by default
58 * 60 6/19/98 3:51p Lawrance
59 * localize control text
61 * 59 6/17/98 11:04a Lawrance
62 * localize the control config strings
64 * 58 6/13/98 5:19p Hoffoss
65 * externalized control config texts.
67 * 57 6/09/98 5:15p Lawrance
68 * French/German localization
70 * 56 6/09/98 10:31a Hoffoss
71 * Created index numbers for all xstr() references. Any new xstr() stuff
72 * added from here on out should be added to the end if the list. The
73 * current list count can be found in FreeSpace.cpp (search for
76 * 55 6/01/98 11:43a John
77 * JAS & MK: Classified all strings for localization.
79 * 54 5/26/98 11:10a Lawrance
80 * Fix bug where window controls get disabled when F1 pressed twice
82 * 53 5/20/98 10:35p Hoffoss
83 * Fixed bug with mouse buttons not working when action isn't continuous.
85 * 52 5/19/98 4:08p Allender
86 * kill default binding for Z axis
88 * 51 5/19/98 12:56p Hoffoss
89 * Added some code to help prevent triple-clicking for binding the mouse
90 * button to an action (why the hell people triple click is beyond me,
93 * 50 5/19/98 11:11a Lawrance
94 * Ensure X and Y axis have defaults!
96 * 49 5/18/98 4:53p Hoffoss
97 * Some force feedback tweaks and pilot initializations there should have
98 * been happening, but weren't, and not are!
100 * 48 5/18/98 10:15a Lawrance
101 * Only do hud squad msg key check when necessary
103 * 47 5/18/98 10:08a Lawrance
104 * deal with overlap between hud squad msg number keys and CC_CONTINUOUS
107 * 46 5/17/98 5:44p Hoffoss
108 * Made throttle never bound by default (ask Sandeep why if interested).
110 * 45 5/14/98 5:32p Hoffoss
111 * Improved axis binding code some more.
113 * 44 5/13/98 7:15p Hoffoss
114 * Fixed remaining bugs with axis binding.
116 * 43 5/13/98 1:17a Hoffoss
117 * Added joystick axes configurability.
119 * 42 5/12/98 3:49p Hoffoss
120 * Fixed bug where double mouse click would bind mouse button right away.
122 * 41 5/11/98 5:43p Hoffoss
123 * Made num lock not bindable.
125 * 40 5/11/98 5:29p Hoffoss
126 * Added mouse button mapped to joystick button support.
128 * 39 5/07/98 6:25p Dave
129 * Fix strange boundary conditions which arise when players die/respawn
130 * while the game is being ended. Spiff up the chatbox doskey thing a bit.
132 * 38 5/05/98 1:48a Lawrance
133 * Add in missing help overlays
135 * 37 4/27/98 10:11a Lawrance
136 * Add in disabled beep for missing buttons
138 * 36 4/25/98 2:59p Hoffoss
139 * Fixed typo that was causing a bug.
141 * 35 4/22/98 1:51a Lawrance
142 * Take out multiplayer key from demo key config
144 * 34 4/16/98 4:29p Hoffoss
145 * Fixed bank_when_pressed functionality when using alt or shift for it.
147 * 33 4/15/98 11:06a Lawrance
148 * fix bug with a multi key showing up in demo, remove obsolete bindings
149 * from demo and full version
151 * 32 4/14/98 2:45p Hoffoss
152 * Made hitting escape to exit screen not play failed sound.
154 * 31 4/14/98 2:27p Hoffoss
155 * Made certain actions be hidden in demo build.
157 * 30 4/13/98 2:38p Hoffoss
158 * Added a tooltip handler and make binding attempts with illegal keys
161 * 29 4/11/98 7:59p Lawrance
162 * Add support for help overlays
164 * 28 4/09/98 4:12p Hoffoss
165 * Changed check_control() to automatically register a control as used if
166 * it detects it being used.
168 * 27 4/08/98 11:11a Hoffoss
169 * Fixed some bugs that showed up due to fixing other bugs the other day
172 * 26 4/07/98 3:47p Hoffoss
173 * Fixed continuous controls checking with respect to modifiers.
175 * 25 4/06/98 11:17a Hoffoss
176 * Fixed num lock/pause interplay bug.
178 * 24 4/03/98 3:51p Hoffoss
179 * Fixed some bugs, and made changed Interplay requested regarding search
182 * 23 3/31/98 4:12p Hoffoss
183 * Made control used status clear at mission init time.
185 * 22 3/23/98 11:28a Hoffoss
186 * Fixed flashing question mark bug.
188 * 21 3/21/98 11:30a John
189 * Fixed bug where joymouse caused you to stay in binding mode when
190 * binding joystick button 1 to something.
192 * 20 3/20/98 3:37p Hoffoss
193 * Tried to fix mitri's bug, failed miserably.
195 * 19 3/19/98 5:04p Dave
196 * Put in support for targeted multiplayer text and voice messaging (all,
197 * friendly, hostile, individual).
199 * 18 3/18/98 12:03p John
200 * Marked all the new strings as externalized or not.
202 * 17 3/18/98 10:16a Hoffoss
205 * 16 3/17/98 11:15a Hoffoss
206 * Made question mark that appears when you are in bind mode flash.
208 * 15 3/17/98 10:48a Hoffoss
209 * Allowed a special hack for "bank while pressed" action to use alt and
210 * shift keys standalone.
212 * 14 3/12/98 3:22p Hoffoss
213 * Fixed 2 bugs with one solution! Yay! Failed sound on bind fixed and
214 * pad enter now not translated to enter.
216 * 13 3/11/98 5:28p Hoffoss
217 * Added control config debug display info to possibly aid in tracking
220 * 12 2/26/98 10:07p Hoffoss
221 * Rewrote state saving and restoring to fix bugs and simplify the code.
223 * 11 2/22/98 12:19p John
224 * Externalized some strings
226 * 10 2/20/98 3:39p Hoffoss
227 * Updated code for new control config screen artwork.
229 * 9 2/09/98 2:50p Hoffoss
230 * Made 'none' show up as gray instead of normal color, to distinguish it
231 * from actions with bound keys.
233 * 8 2/07/98 10:04p Hoffoss
234 * Changed color and placement of "more" indicator.
236 * 7 2/05/98 10:42a Hoffoss
237 * Fixed bug where while in bind mode, you could change the line selected
238 * using the mouse, and binding would work on the new line instead.
240 * 6 2/03/98 5:05p Hoffoss
241 * Added "clear other" button to clear all conflicting bindings with
244 * 5 1/22/98 4:53p Hoffoss
245 * Made training messages/directives display a joystick button in place of
246 * a keypress if there is no keypress bound to the action.
248 * 4 1/20/98 4:20p Hoffoss
249 * Removed confusing behavior of clear button clearing out the other
250 * binding in a conflict.
252 * 3 1/08/98 12:11p Hoffoss
253 * Changed Rudder axis to Roll axis, added new function we can use to
254 * check what joystick axes are valid with.
256 * 2 12/24/97 3:37p Hoffoss
257 * Moved control config stuff to seperate library to Fred can access it as
260 * 1 12/24/97 11:58a Hoffoss
262 * 98 12/22/97 2:15p Hoffoss
263 * Fixed bug where joystick axis lines weren't being displayed.
265 * 97 12/16/97 2:44p Hoffoss
266 * Added clear button to control config screen.
268 * 96 12/12/97 3:07p Hoffoss
269 * Changed how deleting bindings work. Each control of an action can be
270 * deleted independently or both at once.
272 * 95 12/07/97 2:36p John
273 * Made warp out be Alt+J instead of J
275 * 94 12/03/97 4:59p Hoffoss
276 * Added reset sound and change control config sounds around.
278 * 93 12/03/97 4:16p Hoffoss
279 * Changed sound stuff used in interface screens for interface purposes.
285 #include "freespace.h"
286 #include "controlsconfig.h"
287 #include "gamesequence.h"
290 #include "hudsquadmsg.h"
300 #include "missionscreencommon.h"
303 #include "managepilot.h"
304 #include "multi_pmsg.h"
305 #include "contexthelp.h"
308 #include "multiutil.h"
309 #include "alphacolors.h"
311 #define NUM_SYSTEM_KEYS 14
312 #define NUM_BUTTONS 19
315 // coordinate indicies
316 #define CONTROL_X_COORD 0
317 #define CONTROL_Y_COORD 1
318 #define CONTROL_W_COORD 2
319 #define CONTROL_H_COORD 3
321 char* Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS] = {
322 "ControlConfig", // GR_640
323 "2_ControlConfig" // GR_1024
326 char* Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
327 "ControlConfig-m", // GR_640
328 "2_ControlConfig-m" // GR_1024
332 int Control_list_coords[GR_NUM_RESOLUTIONS][4] = {
334 32, 58, 198, 259 // GR_640
337 32, 94, 904, 424 // GR_1024
341 // width of the control name section of the list
342 int Control_list_ctrl_w[GR_NUM_RESOLUTIONS] = {
347 // x start position of the binding area section of the list
348 int Control_list_key_x[GR_NUM_RESOLUTIONS] = {
353 // width of the binding area section of the list
354 int Control_list_key_w[GR_NUM_RESOLUTIONS] = {
359 // display the "more..." text under the control list
360 int Control_more_coords[GR_NUM_RESOLUTIONS][2] = {
369 // area to display "conflicts with..." text
370 int Conflict_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
372 32, 313, 250, 32 // GR_640
375 48, 508, 354, 46 // GR_1024
379 // conflict warning anim coords
380 int Conflict_warning_coords[GR_NUM_RESOLUTIONS][2] = {
389 // for flashing the conflict text
390 #define CONFLICT_FLASH_TIME 250
391 int Conflict_stamp = -1;
392 int Conflict_bright = 0;
394 #define LIST_BUTTONS_MAX 40
395 #define JOY_AXIS 0x80000
397 static int Num_cc_lines;
400 int cc_index; // index into Control_config of item
401 int y; // Y coordinate of line
402 int kx, kw, jx, jw; // x start and width of keyboard and joystick bound text
403 } Cc_lines[CCFG_MAX];
405 // struct to hold backup config_item elements so we can undo them
406 struct config_item_undo {
408 int *index; // array (size) of Control_config indices of replaced elements
409 config_item *list; // array (size) of original elements
410 config_item_undo *next;
413 config_item Control_config_backup[CCFG_MAX];
416 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
417 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
419 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
420 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
423 // all this stuff is localized/externalized
424 #define NUM_AXIS_TEXT 6
425 #define NUM_MOUSE_TEXT 5
426 #define NUM_MOUSE_AXIS_TEXT 2
427 #define NUM_INVERT_TEXT 2
428 char *Joy_axis_action_text[NUM_JOY_AXIS_ACTIONS];
429 char *Joy_axis_text[NUM_AXIS_TEXT];
430 char *Mouse_button_text[NUM_MOUSE_TEXT];
431 char *Mouse_axis_text[NUM_MOUSE_AXIS_TEXT];
432 char *Invert_text[NUM_INVERT_TEXT];
434 ubyte System_keys[NUM_SYSTEM_KEYS] = {
435 KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,
436 KEY_F11, KEY_F12, KEY_PRINT_SCRN
439 int Control_check_count = 0;
441 static int Tab; // which tab we are currently in
442 static int Binding_mode = 0; // are we waiting for a key to bind it?
443 static int Bind_time = 0;
444 static int Search_mode = 0; // are we waiting for a key to bind it?
445 static int Last_key = -1;
446 static int Selected_line = 0; // line that is currently selected for binding
447 static int Selected_item = -1; // -1 = none, 0 = key, 1 = button
448 static int Scroll_offset;
449 static int Axis_override = -1;
450 static int Background_bitmap;
451 static int Conflicts_tabs[NUM_TABS];
452 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list
453 static UI_WINDOW Ui_window;
456 int key; // index of other control in conflict with this one
457 int joy; // index of other control in conflict with this one
458 } Conflicts[CCFG_MAX];
460 int Conflicts_axes[NUM_JOY_AXIS_ACTIONS];
465 #define COMPUTER_TAB 3
466 #define SCROLL_UP_BUTTON 4
467 #define SCROLL_DOWN_BUTTON 5
469 #define SHIFT_TOGGLE 7
470 #define INVERT_AXIS 8
471 #define CANCEL_BUTTON 9
472 #define UNDO_BUTTON 10
473 #define RESET_BUTTON 11
474 #define SEARCH_MODE 12
475 #define BIND_BUTTON 13
476 #define HELP_BUTTON 14
477 #define ACCEPT_BUTTON 15
478 #define CLEAR_OTHER_BUTTON 16
479 #define CLEAR_ALL_BUTTON 17
480 #define CLEAR_BUTTON 18
482 ui_button_info CC_Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
484 ui_button_info("CCB_00", 32, 348, 17, 384, 0), // target tab
485 ui_button_info("CCB_01", 101, 348, 103, 384, 1), // ship tab
486 ui_button_info("CCB_02", 173, 352, 154, 384, 2), // weapon tab
487 ui_button_info("CCB_03", 242, 347, 244, 384, 3), // computer/misc tab
488 ui_button_info("CCB_04", 614, 73, -1, -1, 4), // scroll up
489 ui_button_info("CCB_05", 614, 296, -1, -1, 5), // scroll down
490 ui_button_info("CCB_06", 17, 452, 12, 440, 6), // alt toggle
491 ui_button_info("CCB_07", 56, 452, 50, 440, 7), // shift toggle
492 ui_button_info("CCB_09", 162, 452, 155, 440, 9), // invert
493 ui_button_info("CCB_10", 404, 1, 397, 45, 10), // cancel
494 ui_button_info("CCB_11", 582, 347, 586, 386, 11), // undo
495 ui_button_info("CCB_12", 576, 1, 578, 45, 12), // default
496 ui_button_info("CCB_13", 457, 4, 453, 45, 13), // search
497 ui_button_info("CCB_14", 516, 4, 519, 45, 14), // bind
498 ui_button_info("CCB_15", 540, 428, 500, 440, 15), // help
499 ui_button_info("CCB_16", 574, 432, 571, 412, 16), // accept
500 ui_button_info("CCB_18", 420, 346, 417, 386, 18), // clear other
501 ui_button_info("CCB_19", 476, 346, 474, 386, 19), // clear all
502 ui_button_info("CCB_20", 524, 346, 529, 386, 20), // clear button
505 ui_button_info("2_CCB_00", 51, 557, 27, 615, 0), // target tab
506 ui_button_info("2_CCB_01", 162, 557, 166, 615, 1), // ship tab
507 ui_button_info("2_CCB_02", 277, 563, 246, 615, 2), // weapon tab
508 ui_button_info("2_CCB_03", 388, 555, 391, 615, 3), // computer/misc tab
509 ui_button_info("2_CCB_04", 982, 117, -1, -1, 4), // scroll up
510 ui_button_info("2_CCB_05", 982, 474, -1, -1, 5), // scroll down
511 ui_button_info("2_CCB_06", 28, 723, 24, 704, 6), // alt toggle
512 ui_button_info("2_CCB_07", 89, 723, 80, 704, 7), // shift toggle
513 ui_button_info("2_CCB_09", 260, 723, 249, 704, 9), // invert
514 ui_button_info("2_CCB_10", 646, 2, 635, 71, 10), // cancel
515 ui_button_info("2_CCB_11", 932, 555, 938, 619, 11), // undo
516 ui_button_info("2_CCB_12", 921, 1, 923, 71, 12), // default
517 ui_button_info("2_CCB_13", 732, 6, 726, 71, 13), // search
518 ui_button_info("2_CCB_14", 825, 6, 831, 71, 14), // bind
519 ui_button_info("2_CCB_15", 864, 685, 800, 704, 15), // help
520 ui_button_info("2_CCB_16", 919, 692, 914, 660, 16), // accept
521 ui_button_info("2_CCB_18", 672, 553, 668, 619, 18), // clear other
522 ui_button_info("2_CCB_19", 761, 553, 749, 619, 19), // clear all
523 ui_button_info("2_CCB_20", 838, 553, 846, 619, 20), // clear button
528 #define CC_NUM_TEXT 20
529 UI_XSTR CC_text[GR_NUM_RESOLUTIONS][CC_NUM_TEXT] = {
531 { "Targeting", 1340, 17, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][TARGET_TAB].button },
532 { "Ship", 1341, 103, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIP_TAB].button },
533 { "Weapons", 1065, 154, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][WEAPON_TAB].button },
534 { "Misc", 1411, 244, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][COMPUTER_TAB].button },
535 { "Alt", 1510, 12, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][ALT_TOGGLE].button },
536 { "Shift", 1511, 50, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIFT_TOGGLE].button },
537 { "Invert", 1342, 155, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][INVERT_AXIS].button },
538 { "Cancel", 641, 397, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CANCEL_BUTTON].button },
539 { "Undo", 1343, 586, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][UNDO_BUTTON].button },
540 { "Defaults", 1344, 568, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][RESET_BUTTON].button },
541 { "Search", 1345, 453, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SEARCH_MODE].button },
542 { "Bind", 1346, 519, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][BIND_BUTTON].button },
543 { "Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][HELP_BUTTON].button },
544 { "Accept", 1035, 571, 412, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][ACCEPT_BUTTON].button },
545 { "Clear", 1347, 417, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
546 { "Conflict", 1348, 406, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
547 { "Clear", 1413, 474, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
548 { "All", 1349, 483, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
549 { "Clear", 1414, 529, 388, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
550 { "Selected", 1350, 517, 396, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
553 { "Targeting", 1340, 47, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][TARGET_TAB].button },
554 { "Ship", 1341, 176, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIP_TAB].button },
555 { "Weapons", 1065, 266, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][WEAPON_TAB].button },
556 { "Misc", 1411, 401, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][COMPUTER_TAB].button },
557 { "Alt", 1510, 29, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][ALT_TOGGLE].button },
558 { "Shift", 1511, 85, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIFT_TOGGLE].button },
559 { "Invert", 1342, 254, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][INVERT_AXIS].button },
560 { "Cancel", 641, 655, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CANCEL_BUTTON].button },
561 { "Undo", 1343, 938, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][UNDO_BUTTON].button },
562 { "Defaults", 1344, 923, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][RESET_BUTTON].button },
563 { "Search", 1345, 746, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SEARCH_MODE].button },
564 { "Bind", 1346, 846, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][BIND_BUTTON].button },
565 { "Help", 928, 800, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][HELP_BUTTON].button },
566 { "Accept", 1035, 914, 660, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][ACCEPT_BUTTON].button },
567 { "Clear", 1347, 683, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
568 { "Conflict", 1348, 666, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
569 { "Clear", 1413, 759, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
570 { "All", 1349, 772, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
571 { "Clear", 1414, 871, 619, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
572 { "Selected", 1350, 852, 634, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
576 // linked list head of undo items
577 config_item_undo *Config_item_undo;
579 // same indices as Scan_code_text[]. Indicates if a scancode is allowed to be bound.
580 int Config_allowed[] = {
581 0, 0, 1, 1, 1, 1, 1, 1,
582 1, 1, 1, 1, 1, 1, 1, 1,
583 1, 1, 1, 1, 1, 1, 1, 1,
584 1, 1, 1, 1, 1, 1, 1, 1,
586 1, 1, 1, 1, 1, 1, 1, 1,
587 1, 0, 1, 1, 1, 1, 1, 1,
588 1, 1, 1, 1, 1, 1, 1, 1,
589 1, 1, 1, 0, 0, 0, 0, 0,
591 0, 0, 0, 0, 0, 0, 1, 1,
592 1, 1, 1, 1, 1, 1, 1, 1,
593 1, 1, 1, 1, 0, 0, 0, 0,
594 0, 0, 0, 0, 0, 0, 0, 0,
596 0, 0, 0, 0, 0, 0, 0, 0,
597 0, 0, 0, 0, 0, 0, 0, 0,
598 0, 0, 0, 0, 0, 0, 0, 0,
599 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 0, 0, 0, 0, 0, 0, 0, 0,
604 0, 0, 0, 0, 1, 1, 0, 0,
606 0, 0, 0, 0, 0, 0, 0, 0,
607 0, 0, 0, 0, 0, 0, 0, 0,
608 0, 0, 0, 0, 0, 1, 0, 0,
609 1, 0, 0, 0, 0, 0, 0, 0,
611 0, 0, 0, 0, 0, 0, 0, 1,
612 1, 1, 0, 1, 0, 1, 0, 1,
613 1, 1, 1, 1, 0, 0, 0, 0,
614 0, 0, 0, 0, 0, 0, 0, 0,
616 0, 0, 0, 0, 0, 0, 0, 0,
617 0, 0, 0, 0, 0, 0, 0, 0,
618 0, 0, 0, 0, 0, 0, 0, 0,
619 0, 0, 0, 0, 0, 0, 0, 0,
624 // old invalid demo keys
625 #define INVALID_DEMO_KEYS_MAX 14
626 int Invalid_demo_keys[] = {
637 MULTI_MESSAGE_FRIENDLY,
638 MULTI_MESSAGE_HOSTILE,
639 MULTI_MESSAGE_TARGET,
640 MULTI_OBSERVER_ZOOM_TO
643 #define INVALID_DEMO_KEYS_MAX 0
644 int Invalid_demo_keys[INVALID_DEMO_KEYS_MAX+1]; // +1 is only to prevent a 0-size array;
648 int Show_controls_info = 0;
650 DCF_BOOL(show_controls_info, Show_controls_info)
653 static int Axes_origin[JOY_NUM_AXES];
655 void control_config_detect_axis_reset()
657 joystick_read_raw_axis(JOY_NUM_AXES, Axes_origin);
660 int control_config_detect_axis()
662 int i, d, axis = -1, delta = 16384;
663 int axes_values[JOY_NUM_AXES];
665 joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
666 for (i=0; i<JOY_NUM_AXES; i++) {
667 d = abs(axes_values[i] - Axes_origin[i]);
677 int control_config_valid_action(int n)
682 for (i=0; i<INVALID_DEMO_KEYS_MAX; i++)
683 if (n == Invalid_demo_keys[i])
690 void control_config_conflict_check()
692 int i, j, a, b, c, shift = -1, alt = -1;
694 for (i=0; i<CCFG_MAX; i++) {
695 Conflicts[i].key = Conflicts[i].joy = -1;
696 switch (Control_config[i].key_id) {
709 for (i=0; i<NUM_TABS; i++)
710 Conflicts_tabs[i] = 0;
712 for (i=0; i<CCFG_MAX-1; i++) {
713 if (control_config_valid_action(i)) {
714 for (j=i+1; j<CCFG_MAX; j++) {
715 if (control_config_valid_action(j)) {
716 if (Control_config[i].key_id >= 0) {
718 a = Control_config[i].key_id;
719 b = Control_config[j].key_id;
721 Conflicts[i].key = j;
722 Conflicts[j].key = i;
723 Conflicts_tabs[ Control_config[i].tab ] = 1;
724 Conflicts_tabs[ Control_config[j].tab ] = 1;
727 /* if ((a >= 0) && (a & KEY_SHIFTED) && (shift >= 0)) {
728 Conflicts[i].key = shift;
729 Conflicts[shift].key = i;
730 Conflicts_tabs[ Control_config[i].tab ] = 1;
731 Conflicts_tabs[ Control_config[shift].tab ] = 1;
734 if ((b >= 0) && (b & KEY_SHIFTED) && (shift >= 0)) {
735 Conflicts[j].key = shift;
736 Conflicts[shift].key = j;
737 Conflicts_tabs[ Control_config[j].tab ] = 1;
738 Conflicts_tabs[ Control_config[shift].tab ] = 1;
741 if ((a >= 0) && (a & KEY_ALTED) && (alt >= 0)) {
742 Conflicts[i].key = alt;
743 Conflicts[alt].key = i;
744 Conflicts_tabs[ Control_config[i].tab ] = 1;
745 Conflicts_tabs[ Control_config[alt].tab ] = 1;
748 if ((b >= 0) && (b & KEY_ALTED) && (alt >= 0)) {
749 Conflicts[j].key = alt;
750 Conflicts[alt].key = j;
751 Conflicts_tabs[ Control_config[j].tab ] = 1;
752 Conflicts_tabs[ Control_config[alt].tab ] = 1;
756 if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) {
757 Conflicts[i].joy = j;
758 Conflicts[j].joy = i;
759 Conflicts_tabs[ Control_config[i].tab ] = 1;
760 Conflicts_tabs[ Control_config[j].tab ] = 1;
767 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
768 Conflicts_axes[i] = -1;
770 for (i=0; i<NUM_JOY_AXIS_ACTIONS-1; i++) {
771 for (j=i+1; j<NUM_JOY_AXIS_ACTIONS; j++) {
772 if ((Axis_map_to[i] >= 0) && (Axis_map_to[i] == Axis_map_to[j])) {
773 Conflicts_axes[i] = j;
774 Conflicts_axes[j] = i;
775 Conflicts_tabs[SHIP_TAB] = 1;
781 // do list setup required prior to rendering and checking for the controls listing. Called when list changes
782 void control_config_list_prepare()
785 int font_height = gr_get_font_height();
787 Num_cc_lines = y = z = 0;
788 while (z < CCFG_MAX) {
789 if ((Control_config[z].tab == Tab) && control_config_valid_action(z)) {
790 k = Control_config[z].key_id;
791 j = Control_config[z].joy_id;
792 Cc_lines[Num_cc_lines].label = XSTR(Control_config[z].text, CONTROL_CONFIG_XSTR + z);
793 Cc_lines[Num_cc_lines].cc_index = z;
794 Cc_lines[Num_cc_lines++].y = y;
795 y += font_height + 2;
801 if (Tab == SHIP_TAB) {
802 for (j=0; j<NUM_JOY_AXIS_ACTIONS; j++) {
803 Cc_lines[Num_cc_lines].label = Joy_axis_action_text[j];
804 Cc_lines[Num_cc_lines].cc_index = j | JOY_AXIS;
805 Cc_lines[Num_cc_lines++].y = y;
806 y += font_height + 2;
811 int cc_line_query_visible(int n)
815 if ((n < 0) || (n >= Num_cc_lines))
818 y = Cc_lines[n].y - Cc_lines[Scroll_offset].y;
819 if ((y < 0) || (y + gr_get_font_height() > Control_list_coords[gr_screen.res][CONTROL_H_COORD])){
826 // allocates the required space for one undo block and put it in the beginning of the linked list (top of a stack).
827 // Returns a pointer to this newly allocated block
828 config_item_undo *get_undo_block(int size)
830 config_item_undo *ptr;
832 ptr = (config_item_undo *) malloc( sizeof(config_item_undo) );
834 ptr->next = Config_item_undo;
835 Config_item_undo = ptr;
839 ptr->index = (int *) malloc( sizeof(int) * size );
841 ptr->list = (config_item *) malloc( sizeof(config_item) * size );
852 // frees one undo block. The first one in the list (top of the stack) to be precise.
853 void free_undo_block()
855 config_item_undo *ptr;
857 ptr = Config_item_undo;
861 Config_item_undo = ptr->next;
870 // undo the most recent binding changes
871 int control_config_undo_last()
875 if (!Config_item_undo) {
876 gamesnd_play_iface(SND_GENERAL_FAIL);
880 if (Config_item_undo->index[0] & JOY_AXIS)
883 tab = Control_config[Config_item_undo->index[0]].tab;
885 for (i=1; i<Config_item_undo->size; i++) {
886 if (Config_item_undo->index[i] & JOY_AXIS) {
891 if (Control_config[Config_item_undo->index[i]].tab != tab)
899 for (i=0; i<Config_item_undo->size; i++) {
900 z = Config_item_undo->index[i];
905 ptr = &Config_item_undo->list[i];
906 Axis_map_to[z] = ptr->joy_id;
907 Invert_axis[z] = ptr->used;
910 Control_config[z] = Config_item_undo->list[i];
915 control_config_conflict_check();
916 control_config_list_prepare();
917 gamesnd_play_iface(SND_USER_SELECT);
921 void control_config_save_axis_undo(int axis)
923 config_item_undo *ptr;
926 item.joy_id = (short) Axis_map_to[axis];
928 item.used = Invert_axis[axis];
930 ptr = get_undo_block(1);
931 ptr->index[0] = axis | JOY_AXIS;
935 void control_config_bind_key(int i, int key)
937 config_item_undo *ptr;
939 ptr = get_undo_block(1);
941 ptr->list[0] = Control_config[i];
942 Control_config[i].key_id = (short) key;
945 void control_config_bind_joy(int i, int joy)
947 config_item_undo *ptr;
949 ptr = get_undo_block(1);
951 ptr->list[0] = Control_config[i];
952 Control_config[i].joy_id = (short) joy;
955 void control_config_bind_axis(int i, int axis)
957 control_config_save_axis_undo(i);
958 Axis_map_to[i] = axis;
961 int control_config_remove_binding()
964 config_item_undo *ptr;
966 if (Selected_line < 0) {
967 gamesnd_play_iface(SND_GENERAL_FAIL);
971 z = Cc_lines[Selected_line].cc_index;
974 if (Axis_map_to[z] < 0) {
975 gamesnd_play_iface(SND_GENERAL_FAIL);
979 control_config_save_axis_undo(z);
981 control_config_conflict_check();
982 control_config_list_prepare();
983 gamesnd_play_iface(SND_USER_SELECT);
988 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
989 gamesnd_play_iface(SND_GENERAL_FAIL);
994 ptr = get_undo_block(1);
996 ptr->list[0] = Control_config[z];
998 if (Selected_item && (Control_config[z].joy_id >= 0)) // if not just key selected (which would be 0)
999 Control_config[z].joy_id = (short) -1;
1001 if ((Selected_item != 1) && (Control_config[z].key_id >= 0)) // if not just joy button selected (1)
1002 Control_config[z].key_id = (short) -1;
1004 control_config_conflict_check();
1005 control_config_list_prepare();
1006 gamesnd_play_iface(SND_USER_SELECT);
1011 int control_config_clear_other()
1013 int z, i, j, total = 0;
1014 config_item_undo *ptr;
1016 if (Selected_line < 0) {
1017 gamesnd_play_iface(SND_GENERAL_FAIL);
1021 z = Cc_lines[Selected_line].cc_index;
1026 if (Axis_map_to[z] < 0) {
1027 gamesnd_play_iface(SND_GENERAL_FAIL);
1031 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1032 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z))
1036 gamesnd_play_iface(SND_GENERAL_FAIL);
1040 ptr = get_undo_block(total);
1041 for (i=j=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1042 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
1043 item.joy_id = (short) Axis_map_to[i];
1044 item.used = Invert_axis[i];
1046 ptr->index[j] = i | JOY_AXIS;
1047 ptr->list[j] = item;
1050 Axis_map_to[i] = -1;
1053 control_config_conflict_check();
1054 control_config_list_prepare();
1055 gamesnd_play_iface(SND_USER_SELECT);
1059 for (i=0; i<CCFG_MAX; i++)
1060 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1065 gamesnd_play_iface(SND_GENERAL_FAIL);
1069 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1070 gamesnd_play_iface(SND_GENERAL_FAIL);
1074 // now, back up the old bindings so we can undo if we want to
1075 ptr = get_undo_block(total);
1076 for (i=j=0; i<CCFG_MAX; i++)
1077 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1080 ptr->list[j] = Control_config[i];
1083 if (Control_config[i].key_id == Control_config[z].key_id)
1084 Control_config[i].key_id = (short) -1;
1085 if (Control_config[i].joy_id == Control_config[z].joy_id)
1086 Control_config[i].joy_id = (short) -1;
1089 control_config_conflict_check();
1090 control_config_list_prepare();
1091 gamesnd_play_iface(SND_USER_SELECT);
1095 int control_config_clear_all()
1097 int i, j, total = 0;
1098 config_item_undo *ptr;
1100 // first, determine how many bindings need to be changed
1101 for (i=0; i<CCFG_MAX; i++)
1102 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0))
1106 gamesnd_play_iface(SND_GENERAL_FAIL);
1110 // now, back up the old bindings so we can undo if we want to
1111 ptr = get_undo_block(total);
1112 for (i=j=0; i<CCFG_MAX; i++) {
1113 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
1115 ptr->list[j] = Control_config[i];
1121 for (i=0; i<CCFG_MAX; i++) {
1122 Control_config[i].key_id = Control_config[i].joy_id = -1;
1125 control_config_conflict_check();
1126 control_config_list_prepare();
1127 gamesnd_play_iface(SND_RESET_PRESSED);
1131 extern Joy_info joystick;
1133 int control_config_axis_default(int axis)
1138 if (Axis_map_to_defaults[axis] < 0)
1141 if (!joystick.axis_valid[Axis_map_to_defaults[axis]])
1145 return Axis_map_to_defaults[axis];
1148 int control_config_do_reset()
1150 int i, j, total = 0;
1151 config_item_undo *ptr;
1154 // first, determine how many bindings need to be changed
1155 for (i=0; i<CCFG_MAX; i++)
1156 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default))
1159 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1160 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i]))
1164 gamesnd_play_iface(SND_GENERAL_FAIL);
1168 // now, back up the old bindings so we can undo if we want to
1169 ptr = get_undo_block(total);
1170 for (i=j=0; i<CCFG_MAX; i++) {
1171 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
1173 ptr->list[j] = Control_config[i];
1178 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1179 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
1180 item.joy_id = (short) Axis_map_to[i];
1181 item.used = Invert_axis[i];
1183 ptr->index[j] = i | JOY_AXIS;
1184 ptr->list[j] = item;
1189 control_config_reset_defaults();
1190 control_config_conflict_check();
1191 control_config_list_prepare();
1192 gamesnd_play_iface(SND_RESET_PRESSED);
1196 // This sets all the controls to their default values
1197 void control_config_reset_defaults()
1201 // Reset keyboard defaults
1202 for (i=0; i<CCFG_MAX; i++) {
1203 Control_config[i].key_id = Control_config[i].key_default;
1204 Control_config[i].joy_id = Control_config[i].joy_default;
1207 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1208 Axis_map_to[i] = control_config_axis_default(i);
1209 Invert_axis[i] = Invert_axis_defaults[i];
1213 void control_config_scroll_screen_up()
1215 if (Scroll_offset) {
1217 Assert(Selected_line > Scroll_offset);
1218 while (!cc_line_query_visible(Selected_line))
1222 gamesnd_play_iface(SND_SCROLL);
1225 gamesnd_play_iface(SND_GENERAL_FAIL);
1228 void control_config_scroll_line_up()
1230 if (Selected_line) {
1232 if (Selected_line < Scroll_offset)
1233 Scroll_offset = Selected_line;
1236 gamesnd_play_iface(SND_SCROLL);
1239 gamesnd_play_iface(SND_GENERAL_FAIL);
1242 void control_config_scroll_screen_down()
1244 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]) {
1246 while (!cc_line_query_visible(Selected_line)) {
1248 Assert(Selected_line < Num_cc_lines);
1252 gamesnd_play_iface(SND_SCROLL);
1255 gamesnd_play_iface(SND_GENERAL_FAIL);
1258 void control_config_scroll_line_down()
1260 if (Selected_line < Num_cc_lines - 1) {
1262 Assert(Selected_line > Scroll_offset);
1263 while (!cc_line_query_visible(Selected_line))
1267 gamesnd_play_iface(SND_SCROLL);
1270 gamesnd_play_iface(SND_GENERAL_FAIL);
1273 void control_config_toggle_modifier(int bit)
1277 z = Cc_lines[Selected_line].cc_index;
1278 Assert(!(z & JOY_AXIS));
1279 k = Control_config[z].key_id;
1281 gamesnd_play_iface(SND_GENERAL_FAIL);
1285 control_config_bind_key(z, k ^ bit);
1286 control_config_conflict_check();
1287 gamesnd_play_iface(SND_USER_SELECT);
1290 void control_config_toggle_invert()
1294 z = Cc_lines[Selected_line].cc_index;
1295 Assert(z & JOY_AXIS);
1297 control_config_save_axis_undo(z);
1298 Invert_axis[z] = !Invert_axis[z];
1301 void control_config_do_bind()
1306 // if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
1307 if (Selected_line < 0) {
1308 gamesnd_play_iface(SND_GENERAL_FAIL);
1312 for (i=0; i<NUM_BUTTONS; i++)
1313 if (i != CANCEL_BUTTON) {
1314 CC_Buttons[gr_screen.res][i].button.reset_status();
1315 CC_Buttons[gr_screen.res][i].button.disable();
1318 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1319 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1321 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1322 joy_down_count(i); // clear checking status of all joystick buttons
1325 control_config_detect_axis_reset();
1328 Bind_time = timer_get_milliseconds();
1332 gamesnd_play_iface(SND_USER_SELECT);
1335 void control_config_do_search()
1339 for (i=0; i<NUM_BUTTONS; i++){
1340 if (i != CANCEL_BUTTON) {
1341 CC_Buttons[gr_screen.res][i].button.reset_status();
1342 CC_Buttons[gr_screen.res][i].button.disable();
1346 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1347 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1349 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1350 joy_down_count(i); // clear checking status of all joystick buttons
1356 gamesnd_play_iface(SND_USER_SELECT);
1359 void control_config_do_cancel(int fail = 0)
1365 for (i=0; i<NUM_BUTTONS; i++){
1366 if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
1367 CC_Buttons[gr_screen.res][i].button.enable();
1371 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.reset_status();
1372 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1373 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
1374 CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
1375 CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
1377 Binding_mode = Search_mode = 0;
1379 gamesnd_play_iface(SND_GENERAL_FAIL);
1381 gamesnd_play_iface(SND_USER_SELECT);
1385 int control_config_accept()
1389 for (i=0; i<NUM_TABS; i++)
1390 if (Conflicts_tabs[i])
1394 gamesnd_play_iface(SND_GENERAL_FAIL);
1398 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1399 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1400 gamesnd_play_iface(SND_COMMIT_PRESSED);
1404 void control_config_cancel_exit()
1408 for (i=0; i<CCFG_MAX; i++)
1409 Control_config[i] = Control_config_backup[i];
1411 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1414 void control_config_button_pressed(int n)
1422 Scroll_offset = Selected_line = 0;
1423 control_config_list_prepare();
1424 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1428 control_config_do_bind();
1432 control_config_do_search();
1436 control_config_toggle_modifier(KEY_SHIFTED);
1437 gamesnd_play_iface(SND_USER_SELECT);
1441 control_config_toggle_modifier(KEY_ALTED);
1442 gamesnd_play_iface(SND_USER_SELECT);
1446 control_config_toggle_invert();
1447 gamesnd_play_iface(SND_USER_SELECT);
1450 case SCROLL_UP_BUTTON:
1451 control_config_scroll_screen_up();
1454 case SCROLL_DOWN_BUTTON:
1455 control_config_scroll_screen_down();
1459 control_config_accept();
1463 control_config_remove_binding();
1467 launch_context_help();
1468 gamesnd_play_iface(SND_HELP_PRESSED);
1472 control_config_do_reset();
1476 control_config_undo_last();
1480 control_config_do_cancel();
1483 case CLEAR_OTHER_BUTTON:
1484 control_config_clear_other();
1487 case CLEAR_ALL_BUTTON:
1488 control_config_clear_all();
1493 char *control_config_tooltip_handler(char *str)
1497 if (!stricmp(str, NOX("@conflict"))) {
1498 for (i=0; i<NUM_TABS; i++) {
1499 if (Conflicts_tabs[i])
1500 return XSTR( "Conflict!", 205);
1507 void control_config_init()
1512 // make backup of all controls
1513 for (i=0; i<CCFG_MAX; i++)
1514 Control_config_backup[i] = Control_config[i];
1516 common_set_interface_palette(NOX("ControlConfigPalette")); // set the interface palette
1517 Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1518 Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
1519 Ui_window.tooltip_handler = control_config_tooltip_handler;
1521 // load in help overlay bitmap
1522 help_overlay_load(CONTROL_CONFIG_OVERLAY);
1523 help_overlay_set_state(CONTROL_CONFIG_OVERLAY,0);
1525 // reset conflict flashing
1526 Conflict_stamp = -1;
1528 for (i=0; i<NUM_BUTTONS; i++) {
1529 b = &CC_Buttons[gr_screen.res][i];
1531 if (b->hotspot < 0) { // temporary
1532 b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1); // temporary
1533 b->button.set_highlight_action(common_play_highlight_sound);
1537 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1539 // set up callback for when a mouse first goes over a button
1540 b->button.set_highlight_action(common_play_highlight_sound);
1542 b->button.set_bmaps(b->filename, 5, 1); // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1544 b->button.set_bmaps(b->filename);
1546 b->button.link_hotspot(b->hotspot);
1550 for(i=0; i<CC_NUM_TEXT; i++){
1551 Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
1554 for (i=0; i<LIST_BUTTONS_MAX; i++) {
1555 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1556 List_buttons[i].hide();
1557 List_buttons[i].disable();
1560 // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1561 CC_Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
1562 CC_Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
1563 CC_Buttons[gr_screen.res][BIND_BUTTON].button.set_hotkey(KEY_ENTER);
1564 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_DELETE);
1565 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_Z);
1566 CC_Buttons[gr_screen.res][CLEAR_BUTTON].button.set_hotkey(KEY_DELETE);
1567 CC_Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
1568 CC_Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(KEY_F1);
1569 CC_Buttons[gr_screen.res][RESET_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_R);
1570 CC_Buttons[gr_screen.res][INVERT_AXIS].button.set_hotkey(KEY_I);
1572 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1573 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
1575 Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);
1577 Scroll_offset = Selected_line = 0;
1578 Config_item_undo = NULL;
1579 control_config_conflict_check();
1582 Joy_axis_action_text[0] = strdup(XSTR("Turn (Yaw) Axis", 1016));
1583 Joy_axis_action_text[1] = strdup(XSTR("Pitch Axis", 1017));
1584 Joy_axis_action_text[2] = strdup(XSTR("Bank Axis", 1018));
1585 Joy_axis_action_text[3] = strdup(XSTR("Absolute Throttle Axis", 1019));
1586 Joy_axis_action_text[4] = strdup(XSTR("Relative Throttle Axis", 1020));
1587 Joy_axis_text[0] = strdup(XSTR("Joystick/Mouse X Axis", 1021));
1588 Joy_axis_text[1] = strdup(XSTR("Joystick/Mouse Y Axis", 1022));
1589 Joy_axis_text[2] = strdup(XSTR("Joystick Z Axis", 1023));
1590 Joy_axis_text[3] = strdup(XSTR("Joystick rX Axis", 1024));
1591 Joy_axis_text[4] = strdup(XSTR("Joystick rY Axis", 1025));
1592 Joy_axis_text[5] = strdup(XSTR("Joystick rZ Axis", 1026));
1593 Mouse_button_text[0] = strdup("");
1594 Mouse_button_text[1] = strdup(XSTR("Left Button", 1027));
1595 Mouse_button_text[2] = strdup(XSTR("Right Button", 1028));
1596 Mouse_button_text[3] = strdup(XSTR("Mid Button", 1029));
1597 Mouse_button_text[4] = strdup("");
1598 Mouse_axis_text[0] = strdup(XSTR("L/R", 1030));
1599 Mouse_axis_text[1] = strdup(XSTR("U/B", 1031));
1600 Invert_text[0] = strdup(XSTR("N", 1032));
1601 Invert_text[1] = strdup(XSTR("Y", 1033));
1603 control_config_list_prepare();
1606 void control_config_close()
1610 while (Config_item_undo){
1614 // unload the overlay bitmap
1615 help_overlay_unload(CONTROL_CONFIG_OVERLAY);
1617 if (Background_bitmap){
1618 bm_unload(Background_bitmap);
1621 Ui_window.destroy();
1622 common_free_interface_palette(); // restore game palette
1623 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1628 for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
1629 if(Joy_axis_action_text[idx] != NULL){
1630 free(Joy_axis_action_text[idx]);
1631 Joy_axis_action_text[idx] = NULL;
1634 for(idx=0; idx<NUM_AXIS_TEXT; idx++){
1635 if(Joy_axis_text[idx] != NULL){
1636 free(Joy_axis_text[idx]);
1637 Joy_axis_text[idx] = NULL;
1640 for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
1641 if(Mouse_button_text[idx] != NULL){
1642 free(Mouse_button_text[idx]);
1643 Mouse_button_text[idx] = NULL;
1646 for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
1647 if(Mouse_axis_text[idx] != NULL){
1648 free(Mouse_axis_text[idx]);
1649 Mouse_axis_text[idx] = NULL;
1652 for(idx=0; idx<NUM_INVERT_TEXT; idx++){
1653 if(Invert_text[idx] != NULL){
1654 free(Invert_text[idx]);
1655 Invert_text[idx] = NULL;
1660 void control_config_do_frame(float frametime)
1662 char buf[256], *str, *jptr;
1663 int i, j, k, w, x, y, z, len, line, conflict;
1664 int font_height = gr_get_font_height();
1665 int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
1666 static float timer = 0.0f;
1668 static int bound_timestamp = 0;
1669 static char bound_string[40];
1674 if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1677 z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1678 i = control_config_detect_axis();
1685 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1686 Ui_window.process(0);
1689 strcpy(bound_string, XSTR( "Canceled", 206));
1690 bound_timestamp = timestamp(2500);
1691 control_config_do_cancel();
1697 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1698 if (joy_down_count(i))
1702 if (Axis_override >= 0) {
1703 control_config_bind_axis(z, Axis_override);
1704 strcpy(bound_string, Joy_axis_text[Axis_override]);
1705 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1706 bound_timestamp = timestamp(2500);
1707 control_config_conflict_check();
1708 control_config_list_prepare();
1709 control_config_do_cancel();
1712 control_config_do_cancel(1);
1718 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1719 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1720 Ui_window.set_ignore_gadgets(1);
1724 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1725 Ui_window.process(0);
1727 if ( (k > 0) || B1_JUST_RELEASED ) {
1728 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1729 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1730 Ui_window.set_ignore_gadgets(0);
1735 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1736 Ui_window.set_ignore_gadgets(0);
1740 strcpy(bound_string, XSTR( "Canceled", 206));
1741 bound_timestamp = timestamp(2500);
1742 control_config_do_cancel();
1745 switch (k & KEY_MASK) {
1750 Last_key = k & KEY_MASK;
1755 if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED) // a special hack just for Mike K.
1756 if ( (Last_key >= 0) && (k <= 0) && !keyd_pressed[Last_key] )
1759 if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1760 popup(0, 1, POPUP_OK, XSTR( "That is a non-bindable key. Please try again.", 207));
1764 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1766 z = Cc_lines[Selected_line].cc_index;
1767 Assert(!(z & JOY_AXIS));
1768 control_config_bind_key(z, k);
1770 strcpy(bound_string, textify_scancode(k));
1771 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1772 bound_timestamp = timestamp(2500);
1773 control_config_conflict_check();
1774 control_config_list_prepare();
1775 control_config_do_cancel();
1778 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1779 if (joy_down_count(i)) {
1780 z = Cc_lines[Selected_line].cc_index;
1781 Assert(!(z & JOY_AXIS));
1782 control_config_bind_joy(z, i);
1784 strcpy(bound_string, Joy_button_text[i]);
1785 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1786 bound_timestamp = timestamp(2500);
1787 control_config_conflict_check();
1788 control_config_list_prepare();
1789 control_config_do_cancel();
1793 if (Bind_time + 375 < timer_get_milliseconds()) {
1794 for (i=0; i<NUM_BUTTONS; i++){
1795 if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
1800 if (i == NUM_BUTTONS) { // no buttons pressed
1801 for (i=0; i<MOUSE_NUM_BUTTONS; i++)
1802 if (mouse_down(1 << i)) {
1803 z = Cc_lines[Selected_line].cc_index;
1804 Assert(!(z & JOY_AXIS));
1805 control_config_bind_joy(z, i);
1807 strcpy(bound_string, Joy_button_text[i]);
1808 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1809 bound_timestamp = timestamp(2500);
1810 control_config_conflict_check();
1811 control_config_list_prepare();
1812 control_config_do_cancel();
1813 for (j=0; j<NUM_BUTTONS; j++){
1814 CC_Buttons[gr_screen.res][j].button.reset();
1824 } else if (Search_mode) {
1825 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1826 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1827 Ui_window.set_ignore_gadgets(1);
1831 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1832 Ui_window.process(0);
1834 if ( (k > 0) || B1_JUST_RELEASED ) {
1835 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1836 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1837 Ui_window.set_ignore_gadgets(0);
1842 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1843 Ui_window.set_ignore_gadgets(0);
1847 control_config_do_cancel();
1850 if ((k > 0) && !Config_allowed[k & KEY_MASK])
1853 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1856 for (i=0; i<CCFG_MAX; i++)
1857 if (Control_config[i].key_id == k) {
1863 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1864 if (joy_down_count(i)) {
1866 for (i=0; i<CCFG_MAX; i++)
1867 if (Control_config[i].joy_id == j) {
1875 // check if not on enabled button
1876 for (j=0; j<NUM_BUTTONS; j++){
1877 if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
1882 if (j == NUM_BUTTONS) { // no buttons pressed
1883 for (j=0; j<MOUSE_NUM_BUTTONS; j++)
1884 if (mouse_down(1 << j)) {
1885 for (i=0; i<CCFG_MAX; i++)
1886 if (Control_config[i].joy_id == j) {
1888 for (j=0; j<NUM_BUTTONS; j++){
1889 CC_Buttons[gr_screen.res][j].button.reset();
1899 Tab = Control_config[z].tab;
1900 control_config_list_prepare();
1901 Selected_line = Scroll_offset = 0;
1902 for (i=0; i<Num_cc_lines; i++)
1903 if (Cc_lines[i].cc_index == z) {
1908 while (!cc_line_query_visible(Selected_line)) {
1910 Assert(Scroll_offset < Num_cc_lines);
1916 z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
1917 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
1918 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
1919 CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
1922 z = Cc_lines[Selected_line].cc_index;
1923 k = Control_config[z].key_id;
1924 if ( (k == KEY_LALT) || (k == KEY_RALT) || (k == KEY_LSHIFT) || (k == KEY_RSHIFT) ) {
1925 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
1926 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
1930 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
1932 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1933 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1934 Ui_window.set_ignore_gadgets(1);
1937 k = Ui_window.process();
1939 if ( (k > 0) || B1_JUST_RELEASED ) {
1940 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1941 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1942 Ui_window.set_ignore_gadgets(0);
1947 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1948 Ui_window.set_ignore_gadgets(0);
1952 case KEY_DOWN: // select next line
1953 control_config_scroll_line_down();
1956 case KEY_UP: // select previous line
1957 control_config_scroll_line_up();
1960 case KEY_SHIFTED | KEY_TAB: // activate previous tab
1965 Scroll_offset = Selected_line = 0;
1966 control_config_list_prepare();
1967 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1970 case KEY_TAB: // activate next tab
1972 if (Tab >= NUM_TABS)
1975 Scroll_offset = Selected_line = 0;
1976 control_config_list_prepare();
1977 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1982 if (Selected_item == -2) {
1984 if (Cc_lines[Selected_line].jw < 1) {
1986 if (Cc_lines[Selected_line].kw < 1)
1991 gamesnd_play_iface(SND_SCROLL);
1996 if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1))
1998 else if (!Selected_item && (Cc_lines[Selected_line].kw < 1))
2000 else if (Selected_item > 1)
2003 gamesnd_play_iface(SND_SCROLL);
2006 case KEY_BACKSP: // undo
2007 control_config_undo_last();
2011 control_config_cancel_exit();
2016 for (i=0; i<NUM_BUTTONS; i++){
2017 if (CC_Buttons[gr_screen.res][i].button.pressed()){
2018 control_config_button_pressed(i);
2022 for (i=0; i<LIST_BUTTONS_MAX; i++) {
2023 if (List_buttons[i].is_mouse_on())
2024 select_tease_line = i + Scroll_offset;
2026 if (List_buttons[i].pressed()) {
2027 Selected_line = i + Scroll_offset;
2029 List_buttons[i].get_mouse_pos(&x, &y);
2030 if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw))
2033 if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw))
2036 gamesnd_play_iface(SND_USER_SELECT);
2039 if (List_buttons[i].double_clicked())
2040 control_config_do_bind();
2043 GR_MAYBE_CLEAR_RES(Background_bitmap);
2044 if (Background_bitmap >= 0) {
2045 gr_set_bitmap(Background_bitmap);
2049 // highlight tab with conflict
2051 for (i=z=0; i<NUM_TABS; i++) {
2052 if (Conflicts_tabs[i]) {
2053 CC_Buttons[gr_screen.res][i].button.draw_forced(4);
2059 // maybe switch from bright to normal
2060 if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
2061 Conflict_bright = !Conflict_bright;
2063 Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
2066 // set color and font
2068 if(Conflict_bright){
2069 gr_set_color_fast(&Color_bright_red);
2071 gr_set_color_fast(&Color_red);
2074 // setup the conflict string
2075 char conflict_str[512] = "";
2076 strncpy(conflict_str, XSTR("Conflict!", 205), 511);
2078 gr_get_string_size(&sw, &sh, conflict_str);
2080 gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str);
2084 // might as well always reset the conflict stamp
2085 Conflict_stamp = -1;
2088 for (i=0; i<NUM_TABS; i++) {
2089 if (CC_Buttons[gr_screen.res][i].button.button_down()) {
2094 if (i == NUM_TABS) {
2095 CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
2099 CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
2102 if (Selected_line >= 0) {
2103 z = Cc_lines[Selected_line].cc_index;
2105 if (Invert_axis[z & ~JOY_AXIS]) {
2106 CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
2110 z = Control_config[z].key_id;
2112 if (z & KEY_SHIFTED) {
2113 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
2115 if (z & KEY_ALTED) {
2116 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
2123 CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
2126 z = Cc_lines[Selected_line].cc_index;
2127 x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2;
2128 y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2;
2132 t = (int) (timer * 3);
2134 gr_set_color_fast(&Color_text_normal);
2135 gr_get_string_size(&w, NULL, XSTR( "?", 208));
2136 gr_printf(x - w / 2, y - font_height / 2, XSTR( "?", 208));
2139 } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
2140 i = Conflicts[z].key;
2142 i = Conflicts[z].joy;
2144 gr_set_color_fast(&Color_text_normal);
2145 str = XSTR( "Control conflicts with:", 209);
2146 gr_get_string_size(&w, NULL, str);
2147 gr_printf(x - w / 2, y - font_height, str);
2149 strcpy(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
2150 gr_force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
2151 gr_get_string_size(&w, NULL, buf);
2152 gr_printf(x - w / 2, y, buf);
2154 } else if (*bound_string) {
2155 gr_set_color_fast(&Color_text_normal);
2156 gr_get_string_size(&w, NULL, bound_string);
2157 gr_printf(x - w / 2, y - font_height / 2, bound_string);
2158 if (timestamp_elapsed(bound_timestamp))
2162 // gr_set_color_fast(&Color_text_heading);
2163 // gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]);
2165 // gr_get_string_size(&w, &h, Heading[Tab]);
2166 // y = HEADING_Y + h / 2 - 1;
2167 // gr_line(LIST_X, y, LIST_X + 18, y);
2168 // gr_line(LIST_X + w + 21, y, LIST_X + LIST_W, y);
2170 if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
2171 gr_set_color_fast(&Color_white);
2172 gr_printf(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210));
2176 line = Scroll_offset;
2177 while (cc_line_query_visible(line)) {
2178 z = Cc_lines[line].cc_index;
2179 y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
2181 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);
2182 List_buttons[line - Scroll_offset].enable(!Binding_mode);
2184 Cc_lines[line].kw = Cc_lines[line].jw = 0;
2186 if (line == Selected_line){
2187 c = &Color_text_selected;
2188 } else if (line == select_tease_line) {
2189 c = &Color_text_subselected;
2191 c = &Color_text_normal;
2194 gr_set_color_fast(c);
2195 if (Cc_lines[line].label) {
2196 strcpy(buf, Cc_lines[line].label);
2197 gr_force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
2198 gr_printf(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
2201 if (!(z & JOY_AXIS)) {
2202 k = Control_config[z].key_id;
2203 j = Control_config[z].joy_id;
2204 x = Control_list_key_x[gr_screen.res];
2208 if ((k < 0) && (j < 0)) {
2209 gr_set_color_fast(&Color_grey);
2210 gr_printf(x, y, XSTR( "None", 211));
2214 strcpy(buf, textify_scancode(k));
2215 if (Conflicts[z].key >= 0) {
2216 if (c == &Color_text_normal)
2217 gr_set_color_fast(&Color_text_error);
2219 gr_set_color_fast(&Color_text_error_hi);
2223 } else if (Selected_item == 1) {
2224 gr_set_color_fast(&Color_text_normal);
2227 gr_set_color_fast(c);
2229 gr_printf(x, y, buf);
2232 Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2233 gr_get_string_size(&w, NULL, buf);
2234 Cc_lines[line].kw = w;
2238 gr_set_color_fast(&Color_text_normal);
2239 gr_printf(x, y, XSTR( ", ", 212));
2240 gr_get_string_size(&w, NULL, XSTR( ", ", 212));
2246 strcpy(buf, Joy_button_text[j]);
2247 if (Conflicts[z].joy >= 0) {
2248 if (c == &Color_text_normal)
2249 gr_set_color_fast(&Color_text_error);
2251 gr_set_color_fast(&Color_text_error_hi);
2255 } else if (!Selected_item) {
2256 gr_set_color_fast(&Color_text_normal);
2259 gr_set_color_fast(c);
2261 gr_force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x);
2262 gr_printf(x, y, buf);
2264 Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2265 gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
2270 x = Control_list_key_x[gr_screen.res];
2271 j = Axis_map_to[z & ~JOY_AXIS];
2272 if (Binding_mode && (line == Selected_line))
2276 gr_set_color_fast(&Color_grey);
2277 gr_printf(x, y, XSTR( "None", 211));
2280 if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
2281 if (c == &Color_text_normal)
2282 gr_set_color_fast(&Color_text_error);
2285 gr_set_color_fast(&Color_text_error_hi);
2289 } else if (!Selected_item) {
2290 gr_set_color_fast(&Color_text_normal);
2293 gr_set_color_fast(c);
2295 gr_string(x, y, Joy_axis_text[j]);
2302 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
2304 i = line - Scroll_offset;
2305 while (i < LIST_BUTTONS_MAX)
2306 List_buttons[i++].disable();
2308 // blit help overlay if active
2309 help_overlay_maybe_blit(CONTROL_CONFIG_OVERLAY);
2314 void clear_key_binding(short key)
2318 for (i=0; i<CCFG_MAX; i++) {
2319 if (Control_config[i].key_id == key)
2320 Control_config[i].key_id = -1;
2324 float check_control_timef(int id)
2328 // if type isn't continuous, we shouldn't be using this function, cause it won't work.
2329 Assert(Control_config[id].type == CC_TYPE_CONTINUOUS);
2331 // first, see if control actually used (makes sure modifiers match as well)
2332 if (!check_control(id))
2335 t1 = key_down_timef(Control_config[id].key_id);
2339 t2 = joy_down_time(Control_config[id].joy_id);
2349 void control_check_indicate()
2352 if (Show_controls_info) {
2353 gr_set_color_fast(&HUD_color_debug);
2354 gr_printf(490, 15, NOX("Ctrls checked: %d"), Control_check_count);
2358 Control_check_count = 0;
2361 int check_control(int id, int key)
2364 static int last_key = 0;
2366 Control_check_count++;
2372 // if we're in multiplayer text enter (for chat) mode, check to see if we should ignore controls
2373 if ((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls()){
2377 if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
2378 if (joy_down(Control_config[id].joy_id) || joy_down_count(Control_config[id].joy_id)) {
2383 if ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS))
2384 if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2389 // check what current modifiers are pressed
2391 if (keyd_pressed[KEY_LSHIFT] || key_down_count(KEY_LSHIFT) || keyd_pressed[KEY_RSHIFT] || key_down_count(KEY_RSHIFT))
2392 mask |= KEY_SHIFTED;
2394 if (keyd_pressed[KEY_LALT] || key_down_count(KEY_LALT) || keyd_pressed[KEY_RALT] || key_down_count(KEY_RALT))
2397 z = Control_config[id].key_id;
2399 if ( (z != KEY_LALT) && (z != KEY_RALT) && (z != KEY_LSHIFT) && (z != KEY_RSHIFT) ) {
2400 // if current modifiers don't match action's modifiers, don't register control active.
2401 if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask)
2407 if (keyd_pressed[z] || key_down_count(z)) {
2408 if ( !hud_squadmsg_read_key(z) ) {
2418 if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2426 // get heading, pitch, bank, throttle abs. and throttle rel. values.
2427 void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
2429 int axes_values[JOY_NUM_AXES];
2431 joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
2433 // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
2435 if (Axis_map_to[0] >= 0)
2436 *h = joy_get_scaled_reading(axes_values[Axis_map_to[0]], Axis_map_to[0]);
2439 if (Axis_map_to[1] >= 0)
2440 *p = joy_get_scaled_reading(axes_values[Axis_map_to[1]], Axis_map_to[1]);
2443 if (Axis_map_to[2] >= 0)
2444 *b = joy_get_scaled_reading(axes_values[Axis_map_to[2]], Axis_map_to[2]);
2447 if (Axis_map_to[3] >= 0)
2448 *ta = joy_get_unscaled_reading(axes_values[Axis_map_to[3]], Axis_map_to[3]);
2451 if (Axis_map_to[4] >= 0)
2452 *tr = joy_get_scaled_reading(axes_values[Axis_map_to[4]], Axis_map_to[4]);
2468 void control_used(int id)
2470 Control_config[id].used = timestamp();
2473 void control_config_clear_used_status()
2477 for (i=0; i<CCFG_MAX; i++)
2478 Control_config[i].used = 0;
2481 void control_config_clear()
2485 // Reset keyboard defaults
2486 for (i=0; i<CCFG_MAX; i++)
2487 Control_config[i].key_id = Control_config[i].joy_id = -1;
2490 int control_config_handle_conflict()
2492 if ((Selected_item == -1) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) { // we are deleting a conflict
2493 j = Conflicts[z].joy;
2494 if ((j >= 0) && (Control_config[j].joy_id < 0))
2497 k = Conflicts[z].key;
2498 if ((k >= 0) && (Control_config[k].key_id < 0))
2501 if ((j >= 0) && (k >= 0) && (j != k)) { // deleting 2 conflicts, each in different actions
2502 ptr = get_undo_block(2);
2504 ptr->list[0] = Control_config[j];
2505 Control_config[j].joy_id = (short) -1;
2508 ptr->list[1] = Control_config[k];
2509 Control_config[k].key_id = (short) -1;
2511 } else { // only 1 action in conflict with selected action (might be both controls, though)
2517 ptr = get_undo_block(1);
2519 ptr->list[0] = Control_config[z];
2522 Control_config[z].joy_id = (short) -1;
2525 Control_config[z].key_id = (short) -1;