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.7 2005/03/29 02:18:47 taylor
19 * Various 64-bit platform fixes
20 * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
21 * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
22 * Streaming audio support (big thanks to Pierre Willenbrock!!)
23 * Removed dependance on strings.tbl for FS1 since we don't actually need it now
25 * Revision 1.6 2004/09/20 01:31:44 theoddone33
28 * Revision 1.5 2004/07/04 11:31:43 taylor
29 * amd64 support, compiler warning fixes, don't use software rendering
31 * Revision 1.4 2003/05/25 02:30:42 taylor
34 * Revision 1.3 2002/06/09 04:41:15 relnev
35 * added copyright header
37 * Revision 1.2 2002/05/07 03:16:43 theoddone33
38 * The Great Newline Fix
40 * Revision 1.1.1.1 2002/05/03 03:28:08 root
44 * 13 10/14/99 2:50p Jefff
47 * 12 8/16/99 9:50a Jefff
48 * fixed loading of tab bitmaps
50 * 11 8/11/99 3:21p Jefff
51 * added tab highlights on conflict
53 * 10 7/26/99 5:25p Jefff
54 * removed invalidation of key binding for demo builds
56 * 9 7/19/99 2:13p Dave
57 * Added some new strings for Heiko.
59 * 8 7/15/99 9:20a Andsager
60 * FS2_DEMO initial checkin
62 * 7 6/19/99 2:46p Dave
63 * New control config screen.
65 * 6 1/30/99 5:08p Dave
66 * More new hi-res stuff.Support for nice D3D textures.
68 * 5 1/15/99 11:29a Neilk
69 * Fixed D3D screen/texture pixel formatting problem.
71 * 4 11/05/98 4:18p Dave
72 * First run nebula support. Beefed up localization a bit. Removed all
73 * conditional compiles for foreign versions. Modified mission file
76 * 3 10/13/98 9:28a Dave
77 * Started neatening up freespace.h. Many variables renamed and
78 * reorganized. Added AlphaColors.[h,cpp]
80 * 2 10/07/98 10:52a Dave
83 * 1 10/07/98 10:48a Dave
85 * 61 8/09/98 11:55a Lawrance
86 * if GRAVIS_OEM is defined, map the throttle axis by default
88 * 60 6/19/98 3:51p Lawrance
89 * localize control text
91 * 59 6/17/98 11:04a Lawrance
92 * localize the control config strings
94 * 58 6/13/98 5:19p Hoffoss
95 * externalized control config texts.
97 * 57 6/09/98 5:15p Lawrance
98 * French/German localization
100 * 56 6/09/98 10:31a Hoffoss
101 * Created index numbers for all xstr() references. Any new xstr() stuff
102 * added from here on out should be added to the end if the list. The
103 * current list count can be found in FreeSpace.cpp (search for
106 * 55 6/01/98 11:43a John
107 * JAS & MK: Classified all strings for localization.
109 * 54 5/26/98 11:10a Lawrance
110 * Fix bug where window controls get disabled when F1 pressed twice
112 * 53 5/20/98 10:35p Hoffoss
113 * Fixed bug with mouse buttons not working when action isn't continuous.
115 * 52 5/19/98 4:08p Allender
116 * kill default binding for Z axis
118 * 51 5/19/98 12:56p Hoffoss
119 * Added some code to help prevent triple-clicking for binding the mouse
120 * button to an action (why the hell people triple click is beyond me,
123 * 50 5/19/98 11:11a Lawrance
124 * Ensure X and Y axis have defaults!
126 * 49 5/18/98 4:53p Hoffoss
127 * Some force feedback tweaks and pilot initializations there should have
128 * been happening, but weren't, and not are!
130 * 48 5/18/98 10:15a Lawrance
131 * Only do hud squad msg key check when necessary
133 * 47 5/18/98 10:08a Lawrance
134 * deal with overlap between hud squad msg number keys and CC_CONTINUOUS
137 * 46 5/17/98 5:44p Hoffoss
138 * Made throttle never bound by default (ask Sandeep why if interested).
140 * 45 5/14/98 5:32p Hoffoss
141 * Improved axis binding code some more.
143 * 44 5/13/98 7:15p Hoffoss
144 * Fixed remaining bugs with axis binding.
146 * 43 5/13/98 1:17a Hoffoss
147 * Added joystick axes configurability.
149 * 42 5/12/98 3:49p Hoffoss
150 * Fixed bug where double mouse click would bind mouse button right away.
152 * 41 5/11/98 5:43p Hoffoss
153 * Made num lock not bindable.
155 * 40 5/11/98 5:29p Hoffoss
156 * Added mouse button mapped to joystick button support.
158 * 39 5/07/98 6:25p Dave
159 * Fix strange boundary conditions which arise when players die/respawn
160 * while the game is being ended. Spiff up the chatbox doskey thing a bit.
162 * 38 5/05/98 1:48a Lawrance
163 * Add in missing help overlays
165 * 37 4/27/98 10:11a Lawrance
166 * Add in disabled beep for missing buttons
168 * 36 4/25/98 2:59p Hoffoss
169 * Fixed typo that was causing a bug.
171 * 35 4/22/98 1:51a Lawrance
172 * Take out multiplayer key from demo key config
174 * 34 4/16/98 4:29p Hoffoss
175 * Fixed bank_when_pressed functionality when using alt or shift for it.
177 * 33 4/15/98 11:06a Lawrance
178 * fix bug with a multi key showing up in demo, remove obsolete bindings
179 * from demo and full version
181 * 32 4/14/98 2:45p Hoffoss
182 * Made hitting escape to exit screen not play failed sound.
184 * 31 4/14/98 2:27p Hoffoss
185 * Made certain actions be hidden in demo build.
187 * 30 4/13/98 2:38p Hoffoss
188 * Added a tooltip handler and make binding attempts with illegal keys
191 * 29 4/11/98 7:59p Lawrance
192 * Add support for help overlays
194 * 28 4/09/98 4:12p Hoffoss
195 * Changed check_control() to automatically register a control as used if
196 * it detects it being used.
198 * 27 4/08/98 11:11a Hoffoss
199 * Fixed some bugs that showed up due to fixing other bugs the other day
202 * 26 4/07/98 3:47p Hoffoss
203 * Fixed continuous controls checking with respect to modifiers.
205 * 25 4/06/98 11:17a Hoffoss
206 * Fixed num lock/pause interplay bug.
208 * 24 4/03/98 3:51p Hoffoss
209 * Fixed some bugs, and made changed Interplay requested regarding search
212 * 23 3/31/98 4:12p Hoffoss
213 * Made control used status clear at mission init time.
215 * 22 3/23/98 11:28a Hoffoss
216 * Fixed flashing question mark bug.
218 * 21 3/21/98 11:30a John
219 * Fixed bug where joymouse caused you to stay in binding mode when
220 * binding joystick button 1 to something.
222 * 20 3/20/98 3:37p Hoffoss
223 * Tried to fix mitri's bug, failed miserably.
225 * 19 3/19/98 5:04p Dave
226 * Put in support for targeted multiplayer text and voice messaging (all,
227 * friendly, hostile, individual).
229 * 18 3/18/98 12:03p John
230 * Marked all the new strings as externalized or not.
232 * 17 3/18/98 10:16a Hoffoss
235 * 16 3/17/98 11:15a Hoffoss
236 * Made question mark that appears when you are in bind mode flash.
238 * 15 3/17/98 10:48a Hoffoss
239 * Allowed a special hack for "bank while pressed" action to use alt and
240 * shift keys standalone.
242 * 14 3/12/98 3:22p Hoffoss
243 * Fixed 2 bugs with one solution! Yay! Failed sound on bind fixed and
244 * pad enter now not translated to enter.
246 * 13 3/11/98 5:28p Hoffoss
247 * Added control config debug display info to possibly aid in tracking
250 * 12 2/26/98 10:07p Hoffoss
251 * Rewrote state saving and restoring to fix bugs and simplify the code.
253 * 11 2/22/98 12:19p John
254 * Externalized some strings
256 * 10 2/20/98 3:39p Hoffoss
257 * Updated code for new control config screen artwork.
259 * 9 2/09/98 2:50p Hoffoss
260 * Made 'none' show up as gray instead of normal color, to distinguish it
261 * from actions with bound keys.
263 * 8 2/07/98 10:04p Hoffoss
264 * Changed color and placement of "more" indicator.
266 * 7 2/05/98 10:42a Hoffoss
267 * Fixed bug where while in bind mode, you could change the line selected
268 * using the mouse, and binding would work on the new line instead.
270 * 6 2/03/98 5:05p Hoffoss
271 * Added "clear other" button to clear all conflicting bindings with
274 * 5 1/22/98 4:53p Hoffoss
275 * Made training messages/directives display a joystick button in place of
276 * a keypress if there is no keypress bound to the action.
278 * 4 1/20/98 4:20p Hoffoss
279 * Removed confusing behavior of clear button clearing out the other
280 * binding in a conflict.
282 * 3 1/08/98 12:11p Hoffoss
283 * Changed Rudder axis to Roll axis, added new function we can use to
284 * check what joystick axes are valid with.
286 * 2 12/24/97 3:37p Hoffoss
287 * Moved control config stuff to seperate library to Fred can access it as
290 * 1 12/24/97 11:58a Hoffoss
292 * 98 12/22/97 2:15p Hoffoss
293 * Fixed bug where joystick axis lines weren't being displayed.
295 * 97 12/16/97 2:44p Hoffoss
296 * Added clear button to control config screen.
298 * 96 12/12/97 3:07p Hoffoss
299 * Changed how deleting bindings work. Each control of an action can be
300 * deleted independently or both at once.
302 * 95 12/07/97 2:36p John
303 * Made warp out be Alt+J instead of J
305 * 94 12/03/97 4:59p Hoffoss
306 * Added reset sound and change control config sounds around.
308 * 93 12/03/97 4:16p Hoffoss
309 * Changed sound stuff used in interface screens for interface purposes.
315 #include "freespace.h"
316 #include "controlsconfig.h"
317 #include "gamesequence.h"
320 #include "hudsquadmsg.h"
330 #include "missionscreencommon.h"
333 #include "managepilot.h"
334 #include "multi_pmsg.h"
335 #include "contexthelp.h"
338 #include "multiutil.h"
339 #include "alphacolors.h"
341 #define NUM_SYSTEM_KEYS 14
342 #define NUM_BUTTONS 19
345 // coordinate indicies
346 #define CONTROL_X_COORD 0
347 #define CONTROL_Y_COORD 1
348 #define CONTROL_W_COORD 2
349 #define CONTROL_H_COORD 3
351 char* Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS] = {
352 "ControlConfig", // GR_640
353 "2_ControlConfig" // GR_1024
356 char* Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
357 "ControlConfig-m", // GR_640
358 "2_ControlConfig-m" // GR_1024
362 int Control_list_coords[GR_NUM_RESOLUTIONS][4] = {
367 32, 58, 198, 259 // GR_640
371 32, 94, 904, 424 // GR_1024
375 // width of the control name section of the list
376 int Control_list_ctrl_w[GR_NUM_RESOLUTIONS] = {
381 // x start position of the binding area section of the list
382 int Control_list_key_x[GR_NUM_RESOLUTIONS] = {
387 // width of the binding area section of the list
388 int Control_list_key_w[GR_NUM_RESOLUTIONS] = {
393 // display the "more..." text under the control list
394 int Control_more_coords[GR_NUM_RESOLUTIONS][2] = {
407 // area to display "conflicts with..." text
408 int Conflict_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
413 32, 313, 250, 32 // GR_640
417 48, 508, 354, 46 // GR_1024
421 // conflict warning anim coords
422 int Conflict_warning_coords[GR_NUM_RESOLUTIONS][2] = {
435 // for flashing the conflict text
436 #define CONFLICT_FLASH_TIME 250
437 int Conflict_stamp = -1;
438 int Conflict_bright = 0;
440 #define LIST_BUTTONS_MAX 40
441 #define JOY_AXIS 0x80000
443 static int Num_cc_lines;
446 int cc_index; // index into Control_config of item
447 int y; // Y coordinate of line
448 int kx, kw, jx, jw; // x start and width of keyboard and joystick bound text
449 } Cc_lines[CCFG_MAX];
451 // struct to hold backup config_item elements so we can undo them
452 struct config_item_undo {
454 int *index; // array (size) of Control_config indices of replaced elements
455 config_item *list; // array (size) of original elements
456 config_item_undo *next;
459 config_item Control_config_backup[CCFG_MAX];
462 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
463 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
465 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
466 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
469 // all this stuff is localized/externalized
470 #define NUM_AXIS_TEXT 6
471 #define NUM_MOUSE_TEXT 5
472 #define NUM_MOUSE_AXIS_TEXT 2
473 #define NUM_INVERT_TEXT 2
474 char *Joy_axis_action_text[NUM_JOY_AXIS_ACTIONS];
475 char *Joy_axis_text[NUM_AXIS_TEXT];
476 char *Mouse_button_text[NUM_MOUSE_TEXT];
477 char *Mouse_axis_text[NUM_MOUSE_AXIS_TEXT];
478 char *Invert_text[NUM_INVERT_TEXT];
480 ubyte System_keys[NUM_SYSTEM_KEYS] = {
481 KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,
482 KEY_F11, KEY_F12, KEY_PRINT_SCRN
485 int Control_check_count = 0;
487 static int Tab; // which tab we are currently in
488 static int Binding_mode = 0; // are we waiting for a key to bind it?
489 static int Bind_time = 0;
490 static int Search_mode = 0; // are we waiting for a key to bind it?
491 static int Last_key = -1;
492 static int Selected_line = 0; // line that is currently selected for binding
493 static int Selected_item = -1; // -1 = none, 0 = key, 1 = button
494 static int Scroll_offset;
495 static int Axis_override = -1;
496 static int Background_bitmap;
497 static int Conflicts_tabs[NUM_TABS];
498 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list
499 static UI_WINDOW Ui_window;
502 int key; // index of other control in conflict with this one
503 int joy; // index of other control in conflict with this one
504 } Conflicts[CCFG_MAX];
506 int Conflicts_axes[NUM_JOY_AXIS_ACTIONS];
509 static hud_anim Conflict_warning_anim;
515 #define COMPUTER_TAB 3
516 #define SCROLL_UP_BUTTON 4
517 #define SCROLL_DOWN_BUTTON 5
519 #define SHIFT_TOGGLE 7
520 #define INVERT_AXIS 8
521 #define CANCEL_BUTTON 9
522 #define UNDO_BUTTON 10
523 #define RESET_BUTTON 11
524 #define SEARCH_MODE 12
525 #define BIND_BUTTON 13
526 #define HELP_BUTTON 14
527 #define ACCEPT_BUTTON 15
528 #define CLEAR_OTHER_BUTTON 16
529 #define CLEAR_ALL_BUTTON 17
530 #define CLEAR_BUTTON 18
532 ui_button_info CC_Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
535 ui_button_info("CCB_00", 35, 30, -1, -1, 0), // target tab
536 ui_button_info("CCB_01", 121, 30, -1, -1, 1), // ship tab
537 ui_button_info("CCB_02", 176, 30, -1, -1, 2), // weapon tab
538 ui_button_info("CCB_03", 254, 30, -1, -1, 3), // computer/misc tab
539 ui_button_info("CCB_04", 598, 168, -1, -1, 4), // scroll up
540 ui_button_info("CCB_05", 598, 219, -1, -1, 5), // scroll down
541 ui_button_info("CCB_06", 13, 394, -1, -1, 6), // alt toggle
542 ui_button_info("CCB_07", 58, 394, -1, -1, 7), // shift toggle
543 ui_button_info("CCB_09", 168, 394, -1, -1, 9), // invert
544 ui_button_info("CCB_10", 456, 341, -1, -1, 10), // cancel
545 ui_button_info("CCB_11", 394, 404, -1, -1, 11), // undo
546 ui_button_info("CCB_12", 501, 26, -1, -1, 12), // default
547 ui_button_info("CCB_13", 513, 331, -1, -1, 13), // search
548 ui_button_info("CCB_14", 572, 331, -1, -1, 14), // bind
549 ui_button_info("CCB_15", 469, 429, -1, -1, 15), // help
550 ui_button_info("CCB_16", 562, 411, -1, -1, 16), // accept
551 ui_button_info("CCB_18", 223, 404, -1, -1, 18), // clear other
552 ui_button_info("CCB_19", 289, 404, -1, -1, 19), // clear all
553 ui_button_info("CCB_20", 333, 404, -1, -1, 20), // clear button
555 ui_button_info("CCB_00", 32, 348, 17, 384, 0), // target tab
556 ui_button_info("CCB_01", 101, 348, 103, 384, 1), // ship tab
557 ui_button_info("CCB_02", 173, 352, 154, 384, 2), // weapon tab
558 ui_button_info("CCB_03", 242, 347, 244, 384, 3), // computer/misc tab
559 ui_button_info("CCB_04", 614, 73, -1, -1, 4), // scroll up
560 ui_button_info("CCB_05", 614, 296, -1, -1, 5), // scroll down
561 ui_button_info("CCB_06", 17, 452, 12, 440, 6), // alt toggle
562 ui_button_info("CCB_07", 56, 452, 50, 440, 7), // shift toggle
563 ui_button_info("CCB_09", 162, 452, 155, 440, 9), // invert
564 ui_button_info("CCB_10", 404, 1, 397, 45, 10), // cancel
565 ui_button_info("CCB_11", 582, 347, 586, 386, 11), // undo
566 ui_button_info("CCB_12", 576, 1, 578, 45, 12), // default
567 ui_button_info("CCB_13", 457, 4, 453, 45, 13), // search
568 ui_button_info("CCB_14", 516, 4, 519, 45, 14), // bind
569 ui_button_info("CCB_15", 540, 428, 500, 440, 15), // help
570 ui_button_info("CCB_16", 574, 432, 571, 412, 16), // accept
571 ui_button_info("CCB_18", 420, 346, 417, 386, 18), // clear other
572 ui_button_info("CCB_19", 476, 346, 474, 386, 19), // clear all
573 ui_button_info("CCB_20", 524, 346, 529, 386, 20), // clear button
577 ui_button_info("2_CCB_00", 51, 557, 27, 615, 0), // target tab
578 ui_button_info("2_CCB_01", 162, 557, 166, 615, 1), // ship tab
579 ui_button_info("2_CCB_02", 277, 563, 246, 615, 2), // weapon tab
580 ui_button_info("2_CCB_03", 388, 555, 391, 615, 3), // computer/misc tab
581 ui_button_info("2_CCB_04", 982, 117, -1, -1, 4), // scroll up
582 ui_button_info("2_CCB_05", 982, 474, -1, -1, 5), // scroll down
583 ui_button_info("2_CCB_06", 28, 723, 24, 704, 6), // alt toggle
584 ui_button_info("2_CCB_07", 89, 723, 80, 704, 7), // shift toggle
585 ui_button_info("2_CCB_09", 260, 723, 249, 704, 9), // invert
586 ui_button_info("2_CCB_10", 646, 2, 635, 71, 10), // cancel
587 ui_button_info("2_CCB_11", 932, 555, 938, 619, 11), // undo
588 ui_button_info("2_CCB_12", 921, 1, 923, 71, 12), // default
589 ui_button_info("2_CCB_13", 732, 6, 726, 71, 13), // search
590 ui_button_info("2_CCB_14", 825, 6, 831, 71, 14), // bind
591 ui_button_info("2_CCB_15", 864, 685, 800, 704, 15), // help
592 ui_button_info("2_CCB_16", 919, 692, 914, 660, 16), // accept
593 ui_button_info("2_CCB_18", 672, 553, 668, 619, 18), // clear other
594 ui_button_info("2_CCB_19", 761, 553, 749, 619, 19), // clear all
595 ui_button_info("2_CCB_20", 838, 553, 846, 619, 20), // clear button
601 #define CC_NUM_TEXT 20
603 UI_XSTR CC_text[GR_NUM_RESOLUTIONS][CC_NUM_TEXT] = {
604 // nothing needed for FS1
606 { "Targeting", 1340, 17, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][TARGET_TAB].button },
607 { "Ship", 1341, 103, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIP_TAB].button },
608 { "Weapons", 1065, 154, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][WEAPON_TAB].button },
609 { "Misc", 1411, 244, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][COMPUTER_TAB].button },
610 { "Alt", 1510, 12, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][ALT_TOGGLE].button },
611 { "Shift", 1511, 50, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIFT_TOGGLE].button },
612 { "Invert", 1342, 155, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][INVERT_AXIS].button },
613 { "Cancel", 641, 397, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CANCEL_BUTTON].button },
614 { "Undo", 1343, 586, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][UNDO_BUTTON].button },
615 { "Defaults", 1344, 568, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][RESET_BUTTON].button },
616 { "Search", 1345, 453, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SEARCH_MODE].button },
617 { "Bind", 1346, 519, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][BIND_BUTTON].button },
618 { "Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][HELP_BUTTON].button },
619 { "Accept", 1035, 571, 412, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][ACCEPT_BUTTON].button },
620 { "Clear", 1347, 417, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
621 { "Conflict", 1348, 406, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
622 { "Clear", 1413, 474, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
623 { "All", 1349, 483, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
624 { "Clear", 1414, 529, 388, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
625 { "Selected", 1350, 517, 396, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
628 { "Targeting", 1340, 47, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][TARGET_TAB].button },
629 { "Ship", 1341, 176, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIP_TAB].button },
630 { "Weapons", 1065, 266, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][WEAPON_TAB].button },
631 { "Misc", 1411, 401, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][COMPUTER_TAB].button },
632 { "Alt", 1510, 29, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][ALT_TOGGLE].button },
633 { "Shift", 1511, 85, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIFT_TOGGLE].button },
634 { "Invert", 1342, 254, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][INVERT_AXIS].button },
635 { "Cancel", 641, 655, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CANCEL_BUTTON].button },
636 { "Undo", 1343, 938, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][UNDO_BUTTON].button },
637 { "Defaults", 1344, 923, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][RESET_BUTTON].button },
638 { "Search", 1345, 746, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SEARCH_MODE].button },
639 { "Bind", 1346, 846, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][BIND_BUTTON].button },
640 { "Help", 928, 800, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][HELP_BUTTON].button },
641 { "Accept", 1035, 914, 660, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][ACCEPT_BUTTON].button },
642 { "Clear", 1347, 683, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
643 { "Conflict", 1348, 666, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
644 { "Clear", 1413, 759, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
645 { "All", 1349, 772, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
646 { "Clear", 1414, 871, 619, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
647 { "Selected", 1350, 852, 634, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
652 // linked list head of undo items
653 config_item_undo *Config_item_undo;
655 // same indices as Scan_code_text[]. Indicates if a scancode is allowed to be bound.
656 int Config_allowed[] = {
657 0, 0, 1, 1, 1, 1, 1, 1,
658 1, 1, 1, 1, 1, 1, 1, 1,
659 1, 1, 1, 1, 1, 1, 1, 1,
660 1, 1, 1, 1, 1, 1, 1, 1,
662 1, 1, 1, 1, 1, 1, 1, 1,
663 1, 0, 1, 1, 1, 1, 1, 1,
664 1, 1, 1, 1, 1, 1, 1, 1,
665 1, 1, 1, 0, 0, 0, 0, 0,
667 0, 0, 0, 0, 0, 0, 1, 1,
668 1, 1, 1, 1, 1, 1, 1, 1,
669 1, 1, 1, 1, 0, 0, 0, 0,
670 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,
675 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, 0, 0, 0, 0,
680 0, 0, 0, 0, 1, 1, 0, 0,
682 0, 0, 0, 0, 0, 0, 0, 0,
683 0, 0, 0, 0, 0, 0, 0, 0,
684 0, 0, 0, 0, 0, 1, 0, 0,
685 1, 0, 0, 0, 0, 0, 0, 0,
687 0, 0, 0, 0, 0, 0, 0, 1,
688 1, 1, 0, 1, 0, 1, 0, 1,
689 1, 1, 1, 1, 0, 0, 0, 0,
690 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,
695 0, 0, 0, 0, 0, 0, 0, 0,
700 // old invalid demo keys
701 #define INVALID_DEMO_KEYS_MAX 14
702 int Invalid_demo_keys[] = {
713 MULTI_MESSAGE_FRIENDLY,
714 MULTI_MESSAGE_HOSTILE,
715 MULTI_MESSAGE_TARGET,
716 MULTI_OBSERVER_ZOOM_TO
719 #define INVALID_DEMO_KEYS_MAX 0
720 int Invalid_demo_keys[INVALID_DEMO_KEYS_MAX+1]; // +1 is only to prevent a 0-size array;
724 int Show_controls_info = 0;
726 DCF_BOOL(show_controls_info, Show_controls_info)
729 static int Axes_origin[JOY_NUM_AXES];
731 void control_config_detect_axis_reset()
733 joystick_read_raw_axis(JOY_NUM_AXES, Axes_origin);
736 int control_config_detect_axis()
738 int i, d, axis = -1, delta = 16384;
739 int axes_values[JOY_NUM_AXES];
741 joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
742 for (i=0; i<JOY_NUM_AXES; i++) {
743 d = abs(axes_values[i] - Axes_origin[i]);
753 int control_config_valid_action(int n)
755 #if defined(FS2_DEMO) || defined(FS1_DEMO)
758 for (i=0; i<INVALID_DEMO_KEYS_MAX; i++)
759 if (n == Invalid_demo_keys[i])
766 void control_config_conflict_check()
768 int i, j, a, b, c, shift = -1, alt = -1;
770 for (i=0; i<CCFG_MAX; i++) {
771 Conflicts[i].key = Conflicts[i].joy = -1;
772 switch (Control_config[i].key_id) {
785 for (i=0; i<NUM_TABS; i++)
786 Conflicts_tabs[i] = 0;
788 for (i=0; i<CCFG_MAX-1; i++) {
789 if (control_config_valid_action(i)) {
790 for (j=i+1; j<CCFG_MAX; j++) {
791 if (control_config_valid_action(j)) {
792 if (Control_config[i].key_id >= 0) {
794 a = Control_config[i].key_id;
795 b = Control_config[j].key_id;
797 Conflicts[i].key = j;
798 Conflicts[j].key = i;
799 Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
800 Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
803 /* if ((a >= 0) && (a & KEY_SHIFTED) && (shift >= 0)) {
804 Conflicts[i].key = shift;
805 Conflicts[shift].key = i;
806 Conflicts_tabs[ Control_config[i].tab ] = 1;
807 Conflicts_tabs[ Control_config[shift].tab ] = 1;
810 if ((b >= 0) && (b & KEY_SHIFTED) && (shift >= 0)) {
811 Conflicts[j].key = shift;
812 Conflicts[shift].key = j;
813 Conflicts_tabs[ Control_config[j].tab ] = 1;
814 Conflicts_tabs[ Control_config[shift].tab ] = 1;
817 if ((a >= 0) && (a & KEY_ALTED) && (alt >= 0)) {
818 Conflicts[i].key = alt;
819 Conflicts[alt].key = i;
820 Conflicts_tabs[ Control_config[i].tab ] = 1;
821 Conflicts_tabs[ Control_config[alt].tab ] = 1;
824 if ((b >= 0) && (b & KEY_ALTED) && (alt >= 0)) {
825 Conflicts[j].key = alt;
826 Conflicts[alt].key = j;
827 Conflicts_tabs[ Control_config[j].tab ] = 1;
828 Conflicts_tabs[ Control_config[alt].tab ] = 1;
832 if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) {
833 Conflicts[i].joy = j;
834 Conflicts[j].joy = i;
835 Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
836 Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
843 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
844 Conflicts_axes[i] = -1;
846 for (i=0; i<NUM_JOY_AXIS_ACTIONS-1; i++) {
847 for (j=i+1; j<NUM_JOY_AXIS_ACTIONS; j++) {
848 if ((Axis_map_to[i] >= 0) && (Axis_map_to[i] == Axis_map_to[j])) {
849 Conflicts_axes[i] = j;
850 Conflicts_axes[j] = i;
851 Conflicts_tabs[SHIP_TAB] = 1;
857 // do list setup required prior to rendering and checking for the controls listing. Called when list changes
858 void control_config_list_prepare()
861 int font_height = gr_get_font_height();
863 Num_cc_lines = y = z = 0;
864 while (z < CCFG_MAX) {
865 if ((Control_config[z].tab == Tab) && control_config_valid_action(z)) {
866 k = Control_config[z].key_id;
867 j = Control_config[z].joy_id;
868 Cc_lines[Num_cc_lines].label = XSTR(Control_config[z].text, CONTROL_CONFIG_XSTR + z);
869 Cc_lines[Num_cc_lines].cc_index = z;
870 Cc_lines[Num_cc_lines++].y = y;
871 y += font_height + 2;
877 if (Tab == SHIP_TAB) {
878 for (j=0; j<NUM_JOY_AXIS_ACTIONS; j++) {
879 Cc_lines[Num_cc_lines].label = Joy_axis_action_text[j];
880 Cc_lines[Num_cc_lines].cc_index = j | JOY_AXIS;
881 Cc_lines[Num_cc_lines++].y = y;
882 y += font_height + 2;
887 int cc_line_query_visible(int n)
891 if ((n < 0) || (n >= Num_cc_lines))
894 y = Cc_lines[n].y - Cc_lines[Scroll_offset].y;
895 if ((y < 0) || (y + gr_get_font_height() > Control_list_coords[gr_screen.res][CONTROL_H_COORD])){
902 // allocates the required space for one undo block and put it in the beginning of the linked list (top of a stack).
903 // Returns a pointer to this newly allocated block
904 config_item_undo *get_undo_block(int size)
906 config_item_undo *ptr;
908 ptr = (config_item_undo *) malloc( sizeof(config_item_undo) );
910 ptr->next = Config_item_undo;
911 Config_item_undo = ptr;
915 ptr->index = (int *) malloc( sizeof(int) * size );
917 ptr->list = (config_item *) malloc( sizeof(config_item) * size );
928 // frees one undo block. The first one in the list (top of the stack) to be precise.
929 void free_undo_block()
931 config_item_undo *ptr;
933 ptr = Config_item_undo;
937 Config_item_undo = ptr->next;
946 // undo the most recent binding changes
947 int control_config_undo_last()
951 if (!Config_item_undo) {
952 gamesnd_play_iface(SND_GENERAL_FAIL);
956 if (Config_item_undo->index[0] & JOY_AXIS)
959 tab = Control_config[Config_item_undo->index[0]].tab;
961 for (i=1; i<Config_item_undo->size; i++) {
962 if (Config_item_undo->index[i] & JOY_AXIS) {
967 if (Control_config[Config_item_undo->index[i]].tab != tab)
975 for (i=0; i<Config_item_undo->size; i++) {
976 z = Config_item_undo->index[i];
981 ptr = &Config_item_undo->list[i];
982 Axis_map_to[z] = ptr->joy_id;
983 Invert_axis[z] = ptr->used;
986 Control_config[z] = Config_item_undo->list[i];
991 control_config_conflict_check();
992 control_config_list_prepare();
993 gamesnd_play_iface(SND_USER_SELECT);
997 void control_config_save_axis_undo(int axis)
999 config_item_undo *ptr;
1002 item.joy_id = (short) Axis_map_to[axis];
1004 item.used = Invert_axis[axis];
1006 ptr = get_undo_block(1);
1007 ptr->index[0] = axis | JOY_AXIS;
1008 ptr->list[0] = item;
1011 void control_config_bind_key(int i, int key)
1013 config_item_undo *ptr;
1015 ptr = get_undo_block(1);
1017 ptr->list[0] = Control_config[i];
1018 Control_config[i].key_id = (short) key;
1021 void control_config_bind_joy(int i, int joy)
1023 config_item_undo *ptr;
1025 ptr = get_undo_block(1);
1027 ptr->list[0] = Control_config[i];
1028 Control_config[i].joy_id = (short) joy;
1031 void control_config_bind_axis(int i, int axis)
1033 control_config_save_axis_undo(i);
1034 Axis_map_to[i] = axis;
1037 int control_config_remove_binding()
1040 config_item_undo *ptr;
1042 if (Selected_line < 0) {
1043 gamesnd_play_iface(SND_GENERAL_FAIL);
1047 z = Cc_lines[Selected_line].cc_index;
1050 if (Axis_map_to[z] < 0) {
1051 gamesnd_play_iface(SND_GENERAL_FAIL);
1055 control_config_save_axis_undo(z);
1056 Axis_map_to[z] = -1;
1057 control_config_conflict_check();
1058 control_config_list_prepare();
1059 gamesnd_play_iface(SND_USER_SELECT);
1064 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1065 gamesnd_play_iface(SND_GENERAL_FAIL);
1070 ptr = get_undo_block(1);
1072 ptr->list[0] = Control_config[z];
1074 if (Selected_item && (Control_config[z].joy_id >= 0)) // if not just key selected (which would be 0)
1075 Control_config[z].joy_id = (short) -1;
1077 if ((Selected_item != 1) && (Control_config[z].key_id >= 0)) // if not just joy button selected (1)
1078 Control_config[z].key_id = (short) -1;
1080 control_config_conflict_check();
1081 control_config_list_prepare();
1082 gamesnd_play_iface(SND_USER_SELECT);
1087 int control_config_clear_other()
1089 int z, i, j, total = 0;
1090 config_item_undo *ptr;
1092 if (Selected_line < 0) {
1093 gamesnd_play_iface(SND_GENERAL_FAIL);
1097 z = Cc_lines[Selected_line].cc_index;
1102 if (Axis_map_to[z] < 0) {
1103 gamesnd_play_iface(SND_GENERAL_FAIL);
1107 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1108 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z))
1112 gamesnd_play_iface(SND_GENERAL_FAIL);
1116 ptr = get_undo_block(total);
1117 for (i=j=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1118 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
1119 item.joy_id = (short) Axis_map_to[i];
1120 item.used = Invert_axis[i];
1122 ptr->index[j] = i | JOY_AXIS;
1123 ptr->list[j] = item;
1126 Axis_map_to[i] = -1;
1129 control_config_conflict_check();
1130 control_config_list_prepare();
1131 gamesnd_play_iface(SND_USER_SELECT);
1135 for (i=0; i<CCFG_MAX; i++)
1136 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1141 gamesnd_play_iface(SND_GENERAL_FAIL);
1145 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1146 gamesnd_play_iface(SND_GENERAL_FAIL);
1150 // now, back up the old bindings so we can undo if we want to
1151 ptr = get_undo_block(total);
1152 for (i=j=0; i<CCFG_MAX; i++)
1153 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1156 ptr->list[j] = Control_config[i];
1159 if (Control_config[i].key_id == Control_config[z].key_id)
1160 Control_config[i].key_id = (short) -1;
1161 if (Control_config[i].joy_id == Control_config[z].joy_id)
1162 Control_config[i].joy_id = (short) -1;
1165 control_config_conflict_check();
1166 control_config_list_prepare();
1167 gamesnd_play_iface(SND_USER_SELECT);
1171 int control_config_clear_all()
1173 int i, j, total = 0;
1174 config_item_undo *ptr;
1176 // first, determine how many bindings need to be changed
1177 for (i=0; i<CCFG_MAX; i++)
1178 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0))
1182 gamesnd_play_iface(SND_GENERAL_FAIL);
1186 // now, back up the old bindings so we can undo if we want to
1187 ptr = get_undo_block(total);
1188 for (i=j=0; i<CCFG_MAX; i++) {
1189 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
1191 ptr->list[j] = Control_config[i];
1197 for (i=0; i<CCFG_MAX; i++) {
1198 Control_config[i].key_id = Control_config[i].joy_id = -1;
1201 control_config_conflict_check();
1202 control_config_list_prepare();
1203 gamesnd_play_iface(SND_RESET_PRESSED);
1207 extern Joy_info joystick;
1209 int control_config_axis_default(int axis)
1214 if (Axis_map_to_defaults[axis] < 0)
1217 if (!joystick.axis_valid[Axis_map_to_defaults[axis]])
1221 return Axis_map_to_defaults[axis];
1224 int control_config_do_reset()
1226 int i, j, total = 0;
1227 config_item_undo *ptr;
1230 // first, determine how many bindings need to be changed
1231 for (i=0; i<CCFG_MAX; i++)
1232 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default))
1235 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1236 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i]))
1240 gamesnd_play_iface(SND_GENERAL_FAIL);
1244 // now, back up the old bindings so we can undo if we want to
1245 ptr = get_undo_block(total);
1246 for (i=j=0; i<CCFG_MAX; i++) {
1247 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
1249 ptr->list[j] = Control_config[i];
1254 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1255 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
1256 item.joy_id = (short) Axis_map_to[i];
1257 item.used = Invert_axis[i];
1259 ptr->index[j] = i | JOY_AXIS;
1260 ptr->list[j] = item;
1265 control_config_reset_defaults();
1266 control_config_conflict_check();
1267 control_config_list_prepare();
1268 gamesnd_play_iface(SND_RESET_PRESSED);
1272 // This sets all the controls to their default values
1273 void control_config_reset_defaults()
1277 // Reset keyboard defaults
1278 for (i=0; i<CCFG_MAX; i++) {
1279 Control_config[i].key_id = Control_config[i].key_default;
1280 Control_config[i].joy_id = Control_config[i].joy_default;
1283 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1284 Axis_map_to[i] = control_config_axis_default(i);
1285 Invert_axis[i] = Invert_axis_defaults[i];
1289 void control_config_scroll_screen_up()
1291 if (Scroll_offset) {
1293 Assert(Selected_line > Scroll_offset);
1294 while (!cc_line_query_visible(Selected_line))
1298 gamesnd_play_iface(SND_SCROLL);
1301 gamesnd_play_iface(SND_GENERAL_FAIL);
1304 void control_config_scroll_line_up()
1306 if (Selected_line) {
1308 if (Selected_line < Scroll_offset)
1309 Scroll_offset = Selected_line;
1312 gamesnd_play_iface(SND_SCROLL);
1315 gamesnd_play_iface(SND_GENERAL_FAIL);
1318 void control_config_scroll_screen_down()
1320 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]) {
1322 while (!cc_line_query_visible(Selected_line)) {
1324 Assert(Selected_line < Num_cc_lines);
1328 gamesnd_play_iface(SND_SCROLL);
1331 gamesnd_play_iface(SND_GENERAL_FAIL);
1334 void control_config_scroll_line_down()
1336 if (Selected_line < Num_cc_lines - 1) {
1338 Assert(Selected_line > Scroll_offset);
1339 while (!cc_line_query_visible(Selected_line))
1343 gamesnd_play_iface(SND_SCROLL);
1346 gamesnd_play_iface(SND_GENERAL_FAIL);
1349 void control_config_toggle_modifier(int bit)
1353 z = Cc_lines[Selected_line].cc_index;
1354 Assert(!(z & JOY_AXIS));
1355 k = Control_config[z].key_id;
1357 gamesnd_play_iface(SND_GENERAL_FAIL);
1361 control_config_bind_key(z, k ^ bit);
1362 control_config_conflict_check();
1363 gamesnd_play_iface(SND_USER_SELECT);
1366 void control_config_toggle_invert()
1370 z = Cc_lines[Selected_line].cc_index;
1371 Assert(z & JOY_AXIS);
1373 control_config_save_axis_undo(z);
1374 Invert_axis[z] = !Invert_axis[z];
1377 void control_config_do_bind()
1382 // if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
1383 if (Selected_line < 0) {
1384 gamesnd_play_iface(SND_GENERAL_FAIL);
1388 for (i=0; i<NUM_BUTTONS; i++)
1389 if (i != CANCEL_BUTTON) {
1390 CC_Buttons[gr_screen.res][i].button.reset_status();
1391 CC_Buttons[gr_screen.res][i].button.disable();
1394 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1395 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1397 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1398 joy_down_count(i); // clear checking status of all joystick buttons
1401 control_config_detect_axis_reset();
1404 Bind_time = timer_get_milliseconds();
1408 gamesnd_play_iface(SND_USER_SELECT);
1411 void control_config_do_search()
1415 for (i=0; i<NUM_BUTTONS; i++){
1416 if (i != CANCEL_BUTTON) {
1417 CC_Buttons[gr_screen.res][i].button.reset_status();
1418 CC_Buttons[gr_screen.res][i].button.disable();
1422 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1423 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1425 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1426 joy_down_count(i); // clear checking status of all joystick buttons
1432 gamesnd_play_iface(SND_USER_SELECT);
1435 void control_config_do_cancel(int fail = 0)
1441 for (i=0; i<NUM_BUTTONS; i++){
1442 if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
1443 CC_Buttons[gr_screen.res][i].button.enable();
1447 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.reset_status();
1448 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1449 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
1450 CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
1451 CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
1453 Binding_mode = Search_mode = 0;
1455 gamesnd_play_iface(SND_GENERAL_FAIL);
1457 gamesnd_play_iface(SND_USER_SELECT);
1461 int control_config_accept()
1465 for (i=0; i<NUM_TABS; i++)
1466 if (Conflicts_tabs[i])
1470 gamesnd_play_iface(SND_GENERAL_FAIL);
1474 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1475 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1476 gamesnd_play_iface(SND_COMMIT_PRESSED);
1480 void control_config_cancel_exit()
1484 for (i=0; i<CCFG_MAX; i++)
1485 Control_config[i] = Control_config_backup[i];
1487 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1490 void control_config_button_pressed(int n)
1498 Scroll_offset = Selected_line = 0;
1499 control_config_list_prepare();
1500 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1504 control_config_do_bind();
1508 control_config_do_search();
1512 control_config_toggle_modifier(KEY_SHIFTED);
1513 gamesnd_play_iface(SND_USER_SELECT);
1517 control_config_toggle_modifier(KEY_ALTED);
1518 gamesnd_play_iface(SND_USER_SELECT);
1522 control_config_toggle_invert();
1523 gamesnd_play_iface(SND_USER_SELECT);
1526 case SCROLL_UP_BUTTON:
1527 control_config_scroll_screen_up();
1530 case SCROLL_DOWN_BUTTON:
1531 control_config_scroll_screen_down();
1535 control_config_accept();
1539 control_config_remove_binding();
1543 launch_context_help();
1544 gamesnd_play_iface(SND_HELP_PRESSED);
1548 control_config_do_reset();
1552 control_config_undo_last();
1556 control_config_do_cancel();
1559 case CLEAR_OTHER_BUTTON:
1560 control_config_clear_other();
1563 case CLEAR_ALL_BUTTON:
1564 control_config_clear_all();
1569 char *control_config_tooltip_handler(char *str)
1573 if (!stricmp(str, NOX("@conflict"))) {
1574 for (i=0; i<NUM_TABS; i++) {
1575 if (Conflicts_tabs[i])
1576 return XSTR( "Conflict!", 205);
1583 void control_config_init()
1588 // make backup of all controls
1589 for (i=0; i<CCFG_MAX; i++)
1590 Control_config_backup[i] = Control_config[i];
1592 common_set_interface_palette(NOX("ControlConfigPalette")); // set the interface palette
1593 Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1594 Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
1595 Ui_window.tooltip_handler = control_config_tooltip_handler;
1597 // load in help overlay bitmap
1598 help_overlay_load(CONTROL_CONFIG_OVERLAY);
1599 help_overlay_set_state(CONTROL_CONFIG_OVERLAY,0);
1601 // reset conflict flashing
1602 Conflict_stamp = -1;
1605 hud_anim_init(&Conflict_warning_anim, Conflict_warning_coords[gr_screen.res][0], Conflict_warning_coords[gr_screen.res][1], NOX("ConflictFlash"));
1606 hud_anim_load(&Conflict_warning_anim);
1609 for (i=0; i<NUM_BUTTONS; i++) {
1610 b = &CC_Buttons[gr_screen.res][i];
1612 if (b->hotspot < 0) { // temporary
1613 b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1); // temporary
1614 b->button.set_highlight_action(common_play_highlight_sound);
1618 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1620 // set up callback for when a mouse first goes over a button
1621 b->button.set_highlight_action(common_play_highlight_sound);
1623 b->button.set_bmaps(b->filename, 5, 1); // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1625 b->button.set_bmaps(b->filename);
1627 b->button.link_hotspot(b->hotspot);
1632 for(i=0; i<CC_NUM_TEXT; i++){
1633 Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
1637 for (i=0; i<LIST_BUTTONS_MAX; i++) {
1638 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1639 List_buttons[i].hide();
1640 List_buttons[i].disable();
1643 // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1644 CC_Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
1645 CC_Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
1646 CC_Buttons[gr_screen.res][BIND_BUTTON].button.set_hotkey(KEY_ENTER);
1647 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_DELETE);
1648 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_Z);
1649 CC_Buttons[gr_screen.res][CLEAR_BUTTON].button.set_hotkey(KEY_DELETE);
1650 CC_Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
1651 CC_Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(KEY_F1);
1652 CC_Buttons[gr_screen.res][RESET_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_R);
1653 CC_Buttons[gr_screen.res][INVERT_AXIS].button.set_hotkey(KEY_I);
1655 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1656 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
1658 Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);
1660 Scroll_offset = Selected_line = 0;
1661 Config_item_undo = NULL;
1662 control_config_conflict_check();
1665 Joy_axis_action_text[0] = strdup(XSTR("Turn (Yaw) Axis", 1016));
1666 Joy_axis_action_text[1] = strdup(XSTR("Pitch Axis", 1017));
1667 Joy_axis_action_text[2] = strdup(XSTR("Bank Axis", 1018));
1668 Joy_axis_action_text[3] = strdup(XSTR("Absolute Throttle Axis", 1019));
1669 Joy_axis_action_text[4] = strdup(XSTR("Relative Throttle Axis", 1020));
1670 Joy_axis_text[0] = strdup(XSTR("Joystick/Mouse X Axis", 1021));
1671 Joy_axis_text[1] = strdup(XSTR("Joystick/Mouse Y Axis", 1022));
1672 Joy_axis_text[2] = strdup(XSTR("Joystick Z Axis", 1023));
1673 Joy_axis_text[3] = strdup(XSTR("Joystick rX Axis", 1024));
1674 Joy_axis_text[4] = strdup(XSTR("Joystick rY Axis", 1025));
1675 Joy_axis_text[5] = strdup(XSTR("Joystick rZ Axis", 1026));
1676 Mouse_button_text[0] = strdup("");
1677 Mouse_button_text[1] = strdup(XSTR("Left Button", 1027));
1678 Mouse_button_text[2] = strdup(XSTR("Right Button", 1028));
1679 Mouse_button_text[3] = strdup(XSTR("Mid Button", 1029));
1680 Mouse_button_text[4] = strdup("");
1681 Mouse_axis_text[0] = strdup(XSTR("L/R", 1030));
1682 Mouse_axis_text[1] = strdup(XSTR("U/B", 1031));
1683 Invert_text[0] = strdup(XSTR("N", 1032));
1684 Invert_text[1] = strdup(XSTR("Y", 1033));
1686 control_config_list_prepare();
1689 void control_config_close()
1693 while (Config_item_undo){
1697 // unload the overlay bitmap
1698 help_overlay_unload(CONTROL_CONFIG_OVERLAY);
1700 if (Background_bitmap){
1701 bm_unload(Background_bitmap);
1705 hud_anim_release(&Conflict_warning_anim);
1708 Ui_window.destroy();
1709 common_free_interface_palette(); // restore game palette
1710 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1715 for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
1716 if(Joy_axis_action_text[idx] != NULL){
1717 free(Joy_axis_action_text[idx]);
1718 Joy_axis_action_text[idx] = NULL;
1721 for(idx=0; idx<NUM_AXIS_TEXT; idx++){
1722 if(Joy_axis_text[idx] != NULL){
1723 free(Joy_axis_text[idx]);
1724 Joy_axis_text[idx] = NULL;
1727 for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
1728 if(Mouse_button_text[idx] != NULL){
1729 free(Mouse_button_text[idx]);
1730 Mouse_button_text[idx] = NULL;
1733 for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
1734 if(Mouse_axis_text[idx] != NULL){
1735 free(Mouse_axis_text[idx]);
1736 Mouse_axis_text[idx] = NULL;
1739 for(idx=0; idx<NUM_INVERT_TEXT; idx++){
1740 if(Invert_text[idx] != NULL){
1741 free(Invert_text[idx]);
1742 Invert_text[idx] = NULL;
1747 void control_config_do_frame(float frametime)
1749 char buf[256], *str, *jptr;
1750 int i, j, k, w, x, y, z, len, line, conflict;
1751 int font_height = gr_get_font_height();
1752 int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
1753 static float timer = 0.0f;
1755 static int bound_timestamp = 0;
1756 static char bound_string[40];
1761 if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1764 z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1765 i = control_config_detect_axis();
1772 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1773 Ui_window.process(0);
1776 strcpy(bound_string, XSTR( "Canceled", 206));
1777 bound_timestamp = timestamp(2500);
1778 control_config_do_cancel();
1784 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1785 if (joy_down_count(i))
1789 if (Axis_override >= 0) {
1790 control_config_bind_axis(z, Axis_override);
1791 strcpy(bound_string, Joy_axis_text[Axis_override]);
1792 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1793 bound_timestamp = timestamp(2500);
1794 control_config_conflict_check();
1795 control_config_list_prepare();
1796 control_config_do_cancel();
1799 control_config_do_cancel(1);
1805 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1806 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1807 Ui_window.set_ignore_gadgets(1);
1811 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1812 Ui_window.process(0);
1814 if ( (k > 0) || B1_JUST_RELEASED ) {
1815 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1816 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1817 Ui_window.set_ignore_gadgets(0);
1822 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1823 Ui_window.set_ignore_gadgets(0);
1827 strcpy(bound_string, XSTR( "Canceled", 206));
1828 bound_timestamp = timestamp(2500);
1829 control_config_do_cancel();
1832 switch (k & KEY_MASK) {
1837 Last_key = k & KEY_MASK;
1842 if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED) // a special hack just for Mike K.
1843 if ( (Last_key >= 0) && (k <= 0) && !keyd_pressed[Last_key] )
1846 if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1847 popup(0, 1, POPUP_OK, XSTR( "That is a non-bindable key. Please try again.", 207));
1851 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1853 z = Cc_lines[Selected_line].cc_index;
1854 Assert(!(z & JOY_AXIS));
1855 control_config_bind_key(z, k);
1857 strcpy(bound_string, textify_scancode(k));
1858 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1859 bound_timestamp = timestamp(2500);
1860 control_config_conflict_check();
1861 control_config_list_prepare();
1862 control_config_do_cancel();
1865 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1866 if (joy_down_count(i)) {
1867 z = Cc_lines[Selected_line].cc_index;
1868 Assert(!(z & JOY_AXIS));
1869 control_config_bind_joy(z, i);
1871 strcpy(bound_string, Joy_button_text[i]);
1872 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1873 bound_timestamp = timestamp(2500);
1874 control_config_conflict_check();
1875 control_config_list_prepare();
1876 control_config_do_cancel();
1880 if (Bind_time + 375 < timer_get_milliseconds()) {
1881 for (i=0; i<NUM_BUTTONS; i++){
1882 if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
1887 if (i == NUM_BUTTONS) { // no buttons pressed
1888 for (i=0; i<MOUSE_NUM_BUTTONS; i++)
1889 if (mouse_down(1 << i)) {
1890 z = Cc_lines[Selected_line].cc_index;
1891 Assert(!(z & JOY_AXIS));
1892 control_config_bind_joy(z, i);
1894 strcpy(bound_string, Joy_button_text[i]);
1895 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1896 bound_timestamp = timestamp(2500);
1897 control_config_conflict_check();
1898 control_config_list_prepare();
1899 control_config_do_cancel();
1900 for (j=0; j<NUM_BUTTONS; j++){
1901 CC_Buttons[gr_screen.res][j].button.reset();
1911 } else if (Search_mode) {
1912 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1913 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1914 Ui_window.set_ignore_gadgets(1);
1918 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1919 Ui_window.process(0);
1921 if ( (k > 0) || B1_JUST_RELEASED ) {
1922 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1923 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1924 Ui_window.set_ignore_gadgets(0);
1929 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1930 Ui_window.set_ignore_gadgets(0);
1934 control_config_do_cancel();
1937 if ((k > 0) && !Config_allowed[k & KEY_MASK])
1940 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1943 for (i=0; i<CCFG_MAX; i++)
1944 if (Control_config[i].key_id == k) {
1950 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1951 if (joy_down_count(i)) {
1953 for (i=0; i<CCFG_MAX; i++)
1954 if (Control_config[i].joy_id == j) {
1962 // check if not on enabled button
1963 for (j=0; j<NUM_BUTTONS; j++){
1964 if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
1969 if (j == NUM_BUTTONS) { // no buttons pressed
1970 for (j=0; j<MOUSE_NUM_BUTTONS; j++)
1971 if (mouse_down(1 << j)) {
1972 for (i=0; i<CCFG_MAX; i++)
1973 if (Control_config[i].joy_id == j) {
1975 for (j=0; j<NUM_BUTTONS; j++){
1976 CC_Buttons[gr_screen.res][j].button.reset();
1986 Tab = Control_config[z].tab;
1987 control_config_list_prepare();
1988 Selected_line = Scroll_offset = 0;
1989 for (i=0; i<Num_cc_lines; i++)
1990 if (Cc_lines[i].cc_index == z) {
1995 while (!cc_line_query_visible(Selected_line)) {
1997 Assert(Scroll_offset < Num_cc_lines);
2003 z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
2004 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
2005 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
2006 CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
2009 z = Cc_lines[Selected_line].cc_index;
2010 k = Control_config[z].key_id;
2011 if ( (k == KEY_LALT) || (k == KEY_RALT) || (k == KEY_LSHIFT) || (k == KEY_RSHIFT) ) {
2012 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
2013 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
2017 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
2019 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2020 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
2021 Ui_window.set_ignore_gadgets(1);
2024 k = Ui_window.process();
2026 if ( (k > 0) || B1_JUST_RELEASED ) {
2027 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2028 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
2029 Ui_window.set_ignore_gadgets(0);
2034 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2035 Ui_window.set_ignore_gadgets(0);
2039 case KEY_DOWN: // select next line
2040 control_config_scroll_line_down();
2043 case KEY_UP: // select previous line
2044 control_config_scroll_line_up();
2047 case KEY_SHIFTED | KEY_TAB: // activate previous tab
2052 Scroll_offset = Selected_line = 0;
2053 control_config_list_prepare();
2054 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2057 case KEY_TAB: // activate next tab
2059 if (Tab >= NUM_TABS)
2062 Scroll_offset = Selected_line = 0;
2063 control_config_list_prepare();
2064 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2069 if (Selected_item == -2) {
2071 if (Cc_lines[Selected_line].jw < 1) {
2073 if (Cc_lines[Selected_line].kw < 1)
2078 gamesnd_play_iface(SND_SCROLL);
2083 if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1))
2085 else if (!Selected_item && (Cc_lines[Selected_line].kw < 1))
2087 else if (Selected_item > 1)
2090 gamesnd_play_iface(SND_SCROLL);
2093 case KEY_BACKSP: // undo
2094 control_config_undo_last();
2098 control_config_cancel_exit();
2103 for (i=0; i<NUM_BUTTONS; i++){
2104 if (CC_Buttons[gr_screen.res][i].button.pressed()){
2105 control_config_button_pressed(i);
2109 for (i=0; i<LIST_BUTTONS_MAX; i++) {
2110 if (List_buttons[i].is_mouse_on())
2111 select_tease_line = i + Scroll_offset;
2113 if (List_buttons[i].pressed()) {
2114 Selected_line = i + Scroll_offset;
2116 List_buttons[i].get_mouse_pos(&x, &y);
2117 if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw))
2120 if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw))
2123 gamesnd_play_iface(SND_USER_SELECT);
2126 if (List_buttons[i].double_clicked())
2127 control_config_do_bind();
2130 GR_MAYBE_CLEAR_RES(Background_bitmap);
2131 if (Background_bitmap >= 0) {
2132 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2136 // highlight tab with conflict
2138 for (i=z=0; i<NUM_TABS; i++) {
2139 if (Conflicts_tabs[i]) {
2140 CC_Buttons[gr_screen.res][i].button.draw_forced(4);
2146 // maybe switch from bright to normal
2147 if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
2148 Conflict_bright = !Conflict_bright;
2150 Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
2154 // set color and font
2156 if(Conflict_bright){
2157 gr_set_color_fast(&Color_bright_red);
2159 gr_set_color_fast(&Color_red);
2162 // setup the conflict string
2163 char conflict_str[512] = "";
2164 strncpy(conflict_str, XSTR("Conflict!", 205), 511);
2166 gr_get_string_size(&sw, &sh, conflict_str);
2168 gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str);
2172 hud_anim_render(&Conflict_warning_anim, frametime);
2175 // might as well always reset the conflict stamp
2176 Conflict_stamp = -1;
2179 for (i=0; i<NUM_TABS; i++) {
2180 if (CC_Buttons[gr_screen.res][i].button.button_down()) {
2185 if (i == NUM_TABS) {
2186 CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
2190 CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
2193 if (Selected_line >= 0) {
2194 z = Cc_lines[Selected_line].cc_index;
2196 if (Invert_axis[z & ~JOY_AXIS]) {
2197 CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
2201 z = Control_config[z].key_id;
2203 if (z & KEY_SHIFTED) {
2204 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
2206 if (z & KEY_ALTED) {
2207 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
2214 CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
2217 z = Cc_lines[Selected_line].cc_index;
2218 x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2;
2219 y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2;
2223 t = (int) (timer * 3);
2225 gr_set_color_fast(&Color_text_normal);
2226 gr_get_string_size(&w, NULL, XSTR( "?", 208));
2227 gr_printf(x - w / 2, y - font_height / 2, XSTR( "?", 208));
2230 } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
2231 i = Conflicts[z].key;
2233 i = Conflicts[z].joy;
2235 gr_set_color_fast(&Color_text_normal);
2236 str = XSTR( "Control conflicts with:", 209);
2237 gr_get_string_size(&w, NULL, str);
2238 gr_printf(x - w / 2, y - font_height, str);
2240 strcpy(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
2241 gr_force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
2242 gr_get_string_size(&w, NULL, buf);
2243 gr_printf(x - w / 2, y, buf);
2245 } else if (*bound_string) {
2246 gr_set_color_fast(&Color_text_normal);
2247 gr_get_string_size(&w, NULL, bound_string);
2248 gr_printf(x - w / 2, y - font_height / 2, bound_string);
2249 if (timestamp_elapsed(bound_timestamp))
2253 // gr_set_color_fast(&Color_text_heading);
2254 // gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]);
2256 // gr_get_string_size(&w, &h, Heading[Tab]);
2257 // y = HEADING_Y + h / 2 - 1;
2258 // gr_line(LIST_X, y, LIST_X + 18, y);
2259 // gr_line(LIST_X + w + 21, y, LIST_X + LIST_W, y);
2261 if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
2262 gr_set_color_fast(&Color_white);
2263 gr_printf(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210));
2267 line = Scroll_offset;
2268 while (cc_line_query_visible(line)) {
2269 z = Cc_lines[line].cc_index;
2270 y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
2272 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);
2273 List_buttons[line - Scroll_offset].enable(!Binding_mode);
2275 Cc_lines[line].kw = Cc_lines[line].jw = 0;
2277 if (line == Selected_line){
2278 c = &Color_text_selected;
2279 } else if (line == select_tease_line) {
2280 c = &Color_text_subselected;
2282 c = &Color_text_normal;
2285 gr_set_color_fast(c);
2286 if (Cc_lines[line].label) {
2287 strcpy(buf, Cc_lines[line].label);
2288 gr_force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
2289 gr_printf(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
2292 if (!(z & JOY_AXIS)) {
2293 k = Control_config[z].key_id;
2294 j = Control_config[z].joy_id;
2295 x = Control_list_key_x[gr_screen.res];
2299 if ((k < 0) && (j < 0)) {
2300 gr_set_color_fast(&Color_grey);
2301 gr_printf(x, y, XSTR( "None", 211));
2305 strcpy(buf, textify_scancode(k));
2306 if (Conflicts[z].key >= 0) {
2307 if (c == &Color_text_normal)
2308 gr_set_color_fast(&Color_text_error);
2310 gr_set_color_fast(&Color_text_error_hi);
2314 } else if (Selected_item == 1) {
2315 gr_set_color_fast(&Color_text_normal);
2318 gr_set_color_fast(c);
2320 gr_printf(x, y, buf);
2323 Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2324 gr_get_string_size(&w, NULL, buf);
2325 Cc_lines[line].kw = w;
2329 gr_set_color_fast(&Color_text_normal);
2330 gr_printf(x, y, XSTR( ", ", 212));
2331 gr_get_string_size(&w, NULL, XSTR( ", ", 212));
2337 strcpy(buf, Joy_button_text[j]);
2338 if (Conflicts[z].joy >= 0) {
2339 if (c == &Color_text_normal)
2340 gr_set_color_fast(&Color_text_error);
2342 gr_set_color_fast(&Color_text_error_hi);
2346 } else if (!Selected_item) {
2347 gr_set_color_fast(&Color_text_normal);
2350 gr_set_color_fast(c);
2352 gr_force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x);
2353 gr_printf(x, y, buf);
2355 Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2356 gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
2361 x = Control_list_key_x[gr_screen.res];
2362 j = Axis_map_to[z & ~JOY_AXIS];
2363 if (Binding_mode && (line == Selected_line))
2367 gr_set_color_fast(&Color_grey);
2368 gr_printf(x, y, XSTR( "None", 211));
2371 if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
2372 if (c == &Color_text_normal)
2373 gr_set_color_fast(&Color_text_error);
2376 gr_set_color_fast(&Color_text_error_hi);
2380 } else if (!Selected_item) {
2381 gr_set_color_fast(&Color_text_normal);
2384 gr_set_color_fast(c);
2386 gr_string(x, y, Joy_axis_text[j]);
2393 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
2395 i = line - Scroll_offset;
2396 while (i < LIST_BUTTONS_MAX)
2397 List_buttons[i++].disable();
2399 // blit help overlay if active
2400 help_overlay_maybe_blit(CONTROL_CONFIG_OVERLAY);
2405 void clear_key_binding(short key)
2409 for (i=0; i<CCFG_MAX; i++) {
2410 if (Control_config[i].key_id == key)
2411 Control_config[i].key_id = -1;
2415 float check_control_timef(int id)
2419 // if type isn't continuous, we shouldn't be using this function, cause it won't work.
2420 Assert(Control_config[id].type == CC_TYPE_CONTINUOUS);
2422 // first, see if control actually used (makes sure modifiers match as well)
2423 if (!check_control(id))
2426 t1 = key_down_timef(Control_config[id].key_id);
2430 t2 = joy_down_time(Control_config[id].joy_id);
2440 void control_check_indicate()
2443 if (Show_controls_info) {
2444 gr_set_color_fast(&HUD_color_debug);
2445 gr_printf(490, 15, NOX("Ctrls checked: %d"), Control_check_count);
2449 Control_check_count = 0;
2452 int check_control(int id, int key)
2455 static int last_key = 0;
2457 Control_check_count++;
2463 // if we're in multiplayer text enter (for chat) mode, check to see if we should ignore controls
2464 if ((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls()){
2468 if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
2469 if (joy_down(Control_config[id].joy_id) || joy_down_count(Control_config[id].joy_id)) {
2474 if ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS))
2475 if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2480 // check what current modifiers are pressed
2482 if (keyd_pressed[KEY_LSHIFT] || key_down_count(KEY_LSHIFT) || keyd_pressed[KEY_RSHIFT] || key_down_count(KEY_RSHIFT))
2483 mask |= KEY_SHIFTED;
2485 if (keyd_pressed[KEY_LALT] || key_down_count(KEY_LALT) || keyd_pressed[KEY_RALT] || key_down_count(KEY_RALT))
2488 z = Control_config[id].key_id;
2490 if ( (z != KEY_LALT) && (z != KEY_RALT) && (z != KEY_LSHIFT) && (z != KEY_RSHIFT) ) {
2491 // if current modifiers don't match action's modifiers, don't register control active.
2492 if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask)
2498 if (keyd_pressed[z] || key_down_count(z)) {
2499 if ( !hud_squadmsg_read_key(z) ) {
2509 if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2517 // get heading, pitch, bank, throttle abs. and throttle rel. values.
2518 void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
2520 int axes_values[JOY_NUM_AXES];
2522 joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
2524 // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
2526 if (Axis_map_to[0] >= 0)
2527 *h = joy_get_scaled_reading(axes_values[Axis_map_to[0]], Axis_map_to[0]);
2530 if (Axis_map_to[1] >= 0)
2531 *p = joy_get_scaled_reading(axes_values[Axis_map_to[1]], Axis_map_to[1]);
2534 if (Axis_map_to[2] >= 0)
2535 *b = joy_get_scaled_reading(axes_values[Axis_map_to[2]], Axis_map_to[2]);
2538 if (Axis_map_to[3] >= 0)
2539 *ta = joy_get_unscaled_reading(axes_values[Axis_map_to[3]], Axis_map_to[3]);
2542 if (Axis_map_to[4] >= 0)
2543 *tr = joy_get_scaled_reading(axes_values[Axis_map_to[4]], Axis_map_to[4]);
2559 void control_used(int id)
2561 Control_config[id].used = timestamp();
2564 void control_config_clear_used_status()
2568 for (i=0; i<CCFG_MAX; i++)
2569 Control_config[i].used = 0;
2572 void control_config_clear()
2576 // Reset keyboard defaults
2577 for (i=0; i<CCFG_MAX; i++)
2578 Control_config[i].key_id = Control_config[i].joy_id = -1;
2581 int control_config_handle_conflict()
2583 if ((Selected_item == -1) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) { // we are deleting a conflict
2584 j = Conflicts[z].joy;
2585 if ((j >= 0) && (Control_config[j].joy_id < 0))
2588 k = Conflicts[z].key;
2589 if ((k >= 0) && (Control_config[k].key_id < 0))
2592 if ((j >= 0) && (k >= 0) && (j != k)) { // deleting 2 conflicts, each in different actions
2593 ptr = get_undo_block(2);
2595 ptr->list[0] = Control_config[j];
2596 Control_config[j].joy_id = (short) -1;
2599 ptr->list[1] = Control_config[k];
2600 Control_config[k].key_id = (short) -1;
2602 } else { // only 1 action in conflict with selected action (might be both controls, though)
2608 ptr = get_undo_block(1);
2610 ptr->list[0] = Control_config[z];
2613 Control_config[z].joy_id = (short) -1;
2616 Control_config[z].key_id = (short) -1;