@@ -1,20 +1,20 @@ | |||
# Controls | |||
| Action | Effect | Remarks | | |||
|---------|-----------------------------------|--------------------------------------------------------------------------------------------------------| | |||
| Up | Select Menu Item Up/Look Up | | | |||
| Right | Select Menu Item Right/Move Right | | | |||
| Down | Select Menu Item Down/Crouch | | | |||
| Left | Select Menu Item Left/Move Left | | | |||
| Affirm | Confirm Selected Option | | | |||
| Negate | Go Back | | | |||
| Action0 | Jump | When crouched, player will go down a solid-on-top ground. | | |||
| Action1 | Primary Fire | Each weapon has firing characteristics, see below. | | |||
| Action2 | Secondary Fire | Each weapon has firing characteristics, see below. | | |||
| Action3 | Switch Weapon Mode | Press to select next weapon mode (cycle), hold then press Left/Right to set previous/next weapon mode. | | |||
| Action4 | Switch Weapon | Press to select next weapon (cycle), hold then press Left/Right to set previous/next weapon. | | |||
| Action5 | Item/Inventory | Hold to open inventory, double press to use current item. | | |||
| Action6 | Switch Item | Press to select next item (cycle), hold then press Left/Right to set previous/next item. | | |||
| Action7 | Take Cover | Enter doors, go between objects to hide from enemies. | | |||
| Action8 | Sneak | Hold then press Left/Right to sneak. (do we need this control, or crouch is enough?) | | |||
| Action9 | Reload | Reload always drops current clip regardless if there are rounds left. | | |||
| Action | XInput | PS4 | Effect | Remarks | | |||
|---------|---------:|---------:|-----------------------------------|--------------------------------------------------------------------------------------------------------| | |||
| Up | A1+, A3+ | A1+, A4+ | Select Menu Item Up/Look Up | | | |||
| Right | A0+, A2+ | A0+, A2+ | Select Menu Item Right/Move Right | | | |||
| Down | A1-, A3- | A1-, A4- | Select Menu Item Down/Crouch | | | |||
| Left | A0-, A2- | A0-, A2- | Select Menu Item Left/Move Left | | | |||
| Affirm | B11 | | Confirm Selected Option | | | |||
| Negate | B10 | | Go Back | | | |||
| Action0 | B1 | B2 | Jump | When crouched, player will go down a solid-on-top ground. | | |||
| Action1 | B0 | B1 | Primary Fire | Each weapon has firing characteristics, see below. | | |||
| Action2 | B4 | | Secondary Fire | Each weapon has firing characteristics, see below. | | |||
| Action3 | B3 | | Reload | Reload always drops current clip regardless if there are rounds left. | | |||
| Action4 | B6 | | Switch Weapon Mode | Press to select next weapon mode (cycle), hold then press Left/Right to set previous/next weapon mode. | | |||
| Action5 | B7 | | Switch Weapon | Press to select next weapon (cycle), hold then press Left/Right to set previous/next weapon. | | |||
| Action6 | B8 | | Item/Inventory | Hold to open inventory, double press to use current item. | | |||
| Action7 | B9 | | Switch Item | Press to select next item (cycle), hold then press Left/Right to set previous/next item. | | |||
| Action8 | B13 | | Take Cover | Enter doors, go between objects to hide from enemies. | | |||
| Action9 | B14 | | Sneak | Hold then press Left/Right to sneak. (do we need this control, or crouch is enough?) | |
@@ -5,6 +5,7 @@ MaxFps=30 | |||
[Joystick.0] | |||
DeviceID=0 | |||
AxisThreshold=8000 | |||
GUID=00000000-0000-0000-0000-000000000000 | |||
[Joystick.0.ControlMapping] | |||
Affirm=11 | |||
Negate=10 | |||
@@ -64,6 +65,7 @@ Username=Player 1 | |||
[Joystick.1] | |||
DeviceID=1 | |||
AxisThreshold=8000 | |||
GUID=00000000-0000-0000-0000-000000000000 | |||
[Joystick.1.ControlMapping] | |||
Affirm=11 | |||
Negate=10 | |||
@@ -1,3 +1,4 @@ | |||
#include <SDL_guid.h> | |||
#include "IZ_config.h" | |||
const char* IZ_ConfigGetCommandlineOption(u8 argc, const char* argv[], const char* val) { | |||
@@ -5,7 +6,6 @@ const char* IZ_ConfigGetCommandlineOption(u8 argc, const char* argv[], const cha | |||
int c = argc; | |||
while (--c > 0) { | |||
if (!strncmp(argv[c], val, n)) { | |||
if (!*(argv[c] + n) && c < argc - 1) { | |||
/* coverity treats unchecked argv as "tainted" */ | |||
@@ -164,6 +164,57 @@ void IZ_ConfigOverrideString(IZ_ConfigItem* item, u8 argc, const char* argv[]) { | |||
} | |||
} | |||
typedef bool IZ_ConfigLoadParamsGuidValidator(SDL_GUID); | |||
void IZ_ConfigEnsureValidGuid(IZ_ConfigItem* item, SDL_GUID raw_value, SDL_GUID default_value) { | |||
SDL_GUID* dest = item->dest; | |||
if (item->validator) { | |||
IZ_ConfigLoadParamsGuidValidator* validate = item->validator; | |||
if (validate(raw_value)) { | |||
// within valid values | |||
*dest = raw_value; | |||
return; | |||
} | |||
// outside valid values, we use default value | |||
// | |||
// TODO: what if the default value is also invalid? | |||
*dest = default_value; | |||
return; | |||
} | |||
// no validator, get whatever is the deserialized value | |||
*dest = raw_value; | |||
} | |||
typedef SDL_GUID IZ_ConfigDeserializeGUID(const char*); | |||
typedef void IZ_ConfigSerializeGUID(SDL_GUID, const char[128]); | |||
void IZ_ConfigLoadGuid(IZ_ConfigItem* item, const char* config_path) { | |||
static SDL_GUID raw_value; | |||
static SDL_GUID default_value = { | |||
.data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, | |||
}; | |||
if (item->default_value) { | |||
default_value = *((SDL_GUID *) item->default_value); | |||
} | |||
char buffer[128]; | |||
if (item->transformer.deserialize && item->transformer.serialize) { | |||
IZ_ConfigDeserializeGUID* deserialize = item->transformer.deserialize; | |||
IZ_ConfigSerializeGUID* serialize = item->transformer.serialize; | |||
const char serialized_default_value[128]; | |||
serialize(default_value, serialized_default_value); | |||
ini_gets(item->section, item->key, serialized_default_value, buffer, 128, config_path); | |||
raw_value = deserialize(buffer); | |||
} else { | |||
ini_gets(item->section, item->key, "00000000-0000-0000-0000-000000000000", buffer, 128, config_path); | |||
raw_value = SDL_GUIDFromString(buffer); | |||
} | |||
IZ_ConfigEnsureValidGuid(item, raw_value, default_value); | |||
} | |||
void IZ_ConfigLoad(IZ_ConfigItem item[], const char* config_path) { | |||
u8 i; | |||
for (i = 0; item[i].type != IZ_CONFIG_TYPE_VOID; i += 1) { | |||
@@ -179,12 +230,58 @@ void IZ_ConfigLoad(IZ_ConfigItem item[], const char* config_path) { | |||
break; | |||
case IZ_CONFIG_TYPE_I32: | |||
IZ_ConfigLoadI32(&item[i], config_path); | |||
break; | |||
case IZ_CONFIG_TYPE_GUID: | |||
IZ_ConfigLoadGuid(&item[i], config_path); | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
} | |||
IZ_ConfigSaveItemResult IZ_ConfigSaveGuid(IZ_ConfigItem* item, const char* config_path) { | |||
SDL_GUID dest = *((SDL_GUID*) item->dest); | |||
if (item->validator) { | |||
IZ_ConfigLoadParamsGuidValidator* validate = item->validator; | |||
if (!validate(dest)) { | |||
dest = *((const SDL_GUID*) item->default_value); | |||
} | |||
} | |||
if (item->transformer.deserialize && item->transformer.serialize) { | |||
IZ_ConfigSerializeGUID* serialize = item->transformer.serialize; | |||
const char serialized_value[128]; | |||
serialize(dest, serialized_value); | |||
if (!ini_puts(item->section, item->key, serialized_value, config_path)) { | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
char guid_str[128]; | |||
SDL_GUIDToString(dest, guid_str, 128); | |||
if (!ini_puts(item->section, item->key, guid_str, config_path)) { | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
void IZ_ConfigOverrideGuid(IZ_ConfigItem* item, u8 argc, const char* argv[]) { | |||
if (!item->cmdline_option) { | |||
return; | |||
} | |||
const char* cmdline_buffer; | |||
static SDL_GUID dest; | |||
static SDL_GUID config_value; | |||
config_value = *((SDL_GUID*) item->dest); | |||
if((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, item->cmdline_option))) { | |||
dest = SDL_GUIDFromString(cmdline_buffer); | |||
IZ_ConfigEnsureValidGuid(item, dest, config_value); | |||
} | |||
} | |||
IZ_ConfigSaveResult IZ_ConfigSave(IZ_ConfigItem item[], const char* config_path) { | |||
u8 i; | |||
i64 problems = 0; | |||
@@ -194,6 +291,9 @@ IZ_ConfigSaveResult IZ_ConfigSave(IZ_ConfigItem item[], const char* config_path) | |||
case IZ_CONFIG_TYPE_STRING: | |||
result = IZ_ConfigSaveString(&item[i], config_path); | |||
break; | |||
case IZ_CONFIG_TYPE_GUID: | |||
result = IZ_ConfigSaveGuid(&item[i], config_path); | |||
break; | |||
case IZ_CONFIG_TYPE_U8: | |||
result = IZ_ConfigSaveU8(&item[i], config_path); | |||
break; | |||
@@ -230,6 +330,9 @@ void IZ_ConfigOverride(IZ_ConfigItem item[], u8 argc, const char* argv[]) { | |||
case IZ_CONFIG_TYPE_I32: | |||
IZ_ConfigOverrideI32(&item[i], argc, argv); | |||
break; | |||
case IZ_CONFIG_TYPE_GUID: | |||
IZ_ConfigOverrideGuid(&item[i], argc, argv); | |||
break; | |||
default: | |||
break; | |||
} | |||
@@ -12,6 +12,7 @@ typedef enum { | |||
IZ_CONFIG_TYPE_U8, | |||
IZ_CONFIG_TYPE_U16, | |||
IZ_CONFIG_TYPE_I32, | |||
IZ_CONFIG_TYPE_GUID, | |||
} IZ_ConfigType; | |||
typedef struct { | |||
@@ -106,6 +106,13 @@ void IZ_JoystickHandleHatEvents(IZ_Action* action, SDL_Event e) { | |||
void IZ_JoystickHandleButtonEvents(IZ_JoystickState* state, IZ_Action* action, SDL_Event e) { | |||
u8 control_index; | |||
SDL_JoystickGUID joystick_guid = SDL_JoystickGetGUID(SDL_JoystickFromInstanceID(e.jbutton.which)); | |||
for (u8 zz = 0; zz < 16; zz += 1) { | |||
printf("%02x", joystick_guid.data[zz]); | |||
} | |||
printf("\n"); | |||
for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) { | |||
u8 normalized_button = e.jbutton.button; | |||
@@ -167,8 +174,9 @@ void IZ_JoystickBindStateToConfig(IZ_JoystickState(* state)[IZ_PLAYERS], IZ_Conf | |||
u8 base_index = (player_index * (IZ_CONTROLS - 4 + 2)); | |||
config_items[base_index].dest = &((*state)[player_index].config.device_id); | |||
config_items[base_index + 1].dest = &((*state)[player_index].config.axis_threshold); | |||
config_items[base_index + 2].dest = &((*state)[player_index].config.guid); | |||
for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) { | |||
config_items[base_index + 2 + (control_index - 4)].dest = &((*state)[player_index].config.control_mapping[control_index]); | |||
config_items[base_index + 3 + (control_index - 4)].dest = &((*state)[player_index].config.control_mapping[control_index]); | |||
} | |||
} | |||
} | |||
@@ -209,10 +217,23 @@ void IZ_JoystickInitializeConfigItems(IZ_ConfigItem config_items[]) { | |||
NULL, | |||
}; | |||
config_items[base_index + 2] = (IZ_ConfigItem) { | |||
IZ_CONFIG_TYPE_STRING, | |||
sizeof(u16), | |||
main_section_name, | |||
"GUID", | |||
NULL, | |||
&IZ_JOYSTICK_DEFAULT_STATE[player_index].config.guid, | |||
NULL, | |||
NULL, | |||
}; | |||
// todo add game controller GUID for determining mappings | |||
control_mapping_section_name = SDL_malloc(sizeof(char) * 64); | |||
sprintf(control_mapping_section_name, "Joystick.%d.ControlMapping", player_index); | |||
for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) { | |||
config_items[base_index + 2 + (control_index - 4)] = (IZ_ConfigItem) { | |||
config_items[base_index + 3 + (control_index - 4)] = (IZ_ConfigItem) { | |||
IZ_CONFIG_TYPE_U8, | |||
sizeof(u8), | |||
control_mapping_section_name, | |||
@@ -252,6 +273,7 @@ IZ_ProcedureResult IZ_JoystickInitialize(IZ_JoystickState(* state)[IZ_PLAYERS], | |||
return -2; | |||
} | |||
SDL_GameControllerAddMappingsFromFile("assets/gamecontrollerdb.txt"); | |||
u8 player_index; | |||
u8 joysticks_count = SDL_NumJoysticks(); | |||
for (player_index = 0; player_index < joysticks_count; player_index += 1) { | |||
@@ -3,6 +3,7 @@ | |||
#include <SDL_stdinc.h> | |||
#include <SDL_joystick.h> | |||
#include <SDL_gamecontroller.h> | |||
#include <SDL_events.h> | |||
#include <minIni.h> | |||
#include "../../config/IZ_config.h" | |||
@@ -24,6 +25,7 @@ typedef enum { | |||
typedef struct { | |||
u16 axis_threshold; | |||
SDL_JoystickID device_id; | |||
SDL_GUID guid; | |||
IZ_PadButton control_mapping[IZ_CONTROLS]; | |||
} IZ_JoystickConfig; | |||
@@ -55,6 +57,9 @@ static const IZ_JoystickState IZ_JOYSTICK_DEFAULT_STATE[IZ_PLAYERS] = { | |||
}, | |||
.axis_threshold = 8000u, | |||
.device_id = 0, | |||
.guid = { | |||
.data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } | |||
} | |||
}, | |||
.device = NULL, | |||
}, | |||