mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 17:52:08 -10:00
This PR adds functionality to `Has Xstr:` in `controlconfiguredefaults.tbl`. Previously this value was use as a boolean, 0 or 1 to specify if the string should be used. This PR treats that field as an integer, and thus allows modders to specify which string to use for the control. If the value is 0 (default) then the string is not used, if the value is 1 then the defautl string is used and if the value is greater then 1 the game will use that specified index in strings.tbl. This allows modders to properly translate all control names. Specifially, six controls overlaped with pre-existing string.tbl indexes adn thus could not be translated properly. These are VIEW_TOPDOWN, VIEW_TRACK_TARGET, AUTO_PILOT_TOGGLE, NAV_CYCLE, TOGGLE_GLIDING, and CYCLE_PRIMARY_WEAPON_SEQUENCE. This PR will allow modders to now proerly have full control name translations. It also provides a fix for issue #1629.
2437 lines
64 KiB
C++
2437 lines
64 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "controlconfig/controlsconfig.h"
|
|
#include "debugconsole/console.h"
|
|
#include "freespace.h"
|
|
#include "gamehelp/contexthelp.h"
|
|
#include "gamesequence/gamesequence.h"
|
|
#include "gamesnd/gamesnd.h"
|
|
#include "globalincs/alphacolors.h"
|
|
#include "graphics/font.h"
|
|
#include "hud/hudsquadmsg.h"
|
|
#include "io/joy.h"
|
|
#include "io/key.h"
|
|
#include "io/timer.h"
|
|
#include "missionui/missionscreencommon.h"
|
|
#include "network/multi_pmsg.h"
|
|
#include "network/multiutil.h"
|
|
#include "scripting/scripting.h"
|
|
#include "pilotfile/pilotfile.h"
|
|
#include "popup/popup.h"
|
|
#include "ui/ui.h"
|
|
#include "ui/uidefs.h"
|
|
|
|
|
|
#ifndef NDEBUG
|
|
#include "hud/hud.h"
|
|
#endif
|
|
|
|
|
|
|
|
#define NUM_BUTTONS 19
|
|
#define NUM_TABS 4
|
|
|
|
// coordinate indicies
|
|
#define CONTROL_X_COORD 0
|
|
#define CONTROL_Y_COORD 1
|
|
#define CONTROL_W_COORD 2
|
|
#define CONTROL_H_COORD 3
|
|
|
|
const char* Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS] = {
|
|
"ControlConfig", // GR_640
|
|
"2_ControlConfig" // GR_1024
|
|
};
|
|
|
|
const char* Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
|
|
"ControlConfig-m", // GR_640
|
|
"2_ControlConfig-m" // GR_1024
|
|
};
|
|
|
|
// control list area
|
|
int Control_list_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{
|
|
32, 58, 198, 259 // GR_640
|
|
},
|
|
{
|
|
32, 94, 904, 424 // GR_1024
|
|
}
|
|
};
|
|
|
|
// width of the control name section of the list
|
|
int Control_list_ctrl_w[GR_NUM_RESOLUTIONS] = {
|
|
350, // GR_640
|
|
600 // GR_1024
|
|
};
|
|
|
|
// x start position of the binding area section of the list
|
|
int Control_list_key_x[GR_NUM_RESOLUTIONS] = {
|
|
397, // GR_640
|
|
712 // GR_1024
|
|
};
|
|
|
|
// width of the binding area section of the list
|
|
int Control_list_key_w[GR_NUM_RESOLUTIONS] = {
|
|
198, // GR_640
|
|
230 // GR_1024
|
|
};
|
|
|
|
// display the "more..." text under the control list
|
|
int Control_more_coords[GR_NUM_RESOLUTIONS][2] = {
|
|
{
|
|
320, 326 // GR_640
|
|
},
|
|
{
|
|
500, 542 // GR_1024
|
|
}
|
|
};
|
|
|
|
// area to display "conflicts with..." text
|
|
int Conflict_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{
|
|
32, 313, 250, 32 // GR_640
|
|
},
|
|
{
|
|
48, 508, 354, 46 // GR_1024
|
|
}
|
|
};
|
|
|
|
// conflict warning anim coords
|
|
int Conflict_warning_coords[GR_NUM_RESOLUTIONS][2] = {
|
|
{
|
|
-1, 420 // GR_640
|
|
},
|
|
{
|
|
-1, 669 // GR_1024
|
|
}
|
|
};
|
|
|
|
// for flashing the conflict text
|
|
#define CONFLICT_FLASH_TIME 250
|
|
int Conflict_stamp = -1;
|
|
int Conflict_bright = 0;
|
|
|
|
#define LIST_BUTTONS_MAX 42
|
|
#define JOY_AXIS 0x80000
|
|
|
|
static int Num_cc_lines;
|
|
static struct {
|
|
const char *label;
|
|
int cc_index; // index into Control_config of item
|
|
int y; // Y coordinate of line
|
|
int kx, kw, jx, jw; // x start and width of keyboard and joystick bound text
|
|
} Cc_lines[CCFG_MAX];
|
|
|
|
// struct to hold backup config_item elements so we can undo them
|
|
struct config_item_undo {
|
|
int size;
|
|
int *index; // array (size) of Control_config indices of replaced elements
|
|
config_item *list; // array (size) of original elements
|
|
int reset_to_preset; // if >=0, then we ignore the above list and simply reset to the given preset instead
|
|
config_item_undo *next;
|
|
};
|
|
|
|
config_item Control_config_backup[CCFG_MAX];
|
|
|
|
int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
|
|
int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
|
|
|
|
// all this stuff is localized/externalized
|
|
#define NUM_AXIS_TEXT 6
|
|
#define NUM_MOUSE_TEXT 5
|
|
#define NUM_MOUSE_AXIS_TEXT 2
|
|
#define NUM_INVERT_TEXT 2
|
|
char *Joy_axis_action_text[NUM_JOY_AXIS_ACTIONS];
|
|
char *Joy_axis_text[NUM_AXIS_TEXT];
|
|
char *Mouse_button_text[NUM_MOUSE_TEXT];
|
|
char *Mouse_axis_text[NUM_MOUSE_AXIS_TEXT];
|
|
char *Invert_text[NUM_INVERT_TEXT];
|
|
|
|
int Control_check_count = 0;
|
|
|
|
static int Tab; // which tab we are currently in
|
|
static int Binding_mode = 0; // are we waiting for a key to bind it?
|
|
static int Bind_time = 0;
|
|
static int Search_mode = 0; // are we waiting for a key to bind it?
|
|
static int Last_key = -1;
|
|
static int Selected_line = 0; // line that is currently selected for binding
|
|
static int Selected_item = -1; // -1 = none, 0 = key, 1 = button
|
|
static int Scroll_offset;
|
|
static int Axis_override = -1;
|
|
static int Background_bitmap;
|
|
static int Conflicts_tabs[NUM_TABS];
|
|
static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list
|
|
static UI_WINDOW Ui_window;
|
|
static unsigned int Defaults_cycle_pos; // the controls preset that was last selected
|
|
|
|
int Control_config_overlay_id;
|
|
|
|
static struct {
|
|
int key; // index of other control in conflict with this one
|
|
int joy; // index of other control in conflict with this one
|
|
} Conflicts[CCFG_MAX];
|
|
|
|
int Conflicts_axes[NUM_JOY_AXIS_ACTIONS];
|
|
|
|
#define TARGET_TAB 0
|
|
#define SHIP_TAB 1
|
|
#define WEAPON_TAB 2
|
|
#define COMPUTER_TAB 3
|
|
#define SCROLL_UP_BUTTON 4
|
|
#define SCROLL_DOWN_BUTTON 5
|
|
#define ALT_TOGGLE 6
|
|
#define SHIFT_TOGGLE 7
|
|
#define INVERT_AXIS 8
|
|
#define CANCEL_BUTTON 9
|
|
#define UNDO_BUTTON 10
|
|
#define RESET_BUTTON 11
|
|
#define SEARCH_MODE 12
|
|
#define BIND_BUTTON 13
|
|
#define HELP_BUTTON 14
|
|
#define ACCEPT_BUTTON 15
|
|
#define CLEAR_OTHER_BUTTON 16
|
|
#define CLEAR_ALL_BUTTON 17
|
|
#define CLEAR_BUTTON 18
|
|
|
|
ui_button_info CC_Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
|
|
{ // GR_640
|
|
ui_button_info("CCB_00", 32, 348, 17, 384, 0), // target tab
|
|
ui_button_info("CCB_01", 101, 348, 103, 384, 1), // ship tab
|
|
ui_button_info("CCB_02", 173, 352, 154, 384, 2), // weapon tab
|
|
ui_button_info("CCB_03", 242, 347, 244, 384, 3), // computer/misc tab
|
|
ui_button_info("CCB_04", 614, 73, -1, -1, 4), // scroll up
|
|
ui_button_info("CCB_05", 614, 296, -1, -1, 5), // scroll down
|
|
ui_button_info("CCB_06", 17, 452, 12, 440, 6), // alt toggle
|
|
ui_button_info("CCB_07", 56, 452, 50, 440, 7), // shift toggle
|
|
ui_button_info("CCB_09", 162, 452, 155, 440, 9), // invert
|
|
ui_button_info("CCB_10", 404, 1, 397, 45, 10), // cancel
|
|
ui_button_info("CCB_11", 582, 347, 586, 386, 11), // undo
|
|
ui_button_info("CCB_12", 576, 1, 578, 45, 12), // default
|
|
ui_button_info("CCB_13", 457, 4, 453, 45, 13), // search
|
|
ui_button_info("CCB_14", 516, 4, 519, 45, 14), // bind
|
|
ui_button_info("CCB_15", 540, 428, 500, 440, 15), // help
|
|
ui_button_info("CCB_16", 574, 432, 571, 412, 16), // accept
|
|
ui_button_info("CCB_18", 420, 346, 417, 386, 18), // clear other
|
|
ui_button_info("CCB_19", 476, 346, 474, 386, 19), // clear all
|
|
ui_button_info("CCB_20", 524, 346, 529, 386, 20), // clear button
|
|
},
|
|
{ // GR_1024
|
|
ui_button_info("2_CCB_00", 51, 557, 27, 615, 0), // target tab
|
|
ui_button_info("2_CCB_01", 162, 557, 166, 615, 1), // ship tab
|
|
ui_button_info("2_CCB_02", 277, 563, 246, 615, 2), // weapon tab
|
|
ui_button_info("2_CCB_03", 388, 555, 391, 615, 3), // computer/misc tab
|
|
ui_button_info("2_CCB_04", 982, 117, -1, -1, 4), // scroll up
|
|
ui_button_info("2_CCB_05", 982, 474, -1, -1, 5), // scroll down
|
|
ui_button_info("2_CCB_06", 28, 723, 24, 704, 6), // alt toggle
|
|
ui_button_info("2_CCB_07", 89, 723, 80, 704, 7), // shift toggle
|
|
ui_button_info("2_CCB_09", 260, 723, 249, 704, 9), // invert
|
|
ui_button_info("2_CCB_10", 646, 2, 635, 71, 10), // cancel
|
|
ui_button_info("2_CCB_11", 932, 555, 938, 619, 11), // undo
|
|
ui_button_info("2_CCB_12", 921, 1, 923, 71, 12), // default
|
|
ui_button_info("2_CCB_13", 732, 6, 726, 71, 13), // search
|
|
ui_button_info("2_CCB_14", 825, 6, 831, 71, 14), // bind
|
|
ui_button_info("2_CCB_15", 864, 685, 800, 704, 15), // help
|
|
ui_button_info("2_CCB_16", 919, 692, 914, 660, 16), // accept
|
|
ui_button_info("2_CCB_18", 672, 553, 668, 619, 18), // clear other
|
|
ui_button_info("2_CCB_19", 761, 553, 749, 619, 19), // clear all
|
|
ui_button_info("2_CCB_20", 838, 553, 846, 619, 20), // clear button
|
|
}
|
|
};
|
|
|
|
// strings
|
|
#define CC_NUM_TEXT 20
|
|
UI_XSTR CC_text[GR_NUM_RESOLUTIONS][CC_NUM_TEXT] = {
|
|
{ // GR_640
|
|
{ "Targeting", 1340, 17, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][TARGET_TAB].button },
|
|
{ "Ship", 1341, 103, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIP_TAB].button },
|
|
{ "Weapons", 1065, 154, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][WEAPON_TAB].button },
|
|
{ "Misc", 1411, 244, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][COMPUTER_TAB].button },
|
|
{ "Alt", 1510, 12, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][ALT_TOGGLE].button },
|
|
{ "Shift", 1511, 50, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIFT_TOGGLE].button },
|
|
{ "Invert", 1342, 155, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][INVERT_AXIS].button },
|
|
{ "Cancel", 641, 397, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CANCEL_BUTTON].button },
|
|
{ "Undo", 1343, 586, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][UNDO_BUTTON].button },
|
|
{ "Defaults", 1344, 568, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][RESET_BUTTON].button },
|
|
{ "Search", 1345, 453, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SEARCH_MODE].button },
|
|
{ "Bind", 1346, 519, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][BIND_BUTTON].button },
|
|
{ "Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][HELP_BUTTON].button },
|
|
{ "Accept", 1035, 571, 412, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][ACCEPT_BUTTON].button },
|
|
{ "Clear", 1347, 417, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
|
|
{ "Conflict", 1348, 406, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
|
|
{ "Clear", 1413, 474, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
|
|
{ "All", 1349, 483, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
|
|
{ "Clear", 1414, 529, 388, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
|
|
{ "Selected", 1350, 517, 396, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
|
|
},
|
|
{ // GR_1024
|
|
{ "Targeting", 1340, 47, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][TARGET_TAB].button },
|
|
{ "Ship", 1341, 176, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIP_TAB].button },
|
|
{ "Weapons", 1065, 266, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][WEAPON_TAB].button },
|
|
{ "Misc", 1411, 401, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][COMPUTER_TAB].button },
|
|
{ "Alt", 1510, 29, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][ALT_TOGGLE].button },
|
|
{ "Shift", 1511, 85, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIFT_TOGGLE].button },
|
|
{ "Invert", 1342, 254, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][INVERT_AXIS].button },
|
|
{ "Cancel", 641, 655, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CANCEL_BUTTON].button },
|
|
{ "Undo", 1343, 938, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][UNDO_BUTTON].button },
|
|
{ "Defaults", 1344, 923, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][RESET_BUTTON].button },
|
|
{ "Search", 1345, 746, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SEARCH_MODE].button },
|
|
{ "Bind", 1346, 846, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][BIND_BUTTON].button },
|
|
{ "Help", 928, 800, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][HELP_BUTTON].button },
|
|
{ "Accept", 1035, 914, 660, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][ACCEPT_BUTTON].button },
|
|
{ "Clear", 1347, 683, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
|
|
{ "Conflict", 1348, 666, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
|
|
{ "Clear", 1413, 759, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
|
|
{ "All", 1349, 772, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
|
|
{ "Clear", 1414, 871, 619, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
|
|
{ "Selected", 1350, 852, 634, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
|
|
}
|
|
};
|
|
|
|
// linked list head of undo items
|
|
config_item_undo *Config_item_undo;
|
|
|
|
// same indices as Scan_code_text[]. Indicates if a scancode is allowed to be bound.
|
|
int Config_allowed[] = {
|
|
0, 0, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1,
|
|
|
|
1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 0, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 1, 1, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 1, 0, 0,
|
|
1, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 1,
|
|
1, 1, 0, 1, 0, 1, 0, 1,
|
|
1, 1, 1, 1, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
};
|
|
|
|
#ifndef NDEBUG
|
|
int Show_controls_info = 0;
|
|
|
|
DCF_BOOL(show_controls_info, Show_controls_info);
|
|
#endif
|
|
|
|
static int Axes_origin[JOY_NUM_AXES];
|
|
|
|
static int joy_get_unscaled_reading(int raw)
|
|
{
|
|
int rng;
|
|
|
|
rng = JOY_AXIS_MAX - JOY_AXIS_MIN;
|
|
raw -= JOY_AXIS_MIN; // adjust for linear range starting at 0
|
|
|
|
// cap at limits
|
|
if (raw < 0)
|
|
raw = 0;
|
|
if (raw > rng)
|
|
raw = rng;
|
|
|
|
return (int) ((std::uint64_t) raw * (std::uint64_t) F1_0 / (std::uint64_t) rng); // convert to 0 - F1_0 range, 64bit ints used to avoid uint overflow
|
|
}
|
|
|
|
int joy_get_scaled_reading(int raw)
|
|
{
|
|
int x, d, dead_zone, rng;
|
|
float percent, sensitivity_percent, non_sensitivity_percent;
|
|
|
|
raw -= JOY_AXIS_CENTER;
|
|
|
|
dead_zone = (JOY_AXIS_MAX - JOY_AXIS_MIN) * Joy_dead_zone_size / 100;
|
|
|
|
if (raw < -dead_zone) {
|
|
rng = JOY_AXIS_CENTER - JOY_AXIS_MIN - dead_zone;
|
|
d = -raw - dead_zone;
|
|
} else if (raw > dead_zone) {
|
|
rng = JOY_AXIS_MAX - JOY_AXIS_CENTER - dead_zone;
|
|
d = raw - dead_zone;
|
|
} else
|
|
return 0;
|
|
|
|
if (d > rng)
|
|
d = rng;
|
|
|
|
Assert(Joy_sensitivity >= 0 && Joy_sensitivity <= 9);
|
|
|
|
// compute percentages as a range between 0 and 1
|
|
sensitivity_percent = (float) Joy_sensitivity / 9.0f;
|
|
non_sensitivity_percent = (float) (9 - Joy_sensitivity) / 9.0f;
|
|
|
|
// find percent of max axis is at
|
|
percent = (float) d / (float) rng;
|
|
|
|
// work sensitivity on axis value
|
|
percent = (percent * sensitivity_percent + percent * percent * percent * percent * percent * non_sensitivity_percent);
|
|
|
|
x = (int) ((float) F1_0 * percent);
|
|
|
|
//nprintf(("AI", "d=%6i, sens=%3i, percent=%6.3f, val=%6i, ratio=%6.3f\n", d, Joy_sensitivity, percent, (raw<0) ? -x : x, (float) d/x));
|
|
|
|
if (raw < 0)
|
|
return -x;
|
|
|
|
return x;
|
|
}
|
|
|
|
void control_config_detect_axis_reset()
|
|
{
|
|
joystick_read_raw_axis(JOY_NUM_AXES, Axes_origin);
|
|
}
|
|
|
|
int control_config_detect_axis()
|
|
{
|
|
int i, d, axis = -1, delta = 16384;
|
|
int axes_values[JOY_NUM_AXES];
|
|
int dx, dy, dz, fudge = 7;
|
|
|
|
joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
|
|
for (i=0; i<JOY_NUM_AXES; i++) {
|
|
d = abs(axes_values[i] - Axes_origin[i]);
|
|
if (d > delta) {
|
|
axis = i;
|
|
delta = d;
|
|
}
|
|
}
|
|
|
|
if ( (axis == -1) && Use_mouse_to_fly ) {
|
|
mouse_get_delta( &dx, &dy, &dz );
|
|
|
|
if ( (dx > fudge) || (dx < -fudge) ) {
|
|
axis = 0;
|
|
} else if ( (dy > fudge) || (dy < -fudge) ) {
|
|
axis = 1;
|
|
} else if ( (dz > fudge) || (dz < -fudge) ) {
|
|
axis = 2;
|
|
}
|
|
}
|
|
|
|
return axis;
|
|
}
|
|
|
|
void control_config_conflict_check()
|
|
{
|
|
int i, j;
|
|
|
|
for (i=0; i<CCFG_MAX; i++) {
|
|
Conflicts[i].key = Conflicts[i].joy = -1;
|
|
}
|
|
|
|
for (i=0; i<NUM_TABS; i++) {
|
|
Conflicts_tabs[i] = 0;
|
|
}
|
|
|
|
for (i=0; i<CCFG_MAX-1; i++) {
|
|
for (j=i+1; j<CCFG_MAX; j++) {
|
|
if ((Control_config[i].key_id >= 0) && (Control_config[i].key_id == Control_config[j].key_id)) {
|
|
// If one of the conflicting keys is disabled, then this silently clears
|
|
// the disabled key to prevent the conflict; a conflict is recorded only
|
|
// if both keys are enabled
|
|
|
|
if (Control_config[i].disabled)
|
|
Control_config[i].key_id = (short) -1;
|
|
else if (Control_config[j].disabled)
|
|
Control_config[j].key_id = (short) -1;
|
|
else {
|
|
Conflicts[i].key = j;
|
|
Conflicts[j].key = i;
|
|
Conflicts_tabs[ Control_config[i].tab ] = 1;
|
|
Conflicts_tabs[ Control_config[j].tab ] = 1;
|
|
}
|
|
}
|
|
|
|
if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) {
|
|
// Same as above
|
|
|
|
if (Control_config[i].disabled)
|
|
Control_config[i].joy_id = (short) -1;
|
|
else if (Control_config[j].disabled)
|
|
Control_config[j].joy_id = (short) -1;
|
|
else {
|
|
Conflicts[i].joy = j;
|
|
Conflicts[j].joy = i;
|
|
Conflicts_tabs[ Control_config[i].tab ] = 1;
|
|
Conflicts_tabs[ Control_config[j].tab ] = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
|
|
Conflicts_axes[i] = -1;
|
|
}
|
|
|
|
for (i=0; i<NUM_JOY_AXIS_ACTIONS-1; i++) {
|
|
for (j=i+1; j<NUM_JOY_AXIS_ACTIONS; j++) {
|
|
if ((Axis_map_to[i] >= 0) && (Axis_map_to[i] == Axis_map_to[j])) {
|
|
Conflicts_axes[i] = j;
|
|
Conflicts_axes[j] = i;
|
|
Conflicts_tabs[SHIP_TAB] = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// do list setup required prior to rendering and checking for the controls listing. Called when list changes
|
|
void control_config_list_prepare()
|
|
{
|
|
int j, y, z;
|
|
int font_height = gr_get_font_height();
|
|
|
|
Num_cc_lines = y = z = 0;
|
|
while (z < CCFG_MAX) {
|
|
if (Control_config[z].tab == Tab && !Control_config[z].disabled) {
|
|
if (Control_config[z].hasXSTR > 1) {
|
|
Cc_lines[Num_cc_lines].label = XSTR(Control_config[z].text, Control_config[z].hasXSTR);
|
|
} else if (Control_config[z].hasXSTR == 1) {
|
|
Cc_lines[Num_cc_lines].label = XSTR(Control_config[z].text, CONTROL_CONFIG_XSTR + z);
|
|
} else {
|
|
Cc_lines[Num_cc_lines].label = Control_config[z].text;
|
|
}
|
|
|
|
Cc_lines[Num_cc_lines].cc_index = z;
|
|
Cc_lines[Num_cc_lines++].y = y;
|
|
y += font_height + 2;
|
|
}
|
|
|
|
z++;
|
|
}
|
|
|
|
if (Tab == SHIP_TAB) {
|
|
for (j=0; j<NUM_JOY_AXIS_ACTIONS; j++) {
|
|
Cc_lines[Num_cc_lines].label = Joy_axis_action_text[j];
|
|
Cc_lines[Num_cc_lines].cc_index = j | JOY_AXIS;
|
|
Cc_lines[Num_cc_lines++].y = y;
|
|
y += font_height + 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
int cc_line_query_visible(int n)
|
|
{
|
|
int y;
|
|
|
|
if ((n < 0) || (n >= Num_cc_lines)) {
|
|
return 0;
|
|
}
|
|
|
|
y = Cc_lines[n].y - Cc_lines[Scroll_offset].y;
|
|
if ((y < 0) || (y + gr_get_font_height() > Control_list_coords[gr_screen.res][CONTROL_H_COORD])){
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// allocates the required space for one undo block and put it in the beginning of the linked list (top of a stack).
|
|
// Returns a pointer to this newly allocated block
|
|
config_item_undo *get_undo_block(int size)
|
|
{
|
|
config_item_undo *ptr;
|
|
|
|
ptr = (config_item_undo *) vm_malloc( sizeof(config_item_undo) );
|
|
Assert(ptr);
|
|
ptr->next = Config_item_undo;
|
|
Config_item_undo = ptr;
|
|
|
|
ptr->size = size;
|
|
if (size) {
|
|
ptr->index = (int *) vm_malloc( sizeof(int) * size );
|
|
Assert(ptr->index);
|
|
ptr->list = (config_item *) vm_malloc( sizeof(config_item) * size );
|
|
Assert(ptr->list);
|
|
|
|
} else {
|
|
ptr->index = NULL;
|
|
ptr->list = NULL;
|
|
}
|
|
|
|
ptr->reset_to_preset = -1;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
// frees one undo block. The first one in the list (top of the stack) to be precise.
|
|
void free_undo_block()
|
|
{
|
|
config_item_undo *ptr;
|
|
|
|
ptr = Config_item_undo;
|
|
if (!ptr) {
|
|
return;
|
|
}
|
|
|
|
Config_item_undo = ptr->next;
|
|
if (ptr->size) {
|
|
vm_free(ptr->list);
|
|
vm_free(ptr->index);
|
|
}
|
|
|
|
vm_free(ptr);
|
|
}
|
|
|
|
// undo the most recent binding changes
|
|
int control_config_undo_last()
|
|
{
|
|
int i, z, tab;
|
|
|
|
if (!Config_item_undo) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return -1;
|
|
}
|
|
|
|
if (Config_item_undo->reset_to_preset > -1) {
|
|
control_config_reset_defaults(Config_item_undo->reset_to_preset);
|
|
} else {
|
|
if (Config_item_undo->index[0] & JOY_AXIS) {
|
|
tab = SHIP_TAB;
|
|
} else {
|
|
tab = Control_config[Config_item_undo->index[0]].tab;
|
|
}
|
|
|
|
for (i=1; i<Config_item_undo->size; i++) {
|
|
if (Config_item_undo->index[i] & JOY_AXIS) {
|
|
if (tab != SHIP_TAB) {
|
|
tab = -1;
|
|
}
|
|
|
|
} else {
|
|
if (Control_config[Config_item_undo->index[i]].tab != tab) {
|
|
tab = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tab >= 0) {
|
|
Tab = tab;
|
|
}
|
|
|
|
for (i=0; i<Config_item_undo->size; i++) {
|
|
z = Config_item_undo->index[i];
|
|
if (z & JOY_AXIS) {
|
|
config_item *ptr;
|
|
|
|
z &= ~JOY_AXIS;
|
|
ptr = &Config_item_undo->list[i];
|
|
Axis_map_to[z] = ptr->joy_id;
|
|
Invert_axis[z] = ptr->used;
|
|
|
|
} else {
|
|
Control_config[z] = Config_item_undo->list[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
free_undo_block();
|
|
control_config_conflict_check();
|
|
control_config_list_prepare();
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
return 0;
|
|
}
|
|
|
|
void control_config_save_axis_undo(int axis)
|
|
{
|
|
config_item_undo *ptr;
|
|
config_item item;
|
|
|
|
memset( &item, 0, sizeof(config_item) );
|
|
|
|
item.joy_id = (short) Axis_map_to[axis];
|
|
item.used = Invert_axis[axis];
|
|
|
|
ptr = get_undo_block(1);
|
|
ptr->index[0] = axis | JOY_AXIS;
|
|
ptr->list[0] = item;
|
|
}
|
|
|
|
void control_config_bind_key(int i, int key)
|
|
{
|
|
config_item_undo *ptr;
|
|
|
|
ptr = get_undo_block(1);
|
|
ptr->index[0] = i;
|
|
ptr->list[0] = Control_config[i];
|
|
Control_config[i].key_id = (short) key;
|
|
}
|
|
|
|
void control_config_bind_joy(int i, int joy)
|
|
{
|
|
config_item_undo *ptr;
|
|
|
|
ptr = get_undo_block(1);
|
|
ptr->index[0] = i;
|
|
ptr->list[0] = Control_config[i];
|
|
Control_config[i].joy_id = (short) joy;
|
|
}
|
|
|
|
void control_config_bind_axis(int i, int axis)
|
|
{
|
|
control_config_save_axis_undo(i);
|
|
Axis_map_to[i] = axis;
|
|
}
|
|
|
|
int control_config_remove_binding()
|
|
{
|
|
int z;
|
|
config_item_undo *ptr;
|
|
|
|
if (Selected_line < 0) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return -1;
|
|
}
|
|
|
|
z = Cc_lines[Selected_line].cc_index;
|
|
if (z & JOY_AXIS) {
|
|
z &= ~JOY_AXIS;
|
|
if (Axis_map_to[z] < 0) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return -1;
|
|
}
|
|
|
|
control_config_save_axis_undo(z);
|
|
Axis_map_to[z] = -1;
|
|
control_config_conflict_check();
|
|
control_config_list_prepare();
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
Selected_item = -1;
|
|
return 0;
|
|
}
|
|
|
|
if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return -1;
|
|
}
|
|
|
|
ptr = get_undo_block(1);
|
|
ptr->index[0] = z;
|
|
ptr->list[0] = Control_config[z];
|
|
|
|
if (Selected_item && (Control_config[z].joy_id >= 0)) { // if not just key selected (which would be 0)
|
|
Control_config[z].joy_id = (short) -1;
|
|
}
|
|
|
|
if ((Selected_item != 1) && (Control_config[z].key_id >= 0)) { // if not just joy button selected (1)
|
|
Control_config[z].key_id = (short) -1;
|
|
}
|
|
|
|
control_config_conflict_check();
|
|
control_config_list_prepare();
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
Selected_item = -1;
|
|
return 0;
|
|
}
|
|
|
|
int control_config_clear_other()
|
|
{
|
|
int z, i, j, total = 0;
|
|
config_item_undo *ptr;
|
|
|
|
if (Selected_line < 0) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return -1;
|
|
}
|
|
|
|
z = Cc_lines[Selected_line].cc_index;
|
|
if (z & JOY_AXIS) {
|
|
config_item item;
|
|
|
|
z &= ~JOY_AXIS;
|
|
if (Axis_map_to[z] < 0) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return -1;
|
|
}
|
|
|
|
for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
|
|
if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
|
|
total++;
|
|
}
|
|
}
|
|
|
|
if (!total) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return -1;
|
|
}
|
|
|
|
ptr = get_undo_block(total);
|
|
for (i=j=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
|
|
if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
|
|
memset( &item, 0, sizeof(config_item) );
|
|
|
|
item.joy_id = (short) Axis_map_to[i];
|
|
item.used = Invert_axis[i];
|
|
|
|
ptr->index[j] = i | JOY_AXIS;
|
|
ptr->list[j] = item;
|
|
j++;
|
|
|
|
Axis_map_to[i] = -1;
|
|
}
|
|
}
|
|
control_config_conflict_check();
|
|
control_config_list_prepare();
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
return 0;
|
|
}
|
|
|
|
for (i=0; i<CCFG_MAX; i++) {
|
|
if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) ) {
|
|
if (i != z) {
|
|
total++;
|
|
}
|
|
}
|
|
}
|
|
if (!total) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return -1;
|
|
}
|
|
|
|
if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return -1;
|
|
}
|
|
|
|
// now, back up the old bindings so we can undo if we want to
|
|
ptr = get_undo_block(total);
|
|
for (i=j=0; i<CCFG_MAX; i++) {
|
|
if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) ) {
|
|
if (i != z) {
|
|
ptr->index[j] = i;
|
|
ptr->list[j] = Control_config[i];
|
|
j++;
|
|
|
|
if (Control_config[i].key_id == Control_config[z].key_id) {
|
|
Control_config[i].key_id = (short) -1;
|
|
}
|
|
|
|
if (Control_config[i].joy_id == Control_config[z].joy_id) {
|
|
Control_config[i].joy_id = (short) -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
control_config_conflict_check();
|
|
control_config_list_prepare();
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
return 0;
|
|
}
|
|
|
|
int control_config_clear_all()
|
|
{
|
|
int i, j, total = 0;
|
|
config_item_undo *ptr;
|
|
|
|
// first, determine how many bindings need to be changed
|
|
for (i=0; i<CCFG_MAX; i++) {
|
|
if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
|
|
total++;
|
|
}
|
|
}
|
|
|
|
if (!total) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return -1;
|
|
}
|
|
|
|
// now, back up the old bindings so we can undo if we want to
|
|
ptr = get_undo_block(total);
|
|
for (i=j=0; i<CCFG_MAX; i++) {
|
|
if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
|
|
ptr->index[j] = i;
|
|
ptr->list[j] = Control_config[i];
|
|
j++;
|
|
}
|
|
}
|
|
|
|
Assert(j == total);
|
|
for (i=0; i<CCFG_MAX; i++) {
|
|
Control_config[i].key_id = Control_config[i].joy_id = -1;
|
|
}
|
|
|
|
control_config_conflict_check();
|
|
control_config_list_prepare();
|
|
gamesnd_play_iface(InterfaceSounds::RESET_PRESSED);
|
|
return 0;
|
|
}
|
|
|
|
int control_config_axis_default(int axis)
|
|
{
|
|
Assert(axis >= 0);
|
|
|
|
if ( axis > 1 ) {
|
|
if (Axis_map_to_defaults[axis] < 0) {
|
|
return -1;
|
|
}
|
|
|
|
auto joystick = io::joystick::getCurrentJoystick();
|
|
if (joystick == nullptr || Axis_map_to_defaults[axis] >= joystick->numAxes()) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return Axis_map_to_defaults[axis];
|
|
}
|
|
|
|
int control_config_do_reset()
|
|
{
|
|
int i, j, total = 0;
|
|
config_item_undo *ptr;
|
|
config_item item;
|
|
config_item *preset;
|
|
bool cycling_presets = false;
|
|
|
|
// If there are presets, then we'll cycle to the next preset and reset to that
|
|
if (!Control_config_presets.empty()) {
|
|
cycling_presets = true;
|
|
|
|
if (++Defaults_cycle_pos >= Control_config_presets.size())
|
|
Defaults_cycle_pos = 0;
|
|
|
|
preset = Control_config_presets[Defaults_cycle_pos];
|
|
} else {
|
|
// If there are no presets, then we'll always reset to the hardcoded defaults
|
|
preset = Control_config;
|
|
}
|
|
|
|
// first, determine how many bindings need to be changed
|
|
for (i=0; i<CCFG_MAX; i++) {
|
|
if ((Control_config[i].key_id != preset[i].key_default) || (Control_config[i].joy_id != preset[i].joy_default)) {
|
|
total++;
|
|
}
|
|
}
|
|
|
|
for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
|
|
if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
|
|
total++;
|
|
}
|
|
}
|
|
|
|
if (!total && !cycling_presets) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return -1;
|
|
}
|
|
|
|
// now, back up the old bindings so we can undo if we want to
|
|
ptr = get_undo_block(total);
|
|
for (i=j=0; i<CCFG_MAX; i++) {
|
|
if ((Control_config[i].key_id != preset[i].key_default) || (Control_config[i].joy_id != preset[i].joy_default)) {
|
|
ptr->index[j] = i;
|
|
ptr->list[j] = Control_config[i];
|
|
j++;
|
|
}
|
|
}
|
|
|
|
for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
|
|
if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
|
|
memset( &item, 0, sizeof(config_item) );
|
|
|
|
item.joy_id = (short) Axis_map_to[i];
|
|
item.used = Invert_axis[i];
|
|
|
|
ptr->index[j] = i | JOY_AXIS;
|
|
ptr->list[j] = item;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
Assert(j == total);
|
|
|
|
if (cycling_presets)
|
|
control_config_reset_defaults(Defaults_cycle_pos);
|
|
else
|
|
control_config_reset_defaults();
|
|
|
|
control_config_conflict_check();
|
|
control_config_list_prepare();
|
|
gamesnd_play_iface(InterfaceSounds::RESET_PRESSED);
|
|
return 0;
|
|
}
|
|
|
|
// This sets all the controls to the default values in the given preset
|
|
// If no preset is given, the hardcoded defaults of Control_config are used
|
|
void control_config_reset_defaults(int presetnum)
|
|
{
|
|
int i;
|
|
config_item *preset;
|
|
|
|
if (presetnum >= 0)
|
|
preset = Control_config_presets[presetnum];
|
|
else
|
|
preset = Control_config;
|
|
|
|
// Reset keyboard defaults
|
|
for (i=0; i<CCFG_MAX; i++) {
|
|
// Note that key_default and joy_default are NOT overwritten here;
|
|
// they should retain the values of the first preset because
|
|
// for example the key-pressed SEXP works off the defaults of the first preset
|
|
Control_config[i].key_id = preset[i].key_default;
|
|
Control_config[i].joy_id = preset[i].joy_default;
|
|
Control_config[i].tab = preset[i].tab;
|
|
Control_config[i].hasXSTR = preset[i].hasXSTR;
|
|
Control_config[i].type = preset[i].type;
|
|
Control_config[i].disabled = preset[i].disabled;
|
|
}
|
|
|
|
for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
|
|
Axis_map_to[i] = control_config_axis_default(i);
|
|
Invert_axis[i] = Invert_axis_defaults[i];
|
|
}
|
|
}
|
|
|
|
void control_config_scroll_screen_up()
|
|
{
|
|
if (Scroll_offset) {
|
|
Scroll_offset--;
|
|
Assert(Selected_line > Scroll_offset);
|
|
while (!cc_line_query_visible(Selected_line)) {
|
|
Selected_line--;
|
|
}
|
|
|
|
Selected_item = -1;
|
|
gamesnd_play_iface(InterfaceSounds::SCROLL);
|
|
|
|
} else {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
}
|
|
}
|
|
|
|
void control_config_scroll_line_up()
|
|
{
|
|
if (Selected_line) {
|
|
Selected_line--;
|
|
if (Selected_line < Scroll_offset) {
|
|
Scroll_offset = Selected_line;
|
|
}
|
|
|
|
Selected_item = -1;
|
|
gamesnd_play_iface(InterfaceSounds::SCROLL);
|
|
|
|
} else {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
}
|
|
}
|
|
|
|
void control_config_scroll_screen_down()
|
|
{
|
|
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]) {
|
|
Scroll_offset++;
|
|
while (!cc_line_query_visible(Selected_line)) {
|
|
Selected_line++;
|
|
Assert(Selected_line < Num_cc_lines);
|
|
}
|
|
|
|
Selected_item = -1;
|
|
gamesnd_play_iface(InterfaceSounds::SCROLL);
|
|
|
|
} else {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
}
|
|
}
|
|
|
|
void control_config_scroll_line_down()
|
|
{
|
|
if (Selected_line < Num_cc_lines - 1) {
|
|
Selected_line++;
|
|
Assert(Selected_line > Scroll_offset);
|
|
while (!cc_line_query_visible(Selected_line)) {
|
|
Scroll_offset++;
|
|
}
|
|
|
|
Selected_item = -1;
|
|
gamesnd_play_iface(InterfaceSounds::SCROLL);
|
|
|
|
} else {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
}
|
|
}
|
|
|
|
void control_config_toggle_modifier(int bit)
|
|
{
|
|
int k, z;
|
|
|
|
z = Cc_lines[Selected_line].cc_index;
|
|
Assert(!(z & JOY_AXIS));
|
|
k = Control_config[z].key_id;
|
|
if (k < 0) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return;
|
|
}
|
|
|
|
control_config_bind_key(z, k ^ bit);
|
|
control_config_conflict_check();
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
}
|
|
|
|
void control_config_toggle_invert()
|
|
{
|
|
int z;
|
|
|
|
z = Cc_lines[Selected_line].cc_index;
|
|
Assert(z & JOY_AXIS);
|
|
z &= ~JOY_AXIS;
|
|
control_config_save_axis_undo(z);
|
|
Invert_axis[z] = !Invert_axis[z];
|
|
}
|
|
|
|
void control_config_do_bind()
|
|
{
|
|
int i;
|
|
|
|
game_flush();
|
|
// if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
|
|
if (Selected_line < 0) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return;
|
|
}
|
|
|
|
for (i=0; i<NUM_BUTTONS; i++) {
|
|
if (i != CANCEL_BUTTON) {
|
|
CC_Buttons[gr_screen.res][i].button.reset_status();
|
|
CC_Buttons[gr_screen.res][i].button.disable();
|
|
}
|
|
}
|
|
CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
|
|
CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
|
|
|
|
for (i=0; i<JOY_TOTAL_BUTTONS; i++){
|
|
joy_down_count(i, 1); // clear checking status of all joystick buttons
|
|
}
|
|
|
|
control_config_detect_axis_reset();
|
|
|
|
Binding_mode = 1;
|
|
Bind_time = timer_get_milliseconds();
|
|
Search_mode = 0;
|
|
Last_key = -1;
|
|
Axis_override = -1;
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
}
|
|
|
|
void control_config_do_search()
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<NUM_BUTTONS; i++){
|
|
if (i != CANCEL_BUTTON) {
|
|
CC_Buttons[gr_screen.res][i].button.reset_status();
|
|
CC_Buttons[gr_screen.res][i].button.disable();
|
|
}
|
|
}
|
|
|
|
CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
|
|
CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
|
|
|
|
for (i=0; i<JOY_TOTAL_BUTTONS; i++){
|
|
joy_down_count(i, 1); // clear checking status of all joystick buttons
|
|
}
|
|
|
|
Binding_mode = 0;
|
|
Search_mode = 1;
|
|
Last_key = -1;
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
}
|
|
|
|
void control_config_do_cancel(int fail = 0)
|
|
{
|
|
int i;
|
|
|
|
game_flush();
|
|
|
|
for (i=0; i<NUM_BUTTONS; i++){
|
|
if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
|
|
CC_Buttons[gr_screen.res][i].button.enable();
|
|
}
|
|
}
|
|
|
|
CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.reset_status();
|
|
CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
|
|
CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
|
|
CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
|
|
CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
|
|
|
|
Binding_mode = Search_mode = 0;
|
|
if (fail){
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
} else {
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
}
|
|
}
|
|
|
|
int control_config_accept()
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<NUM_TABS; i++) {
|
|
if (Conflicts_tabs[i]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < NUM_TABS) {
|
|
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
|
return -1;
|
|
}
|
|
|
|
hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
|
|
gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
|
|
gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED);
|
|
return 0;
|
|
}
|
|
|
|
void control_config_cancel_exit()
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<CCFG_MAX; i++) {
|
|
Control_config[i] = Control_config_backup[i];
|
|
}
|
|
|
|
gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
|
|
}
|
|
|
|
void control_config_button_pressed(int n)
|
|
{
|
|
switch (n) {
|
|
case TARGET_TAB:
|
|
case SHIP_TAB:
|
|
case WEAPON_TAB:
|
|
case COMPUTER_TAB:
|
|
Tab = n;
|
|
Scroll_offset = Selected_line = 0;
|
|
control_config_list_prepare();
|
|
gamesnd_play_iface(InterfaceSounds::SCREEN_MODE_PRESSED);
|
|
break;
|
|
|
|
case BIND_BUTTON:
|
|
control_config_do_bind();
|
|
break;
|
|
|
|
case SEARCH_MODE:
|
|
control_config_do_search();
|
|
break;
|
|
|
|
case SHIFT_TOGGLE:
|
|
control_config_toggle_modifier(KEY_SHIFTED);
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
break;
|
|
|
|
case ALT_TOGGLE:
|
|
control_config_toggle_modifier(KEY_ALTED);
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
break;
|
|
|
|
case INVERT_AXIS:
|
|
control_config_toggle_invert();
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
break;
|
|
|
|
case SCROLL_UP_BUTTON:
|
|
control_config_scroll_screen_up();
|
|
break;
|
|
|
|
case SCROLL_DOWN_BUTTON:
|
|
control_config_scroll_screen_down();
|
|
break;
|
|
|
|
case ACCEPT_BUTTON:
|
|
control_config_accept();
|
|
break;
|
|
|
|
case CLEAR_BUTTON:
|
|
control_config_remove_binding();
|
|
break;
|
|
|
|
case HELP_BUTTON:
|
|
launch_context_help();
|
|
gamesnd_play_iface(InterfaceSounds::HELP_PRESSED);
|
|
break;
|
|
|
|
case RESET_BUTTON:
|
|
control_config_do_reset();
|
|
break;
|
|
|
|
case UNDO_BUTTON:
|
|
control_config_undo_last();
|
|
break;
|
|
|
|
case CANCEL_BUTTON:
|
|
control_config_do_cancel();
|
|
break;
|
|
|
|
case CLEAR_OTHER_BUTTON:
|
|
control_config_clear_other();
|
|
break;
|
|
|
|
case CLEAR_ALL_BUTTON:
|
|
control_config_clear_all();
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char *control_config_tooltip_handler(const char *str)
|
|
{
|
|
int i;
|
|
|
|
if (!stricmp(str, NOX("@conflict"))) {
|
|
for (i=0; i<NUM_TABS; i++) {
|
|
if (Conflicts_tabs[i]) {
|
|
return XSTR( "Conflict!", 205);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void control_config_init()
|
|
{
|
|
int i;
|
|
ui_button_info *b;
|
|
|
|
// make backup of all controls
|
|
for (i=0; i<CCFG_MAX; i++) {
|
|
Control_config_backup[i] = Control_config[i];
|
|
}
|
|
|
|
Defaults_cycle_pos = 0;
|
|
|
|
common_set_interface_palette(NOX("ControlConfigPalette")); // set the interface palette
|
|
Ui_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0);
|
|
Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
|
|
Ui_window.tooltip_handler = control_config_tooltip_handler;
|
|
|
|
// load in help overlay bitmap
|
|
Control_config_overlay_id = help_overlay_get_index(CONTROL_CONFIG_OVERLAY);
|
|
help_overlay_set_state(Control_config_overlay_id,gr_screen.res,0);
|
|
|
|
// reset conflict flashing
|
|
Conflict_stamp = -1;
|
|
|
|
for (i=0; i<NUM_BUTTONS; i++) {
|
|
b = &CC_Buttons[gr_screen.res][i];
|
|
|
|
if (b->hotspot < 0) { // temporary
|
|
b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1); // temporary
|
|
b->button.set_highlight_action(common_play_highlight_sound);
|
|
continue;
|
|
}
|
|
|
|
b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
|
|
|
|
// set up callback for when a mouse first goes over a button
|
|
b->button.set_highlight_action(common_play_highlight_sound);
|
|
if (i<4) {
|
|
b->button.set_bmaps(b->filename, 5, 1); // a bit of a hack here, but buttons 0-3 need 4 frames loaded
|
|
} else {
|
|
b->button.set_bmaps(b->filename);
|
|
}
|
|
b->button.link_hotspot(b->hotspot);
|
|
}
|
|
|
|
// create all text
|
|
for(i=0; i<CC_NUM_TEXT; i++){
|
|
Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
|
|
}
|
|
|
|
for (i=0; i<LIST_BUTTONS_MAX; i++) {
|
|
List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
|
|
List_buttons[i].hide();
|
|
List_buttons[i].disable();
|
|
}
|
|
|
|
// set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
|
|
CC_Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
|
|
CC_Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
|
|
CC_Buttons[gr_screen.res][BIND_BUTTON].button.set_hotkey(KEY_ENTER);
|
|
CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_DELETE);
|
|
CC_Buttons[gr_screen.res][UNDO_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_Z);
|
|
CC_Buttons[gr_screen.res][CLEAR_BUTTON].button.set_hotkey(KEY_DELETE);
|
|
CC_Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
|
|
CC_Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(KEY_F1);
|
|
CC_Buttons[gr_screen.res][RESET_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_R);
|
|
CC_Buttons[gr_screen.res][INVERT_AXIS].button.set_hotkey(KEY_I);
|
|
|
|
CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
|
|
CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
|
|
|
|
Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);
|
|
|
|
Scroll_offset = Selected_line = 0;
|
|
Config_item_undo = NULL;
|
|
control_config_conflict_check();
|
|
|
|
// setup strings
|
|
Joy_axis_action_text[0] = vm_strdup(XSTR("Turn (Yaw) Axis", 1016));
|
|
Joy_axis_action_text[1] = vm_strdup(XSTR("Pitch Axis", 1017));
|
|
Joy_axis_action_text[2] = vm_strdup(XSTR("Bank Axis", 1018));
|
|
Joy_axis_action_text[3] = vm_strdup(XSTR("Absolute Throttle Axis", 1019));
|
|
Joy_axis_action_text[4] = vm_strdup(XSTR("Relative Throttle Axis", 1020));
|
|
Joy_axis_text[0] = vm_strdup(XSTR("Joystick/Mouse X Axis", 1021));
|
|
Joy_axis_text[1] = vm_strdup(XSTR("Joystick/Mouse Y Axis", 1022));
|
|
Joy_axis_text[2] = vm_strdup(XSTR("Joystick Z Axis", 1023));
|
|
Joy_axis_text[3] = vm_strdup(XSTR("Joystick rX Axis", 1024));
|
|
Joy_axis_text[4] = vm_strdup(XSTR("Joystick rY Axis", 1025));
|
|
Joy_axis_text[5] = vm_strdup(XSTR("Joystick rZ Axis", 1026));
|
|
Mouse_button_text[0] = vm_strdup("");
|
|
Mouse_button_text[1] = vm_strdup(XSTR("Left Button", 1027));
|
|
Mouse_button_text[2] = vm_strdup(XSTR("Right Button", 1028));
|
|
Mouse_button_text[3] = vm_strdup(XSTR("Mid Button", 1029));
|
|
Mouse_button_text[4] = vm_strdup("");
|
|
Mouse_axis_text[0] = vm_strdup(XSTR("L/R", 1030));
|
|
Mouse_axis_text[1] = vm_strdup(XSTR("U/B", 1031));
|
|
Invert_text[0] = vm_strdup(XSTR("N", 1032));
|
|
Invert_text[1] = vm_strdup(XSTR("Y", 1033));
|
|
|
|
control_config_list_prepare();
|
|
}
|
|
|
|
void control_config_close()
|
|
{
|
|
int idx;
|
|
|
|
while (Config_item_undo){
|
|
free_undo_block();
|
|
}
|
|
|
|
if (Background_bitmap){
|
|
bm_release(Background_bitmap);
|
|
}
|
|
|
|
Ui_window.destroy();
|
|
common_free_interface_palette(); // restore game palette
|
|
hud_squadmsg_save_keys(); // rebuild map for saving/restoring keys in squadmsg mode
|
|
game_flush();
|
|
|
|
if (Game_mode & GM_MULTIPLAYER) {
|
|
Pilot.save_player();
|
|
} else {
|
|
Pilot.save_savefile();
|
|
}
|
|
|
|
// free strings
|
|
for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
|
|
if(Joy_axis_action_text[idx] != NULL){
|
|
vm_free(Joy_axis_action_text[idx]);
|
|
Joy_axis_action_text[idx] = NULL;
|
|
}
|
|
}
|
|
for(idx=0; idx<NUM_AXIS_TEXT; idx++){
|
|
if(Joy_axis_text[idx] != NULL){
|
|
vm_free(Joy_axis_text[idx]);
|
|
Joy_axis_text[idx] = NULL;
|
|
}
|
|
}
|
|
for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
|
|
if(Mouse_button_text[idx] != NULL){
|
|
vm_free(Mouse_button_text[idx]);
|
|
Mouse_button_text[idx] = NULL;
|
|
}
|
|
}
|
|
for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
|
|
if(Mouse_axis_text[idx] != NULL){
|
|
vm_free(Mouse_axis_text[idx]);
|
|
Mouse_axis_text[idx] = NULL;
|
|
}
|
|
}
|
|
for(idx=0; idx<NUM_INVERT_TEXT; idx++){
|
|
if(Invert_text[idx] != NULL){
|
|
vm_free(Invert_text[idx]);
|
|
Invert_text[idx] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void control_config_do_frame(float frametime)
|
|
{
|
|
const char *str;
|
|
char buf[256];
|
|
int i, j, k, w, x, y, z, line, conflict;
|
|
int font_height = gr_get_font_height();
|
|
int select_tease_line = -1; // line mouse is down on, but won't be selected until button released
|
|
static float timer = 0.0f;
|
|
color *c;
|
|
static int bound_timestamp = 0;
|
|
static char bound_string[40];
|
|
|
|
timer += frametime;
|
|
|
|
if (Binding_mode) {
|
|
if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
|
|
int bind = 0;
|
|
|
|
z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
|
|
i = control_config_detect_axis();
|
|
if (i >= 0) {
|
|
Axis_override = i;
|
|
bind = 1;
|
|
}
|
|
|
|
k = game_poll();
|
|
Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
|
|
Ui_window.process(0);
|
|
|
|
if (k == KEY_ESC) {
|
|
strcpy_s(bound_string, XSTR( "Canceled", 206));
|
|
bound_timestamp = timestamp(2500);
|
|
control_config_do_cancel();
|
|
|
|
} else {
|
|
if (k == KEY_ENTER) {
|
|
bind = 1;
|
|
}
|
|
|
|
for (i=0; i<JOY_TOTAL_BUTTONS; i++) {
|
|
if (joy_down_count(i, 1)) {
|
|
bind = 1;
|
|
}
|
|
}
|
|
|
|
if (bind) {
|
|
if (Axis_override >= 0) {
|
|
control_config_bind_axis(z, Axis_override);
|
|
strcpy_s(bound_string, Joy_axis_text[Axis_override]);
|
|
font::force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
|
|
bound_timestamp = timestamp(2500);
|
|
control_config_conflict_check();
|
|
control_config_list_prepare();
|
|
control_config_do_cancel();
|
|
|
|
} else {
|
|
control_config_do_cancel(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
if (help_overlay_active(Control_config_overlay_id)) {
|
|
CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
|
|
Ui_window.set_ignore_gadgets(1);
|
|
}
|
|
|
|
k = game_poll();
|
|
Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
|
|
Ui_window.process(0);
|
|
|
|
if ( (k > 0) || B1_JUST_RELEASED ) {
|
|
if (help_overlay_active(Control_config_overlay_id)) {
|
|
help_overlay_set_state(Control_config_overlay_id, gr_screen.res, 0);
|
|
Ui_window.set_ignore_gadgets(0);
|
|
k = 0;
|
|
}
|
|
}
|
|
|
|
if ( !help_overlay_active(Control_config_overlay_id) ) {
|
|
Ui_window.set_ignore_gadgets(0);
|
|
}
|
|
|
|
if (k == KEY_ESC) {
|
|
strcpy_s(bound_string, XSTR( "Canceled", 206));
|
|
bound_timestamp = timestamp(2500);
|
|
control_config_do_cancel();
|
|
|
|
} else {
|
|
switch (k & KEY_MASK) {
|
|
case KEY_LSHIFT:
|
|
case KEY_RSHIFT:
|
|
case KEY_LALT:
|
|
case KEY_RALT:
|
|
Last_key = k & KEY_MASK;
|
|
k = 0;
|
|
break;
|
|
}
|
|
|
|
if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED || Cc_lines[Selected_line].cc_index == GLIDE_WHEN_PRESSED) {
|
|
if ( (Last_key >= 0) && (k <= 0) && !keyd_pressed[Last_key] ) {
|
|
k = Last_key;
|
|
}
|
|
}
|
|
|
|
if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
|
|
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "That is a non-bindable key. Please try again.", 207));
|
|
k = 0;
|
|
}
|
|
|
|
k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
|
|
if (k > 0) {
|
|
z = Cc_lines[Selected_line].cc_index;
|
|
Assert(!(z & JOY_AXIS));
|
|
control_config_bind_key(z, k);
|
|
|
|
strcpy_s(bound_string, textify_scancode(k));
|
|
font::force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
|
|
bound_timestamp = timestamp(2500);
|
|
control_config_conflict_check();
|
|
control_config_list_prepare();
|
|
control_config_do_cancel();
|
|
}
|
|
|
|
for (i=0; i<JOY_TOTAL_BUTTONS; i++) {
|
|
if (joy_down_count(i, 1)) {
|
|
z = Cc_lines[Selected_line].cc_index;
|
|
Assert(!(z & JOY_AXIS));
|
|
control_config_bind_joy(z, i);
|
|
|
|
strcpy_s(bound_string, Joy_button_text[i]);
|
|
font::force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
|
|
bound_timestamp = timestamp(2500);
|
|
control_config_conflict_check();
|
|
control_config_list_prepare();
|
|
control_config_do_cancel();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Bind_time + 375 < timer_get_milliseconds()) {
|
|
for (i=0; i<NUM_BUTTONS; i++){
|
|
if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == NUM_BUTTONS) { // no buttons pressed
|
|
for (i=0; i<MOUSE_NUM_BUTTONS; i++) {
|
|
if (mouse_down(1 << i)) {
|
|
z = Cc_lines[Selected_line].cc_index;
|
|
Assert(!(z & JOY_AXIS));
|
|
control_config_bind_joy(z, i);
|
|
|
|
strcpy_s(bound_string, Joy_button_text[i]);
|
|
font::force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
|
|
bound_timestamp = timestamp(2500);
|
|
control_config_conflict_check();
|
|
control_config_list_prepare();
|
|
control_config_do_cancel();
|
|
for (j=0; j<NUM_BUTTONS; j++){
|
|
CC_Buttons[gr_screen.res][j].button.reset();
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (Search_mode) {
|
|
if (help_overlay_active(Control_config_overlay_id)) {
|
|
CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
|
|
Ui_window.set_ignore_gadgets(1);
|
|
}
|
|
|
|
k = game_poll();
|
|
Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
|
|
Ui_window.process(0);
|
|
|
|
if ( (k > 0) || B1_JUST_RELEASED ) {
|
|
if ( help_overlay_active(Control_config_overlay_id) ) {
|
|
help_overlay_set_state(Control_config_overlay_id, gr_screen.res, 0);
|
|
Ui_window.set_ignore_gadgets(0);
|
|
k = 0;
|
|
}
|
|
}
|
|
|
|
if ( !help_overlay_active(Control_config_overlay_id) ) {
|
|
Ui_window.set_ignore_gadgets(0);
|
|
}
|
|
|
|
if (k == KEY_ESC) {
|
|
control_config_do_cancel();
|
|
|
|
} else {
|
|
if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
|
|
k = 0;
|
|
}
|
|
|
|
k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
|
|
z = -1;
|
|
if (k > 0) {
|
|
for (i=0; i<CCFG_MAX; i++) {
|
|
if (Control_config[i].key_id == k) {
|
|
z = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i=0; i<JOY_TOTAL_BUTTONS; i++) {
|
|
if (joy_down_count(i, 1)) {
|
|
j = i;
|
|
for (i=0; i<CCFG_MAX; i++) { //-V535
|
|
if (Control_config[i].joy_id == j) {
|
|
z = i;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check if not on enabled button
|
|
for (j=0; j<NUM_BUTTONS; j++){
|
|
if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j == NUM_BUTTONS) { // no buttons pressed
|
|
for (j=0; j<MOUSE_NUM_BUTTONS; j++) {
|
|
if (mouse_down(1 << j)) {
|
|
for (i=0; i<CCFG_MAX; i++) {
|
|
if (Control_config[i].joy_id == j) {
|
|
z = i;
|
|
for (size_t buttonid=0; buttonid<NUM_BUTTONS; buttonid++){
|
|
CC_Buttons[gr_screen.res][buttonid].button.reset();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (z >= 0) {
|
|
Tab = Control_config[z].tab;
|
|
control_config_list_prepare();
|
|
Selected_line = Scroll_offset = 0;
|
|
for (i=0; i<Num_cc_lines; i++) {
|
|
if (Cc_lines[i].cc_index == z) {
|
|
Selected_line = i;
|
|
break;
|
|
}
|
|
}
|
|
while (!cc_line_query_visible(Selected_line)) {
|
|
Scroll_offset++;
|
|
Assert(Scroll_offset < Num_cc_lines);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
|
|
CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
|
|
CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
|
|
CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
|
|
|
|
if (!z) {
|
|
z = Cc_lines[Selected_line].cc_index;
|
|
k = Control_config[z].key_id;
|
|
if ( (k == KEY_LALT) || (k == KEY_RALT) || (k == KEY_LSHIFT) || (k == KEY_RSHIFT) ) {
|
|
CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
|
|
CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
|
|
}
|
|
}
|
|
|
|
CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
|
|
|
|
if ( help_overlay_active(Control_config_overlay_id) ) {
|
|
CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
|
|
Ui_window.set_ignore_gadgets(1);
|
|
}
|
|
|
|
k = Ui_window.process();
|
|
|
|
if ( (k > 0) || B1_JUST_RELEASED ) {
|
|
if ( help_overlay_active(Control_config_overlay_id) ) {
|
|
help_overlay_set_state(Control_config_overlay_id, gr_screen.res, 0);
|
|
Ui_window.set_ignore_gadgets(0);
|
|
k = 0;
|
|
}
|
|
}
|
|
|
|
if ( !help_overlay_active(Control_config_overlay_id) ) {
|
|
Ui_window.set_ignore_gadgets(0);
|
|
}
|
|
|
|
switch (k) {
|
|
case KEY_DOWN: // select next line
|
|
control_config_scroll_line_down();
|
|
break;
|
|
|
|
case KEY_UP: // select previous line
|
|
control_config_scroll_line_up();
|
|
break;
|
|
|
|
case KEY_SHIFTED | KEY_TAB: // activate previous tab
|
|
Tab--;
|
|
if (Tab < 0) {
|
|
Tab = NUM_TABS - 1;
|
|
}
|
|
|
|
Scroll_offset = Selected_line = 0;
|
|
control_config_list_prepare();
|
|
gamesnd_play_iface(InterfaceSounds::SCREEN_MODE_PRESSED);
|
|
break;
|
|
|
|
case KEY_TAB: // activate next tab
|
|
Tab++;
|
|
if (Tab >= NUM_TABS) {
|
|
Tab = 0;
|
|
}
|
|
|
|
Scroll_offset = Selected_line = 0;
|
|
control_config_list_prepare();
|
|
gamesnd_play_iface(InterfaceSounds::SCREEN_MODE_PRESSED);
|
|
break;
|
|
|
|
case KEY_LEFT:
|
|
Selected_item--;
|
|
if (Selected_item == -2) {
|
|
Selected_item = 1;
|
|
if (Cc_lines[Selected_line].jw < 1) {
|
|
Selected_item = 0;
|
|
if (Cc_lines[Selected_line].kw < 1) {
|
|
Selected_item = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
gamesnd_play_iface(InterfaceSounds::SCROLL);
|
|
break;
|
|
|
|
case KEY_RIGHT:
|
|
Selected_item++;
|
|
if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1)) {
|
|
Selected_item = -1;
|
|
} else if (!Selected_item && (Cc_lines[Selected_line].kw < 1)) {
|
|
Selected_item = -1;
|
|
} else if (Selected_item > 1) {
|
|
Selected_item = -1;
|
|
}
|
|
gamesnd_play_iface(InterfaceSounds::SCROLL);
|
|
break;
|
|
|
|
case KEY_BACKSP: // undo
|
|
control_config_undo_last();
|
|
break;
|
|
|
|
case KEY_ESC:
|
|
control_config_cancel_exit();
|
|
break;
|
|
} // end switch
|
|
}
|
|
|
|
for (i=0; i<NUM_BUTTONS; i++){
|
|
if (CC_Buttons[gr_screen.res][i].button.pressed()){
|
|
control_config_button_pressed(i);
|
|
}
|
|
}
|
|
|
|
for (i=0; i<LIST_BUTTONS_MAX; i++) {
|
|
if (List_buttons[i].is_mouse_on()) {
|
|
select_tease_line = i + Scroll_offset;
|
|
}
|
|
|
|
if (List_buttons[i].pressed()) {
|
|
Selected_line = i + Scroll_offset;
|
|
Selected_item = -1;
|
|
List_buttons[i].get_mouse_pos(&x, &y);
|
|
if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw)) {
|
|
Selected_item = 0;
|
|
}
|
|
|
|
if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw)) {
|
|
Selected_item = 1;
|
|
}
|
|
|
|
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
|
}
|
|
|
|
if (List_buttons[i].double_clicked()) {
|
|
control_config_do_bind();
|
|
}
|
|
}
|
|
|
|
GR_MAYBE_CLEAR_RES(Background_bitmap);
|
|
if (Background_bitmap >= 0) {
|
|
gr_set_bitmap(Background_bitmap);
|
|
gr_bitmap(0, 0, GR_RESIZE_MENU);
|
|
}
|
|
|
|
// highlight tab with conflict
|
|
Ui_window.draw();
|
|
for (i=z=0; i<NUM_TABS; i++) {
|
|
if (Conflicts_tabs[i]) {
|
|
CC_Buttons[gr_screen.res][i].button.draw_forced(4);
|
|
z++;
|
|
}
|
|
}
|
|
|
|
if (z) {
|
|
// maybe switch from bright to normal
|
|
if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
|
|
Conflict_bright = !Conflict_bright;
|
|
|
|
Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
|
|
}
|
|
|
|
// set color and font
|
|
font::set_font(font::FONT2);
|
|
if(Conflict_bright){
|
|
gr_set_color_fast(&Color_bright_red);
|
|
} else {
|
|
gr_set_color_fast(&Color_red);
|
|
}
|
|
|
|
// setup the conflict string
|
|
char conflict_str[512] = "";
|
|
strncpy(conflict_str, XSTR("Conflict!", 205), 511);
|
|
int sw, sh;
|
|
gr_get_string_size(&sw, &sh, conflict_str);
|
|
|
|
gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str, GR_RESIZE_MENU);
|
|
|
|
font::set_font(font::FONT1);
|
|
} else {
|
|
// might as well always reset the conflict stamp
|
|
Conflict_stamp = -1;
|
|
}
|
|
|
|
for (i=0; i<NUM_TABS; i++) {
|
|
if (CC_Buttons[gr_screen.res][i].button.button_down()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == NUM_TABS) {
|
|
CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
|
|
}
|
|
|
|
if (Search_mode) {
|
|
CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
|
|
}
|
|
|
|
if (Selected_line >= 0) {
|
|
z = Cc_lines[Selected_line].cc_index;
|
|
if (z & JOY_AXIS) {
|
|
if (Invert_axis[z & ~JOY_AXIS]) {
|
|
CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
|
|
}
|
|
|
|
} else {
|
|
z = Control_config[z].key_id;
|
|
if (z >= 0) {
|
|
if (z & KEY_SHIFTED) {
|
|
CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
|
|
}
|
|
if (z & KEY_ALTED) {
|
|
CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Binding_mode) {
|
|
CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
|
|
}
|
|
|
|
z = Cc_lines[Selected_line].cc_index;
|
|
x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2;
|
|
y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2;
|
|
if (Binding_mode) {
|
|
int t;
|
|
|
|
t = (int) (timer * 3);
|
|
if (t % 2) {
|
|
gr_set_color_fast(&Color_text_normal);
|
|
gr_get_string_size(&w, NULL, XSTR( "?", 208));
|
|
gr_printf_menu(x - w / 2, y - font_height / 2, "%s", XSTR( "?", 208));
|
|
}
|
|
|
|
} else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
|
|
i = Conflicts[z].key;
|
|
if (i < 0) {
|
|
i = Conflicts[z].joy;
|
|
}
|
|
|
|
gr_set_color_fast(&Color_text_normal);
|
|
str = XSTR( "Control conflicts with:", 209);
|
|
gr_get_string_size(&w, NULL, str);
|
|
gr_printf_menu(x - w / 2, y - font_height, "%s", str);
|
|
|
|
if (Control_config[i].hasXSTR > 1) {
|
|
strcpy_s(buf, XSTR(Control_config[i].text, Control_config[i].hasXSTR));
|
|
} else if (Control_config[i].hasXSTR == 1) {
|
|
strcpy_s(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
|
|
} else {
|
|
strcpy_s(buf, Control_config[i].text);
|
|
}
|
|
|
|
font::force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
|
|
gr_get_string_size(&w, NULL, buf);
|
|
gr_printf_menu(x - w / 2, y, "%s", buf);
|
|
|
|
} else if (*bound_string) {
|
|
gr_set_color_fast(&Color_text_normal);
|
|
gr_get_string_size(&w, NULL, bound_string);
|
|
gr_printf_menu(x - w / 2, y - font_height / 2, "%s", bound_string);
|
|
if (timestamp_elapsed(bound_timestamp)) {
|
|
*bound_string = 0;
|
|
}
|
|
}
|
|
|
|
if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
|
|
gr_set_color_fast(&Color_white);
|
|
gr_printf_menu(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], "%s", XSTR( "More...", 210));
|
|
}
|
|
|
|
conflict = 0;
|
|
line = Scroll_offset;
|
|
while (cc_line_query_visible(line)) {
|
|
z = Cc_lines[line].cc_index;
|
|
y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
|
|
|
|
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);
|
|
List_buttons[line - Scroll_offset].enable(!Binding_mode);
|
|
|
|
Cc_lines[line].kw = Cc_lines[line].jw = 0;
|
|
|
|
if (line == Selected_line){
|
|
c = &Color_text_selected;
|
|
} else if (line == select_tease_line) {
|
|
c = &Color_text_subselected;
|
|
} else {
|
|
c = &Color_text_normal;
|
|
}
|
|
|
|
gr_set_color_fast(c);
|
|
if (Cc_lines[line].label) {
|
|
strcpy_s(buf, Cc_lines[line].label);
|
|
font::force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
|
|
gr_printf_menu(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, "%s", buf);
|
|
}
|
|
|
|
if (!(z & JOY_AXIS)) {
|
|
k = Control_config[z].key_id;
|
|
j = Control_config[z].joy_id;
|
|
x = Control_list_key_x[gr_screen.res];
|
|
*buf = 0;
|
|
|
|
if ((k < 0) && (j < 0)) {
|
|
gr_set_color_fast(&Color_grey);
|
|
gr_printf_menu(x, y, "%s", XSTR( "None", 211));
|
|
|
|
} else {
|
|
if (k >= 0) {
|
|
strcpy_s(buf, textify_scancode(k));
|
|
if (Conflicts[z].key >= 0) {
|
|
if (c == &Color_text_normal)
|
|
gr_set_color_fast(&Color_text_error);
|
|
else {
|
|
gr_set_color_fast(&Color_text_error_hi);
|
|
conflict++;
|
|
}
|
|
|
|
} else if (Selected_item == 1) {
|
|
gr_set_color_fast(&Color_text_normal);
|
|
|
|
} else {
|
|
gr_set_color_fast(c);
|
|
}
|
|
|
|
gr_printf_menu(x, y, "%s", buf);
|
|
|
|
Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
|
|
gr_get_string_size(&w, NULL, buf);
|
|
Cc_lines[line].kw = w;
|
|
x += w;
|
|
|
|
if (j >= 0) {
|
|
gr_set_color_fast(&Color_text_normal);
|
|
gr_printf_menu(x, y, "%s", XSTR( ", ", 212));
|
|
gr_get_string_size(&w, NULL, XSTR( ", ", 212));
|
|
x += w;
|
|
}
|
|
}
|
|
|
|
if (j >= 0) {
|
|
strcpy_s(buf, Joy_button_text[j]);
|
|
if (Conflicts[z].joy >= 0) {
|
|
if (c == &Color_text_normal) {
|
|
gr_set_color_fast(&Color_text_error);
|
|
} else {
|
|
gr_set_color_fast(&Color_text_error_hi);
|
|
conflict++;
|
|
}
|
|
|
|
} else if (!Selected_item) {
|
|
gr_set_color_fast(&Color_text_normal);
|
|
|
|
} else {
|
|
gr_set_color_fast(c);
|
|
}
|
|
|
|
font::force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x);
|
|
gr_printf_menu(x, y, "%s", buf);
|
|
|
|
Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
|
|
gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
x = Control_list_key_x[gr_screen.res];
|
|
j = Axis_map_to[z & ~JOY_AXIS];
|
|
if (Binding_mode && (line == Selected_line)) {
|
|
j = Axis_override;
|
|
}
|
|
|
|
if (j < 0) {
|
|
gr_set_color_fast(&Color_grey);
|
|
gr_printf_menu(x, y, "%s", XSTR( "None", 211));
|
|
|
|
} else {
|
|
if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
|
|
if (c == &Color_text_normal) {
|
|
gr_set_color_fast(&Color_text_error);
|
|
|
|
} else {
|
|
gr_set_color_fast(&Color_text_error_hi);
|
|
conflict++;
|
|
}
|
|
|
|
} else if (!Selected_item) {
|
|
gr_set_color_fast(&Color_text_normal);
|
|
|
|
} else {
|
|
gr_set_color_fast(c);
|
|
}
|
|
|
|
gr_string(x, y, Joy_axis_text[j], GR_RESIZE_MENU);
|
|
}
|
|
}
|
|
|
|
line++;
|
|
}
|
|
|
|
CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
|
|
|
|
i = line - Scroll_offset;
|
|
while (i < LIST_BUTTONS_MAX) {
|
|
List_buttons[i++].disable();
|
|
}
|
|
|
|
// If multiple controls presets are provided, display which one is in use
|
|
if (Control_config_presets.size() > 1) {
|
|
SCP_string preset_str;
|
|
int matching_preset = -1;
|
|
|
|
for (i=0; i<(int)Control_config_presets.size(); i++) {
|
|
bool this_preset_matches = true;
|
|
config_item *this_preset = Control_config_presets[i];
|
|
|
|
for (j=0; j<CCFG_MAX; j++) {
|
|
if (!Control_config[j].disabled && Control_config[j].key_id != this_preset[j].key_default) {
|
|
this_preset_matches = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (this_preset_matches) {
|
|
matching_preset = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (matching_preset >= 0) {
|
|
sprintf(preset_str, "Controls: %s", Control_config_preset_names[matching_preset].c_str());
|
|
} else {
|
|
sprintf(preset_str, "Controls: custom");
|
|
|
|
}
|
|
|
|
gr_get_string_size(&w, NULL, preset_str.c_str());
|
|
gr_set_color_fast(&Color_text_normal);
|
|
|
|
if (gr_screen.res == GR_640) {
|
|
gr_string(16, (24 - font_height) / 2, preset_str.c_str(), GR_RESIZE_MENU);
|
|
} else {
|
|
gr_string(24, (40 - font_height) / 2, preset_str.c_str(), GR_RESIZE_MENU);
|
|
}
|
|
}
|
|
|
|
// blit help overlay if active
|
|
help_overlay_maybe_blit(Control_config_overlay_id, gr_screen.res);
|
|
|
|
gr_flip();
|
|
}
|
|
|
|
float check_control_timef(int id)
|
|
{
|
|
float t1, t2;
|
|
|
|
// if type isn't continuous, we shouldn't be using this function, cause it won't work.
|
|
Assert(Control_config[id].type == CC_TYPE_CONTINUOUS);
|
|
|
|
// first, see if control actually used (makes sure modifiers match as well)
|
|
if (!check_control(id)) {
|
|
Control_config[id].continuous_ongoing = false;
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
t1 = key_down_timef(Control_config[id].key_id);
|
|
if (t1) {
|
|
control_used(id);
|
|
}
|
|
|
|
t2 = joy_down_time(Control_config[id].joy_id);
|
|
if (t2) {
|
|
control_used(id);
|
|
}
|
|
|
|
if (t1 + t2) {
|
|
// We want to set this to true only after visiting control_used() (above)
|
|
// to allow it to tell the difference between an ongoing continuous action
|
|
// started before and a continuous action being started right now.
|
|
Control_config[id].continuous_ongoing = true;
|
|
|
|
return t1 + t2;
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
void control_check_indicate()
|
|
{
|
|
#ifndef NDEBUG
|
|
if (Show_controls_info) {
|
|
gr_set_color_fast(&HUD_color_debug);
|
|
gr_printf_no_resize(gr_screen.center_offset_x + gr_screen.center_w - 154, gr_screen.center_offset_y + 5, NOX("Ctrls checked: %d"), Control_check_count);
|
|
}
|
|
#endif
|
|
|
|
Control_check_count = 0;
|
|
}
|
|
|
|
int check_control_used(int id, int key)
|
|
{
|
|
int z, mask;
|
|
static int last_key = 0;
|
|
|
|
Control_check_count++;
|
|
if (key < 0) {
|
|
key = last_key;
|
|
}
|
|
|
|
last_key = key;
|
|
|
|
// if we're in multiplayer text enter (for chat) mode, check to see if we should ignore controls
|
|
if ((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls()){
|
|
return 0;
|
|
}
|
|
|
|
if (Control_config[id].disabled)
|
|
return 0;
|
|
|
|
if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
|
|
if (joy_down(Control_config[id].joy_id) || joy_down_count(Control_config[id].joy_id, 1)) {
|
|
control_used(id);
|
|
return 1;
|
|
}
|
|
|
|
if ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS)) {
|
|
if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
|
|
control_used(id);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// check what current modifiers are pressed
|
|
mask = 0;
|
|
if (keyd_pressed[KEY_LSHIFT] || key_down_count(KEY_LSHIFT) || keyd_pressed[KEY_RSHIFT] || key_down_count(KEY_RSHIFT)) {
|
|
mask |= KEY_SHIFTED;
|
|
}
|
|
|
|
if (keyd_pressed[KEY_LALT] || key_down_count(KEY_LALT) || keyd_pressed[KEY_RALT] || key_down_count(KEY_RALT)) {
|
|
mask |= KEY_ALTED;
|
|
}
|
|
|
|
z = Control_config[id].key_id;
|
|
if (z >= 0) {
|
|
if ( (z != KEY_LALT) && (z != KEY_RALT) && (z != KEY_LSHIFT) && (z != KEY_RSHIFT) ) {
|
|
// if current modifiers don't match action's modifiers, don't register control active.
|
|
if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
z &= KEY_MASK;
|
|
|
|
if (keyd_pressed[z] || key_down_count(z)) {
|
|
control_used(id);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id, 1) ||
|
|
((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS) && mouse_down_count(1 << Control_config[id].joy_id))) {
|
|
//mprintf(("Key used %d", key));
|
|
control_used(id);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Wrapper for check_control_used. Allows the game to ignore the key if told to do so by the ignore-key SEXP.
|
|
*/
|
|
int check_control(int id, int key)
|
|
{
|
|
if (check_control_used(id, key)) {
|
|
if (Ignored_keys[id]) {
|
|
if (Ignored_keys[id] > 0) {
|
|
Ignored_keys[id]--;
|
|
}
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (Control_config[id].continuous_ongoing) {
|
|
// If we reach this point, then it means this is a continuous control
|
|
// which has just been released
|
|
|
|
Script_system.SetHookVar("Action", 's', Control_config[id].text);
|
|
Script_system.RunCondition(CHA_ONACTIONSTOPPED, nullptr, id);
|
|
Script_system.RemHookVar("Action");
|
|
|
|
Control_config[id].continuous_ongoing = false;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// get heading, pitch, bank, throttle abs. and throttle rel. values.
|
|
void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
|
|
{
|
|
int axes_values[JOY_NUM_AXES];
|
|
|
|
joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
|
|
|
|
// joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
|
|
*h = 0;
|
|
if (Axis_map_to[0] >= 0) {
|
|
*h = joy_get_scaled_reading(axes_values[Axis_map_to[0]]);
|
|
}
|
|
|
|
*p = 0;
|
|
if (Axis_map_to[1] >= 0) {
|
|
*p = joy_get_scaled_reading(axes_values[Axis_map_to[1]]);
|
|
}
|
|
|
|
*b = 0;
|
|
if (Axis_map_to[2] >= 0) {
|
|
*b = joy_get_scaled_reading(axes_values[Axis_map_to[2]]);
|
|
}
|
|
|
|
*ta = 0;
|
|
if (Axis_map_to[3] >= 0) {
|
|
*ta = joy_get_unscaled_reading(axes_values[Axis_map_to[3]]);
|
|
}
|
|
|
|
*tr = 0;
|
|
if (Axis_map_to[4] >= 0) {
|
|
*tr = joy_get_scaled_reading(axes_values[Axis_map_to[4]]);
|
|
}
|
|
|
|
if (Invert_axis[0]) {
|
|
*h = -(*h);
|
|
}
|
|
if (Invert_axis[1]) {
|
|
*p = -(*p);
|
|
}
|
|
if (Invert_axis[2]) {
|
|
*b = -(*b);
|
|
}
|
|
if (Invert_axis[3]) {
|
|
*ta = F1_0 - *ta;
|
|
}
|
|
if (Invert_axis[4]) {
|
|
*tr = -(*tr);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int Last_frame_timestamp;
|
|
void control_used(int id)
|
|
{
|
|
// if we have set this key to be ignored, ignore it
|
|
if (Ignored_keys[id]) {
|
|
return;
|
|
}
|
|
|
|
// This check needs to be done because the control code might call this function more than once per frame,
|
|
// and we don't want to run the hooks more than once per frame
|
|
if (Control_config[id].used < Last_frame_timestamp) {
|
|
if (!Control_config[id].continuous_ongoing) {
|
|
Script_system.SetHookVar("Action", 's', Control_config[id].text);
|
|
Script_system.RunCondition(CHA_ONACTION, nullptr, id);
|
|
Script_system.RemHookVar("Action");
|
|
|
|
if (Control_config[id].type == CC_TYPE_CONTINUOUS)
|
|
Control_config[id].continuous_ongoing = true;
|
|
}
|
|
|
|
Control_config[id].used = timestamp();
|
|
}
|
|
}
|
|
|
|
void control_config_clear_used_status()
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<CCFG_MAX; i++) {
|
|
Control_config[i].used = 0;
|
|
}
|
|
}
|
|
|
|
void control_config_clear()
|
|
{
|
|
int i;
|
|
|
|
// Reset keyboard defaults
|
|
for (i=0; i<CCFG_MAX; i++) {
|
|
Control_config[i].key_id = Control_config[i].joy_id = -1;
|
|
}
|
|
} |