$optionsEntryPad = 10; $OptionsMenuCategories[0] = "Video"; $OptionsMenuCategories[1] = "Audio"; $OptionsMenuCategories[2] = "KBM"; $OptionsMenuCategories[3] = "Controller"; //============================================================================== // // OptionsMenu General Functions // //============================================================================== function OptionsMenu::onAdd(%this) { if(!isObject(%this.optionsCategories)) { %this.optionsCategories = new ArrayObject(); } %this.currentCategory = ""; callOnModules("populateOptionsMenuCategories", "Game"); } function OptionsMenu::onWake(%this) { %this.optsListCount = -1; $optionsChangeRequiresRestart = false; %this.populateVideoSettings(); %this.populateAudioSettings(); %this.populateKBMControls(); %this.populateGamepadControls(); //establish the cached prefs values here %this.openOptionsCategory("Video"); } if(!isObject( OptionsMenuActionMap ) ) { new ActionMap(OptionsMenuActionMap){}; OptionsMenuActionMap.bind( keyboard, Escape, tryCloseOptionsMenu); OptionsMenuActionMap.bind( gamepad, btn_b, tryCloseOptionsMenu); OptionsMenuActionMap.bind( keyboard, w, OptionMenuNavigatePrev ); OptionsMenuActionMap.bind( keyboard, s, OptionMenuNavigateNext ); OptionsMenuActionMap.bind( gamepad, yaxis, "D", "-0.23 0.23", OptionMenuStickNavigate ); OptionsMenuActionMap.bind( gamepad, upov, OptionMenuNavigatePrev ); OptionsMenuActionMap.bind( gamepad, dpov, OptionMenuNavigateNext ); OptionsMenuActionMap.bind( keyboard, a, OptionMenuPrevSetting ); OptionsMenuActionMap.bind( keyboard, d, OptionMenuNextSetting ); OptionsMenuActionMap.bind( gamepad, xaxis, "D", "-0.23 0.23", OptionMenuStickChangeSetting ); OptionsMenuActionMap.bind( gamepad, lpov, OptionMenuPrevSetting ); OptionsMenuActionMap.bind( gamepad, rpov, OptionMenuNextSetting ); OptionsMenuActionMap.bind( keyboard, q, OptionsMenuPrevCategory ); OptionsMenuActionMap.bind( gamepad, btn_l, OptionsMenuPrevCategory ); OptionsMenuActionMap.bind( keyboard, e, OptionsMenuNextCategory ); OptionsMenuActionMap.bind( gamepad, btn_r, OptionsMenuNextCategory ); OptionsMenuActionMap.bind( keyboard, R, OptionsMenuReset ); OptionsMenuActionMap.bind( gamepad, btn_x, OptionsMenuReset ); OptionsMenuActionMap.bind( keyboard, Space, OptionsMenuActivateOption ); OptionsMenuActionMap.bind( gamepad, btn_a, OptionsMenuActivateOption ); OptionsMenuActionMap.bind( keyboard, Enter, tryApplyOptions); OptionsMenuActionMap.bind( gamepad, btn_start, tryApplyOptions); } //============================================================================== // This function updates all the elements in the actual lists to ensure they're // sized, stylized and formatted correctly, as well as up to date values function OptionsMenuList::syncGui(%this) { %this.callOnChildren("setHighlighted", false); %btn = %this.getObject(%this.listPosition); if(%btn.class $= "OptionsListEntry" || %btn.class $= "OptionsListSliderEntry" || %btn.class $= "OptionsKeybindEntry") %btn-->button.setHighlighted(true); //iterate over the items and ensure that they are formatted well based on the settings selected foreach(%option in %this) { %container = %option-->valuesContainer; if(%option.class $= "OptionsListEntry") { %hasLevels = %option.optionsObject.getCount() <= 1; %optionObject = %option.optionsObject; //If it's out of range of the options, it's probably a custom value if(%option.currentOptionIndex < %optionObject.getCount() && %option.currentOptionIndex >= 0) { %currentOptionLevel = %optionObject.getObject(%option.currentOptionIndex); %currentOptionLevelTxt = %currentOptionLevel.displayName; } else { %currentOptionLevelTxt = "Custom"; } %optionValTextWidth = %option-->optionValue.profile.getStringWidth(%currentOptionLevelTxt); %option-->optionValue.resize(%container.extent.x - %container-->prevValButton.extent.x - %optionValTextWidth - 20, 0, %optionValTextWidth + 20, %container.extent.y); %option-->optionValue.text = %currentOptionLevelTxt; %container-->prevValButton.position.x = %option-->optionValue.position.x - 20; %container-->nextValButton.position.x = %container.extent.x - %container-->prevValButton.extent.x; //if there's no alternatives, disable the left/right buttons %container-->prevValButton.setHidden(%hasLevels); %container-->nextValButton.setHidden(%hasLevels); } else if(%option.class $= "OptionsListSliderEntry") { } else if(%option.class $= "OptionsKeybindEntry") { if(getWordCount(getField(%option.keymap, 1)) == 2) { %keymap = getField(%option.keymap, 1); %modifierImgAsset = getButtonBitmap(%option.device, getWord(%keymap, 0)); if(%modifierImgAsset $= "UI:Keyboard_Black_Blank_image") %modifierImgAsset = ""; %container-->modifierButton.setBitmap(%modifierImgAsset); // %bindImgAsset = getButtonBitmap(%option.device, getWord(%keymap, 1)); if(%bindImgAsset $= "UI:Keyboard_Black_Blank_image") %bindImgAsset = ""; %container-->bindButton.setBitmap(%bindImgAsset); } else { %bindImgAsset = getButtonBitmap(%option.device, getField(%option.keymap, 1)); if(%bindImgAsset $= "UI:Keyboard_Black_Blank_image") %bindImgAsset = ""; %container-->bindButton.setBitmap(%bindImgAsset); } } } } function OptionsMenuList::checkForUnappliedChanges(%this) { %unappliedChanges = false; foreach(%option in %this) { if(%option.class $= "OptionsListEntry") { if(%option.currentOptionIndex >= 0 && %option.currentOptionIndex < %option.optionsObject.getCount()) { %targetOptionLevel = %option.optionsObject.getObject(%option.currentOptionIndex); if(!%targetOptionLevel.isCurrent()) %unappliedChanges = true; if(%unappliedChanges && %option.optionsObject.requiresRestart) $optionsChangeRequiresRestart = true; } } } return %unappliedChanges; } function OptionsMenuList::applyChanges(%this) { foreach(%option in %this) { if(%option.class $= "OptionsListEntry") { //If it's custom or nonsensical index, there's some kind of external factor going on, so we're //just going to skip applying it because we don't know what we'd be applying if(%option.currentOptionIndex >= 0 && %option.currentOptionIndex < %option.optionsObject.getCount()) { %targetOptionLevel = %option.optionsObject.getObject(%option.currentOptionIndex); if(!%targetOptionLevel.isCurrent()) %targetOptionLevel.apply(); } } } } function OptionsMenu::openOptionsCategory(%this, %categoryName) { VideoSettingsList.setVisible(%categoryName $= "Video"); AudioSettingsList.setVisible(%categoryName $= "Audio"); KBMControlsList.setVisible(%categoryName $= "KBM"); GamepadControlsList.setVisible(%categoryName $= "Controller"); if(%categoryName $= "Video") { $MenuList = VideoSettingsList; //Find our first non-group entry while($MenuList.getObject($MenuList.listPosition).class !$= OptionsListEntry && $MenuList.listPosition < $MenuList.getCount()) { $MenuList.listPosition += 1; } %this.currentCatgeoryIdx = 0; } else if(%categoryName $= "Audio") { $MenuList = AudioSettingsList; %this.currentCatgeoryIdx = 1; } else if(%categoryName $= "KBM") { $MenuList = KBMControlsList; %this.currentCatgeoryIdx = 2; } else if(%categoryName $= "Controller") { $MenuList = GamepadControlsList; %this.currentCatgeoryIdx = 3; } $MenuList.syncGui(); %this.syncGui(); } //============================================================================== // This function updates the non-list items of the menu to be up to date and stylistically // complaint. This ensures keybind hint buttons are presented correctly based on the current input // device function OptionsMenu::syncGui(%this) { OptionsMenuCategoryList.callOnChildren("setHighlighted", false); %btn = OptionsMenuCategoryList.getObject(%this.currentCatgeoryIdx); %btn.setHighlighted(true); %buttonPosX = %btn.position.x + OptionsMenuCategoryList.position.x; OptionsMenuPrevNavIcon.position.x = %buttonPosX - 5; OptionsMenuNextNavIcon.position.x = %buttonPosX + %btn.extent.x - 35; //Update the button imagery to comply to the last input device we'd used %device = Canvas.getLastInputDevice(); if(%device $= "mouse") %device = "keyboard"; OptionsMenuBackBtn.setBitmap(BaseUIActionMap.getCommandButtonBitmap(%device, "BaseUIBackOut")); OptionsMenuResetBtn.setBitmap(OptionsMenuActionMap.getCommandButtonBitmap(%device, "OptionsMenuReset")); OptionsMenuPrevNavIcon.setBitmap(OptionsMenuActionMap.getCommandButtonBitmap(%device, "OptionsMenuPrevCategory")); OptionsMenuNextNavIcon.setBitmap(OptionsMenuActionMap.getCommandButtonBitmap(%device, "OptionsMenuNextCategory")); OptionsMenuApplyBtn.setBitmap(OptionsMenuActionMap.getCommandButtonBitmap(%device, "tryApplyOptions")); OptionsMenuRemapBtn.visible = KBMControlsList.visible || GamepadControlsList.visible; OptionsMenuRemapBtn.setBitmap(OptionsMenuActionMap.getCommandButtonBitmap(%device, "OptionsMenuActivateOption")); } //============================================================================== // Menu navigation functions // Primarily used by keybinds function OptionsMenuPrevCategory(%val) { if(%val) { %currentIdx = OptionsMenu.currentMenuIdx; OptionsMenu.currentMenuIdx -= 1; OptionsMenu.currentMenuIdx = mClamp(OptionsMenu.currentMenuIdx, 0, 3); if(%currentIdx == OptionsMenu.currentMenuIdx) return; %newCategory = $OptionsMenuCategories[OptionsMenu.currentMenuIdx]; OptionsMenu.openOptionsCategory(%newCategory); } } function OptionsMenuNextCategory(%val) { if(%val) { %currentIdx = OptionsMenu.currentMenuIdx; OptionsMenu.currentMenuIdx += 1; OptionsMenu.currentMenuIdx = mClamp(OptionsMenu.currentMenuIdx, 0, 3); if(%currentIdx == OptionsMenu.currentMenuIdx) return; %newCategory = $OptionsMenuCategories[OptionsMenu.currentMenuIdx]; OptionsMenu.openOptionsCategory(%newCategory); } } function OptionMenuNavigatePrev(%val) { if(%val) { $MenuList.listPosition -= 1; while( $MenuList.listPosition >= 0 && ($MenuList.getObject($MenuList.listPosition).class !$= "OptionsListEntry" && $MenuList.getObject($MenuList.listPosition).class !$= "OptionsListSliderEntry" && $MenuList.getObject($MenuList.listPosition).class !$= "OptionsKeybindEntry")) { $MenuList.listPosition -= 1; } if($MenuList.listPosition < 0) $MenuList.listPosition = 0; $MenuList.syncGUI(); $BaseUI::scrollSchedule = schedule($BaseUI::scrollSpeedTimeMs, 0, "OptionMenuNavigatePrev", 1); } else { cancel($BaseUI::scrollSchedule); } } function OptionMenuNavigateNext(%val) { if(%val) { $MenuList.listPosition += 1; while($MenuList.listPosition < $MenuList.getCount() && ($MenuList.getObject($MenuList.listPosition).class !$= "OptionsListEntry" && $MenuList.getObject($MenuList.listPosition).class !$= "OptionsListSliderEntry" && $MenuList.getObject($MenuList.listPosition).class !$= "OptionsKeybindEntry")) { $MenuList.listPosition += 1; } if($MenuList.listPosition >= $MenuList.getCount()) $MenuList.listPosition = $MenuList.getCount()-1; $MenuList.syncGUI(); $BaseUI::scrollSchedule = schedule($BaseUI::scrollSpeedTimeMs, 0, "OptionMenuNavigateNext", 1); } else { cancel($BaseUI::scrollSchedule); } } function OptionMenuStickNavigate(%val) { if(%val == 1) OptionMenuNavigateNext(1); else if(%val == -1) OptionMenuNavigatePrev(1); else cancel($BaseUI::scrollSchedule); } function OptionMenuPrevSetting(%val) { if(!%val) return; %option = $MenuList.getObject($MenuList.listPosition); if(!isObject(%option)) return; if(%option.class $= "OptionsListEntry") { %optionObject = %option.optionsObject; %currentOptionLevel = %optionObject.getObject(%option.currentOptionIndex); %option.currentOptionIndex = mClamp(%option.currentOptionIndex-1, 0, %optionObject.getCount()-1); %newOptionLevel = %optionObject.getObject(%option.currentOptionIndex); //echo("Changed option: " @ %optionObject.optionName @ " from level: " @ %currentOptionLevel.displayName @ " to level: " @ %newOptionLevel.displayName); } else if(%option.class $= "OptionsListSliderEntry") { %sliderCtrl = %option-->valuesContainer-->slider; %minValue = %sliderCtrl.range.x; %maxValue = %sliderCtrl.range.y; %ticks = %sliderCtrl.ticks; %tickIncrementVal = (%maxValue - %minValue) / %ticks; %sliderCtrl.value -= %tickIncrementVal; } $MenuList.syncGUI(); } function OptionMenuNextSetting(%val) { if(!%val) return; %option = $MenuList.getObject($MenuList.listPosition); if(!isObject(%option) ) return; if(%option.class $= "OptionsListEntry") { %optionObject = %option.optionsObject; %currentOptionLevel = %optionObject.getObject(%option.currentOptionIndex); %option.currentOptionIndex = mClamp(%option.currentOptionIndex+1, 0, %optionObject.getCount()-1); %newOptionLevel = %optionObject.getObject(%option.currentOptionIndex); //echo("Changed option: " @ %optionObject.optionName @ " from level: " @ %currentOptionLevel.displayName @ " to level: " @ %newOptionLevel.displayName); } else if(%option.class $= "OptionsListSliderEntry") { %sliderCtrl = %option-->valuesContainer-->slider; %minValue = %sliderCtrl.range.x; %maxValue = %sliderCtrl.range.y; %ticks = %sliderCtrl.ticks; %tickIncrementVal = (%maxValue - %minValue) / %ticks; %sliderCtrl.value += %tickIncrementVal; } $MenuList.syncGUI(); } function OptionMenuStickChangeSetting(%val) { if(%val == 1) OptionMenuNextSetting(1); else if(%val == -1) OptionMenuPrevSetting(1); } function OptionsMenuActivateOption(%val) { if(!%val) return; %option = $MenuList.getObject($MenuList.listPosition); if(!isObject(%option)) return; if(%option.class $= "OptionsKeybindEntry") { %option-->button.execAltCommand(); } } function tryCloseOptionsMenu(%val) { if(!%val) return; $optionsChangeRequiresRestart = false; %unappliedVideoChanges = VideoSettingsList.checkForUnappliedChanges(); %unappliedAudioChanges = AudioSettingsList.checkForUnappliedChanges(); //validate audio prefs if($pref::SFX::masterVolume_tempVar !$= "" && $pref::SFX::masterVolume_tempVar != $pref::SFX::masterVolume) %unappliedAudioChanges = true; for(%i=1; %i < $AudioChannelCount; %i++) { %tempVolume = getVariable("$pref::SFX::channelVolume" @ %i @ "_tempVar"); if(%tempVolume !$= "" && $pref::SFX::channelVolume[ %i ] != %tempVolume) %unappliedAudioChanges = true; } if(%unappliedVideoChanges || %unappliedAudioChanges) { MessageBoxOKCancel("Discard Changes?", "You have unapplied changes to your settings, do you wish to apply or discard them?", "OptionsMenu.applyChangedOptions(); BaseUIBackOut(1);", "BaseUIBackOut(1);", "Apply", "Discard"); } else { BaseUIBackOut(1); } } function tryApplyOptions(%val) { if(!%val) return; $optionsChangeRequiresRestart = false; %unappliedVideoChanges = VideoSettingsList.checkForUnappliedChanges(); %unappliedAudioChanges = AudioSettingsList.checkForUnappliedChanges(); if(%unappliedVideoChanges || %unappliedAudioChanges) OptionsMenu.applyChangedOptions(); } function OptionsMenu::applyChangedOptions(%this) { VideoSettingsList.applyChanges(); AudioSettingsList.applyChanges(); KBMControlsList.applyChanges(); //Saves settings and binds from GamepadControlsList as well //Process the audio channel tempvars to get their values //and then apply them to the actual pref variable, as well as the SFXChannelVolume $pref::SFX::masterVolume = $pref::SFX::masterVolume_tempVar; sfxSetMasterVolume( $pref::SFX::masterVolume ); //0 is always master anyways for(%i=1; %i < $AudioChannelCount; %i++) { %volume = getVariable("$pref::SFX::channelVolume" @ %i @ "_tempVar"); sfxSetChannelVolume( %i, %volume ); $pref::SFX::channelVolume[ %i ] = %volume; } //Finally, write our prefs to file %prefPath = getPrefpath(); export("$pref::*", %prefPath @ "/clientPrefs." @ $TorqueScriptFileExtension, false); if($optionsChangeRequiresRestart) MessageBoxOK("Restart Required", "Some of your changes require the game to be restarted."); } //============================================================================== // Option types function addOptionGroup(%displayName) { OptionsMenu.optsListCount++; %group = new GuiTextCtrl() { text = %displayName; position = "0 0"; extent = "500 45"; profile = "MenuHeaderText"; tooltipProfile = "GuiToolTipProfile"; canSave = false; }; return %group; } function optionsMenuButton::onHighlighted(%this, %highlighted) { %container = %this.getParent(); %container-->optionName.profile = %highlighted ? MenuSubHeaderTextHighlighted : MenuSubHeaderText; %container-->optionDescription.profile = %highlighted ? GuiMLTextProfileHighlighted : GuiMLTextProfile; %valuesContainer = %container-->valuesContainer; %valuesContainer-->optionValue.profile = %highlighted ? GuiMenuTextProfileHL : GuiMenuTextProfile; OptionsMenuSettingsScroll.scrollToObject(%container); } function addOptionEntry(%optionObj) { OptionsMenu.optsListCount++; if(!isObject(%optionObj) || (%optionObj.class !$= "OptionsSettings" && %optionObj.class !$= "AudioOptionsSettings")) { error("addOptionsEntry() - attempting to create a new options entry, but was provided an invalid options object"); return 0; } %qualityLevel = getCurrentQualityLevel(%optionObj); if(isObject(%qualityLevel)) { %qualityLevelText = %qualityLevel.displayName; %qualityLevelIndex = %optionObj.getObjectIndex(%qualityLevel); } else { %qualityLevelText = %qualityLevel; %qualityLevelIndex = %optionObj.getCount(); } %optionNameHeight = 20; if(%optionObj.Description $= "") %optionNameHeight = 40; %entry = new GuiContainer() { position = "0 0"; extent = "800 40"; profile = GuiMenuDefaultProfile; tooltipProfile = "GuiToolTipProfile"; horizSizing = "width"; vertSizing = "bottom"; class = "OptionsListEntry"; optionsObject = %optionObj; currentOptionIndex = %qualityLevelIndex; selectionID = OptionsMenu.optsListCount; canSave = false; new GuiButtonCtrl() { profile = GuiMenuButtonProfile; position = "0 0"; extent = "800 40"; horizSizing = "width"; vertSizing = "height"; internalName = "button"; class = "optionsMenuButton"; }; new GuiTextCtrl() { text = %optionObj.OptionName; position = $optionsEntryPad SPC -1; extent = 400 SPC %optionNameHeight; profile = "MenuSubHeaderText"; tooltipProfile = "GuiToolTipProfile"; internalName = "optionName"; }; new GuiTextCtrl() { text = %optionObj.Description; position = $optionsEntryPad SPC 17; extent = "400 18"; profile = "GuiMLTextProfile"; tooltipProfile = "GuiToolTipProfile"; internalName = "optionDescription"; }; new GuiContainer() { position = "400 0"; extent = "400 40"; profile = GuiModelessDialogProfile; tooltipProfile = "GuiToolTipProfile"; horizSizing = "left"; vertSizing = "height"; internalName = "valuesContainer"; new GuiButtonCtrl() { position = "310 0"; extent = "20 40"; text = "<"; profile = GuiMenuButtonProfile; internalName = "prevValButton"; command = "$MenuList.listPosition = $thisControl.getParent().getParent().selectionID; OptionMenuPrevSetting(1);"; }; new GuiTextCtrl() { text = %qualityLevelText; position = "330 0"; extent = "50 40"; profile = "GuiMenuTextProfile"; tooltipProfile = "GuiToolTipProfile"; horizSizing = "right"; vertSizing = "center"; internalName = "optionValue"; }; new GuiButtonCtrl() { position = "380 0"; extent = "20 40"; text = ">"; profile = GuiMenuButtonProfile; internalName = "nextValButton"; command = "$MenuList.listPosition = $thisControl.getParent().getParent().selectionID; OptionMenuNextSetting(1);"; }; }; }; return %entry; } function addOptionSlider(%optionName, %optionDesc, %prefName, %sliderMin, %sliderMax, %sliderTicks) { OptionsMenu.optsListCount++; %currentVal = getVariable(%prefName); %tempVarName = %prefName @ "_tempVar"; if(%currentVal $= "") %currentVal = %sliderMin; setVariable(%tempVarName, %currentVal); %optionNameHeight = 20; if(%optionDesc $= "") %optionNameHeight = 40; %entry = new GuiContainer() { position = "0 0"; extent = "800 40"; profile = GuiMenuDefaultProfile; tooltipProfile = "GuiToolTipProfile"; horizSizing = "width"; vertSizing = "bottom"; class = "OptionsListSliderEntry"; canSave = false; new GuiButtonCtrl() { profile = GuiMenuButtonProfile; position = "0 0"; extent = "800 40"; horizSizing = "width"; vertSizing = "height"; internalName = "button"; class = "optionsMenuButton"; }; new GuiTextCtrl() { text = %optionName; position = $optionsEntryPad SPC -1; extent = 400 SPC %optionNameHeight; profile = "MenuSubHeaderText"; tooltipProfile = "GuiToolTipProfile"; internalName = "optionName"; }; new GuiTextCtrl() { text = %optionDesc; position = $optionsEntryPad SPC 17; extent = "400 18"; profile = "GuiMLTextProfile"; tooltipProfile = "GuiToolTipProfile"; internalName = "optionDescription"; }; new GuiContainer() { position = "400 0"; extent = "400 40"; profile = GuiModelessDialogProfile; tooltipProfile = "GuiToolTipProfile"; horizSizing = "left"; vertSizing = "height"; internalName = "valuesContainer"; new GuiSliderCtrl() { range = %sliderMin SPC %sliderMax; ticks = %sliderTicks; snap = "1"; value = %currentVal; variable = %tempVarName; useFillBar = "1"; fillBarColor = $TextMediumEmphasisColor; renderTicks = "0"; position = "0 10"; extent = "400 20"; minExtent = "8 2"; horizSizing = "right"; vertSizing = "center"; profile = GuiMenuButtonProfile; visible = "1"; active = "1"; command = "$thisControl.updateSliderValue();"; tooltipProfile = "GuiToolTipProfile"; hovertime = "1000"; isContainer = "0"; canSave = "1"; canSaveDynamicFields = "0"; class = "OptionsSliderEntrySlider"; internalName = "slider"; }; }; }; return %entry; } function OptionsSliderEntrySlider::updateSliderValue(%this) { //update settings value here } //============================================================================== // // Video List Functions // //============================================================================== //============================================================================== // This function utilizes the VideoSettingsGroup SimGroup to populate options. // The object is defined in core/rendering/scripts/graphicsOptions.tscript // A majority of the options are statically defined, but some are dynamically populated // on refresh, like the display device or available resolution options. // Once populated, we loop over the simgroup structure to populate our option entry // rows in the options menu itself. function OptionsMenu::populateVideoSettings(%this) { VideoSettingsList.clear(); VideoSettingsGroup::populateDisplaySettings(); for(%i=0; %i < VideoSettingsGroup.getCount(); %i++) { %setting = VideoSettingsGroup.getObject(%i); if(%setting.class $= "SubOptionsGroup") { %entry = addOptionGroup(%setting.displayName); if(isObject(%entry)) VideoSettingsList.add(%entry); for(%s=0; %s < %setting.getCount(); %s++) { %option = %setting.getObject(%s); %optionsEntry = addOptionEntry(%option); if(isObject(%optionsEntry)) VideoSettingsList.add(%optionsEntry); } } else if(%setting.class $= "OptionsSettings") { %optionsEntry = addOptionEntry(%setting); if(isObject(%optionsEntry)) VideoSettingsList.add(%optionsEntry); } } //Ensure our newly templated options listings are sized right for(%i=0; %i < VideoSettingsList.getCount(); %i++) { %entry = VideoSettingsList.getObject(%i); %entry.resize(0, 0, VideoSettingsList.extent.x - 15, %entry.extent.y); //-10 for the scroll wheel pad } } function OptionsMenu::resetSettings(%this) { MessageBoxOKCancel("", "This will set the graphical settings back to the auto-detected defaults. Do you wish to continue?", "AutodetectGraphics();", ""); } //============================================================================== // // Audio List Functions // //============================================================================== //============================================================================== // This function utilizes the AudioSettingsGroup SimGroup to populate options. // The object is defined in core/sfx/scripts/audioOptions.tscript // Similar to the video options, it can be a mix of static and dynamically populated // option entries, which we then iterate over and populate the entry rows for the menu function OptionsMenu::populateAudioSettings(%this) { AudioSettingsList.clear(); AudioSettingsGroup.populateSettings(); //Process the lists for(%i=0; %i < AudioSettingsGroup.getCount(); %i++) { %setting = AudioSettingsGroup.getObject(%i); if(%setting.class $= "SubOptionsGroup") { %entry = addOptionGroup(%setting.displayName); if(isObject(%entry)) AudioSettingsList.add(%entry); for(%s=0; %s < %setting.getCount(); %s++) { %option = %setting.getObject(%s); %optionsEntry = addOptionEntry(%option); if(isObject(%optionsEntry)) AudioSettingsList.add(%optionsEntry); } } else if(%setting.class $= "AudioOptionsSettings") { %optionsEntry = addOptionEntry(%setting); if(isObject(%optionsEntry)) AudioSettingsList.add(%optionsEntry); } } AudioSettingsList.add(addOptionGroup("Channel Volume")); //Now we'll populate the sliders for the audio channels. //The defaults of these are defined in core/sfx/scripts/audio.tscript //These define the MasterVolume channel, as well as several other common defualt ones //Because it's a variable list, this can be expanded by modules by just upping $AudioChannelCount //and then defining the $AudioChannelName[x] with the displayed name and //and the $AudioChannels[x] variable with the SFXSource object defined to it for the given channel AudioSettingsList.add(addOptionSlider("Master Volume", "", "$pref::SFX::masterVolume", 0, 1, 10)); //We init to 1, because 0 is the reserved for the masterVolume in practice for(%i=1; %i < $AudioChannelCount; %i++) { AudioSettingsList.add(addOptionSlider($AudioChannelsName[%i] @ " Volume", "", "$pref::SFX::channelVolume" @ %i, 0, 1, 10)); } //Ensure our newly templated options listings are sized right for(%i=0; %i < AudioSettingsList.getCount(); %i++) { %entry = AudioSettingsList.getObject(%i); %entry.resize(0, 0, AudioSettingsList.extent.x - 15, %entry.extent.y); //-10 for the scroll wheel pad } } //============================================================================== // // Keyboard & Mouse, Controller List Functions // //============================================================================== function KBMControlsList::applyChanges(%this){ //Save mouse sensitivity $pref::Input::LinkMouseSensitivity = $pref::Input::LinkMouseSensitivity_tempVar; if(!$RemapDirty) return; %prefPath = getPrefpath(); %filePath = (%prefPath @ "/keybinds." @ $TorqueScriptFileExtension); %fileWrite = new fileObject(){}; // Delete the file first if it exists if (isFile(%filePath)) fileDelete( %filepath ); // Open / create the custom binds file %fileWrite.openForWrite(%filePath); // Iterate through the remapabble bindings and write them to the keyBinds file for(%i = 0; %i < $RemapCount; %i++){ %actionMap = $RemapActionMap[%i]; %cmd = $RemapCmd[%i]; %binding = %actionMap.getBinding(%cmd); %device = getField(%binding, 0); %action = getField(%binding, 1); // saves a restoreRemap call to each line of keyBinds file instead of the bind command in order to leverage functions for // removing conflicts and duplicates if (%device !$= "" && %action !$= ""){ %line = "restoreRemap(" @ %device @ ", " @ %actionMap @ ", \"" @ %action @ "\", \"" @ %cmd @ "\");"; %fileWrite.writeLine(%line); } else continue; } %fileWrite.close(); %fileWrite.delete(); $RemapDirty = false; } // restoreRemap() is called from user custom keybinds file which is generated by KBMControlsList::applyChanges() function restoreRemap(%device, %actionMap, %action, %cmd){ // Make sure no other "action" (key / button press) is bound to this command on this actionMap (from remapDlg.tscript) unbindExtraActions( %cmd, %actionMap, %device, 0 ); unbindExtraActions( %cmd, %actionMap, %device, 1 ); %actionMap.bind( %device, %action, %cmd ); } function OptionsMenu::populateKBMControls(%this){ %this.populateKeybinds("keyboard", KBMControlsList); // includes remappable actions declared with "mouse" as the device as well %this.syncGui(); KBMControlsList.syncGui(); } function OptionsMenu::populateGamepadControls(%this){ %this.populateKeybinds("gamepad", GamepadControlsList); %this.syncGui(); GamepadControlsList.syncGui(); } function OptionsMenu::populateKeybinds(%this,%device, %controlsList) { %controlsList.clear(); if (%device $= "keyboard") { %controlsList.add(addOptionGroup("Mouse Options")); %controlsList.add(addOptionSlider("Mouse Sensitivity", "", "$pref::Input::LinkMouseSensitivity", 0, 1, 10)); } //build out our list of action maps %actionMapCount = ActionMapGroup.getCount(); %actionMapList = ""; for(%i=0; %i < %actionMapCount; %i++) { %actionMap = ActionMapGroup.getObject(%i); if(%actionMap == GlobalActionMap.getId()) continue; %actionMapName = %actionMap.humanReadableName $= "" ? %actionMap.getName() : %actionMap.humanReadableName; //see if we have any actual listed remappable keys for this movemap. if so, drop it from the listing %hasRemaps = false; for ( %r = 0; %r < $RemapCount; %r++ ) { %testMapName = $RemapActionMap[%r].humanReadableName $= "" ? $RemapActionMap[%r].getName() : $RemapActionMap[%r].humanReadableName; if(%actionMapName $= %testMapName) { //got a match to at least one, so we're ok to continue %hasRemaps = true; break; } } if(!%hasRemaps) continue; if(%actionMapList $= "") %actionMapList = %actionMapName; else %actionMapList = %actionMapList TAB %actionMapName; } //If we didn't find any valid actionMaps, then just exit out if(%actionMapList $= "") return; if($activeRemapControlSet $= "") $activeRemapControlSet = getField(%actionMapList, 0); echo("============================================"); for(%am = 0; %am < getFieldCount(%actionMapList); %am++) { %currentActionMap = getField(%actionMapList, %am); //only add the group if we've got more than one group, otherwise it's obviously //part of the single grouping if(getFieldCount(%actionMapList) > 1) { %actionMapGroupEntry = addOptionGroup(%currentActionMap); %controlsList.add(%actionMapGroupEntry); } for ( %i = 0; %i < $RemapCount; %i++ ) { //If there was no match go to the next remappable action if(%device $= "" || %device !$= $RemapDevice[%i]) continue; %actionMapName = $RemapActionMap[%i].humanReadableName $= "" ? $RemapActionMap[%i].getName() : $RemapActionMap[%i].humanReadableName; if(%currentActionMap !$= %actionMapName) continue; %keyMap = buildFullMapString( %i, $RemapActionMap[%i], %device ); %description = $RemapDescription[%i]; echo("Added ActionMap Entry: " @ %actionMapName @ " | " @ %device @ " | " @ %keymap @ " | " @ %description); %remapEntry = addActionMapEntry(%actionMapName, %device, %keyMap, %i, %description); %controlsList.add(%remapEntry); } } //Ensure our newly templated options listings are sized right for(%i=0; %i < %controlsList.getCount(); %i++) { %entry = %controlsList.getObject(%i); %entry.resize(0, 0, %controlsList.extent.x - 15, %entry.extent.y); //-10 for the scroll wheel pad } } function buildFullMapString( %index, %actionMap, %deviceType ) { %name = $RemapName[%index]; %cmd = $RemapCmd[%index]; %temp = %actionMap.getBinding( %cmd ); if ( %temp $= "" ) return %name TAB ""; %mapString = ""; %count = getFieldCount( %temp ); for ( %i = 0; %i < %count; %i += 2 ) { if ( %mapString !$= "" ) continue; //%mapString = %mapString @ ", "; %device = getField( %temp, %i + 0 ); %object = getField( %temp, %i + 1 ); if (startsWith(%device,"mouse")) %deviceType = "mouse"; if(%deviceType !$= "" && !startsWith(%device, %deviceType)) continue; %mapString = %mapString @ getMapDisplayName( %device, %object ); } return %name TAB %mapString; } // Also used by remapDlg.tscript function getMapDisplayName( %device, %action ) { if ( %device $= "keyboard" ) return( %action ); else if ( strstr( %device, "mouse" ) != -1 ) { // Substitute "mouse" for "button" in the action string: %pos = strstr( %action, "button" ); if ( %pos != -1 ) { %mods = getSubStr( %action, 0, %pos ); %object = getSubStr( %action, %pos, 1000 ); %instance = getSubStr( %object, strlen( "button" ), 1000 ); return( %mods @ "mouse" @ ( %instance + 1 ) ); } else error( "Mouse input object other than button passed to getDisplayMapName!" ); } else if ( strstr( %device, "joystick" ) != -1 ) { // Substitute "joystick" for "button" in the action string: %pos = strstr( %action, "button" ); if ( %pos != -1 ) { %mods = getSubStr( %action, 0, %pos ); %object = getSubStr( %action, %pos, 1000 ); %instance = getSubStr( %object, strlen( "button" ), 1000 ); return( %mods @ "joystick" @ ( %instance + 1 ) ); } else { %pos = strstr( %action, "pov" ); if ( %pos != -1 ) { %wordCount = getWordCount( %action ); %mods = %wordCount > 1 ? getWords( %action, 0, %wordCount - 2 ) @ " " : ""; %object = getWord( %action, %wordCount - 1 ); switch$ ( %object ) { case "upov": %object = "POV1 up"; case "dpov": %object = "POV1 down"; case "lpov": %object = "POV1 left"; case "rpov": %object = "POV1 right"; case "upov2": %object = "POV2 up"; case "dpov2": %object = "POV2 down"; case "lpov2": %object = "POV2 left"; case "rpov2": %object = "POV2 right"; default: %object = ""; } return( %mods @ %object ); } else error( "Unsupported Joystick input object passed to getDisplayMapName!" ); } } else if ( strstr( %device, "gamepad" ) != -1 ) { return %action; %pos = strstr( %action, "button" ); if ( %pos != -1 ) { %mods = getSubStr( %action, 0, %pos ); %object = getSubStr( %action, %pos, 1000 ); %instance = getSubStr( %object, strlen( "button" ), 1000 ); return( %mods @ "joystick" @ ( %instance + 1 ) ); } else { %pos = strstr( %action, "thumb" ); if ( %pos != -1 ) { //%instance = getSubStr( %action, strlen( "thumb" ), 1000 ); //return( "thumb" @ ( %instance + 1 ) ); return %action; } else error( "Unsupported gamepad input object passed to getDisplayMapName!" ); } } return( "" ); } function addActionMapEntry(%actionMap, %device, %keyMap, %index, %description) { %entry = new GuiContainer() { position = "0 0"; extent = "800 40"; profile = GuiMenuDefaultProfile; tooltipProfile = "GuiToolTipProfile"; horizSizing = "width"; vertSizing = "bottom"; class = "OptionsKeybindEntry"; actionMap = %actionMap; device = %device; keymap = %keyMap; remapIndex = %index; canSave = false; new GuiButtonCtrl() { profile = GuiMenuButtonProfile; position = "0 0"; extent = "800 40"; horizSizing = "width"; vertSizing = "height"; internalName = "button"; class = "OptionsMenuActionMapButton"; altCommand = "doKeyRemap($thisControl.getParent());"; }; new GuiTextCtrl() { text = getField(%keyMap, 0); position = $optionsEntryPad SPC -1; extent = "400 40"; profile = "MenuSubHeaderText"; tooltipProfile = "GuiToolTipProfile"; internalName = "actionName"; }; new GuiContainer() { position = "400 3"; extent = "400 34"; profile = GuiModelessDialogProfile; tooltipProfile = "GuiToolTipProfile"; horizSizing = "left"; vertSizing = "height"; internalName = "valuesContainer"; }; }; %buttonContainer = %entry.findObjectByInternalName("valuesContainer"); echo("Keymap: " @ %keymap @ " | Keymap word count: " @ getWordCount(getField(%keyMap, 1))); if(getWordCount(getField(%keyMap, 1)) == 2) { %modifierBtn = new GuiIconButtonCtrl() { position = 156 SPC -10; extent = "98 45"; BitmapAsset = ""; profile = GuiRemapActionMapButtonProfile; sizeIconToButton = true; makeIconSquare = true; iconLocation = "center"; internalName = "modifierButton"; active = false; }; %combinerText = new GuiTextCtrl(){ position = 264 SPC -5; extent = "20 45"; profile = MenuSubHeaderText; text = " + "; }; %bindButton = new GuiIconButtonCtrl() { position = "300 -10"; extent = "98 45"; BitmapAsset = ""; profile = GuiRemapActionMapButtonProfile; sizeIconToButton = true; makeIconSquare = true; iconLocation = "center"; internalName = "bindButton"; active = false; }; %buttonContainer.add(%modifierBtn); %buttonContainer.add(%combinerText); %buttonContainer.add(%bindButton); } else { %bindButton = new GuiIconButtonCtrl() { position = "300 -10"; extent = "98 45"; BitmapAsset = ""; profile = GuiRemapActionMapButtonProfile; sizeIconToButton = true; makeIconSquare = true; iconLocation = "center"; internalName = "bindButton"; active = false; }; %buttonContainer.add(%bindButton); } return %entry; } function OptionsMenuActionMapButton::onHighlighted(%this, %highlighted) { %container = %this.getParent(); %container-->actionName.profile = %highlighted ? MenuSubHeaderTextHighlighted : MenuSubHeaderText; OptionsMenuSettingsScroll.scrollToObject(%container); } function doKeyRemap( %optionEntry ) { %name = getField(%optionEntry.keymap,0); RemapDlg-->OptRemapText.text = "Re-bind \"" @ %name @ "\" to..." ; OptRemapInputCtrl.index = %optionEntry.remapIndex; $remapListDevice = %optionEntry.device; Canvas.pushDialog( RemapDlg ); }