2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/ControlConfig/ControlsConfig.cpp $
15 * C module for keyboard, joystick and mouse configuration
18 * Revision 1.6 2004/09/20 01:31:44 theoddone33
21 * Revision 1.5 2004/07/04 11:31:43 taylor
22 * amd64 support, compiler warning fixes, don't use software rendering
24 * Revision 1.4 2003/05/25 02:30:42 taylor
27 * Revision 1.3 2002/06/09 04:41:15 relnev
28 * added copyright header
30 * Revision 1.2 2002/05/07 03:16:43 theoddone33
31 * The Great Newline Fix
33 * Revision 1.1.1.1 2002/05/03 03:28:08 root
37 * 13 10/14/99 2:50p Jefff
40 * 12 8/16/99 9:50a Jefff
41 * fixed loading of tab bitmaps
43 * 11 8/11/99 3:21p Jefff
44 * added tab highlights on conflict
46 * 10 7/26/99 5:25p Jefff
47 * removed invalidation of key binding for demo builds
49 * 9 7/19/99 2:13p Dave
50 * Added some new strings for Heiko.
52 * 8 7/15/99 9:20a Andsager
53 * FS2_DEMO initial checkin
55 * 7 6/19/99 2:46p Dave
56 * New control config screen.
58 * 6 1/30/99 5:08p Dave
59 * More new hi-res stuff.Support for nice D3D textures.
61 * 5 1/15/99 11:29a Neilk
62 * Fixed D3D screen/texture pixel formatting problem.
64 * 4 11/05/98 4:18p Dave
65 * First run nebula support. Beefed up localization a bit. Removed all
66 * conditional compiles for foreign versions. Modified mission file
69 * 3 10/13/98 9:28a Dave
70 * Started neatening up freespace.h. Many variables renamed and
71 * reorganized. Added AlphaColors.[h,cpp]
73 * 2 10/07/98 10:52a Dave
76 * 1 10/07/98 10:48a Dave
78 * 61 8/09/98 11:55a Lawrance
79 * if GRAVIS_OEM is defined, map the throttle axis by default
81 * 60 6/19/98 3:51p Lawrance
82 * localize control text
84 * 59 6/17/98 11:04a Lawrance
85 * localize the control config strings
87 * 58 6/13/98 5:19p Hoffoss
88 * externalized control config texts.
90 * 57 6/09/98 5:15p Lawrance
91 * French/German localization
93 * 56 6/09/98 10:31a Hoffoss
94 * Created index numbers for all xstr() references. Any new xstr() stuff
95 * added from here on out should be added to the end if the list. The
96 * current list count can be found in FreeSpace.cpp (search for
99 * 55 6/01/98 11:43a John
100 * JAS & MK: Classified all strings for localization.
102 * 54 5/26/98 11:10a Lawrance
103 * Fix bug where window controls get disabled when F1 pressed twice
105 * 53 5/20/98 10:35p Hoffoss
106 * Fixed bug with mouse buttons not working when action isn't continuous.
108 * 52 5/19/98 4:08p Allender
109 * kill default binding for Z axis
111 * 51 5/19/98 12:56p Hoffoss
112 * Added some code to help prevent triple-clicking for binding the mouse
113 * button to an action (why the hell people triple click is beyond me,
116 * 50 5/19/98 11:11a Lawrance
117 * Ensure X and Y axis have defaults!
119 * 49 5/18/98 4:53p Hoffoss
120 * Some force feedback tweaks and pilot initializations there should have
121 * been happening, but weren't, and not are!
123 * 48 5/18/98 10:15a Lawrance
124 * Only do hud squad msg key check when necessary
126 * 47 5/18/98 10:08a Lawrance
127 * deal with overlap between hud squad msg number keys and CC_CONTINUOUS
130 * 46 5/17/98 5:44p Hoffoss
131 * Made throttle never bound by default (ask Sandeep why if interested).
133 * 45 5/14/98 5:32p Hoffoss
134 * Improved axis binding code some more.
136 * 44 5/13/98 7:15p Hoffoss
137 * Fixed remaining bugs with axis binding.
139 * 43 5/13/98 1:17a Hoffoss
140 * Added joystick axes configurability.
142 * 42 5/12/98 3:49p Hoffoss
143 * Fixed bug where double mouse click would bind mouse button right away.
145 * 41 5/11/98 5:43p Hoffoss
146 * Made num lock not bindable.
148 * 40 5/11/98 5:29p Hoffoss
149 * Added mouse button mapped to joystick button support.
151 * 39 5/07/98 6:25p Dave
152 * Fix strange boundary conditions which arise when players die/respawn
153 * while the game is being ended. Spiff up the chatbox doskey thing a bit.
155 * 38 5/05/98 1:48a Lawrance
156 * Add in missing help overlays
158 * 37 4/27/98 10:11a Lawrance
159 * Add in disabled beep for missing buttons
161 * 36 4/25/98 2:59p Hoffoss
162 * Fixed typo that was causing a bug.
164 * 35 4/22/98 1:51a Lawrance
165 * Take out multiplayer key from demo key config
167 * 34 4/16/98 4:29p Hoffoss
168 * Fixed bank_when_pressed functionality when using alt or shift for it.
170 * 33 4/15/98 11:06a Lawrance
171 * fix bug with a multi key showing up in demo, remove obsolete bindings
172 * from demo and full version
174 * 32 4/14/98 2:45p Hoffoss
175 * Made hitting escape to exit screen not play failed sound.
177 * 31 4/14/98 2:27p Hoffoss
178 * Made certain actions be hidden in demo build.
180 * 30 4/13/98 2:38p Hoffoss
181 * Added a tooltip handler and make binding attempts with illegal keys
184 * 29 4/11/98 7:59p Lawrance
185 * Add support for help overlays
187 * 28 4/09/98 4:12p Hoffoss
188 * Changed check_control() to automatically register a control as used if
189 * it detects it being used.
191 * 27 4/08/98 11:11a Hoffoss
192 * Fixed some bugs that showed up due to fixing other bugs the other day
195 * 26 4/07/98 3:47p Hoffoss
196 * Fixed continuous controls checking with respect to modifiers.
198 * 25 4/06/98 11:17a Hoffoss
199 * Fixed num lock/pause interplay bug.
201 * 24 4/03/98 3:51p Hoffoss
202 * Fixed some bugs, and made changed Interplay requested regarding search
205 * 23 3/31/98 4:12p Hoffoss
206 * Made control used status clear at mission init time.
208 * 22 3/23/98 11:28a Hoffoss
209 * Fixed flashing question mark bug.
211 * 21 3/21/98 11:30a John
212 * Fixed bug where joymouse caused you to stay in binding mode when
213 * binding joystick button 1 to something.
215 * 20 3/20/98 3:37p Hoffoss
216 * Tried to fix mitri's bug, failed miserably.
218 * 19 3/19/98 5:04p Dave
219 * Put in support for targeted multiplayer text and voice messaging (all,
220 * friendly, hostile, individual).
222 * 18 3/18/98 12:03p John
223 * Marked all the new strings as externalized or not.
225 * 17 3/18/98 10:16a Hoffoss
228 * 16 3/17/98 11:15a Hoffoss
229 * Made question mark that appears when you are in bind mode flash.
231 * 15 3/17/98 10:48a Hoffoss
232 * Allowed a special hack for "bank while pressed" action to use alt and
233 * shift keys standalone.
235 * 14 3/12/98 3:22p Hoffoss
236 * Fixed 2 bugs with one solution! Yay! Failed sound on bind fixed and
237 * pad enter now not translated to enter.
239 * 13 3/11/98 5:28p Hoffoss
240 * Added control config debug display info to possibly aid in tracking
243 * 12 2/26/98 10:07p Hoffoss
244 * Rewrote state saving and restoring to fix bugs and simplify the code.
246 * 11 2/22/98 12:19p John
247 * Externalized some strings
249 * 10 2/20/98 3:39p Hoffoss
250 * Updated code for new control config screen artwork.
252 * 9 2/09/98 2:50p Hoffoss
253 * Made 'none' show up as gray instead of normal color, to distinguish it
254 * from actions with bound keys.
256 * 8 2/07/98 10:04p Hoffoss
257 * Changed color and placement of "more" indicator.
259 * 7 2/05/98 10:42a Hoffoss
260 * Fixed bug where while in bind mode, you could change the line selected
261 * using the mouse, and binding would work on the new line instead.
263 * 6 2/03/98 5:05p Hoffoss
264 * Added "clear other" button to clear all conflicting bindings with
267 * 5 1/22/98 4:53p Hoffoss
268 * Made training messages/directives display a joystick button in place of
269 * a keypress if there is no keypress bound to the action.
271 * 4 1/20/98 4:20p Hoffoss
272 * Removed confusing behavior of clear button clearing out the other
273 * binding in a conflict.
275 * 3 1/08/98 12:11p Hoffoss
276 * Changed Rudder axis to Roll axis, added new function we can use to
277 * check what joystick axes are valid with.
279 * 2 12/24/97 3:37p Hoffoss
280 * Moved control config stuff to seperate library to Fred can access it as
283 * 1 12/24/97 11:58a Hoffoss
285 * 98 12/22/97 2:15p Hoffoss
286 * Fixed bug where joystick axis lines weren't being displayed.
288 * 97 12/16/97 2:44p Hoffoss
289 * Added clear button to control config screen.
291 * 96 12/12/97 3:07p Hoffoss
292 * Changed how deleting bindings work. Each control of an action can be
293 * deleted independently or both at once.
295 * 95 12/07/97 2:36p John
296 * Made warp out be Alt+J instead of J
298 * 94 12/03/97 4:59p Hoffoss
299 * Added reset sound and change control config sounds around.
301 * 93 12/03/97 4:16p Hoffoss
302 * Changed sound stuff used in interface screens for interface purposes.
308 #include "freespace.h"
309 #include "controlsconfig.h"
310 #include "gamesequence.h"
313 #include "hudsquadmsg.h"
323 #include "missionscreencommon.h"
326 #include "managepilot.h"
327 #include "multi_pmsg.h"
328 #include "contexthelp.h"
331 #include "multiutil.h"
332 #include "alphacolors.h"
334 #define NUM_SYSTEM_KEYS 14
335 #define NUM_BUTTONS 19
338 // coordinate indicies
339 #define CONTROL_X_COORD 0
340 #define CONTROL_Y_COORD 1
341 #define CONTROL_W_COORD 2
342 #define CONTROL_H_COORD 3
344 char* Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS] = {
345 "ControlConfig", // GR_640
346 "2_ControlConfig" // GR_1024
349 char* Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
350 "ControlConfig-m", // GR_640
351 "2_ControlConfig-m" // GR_1024
355 int Control_list_coords[GR_NUM_RESOLUTIONS][4] = {
360 32, 58, 198, 259 // GR_640
364 32, 94, 904, 424 // GR_1024
368 // width of the control name section of the list
369 int Control_list_ctrl_w[GR_NUM_RESOLUTIONS] = {
374 // x start position of the binding area section of the list
375 int Control_list_key_x[GR_NUM_RESOLUTIONS] = {
380 // width of the binding area section of the list
381 int Control_list_key_w[GR_NUM_RESOLUTIONS] = {
386 // display the "more..." text under the control list
387 int Control_more_coords[GR_NUM_RESOLUTIONS][2] = {
400 // area to display "conflicts with..." text
401 int Conflict_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
406 32, 313, 250, 32 // GR_640
410 48, 508, 354, 46 // GR_1024
414 // conflict warning anim coords
415 int Conflict_warning_coords[GR_NUM_RESOLUTIONS][2] = {
428 // for flashing the conflict text
429 #define CONFLICT_FLASH_TIME 250
430 int Conflict_stamp = -1;
431 int Conflict_bright = 0;
433 #define LIST_BUTTONS_MAX 40
434 #define JOY_AXIS 0x80000
436 static int Num_cc_lines;
439 int cc_index; // index into Control_config of item
440 int y; // Y coordinate of line
441 int kx, kw, jx, jw; // x start and width of keyboard and joystick bound text
442 } Cc_lines[CCFG_MAX];
444 // struct to hold backup config_item elements so we can undo them
445 struct config_item_undo {
447 int *index; // array (size) of Control_config indices of replaced elements
448 config_item *list; // array (size) of original elements
449 config_item_undo *next;
452 config_item Control_config_backup[CCFG_MAX];
455 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
456 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
458 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
459 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
462 // all this stuff is localized/externalized
463 #define NUM_AXIS_TEXT 6
464 #define NUM_MOUSE_TEXT 5
465 #define NUM_MOUSE_AXIS_TEXT 2
466 #define NUM_INVERT_TEXT 2
467 char *Joy_axis_action_text[NUM_JOY_AXIS_ACTIONS];
468 char *Joy_axis_text[NUM_AXIS_TEXT];
469 char *Mouse_button_text[NUM_MOUSE_TEXT];
470 char *Mouse_axis_text[NUM_MOUSE_AXIS_TEXT];
471 char *Invert_text[NUM_INVERT_TEXT];
473 ubyte System_keys[NUM_SYSTEM_KEYS] = {
474 KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,
475 KEY_F11, KEY_F12, KEY_PRINT_SCRN
478 int Control_check_count = 0;
480 static int Tab; // which tab we are currently in
481 static int Binding_mode = 0; // are we waiting for a key to bind it?
482 static int Bind_time = 0;
483 static int Search_mode = 0; // are we waiting for a key to bind it?
484 static int Last_key = -1;
485 static int Selected_line = 0; // line that is currently selected for binding
486 static int Selected_item = -1; // -1 = none, 0 = key, 1 = button
487 static int Scroll_offset;
488 static int Axis_override = -1;
489 static int Background_bitmap;
490 static int Conflicts_tabs[NUM_TABS];
491 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list
492 static UI_WINDOW Ui_window;
495 int key; // index of other control in conflict with this one
496 int joy; // index of other control in conflict with this one
497 } Conflicts[CCFG_MAX];
499 int Conflicts_axes[NUM_JOY_AXIS_ACTIONS];
502 static hud_anim Conflict_warning_anim;
508 #define COMPUTER_TAB 3
509 #define SCROLL_UP_BUTTON 4
510 #define SCROLL_DOWN_BUTTON 5
512 #define SHIFT_TOGGLE 7
513 #define INVERT_AXIS 8
514 #define CANCEL_BUTTON 9
515 #define UNDO_BUTTON 10
516 #define RESET_BUTTON 11
517 #define SEARCH_MODE 12
518 #define BIND_BUTTON 13
519 #define HELP_BUTTON 14
520 #define ACCEPT_BUTTON 15
521 #define CLEAR_OTHER_BUTTON 16
522 #define CLEAR_ALL_BUTTON 17
523 #define CLEAR_BUTTON 18
525 ui_button_info CC_Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
528 ui_button_info("CCB_00", 35, 30, -1, -1, 0), // target tab
529 ui_button_info("CCB_01", 121, 30, -1, -1, 1), // ship tab
530 ui_button_info("CCB_02", 176, 30, -1, -1, 2), // weapon tab
531 ui_button_info("CCB_03", 254, 30, -1, -1, 3), // computer/misc tab
532 ui_button_info("CCB_04", 598, 168, -1, -1, 4), // scroll up
533 ui_button_info("CCB_05", 598, 219, -1, -1, 5), // scroll down
534 ui_button_info("CCB_06", 13, 394, -1, -1, 6), // alt toggle
535 ui_button_info("CCB_07", 58, 394, -1, -1, 7), // shift toggle
536 ui_button_info("CCB_09", 168, 394, -1, -1, 9), // invert
537 ui_button_info("CCB_10", 456, 341, -1, -1, 10), // cancel
538 ui_button_info("CCB_11", 394, 404, -1, -1, 11), // undo
539 ui_button_info("CCB_12", 501, 26, -1, -1, 12), // default
540 ui_button_info("CCB_13", 513, 331, -1, -1, 13), // search
541 ui_button_info("CCB_14", 572, 331, -1, -1, 14), // bind
542 ui_button_info("CCB_15", 469, 429, -1, -1, 15), // help
543 ui_button_info("CCB_16", 562, 411, -1, -1, 16), // accept
544 ui_button_info("CCB_18", 223, 404, -1, -1, 18), // clear other
545 ui_button_info("CCB_19", 289, 404, -1, -1, 19), // clear all
546 ui_button_info("CCB_20", 333, 404, -1, -1, 20), // clear button
548 ui_button_info("CCB_00", 32, 348, 17, 384, 0), // target tab
549 ui_button_info("CCB_01", 101, 348, 103, 384, 1), // ship tab
550 ui_button_info("CCB_02", 173, 352, 154, 384, 2), // weapon tab
551 ui_button_info("CCB_03", 242, 347, 244, 384, 3), // computer/misc tab
552 ui_button_info("CCB_04", 614, 73, -1, -1, 4), // scroll up
553 ui_button_info("CCB_05", 614, 296, -1, -1, 5), // scroll down
554 ui_button_info("CCB_06", 17, 452, 12, 440, 6), // alt toggle
555 ui_button_info("CCB_07", 56, 452, 50, 440, 7), // shift toggle
556 ui_button_info("CCB_09", 162, 452, 155, 440, 9), // invert
557 ui_button_info("CCB_10", 404, 1, 397, 45, 10), // cancel
558 ui_button_info("CCB_11", 582, 347, 586, 386, 11), // undo
559 ui_button_info("CCB_12", 576, 1, 578, 45, 12), // default
560 ui_button_info("CCB_13", 457, 4, 453, 45, 13), // search
561 ui_button_info("CCB_14", 516, 4, 519, 45, 14), // bind
562 ui_button_info("CCB_15", 540, 428, 500, 440, 15), // help
563 ui_button_info("CCB_16", 574, 432, 571, 412, 16), // accept
564 ui_button_info("CCB_18", 420, 346, 417, 386, 18), // clear other
565 ui_button_info("CCB_19", 476, 346, 474, 386, 19), // clear all
566 ui_button_info("CCB_20", 524, 346, 529, 386, 20), // clear button
570 ui_button_info("2_CCB_00", 51, 557, 27, 615, 0), // target tab
571 ui_button_info("2_CCB_01", 162, 557, 166, 615, 1), // ship tab
572 ui_button_info("2_CCB_02", 277, 563, 246, 615, 2), // weapon tab
573 ui_button_info("2_CCB_03", 388, 555, 391, 615, 3), // computer/misc tab
574 ui_button_info("2_CCB_04", 982, 117, -1, -1, 4), // scroll up
575 ui_button_info("2_CCB_05", 982, 474, -1, -1, 5), // scroll down
576 ui_button_info("2_CCB_06", 28, 723, 24, 704, 6), // alt toggle
577 ui_button_info("2_CCB_07", 89, 723, 80, 704, 7), // shift toggle
578 ui_button_info("2_CCB_09", 260, 723, 249, 704, 9), // invert
579 ui_button_info("2_CCB_10", 646, 2, 635, 71, 10), // cancel
580 ui_button_info("2_CCB_11", 932, 555, 938, 619, 11), // undo
581 ui_button_info("2_CCB_12", 921, 1, 923, 71, 12), // default
582 ui_button_info("2_CCB_13", 732, 6, 726, 71, 13), // search
583 ui_button_info("2_CCB_14", 825, 6, 831, 71, 14), // bind
584 ui_button_info("2_CCB_15", 864, 685, 800, 704, 15), // help
585 ui_button_info("2_CCB_16", 919, 692, 914, 660, 16), // accept
586 ui_button_info("2_CCB_18", 672, 553, 668, 619, 18), // clear other
587 ui_button_info("2_CCB_19", 761, 553, 749, 619, 19), // clear all
588 ui_button_info("2_CCB_20", 838, 553, 846, 619, 20), // clear button
594 #define CC_NUM_TEXT 0
596 #define CC_NUM_TEXT 20
598 UI_XSTR CC_text[GR_NUM_RESOLUTIONS][CC_NUM_TEXT] = {
600 // nothing needed for FS1
602 { "Targeting", 1340, 17, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][TARGET_TAB].button },
603 { "Ship", 1341, 103, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIP_TAB].button },
604 { "Weapons", 1065, 154, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][WEAPON_TAB].button },
605 { "Misc", 1411, 244, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][COMPUTER_TAB].button },
606 { "Alt", 1510, 12, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][ALT_TOGGLE].button },
607 { "Shift", 1511, 50, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIFT_TOGGLE].button },
608 { "Invert", 1342, 155, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][INVERT_AXIS].button },
609 { "Cancel", 641, 397, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CANCEL_BUTTON].button },
610 { "Undo", 1343, 586, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][UNDO_BUTTON].button },
611 { "Defaults", 1344, 568, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][RESET_BUTTON].button },
612 { "Search", 1345, 453, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SEARCH_MODE].button },
613 { "Bind", 1346, 519, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][BIND_BUTTON].button },
614 { "Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][HELP_BUTTON].button },
615 { "Accept", 1035, 571, 412, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][ACCEPT_BUTTON].button },
616 { "Clear", 1347, 417, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
617 { "Conflict", 1348, 406, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
618 { "Clear", 1413, 474, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
619 { "All", 1349, 483, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
620 { "Clear", 1414, 529, 388, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
621 { "Selected", 1350, 517, 396, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
625 // nothing needed for FS1
627 { "Targeting", 1340, 47, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][TARGET_TAB].button },
628 { "Ship", 1341, 176, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIP_TAB].button },
629 { "Weapons", 1065, 266, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][WEAPON_TAB].button },
630 { "Misc", 1411, 401, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][COMPUTER_TAB].button },
631 { "Alt", 1510, 29, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][ALT_TOGGLE].button },
632 { "Shift", 1511, 85, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIFT_TOGGLE].button },
633 { "Invert", 1342, 254, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][INVERT_AXIS].button },
634 { "Cancel", 641, 655, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CANCEL_BUTTON].button },
635 { "Undo", 1343, 938, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][UNDO_BUTTON].button },
636 { "Defaults", 1344, 923, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][RESET_BUTTON].button },
637 { "Search", 1345, 746, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SEARCH_MODE].button },
638 { "Bind", 1346, 846, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][BIND_BUTTON].button },
639 { "Help", 928, 800, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][HELP_BUTTON].button },
640 { "Accept", 1035, 914, 660, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][ACCEPT_BUTTON].button },
641 { "Clear", 1347, 683, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
642 { "Conflict", 1348, 666, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
643 { "Clear", 1413, 759, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
644 { "All", 1349, 772, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
645 { "Clear", 1414, 871, 619, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
646 { "Selected", 1350, 852, 634, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
651 // linked list head of undo items
652 config_item_undo *Config_item_undo;
654 // same indices as Scan_code_text[]. Indicates if a scancode is allowed to be bound.
655 int Config_allowed[] = {
656 0, 0, 1, 1, 1, 1, 1, 1,
657 1, 1, 1, 1, 1, 1, 1, 1,
658 1, 1, 1, 1, 1, 1, 1, 1,
659 1, 1, 1, 1, 1, 1, 1, 1,
661 1, 1, 1, 1, 1, 1, 1, 1,
662 1, 0, 1, 1, 1, 1, 1, 1,
663 1, 1, 1, 1, 1, 1, 1, 1,
664 1, 1, 1, 0, 0, 0, 0, 0,
666 0, 0, 0, 0, 0, 0, 1, 1,
667 1, 1, 1, 1, 1, 1, 1, 1,
668 1, 1, 1, 1, 0, 0, 0, 0,
669 0, 0, 0, 0, 0, 0, 0, 0,
671 0, 0, 0, 0, 0, 0, 0, 0,
672 0, 0, 0, 0, 0, 0, 0, 0,
673 0, 0, 0, 0, 0, 0, 0, 0,
674 0, 0, 0, 0, 0, 0, 0, 0,
676 0, 0, 0, 0, 0, 0, 0, 0,
677 0, 0, 0, 0, 0, 0, 0, 0,
678 0, 0, 0, 0, 0, 0, 0, 0,
679 0, 0, 0, 0, 1, 1, 0, 0,
681 0, 0, 0, 0, 0, 0, 0, 0,
682 0, 0, 0, 0, 0, 0, 0, 0,
683 0, 0, 0, 0, 0, 1, 0, 0,
684 1, 0, 0, 0, 0, 0, 0, 0,
686 0, 0, 0, 0, 0, 0, 0, 1,
687 1, 1, 0, 1, 0, 1, 0, 1,
688 1, 1, 1, 1, 0, 0, 0, 0,
689 0, 0, 0, 0, 0, 0, 0, 0,
691 0, 0, 0, 0, 0, 0, 0, 0,
692 0, 0, 0, 0, 0, 0, 0, 0,
693 0, 0, 0, 0, 0, 0, 0, 0,
694 0, 0, 0, 0, 0, 0, 0, 0,
699 // old invalid demo keys
700 #define INVALID_DEMO_KEYS_MAX 14
701 int Invalid_demo_keys[] = {
712 MULTI_MESSAGE_FRIENDLY,
713 MULTI_MESSAGE_HOSTILE,
714 MULTI_MESSAGE_TARGET,
715 MULTI_OBSERVER_ZOOM_TO
718 #define INVALID_DEMO_KEYS_MAX 0
719 int Invalid_demo_keys[INVALID_DEMO_KEYS_MAX+1]; // +1 is only to prevent a 0-size array;
723 int Show_controls_info = 0;
725 DCF_BOOL(show_controls_info, Show_controls_info)
728 static int Axes_origin[JOY_NUM_AXES];
730 void control_config_detect_axis_reset()
732 joystick_read_raw_axis(JOY_NUM_AXES, Axes_origin);
735 int control_config_detect_axis()
737 int i, d, axis = -1, delta = 16384;
738 int axes_values[JOY_NUM_AXES];
740 joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
741 for (i=0; i<JOY_NUM_AXES; i++) {
742 d = abs(axes_values[i] - Axes_origin[i]);
752 int control_config_valid_action(int n)
754 #if defined(FS2_DEMO) || defined(FS1_DEMO)
757 for (i=0; i<INVALID_DEMO_KEYS_MAX; i++)
758 if (n == Invalid_demo_keys[i])
765 void control_config_conflict_check()
767 int i, j, a, b, c, shift = -1, alt = -1;
769 for (i=0; i<CCFG_MAX; i++) {
770 Conflicts[i].key = Conflicts[i].joy = -1;
771 switch (Control_config[i].key_id) {
784 for (i=0; i<NUM_TABS; i++)
785 Conflicts_tabs[i] = 0;
787 for (i=0; i<CCFG_MAX-1; i++) {
788 if (control_config_valid_action(i)) {
789 for (j=i+1; j<CCFG_MAX; j++) {
790 if (control_config_valid_action(j)) {
791 if (Control_config[i].key_id >= 0) {
793 a = Control_config[i].key_id;
794 b = Control_config[j].key_id;
796 Conflicts[i].key = j;
797 Conflicts[j].key = i;
798 Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
799 Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
802 /* if ((a >= 0) && (a & KEY_SHIFTED) && (shift >= 0)) {
803 Conflicts[i].key = shift;
804 Conflicts[shift].key = i;
805 Conflicts_tabs[ Control_config[i].tab ] = 1;
806 Conflicts_tabs[ Control_config[shift].tab ] = 1;
809 if ((b >= 0) && (b & KEY_SHIFTED) && (shift >= 0)) {
810 Conflicts[j].key = shift;
811 Conflicts[shift].key = j;
812 Conflicts_tabs[ Control_config[j].tab ] = 1;
813 Conflicts_tabs[ Control_config[shift].tab ] = 1;
816 if ((a >= 0) && (a & KEY_ALTED) && (alt >= 0)) {
817 Conflicts[i].key = alt;
818 Conflicts[alt].key = i;
819 Conflicts_tabs[ Control_config[i].tab ] = 1;
820 Conflicts_tabs[ Control_config[alt].tab ] = 1;
823 if ((b >= 0) && (b & KEY_ALTED) && (alt >= 0)) {
824 Conflicts[j].key = alt;
825 Conflicts[alt].key = j;
826 Conflicts_tabs[ Control_config[j].tab ] = 1;
827 Conflicts_tabs[ Control_config[alt].tab ] = 1;
831 if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) {
832 Conflicts[i].joy = j;
833 Conflicts[j].joy = i;
834 Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
835 Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
842 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
843 Conflicts_axes[i] = -1;
845 for (i=0; i<NUM_JOY_AXIS_ACTIONS-1; i++) {
846 for (j=i+1; j<NUM_JOY_AXIS_ACTIONS; j++) {
847 if ((Axis_map_to[i] >= 0) && (Axis_map_to[i] == Axis_map_to[j])) {
848 Conflicts_axes[i] = j;
849 Conflicts_axes[j] = i;
850 Conflicts_tabs[SHIP_TAB] = 1;
856 // do list setup required prior to rendering and checking for the controls listing. Called when list changes
857 void control_config_list_prepare()
860 int font_height = gr_get_font_height();
862 Num_cc_lines = y = z = 0;
863 while (z < CCFG_MAX) {
864 if ((Control_config[z].tab == Tab) && control_config_valid_action(z)) {
865 k = Control_config[z].key_id;
866 j = Control_config[z].joy_id;
867 Cc_lines[Num_cc_lines].label = XSTR(Control_config[z].text, CONTROL_CONFIG_XSTR + z);
868 Cc_lines[Num_cc_lines].cc_index = z;
869 Cc_lines[Num_cc_lines++].y = y;
870 y += font_height + 2;
876 if (Tab == SHIP_TAB) {
877 for (j=0; j<NUM_JOY_AXIS_ACTIONS; j++) {
878 Cc_lines[Num_cc_lines].label = Joy_axis_action_text[j];
879 Cc_lines[Num_cc_lines].cc_index = j | JOY_AXIS;
880 Cc_lines[Num_cc_lines++].y = y;
881 y += font_height + 2;
886 int cc_line_query_visible(int n)
890 if ((n < 0) || (n >= Num_cc_lines))
893 y = Cc_lines[n].y - Cc_lines[Scroll_offset].y;
894 if ((y < 0) || (y + gr_get_font_height() > Control_list_coords[gr_screen.res][CONTROL_H_COORD])){
901 // allocates the required space for one undo block and put it in the beginning of the linked list (top of a stack).
902 // Returns a pointer to this newly allocated block
903 config_item_undo *get_undo_block(int size)
905 config_item_undo *ptr;
907 ptr = (config_item_undo *) malloc( sizeof(config_item_undo) );
909 ptr->next = Config_item_undo;
910 Config_item_undo = ptr;
914 ptr->index = (int *) malloc( sizeof(int) * size );
916 ptr->list = (config_item *) malloc( sizeof(config_item) * size );
927 // frees one undo block. The first one in the list (top of the stack) to be precise.
928 void free_undo_block()
930 config_item_undo *ptr;
932 ptr = Config_item_undo;
936 Config_item_undo = ptr->next;
945 // undo the most recent binding changes
946 int control_config_undo_last()
950 if (!Config_item_undo) {
951 gamesnd_play_iface(SND_GENERAL_FAIL);
955 if (Config_item_undo->index[0] & JOY_AXIS)
958 tab = Control_config[Config_item_undo->index[0]].tab;
960 for (i=1; i<Config_item_undo->size; i++) {
961 if (Config_item_undo->index[i] & JOY_AXIS) {
966 if (Control_config[Config_item_undo->index[i]].tab != tab)
974 for (i=0; i<Config_item_undo->size; i++) {
975 z = Config_item_undo->index[i];
980 ptr = &Config_item_undo->list[i];
981 Axis_map_to[z] = ptr->joy_id;
982 Invert_axis[z] = ptr->used;
985 Control_config[z] = Config_item_undo->list[i];
990 control_config_conflict_check();
991 control_config_list_prepare();
992 gamesnd_play_iface(SND_USER_SELECT);
996 void control_config_save_axis_undo(int axis)
998 config_item_undo *ptr;
1001 item.joy_id = (short) Axis_map_to[axis];
1003 item.used = Invert_axis[axis];
1005 ptr = get_undo_block(1);
1006 ptr->index[0] = axis | JOY_AXIS;
1007 ptr->list[0] = item;
1010 void control_config_bind_key(int i, int key)
1012 config_item_undo *ptr;
1014 ptr = get_undo_block(1);
1016 ptr->list[0] = Control_config[i];
1017 Control_config[i].key_id = (short) key;
1020 void control_config_bind_joy(int i, int joy)
1022 config_item_undo *ptr;
1024 ptr = get_undo_block(1);
1026 ptr->list[0] = Control_config[i];
1027 Control_config[i].joy_id = (short) joy;
1030 void control_config_bind_axis(int i, int axis)
1032 control_config_save_axis_undo(i);
1033 Axis_map_to[i] = axis;
1036 int control_config_remove_binding()
1039 config_item_undo *ptr;
1041 if (Selected_line < 0) {
1042 gamesnd_play_iface(SND_GENERAL_FAIL);
1046 z = Cc_lines[Selected_line].cc_index;
1049 if (Axis_map_to[z] < 0) {
1050 gamesnd_play_iface(SND_GENERAL_FAIL);
1054 control_config_save_axis_undo(z);
1055 Axis_map_to[z] = -1;
1056 control_config_conflict_check();
1057 control_config_list_prepare();
1058 gamesnd_play_iface(SND_USER_SELECT);
1063 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1064 gamesnd_play_iface(SND_GENERAL_FAIL);
1069 ptr = get_undo_block(1);
1071 ptr->list[0] = Control_config[z];
1073 if (Selected_item && (Control_config[z].joy_id >= 0)) // if not just key selected (which would be 0)
1074 Control_config[z].joy_id = (short) -1;
1076 if ((Selected_item != 1) && (Control_config[z].key_id >= 0)) // if not just joy button selected (1)
1077 Control_config[z].key_id = (short) -1;
1079 control_config_conflict_check();
1080 control_config_list_prepare();
1081 gamesnd_play_iface(SND_USER_SELECT);
1086 int control_config_clear_other()
1088 int z, i, j, total = 0;
1089 config_item_undo *ptr;
1091 if (Selected_line < 0) {
1092 gamesnd_play_iface(SND_GENERAL_FAIL);
1096 z = Cc_lines[Selected_line].cc_index;
1101 if (Axis_map_to[z] < 0) {
1102 gamesnd_play_iface(SND_GENERAL_FAIL);
1106 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1107 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z))
1111 gamesnd_play_iface(SND_GENERAL_FAIL);
1115 ptr = get_undo_block(total);
1116 for (i=j=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1117 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
1118 item.joy_id = (short) Axis_map_to[i];
1119 item.used = Invert_axis[i];
1121 ptr->index[j] = i | JOY_AXIS;
1122 ptr->list[j] = item;
1125 Axis_map_to[i] = -1;
1128 control_config_conflict_check();
1129 control_config_list_prepare();
1130 gamesnd_play_iface(SND_USER_SELECT);
1134 for (i=0; i<CCFG_MAX; i++)
1135 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1140 gamesnd_play_iface(SND_GENERAL_FAIL);
1144 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1145 gamesnd_play_iface(SND_GENERAL_FAIL);
1149 // now, back up the old bindings so we can undo if we want to
1150 ptr = get_undo_block(total);
1151 for (i=j=0; i<CCFG_MAX; i++)
1152 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1155 ptr->list[j] = Control_config[i];
1158 if (Control_config[i].key_id == Control_config[z].key_id)
1159 Control_config[i].key_id = (short) -1;
1160 if (Control_config[i].joy_id == Control_config[z].joy_id)
1161 Control_config[i].joy_id = (short) -1;
1164 control_config_conflict_check();
1165 control_config_list_prepare();
1166 gamesnd_play_iface(SND_USER_SELECT);
1170 int control_config_clear_all()
1172 int i, j, total = 0;
1173 config_item_undo *ptr;
1175 // first, determine how many bindings need to be changed
1176 for (i=0; i<CCFG_MAX; i++)
1177 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0))
1181 gamesnd_play_iface(SND_GENERAL_FAIL);
1185 // now, back up the old bindings so we can undo if we want to
1186 ptr = get_undo_block(total);
1187 for (i=j=0; i<CCFG_MAX; i++) {
1188 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
1190 ptr->list[j] = Control_config[i];
1196 for (i=0; i<CCFG_MAX; i++) {
1197 Control_config[i].key_id = Control_config[i].joy_id = -1;
1200 control_config_conflict_check();
1201 control_config_list_prepare();
1202 gamesnd_play_iface(SND_RESET_PRESSED);
1206 extern Joy_info joystick;
1208 int control_config_axis_default(int axis)
1213 if (Axis_map_to_defaults[axis] < 0)
1216 if (!joystick.axis_valid[Axis_map_to_defaults[axis]])
1220 return Axis_map_to_defaults[axis];
1223 int control_config_do_reset()
1225 int i, j, total = 0;
1226 config_item_undo *ptr;
1229 // first, determine how many bindings need to be changed
1230 for (i=0; i<CCFG_MAX; i++)
1231 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default))
1234 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1235 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i]))
1239 gamesnd_play_iface(SND_GENERAL_FAIL);
1243 // now, back up the old bindings so we can undo if we want to
1244 ptr = get_undo_block(total);
1245 for (i=j=0; i<CCFG_MAX; i++) {
1246 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
1248 ptr->list[j] = Control_config[i];
1253 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1254 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
1255 item.joy_id = (short) Axis_map_to[i];
1256 item.used = Invert_axis[i];
1258 ptr->index[j] = i | JOY_AXIS;
1259 ptr->list[j] = item;
1264 control_config_reset_defaults();
1265 control_config_conflict_check();
1266 control_config_list_prepare();
1267 gamesnd_play_iface(SND_RESET_PRESSED);
1271 // This sets all the controls to their default values
1272 void control_config_reset_defaults()
1276 // Reset keyboard defaults
1277 for (i=0; i<CCFG_MAX; i++) {
1278 Control_config[i].key_id = Control_config[i].key_default;
1279 Control_config[i].joy_id = Control_config[i].joy_default;
1282 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1283 Axis_map_to[i] = control_config_axis_default(i);
1284 Invert_axis[i] = Invert_axis_defaults[i];
1288 void control_config_scroll_screen_up()
1290 if (Scroll_offset) {
1292 Assert(Selected_line > Scroll_offset);
1293 while (!cc_line_query_visible(Selected_line))
1297 gamesnd_play_iface(SND_SCROLL);
1300 gamesnd_play_iface(SND_GENERAL_FAIL);
1303 void control_config_scroll_line_up()
1305 if (Selected_line) {
1307 if (Selected_line < Scroll_offset)
1308 Scroll_offset = Selected_line;
1311 gamesnd_play_iface(SND_SCROLL);
1314 gamesnd_play_iface(SND_GENERAL_FAIL);
1317 void control_config_scroll_screen_down()
1319 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]) {
1321 while (!cc_line_query_visible(Selected_line)) {
1323 Assert(Selected_line < Num_cc_lines);
1327 gamesnd_play_iface(SND_SCROLL);
1330 gamesnd_play_iface(SND_GENERAL_FAIL);
1333 void control_config_scroll_line_down()
1335 if (Selected_line < Num_cc_lines - 1) {
1337 Assert(Selected_line > Scroll_offset);
1338 while (!cc_line_query_visible(Selected_line))
1342 gamesnd_play_iface(SND_SCROLL);
1345 gamesnd_play_iface(SND_GENERAL_FAIL);
1348 void control_config_toggle_modifier(int bit)
1352 z = Cc_lines[Selected_line].cc_index;
1353 Assert(!(z & JOY_AXIS));
1354 k = Control_config[z].key_id;
1356 gamesnd_play_iface(SND_GENERAL_FAIL);
1360 control_config_bind_key(z, k ^ bit);
1361 control_config_conflict_check();
1362 gamesnd_play_iface(SND_USER_SELECT);
1365 void control_config_toggle_invert()
1369 z = Cc_lines[Selected_line].cc_index;
1370 Assert(z & JOY_AXIS);
1372 control_config_save_axis_undo(z);
1373 Invert_axis[z] = !Invert_axis[z];
1376 void control_config_do_bind()
1381 // if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
1382 if (Selected_line < 0) {
1383 gamesnd_play_iface(SND_GENERAL_FAIL);
1387 for (i=0; i<NUM_BUTTONS; i++)
1388 if (i != CANCEL_BUTTON) {
1389 CC_Buttons[gr_screen.res][i].button.reset_status();
1390 CC_Buttons[gr_screen.res][i].button.disable();
1393 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1394 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1396 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1397 joy_down_count(i); // clear checking status of all joystick buttons
1400 control_config_detect_axis_reset();
1403 Bind_time = timer_get_milliseconds();
1407 gamesnd_play_iface(SND_USER_SELECT);
1410 void control_config_do_search()
1414 for (i=0; i<NUM_BUTTONS; i++){
1415 if (i != CANCEL_BUTTON) {
1416 CC_Buttons[gr_screen.res][i].button.reset_status();
1417 CC_Buttons[gr_screen.res][i].button.disable();
1421 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1422 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1424 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1425 joy_down_count(i); // clear checking status of all joystick buttons
1431 gamesnd_play_iface(SND_USER_SELECT);
1434 void control_config_do_cancel(int fail = 0)
1440 for (i=0; i<NUM_BUTTONS; i++){
1441 if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
1442 CC_Buttons[gr_screen.res][i].button.enable();
1446 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.reset_status();
1447 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1448 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
1449 CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
1450 CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
1452 Binding_mode = Search_mode = 0;
1454 gamesnd_play_iface(SND_GENERAL_FAIL);
1456 gamesnd_play_iface(SND_USER_SELECT);
1460 int control_config_accept()
1464 for (i=0; i<NUM_TABS; i++)
1465 if (Conflicts_tabs[i])
1469 gamesnd_play_iface(SND_GENERAL_FAIL);
1473 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1474 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1475 gamesnd_play_iface(SND_COMMIT_PRESSED);
1479 void control_config_cancel_exit()
1483 for (i=0; i<CCFG_MAX; i++)
1484 Control_config[i] = Control_config_backup[i];
1486 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1489 void control_config_button_pressed(int n)
1497 Scroll_offset = Selected_line = 0;
1498 control_config_list_prepare();
1499 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1503 control_config_do_bind();
1507 control_config_do_search();
1511 control_config_toggle_modifier(KEY_SHIFTED);
1512 gamesnd_play_iface(SND_USER_SELECT);
1516 control_config_toggle_modifier(KEY_ALTED);
1517 gamesnd_play_iface(SND_USER_SELECT);
1521 control_config_toggle_invert();
1522 gamesnd_play_iface(SND_USER_SELECT);
1525 case SCROLL_UP_BUTTON:
1526 control_config_scroll_screen_up();
1529 case SCROLL_DOWN_BUTTON:
1530 control_config_scroll_screen_down();
1534 control_config_accept();
1538 control_config_remove_binding();
1542 launch_context_help();
1543 gamesnd_play_iface(SND_HELP_PRESSED);
1547 control_config_do_reset();
1551 control_config_undo_last();
1555 control_config_do_cancel();
1558 case CLEAR_OTHER_BUTTON:
1559 control_config_clear_other();
1562 case CLEAR_ALL_BUTTON:
1563 control_config_clear_all();
1568 char *control_config_tooltip_handler(char *str)
1572 if (!stricmp(str, NOX("@conflict"))) {
1573 for (i=0; i<NUM_TABS; i++) {
1574 if (Conflicts_tabs[i])
1575 return XSTR( "Conflict!", 205);
1582 void control_config_init()
1587 // make backup of all controls
1588 for (i=0; i<CCFG_MAX; i++)
1589 Control_config_backup[i] = Control_config[i];
1591 common_set_interface_palette(NOX("ControlConfigPalette")); // set the interface palette
1592 Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1593 Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
1594 Ui_window.tooltip_handler = control_config_tooltip_handler;
1596 // load in help overlay bitmap
1597 help_overlay_load(CONTROL_CONFIG_OVERLAY);
1598 help_overlay_set_state(CONTROL_CONFIG_OVERLAY,0);
1600 // reset conflict flashing
1601 Conflict_stamp = -1;
1604 hud_anim_init(&Conflict_warning_anim, Conflict_warning_coords[gr_screen.res][0], Conflict_warning_coords[gr_screen.res][1], NOX("ConflictFlash"));
1605 hud_anim_load(&Conflict_warning_anim);
1608 for (i=0; i<NUM_BUTTONS; i++) {
1609 b = &CC_Buttons[gr_screen.res][i];
1611 if (b->hotspot < 0) { // temporary
1612 b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1); // temporary
1613 b->button.set_highlight_action(common_play_highlight_sound);
1617 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1619 // set up callback for when a mouse first goes over a button
1620 b->button.set_highlight_action(common_play_highlight_sound);
1622 b->button.set_bmaps(b->filename, 5, 1); // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1624 b->button.set_bmaps(b->filename);
1626 b->button.link_hotspot(b->hotspot);
1630 for(i=0; i<CC_NUM_TEXT; i++){
1631 Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
1634 for (i=0; i<LIST_BUTTONS_MAX; i++) {
1635 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1636 List_buttons[i].hide();
1637 List_buttons[i].disable();
1640 // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1641 CC_Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
1642 CC_Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
1643 CC_Buttons[gr_screen.res][BIND_BUTTON].button.set_hotkey(KEY_ENTER);
1644 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_DELETE);
1645 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_Z);
1646 CC_Buttons[gr_screen.res][CLEAR_BUTTON].button.set_hotkey(KEY_DELETE);
1647 CC_Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
1648 CC_Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(KEY_F1);
1649 CC_Buttons[gr_screen.res][RESET_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_R);
1650 CC_Buttons[gr_screen.res][INVERT_AXIS].button.set_hotkey(KEY_I);
1652 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1653 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
1655 Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);
1657 Scroll_offset = Selected_line = 0;
1658 Config_item_undo = NULL;
1659 control_config_conflict_check();
1662 Joy_axis_action_text[0] = strdup(XSTR("Turn (Yaw) Axis", 1016));
1663 Joy_axis_action_text[1] = strdup(XSTR("Pitch Axis", 1017));
1664 Joy_axis_action_text[2] = strdup(XSTR("Bank Axis", 1018));
1665 Joy_axis_action_text[3] = strdup(XSTR("Absolute Throttle Axis", 1019));
1666 Joy_axis_action_text[4] = strdup(XSTR("Relative Throttle Axis", 1020));
1667 Joy_axis_text[0] = strdup(XSTR("Joystick/Mouse X Axis", 1021));
1668 Joy_axis_text[1] = strdup(XSTR("Joystick/Mouse Y Axis", 1022));
1669 Joy_axis_text[2] = strdup(XSTR("Joystick Z Axis", 1023));
1670 Joy_axis_text[3] = strdup(XSTR("Joystick rX Axis", 1024));
1671 Joy_axis_text[4] = strdup(XSTR("Joystick rY Axis", 1025));
1672 Joy_axis_text[5] = strdup(XSTR("Joystick rZ Axis", 1026));
1673 Mouse_button_text[0] = strdup("");
1674 Mouse_button_text[1] = strdup(XSTR("Left Button", 1027));
1675 Mouse_button_text[2] = strdup(XSTR("Right Button", 1028));
1676 Mouse_button_text[3] = strdup(XSTR("Mid Button", 1029));
1677 Mouse_button_text[4] = strdup("");
1678 Mouse_axis_text[0] = strdup(XSTR("L/R", 1030));
1679 Mouse_axis_text[1] = strdup(XSTR("U/B", 1031));
1680 Invert_text[0] = strdup(XSTR("N", 1032));
1681 Invert_text[1] = strdup(XSTR("Y", 1033));
1683 control_config_list_prepare();
1686 void control_config_close()
1690 while (Config_item_undo){
1694 // unload the overlay bitmap
1695 help_overlay_unload(CONTROL_CONFIG_OVERLAY);
1697 if (Background_bitmap){
1698 bm_unload(Background_bitmap);
1702 hud_anim_release(&Conflict_warning_anim);
1705 Ui_window.destroy();
1706 common_free_interface_palette(); // restore game palette
1707 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1712 for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
1713 if(Joy_axis_action_text[idx] != NULL){
1714 free(Joy_axis_action_text[idx]);
1715 Joy_axis_action_text[idx] = NULL;
1718 for(idx=0; idx<NUM_AXIS_TEXT; idx++){
1719 if(Joy_axis_text[idx] != NULL){
1720 free(Joy_axis_text[idx]);
1721 Joy_axis_text[idx] = NULL;
1724 for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
1725 if(Mouse_button_text[idx] != NULL){
1726 free(Mouse_button_text[idx]);
1727 Mouse_button_text[idx] = NULL;
1730 for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
1731 if(Mouse_axis_text[idx] != NULL){
1732 free(Mouse_axis_text[idx]);
1733 Mouse_axis_text[idx] = NULL;
1736 for(idx=0; idx<NUM_INVERT_TEXT; idx++){
1737 if(Invert_text[idx] != NULL){
1738 free(Invert_text[idx]);
1739 Invert_text[idx] = NULL;
1744 void control_config_do_frame(float frametime)
1746 char buf[256], *str, *jptr;
1747 int i, j, k, w, x, y, z, len, line, conflict;
1748 int font_height = gr_get_font_height();
1749 int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
1750 static float timer = 0.0f;
1752 static int bound_timestamp = 0;
1753 static char bound_string[40];
1758 if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1761 z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1762 i = control_config_detect_axis();
1769 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1770 Ui_window.process(0);
1773 strcpy(bound_string, XSTR( "Canceled", 206));
1774 bound_timestamp = timestamp(2500);
1775 control_config_do_cancel();
1781 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1782 if (joy_down_count(i))
1786 if (Axis_override >= 0) {
1787 control_config_bind_axis(z, Axis_override);
1788 strcpy(bound_string, Joy_axis_text[Axis_override]);
1789 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1790 bound_timestamp = timestamp(2500);
1791 control_config_conflict_check();
1792 control_config_list_prepare();
1793 control_config_do_cancel();
1796 control_config_do_cancel(1);
1802 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1803 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1804 Ui_window.set_ignore_gadgets(1);
1808 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1809 Ui_window.process(0);
1811 if ( (k > 0) || B1_JUST_RELEASED ) {
1812 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1813 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1814 Ui_window.set_ignore_gadgets(0);
1819 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1820 Ui_window.set_ignore_gadgets(0);
1824 strcpy(bound_string, XSTR( "Canceled", 206));
1825 bound_timestamp = timestamp(2500);
1826 control_config_do_cancel();
1829 switch (k & KEY_MASK) {
1834 Last_key = k & KEY_MASK;
1839 if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED) // a special hack just for Mike K.
1840 if ( (Last_key >= 0) && (k <= 0) && !keyd_pressed[Last_key] )
1843 if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1844 popup(0, 1, POPUP_OK, XSTR( "That is a non-bindable key. Please try again.", 207));
1848 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1850 z = Cc_lines[Selected_line].cc_index;
1851 Assert(!(z & JOY_AXIS));
1852 control_config_bind_key(z, k);
1854 strcpy(bound_string, textify_scancode(k));
1855 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1856 bound_timestamp = timestamp(2500);
1857 control_config_conflict_check();
1858 control_config_list_prepare();
1859 control_config_do_cancel();
1862 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1863 if (joy_down_count(i)) {
1864 z = Cc_lines[Selected_line].cc_index;
1865 Assert(!(z & JOY_AXIS));
1866 control_config_bind_joy(z, i);
1868 strcpy(bound_string, Joy_button_text[i]);
1869 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1870 bound_timestamp = timestamp(2500);
1871 control_config_conflict_check();
1872 control_config_list_prepare();
1873 control_config_do_cancel();
1877 if (Bind_time + 375 < timer_get_milliseconds()) {
1878 for (i=0; i<NUM_BUTTONS; i++){
1879 if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
1884 if (i == NUM_BUTTONS) { // no buttons pressed
1885 for (i=0; i<MOUSE_NUM_BUTTONS; i++)
1886 if (mouse_down(1 << i)) {
1887 z = Cc_lines[Selected_line].cc_index;
1888 Assert(!(z & JOY_AXIS));
1889 control_config_bind_joy(z, i);
1891 strcpy(bound_string, Joy_button_text[i]);
1892 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1893 bound_timestamp = timestamp(2500);
1894 control_config_conflict_check();
1895 control_config_list_prepare();
1896 control_config_do_cancel();
1897 for (j=0; j<NUM_BUTTONS; j++){
1898 CC_Buttons[gr_screen.res][j].button.reset();
1908 } else if (Search_mode) {
1909 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1910 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1911 Ui_window.set_ignore_gadgets(1);
1915 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1916 Ui_window.process(0);
1918 if ( (k > 0) || B1_JUST_RELEASED ) {
1919 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1920 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1921 Ui_window.set_ignore_gadgets(0);
1926 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1927 Ui_window.set_ignore_gadgets(0);
1931 control_config_do_cancel();
1934 if ((k > 0) && !Config_allowed[k & KEY_MASK])
1937 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1940 for (i=0; i<CCFG_MAX; i++)
1941 if (Control_config[i].key_id == k) {
1947 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1948 if (joy_down_count(i)) {
1950 for (i=0; i<CCFG_MAX; i++)
1951 if (Control_config[i].joy_id == j) {
1959 // check if not on enabled button
1960 for (j=0; j<NUM_BUTTONS; j++){
1961 if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
1966 if (j == NUM_BUTTONS) { // no buttons pressed
1967 for (j=0; j<MOUSE_NUM_BUTTONS; j++)
1968 if (mouse_down(1 << j)) {
1969 for (i=0; i<CCFG_MAX; i++)
1970 if (Control_config[i].joy_id == j) {
1972 for (j=0; j<NUM_BUTTONS; j++){
1973 CC_Buttons[gr_screen.res][j].button.reset();
1983 Tab = Control_config[z].tab;
1984 control_config_list_prepare();
1985 Selected_line = Scroll_offset = 0;
1986 for (i=0; i<Num_cc_lines; i++)
1987 if (Cc_lines[i].cc_index == z) {
1992 while (!cc_line_query_visible(Selected_line)) {
1994 Assert(Scroll_offset < Num_cc_lines);
2000 z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
2001 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
2002 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
2003 CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
2006 z = Cc_lines[Selected_line].cc_index;
2007 k = Control_config[z].key_id;
2008 if ( (k == KEY_LALT) || (k == KEY_RALT) || (k == KEY_LSHIFT) || (k == KEY_RSHIFT) ) {
2009 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
2010 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
2014 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
2016 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2017 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
2018 Ui_window.set_ignore_gadgets(1);
2021 k = Ui_window.process();
2023 if ( (k > 0) || B1_JUST_RELEASED ) {
2024 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2025 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
2026 Ui_window.set_ignore_gadgets(0);
2031 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2032 Ui_window.set_ignore_gadgets(0);
2036 case KEY_DOWN: // select next line
2037 control_config_scroll_line_down();
2040 case KEY_UP: // select previous line
2041 control_config_scroll_line_up();
2044 case KEY_SHIFTED | KEY_TAB: // activate previous tab
2049 Scroll_offset = Selected_line = 0;
2050 control_config_list_prepare();
2051 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2054 case KEY_TAB: // activate next tab
2056 if (Tab >= NUM_TABS)
2059 Scroll_offset = Selected_line = 0;
2060 control_config_list_prepare();
2061 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2066 if (Selected_item == -2) {
2068 if (Cc_lines[Selected_line].jw < 1) {
2070 if (Cc_lines[Selected_line].kw < 1)
2075 gamesnd_play_iface(SND_SCROLL);
2080 if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1))
2082 else if (!Selected_item && (Cc_lines[Selected_line].kw < 1))
2084 else if (Selected_item > 1)
2087 gamesnd_play_iface(SND_SCROLL);
2090 case KEY_BACKSP: // undo
2091 control_config_undo_last();
2095 control_config_cancel_exit();
2100 for (i=0; i<NUM_BUTTONS; i++){
2101 if (CC_Buttons[gr_screen.res][i].button.pressed()){
2102 control_config_button_pressed(i);
2106 for (i=0; i<LIST_BUTTONS_MAX; i++) {
2107 if (List_buttons[i].is_mouse_on())
2108 select_tease_line = i + Scroll_offset;
2110 if (List_buttons[i].pressed()) {
2111 Selected_line = i + Scroll_offset;
2113 List_buttons[i].get_mouse_pos(&x, &y);
2114 if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw))
2117 if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw))
2120 gamesnd_play_iface(SND_USER_SELECT);
2123 if (List_buttons[i].double_clicked())
2124 control_config_do_bind();
2127 GR_MAYBE_CLEAR_RES(Background_bitmap);
2128 if (Background_bitmap >= 0) {
2129 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2133 // highlight tab with conflict
2135 for (i=z=0; i<NUM_TABS; i++) {
2136 if (Conflicts_tabs[i]) {
2137 CC_Buttons[gr_screen.res][i].button.draw_forced(4);
2143 // maybe switch from bright to normal
2144 if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
2145 Conflict_bright = !Conflict_bright;
2147 Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
2151 // set color and font
2153 if(Conflict_bright){
2154 gr_set_color_fast(&Color_bright_red);
2156 gr_set_color_fast(&Color_red);
2159 // setup the conflict string
2160 char conflict_str[512] = "";
2161 strncpy(conflict_str, XSTR("Conflict!", 205), 511);
2163 gr_get_string_size(&sw, &sh, conflict_str);
2165 gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str);
2169 hud_anim_render(&Conflict_warning_anim, frametime);
2172 // might as well always reset the conflict stamp
2173 Conflict_stamp = -1;
2176 for (i=0; i<NUM_TABS; i++) {
2177 if (CC_Buttons[gr_screen.res][i].button.button_down()) {
2182 if (i == NUM_TABS) {
2183 CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
2187 CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
2190 if (Selected_line >= 0) {
2191 z = Cc_lines[Selected_line].cc_index;
2193 if (Invert_axis[z & ~JOY_AXIS]) {
2194 CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
2198 z = Control_config[z].key_id;
2200 if (z & KEY_SHIFTED) {
2201 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
2203 if (z & KEY_ALTED) {
2204 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
2211 CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
2214 z = Cc_lines[Selected_line].cc_index;
2215 x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2;
2216 y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2;
2220 t = (int) (timer * 3);
2222 gr_set_color_fast(&Color_text_normal);
2223 gr_get_string_size(&w, NULL, XSTR( "?", 208));
2224 gr_printf(x - w / 2, y - font_height / 2, XSTR( "?", 208));
2227 } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
2228 i = Conflicts[z].key;
2230 i = Conflicts[z].joy;
2232 gr_set_color_fast(&Color_text_normal);
2233 str = XSTR( "Control conflicts with:", 209);
2234 gr_get_string_size(&w, NULL, str);
2235 gr_printf(x - w / 2, y - font_height, str);
2237 strcpy(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
2238 gr_force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
2239 gr_get_string_size(&w, NULL, buf);
2240 gr_printf(x - w / 2, y, buf);
2242 } else if (*bound_string) {
2243 gr_set_color_fast(&Color_text_normal);
2244 gr_get_string_size(&w, NULL, bound_string);
2245 gr_printf(x - w / 2, y - font_height / 2, bound_string);
2246 if (timestamp_elapsed(bound_timestamp))
2250 // gr_set_color_fast(&Color_text_heading);
2251 // gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]);
2253 // gr_get_string_size(&w, &h, Heading[Tab]);
2254 // y = HEADING_Y + h / 2 - 1;
2255 // gr_line(LIST_X, y, LIST_X + 18, y);
2256 // gr_line(LIST_X + w + 21, y, LIST_X + LIST_W, y);
2258 if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
2259 gr_set_color_fast(&Color_white);
2260 gr_printf(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210));
2264 line = Scroll_offset;
2265 while (cc_line_query_visible(line)) {
2266 z = Cc_lines[line].cc_index;
2267 y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
2269 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);
2270 List_buttons[line - Scroll_offset].enable(!Binding_mode);
2272 Cc_lines[line].kw = Cc_lines[line].jw = 0;
2274 if (line == Selected_line){
2275 c = &Color_text_selected;
2276 } else if (line == select_tease_line) {
2277 c = &Color_text_subselected;
2279 c = &Color_text_normal;
2282 gr_set_color_fast(c);
2283 if (Cc_lines[line].label) {
2284 strcpy(buf, Cc_lines[line].label);
2285 gr_force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
2286 gr_printf(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
2289 if (!(z & JOY_AXIS)) {
2290 k = Control_config[z].key_id;
2291 j = Control_config[z].joy_id;
2292 x = Control_list_key_x[gr_screen.res];
2296 if ((k < 0) && (j < 0)) {
2297 gr_set_color_fast(&Color_grey);
2298 gr_printf(x, y, XSTR( "None", 211));
2302 strcpy(buf, textify_scancode(k));
2303 if (Conflicts[z].key >= 0) {
2304 if (c == &Color_text_normal)
2305 gr_set_color_fast(&Color_text_error);
2307 gr_set_color_fast(&Color_text_error_hi);
2311 } else if (Selected_item == 1) {
2312 gr_set_color_fast(&Color_text_normal);
2315 gr_set_color_fast(c);
2317 gr_printf(x, y, buf);
2320 Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2321 gr_get_string_size(&w, NULL, buf);
2322 Cc_lines[line].kw = w;
2326 gr_set_color_fast(&Color_text_normal);
2327 gr_printf(x, y, XSTR( ", ", 212));
2328 gr_get_string_size(&w, NULL, XSTR( ", ", 212));
2334 strcpy(buf, Joy_button_text[j]);
2335 if (Conflicts[z].joy >= 0) {
2336 if (c == &Color_text_normal)
2337 gr_set_color_fast(&Color_text_error);
2339 gr_set_color_fast(&Color_text_error_hi);
2343 } else if (!Selected_item) {
2344 gr_set_color_fast(&Color_text_normal);
2347 gr_set_color_fast(c);
2349 gr_force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x);
2350 gr_printf(x, y, buf);
2352 Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2353 gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
2358 x = Control_list_key_x[gr_screen.res];
2359 j = Axis_map_to[z & ~JOY_AXIS];
2360 if (Binding_mode && (line == Selected_line))
2364 gr_set_color_fast(&Color_grey);
2365 gr_printf(x, y, XSTR( "None", 211));
2368 if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
2369 if (c == &Color_text_normal)
2370 gr_set_color_fast(&Color_text_error);
2373 gr_set_color_fast(&Color_text_error_hi);
2377 } else if (!Selected_item) {
2378 gr_set_color_fast(&Color_text_normal);
2381 gr_set_color_fast(c);
2383 gr_string(x, y, Joy_axis_text[j]);
2390 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
2392 i = line - Scroll_offset;
2393 while (i < LIST_BUTTONS_MAX)
2394 List_buttons[i++].disable();
2396 // blit help overlay if active
2397 help_overlay_maybe_blit(CONTROL_CONFIG_OVERLAY);
2402 void clear_key_binding(short key)
2406 for (i=0; i<CCFG_MAX; i++) {
2407 if (Control_config[i].key_id == key)
2408 Control_config[i].key_id = -1;
2412 float check_control_timef(int id)
2416 // if type isn't continuous, we shouldn't be using this function, cause it won't work.
2417 Assert(Control_config[id].type == CC_TYPE_CONTINUOUS);
2419 // first, see if control actually used (makes sure modifiers match as well)
2420 if (!check_control(id))
2423 t1 = key_down_timef(Control_config[id].key_id);
2427 t2 = joy_down_time(Control_config[id].joy_id);
2437 void control_check_indicate()
2440 if (Show_controls_info) {
2441 gr_set_color_fast(&HUD_color_debug);
2442 gr_printf(490, 15, NOX("Ctrls checked: %d"), Control_check_count);
2446 Control_check_count = 0;
2449 int check_control(int id, int key)
2452 static int last_key = 0;
2454 Control_check_count++;
2460 // if we're in multiplayer text enter (for chat) mode, check to see if we should ignore controls
2461 if ((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls()){
2465 if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
2466 if (joy_down(Control_config[id].joy_id) || joy_down_count(Control_config[id].joy_id)) {
2471 if ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS))
2472 if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2477 // check what current modifiers are pressed
2479 if (keyd_pressed[KEY_LSHIFT] || key_down_count(KEY_LSHIFT) || keyd_pressed[KEY_RSHIFT] || key_down_count(KEY_RSHIFT))
2480 mask |= KEY_SHIFTED;
2482 if (keyd_pressed[KEY_LALT] || key_down_count(KEY_LALT) || keyd_pressed[KEY_RALT] || key_down_count(KEY_RALT))
2485 z = Control_config[id].key_id;
2487 if ( (z != KEY_LALT) && (z != KEY_RALT) && (z != KEY_LSHIFT) && (z != KEY_RSHIFT) ) {
2488 // if current modifiers don't match action's modifiers, don't register control active.
2489 if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask)
2495 if (keyd_pressed[z] || key_down_count(z)) {
2496 if ( !hud_squadmsg_read_key(z) ) {
2506 if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2514 // get heading, pitch, bank, throttle abs. and throttle rel. values.
2515 void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
2517 int axes_values[JOY_NUM_AXES];
2519 joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
2521 // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
2523 if (Axis_map_to[0] >= 0)
2524 *h = joy_get_scaled_reading(axes_values[Axis_map_to[0]], Axis_map_to[0]);
2527 if (Axis_map_to[1] >= 0)
2528 *p = joy_get_scaled_reading(axes_values[Axis_map_to[1]], Axis_map_to[1]);
2531 if (Axis_map_to[2] >= 0)
2532 *b = joy_get_scaled_reading(axes_values[Axis_map_to[2]], Axis_map_to[2]);
2535 if (Axis_map_to[3] >= 0)
2536 *ta = joy_get_unscaled_reading(axes_values[Axis_map_to[3]], Axis_map_to[3]);
2539 if (Axis_map_to[4] >= 0)
2540 *tr = joy_get_scaled_reading(axes_values[Axis_map_to[4]], Axis_map_to[4]);
2556 void control_used(int id)
2558 Control_config[id].used = timestamp();
2561 void control_config_clear_used_status()
2565 for (i=0; i<CCFG_MAX; i++)
2566 Control_config[i].used = 0;
2569 void control_config_clear()
2573 // Reset keyboard defaults
2574 for (i=0; i<CCFG_MAX; i++)
2575 Control_config[i].key_id = Control_config[i].joy_id = -1;
2578 int control_config_handle_conflict()
2580 if ((Selected_item == -1) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) { // we are deleting a conflict
2581 j = Conflicts[z].joy;
2582 if ((j >= 0) && (Control_config[j].joy_id < 0))
2585 k = Conflicts[z].key;
2586 if ((k >= 0) && (Control_config[k].key_id < 0))
2589 if ((j >= 0) && (k >= 0) && (j != k)) { // deleting 2 conflicts, each in different actions
2590 ptr = get_undo_block(2);
2592 ptr->list[0] = Control_config[j];
2593 Control_config[j].joy_id = (short) -1;
2596 ptr->list[1] = Control_config[k];
2597 Control_config[k].key_id = (short) -1;
2599 } else { // only 1 action in conflict with selected action (might be both controls, though)
2605 ptr = get_undo_block(1);
2607 ptr->list[0] = Control_config[z];
2610 Control_config[z].joy_id = (short) -1;
2613 Control_config[z].key_id = (short) -1;