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

746 lines
22 KiB
C++

#include "Frontend/KeyMappingMenu.h"
#if KEYBOARD_MOUSE_SUPPORT
#include "Frontend/PauseMenu.h"
#include "Frontend/ScaleformMenuHelper.h"
#include "Frontend/Scaleform/ScaleFormMgr.h"
#include "Frontend/ButtonEnum.h"
#include "Frontend/ui_channel.h"
#include "system/control.h"
#include "system/controlMgr.h"
#include "Frontend/UIContexts.h"
#define INVALID_RETURN_INDEX -1
#define RESTORE_DEFAULTS_ID -3000 // just a random number to get it to behave
#define SF_SECONDARY_INPUT_OFFSET 1000
//OPTIMISATIONS_OFF()
FRONTEND_OPTIMISATIONS()
PARAM(kbAutoResolveConflicts, "Never display keybinding conflicts with a warning screen");
bool CKeyMappingMenu::sm_bAutoResolveConflicts = false;
CKeyMappingMenu::CKeyMappingMenu(CMenuScreen& owner)
: CMenuBase(owner)
, m_curMappingInput(UNDEFINED_INPUT)
, m_lastConflict(UNDEFINED_INPUT)
, m_curCategory()
, m_iKeysInCategory(0)
, m_iCurBindingIndex(-1)
, m_bInSubMenu(false)
, m_bTouchedAControl(false)
#if __BANK
, m_pMyGroup(NULL)
#endif
{
#if !__FINAL
if( PARAM_kbAutoResolveConflicts.Get() )
{
sm_bAutoResolveConflicts = !PARAM_kbAutoResolveConflicts.Get();
PARAM_kbAutoResolveConflicts.Set(NULL);
}
#endif
}
CKeyMappingMenu::~CKeyMappingMenu()
{
#if __BANK
if( m_pMyGroup )
{
m_pMyGroup->Destroy();
m_pMyGroup = NULL;
}
#endif
}
void CKeyMappingMenu::Update()
{
CControl& control = CControlMgr::GetPlayerMappingControl();
if( control.IsMappingInProgress() )
{
control.DisableAllInputs();
CControlMgr::GetMainFrontendControl().DisableAllInputs();
uiAssertf(control.GetMappingInProgress() == m_curMappingInput, "Currently mapping %s, but expected %s!", CControl::GetInputName(control.GetMappingInProgress()), CControl::GetInputName(m_curMappingInput));
ioValue::ReadOptions options(ioValue::NO_DEAD_ZONE);
options.SetFlags(ioValue::ReadOptions::F_READ_DISABLED, true);
// we have to check for the controls here because PauseMenu earlies out on us and we don't get UpdateInput called
if( control.GetFrontendCancel().IsReleased(ioValue::BUTTON_DOWN_THRESHOLD, options) && m_curMappingInput != UNDEFINED_INPUT )
{
control.CancelCurrentMapping();
SendControlToScaleform(m_curMappingInput, m_iCurBindingIndex, true, 0);
CPauseMenu::PlayInputSound( FRONTEND_INPUT_BACK );
}
// handle conflicts with a warning screen
else if( control.HasNewMappingConflict() )
{
CWarningMessage& rMessage = CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage();
const CControl::MappingConflictList& conflictList = control.GetMappingConflictList();
bool isMappable = control.AreConflictingInputsMappable();
if( sm_bAutoResolveConflicts && isMappable )
{
AcceptConflict();
}
else if( !rMessage.IsActive() )
{
const char* conflictingInputName = CControl::GetInputName(conflictList[0].m_Input);
CWarningMessage::Data messageData;
messageData.m_TextLabelHeading = "CWS_WARNING";
// build the first input's button string display "~INPUT_VEH_DUCK:0~"
{
atStringBuilder controlButtonString;
controlButtonString.Append("~");
controlButtonString.Append(conflictingInputName);
controlButtonString.Append(":");
// We only need 4 as index is 8bit so max 3 chars plus null.
char indexStr[4] = {0};
formatf(indexStr, "%d", conflictList[0].m_MappingIndex);
controlButtonString.Append(indexStr);
controlButtonString.Append("~");
messageData.m_FirstSubString.Set( controlButtonString.ToString() ); // just send the conflicting key. Since it's the same as the one we're binding ('cuz it's a conflict), this will "just work"
}
char secondPart[MAX_CHARS_IN_EXTENDED_MESSAGE];
int subCount = 0;
const char* pszMessageToUse = nullptr;
CSubStringWithinMessage SubStringArray[3];
atStringBuilder listOfConflicts;
if(!isMappable)
{
messageData.m_TextLabelBody = "UNMAPPABLE_CONFLICT_BODY";
pszMessageToUse = "UNMAPPABLE_CONFLICT_BODY_PT2";
SubStringArray[subCount++].SetTextLabel(conflictingInputName);
SubStringArray[subCount++].SetTextLabel(CControl::GetInputName(m_curMappingInput));
messageData.m_iFlags = FE_WARNING_OK;
messageData.m_acceptPressed = datCallback(MFA(CKeyMappingMenu::IgnoreConflict), this);
}
else
{
messageData.m_acceptPressed = datCallback(MFA(CKeyMappingMenu::AcceptConflict), this);
messageData.m_declinePressed = datCallback(MFA(CKeyMappingMenu::IgnoreConflict), this);
messageData.m_iFlags = FE_WARNING_OK_CANCEL;
if( conflictList.size() == 1 )
{
// just one conflict is easy
messageData.m_TextLabelBody = "MAPPING_CONFLICT_BODY";
pszMessageToUse = "MAPPING_CONFLICT_BODY_PT2";
SubStringArray[subCount++].SetTextLabel(conflictingInputName);
SubStringArray[subCount++].SetTextLabel(CControl::GetInputName(m_curMappingInput));
}
else
{
messageData.m_TextLabelBody = "MAPPING_MULTI_CONFLICT_BODY";
pszMessageToUse = "MAPPING_MULTI_CONFLICT_BODY_PT2";
SubStringArray[subCount++].SetTextLabel(CControl::GetInputName(m_curMappingInput));
int conflictIndex=0;
for(;conflictIndex < conflictList.size() && conflictIndex < conflictList.GetMaxCount()-1; ++conflictIndex) // -1 so we know if there's "more"
{
listOfConflicts.Append(TheText.Get(CControl::GetInputName(conflictList[conflictIndex].m_Input)));
listOfConflicts.Append("\n");
}
if( conflictIndex < conflictList.size() )
listOfConflicts.Append( TheText.Get("MAPPING_MULTI_CONFLICT_BODY_ETC") );
SubStringArray[subCount++].SetCharPointer(listOfConflicts.ToString());
}
}
// last value is always the desired end control
CMessages::InsertNumbersAndSubStringsIntoString(TheText.Get(pszMessageToUse),
NULL, 0,
SubStringArray, subCount,
secondPart, MAX_CHARS_IN_EXTENDED_MESSAGE);
messageData.m_SecondSubString.Set( secondPart );
rMessage.SetMessage(messageData);
} // no message up
} // no conflict
}
// no longer mapping
else if( m_curMappingInput != UNDEFINED_INPUT )
{
control.DisableAllInputs();
CControlMgr::GetMainFrontendControl().DisableAllInputs();
AdjustWarningIndicators();
// make sure we re-establish the buttons
if(m_bInSubMenu)
{
SetInSubmenu(true);
}
m_curMappingInput = UNDEFINED_INPUT;
m_lastConflict = UNDEFINED_INPUT;
// clear the warning message that may have appeared if we were resolving conflicts. Prevents a flash if there's chained conflicts
CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage().Clear();
}
}
bool CKeyMappingMenu::UpdateInput(s32 eInput)
{
if( eInput == PAD_CIRCLE && !m_bInSubMenu )
{
if( !m_conflictingInputs.empty() )
{
if( SUIContexts::IsActive("HadMappingConflict") )
{
CWarningMessage::Data messageData;
messageData.m_TextLabelHeading = "CWS_WARNING";
messageData.m_TextLabelBody = "UNMAPPED_WARNING_NO_ESCAPE";
messageData.m_iFlags = FE_WARNING_OK;
messageData.m_bCloseAfterPress = true;
CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage().SetMessage(messageData);
CPauseMenu::PlayInputSound( FRONTEND_INPUT_BACK );
}
else
{
CPauseMenu::ShowConfirmationAlert("CONFLICT_WARNING", datCallback(MFA(CKeyMappingMenu::AcceptBackingOut), this));
CPauseMenu::PlayInputSound( FRONTEND_INPUT_BACK );
}
return true;
}
else if( !m_unmappedInputs.empty())
{
CPauseMenu::ShowConfirmationAlert("UNMAPPED_WARNING", datCallback(MFA(CKeyMappingMenu::AcceptBackingOut), this));
CPauseMenu::PlayInputSound( FRONTEND_INPUT_BACK );
return true;
}
}
// check if they want to delete an item
if( m_curMappingInput == UNDEFINED_INPUT && CControlMgr::GetMainFrontendControl().GetValue(INPUT_FRONTEND_DELETE).IsReleased() )
{
if( m_bInSubMenu )
{
rage::InputType input;
u8 mappingSlot;
if( GetInputForCurrentSelection(input, mappingSlot) && IsInputBound(input, mappingSlot) && CControl::IsInputMappable(input) )
{
if (input == INPUT_FRONTEND_SOCIAL_CLUB_SECONDARY && mappingSlot == 0)
{
// B* 2349103
// The SCUI hotkey is not technically bindable away from HOME, so we want to prevent this one field
// from being deleted. The alternating (mappingSlot == 1) is still fine.
return true;
}
CControlMgr::GetPlayerMappingControl().DeleteInputMapping(input, CControl::PC_KEYBOARD_MOUSE, mappingSlot);
AdjustWarningIndicators(true);
CPauseMenu::PlayInputSound( FRONTEND_INPUT_X );
SUIContexts::Deactivate("KEYMAP_CAN_DELETE");
CPauseMenu::RedrawInstructionalButtons();
m_bTouchedAControl = true;
return true;
}
}
}
return false;
}
bool CKeyMappingMenu::ShouldBlockCloseAttempt(const char*& rpWarningMessageToDisplay, bool& bCanContinue)
{
if( !m_conflictingInputs.empty() )
{
bCanContinue = false;
rpWarningMessageToDisplay = "UNMAPPED_TU_WARNING";
return true;
}
else if( !m_unmappedInputs.empty() )
{
bCanContinue = true;
rpWarningMessageToDisplay = "UNMAPPED_WARNING";
return true;
}
return false;
}
void CKeyMappingMenu::AcceptBackingOut()
{
CPauseMenu::MENU_SHIFT_DEPTH(kMENUCEPT_SHALLOWER);
}
void CKeyMappingMenu::AcceptConflict()
{
CControlMgr::GetPlayerMappingControl().ReplaceMappingConflict();
}
void CKeyMappingMenu::IgnoreConflict()
{
CControlMgr::GetPlayerMappingControl().CancelCurrentMapping();
CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage().Clear();
CPauseMenu::PlayInputSound( FRONTEND_INPUT_BACK );
}
bool CKeyMappingMenu::Populate(MenuScreenId newScreenId)
{
if( newScreenId.GetValue() != MENU_UNIQUE_ID_KEYMAP)
{
return false;
}
m_bTouchedAControl = false;
// if they had some unsaved controls last time...
if(!SUIContexts::IsActive("HadMappingConflict") && CControlMgr::GetPlayerMappingControl().HasAutoSavedUserControls())
{
CWarningMessage::Data messageData;
messageData.m_TextLabelHeading = "CWS_WARNING";
messageData.m_TextLabelBody = "MAPPING_AUTOSAVE_LOADED";
messageData.m_iFlags = FE_WARNING_YES_NO_CANCEL;
messageData.m_bCloseAfterPress = true;
messageData.m_acceptPressed = datCallback(MFA(CKeyMappingMenu::AcceptAutosave), this);
messageData.m_xPressed = datCallback(MFA(CKeyMappingMenu::RejectAutosave), this);
messageData.m_declinePressed = datCallback(CFA(CPauseMenu::MenuceptionTheKick));
CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage().SetMessage(messageData);
return true;
}
if( CScaleformMenuHelper::SET_COLUMN_TITLE(PM_COLUMN_MIDDLE, TheText.Get("MAPPING_HDR"), false) )
{
CScaleformMgr::AddParamLocString("MAPPING_HDR_PRIM");
CScaleformMgr::AddParamLocString("MAPPING_HDR_SEC");
CScaleformMgr::EndMethod();
}
int lastAddedIndex = AdjustWarningIndicators(false);
// add in restore defaults
CScaleformMenuHelper::SET_DATA_SLOT(PM_COLUMN_LEFT, lastAddedIndex, PREF_OPTIONS_THRESHOLD + MENU_UNIQUE_ID_KEYMAP_LIST, RESTORE_DEFAULTS_ID, eOPTION_DISPLAY_STYLE(5), 0, true, TheText.Get("MAPPING_DEFAULTS"));
CScaleformMenuHelper::DISPLAY_DATA_SLOT(PM_COLUMN_LEFT);
SHOW_COLUMN(PM_COLUMN_LEFT, true);
SHOW_COLUMN(PM_COLUMN_MIDDLE, true);
// necessary to spoof "gain focus"
CPauseMenu::GetMovieWrapper(PAUSE_MENU_MOVIE_CONTENT).CallMethod( "MENU_INTERACTION", PAD_NO_BUTTON_PRESSED );
#if RSG_PC
// Block Social Club Hotkey
g_rlPc.GetUiInterface()->EnableHotkey(false);
#endif
return true;
}
void CKeyMappingMenu::AcceptAutosave()
{
CControl& control = CControlMgr::GetPlayerMappingControl();
control.SetInitialDefaultMappings(true, true);
control.DeleteAutoSavedUserControls();
Populate(MENU_UNIQUE_ID_KEYMAP);
m_bTouchedAControl = true;
}
void CKeyMappingMenu::RejectAutosave()
{
CControl& control = CControlMgr::GetPlayerMappingControl();
control.DeleteAutoSavedUserControls();
Populate(MENU_UNIQUE_ID_KEYMAP);
m_bTouchedAControl = true;
}
int CKeyMappingMenu::AdjustWarningIndicators(bool bIsUpdate /* = true */)
{
m_conflictingInputs.Reset();
m_unmappedInputs.Reset();
CControlMgr::GetPlayerMappingControl().GetUnmappedInputs(m_unmappedInputs);
CControlMgr::GetPlayerMappingControl().GetConflictingInputs(m_conflictingInputs);
// get all of the data-driven binding categories
CControl::MappingCategoriesList mcl;
CControl::GetMappingCategories(mcl);
int iMapIndex = 0;
for(; iMapIndex < mcl.GetCount(); ++iMapIndex)
{
int iShowMissingIcon = 0;
if( !m_unmappedInputs.empty() || !m_conflictingInputs.empty() )
{
CControl::InputList cil;
CControl::GetCategoryInputs(mcl[iMapIndex], cil);
for(InputType eMissingInput : m_unmappedInputs)
{
if( cil.Find(eMissingInput) != -1 )
{
iShowMissingIcon = 1;
break;
}
}
for(InputType eConflictInput : m_conflictingInputs)
{
if( cil.Find(eConflictInput) != -1 )
{
iShowMissingIcon = 1; /// may want to differentiate these icons...
break;
}
}
}
CScaleformMenuHelper::SET_DATA_SLOT(PM_COLUMN_LEFT
, iMapIndex
, PREF_OPTIONS_THRESHOLD + MENU_UNIQUE_ID_KEYMAP_LIST
, mcl[iMapIndex]
, OPTION_DISPLAY_NONE
, iShowMissingIcon
, true
, TheText.Get(mcl[iMapIndex], mcl[iMapIndex].TryGetCStr())
, true
, bIsUpdate
);
}
if( !bIsUpdate )
m_curCategory = mcl[0];
// draw the middle pane
UpdateCategory(m_curCategory, bIsUpdate);
if( !m_conflictingInputs.empty() )
CPauseMenu::SetWarnOnTabChange( "CONFLICT_WARNING");
else if( !m_unmappedInputs.empty() )
CPauseMenu::SetWarnOnTabChange( "UNMAPPED_WARNING" );
else
CPauseMenu::SetWarnOnTabChange( NULL );
// display a warning hint
CPauseMenu::GetMovieWrapper(PAUSE_MENU_MOVIE_CONTENT).CallMethod("SET_DESCRIPTION", 0, (!m_conflictingInputs.empty() || !m_unmappedInputs.empty()) ? TheText.Get("UNMAPPED_INPUTS_HINT") : "");
return iMapIndex;
}
bool CKeyMappingMenu::CheckIncomingFunctions(atHashWithStringBank UNUSED_PARAM(methodName), const GFxValue* UNUSED_PARAM(args))
{
return false;
}
void CKeyMappingMenu::AdjustScrollbar(bool bAreWe)
{
// technically the scroll column exists on the left column
if( bAreWe && m_iKeysInCategory > 16 ) // maximum number visible at once
{
CScaleformMenuHelper::SET_COLUMN_SCROLL(PM_COLUMN_MIDDLE, 0, m_iKeysInCategory+1); // we don't display a number here, so whatever
}
else
{
CScaleformMenuHelper::HIDE_COLUMN_SCROLL(PM_COLUMN_MIDDLE);
}
}
bool CKeyMappingMenu::InitScrollbar()
{
if( CPauseMenu::IsScreenDataValid(MENU_UNIQUE_ID_KEYMAP_LISTITEM) )
{
CMenuScreen& rData = CPauseMenu::GetScreenData(MENU_UNIQUE_ID_KEYMAP_LISTITEM);
rData.INIT_SCROLL_BAR();
}
return false;
}
void CKeyMappingMenu::LayoutChanged( MenuScreenId OUTPUT_ONLY(iPreviousLayout), MenuScreenId iNewLayout, s32 iUniqueId, eLAYOUT_CHANGED_DIR eDir)
{
uiDebugf3("CKeyMappingMenu::LayoutChanged - Prev: %i, New: %i, Unique: %i, Dir: %i", iPreviousLayout.GetValue(), iNewLayout.GetValue(), iUniqueId, eDir);
if( iNewLayout == MENU_UNIQUE_ID_KEYMAP_LIST )
{
if( iUniqueId == RESTORE_DEFAULTS_ID )
{
CScaleformMenuHelper::SET_DATA_SLOT_EMPTY(PM_COLUMN_MIDDLE);
CScaleformMenuHelper::SHOW_COLUMN(PM_COLUMN_MIDDLE, false);
CScaleformMenuHelper::DISPLAY_DATA_SLOT(PM_COLUMN_MIDDLE);
m_curCategory.Clear();
}
else if(eDir != LAYOUT_CHANGED_DIR_BACKWARDS)
{
UpdateCategory( atHashWithStringNotFinal(iUniqueId), false);
}
SetInSubmenu(false);
CPauseMenu::RedrawInstructionalButtons();
}
else if(iNewLayout == MENU_UNIQUE_ID_KEYMAP_LISTITEM)
{
m_iCurHighlightedId = iUniqueId;
SetInSubmenu(true);
}
AdjustScrollbar(true);
}
void CKeyMappingMenu::LoseFocus()
{
uiDebugf3("CKeyMappingMenu::LoseFocus");
CControl& control = CControlMgr::GetPlayerMappingControl();
control.CancelCurrentMapping();
// if they touched something, save it out
if( m_bTouchedAControl )
{
// the true and false is for saving out to the autosave backup save
if( !control.SaveUserControls(false) )
{
control.SaveUserControls(true);
}
}
else
{
uiDisplayf("Didn't save out controls because they didn't touch anything");
}
// We actually need to reload the controls all the time.
control.SetInitialDefaultMappings(true, false);
CPauseMenu::SetWarnOnTabChange( NULL );
m_conflictingInputs.Reset();
m_unmappedInputs.Reset();
SetInSubmenu(false);
m_bTouchedAControl = false;
#if RSG_PC
// Re-Enable Social Club Hotkey
g_rlPc.GetUiInterface()->EnableHotkey(true);
#endif
}
void CKeyMappingMenu::UpdateCategory(const atHashWithStringNotFinal& uCategory, bool bIsUpdate)
{
m_curCategory = uCategory;
if( !bIsUpdate )
{
CScaleformMenuHelper::SET_DATA_SLOT_EMPTY(PM_COLUMN_MIDDLE);
CScaleformMenuHelper::SHOW_COLUMN(PM_COLUMN_MIDDLE, true);
}
CControl::InputList cil;
CControl::GetCategoryInputs(uCategory, cil);
m_iKeysInCategory = 0;
for(s32 iInputIndex = 0; iInputIndex < cil.GetCount(); ++iInputIndex)
{
SendControlToScaleform(cil[iInputIndex], m_iKeysInCategory++, bIsUpdate, 0);
}
if( !bIsUpdate )
{
CScaleformMenuHelper::DISPLAY_DATA_SLOT(PM_COLUMN_MIDDLE);
m_iCurHighlightedId = 0;
}
}
bool CKeyMappingMenu::TriggerEvent(MenuScreenId MenuId, s32 iUniqueId)
{
// player has initiated a binding action!
if( MenuId == MENU_UNIQUE_ID_KEYMAP_LISTITEM )
{
m_iCurHighlightedId = iUniqueId;
u8 mappingSlot;
int iSlotBinding;
if( GetInputForCurrentSelection(m_curMappingInput, mappingSlot, &m_iCurBindingIndex, &iSlotBinding) )
{
if (m_curMappingInput == INPUT_FRONTEND_SOCIAL_CLUB_SECONDARY && mappingSlot == 0)
{
// B* 2349103
// The SCUI hotkey is not technically bindable away from HOME, so we want to prevent this one field
// from being edited. The alternating (mappingSlot == 1) is still fine.
return true;
}
CControlMgr::GetPlayerMappingControl().StartInputMappingScan(m_curMappingInput, CControl::PC_KEYBOARD_MOUSE, mappingSlot);
SendControlToScaleform(m_curMappingInput, m_iCurBindingIndex, true, iSlotBinding);
CPauseMenu::PlayInputSound( FRONTEND_INPUT_ACCEPT );
SUIContexts::Activate("HIDE_ACCEPTBUTTON");
SUIContexts::Deactivate("KEYMAP_CAN_DELETE");
CPauseMenu::RedrawInstructionalButtons();
m_bTouchedAControl = true;
}
return true;
}
else if( MenuId == MENU_UNIQUE_ID_KEYMAP_LIST )
{
// restore the defaults
if( iUniqueId == RESTORE_DEFAULTS_ID )
{
CWarningMessage::Data messageData;
messageData.m_TextLabelHeading = "CWS_WARNING";
messageData.m_TextLabelBody = "MAPPING_RESTORE_DEFAULTS";
messageData.m_iFlags = FE_WARNING_YES_NO;
messageData.m_bCloseAfterPress = true;
messageData.m_acceptPressed = datCallback(MFA(CKeyMappingMenu::AcceptRestoreDefaults), this);
CPauseMenu::GetDynamicPauseMenu()->GetErrorMessage().SetMessage(messageData);
}
else
{
SetInSubmenu(true);
}
}
return false;
}
bool CKeyMappingMenu::GetInputForCurrentSelection(InputType& outInput, u8& outMappingSlot, int* pOutCurSelectionIndex /*= NULL*/, int* pOutSlotBinding /*= NULL*/) const
{
CControl::InputList cil;
CControl::GetCategoryInputs(m_curCategory, cil);
int iAdjIndex = m_iCurHighlightedId;
if( iAdjIndex < SF_SECONDARY_INPUT_OFFSET )
{
outMappingSlot = 0;
if(pOutSlotBinding) *pOutSlotBinding = 1;
}
else
{
iAdjIndex -= SF_SECONDARY_INPUT_OFFSET;
outMappingSlot = 1;
if(pOutSlotBinding) *pOutSlotBinding = 2;
}
if(pOutCurSelectionIndex) *pOutCurSelectionIndex = iAdjIndex;
if( uiVerifyf(iAdjIndex>= 0 && iAdjIndex < cil.GetCount(), "Triggered an action on a bad input ID!") )
{
outInput = cil[iAdjIndex];
return true;
}
outInput = UNDEFINED_INPUT;
return false;
}
void CKeyMappingMenu::AcceptRestoreDefaults()
{
CControlMgr::GetPlayerMappingControl().RestoreDefaultMappings();
CScaleformMenuHelper::SET_COLUMN_HIGHLIGHT(PM_COLUMN_LEFT, 0, true);
CScaleformMenuHelper::SET_COLUMN_FOCUS(PM_COLUMN_LEFT, true, true, true );
AdjustWarningIndicators();
m_bTouchedAControl = true;
}
void CKeyMappingMenu::SendControlToScaleform(InputType input, int iSlotIndex, bool bIsUpdate, int slotBinding)
{
const char* inputName = CControl::GetInputName(input);
bool bShowWarning = m_unmappedInputs.Find(input) != -1 || m_conflictingInputs.Find(input) != -1;
CScaleformMenuHelper::SET_DATA_SLOT(
PM_COLUMN_MIDDLE,
iSlotIndex,
PREF_OPTIONS_THRESHOLD + MENU_UNIQUE_ID_KEYMAP_LISTITEM,
iSlotIndex,
static_cast<eOPTION_DISPLAY_STYLE>(slotBinding),
bShowWarning?1:0,
CControl::IsInputMappable(input) ? 1 : -1, // SF is weird and treats -1 as bad... instead of, say, 0.
TheText.Get(inputName), false, bIsUpdate);
SendInput(input, 0);
SendInput(input, 1);
CScaleformMgr::EndMethod();
}
bool CKeyMappingMenu::IsInputBound(InputType input, u8 mappingSlot) const
{
ioSource source = CControlMgr::GetPlayerMappingControl().GetInputSource(input, ioSource::IOMD_KEYBOARD_MOUSE, mappingSlot, false);
return source.m_Device != IOMS_UNDEFINED && source.m_Parameter != ioSource::UNDEFINED_PARAMETER && source.m_Parameter != KEY_NULL;
}
void CKeyMappingMenu::SendInput(InputType input, u8 slot)
{
// verify we actually have an input bound for this.
if(IsInputBound(input, slot))
{
atStringBuilder builder;
builder.Append("~");
builder.Append(CControl::GetInputName(input));
builder.Append("~");
CScaleformMgr::AddParamString(builder.ToString(), false);
}
else
{
CScaleformMgr::AddParamString("",false); // send an empty string
}
}
void CKeyMappingMenu::SetInSubmenu(bool bInIt)
{
m_bInSubMenu = bInIt;
if( m_bInSubMenu )
{
InputType input;
u8 slot;
if( GetInputForCurrentSelection(input, slot) )
{
if (input == INPUT_FRONTEND_SOCIAL_CLUB_SECONDARY && slot == 0)
{
// B* 2349103
// The SCUI hotkey is not technically bindable away from HOME, so we want to prevent this delete prompt from displaying
SUIContexts::Deactivate("KEYMAP_CAN_DELETE");
}
else
{
SUIContexts::SetActive("KEYMAP_CAN_DELETE", IsInputBound(input, slot) && CControl::IsInputMappable(input) );
}
SUIContexts::SetActive("HIDE_ACCEPTBUTTON", !CControl::IsInputMappable(input));
}
}
else
{
SUIContexts::Deactivate("KEYMAP_CAN_DELETE");
SUIContexts::Deactivate("HIDE_ACCEPTBUTTON");
}
CPauseMenu::RedrawInstructionalButtons();
}
#if __BANK
void CKeyMappingMenu::AddWidgets(bkBank* ToThisBank)
{
if(m_pMyGroup)
return;
m_pMyGroup = ToThisBank->PushGroup("Keymapping Screen");
ToThisBank->AddToggle("Auto accept conflicts", &sm_bAutoResolveConflicts);
ToThisBank->PopGroup();
}
#endif // __BANK
#endif // KEYBOARD_MOUSE_SUPPORT