FS2_Open
Open source remastering of the Freespace 2 engine
controlsconfig.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 1999. All rights reserved.
3  *
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 the
6  * source.
7  *
8 */
9 
10 
11 
12 
14 #include "debugconsole/console.h"
15 #include "freespace2/freespace.h"
16 #include "gamehelp/contexthelp.h"
18 #include "gamesnd/gamesnd.h"
19 #include "globalincs/alphacolors.h"
20 #include "graphics/font.h"
21 #include "hud/hudsquadmsg.h"
22 #include "io/joy.h"
23 #include "io/key.h"
24 #include "io/timer.h"
26 #include "network/multi_pmsg.h"
27 #include "network/multiutil.h"
28 #include "parse/scripting.h"
29 #include "pilotfile/pilotfile.h"
30 #include "popup/popup.h"
31 #include "ui/ui.h"
32 #include "ui/uidefs.h"
33 
34 
35 #ifndef NDEBUG
36 #include "hud/hud.h"
37 #endif
38 
39 
40 
41 #define NUM_SYSTEM_KEYS 14
42 #define NUM_BUTTONS 19
43 #define NUM_TABS 4
44 
45 // coordinate indicies
46 #define CONTROL_X_COORD 0
47 #define CONTROL_Y_COORD 1
48 #define CONTROL_W_COORD 2
49 #define CONTROL_H_COORD 3
50 
52  "ControlConfig", // GR_640
53  "2_ControlConfig" // GR_1024
54 };
55 
57  "ControlConfig-m", // GR_640
58  "2_ControlConfig-m" // GR_1024
59 };
60 
61 // control list area
63  {
64  32, 58, 198, 259 // GR_640
65  },
66  {
67  32, 94, 904, 424 // GR_1024
68  }
69 };
70 
71 // width of the control name section of the list
73  350, // GR_640
74  600 // GR_1024
75 };
76 
77 // x start position of the binding area section of the list
79  397, // GR_640
80  712 // GR_1024
81 };
82 
83 // width of the binding area section of the list
85  198, // GR_640
86  230 // GR_1024
87 };
88 
89 // display the "more..." text under the control list
91  {
92  320, 326 // GR_640
93  },
94  {
95  500, 542 // GR_1024
96  }
97 };
98 
99 // area to display "conflicts with..." text
101  {
102  32, 313, 250, 32 // GR_640
103  },
104  {
105  48, 508, 354, 46 // GR_1024
106  }
107 };
108 
109 // conflict warning anim coords
111  {
112  -1, 420 // GR_640
113  },
114  {
115  -1, 669 // GR_1024
116  }
117 };
118 
119 // for flashing the conflict text
120 #define CONFLICT_FLASH_TIME 250
121 int Conflict_stamp = -1;
123 
124 #define LIST_BUTTONS_MAX 42
125 #define JOY_AXIS 0x80000
126 
127 static int Num_cc_lines;
128 static struct {
129  const char *label;
130  int cc_index; // index into Control_config of item
131  int y; // Y coordinate of line
132  int kx, kw, jx, jw; // x start and width of keyboard and joystick bound text
133 } Cc_lines[CCFG_MAX];
134 
135 // struct to hold backup config_item elements so we can undo them
137  int size;
138  int *index; // array (size) of Control_config indices of replaced elements
139  config_item *list; // array (size) of original elements
140  int reset_to_preset; // if >=0, then we ignore the above list and simply reset to the given preset instead
142 };
143 
145 
148 
149 // all this stuff is localized/externalized
150 #define NUM_AXIS_TEXT 6
151 #define NUM_MOUSE_TEXT 5
152 #define NUM_MOUSE_AXIS_TEXT 2
153 #define NUM_INVERT_TEXT 2
159 
163 };
164 
166 
167 static int Tab; // which tab we are currently in
168 static int Binding_mode = 0; // are we waiting for a key to bind it?
169 static int Bind_time = 0;
170 static int Search_mode = 0; // are we waiting for a key to bind it?
171 static int Last_key = -1;
172 static int Selected_line = 0; // line that is currently selected for binding
173 static int Selected_item = -1; // -1 = none, 0 = key, 1 = button
174 static int Scroll_offset;
175 static int Axis_override = -1;
176 static int Background_bitmap;
177 static int Conflicts_tabs[NUM_TABS];
178 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list
179 static UI_WINDOW Ui_window;
180 static unsigned int Defaults_cycle_pos; // the controls preset that was last selected
181 
183 
184 static struct {
185  int key; // index of other control in conflict with this one
186  int joy; // index of other control in conflict with this one
187 } Conflicts[CCFG_MAX];
188 
190 
191 #define TARGET_TAB 0
192 #define SHIP_TAB 1
193 #define WEAPON_TAB 2
194 #define COMPUTER_TAB 3
195 #define SCROLL_UP_BUTTON 4
196 #define SCROLL_DOWN_BUTTON 5
197 #define ALT_TOGGLE 6
198 #define SHIFT_TOGGLE 7
199 #define INVERT_AXIS 8
200 #define CANCEL_BUTTON 9
201 #define UNDO_BUTTON 10
202 #define RESET_BUTTON 11
203 #define SEARCH_MODE 12
204 #define BIND_BUTTON 13
205 #define HELP_BUTTON 14
206 #define ACCEPT_BUTTON 15
207 #define CLEAR_OTHER_BUTTON 16
208 #define CLEAR_ALL_BUTTON 17
209 #define CLEAR_BUTTON 18
210 
212  { // GR_640
213  ui_button_info("CCB_00", 32, 348, 17, 384, 0), // target tab
214  ui_button_info("CCB_01", 101, 348, 103, 384, 1), // ship tab
215  ui_button_info("CCB_02", 173, 352, 154, 384, 2), // weapon tab
216  ui_button_info("CCB_03", 242, 347, 244, 384, 3), // computer/misc tab
217  ui_button_info("CCB_04", 614, 73, -1, -1, 4), // scroll up
218  ui_button_info("CCB_05", 614, 296, -1, -1, 5), // scroll down
219  ui_button_info("CCB_06", 17, 452, 12, 440, 6), // alt toggle
220  ui_button_info("CCB_07", 56, 452, 50, 440, 7), // shift toggle
221  ui_button_info("CCB_09", 162, 452, 155, 440, 9), // invert
222  ui_button_info("CCB_10", 404, 1, 397, 45, 10), // cancel
223  ui_button_info("CCB_11", 582, 347, 586, 386, 11), // undo
224  ui_button_info("CCB_12", 576, 1, 578, 45, 12), // default
225  ui_button_info("CCB_13", 457, 4, 453, 45, 13), // search
226  ui_button_info("CCB_14", 516, 4, 519, 45, 14), // bind
227  ui_button_info("CCB_15", 540, 428, 500, 440, 15), // help
228  ui_button_info("CCB_16", 574, 432, 571, 412, 16), // accept
229  ui_button_info("CCB_18", 420, 346, 417, 386, 18), // clear other
230  ui_button_info("CCB_19", 476, 346, 474, 386, 19), // clear all
231  ui_button_info("CCB_20", 524, 346, 529, 386, 20), // clear button
232  },
233  { // GR_1024
234  ui_button_info("2_CCB_00", 51, 557, 27, 615, 0), // target tab
235  ui_button_info("2_CCB_01", 162, 557, 166, 615, 1), // ship tab
236  ui_button_info("2_CCB_02", 277, 563, 246, 615, 2), // weapon tab
237  ui_button_info("2_CCB_03", 388, 555, 391, 615, 3), // computer/misc tab
238  ui_button_info("2_CCB_04", 982, 117, -1, -1, 4), // scroll up
239  ui_button_info("2_CCB_05", 982, 474, -1, -1, 5), // scroll down
240  ui_button_info("2_CCB_06", 28, 723, 24, 704, 6), // alt toggle
241  ui_button_info("2_CCB_07", 89, 723, 80, 704, 7), // shift toggle
242  ui_button_info("2_CCB_09", 260, 723, 249, 704, 9), // invert
243  ui_button_info("2_CCB_10", 646, 2, 635, 71, 10), // cancel
244  ui_button_info("2_CCB_11", 932, 555, 938, 619, 11), // undo
245  ui_button_info("2_CCB_12", 921, 1, 923, 71, 12), // default
246  ui_button_info("2_CCB_13", 732, 6, 726, 71, 13), // search
247  ui_button_info("2_CCB_14", 825, 6, 831, 71, 14), // bind
248  ui_button_info("2_CCB_15", 864, 685, 800, 704, 15), // help
249  ui_button_info("2_CCB_16", 919, 692, 914, 660, 16), // accept
250  ui_button_info("2_CCB_18", 672, 553, 668, 619, 18), // clear other
251  ui_button_info("2_CCB_19", 761, 553, 749, 619, 19), // clear all
252  ui_button_info("2_CCB_20", 838, 553, 846, 619, 20), // clear button
253  }
254 };
255 
256 // strings
257 #define CC_NUM_TEXT 20
259  { // GR_640
260  { "Targeting", 1340, 17, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][TARGET_TAB].button },
261  { "Ship", 1341, 103, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIP_TAB].button },
262  { "Weapons", 1065, 154, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][WEAPON_TAB].button },
263  { "Misc", 1411, 244, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][COMPUTER_TAB].button },
264  { "Alt", 1510, 12, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][ALT_TOGGLE].button },
265  { "Shift", 1511, 50, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIFT_TOGGLE].button },
266  { "Invert", 1342, 155, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][INVERT_AXIS].button },
267  { "Cancel", 641, 397, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CANCEL_BUTTON].button },
268  { "Undo", 1343, 586, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][UNDO_BUTTON].button },
269  { "Defaults", 1344, 568, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][RESET_BUTTON].button },
270  { "Search", 1345, 453, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SEARCH_MODE].button },
271  { "Bind", 1346, 519, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][BIND_BUTTON].button },
272  { "Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][HELP_BUTTON].button },
273  { "Accept", 1035, 571, 412, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][ACCEPT_BUTTON].button },
274  { "Clear", 1347, 417, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
275  { "Conflict", 1348, 406, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
276  { "Clear", 1413, 474, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
277  { "All", 1349, 483, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
278  { "Clear", 1414, 529, 388, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
279  { "Selected", 1350, 517, 396, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
280  },
281  { // GR_1024
282  { "Targeting", 1340, 47, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][TARGET_TAB].button },
283  { "Ship", 1341, 176, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIP_TAB].button },
284  { "Weapons", 1065, 266, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][WEAPON_TAB].button },
285  { "Misc", 1411, 401, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][COMPUTER_TAB].button },
286  { "Alt", 1510, 29, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][ALT_TOGGLE].button },
287  { "Shift", 1511, 85, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIFT_TOGGLE].button },
288  { "Invert", 1342, 254, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][INVERT_AXIS].button },
289  { "Cancel", 641, 655, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CANCEL_BUTTON].button },
290  { "Undo", 1343, 938, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][UNDO_BUTTON].button },
291  { "Defaults", 1344, 923, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][RESET_BUTTON].button },
292  { "Search", 1345, 746, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SEARCH_MODE].button },
293  { "Bind", 1346, 846, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][BIND_BUTTON].button },
294  { "Help", 928, 800, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][HELP_BUTTON].button },
295  { "Accept", 1035, 914, 660, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][ACCEPT_BUTTON].button },
296  { "Clear", 1347, 683, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
297  { "Conflict", 1348, 666, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
298  { "Clear", 1413, 759, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
299  { "All", 1349, 772, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
300  { "Clear", 1414, 871, 619, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
301  { "Selected", 1350, 852, 634, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
302  }
303 };
304 
305 // linked list head of undo items
307 
308 // same indices as Scan_code_text[]. Indicates if a scancode is allowed to be bound.
309 int Config_allowed[] = {
310  0, 0, 1, 1, 1, 1, 1, 1,
311  1, 1, 1, 1, 1, 1, 1, 1,
312  1, 1, 1, 1, 1, 1, 1, 1,
313  1, 1, 1, 1, 1, 1, 1, 1,
314 
315  1, 1, 1, 1, 1, 1, 1, 1,
316  1, 0, 1, 1, 1, 1, 1, 1,
317  1, 1, 1, 1, 1, 1, 1, 1,
318  1, 1, 1, 0, 0, 0, 0, 0,
319 
320  0, 0, 0, 0, 0, 0, 1, 1,
321  1, 1, 1, 1, 1, 1, 1, 1,
322  1, 1, 1, 1, 0, 0, 0, 0,
323  0, 0, 0, 0, 0, 0, 0, 0,
324 
325  0, 0, 0, 0, 0, 0, 0, 0,
326  0, 0, 0, 0, 0, 0, 0, 0,
327  0, 0, 0, 0, 0, 0, 0, 0,
328  0, 0, 0, 0, 0, 0, 0, 0,
329 
330  0, 0, 0, 0, 0, 0, 0, 0,
331  0, 0, 0, 0, 0, 0, 0, 0,
332  0, 0, 0, 0, 0, 0, 0, 0,
333  0, 0, 0, 0, 1, 1, 0, 0,
334 
335  0, 0, 0, 0, 0, 0, 0, 0,
336  0, 0, 0, 0, 0, 0, 0, 0,
337  0, 0, 0, 0, 0, 1, 0, 0,
338  1, 0, 0, 0, 0, 0, 0, 0,
339 
340  0, 0, 0, 0, 0, 0, 0, 1,
341  1, 1, 0, 1, 0, 1, 0, 1,
342  1, 1, 1, 1, 0, 0, 0, 0,
343  0, 0, 0, 0, 0, 0, 0, 0,
344 
345  0, 0, 0, 0, 0, 0, 0, 0,
346  0, 0, 0, 0, 0, 0, 0, 0,
347  0, 0, 0, 0, 0, 0, 0, 0,
348  0, 0, 0, 0, 0, 0, 0, 0,
349 };
350 
351 #ifndef NDEBUG
353 
354 DCF_BOOL(show_controls_info, Show_controls_info);
355 #endif
356 
357 static int Axes_origin[JOY_NUM_AXES];
358 
360 {
361  joystick_read_raw_axis(JOY_NUM_AXES, Axes_origin);
362 }
363 
365 {
366  int i, d, axis = -1, delta = 16384;
367  int axes_values[JOY_NUM_AXES];
368  int dx, dy, dz, fudge = 7;
369 
370  joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
371  for (i=0; i<JOY_NUM_AXES; i++) {
372  d = abs(axes_values[i] - Axes_origin[i]);
373  if (d > delta) {
374  axis = i;
375  delta = d;
376  }
377  }
378 
379  if ( (axis == -1) && Use_mouse_to_fly ) {
380  mouse_get_delta( &dx, &dy, &dz );
381 
382  if ( (dx > fudge) || (dx < -fudge) ) {
383  axis = 0;
384  } else if ( (dy > fudge) || (dy < -fudge) ) {
385  axis = 1;
386  } else if ( (dz > fudge) || (dz < -fudge) ) {
387  axis = 2;
388  }
389  }
390 
391  return axis;
392 }
393 
395 {
396  int i, j;
397 
398  for (i=0; i<CCFG_MAX; i++) {
399  Conflicts[i].key = Conflicts[i].joy = -1;
400  }
401 
402  for (i=0; i<NUM_TABS; i++) {
403  Conflicts_tabs[i] = 0;
404  }
405 
406  for (i=0; i<CCFG_MAX-1; i++) {
407  for (j=i+1; j<CCFG_MAX; j++) {
408  if ((Control_config[i].key_id >= 0) && (Control_config[i].key_id == Control_config[j].key_id)) {
409  // If one of the conflicting keys is disabled, then this silently clears
410  // the disabled key to prevent the conflict; a conflict is recorded only
411  // if both keys are enabled
412 
413  if (Control_config[i].disabled)
414  Control_config[i].key_id = (short) -1;
415  else if (Control_config[j].disabled)
416  Control_config[j].key_id = (short) -1;
417  else {
418  Conflicts[i].key = j;
419  Conflicts[j].key = i;
420  Conflicts_tabs[ Control_config[i].tab ] = 1;
421  Conflicts_tabs[ Control_config[j].tab ] = 1;
422  }
423  }
424 
425  if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) {
426  // Same as above
427 
428  if (Control_config[i].disabled)
429  Control_config[i].joy_id = (short) -1;
430  else if (Control_config[j].disabled)
431  Control_config[j].joy_id = (short) -1;
432  else {
433  Conflicts[i].joy = j;
434  Conflicts[j].joy = i;
435  Conflicts_tabs[ Control_config[i].tab ] = 1;
436  Conflicts_tabs[ Control_config[j].tab ] = 1;
437  }
438  }
439  }
440  }
441 
442  for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
443  Conflicts_axes[i] = -1;
444  }
445 
446  for (i=0; i<NUM_JOY_AXIS_ACTIONS-1; i++) {
447  for (j=i+1; j<NUM_JOY_AXIS_ACTIONS; j++) {
448  if ((Axis_map_to[i] >= 0) && (Axis_map_to[i] == Axis_map_to[j])) {
449  Conflicts_axes[i] = j;
450  Conflicts_axes[j] = i;
451  Conflicts_tabs[SHIP_TAB] = 1;
452  }
453  }
454  }
455 }
456 
457 // do list setup required prior to rendering and checking for the controls listing. Called when list changes
459 {
460  int j, y, z;
461  int font_height = gr_get_font_height();
462 
463  Num_cc_lines = y = z = 0;
464  while (z < CCFG_MAX) {
465  if (Control_config[z].tab == Tab && !Control_config[z].disabled) {
466  if (Control_config[z].hasXSTR) {
467  Cc_lines[Num_cc_lines].label = XSTR(Control_config[z].text, CONTROL_CONFIG_XSTR + z);
468  } else {
469  Cc_lines[Num_cc_lines].label = Control_config[z].text;
470  }
471 
472  Cc_lines[Num_cc_lines].cc_index = z;
473  Cc_lines[Num_cc_lines++].y = y;
474  y += font_height + 2;
475  }
476 
477  z++;
478  }
479 
480  if (Tab == SHIP_TAB) {
481  for (j=0; j<NUM_JOY_AXIS_ACTIONS; j++) {
482  Cc_lines[Num_cc_lines].label = Joy_axis_action_text[j];
483  Cc_lines[Num_cc_lines].cc_index = j | JOY_AXIS;
484  Cc_lines[Num_cc_lines++].y = y;
485  y += font_height + 2;
486  }
487  }
488 }
489 
491 {
492  int y;
493 
494  if ((n < 0) || (n >= Num_cc_lines)) {
495  return 0;
496  }
497 
498  y = Cc_lines[n].y - Cc_lines[Scroll_offset].y;
500  return 0;
501  }
502 
503  return 1;
504 }
505 
506 // allocates the required space for one undo block and put it in the beginning of the linked list (top of a stack).
507 // Returns a pointer to this newly allocated block
509 {
510  config_item_undo *ptr;
511 
512  ptr = (config_item_undo *) vm_malloc( sizeof(config_item_undo) );
513  Assert(ptr);
514  ptr->next = Config_item_undo;
515  Config_item_undo = ptr;
516 
517  ptr->size = size;
518  if (size) {
519  ptr->index = (int *) vm_malloc( sizeof(int) * size );
520  Assert(ptr->index);
521  ptr->list = (config_item *) vm_malloc( sizeof(config_item) * size );
522  Assert(ptr->list);
523 
524  } else {
525  ptr->index = NULL;
526  ptr->list = NULL;
527  }
528 
529  ptr->reset_to_preset = -1;
530 
531  return ptr;
532 }
533 
534 // frees one undo block. The first one in the list (top of the stack) to be precise.
536 {
537  config_item_undo *ptr;
538 
539  ptr = Config_item_undo;
540  if (!ptr) {
541  return;
542  }
543 
544  Config_item_undo = ptr->next;
545  if (ptr->size) {
546  vm_free(ptr->list);
547  vm_free(ptr->index);
548  }
549 
550  vm_free(ptr);
551 }
552 
553 // undo the most recent binding changes
555 {
556  int i, z, tab;
557 
558  if (!Config_item_undo) {
560  return -1;
561  }
562 
563  if (Config_item_undo->reset_to_preset > -1) {
565  } else {
566  if (Config_item_undo->index[0] & JOY_AXIS) {
567  tab = SHIP_TAB;
568  } else {
569  tab = Control_config[Config_item_undo->index[0]].tab;
570  }
571 
572  for (i=1; i<Config_item_undo->size; i++) {
573  if (Config_item_undo->index[i] & JOY_AXIS) {
574  if (tab != SHIP_TAB) {
575  tab = -1;
576  }
577 
578  } else {
579  if (Control_config[Config_item_undo->index[i]].tab != tab) {
580  tab = -1;
581  }
582  }
583  }
584 
585  if (tab >= 0) {
586  Tab = tab;
587  }
588 
589  for (i=0; i<Config_item_undo->size; i++) {
590  z = Config_item_undo->index[i];
591  if (z & JOY_AXIS) {
592  config_item *ptr;
593 
594  z &= ~JOY_AXIS;
595  ptr = &Config_item_undo->list[i];
596  Axis_map_to[z] = ptr->joy_id;
597  Invert_axis[z] = ptr->used;
598 
599  } else {
600  Control_config[z] = Config_item_undo->list[i];
601  }
602  }
603  }
604 
605  free_undo_block();
609  return 0;
610 }
611 
613 {
614  config_item_undo *ptr;
615  config_item item;
616 
617  memset( &item, 0, sizeof(config_item) );
618 
619  item.joy_id = (short) Axis_map_to[axis];
620  item.used = Invert_axis[axis];
621 
622  ptr = get_undo_block(1);
623  ptr->index[0] = axis | JOY_AXIS;
624  ptr->list[0] = item;
625 }
626 
628 {
629  config_item_undo *ptr;
630 
631  ptr = get_undo_block(1);
632  ptr->index[0] = i;
633  ptr->list[0] = Control_config[i];
634  Control_config[i].key_id = (short) key;
635 }
636 
638 {
639  config_item_undo *ptr;
640 
641  ptr = get_undo_block(1);
642  ptr->index[0] = i;
643  ptr->list[0] = Control_config[i];
644  Control_config[i].joy_id = (short) joy;
645 }
646 
647 void control_config_bind_axis(int i, int axis)
648 {
650  Axis_map_to[i] = axis;
651 }
652 
654 {
655  int z;
656  config_item_undo *ptr;
657 
658  if (Selected_line < 0) {
660  return -1;
661  }
662 
663  z = Cc_lines[Selected_line].cc_index;
664  if (z & JOY_AXIS) {
665  z &= ~JOY_AXIS;
666  if (Axis_map_to[z] < 0) {
668  return -1;
669  }
670 
672  Axis_map_to[z] = -1;
676  Selected_item = -1;
677  return 0;
678  }
679 
680  if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
682  return -1;
683  }
684 
685  ptr = get_undo_block(1);
686  ptr->index[0] = z;
687  ptr->list[0] = Control_config[z];
688 
689  if (Selected_item && (Control_config[z].joy_id >= 0)) { // if not just key selected (which would be 0)
690  Control_config[z].joy_id = (short) -1;
691  }
692 
693  if ((Selected_item != 1) && (Control_config[z].key_id >= 0)) { // if not just joy button selected (1)
694  Control_config[z].key_id = (short) -1;
695  }
696 
700  Selected_item = -1;
701  return 0;
702 }
703 
705 {
706  int z, i, j, total = 0;
707  config_item_undo *ptr;
708 
709  if (Selected_line < 0) {
711  return -1;
712  }
713 
714  z = Cc_lines[Selected_line].cc_index;
715  if (z & JOY_AXIS) {
716  config_item item;
717 
718  z &= ~JOY_AXIS;
719  if (Axis_map_to[z] < 0) {
721  return -1;
722  }
723 
724  for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
725  if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
726  total++;
727  }
728  }
729 
730  if (!total) {
732  return -1;
733  }
734 
735  ptr = get_undo_block(total);
736  for (i=j=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
737  if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
738  memset( &item, 0, sizeof(config_item) );
739 
740  item.joy_id = (short) Axis_map_to[i];
741  item.used = Invert_axis[i];
742 
743  ptr->index[j] = i | JOY_AXIS;
744  ptr->list[j] = item;
745  j++;
746 
747  Axis_map_to[i] = -1;
748  }
749  }
753  return 0;
754  }
755 
756  for (i=0; i<CCFG_MAX; i++) {
757  if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) ) {
758  if (i != z) {
759  total++;
760  }
761  }
762  }
763  if (!total) {
765  return -1;
766  }
767 
768  if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
770  return -1;
771  }
772 
773  // now, back up the old bindings so we can undo if we want to
774  ptr = get_undo_block(total);
775  for (i=j=0; i<CCFG_MAX; i++) {
776  if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) ) {
777  if (i != z) {
778  ptr->index[j] = i;
779  ptr->list[j] = Control_config[i];
780  j++;
781 
782  if (Control_config[i].key_id == Control_config[z].key_id) {
783  Control_config[i].key_id = (short) -1;
784  }
785 
786  if (Control_config[i].joy_id == Control_config[z].joy_id) {
787  Control_config[i].joy_id = (short) -1;
788  }
789  }
790  }
791  }
795  return 0;
796 }
797 
799 {
800  int i, j, total = 0;
801  config_item_undo *ptr;
802 
803  // first, determine how many bindings need to be changed
804  for (i=0; i<CCFG_MAX; i++) {
805  if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
806  total++;
807  }
808  }
809 
810  if (!total) {
812  return -1;
813  }
814 
815  // now, back up the old bindings so we can undo if we want to
816  ptr = get_undo_block(total);
817  for (i=j=0; i<CCFG_MAX; i++) {
818  if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
819  ptr->index[j] = i;
820  ptr->list[j] = Control_config[i];
821  j++;
822  }
823  }
824 
825  Assert(j == total);
826  for (i=0; i<CCFG_MAX; i++) {
828  }
829 
833  return 0;
834 }
835 
836 extern Joy_info joystick;
837 
839 {
840  Assert(axis >= 0);
841 
842  if ( axis > 1 ) {
843  if (Axis_map_to_defaults[axis] < 0) {
844  return -1;
845  }
846 
847  if (!joystick.axis_valid[Axis_map_to_defaults[axis]]) {
848  return -1;
849  }
850  }
851 
852  return Axis_map_to_defaults[axis];
853 }
854 
856 {
857  int i, j, total = 0;
858  config_item_undo *ptr;
859  config_item item;
860  config_item *preset;
861  bool cycling_presets = false;
862 
863  // If there are presets, then we'll cycle to the next preset and reset to that
864  if (Control_config_presets.size() >= 1) {
865  cycling_presets = true;
866 
867  if (++Defaults_cycle_pos >= Control_config_presets.size())
868  Defaults_cycle_pos = 0;
869 
870  preset = Control_config_presets[Defaults_cycle_pos];
871  } else {
872  // If there are no presets, then we'll always reset to the hardcoded defaults
873  preset = Control_config;
874  }
875 
876  // first, determine how many bindings need to be changed
877  for (i=0; i<CCFG_MAX; i++) {
878  if ((Control_config[i].key_id != preset[i].key_default) || (Control_config[i].joy_id != preset[i].joy_default)) {
879  total++;
880  }
881  }
882 
883  for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
885  total++;
886  }
887  }
888 
889  if (!total && !cycling_presets) {
891  return -1;
892  }
893 
894  // now, back up the old bindings so we can undo if we want to
895  ptr = get_undo_block(total);
896  for (i=j=0; i<CCFG_MAX; i++) {
897  if ((Control_config[i].key_id != preset[i].key_default) || (Control_config[i].joy_id != preset[i].joy_default)) {
898  ptr->index[j] = i;
899  ptr->list[j] = Control_config[i];
900  j++;
901  }
902  }
903 
904  for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
906  memset( &item, 0, sizeof(config_item) );
907 
908  item.joy_id = (short) Axis_map_to[i];
909  item.used = Invert_axis[i];
910 
911  ptr->index[j] = i | JOY_AXIS;
912  ptr->list[j] = item;
913  j++;
914  }
915  }
916 
917  Assert(j == total);
918 
919  if (cycling_presets)
920  control_config_reset_defaults(Defaults_cycle_pos);
921  else
923 
927  return 0;
928 }
929 
930 // This sets all the controls to the default values in the given preset
931 // If no preset is given, the hardcoded defaults of Control_config are used
932 void control_config_reset_defaults(int presetnum)
933 {
934  int i;
935  config_item *preset;
936 
937  if (presetnum >= 0)
938  preset = Control_config_presets[presetnum];
939  else
940  preset = Control_config;
941 
942  // Reset keyboard defaults
943  for (i=0; i<CCFG_MAX; i++) {
944  // Note that key_default and joy_default are NOT overwritten here;
945  // they should retain the values of the first preset because
946  // for example the key-pressed SEXP works off the defaults of the first preset
947  Control_config[i].key_id = preset[i].key_default;
948  Control_config[i].joy_id = preset[i].joy_default;
949  Control_config[i].tab = preset[i].tab;
950  Control_config[i].hasXSTR = preset[i].hasXSTR;
951  Control_config[i].type = preset[i].type;
952  Control_config[i].disabled = preset[i].disabled;
953  }
954 
955  for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
958  }
959 }
960 
962 {
963  if (Scroll_offset) {
964  Scroll_offset--;
965  Assert(Selected_line > Scroll_offset);
966  while (!cc_line_query_visible(Selected_line)) {
967  Selected_line--;
968  }
969 
970  Selected_item = -1;
972 
973  } else {
975  }
976 }
977 
979 {
980  if (Selected_line) {
981  Selected_line--;
982  if (Selected_line < Scroll_offset) {
983  Scroll_offset = Selected_line;
984  }
985 
986  Selected_item = -1;
988 
989  } else {
991  }
992 }
993 
995 {
996  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]) {
997  Scroll_offset++;
998  while (!cc_line_query_visible(Selected_line)) {
999  Selected_line++;
1000  Assert(Selected_line < Num_cc_lines);
1001  }
1002 
1003  Selected_item = -1;
1005 
1006  } else {
1008  }
1009 }
1010 
1012 {
1013  if (Selected_line < Num_cc_lines - 1) {
1014  Selected_line++;
1015  Assert(Selected_line > Scroll_offset);
1016  while (!cc_line_query_visible(Selected_line)) {
1017  Scroll_offset++;
1018  }
1019 
1020  Selected_item = -1;
1022 
1023  } else {
1025  }
1026 }
1027 
1029 {
1030  int k, z;
1031 
1032  z = Cc_lines[Selected_line].cc_index;
1033  Assert(!(z & JOY_AXIS));
1034  k = Control_config[z].key_id;
1035  if (k < 0) {
1037  return;
1038  }
1039 
1040  control_config_bind_key(z, k ^ bit);
1043 }
1044 
1046 {
1047  int z;
1048 
1049  z = Cc_lines[Selected_line].cc_index;
1050  Assert(z & JOY_AXIS);
1051  z &= ~JOY_AXIS;
1053  Invert_axis[z] = !Invert_axis[z];
1054 }
1055 
1057 {
1058  int i;
1059 
1060  game_flush();
1061 // if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
1062  if (Selected_line < 0) {
1064  return;
1065  }
1066 
1067  for (i=0; i<NUM_BUTTONS; i++) {
1068  if (i != CANCEL_BUTTON) {
1069  CC_Buttons[gr_screen.res][i].button.reset_status();
1070  CC_Buttons[gr_screen.res][i].button.disable();
1071  }
1072  }
1073  CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1075 
1076  for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1077  joy_down_count(i, 1); // clear checking status of all joystick buttons
1078  }
1079 
1081 
1082  Binding_mode = 1;
1083  Bind_time = timer_get_milliseconds();
1084  Search_mode = 0;
1085  Last_key = -1;
1086  Axis_override = -1;
1088 }
1089 
1091 {
1092  int i;
1093 
1094  for (i=0; i<NUM_BUTTONS; i++){
1095  if (i != CANCEL_BUTTON) {
1096  CC_Buttons[gr_screen.res][i].button.reset_status();
1097  CC_Buttons[gr_screen.res][i].button.disable();
1098  }
1099  }
1100 
1101  CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1103 
1104  for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1105  joy_down_count(i, 1); // clear checking status of all joystick buttons
1106  }
1107 
1108  Binding_mode = 0;
1109  Search_mode = 1;
1110  Last_key = -1;
1112 }
1113 
1114 void control_config_do_cancel(int fail = 0)
1115 {
1116  int i;
1117 
1118  game_flush();
1119 
1120  for (i=0; i<NUM_BUTTONS; i++){
1121  if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
1122  CC_Buttons[gr_screen.res][i].button.enable();
1123  }
1124  }
1125 
1127  CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1128  CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
1129  CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
1130  CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
1131 
1132  Binding_mode = Search_mode = 0;
1133  if (fail){
1135  } else {
1137  }
1138 }
1139 
1141 {
1142  int i;
1143 
1144  for (i=0; i<NUM_TABS; i++) {
1145  if (Conflicts_tabs[i]) {
1146  break;
1147  }
1148  }
1149 
1150  if (i < NUM_TABS) {
1152  return -1;
1153  }
1154 
1155  hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1158  return 0;
1159 }
1160 
1162 {
1163  int i;
1164 
1165  for (i=0; i<CCFG_MAX; i++) {
1166  Control_config[i] = Control_config_backup[i];
1167  }
1168 
1170 }
1171 
1173 {
1174  switch (n) {
1175  case TARGET_TAB:
1176  case SHIP_TAB:
1177  case WEAPON_TAB:
1178  case COMPUTER_TAB:
1179  Tab = n;
1180  Scroll_offset = Selected_line = 0;
1183  break;
1184 
1185  case BIND_BUTTON:
1187  break;
1188 
1189  case SEARCH_MODE:
1191  break;
1192 
1193  case SHIFT_TOGGLE:
1196  break;
1197 
1198  case ALT_TOGGLE:
1201  break;
1202 
1203  case INVERT_AXIS:
1206  break;
1207 
1208  case SCROLL_UP_BUTTON:
1210  break;
1211 
1212  case SCROLL_DOWN_BUTTON:
1214  break;
1215 
1216  case ACCEPT_BUTTON:
1218  break;
1219 
1220  case CLEAR_BUTTON:
1222  break;
1223 
1224  case HELP_BUTTON:
1227  break;
1228 
1229  case RESET_BUTTON:
1231  break;
1232 
1233  case UNDO_BUTTON:
1235  break;
1236 
1237  case CANCEL_BUTTON:
1239  break;
1240 
1241  case CLEAR_OTHER_BUTTON:
1243  break;
1244 
1245  case CLEAR_ALL_BUTTON:
1247  break;
1248  }
1249 }
1250 
1251 const char *control_config_tooltip_handler(const char *str)
1252 {
1253  int i;
1254 
1255  if (!stricmp(str, NOX("@conflict"))) {
1256  for (i=0; i<NUM_TABS; i++) {
1257  if (Conflicts_tabs[i]) {
1258  return XSTR( "Conflict!", 205);
1259  }
1260  }
1261  }
1262 
1263  return NULL;
1264 }
1265 
1267 {
1268  int i;
1269  ui_button_info *b;
1270 
1271  // make backup of all controls
1272  for (i=0; i<CCFG_MAX; i++) {
1273  Control_config_backup[i] = Control_config[i];
1274  }
1275 
1276  Defaults_cycle_pos = 0;
1277 
1278  common_set_interface_palette(NOX("ControlConfigPalette")); // set the interface palette
1282 
1283  // load in help overlay bitmap
1286 
1287  // reset conflict flashing
1288  Conflict_stamp = -1;
1289 
1290  for (i=0; i<NUM_BUTTONS; i++) {
1291  b = &CC_Buttons[gr_screen.res][i];
1292 
1293  if (b->hotspot < 0) { // temporary
1294  b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1); // temporary
1296  continue;
1297  }
1298 
1299  b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1300 
1301  // set up callback for when a mouse first goes over a button
1303  if (i<4) {
1304  b->button.set_bmaps(b->filename, 5, 1); // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1305  } else {
1306  b->button.set_bmaps(b->filename);
1307  }
1308  b->button.link_hotspot(b->hotspot);
1309  }
1310 
1311  // create all text
1312  for(i=0; i<CC_NUM_TEXT; i++){
1313  Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
1314  }
1315 
1316  for (i=0; i<LIST_BUTTONS_MAX; i++) {
1317  List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1318  List_buttons[i].hide();
1319  List_buttons[i].disable();
1320  }
1321 
1322  // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1333 
1334  CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1336 
1337  Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);
1338 
1339  Scroll_offset = Selected_line = 0;
1340  Config_item_undo = NULL;
1342 
1343  // setup strings
1344  Joy_axis_action_text[0] = vm_strdup(XSTR("Turn (Yaw) Axis", 1016));
1345  Joy_axis_action_text[1] = vm_strdup(XSTR("Pitch Axis", 1017));
1346  Joy_axis_action_text[2] = vm_strdup(XSTR("Bank Axis", 1018));
1347  Joy_axis_action_text[3] = vm_strdup(XSTR("Absolute Throttle Axis", 1019));
1348  Joy_axis_action_text[4] = vm_strdup(XSTR("Relative Throttle Axis", 1020));
1349  Joy_axis_text[0] = vm_strdup(XSTR("Joystick/Mouse X Axis", 1021));
1350  Joy_axis_text[1] = vm_strdup(XSTR("Joystick/Mouse Y Axis", 1022));
1351  Joy_axis_text[2] = vm_strdup(XSTR("Joystick Z Axis", 1023));
1352  Joy_axis_text[3] = vm_strdup(XSTR("Joystick rX Axis", 1024));
1353  Joy_axis_text[4] = vm_strdup(XSTR("Joystick rY Axis", 1025));
1354  Joy_axis_text[5] = vm_strdup(XSTR("Joystick rZ Axis", 1026));
1355  Mouse_button_text[0] = vm_strdup("");
1356  Mouse_button_text[1] = vm_strdup(XSTR("Left Button", 1027));
1357  Mouse_button_text[2] = vm_strdup(XSTR("Right Button", 1028));
1358  Mouse_button_text[3] = vm_strdup(XSTR("Mid Button", 1029));
1359  Mouse_button_text[4] = vm_strdup("");
1360  Mouse_axis_text[0] = vm_strdup(XSTR("L/R", 1030));
1361  Mouse_axis_text[1] = vm_strdup(XSTR("U/B", 1031));
1362  Invert_text[0] = vm_strdup(XSTR("N", 1032));
1363  Invert_text[1] = vm_strdup(XSTR("Y", 1033));
1364 
1366 }
1367 
1369 {
1370  int idx;
1371 
1372  while (Config_item_undo){
1373  free_undo_block();
1374  }
1375 
1376  if (Background_bitmap){
1377  bm_release(Background_bitmap);
1378  }
1379 
1380  Ui_window.destroy();
1381  common_free_interface_palette(); // restore game palette
1382  hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
1383  game_flush();
1384 
1385  if (Game_mode & GM_MULTIPLAYER) {
1386  Pilot.save_player();
1387  } else {
1388  Pilot.save_savefile();
1389  }
1390 
1391  // free strings
1392  for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
1393  if(Joy_axis_action_text[idx] != NULL){
1395  Joy_axis_action_text[idx] = NULL;
1396  }
1397  }
1398  for(idx=0; idx<NUM_AXIS_TEXT; idx++){
1399  if(Joy_axis_text[idx] != NULL){
1400  vm_free(Joy_axis_text[idx]);
1401  Joy_axis_text[idx] = NULL;
1402  }
1403  }
1404  for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
1405  if(Mouse_button_text[idx] != NULL){
1406  vm_free(Mouse_button_text[idx]);
1407  Mouse_button_text[idx] = NULL;
1408  }
1409  }
1410  for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
1411  if(Mouse_axis_text[idx] != NULL){
1412  vm_free(Mouse_axis_text[idx]);
1413  Mouse_axis_text[idx] = NULL;
1414  }
1415  }
1416  for(idx=0; idx<NUM_INVERT_TEXT; idx++){
1417  if(Invert_text[idx] != NULL){
1418  vm_free(Invert_text[idx]);
1419  Invert_text[idx] = NULL;
1420  }
1421  }
1422 }
1423 
1424 void control_config_do_frame(float frametime)
1425 {
1426  const char *str;
1427  char buf[256];
1428  int i, j, k, w, x, y, z, line, conflict;
1429  int font_height = gr_get_font_height();
1430  int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
1431  static float timer = 0.0f;
1432  color *c;
1433  static int bound_timestamp = 0;
1434  static char bound_string[40];
1435 
1436  timer += frametime;
1437 
1438  if (Binding_mode) {
1439  if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1440  int bind = 0;
1441 
1442  z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1444  if (i >= 0) {
1445  Axis_override = i;
1446  bind = 1;
1447  }
1448 
1449  k = game_poll();
1451  Ui_window.process(0);
1452 
1453  if (k == KEY_ESC) {
1454  strcpy_s(bound_string, XSTR( "Canceled", 206));
1455  bound_timestamp = timestamp(2500);
1457 
1458  } else {
1459  if (k == KEY_ENTER) {
1460  bind = 1;
1461  }
1462 
1463  for (i=0; i<JOY_TOTAL_BUTTONS; i++) {
1464  if (joy_down_count(i, 1)) {
1465  bind = 1;
1466  }
1467  }
1468 
1469  if (bind) {
1470  if (Axis_override >= 0) {
1471  control_config_bind_axis(z, Axis_override);
1472  strcpy_s(bound_string, Joy_axis_text[Axis_override]);
1474  bound_timestamp = timestamp(2500);
1478 
1479  } else {
1481  }
1482  }
1483  }
1484 
1485  } else {
1487  CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1488  Ui_window.set_ignore_gadgets(1);
1489  }
1490 
1491  k = game_poll();
1493  Ui_window.process(0);
1494 
1495  if ( (k > 0) || B1_JUST_RELEASED ) {
1498  Ui_window.set_ignore_gadgets(0);
1499  k = 0;
1500  }
1501  }
1502 
1504  Ui_window.set_ignore_gadgets(0);
1505  }
1506 
1507  if (k == KEY_ESC) {
1508  strcpy_s(bound_string, XSTR( "Canceled", 206));
1509  bound_timestamp = timestamp(2500);
1511 
1512  } else {
1513  switch (k & KEY_MASK) {
1514  case KEY_LSHIFT:
1515  case KEY_RSHIFT:
1516  case KEY_LALT:
1517  case KEY_RALT:
1518  Last_key = k & KEY_MASK;
1519  k = 0;
1520  break;
1521  }
1522 
1523  if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED || Cc_lines[Selected_line].cc_index == GLIDE_WHEN_PRESSED) {
1524  if ( (Last_key >= 0) && (k <= 0) && !keyd_pressed[Last_key] ) {
1525  k = Last_key;
1526  }
1527  }
1528 
1529  if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1530  popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "That is a non-bindable key. Please try again.", 207));
1531  k = 0;
1532  }
1533 
1534  k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1535  if (k > 0) {
1536  z = Cc_lines[Selected_line].cc_index;
1537  Assert(!(z & JOY_AXIS));
1539 
1540  strcpy_s(bound_string, textify_scancode(k));
1542  bound_timestamp = timestamp(2500);
1546  }
1547 
1548  for (i=0; i<JOY_TOTAL_BUTTONS; i++) {
1549  if (joy_down_count(i, 1)) {
1550  z = Cc_lines[Selected_line].cc_index;
1551  Assert(!(z & JOY_AXIS));
1553 
1554  strcpy_s(bound_string, Joy_button_text[i]);
1556  bound_timestamp = timestamp(2500);
1560  break;
1561  }
1562  }
1563 
1564  if (Bind_time + 375 < timer_get_milliseconds()) {
1565  for (i=0; i<NUM_BUTTONS; i++){
1566  if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
1567  break;
1568  }
1569  }
1570 
1571  if (i == NUM_BUTTONS) { // no buttons pressed
1572  for (i=0; i<MOUSE_NUM_BUTTONS; i++) {
1573  if (mouse_down(1 << i)) {
1574  z = Cc_lines[Selected_line].cc_index;
1575  Assert(!(z & JOY_AXIS));
1577 
1578  strcpy_s(bound_string, Joy_button_text[i]);
1580  bound_timestamp = timestamp(2500);
1584  for (j=0; j<NUM_BUTTONS; j++){
1585  CC_Buttons[gr_screen.res][j].button.reset();
1586  }
1587 
1588  break;
1589  }
1590  }
1591  }
1592  }
1593  }
1594  }
1595 
1596  } else if (Search_mode) {
1598  CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1599  Ui_window.set_ignore_gadgets(1);
1600  }
1601 
1602  k = game_poll();
1604  Ui_window.process(0);
1605 
1606  if ( (k > 0) || B1_JUST_RELEASED ) {
1609  Ui_window.set_ignore_gadgets(0);
1610  k = 0;
1611  }
1612  }
1613 
1615  Ui_window.set_ignore_gadgets(0);
1616  }
1617 
1618  if (k == KEY_ESC) {
1620 
1621  } else {
1622  if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1623  k = 0;
1624  }
1625 
1626  k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1627  z = -1;
1628  if (k > 0) {
1629  for (i=0; i<CCFG_MAX; i++) {
1630  if (Control_config[i].key_id == k) {
1631  z = i;
1632  break;
1633  }
1634  }
1635  }
1636 
1637  for (i=0; i<JOY_TOTAL_BUTTONS; i++) {
1638  if (joy_down_count(i, 1)) {
1639  j = i;
1640  for (i=0; i<CCFG_MAX; i++) { //-V535
1641  if (Control_config[i].joy_id == j) {
1642  z = i;
1643  break;
1644  }
1645  }
1646  break;
1647  }
1648  }
1649 
1650  // check if not on enabled button
1651  for (j=0; j<NUM_BUTTONS; j++){
1652  if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
1653  break;
1654  }
1655  }
1656 
1657  if (j == NUM_BUTTONS) { // no buttons pressed
1658  for (j=0; j<MOUSE_NUM_BUTTONS; j++) {
1659  if (mouse_down(1 << j)) {
1660  for (i=0; i<CCFG_MAX; i++) {
1661  if (Control_config[i].joy_id == j) {
1662  z = i;
1663  for (size_t buttonid=0; buttonid<NUM_BUTTONS; buttonid++){
1664  CC_Buttons[gr_screen.res][buttonid].button.reset();
1665  }
1666  break;
1667  }
1668  }
1669  break;
1670  }
1671  }
1672  }
1673 
1674  if (z >= 0) {
1675  Tab = Control_config[z].tab;
1677  Selected_line = Scroll_offset = 0;
1678  for (i=0; i<Num_cc_lines; i++) {
1679  if (Cc_lines[i].cc_index == z) {
1680  Selected_line = i;
1681  break;
1682  }
1683  }
1684  while (!cc_line_query_visible(Selected_line)) {
1685  Scroll_offset++;
1686  Assert(Scroll_offset < Num_cc_lines);
1687  }
1688  }
1689  }
1690 
1691  } else {
1692  z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
1693  CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
1694  CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
1695  CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
1696 
1697  if (!z) {
1698  z = Cc_lines[Selected_line].cc_index;
1699  k = Control_config[z].key_id;
1700  if ( (k == KEY_LALT) || (k == KEY_RALT) || (k == KEY_LSHIFT) || (k == KEY_RSHIFT) ) {
1701  CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
1702  CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
1703  }
1704  }
1705 
1706  CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
1707 
1709  CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1710  Ui_window.set_ignore_gadgets(1);
1711  }
1712 
1713  k = Ui_window.process();
1714 
1715  if ( (k > 0) || B1_JUST_RELEASED ) {
1718  Ui_window.set_ignore_gadgets(0);
1719  k = 0;
1720  }
1721  }
1722 
1724  Ui_window.set_ignore_gadgets(0);
1725  }
1726 
1727  switch (k) {
1728  case KEY_DOWN: // select next line
1730  break;
1731 
1732  case KEY_UP: // select previous line
1734  break;
1735 
1736  case KEY_SHIFTED | KEY_TAB: // activate previous tab
1737  Tab--;
1738  if (Tab < 0) {
1739  Tab = NUM_TABS - 1;
1740  }
1741 
1742  Scroll_offset = Selected_line = 0;
1745  break;
1746 
1747  case KEY_TAB: // activate next tab
1748  Tab++;
1749  if (Tab >= NUM_TABS) {
1750  Tab = 0;
1751  }
1752 
1753  Scroll_offset = Selected_line = 0;
1756  break;
1757 
1758  case KEY_LEFT:
1759  Selected_item--;
1760  if (Selected_item == -2) {
1761  Selected_item = 1;
1762  if (Cc_lines[Selected_line].jw < 1) {
1763  Selected_item = 0;
1764  if (Cc_lines[Selected_line].kw < 1) {
1765  Selected_item = -1;
1766  }
1767  }
1768  }
1769 
1771  break;
1772 
1773  case KEY_RIGHT:
1774  Selected_item++;
1775  if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1)) {
1776  Selected_item = -1;
1777  } else if (!Selected_item && (Cc_lines[Selected_line].kw < 1)) {
1778  Selected_item = -1;
1779  } else if (Selected_item > 1) {
1780  Selected_item = -1;
1781  }
1783  break;
1784 
1785  case KEY_BACKSP: // undo
1787  break;
1788 
1789  case KEY_ESC:
1791  break;
1792  } // end switch
1793  }
1794 
1795  for (i=0; i<NUM_BUTTONS; i++){
1796  if (CC_Buttons[gr_screen.res][i].button.pressed()){
1798  }
1799  }
1800 
1801  for (i=0; i<LIST_BUTTONS_MAX; i++) {
1802  if (List_buttons[i].is_mouse_on()) {
1803  select_tease_line = i + Scroll_offset;
1804  }
1805 
1806  if (List_buttons[i].pressed()) {
1807  Selected_line = i + Scroll_offset;
1808  Selected_item = -1;
1809  List_buttons[i].get_mouse_pos(&x, &y);
1810  if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw)) {
1811  Selected_item = 0;
1812  }
1813 
1814  if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw)) {
1815  Selected_item = 1;
1816  }
1817 
1819  }
1820 
1821  if (List_buttons[i].double_clicked()) {
1823  }
1824  }
1825 
1826  GR_MAYBE_CLEAR_RES(Background_bitmap);
1827  if (Background_bitmap >= 0) {
1828  gr_set_bitmap(Background_bitmap);
1829  gr_bitmap(0, 0, GR_RESIZE_MENU);
1830  }
1831 
1832  // highlight tab with conflict
1833  Ui_window.draw();
1834  for (i=z=0; i<NUM_TABS; i++) {
1835  if (Conflicts_tabs[i]) {
1836  CC_Buttons[gr_screen.res][i].button.draw_forced(4);
1837  z++;
1838  }
1839  }
1840 
1841  if (z) {
1842  // maybe switch from bright to normal
1845 
1847  }
1848 
1849  // set color and font
1850  gr_set_font(FONT2);
1851  if(Conflict_bright){
1853  } else {
1855  }
1856 
1857  // setup the conflict string
1858  char conflict_str[512] = "";
1859  strncpy(conflict_str, XSTR("Conflict!", 205), 511);
1860  int sw, sh;
1861  gr_get_string_size(&sw, &sh, conflict_str);
1862 
1863  gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str, GR_RESIZE_MENU);
1864 
1865  gr_set_font(FONT1);
1866  } else {
1867  // might as well always reset the conflict stamp
1868  Conflict_stamp = -1;
1869  }
1870 
1871  for (i=0; i<NUM_TABS; i++) {
1872  if (CC_Buttons[gr_screen.res][i].button.button_down()) {
1873  break;
1874  }
1875  }
1876 
1877  if (i == NUM_TABS) {
1878  CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
1879  }
1880 
1881  if (Search_mode) {
1882  CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
1883  }
1884 
1885  if (Selected_line >= 0) {
1886  z = Cc_lines[Selected_line].cc_index;
1887  if (z & JOY_AXIS) {
1888  if (Invert_axis[z & ~JOY_AXIS]) {
1889  CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
1890  }
1891 
1892  } else {
1893  z = Control_config[z].key_id;
1894  if (z >= 0) {
1895  if (z & KEY_SHIFTED) {
1896  CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
1897  }
1898  if (z & KEY_ALTED) {
1899  CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
1900  }
1901  }
1902  }
1903  }
1904 
1905  if (Binding_mode) {
1906  CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
1907  }
1908 
1909  z = Cc_lines[Selected_line].cc_index;
1912  if (Binding_mode) {
1913  int t;
1914 
1915  t = (int) (timer * 3);
1916  if (t % 2) {
1918  gr_get_string_size(&w, NULL, XSTR( "?", 208));
1919  gr_printf_menu(x - w / 2, y - font_height / 2, XSTR( "?", 208));
1920  }
1921 
1922  } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
1923  i = Conflicts[z].key;
1924  if (i < 0) {
1925  i = Conflicts[z].joy;
1926  }
1927 
1929  str = XSTR( "Control conflicts with:", 209);
1930  gr_get_string_size(&w, NULL, str);
1931  gr_printf_menu(x - w / 2, y - font_height, str);
1932 
1933  if (Control_config[i].hasXSTR) {
1934  strcpy_s(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
1935  } else {
1936  strcpy_s(buf, Control_config[i].text);
1937  }
1938 
1940  gr_get_string_size(&w, NULL, buf);
1941  gr_printf_menu(x - w / 2, y, buf);
1942 
1943  } else if (*bound_string) {
1945  gr_get_string_size(&w, NULL, bound_string);
1946  gr_printf_menu(x - w / 2, y - font_height / 2, bound_string);
1947  if (timestamp_elapsed(bound_timestamp)) {
1948  *bound_string = 0;
1949  }
1950  }
1951 
1952  if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
1955  }
1956 
1957  conflict = 0;
1958  line = Scroll_offset;
1959  while (cc_line_query_visible(line)) {
1960  z = Cc_lines[line].cc_index;
1961  y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
1962 
1963  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);
1964  List_buttons[line - Scroll_offset].enable(!Binding_mode);
1965 
1966  Cc_lines[line].kw = Cc_lines[line].jw = 0;
1967 
1968  if (line == Selected_line){
1969  c = &Color_text_selected;
1970  } else if (line == select_tease_line) {
1972  } else {
1973  c = &Color_text_normal;
1974  }
1975 
1976  gr_set_color_fast(c);
1977  if (Cc_lines[line].label) {
1978  strcpy_s(buf, Cc_lines[line].label);
1980  gr_printf_menu(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
1981  }
1982 
1983  if (!(z & JOY_AXIS)) {
1984  k = Control_config[z].key_id;
1985  j = Control_config[z].joy_id;
1987  *buf = 0;
1988 
1989  if ((k < 0) && (j < 0)) {
1991  gr_printf_menu(x, y, XSTR( "None", 211));
1992 
1993  } else {
1994  if (k >= 0) {
1995  strcpy_s(buf, textify_scancode(k));
1996  if (Conflicts[z].key >= 0) {
1997  if (c == &Color_text_normal)
1999  else {
2001  conflict++;
2002  }
2003 
2004  } else if (Selected_item == 1) {
2006 
2007  } else {
2008  gr_set_color_fast(c);
2009  }
2010 
2011  gr_printf_menu(x, y, buf);
2012 
2013  Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2014  gr_get_string_size(&w, NULL, buf);
2015  Cc_lines[line].kw = w;
2016  x += w;
2017 
2018  if (j >= 0) {
2020  gr_printf_menu(x, y, XSTR( ", ", 212));
2021  gr_get_string_size(&w, NULL, XSTR( ", ", 212));
2022  x += w;
2023  }
2024  }
2025 
2026  if (j >= 0) {
2027  strcpy_s(buf, Joy_button_text[j]);
2028  if (Conflicts[z].joy >= 0) {
2029  if (c == &Color_text_normal) {
2031  } else {
2033  conflict++;
2034  }
2035 
2036  } else if (!Selected_item) {
2038 
2039  } else {
2040  gr_set_color_fast(c);
2041  }
2042 
2044  gr_printf_menu(x, y, buf);
2045 
2046  Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2047  gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
2048  }
2049  }
2050 
2051  } else {
2053  j = Axis_map_to[z & ~JOY_AXIS];
2054  if (Binding_mode && (line == Selected_line)) {
2055  j = Axis_override;
2056  }
2057 
2058  if (j < 0) {
2060  gr_printf_menu(x, y, XSTR( "None", 211));
2061 
2062  } else {
2063  if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
2064  if (c == &Color_text_normal) {
2066 
2067  } else {
2069  conflict++;
2070  }
2071 
2072  } else if (!Selected_item) {
2074 
2075  } else {
2076  gr_set_color_fast(c);
2077  }
2078 
2080  }
2081  }
2082 
2083  line++;
2084  }
2085 
2086  CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
2087 
2088  i = line - Scroll_offset;
2089  while (i < LIST_BUTTONS_MAX) {
2090  List_buttons[i++].disable();
2091  }
2092 
2093  // If multiple controls presets are provided, display which one is in use
2094  if (Control_config_presets.size() > 1) {
2095  SCP_string preset_str;
2096  int matching_preset = -1;
2097 
2098  for (i=0; i<(int)Control_config_presets.size(); i++) {
2099  bool this_preset_matches = true;
2100  config_item *this_preset = Control_config_presets[i];
2101 
2102  for (j=0; j<CCFG_MAX; j++) {
2103  if (!Control_config[j].disabled && Control_config[j].key_id != this_preset[j].key_default) {
2104  this_preset_matches = false;
2105  break;
2106  }
2107  }
2108 
2109  if (this_preset_matches) {
2110  matching_preset = i;
2111  break;
2112  }
2113  }
2114 
2115  if (matching_preset >= 0) {
2116  sprintf(preset_str, "Controls: %s", Control_config_preset_names[matching_preset].c_str());
2117  } else {
2118  sprintf(preset_str, "Controls: custom");
2119 
2120  }
2121 
2122  gr_get_string_size(&w, NULL, preset_str.c_str());
2124 
2125  if (gr_screen.res == GR_640) {
2126  gr_string(16, (24 - font_height) / 2, preset_str.c_str(), GR_RESIZE_MENU);
2127  } else {
2128  gr_string(24, (40 - font_height) / 2, preset_str.c_str(), GR_RESIZE_MENU);
2129  }
2130  }
2131 
2132  // blit help overlay if active
2134 
2135  gr_flip();
2136 }
2137 
2139 {
2140  int i;
2141 
2142  for (i=0; i<CCFG_MAX; i++) {
2143  if (Control_config[i].key_id == key) {
2144  Control_config[i].key_id = -1;
2145  }
2146  }
2147 }
2148 
2149 float check_control_timef(int id)
2150 {
2151  float t1, t2;
2152 
2153  // if type isn't continuous, we shouldn't be using this function, cause it won't work.
2155 
2156  // first, see if control actually used (makes sure modifiers match as well)
2157  if (!check_control(id)) {
2159 
2160  return 0.0f;
2161  }
2162 
2163  t1 = key_down_timef(Control_config[id].key_id);
2164  if (t1) {
2165  control_used(id);
2166  }
2167 
2168  t2 = joy_down_time(Control_config[id].joy_id);
2169  if (t2) {
2170  control_used(id);
2171  }
2172 
2173  if (t1 + t2) {
2174  // We want to set this to true only after visiting control_used() (above)
2175  // to allow it to tell the difference between an ongoing continuous action
2176  // started before and a continuous action being started right now.
2178 
2179  return t1 + t2;
2180  }
2181 
2182  return 1.0f;
2183 }
2184 
2186 {
2187 #ifndef NDEBUG
2188  if (Show_controls_info) {
2191  }
2192 #endif
2193 
2194  Control_check_count = 0;
2195 }
2196 
2197 int check_control_used(int id, int key)
2198 {
2199  int z, mask;
2200  static int last_key = 0;
2201 
2203  if (key < 0) {
2204  key = last_key;
2205  }
2206 
2207  last_key = key;
2208 
2209  // if we're in multiplayer text enter (for chat) mode, check to see if we should ignore controls
2211  return 0;
2212  }
2213 
2214  if (Control_config[id].disabled)
2215  return 0;
2216 
2217  if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
2218  if (joy_down(Control_config[id].joy_id) || joy_down_count(Control_config[id].joy_id, 1)) {
2219  control_used(id);
2220  return 1;
2221  }
2222 
2223  if ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS)) {
2224  if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2225  control_used(id);
2226  return 1;
2227  }
2228  }
2229 
2230  // check what current modifiers are pressed
2231  mask = 0;
2233  mask |= KEY_SHIFTED;
2234  }
2235 
2237  mask |= KEY_ALTED;
2238  }
2239 
2240  z = Control_config[id].key_id;
2241  if (z >= 0) {
2242  if ( (z != KEY_LALT) && (z != KEY_RALT) && (z != KEY_LSHIFT) && (z != KEY_RSHIFT) ) {
2243  // if current modifiers don't match action's modifiers, don't register control active.
2244  if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask) {
2245  return 0;
2246  }
2247  }
2248 
2249  z &= KEY_MASK;
2250 
2251  if (keyd_pressed[z] || key_down_count(z)) {
2252  control_used(id);
2253  return 1;
2254  }
2255  }
2256 
2257  return 0;
2258  }
2259 
2260  if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id, 1) ||
2261  ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS) && mouse_down_count(1 << Control_config[id].joy_id))) {
2262  //mprintf(("Key used %d", key));
2263  control_used(id);
2264  return 1;
2265  }
2266 
2267  return 0;
2268 }
2269 
2273 int check_control(int id, int key)
2274 {
2275  if (check_control_used(id, key)) {
2276  if (Ignored_keys[id]) {
2277  if (Ignored_keys[id] > 0) {
2278  Ignored_keys[id]--;
2279  }
2280  return 0;
2281  }
2282  return 1;
2283  }
2284 
2285  if (Control_config[id].continuous_ongoing) {
2286  // If we reach this point, then it means this is a continuous control
2287  // which has just been released
2288 
2289  Script_system.SetHookVar("Action", 's', Control_config[id].text);
2290  Script_system.RunCondition(CHA_ONACTIONSTOPPED, '\0', NULL, NULL, id);
2291  Script_system.RemHookVar("Action");
2292 
2294  }
2295 
2296  return 0;
2297 }
2298 
2299 // get heading, pitch, bank, throttle abs. and throttle rel. values.
2300 void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
2301 {
2302  int axes_values[JOY_NUM_AXES];
2303 
2304  joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
2305 
2306  // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
2307  *h = 0;
2308  if (Axis_map_to[0] >= 0) {
2309  *h = joy_get_scaled_reading(axes_values[Axis_map_to[0]], Axis_map_to[0]);
2310  }
2311 
2312  *p = 0;
2313  if (Axis_map_to[1] >= 0) {
2314  *p = joy_get_scaled_reading(axes_values[Axis_map_to[1]], Axis_map_to[1]);
2315  }
2316 
2317  *b = 0;
2318  if (Axis_map_to[2] >= 0) {
2319  *b = joy_get_scaled_reading(axes_values[Axis_map_to[2]], Axis_map_to[2]);
2320  }
2321 
2322  *ta = 0;
2323  if (Axis_map_to[3] >= 0) {
2324  *ta = joy_get_unscaled_reading(axes_values[Axis_map_to[3]], Axis_map_to[3]);
2325  }
2326 
2327  *tr = 0;
2328  if (Axis_map_to[4] >= 0) {
2329  *tr = joy_get_scaled_reading(axes_values[Axis_map_to[4]], Axis_map_to[4]);
2330  }
2331 
2332  if (Invert_axis[0]) {
2333  *h = -(*h);
2334  }
2335  if (Invert_axis[1]) {
2336  *p = -(*p);
2337  }
2338  if (Invert_axis[2]) {
2339  *b = -(*b);
2340  }
2341  if (Invert_axis[3]) {
2342  *ta = F1_0 - *ta;
2343  }
2344  if (Invert_axis[4]) {
2345  *tr = -(*tr);
2346  }
2347 
2348  return;
2349 }
2350 
2352 void control_used(int id)
2353 {
2354  // if we have set this key to be ignored, ignore it
2355  if (Ignored_keys[id]) {
2356  return;
2357  }
2358 
2359  // This check needs to be done because the control code might call this function more than once per frame,
2360  // and we don't want to run the hooks more than once per frame
2361  if (Control_config[id].used < Last_frame_timestamp) {
2362  if (!Control_config[id].continuous_ongoing) {
2363  Script_system.SetHookVar("Action", 's', Control_config[id].text);
2364  Script_system.RunCondition(CHA_ONACTION, '\0', NULL, NULL, id);
2365  Script_system.RemHookVar("Action");
2366 
2369  }
2370 
2372  }
2373 }
2374 
2376 {
2377  int i;
2378 
2379  for (i=0; i<CCFG_MAX; i++) {
2380  Control_config[i].used = 0;
2381  }
2382 }
2383 
2385 {
2386  int i;
2387 
2388  // Reset keyboard defaults
2389  for (i=0; i<CCFG_MAX; i++) {
2391  }
2392 }
2393 
2395 {
2396  return 0;
2397 }
void game_flush()
Definition: fredstubs.cpp:83
#define JOY_NUM_AXES
Definition: joy.h:18
void set_highlight_action(void(*_user_function)(void))
Definition: button.cpp:375
pilotfile Pilot
Definition: pilotfile.cpp:7
int enabled()
Definition: gadget.cpp:454
#define SCROLL_DOWN_BUTTON
int timestamp(int delta_ms)
Definition: timer.cpp:226
#define KEY_F4
Definition: key.h:146
int Conflict_bright
int i
Definition: multi_pxo.cpp:466
#define vm_free(ptr)
Definition: pstypes.h:548
#define MOUSE_NUM_BUTTONS
Definition: mouse.h:47
void reset()
Definition: gadget.cpp:37
void add_XSTR(char *string, int _xstr_id, int _x, int _y, UI_GADGET *_assoc, int _color_type, int _font_id=-1)
Definition: window.cpp:476
int Config_allowed[]
void reset_status()
Definition: button.cpp:78
char * Mouse_button_text[NUM_MOUSE_TEXT]
void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
#define NUM_MOUSE_AXIS_TEXT
reset (or similar button) pressed
Definition: gamesnd.h:306
Definition: joy.h:27
#define KEY_DOWN
Definition: key.h:180
#define KEY_F10
Definition: key.h:152
int joy_down(int btn)
Definition: joy-unix.cpp:93
int Conflict_warning_coords[GR_NUM_RESOLUTIONS][2]
int Game_mode
Definition: systemvars.cpp:24
#define F1_0
Definition: fix.h:15
GLfloat GLfloat GLfloat GLfloat h
Definition: Glext.h:7280
#define KEY_LEFT
Definition: key.h:181
void gr_flip()
Definition: 2d.cpp:2113
commit pressed
Definition: gamesnd.h:294
#define CHA_ONACTIONSTOPPED
Definition: scripting.h:76
float key_down_timef(uint scancode)
Definition: key.cpp:568
int x
Definition: ui.h:658
short key_id
actual key bound to action
#define KEY_F12
Definition: key.h:154
#define JOY_AXIS
#define KEY_PAGEDOWN
Definition: key.h:178
int is_mouse_on()
Definition: gadget.cpp:371
void RemHookVar(char *name)
Definition: scripting.cpp:749
#define GR_RESIZE_MENU
Definition: 2d.h:684
#define HELP_BUTTON
color Color_red
Definition: alphacolors.cpp:34
char ** Joy_button_text
int y
Definition: ui.h:658
bool save_savefile()
Definition: csg.cpp:1524
int multi_ignore_controls(int key)
Definition: multiutil.cpp:2845
char * Invert_text[NUM_INVERT_TEXT]
#define KEY_RIGHT
Definition: key.h:182
int Control_config_overlay_id
int check_control_used(int id, int key)
char * Mouse_axis_text[NUM_MOUSE_AXIS_TEXT]
int kw
#define KEY_Z
Definition: key.h:107
#define CONTROL_W_COORD
Assert(pm!=NULL)
#define KEY_F11
Definition: key.h:153
#define GR_NUM_RESOLUTIONS
Definition: 2d.h:651
#define NUM_TABS
__inline void gr_string(int x, int y, const char *string, int resize_mode=GR_RESIZE_FULL)
Definition: 2d.h:769
#define WEAPON_TAB
int Invert_axis_defaults[]
general failure sound for any event
Definition: gamesnd.h:297
Definition: 2d.h:95
help pressed
Definition: gamesnd.h:293
int res
Definition: 2d.h:370
void clear_key_binding(short key)
char * Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS]
int Conflict_stamp
int max_h_unscaled
Definition: 2d.h:361
#define KEY_LSHIFT
Definition: key.h:134
#define CONTROL_CONFIG_XSTR
#define KEY_F8
Definition: key.h:150
#define CONTROL_H_COORD
#define KEY_F5
Definition: key.h:147
void control_check_indicate()
int center_offset_y
Definition: 2d.h:364
#define GR_MAYBE_CLEAR_RES(bmap)
Definition: 2d.h:639
fix t2
Definition: animplay.cpp:37
#define NUM_SYSTEM_KEYS
int center_w
Definition: 2d.h:363
void control_config_do_cancel(int fail=0)
void control_config_conflict_check()
void help_overlay_set_state(int overlay_id, int resolution_index, int state)
void set_ignore_gadgets(int state)
Definition: window.cpp:471
virtual void hide(int n)
Definition: gadget.cpp:207
void gr_set_color_fast(color *dst)
Definition: 2d.cpp:1197
#define KEY_PAGEUP
Definition: key.h:175
Definition: ui.h:195
std::basic_string< char, std::char_traits< char >, std::allocator< char > > SCP_string
Definition: vmallocator.h:21
int key
void gr_set_bitmap(int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha)
Definition: 2d.cpp:2105
GLsizeiptr size
Definition: Glext.h:5496
int max_w_unscaled
Definition: 2d.h:361
int control_config_do_reset()
#define KEY_DELETE
Definition: key.h:176
int y
bool disabled
whether this action should be available at all
char * textify_scancode(int code)
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: Glext.h:7308
const char *(* tooltip_handler)(const char *text)
Definition: ui.h:649
int bm_release(int handle, int clear_render_targets)
Frees both a bitmap's data and it's associated slot.
Definition: bmpman.cpp:2603
script_state Script_system("FS2_Open Scripting")
#define UNDO_BUTTON
GLenum type
Definition: Gl.h:1492
void control_config_do_bind()
#define B1_JUST_RELEASED
Definition: uidefs.h:50
short joy_id
joystick button bound to action
config_item Control_config[]
Stores the keyboard configuration.
#define KEY_I
Definition: key.h:90
#define CONTROL_CONFIG_OVERLAY
Definition: contexthelp.h:26
ui_button_info CC_Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS]
int control_config_undo_last()
#define KEY_F7
Definition: key.h:149
int control_config_remove_binding()
void common_set_interface_palette(char *filename)
color Color_text_error
Definition: alphacolors.cpp:27
short joy_default
default joystick button bound to action
void draw_forced(int frame_num)
Definition: button.cpp:104
void control_config_save_axis_undo(int axis)
int mouse_down(int btn)
Definition: mouse.cpp:315
#define NUM_BUTTONS
void destroy()
Definition: window.cpp:189
int Control_more_coords[GR_NUM_RESOLUTIONS][2]
int key_down_count(int scancode)
Definition: key.cpp:653
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
int Control_list_key_w[GR_NUM_RESOLUTIONS]
void set_mask_bmap(char *fname)
Definition: window.cpp:75
char * Joy_axis_text[NUM_AXIS_TEXT]
int set_bmaps(char *ani_filename, int nframes=3, int start_frame=1)
Definition: gadget.cpp:71
void common_free_interface_palette()
void control_config_scroll_line_up()
int Last_frame_timestamp
int joystick_read_raw_axis(int num_axes, int *axis)
Definition: joy-unix.cpp:549
#define GM_MULTIPLAYER
Definition: systemvars.h:18
int Ignored_keys[CCFG_MAX]
Definition: keycontrol.cpp:471
int Control_list_ctrl_w[GR_NUM_RESOLUTIONS]
int control_config_clear_all()
bool continuous_ongoing
whether this action is a continuous one and is currently ongoing
#define SHIP_TAB
int hotspot
Definition: ui.h:659
#define ACCEPT_BUTTON
int pressed()
Definition: button.cpp:325
void control_config_scroll_screen_down()
#define KEY_SHIFTED
Definition: key.h:62
#define CLEAR_ALL_BUTTON
int Conflicts_axes[NUM_JOY_AXIS_ACTIONS]
float check_control_timef(int id)
#define w(p)
Definition: modelsinc.h:68
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
void control_config_init()
GLdouble GLdouble z
Definition: Glext.h:5451
#define ALT_TOGGLE
#define KEY_ENTER
Definition: key.h:125
config_item_undo * get_undo_block(int size)
config_item_undo * next
#define GR_640
Definition: 2d.h:652
#define vm_strdup(ptr)
Definition: pstypes.h:549
const char * control_config_tooltip_handler(const char *str)
char tab
what tab (category) it belongs in
int Use_mouse_to_fly
Definition: mouse.cpp:51
#define BIND_BUTTON
int Control_check_count
int control_config_handle_conflict()
void control_config_toggle_invert()
#define KEY_R
Definition: key.h:99
UI_BUTTON button
Definition: ui.h:660
int control_config_accept()
GLenum GLuint id
Definition: Glext.h:5156
#define COMPUTER_TAB
#define KEY_RALT
Definition: key.h:138
int use_hack_to_get_around_stupid_problem_flag
Definition: ui.h:652
void control_used(int id)
void free_undo_block()
#define KEY_BACKSP
Definition: key.h:126
color HUD_color_debug
Definition: hud.cpp:85
bool save_player(player *_p=NULL)
Definition: plr.cpp:935
#define delta
Definition: fvi.cpp:418
int idx
Definition: multiui.cpp:761
GLdouble GLdouble t
Definition: Glext.h:5329
#define NUM_MOUSE_TEXT
void control_config_bind_axis(int i, int axis)
config_item Control_config_backup[CCFG_MAX]
void mouse_get_delta(int *dx, int *dy, int *dz)
Definition: mouse.cpp:358
GLint GLint GLint GLint GLint x
Definition: Glext.h:5182
int control_config_clear_other()
GLclampd n
Definition: Glext.h:7286
color Color_grey
Definition: alphacolors.cpp:32
unsigned char ubyte
Definition: pstypes.h:62
#define CONFLICT_FLASH_TIME
const char * XSTR(const char *str, int index)
Definition: localize.cpp:851
bool hasXSTR
whether we should translate this with an XSTR
void control_config_do_frame(float frametime)
#define KEY_F6
Definition: key.h:148
user_click (mouse selects a control)
Definition: gamesnd.h:305
color Color_bright_red
Definition: alphacolors.cpp:34
#define KEY_PRINT_SCRN
Definition: key.h:184
int max_w
Definition: 2d.h:360
#define NOX(s)
Definition: pstypes.h:473
int control_config_detect_axis()
char type
manner control should be checked in
int used
has control been used yet in mission? If so, this is the timestamp
#define KEY_F1
Definition: key.h:143
color Color_text_error_hi
Definition: alphacolors.cpp:27
#define CC_NUM_TEXT
#define vm_malloc(size)
Definition: pstypes.h:547
void set_hotkey(int keycode)
Definition: gadget.cpp:280
#define INVERT_AXIS
int RunCondition(int condition, char format='\0', void *data=NULL, class object *objp=NULL, int more_data=0)
Definition: scripting.cpp:924
fix t1
Definition: animplay.cpp:37
int Invert_axis[]
int bm_load(const char *real_filename)
Loads a bitmap so we can draw with it later.
Definition: bmpman.cpp:1119
color Color_text_subselected
Definition: alphacolors.cpp:26
GLboolean GLboolean GLboolean b
Definition: Glext.h:5781
void control_config_list_prepare()
short key_default
default key bound to action
#define CLEAR_OTHER_BUTTON
void update_dimensions(int _x, int _y, int _w, int _h)
Definition: gadget.cpp:187
color Color_text_selected
Definition: alphacolors.cpp:26
float joy_down_time(int btn)
Definition: joy-unix.cpp:120
#define FONT1
Definition: font.h:65
scroll pressed (and scroll)
Definition: gamesnd.h:296
color Color_white
Definition: alphacolors.cpp:32
void link_hotspot(int num)
Definition: gadget.cpp:50
void control_config_clear()
#define JOY_TOTAL_BUTTONS
Definition: joy.h:17
void create(UI_WINDOW *wnd, char *_text, int _x, int _y, int _w, int _h, int do_repeat=0, int ignore_focus=0)
Definition: button.cpp:26
#define KEY_ESC
Definition: key.h:124
const char * label
press briefing, ship selection or weapons bar (top-left)
Definition: gamesnd.h:291
int game_poll()
Definition: fredstubs.cpp:70
#define UI_XSTR_COLOR_PINK
Definition: ui.h:161
#define KEY_ALTED
Definition: key.h:63
void control_config_reset_defaults(int presetnum)
#define FONT2
Definition: font.h:66
int button_down()
Definition: button.cpp:363
#define TARGET_TAB
ubyte System_keys[NUM_SYSTEM_KEYS]
int kx
void create(int _x, int _y, int _w, int _h, int _flags, int _f_id=-1)
Definition: window.cpp:140
int joy_get_scaled_reading(int raw, int axn)
Definition: joy-unix.cpp:204
int gr_force_fit_string(char *str, int max_str, int max_width)
Definition: font.cpp:48
#define KEY_MASK
Definition: key.h:67
#define CONTROL_Y_COORD
A continous control that is activated as long as the key or button is held down.
Definition: ui.h:584
void control_config_scroll_screen_up()
screen gr_screen
Definition: 2d.cpp:46
void gr_get_string_size(int *w, int *h, const char *text, int len=9999)
Definition: font.cpp:196
void hud_squadmsg_save_keys(int do_scroll)
int Show_controls_info
Definition: ui.h:162
void enable(int n=1)
Definition: gadget.cpp:440
#define KEY_F2
Definition: key.h:144
int gr_get_font_height()
Definition: font.cpp:187
char * text
describes the action in the config screen
int center_offset_x
Definition: 2d.h:364
GLfloat GLfloat p
Definition: Glext.h:8373
An overhauled/updated debug console to allow monitoring, testing, and general debugging of new featur...
ubyte keyd_pressed[NUM_KEYS]
Definition: key.cpp:42
void common_play_highlight_sound()
Definition: gamesnd.cpp:1151
int jw
The total number of defined control actions (or last define + 1)
#define KEY_LALT
Definition: key.h:137
#define timestamp_elapsed(stamp)
Definition: timer.h:102
#define SHIFT_TOGGLE
void get_mouse_pos(int *xx, int *yy)
Definition: gadget.cpp:341
char * Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS]
void control_config_detect_axis_reset()
void SetHookVar(char *name, char format, void *data=NULL)
Definition: scripting.cpp:650
#define CHA_ONACTION
Definition: scripting.h:75
void control_config_close()
#define NUM_AXIS_TEXT
int Control_list_coords[GR_NUM_RESOLUTIONS][4]
int check_control(int id, int key)
int control_config_axis_default(int axis)
char * Joy_axis_action_text[NUM_JOY_AXIS_ACTIONS]
#define KEY_CTRLED
Definition: key.h:64
#define KEY_TAB
Definition: key.h:127
char * filename
Definition: ui.h:657
config_item_undo * Config_item_undo
void control_config_bind_joy(int i, int joy)
void gr_bitmap(int _x, int _y, int resize_mode)
Definition: 2d.cpp:1303
#define RESET_BUTTON
#define KEY_UP
Definition: key.h:179
#define UI_XSTR_COLOR_GREEN
Definition: ui.h:160
SCP_vector< config_item * > Control_config_presets
BANK_WHEN_PRESSED.
int Axis_map_to_defaults[]
color Color_text_normal
Definition: alphacolors.cpp:26
int joy_down_count(int btn, int reset_count)
Definition: joy-unix.cpp:105
#define NUM_INVERT_TEXT
int joy
void control_config_scroll_line_down()
void control_config_cancel_exit()
#define SEARCH_MODE
int cc_line_query_visible(int n)
int joy_get_unscaled_reading(int raw, int axn)
Definition: joy-unix.cpp:171
int Conflict_wnd_coords[GR_NUM_RESOLUTIONS][4]
#define SCROLL_UP_BUTTON
void launch_context_help()
void gamesnd_play_iface(int n)
Definition: gamesnd.cpp:260
void _cdecl gr_printf_no_resize(int x, int y, const char *format,...)
Definition: font.cpp:342
config_item * list
void disable()
Definition: gadget.cpp:432
void draw()
Definition: window.cpp:220
struct ui_button_info ui_button_info
void gr_set_font(int fontnum)
Definition: font.cpp:566
Joy_info joystick
Definition: joy-unix.cpp:45
#define CLEAR_BUTTON
#define CANCEL_BUTTON
SCP_vector< SCP_string > Control_config_preset_names
int mouse_down_count(int n, int reset_count)
Definition: mouse.cpp:236
void control_config_bind_key(int i, int key)
void control_config_clear_used_status()
int process(int key_in=-1, int process_mouse=1)
Definition: window.cpp:401
int help_overlay_get_index(const char *overlay_name)
int axis_valid[JOY_NUM_AXES]
Definition: joy.h:28
#define LIST_BUTTONS_MAX
DCF_BOOL(show_controls_info, Show_controls_info)
void _cdecl gr_printf_menu(int x, int y, const char *format,...)
Definition: font.cpp:314
int timer_get_milliseconds()
Definition: timer.cpp:150
void gameseq_post_event(int event)
GLenum GLint GLuint mask
Definition: Glext.h:5605
The total number of actions an axis may map to.
#define stricmp(s1, s2)
Definition: config.h:271
void control_config_do_search()
void control_config_toggle_modifier(int bit)
void help_overlay_maybe_blit(int overlay_id, int resolution_index)
int help_overlay_active(int overlay_id)
#define CONTROL_X_COORD
const GLubyte * c
Definition: Glext.h:8376
#define KEY_F3
Definition: key.h:145
void control_config_button_pressed(int n)
UI_XSTR CC_text[GR_NUM_RESOLUTIONS][CC_NUM_TEXT]
#define KEY_F9
Definition: key.h:151
GLint y
Definition: Gl.h:1505
int jx
int cc_index
int Control_list_key_x[GR_NUM_RESOLUTIONS]
#define strcpy_s(...)
Definition: safe_strings.h:67
int Axis_map_to[]
#define KEY_RSHIFT
Definition: key.h:135