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.8 2006/04/26 19:37:47 taylor
19 * some text position fixes for FS1
21 * Revision 1.7 2005/03/29 02:18:47 taylor
22 * Various 64-bit platform fixes
23 * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
24 * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
25 * Streaming audio support (big thanks to Pierre Willenbrock!!)
26 * Removed dependance on strings.tbl for FS1 since we don't actually need it now
28 * Revision 1.6 2004/09/20 01:31:44 theoddone33
31 * Revision 1.5 2004/07/04 11:31:43 taylor
32 * amd64 support, compiler warning fixes, don't use software rendering
34 * Revision 1.4 2003/05/25 02:30:42 taylor
37 * Revision 1.3 2002/06/09 04:41:15 relnev
38 * added copyright header
40 * Revision 1.2 2002/05/07 03:16:43 theoddone33
41 * The Great Newline Fix
43 * Revision 1.1.1.1 2002/05/03 03:28:08 root
47 * 13 10/14/99 2:50p Jefff
50 * 12 8/16/99 9:50a Jefff
51 * fixed loading of tab bitmaps
53 * 11 8/11/99 3:21p Jefff
54 * added tab highlights on conflict
56 * 10 7/26/99 5:25p Jefff
57 * removed invalidation of key binding for demo builds
59 * 9 7/19/99 2:13p Dave
60 * Added some new strings for Heiko.
62 * 8 7/15/99 9:20a Andsager
63 * FS2_DEMO initial checkin
65 * 7 6/19/99 2:46p Dave
66 * New control config screen.
68 * 6 1/30/99 5:08p Dave
69 * More new hi-res stuff.Support for nice D3D textures.
71 * 5 1/15/99 11:29a Neilk
72 * Fixed D3D screen/texture pixel formatting problem.
74 * 4 11/05/98 4:18p Dave
75 * First run nebula support. Beefed up localization a bit. Removed all
76 * conditional compiles for foreign versions. Modified mission file
79 * 3 10/13/98 9:28a Dave
80 * Started neatening up freespace.h. Many variables renamed and
81 * reorganized. Added AlphaColors.[h,cpp]
83 * 2 10/07/98 10:52a Dave
86 * 1 10/07/98 10:48a Dave
88 * 61 8/09/98 11:55a Lawrance
89 * if GRAVIS_OEM is defined, map the throttle axis by default
91 * 60 6/19/98 3:51p Lawrance
92 * localize control text
94 * 59 6/17/98 11:04a Lawrance
95 * localize the control config strings
97 * 58 6/13/98 5:19p Hoffoss
98 * externalized control config texts.
100 * 57 6/09/98 5:15p Lawrance
101 * French/German localization
103 * 56 6/09/98 10:31a Hoffoss
104 * Created index numbers for all xstr() references. Any new xstr() stuff
105 * added from here on out should be added to the end if the list. The
106 * current list count can be found in FreeSpace.cpp (search for
109 * 55 6/01/98 11:43a John
110 * JAS & MK: Classified all strings for localization.
112 * 54 5/26/98 11:10a Lawrance
113 * Fix bug where window controls get disabled when F1 pressed twice
115 * 53 5/20/98 10:35p Hoffoss
116 * Fixed bug with mouse buttons not working when action isn't continuous.
118 * 52 5/19/98 4:08p Allender
119 * kill default binding for Z axis
121 * 51 5/19/98 12:56p Hoffoss
122 * Added some code to help prevent triple-clicking for binding the mouse
123 * button to an action (why the hell people triple click is beyond me,
126 * 50 5/19/98 11:11a Lawrance
127 * Ensure X and Y axis have defaults!
129 * 49 5/18/98 4:53p Hoffoss
130 * Some force feedback tweaks and pilot initializations there should have
131 * been happening, but weren't, and not are!
133 * 48 5/18/98 10:15a Lawrance
134 * Only do hud squad msg key check when necessary
136 * 47 5/18/98 10:08a Lawrance
137 * deal with overlap between hud squad msg number keys and CC_CONTINUOUS
140 * 46 5/17/98 5:44p Hoffoss
141 * Made throttle never bound by default (ask Sandeep why if interested).
143 * 45 5/14/98 5:32p Hoffoss
144 * Improved axis binding code some more.
146 * 44 5/13/98 7:15p Hoffoss
147 * Fixed remaining bugs with axis binding.
149 * 43 5/13/98 1:17a Hoffoss
150 * Added joystick axes configurability.
152 * 42 5/12/98 3:49p Hoffoss
153 * Fixed bug where double mouse click would bind mouse button right away.
155 * 41 5/11/98 5:43p Hoffoss
156 * Made num lock not bindable.
158 * 40 5/11/98 5:29p Hoffoss
159 * Added mouse button mapped to joystick button support.
161 * 39 5/07/98 6:25p Dave
162 * Fix strange boundary conditions which arise when players die/respawn
163 * while the game is being ended. Spiff up the chatbox doskey thing a bit.
165 * 38 5/05/98 1:48a Lawrance
166 * Add in missing help overlays
168 * 37 4/27/98 10:11a Lawrance
169 * Add in disabled beep for missing buttons
171 * 36 4/25/98 2:59p Hoffoss
172 * Fixed typo that was causing a bug.
174 * 35 4/22/98 1:51a Lawrance
175 * Take out multiplayer key from demo key config
177 * 34 4/16/98 4:29p Hoffoss
178 * Fixed bank_when_pressed functionality when using alt or shift for it.
180 * 33 4/15/98 11:06a Lawrance
181 * fix bug with a multi key showing up in demo, remove obsolete bindings
182 * from demo and full version
184 * 32 4/14/98 2:45p Hoffoss
185 * Made hitting escape to exit screen not play failed sound.
187 * 31 4/14/98 2:27p Hoffoss
188 * Made certain actions be hidden in demo build.
190 * 30 4/13/98 2:38p Hoffoss
191 * Added a tooltip handler and make binding attempts with illegal keys
194 * 29 4/11/98 7:59p Lawrance
195 * Add support for help overlays
197 * 28 4/09/98 4:12p Hoffoss
198 * Changed check_control() to automatically register a control as used if
199 * it detects it being used.
201 * 27 4/08/98 11:11a Hoffoss
202 * Fixed some bugs that showed up due to fixing other bugs the other day
205 * 26 4/07/98 3:47p Hoffoss
206 * Fixed continuous controls checking with respect to modifiers.
208 * 25 4/06/98 11:17a Hoffoss
209 * Fixed num lock/pause interplay bug.
211 * 24 4/03/98 3:51p Hoffoss
212 * Fixed some bugs, and made changed Interplay requested regarding search
215 * 23 3/31/98 4:12p Hoffoss
216 * Made control used status clear at mission init time.
218 * 22 3/23/98 11:28a Hoffoss
219 * Fixed flashing question mark bug.
221 * 21 3/21/98 11:30a John
222 * Fixed bug where joymouse caused you to stay in binding mode when
223 * binding joystick button 1 to something.
225 * 20 3/20/98 3:37p Hoffoss
226 * Tried to fix mitri's bug, failed miserably.
228 * 19 3/19/98 5:04p Dave
229 * Put in support for targeted multiplayer text and voice messaging (all,
230 * friendly, hostile, individual).
232 * 18 3/18/98 12:03p John
233 * Marked all the new strings as externalized or not.
235 * 17 3/18/98 10:16a Hoffoss
238 * 16 3/17/98 11:15a Hoffoss
239 * Made question mark that appears when you are in bind mode flash.
241 * 15 3/17/98 10:48a Hoffoss
242 * Allowed a special hack for "bank while pressed" action to use alt and
243 * shift keys standalone.
245 * 14 3/12/98 3:22p Hoffoss
246 * Fixed 2 bugs with one solution! Yay! Failed sound on bind fixed and
247 * pad enter now not translated to enter.
249 * 13 3/11/98 5:28p Hoffoss
250 * Added control config debug display info to possibly aid in tracking
253 * 12 2/26/98 10:07p Hoffoss
254 * Rewrote state saving and restoring to fix bugs and simplify the code.
256 * 11 2/22/98 12:19p John
257 * Externalized some strings
259 * 10 2/20/98 3:39p Hoffoss
260 * Updated code for new control config screen artwork.
262 * 9 2/09/98 2:50p Hoffoss
263 * Made 'none' show up as gray instead of normal color, to distinguish it
264 * from actions with bound keys.
266 * 8 2/07/98 10:04p Hoffoss
267 * Changed color and placement of "more" indicator.
269 * 7 2/05/98 10:42a Hoffoss
270 * Fixed bug where while in bind mode, you could change the line selected
271 * using the mouse, and binding would work on the new line instead.
273 * 6 2/03/98 5:05p Hoffoss
274 * Added "clear other" button to clear all conflicting bindings with
277 * 5 1/22/98 4:53p Hoffoss
278 * Made training messages/directives display a joystick button in place of
279 * a keypress if there is no keypress bound to the action.
281 * 4 1/20/98 4:20p Hoffoss
282 * Removed confusing behavior of clear button clearing out the other
283 * binding in a conflict.
285 * 3 1/08/98 12:11p Hoffoss
286 * Changed Rudder axis to Roll axis, added new function we can use to
287 * check what joystick axes are valid with.
289 * 2 12/24/97 3:37p Hoffoss
290 * Moved control config stuff to seperate library to Fred can access it as
293 * 1 12/24/97 11:58a Hoffoss
295 * 98 12/22/97 2:15p Hoffoss
296 * Fixed bug where joystick axis lines weren't being displayed.
298 * 97 12/16/97 2:44p Hoffoss
299 * Added clear button to control config screen.
301 * 96 12/12/97 3:07p Hoffoss
302 * Changed how deleting bindings work. Each control of an action can be
303 * deleted independently or both at once.
305 * 95 12/07/97 2:36p John
306 * Made warp out be Alt+J instead of J
308 * 94 12/03/97 4:59p Hoffoss
309 * Added reset sound and change control config sounds around.
311 * 93 12/03/97 4:16p Hoffoss
312 * Changed sound stuff used in interface screens for interface purposes.
318 #include "freespace.h"
319 #include "controlsconfig.h"
320 #include "gamesequence.h"
323 #include "hudsquadmsg.h"
333 #include "missionscreencommon.h"
336 #include "managepilot.h"
337 #include "multi_pmsg.h"
338 #include "contexthelp.h"
341 #include "multiutil.h"
342 #include "alphacolors.h"
344 #define NUM_SYSTEM_KEYS 14
345 #define NUM_BUTTONS 19
348 // coordinate indicies
349 #define CONTROL_X_COORD 0
350 #define CONTROL_Y_COORD 1
351 #define CONTROL_W_COORD 2
352 #define CONTROL_H_COORD 3
354 const char* Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS] = {
355 "ControlConfig", // GR_640
356 "2_ControlConfig" // GR_1024
359 const char* Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
360 "ControlConfig-m", // GR_640
361 "2_ControlConfig-m" // GR_1024
365 int Control_list_coords[GR_NUM_RESOLUTIONS][4] = {
370 32, 58, 198, 259 // GR_640
374 32, 94, 904, 424 // GR_1024
378 // width of the control name section of the list
379 int Control_list_ctrl_w[GR_NUM_RESOLUTIONS] = {
384 // x start position of the binding area section of the list
385 int Control_list_key_x[GR_NUM_RESOLUTIONS] = {
390 // width of the binding area section of the list
391 int Control_list_key_w[GR_NUM_RESOLUTIONS] = {
396 // display the "more..." text under the control list
397 int Control_more_coords[GR_NUM_RESOLUTIONS][2] = {
410 // area to display "conflicts with..." text
411 int Conflict_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
416 32, 313, 250, 32 // GR_640
420 48, 508, 354, 46 // GR_1024
424 // conflict warning anim coords
425 int Conflict_warning_coords[GR_NUM_RESOLUTIONS][2] = {
438 // for flashing the conflict text
439 #define CONFLICT_FLASH_TIME 250
440 int Conflict_stamp = -1;
441 int Conflict_bright = 0;
443 #define LIST_BUTTONS_MAX 40
444 #define JOY_AXIS 0x80000
446 static int Num_cc_lines;
449 int cc_index; // index into Control_config of item
450 int y; // Y coordinate of line
451 int kx, kw, jx, jw; // x start and width of keyboard and joystick bound text
452 } Cc_lines[CCFG_MAX];
454 // struct to hold backup config_item elements so we can undo them
455 struct config_item_undo {
457 int *index; // array (size) of Control_config indices of replaced elements
458 config_item *list; // array (size) of original elements
459 config_item_undo *next;
462 config_item Control_config_backup[CCFG_MAX];
465 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
466 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
468 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
469 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
472 // all this stuff is localized/externalized
473 #define NUM_AXIS_TEXT 6
474 #define NUM_MOUSE_TEXT 5
475 #define NUM_MOUSE_AXIS_TEXT 2
476 #define NUM_INVERT_TEXT 2
477 char *Joy_axis_action_text[NUM_JOY_AXIS_ACTIONS];
478 char *Joy_axis_text[NUM_AXIS_TEXT];
479 char *Mouse_button_text[NUM_MOUSE_TEXT];
480 char *Mouse_axis_text[NUM_MOUSE_AXIS_TEXT];
481 char *Invert_text[NUM_INVERT_TEXT];
483 ubyte System_keys[NUM_SYSTEM_KEYS] = {
484 KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,
485 KEY_F11, KEY_F12, KEY_PRINT_SCRN
488 int Control_check_count = 0;
490 static int Tab; // which tab we are currently in
491 static int Binding_mode = 0; // are we waiting for a key to bind it?
492 static int Bind_time = 0;
493 static int Search_mode = 0; // are we waiting for a key to bind it?
494 static int Last_key = -1;
495 static int Selected_line = 0; // line that is currently selected for binding
496 static int Selected_item = -1; // -1 = none, 0 = key, 1 = button
497 static int Scroll_offset;
498 static int Axis_override = -1;
499 static int Background_bitmap;
500 static int Conflicts_tabs[NUM_TABS];
501 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list
502 static UI_WINDOW Ui_window;
505 int key; // index of other control in conflict with this one
506 int joy; // index of other control in conflict with this one
507 } Conflicts[CCFG_MAX];
509 int Conflicts_axes[NUM_JOY_AXIS_ACTIONS];
512 static hud_anim Conflict_warning_anim;
518 #define COMPUTER_TAB 3
519 #define SCROLL_UP_BUTTON 4
520 #define SCROLL_DOWN_BUTTON 5
522 #define SHIFT_TOGGLE 7
523 #define INVERT_AXIS 8
524 #define CANCEL_BUTTON 9
525 #define UNDO_BUTTON 10
526 #define RESET_BUTTON 11
527 #define SEARCH_MODE 12
528 #define BIND_BUTTON 13
529 #define HELP_BUTTON 14
530 #define ACCEPT_BUTTON 15
531 #define CLEAR_OTHER_BUTTON 16
532 #define CLEAR_ALL_BUTTON 17
533 #define CLEAR_BUTTON 18
535 ui_button_info CC_Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
538 ui_button_info("CCB_00", 35, 30, -1, -1, 0), // target tab
539 ui_button_info("CCB_01", 121, 30, -1, -1, 1), // ship tab
540 ui_button_info("CCB_02", 176, 30, -1, -1, 2), // weapon tab
541 ui_button_info("CCB_03", 254, 30, -1, -1, 3), // computer/misc tab
542 ui_button_info("CCB_04", 598, 168, -1, -1, 4), // scroll up
543 ui_button_info("CCB_05", 598, 219, -1, -1, 5), // scroll down
544 ui_button_info("CCB_06", 13, 394, -1, -1, 6), // alt toggle
545 ui_button_info("CCB_07", 58, 394, -1, -1, 7), // shift toggle
546 ui_button_info("CCB_09", 168, 394, -1, -1, 9), // invert
547 ui_button_info("CCB_10", 456, 341, -1, -1, 10), // cancel
548 ui_button_info("CCB_11", 394, 404, -1, -1, 11), // undo
549 ui_button_info("CCB_12", 501, 26, -1, -1, 12), // default
550 ui_button_info("CCB_13", 513, 331, -1, -1, 13), // search
551 ui_button_info("CCB_14", 572, 331, -1, -1, 14), // bind
552 ui_button_info("CCB_15", 469, 429, -1, -1, 15), // help
553 ui_button_info("CCB_16", 562, 411, -1, -1, 16), // accept
554 ui_button_info("CCB_18", 223, 404, -1, -1, 18), // clear other
555 ui_button_info("CCB_19", 289, 404, -1, -1, 19), // clear all
556 ui_button_info("CCB_20", 333, 404, -1, -1, 20), // clear button
558 ui_button_info("CCB_00", 32, 348, 17, 384, 0), // target tab
559 ui_button_info("CCB_01", 101, 348, 103, 384, 1), // ship tab
560 ui_button_info("CCB_02", 173, 352, 154, 384, 2), // weapon tab
561 ui_button_info("CCB_03", 242, 347, 244, 384, 3), // computer/misc tab
562 ui_button_info("CCB_04", 614, 73, -1, -1, 4), // scroll up
563 ui_button_info("CCB_05", 614, 296, -1, -1, 5), // scroll down
564 ui_button_info("CCB_06", 17, 452, 12, 440, 6), // alt toggle
565 ui_button_info("CCB_07", 56, 452, 50, 440, 7), // shift toggle
566 ui_button_info("CCB_09", 162, 452, 155, 440, 9), // invert
567 ui_button_info("CCB_10", 404, 1, 397, 45, 10), // cancel
568 ui_button_info("CCB_11", 582, 347, 586, 386, 11), // undo
569 ui_button_info("CCB_12", 576, 1, 578, 45, 12), // default
570 ui_button_info("CCB_13", 457, 4, 453, 45, 13), // search
571 ui_button_info("CCB_14", 516, 4, 519, 45, 14), // bind
572 ui_button_info("CCB_15", 540, 428, 500, 440, 15), // help
573 ui_button_info("CCB_16", 574, 432, 571, 412, 16), // accept
574 ui_button_info("CCB_18", 420, 346, 417, 386, 18), // clear other
575 ui_button_info("CCB_19", 476, 346, 474, 386, 19), // clear all
576 ui_button_info("CCB_20", 524, 346, 529, 386, 20), // clear button
580 ui_button_info("2_CCB_00", 51, 557, 27, 615, 0), // target tab
581 ui_button_info("2_CCB_01", 162, 557, 166, 615, 1), // ship tab
582 ui_button_info("2_CCB_02", 277, 563, 246, 615, 2), // weapon tab
583 ui_button_info("2_CCB_03", 388, 555, 391, 615, 3), // computer/misc tab
584 ui_button_info("2_CCB_04", 982, 117, -1, -1, 4), // scroll up
585 ui_button_info("2_CCB_05", 982, 474, -1, -1, 5), // scroll down
586 ui_button_info("2_CCB_06", 28, 723, 24, 704, 6), // alt toggle
587 ui_button_info("2_CCB_07", 89, 723, 80, 704, 7), // shift toggle
588 ui_button_info("2_CCB_09", 260, 723, 249, 704, 9), // invert
589 ui_button_info("2_CCB_10", 646, 2, 635, 71, 10), // cancel
590 ui_button_info("2_CCB_11", 932, 555, 938, 619, 11), // undo
591 ui_button_info("2_CCB_12", 921, 1, 923, 71, 12), // default
592 ui_button_info("2_CCB_13", 732, 6, 726, 71, 13), // search
593 ui_button_info("2_CCB_14", 825, 6, 831, 71, 14), // bind
594 ui_button_info("2_CCB_15", 864, 685, 800, 704, 15), // help
595 ui_button_info("2_CCB_16", 919, 692, 914, 660, 16), // accept
596 ui_button_info("2_CCB_18", 672, 553, 668, 619, 18), // clear other
597 ui_button_info("2_CCB_19", 761, 553, 749, 619, 19), // clear all
598 ui_button_info("2_CCB_20", 838, 553, 846, 619, 20), // clear button
604 #define CC_NUM_TEXT 20
606 UI_XSTR CC_text[GR_NUM_RESOLUTIONS][CC_NUM_TEXT] = {
607 // nothing needed for FS1
609 { "Targeting", 1340, 17, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][TARGET_TAB].button },
610 { "Ship", 1341, 103, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIP_TAB].button },
611 { "Weapons", 1065, 154, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][WEAPON_TAB].button },
612 { "Misc", 1411, 244, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][COMPUTER_TAB].button },
613 { "Alt", 1510, 12, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][ALT_TOGGLE].button },
614 { "Shift", 1511, 50, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIFT_TOGGLE].button },
615 { "Invert", 1342, 155, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][INVERT_AXIS].button },
616 { "Cancel", 641, 397, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CANCEL_BUTTON].button },
617 { "Undo", 1343, 586, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][UNDO_BUTTON].button },
618 { "Defaults", 1344, 568, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][RESET_BUTTON].button },
619 { "Search", 1345, 453, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SEARCH_MODE].button },
620 { "Bind", 1346, 519, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][BIND_BUTTON].button },
621 { "Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][HELP_BUTTON].button },
622 { "Accept", 1035, 571, 412, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][ACCEPT_BUTTON].button },
623 { "Clear", 1347, 417, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
624 { "Conflict", 1348, 406, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
625 { "Clear", 1413, 474, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
626 { "All", 1349, 483, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
627 { "Clear", 1414, 529, 388, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
628 { "Selected", 1350, 517, 396, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
631 { "Targeting", 1340, 47, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][TARGET_TAB].button },
632 { "Ship", 1341, 176, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIP_TAB].button },
633 { "Weapons", 1065, 266, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][WEAPON_TAB].button },
634 { "Misc", 1411, 401, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][COMPUTER_TAB].button },
635 { "Alt", 1510, 29, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][ALT_TOGGLE].button },
636 { "Shift", 1511, 85, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIFT_TOGGLE].button },
637 { "Invert", 1342, 254, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][INVERT_AXIS].button },
638 { "Cancel", 641, 655, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CANCEL_BUTTON].button },
639 { "Undo", 1343, 938, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][UNDO_BUTTON].button },
640 { "Defaults", 1344, 923, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][RESET_BUTTON].button },
641 { "Search", 1345, 746, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SEARCH_MODE].button },
642 { "Bind", 1346, 846, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][BIND_BUTTON].button },
643 { "Help", 928, 800, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][HELP_BUTTON].button },
644 { "Accept", 1035, 914, 660, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][ACCEPT_BUTTON].button },
645 { "Clear", 1347, 683, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
646 { "Conflict", 1348, 666, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
647 { "Clear", 1413, 759, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
648 { "All", 1349, 772, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
649 { "Clear", 1414, 871, 619, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
650 { "Selected", 1350, 852, 634, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
655 // linked list head of undo items
656 config_item_undo *Config_item_undo;
658 // same indices as Scan_code_text[]. Indicates if a scancode is allowed to be bound.
659 int Config_allowed[] = {
660 0, 0, 1, 1, 1, 1, 1, 1,
661 1, 1, 1, 1, 1, 1, 1, 1,
662 1, 1, 1, 1, 1, 1, 1, 1,
663 1, 1, 1, 1, 1, 1, 1, 1,
665 1, 1, 1, 1, 1, 1, 1, 1,
666 1, 0, 1, 1, 1, 1, 1, 1,
667 1, 1, 1, 1, 1, 1, 1, 1,
668 1, 1, 1, 0, 0, 0, 0, 0,
670 0, 0, 0, 0, 0, 0, 1, 1,
671 1, 1, 1, 1, 1, 1, 1, 1,
672 1, 1, 1, 1, 0, 0, 0, 0,
673 0, 0, 0, 0, 0, 0, 0, 0,
675 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,
680 0, 0, 0, 0, 0, 0, 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, 1, 1, 0, 0,
685 0, 0, 0, 0, 0, 0, 0, 0,
686 0, 0, 0, 0, 0, 0, 0, 0,
687 0, 0, 0, 0, 0, 1, 0, 0,
688 1, 0, 0, 0, 0, 0, 0, 0,
690 0, 0, 0, 0, 0, 0, 0, 1,
691 1, 1, 0, 1, 0, 1, 0, 1,
692 1, 1, 1, 1, 0, 0, 0, 0,
693 0, 0, 0, 0, 0, 0, 0, 0,
695 0, 0, 0, 0, 0, 0, 0, 0,
696 0, 0, 0, 0, 0, 0, 0, 0,
697 0, 0, 0, 0, 0, 0, 0, 0,
698 0, 0, 0, 0, 0, 0, 0, 0,
703 // old invalid demo keys
704 #define INVALID_DEMO_KEYS_MAX 14
705 int Invalid_demo_keys[] = {
716 MULTI_MESSAGE_FRIENDLY,
717 MULTI_MESSAGE_HOSTILE,
718 MULTI_MESSAGE_TARGET,
719 MULTI_OBSERVER_ZOOM_TO
722 #define INVALID_DEMO_KEYS_MAX 0
723 int Invalid_demo_keys[INVALID_DEMO_KEYS_MAX+1]; // +1 is only to prevent a 0-size array;
727 int Show_controls_info = 0;
729 DCF_BOOL(show_controls_info, Show_controls_info)
732 static int Axes_origin[JOY_NUM_AXES];
734 void control_config_detect_axis_reset()
736 joystick_read_raw_axis(JOY_NUM_AXES, Axes_origin);
739 int control_config_detect_axis()
741 int i, d, axis = -1, delta = 16384;
742 int axes_values[JOY_NUM_AXES];
744 joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
745 for (i=0; i<JOY_NUM_AXES; i++) {
746 d = abs(axes_values[i] - Axes_origin[i]);
756 int control_config_valid_action(int n)
758 #if defined(FS2_DEMO) || defined(FS1_DEMO)
761 for (i=0; i<INVALID_DEMO_KEYS_MAX; i++)
762 if (n == Invalid_demo_keys[i])
769 void control_config_conflict_check()
771 int i, j, a, b, c, shift = -1, alt = -1;
773 for (i=0; i<CCFG_MAX; i++) {
774 Conflicts[i].key = Conflicts[i].joy = -1;
775 switch (Control_config[i].key_id) {
788 for (i=0; i<NUM_TABS; i++)
789 Conflicts_tabs[i] = 0;
791 for (i=0; i<CCFG_MAX-1; i++) {
792 if (control_config_valid_action(i)) {
793 for (j=i+1; j<CCFG_MAX; j++) {
794 if (control_config_valid_action(j)) {
795 if (Control_config[i].key_id >= 0) {
797 a = Control_config[i].key_id;
798 b = Control_config[j].key_id;
800 Conflicts[i].key = j;
801 Conflicts[j].key = i;
802 Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
803 Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
806 /* if ((a >= 0) && (a & KEY_SHIFTED) && (shift >= 0)) {
807 Conflicts[i].key = shift;
808 Conflicts[shift].key = i;
809 Conflicts_tabs[ Control_config[i].tab ] = 1;
810 Conflicts_tabs[ Control_config[shift].tab ] = 1;
813 if ((b >= 0) && (b & KEY_SHIFTED) && (shift >= 0)) {
814 Conflicts[j].key = shift;
815 Conflicts[shift].key = j;
816 Conflicts_tabs[ Control_config[j].tab ] = 1;
817 Conflicts_tabs[ Control_config[shift].tab ] = 1;
820 if ((a >= 0) && (a & KEY_ALTED) && (alt >= 0)) {
821 Conflicts[i].key = alt;
822 Conflicts[alt].key = i;
823 Conflicts_tabs[ Control_config[i].tab ] = 1;
824 Conflicts_tabs[ Control_config[alt].tab ] = 1;
827 if ((b >= 0) && (b & KEY_ALTED) && (alt >= 0)) {
828 Conflicts[j].key = alt;
829 Conflicts[alt].key = j;
830 Conflicts_tabs[ Control_config[j].tab ] = 1;
831 Conflicts_tabs[ Control_config[alt].tab ] = 1;
835 if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) {
836 Conflicts[i].joy = j;
837 Conflicts[j].joy = i;
838 Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
839 Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
846 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
847 Conflicts_axes[i] = -1;
849 for (i=0; i<NUM_JOY_AXIS_ACTIONS-1; i++) {
850 for (j=i+1; j<NUM_JOY_AXIS_ACTIONS; j++) {
851 if ((Axis_map_to[i] >= 0) && (Axis_map_to[i] == Axis_map_to[j])) {
852 Conflicts_axes[i] = j;
853 Conflicts_axes[j] = i;
854 Conflicts_tabs[SHIP_TAB] = 1;
860 // do list setup required prior to rendering and checking for the controls listing. Called when list changes
861 void control_config_list_prepare()
864 int font_height = gr_get_font_height();
866 Num_cc_lines = y = z = 0;
867 while (z < CCFG_MAX) {
868 if ((Control_config[z].tab == Tab) && control_config_valid_action(z)) {
869 k = Control_config[z].key_id;
870 j = Control_config[z].joy_id;
871 Cc_lines[Num_cc_lines].label = XSTR(Control_config[z].text, CONTROL_CONFIG_XSTR + z);
872 Cc_lines[Num_cc_lines].cc_index = z;
873 Cc_lines[Num_cc_lines++].y = y;
874 y += font_height + 2;
880 if (Tab == SHIP_TAB) {
881 for (j=0; j<NUM_JOY_AXIS_ACTIONS; j++) {
882 Cc_lines[Num_cc_lines].label = Joy_axis_action_text[j];
883 Cc_lines[Num_cc_lines].cc_index = j | JOY_AXIS;
884 Cc_lines[Num_cc_lines++].y = y;
885 y += font_height + 2;
890 int cc_line_query_visible(int n)
894 if ((n < 0) || (n >= Num_cc_lines))
897 y = Cc_lines[n].y - Cc_lines[Scroll_offset].y;
898 if ((y < 0) || (y + gr_get_font_height() > Control_list_coords[gr_screen.res][CONTROL_H_COORD])){
905 // allocates the required space for one undo block and put it in the beginning of the linked list (top of a stack).
906 // Returns a pointer to this newly allocated block
907 config_item_undo *get_undo_block(int size)
909 config_item_undo *ptr;
911 ptr = (config_item_undo *) malloc( sizeof(config_item_undo) );
913 ptr->next = Config_item_undo;
914 Config_item_undo = ptr;
918 ptr->index = (int *) malloc( sizeof(int) * size );
920 ptr->list = (config_item *) malloc( sizeof(config_item) * size );
931 // frees one undo block. The first one in the list (top of the stack) to be precise.
932 void free_undo_block()
934 config_item_undo *ptr;
936 ptr = Config_item_undo;
940 Config_item_undo = ptr->next;
949 // undo the most recent binding changes
950 int control_config_undo_last()
954 if (!Config_item_undo) {
955 gamesnd_play_iface(SND_GENERAL_FAIL);
959 if (Config_item_undo->index[0] & JOY_AXIS)
962 tab = Control_config[Config_item_undo->index[0]].tab;
964 for (i=1; i<Config_item_undo->size; i++) {
965 if (Config_item_undo->index[i] & JOY_AXIS) {
970 if (Control_config[Config_item_undo->index[i]].tab != tab)
978 for (i=0; i<Config_item_undo->size; i++) {
979 z = Config_item_undo->index[i];
984 ptr = &Config_item_undo->list[i];
985 Axis_map_to[z] = ptr->joy_id;
986 Invert_axis[z] = ptr->used;
989 Control_config[z] = Config_item_undo->list[i];
994 control_config_conflict_check();
995 control_config_list_prepare();
996 gamesnd_play_iface(SND_USER_SELECT);
1000 void control_config_save_axis_undo(int axis)
1002 config_item_undo *ptr;
1005 item.joy_id = (short) Axis_map_to[axis];
1007 item.used = Invert_axis[axis];
1009 ptr = get_undo_block(1);
1010 ptr->index[0] = axis | JOY_AXIS;
1011 ptr->list[0] = item;
1014 void control_config_bind_key(int i, int key)
1016 config_item_undo *ptr;
1018 ptr = get_undo_block(1);
1020 ptr->list[0] = Control_config[i];
1021 Control_config[i].key_id = (short) key;
1024 void control_config_bind_joy(int i, int joy)
1026 config_item_undo *ptr;
1028 ptr = get_undo_block(1);
1030 ptr->list[0] = Control_config[i];
1031 Control_config[i].joy_id = (short) joy;
1034 void control_config_bind_axis(int i, int axis)
1036 control_config_save_axis_undo(i);
1037 Axis_map_to[i] = axis;
1040 int control_config_remove_binding()
1043 config_item_undo *ptr;
1045 if (Selected_line < 0) {
1046 gamesnd_play_iface(SND_GENERAL_FAIL);
1050 z = Cc_lines[Selected_line].cc_index;
1053 if (Axis_map_to[z] < 0) {
1054 gamesnd_play_iface(SND_GENERAL_FAIL);
1058 control_config_save_axis_undo(z);
1059 Axis_map_to[z] = -1;
1060 control_config_conflict_check();
1061 control_config_list_prepare();
1062 gamesnd_play_iface(SND_USER_SELECT);
1067 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1068 gamesnd_play_iface(SND_GENERAL_FAIL);
1073 ptr = get_undo_block(1);
1075 ptr->list[0] = Control_config[z];
1077 if (Selected_item && (Control_config[z].joy_id >= 0)) // if not just key selected (which would be 0)
1078 Control_config[z].joy_id = (short) -1;
1080 if ((Selected_item != 1) && (Control_config[z].key_id >= 0)) // if not just joy button selected (1)
1081 Control_config[z].key_id = (short) -1;
1083 control_config_conflict_check();
1084 control_config_list_prepare();
1085 gamesnd_play_iface(SND_USER_SELECT);
1090 int control_config_clear_other()
1092 int z, i, j, total = 0;
1093 config_item_undo *ptr;
1095 if (Selected_line < 0) {
1096 gamesnd_play_iface(SND_GENERAL_FAIL);
1100 z = Cc_lines[Selected_line].cc_index;
1105 if (Axis_map_to[z] < 0) {
1106 gamesnd_play_iface(SND_GENERAL_FAIL);
1110 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1111 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z))
1115 gamesnd_play_iface(SND_GENERAL_FAIL);
1119 ptr = get_undo_block(total);
1120 for (i=j=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1121 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
1122 item.joy_id = (short) Axis_map_to[i];
1123 item.used = Invert_axis[i];
1125 ptr->index[j] = i | JOY_AXIS;
1126 ptr->list[j] = item;
1129 Axis_map_to[i] = -1;
1132 control_config_conflict_check();
1133 control_config_list_prepare();
1134 gamesnd_play_iface(SND_USER_SELECT);
1138 for (i=0; i<CCFG_MAX; i++)
1139 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1144 gamesnd_play_iface(SND_GENERAL_FAIL);
1148 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1149 gamesnd_play_iface(SND_GENERAL_FAIL);
1153 // now, back up the old bindings so we can undo if we want to
1154 ptr = get_undo_block(total);
1155 for (i=j=0; i<CCFG_MAX; i++)
1156 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1159 ptr->list[j] = Control_config[i];
1162 if (Control_config[i].key_id == Control_config[z].key_id)
1163 Control_config[i].key_id = (short) -1;
1164 if (Control_config[i].joy_id == Control_config[z].joy_id)
1165 Control_config[i].joy_id = (short) -1;
1168 control_config_conflict_check();
1169 control_config_list_prepare();
1170 gamesnd_play_iface(SND_USER_SELECT);
1174 int control_config_clear_all()
1176 int i, j, total = 0;
1177 config_item_undo *ptr;
1179 // first, determine how many bindings need to be changed
1180 for (i=0; i<CCFG_MAX; i++)
1181 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0))
1185 gamesnd_play_iface(SND_GENERAL_FAIL);
1189 // now, back up the old bindings so we can undo if we want to
1190 ptr = get_undo_block(total);
1191 for (i=j=0; i<CCFG_MAX; i++) {
1192 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
1194 ptr->list[j] = Control_config[i];
1200 for (i=0; i<CCFG_MAX; i++) {
1201 Control_config[i].key_id = Control_config[i].joy_id = -1;
1204 control_config_conflict_check();
1205 control_config_list_prepare();
1206 gamesnd_play_iface(SND_RESET_PRESSED);
1210 extern Joy_info joystick;
1212 int control_config_axis_default(int axis)
1217 if (Axis_map_to_defaults[axis] < 0)
1220 if (!joystick.axis_valid[Axis_map_to_defaults[axis]])
1224 return Axis_map_to_defaults[axis];
1227 int control_config_do_reset()
1229 int i, j, total = 0;
1230 config_item_undo *ptr;
1233 // first, determine how many bindings need to be changed
1234 for (i=0; i<CCFG_MAX; i++)
1235 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default))
1238 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1239 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i]))
1243 gamesnd_play_iface(SND_GENERAL_FAIL);
1247 // now, back up the old bindings so we can undo if we want to
1248 ptr = get_undo_block(total);
1249 for (i=j=0; i<CCFG_MAX; i++) {
1250 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
1252 ptr->list[j] = Control_config[i];
1257 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1258 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
1259 item.joy_id = (short) Axis_map_to[i];
1260 item.used = Invert_axis[i];
1262 ptr->index[j] = i | JOY_AXIS;
1263 ptr->list[j] = item;
1268 control_config_reset_defaults();
1269 control_config_conflict_check();
1270 control_config_list_prepare();
1271 gamesnd_play_iface(SND_RESET_PRESSED);
1275 // This sets all the controls to their default values
1276 void control_config_reset_defaults()
1280 // Reset keyboard defaults
1281 for (i=0; i<CCFG_MAX; i++) {
1282 Control_config[i].key_id = Control_config[i].key_default;
1283 Control_config[i].joy_id = Control_config[i].joy_default;
1286 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1287 Axis_map_to[i] = control_config_axis_default(i);
1288 Invert_axis[i] = Invert_axis_defaults[i];
1292 void control_config_scroll_screen_up()
1294 if (Scroll_offset) {
1296 Assert(Selected_line > Scroll_offset);
1297 while (!cc_line_query_visible(Selected_line))
1301 gamesnd_play_iface(SND_SCROLL);
1304 gamesnd_play_iface(SND_GENERAL_FAIL);
1307 void control_config_scroll_line_up()
1309 if (Selected_line) {
1311 if (Selected_line < Scroll_offset)
1312 Scroll_offset = Selected_line;
1315 gamesnd_play_iface(SND_SCROLL);
1318 gamesnd_play_iface(SND_GENERAL_FAIL);
1321 void control_config_scroll_screen_down()
1323 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]) {
1325 while (!cc_line_query_visible(Selected_line)) {
1327 Assert(Selected_line < Num_cc_lines);
1331 gamesnd_play_iface(SND_SCROLL);
1334 gamesnd_play_iface(SND_GENERAL_FAIL);
1337 void control_config_scroll_line_down()
1339 if (Selected_line < Num_cc_lines - 1) {
1341 Assert(Selected_line > Scroll_offset);
1342 while (!cc_line_query_visible(Selected_line))
1346 gamesnd_play_iface(SND_SCROLL);
1349 gamesnd_play_iface(SND_GENERAL_FAIL);
1352 void control_config_toggle_modifier(int bit)
1356 z = Cc_lines[Selected_line].cc_index;
1357 Assert(!(z & JOY_AXIS));
1358 k = Control_config[z].key_id;
1360 gamesnd_play_iface(SND_GENERAL_FAIL);
1364 control_config_bind_key(z, k ^ bit);
1365 control_config_conflict_check();
1366 gamesnd_play_iface(SND_USER_SELECT);
1369 void control_config_toggle_invert()
1373 z = Cc_lines[Selected_line].cc_index;
1374 Assert(z & JOY_AXIS);
1376 control_config_save_axis_undo(z);
1377 Invert_axis[z] = !Invert_axis[z];
1380 void control_config_do_bind()
1385 // if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
1386 if (Selected_line < 0) {
1387 gamesnd_play_iface(SND_GENERAL_FAIL);
1391 for (i=0; i<NUM_BUTTONS; i++)
1392 if (i != CANCEL_BUTTON) {
1393 CC_Buttons[gr_screen.res][i].button.reset_status();
1394 CC_Buttons[gr_screen.res][i].button.disable();
1397 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1398 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1400 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1401 joy_down_count(i); // clear checking status of all joystick buttons
1404 control_config_detect_axis_reset();
1407 Bind_time = timer_get_milliseconds();
1411 gamesnd_play_iface(SND_USER_SELECT);
1414 void control_config_do_search()
1418 for (i=0; i<NUM_BUTTONS; i++){
1419 if (i != CANCEL_BUTTON) {
1420 CC_Buttons[gr_screen.res][i].button.reset_status();
1421 CC_Buttons[gr_screen.res][i].button.disable();
1425 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1426 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1428 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1429 joy_down_count(i); // clear checking status of all joystick buttons
1435 gamesnd_play_iface(SND_USER_SELECT);
1438 void control_config_do_cancel(int fail = 0)
1444 for (i=0; i<NUM_BUTTONS; i++){
1445 if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
1446 CC_Buttons[gr_screen.res][i].button.enable();
1450 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.reset_status();
1451 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1452 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
1453 CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
1454 CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
1456 Binding_mode = Search_mode = 0;
1458 gamesnd_play_iface(SND_GENERAL_FAIL);
1460 gamesnd_play_iface(SND_USER_SELECT);
1464 int control_config_accept()
1468 for (i=0; i<NUM_TABS; i++)
1469 if (Conflicts_tabs[i])
1473 gamesnd_play_iface(SND_GENERAL_FAIL);
1477 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1478 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1479 gamesnd_play_iface(SND_COMMIT_PRESSED);
1483 void control_config_cancel_exit()
1487 for (i=0; i<CCFG_MAX; i++)
1488 Control_config[i] = Control_config_backup[i];
1490 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1493 void control_config_button_pressed(int n)
1501 Scroll_offset = Selected_line = 0;
1502 control_config_list_prepare();
1503 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1507 control_config_do_bind();
1511 control_config_do_search();
1515 control_config_toggle_modifier(KEY_SHIFTED);
1516 gamesnd_play_iface(SND_USER_SELECT);
1520 control_config_toggle_modifier(KEY_ALTED);
1521 gamesnd_play_iface(SND_USER_SELECT);
1525 control_config_toggle_invert();
1526 gamesnd_play_iface(SND_USER_SELECT);
1529 case SCROLL_UP_BUTTON:
1530 control_config_scroll_screen_up();
1533 case SCROLL_DOWN_BUTTON:
1534 control_config_scroll_screen_down();
1538 control_config_accept();
1542 control_config_remove_binding();
1546 launch_context_help();
1547 gamesnd_play_iface(SND_HELP_PRESSED);
1551 control_config_do_reset();
1555 control_config_undo_last();
1559 control_config_do_cancel();
1562 case CLEAR_OTHER_BUTTON:
1563 control_config_clear_other();
1566 case CLEAR_ALL_BUTTON:
1567 control_config_clear_all();
1572 const char *control_config_tooltip_handler(const char *str)
1576 if (!stricmp(str, NOX("@conflict"))) {
1577 for (i=0; i<NUM_TABS; i++) {
1578 if (Conflicts_tabs[i])
1579 return XSTR( "Conflict!", 205);
1586 void control_config_init()
1591 // make backup of all controls
1592 for (i=0; i<CCFG_MAX; i++)
1593 Control_config_backup[i] = Control_config[i];
1595 common_set_interface_palette(NOX("ControlConfigPalette")); // set the interface palette
1596 Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1597 Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
1598 Ui_window.tooltip_handler = control_config_tooltip_handler;
1600 // load in help overlay bitmap
1601 help_overlay_load(CONTROL_CONFIG_OVERLAY);
1602 help_overlay_set_state(CONTROL_CONFIG_OVERLAY,0);
1604 // reset conflict flashing
1605 Conflict_stamp = -1;
1608 hud_anim_init(&Conflict_warning_anim, Conflict_warning_coords[gr_screen.res][0], Conflict_warning_coords[gr_screen.res][1], NOX("ConflictFlash"));
1609 hud_anim_load(&Conflict_warning_anim);
1612 for (i=0; i<NUM_BUTTONS; i++) {
1613 b = &CC_Buttons[gr_screen.res][i];
1615 if (b->hotspot < 0) { // temporary
1616 b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1); // temporary
1617 b->button.set_highlight_action(common_play_highlight_sound);
1621 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1623 // set up callback for when a mouse first goes over a button
1624 b->button.set_highlight_action(common_play_highlight_sound);
1626 b->button.set_bmaps(b->filename, 5, 1); // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1628 b->button.set_bmaps(b->filename);
1630 b->button.link_hotspot(b->hotspot);
1635 for(i=0; i<CC_NUM_TEXT; i++){
1636 Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
1640 for (i=0; i<LIST_BUTTONS_MAX; i++) {
1641 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1642 List_buttons[i].hide();
1643 List_buttons[i].disable();
1646 // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1647 CC_Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
1648 CC_Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
1649 CC_Buttons[gr_screen.res][BIND_BUTTON].button.set_hotkey(KEY_ENTER);
1650 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_DELETE);
1651 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_Z);
1652 CC_Buttons[gr_screen.res][CLEAR_BUTTON].button.set_hotkey(KEY_DELETE);
1653 CC_Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
1654 CC_Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(KEY_F1);
1655 CC_Buttons[gr_screen.res][RESET_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_R);
1656 CC_Buttons[gr_screen.res][INVERT_AXIS].button.set_hotkey(KEY_I);
1658 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1659 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
1661 Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);
1663 Scroll_offset = Selected_line = 0;
1664 Config_item_undo = NULL;
1665 control_config_conflict_check();
1668 Joy_axis_action_text[0] = strdup(XSTR("Turn (Yaw) Axis", 1016));
1669 Joy_axis_action_text[1] = strdup(XSTR("Pitch Axis", 1017));
1670 Joy_axis_action_text[2] = strdup(XSTR("Bank Axis", 1018));
1671 Joy_axis_action_text[3] = strdup(XSTR("Absolute Throttle Axis", 1019));
1672 Joy_axis_action_text[4] = strdup(XSTR("Relative Throttle Axis", 1020));
1673 Joy_axis_text[0] = strdup(XSTR("Joystick/Mouse X Axis", 1021));
1674 Joy_axis_text[1] = strdup(XSTR("Joystick/Mouse Y Axis", 1022));
1675 Joy_axis_text[2] = strdup(XSTR("Joystick Z Axis", 1023));
1676 Joy_axis_text[3] = strdup(XSTR("Joystick rX Axis", 1024));
1677 Joy_axis_text[4] = strdup(XSTR("Joystick rY Axis", 1025));
1678 Joy_axis_text[5] = strdup(XSTR("Joystick rZ Axis", 1026));
1679 Mouse_button_text[0] = strdup("");
1680 Mouse_button_text[1] = strdup(XSTR("Left Button", 1027));
1681 Mouse_button_text[2] = strdup(XSTR("Right Button", 1028));
1682 Mouse_button_text[3] = strdup(XSTR("Mid Button", 1029));
1683 Mouse_button_text[4] = strdup("");
1684 Mouse_axis_text[0] = strdup(XSTR("L/R", 1030));
1685 Mouse_axis_text[1] = strdup(XSTR("U/B", 1031));
1686 Invert_text[0] = strdup(XSTR("N", 1032));
1687 Invert_text[1] = strdup(XSTR("Y", 1033));
1689 control_config_list_prepare();
1692 void control_config_close()
1696 while (Config_item_undo){
1700 // unload the overlay bitmap
1701 help_overlay_unload(CONTROL_CONFIG_OVERLAY);
1703 if (Background_bitmap){
1704 bm_unload(Background_bitmap);
1708 hud_anim_release(&Conflict_warning_anim);
1711 Ui_window.destroy();
1712 common_free_interface_palette(); // restore game palette
1713 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1718 for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
1719 if(Joy_axis_action_text[idx] != NULL){
1720 free(Joy_axis_action_text[idx]);
1721 Joy_axis_action_text[idx] = NULL;
1724 for(idx=0; idx<NUM_AXIS_TEXT; idx++){
1725 if(Joy_axis_text[idx] != NULL){
1726 free(Joy_axis_text[idx]);
1727 Joy_axis_text[idx] = NULL;
1730 for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
1731 if(Mouse_button_text[idx] != NULL){
1732 free(Mouse_button_text[idx]);
1733 Mouse_button_text[idx] = NULL;
1736 for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
1737 if(Mouse_axis_text[idx] != NULL){
1738 free(Mouse_axis_text[idx]);
1739 Mouse_axis_text[idx] = NULL;
1742 for(idx=0; idx<NUM_INVERT_TEXT; idx++){
1743 if(Invert_text[idx] != NULL){
1744 free(Invert_text[idx]);
1745 Invert_text[idx] = NULL;
1750 void control_config_do_frame(float frametime)
1752 char buf[256], *jptr;
1753 int i, j, k, w, x, y, z, len, line, conflict;
1754 int font_height = gr_get_font_height();
1755 int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
1756 static float timer = 0.0f;
1758 static int bound_timestamp = 0;
1759 static char bound_string[40];
1764 if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1767 z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1768 i = control_config_detect_axis();
1775 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1776 Ui_window.process(0);
1779 strcpy(bound_string, XSTR( "Canceled", 206));
1780 bound_timestamp = timestamp(2500);
1781 control_config_do_cancel();
1787 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1788 if (joy_down_count(i))
1792 if (Axis_override >= 0) {
1793 control_config_bind_axis(z, Axis_override);
1794 strcpy(bound_string, Joy_axis_text[Axis_override]);
1795 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1796 bound_timestamp = timestamp(2500);
1797 control_config_conflict_check();
1798 control_config_list_prepare();
1799 control_config_do_cancel();
1802 control_config_do_cancel(1);
1808 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1809 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1810 Ui_window.set_ignore_gadgets(1);
1814 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1815 Ui_window.process(0);
1817 if ( (k > 0) || B1_JUST_RELEASED ) {
1818 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1819 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1820 Ui_window.set_ignore_gadgets(0);
1825 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1826 Ui_window.set_ignore_gadgets(0);
1830 strcpy(bound_string, XSTR( "Canceled", 206));
1831 bound_timestamp = timestamp(2500);
1832 control_config_do_cancel();
1835 switch (k & KEY_MASK) {
1840 Last_key = k & KEY_MASK;
1845 if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED) // a special hack just for Mike K.
1846 if ( (Last_key >= 0) && (k <= 0) && !keyd_pressed[Last_key] )
1849 if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1850 popup(0, 1, POPUP_OK, XSTR( "That is a non-bindable key. Please try again.", 207));
1854 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1856 z = Cc_lines[Selected_line].cc_index;
1857 Assert(!(z & JOY_AXIS));
1858 control_config_bind_key(z, k);
1860 strcpy(bound_string, textify_scancode(k));
1861 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1862 bound_timestamp = timestamp(2500);
1863 control_config_conflict_check();
1864 control_config_list_prepare();
1865 control_config_do_cancel();
1868 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1869 if (joy_down_count(i)) {
1870 z = Cc_lines[Selected_line].cc_index;
1871 Assert(!(z & JOY_AXIS));
1872 control_config_bind_joy(z, i);
1874 strcpy(bound_string, Joy_button_text[i]);
1875 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1876 bound_timestamp = timestamp(2500);
1877 control_config_conflict_check();
1878 control_config_list_prepare();
1879 control_config_do_cancel();
1883 if (Bind_time + 375 < timer_get_milliseconds()) {
1884 for (i=0; i<NUM_BUTTONS; i++){
1885 if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
1890 if (i == NUM_BUTTONS) { // no buttons pressed
1891 for (i=0; i<MOUSE_NUM_BUTTONS; i++)
1892 if (mouse_down(1 << i)) {
1893 z = Cc_lines[Selected_line].cc_index;
1894 Assert(!(z & JOY_AXIS));
1895 control_config_bind_joy(z, i);
1897 strcpy(bound_string, Joy_button_text[i]);
1898 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1899 bound_timestamp = timestamp(2500);
1900 control_config_conflict_check();
1901 control_config_list_prepare();
1902 control_config_do_cancel();
1903 for (j=0; j<NUM_BUTTONS; j++){
1904 CC_Buttons[gr_screen.res][j].button.reset();
1914 } else if (Search_mode) {
1915 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1916 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1917 Ui_window.set_ignore_gadgets(1);
1921 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1922 Ui_window.process(0);
1924 if ( (k > 0) || B1_JUST_RELEASED ) {
1925 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1926 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1927 Ui_window.set_ignore_gadgets(0);
1932 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1933 Ui_window.set_ignore_gadgets(0);
1937 control_config_do_cancel();
1940 if ((k > 0) && !Config_allowed[k & KEY_MASK])
1943 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1946 for (i=0; i<CCFG_MAX; i++)
1947 if (Control_config[i].key_id == k) {
1953 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1954 if (joy_down_count(i)) {
1956 for (i=0; i<CCFG_MAX; i++)
1957 if (Control_config[i].joy_id == j) {
1965 // check if not on enabled button
1966 for (j=0; j<NUM_BUTTONS; j++){
1967 if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
1972 if (j == NUM_BUTTONS) { // no buttons pressed
1973 for (j=0; j<MOUSE_NUM_BUTTONS; j++)
1974 if (mouse_down(1 << j)) {
1975 for (i=0; i<CCFG_MAX; i++)
1976 if (Control_config[i].joy_id == j) {
1978 for (j=0; j<NUM_BUTTONS; j++){
1979 CC_Buttons[gr_screen.res][j].button.reset();
1989 Tab = Control_config[z].tab;
1990 control_config_list_prepare();
1991 Selected_line = Scroll_offset = 0;
1992 for (i=0; i<Num_cc_lines; i++)
1993 if (Cc_lines[i].cc_index == z) {
1998 while (!cc_line_query_visible(Selected_line)) {
2000 Assert(Scroll_offset < Num_cc_lines);
2006 z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
2007 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
2008 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
2009 CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
2012 z = Cc_lines[Selected_line].cc_index;
2013 k = Control_config[z].key_id;
2014 if ( (k == KEY_LALT) || (k == KEY_RALT) || (k == KEY_LSHIFT) || (k == KEY_RSHIFT) ) {
2015 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
2016 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
2020 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
2022 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2023 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
2024 Ui_window.set_ignore_gadgets(1);
2027 k = Ui_window.process();
2029 if ( (k > 0) || B1_JUST_RELEASED ) {
2030 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2031 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
2032 Ui_window.set_ignore_gadgets(0);
2037 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2038 Ui_window.set_ignore_gadgets(0);
2042 case KEY_DOWN: // select next line
2043 control_config_scroll_line_down();
2046 case KEY_UP: // select previous line
2047 control_config_scroll_line_up();
2050 case KEY_SHIFTED | KEY_TAB: // activate previous tab
2055 Scroll_offset = Selected_line = 0;
2056 control_config_list_prepare();
2057 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2060 case KEY_TAB: // activate next tab
2062 if (Tab >= NUM_TABS)
2065 Scroll_offset = Selected_line = 0;
2066 control_config_list_prepare();
2067 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2072 if (Selected_item == -2) {
2074 if (Cc_lines[Selected_line].jw < 1) {
2076 if (Cc_lines[Selected_line].kw < 1)
2081 gamesnd_play_iface(SND_SCROLL);
2086 if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1))
2088 else if (!Selected_item && (Cc_lines[Selected_line].kw < 1))
2090 else if (Selected_item > 1)
2093 gamesnd_play_iface(SND_SCROLL);
2096 case KEY_BACKSP: // undo
2097 control_config_undo_last();
2101 control_config_cancel_exit();
2106 for (i=0; i<NUM_BUTTONS; i++){
2107 if (CC_Buttons[gr_screen.res][i].button.pressed()){
2108 control_config_button_pressed(i);
2112 for (i=0; i<LIST_BUTTONS_MAX; i++) {
2113 if (List_buttons[i].is_mouse_on())
2114 select_tease_line = i + Scroll_offset;
2116 if (List_buttons[i].pressed()) {
2117 Selected_line = i + Scroll_offset;
2119 List_buttons[i].get_mouse_pos(&x, &y);
2120 if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw))
2123 if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw))
2126 gamesnd_play_iface(SND_USER_SELECT);
2129 if (List_buttons[i].double_clicked())
2130 control_config_do_bind();
2133 GR_MAYBE_CLEAR_RES(Background_bitmap);
2134 if (Background_bitmap >= 0) {
2135 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2139 // highlight tab with conflict
2141 for (i=z=0; i<NUM_TABS; i++) {
2142 if (Conflicts_tabs[i]) {
2143 CC_Buttons[gr_screen.res][i].button.draw_forced(4);
2149 // maybe switch from bright to normal
2150 if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
2151 Conflict_bright = !Conflict_bright;
2153 Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
2157 // set color and font
2159 if(Conflict_bright){
2160 gr_set_color_fast(&Color_bright_red);
2162 gr_set_color_fast(&Color_red);
2165 // setup the conflict string
2166 char conflict_str[512] = "";
2167 strncpy(conflict_str, XSTR("Conflict!", 205), 511);
2169 gr_get_string_size(&sw, &sh, conflict_str);
2171 gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str);
2175 hud_anim_render(&Conflict_warning_anim, frametime);
2178 // might as well always reset the conflict stamp
2179 Conflict_stamp = -1;
2182 for (i=0; i<NUM_TABS; i++) {
2183 if (CC_Buttons[gr_screen.res][i].button.button_down()) {
2188 if (i == NUM_TABS) {
2189 CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
2193 CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
2196 if (Selected_line >= 0) {
2197 z = Cc_lines[Selected_line].cc_index;
2199 if (Invert_axis[z & ~JOY_AXIS]) {
2200 CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
2204 z = Control_config[z].key_id;
2206 if (z & KEY_SHIFTED) {
2207 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
2209 if (z & KEY_ALTED) {
2210 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
2217 CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
2220 z = Cc_lines[Selected_line].cc_index;
2221 x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2;
2222 y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2;
2226 t = (int) (timer * 3);
2228 gr_set_color_fast(&Color_text_normal);
2229 gr_get_string_size(&w, NULL, XSTR( "?", 208));
2230 gr_printf(x - w / 2, y - font_height / 2, XSTR( "?", 208));
2233 } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
2234 i = Conflicts[z].key;
2236 i = Conflicts[z].joy;
2238 gr_set_color_fast(&Color_text_normal);
2239 const char *str = XSTR( "Control conflicts with:", 209);
2240 gr_get_string_size(&w, NULL, str);
2241 gr_printf(x - w / 2, y - font_height, str);
2243 strcpy(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
2244 gr_force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
2245 gr_get_string_size(&w, NULL, buf);
2246 gr_printf(x - w / 2, y, buf);
2248 } else if (*bound_string) {
2249 gr_set_color_fast(&Color_text_normal);
2250 gr_get_string_size(&w, NULL, bound_string);
2251 gr_printf(x - w / 2, y - font_height / 2, bound_string);
2252 if (timestamp_elapsed(bound_timestamp))
2256 // gr_set_color_fast(&Color_text_heading);
2257 // gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]);
2259 // gr_get_string_size(&w, &h, Heading[Tab]);
2260 // y = HEADING_Y + h / 2 - 1;
2261 // gr_line(LIST_X, y, LIST_X + 18, y);
2262 // gr_line(LIST_X + w + 21, y, LIST_X + LIST_W, y);
2264 if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
2265 gr_set_color_fast(&Color_white);
2266 gr_printf(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210));
2270 line = Scroll_offset;
2271 while (cc_line_query_visible(line)) {
2272 z = Cc_lines[line].cc_index;
2273 y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
2275 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);
2276 List_buttons[line - Scroll_offset].enable(!Binding_mode);
2278 Cc_lines[line].kw = Cc_lines[line].jw = 0;
2280 if (line == Selected_line){
2281 c = &Color_text_selected;
2282 } else if (line == select_tease_line) {
2283 c = &Color_text_subselected;
2285 c = &Color_text_normal;
2288 gr_set_color_fast(c);
2289 if (Cc_lines[line].label) {
2290 strcpy(buf, Cc_lines[line].label);
2291 gr_force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
2292 gr_printf(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
2295 if (!(z & JOY_AXIS)) {
2296 k = Control_config[z].key_id;
2297 j = Control_config[z].joy_id;
2298 x = Control_list_key_x[gr_screen.res];
2302 if ((k < 0) && (j < 0)) {
2303 gr_set_color_fast(&Color_grey);
2304 gr_printf(x, y, XSTR( "None", 211));
2308 strcpy(buf, textify_scancode(k));
2309 if (Conflicts[z].key >= 0) {
2310 if (c == &Color_text_normal)
2311 gr_set_color_fast(&Color_text_error);
2313 gr_set_color_fast(&Color_text_error_hi);
2317 } else if (Selected_item == 1) {
2318 gr_set_color_fast(&Color_text_normal);
2321 gr_set_color_fast(c);
2323 gr_printf(x, y, buf);
2326 Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2327 gr_get_string_size(&w, NULL, buf);
2328 Cc_lines[line].kw = w;
2332 gr_set_color_fast(&Color_text_normal);
2333 gr_printf(x, y, XSTR( ", ", 212));
2334 gr_get_string_size(&w, NULL, XSTR( ", ", 212));
2340 strcpy(buf, Joy_button_text[j]);
2341 if (Conflicts[z].joy >= 0) {
2342 if (c == &Color_text_normal)
2343 gr_set_color_fast(&Color_text_error);
2345 gr_set_color_fast(&Color_text_error_hi);
2349 } else if (!Selected_item) {
2350 gr_set_color_fast(&Color_text_normal);
2353 gr_set_color_fast(c);
2355 gr_force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x);
2356 gr_printf(x, y, buf);
2358 Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2359 gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
2364 x = Control_list_key_x[gr_screen.res];
2365 j = Axis_map_to[z & ~JOY_AXIS];
2366 if (Binding_mode && (line == Selected_line))
2370 gr_set_color_fast(&Color_grey);
2371 gr_printf(x, y, XSTR( "None", 211));
2374 if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
2375 if (c == &Color_text_normal)
2376 gr_set_color_fast(&Color_text_error);
2379 gr_set_color_fast(&Color_text_error_hi);
2383 } else if (!Selected_item) {
2384 gr_set_color_fast(&Color_text_normal);
2387 gr_set_color_fast(c);
2389 gr_string(x, y, Joy_axis_text[j]);
2396 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
2398 i = line - Scroll_offset;
2399 while (i < LIST_BUTTONS_MAX)
2400 List_buttons[i++].disable();
2402 // blit help overlay if active
2403 help_overlay_maybe_blit(CONTROL_CONFIG_OVERLAY);
2408 void clear_key_binding(short key)
2412 for (i=0; i<CCFG_MAX; i++) {
2413 if (Control_config[i].key_id == key)
2414 Control_config[i].key_id = -1;
2418 float check_control_timef(int id)
2422 // if type isn't continuous, we shouldn't be using this function, cause it won't work.
2423 Assert(Control_config[id].type == CC_TYPE_CONTINUOUS);
2425 // first, see if control actually used (makes sure modifiers match as well)
2426 if (!check_control(id))
2429 t1 = key_down_timef(Control_config[id].key_id);
2433 t2 = joy_down_time(Control_config[id].joy_id);
2443 void control_check_indicate()
2446 if (Show_controls_info) {
2447 gr_set_color_fast(&HUD_color_debug);
2448 gr_printf(490, 15, NOX("Ctrls checked: %d"), Control_check_count);
2452 Control_check_count = 0;
2455 int check_control(int id, int key)
2458 static int last_key = 0;
2460 Control_check_count++;
2466 // if we're in multiplayer text enter (for chat) mode, check to see if we should ignore controls
2467 if ((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls()){
2471 if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
2472 if (joy_down(Control_config[id].joy_id) || joy_down_count(Control_config[id].joy_id)) {
2477 if ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS))
2478 if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2483 // check what current modifiers are pressed
2485 if (keyd_pressed[KEY_LSHIFT] || key_down_count(KEY_LSHIFT) || keyd_pressed[KEY_RSHIFT] || key_down_count(KEY_RSHIFT))
2486 mask |= KEY_SHIFTED;
2488 if (keyd_pressed[KEY_LALT] || key_down_count(KEY_LALT) || keyd_pressed[KEY_RALT] || key_down_count(KEY_RALT))
2491 z = Control_config[id].key_id;
2493 if ( (z != KEY_LALT) && (z != KEY_RALT) && (z != KEY_LSHIFT) && (z != KEY_RSHIFT) ) {
2494 // if current modifiers don't match action's modifiers, don't register control active.
2495 if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask)
2501 if (keyd_pressed[z] || key_down_count(z)) {
2502 if ( !hud_squadmsg_read_key(z) ) {
2512 if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2520 // get heading, pitch, bank, throttle abs. and throttle rel. values.
2521 void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
2523 int axes_values[JOY_NUM_AXES];
2525 joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
2527 // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
2529 if (Axis_map_to[0] >= 0)
2530 *h = joy_get_scaled_reading(axes_values[Axis_map_to[0]], Axis_map_to[0]);
2533 if (Axis_map_to[1] >= 0)
2534 *p = joy_get_scaled_reading(axes_values[Axis_map_to[1]], Axis_map_to[1]);
2537 if (Axis_map_to[2] >= 0)
2538 *b = joy_get_scaled_reading(axes_values[Axis_map_to[2]], Axis_map_to[2]);
2541 if (Axis_map_to[3] >= 0)
2542 *ta = joy_get_unscaled_reading(axes_values[Axis_map_to[3]], Axis_map_to[3]);
2545 if (Axis_map_to[4] >= 0)
2546 *tr = joy_get_scaled_reading(axes_values[Axis_map_to[4]], Axis_map_to[4]);
2562 void control_used(int id)
2564 Control_config[id].used = timestamp();
2567 void control_config_clear_used_status()
2571 for (i=0; i<CCFG_MAX; i++)
2572 Control_config[i].used = 0;
2575 void control_config_clear()
2579 // Reset keyboard defaults
2580 for (i=0; i<CCFG_MAX; i++)
2581 Control_config[i].key_id = Control_config[i].joy_id = -1;
2584 int control_config_handle_conflict()
2586 if ((Selected_item == -1) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) { // we are deleting a conflict
2587 j = Conflicts[z].joy;
2588 if ((j >= 0) && (Control_config[j].joy_id < 0))
2591 k = Conflicts[z].key;
2592 if ((k >= 0) && (Control_config[k].key_id < 0))
2595 if ((j >= 0) && (k >= 0) && (j != k)) { // deleting 2 conflicts, each in different actions
2596 ptr = get_undo_block(2);
2598 ptr->list[0] = Control_config[j];
2599 Control_config[j].joy_id = (short) -1;
2602 ptr->list[1] = Control_config[k];
2603 Control_config[k].key_id = (short) -1;
2605 } else { // only 1 action in conflict with selected action (might be both controls, though)
2611 ptr = get_undo_block(1);
2613 ptr->list[0] = Control_config[z];
2616 Control_config[z].joy_id = (short) -1;
2619 Control_config[z].key_id = (short) -1;