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()
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 = 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 = -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 = -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 int control_config_axis_default(int axis)
1215 if (Axis_map_to_defaults[axis] < 0)
1218 if ( !joy_axis_valid(Axis_map_to_defaults[axis]) )
1222 return Axis_map_to_defaults[axis];
1225 int control_config_do_reset()
1227 int i, j, total = 0;
1228 config_item_undo *ptr;
1231 // first, determine how many bindings need to be changed
1232 for (i=0; i<CCFG_MAX; i++)
1233 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default))
1236 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1237 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i]))
1241 gamesnd_play_iface(SND_GENERAL_FAIL);
1245 // now, back up the old bindings so we can undo if we want to
1246 ptr = get_undo_block(total);
1247 for (i=j=0; i<CCFG_MAX; i++) {
1248 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
1250 ptr->list[j] = Control_config[i];
1255 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1256 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
1257 item.joy_id = (short) Axis_map_to[i];
1258 item.used = Invert_axis[i];
1260 ptr->index[j] = i | JOY_AXIS;
1261 ptr->list[j] = item;
1266 control_config_reset_defaults();
1267 control_config_conflict_check();
1268 control_config_list_prepare();
1269 gamesnd_play_iface(SND_RESET_PRESSED);
1273 // This sets all the controls to their default values
1274 void control_config_reset_defaults()
1278 // Reset keyboard defaults
1279 for (i=0; i<CCFG_MAX; i++) {
1280 Control_config[i].key_id = Control_config[i].key_default;
1281 Control_config[i].joy_id = Control_config[i].joy_default;
1284 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1285 Axis_map_to[i] = control_config_axis_default(i);
1286 Invert_axis[i] = Invert_axis_defaults[i];
1290 void control_config_scroll_screen_up()
1292 if (Scroll_offset) {
1294 Assert(Selected_line > Scroll_offset);
1295 while (!cc_line_query_visible(Selected_line))
1299 gamesnd_play_iface(SND_SCROLL);
1302 gamesnd_play_iface(SND_GENERAL_FAIL);
1305 void control_config_scroll_line_up()
1307 if (Selected_line) {
1309 if (Selected_line < Scroll_offset)
1310 Scroll_offset = Selected_line;
1313 gamesnd_play_iface(SND_SCROLL);
1316 gamesnd_play_iface(SND_GENERAL_FAIL);
1319 void control_config_scroll_screen_down()
1321 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]) {
1323 while (!cc_line_query_visible(Selected_line)) {
1325 Assert(Selected_line < Num_cc_lines);
1329 gamesnd_play_iface(SND_SCROLL);
1332 gamesnd_play_iface(SND_GENERAL_FAIL);
1335 void control_config_scroll_line_down()
1337 if (Selected_line < Num_cc_lines - 1) {
1339 Assert(Selected_line > Scroll_offset);
1340 while (!cc_line_query_visible(Selected_line))
1344 gamesnd_play_iface(SND_SCROLL);
1347 gamesnd_play_iface(SND_GENERAL_FAIL);
1350 void control_config_toggle_modifier(int bit)
1354 z = Cc_lines[Selected_line].cc_index;
1355 Assert(!(z & JOY_AXIS));
1356 k = Control_config[z].key_id;
1358 gamesnd_play_iface(SND_GENERAL_FAIL);
1362 control_config_bind_key(z, k ^ bit);
1363 control_config_conflict_check();
1364 gamesnd_play_iface(SND_USER_SELECT);
1367 void control_config_toggle_invert()
1371 z = Cc_lines[Selected_line].cc_index;
1372 Assert(z & JOY_AXIS);
1374 control_config_save_axis_undo(z);
1375 Invert_axis[z] = !Invert_axis[z];
1378 void control_config_do_bind()
1383 // if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
1384 if (Selected_line < 0) {
1385 gamesnd_play_iface(SND_GENERAL_FAIL);
1389 for (i=0; i<NUM_BUTTONS; i++)
1390 if (i != CANCEL_BUTTON) {
1391 CC_Buttons[gr_screen.res][i].button.reset_status();
1392 CC_Buttons[gr_screen.res][i].button.disable();
1395 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1396 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(SDLK_ESCAPE);
1398 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1399 joy_down_count(i); // clear checking status of all joystick buttons
1402 control_config_detect_axis_reset();
1405 Bind_time = timer_get_milliseconds();
1409 gamesnd_play_iface(SND_USER_SELECT);
1412 void control_config_do_search()
1416 for (i=0; i<NUM_BUTTONS; i++){
1417 if (i != CANCEL_BUTTON) {
1418 CC_Buttons[gr_screen.res][i].button.reset_status();
1419 CC_Buttons[gr_screen.res][i].button.disable();
1423 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1424 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(SDLK_ESCAPE);
1426 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1427 joy_down_count(i); // clear checking status of all joystick buttons
1433 gamesnd_play_iface(SND_USER_SELECT);
1436 void control_config_do_cancel(int fail = 0)
1442 for (i=0; i<NUM_BUTTONS; i++){
1443 if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
1444 CC_Buttons[gr_screen.res][i].button.enable();
1448 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.reset_status();
1449 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1450 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
1451 CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
1452 CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
1454 Binding_mode = Search_mode = 0;
1456 gamesnd_play_iface(SND_GENERAL_FAIL);
1458 gamesnd_play_iface(SND_USER_SELECT);
1462 int control_config_accept()
1466 for (i=0; i<NUM_TABS; i++)
1467 if (Conflicts_tabs[i])
1471 gamesnd_play_iface(SND_GENERAL_FAIL);
1475 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1476 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1477 gamesnd_play_iface(SND_COMMIT_PRESSED);
1481 void control_config_cancel_exit()
1485 for (i=0; i<CCFG_MAX; i++)
1486 Control_config[i] = Control_config_backup[i];
1488 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1491 void control_config_button_pressed(int n)
1499 Scroll_offset = Selected_line = 0;
1500 control_config_list_prepare();
1501 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1505 control_config_do_bind();
1509 control_config_do_search();
1513 control_config_toggle_modifier(KEY_SHIFTED);
1514 gamesnd_play_iface(SND_USER_SELECT);
1518 control_config_toggle_modifier(KEY_ALTED);
1519 gamesnd_play_iface(SND_USER_SELECT);
1523 control_config_toggle_invert();
1524 gamesnd_play_iface(SND_USER_SELECT);
1527 case SCROLL_UP_BUTTON:
1528 control_config_scroll_screen_up();
1531 case SCROLL_DOWN_BUTTON:
1532 control_config_scroll_screen_down();
1536 control_config_accept();
1540 control_config_remove_binding();
1544 launch_context_help();
1545 gamesnd_play_iface(SND_HELP_PRESSED);
1549 control_config_do_reset();
1553 control_config_undo_last();
1557 control_config_do_cancel();
1560 case CLEAR_OTHER_BUTTON:
1561 control_config_clear_other();
1564 case CLEAR_ALL_BUTTON:
1565 control_config_clear_all();
1570 const char *control_config_tooltip_handler(const char *str)
1574 if (!stricmp(str, NOX("@conflict"))) {
1575 for (i=0; i<NUM_TABS; i++) {
1576 if (Conflicts_tabs[i])
1577 return XSTR( "Conflict!", 205);
1584 void control_config_init()
1589 // make backup of all controls
1590 for (i=0; i<CCFG_MAX; i++)
1591 Control_config_backup[i] = Control_config[i];
1593 common_set_interface_palette(NOX("ControlConfigPalette")); // set the interface palette
1594 Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1595 Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
1596 Ui_window.tooltip_handler = control_config_tooltip_handler;
1598 // load in help overlay bitmap
1599 help_overlay_load(CONTROL_CONFIG_OVERLAY);
1600 help_overlay_set_state(CONTROL_CONFIG_OVERLAY,0);
1602 // reset conflict flashing
1603 Conflict_stamp = -1;
1606 hud_anim_init(&Conflict_warning_anim, Conflict_warning_coords[gr_screen.res][0], Conflict_warning_coords[gr_screen.res][1], NOX("ConflictFlash"));
1607 hud_anim_load(&Conflict_warning_anim);
1610 for (i=0; i<NUM_BUTTONS; i++) {
1611 b = &CC_Buttons[gr_screen.res][i];
1613 if (b->hotspot < 0) { // temporary
1614 b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1); // temporary
1615 b->button.set_highlight_action(common_play_highlight_sound);
1619 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1621 // set up callback for when a mouse first goes over a button
1622 b->button.set_highlight_action(common_play_highlight_sound);
1624 b->button.set_bmaps(b->filename, 5, 1); // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1626 b->button.set_bmaps(b->filename);
1628 b->button.link_hotspot(b->hotspot);
1633 for(i=0; i<CC_NUM_TEXT; i++){
1634 Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
1638 for (i=0; i<LIST_BUTTONS_MAX; i++) {
1639 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1640 List_buttons[i].hide();
1641 List_buttons[i].disable();
1644 // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1645 CC_Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(SDLK_PAGEUP);
1646 CC_Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(SDLK_PAGEDOWN);
1647 CC_Buttons[gr_screen.res][BIND_BUTTON].button.set_hotkey(SDLK_RETURN);
1648 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_DELETE);
1649 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_z);
1650 CC_Buttons[gr_screen.res][CLEAR_BUTTON].button.set_hotkey(SDLK_DELETE);
1651 CC_Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_RETURN);
1652 CC_Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(SDLK_F1);
1653 CC_Buttons[gr_screen.res][RESET_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_r);
1654 CC_Buttons[gr_screen.res][INVERT_AXIS].button.set_hotkey(SDLK_i);
1656 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1657 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
1659 Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);
1661 Scroll_offset = Selected_line = 0;
1662 Config_item_undo = NULL;
1663 control_config_conflict_check();
1666 Joy_axis_action_text[0] = strdup(XSTR("Turn (Yaw) Axis", 1016));
1667 Joy_axis_action_text[1] = strdup(XSTR("Pitch Axis", 1017));
1668 Joy_axis_action_text[2] = strdup(XSTR("Bank Axis", 1018));
1669 Joy_axis_action_text[3] = strdup(XSTR("Absolute Throttle Axis", 1019));
1670 Joy_axis_action_text[4] = strdup(XSTR("Relative Throttle Axis", 1020));
1671 Joy_axis_text[0] = strdup(XSTR("Joystick/Mouse X Axis", 1021));
1672 Joy_axis_text[1] = strdup(XSTR("Joystick/Mouse Y Axis", 1022));
1673 Joy_axis_text[2] = strdup(XSTR("Joystick Z Axis", 1023));
1674 Joy_axis_text[3] = strdup(XSTR("Joystick rX Axis", 1024));
1675 Joy_axis_text[4] = strdup(XSTR("Joystick rY Axis", 1025));
1676 Joy_axis_text[5] = strdup(XSTR("Joystick rZ Axis", 1026));
1677 Mouse_button_text[0] = strdup("");
1678 Mouse_button_text[1] = strdup(XSTR("Left Button", 1027));
1679 Mouse_button_text[2] = strdup(XSTR("Right Button", 1028));
1680 Mouse_button_text[3] = strdup(XSTR("Mid Button", 1029));
1681 Mouse_button_text[4] = strdup("");
1682 Mouse_axis_text[0] = strdup(XSTR("L/R", 1030));
1683 Mouse_axis_text[1] = strdup(XSTR("U/B", 1031));
1684 Invert_text[0] = strdup(XSTR("N", 1032));
1685 Invert_text[1] = strdup(XSTR("Y", 1033));
1687 control_config_list_prepare();
1690 void control_config_close()
1694 while (Config_item_undo){
1698 // unload the overlay bitmap
1699 help_overlay_unload(CONTROL_CONFIG_OVERLAY);
1701 if (Background_bitmap){
1702 bm_unload(Background_bitmap);
1706 hud_anim_release(&Conflict_warning_anim);
1709 Ui_window.destroy();
1710 common_free_interface_palette(); // restore game palette
1711 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1716 for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
1717 if(Joy_axis_action_text[idx] != NULL){
1718 free(Joy_axis_action_text[idx]);
1719 Joy_axis_action_text[idx] = NULL;
1722 for(idx=0; idx<NUM_AXIS_TEXT; idx++){
1723 if(Joy_axis_text[idx] != NULL){
1724 free(Joy_axis_text[idx]);
1725 Joy_axis_text[idx] = NULL;
1728 for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
1729 if(Mouse_button_text[idx] != NULL){
1730 free(Mouse_button_text[idx]);
1731 Mouse_button_text[idx] = NULL;
1734 for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
1735 if(Mouse_axis_text[idx] != NULL){
1736 free(Mouse_axis_text[idx]);
1737 Mouse_axis_text[idx] = NULL;
1740 for(idx=0; idx<NUM_INVERT_TEXT; idx++){
1741 if(Invert_text[idx] != NULL){
1742 free(Invert_text[idx]);
1743 Invert_text[idx] = NULL;
1748 void control_config_do_frame(float frametime)
1750 char buf[256], *jptr;
1751 int i, j, k, w, x, y, z, len, line, conflict;
1752 int font_height = gr_get_font_height();
1753 int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
1754 static float timer = 0.0f;
1756 static int bound_timestamp = 0;
1757 static char bound_string[40];
1762 if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1765 z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1766 i = control_config_detect_axis();
1773 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1774 Ui_window.process(0);
1776 if (k == SDLK_ESCAPE) {
1777 strcpy(bound_string, XSTR( "Canceled", 206));
1778 bound_timestamp = timestamp(2500);
1779 control_config_do_cancel();
1782 if (k == SDLK_RETURN)
1785 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1786 if (joy_down_count(i))
1790 if (Axis_override >= 0) {
1791 control_config_bind_axis(z, Axis_override);
1792 strcpy(bound_string, Joy_axis_text[Axis_override]);
1793 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1794 bound_timestamp = timestamp(2500);
1795 control_config_conflict_check();
1796 control_config_list_prepare();
1797 control_config_do_cancel();
1800 control_config_do_cancel(1);
1806 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1807 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1808 Ui_window.set_ignore_gadgets(1);
1812 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1813 Ui_window.process(0);
1815 if ( (k > 0) || B1_JUST_RELEASED ) {
1816 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1817 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1818 Ui_window.set_ignore_gadgets(0);
1823 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1824 Ui_window.set_ignore_gadgets(0);
1827 if (k == SDLK_ESCAPE) {
1828 strcpy(bound_string, XSTR( "Canceled", 206));
1829 bound_timestamp = timestamp(2500);
1830 control_config_do_cancel();
1833 switch (k & KEY_MASK) {
1838 Last_key = k & KEY_MASK;
1843 if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED) // a special hack just for Mike K.
1844 if ( (Last_key >= 0) && (k <= 0) && !key_pressed(Last_key) )
1847 if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1848 popup(0, 1, POPUP_OK, XSTR( "That is a non-bindable key. Please try again.", 207));
1852 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1854 z = Cc_lines[Selected_line].cc_index;
1855 Assert(!(z & JOY_AXIS));
1856 control_config_bind_key(z, k);
1858 strcpy(bound_string, textify_scancode(k));
1859 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1860 bound_timestamp = timestamp(2500);
1861 control_config_conflict_check();
1862 control_config_list_prepare();
1863 control_config_do_cancel();
1866 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1867 if (joy_down_count(i)) {
1868 z = Cc_lines[Selected_line].cc_index;
1869 Assert(!(z & JOY_AXIS));
1870 control_config_bind_joy(z, i);
1872 strcpy(bound_string, Joy_button_text[i]);
1873 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1874 bound_timestamp = timestamp(2500);
1875 control_config_conflict_check();
1876 control_config_list_prepare();
1877 control_config_do_cancel();
1881 if (Bind_time + 375 < timer_get_milliseconds()) {
1882 for (i=0; i<NUM_BUTTONS; i++){
1883 if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
1888 if (i == NUM_BUTTONS) { // no buttons pressed
1889 for (i=0; i<MOUSE_NUM_BUTTONS; i++)
1890 if (mouse_down(1 << i)) {
1891 z = Cc_lines[Selected_line].cc_index;
1892 Assert(!(z & JOY_AXIS));
1893 control_config_bind_joy(z, i);
1895 strcpy(bound_string, Joy_button_text[i]);
1896 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1897 bound_timestamp = timestamp(2500);
1898 control_config_conflict_check();
1899 control_config_list_prepare();
1900 control_config_do_cancel();
1901 for (j=0; j<NUM_BUTTONS; j++){
1902 CC_Buttons[gr_screen.res][j].button.reset();
1912 } else if (Search_mode) {
1913 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1914 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1915 Ui_window.set_ignore_gadgets(1);
1919 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1920 Ui_window.process(0);
1922 if ( (k > 0) || B1_JUST_RELEASED ) {
1923 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1924 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1925 Ui_window.set_ignore_gadgets(0);
1930 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1931 Ui_window.set_ignore_gadgets(0);
1934 if (k == SDLK_ESCAPE) {
1935 control_config_do_cancel();
1938 if ((k > 0) && !Config_allowed[k & KEY_MASK])
1941 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1944 for (i=0; i<CCFG_MAX; i++)
1945 if (Control_config[i].key_id == k) {
1951 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1952 if (joy_down_count(i)) {
1954 for (i=0; i<CCFG_MAX; i++)
1955 if (Control_config[i].joy_id == j) {
1963 // check if not on enabled button
1964 for (j=0; j<NUM_BUTTONS; j++){
1965 if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
1970 if (j == NUM_BUTTONS) { // no buttons pressed
1971 for (j=0; j<MOUSE_NUM_BUTTONS; j++)
1972 if (mouse_down(1 << j)) {
1973 for (i=0; i<CCFG_MAX; i++)
1974 if (Control_config[i].joy_id == j) {
1976 for (j=0; j<NUM_BUTTONS; j++){
1977 CC_Buttons[gr_screen.res][j].button.reset();
1987 Tab = Control_config[z].tab;
1988 control_config_list_prepare();
1989 Selected_line = Scroll_offset = 0;
1990 for (i=0; i<Num_cc_lines; i++)
1991 if (Cc_lines[i].cc_index == z) {
1996 while (!cc_line_query_visible(Selected_line)) {
1998 Assert(Scroll_offset < Num_cc_lines);
2004 z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
2005 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
2006 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
2007 CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
2010 z = Cc_lines[Selected_line].cc_index;
2011 k = Control_config[z].key_id;
2012 if ( (k == SDLK_LALT) || (k == SDLK_RALT) || (k == SDLK_LSHIFT) || (k == SDLK_RSHIFT) ) {
2013 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
2014 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
2018 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
2020 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2021 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
2022 Ui_window.set_ignore_gadgets(1);
2025 k = Ui_window.process();
2027 if ( (k > 0) || B1_JUST_RELEASED ) {
2028 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2029 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
2030 Ui_window.set_ignore_gadgets(0);
2035 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2036 Ui_window.set_ignore_gadgets(0);
2040 case SDLK_DOWN: // select next line
2041 control_config_scroll_line_down();
2044 case SDLK_UP: // select previous line
2045 control_config_scroll_line_up();
2048 case KEY_SHIFTED | SDLK_TAB: // activate previous tab
2053 Scroll_offset = Selected_line = 0;
2054 control_config_list_prepare();
2055 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2058 case SDLK_TAB: // activate next tab
2060 if (Tab >= NUM_TABS)
2063 Scroll_offset = Selected_line = 0;
2064 control_config_list_prepare();
2065 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2070 if (Selected_item == -2) {
2072 if (Cc_lines[Selected_line].jw < 1) {
2074 if (Cc_lines[Selected_line].kw < 1)
2079 gamesnd_play_iface(SND_SCROLL);
2084 if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1))
2086 else if (!Selected_item && (Cc_lines[Selected_line].kw < 1))
2088 else if (Selected_item > 1)
2091 gamesnd_play_iface(SND_SCROLL);
2094 case SDLK_BACKSPACE: // undo
2095 control_config_undo_last();
2099 control_config_cancel_exit();
2104 for (i=0; i<NUM_BUTTONS; i++){
2105 if (CC_Buttons[gr_screen.res][i].button.pressed()){
2106 control_config_button_pressed(i);
2110 for (i=0; i<LIST_BUTTONS_MAX; i++) {
2111 if (List_buttons[i].is_mouse_on())
2112 select_tease_line = i + Scroll_offset;
2114 if (List_buttons[i].pressed()) {
2115 Selected_line = i + Scroll_offset;
2117 List_buttons[i].get_mouse_pos(&x, &y);
2118 if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw))
2121 if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw))
2124 gamesnd_play_iface(SND_USER_SELECT);
2127 if (List_buttons[i].double_clicked())
2128 control_config_do_bind();
2131 GR_MAYBE_CLEAR_RES(Background_bitmap);
2132 if (Background_bitmap >= 0) {
2133 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2137 // highlight tab with conflict
2139 for (i=z=0; i<NUM_TABS; i++) {
2140 if (Conflicts_tabs[i]) {
2141 CC_Buttons[gr_screen.res][i].button.draw_forced(4);
2147 // maybe switch from bright to normal
2148 if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
2149 Conflict_bright = !Conflict_bright;
2151 Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
2155 // set color and font
2157 if(Conflict_bright){
2158 gr_set_color_fast(&Color_bright_red);
2160 gr_set_color_fast(&Color_red);
2163 // setup the conflict string
2164 char conflict_str[512] = "";
2165 strncpy(conflict_str, XSTR("Conflict!", 205), 511);
2167 gr_get_string_size(&sw, &sh, conflict_str);
2169 gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str);
2173 hud_anim_render(&Conflict_warning_anim, frametime);
2176 // might as well always reset the conflict stamp
2177 Conflict_stamp = -1;
2180 for (i=0; i<NUM_TABS; i++) {
2181 if (CC_Buttons[gr_screen.res][i].button.button_down()) {
2186 if (i == NUM_TABS) {
2187 CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
2191 CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
2194 if (Selected_line >= 0) {
2195 z = Cc_lines[Selected_line].cc_index;
2197 if (Invert_axis[z & ~JOY_AXIS]) {
2198 CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
2202 z = Control_config[z].key_id;
2204 if (z & KEY_SHIFTED) {
2205 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
2207 if (z & KEY_ALTED) {
2208 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
2215 CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
2218 z = Cc_lines[Selected_line].cc_index;
2219 x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2;
2220 y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2;
2224 t = (int) (timer * 3);
2226 gr_set_color_fast(&Color_text_normal);
2227 gr_get_string_size(&w, NULL, XSTR( "?", 208));
2228 gr_printf(x - w / 2, y - font_height / 2, XSTR( "?", 208));
2231 } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
2232 i = Conflicts[z].key;
2234 i = Conflicts[z].joy;
2236 gr_set_color_fast(&Color_text_normal);
2237 const char *str = XSTR( "Control conflicts with:", 209);
2238 gr_get_string_size(&w, NULL, str);
2239 gr_printf(x - w / 2, y - font_height, str);
2241 strcpy(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
2242 gr_force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
2243 gr_get_string_size(&w, NULL, buf);
2244 gr_printf(x - w / 2, y, buf);
2246 } else if (*bound_string) {
2247 gr_set_color_fast(&Color_text_normal);
2248 gr_get_string_size(&w, NULL, bound_string);
2249 gr_printf(x - w / 2, y - font_height / 2, bound_string);
2250 if (timestamp_elapsed(bound_timestamp))
2254 // gr_set_color_fast(&Color_text_heading);
2255 // gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]);
2257 // gr_get_string_size(&w, &h, Heading[Tab]);
2258 // y = HEADING_Y + h / 2 - 1;
2259 // gr_line(LIST_X, y, LIST_X + 18, y);
2260 // gr_line(LIST_X + w + 21, y, LIST_X + LIST_W, y);
2262 if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
2263 gr_set_color_fast(&Color_white);
2264 gr_printf(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210));
2268 line = Scroll_offset;
2269 while (cc_line_query_visible(line)) {
2270 z = Cc_lines[line].cc_index;
2271 y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
2273 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);
2274 List_buttons[line - Scroll_offset].enable(!Binding_mode);
2276 Cc_lines[line].kw = Cc_lines[line].jw = 0;
2278 if (line == Selected_line){
2279 c = &Color_text_selected;
2280 } else if (line == select_tease_line) {
2281 c = &Color_text_subselected;
2283 c = &Color_text_normal;
2286 gr_set_color_fast(c);
2287 if (Cc_lines[line].label) {
2288 strcpy(buf, Cc_lines[line].label);
2289 gr_force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
2290 gr_printf(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
2293 if (!(z & JOY_AXIS)) {
2294 k = Control_config[z].key_id;
2295 j = Control_config[z].joy_id;
2296 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 strcpy(buf, textify_scancode(k));
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);
2324 Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2325 gr_get_string_size(&w, NULL, buf);
2326 Cc_lines[line].kw = w;
2330 gr_set_color_fast(&Color_text_normal);
2331 gr_printf(x, y, XSTR( ", ", 212));
2332 gr_get_string_size(&w, NULL, XSTR( ", ", 212));
2338 strcpy(buf, Joy_button_text[j]);
2339 if (Conflicts[z].joy >= 0) {
2340 if (c == &Color_text_normal)
2341 gr_set_color_fast(&Color_text_error);
2343 gr_set_color_fast(&Color_text_error_hi);
2347 } else if (!Selected_item) {
2348 gr_set_color_fast(&Color_text_normal);
2351 gr_set_color_fast(c);
2353 gr_force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x);
2354 gr_printf(x, y, buf);
2356 Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2357 gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
2362 x = Control_list_key_x[gr_screen.res];
2363 j = Axis_map_to[z & ~JOY_AXIS];
2364 if (Binding_mode && (line == Selected_line))
2368 gr_set_color_fast(&Color_grey);
2369 gr_printf(x, y, XSTR( "None", 211));
2372 if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
2373 if (c == &Color_text_normal)
2374 gr_set_color_fast(&Color_text_error);
2377 gr_set_color_fast(&Color_text_error_hi);
2381 } else if (!Selected_item) {
2382 gr_set_color_fast(&Color_text_normal);
2385 gr_set_color_fast(c);
2387 gr_string(x, y, Joy_axis_text[j]);
2394 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
2396 i = line - Scroll_offset;
2397 while (i < LIST_BUTTONS_MAX)
2398 List_buttons[i++].disable();
2400 // blit help overlay if active
2401 help_overlay_maybe_blit(CONTROL_CONFIG_OVERLAY);
2406 void clear_key_binding(short key)
2410 for (i=0; i<CCFG_MAX; i++) {
2411 if (Control_config[i].key_id == key)
2412 Control_config[i].key_id = -1;
2416 float check_control_timef(int id)
2420 // if type isn't continuous, we shouldn't be using this function, cause it won't work.
2421 Assert(Control_config[id].type == CC_TYPE_CONTINUOUS);
2423 // first, see if control actually used (makes sure modifiers match as well)
2424 if (!check_control(id))
2427 t1 = key_down_timef(Control_config[id].key_id);
2431 t2 = joy_down_time(Control_config[id].joy_id);
2441 void control_check_indicate()
2444 if (Show_controls_info) {
2445 gr_set_color_fast(&HUD_color_debug);
2446 gr_printf(490, 15, NOX("Ctrls checked: %d"), Control_check_count);
2450 Control_check_count = 0;
2453 int check_control(int id, int key)
2456 static int last_key = 0;
2458 Control_check_count++;
2464 // if we're in multiplayer text enter (for chat) mode, check to see if we should ignore controls
2465 if ((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls()){
2469 if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
2470 if (joy_down(Control_config[id].joy_id) || joy_down_count(Control_config[id].joy_id)) {
2475 if ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS))
2476 if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2481 // check what current modifiers are pressed
2482 mask = key_get_shift_status();
2484 z = Control_config[id].key_id;
2486 if ( (z != SDLK_LALT) && (z != SDLK_RALT) && (z != SDLK_LSHIFT) && (z != SDLK_RSHIFT) ) {
2487 // if current modifiers don't match action's modifiers, don't register control active.
2488 if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask)
2494 if (key_pressed(z) || key_down_count(z)) {
2495 if ( !hud_squadmsg_read_key(z) ) {
2505 if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2513 // get heading, pitch, bank, throttle abs. and throttle rel. values.
2514 void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
2516 // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
2518 if (Axis_map_to[0] >= 0)
2519 *h = joy_get_scaled_reading(Axis_map_to[0]);
2522 if (Axis_map_to[1] >= 0)
2523 *p = joy_get_scaled_reading(Axis_map_to[1]);
2526 if (Axis_map_to[2] >= 0)
2527 *b = joy_get_scaled_reading(Axis_map_to[2]);
2530 if (Axis_map_to[3] >= 0)
2531 *ta = joy_get_unscaled_reading(Axis_map_to[3]);
2534 if (Axis_map_to[4] >= 0)
2535 *tr = joy_get_scaled_reading(Axis_map_to[4]);
2551 void control_used(int id)
2553 Control_config[id].used = timestamp();
2556 void control_config_clear_used_status()
2560 for (i=0; i<CCFG_MAX; i++)
2561 Control_config[i].used = 0;
2564 void control_config_clear()
2568 // Reset keyboard defaults
2569 for (i=0; i<CCFG_MAX; i++)
2570 Control_config[i].key_id = Control_config[i].joy_id = -1;
2573 int control_config_handle_conflict()
2575 if ((Selected_item == -1) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) { // we are deleting a conflict
2576 j = Conflicts[z].joy;
2577 if ((j >= 0) && (Control_config[j].joy_id < 0))
2580 k = Conflicts[z].key;
2581 if ((k >= 0) && (Control_config[k].key_id < 0))
2584 if ((j >= 0) && (k >= 0) && (j != k)) { // deleting 2 conflicts, each in different actions
2585 ptr = get_undo_block(2);
2587 ptr->list[0] = Control_config[j];
2588 Control_config[j].joy_id = (short) -1;
2591 ptr->list[1] = Control_config[k];
2592 Control_config[k].key_id = (short) -1;
2594 } else { // only 1 action in conflict with selected action (might be both controls, though)
2600 ptr = get_undo_block(1);
2602 ptr->list[0] = Control_config[z];
2605 Control_config[z].joy_id = (short) -1;
2608 Control_config[z].key_id = (short) -1;