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 // SDLK_ESCAPE, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10,
485 // SDLK_F11, SDLK_F12, SDLK_PRINTSCREEN
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()
773 // int shift = -1, alt = -1;
775 for (i=0; i<CCFG_MAX; i++) {
776 Conflicts[i].key = Conflicts[i].joy = -1;
778 switch (Control_config[i].key_id) {
793 for (i=0; i<NUM_TABS; i++)
794 Conflicts_tabs[i] = 0;
796 for (i=0; i<CCFG_MAX-1; i++) {
797 if (control_config_valid_action(i)) {
798 for (j=i+1; j<CCFG_MAX; j++) {
799 if (control_config_valid_action(j)) {
800 if (Control_config[i].key_id >= 0) {
801 a = Control_config[i].key_id;
802 b = Control_config[j].key_id;
804 Conflicts[i].key = j;
805 Conflicts[j].key = i;
806 Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
807 Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
810 /* if ((a >= 0) && (a & KEY_SHIFTED) && (shift >= 0)) {
811 Conflicts[i].key = shift;
812 Conflicts[shift].key = i;
813 Conflicts_tabs[ Control_config[i].tab ] = 1;
814 Conflicts_tabs[ Control_config[shift].tab ] = 1;
817 if ((b >= 0) && (b & KEY_SHIFTED) && (shift >= 0)) {
818 Conflicts[j].key = shift;
819 Conflicts[shift].key = j;
820 Conflicts_tabs[ Control_config[j].tab ] = 1;
821 Conflicts_tabs[ Control_config[shift].tab ] = 1;
824 if ((a >= 0) && (a & KEY_ALTED) && (alt >= 0)) {
825 Conflicts[i].key = alt;
826 Conflicts[alt].key = i;
827 Conflicts_tabs[ Control_config[i].tab ] = 1;
828 Conflicts_tabs[ Control_config[alt].tab ] = 1;
831 if ((b >= 0) && (b & KEY_ALTED) && (alt >= 0)) {
832 Conflicts[j].key = alt;
833 Conflicts[alt].key = j;
834 Conflicts_tabs[ Control_config[j].tab ] = 1;
835 Conflicts_tabs[ Control_config[alt].tab ] = 1;
839 if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) {
840 Conflicts[i].joy = j;
841 Conflicts[j].joy = i;
842 Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
843 Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
850 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
851 Conflicts_axes[i] = -1;
853 for (i=0; i<NUM_JOY_AXIS_ACTIONS-1; i++) {
854 for (j=i+1; j<NUM_JOY_AXIS_ACTIONS; j++) {
855 if ((Axis_map_to[i] >= 0) && (Axis_map_to[i] == Axis_map_to[j])) {
856 Conflicts_axes[i] = j;
857 Conflicts_axes[j] = i;
858 Conflicts_tabs[SHIP_TAB] = 1;
864 // do list setup required prior to rendering and checking for the controls listing. Called when list changes
865 void control_config_list_prepare()
868 int font_height = gr_get_font_height();
870 Num_cc_lines = y = z = 0;
871 while (z < CCFG_MAX) {
872 if ((Control_config[z].tab == Tab) && control_config_valid_action(z)) {
873 Cc_lines[Num_cc_lines].label = XSTR(Control_config[z].text, CONTROL_CONFIG_XSTR + z);
874 Cc_lines[Num_cc_lines].cc_index = z;
875 Cc_lines[Num_cc_lines++].y = y;
876 y += font_height + 2;
882 if (Tab == SHIP_TAB) {
883 for (j=0; j<NUM_JOY_AXIS_ACTIONS; j++) {
884 Cc_lines[Num_cc_lines].label = Joy_axis_action_text[j];
885 Cc_lines[Num_cc_lines].cc_index = j | JOY_AXIS;
886 Cc_lines[Num_cc_lines++].y = y;
887 y += font_height + 2;
892 int cc_line_query_visible(int n)
896 if ((n < 0) || (n >= Num_cc_lines))
899 y = Cc_lines[n].y - Cc_lines[Scroll_offset].y;
900 if ((y < 0) || (y + gr_get_font_height() > Control_list_coords[gr_screen.res][CONTROL_H_COORD])){
907 // allocates the required space for one undo block and put it in the beginning of the linked list (top of a stack).
908 // Returns a pointer to this newly allocated block
909 config_item_undo *get_undo_block(int size)
911 config_item_undo *ptr;
913 ptr = (config_item_undo *) malloc( sizeof(config_item_undo) );
915 ptr->next = Config_item_undo;
916 Config_item_undo = ptr;
920 ptr->index = (int *) malloc( sizeof(int) * size );
921 SDL_assert(ptr->index);
922 ptr->list = (config_item *) malloc( sizeof(config_item) * size );
923 SDL_assert(ptr->list);
933 // frees one undo block. The first one in the list (top of the stack) to be precise.
934 void free_undo_block()
936 config_item_undo *ptr;
938 ptr = Config_item_undo;
942 Config_item_undo = ptr->next;
951 // undo the most recent binding changes
952 int control_config_undo_last()
956 if (!Config_item_undo) {
957 gamesnd_play_iface(SND_GENERAL_FAIL);
961 if (Config_item_undo->index[0] & JOY_AXIS)
964 tab = Control_config[Config_item_undo->index[0]].tab;
966 for (i=1; i<Config_item_undo->size; i++) {
967 if (Config_item_undo->index[i] & JOY_AXIS) {
972 if (Control_config[Config_item_undo->index[i]].tab != tab)
980 for (i=0; i<Config_item_undo->size; i++) {
981 z = Config_item_undo->index[i];
986 ptr = &Config_item_undo->list[i];
987 Axis_map_to[z] = ptr->joy_id;
988 Invert_axis[z] = ptr->used;
991 Control_config[z] = Config_item_undo->list[i];
996 control_config_conflict_check();
997 control_config_list_prepare();
998 gamesnd_play_iface(SND_USER_SELECT);
1002 void control_config_save_axis_undo(int axis)
1004 config_item_undo *ptr;
1007 item.joy_id = (short) Axis_map_to[axis];
1009 item.used = Invert_axis[axis];
1011 ptr = get_undo_block(1);
1012 ptr->index[0] = axis | JOY_AXIS;
1013 ptr->list[0] = item;
1016 void control_config_bind_key(int i, int key)
1018 config_item_undo *ptr;
1020 ptr = get_undo_block(1);
1022 ptr->list[0] = Control_config[i];
1023 Control_config[i].key_id = key;
1026 void control_config_bind_joy(int i, int joy)
1028 config_item_undo *ptr;
1030 ptr = get_undo_block(1);
1032 ptr->list[0] = Control_config[i];
1033 Control_config[i].joy_id = (short) joy;
1036 void control_config_bind_axis(int i, int axis)
1038 control_config_save_axis_undo(i);
1039 Axis_map_to[i] = axis;
1042 int control_config_remove_binding()
1045 config_item_undo *ptr;
1047 if (Selected_line < 0) {
1048 gamesnd_play_iface(SND_GENERAL_FAIL);
1052 z = Cc_lines[Selected_line].cc_index;
1055 if (Axis_map_to[z] < 0) {
1056 gamesnd_play_iface(SND_GENERAL_FAIL);
1060 control_config_save_axis_undo(z);
1061 Axis_map_to[z] = -1;
1062 control_config_conflict_check();
1063 control_config_list_prepare();
1064 gamesnd_play_iface(SND_USER_SELECT);
1069 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1070 gamesnd_play_iface(SND_GENERAL_FAIL);
1074 ptr = get_undo_block(1);
1076 ptr->list[0] = Control_config[z];
1078 if (Selected_item && (Control_config[z].joy_id >= 0)) // if not just key selected (which would be 0)
1079 Control_config[z].joy_id = (short) -1;
1081 if ((Selected_item != 1) && (Control_config[z].key_id >= 0)) // if not just joy button selected (1)
1082 Control_config[z].key_id = -1;
1084 control_config_conflict_check();
1085 control_config_list_prepare();
1086 gamesnd_play_iface(SND_USER_SELECT);
1091 int control_config_clear_other()
1093 int z, i, j, total = 0;
1094 config_item_undo *ptr;
1096 if (Selected_line < 0) {
1097 gamesnd_play_iface(SND_GENERAL_FAIL);
1101 z = Cc_lines[Selected_line].cc_index;
1106 if (Axis_map_to[z] < 0) {
1107 gamesnd_play_iface(SND_GENERAL_FAIL);
1111 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1112 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z))
1116 gamesnd_play_iface(SND_GENERAL_FAIL);
1120 ptr = get_undo_block(total);
1121 for (i=j=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1122 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
1123 item.joy_id = (short) Axis_map_to[i];
1124 item.used = Invert_axis[i];
1126 ptr->index[j] = i | JOY_AXIS;
1127 ptr->list[j] = item;
1130 Axis_map_to[i] = -1;
1133 control_config_conflict_check();
1134 control_config_list_prepare();
1135 gamesnd_play_iface(SND_USER_SELECT);
1139 for (i=0; i<CCFG_MAX; i++)
1140 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1145 gamesnd_play_iface(SND_GENERAL_FAIL);
1149 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1150 gamesnd_play_iface(SND_GENERAL_FAIL);
1154 // now, back up the old bindings so we can undo if we want to
1155 ptr = get_undo_block(total);
1156 for (i=j=0; i<CCFG_MAX; i++)
1157 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1160 ptr->list[j] = Control_config[i];
1163 if (Control_config[i].key_id == Control_config[z].key_id)
1164 Control_config[i].key_id = -1;
1165 if (Control_config[i].joy_id == Control_config[z].joy_id)
1166 Control_config[i].joy_id = (short) -1;
1169 control_config_conflict_check();
1170 control_config_list_prepare();
1171 gamesnd_play_iface(SND_USER_SELECT);
1175 int control_config_clear_all()
1177 int i, j, total = 0;
1178 config_item_undo *ptr;
1180 // first, determine how many bindings need to be changed
1181 for (i=0; i<CCFG_MAX; i++)
1182 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0))
1186 gamesnd_play_iface(SND_GENERAL_FAIL);
1190 // now, back up the old bindings so we can undo if we want to
1191 ptr = get_undo_block(total);
1192 for (i=j=0; i<CCFG_MAX; i++) {
1193 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
1195 ptr->list[j] = Control_config[i];
1200 SDL_assert(j == total);
1201 for (i=0; i<CCFG_MAX; i++) {
1202 Control_config[i].key_id = Control_config[i].joy_id = -1;
1205 control_config_conflict_check();
1206 control_config_list_prepare();
1207 gamesnd_play_iface(SND_RESET_PRESSED);
1211 int control_config_axis_default(int axis)
1213 SDL_assert(axis >= 0);
1216 if (Axis_map_to_defaults[axis] < 0)
1219 if ( !joy_axis_valid(Axis_map_to_defaults[axis]) )
1223 return Axis_map_to_defaults[axis];
1226 int control_config_do_reset()
1228 int i, j, total = 0;
1229 config_item_undo *ptr;
1232 // first, determine how many bindings need to be changed
1233 for (i=0; i<CCFG_MAX; i++)
1234 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default))
1237 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1238 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i]))
1242 gamesnd_play_iface(SND_GENERAL_FAIL);
1246 // now, back up the old bindings so we can undo if we want to
1247 ptr = get_undo_block(total);
1248 for (i=j=0; i<CCFG_MAX; i++) {
1249 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
1251 ptr->list[j] = Control_config[i];
1256 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1257 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
1258 item.joy_id = (short) Axis_map_to[i];
1259 item.used = Invert_axis[i];
1261 ptr->index[j] = i | JOY_AXIS;
1262 ptr->list[j] = item;
1266 SDL_assert(j == total);
1267 control_config_reset_defaults();
1268 control_config_conflict_check();
1269 control_config_list_prepare();
1270 gamesnd_play_iface(SND_RESET_PRESSED);
1274 // This sets all the controls to their default values
1275 void control_config_reset_defaults()
1279 // Reset keyboard defaults
1280 for (i=0; i<CCFG_MAX; i++) {
1281 Control_config[i].key_id = Control_config[i].key_default;
1282 Control_config[i].joy_id = Control_config[i].joy_default;
1285 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1286 Axis_map_to[i] = control_config_axis_default(i);
1287 Invert_axis[i] = Invert_axis_defaults[i];
1291 void control_config_scroll_screen_up()
1293 if (Scroll_offset) {
1295 SDL_assert(Selected_line > Scroll_offset);
1296 while (!cc_line_query_visible(Selected_line))
1300 gamesnd_play_iface(SND_SCROLL);
1303 gamesnd_play_iface(SND_GENERAL_FAIL);
1306 void control_config_scroll_line_up()
1308 if (Selected_line) {
1310 if (Selected_line < Scroll_offset)
1311 Scroll_offset = Selected_line;
1314 gamesnd_play_iface(SND_SCROLL);
1317 gamesnd_play_iface(SND_GENERAL_FAIL);
1320 void control_config_scroll_screen_down()
1322 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]) {
1324 while (!cc_line_query_visible(Selected_line)) {
1326 SDL_assert(Selected_line < Num_cc_lines);
1330 gamesnd_play_iface(SND_SCROLL);
1333 gamesnd_play_iface(SND_GENERAL_FAIL);
1336 void control_config_scroll_line_down()
1338 if (Selected_line < Num_cc_lines - 1) {
1340 SDL_assert(Selected_line > Scroll_offset);
1341 while (!cc_line_query_visible(Selected_line))
1345 gamesnd_play_iface(SND_SCROLL);
1348 gamesnd_play_iface(SND_GENERAL_FAIL);
1351 void control_config_toggle_modifier(int bit)
1355 z = Cc_lines[Selected_line].cc_index;
1356 SDL_assert(!(z & JOY_AXIS));
1357 k = Control_config[z].key_id;
1359 gamesnd_play_iface(SND_GENERAL_FAIL);
1363 control_config_bind_key(z, k ^ bit);
1364 control_config_conflict_check();
1365 gamesnd_play_iface(SND_USER_SELECT);
1368 void control_config_toggle_invert()
1372 z = Cc_lines[Selected_line].cc_index;
1373 SDL_assert(z & JOY_AXIS);
1375 control_config_save_axis_undo(z);
1376 Invert_axis[z] = !Invert_axis[z];
1379 void control_config_do_bind()
1384 // if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
1385 if (Selected_line < 0) {
1386 gamesnd_play_iface(SND_GENERAL_FAIL);
1390 for (i=0; i<NUM_BUTTONS; i++)
1391 if (i != CANCEL_BUTTON) {
1392 CC_Buttons[gr_screen.res][i].button.reset_status();
1393 CC_Buttons[gr_screen.res][i].button.disable();
1396 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1397 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(SDLK_ESCAPE);
1399 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1400 joy_down_count(i); // clear checking status of all joystick buttons
1403 control_config_detect_axis_reset();
1406 Bind_time = timer_get_milliseconds();
1410 gamesnd_play_iface(SND_USER_SELECT);
1413 void control_config_do_search()
1417 for (i=0; i<NUM_BUTTONS; i++){
1418 if (i != CANCEL_BUTTON) {
1419 CC_Buttons[gr_screen.res][i].button.reset_status();
1420 CC_Buttons[gr_screen.res][i].button.disable();
1424 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1425 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(SDLK_ESCAPE);
1427 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1428 joy_down_count(i); // clear checking status of all joystick buttons
1434 gamesnd_play_iface(SND_USER_SELECT);
1437 void control_config_do_cancel(int fail = 0)
1443 for (i=0; i<NUM_BUTTONS; i++){
1444 if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
1445 CC_Buttons[gr_screen.res][i].button.enable();
1449 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.reset_status();
1450 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1451 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
1452 CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
1453 CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
1455 Binding_mode = Search_mode = 0;
1457 gamesnd_play_iface(SND_GENERAL_FAIL);
1459 gamesnd_play_iface(SND_USER_SELECT);
1463 int control_config_accept()
1467 for (i=0; i<NUM_TABS; i++)
1468 if (Conflicts_tabs[i])
1472 gamesnd_play_iface(SND_GENERAL_FAIL);
1476 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1477 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1478 gamesnd_play_iface(SND_COMMIT_PRESSED);
1482 void control_config_cancel_exit()
1486 for (i=0; i<CCFG_MAX; i++)
1487 Control_config[i] = Control_config_backup[i];
1489 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1492 void control_config_button_pressed(int n)
1500 Scroll_offset = Selected_line = 0;
1501 control_config_list_prepare();
1502 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1506 control_config_do_bind();
1510 control_config_do_search();
1514 control_config_toggle_modifier(KEY_SHIFTED);
1515 gamesnd_play_iface(SND_USER_SELECT);
1519 control_config_toggle_modifier(KEY_ALTED);
1520 gamesnd_play_iface(SND_USER_SELECT);
1524 control_config_toggle_invert();
1525 gamesnd_play_iface(SND_USER_SELECT);
1528 case SCROLL_UP_BUTTON:
1529 control_config_scroll_screen_up();
1532 case SCROLL_DOWN_BUTTON:
1533 control_config_scroll_screen_down();
1537 control_config_accept();
1541 control_config_remove_binding();
1545 launch_context_help();
1546 gamesnd_play_iface(SND_HELP_PRESSED);
1550 control_config_do_reset();
1554 control_config_undo_last();
1558 control_config_do_cancel();
1561 case CLEAR_OTHER_BUTTON:
1562 control_config_clear_other();
1565 case CLEAR_ALL_BUTTON:
1566 control_config_clear_all();
1571 const char *control_config_tooltip_handler(const char *str)
1575 if (!SDL_strcasecmp(str, NOX("@conflict"))) {
1576 for (i=0; i<NUM_TABS; i++) {
1577 if (Conflicts_tabs[i])
1578 return XSTR( "Conflict!", 205);
1585 void control_config_init()
1590 // make backup of all controls
1591 for (i=0; i<CCFG_MAX; i++)
1592 Control_config_backup[i] = Control_config[i];
1594 common_set_interface_palette(NOX("ControlConfigPalette")); // set the interface palette
1595 Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1596 Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
1597 Ui_window.tooltip_handler = control_config_tooltip_handler;
1599 // load in help overlay bitmap
1600 help_overlay_load(CONTROL_CONFIG_OVERLAY);
1601 help_overlay_set_state(CONTROL_CONFIG_OVERLAY,0);
1603 // reset conflict flashing
1604 Conflict_stamp = -1;
1607 hud_anim_init(&Conflict_warning_anim, Conflict_warning_coords[gr_screen.res][0], Conflict_warning_coords[gr_screen.res][1], NOX("ConflictFlash"));
1608 hud_anim_load(&Conflict_warning_anim);
1611 for (i=0; i<NUM_BUTTONS; i++) {
1612 b = &CC_Buttons[gr_screen.res][i];
1614 if (b->hotspot < 0) { // temporary
1615 b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1); // temporary
1616 b->button.set_highlight_action(common_play_highlight_sound);
1620 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1622 // set up callback for when a mouse first goes over a button
1623 b->button.set_highlight_action(common_play_highlight_sound);
1625 b->button.set_bmaps(b->filename, 5, 1); // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1627 b->button.set_bmaps(b->filename);
1629 b->button.link_hotspot(b->hotspot);
1634 for(i=0; i<CC_NUM_TEXT; i++){
1635 Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
1639 for (i=0; i<LIST_BUTTONS_MAX; i++) {
1640 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1641 List_buttons[i].hide();
1642 List_buttons[i].disable();
1645 // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1646 CC_Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(SDLK_PAGEUP);
1647 CC_Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(SDLK_PAGEDOWN);
1648 CC_Buttons[gr_screen.res][BIND_BUTTON].button.set_hotkey(SDLK_RETURN);
1649 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_DELETE);
1650 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_z);
1651 CC_Buttons[gr_screen.res][CLEAR_BUTTON].button.set_hotkey(SDLK_DELETE);
1652 CC_Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_RETURN);
1653 CC_Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(SDLK_F1);
1654 CC_Buttons[gr_screen.res][RESET_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_r);
1655 CC_Buttons[gr_screen.res][INVERT_AXIS].button.set_hotkey(SDLK_i);
1657 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1658 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
1660 Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);
1662 Scroll_offset = Selected_line = 0;
1663 Config_item_undo = NULL;
1664 control_config_conflict_check();
1667 Joy_axis_action_text[0] = strdup(XSTR("Turn (Yaw) Axis", 1016));
1668 Joy_axis_action_text[1] = strdup(XSTR("Pitch Axis", 1017));
1669 Joy_axis_action_text[2] = strdup(XSTR("Bank Axis", 1018));
1670 Joy_axis_action_text[3] = strdup(XSTR("Absolute Throttle Axis", 1019));
1671 Joy_axis_action_text[4] = strdup(XSTR("Relative Throttle Axis", 1020));
1672 Joy_axis_text[0] = strdup(XSTR("Joystick/Mouse X Axis", 1021));
1673 Joy_axis_text[1] = strdup(XSTR("Joystick/Mouse Y Axis", 1022));
1674 Joy_axis_text[2] = strdup(XSTR("Joystick Z Axis", 1023));
1675 Joy_axis_text[3] = strdup(XSTR("Joystick rX Axis", 1024));
1676 Joy_axis_text[4] = strdup(XSTR("Joystick rY Axis", 1025));
1677 Joy_axis_text[5] = strdup(XSTR("Joystick rZ Axis", 1026));
1678 Mouse_button_text[0] = strdup("");
1679 Mouse_button_text[1] = strdup(XSTR("Left Button", 1027));
1680 Mouse_button_text[2] = strdup(XSTR("Right Button", 1028));
1681 Mouse_button_text[3] = strdup(XSTR("Mid Button", 1029));
1682 Mouse_button_text[4] = strdup("");
1683 Mouse_axis_text[0] = strdup(XSTR("L/R", 1030));
1684 Mouse_axis_text[1] = strdup(XSTR("U/B", 1031));
1685 Invert_text[0] = strdup(XSTR("N", 1032));
1686 Invert_text[1] = strdup(XSTR("Y", 1033));
1688 control_config_list_prepare();
1691 void control_config_close()
1695 while (Config_item_undo){
1699 // unload the overlay bitmap
1700 help_overlay_unload(CONTROL_CONFIG_OVERLAY);
1702 if (Background_bitmap){
1703 bm_unload(Background_bitmap);
1707 hud_anim_release(&Conflict_warning_anim);
1710 Ui_window.destroy();
1711 common_free_interface_palette(); // restore game palette
1712 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1717 for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
1718 if(Joy_axis_action_text[idx] != NULL){
1719 free(Joy_axis_action_text[idx]);
1720 Joy_axis_action_text[idx] = NULL;
1723 for(idx=0; idx<NUM_AXIS_TEXT; idx++){
1724 if(Joy_axis_text[idx] != NULL){
1725 free(Joy_axis_text[idx]);
1726 Joy_axis_text[idx] = NULL;
1729 for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
1730 if(Mouse_button_text[idx] != NULL){
1731 free(Mouse_button_text[idx]);
1732 Mouse_button_text[idx] = NULL;
1735 for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
1736 if(Mouse_axis_text[idx] != NULL){
1737 free(Mouse_axis_text[idx]);
1738 Mouse_axis_text[idx] = NULL;
1741 for(idx=0; idx<NUM_INVERT_TEXT; idx++){
1742 if(Invert_text[idx] != NULL){
1743 free(Invert_text[idx]);
1744 Invert_text[idx] = NULL;
1749 void control_config_do_frame(float frametime)
1752 int i, j, k, w, x, y, z, line, conflict;
1753 int font_height = gr_get_font_height();
1754 int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
1755 static float timer = 0.0f;
1757 static int bound_timestamp = 0;
1758 static char bound_string[40];
1763 if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1766 z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1767 i = control_config_detect_axis();
1774 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1775 Ui_window.process(0);
1777 if (k == SDLK_ESCAPE) {
1778 SDL_strlcpy(bound_string, XSTR( "Canceled", 206), SDL_arraysize(bound_string));
1779 bound_timestamp = timestamp(2500);
1780 control_config_do_cancel();
1783 if (k == SDLK_RETURN)
1786 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1787 if (joy_down_count(i))
1791 if (Axis_override >= 0) {
1792 control_config_bind_axis(z, Axis_override);
1793 SDL_strlcpy(bound_string, Joy_axis_text[Axis_override], SDL_arraysize(bound_string));
1794 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1795 bound_timestamp = timestamp(2500);
1796 control_config_conflict_check();
1797 control_config_list_prepare();
1798 control_config_do_cancel();
1801 control_config_do_cancel(1);
1807 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1808 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1809 Ui_window.set_ignore_gadgets(1);
1813 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1814 Ui_window.process(0);
1816 if ( (k > 0) || B1_JUST_RELEASED ) {
1817 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1818 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1819 Ui_window.set_ignore_gadgets(0);
1824 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1825 Ui_window.set_ignore_gadgets(0);
1828 if (k == SDLK_ESCAPE) {
1829 SDL_strlcpy(bound_string, XSTR( "Canceled", 206), SDL_arraysize(bound_string));
1830 bound_timestamp = timestamp(2500);
1831 control_config_do_cancel();
1834 switch (k & KEY_MASK) {
1839 Last_key = k & KEY_MASK;
1844 if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED) // a special hack just for Mike K.
1845 if ( (Last_key >= 0) && (k <= 0) && !key_pressed(Last_key) )
1848 if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1849 popup(0, 1, POPUP_OK, XSTR( "That is a non-bindable key. Please try again.", 207));
1853 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1855 z = Cc_lines[Selected_line].cc_index;
1856 SDL_assert(!(z & JOY_AXIS));
1857 control_config_bind_key(z, k);
1859 SDL_strlcpy(bound_string, textify_scancode(k), SDL_arraysize(bound_string));
1860 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1861 bound_timestamp = timestamp(2500);
1862 control_config_conflict_check();
1863 control_config_list_prepare();
1864 control_config_do_cancel();
1867 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1868 if (joy_down_count(i)) {
1869 z = Cc_lines[Selected_line].cc_index;
1870 SDL_assert(!(z & JOY_AXIS));
1871 control_config_bind_joy(z, i);
1873 SDL_strlcpy(bound_string, Joy_button_text[i], SDL_arraysize(bound_string));
1874 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1875 bound_timestamp = timestamp(2500);
1876 control_config_conflict_check();
1877 control_config_list_prepare();
1878 control_config_do_cancel();
1882 if (Bind_time + 375 < timer_get_milliseconds()) {
1883 for (i=0; i<NUM_BUTTONS; i++){
1884 if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
1889 if (i == NUM_BUTTONS) { // no buttons pressed
1890 for (i=0; i<MOUSE_NUM_BUTTONS; i++)
1891 if (mouse_down(1 << i)) {
1892 z = Cc_lines[Selected_line].cc_index;
1893 SDL_assert(!(z & JOY_AXIS));
1894 control_config_bind_joy(z, i);
1896 SDL_strlcpy(bound_string, Joy_button_text[i], SDL_arraysize(bound_string));
1897 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1898 bound_timestamp = timestamp(2500);
1899 control_config_conflict_check();
1900 control_config_list_prepare();
1901 control_config_do_cancel();
1902 for (j=0; j<NUM_BUTTONS; j++){
1903 CC_Buttons[gr_screen.res][j].button.reset();
1913 } else if (Search_mode) {
1914 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1915 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1916 Ui_window.set_ignore_gadgets(1);
1920 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1921 Ui_window.process(0);
1923 if ( (k > 0) || B1_JUST_RELEASED ) {
1924 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1925 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1926 Ui_window.set_ignore_gadgets(0);
1931 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1932 Ui_window.set_ignore_gadgets(0);
1935 if (k == SDLK_ESCAPE) {
1936 control_config_do_cancel();
1939 if ((k > 0) && !Config_allowed[k & KEY_MASK])
1942 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1945 for (i=0; i<CCFG_MAX; i++)
1946 if (Control_config[i].key_id == k) {
1952 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1953 if (joy_down_count(i)) {
1955 for (i=0; i<CCFG_MAX; i++)
1956 if (Control_config[i].joy_id == j) {
1964 // check if not on enabled button
1965 for (j=0; j<NUM_BUTTONS; j++){
1966 if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
1971 if (j == NUM_BUTTONS) { // no buttons pressed
1972 for (j=0; j<MOUSE_NUM_BUTTONS; j++)
1973 if (mouse_down(1 << j)) {
1974 for (i=0; i<CCFG_MAX; i++)
1975 if (Control_config[i].joy_id == j) {
1977 for (j=0; j<NUM_BUTTONS; j++){
1978 CC_Buttons[gr_screen.res][j].button.reset();
1988 Tab = Control_config[z].tab;
1989 control_config_list_prepare();
1990 Selected_line = Scroll_offset = 0;
1991 for (i=0; i<Num_cc_lines; i++)
1992 if (Cc_lines[i].cc_index == z) {
1997 while (!cc_line_query_visible(Selected_line)) {
1999 SDL_assert(Scroll_offset < Num_cc_lines);
2005 z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
2006 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
2007 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
2008 CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
2011 z = Cc_lines[Selected_line].cc_index;
2012 k = Control_config[z].key_id;
2013 if ( (k == SDLK_LALT) || (k == SDLK_RALT) || (k == SDLK_LSHIFT) || (k == SDLK_RSHIFT) ) {
2014 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
2015 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
2019 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
2021 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2022 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
2023 Ui_window.set_ignore_gadgets(1);
2026 k = Ui_window.process();
2028 if ( (k > 0) || B1_JUST_RELEASED ) {
2029 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2030 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
2031 Ui_window.set_ignore_gadgets(0);
2036 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2037 Ui_window.set_ignore_gadgets(0);
2041 case SDLK_DOWN: // select next line
2042 control_config_scroll_line_down();
2045 case SDLK_UP: // select previous line
2046 control_config_scroll_line_up();
2049 case KEY_SHIFTED | SDLK_TAB: // activate previous tab
2054 Scroll_offset = Selected_line = 0;
2055 control_config_list_prepare();
2056 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2059 case SDLK_TAB: // activate next tab
2061 if (Tab >= NUM_TABS)
2064 Scroll_offset = Selected_line = 0;
2065 control_config_list_prepare();
2066 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2071 if (Selected_item == -2) {
2073 if (Cc_lines[Selected_line].jw < 1) {
2075 if (Cc_lines[Selected_line].kw < 1)
2080 gamesnd_play_iface(SND_SCROLL);
2085 if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1))
2087 else if (!Selected_item && (Cc_lines[Selected_line].kw < 1))
2089 else if (Selected_item > 1)
2092 gamesnd_play_iface(SND_SCROLL);
2095 case SDLK_BACKSPACE: // undo
2096 control_config_undo_last();
2100 control_config_cancel_exit();
2105 for (i=0; i<NUM_BUTTONS; i++){
2106 if (CC_Buttons[gr_screen.res][i].button.pressed()){
2107 control_config_button_pressed(i);
2111 for (i=0; i<LIST_BUTTONS_MAX; i++) {
2112 if (List_buttons[i].is_mouse_on())
2113 select_tease_line = i + Scroll_offset;
2115 if (List_buttons[i].pressed()) {
2116 Selected_line = i + Scroll_offset;
2118 List_buttons[i].get_mouse_pos(&x, &y);
2119 if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw))
2122 if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw))
2125 gamesnd_play_iface(SND_USER_SELECT);
2128 if (List_buttons[i].double_clicked())
2129 control_config_do_bind();
2132 GR_MAYBE_CLEAR_RES(Background_bitmap);
2133 if (Background_bitmap >= 0) {
2134 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2138 // highlight tab with conflict
2140 for (i=z=0; i<NUM_TABS; i++) {
2141 if (Conflicts_tabs[i]) {
2142 CC_Buttons[gr_screen.res][i].button.draw_forced(4);
2148 // maybe switch from bright to normal
2149 if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
2150 Conflict_bright = !Conflict_bright;
2152 Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
2156 // set color and font
2158 if(Conflict_bright){
2159 gr_set_color_fast(&Color_bright_red);
2161 gr_set_color_fast(&Color_red);
2164 // setup the conflict string
2165 char conflict_str[512] = "";
2166 SDL_strlcpy(conflict_str, XSTR("Conflict!", 205), SDL_arraysize(conflict_str));
2168 gr_get_string_size(&sw, &sh, conflict_str);
2170 gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str);
2174 hud_anim_render(&Conflict_warning_anim, frametime);
2177 // might as well always reset the conflict stamp
2178 Conflict_stamp = -1;
2181 for (i=0; i<NUM_TABS; i++) {
2182 if (CC_Buttons[gr_screen.res][i].button.button_down()) {
2187 if (i == NUM_TABS) {
2188 CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
2192 CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
2195 if (Selected_line >= 0) {
2196 z = Cc_lines[Selected_line].cc_index;
2198 if (Invert_axis[z & ~JOY_AXIS]) {
2199 CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
2203 z = Control_config[z].key_id;
2205 if (z & KEY_SHIFTED) {
2206 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
2208 if (z & KEY_ALTED) {
2209 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
2216 CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
2219 z = Cc_lines[Selected_line].cc_index;
2220 x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2;
2221 y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2;
2225 t = (int) (timer * 3);
2227 gr_set_color_fast(&Color_text_normal);
2228 gr_get_string_size(&w, NULL, XSTR( "?", 208));
2229 gr_printf(x - w / 2, y - font_height / 2, XSTR( "?", 208));
2232 } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
2233 i = Conflicts[z].key;
2235 i = Conflicts[z].joy;
2237 gr_set_color_fast(&Color_text_normal);
2238 const char *str = XSTR( "Control conflicts with:", 209);
2239 gr_get_string_size(&w, NULL, str);
2240 gr_printf(x - w / 2, y - font_height, str);
2242 SDL_strlcpy(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i), SDL_arraysize(buf));
2243 gr_force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
2244 gr_get_string_size(&w, NULL, buf);
2245 gr_printf(x - w / 2, y, buf);
2247 } else if (*bound_string) {
2248 gr_set_color_fast(&Color_text_normal);
2249 gr_get_string_size(&w, NULL, bound_string);
2250 gr_printf(x - w / 2, y - font_height / 2, bound_string);
2251 if (timestamp_elapsed(bound_timestamp))
2255 // gr_set_color_fast(&Color_text_heading);
2256 // gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]);
2258 // gr_get_string_size(&w, &h, Heading[Tab]);
2259 // y = HEADING_Y + h / 2 - 1;
2260 // gr_line(LIST_X, y, LIST_X + 18, y);
2261 // gr_line(LIST_X + w + 21, y, LIST_X + LIST_W, y);
2263 if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
2264 gr_set_color_fast(&Color_white);
2265 gr_printf(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210));
2269 line = Scroll_offset;
2270 while (cc_line_query_visible(line)) {
2271 z = Cc_lines[line].cc_index;
2272 y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
2274 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);
2275 List_buttons[line - Scroll_offset].enable(!Binding_mode);
2277 Cc_lines[line].kw = Cc_lines[line].jw = 0;
2279 if (line == Selected_line){
2280 c = &Color_text_selected;
2281 } else if (line == select_tease_line) {
2282 c = &Color_text_subselected;
2284 c = &Color_text_normal;
2287 gr_set_color_fast(c);
2288 if (Cc_lines[line].label) {
2289 SDL_strlcpy(buf, Cc_lines[line].label, SDL_arraysize(buf));
2290 gr_force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
2291 gr_printf(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
2294 if (!(z & JOY_AXIS)) {
2295 k = Control_config[z].key_id;
2296 j = Control_config[z].joy_id;
2297 x = Control_list_key_x[gr_screen.res];
2300 if ((k < 0) && (j < 0)) {
2301 gr_set_color_fast(&Color_grey);
2302 gr_printf(x, y, XSTR( "None", 211));
2306 SDL_strlcpy(buf, textify_scancode(k), SDL_arraysize(buf));
2307 if (Conflicts[z].key >= 0) {
2308 if (c == &Color_text_normal)
2309 gr_set_color_fast(&Color_text_error);
2311 gr_set_color_fast(&Color_text_error_hi);
2315 } else if (Selected_item == 1) {
2316 gr_set_color_fast(&Color_text_normal);
2319 gr_set_color_fast(c);
2321 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 SDL_strlcpy(buf, Joy_button_text[j], SDL_arraysize(buf));
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 SDL_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
2481 int tmp = key_get_shift_status();
2484 if (tmp & KEY_SHIFTED) {
2485 mask |= KEY_SHIFTED;
2488 if (tmp & KEY_ALTED) {
2492 z = Control_config[id].key_id;
2494 if ( (z != SDLK_LALT) && (z != SDLK_RALT) && (z != SDLK_LSHIFT) && (z != SDLK_RSHIFT) ) {
2495 // if current modifiers don't match action's modifiers, don't register control active.
2496 if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask)
2502 if (key_pressed(z) || key_down_count(z)) {
2503 if ( !hud_squadmsg_read_key(z) ) {
2513 if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2521 // get heading, pitch, bank, throttle abs. and throttle rel. values.
2522 void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
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(Axis_map_to[0]);
2530 if (Axis_map_to[1] >= 0)
2531 *p = joy_get_scaled_reading(Axis_map_to[1]);
2534 if (Axis_map_to[2] >= 0)
2535 *b = joy_get_scaled_reading(Axis_map_to[2]);
2538 if (Axis_map_to[3] >= 0)
2539 *ta = joy_get_unscaled_reading(Axis_map_to[3]);
2542 if (Axis_map_to[4] >= 0)
2543 *tr = joy_get_scaled_reading(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;