2 * $Logfile: /Freespace2/code/ControlConfig/ControlsConfig.cpp $
7 * C module for keyboard, joystick and mouse configuration
10 * Revision 1.2 2002/05/07 03:16:43 theoddone33
11 * The Great Newline Fix
13 * Revision 1.1.1.1 2002/05/03 03:28:08 root
17 * 13 10/14/99 2:50p Jefff
20 * 12 8/16/99 9:50a Jefff
21 * fixed loading of tab bitmaps
23 * 11 8/11/99 3:21p Jefff
24 * added tab highlights on conflict
26 * 10 7/26/99 5:25p Jefff
27 * removed invalidation of key binding for demo builds
29 * 9 7/19/99 2:13p Dave
30 * Added some new strings for Heiko.
32 * 8 7/15/99 9:20a Andsager
33 * FS2_DEMO initial checkin
35 * 7 6/19/99 2:46p Dave
36 * New control config screen.
38 * 6 1/30/99 5:08p Dave
39 * More new hi-res stuff.Support for nice D3D textures.
41 * 5 1/15/99 11:29a Neilk
42 * Fixed D3D screen/texture pixel formatting problem.
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
49 * 3 10/13/98 9:28a Dave
50 * Started neatening up freespace.h. Many variables renamed and
51 * reorganized. Added AlphaColors.[h,cpp]
53 * 2 10/07/98 10:52a Dave
56 * 1 10/07/98 10:48a Dave
58 * 61 8/09/98 11:55a Lawrance
59 * if GRAVIS_OEM is defined, map the throttle axis by default
61 * 60 6/19/98 3:51p Lawrance
62 * localize control text
64 * 59 6/17/98 11:04a Lawrance
65 * localize the control config strings
67 * 58 6/13/98 5:19p Hoffoss
68 * externalized control config texts.
70 * 57 6/09/98 5:15p Lawrance
71 * French/German localization
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
79 * 55 6/01/98 11:43a John
80 * JAS & MK: Classified all strings for localization.
82 * 54 5/26/98 11:10a Lawrance
83 * Fix bug where window controls get disabled when F1 pressed twice
85 * 53 5/20/98 10:35p Hoffoss
86 * Fixed bug with mouse buttons not working when action isn't continuous.
88 * 52 5/19/98 4:08p Allender
89 * kill default binding for Z axis
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,
96 * 50 5/19/98 11:11a Lawrance
97 * Ensure X and Y axis have defaults!
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!
103 * 48 5/18/98 10:15a Lawrance
104 * Only do hud squad msg key check when necessary
106 * 47 5/18/98 10:08a Lawrance
107 * deal with overlap between hud squad msg number keys and CC_CONTINUOUS
110 * 46 5/17/98 5:44p Hoffoss
111 * Made throttle never bound by default (ask Sandeep why if interested).
113 * 45 5/14/98 5:32p Hoffoss
114 * Improved axis binding code some more.
116 * 44 5/13/98 7:15p Hoffoss
117 * Fixed remaining bugs with axis binding.
119 * 43 5/13/98 1:17a Hoffoss
120 * Added joystick axes configurability.
122 * 42 5/12/98 3:49p Hoffoss
123 * Fixed bug where double mouse click would bind mouse button right away.
125 * 41 5/11/98 5:43p Hoffoss
126 * Made num lock not bindable.
128 * 40 5/11/98 5:29p Hoffoss
129 * Added mouse button mapped to joystick button support.
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.
135 * 38 5/05/98 1:48a Lawrance
136 * Add in missing help overlays
138 * 37 4/27/98 10:11a Lawrance
139 * Add in disabled beep for missing buttons
141 * 36 4/25/98 2:59p Hoffoss
142 * Fixed typo that was causing a bug.
144 * 35 4/22/98 1:51a Lawrance
145 * Take out multiplayer key from demo key config
147 * 34 4/16/98 4:29p Hoffoss
148 * Fixed bank_when_pressed functionality when using alt or shift for it.
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
154 * 32 4/14/98 2:45p Hoffoss
155 * Made hitting escape to exit screen not play failed sound.
157 * 31 4/14/98 2:27p Hoffoss
158 * Made certain actions be hidden in demo build.
160 * 30 4/13/98 2:38p Hoffoss
161 * Added a tooltip handler and make binding attempts with illegal keys
164 * 29 4/11/98 7:59p Lawrance
165 * Add support for help overlays
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.
171 * 27 4/08/98 11:11a Hoffoss
172 * Fixed some bugs that showed up due to fixing other bugs the other day
175 * 26 4/07/98 3:47p Hoffoss
176 * Fixed continuous controls checking with respect to modifiers.
178 * 25 4/06/98 11:17a Hoffoss
179 * Fixed num lock/pause interplay bug.
181 * 24 4/03/98 3:51p Hoffoss
182 * Fixed some bugs, and made changed Interplay requested regarding search
185 * 23 3/31/98 4:12p Hoffoss
186 * Made control used status clear at mission init time.
188 * 22 3/23/98 11:28a Hoffoss
189 * Fixed flashing question mark bug.
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.
195 * 20 3/20/98 3:37p Hoffoss
196 * Tried to fix mitri's bug, failed miserably.
198 * 19 3/19/98 5:04p Dave
199 * Put in support for targeted multiplayer text and voice messaging (all,
200 * friendly, hostile, individual).
202 * 18 3/18/98 12:03p John
203 * Marked all the new strings as externalized or not.
205 * 17 3/18/98 10:16a Hoffoss
208 * 16 3/17/98 11:15a Hoffoss
209 * Made question mark that appears when you are in bind mode flash.
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.
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.
219 * 13 3/11/98 5:28p Hoffoss
220 * Added control config debug display info to possibly aid in tracking
223 * 12 2/26/98 10:07p Hoffoss
224 * Rewrote state saving and restoring to fix bugs and simplify the code.
226 * 11 2/22/98 12:19p John
227 * Externalized some strings
229 * 10 2/20/98 3:39p Hoffoss
230 * Updated code for new control config screen artwork.
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.
236 * 8 2/07/98 10:04p Hoffoss
237 * Changed color and placement of "more" indicator.
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.
243 * 6 2/03/98 5:05p Hoffoss
244 * Added "clear other" button to clear all conflicting bindings with
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.
251 * 4 1/20/98 4:20p Hoffoss
252 * Removed confusing behavior of clear button clearing out the other
253 * binding in a conflict.
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.
259 * 2 12/24/97 3:37p Hoffoss
260 * Moved control config stuff to seperate library to Fred can access it as
263 * 1 12/24/97 11:58a Hoffoss
265 * 98 12/22/97 2:15p Hoffoss
266 * Fixed bug where joystick axis lines weren't being displayed.
268 * 97 12/16/97 2:44p Hoffoss
269 * Added clear button to control config screen.
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.
275 * 95 12/07/97 2:36p John
276 * Made warp out be Alt+J instead of J
278 * 94 12/03/97 4:59p Hoffoss
279 * Added reset sound and change control config sounds around.
281 * 93 12/03/97 4:16p Hoffoss
282 * Changed sound stuff used in interface screens for interface purposes.
288 #include "freespace.h"
289 #include "controlsconfig.h"
290 #include "gamesequence.h"
293 #include "hudsquadmsg.h"
303 #include "missionscreencommon.h"
306 #include "managepilot.h"
307 #include "multi_pmsg.h"
308 #include "contexthelp.h"
311 #include "multiutil.h"
312 #include "alphacolors.h"
314 #define NUM_SYSTEM_KEYS 14
315 #define NUM_BUTTONS 19
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
324 char* Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS] = {
325 "ControlConfig", // GR_640
326 "2_ControlConfig" // GR_1024
329 char* Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
330 "ControlConfig-m", // GR_640
331 "2_ControlConfig-m" // GR_1024
335 int Control_list_coords[GR_NUM_RESOLUTIONS][4] = {
337 32, 58, 198, 259 // GR_640
340 32, 94, 904, 424 // GR_1024
344 // width of the control name section of the list
345 int Control_list_ctrl_w[GR_NUM_RESOLUTIONS] = {
350 // x start position of the binding area section of the list
351 int Control_list_key_x[GR_NUM_RESOLUTIONS] = {
356 // width of the binding area section of the list
357 int Control_list_key_w[GR_NUM_RESOLUTIONS] = {
362 // display the "more..." text under the control list
363 int Control_more_coords[GR_NUM_RESOLUTIONS][2] = {
372 // area to display "conflicts with..." text
373 int Conflict_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
375 32, 313, 250, 32 // GR_640
378 48, 508, 354, 46 // GR_1024
382 // conflict warning anim coords
383 int Conflict_warning_coords[GR_NUM_RESOLUTIONS][2] = {
392 // for flashing the conflict text
393 #define CONFLICT_FLASH_TIME 250
394 int Conflict_stamp = -1;
395 int Conflict_bright = 0;
397 #define LIST_BUTTONS_MAX 40
398 #define JOY_AXIS 0x80000
400 static int Num_cc_lines;
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];
408 // struct to hold backup config_item elements so we can undo them
409 struct config_item_undo {
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;
416 config_item Control_config_backup[CCFG_MAX];
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 };
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 };
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];
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
442 int Control_check_count = 0;
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;
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];
463 int Conflicts_axes[NUM_JOY_AXIS_ACTIONS];
468 #define COMPUTER_TAB 3
469 #define SCROLL_UP_BUTTON 4
470 #define SCROLL_DOWN_BUTTON 5
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
485 ui_button_info CC_Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
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
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
531 #define CC_NUM_TEXT 20
532 UI_XSTR CC_text[GR_NUM_RESOLUTIONS][CC_NUM_TEXT] = {
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 },
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 },
579 // linked list head of undo items
580 config_item_undo *Config_item_undo;
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,
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,
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,
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,
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,
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,
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,
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,
627 // old invalid demo keys
628 #define INVALID_DEMO_KEYS_MAX 14
629 int Invalid_demo_keys[] = {
640 MULTI_MESSAGE_FRIENDLY,
641 MULTI_MESSAGE_HOSTILE,
642 MULTI_MESSAGE_TARGET,
643 MULTI_OBSERVER_ZOOM_TO
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;
651 int Show_controls_info = 0;
653 DCF_BOOL(show_controls_info, Show_controls_info)
656 static int Axes_origin[JOY_NUM_AXES];
658 void control_config_detect_axis_reset()
660 joystick_read_raw_axis(JOY_NUM_AXES, Axes_origin);
663 int control_config_detect_axis()
665 int i, d, axis = -1, delta = 16384;
666 int axes_values[JOY_NUM_AXES];
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]);
680 int control_config_valid_action(int n)
685 for (i=0; i<INVALID_DEMO_KEYS_MAX; i++)
686 if (n == Invalid_demo_keys[i])
693 void control_config_conflict_check()
695 int i, j, a, b, c, shift = -1, alt = -1;
697 for (i=0; i<CCFG_MAX; i++) {
698 Conflicts[i].key = Conflicts[i].joy = -1;
699 switch (Control_config[i].key_id) {
712 for (i=0; i<NUM_TABS; i++)
713 Conflicts_tabs[i] = 0;
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) {
721 a = Control_config[i].key_id;
722 b = Control_config[j].key_id;
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;
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;
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;
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;
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;
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;
770 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
771 Conflicts_axes[i] = -1;
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;
784 // do list setup required prior to rendering and checking for the controls listing. Called when list changes
785 void control_config_list_prepare()
788 int font_height = gr_get_font_height();
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;
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;
814 int cc_line_query_visible(int n)
818 if ((n < 0) || (n >= Num_cc_lines))
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])){
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)
833 config_item_undo *ptr;
835 ptr = (config_item_undo *) malloc( sizeof(config_item_undo) );
837 ptr->next = Config_item_undo;
838 Config_item_undo = ptr;
842 ptr->index = (int *) malloc( sizeof(int) * size );
844 ptr->list = (config_item *) malloc( sizeof(config_item) * size );
855 // frees one undo block. The first one in the list (top of the stack) to be precise.
856 void free_undo_block()
858 config_item_undo *ptr;
860 ptr = Config_item_undo;
864 Config_item_undo = ptr->next;
873 // undo the most recent binding changes
874 int control_config_undo_last()
878 if (!Config_item_undo) {
879 gamesnd_play_iface(SND_GENERAL_FAIL);
883 if (Config_item_undo->index[0] & JOY_AXIS)
886 tab = Control_config[Config_item_undo->index[0]].tab;
888 for (i=1; i<Config_item_undo->size; i++) {
889 if (Config_item_undo->index[i] & JOY_AXIS) {
894 if (Control_config[Config_item_undo->index[i]].tab != tab)
902 for (i=0; i<Config_item_undo->size; i++) {
903 z = Config_item_undo->index[i];
908 ptr = &Config_item_undo->list[i];
909 Axis_map_to[z] = ptr->joy_id;
910 Invert_axis[z] = ptr->used;
913 Control_config[z] = Config_item_undo->list[i];
918 control_config_conflict_check();
919 control_config_list_prepare();
920 gamesnd_play_iface(SND_USER_SELECT);
924 void control_config_save_axis_undo(int axis)
926 config_item_undo *ptr;
929 item.joy_id = (short) Axis_map_to[axis];
931 item.used = Invert_axis[axis];
933 ptr = get_undo_block(1);
934 ptr->index[0] = axis | JOY_AXIS;
938 void control_config_bind_key(int i, int key)
940 config_item_undo *ptr;
942 ptr = get_undo_block(1);
944 ptr->list[0] = Control_config[i];
945 Control_config[i].key_id = (short) key;
948 void control_config_bind_joy(int i, int joy)
950 config_item_undo *ptr;
952 ptr = get_undo_block(1);
954 ptr->list[0] = Control_config[i];
955 Control_config[i].joy_id = (short) joy;
958 void control_config_bind_axis(int i, int axis)
960 control_config_save_axis_undo(i);
961 Axis_map_to[i] = axis;
964 int control_config_remove_binding()
967 config_item_undo *ptr;
969 if (Selected_line < 0) {
970 gamesnd_play_iface(SND_GENERAL_FAIL);
974 z = Cc_lines[Selected_line].cc_index;
977 if (Axis_map_to[z] < 0) {
978 gamesnd_play_iface(SND_GENERAL_FAIL);
982 control_config_save_axis_undo(z);
984 control_config_conflict_check();
985 control_config_list_prepare();
986 gamesnd_play_iface(SND_USER_SELECT);
991 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
992 gamesnd_play_iface(SND_GENERAL_FAIL);
997 ptr = get_undo_block(1);
999 ptr->list[0] = Control_config[z];
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;
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;
1007 control_config_conflict_check();
1008 control_config_list_prepare();
1009 gamesnd_play_iface(SND_USER_SELECT);
1014 int control_config_clear_other()
1016 int z, i, j, total = 0;
1017 config_item_undo *ptr;
1019 if (Selected_line < 0) {
1020 gamesnd_play_iface(SND_GENERAL_FAIL);
1024 z = Cc_lines[Selected_line].cc_index;
1029 if (Axis_map_to[z] < 0) {
1030 gamesnd_play_iface(SND_GENERAL_FAIL);
1034 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1035 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z))
1039 gamesnd_play_iface(SND_GENERAL_FAIL);
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];
1049 ptr->index[j] = i | JOY_AXIS;
1050 ptr->list[j] = item;
1053 Axis_map_to[i] = -1;
1056 control_config_conflict_check();
1057 control_config_list_prepare();
1058 gamesnd_play_iface(SND_USER_SELECT);
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) )
1068 gamesnd_play_iface(SND_GENERAL_FAIL);
1072 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1073 gamesnd_play_iface(SND_GENERAL_FAIL);
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) )
1083 ptr->list[j] = Control_config[i];
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;
1092 control_config_conflict_check();
1093 control_config_list_prepare();
1094 gamesnd_play_iface(SND_USER_SELECT);
1098 int control_config_clear_all()
1100 int i, j, total = 0;
1101 config_item_undo *ptr;
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))
1109 gamesnd_play_iface(SND_GENERAL_FAIL);
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)) {
1118 ptr->list[j] = Control_config[i];
1124 for (i=0; i<CCFG_MAX; i++) {
1125 Control_config[i].key_id = Control_config[i].joy_id = -1;
1128 control_config_conflict_check();
1129 control_config_list_prepare();
1130 gamesnd_play_iface(SND_RESET_PRESSED);
1134 extern Joy_info joystick;
1136 int control_config_axis_default(int axis)
1141 if (Axis_map_to_defaults[axis] < 0)
1144 if (!joystick.axis_valid[Axis_map_to_defaults[axis]])
1148 return Axis_map_to_defaults[axis];
1151 int control_config_do_reset()
1153 int i, j, total = 0;
1154 config_item_undo *ptr;
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))
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]))
1167 gamesnd_play_iface(SND_GENERAL_FAIL);
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)) {
1176 ptr->list[j] = Control_config[i];
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];
1186 ptr->index[j] = i | JOY_AXIS;
1187 ptr->list[j] = item;
1192 control_config_reset_defaults();
1193 control_config_conflict_check();
1194 control_config_list_prepare();
1195 gamesnd_play_iface(SND_RESET_PRESSED);
1199 // This sets all the controls to their default values
1200 void control_config_reset_defaults()
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;
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];
1216 void control_config_scroll_screen_up()
1218 if (Scroll_offset) {
1220 Assert(Selected_line > Scroll_offset);
1221 while (!cc_line_query_visible(Selected_line))
1225 gamesnd_play_iface(SND_SCROLL);
1228 gamesnd_play_iface(SND_GENERAL_FAIL);
1231 void control_config_scroll_line_up()
1233 if (Selected_line) {
1235 if (Selected_line < Scroll_offset)
1236 Scroll_offset = Selected_line;
1239 gamesnd_play_iface(SND_SCROLL);
1242 gamesnd_play_iface(SND_GENERAL_FAIL);
1245 void control_config_scroll_screen_down()
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]) {
1249 while (!cc_line_query_visible(Selected_line)) {
1251 Assert(Selected_line < Num_cc_lines);
1255 gamesnd_play_iface(SND_SCROLL);
1258 gamesnd_play_iface(SND_GENERAL_FAIL);
1261 void control_config_scroll_line_down()
1263 if (Selected_line < Num_cc_lines - 1) {
1265 Assert(Selected_line > Scroll_offset);
1266 while (!cc_line_query_visible(Selected_line))
1270 gamesnd_play_iface(SND_SCROLL);
1273 gamesnd_play_iface(SND_GENERAL_FAIL);
1276 void control_config_toggle_modifier(int bit)
1280 z = Cc_lines[Selected_line].cc_index;
1281 Assert(!(z & JOY_AXIS));
1282 k = Control_config[z].key_id;
1284 gamesnd_play_iface(SND_GENERAL_FAIL);
1288 control_config_bind_key(z, k ^ bit);
1289 control_config_conflict_check();
1290 gamesnd_play_iface(SND_USER_SELECT);
1293 void control_config_toggle_invert()
1297 z = Cc_lines[Selected_line].cc_index;
1298 Assert(z & JOY_AXIS);
1300 control_config_save_axis_undo(z);
1301 Invert_axis[z] = !Invert_axis[z];
1304 void control_config_do_bind()
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);
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();
1321 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1322 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1324 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1325 joy_down_count(i); // clear checking status of all joystick buttons
1328 control_config_detect_axis_reset();
1331 Bind_time = timer_get_milliseconds();
1335 gamesnd_play_iface(SND_USER_SELECT);
1338 void control_config_do_search()
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();
1349 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1350 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1352 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1353 joy_down_count(i); // clear checking status of all joystick buttons
1359 gamesnd_play_iface(SND_USER_SELECT);
1362 void control_config_do_cancel(int fail = 0)
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();
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();
1380 Binding_mode = Search_mode = 0;
1382 gamesnd_play_iface(SND_GENERAL_FAIL);
1384 gamesnd_play_iface(SND_USER_SELECT);
1388 int control_config_accept()
1392 for (i=0; i<NUM_TABS; i++)
1393 if (Conflicts_tabs[i])
1397 gamesnd_play_iface(SND_GENERAL_FAIL);
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);
1407 void control_config_cancel_exit()
1411 for (i=0; i<CCFG_MAX; i++)
1412 Control_config[i] = Control_config_backup[i];
1414 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1417 void control_config_button_pressed(int n)
1425 Scroll_offset = Selected_line = 0;
1426 control_config_list_prepare();
1427 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1431 control_config_do_bind();
1435 control_config_do_search();
1439 control_config_toggle_modifier(KEY_SHIFTED);
1440 gamesnd_play_iface(SND_USER_SELECT);
1444 control_config_toggle_modifier(KEY_ALTED);
1445 gamesnd_play_iface(SND_USER_SELECT);
1449 control_config_toggle_invert();
1450 gamesnd_play_iface(SND_USER_SELECT);
1453 case SCROLL_UP_BUTTON:
1454 control_config_scroll_screen_up();
1457 case SCROLL_DOWN_BUTTON:
1458 control_config_scroll_screen_down();
1462 control_config_accept();
1466 control_config_remove_binding();
1470 launch_context_help();
1471 gamesnd_play_iface(SND_HELP_PRESSED);
1475 control_config_do_reset();
1479 control_config_undo_last();
1483 control_config_do_cancel();
1486 case CLEAR_OTHER_BUTTON:
1487 control_config_clear_other();
1490 case CLEAR_ALL_BUTTON:
1491 control_config_clear_all();
1496 char *control_config_tooltip_handler(char *str)
1500 if (!stricmp(str, NOX("@conflict"))) {
1501 for (i=0; i<NUM_TABS; i++) {
1502 if (Conflicts_tabs[i])
1503 return XSTR( "Conflict!", 205);
1510 void control_config_init()
1515 // make backup of all controls
1516 for (i=0; i<CCFG_MAX; i++)
1517 Control_config_backup[i] = Control_config[i];
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;
1524 // load in help overlay bitmap
1525 help_overlay_load(CONTROL_CONFIG_OVERLAY);
1526 help_overlay_set_state(CONTROL_CONFIG_OVERLAY,0);
1528 // reset conflict flashing
1529 Conflict_stamp = -1;
1531 for (i=0; i<NUM_BUTTONS; i++) {
1532 b = &CC_Buttons[gr_screen.res][i];
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);
1540 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1542 // set up callback for when a mouse first goes over a button
1543 b->button.set_highlight_action(common_play_highlight_sound);
1545 b->button.set_bmaps(b->filename, 5, 1); // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1547 b->button.set_bmaps(b->filename);
1549 b->button.link_hotspot(b->hotspot);
1553 for(i=0; i<CC_NUM_TEXT; i++){
1554 Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
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();
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);
1575 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1576 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
1578 Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);
1580 Scroll_offset = Selected_line = 0;
1581 Config_item_undo = NULL;
1582 control_config_conflict_check();
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));
1606 control_config_list_prepare();
1609 void control_config_close()
1613 while (Config_item_undo){
1617 // unload the overlay bitmap
1618 help_overlay_unload(CONTROL_CONFIG_OVERLAY);
1620 if (Background_bitmap){
1621 bm_unload(Background_bitmap);
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
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;
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;
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;
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;
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;
1663 void control_config_do_frame(float frametime)
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;
1671 static int bound_timestamp = 0;
1672 static char bound_string[40];
1677 if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1680 z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1681 i = control_config_detect_axis();
1688 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1689 Ui_window.process(0);
1692 strcpy(bound_string, XSTR( "Canceled", 206));
1693 bound_timestamp = timestamp(2500);
1694 control_config_do_cancel();
1700 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1701 if (joy_down_count(i))
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();
1715 control_config_do_cancel(1);
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);
1727 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1728 Ui_window.process(0);
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);
1738 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1739 Ui_window.set_ignore_gadgets(0);
1743 strcpy(bound_string, XSTR( "Canceled", 206));
1744 bound_timestamp = timestamp(2500);
1745 control_config_do_cancel();
1748 switch (k & KEY_MASK) {
1753 Last_key = k & KEY_MASK;
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] )
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));
1767 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1769 z = Cc_lines[Selected_line].cc_index;
1770 Assert(!(z & JOY_AXIS));
1771 control_config_bind_key(z, k);
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();
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);
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();
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()) ){
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);
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();
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);
1834 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1835 Ui_window.process(0);
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);
1845 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1846 Ui_window.set_ignore_gadgets(0);
1850 control_config_do_cancel();
1853 if ((k > 0) && !Config_allowed[k & KEY_MASK])
1856 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1859 for (i=0; i<CCFG_MAX; i++)
1860 if (Control_config[i].key_id == k) {
1866 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1867 if (joy_down_count(i)) {
1869 for (i=0; i<CCFG_MAX; i++)
1870 if (Control_config[i].joy_id == j) {
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()) ){
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) {
1891 for (j=0; j<NUM_BUTTONS; j++){
1892 CC_Buttons[gr_screen.res][j].button.reset();
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) {
1911 while (!cc_line_query_visible(Selected_line)) {
1913 Assert(Scroll_offset < Num_cc_lines);
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);
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);
1933 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
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);
1940 k = Ui_window.process();
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);
1950 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1951 Ui_window.set_ignore_gadgets(0);
1955 case KEY_DOWN: // select next line
1956 control_config_scroll_line_down();
1959 case KEY_UP: // select previous line
1960 control_config_scroll_line_up();
1963 case KEY_SHIFTED | KEY_TAB: // activate previous tab
1968 Scroll_offset = Selected_line = 0;
1969 control_config_list_prepare();
1970 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1973 case KEY_TAB: // activate next tab
1975 if (Tab >= NUM_TABS)
1978 Scroll_offset = Selected_line = 0;
1979 control_config_list_prepare();
1980 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1985 if (Selected_item == -2) {
1987 if (Cc_lines[Selected_line].jw < 1) {
1989 if (Cc_lines[Selected_line].kw < 1)
1994 gamesnd_play_iface(SND_SCROLL);
1999 if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1))
2001 else if (!Selected_item && (Cc_lines[Selected_line].kw < 1))
2003 else if (Selected_item > 1)
2006 gamesnd_play_iface(SND_SCROLL);
2009 case KEY_BACKSP: // undo
2010 control_config_undo_last();
2014 control_config_cancel_exit();
2019 for (i=0; i<NUM_BUTTONS; i++){
2020 if (CC_Buttons[gr_screen.res][i].button.pressed()){
2021 control_config_button_pressed(i);
2025 for (i=0; i<LIST_BUTTONS_MAX; i++) {
2026 if (List_buttons[i].is_mouse_on())
2027 select_tease_line = i + Scroll_offset;
2029 if (List_buttons[i].pressed()) {
2030 Selected_line = i + Scroll_offset;
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))
2036 if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw))
2039 gamesnd_play_iface(SND_USER_SELECT);
2042 if (List_buttons[i].double_clicked())
2043 control_config_do_bind();
2046 GR_MAYBE_CLEAR_RES(Background_bitmap);
2047 if (Background_bitmap >= 0) {
2048 gr_set_bitmap(Background_bitmap);
2052 // highlight tab with conflict
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);
2062 // maybe switch from bright to normal
2063 if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
2064 Conflict_bright = !Conflict_bright;
2066 Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
2069 // set color and font
2071 if(Conflict_bright){
2072 gr_set_color_fast(&Color_bright_red);
2074 gr_set_color_fast(&Color_red);
2077 // setup the conflict string
2078 char conflict_str[512] = "";
2079 strncpy(conflict_str, XSTR("Conflict!", 205), 511);
2081 gr_get_string_size(&sw, &sh, conflict_str);
2083 gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str);
2087 // might as well always reset the conflict stamp
2088 Conflict_stamp = -1;
2091 for (i=0; i<NUM_TABS; i++) {
2092 if (CC_Buttons[gr_screen.res][i].button.button_down()) {
2097 if (i == NUM_TABS) {
2098 CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
2102 CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
2105 if (Selected_line >= 0) {
2106 z = Cc_lines[Selected_line].cc_index;
2108 if (Invert_axis[z & ~JOY_AXIS]) {
2109 CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
2113 z = Control_config[z].key_id;
2115 if (z & KEY_SHIFTED) {
2116 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
2118 if (z & KEY_ALTED) {
2119 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
2126 CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
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;
2135 t = (int) (timer * 3);
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));
2142 } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
2143 i = Conflicts[z].key;
2145 i = Conflicts[z].joy;
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);
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);
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))
2165 // gr_set_color_fast(&Color_text_heading);
2166 // gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]);
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);
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));
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;
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);
2187 Cc_lines[line].kw = Cc_lines[line].jw = 0;
2189 if (line == Selected_line){
2190 c = &Color_text_selected;
2191 } else if (line == select_tease_line) {
2192 c = &Color_text_subselected;
2194 c = &Color_text_normal;
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);
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];
2211 if ((k < 0) && (j < 0)) {
2212 gr_set_color_fast(&Color_grey);
2213 gr_printf(x, y, XSTR( "None", 211));
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);
2222 gr_set_color_fast(&Color_text_error_hi);
2226 } else if (Selected_item == 1) {
2227 gr_set_color_fast(&Color_text_normal);
2230 gr_set_color_fast(c);
2232 gr_printf(x, y, 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;
2241 gr_set_color_fast(&Color_text_normal);
2242 gr_printf(x, y, XSTR( ", ", 212));
2243 gr_get_string_size(&w, NULL, XSTR( ", ", 212));
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);
2254 gr_set_color_fast(&Color_text_error_hi);
2258 } else if (!Selected_item) {
2259 gr_set_color_fast(&Color_text_normal);
2262 gr_set_color_fast(c);
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);
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);
2273 x = Control_list_key_x[gr_screen.res];
2274 j = Axis_map_to[z & ~JOY_AXIS];
2275 if (Binding_mode && (line == Selected_line))
2279 gr_set_color_fast(&Color_grey);
2280 gr_printf(x, y, XSTR( "None", 211));
2283 if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
2284 if (c == &Color_text_normal)
2285 gr_set_color_fast(&Color_text_error);
2288 gr_set_color_fast(&Color_text_error_hi);
2292 } else if (!Selected_item) {
2293 gr_set_color_fast(&Color_text_normal);
2296 gr_set_color_fast(c);
2298 gr_string(x, y, Joy_axis_text[j]);
2305 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
2307 i = line - Scroll_offset;
2308 while (i < LIST_BUTTONS_MAX)
2309 List_buttons[i++].disable();
2311 // blit help overlay if active
2312 help_overlay_maybe_blit(CONTROL_CONFIG_OVERLAY);
2317 void clear_key_binding(short key)
2321 for (i=0; i<CCFG_MAX; i++) {
2322 if (Control_config[i].key_id == key)
2323 Control_config[i].key_id = -1;
2327 float check_control_timef(int id)
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);
2334 // first, see if control actually used (makes sure modifiers match as well)
2335 if (!check_control(id))
2338 t1 = key_down_timef(Control_config[id].key_id);
2342 t2 = joy_down_time(Control_config[id].joy_id);
2352 void control_check_indicate()
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);
2361 Control_check_count = 0;
2364 int check_control(int id, int key)
2367 static int last_key = 0;
2369 Control_check_count++;
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()){
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)) {
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)) {
2392 // check what current modifiers are pressed
2394 if (keyd_pressed[KEY_LSHIFT] || key_down_count(KEY_LSHIFT) || keyd_pressed[KEY_RSHIFT] || key_down_count(KEY_RSHIFT))
2395 mask |= KEY_SHIFTED;
2397 if (keyd_pressed[KEY_LALT] || key_down_count(KEY_LALT) || keyd_pressed[KEY_RALT] || key_down_count(KEY_RALT))
2400 z = Control_config[id].key_id;
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)
2410 if (keyd_pressed[z] || key_down_count(z)) {
2411 if ( !hud_squadmsg_read_key(z) ) {
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)) {
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)
2432 int axes_values[JOY_NUM_AXES];
2434 joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
2436 // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
2438 if (Axis_map_to[0] >= 0)
2439 *h = joy_get_scaled_reading(axes_values[Axis_map_to[0]], Axis_map_to[0]);
2442 if (Axis_map_to[1] >= 0)
2443 *p = joy_get_scaled_reading(axes_values[Axis_map_to[1]], Axis_map_to[1]);
2446 if (Axis_map_to[2] >= 0)
2447 *b = joy_get_scaled_reading(axes_values[Axis_map_to[2]], Axis_map_to[2]);
2450 if (Axis_map_to[3] >= 0)
2451 *ta = joy_get_unscaled_reading(axes_values[Axis_map_to[3]], Axis_map_to[3]);
2454 if (Axis_map_to[4] >= 0)
2455 *tr = joy_get_scaled_reading(axes_values[Axis_map_to[4]], Axis_map_to[4]);
2471 void control_used(int id)
2473 Control_config[id].used = timestamp();
2476 void control_config_clear_used_status()
2480 for (i=0; i<CCFG_MAX; i++)
2481 Control_config[i].used = 0;
2484 void control_config_clear()
2488 // Reset keyboard defaults
2489 for (i=0; i<CCFG_MAX; i++)
2490 Control_config[i].key_id = Control_config[i].joy_id = -1;
2493 int control_config_handle_conflict()
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))
2500 k = Conflicts[z].key;
2501 if ((k >= 0) && (Control_config[k].key_id < 0))
2504 if ((j >= 0) && (k >= 0) && (j != k)) { // deleting 2 conflicts, each in different actions
2505 ptr = get_undo_block(2);
2507 ptr->list[0] = Control_config[j];
2508 Control_config[j].joy_id = (short) -1;
2511 ptr->list[1] = Control_config[k];
2512 Control_config[k].key_id = (short) -1;
2514 } else { // only 1 action in conflict with selected action (might be both controls, though)
2520 ptr = get_undo_block(1);
2522 ptr->list[0] = Control_config[z];
2525 Control_config[z].joy_id = (short) -1;
2528 Control_config[z].key_id = (short) -1;