Files
GTASource/game/system/control.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

7453 lines
232 KiB
C++

//
// name: control.cpp
// description: Class converting controller inputs into player actions
#include "system/control.h"
#include <stdio.h>
#if __PPU
// PS3 headers
#include <cell/error.h>
#include <sysutil/sysutil_sysparam.h>
#endif //__PPU
// Rage headers
#include "input/input.h"
#include "fragment/type.h"
#include "system/ipc.h"
#include "system/param.h"
#include "profile/timebars.h"
#include "fwsys/timer.h"
#include "rline/rlpc.h"
#if __WIN32PC
#include "atl/map.h"
#include "string/unicode.h"
#endif // __WIN32PC
// game headers
#include "Frontend/PauseMenu.h"
#include "Frontend/ProfileSettings.h"
#include "frontend/VideoEditor/VideoEditorInterface.h"
#include "system/controlmgr.h"
#include "system/keyboard.h"
#include "Peds/PlayerSpecialAbility.h"
#if __BANK
# include "text/TextFormat.h"
#endif
#include "camera/CamInterface.h"
#include "camera/gameplay/GameplayDirector.h"
#if KEYBOARD_MOUSE_SUPPORT
#include "stats/StatsInterface.h"
#endif // KEYBOARD_MOUSE_SUPPORT
//OPTIMISATIONS_OFF()
RAGE_DEFINE_CHANNEL(Control)
#define DEFAULT_SETTINGS_EXT "meta"
#define DEFAULT_SETTINGS_DIR "common:/data/control/"
#define INPUT_SETTINGS_FILE DEFAULT_SETTINGS_DIR "settings." DEFAULT_SETTINGS_EXT
#define COMMON_LAYOUT_FILE DEFAULT_SETTINGS_DIR "common." DEFAULT_SETTINGS_EXT
#define STANDARD_LAYOUT_FILE DEFAULT_SETTINGS_DIR "standard." DEFAULT_SETTINGS_EXT
#define TRIGGER_SWAP_LAYOUT_FILE DEFAULT_SETTINGS_DIR "trigger_swap." DEFAULT_SETTINGS_EXT
#define SOUTHPAW_LAYOUT_FILE DEFAULT_SETTINGS_DIR "southpaw." DEFAULT_SETTINGS_EXT
#define SOUTHPAW_TRIGGER_SWAP_LAYOUT_FILE DEFAULT_SETTINGS_DIR "southpaw_trigger_swap." DEFAULT_SETTINGS_EXT
#define STANDARD_SCRIPT_LAYOUT_FILE DEFAULT_SETTINGS_DIR "standard_scripted." DEFAULT_SETTINGS_EXT
#define TRIGGER_SWAP_SCRIPT_LAYOUT_FILE DEFAULT_SETTINGS_DIR "trigger_swap_scripted." DEFAULT_SETTINGS_EXT
#define SOUTHPAW_SCRIPT_LAYOUT_FILE DEFAULT_SETTINGS_DIR "southpaw_scripted." DEFAULT_SETTINGS_EXT
#define SOUTHPAW_TRIGGER_SWAP_SCRIPT_LAYOUT_FILE DEFAULT_SETTINGS_DIR "southpaw_trigger_swap_scripted." DEFAULT_SETTINGS_EXT
#define STANDARD_TPS_LAYOUT_FILE DEFAULT_SETTINGS_DIR "standard_tps." DEFAULT_SETTINGS_EXT
#define TRIGGER_SWAP_TPS_LAYOUT_FILE DEFAULT_SETTINGS_DIR "trigger_swap_tps." DEFAULT_SETTINGS_EXT
#define SOUTHPAW_TPS_LAYOUT_FILE DEFAULT_SETTINGS_DIR "southpaw_tps." DEFAULT_SETTINGS_EXT
#define SOUTHPAW_TRIGGER_SWAP_TPS_LAYOUT_FILE DEFAULT_SETTINGS_DIR "southpaw_trigger_swap_tps." DEFAULT_SETTINGS_EXT
#define STANDARD_FPS_LAYOUT_FILE DEFAULT_SETTINGS_DIR "standard_fps." DEFAULT_SETTINGS_EXT
#define TRIGGER_SWAP_FPS_LAYOUT_FILE DEFAULT_SETTINGS_DIR "trigger_swap_fps." DEFAULT_SETTINGS_EXT
#define SOUTHPAW_FPS_LAYOUT_FILE DEFAULT_SETTINGS_DIR "southpaw_fps." DEFAULT_SETTINGS_EXT
#define SOUTHPAW_TRIGGER_SWAP_FPS_LAYOUT_FILE DEFAULT_SETTINGS_DIR "southpaw_trigger_swap_fps." DEFAULT_SETTINGS_EXT
#define STANDARD_FPS_ALTERNATE_LAYOUT_FILE DEFAULT_SETTINGS_DIR "standard_fps_alternate." DEFAULT_SETTINGS_EXT
#define TRIGGER_SWAP_FPS_ALTERNATE_LAYOUT_FILE DEFAULT_SETTINGS_DIR "trigger_swap_fps_alternate." DEFAULT_SETTINGS_EXT
#define SOUTHPAW_FPS_ALTERNATE_LAYOUT_FILE DEFAULT_SETTINGS_DIR "southpaw_fps_alternate." DEFAULT_SETTINGS_EXT
#define SOUTHPAW_TRIGGER_SWAP_FPS_ALTERNATE_LAYOUT_FILE DEFAULT_SETTINGS_DIR "southpaw_trigger_swap_fps_alternate." DEFAULT_SETTINGS_EXT
#define DEFAULT_ACCEPT_CANCEL_SETTINGS_FILE DEFAULT_SETTINGS_DIR "standard_accept_cancel." DEFAULT_SETTINGS_EXT
#define ALTERNATE_ACCEPT_CANCEL_SETTINGS_FILE DEFAULT_SETTINGS_DIR "alternate_accept_cancel." DEFAULT_SETTINGS_EXT
#define DEFAULT_DUCK_HANDBRAKE_SETTINGS_FILE DEFAULT_SETTINGS_DIR "standard_duck_handbrake." DEFAULT_SETTINGS_EXT
#define ALTERNATE_DUCK_HANDBRAKE_SETTINGS_FILE DEFAULT_SETTINGS_DIR "alternate_duck_handbrake." DEFAULT_SETTINGS_EXT
#if RSG_ORBIS
#define GESTURES_LAYOUT_FILE DEFAULT_SETTINGS_DIR "gestures." DEFAULT_SETTINGS_EXT
#endif
#if KEYBOARD_MOUSE_SUPPORT
#define PC_SETTINGS_DIR "platform:/data/control/"
#define PC_DEVICE_SETTINGS_DIR PC_SETTINGS_DIR "Devices/"
#define PC_INPUT_SETTINGS_FILE PC_SETTINGS_DIR "settings." DEFAULT_SETTINGS_EXT
#define PC_DEFAULT_SETTINGS_FILE PC_SETTINGS_DIR "default." DEFAULT_SETTINGS_EXT
#define PC_FIXED_SETTINGS_FILE PC_SETTINGS_DIR "fixed." DEFAULT_SETTINGS_EXT
#define PC_STANDARD_MOUSE_SETTINGS_FILE PC_SETTINGS_DIR "standard_mouse." DEFAULT_SETTINGS_EXT
#define PC_INVERTED_MOUSE_SETTINGS_FILE PC_SETTINGS_DIR "inverted_mouse." DEFAULT_SETTINGS_EXT
#define PC_GAMEPAD_DEFINITION_FILE PC_SETTINGS_DIR "gamepad." DEFAULT_SETTINGS_EXT
#define PC_DYNAMIC_SETTINGS_FILE PC_SETTINGS_DIR "dynamic." DEFAULT_SETTINGS_EXT
#define PC_MOUSE_VEHICLE_CAR_CENTERED_FILE PC_SETTINGS_DIR "mouse_vehicle_car_centered." DEFAULT_SETTINGS_EXT
#define PC_MOUSE_VEHICLE_CAR_SCALED_FILE PC_SETTINGS_DIR "mouse_vehicle_car_scaled." DEFAULT_SETTINGS_EXT
#define PC_MOUSE_VEHICLE_FLY_CENTERED_FILE PC_SETTINGS_DIR "mouse_vehicle_fly_centered." DEFAULT_SETTINGS_EXT
#define PC_MOUSE_VEHICLE_FLY_SCALED_FILE PC_SETTINGS_DIR "mouse_vehicle_fly_scaled." DEFAULT_SETTINGS_EXT
#define PC_MOUSE_VEHICLE_SUB_CENTERED_FILE PC_SETTINGS_DIR "mouse_vehicle_sub_centered." DEFAULT_SETTINGS_EXT
#define PC_MOUSE_VEHICLE_SUB_SCALED_FILE PC_SETTINGS_DIR "mouse_vehicle_sub_scaled." DEFAULT_SETTINGS_EXT
#define PC_INVERTED_MOUSE_VEHICLE_FLY_CENTERED_FILE PC_SETTINGS_DIR "inverted_mouse_vehicle_fly_centered." DEFAULT_SETTINGS_EXT
#define PC_INVERTED_MOUSE_VEHICLE_FLY_SCALED_FILE PC_SETTINGS_DIR "inverted_mouse_vehicle_fly_scaled." DEFAULT_SETTINGS_EXT
#define PC_INVERTED_MOUSE_VEHICLE_SUB_CENTERED_FILE PC_SETTINGS_DIR "inverted_mouse_vehicle_sub_centered." DEFAULT_SETTINGS_EXT
#define PC_INVERTED_MOUSE_VEHICLE_SUB_SCALED_FILE PC_SETTINGS_DIR "inverted_mouse_vehicle_sub_scaled." DEFAULT_SETTINGS_EXT
#define PC_MOUSE_VEHICLE_FLY_CENTERED_SWAPPED_FILE PC_SETTINGS_DIR "mouse_vehicle_fly_centered_xy_swapped." DEFAULT_SETTINGS_EXT
#define PC_MOUSE_VEHICLE_FLY_SCALED_SWAPPED_FILE PC_SETTINGS_DIR "mouse_vehicle_fly_scaled_xy_swapped." DEFAULT_SETTINGS_EXT
#define PC_INVERTED_MOUSE_VEHICLE_FLY_CENTERED_SWAPPED_FILE PC_SETTINGS_DIR "inverted_mouse_vehicle_fly_centered_xy_swapped." DEFAULT_SETTINGS_EXT
#define PC_INVERTED_MOUSE_VEHICLE_FLY_SCALED_SWAPPED_FILE PC_SETTINGS_DIR "inverted_mouse_vehicle_fly_scaled_xy_swapped." DEFAULT_SETTINGS_EXT
#define KEYBOARD_LAYOUT_DIR PC_SETTINGS_DIR "Keyboard Layout/"
#define KEYBOARD_LAYOUT_EXTENSION ".meta"
#define DEFAULT_KEYBOARD_LAYOUT "en"
#define USER_SETTINGS_EXT "xml"
#define AUTOSAVE_EXT "autosave"
// TODO: update once the user path is known.
#define PC_USER_SETTINGS_DIR "control/"
#define PC_USER_SETTINGS_FILE "user." USER_SETTINGS_EXT
#define PC_AUTOSAVE_USER_SETTINGS_FILE "user." AUTOSAVE_EXT
#define PC_USER_GAMEPAD_DEFINITION_FILE "gamepad." USER_SETTINGS_EXT
#define BUFFER_SIZE 50
# if __BANK
PARAM(noKeyboardMouseControls,"[control] Stops PC controls from being loaded.");
# endif // __BANK
NOSTRIP_PC_PARAM(keyboardLocal, "[control] Sets the keyboard layout to the specified region.");
#if !RSG_FINAL
XPARAM(enableXInputEmulation);
#endif // !RSG_FINAL
#endif // KEYBOARD_MOUSE_SUPPORT
#if USE_STEAM_CONTROLLER
#define STEAM_CONTROLLER_SETTINGS_FILE PC_SETTINGS_DIR "steam_controller." DEFAULT_SETTINGS_EXT
PARAM(steamControllerSettingsFile, "[control] The settings file for the steam controller.");
#endif // USE_STEAM_CONTROLLER
#define ALLOW_USER_CONTROLS (1 && KEYBOARD_MOUSE_SUPPORT)
AI_OPTIMISATIONS()
#if FPS_MODE_SUPPORTED
// TODO: Remove this once this is an option in the pause menu.
XPARAM(firstperson);
#endif // FPS_MODE_SUPPORTED
CControl::Settings CControl::ms_settings = CControl::Settings();
ControlInput::ControlSettings CControl::ms_StandardScriptedMappings;
s32 CControl::ms_LastTimeTouched = 0;
rage::sysIpcCurrentThreadId CControl::ms_threadId = sysIpcCurrentThreadIdInvalid;
rage::dev_u32 CControl::SYSTEM_EVENT_TIMOUT_DURATION = 1000u;
// TODO: For now, use the enum name until we create proper strings.
extern const char* parser_rage__Mapper_Strings[];
extern ::rage::parEnumData parser_rage__InputType_Data;
extern ::rage::parEnumData parser_rage__InputGroup_Data;
extern ::rage::parEnumData parser_rage__ioMapperParameter_Data;
extern ::rage::parEnumData parser_rage__ioMapperSource_Data;
#if !__FINAL
atString CControl::ms_ThreadlStack;
sysCriticalSectionToken CControl::ms_Lock;
#endif // !__FINAL
/*
// enum of standard default types for each supported pad style (360 or PS2)
enum {
DEFAULT_360_PAD_CONTROLS_CONFIG = 0,
DEFAULT_PS2_PAD_CONTROLS_CONFIG,
MAX_DEFAULT_PAD_CONTROLS_CONFIGS
};
*/
//
// name: CControl::CControl
// description: constructor
CControl::CControl()
{
}
CControl::~CControl()
{
g_SysService.RemoveDelegate(&m_Delegate);
m_Delegate.Reset();
}
void CControl::Init(unsigned initMode)
{
if(initMode == INIT_SESSION)
{
ms_LastTimeTouched = fwTimer::GetTimeInMilliseconds();
WIN32PC_ONLY(ShutdownPCScriptControlMappings());
}
}
//
// name: CControl::Init
// description: initialise structures
void CControl::Init(s32 device)
{
sysCriticalSection lock(m_Cs);
if(m_Delegate.IsBound() == false)
{
m_Delegate.Bind(this, &CControl::HandleSystemEvent);
g_SysService.AddDelegate(&m_Delegate);
}
for(s32 i = 0; i < MAX_INPUTS; ++i)
{
DEV_ONLY(m_inputs[i].SetId(i));
m_isInputMapped[i] = false;
}
SetPad(device);
ms_settings.Init();
ms_LastTimeTouched=fwTimer::GetTimeInMilliseconds();
m_InputDisableEndTime = 0;
m_DisableOptions = ioValue::DisableOptions();
m_bInputsStillDisabled = false;
m_useAlternateScriptedControlsNextFrame = false;
FPS_MODE_SUPPORTED_ONLY(m_FpsControlLayout = INVALID_LAYOUT);
m_TpsControlLayout = INVALID_LAYOUT;
m_HandbrakeControl = -1; // Invalid.
m_scriptedInputLayout = STANDARD_TPS_LAYOUT;
m_ToggleRunOn = false;
m_WasControlsRefreshedThisFrame = false;
m_HasScriptCheckControlsRefresh = false;
m_WasControlsRefreshedLastFrame = false;
m_WasUsingFpsMode = false;
#if KEYBOARD_MOUSE_SUPPORT
m_currentMapping.m_ScanMapper = NULL;
m_LastKnownSource = ioSource::UNKNOWN_SOURCE;
m_MouseSettings = 0u;
m_HasLoadedDefaultControls = false;
m_IsUserSignedIn = false;
m_ToggleAim = false;
m_ToggleAimOn = false;
m_ResetToggleAim = false;
m_ToggleBikeSprintOn = false;
m_BackButtonEnableTime = 0;
m_LastFrameDeviceIndex = ioSource::IOMD_UNKNOWN;
m_RecachedConflictCount = true;
m_CachedConflictCount = 0u;
m_DriveCameraToggleOn = false;
m_UseSecondaryDriveCameraToggle = false;
m_CurrentLoadedDynamicScheme = atHashString("", 0);
m_CurrentScriptDynamicScheme = atHashString("", 0);
m_CurrentCodeOverrideDynamicScheme = atHashString("", 0);
#if DEBUG_DRAW
m_OnFootMouseSensitivity = 0.0f;
m_InVehicleMouseSensitivity = 0.0f;
#endif // DEBUG_DRAW
#endif // KEYBOARD_MOUSE_SUPPORT
#if USE_STEAM_CONTROLLER
m_SteamController = 0;
m_SteamActionSet = 0;
m_SteamHandleMap.Reset();
#endif // USE_STEAM_CONTROLLER
#if __WIN32PC
// TR TODO: Tie this to the pause menu.
#if __BANK
safecpy(m_RagPCControlScheme, "Default", RagData::TEXT_SIZE);
m_RagLoadedPCControlScheme[0] = '\0';
#endif // __BANK
// TR TODO: Read these from the frontend.
m_MouseVehicleCarAutoCenter = true;
m_MouseVehicleFlyAutoCenter = true;
m_MouseVehicleSubAutoCenter = false;
m_UseExtraDevices = false;
#endif // __WIN32PC
#if LIGHT_EFFECTS_SUPPORT
#if RSG_BANK
m_RagLightEffectEnabled = false;
m_RagLightEffectColor = Color32(255, 255, 255);
m_RagLightEffect = ioConstantLightEffect(m_RagLightEffectColor);
m_LightEffects[RAG_LIGHT_EFFECT].m_Effect = NULL;
m_LightEffects[RAG_LIGHT_EFFECT].m_StartTime = 0u;
#endif // RSG_BANK
m_ActiveLightDevices.Reset();
#if RSG_ORBIS
m_PadLightDevice = ioPadLightDevice(device);
m_ActiveLightDevices.Push(&m_PadLightDevice);
#elif RSG_PC
if(m_LogitechLightDevice.IsValid())
{
m_ActiveLightDevices.Push(&m_LogitechLightDevice);
}
#endif // RSG_ORBIS
m_LightEffectsEnabled = true;
ResetLightDeviceColor();
#endif // LIGHT_EFFECTS_SUPPORT
#if RSG_ORBIS
m_IsRemotePlayer = false;
m_IsAcceptCrossSetting = 0;
#endif // RSG_ORBIS
#if USE_ACTUATOR_EFFECTS
m_Recoil = fwRecoilEffect();
m_Rumble = ioRumbleEffect();
#endif // USE_ACTUATOR_EFFECTS
m_ShakeSupressId = NO_SUPPRESS;
m_SystemFrontendBackOccurredTime = 0u;
m_SystemPhoneBackOccurredTime = 0u;
m_SystemPlayPauseOccurredTime = 0u;
m_SystemFrontendViewOccuredTime = 0u;
m_SystemGameViewOccuredTime = 0u;
}
//
// name: CControl::SetPad
// description: Set the pad number for this control
void CControl::SetPad(s32 device)
{
m_padNum = device;
#if LIGHT_EFFECTS_SUPPORT
ORBIS_ONLY(m_PadLightDevice = ioPadLightDevice(device));
#endif // LIGHT_EFFECTS_SUPPORT
for(u32 i = 0; i < MAPPERS_MAX; ++i)
m_Mappers[i].SetPlayer(m_padNum);
}
bool CControl::GetPedSprintIsDown() const
{
if(m_inputs[INPUT_SPRINT].IsEnabled() && CanUseToggleRun())
{
if(m_ToggleRunOn)
{
return m_inputs[INPUT_SPRINT].IsUp() || !m_inputs[INPUT_SPRINT].HistoryHeldDown(KEYBOARD_START_SPRINT_HELD_TIME);
}
else
{
return m_inputs[INPUT_SPRINT].HistoryHeldDown(KEYBOARD_START_SPRINT_HELD_TIME);
}
}
return m_inputs[INPUT_SPRINT].IsDown();
}
bool CControl::GetPedSprintIsPressed() const
{
if(m_inputs[INPUT_SPRINT].IsEnabled() && CanUseToggleRun() && m_ToggleRunOn)
{
return m_inputs[INPUT_SPRINT].IsReleased();
}
return m_inputs[INPUT_SPRINT].IsPressed();
}
bool CControl::GetPedSprintIsReleased() const
{
if(m_inputs[INPUT_SPRINT].IsEnabled() && CanUseToggleRun() && m_ToggleRunOn)
{
return m_inputs[INPUT_SPRINT].IsPressed();
}
return m_inputs[INPUT_SPRINT].IsReleased();
}
bool CControl::GetPedSprintHistoryIsPressed(u32 durationMS) const
{
if(m_inputs[INPUT_SPRINT].IsEnabled() && CanUseToggleRun() && m_ToggleRunOn)
{
return m_inputs[INPUT_SPRINT].HistoryReleased(durationMS);
}
return m_inputs[INPUT_SPRINT].HistoryPressed(durationMS);
}
bool CControl::GetPedSprintHistoryHeldDown(u32 durationMS) const
{
if(m_inputs[INPUT_SPRINT].IsEnabled() && CanUseToggleRun() && m_ToggleRunOn)
{
return m_inputs[INPUT_SPRINT].HistoryReleased(durationMS);
}
return m_inputs[INPUT_SPRINT].HistoryHeldDown(durationMS);
}
bool CControl::GetPedSprintHistoryReleased(u32 durationMS) const
{
if(m_inputs[INPUT_SPRINT].IsEnabled() && CanUseToggleRun() && m_ToggleRunOn)
{
return m_inputs[INPUT_SPRINT].HistoryPressed(durationMS);
}
return m_inputs[INPUT_SPRINT].HistoryReleased(durationMS);
}
bool CControl::GetVehiclePushbikeSprintIsDown() const
{
#if KEYBOARD_MOUSE_SUPPORT
if(m_inputs[INPUT_VEH_PUSHBIKE_SPRINT].IsEnabled() && IsUsingKeyboardAndMouseForMovement() && m_ToggleBikeSprintOn)
{
return m_inputs[INPUT_VEH_PUSHBIKE_SPRINT].IsUp();
}
#endif // KEYBOARD_MOUSE_SUPPORT
return m_inputs[INPUT_VEH_PUSHBIKE_SPRINT].IsDown();
}
bool CControl::IsVehicleAttackInputDown() const
{
#if KEYBOARD_MOUSE_SUPPORT
// PREF_ALTERNATE_DRIVEBY shouldn't change mouse.
if(WasKeyboardMouseLastKnownSource())
{
return GetVehicleAttack().IsDown();
}
#endif // KEYBOARD_MOUSE_SUPPORT
if(CPauseMenu::GetMenuPreference(PREF_ALTERNATE_DRIVEBY))
{
return GetVehicleAttack().IsDown();
}
else
{
return GetVehicleAim().IsDown();
}
}
bool CControl::IsVehicleAttackInputReleased() const
{
#if KEYBOARD_MOUSE_SUPPORT
// PREF_ALTERNATE_DRIVEBY shouldn't change mouse.
if(WasKeyboardMouseLastKnownSource())
{
return GetVehicleAttack().IsReleased();
}
#endif // KEYBOARD_MOUSE_SUPPORT
if(CPauseMenu::GetMenuPreference(PREF_ALTERNATE_DRIVEBY))
{
return GetVehicleAttack().IsReleased();
}
else
{
return GetVehicleAim().IsReleased();
}
}
float CControl::GetVehicleAttackInputNorm() const
{
#if KEYBOARD_MOUSE_SUPPORT
// PREF_ALTERNATE_DRIVEBY shouldn't change mouse.
if(WasKeyboardMouseLastKnownSource())
{
return GetVehicleAttack().GetNorm();
}
#endif // KEYBOARD_MOUSE_SUPPORT
if(CPauseMenu::GetMenuPreference(PREF_ALTERNATE_DRIVEBY))
{
return GetVehicleAttack().GetNorm();
}
else
{
return GetVehicleAim().GetNorm();
}
}
void CControl::HandleSystemEvent(sysServiceEvent* evt)
{
if(evt)
{
if(evt->GetType() == sysServiceEvent::BACK)
{
m_SystemFrontendBackOccurredTime = fwTimer::GetSystemTimeInMilliseconds();
m_SystemPhoneBackOccurredTime = fwTimer::GetSystemTimeInMilliseconds();
}
else if(evt->GetType() == sysServiceEvent::PAUSE || evt->GetType() == sysServiceEvent::PLAY || evt->GetType() == sysServiceEvent::MENU)
{
m_SystemPlayPauseOccurredTime = fwTimer::GetSystemTimeInMilliseconds();
}
else if(evt->GetType() == sysServiceEvent::VIEW)
{
m_SystemFrontendViewOccuredTime = fwTimer::GetSystemTimeInMilliseconds();
m_SystemGameViewOccuredTime = fwTimer::GetSystemTimeInMilliseconds();
}
}
}
//
// name: CControl::LoadControls
// description: save the controls to a file
bool CControl::LoadControls(char *pFilename, s32 iVersionNum)
{
bool result = false;
fiStream *inStream = ASSET.Open(pFilename,"cfg");
if (inStream)
{
// we've got a file, so we need to clear controls before we load it
for(u32 i = 0; i < MAPPERS_MAX; ++i)
m_Mappers[i].ClearValues();
result = m_Mappers[ON_FOOT].Load(inStream,iVersionNum);
if (result)
{
result = m_Mappers[MELEE].Load(inStream,iVersionNum);
}
if (result)
{
result = m_Mappers[IN_VEHICLE].Load(inStream,iVersionNum);
}
if (result)
{
result = m_Mappers[PARACHUTE].Load(inStream,iVersionNum);
}
if (result)
{
result = m_Mappers[FRONTEND].Load(inStream,iVersionNum);
}
if (result)
{
result = m_Mappers[REPLAY].Load(inStream,iVersionNum);
}
inStream->Close();
}
return result;
}
//
// name: CControl::SaveControls
// description: save the controls to a file
bool CControl::SaveControls(char *pFilename, s32 iVersionNum)
{
bool result = false;
fiStream *outStream = ASSET.Create(pFilename,"cfg");
if (outStream)
{
result = m_Mappers[ON_FOOT].Save(outStream,iVersionNum);
if (result)
result = m_Mappers[MELEE].Save(outStream,iVersionNum);
if (result)
result = m_Mappers[IN_VEHICLE].Save(outStream,iVersionNum);
if (result)
result = m_Mappers[FRONTEND].Save(outStream,iVersionNum);
if (result)
result = m_Mappers[REPLAY].Save(outStream,iVersionNum);
outStream->Close();
}
return result;
}
//
// name: CControl::AddActionToString
// description: adds the action to pString that can be displayed
bool CControl::AddActionToString(s32 action, char *pString, ioMapper *map_type)
{
char this_string[50];
bool bDirectString = true;
switch (map_type->GetMapperSource(map_type->FindValue(m_inputs[action])))
{
case IOMS_DIGITALBUTTON_AXIS:
{
switch(action)
{
case INPUT_MOVE_LR:
case INPUT_MOVE_UD:
case INPUT_LOOK_LR:
case INPUT_LOOK_UD:
case INPUT_VEH_MOVE_UD:
case INPUT_VEH_MOVE_LR:
case INPUT_VEH_GUN_LR:
case INPUT_VEH_GUN_UD:
{
unsigned int other_key = (map_type->GetParameter(map_type->FindValue(m_inputs[action]))); // get key on opposite axis
other_key &= 0xFF;
unsigned int c = 1;
sprintf(this_string, "NOPAD");
for (int cc = 1; cc < 32; cc++)
{
if (other_key & c)
{
sprintf(this_string, "PAD %d", cc); // sets the correct pad value
bDirectString = false; // use this actual string rather than from text file
break;
}
c *= 2;
}
break;
}
default:
{
Assert(0);
// cannot be used
break;
}
}
break;
}
case IOMS_KEYBOARD:
{
sprintf(this_string, "ACTN_%d", map_type->GetParameter(map_type->FindValue(m_inputs[action])));
break;
}
case IOMS_JOYSTICK_BUTTON:
case IOMS_PAD_DIGITALBUTTON:
case IOMS_PAD_DIGITALBUTTONANY:
case IOMS_PAD_ANALOGBUTTON:
{
unsigned int pad_num = map_type->GetParameter(map_type->FindValue(m_inputs[action]));
unsigned int c = 1;
sprintf(this_string, "NOPAD");
for (int cc = 1; cc < 32; cc++)
{
if (pad_num & c)
{
sprintf(this_string, "PAD %d", cc); // sets the correct pad value
bDirectString = false; // use this actual string rather than from text file
break;
}
c *= 2;
}
break;
}
case IOMS_MOUSE_BUTTON:
{
unsigned int but_num = map_type->GetParameter(map_type->FindValue(m_inputs[action]));
switch (but_num)
{
case ioMouse::MOUSE_LEFT:
{
sprintf(this_string, "MS_LEFT");
break;
}
case ioMouse::MOUSE_RIGHT:
{
sprintf(this_string, "MS_RIGHT");
break;
}
case ioMouse::MOUSE_MIDDLE:
{
sprintf(this_string, "MS_MID");
break;
}
case ioMouse::MOUSE_EXTRABTN1:
{
sprintf(this_string, "MS_BUT1");
break;
}
case ioMouse::MOUSE_EXTRABTN2:
{
sprintf(this_string, "MS_BUT2");
break;
}
case ioMouse::MOUSE_EXTRABTN3:
{
sprintf(this_string, "MS_BUT3");
break;
}
case ioMouse::MOUSE_EXTRABTN4:
{
sprintf(this_string, "MS_BUT4");
break;
}
case ioMouse::MOUSE_EXTRABTN5:
{
sprintf(this_string, "MS_BUT5");
break;
}
default:
{
Assertf(0, "Unknown mouse button pressed");
break;
}
}
break;
}
case IOMS_PAD_AXIS:
{
switch(action)
{
case INPUT_MOVE_LR:
case INPUT_MOVE_UD:
case INPUT_LOOK_LR:
case INPUT_LOOK_UD:
case INPUT_VEH_MOVE_UD:
case INPUT_VEH_MOVE_LR:
case INPUT_VEH_GUN_LR:
case INPUT_VEH_GUN_UD:
{
sprintf(this_string, "AXISLR_%d", map_type->GetParameter(map_type->FindValue(m_inputs[action])));
break;
}
default:
{
Assert(0);
// cannot be used
break;
}
}
break;
}
case IOMS_JOYSTICK_AXIS:
{
switch(action)
{
case INPUT_MOVE_LR:
case INPUT_MOVE_UD:
case INPUT_LOOK_LR:
case INPUT_LOOK_UD:
case INPUT_VEH_MOVE_UD:
case INPUT_VEH_MOVE_LR:
case INPUT_VEH_GUN_LR:
case INPUT_VEH_GUN_UD:
{
sprintf(this_string, "AXISJ_%d", map_type->GetParameter(map_type->FindValue(m_inputs[action])));
break;
}
default:
{
Assert(0);
// cannot be used
break;
}
}
break;
}
case IOMS_MOUSE_ABSOLUTEAXIS:
{
switch(action)
{
case INPUT_MOVE_LR:
case INPUT_MOVE_UD:
case INPUT_LOOK_LR:
case INPUT_LOOK_UD:
case INPUT_VEH_MOVE_UD:
case INPUT_VEH_MOVE_LR:
case INPUT_VEH_GUN_LR:
case INPUT_VEH_GUN_UD:
{
switch (map_type->GetParameter(map_type->FindValue(m_inputs[action])))
{
case IOM_AXIS_X:
{
sprintf(this_string, "AXISM_X");
break;
}
case IOM_AXIS_Y:
{
sprintf(this_string, "AXISM_Y");
break;
}
default:
{
Assert(0);
// cannot be used
break;
}
}
break;
}
default:
{
Assert(0);
// cannot be used
break;
}
}
break;
}
case IOMS_MOUSE_WHEEL:
{
switch(action)
{
case INPUT_MOVE_LR:
case INPUT_MOVE_UD:
case INPUT_LOOK_LR:
case INPUT_LOOK_UD:
case INPUT_VEH_MOVE_UD:
case INPUT_VEH_MOVE_LR:
case INPUT_VEH_GUN_LR:
case INPUT_VEH_GUN_UD:
{
// cannot be assigned
break;
}
default:
{
switch (map_type->GetParameter(map_type->FindValue(m_inputs[action])))
{
case IOM_WHEEL_UP:
{
sprintf(this_string, "AXISW_U");
break;
}
case IOM_WHEEL_DOWN:
{
sprintf(this_string, "AXISW_D");
break;
}
default:
{
Assert(0);
// cannot be used
break;
}
}
break;
}
}
break;
}
default:
{
// unknown input value:
sprintf(this_string, "UNKNOWN");
bDirectString = false;
break;
}
}
strcat(pString, this_string);
return (bDirectString); // whether to use this string or one from the text file (used for pad button text)
}
void CControl::EnableAlternateScriptedControlsLayout()
{
sysCriticalSection lock(m_Cs);
m_useAlternateScriptedControlsNextFrame = true;
LoadScriptedSettings(false, false);
}
void CControl::UseDefaultScriptedControlsLayout()
{
sysCriticalSection lock(m_Cs);
m_useAlternateScriptedControlsNextFrame = false;
LoadScriptedSettings(true, false);
}
void CControl::LoadScriptedSettings(bool loadStandardControls, bool forceLoad)
{
eControlLayout desiredLayout = INVALID_LAYOUT;
const ControlInput::ControlSettings* scriptSettings = NULL;
if(loadStandardControls)
{
desiredLayout = STANDARD_TPS_LAYOUT;
scriptSettings = &ms_StandardScriptedMappings;
}
#if FPS_MODE_SUPPORTED
else if(IsUsingFpsMode())
{
desiredLayout = ConvertToScriptedLayout(GetFpsLayout());
scriptSettings = &m_AlternateFpsScriptedSettings;
}
#endif // FPS_MODE_SUPPORTED
else
{
desiredLayout = ConvertToScriptedLayout(GetTpsLayout());
scriptSettings = &m_AlternateTpsScriptedSettings;
}
if((desiredLayout != m_scriptedInputLayout && scriptSettings != NULL) || forceLoad)
{
// remove current mappings
for(s32 input = SCRIPTED_INPUT_FIRST; input <= SCRIPTED_INPUT_LAST; ++input)
{
ioMapper& mapper = m_Mappers[ms_settings.m_InputMappers[input]];
mapper.RemoveDeviceMappings(m_inputs[input], ioSource::IOMD_DEFAULT);
#if RSG_PC
if(m_UseExtraDevices)
{
for(s32 joystickIndex = 0; joystickIndex < ioJoystick::GetStickCount(); ++joystickIndex)
{
CPad& pad = CControlMgr::GetPad(joystickIndex);
// if the pad is not connected then it is a DirectInput Device. If it is connected check that is is not using pad
// rather than direct input mappings.
if( !pad.IsConnected() || (!pad.IsXBox360CompatiblePad() && !pad.IsPs3Pad()) )
{
mapper.RemoveDeviceMappings(m_inputs[input], joystickIndex);
}
}
}
#endif // RSG_PC
}
LoadSettings(*scriptSettings, ioSource::IOMD_DEFAULT);
#if RSG_PC
if(m_UseExtraDevices)
{
for(s32 joystickIndex = 0; joystickIndex < ioJoystick::GetStickCount(); ++joystickIndex)
{
CPad& pad = CControlMgr::GetPad(joystickIndex);
// if the pad is not connected then it is a DirectInput Device. If it is connected check that is is not using pad
// rather than direct input mappings.
if( !pad.IsConnected() || (!pad.IsXBox360CompatiblePad() && !pad.IsPs3Pad()) )
{
const ControlInput::Gamepad::Definition* definition = ms_settings.GetGamepadDefinition(ioJoystick::GetStick(joystickIndex));
if(definition != NULL)
{
LoadGamepadControls(*definition, *scriptSettings, joystickIndex);
}
}
}
}
#endif // RSG_PC
m_scriptedInputLayout = desiredLayout;
}
}
#define PAD_OVERRIDE -1
/////////////////////////////////////////////////////////////////////////////////////
// NAME: SetDefaultLookInversions
// PURPOSE: Set the look inversons based upon the users settings.
/////////////////////////////////////////////////////////////////////////////////////
void CControl::SetDefaultLookInversions()
{
if(CPauseMenu::GetMenuPreference(PREF_INVERT_LOOK))
{
m_inputs[INPUT_LOOK_UD].MakeInverted();
m_inputs[INPUT_VEH_GUN_UD].MakeInverted();
m_inputs[INPUT_SCALED_LOOK_UD].MakeInverted();
}
else
{
m_inputs[INPUT_LOOK_UD].MakeNormal();
m_inputs[INPUT_VEH_GUN_UD].MakeNormal();
m_inputs[INPUT_SCALED_LOOK_UD].MakeNormal();
}
}
/////////////////////////////////////////////////////////////////////////////////////
// NAME: SetInitialDefaultMappings
// PURPOSE: hard codes the initial values of the control mappings.
/////////////////////////////////////////////////////////////////////////////////////
void CControl::SetInitialDefaultMappings(bool forceReload, bool WIN32PC_ONLY(loadAutoSavedSettings) BANK_ONLY(, const char* customMappingsFile /*= NULL*/))
{
sysCriticalSection lock(m_Cs);
ClearInputMappings();
#if (RSG_PC && ALLOW_USER_CONTROLS) || (__BANK)
bool result = false;
#endif
// We are using the pause menu preferences instead of the CProfileSettings as the profile
// settings do not appear to be updated when we are informed of a control change.
const bool rebuild = ( forceReload || m_TpsControlLayout != GetTpsLayout()
FPS_MODE_SUPPORTED_ONLY(|| m_FpsControlLayout != GetFpsLayout())
ORBIS_ONLY(|| m_IsRemotePlayer != GetPad().IsRemotePlayPad() || m_IsAcceptCrossSetting != CPauseMenu::GetMenuPreference(PREF_ACCEPT_IS_CROSS)) );
#if __BANK
// Load custom settings path
if(customMappingsFile != NULL)
{
ControlInput::ControlSettings tmpSettings;
result = LoadSettingsFile(customMappingsFile, false, tmpSettings);
if(result)
{
LoadSettings(tmpSettings, ioSource::IOMD_DEFAULT);
}
}
if(!result)
#endif
{
if(rebuild)
{
//! reset handbrake control when we rebuild as otherwise they'll never be remapped unless we change profile option.
m_HandbrakeControl = -1;
ClearToggleRun();
// Clear previous settings.
m_GamepadCommonSettings.m_Mappings.Resize(0);
m_GamepadTpsBaseLayoutSettings.m_Mappings.Resize(0);
m_GamepadTpsSpecificLayoutSettings.m_Mappings.Resize(0);
m_GamepadAcceptCancelSettings.m_Mappings.Resize(0);
m_GamepadDuckBrakeSettings.m_Mappings.Resize(0);
ORBIS_ONLY(m_GamepadGestureSettings.m_Mappings.Resize(0));
#if FPS_MODE_SUPPORTED
m_GamepadFpsBaseLayoutSettings.m_Mappings.Resize(0);
m_GamepadFpsSpecificLayoutSettings.m_Mappings.Resize(0);
// Load FPS mode settings files.
eControlLayout fpsLayout = GetFpsLayout();
LoadSettingsFile(GetGamepadBaseLayoutFile(fpsLayout), false, m_GamepadFpsBaseLayoutSettings);
LoadSettingsFile(GetGamepadSpecificLayoutFile(fpsLayout), false, m_GamepadFpsSpecificLayoutSettings);
#endif // FPS_MODE_SUPPORTED
// Load TPS mode settings files.
eControlLayout tpsLayout = GetTpsLayout();
LoadSettingsFile(COMMON_LAYOUT_FILE, false, m_GamepadCommonSettings);
LoadSettingsFile(GetGamepadBaseLayoutFile(tpsLayout), false, m_GamepadTpsBaseLayoutSettings);
LoadSettingsFile(GetGamepadSpecificLayoutFile(tpsLayout), false, m_GamepadTpsSpecificLayoutSettings);
LoadSettingsFile(GetAcceptCancelLayoutFile(), false, m_GamepadAcceptCancelSettings);
ORBIS_ONLY(LoadSettingsFile(GESTURES_LAYOUT_FILE, false, m_GamepadGestureSettings));
}
const s32 handbrakeControl = CPauseMenu::GetMenuPreference(PREF_ALTERNATE_HANDBRAKE);
if(m_HandbrakeControl != handbrakeControl)
{
m_HandbrakeControl = handbrakeControl;
LoadSettingsFile(GetDuckHandbrakeLayoutFile(), false, m_GamepadDuckBrakeSettings);
}
// Load mappings from settings.
LoadSettings(m_GamepadCommonSettings, ioSource::IOMD_DEFAULT);
#if FPS_MODE_SUPPORTED
// TODO: Use pause menu setting once it is hooked up!
if(IsUsingFpsMode())
{
LoadSettings(m_GamepadFpsBaseLayoutSettings, ioSource::IOMD_DEFAULT);
LoadSettings(m_GamepadFpsSpecificLayoutSettings, ioSource::IOMD_DEFAULT);
}
else
#endif // FPS_MODE_SUPPORTED
{
LoadSettings(m_GamepadTpsBaseLayoutSettings, ioSource::IOMD_DEFAULT);
LoadSettings(m_GamepadTpsSpecificLayoutSettings, ioSource::IOMD_DEFAULT);
}
LoadSettings(m_GamepadAcceptCancelSettings, ioSource::IOMD_DEFAULT);
LoadSettings(m_GamepadDuckBrakeSettings, ioSource::IOMD_DEFAULT);
ORBIS_ONLY(LoadSettings(m_GamepadGestureSettings, ioSource::IOMD_DEFAULT));
ORBIS_ONLY(m_IsRemotePlayer = GetPad().IsRemotePlayPad());
}
ORBIS_ONLY(m_IsAcceptCrossSetting = CPauseMenu::GetMenuPreference(PREF_ACCEPT_IS_CROSS));
m_TpsControlLayout = GetTpsLayout();
FPS_MODE_SUPPORTED_ONLY(m_FpsControlLayout = GetFpsLayout());
if(rebuild)
{
LoadScriptedMappings();
}
// This actually loads the scripted controls.
LoadScriptedSettings(true, true);
#if RSG_PC
if(m_UseExtraDevices)
{
if(forceReload)
{
ms_settings.RebuildGamepadDefinitionList();
}
LoadDeviceControls(loadAutoSavedSettings);
}
#endif // RSG_PC
#if KEYBOARD_MOUSE_SUPPORT
#if __BANK
// in bank mode we can disable pc input to better work with rag.
if(!PARAM_noKeyboardMouseControls.Get())
#endif // __BANK
{
// We are using the pause menu preferences instead of the CProfileSettings as the profile
// settings do not appear to be updated when we are informed of a control change.
const bool axisInversion = (CPauseMenu::GetMenuPreference(PREF_INVERT_LOOK) != 0);
const bool mouseInversion = (CPauseMenu::GetMenuPreference(PREF_INVERT_MOUSE) != 0);
const bool mouseFlyingInversion = (CPauseMenu::GetMenuPreference(PREF_INVERT_MOUSE_FLYING) != 0);
const bool mouseSubInversion = (CPauseMenu::GetMenuPreference(PREF_INVERT_MOUSE_SUB) != 0);
const bool mouseFlyingXYSwap = (CPauseMenu::GetMenuPreference(PREF_SWAP_ROLL_YAW_MOUSE_FLYING) != 0);
// If the user signed in status has changed then we are using different settings files. Force a reload!.
bool isUserSignedIn = IsUserSignedIn();
if(isUserSignedIn != m_IsUserSignedIn)
{
m_IsUserSignedIn = isUserSignedIn;
forceReload = true;
}
u32 mouseSettings = 0u;
// Axis (gamepad) inversion is done on an input so if the input is assigned a value of 1.0f
// it gets inverted to -1.0f. To separate out mouse inversion from gamepad inversion, we map
// invert the value as it is assigned to the input. The result of this is that the two can
// conflict.
// i.e. inversing the input/gamepad will invert the mouse, if we want to invert the gamepad
// without the mouse we need to invert both so that the mouse's value get inverted then
// inverted back.
if((axisInversion && mouseInversion) || (axisInversion == false && mouseInversion == false))
{
mouseSettings |= MOUSE_INVERTED;
}
// Reload the mouse look settings if needed.
if( forceReload ||
(mouseSettings & MOUSE_INVERTED) != (m_MouseSettings & MOUSE_INVERTED) )
{
m_MouseLookSettings.m_Mappings.Resize(0);
const bool bLoadInvertedMouseSettings = (mouseSettings & MOUSE_INVERTED) == 0;
if(!bLoadInvertedMouseSettings)
{
LoadSettingsFile(PC_STANDARD_MOUSE_SETTINGS_FILE, false, m_MouseLookSettings);
}
else
{
LoadSettingsFile(PC_INVERTED_MOUSE_SETTINGS_FILE, false, m_MouseLookSettings);
}
}
// TODO: Hook these up when we have pause menu options.
if(m_MouseVehicleCarAutoCenter)
{
mouseSettings |= MOUSE_CAR_AUTO_CENTER;
}
// TODO: Hook these up when we have pause menu options.
if(m_MouseVehicleFlyAutoCenter)
{
mouseSettings |= MOUSE_FLY_AUTO_CENTER;
}
// TODO: Hook these up when we have pause menu options.
if(m_MouseVehicleSubAutoCenter)
{
mouseSettings |= MOUSE_SUB_AUTO_CENTER;
}
bool bMouseSteering = CControl::GetMouseSteeringMode(PREF_MOUSE_DRIVE) == eMSM_Vehicle;
bool bCameraMouseSteering = CControl::GetMouseSteeringMode(PREF_MOUSE_DRIVE) == eMSM_Camera;
if(( bMouseSteering && !m_DriveCameraToggleOn) || (bCameraMouseSteering && m_DriveCameraToggleOn))
{
mouseSettings |= MOUSE_VEHICLE_CAR;
}
// Reload the mouse driving settings if needed.
if( forceReload ||
(mouseSettings & MOUSE_VEHICLE_CAR) != (m_MouseSettings & MOUSE_VEHICLE_CAR) ||
(mouseSettings & MOUSE_CAR_AUTO_CENTER) != (m_MouseSettings & MOUSE_CAR_AUTO_CENTER) )
{
m_MouseCarSettings.m_Mappings.Resize(0);
// Load mouse car controls if required.
if((mouseSettings & MOUSE_VEHICLE_CAR) != 0)
{
if((mouseSettings & MOUSE_CAR_AUTO_CENTER) != 0)
{
LoadSettingsFile(PC_MOUSE_VEHICLE_CAR_SCALED_FILE, false, m_MouseCarSettings);
}
else
{
LoadSettingsFile(PC_MOUSE_VEHICLE_CAR_CENTERED_FILE, false, m_MouseCarSettings);
}
}
}
bool bMouseFlySteering = CControl::GetMouseSteeringMode(PREF_MOUSE_FLY) == CControl::eMSM_Vehicle;
bool bCameraMouseFlySteering = CControl::GetMouseSteeringMode(PREF_MOUSE_FLY) == CControl::eMSM_Camera;
if((bMouseFlySteering && !m_DriveCameraToggleOn) || (bCameraMouseFlySteering && m_DriveCameraToggleOn))
{
if(((axisInversion && mouseFlyingInversion) || (axisInversion == false && mouseFlyingInversion == false)) == 0)// Yes this is confusing but look at comment about mouse inversion above
{
mouseSettings |= MOUSE_VEHICLE_FLY_INVERTED;
}
else
{
mouseSettings |= MOUSE_VEHICLE_FLY;
}
if(mouseFlyingXYSwap)
{
mouseSettings |= MOUSE_FLY_SWAP_XY;
}
}
// Reload the mouse flying settings if needed.
if( forceReload ||
(mouseSettings & MOUSE_VEHICLE_FLY) != (m_MouseSettings & MOUSE_VEHICLE_FLY) ||
(mouseSettings & MOUSE_VEHICLE_FLY_INVERTED) != (m_MouseSettings & MOUSE_VEHICLE_FLY_INVERTED) ||
(mouseSettings & MOUSE_FLY_AUTO_CENTER) != (m_MouseSettings & MOUSE_FLY_AUTO_CENTER) ||
(mouseSettings & MOUSE_FLY_SWAP_XY) != (m_MouseSettings & MOUSE_FLY_SWAP_XY) )
{
m_MouseFlySettings.m_Mappings.Resize(0);
// Load mouse fly controls if required.
if((mouseSettings & MOUSE_VEHICLE_FLY) != 0 || (mouseSettings & MOUSE_VEHICLE_FLY_INVERTED) != 0)
{
//m_MouseFlySettings
if(mouseFlyingInversion)
{
if((mouseSettings & MOUSE_FLY_AUTO_CENTER) != 0)
{
if(mouseSettings & MOUSE_FLY_SWAP_XY)
{
LoadSettingsFile(PC_INVERTED_MOUSE_VEHICLE_FLY_SCALED_SWAPPED_FILE, false, m_MouseFlySettings);
}
else
{
LoadSettingsFile(PC_INVERTED_MOUSE_VEHICLE_FLY_SCALED_FILE, false, m_MouseFlySettings);
}
}
else
{
if(mouseSettings & MOUSE_FLY_SWAP_XY)
{
LoadSettingsFile(PC_INVERTED_MOUSE_VEHICLE_FLY_CENTERED_SWAPPED_FILE, false, m_MouseFlySettings);
}
else
{
LoadSettingsFile(PC_INVERTED_MOUSE_VEHICLE_FLY_CENTERED_FILE, false, m_MouseFlySettings);
}
}
}
else
{
if((mouseSettings & MOUSE_FLY_AUTO_CENTER) != 0)
{
if(mouseSettings & MOUSE_FLY_SWAP_XY)
{
LoadSettingsFile(PC_MOUSE_VEHICLE_FLY_SCALED_SWAPPED_FILE, false, m_MouseFlySettings);
}
else
{
LoadSettingsFile(PC_MOUSE_VEHICLE_FLY_SCALED_FILE, false, m_MouseFlySettings);
}
}
else
{
if(mouseSettings & MOUSE_FLY_SWAP_XY)
{
LoadSettingsFile(PC_MOUSE_VEHICLE_FLY_CENTERED_SWAPPED_FILE, false, m_MouseFlySettings);
}
else
{
LoadSettingsFile(PC_MOUSE_VEHICLE_FLY_CENTERED_FILE, false, m_MouseFlySettings);
}
}
}
}
}
bool bMouseSubSteering = CControl::GetMouseSteeringMode(PREF_MOUSE_SUB) == CControl::eMSM_Vehicle;
bool bCameraMouseSubSteering = CControl::GetMouseSteeringMode(PREF_MOUSE_SUB) == CControl::eMSM_Camera;
if((bMouseSubSteering && !m_DriveCameraToggleOn) || (bCameraMouseSubSteering && m_DriveCameraToggleOn))
{
if(((axisInversion && mouseSubInversion) || (axisInversion == false && mouseSubInversion == false)) == 0)// Yes this is confusing but look at comment about mouse inversion above
{
mouseSettings |= MOUSE_VEHICLE_SUB_INVERTED;
}
else
{
mouseSettings |= MOUSE_VEHICLE_SUB;
}
}
// Reload the mouse sub settings if needed.
if( forceReload ||
(mouseSettings & MOUSE_VEHICLE_SUB) != (m_MouseSettings & MOUSE_VEHICLE_SUB) ||
(mouseSettings & MOUSE_VEHICLE_SUB_INVERTED) != (m_MouseSettings & MOUSE_VEHICLE_SUB_INVERTED) )
{
m_MouseSubSettings.m_Mappings.Resize(0);
// Load mouse sub controls if required.
if((mouseSettings & MOUSE_VEHICLE_SUB) != 0 || (mouseSettings & MOUSE_VEHICLE_SUB_INVERTED) != 0)
{
if(mouseSubInversion)
{
if((mouseSettings & MOUSE_SUB_AUTO_CENTER) != 0)
{
LoadSettingsFile(PC_INVERTED_MOUSE_VEHICLE_SUB_SCALED_FILE, false, m_MouseSubSettings);
}
else
{
LoadSettingsFile(PC_INVERTED_MOUSE_VEHICLE_SUB_CENTERED_FILE, false, m_MouseSubSettings);
}
}
else
{
if((mouseSettings & MOUSE_SUB_AUTO_CENTER) != 0)
{
LoadSettingsFile(PC_MOUSE_VEHICLE_SUB_SCALED_FILE, false, m_MouseSubSettings);
}
else
{
LoadSettingsFile(PC_MOUSE_VEHICLE_SUB_CENTERED_FILE, false, m_MouseSubSettings);
}
}
}
}
// Reload the PC controls if required.
if(forceReload || !m_HasLoadedDefaultControls)
{
m_RecachedConflictCount = true;
// Clear previous settings.
m_UserKeyboardMouseSettings.m_Mappings.Resize(0);
#if RSG_PC && ALLOW_USER_CONTROLS
result = false;
char path[PATH_BUFFER_SIZE] = {0};
GetUserSettingsPath(path, PC_AUTOSAVE_USER_SETTINGS_FILE);
if(loadAutoSavedSettings && ASSET.Exists(path, ""))
{
// User file that they can edit so we read safe but slow.
result = LoadSettingsFile(path, true, m_UserKeyboardMouseSettings);
}
GetUserSettingsPath(path, PC_USER_SETTINGS_FILE);
if(!result && ASSET.Exists(path, ""))
{
// User file that they can edit so we read safe but slow.
LoadSettingsFile(path, true, m_UserKeyboardMouseSettings);
}
#endif // RSG_PC && ALLOW_USER_CONTROLS
}
// Load the default and fixed controls on first load only, assume these never change!
if(!m_HasLoadedDefaultControls)
{
m_HasLoadedDefaultControls = true;
m_DefaultKeyboardMouseSettings.m_Mappings.Resize(0);
m_KeyboardMouseFixedSettings.m_Mappings.Resize(0);
// load default controls instead of user controls.
LoadSettingsFile(PC_DEFAULT_SETTINGS_FILE, false, m_DefaultKeyboardMouseSettings);
// Load default FIXED settings.
LoadSettingsFile(PC_FIXED_SETTINGS_FILE, false, m_KeyboardMouseFixedSettings);
}
// Load mappings from settings.
LoadSettings(m_DefaultKeyboardMouseSettings, ioSource::IOMD_KEYBOARD_MOUSE);
ReplaceSettings(m_UserKeyboardMouseSettings, ioSource::IOMD_KEYBOARD_MOUSE);
AddKeyboardMouseExtraSettings(m_KeyboardMouseFixedSettings);
AddKeyboardMouseExtraSettings(m_MouseLookSettings);
AddKeyboardMouseExtraSettings(m_MouseCarSettings);
AddKeyboardMouseExtraSettings(m_MouseFlySettings);
AddKeyboardMouseExtraSettings(m_MouseSubSettings);
m_MouseSettings = mouseSettings;
// We need to reset the current scheme here as the controls have been trashed. If we don't, they will fail to
// load as it believes they are already loaded.
m_CurrentLoadedDynamicScheme = atHashString("", 0);
LoadDynamicControlMappings();
// TODO: Enable this once there all conflicts have been fixed otherwise we will get an assert on startup.
// BANK_ONLY(GetNumberOfMappingConflicts());
}
#endif // KEYBOARD_MOUSE_SUPPORT
// Map all unmapped inputs to a blank mapping so that the inputs get updated (this is to work around a limitation in ioMapper). We use
// A keyboard mapping of KEY_NULL for this as it means nothing.
for (u32 i = 0; i < MAX_INPUTS; ++i)
{
if(m_isInputMapped[i] == false)
{
m_Mappers[ms_settings.m_InputMappers[i]].Map(IOMS_KEYBOARD, KEY_NULL, m_inputs[i]);
m_isInputMapped[i] = true;
}
}
SetDefaultLookInversions();
}
void CControl::ClearInputMappings()
{
sysCriticalSection lock(m_Cs);
m_WasControlsRefreshedThisFrame = true;
// 1st set the hard coded defaults: (this must be done so we define what maps we use in Rage)...
for(u32 i = 0; i < MAPPERS_MAX; ++i)
{
m_Mappers[i].Reset();
}
for(u32 i = 0; i < MAX_INPUTS; ++i)
{
m_isInputMapped[i] = false;
}
for(u32 i = 0; i < ms_settings.m_HistorySupport.size(); ++i)
{
m_inputs[ms_settings.m_HistorySupport[i]].SupportHistory();
}
}
rage::ioSource CControl::GetInputSource( InputType input, s32 KEYBOARD_MOUSE_ONLY(device), u8 KEYBOARD_MOUSE_ONLY(mappingIndex), bool KEYBOARD_MOUSE_ONLY(allowFallback) ) const
{
sysCriticalSection lock(m_Cs);
ioMapper::ioSourceList sources;
GetInputSources(input, sources);
// TODO: Get the source that has the same device as the last input source.
if(sources.size() > 0)
{
#if KEYBOARD_MOUSE_SUPPORT
// We could check that the mapper for this input is valid but GetInputSources() does this and if there is a source the mapper must have been
// valid to extract them.
const ioMapper& mapper = m_Mappers[ms_settings.m_InputMappers[input]];
// We will default to PC mappings if we cannot find the source required.
ioSource alternativeSource = ioSource::UNKNOWN_SOURCE;
s32 lastDeviceIndex = m_LastKnownSource.m_DeviceIndex;
if(device != ioSource::IOMD_DEFAULT && device != ioSource::IOMD_ANY)
{
lastDeviceIndex = device;
}
// default to keyboard/mouse.
if(!ioSource::IsValidDevice(lastDeviceIndex))
{
lastDeviceIndex = ioSource::IOMD_KEYBOARD_MOUSE;
}
else if(lastDeviceIndex != ioSource::IOMD_KEYBOARD_MOUSE STEAM_CONTROLLER_ONLY(&& lastDeviceIndex != ioSource::IOMD_GAME))
{
lastDeviceIndex = ioSource::IOMD_DEFAULT;
}
u8 currentIndex = 0;
// Find the appropriate source.
for(s32 i = 0; i < sources.size(); ++i)
{
if(lastDeviceIndex == sources[i].m_DeviceIndex || (sources[i].m_DeviceIndex == ioSource::IOMD_DEFAULT && mapper.GetPlayer() == lastDeviceIndex))
{
// currentIndex can be greater than mappingIndex if the mapping was invalid and allowFallback was true and there was no previous valid mapping.
if(currentIndex >= mappingIndex)
{
// Never return KEY_NULL mapping.
if(sources[i].m_DeviceIndex != ioSource::IOMD_KEYBOARD_MOUSE || sources[i].m_Parameter != KEY_NULL)
{
return sources[i];
}
// If we should stop searching.
else if(allowFallback == false || ioSource::IsValidDevice(alternativeSource.m_DeviceIndex))
{
break;
}
}
++currentIndex;
}
// NOTE: This has been broken down as it was getting hard to read and needed updated for steam controller support.
// If the source's device is the device we want or it's the keyboard/mouse (and not KEY_NULL which means invalid), then we might be able to use it.
if((sources[i].m_DeviceIndex == lastDeviceIndex || sources[i].m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE) && sources[i].m_Parameter != KEY_NULL)
{
// If the alternate device is not valid then fall back to the new source.
if(!ioSource::IsValidDevice(alternativeSource.m_DeviceIndex))
{
alternativeSource = sources[i];
}
// If the alternate device is valid but not the device we would prefer and the source is the device we would prefer then fall back to that instead.
else if(alternativeSource.m_DeviceIndex != lastDeviceIndex && sources[i].m_DeviceIndex == lastDeviceIndex)
{
alternativeSource = sources[i];
}
}
}
ioSource result = ioSource::UNKNOWN_SOURCE;
if(allowFallback && ioSource::IsValidDevice(alternativeSource.m_DeviceIndex))
{
result = alternativeSource;
}
else if(lastDeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE)
{
result = ioSource(IOMS_KEYBOARD, KEY_NULL, ioSource::IOMD_KEYBOARD_MOUSE);
}
return result;
#else
// Never return KEY_NULL mapping.
if(sources[0].m_DeviceIndex != ioSource::IOMD_KEYBOARD_MOUSE || sources[0].m_Parameter != KEY_NULL)
{
return sources[0];
}
#endif // KEYBOARD_MOUSE_SUPPORT
}
return ioSource::UNKNOWN_SOURCE;
}
void CControl::GetInputSources( InputType input, ioMapper::ioSourceList& sources ) const
{
if(controlVerifyf(input < MAX_INPUTS && input != UNDEFINED_INPUT, "Invalid input with name '%s' and value %d used to retrieve input sources!", GetInputName(input), input))
{
STEAM_CONTROLLER_ONLY(GetSteamControllerInputSources(input, sources));
Mapper mapperId = ms_settings.m_InputMappers[input];
controlAssertf(mapperId != DEPRECATED_MAPPER, "%s is Deprecated so there can be no sources mapped to it!", GetInputName(input));
if(controlVerifyf(mapperId >= 0 && mapperId < MAPPERS_MAX, "%s has an invalid mapper assigned to it!", GetInputName(input)))
{
const ioMapper& mapper = m_Mappers[mapperId];
mapper.GetMappedSources(m_inputs[input], sources);
}
}
}
void CControl::GetSpecificInputSources( InputType input, ioMapper::ioSourceList& sources, s32 deviceId ) const
{
GetInputSources(input, sources);
s32 current = 0;
// remove all other sources from a different device.
for(s32 i = 0; i < sources.GetCount(); ++i)
{
// If we are not removing the element then copy it to its new location.
if(sources[i].m_DeviceIndex == deviceId)
{
if(current != i)
{
sources[current] = sources[i];
}
++current;
}
}
sources.SetCount(current);
}
//
// name: Update
// description: Get pad class for this control
void CControl::Update()
{
#if RSG_ORBIS
// If the user has changed from remote play to local play (or vise vera) then reload the controls for this CControl only.
if(GetPad().IsRemotePlayPad() != m_IsRemotePlayer)
{
SetInitialDefaultMappings(false);
}
#endif // RSG_ORBIS
sysCriticalSection lock(m_Cs);
#if KEYBOARD_MOUSE_SUPPORT
// Reset the controls once the user first logs in.
if(BANK_ONLY(!PARAM_noKeyboardMouseControls.Get() &&) !m_IsUserSignedIn && IsUserSignedIn())
{
SetInitialDefaultMappings(true);
}
#endif // KEYBOARD_MOUSE_SUPPORT
#if FPS_MODE_SUPPORTED
if(IsControlUpdateThread())
{
bool isUsingFpsMode = IsUsingFpsMode();
if(m_WasUsingFpsMode != isUsingFpsMode)
{
CControlMgr::ReInitControls();
}
m_WasUsingFpsMode = isUsingFpsMode;
}
#endif // FPS_MODE_SUPPORTED
if(m_WasControlsRefreshedLastFrame)
{
m_HasScriptCheckControlsRefresh = false;
m_WasControlsRefreshedLastFrame = false;
}
if(m_WasControlsRefreshedThisFrame && m_HasScriptCheckControlsRefresh)
{
m_WasControlsRefreshedThisFrame = false;
m_WasControlsRefreshedLastFrame = true;
}
KEYBOARD_MOUSE_ONLY(UpdateKeyBindings());
WIN32PC_ONLY(UpdateJoystickBindings());
LIGHT_EFFECTS_ONLY(UpdateLightEffects());
PF_PUSH_TIMEBAR("controls.Update.replaceScriptedControls");
// If script changes the controls then we reset them here after two frames (as normal). If this happens in the loading screen the controls are updated by
// the render thread. As the two are out of sync, this can cause disk thrashing. For this reason we only reset on the controls update thread!
if(IsControlUpdateThread())
{
if(m_useAlternateScriptedControlsNextFrame)
{
m_useAlternateScriptedControlsNextFrame = false;
}
else if(m_scriptedInputLayout != STANDARD_TPS_LAYOUT)
{
UseDefaultScriptedControlsLayout();
}
}
PF_POP_TIMEBAR();
PF_PUSH_TIMEBAR("controls.Update.inputMappers");
UpdateInputMappers();
PF_POP_TIMEBAR();
STEAM_CONTROLLER_ONLY(UpdateSteamController());
PF_PUSH_TIMEBAR("controls.Update.timedDisableInputs");
if(m_InputDisableEndTime > fwTimer::GetSystemTimeInMilliseconds())
{
DisableAllInputs(m_DisableOptions);
m_bInputsStillDisabled = true;
}
else
{
// Inputs only get disabled one frame after we flag them to be disabled, so the inputs will still be disabled here.
if (m_bInputsStillDisabled && m_InputDisableEndTime == 0)
{
m_bInputsStillDisabled = false;
}
m_InputDisableEndTime = 0;
m_DisableOptions = ioValue::DEFAULT_DISABLE_OPTIONS;
}
if(m_BackButtonEnableTime <= fwTimer::GetSystemTimeInMilliseconds())
{
m_BackButtonEnableTime = 0;
}
else
{
DisableBackButtonInputs();
}
PF_POP_TIMEBAR();
#if RSG_BANK || RSG_PC
ioValue::ReadOptions options = ioValue::DEFAULT_UNBOUND_OPTIONS;
options.SetFlags(ioValue::ReadOptions::F_READ_DISABLED, true);
#endif // RSG_BANK || RSG_PC
#if __BANK
PF_PUSH_TIMEBAR("controls.Update.bankDisableInputs");
// Disable inputs whilst they are disabled in rag.
for(u32 i = 0; i < MAX_INPUTS; ++i)
{
if(m_ragData[i].m_ForceDisabled)
{
m_inputs[i].Disable();
}
m_ragData[i].m_Enabled = m_inputs[i].IsEnabled();
m_ragData[i].m_InputValue = m_inputs[i].GetLastUnboundNorm(options);
}
PF_POP_TIMEBAR();
#endif // __BANK
m_pedCollectedPickupConsumed = false; // In the next frame the button hasn't been used yet.
// If script changes the controls then we reset them here after two frames (as normal). If this happens in the loading screen the controls are updated by
// the render thread. As the two are out of sync, this can cause disk thrashing. For this reason we only reset on the controls update thread!
if(IsControlUpdateThread())
{
PF_PUSH_TIMEBAR("controls.Update.RunAimToggle");
if(!CPlayerSpecialAbilityManager::HasSpecialAbilityDisabledSprint() && (m_inputs[INPUT_SPRINT].GetLastSource().m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE || CanUseToggleRun()))
{
if(CPlayerSpecialAbilityManager::CanActivateSprintToggle())
{
m_ToggleRunOn = !m_ToggleRunOn;
CPlayerSpecialAbilityManager::ResetActivateSprintToggle();
}
else
{
if(m_inputs[INPUT_SPRINT].IsReleased() && !m_inputs[INPUT_SPRINT].IsReleasedAfterHistoryHeldDown(KEYBOARD_START_SPRINT_HELD_TIME) )
{
m_ToggleRunOn = !m_ToggleRunOn;
}
}
}
PF_POP_TIMEBAR();
}
//Now work out if control has been touched
//and log the current time.
#if KEYBOARD_MOUSE_SUPPORT
PF_PUSH_TIMEBAR("controls.Update.pcToggles");
if( m_inputs[INPUT_VEH_PUSHBIKE_SPRINT].GetLastSource().m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE &&
m_inputs[INPUT_VEH_PUSHBIKE_SPRINT].IsReleased() &&
!m_inputs[INPUT_VEH_PUSHBIKE_SPRINT].IsReleasedAfterHistoryHeldDown(KEYBOARD_START_SPRINT_HELD_TIME) )
{
m_ToggleBikeSprintOn = !m_ToggleBikeSprintOn;
}
if(m_ToggleAim && CanUseToggleAim())
{
if(!m_ResetToggleAim && !m_ToggleAimOn && m_inputs[INPUT_AIM].IsReleased() && m_inputs[INPUT_AIM].GetLastSource().m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE)
{
m_ToggleAimOn = true;
}
// Use pressed to turn toggle aim off as AI states can turn this off if we don't. We would then turn it back on.
else if(m_ToggleAimOn && m_inputs[INPUT_AIM].IsPressed())
{
// Do not re-enable on the Release of this press.
m_ResetToggleAim = true;
// reset if using a controller.
m_ToggleAimOn = false;
}
}
else if(m_ToggleAimOn)
{
m_ToggleAimOn = false;
}
if(m_ResetToggleAim && m_inputs[INPUT_AIM].IsUp())
{
m_ResetToggleAim = false;
}
UpdateLastFrameDeviceIndex();
PF_POP_TIMEBAR();
enum PcInputType
{
OTHER,
KEYBOARD_MOUSE,
STEAM_CONTROLLER,
};
#if USE_STEAM_CONTROLLER
const static PcInputType priorityDevice = STEAM_CONTROLLER;
#else
const static PcInputType priorityDevice = KEYBOARD_MOUSE;
#endif // USE_STEAM_CONTROLLER
PcInputType foundType = OTHER;
#endif // KEYBOARD_MOUSE_SUPPORT
PF_PUSH_TIMEBAR("controls.Update.legacyInputOccurredEvent");
// Update the deadzone value to 75% when detecting a switch from keyboard/mouse to gamepad (url:bugstar:2214956).
KEYBOARD_MOUSE_ONLY(options.m_DeadZone = 0.75f);
for(u32 i=0;i<MAX_INPUTS;i++)
{
#if KEYBOARD_MOUSE_SUPPORT
// If the input is fired and the source is from a valid device. We check that the input is fired first as some inputs will
// the source as a gamepad if one is attached due to stick noise. GetNorm() checks this but applies deadzoning when appropriate.
if( foundType < priorityDevice && ioSource::IsValidDevice(m_inputs[i].GetSource().m_DeviceIndex) &&
m_inputs[i].GetUnboundNorm(options) &&
m_inputs[i].GetSource().m_Device != IOMS_MOUSE_CENTEREDAXIS &&
m_inputs[i].GetSource().m_Device != IOMS_MOUSE_ABSOLUTEAXIS &&
m_inputs[i].GetSource().m_Device != IOMS_MOUSE_NORMALIZED &&
m_inputs[i].GetSource().m_Parameter != KEY_GRAVE )
{
const ioSource& source = m_inputs[i].GetSource();
#if USE_STEAM_CONTROLLER
if(foundType < STEAM_CONTROLLER && source.m_DeviceIndex == ioSource::IOMD_GAME && source.m_Device == IOMS_GAME_CONTROLLED)
{
m_LastKnownSource = source;
foundType = STEAM_CONTROLLER;
}
else
#endif // USE_STEAM_CONTROLLER
// If this source is a keyboard/mouse then we stop looking as the keyboard/mouse overrides any other input source.
if(foundType < KEYBOARD_MOUSE && source.m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE)
{
m_LastKnownSource = source;
foundType = KEYBOARD_MOUSE;
}
else if(foundType == OTHER)
{
m_LastKnownSource = m_inputs[i].GetSource();
}
}
#endif // KEYBOARD_MOUSE_SUPPORT
switch(i)
{
case INPUT_LOOK_LR:
case INPUT_LOOK_UD:
case INPUT_VEH_MOVE_UD:
case INPUT_VEH_MOVE_LR:
case INPUT_VEH_GUN_LR:
case INPUT_VEH_GUN_UD:
case INPUT_SNIPER_ZOOM:
{
if(m_inputs[i].GetNorm() != 0.0f)
{
SetInputOccurred();
}
break;
}
default:
{
if( m_inputs[i].IsDown()
KEYBOARD_MOUSE_ONLY(&& m_inputs[i].GetSource().m_Device != IOMS_MOUSE_CENTEREDAXIS)
KEYBOARD_MOUSE_ONLY(&& m_inputs[i].GetSource().m_Device != IOMS_MOUSE_ABSOLUTEAXIS)
KEYBOARD_MOUSE_ONLY(&& m_inputs[i].GetSource().m_Device != IOMS_MOUSE_NORMALIZED) )
{
SetInputOccurred();
}
break;
}
}
}
PF_POP_TIMEBAR();
#if KEYBOARD_MOUSE_SUPPORT
if(m_LastFrameDeviceIndex != m_LastKnownSource.m_DeviceIndex)
{
StopPlayerPadShaking();
}
#endif // KEYBOARD_MOUSE_SUPPORT
}
#if LIGHT_EFFECTS_SUPPORT
void CControl::SetLightEffect( const ioLightEffect* effect, LightEffectType type )
{
sysCriticalSection lock(m_Cs);
if(effect != NULL)
{
m_LightEffects[type].m_Effect = effect;
m_LightEffects[type].m_StartTime = fwTimer::GetSystemTimeInMilliseconds();
}
}
void CControl::ClearLightEffect( const ioLightEffect* effect, LightEffectType type )
{
sysCriticalSection lock(m_Cs);
if(effect == m_LightEffects[type].m_Effect)
{
m_LightEffects[type].m_Effect = NULL;
m_LightEffects[type].m_StartTime = 0u;
}
}
void CControl::UpdateLightEffects()
{
if( m_LightEffectsEnabled )
{
const u32 timeNow = fwTimer::GetSystemTimeInMilliseconds();
for(s32 effectIndex = m_LightEffects.size() -1; effectIndex >= 0; --effectIndex)
{
if(m_LightEffects[effectIndex].m_Effect != NULL)
{
float progress = static_cast<float>(timeNow - m_LightEffects[effectIndex].m_StartTime) /
static_cast<float>(m_LightEffects[effectIndex].m_Effect->GetDuration());
if(progress > 1.0f)
{
// Cycle effect.
m_LightEffects[effectIndex].m_StartTime = timeNow;
}
progress = Clamp(progress, 0.0f, 1.0f);
for(s32 deviceIndex = 0; deviceIndex < m_ActiveLightDevices.size(); ++deviceIndex)
{
m_LightEffects[effectIndex].m_Effect->Update(progress, m_ActiveLightDevices[deviceIndex]);
}
return;
}
}
}
}
void CControl::ResetLightDeviceColor()
{
sysCriticalSection lock(m_Cs);
for(u32 i = 0; i < MAX_LIGHT_EFFECT_TYPES; ++i)
{
m_LightEffects[i].m_Effect = NULL;
m_LightEffects[i].m_StartTime = 0u;
}
SetLightEffectsToDefault();
}
void CControl::SetLightEffectEnabled(bool enabled)
{
sysCriticalSection lock(m_Cs);
m_LightEffectsEnabled = enabled;
if(enabled == false)
{
SetLightEffectsToDefault();
}
}
void CControl::SetLightEffectsToDefault()
{
for(u32 i = 0; i < m_ActiveLightDevices.size(); ++i)
{
m_ActiveLightDevices[i]->SetAllLightsToDefaultColor();
}
}
#if RSG_BANK
void CControl::UpdateRagLightEffect()
{
if(m_RagLightEffectEnabled)
{
m_RagLightEffect = ioConstantLightEffect(m_RagLightEffectColor);
m_LightEffects[RAG_LIGHT_EFFECT].m_Effect = &m_RagLightEffect;
m_LightEffects[RAG_LIGHT_EFFECT].m_StartTime = fwTimer::GetSystemTimeInMilliseconds();
}
else
{
m_LightEffects[RAG_LIGHT_EFFECT].m_Effect = NULL;
m_LightEffects[RAG_LIGHT_EFFECT].m_StartTime = 0u;
}
}
CControl::DebugLightDevice::DebugLightDevice()
: m_Color(0, 0, 0)
{
}
bool CControl::DebugLightDevice::IsValid() const
{
return true;
}
bool CControl::DebugLightDevice::HasLightSource(Source source) const
{
return source == ioLightDevice::MIDDLE;
}
void CControl::DebugLightDevice::SetLightColor(Source source, u8 red, u8 green, u8 blue)
{
if(source == ioLightDevice::MIDDLE)
{
m_Color = Color32(red, green, blue);
}
}
void CControl::DebugLightDevice::SetAllLightsToDefaultColor()
{
m_Color = Color32(0, 0, 0);
}
void CControl::DebugLightDevice::Update()
{
}
#endif // RSG_BANK
#endif // LIGHT_EFFECTS_SUPPORT
bool CControl::GetOrientation(Quaternion& ORBIS_ONLY(result))
{
#if RSG_ORBIS
CPad &pad = GetPad();
return pad.GetOrientation(result);
#else
return false;
#endif // RSG_ORBIS
}
void CControl::ResetOrientation()
{
#if RSG_ORBIS
CPad &pad = GetPad();
pad.ResetOrientation();
#endif // RSG_ORBIS
}
void CControl::SetInputOccurred()
{
ms_LastTimeTouched=fwTimer::GetTimeInMilliseconds();
}
void CControl::DisableAllInputs(u32 duration, const ioValue::DisableOptions& options)
{
controlDebugf2("All inputs disabled for %dms.", duration);
u32 time = duration + fwTimer::GetSystemTimeInMilliseconds();
if(time > m_InputDisableEndTime)
{
if(m_InputDisableEndTime == 0)
{
m_DisableOptions = options;
}
else if(m_DisableOptions.m_DisableFlags != options.m_DisableFlags)
{
controlWarningf("Two timed input disables have conflicting flag. Flags will be set if they are set in both!");
m_DisableOptions.Merge(options);
}
m_InputDisableEndTime = time;
}
DisableAllInputs(m_DisableOptions);
}
void CControl::DisableAllInputs(const ioValue::DisableOptions& options)
{
controlDebugf2("All inputs disabled.");
for(int i = 0; i < MAX_INPUTS; ++i)
{
m_inputs[i].Disable(options);
}
}
void CControl::SetPedWalkInverted(bool invert)
{
m_inputs[INPUT_MOVE_LR].SetInverted(invert);
m_inputs[INPUT_MOVE_UD].SetInverted(invert);
}
void CControl::SetPedLookInverted(bool invert)
{
m_inputs[INPUT_LOOK_LR].SetInverted(invert);
m_inputs[INPUT_LOOK_UD].SetInverted(invert);
}
void CControl::SetInputExclusive(int action, const ioValue::DisableOptions& options)
{
controlDebugf2("Input %s (%d) set exlusive.", GetInputName(static_cast<InputType>(action)), action);
DebugOutputScriptCallStack();
ioMapper::ioSourceList sources;
GetInputSources(static_cast<InputType>(action), sources);
DisableInputsByActiveSources(sources, options);
InputType firstDependent;
InputType secondDependent;
// Also disable related inputs.
for(int i = 0; i < ms_settings.m_AxisDefinitions.size(); ++i)
{
firstDependent = UNDEFINED_INPUT;
secondDependent = UNDEFINED_INPUT;
const ControlInput::AxisDefinition& definition = ms_settings.m_AxisDefinitions[i];
if(definition.m_Input == action)
{
firstDependent = definition.m_Positive;
secondDependent = definition.m_Negative;
}
else if(definition.m_Positive == action)
{
firstDependent = definition.m_Input;
secondDependent = definition.m_Negative;
}
else if(definition.m_Negative == action)
{
firstDependent = definition.m_Input;
secondDependent = definition.m_Positive;
}
if(firstDependent != UNDEFINED_INPUT)
{
ioMapper::ioSourceList dependents;
GetInputSources(firstDependent, dependents);
DisableInputsByActiveSources(dependents, options);
}
if(secondDependent != UNDEFINED_INPUT)
{
ioMapper::ioSourceList dependents;
GetInputSources(secondDependent, dependents);
DisableInputsByActiveSources(dependents, options);
}
}
// we use this function rather than setting the input to enabled directly as it has some rag override checks.
EnableInput(action);
}
void CControl::DisableInputsByActiveSources(const ioMapper::ioSourceList& sources, const ioValue::DisableOptions& options)
{
ioMapper::ioSourceList linkedSources;
for(u32 sourceIndex = 0; sourceIndex < sources.GetCount(); ++sourceIndex)
{
#if KEYBOARD_MOUSE_SUPPORT
// If we are using the keyboard/mouse and this source is a keyboard/mouse or we are not using the keyboard and
// mouse and the source is not a keyboard/mouse.
if( (WasKeyboardMouseLastKnownSource() && sources[sourceIndex].m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE) ||
(WasKeyboardMouseLastKnownSource() == false && sources[sourceIndex].m_DeviceIndex != ioSource::IOMD_KEYBOARD_MOUSE) )
#endif // KEYBOARD_MOUSE_SUPPORT
{
DisableInputsBySource(sources[sourceIndex], options);
ioMapper::GetRelatedSources(sources[sourceIndex], linkedSources);
for(u32 linkedIndex = 0; linkedIndex < linkedSources.size(); ++linkedIndex)
{
DisableInputsBySource(linkedSources[linkedIndex], options);
}
linkedSources.clear();
}
}
}
void CControl::DisableInputsBySource(const ioSource& source, const ioValue::DisableOptions& options)
{
ioMapper::ioValueList values;
for(u32 mapperIndex = 0; mapperIndex < MAPPERS_MAX; ++mapperIndex)
{
// Ignore invalid key.
if(source.m_DeviceIndex != ioSource::IOMD_KEYBOARD_MOUSE || source.m_Parameter != KEY_NULL)
{
m_Mappers[mapperIndex].GetMappedValues(source, values);
for(u32 valueIndex = 0; valueIndex < values.GetCount(); ++valueIndex)
{
#if __DEV
// The id is only stored in the value in dev builds
controlDebugf2("%s (%d) disabled.", GetInputName(static_cast<InputType>(values[valueIndex]->GetId())), values[valueIndex]->GetId());
#endif // __DEV
DoDisableInput(static_cast<unsigned>(values[valueIndex] - m_inputs), options, false);
}
values.clear();
}
}
}
bool CControl::IsInputDisabled(const ioValue &value) const
{
return !value.IsEnabled();
}
int CControl::InputHowLongAgo() const
{
return (fwTimer::GetTimeInMilliseconds() - ms_LastTimeTouched);
}
void CControl::UpdateInputMappers()
{
// ALWAYS update the mappers as bad stuff happens, stead, disable inputs rather than stopping them updated otherwise they get stuck with their last value which
// Can break things (particularly the detection of which device the player is using!). This fixes url:bugstar:2199263 and is the cause of url:bugstar:2193623 which
// we fixed separately.
for(u32 i = 0; i < MAPPERS_MAX; ++i)
{
m_Mappers[i].Update(fwTimer::GetSystemTimeInMilliseconds(), false, fwTimer::GetTimeStep());
}
const bool disableInputs = CPauseMenu::IsActive( PM_WaitUntilFullyFadedInOnly ) REPLAY_ONLY(|| CReplayMgr::IsEditModeActive() || CVideoEditorInterface::IsActive());
if( disableInputs REPLAY_ONLY(&& !CReplayMgr::IsEditingCamera()) )
{
ioValue::DisableOptions options(ioValue::DisableOptions::F_ALLOW_SUSTAINED);
// disable all non-frontend inputs.
for(u32 i = 0; i < MAX_INPUTS; ++i)
{
if (ms_settings.m_InputMappers[i] != FRONTEND REPLAY_ONLY(&& ms_settings.m_InputMappers[i] != REPLAY))
{
m_inputs[i].Disable(options);
}
}
}
if(CLiveManager::IsInitialized() == false || CLiveManager::IsSystemUiShowing() == false)
{
UpdateInputFromSystemEvent(INPUT_FRONTEND_CANCEL, m_SystemFrontendBackOccurredTime);
UpdateInputFromSystemEvent(INPUT_CELLPHONE_CANCEL, m_SystemPhoneBackOccurredTime);
UpdateInputFromSystemEvent(INPUT_FRONTEND_PAUSE, m_SystemPlayPauseOccurredTime);
// NOTE: We treat the game and frontend inputs seperately when the 'view' event occurs. This is because the
// inputs can be disabled indipendently.
UpdateInputFromSystemEvent(INPUT_FRONTEND_SELECT, m_SystemFrontendViewOccuredTime);
UpdateInputFromSystemEvent(INPUT_NEXT_CAMERA, m_SystemGameViewOccuredTime);
}
}
void CControl::UpdateInputFromSystemEvent(InputType input, u32& timer)
{
if(timer > 0u)
{
u32 now = fwTimer::GetSystemTimeInMilliseconds();
// Give up if the input is disabled and its been too long. If we don't do this the input could be disabled for ages (e.g. a cutscene)
// and then suddenly we would simulate a button press a good while after the system told us to. We fire the input when we give up
// as some menu's script disable the inputs and read the disabled input value (fixes url:bugstar:1905198).
if(m_inputs[input].IsEnabled() || (timer + SYSTEM_EVENT_TIMOUT_DURATION) <= now)
{
m_inputs[input].SetCurrentValue(
ioValue::MAX_AXIS,
ioSource(IOMS_UNDEFINED, ioSource::UNDEFINED_PARAMETER, ioSource::IOMD_SYSTEM) );
timer = 0u;
}
}
}
//
// name: GetPad
// description: Get pad class for this control
CPad& CControl::GetPad()
{
return CControlMgr::GetPad(m_padNum);
}
//
// name: InitEmptyControls
// description: Init this CControl as if no input had occured.
void CControl::InitEmptyControls()
{
}
void CControl::EnableAllInputs()
{
controlDebugf2("All inputs enabled.");
DebugOutputScriptCallStack();
for(int i = 0; i < MAX_INPUTS; ++i)
{
#if __BANK
if(!m_ragData[i].m_ForceDisabled)
{
#endif // __BANK
m_inputs[i].Enable();
#if __BANK
}
#endif // __BANK
}
}
void CControl::DisableInputsOnBackButton(u32 duration /*= 2000*/)
{
m_BackButtonEnableTime = fwTimer::GetSystemTimeInMilliseconds() + duration;
DisableBackButtonInputs();
}
void CControl::DisableInput(ioMapperSource source, unsigned param, const ioValue::DisableOptions& UNUSED_PARAM(options))
{
controlDebugf2("Disabling all inputs with source %d and param %d.", source, param);
DebugOutputScriptCallStack();
ioMapper* pCurrMapper = NULL;
// ioMapper::Map adjusts the parameter depending on the pad override
param |= (static_cast<unsigned>(PAD_OVERRIDE) << 24);
for (u32 mapper=0; mapper< MAPPERS_MAX; mapper++)
{
pCurrMapper = &m_Mappers[mapper];
if (pCurrMapper)
{
for (u32 i=0; i<pCurrMapper->GetCount(); i++)
{
if (pCurrMapper->GetMapperSource(i) == source && pCurrMapper->GetParameter(i) == param)
{
ioValue* value = pCurrMapper->GetValue(i);
if (AssertVerify(value))
{
for (u32 j=0; j<MAX_INPUTS; j++)
{
if (&m_inputs[j] == value)
{
//m_inputs[j].Disable(options);
}
}
}
}
}
}
}
}
void CControl::DoDisableInput(unsigned input, const ioValue::DisableOptions& options, bool disableRelatedInputs)
{
m_inputs[input].Disable(options);
if(disableRelatedInputs)
{
controlDebugf2("Disabling related inputs to %s.", GetInputName(static_cast<InputType>(input)));
// Check if the input has related inputs.
for(u32 groupIndex = 0; groupIndex < ms_settings.m_RelatedInputs.size(); ++groupIndex)
{
const ControlInput::RelatedInputs& relatedInputs = ms_settings.m_RelatedInputs[groupIndex];
if(relatedInputs.m_Inputs.Find(static_cast<InputType>(input)) != -1)
{
for (u32 inputIndex = 0; inputIndex < relatedInputs.m_Inputs.size(); ++inputIndex)
{
InputType relatedInput = relatedInputs.m_Inputs[inputIndex];
controlDebugf2( "%s has been disabled as it is related to %s.",
GetInputName(relatedInput),
GetInputName(static_cast<InputType>(input)) );
m_inputs[relatedInput].Disable(options);
}
}
}
}
}
void CControl::DoEnableInput(unsigned input, bool enableRelatedInputs)
{
#if __BANK
if(!m_ragData[input].m_ForceDisabled)
#endif // __BANK
{
m_inputs[input].Enable();
}
if(enableRelatedInputs)
{
controlDebugf2("Enabling related inputs to %s.", GetInputName(static_cast<InputType>(input)));
// Check if the input has related inputs.
for(u32 groupIndex = 0; groupIndex < ms_settings.m_RelatedInputs.size(); ++groupIndex)
{
const ControlInput::RelatedInputs& relatedInputs = ms_settings.m_RelatedInputs[groupIndex];
if(relatedInputs.m_Inputs.Find(static_cast<InputType>(input)) != -1)
{
for (u32 inputIndex = 0; inputIndex < relatedInputs.m_Inputs.size(); ++inputIndex)
{
InputType relatedInput = relatedInputs.m_Inputs[inputIndex];
#if __BANK
if(!m_ragData[relatedInput].m_ForceDisabled)
#endif // __BANK
{
controlDebugf2("%s has been enabled as it is related to %s.", GetInputName(relatedInput), GetInputName(static_cast<InputType>(input)));
m_inputs[relatedInput].Enable();
}
}
}
}
}
}
bool CControl::IsInputDisabled(unsigned inputIndex) const
{
if(controlVerifyf(inputIndex < MAX_INPUTS, "Invalid input index!"))
return !m_inputs[inputIndex].IsEnabled();
else
return true; // input does not exist so act as if it is disabled.
}
const ioValue& CControl::GetValue( s32 index ) const
{
switch(index)
{
case INPUT_MOVE_LEFT:
case INPUT_MOVE_RIGHT:
return m_inputs[INPUT_MOVE_LR];
case INPUT_MOVE_UP:
case INPUT_MOVE_DOWN:
return m_inputs[INPUT_MOVE_UD];
case INPUT_LOOK_LEFT:
case INPUT_LOOK_RIGHT:
return m_inputs[INPUT_LOOK_LR];
case INPUT_LOOK_UP:
case INPUT_LOOK_DOWN:
return m_inputs[INPUT_LOOK_UD];
case INPUT_SNIPER_ZOOM_IN:
case INPUT_SNIPER_ZOOM_OUT:
case INPUT_SNIPER_ZOOM_IN_ALTERNATE:
case INPUT_SNIPER_ZOOM_OUT_ALTERNATE:
return m_inputs[INPUT_SNIPER_ZOOM];
case INPUT_VEH_MOVE_LEFT:
case INPUT_VEH_MOVE_RIGHT:
return m_inputs[INPUT_VEH_MOVE_LR];
case INPUT_VEH_MOVE_UP:
case INPUT_VEH_MOVE_DOWN:
return m_inputs[INPUT_VEH_MOVE_UD];
case INPUT_VEH_GUN_LEFT:
case INPUT_VEH_GUN_RIGHT:
return m_inputs[INPUT_VEH_GUN_LR];
case INPUT_VEH_GUN_UP:
case INPUT_VEH_GUN_DOWN:
return m_inputs[INPUT_VEH_GUN_LR];
// I'm certain these are not used but they are here just in case.
case INPUT_VEH_LOOK_LEFT:
case INPUT_VEH_LOOK_RIGHT:
return m_inputs[INPUT_LOOK_LR];
case INPUT_MELEE_ATTACK1:
return m_inputs[INPUT_MELEE_ATTACK_LIGHT];
case INPUT_MELEE_ATTACK2:
return m_inputs[INPUT_MELEE_ATTACK_HEAVY];
case INPUT_NEXT_WEAPON:
return m_inputs[INPUT_WEAPON_WHEEL_NEXT];
case INPUT_PREV_WEAPON:
return m_inputs[INPUT_WEAPON_WHEEL_PREV];
default:
return m_inputs[index];
}
}
void CControl::SetWeaponSelectExclusive()
{
SetInputExclusive(INPUT_WEAPON_WHEEL_NEXT);
SetInputExclusive(INPUT_WEAPON_WHEEL_PREV);
SetInputExclusive(INPUT_WEAPON_WHEEL_UD);
SetInputExclusive(INPUT_WEAPON_WHEEL_LR);
// On PC NEXT and PREV use the mouse wheel which means that they will disable each other (making INPUT_WEAPON_WHEEL_NEXT) disabled.
EnableInput(INPUT_WEAPON_WHEEL_NEXT);
}
void CControl::SetInputValueNextFrame(InputType input, float value)
{
if(controlVerifyf(input >= 0 && input < MAX_INPUTS, "Invalid Input %d", input))
{
m_inputs[input].SetNextValue(value, ioSource(IOMS_UNDEFINED, ioSource::UNDEFINED_PARAMETER, ioSource::IOMD_SYSTEM));
}
}
void CControl::SetInputValueNextFrame(InputType input, float value, ioSource source)
{
if(controlVerifyf(input >= 0 && input < MAX_INPUTS, "Invalid Input %d", input))
{
m_inputs[input].SetNextValue(value, source);
}
}
void CControl::ShakeController( int duration, int frequency, bool IsScriptCommand, ioRumbleEffect *pRumbleEffect)
{
if(m_ShakeSupressId != NO_SUPPRESS)
{
return;
}
#if KEYBOARD_MOUSE_SUPPORT
if(m_LastKnownSource.m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE || !ioSource::IsValidDevice(m_LastKnownSource.m_DeviceIndex))
{
return;
}
s32 deviceId = (m_LastKnownSource.m_DeviceIndex == ioSource::IOMD_DEFAULT) ? m_padNum : m_LastKnownSource.m_DeviceIndex;
#if __WIN32PC
// if a joystick vibrate it.
if(GetMappingType(m_LastKnownSource.m_Device) == JOYSTICK)
{
ioJoystick::ShakeDevice(deviceId, frequency, 0, duration);
return;
}
#endif // __WIN32PC
CPad &pad = CControlMgr::GetPad(deviceId);
#else
CPad &pad = GetPad();
#endif // KEYBOARD_MOUSE_SUPPORT
#if USE_ACTUATOR_EFFECTS
if (pRumbleEffect)
{
pad.ApplyActuatorEffect(pRumbleEffect, false);
}
else
{
m_Rumble = ioRumbleEffect(duration, static_cast<float>(frequency) / MAX_VIBRATION_FREQUENCY);
pad.ApplyActuatorEffect(&m_Rumble, IsScriptCommand);
}
#else
pad.AllowShake();
pad.SetScriptShake(IsScriptCommand);
pad.StartShake(duration, frequency);
pad.SetScriptShake(false);
#endif // USE_ACTUATOR_EFFECTS
}
void CControl::StartPlayerPadShake_Distance( u32 Duration, s32 Frequency, float X, float Y, float Z )
{
if(m_ShakeSupressId != NO_SUPPRESS)
{
return;
}
#if KEYBOARD_MOUSE_SUPPORT
if(m_LastKnownSource.m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE || !ioSource::IsValidDevice(m_LastKnownSource.m_DeviceIndex))
{
return;
}
s32 deviceId = (m_LastKnownSource.m_DeviceIndex == ioSource::IOMD_DEFAULT) ? m_padNum : m_LastKnownSource.m_DeviceIndex;
#if __WIN32PC
// if a joystick vibrate it.
if(GetMappingType(m_LastKnownSource.m_Device) == JOYSTICK)
{
ioJoystick::ShakeDevice(deviceId, static_cast<s32>(X), static_cast<s32>(Y), Duration);
return;
}
#endif // __WIN32PC
CPad &pad = CControlMgr::GetPad(deviceId);
#else
CPad &pad = GetPad();
#endif // KEYBOARD_MOUSE_SUPPORT
#if USE_ACTUATOR_EFFECTS
// NOTE: This code was taken from the CPad::StartShake_Distance to work create a compatible rumble effect.
Vector3 pos(X,Y,Z);
float Distance2 = (camInterface::GetPos() - pos).Mag2();
if (Distance2 < (70.0f * 70.0f) ) // For now simple on/off. Might get more sophisticated
{
m_Rumble = ioRumbleEffect(Duration, 0.0f, static_cast<float>(Frequency) / MAX_VIBRATION_FREQUENCY);
pad.ApplyActuatorEffect(&m_Rumble);
}
#else
pad.StartShake_Distance(Duration, Frequency, X, Y, Z);
#endif // USE_ACTUATOR_EFFECTS
}
void CControl::StartShaking( s32 MotorFrequency0, s32 MotorFrequency1 )
{
if(m_ShakeSupressId != NO_SUPPRESS)
{
return;
}
#if KEYBOARD_MOUSE_SUPPORT
if(m_LastKnownSource.m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE || !ioSource::IsValidDevice(m_LastKnownSource.m_DeviceIndex))
{
return;
}
s32 deviceId = (m_LastKnownSource.m_DeviceIndex == ioSource::IOMD_DEFAULT) ? m_padNum : m_LastKnownSource.m_DeviceIndex;
#if __WIN32PC
// if a joystick vibrate it.
if(GetMappingType(m_LastKnownSource.m_Device) == JOYSTICK)
{
ioJoystick::ShakeDevice(deviceId, MotorFrequency0, MotorFrequency1, ioJoystick::IOJ_INFINITE);
return;
}
#endif // __WIN32PC
CPad &pad = CControlMgr::GetPad(deviceId);
#else
CPad &pad = GetPad();
#endif // KEYBOARD_MOUSE_SUPPORT
#if USE_ACTUATOR_EFFECTS
// MOTORS: MotorFrequency0 is Heavy, MotorFrequency1 is Light
// Pad system expects (Heavy, Light) due to legacy DualShock setup
// Actuator system expects (Light, Heavy) so make sure values are switched (or fix actuator system at some point!)
m_Rumble = ioRumbleEffect(1, static_cast<float>(MotorFrequency1) / MAX_VIBRATION_FREQUENCY, static_cast<float>(MotorFrequency0) / MAX_VIBRATION_FREQUENCY);
pad.ApplyActuatorEffect(&m_Rumble);
#else
pad.StartShake(MotorFrequency0, MotorFrequency1);
#endif // USE_ACTUATOR_EFFECTS
}
void CControl::StartPlayerPadShake(u32 Duration0, s32 MotorFrequency0, u32 Duration1, s32 MotorFrequency1, s32 DelayAfterThisOne)
{
if(m_ShakeSupressId != NO_SUPPRESS)
{
return;
}
#if KEYBOARD_MOUSE_SUPPORT
if(m_LastKnownSource.m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE || !ioSource::IsValidDevice(m_LastKnownSource.m_DeviceIndex))
{
return;
}
s32 deviceId = (m_LastKnownSource.m_DeviceIndex == ioSource::IOMD_DEFAULT) ? m_padNum : m_LastKnownSource.m_DeviceIndex;
#if __WIN32PC
// if a joystick vibrate it.
if(GetMappingType(m_LastKnownSource.m_Device) == JOYSTICK)
{
ioJoystick::ShakeDevice(deviceId, MotorFrequency0, MotorFrequency1, Duration0);
return;
}
#endif // __WIN32PC
CPad &pad = CControlMgr::GetPad(deviceId);
#else
CPad &pad = GetPad();
#endif // KEYBOARD_MOUSE_SUPPORT
#if USE_ACTUATOR_EFFECTS
u32 duration = Max(Duration0, Duration1);
// MOTORS: MotorFrequency0 is Heavy, MotorFrequency1 is Light
// Pad system expects (Heavy, Light) due to legacy DualShock setup
// Actuator system expects (Light, Heavy) so make sure values are switched (or fix actuator system at some point!)
m_Rumble = ioRumbleEffect(duration, static_cast<float>(MotorFrequency1) /MAX_VIBRATION_FREQUENCY, static_cast<float>(MotorFrequency0) / MAX_VIBRATION_FREQUENCY);
pad.ApplyActuatorEffect(&m_Rumble, false, CPad::NO_SUPPRESS, DelayAfterThisOne);
#else
pad.StartShake(Duration0, MotorFrequency0, Duration1, MotorFrequency1, DelayAfterThisOne);
#endif // USE_ACTUATOR_EFFECTS
}
#if HAS_TRIGGER_RUMBLE
void CControl::StartTriggerShake( u32 durationLeft, u32 freqLeft, u32 durationRight, u32 freqRight, s32 DelayAfterThisOne, bool isScriptedCommand )
{
if(m_ShakeSupressId != NO_SUPPRESS)
{
return;
}
CPad &pad = GetPad();
u32 duration = Max( durationLeft, durationRight );
m_TriggerRumble = ioTriggerRumbleEffect( duration, static_cast<float>( freqLeft ) / MAX_VIBRATION_FREQUENCY, static_cast<float>( freqRight ) / MAX_VIBRATION_FREQUENCY );
pad.ApplyActuatorEffect( &m_TriggerRumble, isScriptedCommand, CPad::NO_SUPPRESS, DelayAfterThisOne );
}
#endif // #if HAS_TRIGGER_RUMBLE
void CControl::ApplyRecoilEffect( u32 duration, float intensity, float triggerIntensity )
{
if(m_ShakeSupressId != NO_SUPPRESS)
{
return;
}
#if USE_ACTUATOR_EFFECTS
#if KEYBOARD_MOUSE_SUPPORT
if(m_LastKnownSource.m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE || !ioSource::IsValidDevice(m_LastKnownSource.m_DeviceIndex))
{
return;
}
s32 deviceId = (m_LastKnownSource.m_DeviceIndex == ioSource::IOMD_DEFAULT) ? m_padNum : m_LastKnownSource.m_DeviceIndex;
CPad &pad = CControlMgr::GetPad(deviceId);
#else
CPad &pad = GetPad();
#endif // KEYBOARD_MOUSE_SUPPORT
//Pick trigger to rumble based on input mappings
fwRecoilEffect::Trigger triggerToRumble = fwRecoilEffect::NONE;
if (triggerIntensity > 0)
{
const ioSource &controlSource = GetPedAttack().GetSource();
if (controlSource.m_Device == IOMS_PAD_ANALOGBUTTON || controlSource.m_Device == IOMS_PAD_DIGITALBUTTON)
{
ioMapperParameter controlParam = ioMapper::ConvertParameterToMapperValue(controlSource.m_Device, controlSource.m_Parameter);
if (controlParam == R2_INDEX || controlParam == R2)
triggerToRumble = fwRecoilEffect::RIGHT;
else if (controlParam == L2_INDEX || controlParam == L2)
triggerToRumble = fwRecoilEffect::LEFT;
}
}
m_Recoil = fwRecoilEffect(duration, triggerToRumble, intensity, triggerIntensity);
pad.ApplyActuatorEffect(&m_Recoil);
#else
ShakeController(duration, static_cast<u32>(intensity * MAX_VIBRATION_FREQUENCY), false);
#endif // USE_ACTUATOR_EFFECTS
}
void CControl::StopPlayerPadShaking(bool bForce)
{
#if __WIN32PC
ioJoystick::StopAllForces();
#endif // __WIN32PC
GetPad().StopShaking(bForce);
#if USE_ACTUATOR_EFFECTS
m_Recoil = fwRecoilEffect();
m_Rumble = ioRumbleEffect();
#endif // USE_ACTUATOR_EFFECTS
}
void CControl::AllowPlayerPadShaking()
{
GetPad().AllowShake();
}
bool CControl::IsPlayerPadShaking()
{
return GetPad().IsShaking();
}
eControlLayout CControl::ConvertToScriptedLayout(eControlLayout layout) const
{
switch(layout)
{
case STANDARD_TPS_LAYOUT:
case STANDARD_FPS_LAYOUT:
case STANDARD_FPS_ALTERNATE_LAYOUT:
return STANDARD_TPS_LAYOUT;
case TRIGGER_SWAP_TPS_LAYOUT:
case TRIGGER_SWAP_FPS_LAYOUT:
case TRIGGER_SWAP_FPS_ALTERNATE_LAYOUT:
return TRIGGER_SWAP_TPS_LAYOUT;
case SOUTHPAW_TPS_LAYOUT:
case SOUTHPAW_FPS_LAYOUT:
case SOUTHPAW_FPS_ALTERNATE_LAYOUT:
return SOUTHPAW_TPS_LAYOUT;
case SOUTHPAW_TRIGGER_SWAP_TPS_LAYOUT:
case SOUTHPAW_TRIGGER_SWAP_FPS_LAYOUT:
case SOUTHPAW_TRIGGER_SWAP_FPS_ALTERNATE_LAYOUT:
return SOUTHPAW_TRIGGER_SWAP_TPS_LAYOUT;
default:
controlAssertf(false, "Unknown third person shooter gamepad configuration %d, using default!", layout);
return STANDARD_TPS_LAYOUT;
}
}
const char* CControl::GetScriptedInputLayoutFile(eControlLayout layout)
{
// Control Layouts
switch(ConvertToScriptedLayout(layout))
{
case STANDARD_TPS_LAYOUT:
return STANDARD_SCRIPT_LAYOUT_FILE;
case TRIGGER_SWAP_TPS_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return STANDARD_SCRIPT_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return TRIGGER_SWAP_SCRIPT_LAYOUT_FILE;
}
case SOUTHPAW_TPS_LAYOUT:
return SOUTHPAW_SCRIPT_LAYOUT_FILE;
case SOUTHPAW_TRIGGER_SWAP_TPS_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return SOUTHPAW_SCRIPT_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return SOUTHPAW_TRIGGER_SWAP_SCRIPT_LAYOUT_FILE;
}
default:
controlAssertf(false, "Unknown scripted third person gamepad configuration %d, using default!", layout);
return STANDARD_SCRIPT_LAYOUT_FILE;
}
}
const char* CControl::GetGamepadBaseLayoutFile(eControlLayout layout)
{
// Control Layouts
switch(layout)
{
case STANDARD_TPS_LAYOUT:
case STANDARD_FPS_LAYOUT:
case STANDARD_FPS_ALTERNATE_LAYOUT:
return STANDARD_LAYOUT_FILE;
case TRIGGER_SWAP_TPS_LAYOUT:
case TRIGGER_SWAP_FPS_LAYOUT:
case TRIGGER_SWAP_FPS_ALTERNATE_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return STANDARD_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return TRIGGER_SWAP_LAYOUT_FILE;
}
case SOUTHPAW_TPS_LAYOUT:
case SOUTHPAW_FPS_LAYOUT:
case SOUTHPAW_FPS_ALTERNATE_LAYOUT:
return SOUTHPAW_LAYOUT_FILE;
case SOUTHPAW_TRIGGER_SWAP_TPS_LAYOUT:
case SOUTHPAW_TRIGGER_SWAP_FPS_LAYOUT:
case SOUTHPAW_TRIGGER_SWAP_FPS_ALTERNATE_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return SOUTHPAW_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return SOUTHPAW_TRIGGER_SWAP_LAYOUT_FILE;
}
default:
controlAssertf(false, "Unknown gamepad third person configuration %d, using default!", layout);
return STANDARD_LAYOUT_FILE;
}
}
const char* CControl::GetGamepadSpecificLayoutFile(eControlLayout layout)
{
// Control Layouts
switch(layout)
{
case STANDARD_TPS_LAYOUT:
return STANDARD_TPS_LAYOUT_FILE;
case TRIGGER_SWAP_TPS_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return STANDARD_TPS_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return TRIGGER_SWAP_TPS_LAYOUT_FILE;
}
case SOUTHPAW_TPS_LAYOUT:
return SOUTHPAW_TPS_LAYOUT_FILE;
case SOUTHPAW_TRIGGER_SWAP_TPS_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return SOUTHPAW_TPS_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return SOUTHPAW_TRIGGER_SWAP_TPS_LAYOUT_FILE;
}
case STANDARD_FPS_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return STANDARD_TPS_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return STANDARD_FPS_LAYOUT_FILE;
}
case TRIGGER_SWAP_FPS_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return STANDARD_TPS_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return TRIGGER_SWAP_FPS_LAYOUT_FILE;
}
case SOUTHPAW_FPS_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return SOUTHPAW_TPS_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return SOUTHPAW_FPS_LAYOUT_FILE;
}
case SOUTHPAW_TRIGGER_SWAP_FPS_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return SOUTHPAW_TPS_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return SOUTHPAW_TRIGGER_SWAP_FPS_LAYOUT_FILE;
}
case STANDARD_FPS_ALTERNATE_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return STANDARD_TPS_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return STANDARD_FPS_ALTERNATE_LAYOUT_FILE;
}
case TRIGGER_SWAP_FPS_ALTERNATE_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return STANDARD_TPS_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return TRIGGER_SWAP_FPS_ALTERNATE_LAYOUT_FILE;
}
case SOUTHPAW_FPS_ALTERNATE_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return SOUTHPAW_TPS_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return SOUTHPAW_FPS_ALTERNATE_LAYOUT_FILE;
}
case SOUTHPAW_TRIGGER_SWAP_FPS_ALTERNATE_LAYOUT:
#if RSG_ORBIS
if(IsUsingRemotePlay())
{
return SOUTHPAW_TPS_LAYOUT_FILE;
}
else
#endif // RSG_ORIBS
{
return SOUTHPAW_TRIGGER_SWAP_FPS_ALTERNATE_LAYOUT_FILE;
}
default:
controlAssertf(false, "Unknown third person shooter gamepad configuration %d, using default!", layout);
return STANDARD_TPS_LAYOUT_FILE;
}
}
const char* CControl::GetAcceptCancelLayoutFile()
{
#if __PPU || RSG_ORBIS
if(!CPauseMenu::GetMenuPreference(PREF_ACCEPT_IS_CROSS))
{
return ALTERNATE_ACCEPT_CANCEL_SETTINGS_FILE;
}
#endif // __PPU
return DEFAULT_ACCEPT_CANCEL_SETTINGS_FILE;
}
const char* CControl::GetDuckHandbrakeLayoutFile()
{
const s32 iDuckBrakeMenuPref = CPauseMenu::GetMenuPreference(PREF_ALTERNATE_HANDBRAKE);
if(iDuckBrakeMenuPref > 0)
{
return ALTERNATE_DUCK_HANDBRAKE_SETTINGS_FILE;
}
return DEFAULT_DUCK_HANDBRAKE_SETTINGS_FILE;
}
#if FPS_MODE_SUPPORTED
bool CControl::IsUsingFpsMode() const
{
if(camInterface::IsInitialized())
{
return camInterface::GetGameplayDirector().ShouldUse1stPersonControls();
}
return false;
}
eControlLayout CControl::GetFpsLayout() const
{
// Add STANDARD_FPS_LAYOUT since this is the start of FPS layouts and UI options for PREF_CONTROL_CONFIG_FPS for FPS modes starts at 0.
return static_cast<eControlLayout>(CPauseMenu::GetMenuPreference(PREF_CONTROL_CONFIG_FPS));
}
#endif // FPS_MODE_SUPPORTED
eControlLayout CControl::GetTpsLayout() const
{
return static_cast<eControlLayout>(CPauseMenu::GetMenuPreference(PREF_CONTROL_CONFIG));
}
eControlLayout CControl::GetActiveLayout() const
{
#if FPS_MODE_SUPPORTED
if(IsUsingFpsMode())
{
return GetFpsLayout();
}
else
#endif // FPS_MODE_SUPPORTED
{
return GetTpsLayout();
}
}
bool CControl::LoadSettingsFile(const char* settingsFile, bool readSafeButSlow, ControlInput::ControlSettings& controlSettings)
{
parSettings settings;
// we want to detect errors when parsing the xml.
settings.SetFlag(parSettings::READ_SAFE_BUT_SLOW, readSafeButSlow);
INIT_PARSER;
bool result = PARSER.LoadObject(settingsFile, "", controlSettings, &settings);
controlAssertf(result, "Error loading control configuration from file: %s", settingsFile);
SHUTDOWN_PARSER;
return result;
}
bool CControl::LoadOverrideSettingsFile(const char* settingsFile, s32 deviceId, bool readSafeButSlow)
{
parSettings settings;
// we want to detect errors when parsing the xml.
settings.SetFlag(parSettings::READ_SAFE_BUT_SLOW, readSafeButSlow);
INIT_PARSER;
ControlInput::ControlSettings controlSettings;
bool result = PARSER.LoadObject(settingsFile, "", controlSettings, &settings);
if(controlVerifyf(result, "Error loading control configuration from file: %s", settingsFile))
{
for(u32 i = 0; i < controlSettings.m_Mappings.size(); ++i)
{
const InputType input = controlSettings.m_Mappings[i].m_Input;
ioMapper& mapper = m_Mappers[ms_settings.m_InputMappers[input]];
mapper.RemoveDeviceMappings(m_inputs[input], ioSource::IOMD_DEFAULT);
}
LoadSettings(controlSettings, deviceId);
}
SHUTDOWN_PARSER;
return result;
}
void CControl::LoadSettings(const ControlInput::ControlSettings &settings, s32 deviceId )
{
controlAssertf(ioSource::IsValidDevice(deviceId), "Invalid device id, using default!");
for (u32 i = 0; i < settings.m_Mappings.size(); ++i)
{
const ControlInput::Mapping &mapping = settings.m_Mappings[i];
ioSource source;
source.m_Device = mapping.m_Source;
source.m_DeviceIndex = deviceId;
switch(mapping.m_Source)
{
case IOMS_MKB_AXIS:
// Keyboard axis expects the first parameter to be the high bits and the second parameter to be the low bits.
controlAssertf(mapping.m_Parameters.size() == 2, "Invalid number of parameters for a keyboard axis, only the first two will be used (if possible)!");
if(mapping.m_Parameters.size() >= 2)
{
source.m_Parameter = ioMapper::MakeMkbAxis(mapping.m_Parameters[0],mapping.m_Parameters[1]);
}
break;
case IOMS_DIGITALBUTTON_AXIS:
// Keyboard axis expects the first parameter to be the high bits and the second parameter to be the low bits.
controlAssertf(mapping.m_Parameters.size() == 2, "Invalid number of parameters for a keyboard axis, only the first two will be used (if possible)!");
if(mapping.m_Parameters.size() >= 2)
{
u32 high = ioMapper::ConvertParameterToDeviceValue(mapping.m_Source, mapping.m_Parameters[0]);
u32 low = ioMapper::ConvertParameterToDeviceValue(mapping.m_Source, mapping.m_Parameters[1]);
source.m_Parameter = high << 8 | low;
}
break;
case IOMS_MOUSE_BUTTON:
case IOMS_PAD_DIGITALBUTTON:
case IOMS_PAD_DEBUGBUTTON:
{
u32 buttons = 0;
for(u32 parameterIndex = 0; parameterIndex < mapping.m_Parameters.size(); ++parameterIndex)
{
buttons |= ioMapper::ConvertParameterToDeviceValue(mapping.m_Source, mapping.m_Parameters[parameterIndex]);
}
source.m_Parameter = buttons;
}
break;
default:
controlAssertf(mapping.m_Parameters.size() == 1, "Invalid number of parameters, only the first will be used (if possible)!");
if(mapping.m_Parameters.size() >= 1)
{
source.m_Parameter = ioMapper::ConvertParameterToDeviceValue(mapping.m_Source, mapping.m_Parameters[0]);
}
}
AddMapping(mapping.m_Input, source);
}
}
void CControl::ReplaceSettings(const ControlInput::ControlSettings &settings, s32 deviceId)
{
for(u32 i = 0; i < settings.m_Mappings.size(); ++i)
{
const ControlInput::Mapping& mapping = settings.m_Mappings[i];
const u32 mapperIndex = ms_settings.m_InputMappers[mapping.m_Input];
if(mapperIndex != DEPRECATED_MAPPER && controlVerifyf(mapperIndex >= 0 && mapperIndex <= MAPPERS_MAX, "Invalid mapper for '%s'!", GetInputName(mapping.m_Input)))
{
m_Mappers[mapperIndex].RemoveDeviceMappings(m_inputs[mapping.m_Input], deviceId);
}
}
LoadSettings(settings, deviceId);
}
void CControl::LoadScriptedMappings()
{
ms_StandardScriptedMappings.m_Mappings.clear();
m_AlternateTpsScriptedSettings.m_Mappings.clear();
INIT_PARSER;
// we want to detect errors when parsing the xml.
parSettings settings;
settings.SetFlag(parSettings::CULL_OTHER_PLATFORM_DATA, true);
bool result = PARSER.LoadObject(STANDARD_SCRIPT_LAYOUT_FILE, "", ms_StandardScriptedMappings, &settings);
controlAssertf(result, "Error loading standard scripted configuration from file: %s", STANDARD_SCRIPT_LAYOUT_FILE);
eControlLayout tpsLayout = GetTpsLayout();
// Only load alternate layout settings if the user is not using standard controls layout.
if(tpsLayout != STANDARD_TPS_LAYOUT && tpsLayout != STANDARD_FPS_LAYOUT)
{
result = PARSER.LoadObject(GetScriptedInputLayoutFile(tpsLayout), "", m_AlternateTpsScriptedSettings, &settings);
controlAssertf(result, "Error loading alternate scripted configuration from file: %s", GetScriptedInputLayoutFile(tpsLayout));
}
#if FPS_MODE_SUPPORTED
m_AlternateFpsScriptedSettings.m_Mappings.Resize(0);
eControlLayout fpsLayout = GetFpsLayout();
// Only load alternate layout settings if the user is not using standard controls layout.
if(fpsLayout != STANDARD_TPS_LAYOUT && fpsLayout != STANDARD_FPS_LAYOUT)
{
result = PARSER.LoadObject(GetScriptedInputLayoutFile(fpsLayout), "", m_AlternateFpsScriptedSettings, &settings);
controlAssertf(result, "Error loading alternate scripted configuration from file: %s", GetScriptedInputLayoutFile(fpsLayout));
}
#endif // FPS_MODE_SUPPORTED
SHUTDOWN_PARSER;
m_useAlternateScriptedControlsNextFrame = false;
m_scriptedInputLayout = STANDARD_TPS_LAYOUT;
}
CControl::MappingType CControl::GetMappingType( ioMapperSource source )
{
// we ignore keyboard axis as keyboard axis (and IOMS_DIGITALBUTTON_AXIS for that matter) is mapped differently.
switch(source)
{
case IOMS_KEYBOARD:
case IOMS_MKB_AXIS:
case IOMS_MOUSE_ABSOLUTEAXIS:
case IOMS_MOUSE_CENTEREDAXIS:
case IOMS_MOUSE_RELATIVEAXIS:
case IOMS_MOUSE_SCALEDAXIS:
case IOMS_MOUSE_WHEEL:
case IOMS_MOUSE_BUTTON:
case IOMS_MOUSE_BUTTONANY:
return PC_KEYBOARD_MOUSE;
case IOMS_PAD_AXIS:
return PAD_AXIS;
case IOMS_JOYSTICK_AXIS:
case IOMS_JOYSTICK_IAXIS:
case IOMS_JOYSTICK_AXIS_NEGATIVE:
case IOMS_JOYSTICK_AXIS_POSITIVE:
case IOMS_JOYSTICK_BUTTON:
case IOMS_JOYSTICK_POV:
return JOYSTICK;
default:
// unknown type.
return UNKNOWN_MAPPING_TYPE;
}
}
// Extremely poor implementation, this will need replacing once a translation for the inputs has been creating.
const char* CControl::GetParameterText( ioMapperParameter parameter )
{
for(u32 i = 0; i < parser_rage__ioMapperParameter_Data.m_NumEnums; ++i)
{
if(parser_rage__ioMapperParameter_Data.m_Enums[i].m_Value == parameter)
return parser_rage__ioMapperParameter_Data.m_Names[i];
}
controlErrorf("Unknown parameter (%d)!", parameter);
return "";
}
const char* CControl::GetSourceText( ioMapperSource source )
{
for(u32 i = 0; i < parser_rage__ioMapperSource_Data.m_NumEnums; ++i)
{
if(parser_rage__ioMapperSource_Data.m_Enums[i].m_Value == source)
return parser_rage__ioMapperSource_Data.m_Names[i];
}
controlErrorf("Unknown source (%d)!", source);
return "";
}
rage::InputType CControl::GetInputByName( const char *name )
{
for(u32 i = 0; i < parser_rage__InputType_Data.m_NumEnums; ++i)
{
if(stricmp(parser_rage__InputType_Data.m_Names[i], name) == 0)
return static_cast<InputType>(parser_rage__InputType_Data.m_Enums[i].m_Value);
}
controlErrorf("%s is not a valid input!", name);
return UNDEFINED_INPUT;
}
rage::InputGroup CControl::GetInputGroupByName( const char *name )
{
for(u32 i = 0; i < parser_rage__InputGroup_Data.m_NumEnums; ++i)
{
if(stricmp(parser_rage__InputGroup_Data.m_Names[i], name) == 0)
return static_cast<InputGroup>(parser_rage__InputGroup_Data.m_Enums[i].m_Value);
}
controlErrorf("%s is not a valid input group!", name);
return INPUTGROUP_INVALID;
}
const char* CControl::GetInputName( InputType input )
{
if(controlVerifyf(input < MAX_INPUTS && input != UNDEFINED_INPUT, "Invalid input with value %d!", input ) )
{
return parser_rage__InputType_Data.m_Names[input];
}
return "";
}
const char* CControl::GetInputGroupName( InputGroup inputGroup )
{
if(controlVerifyf(inputGroup < MAX_INPUTGROUPS && INPUTGROUP_INVALID, "Invalid input group!"))
{
return parser_rage__InputGroup_Data.m_Names[inputGroup];
}
return "";
}
void CControl::AddMapping( const InputType input, const ioSource& source )
{
MappingType type = GetMappingType(source.m_Device);
// use the same code as UpdateMapping but pass in the mapping number of max int.
UpdateMapping(input, type, source, 0xFFFFFFFFu, true);
}
void CControl::UpdateMapping(const InputType input, const MappingType type, const ioSource& source, u32 mappingNumber, bool updateDependantMappings)
{
if(input != UNDEFINED_INPUT)
{
s32 mapperId = ms_settings.m_InputMappers[input];
controlAssertf(mapperId != DEPRECATED_MAPPER, "%s is deprecated so it is not possible to map a device to it!", GetInputName(input));
if(controlVerifyf(mapperId >= 0 && mapperId < MAPPERS_MAX, "%s has an invalid mapper so it is not possible to map a device to it!", GetInputName(input)))
{
ioMapper& mapper = m_Mappers[mapperId];
// find previous sources mapped to input.
ioMapper::ioSourceList sources;
mapper.GetMappedSources(m_inputs[input], sources);
// keep track of when the primary mapping is found (so we can skip it when searching for the secondary mapping).
u32 mappingCount = 0;
bool updated = false;
for(u32 i = 0; i < sources.size() && !updated; ++i)
{
// check if the device is mappable (e.g. the keyboard).
if(GetMappingType(sources[i].m_Device) == type)
{
if(mappingCount == mappingNumber)
{
if(updateDependantMappings)
{
// remove previous dependent mappings.
UpdateDependantMappings(input, type, sources[i], mappingNumber, REMOVE);
}
// replace previous mappings.
mapper.Change(i, source, m_inputs[input]);
KEYBOARD_MOUSE_ONLY(m_RecachedConflictCount = true);
m_isInputMapped[input] = true;
#if __BANK && __WIN32PC
// update rag.
// joystick text.
if(type == JOYSTICK)
{
GetMappingParameterName(source, m_ragData[input].m_Joystick, RagData::TEXT_SIZE);
}
// Primary text.
else if(mappingCount == 0)
{
GetMappingParameterName(source, m_ragData[input].m_KeyboardMousePrimary, RagData::TEXT_SIZE);
}
// secondary text.
else if(mappingCount == 1)
{
GetMappingParameterName(source, m_ragData[input].m_KeyboardMouseSecondary, RagData::TEXT_SIZE);
}
#endif // __BANK && __WIN32PC
updated = true;
}
// only increment the counter if we have not found the mapping we are looking for.
if(!updated)
{
++mappingCount;
}
}
}
// if nothing was updated then add the mapping.
if(!updated)
{
mapper.Map(source.m_Device, source.m_Parameter, m_inputs[input], source.m_DeviceIndex);
m_isInputMapped[input] = true;
#if __BANK && __WIN32PC
// update rag
// joystick text.
if(type == JOYSTICK)
{
GetMappingParameterName(source, m_ragData[input].m_Joystick, RagData::TEXT_SIZE);
}
// Primary text.
else if(type == PC_KEYBOARD_MOUSE)
{
if(mappingCount == 0)
{
GetMappingParameterName(source, m_ragData[input].m_KeyboardMousePrimary, RagData::TEXT_SIZE);
}
// secondary text.
else if(mappingCount == 1)
{
GetMappingParameterName(source, m_ragData[input].m_KeyboardMouseSecondary, RagData::TEXT_SIZE);
}
}
#endif // __BANK && __WIN32PC
}
if(type != UNKNOWN_MAPPING_TYPE)
{
if(mappingNumber > mappingCount)
{
// the input was added so update the index accordingly.
mappingNumber = mappingCount;
}
if(updateDependantMappings)
{
UpdateDependantMappings(input, type, source, mappingNumber, ADD_UPDATE);
}
}
}
}
}
void CControl::UpdateDependantMappings( const InputType input, const MappingType type, const ioSource& source, u32 mappingNumber, UpdateType updateType )
{
UpdateAxisDefinitionMappings(input, type, source, mappingNumber, updateType);
KEYBOARD_MOUSE_ONLY(UpdateIdenticalMappings(input, type, source, mappingNumber));
}
void CControl::UpdateAxisDefinitionMappings( const InputType input, const MappingType type, const ioSource& source, u32 mappingNumber, UpdateType updateType )
{
// we need a copy to find the device id.
ioSource thisSource = source;
// find all inputs that are dependent on this input.
for(u32 axisIndex = 0; axisIndex < ms_settings.m_AxisDefinitions.size(); ++axisIndex)
{
const ControlInput::AxisDefinition& axisDefinition = ms_settings.m_AxisDefinitions[axisIndex];
// see if the mapping is dependent.
if(axisDefinition.m_Negative == input || axisDefinition.m_Positive == input)
{
InputType altInputId = UNDEFINED_INPUT;
// Get the other input this axis is based on.
if(axisDefinition.m_Negative == input)
{
altInputId = axisDefinition.m_Positive;
}
else
{
altInputId = axisDefinition.m_Negative;
}
Mapper mapperId = ms_settings.m_InputMappers[altInputId];
controlAssertf(mapperId != DEPRECATED_MAPPER, "%s is deprecated so it is not possible to map a device to it!", GetInputName(altInputId));
if(controlVerifyf(mapperId >= 0 && mapperId < MAPPERS_MAX, "%s has an invalid mapper so it is not possible to map a device to it!", GetInputName(altInputId)))
{
ioMapper::ioSourceList sources;
GetSpecificInputSources(altInputId, sources, source.m_DeviceIndex);
const static ioSource NULL_MAPPING = ioSource(IOMS_KEYBOARD, KEY_NULL, ioSource::IOMD_KEYBOARD_MOUSE);
const ioSource *negative = &NULL_MAPPING;
const ioSource *positive = &NULL_MAPPING;
const bool doesMappingExist = mappingNumber < sources.size();
if(axisDefinition.m_Negative == input)
{
negative = &thisSource;
if(doesMappingExist)
{
positive = &sources[mappingNumber];
}
}
else
{
if(doesMappingExist)
{
negative = &sources[mappingNumber];
}
positive = &thisSource;
}
// Retrieves the axis source based on two other inputs.
ioSource axisSource = GetAxisCompatableSource(*negative, *positive);
if(axisSource.m_Device != IOMS_UNDEFINED)
{
if(updateType == REMOVE)
{
RemoveMapping(axisSource, axisDefinition.m_Input, type, mappingNumber);
}
else
{
UpdateMapping(axisDefinition.m_Input, type, axisSource, mappingNumber, true);
}
}
else if(updateType == ADD_UPDATE && doesMappingExist)
{
// remove previous incompatible input.
RemoveMapping(sources[mappingNumber], altInputId, type, mappingNumber);
}
}
}
}
}
bool CControl::RemoveMapping(const ioSource& source, InputType inputId, MappingType WIN32PC_ONLY(BANK_ONLY(type)), u32 WIN32PC_ONLY(BANK_ONLY(mappingNumber)))
{
bool result = false;
Mapper mapperId = ms_settings.m_InputMappers[inputId];
controlAssertf(mapperId != DEPRECATED_MAPPER, "%s is deprecated so it is not possible to map a device to it!", GetInputName(inputId));
if(controlVerifyf(mapperId >= 0 && mapperId < MAPPERS_MAX, "%s has an invalid mapper so it is not possible to map a device to it!", GetInputName(inputId)))
{
result = m_Mappers[mapperId].Remove(source, m_inputs[inputId]);
#if __BANK && __WIN32PC
if(result)
{
switch(type)
{
case JOYSTICK:
safecpy(m_ragData[inputId].m_Joystick, "<NOT ASSIGNED>", RagData::TEXT_SIZE);
break;
case PC_KEYBOARD_MOUSE:
if(mappingNumber == 0)
{
safecpy(m_ragData[inputId].m_KeyboardMousePrimary, "<NOT ASSIGNED>", RagData::TEXT_SIZE);
}
else if(mappingNumber == 1)
{
safecpy(m_ragData[inputId].m_KeyboardMouseSecondary, "<NOT ASSIGNED>", RagData::TEXT_SIZE);
}
break;
default:
break;
}
}
#endif // __BANK && __WIN32PC
}
return result;
}
ioSource CControl::GetAxisCompatableSource( const ioSource& negative, const ioSource& positive )
{
ioSource source;
source.m_Device = IOMS_UNDEFINED;
switch(negative.m_Device)
{
case IOMS_KEYBOARD:
case IOMS_MOUSE_BUTTON:
case IOMS_MOUSE_WHEEL:
{
if(positive.m_Parameter == IOM_WHEEL_UP && negative.m_Parameter == IOM_WHEEL_DOWN)
{
source.m_Device = IOMS_MOUSE_WHEEL;
source.m_Parameter = IOM_AXIS_WHEEL_RELATIVE;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
else if(positive.m_Parameter == IOM_WHEEL_DOWN && negative.m_Parameter == IOM_WHEEL_UP)
{
source.m_Device = IOMS_MOUSE_WHEEL;
source.m_Parameter = IOM_IAXIS_WHEEL_RELATIVE;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
else
{
source.m_Parameter = ioMapper::MakeMkbAxis(
ioMapper::ConvertParameterToMapperValue(positive.m_Device, positive.m_Parameter),
ioMapper::ConvertParameterToMapperValue(negative.m_Device, negative.m_Parameter) );
source.m_Device = IOMS_MKB_AXIS;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
break;
}
case IOMS_PAD_DIGITALBUTTON:
{
// errornous params.
if(negative.m_Device != positive.m_Device)
return source;
source.m_Device = IOMS_DIGITALBUTTON_AXIS;
source.m_Parameter = negative.m_Parameter | positive.m_Parameter << 8;
source.m_DeviceIndex = negative.m_DeviceIndex;
break;
}
case IOMS_PAD_AXIS:
{
// errornous params.
if(negative.m_Device != positive.m_Device || negative.m_DeviceIndex != positive.m_DeviceIndex)
return source;
ioMapperParameter negParam = ioMapper::ConvertParameterToMapperValue(negative.m_Device, negative.m_Parameter);
ioMapperParameter posParam = ioMapper::ConvertParameterToMapperValue(positive.m_Device, positive.m_Parameter);
if( (negParam == IOM_AXIS_LY_UP && posParam == IOM_AXIS_LY_DOWN)
|| (negParam == IOM_AXIS_LUP && posParam == IOM_AXIS_LDOWN) )
{
source.m_Device = IOMS_PAD_AXIS;
source.m_Parameter = IOM_AXIS_LY;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
else if( (negParam == IOM_AXIS_LX_LEFT && posParam == IOM_AXIS_LX_RIGHT)
|| (negParam == IOM_AXIS_LLEFT && posParam == IOM_AXIS_LRIGHT) )
{
source.m_Device = IOMS_PAD_AXIS;
source.m_Parameter = IOM_AXIS_LX;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
else if( (negParam == IOM_AXIS_RY_UP && posParam == IOM_AXIS_RY_DOWN)
|| (negParam == IOM_AXIS_RUP && posParam == IOM_AXIS_RDOWN) )
{
source.m_Device = IOMS_PAD_AXIS;
source.m_Parameter = IOM_AXIS_RY;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
else if( (negParam == IOM_AXIS_RX_LEFT && posParam == IOM_AXIS_RX_RIGHT)
|| (negParam == IOM_AXIS_RLEFT && posParam == IOM_AXIS_RRIGHT) )
{
source.m_Device = IOMS_PAD_AXIS;
source.m_Parameter = IOM_AXIS_RX;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
else
{
// During load, we temporarily map to key null until the other direction is know. Ignore this.
controlAssertf( (positive.m_Device == IOMS_KEYBOARD && positive.m_Parameter == KEY_NULL) || (negative.m_Device == IOMS_KEYBOARD && negative.m_Parameter == KEY_NULL),
"unsupported gamepad axis assigned to axis!");
}
break;
}
case IOMS_JOYSTICK_AXIS_NEGATIVE:
{
if(positive.m_Device != IOMS_JOYSTICK_AXIS_POSITIVE || positive.m_Parameter != negative.m_Parameter || positive.m_DeviceIndex != negative.m_DeviceIndex)
{
return source;
}
ioMapperParameter negParam = ioMapper::ConvertParameterToMapperValue(negative.m_Device, negative.m_Parameter);
ioMapperParameter posParam = ioMapper::ConvertParameterToMapperValue(positive.m_Device, positive.m_Parameter);
if( negParam >= IOM_JOYSTICK_AXIS1 && negParam <= IOM_JOYSTICK_AXIS8 &&
posParam >= IOM_JOYSTICK_AXIS1 && posParam <= IOM_JOYSTICK_AXIS8 )
{
source.m_Device = IOMS_JOYSTICK_AXIS;
source.m_Parameter = negative.m_Parameter;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
break;
}
case IOMS_JOYSTICK_AXIS_POSITIVE:
{
if(positive.m_Device != IOMS_JOYSTICK_AXIS_NEGATIVE || positive.m_Parameter != negative.m_Parameter || positive.m_DeviceIndex != negative.m_DeviceIndex)
{
return source;
}
ioMapperParameter negParam = ioMapper::ConvertParameterToMapperValue(negative.m_Device, negative.m_Parameter);
ioMapperParameter posParam = ioMapper::ConvertParameterToMapperValue(positive.m_Device, positive.m_Parameter);
if( negParam >= IOM_JOYSTICK_AXIS1 && negParam <= IOM_JOYSTICK_AXIS8 &&
posParam >= IOM_JOYSTICK_AXIS1 && posParam <= IOM_JOYSTICK_AXIS8 )
{
source.m_Device = IOMS_JOYSTICK_IAXIS;
source.m_Parameter = negative.m_Parameter;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
break;
}
case IOMS_JOYSTICK_POV:
{
if(positive.m_Device != IOMS_JOYSTICK_POV || positive.m_DeviceIndex != negative.m_DeviceIndex || positive.m_Parameter == negative.m_Parameter)
{
return source;
}
source.m_Device = IOMS_JOYSTICK_POV_AXIS;
source.m_Parameter = negative.m_Parameter | positive.m_Parameter << 8;
source.m_DeviceIndex = negative.m_DeviceIndex;
break;
}
case IOMS_MOUSE_ABSOLUTEAXIS:
case IOMS_MOUSE_CENTEREDAXIS:
case IOMS_MOUSE_RELATIVEAXIS:
case IOMS_MOUSE_SCALEDAXIS:
{
if( (positive.m_Parameter == IOM_AXIS_X_RIGHT && negative.m_Parameter == IOM_AXIS_X_LEFT)
|| (positive.m_Parameter == IOM_AXIS_X && negative.m_Parameter == IOM_AXIS_X) )
{
source.m_Device = negative.m_Device;
source.m_Parameter = IOM_AXIS_X;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
else if( (positive.m_Parameter == IOM_AXIS_X_LEFT && negative.m_Parameter == IOM_AXIS_X_RIGHT)
|| (positive.m_Parameter == IOM_IAXIS_X && negative.m_Parameter == IOM_IAXIS_X) )
{
source.m_Device = negative.m_Device;
source.m_Parameter = IOM_IAXIS_X;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
else if( (positive.m_Parameter == IOM_AXIS_Y_DOWN && negative.m_Parameter == IOM_AXIS_Y_UP)
|| (positive.m_Parameter == IOM_AXIS_Y && negative.m_Parameter == IOM_AXIS_Y) )
{
source.m_Device = negative.m_Device;
source.m_Parameter = IOM_AXIS_Y;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
else if( (positive.m_Parameter == IOM_AXIS_Y_UP && negative.m_Parameter == IOM_AXIS_Y_DOWN)
|| (positive.m_Parameter == IOM_IAXIS_Y && negative.m_Parameter == IOM_IAXIS_Y) )
{
source.m_Device = negative.m_Device;
source.m_Parameter = IOM_IAXIS_Y;
source.m_DeviceIndex = negative.m_DeviceIndex;
}
else
{
// During load, we temporarily map to key null until the other direction is know. Ignore this.
controlAssertf( (positive.m_Device == IOMS_KEYBOARD && positive.m_Parameter == KEY_NULL) || (negative.m_Device == IOMS_KEYBOARD && negative.m_Parameter == KEY_NULL),
"Incompatible mouse axis type assigned to input!");
}
break;
}
default:
break;
}
return source;
}
// TEMPORARY UNTIL STRING READ FROM LANGUAGE FILE
#if KEYBOARD_MOUSE_SUPPORT
void CControl::GetMappingParameterName( const ioSource& source, char *buffer, size_t bufferSize )
{
if(source.m_Device == IOMS_MKB_AXIS)
{
ioMapperParameter negative = ioMapper::ConvertParameterToMapperValue(source.m_Device, ioMapper::GetMkbAxisNegative(source.m_Parameter, true));
ioMapperParameter positive = ioMapper::ConvertParameterToMapperValue(source.m_Device, ioMapper::GetMkbAxisPositive(source.m_Parameter, true));
formatf(buffer, bufferSize, "%s, %s", GetParameterText(negative), GetParameterText(positive));
}
else if(source.m_Device == IOMS_DIGITALBUTTON_AXIS || source.m_Device == IOMS_JOYSTICK_POV_AXIS)
{
ioMapperParameter parameter1 = ioMapper::ConvertParameterToMapperValue(source.m_Device, source.m_Parameter & 0xFF);
ioMapperParameter parameter2 = ioMapper::ConvertParameterToMapperValue(source.m_Device, (source.m_Parameter & 0xFF00) >> 8);
formatf(buffer, bufferSize, "%s, %s", GetParameterText(parameter1), GetParameterText(parameter2));
}
else
{
ioMapperParameter parameter = ioMapper::ConvertParameterToMapperValue(source.m_Device, source.m_Parameter);
safecpy(buffer, GetParameterText(parameter), bufferSize);
}
}
#endif // KEYBOARD_MOUSE_SUPPORT
void CControl::EnableFrontendInputs()
{
for(u32 i = INPUT_FRONTEND_CONTROL_BEGIN; i <= INPUT_FRONTEND_CONTROL_END; ++i)
{
#if __BANK
if(!m_ragData[i].m_ForceDisabled)
#endif // __BANK
{
m_inputs[i].Enable();
}
}
}
void CControl::DisableBackButtonInputs()
{
ioMapper::ioSourceList sources;
GetInputSources(INPUT_FRONTEND_CANCEL, sources);
DisableInputsByActiveSources(sources, ioValue::DEFAULT_DISABLE_OPTIONS);
}
bool CControl::IsUsingAnalogueAiming() const
{
#if RSG_ORBIS
// Remote play (vita) does not have analogue aiming.
if(IsUsingRemotePlay())
{
return false;
}
else
#endif // RSG_ORBIS
{
eControlLayout layout = GetActiveLayout();
return (layout != TRIGGER_SWAP_TPS_LAYOUT && layout != SOUTHPAW_TRIGGER_SWAP_TPS_LAYOUT);
}
}
CControl::eMouseSteeringMode CControl::GetMouseSteeringMode(s32 iPref)
{
return (CControl::eMouseSteeringMode)CPauseMenu::GetMenuPreference(iPref);
}
bool CControl::IsMouseSteeringOn(s32 iPref)
{
switch(GetMouseSteeringMode(iPref))
{
case(eMSM_Vehicle):
return true;
default:
break;
}
return false;
}
void CControl::SetVehicleSteeringExclusive()
{
#if KEYBOARD_MOUSE_SUPPORT
if(m_inputs[INPUT_VEH_MOVE_LR].IsEnabled())
{
bool bMouseSteering = CControl::GetMouseSteeringMode(PREF_MOUSE_DRIVE) == eMSM_Vehicle;
bool bCameraMouseSteering = CControl::GetMouseSteeringMode(PREF_MOUSE_DRIVE) == eMSM_Camera;
// Do not set exclusivity if the steering has been disabled by some other code and the user is not shooting or aiming.
if( ((bMouseSteering && m_inputs[INPUT_VEH_ATTACK].IsUp() && m_inputs[INPUT_VEH_AIM].IsUp() && !m_DriveCameraToggleOn) || (bCameraMouseSteering && m_DriveCameraToggleOn)) && WasKeyboardMouseLastKnownSource() && (m_inputs[INPUT_VEH_MOUSE_CONTROL_OVERRIDE].IsUp() || m_UseSecondaryDriveCameraToggle) )
{
// Disable mouse looking up and down (the left and right gets disabled when we set INPUT_VEH_MOVE_LR exclusive).
DisableInput(INPUT_LOOK_UD);
DisableInput(INPUT_LOOK_UP_ONLY);
DisableInput(INPUT_LOOK_DOWN_ONLY);
SetInputExclusive(INPUT_VEH_MOVE_LR);
// enable only versions, we use EnableInput() function for extra rag checks.
EnableInput(INPUT_VEH_MOVE_LEFT_ONLY);
EnableInput(INPUT_VEH_MOVE_RIGHT_ONLY);
}
else
{
// Auto center is scaled, centered axis does not auto center.
const ioMapperSource source = (m_MouseVehicleCarAutoCenter) ? IOMS_MOUSE_SCALEDAXIS : IOMS_MOUSE_CENTEREDAXIS;
m_inputs[INPUT_VEH_MOVE_LR].Ignore(source);
m_inputs[INPUT_VEH_MOVE_LEFT_ONLY].Ignore(source);
m_inputs[INPUT_VEH_MOVE_RIGHT_ONLY].Ignore(source);
}
}
#endif // KEYBOARD_MOUSE_SUPPORT
}
void CControl::SetVehicleFlySteeringExclusive()
{
#if KEYBOARD_MOUSE_SUPPORT
if(m_inputs[INPUT_VEH_FLY_ROLL_LR].IsEnabled() && m_inputs[INPUT_VEH_FLY_PITCH_UD].IsEnabled())
{
bool bMouseFlySteering = CControl::GetMouseSteeringMode(PREF_MOUSE_FLY) == CControl::eMSM_Vehicle;
bool bCameraMouseFlySteering = CControl::GetMouseSteeringMode(PREF_MOUSE_FLY) == CControl::eMSM_Camera;
bool mouseFlyingXYSwap = (CPauseMenu::GetMenuPreference(PREF_SWAP_ROLL_YAW_MOUSE_FLYING) != 0);
// Do not set exclusivity if the steering has been disabled by some other code and the user is not shooting or aiming.
// TR TODO: Should we be using INPUT_AIM or check the right mouse button directly as we are in PC specific code?
if( ((bMouseFlySteering && !m_DriveCameraToggleOn) ||(bCameraMouseFlySteering && m_DriveCameraToggleOn)) && WasKeyboardMouseLastKnownSource())
{
if(mouseFlyingXYSwap)
{
SetInputExclusive(INPUT_VEH_FLY_PITCH_UD);
SetInputExclusive(INPUT_VEH_FLY_YAW_LEFT);
SetInputExclusive(INPUT_VEH_FLY_YAW_RIGHT);
// enable only versions, we use EnableInput() function for extra rag checks.
EnableInput(INPUT_VEH_FLY_PITCH_UP_ONLY);
EnableInput(INPUT_VEH_FLY_PITCH_DOWN_ONLY);
EnableInput(INPUT_VEH_FLY_YAW_LEFT);
EnableInput(INPUT_VEH_FLY_YAW_RIGHT);
}
else
{
SetInputExclusive(INPUT_VEH_FLY_ROLL_LR);
SetInputExclusive(INPUT_VEH_FLY_PITCH_UD);
// enable only versions, we use EnableInput() function for extra rag checks.
EnableInput(INPUT_VEH_FLY_ROLL_LEFT_ONLY);
EnableInput(INPUT_VEH_FLY_ROLL_RIGHT_ONLY);
EnableInput(INPUT_VEH_FLY_PITCH_UP_ONLY);
EnableInput(INPUT_VEH_FLY_PITCH_DOWN_ONLY);
}
}
else
{
if(mouseFlyingXYSwap)
{
// Auto center is scaled, centered axis does not auto center.
const ioMapperSource source = (m_MouseVehicleFlyAutoCenter) ? IOMS_MOUSE_SCALEDAXIS : IOMS_MOUSE_CENTEREDAXIS;
m_inputs[INPUT_VEH_FLY_PITCH_UD].Ignore(source);
m_inputs[INPUT_VEH_FLY_PITCH_UP_ONLY].Ignore(source);
m_inputs[INPUT_VEH_FLY_PITCH_DOWN_ONLY].Ignore(source);
m_inputs[INPUT_VEH_FLY_YAW_LEFT].Ignore(source);
m_inputs[INPUT_VEH_FLY_YAW_RIGHT].Ignore(source);
}
else
{
// Auto center is scaled, centered axis does not auto center.
const ioMapperSource source = (m_MouseVehicleFlyAutoCenter) ? IOMS_MOUSE_SCALEDAXIS : IOMS_MOUSE_CENTEREDAXIS;
m_inputs[INPUT_VEH_FLY_ROLL_LR].Ignore(source);
m_inputs[INPUT_VEH_FLY_PITCH_UD].Ignore(source);
m_inputs[INPUT_VEH_FLY_ROLL_LEFT_ONLY].Ignore(source);
m_inputs[INPUT_VEH_FLY_ROLL_RIGHT_ONLY].Ignore(source);
m_inputs[INPUT_VEH_FLY_PITCH_UP_ONLY].Ignore(source);
m_inputs[INPUT_VEH_FLY_PITCH_DOWN_ONLY].Ignore(source);
}
}
}
#endif // KEYBOARD_MOUSE_SUPPORT
}
void CControl::SetVehicleSubSteeringExclusive()
{
#if KEYBOARD_MOUSE_SUPPORT
if(m_inputs[INPUT_VEH_SUB_TURN_LR].IsEnabled() && m_inputs[INPUT_VEH_SUB_PITCH_UD].IsEnabled())
{
bool bMouseSubSteering = CControl::GetMouseSteeringMode(PREF_MOUSE_SUB) == CControl::eMSM_Vehicle;
bool bCameraMouseSubSteering = CControl::GetMouseSteeringMode(PREF_MOUSE_SUB) == CControl::eMSM_Camera;
// Do not set exclusivity if the steering has been disabled by some other code and the user is not shooting or aiming.
if( ((bMouseSubSteering && m_inputs[INPUT_VEH_FLY_ATTACK].IsUp() && !m_DriveCameraToggleOn) || (bCameraMouseSubSteering && m_DriveCameraToggleOn)) && WasKeyboardMouseLastKnownSource() )
{
SetInputExclusive(INPUT_VEH_SUB_TURN_LR);
SetInputExclusive(INPUT_VEH_SUB_PITCH_UD);
// enable only versions, we use EnableInput() function for extra rag checks.
EnableInput(INPUT_VEH_SUB_TURN_LEFT_ONLY);
EnableInput(INPUT_VEH_SUB_TURN_RIGHT_ONLY);
EnableInput(INPUT_VEH_SUB_PITCH_UP_ONLY);
EnableInput(INPUT_VEH_SUB_PITCH_DOWN_ONLY);
EnableInput(INPUT_VEH_SUB_TURN_HARD_LEFT);
EnableInput(INPUT_VEH_SUB_TURN_HARD_RIGHT);
}
else
{
// Auto center is scaled, centered axis does not auto center.
const ioMapperSource source = (m_MouseVehicleSubAutoCenter) ? IOMS_MOUSE_SCALEDAXIS : IOMS_MOUSE_CENTEREDAXIS;
m_inputs[INPUT_VEH_SUB_TURN_LR].Ignore(source);
m_inputs[INPUT_VEH_SUB_PITCH_UD].Ignore(source);
m_inputs[INPUT_VEH_SUB_TURN_LEFT_ONLY].Ignore(source);
m_inputs[INPUT_VEH_SUB_TURN_RIGHT_ONLY].Ignore(source);
m_inputs[INPUT_VEH_SUB_PITCH_UP_ONLY].Ignore(source);
m_inputs[INPUT_VEH_SUB_PITCH_DOWN_ONLY].Ignore(source);
m_inputs[INPUT_VEH_SUB_TURN_HARD_LEFT].Ignore(source);
m_inputs[INPUT_VEH_SUB_TURN_HARD_RIGHT].Ignore(source);
}
}
#endif // KEYBOARD_MOUSE_SUPPORT
}
bool CControl::IsUsingCursor() const
{
#if KEYBOARD_MOUSE_SUPPORT
return WasKeyboardMouseLastKnownSource();
#elif RSG_TOUCHPAD
return (CControlMgr::GetPad(m_padNum).GetNumTouches() != 0);
#else
return false;
#endif // KEYBOARD_MOUSE_SUPPORT
}
bool CControl::IsUsingRemotePlay() const
{
#if RSG_ORBIS
return CControlMgr::GetPad(m_padNum).IsRemotePlayPad();
#else
return false;
#endif // RSG_ORBIS
}
bool CControl::ScriptCheckForControlsChange()
{
if(m_WasControlsRefreshedThisFrame)
{
m_HasScriptCheckControlsRefresh = true;
}
return m_WasControlsRefreshedThisFrame ||
m_WasControlsRefreshedLastFrame
KEYBOARD_MOUSE_ONLY(|| m_LastFrameDeviceIndex != m_LastKnownSource.m_DeviceIndex);
}
bool CControl::CanUseToggleRun() const
{
const eControlLayout layout = GetActiveLayout();
return layout == STANDARD_FPS_LAYOUT ||
layout == TRIGGER_SWAP_FPS_LAYOUT ||
layout == SOUTHPAW_FPS_LAYOUT ||
layout == SOUTHPAW_TRIGGER_SWAP_FPS_LAYOUT
KEYBOARD_MOUSE_ONLY(|| WasKeyboardMouseLastKnownSource());
}
void CControl::ClearToggleRun()
{
bool bToggleRunValue = false;
#if KEYBOARD_MOUSE_SUPPORT && FPS_MODE_SUPPORTED
// B*2312353: Default toggle run value to on when in FPS mode using mouse/keyboard.
const CPed* pPed = CPedFactory::GetFactory() ? CGameWorld::FindLocalPlayer() : NULL;
if (pPed && pPed->IsFirstPersonShooterModeEnabledForPlayer(false) && WasKeyboardMouseLastKnownSource() && !pPed->GetIsSwimming())
{
bToggleRunValue = true;
}
#endif // KEYBOARD_MOUSE_SUPPORT && FPS_MODE_SUPPORTED
m_ToggleRunOn = bToggleRunValue;
}
bool CControl::CanUseToggleAim() const
{
const CPed* pPed = CGameWorld::FindLocalPlayer();
if(pPed)
{
const CPedWeaponManager *pWeapMgr = pPed->GetWeaponManager();
if (pWeapMgr)
{
if(pWeapMgr->GetEquippedVehicleWeapon())
{
if( pWeapMgr->GetEquippedVehicleWeapon()->GetWeaponInfo())
{
return pWeapMgr->GetEquippedVehicleWeapon()->GetWeaponInfo()->GetIsGun();
}
}
else if(pWeapMgr->GetEquippedWeaponInfo() && pWeapMgr->GetEquippedWeaponInfo() == pWeapMgr->GetSelectedWeaponInfo())
{
return pWeapMgr->GetEquippedWeaponInfo()->GetIsGun();
}
}
}
return false;
}
CControl::Settings::Settings()
: m_AxisDefinitions()
, m_HistorySupport()
#if KEYBOARD_MOUSE_SUPPORT
, m_MappingSettings()
, m_DynamicMappingList()
, m_InputCategories(0)
#endif // KEYBOARD_MOUSE_SUPPORT
, m_NeedsInitializing(true)
{}
void CControl::Settings::Init()
{
if(m_NeedsInitializing)
{
m_NeedsInitializing = false;
INIT_PARSER;
ControlInput::InputSettings settings;
controlVerifyf(PARSER.LoadObject(INPUT_SETTINGS_FILE, "", settings), "Error loading input settings file!");
m_HistorySupport = settings.m_HistorySupport;
m_RelatedInputs = settings.m_RelatedInputs;
KEYBOARD_MOUSE_ONLY(LoadKeyboardLayout());
m_AxisDefinitions = settings.m_AxisDefinitions;
WIN32PC_ONLY(m_GamepadDefinitionList.m_Devices.Reset());
// set all mappers to an undefined mapper.
for(u32 i = 0; i < MAX_INPUTS; ++i)
{
m_InputMappers[i] = UNDEFINED_MAPPER;
}
// put inputs mappers into an array indexed by input id.
for(u32 i = 0; i < settings.m_MapperAssignements.size(); ++i)
{
ControlInput::InputMapperAssignment assignment = settings.m_MapperAssignements[i];
for(u32 inputIndex = 0; inputIndex < assignment.m_Inputs.size(); ++inputIndex)
{
u32 inputId = assignment.m_Inputs[inputIndex];
controlAssertf( (assignment.m_Mapper > UNDEFINED_MAPPER && assignment.m_Mapper < MAPPERS_MAX) || assignment.m_Mapper == DEPRECATED_MAPPER,
"Invalid mapper assigned to input %s!",
parser_rage__InputType_Data.m_Names[inputId] );
controlAssertf( m_InputMappers[inputId] == UNDEFINED_MAPPER, "'%s' is assigned to the '%s' mapper but is also assigned to '%s' in '%s'!",
parser_rage__InputType_Data.m_Names[inputId],
parser_rage__Mapper_Strings[m_InputMappers[inputId]],
parser_rage__Mapper_Strings[assignment.m_Mapper],
INPUT_SETTINGS_FILE );
m_InputMappers[inputId] = assignment.m_Mapper;
}
}
// Check for any inputs that do not have a mapper assigned.
for(s32 i = 0; i < MAX_INPUTS; ++i)
{
if( !controlVerifyf(m_InputMappers[i] != UNDEFINED_MAPPER, "Input %s is not assigned to a mapper in %s!", GetInputName(static_cast<InputType>(i)), INPUT_SETTINGS_FILE) )
{
m_InputMappers[i] = (Mapper)0;
}
}
controlAssertf( settings.m_InputGroupDefinitions.size() == MAX_INPUTGROUPS,
"The wrong number of INPUTGROUP definitions exist in '%s'. Expected %d but got %d!",
INPUT_SETTINGS_FILE,
MAX_INPUTGROUPS,
settings.m_InputGroupDefinitions.size() );
for(s32 i = 0; i < settings.m_InputGroupDefinitions.size(); ++i)
{
InputGroup group = settings.m_InputGroupDefinitions[i].m_InputGroup;
if( controlVerifyf(m_InputGroupDefinitions[group].size() == 0, "Duplicate definition for '%s' in '%s'!", GetInputGroupName(group), INPUT_SETTINGS_FILE) )
{
m_InputGroupDefinitions[group] = settings.m_InputGroupDefinitions[i].m_Inputs;
}
}
#if KEYBOARD_MOUSE_SUPPORT
// As it is only possible to re-map controls in on pc we do not need to store information on mapping inputs on other platforms.
controlVerifyf(PARSER.LoadObject(PC_INPUT_SETTINGS_FILE, "", m_MappingSettings), "Error loading keyboard/mouse settings file!");
// Create a catagory lookup by input.
atMap<atHashString, ControlInput::InputList>::Iterator it = m_MappingSettings.m_Categories.CreateIterator();
for (it.Start(); !it.AtEnd(); it.Next())
{
for (u32 i = 0; i < it.GetData().m_Inputs.size(); ++i)
{
const InputType input = it.GetData().m_Inputs[i];
controlAssertf( m_InputCategories[input] == 0,
"Input '%s' (%d) has been placed in more than one catagory inside '" PC_INPUT_SETTINGS_FILE "'!",
GetInputName(input),
input );
m_InputCategories[input] = it.GetKey().GetHash();
}
}
m_MouseSettings = m_MappingSettings.m_MouseSettings;
// By default the input is mappable unless marked as unmappable.
for(s32 input = 0; input < m_InputMappable.size(); ++input)
{
m_InputMappable[input] = true;
}
// Mark all unmappable inputs as unmappable. This store these hash lookups in a better way for looking up.
for(s32 catagoryIndex = 0; catagoryIndex < ms_settings.m_MappingSettings.m_UnmappableList.size(); ++catagoryIndex)
{
const ControlInput::InputList* inputs = ms_settings.m_MappingSettings.m_Categories.Access(m_MappingSettings.m_UnmappableList[catagoryIndex]);
if(inputs != NULL)
{
for(s32 inputIndex = 0; inputIndex < inputs->m_Inputs.size(); ++inputIndex)
{
m_InputMappable[inputs->m_Inputs[inputIndex]] = false;
}
}
}
// Free memory.
ms_settings.m_MappingSettings.m_UnmappableList.Reset();
controlVerifyf(PARSER.LoadObject(PC_DYNAMIC_SETTINGS_FILE, "", m_DynamicMappingList), "Error loading dynamic mappings file!");
#if RSG_ASSERT
// ensure all inputs are in a catagory.
for(u32 i = 0; i < m_InputCategories.size(); ++i)
{
controlAssertf( m_InputCategories[i] != 0,
"'%s' (%d) has not been assigned a catagory inside '" PC_INPUT_SETTINGS_FILE "'!",
GetInputName(static_cast<InputType>(i)),
i );
}
// ensure all mappings are mapped correctly.
::rage::atMap< ::rage::atHashWithStringNotFinal, rage::ControlInput::DynamicMappings >::Iterator iterator = m_DynamicMappingList.m_DynamicMappings.CreateIterator();
for(iterator.Start(); !iterator.AtEnd(); iterator.Next())
{
controlAssertf( iterator.GetData().m_Mappings.size() == iterator.GetData().m_Mappings.max_size(),
"Incorrect number of mappings for dynamic mappings!" );
}
#endif // RSG_ASSERT
#endif // KEYBOARD_MOUSE_SUPPORT
WIN32PC_ONLY(RebuildGamepadDefinitionList());
SHUTDOWN_PARSER;
}
}
int CControl::Settings::InputCompare(const InputType* a, const InputType* b)
{
return *a - *b;
}
#if KEYBOARD_MOUSE_SUPPORT
void CControl::Settings::LoadKeyboardLayout()
{
const char* localName = NULL;
char16 wideLocalName[LOCALE_NAME_MAX_LENGTH];
GetSystemDefaultLocaleName(reinterpret_cast<wchar_t*>(wideLocalName), LOCALE_NAME_MAX_LENGTH);
USES_CONVERSION;
const char* defaultLocalName = WIDE_TO_UTF8(wideLocalName);
if(PARAM_keyboardLocal.Get())
{
PARAM_keyboardLocal.Get(localName);
controlDisplayf("Looking for keyboard layout %s as requested by command line.", localName);
}
else if(StringLength(defaultLocalName) > 0)
{
localName = defaultLocalName;
controlDisplayf("Looking for keyboard layout %s as returned by system.", localName);
}
else
{
localName = DEFAULT_KEYBOARD_LAYOUT;
controlAssertf(false, "Looking for keyboard layout %s as we cannot retrieve correct layout from the system!", localName);
}
char filePath[RAGE_MAX_PATH];
controlDisplayf("Attempting to load keyboard layout at '%s%s'.", localName, KEYBOARD_LAYOUT_EXTENSION);
formatf(filePath, "%s%s%s", KEYBOARD_LAYOUT_DIR, localName, KEYBOARD_LAYOUT_EXTENSION);
// If the keyboard layout file does not exist then try and load the region neutral version.
if(!ASSET.Exists(filePath, ""))
{
// Get the neutral region name (e.g. en-GB and en-US will just be en).
char localNeutralFileName[LOCALE_NAME_MAX_LENGTH] = {0};
safecpy(localNeutralFileName, localName);
char* newEndPos = strstr(localNeutralFileName, "-");
if(newEndPos)
{
*newEndPos = 0;
}
controlDisplayf("Attempting to Fall back to keyboard layout at '%s%s'.", localNeutralFileName, KEYBOARD_LAYOUT_EXTENSION);
formatf(filePath, "%s%s%s", KEYBOARD_LAYOUT_DIR, localNeutralFileName, KEYBOARD_LAYOUT_EXTENSION);
// If the region neutral keyboard layout file does not exist then use the default en-US version.
if(!ASSET.Exists(filePath, ""))
{
controlDisplayf("Could not fall back to '%s%s', falling back to default keyboard layout at '%s%s'.",
localNeutralFileName,
KEYBOARD_LAYOUT_EXTENSION,
DEFAULT_KEYBOARD_LAYOUT,
KEYBOARD_LAYOUT_EXTENSION);
formatf(filePath, "%s%s%s", KEYBOARD_LAYOUT_DIR, DEFAULT_KEYBOARD_LAYOUT, KEYBOARD_LAYOUT_EXTENSION);
}
}
LoadKeyboardLayoutFile(filePath);
}
void CControl::Settings::LoadKeyboardLayoutFile(const char* layoutFile)
{
ControlInput::Keyboard::Layout layout;
controlVerifyf(PARSER.LoadObject(layoutFile, "", layout), "Error loading keyboard layout file '%s'!", layoutFile);
#if RSG_ASSERT
atRangeArray<bool, ControlInput::Keyboard::MAX_NUM_KEYS> keyLoaded;
#endif // RSG_ASSERT
m_KeyInfo.Resize(m_KeyInfo.max_size());
controlAssert(m_KeyInfo.max_size() == keyLoaded.max_size());
for(u32 i = 0; i < m_KeyInfo.size(); ++i)
{
m_KeyInfo[i].m_Icon = KeyInfo::INVALID_ICON;
safecpy(m_KeyInfo[i].m_Text, "???");
ASSERT_ONLY(keyLoaded[i] = false);
}
for(u32 i = 0; i < layout.m_Keys.size(); ++i)
{
ControlInput::Keyboard::KeyInfo& key = layout.m_Keys[i];
controlAssertf(key.m_Key != 0, "Error parsing keyboard layout file '%s'; invalid key name", layoutFile);
if(key.m_Key > 0 && key.m_Key < m_KeyInfo.size())
{
KeyInfo& info = m_KeyInfo[key.m_Key];
controlAssertf( keyLoaded[key.m_Key] == false,
"'%s' has multiple entries in keyboard layout file '%s'!",
GetParameterText(static_cast<ioMapperParameter>(key.m_Key)),
layoutFile );
ASSERT_ONLY(keyLoaded[key.m_Key] = true);
if(key.m_Icon == KeyInfo::TEXT_ICON || key.m_Icon == KeyInfo::LARGE_TEXT_ICON)
{
if( controlVerifyf(StringLength(key.m_Text) != 0,
"Invalid key text for %s as there is not icon in '%s'!",
GetParameterText(static_cast<ioMapperParameter>(key.m_Key)),
layoutFile) )
{
info.m_Icon = key.m_Icon;
safecpy(info.m_Text, key.m_Text);
}
else
{
info.m_Icon = KeyInfo::INVALID_ICON;
}
}
else
{
info.m_Icon = key.m_Icon;
}
}
}
#if RSG_ASSERT
// NOTE: This is incredibly slow as GetParameterText() does a linier search through an array of all parameter names
// (not just key codes).
//
// We are only doing this so we can catch a keycode that does not have icon information in debug builds!
for(u32 i = 0; i < keyLoaded.size(); ++i)
{
const char* keyName = GetParameterText(static_cast<ioMapperParameter>(i));
if( strnicmp(keyName, "KEY_", 4) == 0 &&
stricmp(keyName, "KEY_NULL") != 0 &&
strnicmp(keyName, "KEY_RAGE_", 9) != 0 &&
stricmp(keyName, "KEY_CHATPAD_GREEN_SHIFT") != 0 &&
stricmp(keyName, "KEY_CHATPAD_ORANGE_SHIFT") != 0 )
{
controlAssertf(keyLoaded[i] != false, "'%s' has no keyboard layout information in '%s'!", keyName, layoutFile);
}
}
#endif /// RSG_ASSERT
}
void CControl::Settings::EnumKeyboardLayoutFilesCallback(const fiFindData& data, void* userArg)
{
const char* ext = ASSET.FindExtensionInPath(data.m_Name);
if(ext != NULL && strcasecmp(ext, "." DEFAULT_SETTINGS_EXT) == 0)
{
static_cast<EnumFileList*>(userArg)->PushAndGrow(data.m_Name);
}
}
#endif // KEYBOARD_MOUSE_SUPPORT
#if RSG_PC
void CControl::Settings::RebuildGamepadDefinitionList()
{
controlDisplayf("Rebuilding gamepad definition scan!");
m_GamepadDefinitionList.m_Devices.Reset();
// load default gamepad definitions.
controlDisplayf("Loading default gamepad definition file '" PC_GAMEPAD_DEFINITION_FILE "'!");
if( ASSET.Exists(PC_GAMEPAD_DEFINITION_FILE, "")
&& !controlVerifyf(PARSER.LoadObject(PC_GAMEPAD_DEFINITION_FILE, "", m_GamepadDefinitionList), "Error loading default gamepad definitions file!") )
{
// error loading so tidy up structure.
m_GamepadDefinitionList.m_Devices.Reset();
controlErrorf("Error loading gamepad definition file '" PC_GAMEPAD_DEFINITION_FILE "'!");
}
char path[PATH_BUFFER_SIZE] = {0};
GetUserSettingsPath(path, PC_USER_GAMEPAD_DEFINITION_FILE);
ControlInput::Gamepad::DefinitionList userDefinitions;
if( ASSET.Exists(path, "") )
{
controlDisplayf("Loading user gamepad definition file '%s'!", path);
if(controlVerifyf(PARSER.LoadObject(path, "", userDefinitions), "Error loading user gamepad definitions file!") )
{
// If the user settings successfully loaded then copy the user settings over the defaults.
atMap< atFinalHashString, ControlInput::Gamepad::Definition >::Iterator iterator = userDefinitions.m_Devices.CreateIterator();
for(iterator.Start(); !iterator.AtEnd(); iterator.Next())
{
controlDisplayf("Custom user definition for device '%s' loaded!", iterator.GetKey().GetCStr());
m_GamepadDefinitionList.m_Devices[iterator.GetKey()] = iterator.GetData();
}
}
else
{
controlErrorf("Error loading gamepad definition file '%s'!", path);
}
}
else
{
controlDisplayf("User gamepad definition file '%s' does not exist!", path);
}
}
const ControlInput::Gamepad::Definition* CControl::Settings::GetGamepadDefinition(const ioJoystick &stick) const
{
const ControlInput::Gamepad::Definition* definition = m_GamepadDefinitionList.m_Devices.Access(atFinalHashString(stick.GetProductGuidStr()));
if(definition == NULL)
{
definition = m_GamepadDefinitionList.m_Devices.Access(atFinalHashString("Default"));
}
return definition;
}
const ControlInput::Gamepad::Definition* CControl::Settings::GetValidGamepadDefinition(const ioJoystick &stick) const
{
const ControlInput::Gamepad::Definition* definition = GetGamepadDefinition(stick);
if(definition != NULL && definition->m_Definitions.size() > 0)
{
return definition;
}
return NULL;
}
#if !RSG_FINAL
void CControl::EmulateXInputPads()
{
// This is a hack to get the debug cammera working on the PS4 controller on PC. This is needed for people working from home.
if(PARAM_enableXInputEmulation.Get())
{
for(int stickIndex = 0, padIndex = 0; stickIndex < ioJoystick::GetStickCount() && padIndex < ioPad::MAX_PADS; ++stickIndex)
{
const ioJoystick& stick = ioJoystick::GetStick(stickIndex);
const ControlInput::Gamepad::Definition* pDefinition = ms_settings.GetGamepadDefinition(stick);
if(pDefinition)
{
for(int definitionIndex = 0; definitionIndex < pDefinition->m_Definitions.size(); ++definitionIndex)
{
const rage::ControlInput::Gamepad::Source& source = pDefinition->m_Definitions[definitionIndex];
const float value = GetJoystickValue(stick, source.m_JoystickParameter);
SetPadValue(padIndex, source.m_PadParameter, value);
}
++padIndex;
}
}
// Moved to end because sm_Updaters may modify pad values (e.g. ioFFWheel)
PF_PUSH_TIMEBAR("ioPad::UpdateDebugAll");
ioPad::UpdateDebugAll();
PF_POP_TIMEBAR();
}
}
float CControl::GetJoystickValue(const ioJoystick& stick, ioMapperParameter param)
{
if(param >= IOM_JOYSTICK_BUTTON1 && param <= IOM_JOYSTICK_BUTTON32)
{
const unsigned buttonBit = 0x1 << (param - IOM_JOYSTICK_BUTTON1);
if((stick.GetButtons() & buttonBit) == buttonBit)
{
return 1.0f;
}
else
{
return 0.0f;
}
}
else if(param >= IOM_JOYSTICK_AXIS1 && param <= IOM_JOYSTICK_AXIS8)
{
const int axis = param - IOM_JOYSTICK_AXIS1;
return stick.GetNormAxis(axis);
}
else if(param >= IOM_POV1_UP && param <= IOM_POV4_LEFT)
{
enum {
POV_UP = 0,
POV_RIGHT,
POV_DOWN,
POV_LEFT
};
const int povOffset = param - IOM_POV1_UP;
const int povIndex = povOffset / 4; // 4 for the 4 directions, up down, left and right.
const int povDirection = povOffset % 4;
const int povValue = stick.GetPOV(povIndex);
// -1 is center
if(povValue != -1)
{
if( (povDirection == POV_UP && (povValue > 27000 || povValue < 9000)) || // up
(povDirection == POV_RIGHT && (povValue > 0 && povValue < 18000)) || // right
(povDirection == POV_DOWN && (povValue > 9000 && povValue < 27000)) || // down
(povDirection == POV_LEFT && (povValue > 18000)) ) // left
{
return 1.0f;
}
}
return 0.0f;
}
return 0.0f;
}
void CControl::SetPadValue(int padIndex, ioMapperParameter param, float value)
{
ioPad& pad = ioPad::GetPad(padIndex);
if(param >= L2 && param <= LLEFT)
{
const int button = param - L2;
pad.MergeButton(button, value);
}
else if(param >= IOM_AXIS_LX && param <= IOM_AXIS_RY)
{
const int axis = param - IOM_AXIS_LX;
pad.MergeAxis(axis, value);
}
}
#endif // !RSG_FINAL
#endif // RSG_PC
#if __BANK
CControl::RagData::RagData()
: m_ForceDisabled(false)
, m_InputValue(0.0f)
, m_Enabled(true)
{
#if __WIN32PC
m_KeyboardMousePrimary[0] = '\0';
m_KeyboardMouseSecondary[0] = '\0';
#endif // __WIN32PC
}
void CControl::InitWidgets( bkBank& bank, const char *title )
{
bank.PushGroup(title);
char buffer[255];
buffer[0] = '\0';
// create a list of available inputs.
// go through each input and add key mapping options.
for(s32 mapperIndex = 0; mapperIndex < MAPPERS_MAX; ++mapperIndex)
{
//TODO: replace parser text with descriptive alternative.
bank.PushGroup(parser_rage__Mapper_Strings[mapperIndex]);
for(s32 inputIndex = 0; inputIndex < MAX_INPUTS; ++inputIndex)
{
if(ms_settings.m_InputMappers[inputIndex] == mapperIndex)
{
//TODO: replace parser text with descriptive alternative.
const char *inputName = parser_rage__InputType_Data.m_Names[inputIndex];
RagData& data = m_ragData[inputIndex];
bank.AddTitle(inputName);
bank.AddToggle("Force Disable:", &data.m_ForceDisabled);
bank.AddText("Enabled:", &data.m_Enabled, true);
bank.AddText("Value:", &data.m_InputValue, true);
bank.AddSeparator();
}
}
bank.PopGroup();
}
#if LIGHT_EFFECTS_SUPPORT
bank.AddToggle("Override Light Effect", &m_RagLightEffectEnabled, datCallback(MFA(CControl::UpdateRagLightEffect), this));
bank.AddColor("Light Effect Color", &m_RagLightEffectColor, datCallback(MFA(CControl::UpdateRagLightEffect), this));
bank.AddColor("Output Effect Color", &m_RagDebugLightDevice.m_Color);
m_ActiveLightDevices.Push(&m_RagDebugLightDevice);
#endif // LIGHT_EFFECTS_SUPPORT
bank.AddToggle("Toggle Run", &m_ToggleRunOn);
bank.PopGroup();
}
void CControl::SetMappingsTest(s32 iIndex)
{
SetInitialDefaultMappings(false);
switch(iIndex)
{
case 0:
break;
case 1:
break;
case 2:
break;
default:
Assertf(false, "Invalid index [%d] passed to CControl::SetMappingsTest", iIndex);
break;
}
}
#if __WIN32PC
CControl::JoystickDefinitionRagData::JoystickDefinitionRagData()
: m_LastDeviceIndex(0)
, m_AutoSave(true)
{
m_Guid[0] = '\0';
m_Status[0] = '\0';
for(int i = 0; i < ioJoystick::MAX_STICKS; ++i)
{
m_Name[i][0] = '\0';
m_NameComboHook[i] = m_Name[i];
}
for(int i = 0; i < NUM_PAD_SOURCES; ++i)
{
m_Parameters[i][0] = '\0';
m_Sources[i][0] = '\0';
}
safecpy(m_SettingsPath, PC_GAMEPAD_DEFINITION_FILE, RAGE_MAX_PATH);
}
void CControl::AddKeyboardMouseUpdateButtonToRag(bkBank& bank, InputType input, const char* sourceName, bool isPrimary)
{
const char* typeStr = NULL;
Member1 func = NULL;
char* inputString = NULL;
if(isPrimary)
{
typeStr = "Primary";
inputString = m_ragData[input].m_KeyboardMousePrimary;
func = MFA1(CControl::UpdatePrimaryKeyMapping);
}
else
{
typeStr = "Secondary";
inputString = m_ragData[input].m_KeyboardMouseSecondary;
func = MFA1(CControl::UpdateSecondaryKeyMapping);
}
char buffer[255];
formatf(buffer, "%s Assigned:", typeStr);
safecpy(inputString, sourceName, RagData::TEXT_SIZE);
bank.AddText(buffer, inputString, RagData::TEXT_SIZE, true);
formatf(buffer, "Update %s", typeStr);
bank.AddButton( buffer, datCallback(func, this, (CallbackData)input) );
}
void CControl::AddJoystickUpdateButtonToRag(bkBank& bank, InputType input, const char* sourceName)
{
safecpy(m_ragData[input].m_Joystick, sourceName, RagData::TEXT_SIZE);
bank.AddText("Joystick Assigned:", m_ragData[input].m_Joystick, RagData::TEXT_SIZE, true);
bank.AddButton( "Update Joystick", datCallback(MFA1(CControl::UpdateJoystickMapping), this, (CallbackData)input) );
}
void CControl::UpdatePrimaryKeyMapping( CallbackData data )
{
// Unfortunately we need to cast to uptr before we can cast to InputType.
m_currentMapping.m_Input = static_cast<InputType>((uptr)data);
m_currentMapping.m_Type = PC_KEYBOARD_MOUSE;
m_currentMapping.m_MappingIndex = 0;
m_currentMapping.m_MappingCameFromRag = true; // Rag will automatically overwrite conflicts.
}
void CControl::UpdateSecondaryKeyMapping( CallbackData data )
{
// Unfortunately we need to cast to uptr before we can cast to InputType.
m_currentMapping.m_Input = static_cast<InputType>((uptr)data);
m_currentMapping.m_Type = PC_KEYBOARD_MOUSE;
m_currentMapping.m_MappingIndex = 1;
m_currentMapping.m_MappingCameFromRag = true; // Rag will automatically overwrite conflicts.
}
void CControl::UpdateJoystickMapping( CallbackData data )
{
// Unfortunately we need to cast to uptr before we can cast to InputType.
m_currentMapping.m_Input = static_cast<InputType>((uptr)data);
m_currentMapping.m_Type = JOYSTICK;
m_currentMapping.m_MappingIndex = 0;
m_currentMapping.m_MappingCameFromRag = true; // Rag will automatically overwrite conflicts.
}
void CControl::SwitchJoystickDefinitionDevice()
{
if(controlVerifyf(m_CurrentJoystickDefinition.m_DeviceIndex < ioJoystick::GetStickCount(), "Invalid stick id"))
{
// ensure the stick is not an XInput device.
if(CControlMgr::GetPad(m_CurrentJoystickDefinition.m_DeviceIndex).IsXBox360CompatiblePad())
{
m_CurrentJoystickDefinition.m_DeviceIndex = m_JoystickDefinitionRagData.m_LastDeviceIndex;
safecpy(m_JoystickDefinitionRagData.m_Status, "Cannot update XInput device!", JoystickDefinitionRagData::TEXT_SIZE);
}
else if(!m_JoystickDefinitionRagData.m_AutoSave || SaveCurrentJoystickDefinition(m_JoystickDefinitionRagData.m_SettingsPath))
{
if(!m_JoystickDefinitionRagData.m_AutoSave)
{
safecpy(m_JoystickDefinitionRagData.m_Status, "Auto-saved succeeded!", JoystickDefinitionRagData::TEXT_SIZE);
}
LoadCurrentJoystickDefinition(m_JoystickDefinitionRagData.m_SettingsPath);
m_JoystickDefinitionRagData.m_LastDeviceIndex = m_CurrentJoystickDefinition.m_DeviceIndex;
}
else
{
m_CurrentJoystickDefinition.m_DeviceIndex = m_JoystickDefinitionRagData.m_LastDeviceIndex;
safecpy(m_JoystickDefinitionRagData.m_Status, "Save failed! Check read-only status.", JoystickDefinitionRagData::TEXT_SIZE);
}
}
else
{
m_CurrentJoystickDefinition.m_DeviceIndex = m_JoystickDefinitionRagData.m_LastDeviceIndex;
safecpy(m_JoystickDefinitionRagData.m_Status, "Invalid Stick id!", JoystickDefinitionRagData::TEXT_SIZE);
}
}
void CControl::CalibrateJoystickDevice()
{
m_CurrentJoystickDefinition.m_Calibrate = true;
}
void CControl::SaveJoystickDefinitionDevice()
{
if(SaveCurrentJoystickDefinition(m_JoystickDefinitionRagData.m_SettingsPath) && ioJoystick::SaveUserCalibrations(true))
{
safecpy(m_JoystickDefinitionRagData.m_Status, "Successfully Saved", JoystickDefinitionRagData::TEXT_SIZE);
}
}
void CControl::ScanJoystickDefinitionDevice( CallbackData data )
{
GamepadDefinitionSource param = static_cast<GamepadDefinitionSource>((uptr)data);
if(controlVerifyf(param >= 0 && param < NUM_PAD_SOURCES, "Invalid gamepad parameter!"))
{
m_CurrentJoystickDefinition.m_PadParameter = ConvertGamepadInputToParameter(param);
}
}
void CControl::UpdateJoystickDefinitionRagData()
{
safecpy(m_JoystickDefinitionRagData.m_Guid, m_CurrentJoystickDefinition.m_Guid.GetCStr(), ControlInput::Gamepad::GUID_SIZE);
for(u32 i = 0; i < NUM_PAD_SOURCES; ++i)
{
m_JoystickDefinitionRagData.m_Parameters[i][0] = '\0';
m_JoystickDefinitionRagData.m_Sources[i][0] = '\0';
}
m_JoystickDefinitionRagData.m_Status[0] = '\0';
for(u32 i = 0; i < m_CurrentJoystickDefinition.m_Data.m_Definitions.size(); ++i)
{
ControlInput::Gamepad::Source& definition = m_CurrentJoystickDefinition.m_Data.m_Definitions[i];
GamepadDefinitionSource param = ConvertParameterToGampadInput(definition.m_PadParameter);
if(param != INVALID_PAD_SOURCE)
{
safecpy( m_JoystickDefinitionRagData.m_Parameters[param],
GetParameterText(definition.m_JoystickParameter),
JoystickDefinitionRagData::TEXT_SIZE );
safecpy( m_JoystickDefinitionRagData.m_Sources[param],
GetSourceText(definition.m_JoystickSource),
JoystickDefinitionRagData::TEXT_SIZE );
}
}
}
// Create mapping widgets in rag.
void CControl::InitMappingWidgets(bkBank& bank)
{
char buffer[255];
buffer[0] = '\0';
bank.PushGroup("PC Mappings");
bank.AddToggle("Toggle Aim", &m_ToggleAim);
bank.AddText("Current Dynamic Control Scheme:", m_RagLoadedPCControlScheme, RagData::TEXT_SIZE, true);
bank.AddText("Dynamic Control Scheme:", m_RagPCControlScheme, RagData::TEXT_SIZE);
bank.AddButton("Load Dynamic Control Scheme", datCallback(MFA(CControl::RagLoadPCMappings), this));
bank.PushGroup("Mouse");
bank.AddToggle("Auto-centered Steering for Cars", &m_MouseVehicleCarAutoCenter, datCallback(MFA(CControl::ReloadMappings), this));
bank.AddToggle("Auto-centered Steering for Flying", &m_MouseVehicleFlyAutoCenter, datCallback(MFA(CControl::ReloadMappings), this));
bank.AddToggle("Auto-centered Steering for Subs", &m_MouseVehicleSubAutoCenter, datCallback(MFA(CControl::ReloadMappings), this));
bank.AddSlider("On Foot Min Sensitivity", &ms_settings.m_MouseSettings.m_OnFootMinMouseSensitivity, 0.0001f, 100.0f, 0.1f, datCallback(MFA(CControl::ReloadMappings), this));
bank.AddSlider("On Foot Max Sensitivity", &ms_settings.m_MouseSettings.m_OnFootMaxMouseSensitivity, 1.0f, 100.0f, 0.1f, datCallback(MFA(CControl::ReloadMappings), this));
bank.AddSlider("On Foot Power Curve", &ms_settings.m_MouseSettings.m_OnFootMouseSensitivityPower, 0.0001f, 100.0f, 0.1f, datCallback(MFA(CControl::ReloadMappings), this));
bank.AddSlider("In Vehicle Min Sensitivity", &ms_settings.m_MouseSettings.m_InVehicleMinMouseSensitivity, 0.0001f, 100.0f, 0.1f, datCallback(MFA(CControl::ReloadMappings), this));
bank.AddSlider("In Vehicle Max Sensitivity", &ms_settings.m_MouseSettings.m_InVehicleMaxMouseSensitivity, 0.0001f, 100.0f, 0.1f, datCallback(MFA(CControl::ReloadMappings), this));
bank.AddSlider("In Vehicle Power Curve", &ms_settings.m_MouseSettings.m_InVehicleMouseSensitivityPower, 0.0001f, 100.0f, 0.1f, datCallback(MFA(CControl::ReloadMappings), this));
bank.PopGroup();
for(u32 mappingListIndex = 0; mappingListIndex < ms_settings.m_MappingSettings.m_MappingList.size(); ++mappingListIndex)
{
// The current mapping list (e.g. page/section of controls such as On Foot or Melee).
const ControlInput::MappingList& mappingList = ms_settings.m_MappingSettings.m_MappingList[mappingListIndex];
bank.PushGroup(mappingList.m_Name.GetCStr());
// Loop through all categories in the mapping list.
for (u32 catagoryIndex = 0; catagoryIndex < mappingList.m_Categories.size(); ++catagoryIndex)
{
const ControlInput::InputList* list = ms_settings.m_MappingSettings.m_Categories.Access(mappingList.m_Categories[catagoryIndex]);
if( controlVerifyf(list != NULL,
"Invalid category '%s' (%d) whilst showing bank mappings widgets",
mappingList.m_Categories[catagoryIndex].TryGetCStr(),
mappingList.m_Categories[catagoryIndex].GetHash()) )
{
const atArray<InputType>& inputs = list->m_Inputs;
// Loop through all the inputs in the category.
for(u32 inputIndex = 0; inputIndex < inputs.size(); ++inputIndex)
{
InputType input = inputs[inputIndex];
Mapper mapperId = ms_settings.m_InputMappers[input];
bank.AddTitle(GetInputName(input));
if( mapperId != DEPRECATED_MAPPER &&
controlVerifyf(mapperId >= 0 && mapperId < MAPPERS_MAX, "Invalid mapper assigned to %s!", GetInputName(input)) )
{
ioMapper::ioSourceList sources;
const ioMapper& mapper = m_Mappers[ms_settings.m_InputMappers[input]];
mapper.GetMappedSources(m_inputs[input], sources);// Get all mapping to the input.
// Each input can have a primary and a secondary mapping.
u32 mappingCount = 0;
// indicates if the joystick mapping has been added.
bool joystickAdded = false;
char buffer[RagData::TEXT_SIZE];
// only show two mappings for an input
for(s32 sourceIndex = 0; sourceIndex < sources.size() && mappingCount < 2; ++sourceIndex)
{
const ioSource& source = sources[sourceIndex];
MappingType type = GetMappingType(source.m_Device);
// only add the first two pc keyboard/mouse mappings.
if(mappingCount < 2 && type == PC_KEYBOARD_MOUSE)
{
GetMappingParameterName(source, buffer, RagData::TEXT_SIZE);
AddKeyboardMouseUpdateButtonToRag(bank, input, buffer, (mappingCount == 0));
++mappingCount;
}
// add joystick text
else if(!joystickAdded && type == JOYSTICK)
{
GetMappingParameterName(source, buffer, RagData::TEXT_SIZE);
AddJoystickUpdateButtonToRag(bank, input, buffer);
joystickAdded = true;
}
}
// if there are no primary mappings then add a button to map it
if(mappingCount == 0)
{
AddKeyboardMouseUpdateButtonToRag(bank, input, "<NOT ASSIGNED>", true);
}
// if there are no secondary mappings then add a button to map it
if(mappingCount < 2)
{
AddKeyboardMouseUpdateButtonToRag(bank, input, "<NOT ASSIGNED>", false);
}
// if there are no mapping for joystick
if(joystickAdded == false)
{
AddJoystickUpdateButtonToRag(bank, input, "<NOT ASSIGNED>");
}
}
bank.AddSeparator();
}
}
}
bank.PopGroup();
}
// add save load buttons.
bank.AddButton("Save All Control Changes", datCallback(MFA(CControl::SaveAllUserControls), this));
bank.AddButton("Validate Keyboard & Mouse Controls", datCallback(MFA(CControl::GetNumberOfMappingConflicts), this));
bank.PopGroup();
}
void CControl::InitJoystickCalibrationWidgets( bkBank &bank )
{
USES_CONVERSION;
if(ioJoystick::GetStickCount() > 0)
{
char16 buffer[100];
bool indexSet = false;
for(u8 i = 0; i < ioJoystick::GetStickCount(); ++i)
{
const CPad& pad = CControlMgr::GetPad(i);
// Do not add XInput devices.
if(!pad.IsXBox360CompatiblePad())
{
buffer[0] = '\0';
const ioJoystick& stick = ioJoystick::GetStick(i);
stick.GetProductName(buffer, 100);
safecpy(m_JoystickDefinitionRagData.m_Name[i], W2A(buffer), JoystickDefinitionRagData::NAME_LENGTH);
if(!indexSet)
{
indexSet = true;
m_CurrentJoystickDefinition.m_DeviceIndex = i;
m_JoystickDefinitionRagData.m_LastDeviceIndex = i;
}
}
else
{
buffer[0] = '\0';
safecpy(m_JoystickDefinitionRagData.m_Name[i], "--XInput Device--", JoystickDefinitionRagData::NAME_LENGTH);
}
}
// only add if there is a valid DirectInput device.
if(indexSet)
{
// Add Pad Definitions.
bank.PushGroup("Pad Definitions");
bank.AddText("Status:", m_JoystickDefinitionRagData.m_Status, JoystickDefinitionRagData::TEXT_SIZE, true);
bank.AddText("Settings Path:", m_JoystickDefinitionRagData.m_SettingsPath, RAGE_MAX_PATH);
bank.AddToggle("Autosave:", &m_JoystickDefinitionRagData.m_AutoSave);
bank.AddButton("Save Settings", datCallback(MFA(CControl::SaveJoystickDefinitionDevice), this));
bank.AddButton("Reload Controls", datCallback(MFA(CControl::ReloadMappings), this));
bank.AddButton("Calibrate Device", datCallback(MFA(CControl::CalibrateJoystickDevice), this));
bank.AddSeparator();
bank.AddCombo( "Device:",
&m_CurrentJoystickDefinition.m_DeviceIndex,
ioJoystick::GetStickCount(),
&m_JoystickDefinitionRagData.m_NameComboHook[0],
datCallback(MFA(CControl::SwitchJoystickDefinitionDevice), this) );
bank.AddText("GUID:", m_JoystickDefinitionRagData.m_Guid, ControlInput::Gamepad::GUID_SIZE, true);
bank.AddSeparator();
for(int i = 0; i < NUM_PAD_SOURCES; ++i)
{
ioMapperParameter param = ConvertGamepadInputToParameter(static_cast<GamepadDefinitionSource>(i));
bank.AddTitle(GetParameterText(param));
bank.AddText("Parameter:", m_JoystickDefinitionRagData.m_Parameters[i], JoystickDefinitionRagData::TEXT_SIZE, true);
bank.AddText("Source:", m_JoystickDefinitionRagData.m_Sources[i], JoystickDefinitionRagData::TEXT_SIZE, true);
if(!JoystickDefinitionData::IsAutoMapped(param))
{
bank.AddButton("Scan Device", datCallback(MFA1(CControl::ScanJoystickDefinitionDevice), this, (CallbackData)i));
}
bank.AddSeparator();
}
bank.PopGroup();
LoadCurrentJoystickDefinition(m_JoystickDefinitionRagData.m_SettingsPath);
}
}
}
void CControl::ReloadMappings()
{
if(!m_JoystickDefinitionRagData.m_AutoSave || SaveCurrentJoystickDefinition(m_JoystickDefinitionRagData.m_SettingsPath))
{
SetInitialDefaultMappings(true);
LoadDeviceControls(false);
ResetKeyboardMouseSettings();
safecpy(m_JoystickDefinitionRagData.m_Status, "Controls successfully loaded!", JoystickDefinitionRagData::TEXT_SIZE);
}
else
{
safecpy(m_JoystickDefinitionRagData.m_Status, "Auto-save Failed, controls not loaded!", JoystickDefinitionRagData::TEXT_SIZE);
}
}
void CControl::SaveAllUserControls()
{
SaveUserControls();
for(s32 i = 0; i < ioJoystick::GetStickCount(); ++i)
{
if(!CControlMgr::GetPad(i).IsXBox360CompatiblePad())
{
SaveUserControls(false, i);
}
}
}
#endif // __WIN32PC
#endif // __BANK
#if KEYBOARD_MOUSE_SUPPORT
void CControl::AddKeyboardMouseExtraSettings(const ControlInput::ControlSettings &settings)
{
ioMapper::ioSourceList sources;
for(u32 inputIndex = 0; inputIndex < settings.m_Mappings.size(); ++inputIndex)
{
const InputType input = settings.m_Mappings[inputIndex].m_Input;
GetSpecificInputSources(input, sources, ioSource::IOMD_KEYBOARD_MOUSE);
s32 needed = NUM_MAPPING_SCREEN_SLOTS - sources.size();
for(s32 slotIndex = 0; slotIndex < needed; ++slotIndex)
{
const static ioSource NULL_MAPPING = ioSource(IOMS_KEYBOARD, KEY_NULL, ioSource::IOMD_KEYBOARD_MOUSE);
AddMapping(input, NULL_MAPPING);
}
sources.Reset();
}
LoadSettings(settings, ioSource::IOMD_KEYBOARD_MOUSE);
}
void CControl::GetUserSettingsPath(char (&path)[PATH_BUFFER_SIZE], const char* const file)
{
CompileTimeAssert(PATH_BUFFER_SIZE == rgsc::RGSC_MAX_PATH);
if(IsUserSignedIn())
{
if(controlVerifyf(SUCCEEDED(g_rlPc.GetFileSystem()->GetTitleProfileDirectory(path, true)), "Failed to to get user directory!"))
{
safecat(path, PC_USER_SETTINGS_DIR);
safecat(path, file);
return;
}
else
{
controlErrorf("Failed to to get user directory!");
}
}
// Fallback to default path!
formatf(path, "user:/%s%s", PC_USER_SETTINGS_DIR, file);
}
bool CControl::IsUserSignedIn()
{
return g_rlPc.IsInitialized() && g_rlPc.GetProfileManager() && g_rlPc.GetProfileManager()->IsSignedIn();
}
bool CControl::UpdateMappingFromInputScan()
{
bool updated = false;
if(m_currentMapping.m_Input != UNDEFINED_INPUT)
{
// scan input for a new source.
ioSource source = m_currentMapping.m_Source;
if( source.m_DeviceIndex != ioSource::IOMD_UNKNOWN || // if we've already mapped it, skip the scan (since we'll wipe out our saved conflict)
GetMappingType(source.m_Device) == m_currentMapping.m_Type ||
(m_currentMapping.m_ScanMapper != NULL &&
m_currentMapping.m_ScanMapper->Scan(source, ioMapper::DEFAULT_DEVICE_SCAN_OPTIONS) &&
GetMappingType(source.m_Device) == m_currentMapping.m_Type) )
{
controlDebugf1( "Update key mapping for %s to %s.",
GetInputName(m_currentMapping.m_Input),
GetParameterText(ioMapper::ConvertParameterToMapperValue(source.m_Device, source.m_Parameter)) );
// NOTE: GetConflictingInputData() is slow.
if(!GetConflictingInputData(m_currentMapping.m_Input, source, true, m_currentMapping.m_Conflict, false))
{
UpdateMapping(m_currentMapping.m_Input, m_currentMapping.m_Type, source, m_currentMapping.m_MappingIndex, true);
updated = true;
controlDebugf1("Key mapping update successful.");
}
else if (m_currentMapping.m_Conflict.m_Conflicts.size() == 1 && m_currentMapping.m_Conflict.m_Conflicts[0].m_Input == m_currentMapping.m_Input)
{
if(m_currentMapping.m_Conflict.m_Conflicts[0].m_MappingIndex >= NUM_MAPPING_SCREEN_SLOTS)
{
CancelCurrentMapping();
}
else if(m_currentMapping.m_MappingIndex != m_currentMapping.m_Conflict.m_Conflicts[0].m_MappingIndex)
{
controlDebugf1( "Moving %s for %s from slot %d to %d.",
GetParameterText(ioMapper::ConvertParameterToMapperValue(source.m_Device, source.m_Parameter)),
GetInputName(m_currentMapping.m_Input),
m_currentMapping.m_Conflict.m_Conflicts[0].m_MappingIndex,
m_currentMapping.m_MappingIndex );
// Add mapping to new slot.
UpdateMapping(m_currentMapping.m_Input, m_currentMapping.m_Type, source, m_currentMapping.m_MappingIndex, true);
// Remove mapping from previous slot.
const ioSource unmapped(IOMS_KEYBOARD, KEY_NULL, ioSource::IOMD_KEYBOARD_MOUSE);
UpdateMapping( m_currentMapping.m_Conflict.m_Conflicts[0].m_Input,
m_currentMapping.m_Type,
unmapped,
m_currentMapping.m_Conflict.m_Conflicts[0].m_MappingIndex,
true );
}
else
{
controlDebugf1( "%s is already mapped to %s in slot %d, ignoring.",
GetParameterText(ioMapper::ConvertParameterToMapperValue(source.m_Device, source.m_Parameter)),
GetInputName(m_currentMapping.m_Input),
m_currentMapping.m_MappingIndex );
}
m_currentMapping.m_Conflict = MappingConflictData();
updated = true;
}
else
{
m_currentMapping.m_Source = source;
// We cache everything needed to update the mapping so the user has the option to replace the previous mapping.
updated = false;
controlDebugf1( "Key mapping failed! %s is already mapped to %s and %d more, waiting for user response.",
GetParameterText(ioMapper::ConvertParameterToMapperValue(source.m_Device, source.m_Parameter)),
GetInputName(m_currentMapping.m_Conflict.m_Conflicts[0].m_Input),
m_currentMapping.m_Conflict.m_NumConflictingInputs - 1 );
}
}
}
return updated;
}
void CControl::UpdateIdenticalMappings( const InputType input, const MappingType type, const ioSource& source, u32 mappingNumber )
{
// Identical mappings are for keyboard/mouse only!
if(type == PC_KEYBOARD_MOUSE)
{
// Check all input lists.
for (u32 listIdx = 0; listIdx < ms_settings.m_MappingSettings.m_IdenticalMappingLists.size(); ++listIdx)
{
const ControlInput::InputList& inputList = ms_settings.m_MappingSettings.m_IdenticalMappingLists[listIdx];
// See if the input is in this list, if it is, then all other inputs in this list need to be updated.
for(u32 inputIdx = 0; inputIdx < inputList.m_Inputs.size(); ++inputIdx)
{
if(inputList.m_Inputs[inputIdx] == input)
{
UpdateIdenticalMappingsInList(input, inputList, type, source, mappingNumber);
}
}
}
}
}
void CControl::UpdateIdenticalMappingsInList( const InputType input,
const ControlInput::InputList& inputList,
const MappingType type,
const ioSource& source,
u32 mappingNumber )
{
for(u32 i = 0; i < inputList.m_Inputs.size(); ++i)
{
if(inputList.m_Inputs[i] != input)
{
UpdateMapping(inputList.m_Inputs[i], type, source, mappingNumber, false);
}
}
}
bool CControl::GetConflictingInputData(const InputType input, const ioSource& source, bool allowConfictWithSelf, MappingConflictData& conflictData, bool assertOnConflict) const
{
// For sanity, clear previous conflicting data.
conflictData.m_Conflicts.Reset();
conflictData.m_NumConflictingInputs = 0u;
const u32 catagory = ms_settings.m_InputCategories[input];
// Check each conflict list and see if the inputs catagory is in it, if it is then we need to check all inputs on the
// list to see if any of them are already mapped to this source.
for(u32 listIdx = 0; listIdx < ms_settings.m_MappingSettings.m_ConflictList.size(); ++listIdx)
{
const ControlInput::ConflictList& conflictList = ms_settings.m_MappingSettings.m_ConflictList[listIdx];
// Loop through all categories in the mapping list.
for (u32 catIdx = 0; catIdx < conflictList.m_Categories.size(); ++catIdx)
{
// If the input's catagory is in the conflict list then we need to check for conflicts on all inputs in all
// the categories. If there is a conflict then the conflict mapping data has been saved inside m_currentMapping.
if(conflictList.m_Categories[catIdx].GetHash() == catagory)
{
GetConflictingInputData(input, conflictList, source, allowConfictWithSelf, conflictData, assertOnConflict);
}
}
}
return conflictData.m_Conflicts.size() > 0;
}
bool CControl::GetConflictingInputData( const InputType input,
const ControlInput::ConflictList& conflictList,
const ioSource& source,
bool allowConfictWithSelf,
MappingConflictData& conflictData,
bool ASSERT_ONLY(assertOnConflict)) const
{
const MappingType type = GetMappingType(source.m_Device);
controlDebugf2("Testing for mapping conflict for '%s'.", GetInputName(input));
// The keyboard key 'KEY_NULL' represents unbound and never conflicts!
if(type == PC_KEYBOARD_MOUSE && source.m_Parameter != KEY_NULL)
{
for (u32 catIdx = 0; catIdx < conflictList.m_Categories.size(); ++catIdx)
{
controlDebugf2("Testing for mapping conflict for '%s' in '%s' category.", GetInputName(input), conflictList.m_Categories[catIdx].GetCStr());
const ControlInput::InputList* list = ms_settings.m_MappingSettings.m_Categories.Access(conflictList.m_Categories[catIdx]);
if( controlVerifyf(list != NULL,
"Invalid category '%s' (%d) whilst checking mapping conflict!",
conflictList.m_Categories[catIdx].TryGetCStr(),
conflictList.m_Categories[catIdx].GetHash()) )
{
const ControlInput::InputList& inputList = *list;
for(u32 inputIdx = 0; inputIdx < inputList.m_Inputs.size(); ++inputIdx)
{
const InputType testInput = inputList.m_Inputs[inputIdx];
const ioMapper& mapper = m_Mappers[ms_settings.m_InputMappers[testInput]];
controlDebugf2("Testing for mapping conflict for '%s' with '%s'.", GetInputName(input), GetInputName(testInput));
ioMapper::ioSourceList sources;
mapper.GetMappedSources(m_inputs[testInput], sources);
u8 mappingIndex = 0;
for(u32 sourceIdx = 0; sourceIdx < sources.size(); ++sourceIdx)
{
const ioSource& testSource = sources[sourceIdx];
// If there is a conflict and no exceptions.
if( (testInput != input || allowConfictWithSelf) && testSource.m_Device == source.m_Device && testSource.m_Parameter == source.m_Parameter &&
testSource.m_DeviceIndex == source.m_DeviceIndex && HasConflictException(input, testInput) == false )
{
controlWarningf("'%s' conflicts with '%s'.", GetInputName(input), GetInputName(testInput));
s32 inputIndex = GetConflictIndex(conflictData.m_Conflicts, testInput);
if(inputIndex == -1 || conflictData.m_Conflicts[inputIndex].m_MappingIndex != mappingIndex)
{
controlAssertf(!assertOnConflict, "'%s' conflicts with '%s' and its a new conflict.", GetInputName(input), GetInputName(testInput));
controlWarningf("Mapping Conflict! %s is mapped to %s and %s!",
GetParameterText(ioMapper::ConvertParameterToMapperValue(testSource.m_Device, testSource.m_Parameter)),
GetInputName(input),
GetInputName(testInput) );
conflictData.m_IsNewConflict = true;
}
// If there are no conflicts yet add the input to the list.
if(conflictData.m_Conflicts.size() == 0)
{
conflictData.m_Conflicts.Push(MappingConflict(testInput, mappingIndex));
++conflictData.m_NumConflictingInputs;
}
// If the input is not already in the list of conflicts.
else if(inputIndex == -1)
{
// If the input is not mappable add the input to the front. NOTE that a mapping in a slot >= NUM_MAPPING_SCREEN_SLOTS
// is considered not mappable!
if(!IsInputMappable(testInput) || mappingIndex >= NUM_MAPPING_SCREEN_SLOTS)
{
// Make space if needed.
if(conflictData.m_Conflicts.size() == conflictData.m_Conflicts.max_size())
{
conflictData.m_Conflicts.Pop();
}
conflictData.m_Conflicts.Insert(0) = MappingConflict(testInput, mappingIndex);
++conflictData.m_NumConflictingInputs;
}
// If there is space in the array and the conflict.
else if(conflictData.m_Conflicts.size() < conflictData.m_Conflicts.max_size())
{
conflictData.m_Conflicts.Push(MappingConflict(testInput, mappingIndex));
++conflictData.m_NumConflictingInputs;
}
}
}
else if(GetMappingType(testSource.m_Device) == type)
{
++mappingIndex;
}
}
}
}
}
}
return conflictData.m_Conflicts.size() > 0;
}
s32 CControl::GetConflictIndex(const MappingConflictList& conflicts, InputType input)
{
for(s32 i = 0; i < conflicts.size(); ++i)
{
if(conflicts[i].m_Input == input)
{
return i;
}
}
return -1;
}
bool CControl::HasConflictException(const InputType input1, const InputType input2) const
{
controlDebugf2("Testing for conflict exception for '%s' and '%s'.", GetInputName(input1), GetInputName(input2));
// Testing if an input conflicts with itself can have undefined results based on the XML. Always consider as a conflict!
// Higher up code handles this case.
if(input1 == input2)
{
controlDebugf2("Two inputs are identical, an input always conflicts with itself!");
return false;
}
for(u32 i = 0; i < ms_settings.m_MappingSettings.m_ConflictExceptions.size(); ++i)
{
const ControlInput::InputList& exceptions = ms_settings.m_MappingSettings.m_ConflictExceptions[i];
bool input1InList = false;
for(u32 inputIndex = 0; inputIndex < exceptions.m_Inputs.size(); ++inputIndex)
{
if(exceptions.m_Inputs[inputIndex] == input1)
{
input1InList = true;
break;
}
}
if(input1InList)
{
for(u32 inputIndex = 0; inputIndex < exceptions.m_Inputs.size(); ++inputIndex)
{
if(exceptions.m_Inputs[inputIndex] == input2)
{
controlDebugf1("'%s' has a conflict exception for '%s'.", GetInputName(input1), GetInputName(input2));
return true;
}
}
}
}
// NOTE: If two inputs are identical mappings they implicitly have a conflict exception.
for(u32 i = 0; i < ms_settings.m_MappingSettings.m_IdenticalMappingLists.size(); ++i)
{
const ControlInput::InputList& exceptions = ms_settings.m_MappingSettings.m_IdenticalMappingLists[i];
bool input1InList = false;
for(u32 inputIndex = 0; inputIndex < exceptions.m_Inputs.size(); ++inputIndex)
{
if(exceptions.m_Inputs[inputIndex] == input1)
{
input1InList = true;
break;
}
}
if(input1InList)
{
for(u32 inputIndex = 0; inputIndex < exceptions.m_Inputs.size(); ++inputIndex)
{
if(exceptions.m_Inputs[inputIndex] == input2)
{
controlDebugf2("'%s' is and identical mapping for '%s' so cannot conflict.", GetInputName(input1), GetInputName(input2));
return true;
}
}
}
}
controlDebugf2("No conflict exception found for '%s' and '%s'.", GetInputName(input1), GetInputName(input2));
return false;
}
void CControl::UpdateLastFrameDeviceIndex()
{
//Update using keyboard
if (NetworkInterface::IsGameInProgress())
{
StatsInterface::UpdateUsingKeyboard(WasKeyboardMouseLastKnownSource(), m_LastFrameDeviceIndex, m_LastKnownSource.m_DeviceIndex);
}
m_LastFrameDeviceIndex = m_LastKnownSource.m_DeviceIndex;
}
void CControl::ResetKeyboardMouseSettings()
{
float sensitivity = CalculateMouseSensitivity( (static_cast<float>(CPauseMenu::GetMenuPreference(PREF_MOUSE_ON_FOOT_SCALE)) / MAX_MOUSE_SCALE),
ms_settings.m_MouseSettings.m_OnFootMinMouseSensitivity,
ms_settings.m_MouseSettings.m_OnFootMaxMouseSensitivity,
ms_settings.m_MouseSettings.m_OnFootMouseSensitivityPower );
m_Mappers[ON_FOOT].SetMouseScale(sensitivity);
DEBUG_DRAW_ONLY(m_OnFootMouseSensitivity = sensitivity);
eMenuPref eVehicleMouseScale = PREF_MOUSE_DRIVING_SCALE;
const CPed* pPed = CGameWorld::FindLocalPlayerSafe();
if(pPed && pPed->GetVehiclePedInside())
{
switch(pPed->GetVehiclePedInside()->GetVehicleType())
{
case VEHICLE_TYPE_PLANE:
{
eVehicleMouseScale = PREF_MOUSE_PLANE_SCALE;
break;
}
case VEHICLE_TYPE_HELI: // fall through
case VEHICLE_TYPE_BLIMP: // fall though
case VEHICLE_TYPE_AUTOGYRO:
{
eVehicleMouseScale = PREF_MOUSE_HELI_SCALE;
break;
}
case VEHICLE_TYPE_SUBMARINE:
{
eVehicleMouseScale = PREF_MOUSE_SUB_SCALE;
break;
}
default:
{
eVehicleMouseScale = PREF_MOUSE_DRIVING_SCALE;
break;
}
}
}
sensitivity = CalculateMouseSensitivity( (static_cast<float>(CPauseMenu::GetMenuPreference(eVehicleMouseScale)) / MAX_MOUSE_SCALE),
ms_settings.m_MouseSettings.m_InVehicleMinMouseSensitivity,
ms_settings.m_MouseSettings.m_InVehicleMaxMouseSensitivity,
ms_settings.m_MouseSettings.m_InVehicleMouseSensitivityPower );
m_Mappers[IN_VEHICLE].SetMouseScale(sensitivity);
DEBUG_DRAW_ONLY(m_InVehicleMouseSensitivity = sensitivity);
}
bool CControl::AreConflictingInputsMappable() const
{
if(HasMappingConflict())
{
const MappingConflictList& conflicts = GetMappingConflictList();
for(u32 i = 0; i < conflicts.size(); ++i)
{
// NOTE that a mapping in a slot >= NUM_MAPPING_SCREEN_SLOTSis considered not mappable!
if(!IsInputMappable(conflicts[i].m_Input) || conflicts[i].m_MappingIndex >= NUM_MAPPING_SCREEN_SLOTS)
{
return false;
}
}
}
return true;
}
#if DEBUG_DRAW
void CControl::DebugDrawMouseInput()
{
if(CControlMgr::IsDebugPadValuesOn() && grcDebugDraw::GetIsInitialized() && CLoadingScreens::AreActive() == false)
{
Vector2 pos(0.3f, 0.8f);
const float dy = grcDebugDraw::GetScreenSpaceTextHeight() / static_cast<float>(grcDevice::GetHeight());
char buffer[128] = {0};
// Relative Movement
formatf( buffer,
"Mouse Relative: (%d, %d)",
ioMouse::GetDX(),
ioMouse::GetDY() );
grcDebugDraw::Text(pos, Color32(255,0,0), buffer);
pos.y += dy;
// Scaled Relative (On Foot) Movement
formatf( buffer,
"Mouse Relative (On Foot): (%.3f, %.3f)",
static_cast<float>(ioMouse::GetDX()) * m_OnFootMouseSensitivity,
static_cast<float>(ioMouse::GetDY()) * m_OnFootMouseSensitivity);
grcDebugDraw::Text(pos, Color32(255,0,0), buffer);
pos.y += dy;
// Scaled Relative (On Foot) Movement
formatf( buffer,
"Mouse Relative (On Foot): (%.3f, %.3f)",
static_cast<float>(ioMouse::GetDX()) * m_InVehicleMouseSensitivity,
static_cast<float>(ioMouse::GetDY()) * m_InVehicleMouseSensitivity);
grcDebugDraw::Text(pos, Color32(255,0,0), buffer);
pos.y += dy;
// Absolute Position
formatf( buffer,
"Mouse Absolute: (%.3f, %.3f)",
ioMouse::GetNormX(),
ioMouse::GetNormY() );
grcDebugDraw::Text(pos, Color32(255,0,0), buffer);
pos.y += dy;
struct sDebugMouseEvent
{
Vector2 vNormPos;
u32 uTime;
unsigned mouseButton;
bool bIsPress;
void Draw(float fPercentComplete, bool bSolid)
{
static BankFloat MIN_SCREEN_SIZE = 0.010f;
static BankFloat MAX_SCREEN_SIZE = 0.030f;
Color32 drawColor(0x66666666);
drawColor.SetAlpha( Min(255,int(196.0*rage::dSlowOut(fPercentComplete) + 40.0)));
if( mouseButton & ioMouse::MOUSE_LEFT ) drawColor.SetGreen(255);
if( mouseButton & ioMouse::MOUSE_MIDDLE ) drawColor.SetBlue(255);
if( mouseButton & ioMouse::MOUSE_RIGHT ) drawColor.SetRed(255);
grcDebugDraw::Circle(vNormPos, Lerp(fPercentComplete, MIN_SCREEN_SIZE, MAX_SCREEN_SIZE), drawColor, bSolid);
}
};
Vector2 vNormPos(ioMouse::GetX() / static_cast<float>(grcDevice::GetWidth()), ioMouse::GetY() / static_cast<float>(grcDevice::GetHeight()));
// draw shadow of cursor first, so that mouse states sit on top of it
grcDebugDraw::Cross(vNormPos+Vector2(1/ static_cast<float>(grcDevice::GetWidth()), 1 / static_cast<float>(grcDevice::GetHeight())), 0.01f, Color_black);
static atArray<sDebugMouseEvent> s_MouseEvents;
u32 now = fwTimer::GetTimeInMilliseconds_NonPausedNonScaledClipped();
if( unsigned uDownBtns = ioMouse::GetButtons() )
{
sDebugMouseEvent newEvent;
newEvent.vNormPos = vNormPos;
newEvent.mouseButton = uDownBtns & (ioMouse::MOUSE_LEFT | ioMouse::MOUSE_RIGHT | ioMouse::MOUSE_MIDDLE); // only the three buttons will do for now
// just show current live state now
newEvent.Draw(0.0f, true);
}
if( unsigned uPressedBtns = ioMouse::GetPressedButtons() )
{
sDebugMouseEvent newEvent;
newEvent.uTime = now;
newEvent.vNormPos = vNormPos;
newEvent.mouseButton = uPressedBtns & (ioMouse::MOUSE_LEFT | ioMouse::MOUSE_RIGHT | ioMouse::MOUSE_MIDDLE); // only the three buttons will do for now
newEvent.bIsPress = true;
s_MouseEvents.PushAndGrow(newEvent);
}
if( unsigned uReleasedBtns = ioMouse::GetReleasedButtons() )
{
sDebugMouseEvent newEvent;
newEvent.uTime = now;
newEvent.vNormPos = vNormPos;
newEvent.mouseButton = uReleasedBtns & (ioMouse::MOUSE_LEFT | ioMouse::MOUSE_RIGHT | ioMouse::MOUSE_MIDDLE); // only the three buttons will do for now
newEvent.bIsPress = false;
s_MouseEvents.PushAndGrow(newEvent);
}
static BankFloat PRESS_LIFETIME = 125.0f;
static BankFloat RELEASE_LIFETIME = 250.0f;
// work from the back forward so if we delete we don't adjust the rest of the stack
for( int i=s_MouseEvents.GetCount()-1; i >= 0; i-- )
{
sDebugMouseEvent& curEvent = s_MouseEvents[i];
float fPercentComplete = float(now-curEvent.uTime)/(curEvent.bIsPress ? PRESS_LIFETIME : RELEASE_LIFETIME);
if( fPercentComplete > 1.0 )
{
s_MouseEvents.Delete(i);
continue;
}
// press events shrink towards the min size at twice the normal rate
curEvent.Draw( curEvent.bIsPress ? (1.0f-fPercentComplete) : fPercentComplete, false);
}
// draw the current mouse position last
grcDebugDraw::Cross(vNormPos, 0.01f, Color_HotPink);
}
}
#endif // DEBUG_DRAW
float CControl::CalculateMouseSensitivity(float percentage, float min, float max, float powArc)
{
if(controlVerifyf(powArc > 0.0f, "Invalid power to scale (%f), setting to 1.0f (linear)!", powArc) == false)
{
powArc = 1.0f;
}
if(controlVerifyf(min < max, "min (%f, is greater than max %f, swapping!", min, max) == false)
{
float tmp = min;
min = max;
max = tmp;
}
controlAssertf(percentage >= 0.0f && percentage <= 1.0f, "Invalid percentage %f, clamping!", percentage);
Clamp(percentage, 0.0f, 1.0f);
const float range = max - min;
return (powf(percentage, powArc) * range) + min;
}
void CControl::GetMappingCategories(MappingCategoriesList& categories)
{
categories.Reserve(ms_settings.m_MappingSettings.m_MappingList.size());
for(u32 i = 0; i < ms_settings.m_MappingSettings.m_MappingList.size(); ++i)
{
categories.PushAndGrow(ms_settings.m_MappingSettings.m_MappingList[i].m_Name);
}
}
void CControl::GetCategoryInputs(const atHashString& category, InputList& categoryInputs)
{
// Find the catagory.
for(u32 categoryIndex = 0; categoryIndex < ms_settings.m_MappingSettings.m_MappingList.size(); ++categoryIndex)
{
if(category == ms_settings.m_MappingSettings.m_MappingList[categoryIndex].m_Name)
{
const ControlInput::MappingList& mappingList = ms_settings.m_MappingSettings.m_MappingList[categoryIndex];
// First find out how many inputs there are so we can reserve the memory.
u32 numInputs = 0u;
// NOTE: in the mapping files the 'sections' are called categories but from the point of view of the mapping screen,
// categories are things like 'On Foot'.
for(u32 sectionIndex = 0; sectionIndex < mappingList.m_Categories.size(); ++sectionIndex)
{
const ControlInput::InputList* inputList = ms_settings.m_MappingSettings.m_Categories.Access(mappingList.m_Categories[sectionIndex]);
if(controlVerifyf(inputList != NULL, "Invalid category '%s'!", mappingList.m_Categories[sectionIndex].GetCStr()))
{
numInputs += inputList->m_Inputs.size();
}
}
categoryInputs.Reserve(numInputs);
// Get the inputs. NOTE: in the mapping files the 'sections' are called categories but from the point of view of the mapping screen,
// categories are things like 'On Foot'.
for(u32 sectionIndex = 0; sectionIndex < mappingList.m_Categories.size(); ++sectionIndex)
{
const ControlInput::InputList* inputList = ms_settings.m_MappingSettings.m_Categories.Access(mappingList.m_Categories[sectionIndex]);
if(inputList != NULL)
{
for(u32 inputIndex = 0; inputIndex < inputList->m_Inputs.size(); ++inputIndex)
{
categoryInputs.PushAndGrow(inputList->m_Inputs[inputIndex]);
}
}
}
return;
}
}
}
void CControl::StartInputMappingScan(InputType input, MappingType type, u8 mappingSlot)
{
m_currentMapping.Clear();
m_currentMapping.m_Input = input;
m_currentMapping.m_Type = type;
m_currentMapping.m_MappingIndex = mappingSlot;
BANK_ONLY(m_currentMapping.m_MappingCameFromRag = false);
}
void CControl::CancelCurrentMapping()
{
m_currentMapping.Clear();
}
void CControl::DeleteInputMapping(InputType input, MappingType type, u8 mappingSlot)
{
if(controlVerifyf(mappingSlot < NUM_MAPPING_SCREEN_SLOTS, "Cannot delete a mapping in slot %d as its not changable!", mappingSlot))
{
// Remove mapping from previous slot.
const ioSource unmapped(IOMS_KEYBOARD, KEY_NULL, ioSource::IOMD_KEYBOARD_MOUSE);
UpdateMapping( input,
type,
unmapped,
mappingSlot,
true );
}
}
#endif // KEYBOARD_MOUSE_SUPPORT
#if USE_STEAM_CONTROLLER
void CControl::CheckForSteamController()
{
if(m_SteamController == 0 && SteamController())
{
static bool hasInit = false;
if(!hasInit)
{
SteamController()->Init();
hasInit = true;
}
ControllerHandle_t controllers[STEAM_CONTROLLER_MAX_COUNT] = {0};
const int result = SteamController()->GetConnectedControllers(controllers);
if(result > 0)
{
m_SteamController = controllers[0];
INIT_PARSER;
const char* path = STEAM_CONTROLLER_SETTINGS_FILE;
#if RSG_BANK
if(PARAM_steamControllerSettingsFile.Get())
{
PARAM_steamControllerSettingsFile.Get(path);
}
#endif // RSG_BANK
controlVerifyf(PARSER.LoadObject(path, "", m_SteamControllerSettings), "Failed to load '%s'!", path);
SHUTDOWN_PARSER;
m_SteamActionSet = SteamController()->GetActionSetHandle(m_SteamControllerSettings.m_LoadSet);
SteamController()->ActivateActionSet(m_SteamController, m_SteamActionSet);
for(u32 i = 0; i < m_SteamControllerSettings.m_Buttons.size(); ++i)
{
const ControlInput::SteamController::SingleMappings& mapping = m_SteamControllerSettings.m_Buttons[i];
ControllerDigitalActionHandle_t handle = SteamController()->GetDigitalActionHandle(mapping.m_Action);
controlDisplayf("Steam Controller Digital Action '%s' has handle %p", mapping.m_Action, handle);
m_SteamHandleMap[mapping.m_Action] = handle;
}
for(u32 i = 0; i < m_SteamControllerSettings.m_Triggers.size(); ++i)
{
const ControlInput::SteamController::SingleMappings& mapping = m_SteamControllerSettings.m_Triggers[i];
ControllerAnalogActionHandle_t handle = SteamController()->GetAnalogActionHandle(mapping.m_Action);
controlDisplayf("Steam Controller Trigger Action '%s' has handle %p", mapping.m_Action, handle);
m_SteamHandleMap[mapping.m_Action] = handle;
}
for(u32 i = 0; i < m_SteamControllerSettings.m_Axis.size(); ++i)
{
const ControlInput::SteamController::AxisMappings& mapping = m_SteamControllerSettings.m_Axis[i];
ControllerAnalogActionHandle_t handle = SteamController()->GetAnalogActionHandle(mapping.m_Action);
controlDisplayf("Steam Controller Digital Action '%s' has handle %p", mapping.m_Action, handle);
m_SteamHandleMap[mapping.m_Action] = handle;
}
}
}
}
void CControl::UpdateSteamController()
{
if(m_SteamController != 0 && SteamController())
{
EControllerActionOrigin mappedOrigins[STEAM_CONTROLLER_MAX_ORIGINS] = {k_EControllerActionOrigin_None};
SteamController()->ActivateActionSet(m_SteamController, m_SteamActionSet);
for(u32 mappingIndex = 0; mappingIndex < m_SteamControllerSettings.m_Buttons.size(); ++mappingIndex)
{
const ControlInput::SteamController::SingleMappings mapping = m_SteamControllerSettings.m_Buttons[mappingIndex];
const ControllerDigitalActionHandle_t handle = m_SteamHandleMap[mapping.m_Action];
ControllerDigitalActionData_t data = SteamController()->GetDigitalActionData(m_SteamController, handle);
if(data.bActive)
{
const float value = (data.bState) ? 1.0f : 0.0f;
EControllerActionOrigin origin = k_EControllerActionOrigin_None;
if(SteamController()->GetDigitalActionOrigins(m_SteamController, m_SteamActionSet, handle, mappedOrigins) > 0)
{
origin = mappedOrigins[0];
}
m_SteamSources[origin].m_Type = SteamSource::BUTTON;
m_SteamSources[origin].m_Index = mappingIndex;
for(u32 inputIndex = 0; inputIndex < mapping.m_Inputs.size(); ++inputIndex)
{
UpdateInputFromSteamSource(mapping.m_Inputs[inputIndex], value, origin);
}
}
}
for(u32 mappingIndex = 0; mappingIndex < m_SteamControllerSettings.m_Triggers.size(); ++mappingIndex)
{
const ControlInput::SteamController::SingleMappings mapping = m_SteamControllerSettings.m_Triggers[mappingIndex];
const ControllerAnalogActionHandle_t handle = m_SteamHandleMap[mapping.m_Action];
ControllerAnalogActionData_t data = SteamController()->GetAnalogActionData(m_SteamController, handle);
if(data.bActive)
{
EControllerActionOrigin origin = k_EControllerActionOrigin_None;
if(SteamController()->GetAnalogActionOrigins(m_SteamController, m_SteamActionSet, handle, mappedOrigins) > 0)
{
origin = mappedOrigins[0];
}
m_SteamSources[origin].m_Type = SteamSource::TRIGGER;
m_SteamSources[origin].m_Index = mappingIndex;
for(u32 inputIndex = 0; inputIndex < mapping.m_Inputs.size(); ++inputIndex)
{
UpdateInputFromSteamSource(mapping.m_Inputs[inputIndex], data.x, origin);
}
}
}
for(u32 mappingIndex = 0; mappingIndex < m_SteamControllerSettings.m_Axis.size(); ++mappingIndex)
{
const ControlInput::SteamController::AxisMappings mapping = m_SteamControllerSettings.m_Axis[mappingIndex];
const ControllerAnalogActionHandle_t handle = m_SteamHandleMap[mapping.m_Action];
ControllerAnalogActionData_t data = SteamController()->GetAnalogActionData(m_SteamController, handle);
if(data.bActive)
{
EControllerActionOrigin origin = k_EControllerActionOrigin_None;
if(SteamController()->GetAnalogActionOrigins(m_SteamController, m_SteamActionSet, handle, mappedOrigins) > 0)
{
origin = mappedOrigins[0];
// We only support 1 icon for the gyro so use only the move.
if(origin > k_EControllerActionOrigin_Gyro_Move && origin <= k_EControllerActionOrigin_Gyro_Roll)
{
origin = k_EControllerActionOrigin_Gyro_Move;
}
}
m_SteamSources[origin].m_Type = SteamSource::AXIS;
m_SteamSources[origin].m_Index = mappingIndex;
// The Y axis is in a different direction to what we use internally.
if(data.eMode == k_EControllerSourceMode_JoystickMove)
{
data.y = -data.y;
}
data.x *= mapping.m_Scale;
data.y *= mapping.m_Scale;
for(u32 inputIndex = 0; inputIndex < mapping.m_XInputs.size(); ++inputIndex)
{
UpdateInputFromSteamSource(mapping.m_XInputs[inputIndex], data.x, origin);
}
if(data.x < 0.0f)
{
for(u32 inputIndex = 0; inputIndex < mapping.m_XLeftInputs.size(); ++inputIndex)
{
UpdateInputFromSteamSource(mapping.m_XLeftInputs[inputIndex], -data.x, origin);
}
}
else
{
for(u32 inputIndex = 0; inputIndex < mapping.m_XRightInputs.size(); ++inputIndex)
{
UpdateInputFromSteamSource(mapping.m_XRightInputs[inputIndex], data.x, origin);
}
}
for(u32 inputIndex = 0; inputIndex < mapping.m_YInputs.size(); ++inputIndex)
{
UpdateInputFromSteamSource(mapping.m_YInputs[inputIndex], data.y, origin);
}
if(data.y < 0.0f)
{
for(u32 inputIndex = 0; inputIndex < mapping.m_YUpInputs.size(); ++inputIndex)
{
UpdateInputFromSteamSource(mapping.m_YUpInputs[inputIndex], -data.y, origin);
}
}
else
{
for(u32 inputIndex = 0; inputIndex < mapping.m_YDownInputs.size(); ++inputIndex)
{
UpdateInputFromSteamSource(mapping.m_YDownInputs[inputIndex], data.y, origin);
}
}
}
}
}
}
void CControl::UpdateInputFromSteamSource(InputType input, float value, EControllerActionOrigin source)
{
if(controlVerifyf(input >= 0 && input < MAX_INPUTS, "Invalid input %d!", input))
{
ioValue& ioVal = m_inputs[input];
if( Abs(ioVal.GetValue()) < Abs(value))
{
// Invert the value back if the input is inverted as steam handles inversions itself.
if(ioVal.IsInverted())
{
value = -value;
}
ioVal.SetCurrentValue(value, ioSource(IOMS_GAME_CONTROLLED, source, ioSource::IOMD_GAME));
}
}
}
void CControl::GetSteamControllerInputSources(InputType input, ioMapper::ioSourceList& sources) const
{
if(m_SteamController && SteamController())
{
EControllerActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS] = {k_EControllerActionOrigin_None};
for(u32 buttonIndex = 0; buttonIndex < m_SteamControllerSettings.m_Buttons.size(); ++buttonIndex)
{
const ControlInput::SteamController::SingleMappings& mapping = m_SteamControllerSettings.m_Buttons[buttonIndex];
if(mapping.m_Inputs.Find(input) != -1)
{
const u64* handle = m_SteamHandleMap.Access(mapping.m_Action);
if(handle && SteamController()->GetDigitalActionOrigins(m_SteamController, m_SteamActionSet, *handle, origins) > 0)
{
sources.Push(ioSource(IOMS_GAME_CONTROLLED, origins[0], ioSource::IOMD_GAME));
}
}
}
for(u32 triggerIndex = 0; triggerIndex < m_SteamControllerSettings.m_Triggers.size(); ++triggerIndex)
{
const ControlInput::SteamController::SingleMappings& mapping = m_SteamControllerSettings.m_Triggers[triggerIndex];
if(mapping.m_Inputs.Find(input) != -1)
{
const u64* handle = m_SteamHandleMap.Access(mapping.m_Action);
if(handle && SteamController()->GetAnalogActionOrigins(m_SteamController, m_SteamActionSet, *handle, origins) > 0)
{
sources.Push(ioSource(IOMS_GAME_CONTROLLED, origins[0], ioSource::IOMD_GAME));
}
}
}
for(u32 axisIndex = 0; axisIndex < m_SteamControllerSettings.m_Axis.size(); ++axisIndex)
{
const ControlInput::SteamController::AxisMappings& mapping = m_SteamControllerSettings.m_Axis[axisIndex];
// Axis we need to check 6 array.
if( mapping.m_XInputs.Find(input) != -1 || mapping.m_YInputs.Find(input) != -1 ||
mapping.m_XLeftInputs.Find(input) != -1 || mapping.m_XRightInputs.Find(input) != -1 ||
mapping.m_YUpInputs.Find(input) != -1 || mapping.m_YDownInputs.Find(input) != -1 )
{
const u64* handle = m_SteamHandleMap.Access(mapping.m_Action);
if(handle && SteamController()->GetAnalogActionOrigins(m_SteamController, m_SteamActionSet, *handle, origins) > 0)
{
// We only support 1 icon for the gyro so use only the move.
if(origins[0] > k_EControllerActionOrigin_Gyro_Move && origins[0] <= k_EControllerActionOrigin_Gyro_Roll)
{
origins[0] = k_EControllerActionOrigin_Gyro_Move;
}
sources.Push(ioSource(IOMS_GAME_CONTROLLED, origins[0], ioSource::IOMD_GAME));
}
}
}
}
}
void CControl::DisableInputOnSteamControllerSource(const ioSource& source, const ioValue::DisableOptions& options)
{
if(source.m_Device == IOMS_GAME_CONTROLLED && source.m_DeviceIndex == ioSource::IOMD_GAME)
{
EControllerActionOrigin origin = static_cast<EControllerActionOrigin>(source.m_Parameter);
if(origin > k_EControllerActionOrigin_Gyro_Move && origin <= k_EControllerActionOrigin_Gyro_Roll)
{
origin = k_EControllerActionOrigin_Gyro_Move;
}
const SteamSource& steamSouce = m_SteamSources[origin];
switch(steamSouce.m_Type)
{
case SteamSource::BUTTON:
DisableInputsInArray(m_SteamControllerSettings.m_Buttons[steamSouce.m_Index].m_Inputs, options);
break;
case SteamSource::TRIGGER:
DisableInputsInArray(m_SteamControllerSettings.m_Triggers[steamSouce.m_Index].m_Inputs, options);
break;
case SteamSource::AXIS:
DisableInputsInArray(m_SteamControllerSettings.m_Axis[steamSouce.m_Index].m_XInputs, options);
DisableInputsInArray(m_SteamControllerSettings.m_Axis[steamSouce.m_Index].m_XLeftInputs, options);
DisableInputsInArray(m_SteamControllerSettings.m_Axis[steamSouce.m_Index].m_XRightInputs, options);
DisableInputsInArray(m_SteamControllerSettings.m_Axis[steamSouce.m_Index].m_YInputs, options);
DisableInputsInArray(m_SteamControllerSettings.m_Axis[steamSouce.m_Index].m_YUpInputs, options);
DisableInputsInArray(m_SteamControllerSettings.m_Axis[steamSouce.m_Index].m_YDownInputs, options);
break;
default:
break;
}
}
}
void CControl::DisableInputsInArray(const atArray<InputType>& inputs, const ioValue::DisableOptions& options)
{
for (u32 i = 0; i < inputs.size(); ++i)
{
DoDisableInput(inputs[i], options, false);
}
}
#endif // USE_STEAM_CONTROLLER
#if __WIN32PC
CControl::JoystickDefinitionData::JoystickDefinitionData()
: m_Data()
, m_Guid()
, m_PadParameter(IOM_UNDEFINED)
, m_ConflictingSource(INVALID_PAD_SOURCE)
, m_DeviceIndex(0)
, m_ScanStarted(false)
, m_ScanReady(false)
, m_Calibrate(false)
{
m_ScanOptions.m_JoystickOptions = ioMapper::ScanOptions::JOYSTICK_BUTTONS
| ioMapper::ScanOptions::JOYSTICK_POV
| ioMapper::ScanOptions::JOYSTICK_AXIS
| ioMapper::ScanOptions::JOYSTICK_AXIS_DIRECTION;
}
bool CControl::JoystickDefinitionData::GetAutoMappings( const ioSource& negativeSource, ioSource& positiveMapping, ioSource& fullAxisMapping, bool isPositive )
{
bool mappingFound = false;
if(negativeSource.m_Parameter >= IOM_JOYSTICK_AXIS1 && negativeSource.m_Parameter <= IOM_JOYSTICK_AXIS8)
{
if(negativeSource.m_Device == IOMS_JOYSTICK_AXIS_NEGATIVE)
{
positiveMapping = negativeSource;
fullAxisMapping = negativeSource;
positiveMapping.m_Device = IOMS_JOYSTICK_AXIS_POSITIVE;
if(isPositive)
{
fullAxisMapping.m_Device = IOMS_JOYSTICK_IAXIS;
}
else
{
fullAxisMapping.m_Device = IOMS_JOYSTICK_AXIS;
}
mappingFound = true;
}
else if(negativeSource.m_Device == IOMS_JOYSTICK_AXIS_POSITIVE)
{
positiveMapping = negativeSource;
fullAxisMapping = negativeSource;
positiveMapping.m_Device = IOMS_JOYSTICK_AXIS_NEGATIVE;
fullAxisMapping.m_Device = IOMS_JOYSTICK_IAXIS;
if(isPositive)
{
fullAxisMapping.m_Device = IOMS_JOYSTICK_AXIS;
}
else
{
fullAxisMapping.m_Device = IOMS_JOYSTICK_IAXIS;
}
mappingFound = true;
}
}
return mappingFound;
}
bool CControl::JoystickDefinitionData::GetAutoMappingParameters( const ioMapperParameter& parameter, ioMapperParameter& opposite, ioMapperParameter& fullAxis )
{
switch(parameter)
{
case IOM_AXIS_LX_RIGHT:
opposite = IOM_AXIS_LX_LEFT;
fullAxis = IOM_AXIS_LX;
return true;
case IOM_AXIS_LX_LEFT:
opposite = IOM_AXIS_LX_RIGHT;
fullAxis = IOM_AXIS_LX;
return true;
case IOM_AXIS_LY_DOWN:
opposite = IOM_AXIS_LY_UP;
fullAxis = IOM_AXIS_LY;
return true;
case IOM_AXIS_LY_UP:
opposite = IOM_AXIS_LY_DOWN;
fullAxis = IOM_AXIS_LY;
return true;
case IOM_AXIS_RX_RIGHT:
opposite = IOM_AXIS_RX_LEFT;
fullAxis = IOM_AXIS_RX;
return true;
case IOM_AXIS_RX_LEFT:
opposite = IOM_AXIS_RX_RIGHT;
fullAxis = IOM_AXIS_RX;
return true;
case IOM_AXIS_RY_DOWN:
opposite = IOM_AXIS_RY_UP;
fullAxis = IOM_AXIS_RY;
return true;
case IOM_AXIS_RY_UP:
opposite = IOM_AXIS_RY_DOWN;
fullAxis = IOM_AXIS_RY;
return true;
default:
return false;
}
}
ioMapperParameter CControl::JoystickDefinitionData::GetAlternateButtonMappingParameters( ioMapperParameter button )
{
switch(button)
{
case L2:
return L2_INDEX;
case R2:
return R2_INDEX;
case L1:
return L1_INDEX;
case R1:
return R1_INDEX;
case RUP:
return RUP_INDEX;
case RRIGHT:
return RRIGHT_INDEX;
case RDOWN:
return RDOWN_INDEX;
case RLEFT:
return RLEFT_INDEX;
case SELECT:
return SELECT_INDEX;
case L3:
return L3_INDEX;
case R3:
return R3_INDEX;
case START:
return START_INDEX;
case LUP:
return LUP_INDEX;
case LRIGHT:
return LRIGHT_INDEX;
case LDOWN:
return LDOWN_INDEX;
case LLEFT:
return LLEFT_INDEX;
case TOUCH:
return TOUCH_INDEX;
default:
return IOM_UNDEFINED;
}
}
void CControl::JoystickDefinitionData::ConvertPadMappingsToJoystickMappings(const ControlInput::Gamepad::Definition& configuration,
const ControlInput::ControlSettings& padMappings,
ControlInput::ControlSettings& joystickMappings )
{
joystickMappings.m_Mappings.Reserve(padMappings.m_Mappings.size());
// Convert mappings to direct input equivalents.
for(s32 mappingIndex = 0; mappingIndex < padMappings.m_Mappings.size(); ++mappingIndex)
{
const ControlInput::Mapping& mapping = padMappings.m_Mappings[mappingIndex];
if(mapping.m_Parameters.size() > 0)
{
// There is no guarantee of the order of inputs.
for(s32 configIndex = 0; configIndex < configuration.m_Definitions.size(); ++configIndex)
{
const ControlInput::Gamepad::Source& inputConfig = configuration.m_Definitions[configIndex];
// if this is the pad parameter we are looking for then use its joystick equivalent.
if( inputConfig.m_PadParameter == mapping.m_Parameters[0] ||
GetAlternateButtonMappingParameters(inputConfig.m_PadParameter) == mapping.m_Parameters[0])
{
ControlInput::Mapping newMapping;
newMapping.m_Input = mapping.m_Input;
newMapping.m_Source = inputConfig.m_JoystickSource;
newMapping.m_Parameters.Reserve(1);
newMapping.m_Parameters.Push(inputConfig.m_JoystickParameter);
joystickMappings.m_Mappings.PushAndGrow(newMapping);
// We don't stop the loop as some Joystick definitions (notably the default) may have several alternative for
// each input (such as axis or button for triggers).
}
}
}
}
}
void CControl::UpdateJoystickBindings()
{
if(m_CurrentJoystickDefinition.m_Calibrate)
{
if(ioJoystick::GetStick(m_CurrentJoystickDefinition.m_DeviceIndex).HasInputFocus())
{
ioJoystick::AutoCalibrate(m_CurrentJoystickDefinition.m_DeviceIndex, true);
m_CurrentJoystickDefinition.m_Calibrate = false;
}
// Do not scan the device if we are/need calibrating.
return;
}
PF_PUSH_TIMEBAR("controls.Update.joystickBindScan");
if(m_CurrentJoystickDefinition.m_PadParameter != IOM_UNDEFINED)
{
if(!m_CurrentJoystickDefinition.m_ScanStarted)
{
controlDebugf1( "Gamepad Definition - Start scan for '%s'. Waiting for no input.",
GetParameterText(m_CurrentJoystickDefinition.m_PadParameter) );
m_CurrentJoystickDefinition.m_ScanStarted = true;
m_CurrentJoystickDefinition.m_ScanReady = false;
ioMapper::BeginScan();
}
ioSource source;
m_CurrentJoystickDefinition.m_ScanOptions.m_DeviceID = m_CurrentJoystickDefinition.m_DeviceIndex;
// Wait for the button to be released before we allow scanning of the next button.
if(m_CurrentJoystickDefinition.m_ScanReady == false && m_Mappers[0].Scan(source, m_CurrentJoystickDefinition.m_ScanOptions) == false)
{
controlDebugf1( "Gamepad Definition - No Input detected, now scanning for '%s'.",
GetParameterText(m_CurrentJoystickDefinition.m_PadParameter) );
m_CurrentJoystickDefinition.m_ScanReady = true;
}
else if(m_CurrentJoystickDefinition.m_ScanReady && m_Mappers[0].Scan(source, m_CurrentJoystickDefinition.m_ScanOptions))
{
ioMapperParameter param = ioMapper::ConvertParameterToMapperValue(source.m_Device, source.m_Parameter);
controlDebugf1( "Gamepad Definition - Mapped '%s' to '%s'.",
GetParameterText(m_CurrentJoystickDefinition.m_PadParameter),
GetParameterText(param) );
// update parameter to mapper parameter.
source.m_Parameter = param;
// find the corresponding data.
ControlInput::Gamepad::Source definition;
definition.m_PadParameter = m_CurrentJoystickDefinition.m_PadParameter;
definition.m_JoystickParameter = param;
definition.m_JoystickSource = source.m_Device;
// Do not map a button/axis to more than one button/axis.
if( source.m_DeviceIndex < 0 || CControlMgr::GetPad(source.m_DeviceIndex).IsXBox360CompatiblePad() || IsGamepadDefinitionValid(definition) == false ||
(m_CurrentJoystickDefinition.m_DeviceIndex != ioSource::IOMD_ANY && source.m_DeviceIndex != m_CurrentJoystickDefinition.m_DeviceIndex) )
{
controlDebugf1( "Gamepad Definition - '%s' invalid for '%s', ignoring.",
GetParameterText(definition.m_JoystickParameter),
GetParameterText(m_CurrentJoystickDefinition.m_PadParameter) );
// Wait for button to be released.
m_CurrentJoystickDefinition.m_ScanReady = false;
}
else
{
m_CurrentJoystickDefinition.m_ConflictingSource = GetCurrentGamepadDefinition(definition);
if(m_CurrentJoystickDefinition.m_ConflictingSource != INVALID_PAD_SOURCE)
{
controlDebugf1( "Gamepad Definition - '%s' already mapped to '%s', ignoring.",
GetParameterText(definition.m_JoystickParameter),
GetParameterText(ConvertGamepadInputToParameter(m_CurrentJoystickDefinition.m_ConflictingSource)) );
// Wait for button to be released.
m_CurrentJoystickDefinition.m_ScanReady = false;
}
else
{
UpdateCurrentGamepadDefinition(definition);
ioMapperParameter positive;
ioMapperParameter fullAxis;
ioSource positiveSource;
ioSource fullAxisSource;
const bool isPositive = ( definition.m_PadParameter == IOM_AXIS_LX_RIGHT || definition.m_PadParameter == IOM_AXIS_LY_DOWN ||
definition.m_PadParameter == IOM_AXIS_RX_RIGHT || definition.m_PadParameter == IOM_AXIS_RY_DOWN );
// if the input should auto-map related axis and auto mappings can be generated.
if( JoystickDefinitionData::GetAutoMappingParameters(definition.m_PadParameter, positive, fullAxis)
&& JoystickDefinitionData::GetAutoMappings(source, positiveSource, fullAxisSource, isPositive) )
{
definition.m_PadParameter = positive;
definition.m_JoystickParameter = param;
definition.m_JoystickSource = positiveSource.m_Device;
UpdateCurrentGamepadDefinition(definition);
definition.m_PadParameter = fullAxis;
definition.m_JoystickParameter = param;
definition.m_JoystickSource = fullAxisSource.m_Device;
UpdateCurrentGamepadDefinition(definition);
}
BANK_ONLY(UpdateJoystickDefinitionRagData());
ioMapper::EndScan();
m_CurrentJoystickDefinition.m_ScanStarted = false;
m_CurrentJoystickDefinition.m_ScanReady = false;
m_CurrentJoystickDefinition.m_PadParameter = IOM_UNDEFINED;
m_CurrentJoystickDefinition.m_DeviceIndex = source.m_DeviceIndex;
m_CurrentJoystickDefinition.m_Guid = ioJoystick::GetStick(m_CurrentJoystickDefinition.m_DeviceIndex).GetProductGuidStr();
}
}
}
}
PF_POP_TIMEBAR();
}
void CControl::LoadGamepadControls(const ControlInput::Gamepad::Definition& definition, const ControlInput::ControlSettings& settings, s32 padIndex)
{
#if !RSG_FINAL
if(PARAM_enableXInputEmulation.Get())
{
return;
}
#endif // !RSG_FINAL
ControlInput::ControlSettings joystickSettings;
JoystickDefinitionData::ConvertPadMappingsToJoystickMappings(definition, settings, joystickSettings);
LoadSettings(joystickSettings, padIndex);
}
void CControl::ApplyDirectionalForce(float FORCE_FEEDBACK_ONLY(X), float FORCE_FEEDBACK_ONLY(Y), s32 FORCE_FEEDBACK_ONLY(timeMS))
{
#if FORCE_FEEDBACK_SUPPORT
if(m_LastKnownSource.m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE || !ioSource::IsValidDevice(m_LastKnownSource.m_DeviceIndex))
{
return;
}
s32 deviceId = (m_LastKnownSource.m_DeviceIndex == ioSource::IOMD_DEFAULT) ? m_padNum : m_LastKnownSource.m_DeviceIndex;
// if a joystick vibrate it.
if(GetMappingType(m_LastKnownSource.m_Device) == JOYSTICK)
{
ioJoystick::ApplyDirectionalForce(deviceId, X, Y, timeMS);
}
#endif // FORCE_FEEDBACK_SUPPORT
}
bool CControl::LoadPCScriptControlMappings(const atHashString& mappingSceme)
{
sysCriticalSection lock(m_Cs);
Assertf(strcmp(m_ControlSchemeScriptOwner, "") == 0, "'%s' has setup scripted controls but not shut them down!", m_ControlSchemeScriptOwner);
ASSERT_ONLY(safecpy(m_ControlSchemeScriptOwner, CTheScripts::GetCurrentScriptName()));
m_CurrentScriptDynamicScheme = mappingSceme;
return LoadDynamicControlMappings();
}
bool CControl::SwitchPCScriptControlMappings(const atHashString& mappingSceme)
{
sysCriticalSection lock(m_Cs);
Assertf( strcmp(m_ControlSchemeScriptOwner, CTheScripts::GetCurrentScriptName()) == 0,
"'%s' has called 'LOAD_PC_SCRIPTED_CONTROL_SCHEME()' without initialising PC scripted controls!",
CTheScripts::GetCurrentScriptName() );
ASSERT_ONLY(safecpy(m_ControlSchemeScriptOwner, CTheScripts::GetCurrentScriptName()));
m_CurrentScriptDynamicScheme = mappingSceme;
return LoadDynamicControlMappings();
}
void CControl::ShutdownPCScriptControlMappings()
{
sysCriticalSection lock(m_Cs);
ASSERT_ONLY(m_ControlSchemeScriptOwner[0] = '\0');
m_CurrentScriptDynamicScheme = atHashString("Default",0xE4DF46D5);
LoadDynamicControlMappings();
}
bool CControl::LoadVideoEditorScriptControlMappings()
{
sysCriticalSection lock(m_Cs);
m_CurrentCodeOverrideDynamicScheme = atHashString("REPLAY CAMERA",0xfcdb2688);
return LoadDynamicControlMappings();
}
bool CControl::UnloadVideoEditorScriptControlMappings()
{
sysCriticalSection lock(m_Cs);
m_CurrentCodeOverrideDynamicScheme = atHashString("Default",0xE4DF46D5);
return LoadDynamicControlMappings();
}
bool CControl::LoadDynamicControlMappings()
{
const static atHashString defaultMappings("Default",0xE4DF46D5);
atHashString mappingScheme = m_CurrentScriptDynamicScheme;
if(m_CurrentCodeOverrideDynamicScheme.GetHash() != 0 && m_CurrentCodeOverrideDynamicScheme != defaultMappings)
{
mappingScheme = m_CurrentCodeOverrideDynamicScheme;
}
bool result = true;
if(m_CurrentLoadedDynamicScheme != mappingScheme)
{
// ensure the requested mapping scheme is valid.
ControlInput::DynamicMappings* mappings = ms_settings.m_DynamicMappingList.m_DynamicMappings.Access(mappingScheme);
controlAssertf(mappings != NULL, "Dynamic mapping '%s' does not exist! Reverting to default!", mappingScheme.GetCStr());
if(mappings == NULL)
{
// we have failed to find the mapping but we can use the default.
result = false;
if(m_CurrentLoadedDynamicScheme != defaultMappings)
{
// fall back to default!
mappings = ms_settings.m_DynamicMappingList.m_DynamicMappings.Access(defaultMappings);
controlAssertf(mappings != NULL, "Default dynamic mapping does not exist!");
#if __BANK
if(mappings != NULL)
{
safecpy(m_RagLoadedPCControlScheme, "Default", RagData::TEXT_SIZE);
}
else
{
safecpy(m_RagLoadedPCControlScheme, "<Error: NO CONTROLS LOADED>", RagData::TEXT_SIZE);
}
#endif // __BANK
}
}
#if __BANK
else
{
safecpy(m_RagLoadedPCControlScheme, mappingScheme.GetCStr(), RagData::TEXT_SIZE);
}
#endif // __BANK
if(mappings != NULL)
{
ioMapper::ioSourceList primarySources;
ioMapper::ioSourceList secondarySources;
for(s32 mappingIndex = 0; mappingIndex < mappings->m_Mappings.size(); ++mappingIndex)
{
const ControlInput::DynamicMapping& mapping = mappings->m_Mappings[mappingIndex];
ioMapper& mapper = m_Mappers[ms_settings.m_InputMappers[mapping.m_Input]];
// firstly we need to unmap all the previous mappings. We could have removed all these at the start
// but then if we forget to map something we could have an input that is not mapped!
mapper.RemoveDeviceMappings(m_inputs[mapping.m_Input], ioSource::IOMD_KEYBOARD_MOUSE);
// Ensure we are mapping for a scripted input only and that there is a dynamic mapping for the scripted input
if( controlVerifyf(mapping.m_Input >= SCRIPTED_INPUT_FIRST && mapping.m_Input <= SCRIPTED_INPUT_LAST, "Invalid dynamic input!")
&& controlVerifyf(mapping.m_Mappings.GetCount() > 0, "No mappings for dynamic input %s!", GetInputName(mapping.m_Input)))
{
// UNDEFINED_INPUT means no mapping for this scripted input.
if(mapping.m_Mappings[0] != UNDEFINED_INPUT && ValidPrimaryDynamicMapping(mapping))
{
// DYNAMIC_MAPPING_MOUSE_X uses the mouse x cursor position from the center of the window.
if(mapping.m_Mappings[0] == DYNAMIC_MAPPING_MOUSE_X)
{
controlAssertf(mapping.m_Mappings.GetCount() == 1, "%s is mapped to DYNAMIC_MAPPING_MOUSE_X and another input!", GetInputName(mapping.m_Input));
mapper.Map(IOMS_MOUSE_CENTEREDAXIS, IOM_AXIS_X, m_inputs[mapping.m_Input], ioSource::IOMD_KEYBOARD_MOUSE);
m_isInputMapped[mapping.m_Input] = true;
}
// DYNAMIC_MAPPING_MOUSE_X uses the mouse y cursor position from the center of the window.
else if(mapping.m_Mappings[0] == DYNAMIC_MAPPING_MOUSE_Y)
{
controlAssertf(mapping.m_Mappings.GetCount() == 1, "%s is mapped to DYNAMIC_MAPPING_MOUSE_Y and another input!", GetInputName(mapping.m_Input));
mapper.Map(IOMS_MOUSE_CENTEREDAXIS, IOM_AXIS_Y, m_inputs[mapping.m_Input], ioSource::IOMD_KEYBOARD_MOUSE);
m_isInputMapped[mapping.m_Input] = true;
}
// If there is one mapping or the second is valid then proceed with the mapping!
else if(ValidSecondaryDynamicMapping(mapping))
{
primarySources.clear();
GetSpecificInputSources(mapping.m_Mappings[0], primarySources, ioSource::IOMD_KEYBOARD_MOUSE);
s32 numSources = primarySources.GetCount();
// If there is more than one mapping then we are trying to create an axis out of other inputs.
if(mapping.m_Mappings.GetCount() > 1)
{
secondarySources.clear();
GetSpecificInputSources(mapping.m_Mappings[1], secondarySources, ioSource::IOMD_KEYBOARD_MOUSE);
// The number of mappings should be the same, if not, then we use the smaller size and ignore the rest of the mapping when creating an axis.
// This will happen if the user has a primary and secondary mapping for one input but only a primary for the other.
if(primarySources.GetCount() == secondarySources.GetCount())
{
controlWarningf("Dynamic mapping using two inputs have mismatching mapping amounts, excess will be ignored!");
}
numSources = Min(primarySources.GetCount(), secondarySources.GetCount());
}
ioSource source;
// Go through all sources and create mappings.
for(s32 sourceIndex = 0; sourceIndex < numSources; ++sourceIndex)
{
// If we are trying to create an axis then get a compatible source.
if(mapping.m_Mappings.GetCount() > 1)
{
source = GetAxisCompatableSource(primarySources[sourceIndex], secondarySources[sourceIndex]);
}
else
{
source = primarySources[sourceIndex];
}
// Ensure the source is valid (this is primarily for axis but there is no harm in checking).
if(controlVerifyf(source.m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE, "Dynamic mapping for '%s' is not incompatible!", GetInputName(mapping.m_Input)))
{
mapper.Map(source.m_Device, source.m_Parameter, m_inputs[mapping.m_Input], ioSource::IOMD_KEYBOARD_MOUSE);
m_isInputMapped[mapping.m_Input] = true;
}
}
}
}
else
{
controlAssertf(mapping.m_Mappings.GetCount() == 1, "%s is mapped to UNDEFINED_INPUT and another input!", GetInputName(mapping.m_Input));
}
}
}
m_CurrentLoadedDynamicScheme = mappingScheme;
}
}
return result;
}
void CControl::RestoreDefaultMappings()
{
char path[PATH_BUFFER_SIZE] = {0};
GetUserSettingsPath(path, PC_USER_SETTINGS_FILE);
if(ASSET.Exists(path, ""))
{
const fiDevice* device = fiDevice::GetDevice(path, false);
if(controlVerifyf(device != NULL, "Failed to get user settings device!"))
{
controlVerifyf(device->Delete(path), "Failed to delete user settings file at '%s'!", path);
}
}
SetInitialDefaultMappings(true);
}
bool CControl::DoesUsersMappingFileNeedUpdating() const
{
sysCriticalSection lock(m_Cs);
if(m_RecachedConflictCount)
{
m_CachedConflictCount = GetNumberOfMappingConflicts();
m_RecachedConflictCount = false;
}
return m_CachedConflictCount > 0;
}
void CControl::GetUnmappedInputs(InputList& unmappedInputs ASSERT_ONLY(, bool assertOnUnmappedInput)) const
{
for(s32 input = 0; input < MAX_INPUTS; ++input)
{
if(ms_settings.m_InputMappers[input] != DEPRECATED_MAPPER)
{
ioMapper::ioSourceList sources;
GetSpecificInputSources(static_cast<InputType>(input), sources, ioSource::IOMD_KEYBOARD_MOUSE);
// The input does not have a keyboard/mouse mapping so check it is marked as unmappable.
bool hasMapping = false;
// Any mapping in index NUM_MAPPING_SCREEN_SLOTS or greater cannot be changed. We do not count it for mappings!
const s32 numChangableMappings = Min(sources.size(), NUM_MAPPING_SCREEN_SLOTS);
for(s32 sourceIndex = 0; hasMapping == false && sourceIndex < numChangableMappings; ++sourceIndex)
{
hasMapping = (sources[sourceIndex].m_Parameter != KEY_NULL);
}
if(hasMapping == false)
{
if(ms_settings.m_InputMappable[input])
{
// We add error tty so we can get a list of unmapped inputs!
controlErrorf("'%s' is currently unmapped!", GetInputName(static_cast<InputType>(input)));
controlAssertf(assertOnUnmappedInput == false, "'%s' is currently unmapped!", GetInputName(static_cast<InputType>(input)));
unmappedInputs.PushAndGrow(static_cast<InputType>(input));
}
}
}
}
}
bool CControl::SaveUserControls(bool isAutoSave, s32 deviceId)
{
#if RSG_ASSERT
if(deviceId == ioSource::IOMD_KEYBOARD_MOUSE && !isAutoSave)
{
GetNumberOfMappingConflicts(true);
}
#endif // RSG_ASSERT
#if ALLOW_USER_CONTROLS
// If the device is not the keyboard/mouse and (is a valid non XInput gamepad without a gamepad definition).
if( deviceId != ioSource::IOMD_KEYBOARD_MOUSE &&
(!IsValidJoystickDevice(deviceId) ||
controlVerifyf(!ms_settings.GetValidGamepadDefinition(ioJoystick::GetStick(deviceId)), "Cannot save custom device settings as a gamepad definition exists!")) )
{
return false;
}
INIT_PARSER;
// Due to the way that inputs are shown on the mapping screen, an input may be in multiple categories.
// Keep track of each input that has been saved.
bool inputAlreadySaved[MAX_INPUTS];
sysMemSet(inputAlreadySaved, 0, sizeof(bool) * MAX_INPUTS);
ControlInput::DeviceSettings deviceSettings;
// Go through the controls in the order they are shown on the screen and check if they have changed from the defaults, if they have we then
// save them out. This will automatically ignore un-mappable controls and group the controls relative to their on-screen placement making
// them easier for users to alter; it also means we do not need to keep a separate list of un-mappable inputs. The downside is nested loops
// and less efficient code but this only runs when the user saves their controls.
//
// The structure of the controls screen order is as follows:
// MappingList - Defines a page/section of controls e.g. On Foot.
// +--- m_Categories - An array of hash string name of a category. E.g. In Car will have a general vehicle category and car specific inputs.
// +--- Input - Categories are an array of inputs.
for(u32 mappingListIndex = 0; mappingListIndex < ms_settings.m_MappingSettings.m_MappingList.size(); ++mappingListIndex)
{
// The current mapping list (e.g. page/section of controls such as On Foot or Melee).
const ControlInput::MappingList& mappingList = ms_settings.m_MappingSettings.m_MappingList[mappingListIndex];
// Loop through all categories in the mapping list.
for (u32 catagoryIndex = 0; catagoryIndex < mappingList.m_Categories.size(); ++catagoryIndex)
{
const ControlInput::InputList* list = ms_settings.m_MappingSettings.m_Categories.Access(mappingList.m_Categories[catagoryIndex]);
if( controlVerifyf(list != NULL,
"Invalid category '%s' (%d) whilst saving control mappings",
mappingList.m_Categories[catagoryIndex].TryGetCStr(),
mappingList.m_Categories[catagoryIndex].GetHash()) )
{
const atArray<InputType>& inputs = list->m_Inputs;
// Loop through all the inputs in the category.
for(u32 inputIndex = 0; inputIndex < inputs.size(); ++inputIndex)
{
InputType input = inputs[inputIndex];
Mapper mapperId = ms_settings.m_InputMappers[input];
if( inputAlreadySaved[input] == false &&
mapperId != DEPRECATED_MAPPER &&
controlVerifyf(mapperId >= 0 && mapperId < MAPPERS_MAX, "Invalid mapper assigned to %s!", GetInputName(input)) )
{
inputAlreadySaved[input] = true;
SaveChangedInput(input, deviceId, m_DefaultKeyboardMouseSettings, deviceSettings.m_ControlSettings);
}
}
}
}
}
if(deviceId == ioSource::IOMD_KEYBOARD_MOUSE && !isAutoSave)
{
InputList unmappedInputs;
GetUnmappedInputs(unmappedInputs ASSERT_ONLY(, true));
if(unmappedInputs.size() > 0)
{
return false;
}
}
char path[PATH_BUFFER_SIZE] = {0};
if(deviceId == ioSource::IOMD_KEYBOARD_MOUSE)
{
if(isAutoSave)
{
GetUserSettingsPath(path, PC_AUTOSAVE_USER_SETTINGS_FILE);
}
else
{
GetUserSettingsPath(path, PC_USER_SETTINGS_FILE);
}
ASSET.CreateLeadingPath(path);
PARSER.SaveObjectAnyBuild(path, "", &deviceSettings.m_ControlSettings);
}
else
{
const ioJoystick& stick = ioJoystick::GetStick(deviceId);
stick.GetProductName(deviceSettings.m_Description, COUNTOF(deviceSettings.m_Description));
GetUserSettingsPath(path);
safecat(path, stick.GetProductGuidStr());
safecat(path, ".");
if(isAutoSave)
{
safecat(path, AUTOSAVE_EXT);
}
else
{
safecat(path, USER_SETTINGS_EXT);
}
ASSET.CreateLeadingPath(path);
PARSER.SaveObjectAnyBuild(path, "", &deviceSettings);
}
SHUTDOWN_PARSER;
#endif // ALLOW_USER_CONTROLS
return true;
}
bool CControl::HasAutoSavedUserControls(s32 deviceId) const
{
char path[PATH_BUFFER_SIZE] = {0};
if(deviceId == ioSource::IOMD_KEYBOARD_MOUSE)
{
GetUserSettingsPath(path, PC_AUTOSAVE_USER_SETTINGS_FILE);
}
else if(controlVerifyf(IsValidJoystickDevice(deviceId), "Invalid Device ID %d!", deviceId))
{
char fileName[50] = {0};
formatf(fileName, "%s.%s", ioJoystick::GetStick(deviceId).GetProductGuidStr(), AUTOSAVE_EXT);
GetUserSettingsPath(path, fileName);
}
else
{
return false;
}
return ASSET.Exists(path, "");
}
bool CControl::DeleteAutoSavedUserControls(s32 deviceId) const
{
char path[PATH_BUFFER_SIZE] = {0};
if(deviceId == ioSource::IOMD_KEYBOARD_MOUSE)
{
GetUserSettingsPath(path, PC_AUTOSAVE_USER_SETTINGS_FILE);
}
else if(controlVerifyf(IsValidJoystickDevice(deviceId), "Invalid Device ID %d!", deviceId))
{
char fileName[50] = {0};
formatf(fileName, "%s.%s", ioJoystick::GetStick(deviceId).GetProductGuidStr(), AUTOSAVE_EXT);
GetUserSettingsPath(path, fileName);
}
else
{
return false;
}
const fiDevice* device = fiDevice::GetDevice(path, false);
if(device != NULL)
{
return device->Delete(path);
}
return false;
}
void CControl::SaveChangedInput(const InputType input, const s32 deviceId, const ControlInput::ControlSettings& defaultSettings, ControlInput::ControlSettings& userSettings) const
{
ioMapper::ioSourceList sources;
GetSpecificInputSources(input, sources, deviceId);
// The converted ioSourceList to ControlInput::Mapping.
u32 nullMappingsToAdd = 0u;
atFixedArray<ControlInput::Mapping, NUM_MAPPING_SCREEN_SLOTS> newSettings;
for(s32 sourceIdx = 0; sourceIdx < Min(sources.size(), NUM_MAPPING_SCREEN_SLOTS); ++sourceIdx)
{
const ioSource& source = sources[sourceIdx];
ControlInput::Mapping mapping;
mapping.m_Input = input;
mapping.m_Source = source.m_Device;
// for now just assume there is one parameter.
// TODO: we might need to check this later.
if(source.m_Device == IOMS_MKB_AXIS)
{
mapping.m_Parameters.Reserve(2);
mapping.m_Parameters.Push(ioMapper::ConvertParameterToMapperValue(IOMS_MKB_AXIS, ioMapper::GetMkbAxisPositive(source.m_Parameter, true)));
mapping.m_Parameters.Push(ioMapper::ConvertParameterToMapperValue(IOMS_MKB_AXIS, ioMapper::GetMkbAxisNegative(source.m_Parameter, true)));
}
else
{
mapping.m_Parameters.Reserve(1);
mapping.m_Parameters.Push(ioMapper::ConvertParameterToMapperValue(source.m_Device, source.m_Parameter));
}
// If null mapping then only add it when we find a non-null mapping unless its the first mapping.
if(newSettings.size() > 0 && mapping.m_Source == IOMS_KEYBOARD && mapping.m_Parameters.size() == 1 && mapping.m_Parameters[0] == KEY_NULL)
{
++nullMappingsToAdd;
}
else
{
// If there is a null mapping before a non-null mapping add a null mapping for each null mapping before.
if(nullMappingsToAdd > 0)
{
ControlInput::Mapping nullMapping;
nullMapping.m_Input = input;
nullMapping.m_Source = IOMS_KEYBOARD;
nullMapping.m_Parameters.Reserve(1);
nullMapping.m_Parameters.Push(KEY_NULL);
for(u32 nullIndex = 0; nullIndex < nullMappingsToAdd; ++nullIndex)
{
newSettings.Push(nullMapping);
}
nullMappingsToAdd = 0;
}
newSettings.Push(mapping);
}
}
bool settingChanged = false;
u32 mappingIndex = 0;
// Check for change
for(u32 defaultIdx = 0; defaultIdx < defaultSettings.m_Mappings.size(); ++defaultIdx)
{
const ControlInput::Mapping& defaultMapping = defaultSettings.m_Mappings[defaultIdx];
if(defaultMapping.m_Input == input)
{
if( mappingIndex >= newSettings.size() ||
!AreTwoMappingsEqual(defaultMapping, newSettings[mappingIndex]))
{
settingChanged = true;
break;
}
++mappingIndex;
// Any mapping over NUM_MAPPING_SCREEN_SLOTS is considered fixed and unchangeable!
if(mappingIndex >= NUM_MAPPING_SCREEN_SLOTS)
{
break;
}
}
}
if(settingChanged || mappingIndex < newSettings.size())
{
for(u32 settingIdx = 0; settingIdx < newSettings.size(); ++settingIdx)
{
userSettings.m_Mappings.PushAndGrow(newSettings[settingIdx]);
}
}
}
bool CControl::AreTwoMappingsEqual(const ControlInput::Mapping& lhs, const ControlInput::Mapping& rhs)
{
if(lhs.m_Source != rhs.m_Source || lhs.m_Parameters.size() != rhs.m_Parameters.size())
{
return false;
}
// compare each element of params.
for(u32 paramIndex = 0; paramIndex < rhs.m_Parameters.size(); ++paramIndex)
{
if(lhs.m_Parameters[paramIndex] != rhs.m_Parameters[paramIndex])
{
return false;
}
}
return true;
}
void CControl::UpdateKeyBindings()
{
PF_PUSH_TIMEBAR("controls.Update.keyBindScan");
if(m_currentMapping.m_Input != UNDEFINED_INPUT)
{
if(m_currentMapping.m_ScanMapper == NULL)
{
s32 mapperId = ms_settings.m_InputMappers[m_currentMapping.m_Input];
controlAssertf( mapperId != DEPRECATED_MAPPER,
"%s is deprecated so cannot map a device to it!",
GetInputName(m_currentMapping.m_Input) );
if(controlVerifyf( mapperId >= 0 && mapperId < MAPPERS_MAX,
"%s has an invalid mapper so cannot map a device it it!",
GetInputName(m_currentMapping.m_Input) ))
{
m_currentMapping.m_ScanMapper = &m_Mappers[mapperId];
ioMapper::BeginScan();
}
else
{
// Reset on error.
m_currentMapping = CurrentMappingData();
}
}
else if(m_currentMapping.m_Conflict.m_Conflicts.size() > 0)
{
if( m_currentMapping.m_CancelMapping )
{
m_currentMapping = CurrentMappingData();
ioMapper::EndScan();
}
else if ( m_currentMapping.m_ReplaceConflict BANK_ONLY(|| m_currentMapping.m_MappingCameFromRag) )
{
if(controlVerifyf(AreConflictingInputsMappable(), "Mapping cannot be replaced as a conflict is not re-mappable! canceling mapping!"))
{
for(u32 i = 0; i < m_currentMapping.m_Conflict.m_Conflicts.size(); ++i)
{
// Clear conflict resolution options just in case.
m_currentMapping.m_CancelMapping = false;
m_currentMapping.m_ReplaceConflict = false;
const ioSource unmapped(IOMS_KEYBOARD, KEY_NULL, ioSource::IOMD_KEYBOARD_MOUSE);
UpdateMapping( m_currentMapping.m_Conflict.m_Conflicts[i].m_Input,
m_currentMapping.m_Type,
unmapped,
m_currentMapping.m_Conflict.m_Conflicts[i].m_MappingIndex,
true);
}
m_currentMapping.m_Conflict = MappingConflictData();
}
else
{
CancelCurrentMapping();
}
}
}
else if(UpdateMappingFromInputScan())
{
m_currentMapping = CurrentMappingData();
ioMapper::EndScan();
}
}
PF_POP_TIMEBAR();
}
u32 CControl::GetNumberOfMappingConflicts(bool assertOnConflict /* = false */) const
{
InputList unmappedInputs;
GetConflictingInputs(unmappedInputs, assertOnConflict);
// We divide by two because if A conflicts with B then both will be marked as conflicting with each other.
return unmappedInputs.size() / 2;
}
void CControl::GetConflictingInputs(InputList& conflictingInputs, bool assertOnConflict) const
{
// Check for any conflict mappings. NOTE: this will be very slow!
ioMapper::ioSourceList sources;
MappingConflictData tmpData;
for(u32 inpIdx = 0; inpIdx < MAX_INPUTS; ++inpIdx)
{
const u32 mapperId = ms_settings.m_InputMappers[inpIdx];
if(mapperId != DEPRECATED_MAPPER && controlVerifyf(mapperId >= 0 && mapperId < MAPPERS_MAX, "Invalid mapper %d!", mapperId))
{
const InputType input = static_cast<InputType>(inpIdx);
GetSpecificInputSources(input, sources, ioSource::IOMD_KEYBOARD_MOUSE);
for(u32 srcIdx = 0; srcIdx < sources.size(); ++srcIdx)
{
bool hasConflict = GetConflictingInputData(input, sources[srcIdx], false, tmpData, assertOnConflict);
s32 conflictIndex = GetConflictIndex(tmpData.m_Conflicts, input);
if( hasConflict && (conflictIndex == -1 || srcIdx != tmpData.m_Conflicts[conflictIndex].m_MappingIndex))
{
conflictingInputs.PushAndGrow(input);
for(u32 i = 0; i < tmpData.m_Conflicts.size(); ++i)
{
conflictingInputs.PushAndGrow(tmpData.m_Conflicts[i].m_Input);
}
}
// reset
tmpData.Clear();
}
sources.Reset();
}
}
// remove duplicates
for(int i=0; i < conflictingInputs.GetCount(); ++i)
{
int j=conflictingInputs.Find(conflictingInputs[i], i+1);
while(j!=-1)
{
conflictingInputs.DeleteFast(j);
j=conflictingInputs.Find(conflictingInputs[i], j);
}
}
}
void CControl::LoadDeviceControls(bool loadAutosave)
{
#if RSG_PC
sysCriticalSection lock(m_Cs);
INIT_PARSER;
parSettings settings;
// we want to detect errors when parsing the xml.
settings.SetFlag(parSettings::READ_SAFE_BUT_SLOW, true);
// Cache the user settings diretory so we do not keep calling the dll.
char userSettingsDir[PATH_BUFFER_SIZE] = {0};
GetUserSettingsPath(userSettingsDir);
char filePath[PATH_BUFFER_SIZE] = {0};
for(s32 i = 0; i < ioJoystick::GetStickCount(); ++i)
{
CPad& pad = CControlMgr::GetPad(i);
// if the pad is not connected then it is a DirectInput Device. If it is connected check that is is not using pad
// rather than direct input mappings.
if( !pad.IsConnected() || (!pad.IsXBox360CompatiblePad() && !pad.IsPs3Pad()) )
{
const ioJoystick& stick = ioJoystick::GetStick(i);
bool mappingsLoaded = false;
if(!loadAutosave || !ASSET.Exists(formatf(filePath, "%s%s.%s", userSettingsDir, stick.GetProductGuidStr(), AUTOSAVE_EXT), ""))
{
formatf(filePath, "%s%s.%s", userSettingsDir, stick.GetProductGuidStr(), USER_SETTINGS_EXT);
}
if(ASSET.Exists(filePath, ""))
{
ControlInput::DeviceSettings deviceSettings;
controlDisplayf("Loading user device file '%s' for device!", filePath);
if(controlVerifyf(PARSER.LoadObject(filePath, "", deviceSettings, &settings), "Failed to load device settings file '%s'!", filePath))
{
LoadSettings(deviceSettings.m_ControlSettings, i);
mappingsLoaded = true;
}
}
if(!mappingsLoaded)
{
const ControlInput::Gamepad::Definition* definition = NULL;
// If there is a calibrated gamepad (joystick definition) in memory then use that.
if(m_CurrentJoystickDefinition.m_Guid == atFinalHashString(stick.GetProductGuidStr()) && m_CurrentJoystickDefinition.m_Data.m_Definitions.size() > 0)
{
controlDisplayf("Using currently loaded gamepad definition data for device!");
definition = &m_CurrentJoystickDefinition.m_Data;
}
else
{
controlDisplayf("Using gamepad definition data for device!");
definition = ms_settings.GetValidGamepadDefinition(stick);
}
if(definition != NULL)
{
LoadGamepadControls(*definition, m_GamepadCommonSettings, i);
#if FPS_MODE_SUPPORTED
if(IsUsingFpsMode())
{
LoadGamepadControls(*definition, m_GamepadFpsBaseLayoutSettings, i);
LoadGamepadControls(*definition, m_GamepadFpsSpecificLayoutSettings, i);
}
else
#endif // FPS_MODE_SUPPORTED
{
LoadGamepadControls(*definition, m_GamepadTpsBaseLayoutSettings, i);
LoadGamepadControls(*definition, m_GamepadTpsSpecificLayoutSettings, i);
}
LoadGamepadControls(*definition, m_GamepadAcceptCancelSettings, i);
LoadGamepadControls(*definition, m_GamepadDuckBrakeSettings, i);
mappingsLoaded = true;
}
}
if(!mappingsLoaded && ASSET.Exists(formatf(filePath, "%s%s.%s", PC_DEVICE_SETTINGS_DIR, stick.GetProductGuidStr(), DEFAULT_SETTINGS_EXT), ""))
{
ControlInput::DeviceSettings deviceSettings;
controlDisplayf("Loading default device file '%s' for device!", filePath);
if(controlVerifyf(PARSER.LoadObject(filePath, "", deviceSettings, &settings), "Failed to load device settings file '%s'!", filePath))
{
LoadSettings(deviceSettings.m_ControlSettings, i);
mappingsLoaded = true;
}
}
}
}
SHUTDOWN_PARSER;
#endif // RSG_PC
}
void CControl::ScanGamepadDefinitionSource(GamepadDefinitionSource source)
{
if(Verifyf(source >= 0 && source < NUM_PAD_SOURCES, "Invalid GampadDefinitionSource (%d)!", source))
{
m_CurrentJoystickDefinition.m_PadParameter = ConvertGamepadInputToParameter(source);
}
else
{
// Cancel any existing mappings just in case.
CancelGampadDefinitionSourceScan();
}
}
bool CControl::ClearGamepadDefinitionSource(GamepadDefinitionSource source)
{
ioMapperParameter param = ConvertGamepadInputToParameter(source);
if(controlVerifyf(param != IOM_UNDEFINED, "Invalid GamepadDefinitionSource %d", source))
{
for(u32 i = 0; i < m_CurrentJoystickDefinition.m_Data.m_Definitions.size(); ++i)
{
if(m_CurrentJoystickDefinition.m_Data.m_Definitions[i].m_PadParameter == param)
{
m_CurrentJoystickDefinition.m_Data.m_Definitions.DeleteFast(i);
return true;
}
}
}
return false;
}
void CControl::CancelGampadDefinitionScan()
{
CancelGampadDefinitionSourceScan();
m_CurrentJoystickDefinition.m_Data.m_Definitions.Reset();
}
void CControl::CancelGampadDefinitionSourceScan()
{
m_CurrentJoystickDefinition.m_PadParameter = IOM_UNDEFINED;
m_CurrentJoystickDefinition.m_ScanStarted = false;
m_CurrentJoystickDefinition.m_ScanReady = false;
ClearConflictingDefinitionSource();
}
bool CControl::IsGampadDefinitionScanInProgress() const
{
return m_CurrentJoystickDefinition.m_PadParameter != IOM_UNDEFINED;
}
bool CControl::IsValidJoystickDevice(s32 deviceId) const
{
return deviceId >= 0 && deviceId < ioJoystick::GetStickCount() && CControlMgr::GetPad(deviceId).IsXBox360CompatiblePad();
}
const ioMapperParameter CControl::ms_GAMEPAD_DEFINITON_TO_MAPPER_PARAM[] =
{
RDOWN, RRIGHT, RLEFT, RUP,
L1, L2, R1, R2,
START, SELECT,
LUP, LDOWN, LLEFT, LRIGHT,
L3, IOM_AXIS_LY_UP, IOM_AXIS_LX_RIGHT,
R3, IOM_AXIS_RY_UP, IOM_AXIS_RX_RIGHT,
};
CControl::GamepadDefinitionSource CControl::ConvertParameterToGampadInput( ioMapperParameter param )
{
// This is here as ms_GAMEPAD_DEFINITON_TO_MAPPER_PARAM is private.
CompileTimeAssert(NUM_PAD_SOURCES == COUNTOF(ms_GAMEPAD_DEFINITON_TO_MAPPER_PARAM));
// Convert pad index to pad button type first.
if((param & IOMT_PAD_INDEX) == IOMT_PAD_INDEX)
{
param = static_cast<ioMapperParameter>((param & ~IOMT_PAD_INDEX) | IOMT_PAD_BUTTON);
}
for(u32 i = 0; i < NUM_PAD_SOURCES; ++i)
{
if(ms_GAMEPAD_DEFINITON_TO_MAPPER_PARAM[i] == param)
{
return static_cast<GamepadDefinitionSource>(i);
}
}
return INVALID_PAD_SOURCE;
}
rage::ioMapperParameter CControl::ConvertGamepadInputToParameter( GamepadDefinitionSource input )
{
if(Verifyf(input >= 0 && input < NUM_PAD_SOURCES, "Invalid GamepadDefinitionSource (%d)!", input))
{
return ms_GAMEPAD_DEFINITON_TO_MAPPER_PARAM[input];
}
else
{
return IOM_UNDEFINED;
}
}
void CControl::StartGamepadDefinitionScan()
{
m_CurrentJoystickDefinition.m_Data.m_Definitions.Reset();
ClearConflictingDefinitionSource();
m_CurrentJoystickDefinition.m_PadParameter = IOM_UNDEFINED;
}
void CControl::EndGamepadDefinitionScan(bool saveGamepadDefinition)
{
if(saveGamepadDefinition)
{
char path[PATH_BUFFER_SIZE] = {0};
GetUserSettingsPath(path, PC_USER_GAMEPAD_DEFINITION_FILE);
controlDisplayf("Saving gamepad definition to '%s'!", path);
controlVerifyf( SaveCurrentJoystickDefinition(path),
"Failed to save gamepad definition file ('%s')!",
path );
ms_settings.RebuildGamepadDefinitionList();
}
else
{
controlDisplayf("Cancelling gamepad definition scan!");
}
CancelGampadDefinitionScan();
m_CurrentJoystickDefinition.m_Guid = 0;
SetInitialDefaultMappings(true);
}
bool CControl::SaveCurrentJoystickDefinition( const char* userFilePath ) const
{
bool saved = false;
// don't save empty definitions
if(m_CurrentJoystickDefinition.m_Data.m_Definitions.size() > 0 && controlVerifyf(m_CurrentJoystickDefinition.m_Guid != 0, "Invalid GUID for gamepad!"))
{
// first we need to load the user file so we do not overwrite other device settings.
INIT_PARSER;
ControlInput::Gamepad::DefinitionList definitions;
parSettings settings;
// we want to detect errors when parsing the xml.
settings.SetFlag(parSettings::READ_SAFE_BUT_SLOW, true);
// If the file does not exist then it is still ok to continue.
if(ASSET.Exists(userFilePath, ""))
{
controlVerifyf(PARSER.LoadObject(userFilePath, "", definitions, &settings), "Error loading joystick definition if file '%s'", userFilePath);
}
definitions.m_Devices[m_CurrentJoystickDefinition.m_Guid] = m_CurrentJoystickDefinition.m_Data;
ASSET.CreateLeadingPath(userFilePath);
saved = PARSER.SaveObjectAnyBuild(userFilePath, "", &definitions);
controlAssertf(saved, "Error saving joystick definition file '%s'", userFilePath);
SHUTDOWN_PARSER;
}
else
{
// treat empty definitions as a success.
saved = true;
}
return saved;
}
bool CControl::LoadCurrentJoystickDefinition( const char* userFilePath )
{
bool found = false;
const ControlInput::Gamepad::Definition* definition = NULL;
m_CurrentJoystickDefinition.m_Guid = ioJoystick::GetStick(m_CurrentJoystickDefinition.m_DeviceIndex).GetProductGuidStr();
m_CurrentJoystickDefinition.m_Data.m_Definitions.clear();
// do not assert if the file does not exist.
char defaultUserGampadPath[PATH_BUFFER_SIZE] = {0};
GetUserSettingsPath(defaultUserGampadPath, PC_USER_GAMEPAD_DEFINITION_FILE);
if(ASSET.Exists(userFilePath, "") && strcasecmp(userFilePath, PC_GAMEPAD_DEFINITION_FILE) != 0 && strcasecmp(userFilePath, defaultUserGampadPath) != 0)
{
// first we need to load the user file so we do not overwrite other device settings.
INIT_PARSER;
parSettings settings;
// we want to detect errors when parsing the xml.
settings.SetFlag(parSettings::READ_SAFE_BUT_SLOW, true);
// If the default settings file exists and we FAIL to load it.
ControlInput::Gamepad::DefinitionList definitions;
if( controlVerifyf(PARSER.LoadObject(userFilePath, "", definitions, &settings), "Error loading joystick definition file '%s'!", userFilePath) )
{
definition = definitions.m_Devices.Access(m_CurrentJoystickDefinition.m_Guid);
}
SHUTDOWN_PARSER;
}
if(definition == NULL)
{
definition = ms_settings.GetGamepadDefinition(ioJoystick::GetStick(m_CurrentJoystickDefinition.m_DeviceIndex));
}
if(definition != NULL)
{
found = true;
m_CurrentJoystickDefinition.m_Data = *definition;
}
BANK_ONLY(UpdateJoystickDefinitionRagData());
return found;
}
void CControl::UpdateCurrentGamepadDefinition( const ControlInput::Gamepad::Source& definition )
{
bool updated = false;
for(u32 i = 0; !updated && i < m_CurrentJoystickDefinition.m_Data.m_Definitions.size(); ++i)
{
if(m_CurrentJoystickDefinition.m_Data.m_Definitions[i].m_PadParameter == definition.m_PadParameter)
{
updated = true;
m_CurrentJoystickDefinition.m_Data.m_Definitions[i] = definition;
}
}
if(!updated)
{
// add definition.
m_CurrentJoystickDefinition.m_Data.m_Definitions.PushAndGrow(definition);
}
}
CControl::GamepadDefinitionSource CControl::GetCurrentGamepadDefinition(const ControlInput::Gamepad::Source& definition ) const
{
for(u32 i = 0; i < m_CurrentJoystickDefinition.m_Data.m_Definitions.size(); ++i)
{
const ControlInput::Gamepad::Source& current = m_CurrentJoystickDefinition.m_Data.m_Definitions[i];
// If the current is mapped to the same button/stick as definition && (current is not a trigger || definition is not a trigger)
if( current.m_JoystickParameter == definition.m_JoystickParameter &&
((current.m_PadParameter != L2 && current.m_PadParameter != L2_INDEX && current.m_PadParameter != R2 && current.m_PadParameter != R2_INDEX) ||
(definition.m_PadParameter != L2 && definition.m_PadParameter != L2_INDEX && definition.m_PadParameter != R2 && definition.m_PadParameter != R2_INDEX)) )
{
return ConvertParameterToGampadInput(current.m_PadParameter);
}
}
return INVALID_PAD_SOURCE;
}
bool CControl::IsGamepadDefinitionValid( const ControlInput::Gamepad::Source& definition ) const
{
// If definition.m_padParameter and definition.m_JoystickParameter are the same thing (axis or buttons) or definition.m_PadParameter is a trigger.
return ((definition.m_PadParameter & IOMT_PAD_AXIS) == IOMT_PAD_AXIS && (definition.m_JoystickParameter & IOMT_JOYSTICK_AXIS) == IOMT_JOYSTICK_AXIS) ||
((definition.m_PadParameter & IOMT_PAD_AXIS) == 0 && (definition.m_JoystickParameter & IOMT_JOYSTICK_AXIS) == 0 ) ||
definition.m_PadParameter == L2 || definition.m_PadParameter == L2_INDEX || definition.m_PadParameter == R2 || definition.m_PadParameter == R2_INDEX;
}
#endif // __WIN32PC
#if !__FINAL
void CControl::ChanneledOutputForStackTrace(const char* fmt, ...)
{
char buffer[256];
va_list args;
va_start(args, fmt);
vformatf(buffer, sizeof(buffer), fmt, args);
va_end(args);
ms_ThreadlStack += buffer;
ms_ThreadlStack += "\n";
}
#endif // !__FINAL