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;
777 switch (Control_config[i].key_id) {
791 for (i=0; i<NUM_TABS; i++)
792 Conflicts_tabs[i] = 0;
794 for (i=0; i<CCFG_MAX-1; i++) {
795 if (control_config_valid_action(i)) {
796 for (j=i+1; j<CCFG_MAX; j++) {
797 if (control_config_valid_action(j)) {
798 if (Control_config[i].key_id >= 0) {
799 a = Control_config[i].key_id;
800 b = Control_config[j].key_id;
802 Conflicts[i].key = j;
803 Conflicts[j].key = i;
804 Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
805 Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
808 /* if ((a >= 0) && (a & KEY_SHIFTED) && (shift >= 0)) {
809 Conflicts[i].key = shift;
810 Conflicts[shift].key = i;
811 Conflicts_tabs[ Control_config[i].tab ] = 1;
812 Conflicts_tabs[ Control_config[shift].tab ] = 1;
815 if ((b >= 0) && (b & KEY_SHIFTED) && (shift >= 0)) {
816 Conflicts[j].key = shift;
817 Conflicts[shift].key = j;
818 Conflicts_tabs[ Control_config[j].tab ] = 1;
819 Conflicts_tabs[ Control_config[shift].tab ] = 1;
822 if ((a >= 0) && (a & KEY_ALTED) && (alt >= 0)) {
823 Conflicts[i].key = alt;
824 Conflicts[alt].key = i;
825 Conflicts_tabs[ Control_config[i].tab ] = 1;
826 Conflicts_tabs[ Control_config[alt].tab ] = 1;
829 if ((b >= 0) && (b & KEY_ALTED) && (alt >= 0)) {
830 Conflicts[j].key = alt;
831 Conflicts[alt].key = j;
832 Conflicts_tabs[ Control_config[j].tab ] = 1;
833 Conflicts_tabs[ Control_config[alt].tab ] = 1;
837 if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) {
838 Conflicts[i].joy = j;
839 Conflicts[j].joy = i;
840 Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
841 Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
848 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
849 Conflicts_axes[i] = -1;
851 for (i=0; i<NUM_JOY_AXIS_ACTIONS-1; i++) {
852 for (j=i+1; j<NUM_JOY_AXIS_ACTIONS; j++) {
853 if ((Axis_map_to[i] >= 0) && (Axis_map_to[i] == Axis_map_to[j])) {
854 Conflicts_axes[i] = j;
855 Conflicts_axes[j] = i;
856 Conflicts_tabs[SHIP_TAB] = 1;
862 // do list setup required prior to rendering and checking for the controls listing. Called when list changes
863 void control_config_list_prepare()
866 int font_height = gr_get_font_height();
868 Num_cc_lines = y = z = 0;
869 while (z < CCFG_MAX) {
870 if ((Control_config[z].tab == Tab) && control_config_valid_action(z)) {
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 );
919 SDL_assert(ptr->index);
920 ptr->list = (config_item *) malloc( sizeof(config_item) * size );
921 SDL_assert(ptr->list);
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);
1072 ptr = get_undo_block(1);
1074 ptr->list[0] = Control_config[z];
1076 if (Selected_item && (Control_config[z].joy_id >= 0)) // if not just key selected (which would be 0)
1077 Control_config[z].joy_id = (short) -1;
1079 if ((Selected_item != 1) && (Control_config[z].key_id >= 0)) // if not just joy button selected (1)
1080 Control_config[z].key_id = -1;
1082 control_config_conflict_check();
1083 control_config_list_prepare();
1084 gamesnd_play_iface(SND_USER_SELECT);
1089 int control_config_clear_other()
1091 int z, i, j, total = 0;
1092 config_item_undo *ptr;
1094 if (Selected_line < 0) {
1095 gamesnd_play_iface(SND_GENERAL_FAIL);
1099 z = Cc_lines[Selected_line].cc_index;
1104 if (Axis_map_to[z] < 0) {
1105 gamesnd_play_iface(SND_GENERAL_FAIL);
1109 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1110 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z))
1114 gamesnd_play_iface(SND_GENERAL_FAIL);
1118 ptr = get_undo_block(total);
1119 for (i=j=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1120 if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
1121 item.joy_id = (short) Axis_map_to[i];
1122 item.used = Invert_axis[i];
1124 ptr->index[j] = i | JOY_AXIS;
1125 ptr->list[j] = item;
1128 Axis_map_to[i] = -1;
1131 control_config_conflict_check();
1132 control_config_list_prepare();
1133 gamesnd_play_iface(SND_USER_SELECT);
1137 for (i=0; i<CCFG_MAX; i++)
1138 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1143 gamesnd_play_iface(SND_GENERAL_FAIL);
1147 if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1148 gamesnd_play_iface(SND_GENERAL_FAIL);
1152 // now, back up the old bindings so we can undo if we want to
1153 ptr = get_undo_block(total);
1154 for (i=j=0; i<CCFG_MAX; i++)
1155 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1158 ptr->list[j] = Control_config[i];
1161 if (Control_config[i].key_id == Control_config[z].key_id)
1162 Control_config[i].key_id = -1;
1163 if (Control_config[i].joy_id == Control_config[z].joy_id)
1164 Control_config[i].joy_id = (short) -1;
1167 control_config_conflict_check();
1168 control_config_list_prepare();
1169 gamesnd_play_iface(SND_USER_SELECT);
1173 int control_config_clear_all()
1175 int i, j, total = 0;
1176 config_item_undo *ptr;
1178 // first, determine how many bindings need to be changed
1179 for (i=0; i<CCFG_MAX; i++)
1180 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0))
1184 gamesnd_play_iface(SND_GENERAL_FAIL);
1188 // now, back up the old bindings so we can undo if we want to
1189 ptr = get_undo_block(total);
1190 for (i=j=0; i<CCFG_MAX; i++) {
1191 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
1193 ptr->list[j] = Control_config[i];
1198 SDL_assert(j == total);
1199 for (i=0; i<CCFG_MAX; i++) {
1200 Control_config[i].key_id = Control_config[i].joy_id = -1;
1203 control_config_conflict_check();
1204 control_config_list_prepare();
1205 gamesnd_play_iface(SND_RESET_PRESSED);
1209 int control_config_axis_default(int axis)
1211 SDL_assert(axis >= 0);
1214 if (Axis_map_to_defaults[axis] < 0)
1217 if ( !joy_axis_valid(Axis_map_to_defaults[axis]) )
1221 return Axis_map_to_defaults[axis];
1224 int control_config_do_reset()
1226 int i, j, total = 0;
1227 config_item_undo *ptr;
1230 // first, determine how many bindings need to be changed
1231 for (i=0; i<CCFG_MAX; i++)
1232 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default))
1235 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1236 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i]))
1240 gamesnd_play_iface(SND_GENERAL_FAIL);
1244 // now, back up the old bindings so we can undo if we want to
1245 ptr = get_undo_block(total);
1246 for (i=j=0; i<CCFG_MAX; i++) {
1247 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
1249 ptr->list[j] = Control_config[i];
1254 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1255 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
1256 item.joy_id = (short) Axis_map_to[i];
1257 item.used = Invert_axis[i];
1259 ptr->index[j] = i | JOY_AXIS;
1260 ptr->list[j] = item;
1264 SDL_assert(j == total);
1265 control_config_reset_defaults();
1266 control_config_conflict_check();
1267 control_config_list_prepare();
1268 gamesnd_play_iface(SND_RESET_PRESSED);
1272 // This sets all the controls to their default values
1273 void control_config_reset_defaults()
1277 // Reset keyboard defaults
1278 for (i=0; i<CCFG_MAX; i++) {
1279 Control_config[i].key_id = Control_config[i].key_default;
1280 Control_config[i].joy_id = Control_config[i].joy_default;
1283 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1284 Axis_map_to[i] = control_config_axis_default(i);
1285 Invert_axis[i] = Invert_axis_defaults[i];
1289 void control_config_scroll_screen_up()
1291 if (Scroll_offset) {
1293 SDL_assert(Selected_line > Scroll_offset);
1294 while (!cc_line_query_visible(Selected_line))
1298 gamesnd_play_iface(SND_SCROLL);
1301 gamesnd_play_iface(SND_GENERAL_FAIL);
1304 void control_config_scroll_line_up()
1306 if (Selected_line) {
1308 if (Selected_line < Scroll_offset)
1309 Scroll_offset = Selected_line;
1312 gamesnd_play_iface(SND_SCROLL);
1315 gamesnd_play_iface(SND_GENERAL_FAIL);
1318 void control_config_scroll_screen_down()
1320 if (Cc_lines[Num_cc_lines - 1].y + gr_get_font_height() > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
1322 while (!cc_line_query_visible(Selected_line)) {
1324 SDL_assert(Selected_line < Num_cc_lines);
1328 gamesnd_play_iface(SND_SCROLL);
1331 gamesnd_play_iface(SND_GENERAL_FAIL);
1334 void control_config_scroll_line_down()
1336 if (Selected_line < Num_cc_lines - 1) {
1338 SDL_assert(Selected_line > Scroll_offset);
1339 while (!cc_line_query_visible(Selected_line))
1343 gamesnd_play_iface(SND_SCROLL);
1346 gamesnd_play_iface(SND_GENERAL_FAIL);
1349 void control_config_toggle_modifier(int bit)
1353 z = Cc_lines[Selected_line].cc_index;
1354 SDL_assert(!(z & JOY_AXIS));
1355 k = Control_config[z].key_id;
1357 gamesnd_play_iface(SND_GENERAL_FAIL);
1361 control_config_bind_key(z, k ^ bit);
1362 control_config_conflict_check();
1363 gamesnd_play_iface(SND_USER_SELECT);
1366 void control_config_toggle_invert()
1370 z = Cc_lines[Selected_line].cc_index;
1371 SDL_assert(z & JOY_AXIS);
1373 control_config_save_axis_undo(z);
1374 Invert_axis[z] = !Invert_axis[z];
1377 void control_config_do_bind()
1382 // if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
1383 if (Selected_line < 0) {
1384 gamesnd_play_iface(SND_GENERAL_FAIL);
1388 for (i=0; i<NUM_BUTTONS; i++)
1389 if (i != CANCEL_BUTTON) {
1390 CC_Buttons[gr_screen.res][i].button.reset_status();
1391 CC_Buttons[gr_screen.res][i].button.disable();
1394 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1395 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(SDLK_ESCAPE);
1397 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1398 joy_down_count(i); // clear checking status of all joystick buttons
1401 control_config_detect_axis_reset();
1404 Bind_time = timer_get_milliseconds();
1408 gamesnd_play_iface(SND_USER_SELECT);
1411 void control_config_do_search()
1415 for (i=0; i<NUM_BUTTONS; i++){
1416 if (i != CANCEL_BUTTON) {
1417 CC_Buttons[gr_screen.res][i].button.reset_status();
1418 CC_Buttons[gr_screen.res][i].button.disable();
1422 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1423 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(SDLK_ESCAPE);
1425 for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1426 joy_down_count(i); // clear checking status of all joystick buttons
1432 gamesnd_play_iface(SND_USER_SELECT);
1435 void control_config_do_cancel(int fail = 0)
1441 for (i=0; i<NUM_BUTTONS; i++){
1442 if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
1443 CC_Buttons[gr_screen.res][i].button.enable();
1447 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.reset_status();
1448 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1449 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
1450 CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
1451 CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
1453 Binding_mode = Search_mode = 0;
1455 gamesnd_play_iface(SND_GENERAL_FAIL);
1457 gamesnd_play_iface(SND_USER_SELECT);
1461 int control_config_accept()
1465 for (i=0; i<NUM_TABS; i++)
1466 if (Conflicts_tabs[i])
1470 gamesnd_play_iface(SND_GENERAL_FAIL);
1474 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1475 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1476 gamesnd_play_iface(SND_COMMIT_PRESSED);
1480 void control_config_cancel_exit()
1484 for (i=0; i<CCFG_MAX; i++)
1485 Control_config[i] = Control_config_backup[i];
1487 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1490 void control_config_button_pressed(int n)
1498 Scroll_offset = Selected_line = 0;
1499 control_config_list_prepare();
1500 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1504 control_config_do_bind();
1508 control_config_do_search();
1512 control_config_toggle_modifier(KEY_SHIFTED);
1513 gamesnd_play_iface(SND_USER_SELECT);
1517 control_config_toggle_modifier(KEY_ALTED);
1518 gamesnd_play_iface(SND_USER_SELECT);
1522 control_config_toggle_invert();
1523 gamesnd_play_iface(SND_USER_SELECT);
1526 case SCROLL_UP_BUTTON:
1527 control_config_scroll_screen_up();
1530 case SCROLL_DOWN_BUTTON:
1531 control_config_scroll_screen_down();
1535 control_config_accept();
1539 control_config_remove_binding();
1543 launch_context_help();
1544 gamesnd_play_iface(SND_HELP_PRESSED);
1548 control_config_do_reset();
1552 control_config_undo_last();
1556 control_config_do_cancel();
1559 case CLEAR_OTHER_BUTTON:
1560 control_config_clear_other();
1563 case CLEAR_ALL_BUTTON:
1564 control_config_clear_all();
1569 const char *control_config_tooltip_handler(const char *str)
1573 if (!SDL_strcasecmp(str, NOX("@conflict"))) {
1574 for (i=0; i<NUM_TABS; i++) {
1575 if (Conflicts_tabs[i])
1576 return XSTR( "Conflict!", 205);
1583 void control_config_init()
1588 // make backup of all controls
1589 for (i=0; i<CCFG_MAX; i++)
1590 Control_config_backup[i] = Control_config[i];
1592 common_set_interface_palette(NOX("ControlConfigPalette")); // set the interface palette
1593 Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1594 Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
1595 Ui_window.tooltip_handler = control_config_tooltip_handler;
1597 // load in help overlay bitmap
1598 help_overlay_load(CONTROL_CONFIG_OVERLAY);
1599 help_overlay_set_state(CONTROL_CONFIG_OVERLAY,0);
1601 // reset conflict flashing
1602 Conflict_stamp = -1;
1605 hud_anim_init(&Conflict_warning_anim, Conflict_warning_coords[gr_screen.res][0], Conflict_warning_coords[gr_screen.res][1], NOX("ConflictFlash"));
1606 hud_anim_load(&Conflict_warning_anim);
1609 for (i=0; i<NUM_BUTTONS; i++) {
1610 b = &CC_Buttons[gr_screen.res][i];
1612 if (b->hotspot < 0) { // temporary
1613 b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1); // temporary
1614 b->button.set_highlight_action(common_play_highlight_sound);
1618 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1620 // set up callback for when a mouse first goes over a button
1621 b->button.set_highlight_action(common_play_highlight_sound);
1623 b->button.set_bmaps(b->filename, 5, 1); // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1625 b->button.set_bmaps(b->filename);
1627 b->button.link_hotspot(b->hotspot);
1632 for(i=0; i<CC_NUM_TEXT; i++){
1633 Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
1637 for (i=0; i<LIST_BUTTONS_MAX; i++) {
1638 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1639 List_buttons[i].hide();
1640 List_buttons[i].disable();
1643 // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1644 CC_Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(SDLK_PAGEUP);
1645 CC_Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(SDLK_PAGEDOWN);
1646 CC_Buttons[gr_screen.res][BIND_BUTTON].button.set_hotkey(SDLK_RETURN);
1647 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_DELETE);
1648 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_z);
1649 CC_Buttons[gr_screen.res][CLEAR_BUTTON].button.set_hotkey(SDLK_DELETE);
1650 CC_Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_RETURN);
1651 CC_Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(SDLK_F1);
1652 CC_Buttons[gr_screen.res][RESET_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_r);
1653 CC_Buttons[gr_screen.res][INVERT_AXIS].button.set_hotkey(SDLK_i);
1655 CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1656 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
1658 Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);
1660 Scroll_offset = Selected_line = 0;
1661 Config_item_undo = NULL;
1662 control_config_conflict_check();
1665 Joy_axis_action_text[0] = strdup(XSTR("Turn (Yaw) Axis", 1016));
1666 Joy_axis_action_text[1] = strdup(XSTR("Pitch Axis", 1017));
1667 Joy_axis_action_text[2] = strdup(XSTR("Bank Axis", 1018));
1668 Joy_axis_action_text[3] = strdup(XSTR("Absolute Throttle Axis", 1019));
1669 Joy_axis_action_text[4] = strdup(XSTR("Relative Throttle Axis", 1020));
1670 Joy_axis_text[0] = strdup(XSTR("Joystick/Mouse X Axis", 1021));
1671 Joy_axis_text[1] = strdup(XSTR("Joystick/Mouse Y Axis", 1022));
1672 Joy_axis_text[2] = strdup(XSTR("Joystick Z Axis", 1023));
1673 Joy_axis_text[3] = strdup(XSTR("Joystick rX Axis", 1024));
1674 Joy_axis_text[4] = strdup(XSTR("Joystick rY Axis", 1025));
1675 Joy_axis_text[5] = strdup(XSTR("Joystick rZ Axis", 1026));
1676 Mouse_button_text[0] = strdup("");
1677 Mouse_button_text[1] = strdup(XSTR("Left Button", 1027));
1678 Mouse_button_text[2] = strdup(XSTR("Right Button", 1028));
1679 Mouse_button_text[3] = strdup(XSTR("Mid Button", 1029));
1680 Mouse_button_text[4] = strdup("");
1681 Mouse_axis_text[0] = strdup(XSTR("L/R", 1030));
1682 Mouse_axis_text[1] = strdup(XSTR("U/B", 1031));
1683 Invert_text[0] = strdup(XSTR("N", 1032));
1684 Invert_text[1] = strdup(XSTR("Y", 1033));
1686 control_config_list_prepare();
1689 void control_config_close()
1693 while (Config_item_undo){
1697 // unload the overlay bitmap
1698 help_overlay_unload(CONTROL_CONFIG_OVERLAY);
1700 if (Background_bitmap){
1701 bm_unload(Background_bitmap);
1705 hud_anim_release(&Conflict_warning_anim);
1708 Ui_window.destroy();
1709 common_free_interface_palette(); // restore game palette
1710 hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1715 for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
1716 if(Joy_axis_action_text[idx] != NULL){
1717 free(Joy_axis_action_text[idx]);
1718 Joy_axis_action_text[idx] = NULL;
1721 for(idx=0; idx<NUM_AXIS_TEXT; idx++){
1722 if(Joy_axis_text[idx] != NULL){
1723 free(Joy_axis_text[idx]);
1724 Joy_axis_text[idx] = NULL;
1727 for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
1728 if(Mouse_button_text[idx] != NULL){
1729 free(Mouse_button_text[idx]);
1730 Mouse_button_text[idx] = NULL;
1733 for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
1734 if(Mouse_axis_text[idx] != NULL){
1735 free(Mouse_axis_text[idx]);
1736 Mouse_axis_text[idx] = NULL;
1739 for(idx=0; idx<NUM_INVERT_TEXT; idx++){
1740 if(Invert_text[idx] != NULL){
1741 free(Invert_text[idx]);
1742 Invert_text[idx] = NULL;
1747 void control_config_do_frame(float frametime)
1750 int i, j, k, w, x, y, z, line, conflict;
1751 int font_height = gr_get_font_height();
1752 int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
1753 static float timer = 0.0f;
1755 static int bound_timestamp = 0;
1756 static char bound_string[40];
1761 if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1764 z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1765 i = control_config_detect_axis();
1772 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1773 Ui_window.process(0);
1775 if (k == SDLK_ESCAPE) {
1776 strcpy(bound_string, XSTR( "Canceled", 206));
1777 bound_timestamp = timestamp(2500);
1778 control_config_do_cancel();
1781 if (k == SDLK_RETURN)
1784 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1785 if (joy_down_count(i))
1789 if (Axis_override >= 0) {
1790 control_config_bind_axis(z, Axis_override);
1791 strcpy(bound_string, Joy_axis_text[Axis_override]);
1792 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1793 bound_timestamp = timestamp(2500);
1794 control_config_conflict_check();
1795 control_config_list_prepare();
1796 control_config_do_cancel();
1799 control_config_do_cancel(1);
1805 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1806 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1807 Ui_window.set_ignore_gadgets(1);
1811 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1812 Ui_window.process(0);
1814 if ( (k > 0) || B1_JUST_RELEASED ) {
1815 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1816 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1817 Ui_window.set_ignore_gadgets(0);
1822 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1823 Ui_window.set_ignore_gadgets(0);
1826 if (k == SDLK_ESCAPE) {
1827 strcpy(bound_string, XSTR( "Canceled", 206));
1828 bound_timestamp = timestamp(2500);
1829 control_config_do_cancel();
1832 switch (k & KEY_MASK) {
1837 Last_key = k & KEY_MASK;
1842 if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED) // a special hack just for Mike K.
1843 if ( (Last_key >= 0) && (k <= 0) && !key_pressed(Last_key) )
1846 if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1847 popup(0, 1, POPUP_OK, XSTR( "That is a non-bindable key. Please try again.", 207));
1851 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1853 z = Cc_lines[Selected_line].cc_index;
1854 SDL_assert(!(z & JOY_AXIS));
1855 control_config_bind_key(z, k);
1857 strcpy(bound_string, textify_scancode(k));
1858 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1859 bound_timestamp = timestamp(2500);
1860 control_config_conflict_check();
1861 control_config_list_prepare();
1862 control_config_do_cancel();
1865 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1866 if (joy_down_count(i)) {
1867 z = Cc_lines[Selected_line].cc_index;
1868 SDL_assert(!(z & JOY_AXIS));
1869 control_config_bind_joy(z, i);
1871 strcpy(bound_string, Joy_button_text[i]);
1872 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1873 bound_timestamp = timestamp(2500);
1874 control_config_conflict_check();
1875 control_config_list_prepare();
1876 control_config_do_cancel();
1880 if (Bind_time + 375 < timer_get_milliseconds()) {
1881 for (i=0; i<NUM_BUTTONS; i++){
1882 if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
1887 if (i == NUM_BUTTONS) { // no buttons pressed
1888 for (i=0; i<MOUSE_NUM_BUTTONS; i++)
1889 if (mouse_down(1 << i)) {
1890 z = Cc_lines[Selected_line].cc_index;
1891 SDL_assert(!(z & JOY_AXIS));
1892 control_config_bind_joy(z, i);
1894 strcpy(bound_string, Joy_button_text[i]);
1895 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1896 bound_timestamp = timestamp(2500);
1897 control_config_conflict_check();
1898 control_config_list_prepare();
1899 control_config_do_cancel();
1900 for (j=0; j<NUM_BUTTONS; j++){
1901 CC_Buttons[gr_screen.res][j].button.reset();
1911 } else if (Search_mode) {
1912 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1913 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1914 Ui_window.set_ignore_gadgets(1);
1918 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1919 Ui_window.process(0);
1921 if ( (k > 0) || B1_JUST_RELEASED ) {
1922 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1923 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1924 Ui_window.set_ignore_gadgets(0);
1929 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1930 Ui_window.set_ignore_gadgets(0);
1933 if (k == SDLK_ESCAPE) {
1934 control_config_do_cancel();
1937 if ((k > 0) && !Config_allowed[k & KEY_MASK])
1940 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1943 for (i=0; i<CCFG_MAX; i++)
1944 if (Control_config[i].key_id == k) {
1950 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1951 if (joy_down_count(i)) {
1953 for (i=0; i<CCFG_MAX; i++)
1954 if (Control_config[i].joy_id == j) {
1962 // check if not on enabled button
1963 for (j=0; j<NUM_BUTTONS; j++){
1964 if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
1969 if (j == NUM_BUTTONS) { // no buttons pressed
1970 for (j=0; j<MOUSE_NUM_BUTTONS; j++)
1971 if (mouse_down(1 << j)) {
1972 for (i=0; i<CCFG_MAX; i++)
1973 if (Control_config[i].joy_id == j) {
1975 for (j=0; j<NUM_BUTTONS; j++){
1976 CC_Buttons[gr_screen.res][j].button.reset();
1986 Tab = Control_config[z].tab;
1987 control_config_list_prepare();
1988 Selected_line = Scroll_offset = 0;
1989 for (i=0; i<Num_cc_lines; i++)
1990 if (Cc_lines[i].cc_index == z) {
1995 while (!cc_line_query_visible(Selected_line)) {
1997 SDL_assert(Scroll_offset < Num_cc_lines);
2003 z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
2004 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
2005 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
2006 CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
2009 z = Cc_lines[Selected_line].cc_index;
2010 k = Control_config[z].key_id;
2011 if ( (k == SDLK_LALT) || (k == SDLK_RALT) || (k == SDLK_LSHIFT) || (k == SDLK_RSHIFT) ) {
2012 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
2013 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
2017 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
2019 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2020 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
2021 Ui_window.set_ignore_gadgets(1);
2024 k = Ui_window.process();
2026 if ( (k > 0) || B1_JUST_RELEASED ) {
2027 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2028 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
2029 Ui_window.set_ignore_gadgets(0);
2034 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2035 Ui_window.set_ignore_gadgets(0);
2039 case SDLK_DOWN: // select next line
2040 control_config_scroll_line_down();
2043 case SDLK_UP: // select previous line
2044 control_config_scroll_line_up();
2047 case KEY_SHIFTED | SDLK_TAB: // activate previous tab
2052 Scroll_offset = Selected_line = 0;
2053 control_config_list_prepare();
2054 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2057 case SDLK_TAB: // activate next tab
2059 if (Tab >= NUM_TABS)
2062 Scroll_offset = Selected_line = 0;
2063 control_config_list_prepare();
2064 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2069 if (Selected_item == -2) {
2071 if (Cc_lines[Selected_line].jw < 1) {
2073 if (Cc_lines[Selected_line].kw < 1)
2078 gamesnd_play_iface(SND_SCROLL);
2083 if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1))
2085 else if (!Selected_item && (Cc_lines[Selected_line].kw < 1))
2087 else if (Selected_item > 1)
2090 gamesnd_play_iface(SND_SCROLL);
2093 case SDLK_BACKSPACE: // undo
2094 control_config_undo_last();
2098 control_config_cancel_exit();
2103 for (i=0; i<NUM_BUTTONS; i++){
2104 if (CC_Buttons[gr_screen.res][i].button.pressed()){
2105 control_config_button_pressed(i);
2109 for (i=0; i<LIST_BUTTONS_MAX; i++) {
2110 if (List_buttons[i].is_mouse_on())
2111 select_tease_line = i + Scroll_offset;
2113 if (List_buttons[i].pressed()) {
2114 Selected_line = i + Scroll_offset;
2116 List_buttons[i].get_mouse_pos(&x, &y);
2117 if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw))
2120 if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw))
2123 gamesnd_play_iface(SND_USER_SELECT);
2126 if (List_buttons[i].double_clicked())
2127 control_config_do_bind();
2130 GR_MAYBE_CLEAR_RES(Background_bitmap);
2131 if (Background_bitmap >= 0) {
2132 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2136 // highlight tab with conflict
2138 for (i=z=0; i<NUM_TABS; i++) {
2139 if (Conflicts_tabs[i]) {
2140 CC_Buttons[gr_screen.res][i].button.draw_forced(4);
2146 // maybe switch from bright to normal
2147 if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
2148 Conflict_bright = !Conflict_bright;
2150 Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
2154 // set color and font
2156 if(Conflict_bright){
2157 gr_set_color_fast(&Color_bright_red);
2159 gr_set_color_fast(&Color_red);
2162 // setup the conflict string
2163 char conflict_str[512] = "";
2164 strncpy(conflict_str, XSTR("Conflict!", 205), 511);
2166 gr_get_string_size(&sw, &sh, conflict_str);
2168 gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str);
2172 hud_anim_render(&Conflict_warning_anim, frametime);
2175 // might as well always reset the conflict stamp
2176 Conflict_stamp = -1;
2179 for (i=0; i<NUM_TABS; i++) {
2180 if (CC_Buttons[gr_screen.res][i].button.button_down()) {
2185 if (i == NUM_TABS) {
2186 CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
2190 CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
2193 if (Selected_line >= 0) {
2194 z = Cc_lines[Selected_line].cc_index;
2196 if (Invert_axis[z & ~JOY_AXIS]) {
2197 CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
2201 z = Control_config[z].key_id;
2203 if (z & KEY_SHIFTED) {
2204 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
2206 if (z & KEY_ALTED) {
2207 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
2214 CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
2217 z = Cc_lines[Selected_line].cc_index;
2218 x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2;
2219 y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2;
2223 t = (int) (timer * 3);
2225 gr_set_color_fast(&Color_text_normal);
2226 gr_get_string_size(&w, NULL, XSTR( "?", 208));
2227 gr_printf(x - w / 2, y - font_height / 2, XSTR( "?", 208));
2230 } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
2231 i = Conflicts[z].key;
2233 i = Conflicts[z].joy;
2235 gr_set_color_fast(&Color_text_normal);
2236 const char *str = XSTR( "Control conflicts with:", 209);
2237 gr_get_string_size(&w, NULL, str);
2238 gr_printf(x - w / 2, y - font_height, str);
2240 strcpy(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
2241 gr_force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
2242 gr_get_string_size(&w, NULL, buf);
2243 gr_printf(x - w / 2, y, buf);
2245 } else if (*bound_string) {
2246 gr_set_color_fast(&Color_text_normal);
2247 gr_get_string_size(&w, NULL, bound_string);
2248 gr_printf(x - w / 2, y - font_height / 2, bound_string);
2249 if (timestamp_elapsed(bound_timestamp))
2253 // gr_set_color_fast(&Color_text_heading);
2254 // gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]);
2256 // gr_get_string_size(&w, &h, Heading[Tab]);
2257 // y = HEADING_Y + h / 2 - 1;
2258 // gr_line(LIST_X, y, LIST_X + 18, y);
2259 // gr_line(LIST_X + w + 21, y, LIST_X + LIST_W, y);
2261 if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
2262 gr_set_color_fast(&Color_white);
2263 gr_printf(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210));
2267 line = Scroll_offset;
2268 while (cc_line_query_visible(line)) {
2269 z = Cc_lines[line].cc_index;
2270 y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
2272 List_buttons[line - Scroll_offset].update_dimensions(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, Control_list_coords[gr_screen.res][CONTROL_W_COORD], font_height);
2273 List_buttons[line - Scroll_offset].enable(!Binding_mode);
2275 Cc_lines[line].kw = Cc_lines[line].jw = 0;
2277 if (line == Selected_line){
2278 c = &Color_text_selected;
2279 } else if (line == select_tease_line) {
2280 c = &Color_text_subselected;
2282 c = &Color_text_normal;
2285 gr_set_color_fast(c);
2286 if (Cc_lines[line].label) {
2287 strcpy(buf, Cc_lines[line].label);
2288 gr_force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
2289 gr_printf(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
2292 if (!(z & JOY_AXIS)) {
2293 k = Control_config[z].key_id;
2294 j = Control_config[z].joy_id;
2295 x = Control_list_key_x[gr_screen.res];
2298 if ((k < 0) && (j < 0)) {
2299 gr_set_color_fast(&Color_grey);
2300 gr_printf(x, y, XSTR( "None", 211));
2304 strcpy(buf, textify_scancode(k));
2305 if (Conflicts[z].key >= 0) {
2306 if (c == &Color_text_normal)
2307 gr_set_color_fast(&Color_text_error);
2309 gr_set_color_fast(&Color_text_error_hi);
2313 } else if (Selected_item == 1) {
2314 gr_set_color_fast(&Color_text_normal);
2317 gr_set_color_fast(c);
2319 gr_printf(x, y, buf);
2321 Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2322 gr_get_string_size(&w, NULL, buf);
2323 Cc_lines[line].kw = w;
2327 gr_set_color_fast(&Color_text_normal);
2328 gr_printf(x, y, XSTR( ", ", 212));
2329 gr_get_string_size(&w, NULL, XSTR( ", ", 212));
2335 strcpy(buf, Joy_button_text[j]);
2336 if (Conflicts[z].joy >= 0) {
2337 if (c == &Color_text_normal)
2338 gr_set_color_fast(&Color_text_error);
2340 gr_set_color_fast(&Color_text_error_hi);
2344 } else if (!Selected_item) {
2345 gr_set_color_fast(&Color_text_normal);
2348 gr_set_color_fast(c);
2350 gr_force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x);
2351 gr_printf(x, y, buf);
2353 Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2354 gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
2359 x = Control_list_key_x[gr_screen.res];
2360 j = Axis_map_to[z & ~JOY_AXIS];
2361 if (Binding_mode && (line == Selected_line))
2365 gr_set_color_fast(&Color_grey);
2366 gr_printf(x, y, XSTR( "None", 211));
2369 if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
2370 if (c == &Color_text_normal)
2371 gr_set_color_fast(&Color_text_error);
2374 gr_set_color_fast(&Color_text_error_hi);
2378 } else if (!Selected_item) {
2379 gr_set_color_fast(&Color_text_normal);
2382 gr_set_color_fast(c);
2384 gr_string(x, y, Joy_axis_text[j]);
2391 CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
2393 i = line - Scroll_offset;
2394 while (i < LIST_BUTTONS_MAX)
2395 List_buttons[i++].disable();
2397 // blit help overlay if active
2398 help_overlay_maybe_blit(CONTROL_CONFIG_OVERLAY);
2403 void clear_key_binding(short key)
2407 for (i=0; i<CCFG_MAX; i++) {
2408 if (Control_config[i].key_id == key)
2409 Control_config[i].key_id = -1;
2413 float check_control_timef(int id)
2417 // if type isn't continuous, we shouldn't be using this function, cause it won't work.
2418 SDL_assert(Control_config[id].type == CC_TYPE_CONTINUOUS);
2420 // first, see if control actually used (makes sure modifiers match as well)
2421 if (!check_control(id))
2424 t1 = key_down_timef(Control_config[id].key_id);
2428 t2 = joy_down_time(Control_config[id].joy_id);
2438 void control_check_indicate()
2441 if (Show_controls_info) {
2442 gr_set_color_fast(&HUD_color_debug);
2443 gr_printf(490, 15, NOX("Ctrls checked: %d"), Control_check_count);
2447 Control_check_count = 0;
2450 int check_control(int id, int key)
2453 static int last_key = 0;
2455 Control_check_count++;
2461 // if we're in multiplayer text enter (for chat) mode, check to see if we should ignore controls
2462 if ((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls()){
2466 if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
2467 if (joy_down(Control_config[id].joy_id) || joy_down_count(Control_config[id].joy_id)) {
2472 if ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS))
2473 if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2478 // check what current modifiers are pressed
2479 int tmp = key_get_shift_status();
2482 if (tmp & KEY_SHIFTED) {
2483 mask |= KEY_SHIFTED;
2486 if (tmp & KEY_ALTED) {
2490 z = Control_config[id].key_id;
2492 if ( (z != SDLK_LALT) && (z != SDLK_RALT) && (z != SDLK_LSHIFT) && (z != SDLK_RSHIFT) ) {
2493 // if current modifiers don't match action's modifiers, don't register control active.
2494 if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask)
2500 if (key_pressed(z) || key_down_count(z)) {
2501 if ( !hud_squadmsg_read_key(z) ) {
2511 if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2519 // get heading, pitch, bank, throttle abs. and throttle rel. values.
2520 void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
2522 // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
2524 if (Axis_map_to[0] >= 0)
2525 *h = joy_get_scaled_reading(Axis_map_to[0]);
2528 if (Axis_map_to[1] >= 0)
2529 *p = joy_get_scaled_reading(Axis_map_to[1]);
2532 if (Axis_map_to[2] >= 0)
2533 *b = joy_get_scaled_reading(Axis_map_to[2]);
2536 if (Axis_map_to[3] >= 0)
2537 *ta = joy_get_unscaled_reading(Axis_map_to[3]);
2540 if (Axis_map_to[4] >= 0)
2541 *tr = joy_get_scaled_reading(Axis_map_to[4]);
2557 void control_used(int id)
2559 Control_config[id].used = timestamp();
2562 void control_config_clear_used_status()
2566 for (i=0; i<CCFG_MAX; i++)
2567 Control_config[i].used = 0;
2570 void control_config_clear()
2574 // Reset keyboard defaults
2575 for (i=0; i<CCFG_MAX; i++)
2576 Control_config[i].key_id = Control_config[i].joy_id = -1;
2579 int control_config_handle_conflict()
2581 if ((Selected_item == -1) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) { // we are deleting a conflict
2582 j = Conflicts[z].joy;
2583 if ((j >= 0) && (Control_config[j].joy_id < 0))
2586 k = Conflicts[z].key;
2587 if ((k >= 0) && (Control_config[k].key_id < 0))
2590 if ((j >= 0) && (k >= 0) && (j != k)) { // deleting 2 conflicts, each in different actions
2591 ptr = get_undo_block(2);
2593 ptr->list[0] = Control_config[j];
2594 Control_config[j].joy_id = (short) -1;
2597 ptr->list[1] = Control_config[k];
2598 Control_config[k].key_id = (short) -1;
2600 } else { // only 1 action in conflict with selected action (might be both controls, though)
2606 ptr = get_undo_block(1);
2608 ptr->list[0] = Control_config[z];
2611 Control_config[z].joy_id = (short) -1;
2614 Control_config[z].key_id = (short) -1;