├── .gitignore ├── Fonts ├── attribution.txt ├── johnny fever.otf └── typodermic-eula-03-2020.pdf ├── example.json ├── .gitattributes ├── Images ├── Logo.png ├── Logo_Wide.png ├── MCMRecorder.xcf └── JContainers_SSE15.png ├── McmRecorder.esp ├── Scripts ├── Source │ ├── McmRecorder_Compatibility.psc │ ├── McmRecorder_PlayerAlias.psc │ ├── McmRecorder_Logging.psc │ ├── McmRecorder_RecordingInfo.psc │ ├── McmRecorder_Config.psc │ ├── McmRecorder_VRIK.psc │ ├── McmRecorder_JDB.psc │ ├── McmRecorder_UI.psc │ ├── McmRecorder_KeyboardShortcuts.psc │ ├── McmRecorder_RecordingFiles.psc │ ├── McmRecorder.psc │ ├── McmRecorder_McmFields.psc │ ├── McmRecorder_Recorder.psc │ ├── SKI_ConfigManager.psc │ ├── McmRecorderMCM.psc │ ├── McmRecorder_Player.psc │ └── SKI_ConfigBase.psc ├── McmRecorder.pex ├── McmRecorderMCM.pex ├── McmRecorder_JDB.pex ├── McmRecorder_UI.pex ├── McmRecorder_VRIK.pex ├── SKI_ConfigBase.pex ├── McmRecorder_Config.pex ├── McmRecorder_Player.pex ├── SKI_ConfigManager.pex ├── McmRecorder_Logging.pex ├── McmRecorder_McmFields.pex ├── McmRecorder_Recorder.pex ├── McmRecorder_Shortcuts.pex ├── McmRecorder_PlayerAlias.pex ├── McmRecorder_Compatibility.pex ├── McmRecorder_RecordingFiles.pex ├── McmRecorder_RecordingInfo.pex └── McmRecorder_KeyboardShortcuts.pex ├── McmRecorder.SE.esp ├── meta.ini ├── McmRecorder.json ├── McmRecorder.LE.json ├── README.md ├── .vscode ├── launch.json └── tasks.json ├── McmRecorder.code-workspace ├── LICENSE ├── skyrimse.ppj └── TODO.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.zip 2 | *.pas -------------------------------------------------------------------------------- /Fonts/attribution.txt: -------------------------------------------------------------------------------- 1 | https://www.1001fonts.com/johnny-fever-font.html -------------------------------------------------------------------------------- /example.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "ModName": "No Kids", "click": "Enable" } 3 | ] -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Images/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Images/Logo.png -------------------------------------------------------------------------------- /McmRecorder.esp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/McmRecorder.esp -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_Compatibility.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_Compatibility 2 | -------------------------------------------------------------------------------- /McmRecorder.SE.esp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/McmRecorder.SE.esp -------------------------------------------------------------------------------- /Images/Logo_Wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Images/Logo_Wide.png -------------------------------------------------------------------------------- /Fonts/johnny fever.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Fonts/johnny fever.otf -------------------------------------------------------------------------------- /Images/MCMRecorder.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Images/MCMRecorder.xcf -------------------------------------------------------------------------------- /Scripts/McmRecorder.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder.pex -------------------------------------------------------------------------------- /Images/JContainers_SSE15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Images/JContainers_SSE15.png -------------------------------------------------------------------------------- /Scripts/McmRecorderMCM.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorderMCM.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_JDB.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_JDB.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_UI.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_UI.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_VRIK.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_VRIK.pex -------------------------------------------------------------------------------- /Scripts/SKI_ConfigBase.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/SKI_ConfigBase.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_Config.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_Config.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_Player.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_Player.pex -------------------------------------------------------------------------------- /Scripts/SKI_ConfigManager.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/SKI_ConfigManager.pex -------------------------------------------------------------------------------- /Fonts/typodermic-eula-03-2020.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Fonts/typodermic-eula-03-2020.pdf -------------------------------------------------------------------------------- /Scripts/McmRecorder_Logging.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_Logging.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_McmFields.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_McmFields.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_Recorder.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_Recorder.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_Shortcuts.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_Shortcuts.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_PlayerAlias.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_PlayerAlias.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_Compatibility.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_Compatibility.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_RecordingFiles.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_RecordingFiles.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_RecordingInfo.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_RecordingInfo.pex -------------------------------------------------------------------------------- /Scripts/McmRecorder_KeyboardShortcuts.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrowrpurr/McmRecorder/HEAD/Scripts/McmRecorder_KeyboardShortcuts.pex -------------------------------------------------------------------------------- /meta.ini: -------------------------------------------------------------------------------- 1 | [General] 2 | modid=0 3 | version= 4 | newestVersion= 5 | category=0 6 | installationFile= 7 | 8 | [installedFiles] 9 | size=0 10 | -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_PlayerAlias.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_PlayerAlias extends ReferenceAlias 2 | 3 | event OnPlayerLoadGame() 4 | (GetOwningQuest() as McmRecorder).SaveGameLoaded() 5 | endEvent 6 | -------------------------------------------------------------------------------- /McmRecorder.json: -------------------------------------------------------------------------------- 1 | { 2 | "notifications": { 3 | "console": "true", 4 | "upper-left": "true", 5 | "popup": "true" 6 | }, 7 | "defaults": { 8 | "mcmLoad": 5.0 9 | } 10 | } -------------------------------------------------------------------------------- /McmRecorder.LE.json: -------------------------------------------------------------------------------- 1 | { 2 | "notifications": { 3 | "console": "true", 4 | "upper-left": "true", 5 | "popup": "true" 6 | }, 7 | "defaults": { 8 | "mcmLoad": 5.0 9 | } 10 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mcm Recorder 2 | 3 | Skyrim Mod Configuration Menu Recorder (for recording and playing back game configs) 4 | 5 | Note: the `SKI_ConfigBase` and `SKI_ConfigManager` scripts are NOT OWNED BY ME. 6 | 7 | They are owned by [schlangster](https://github.com/schlangster/skyui) and their inclusion in this project does NOT mean that they are MIT licensed. -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // created by vscode papyrus-lang papyrus.skyrimSpecialEdition.generateProject 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "Skyrim SE", 7 | "type": "papyrus", 8 | "request": "attach", 9 | "game": "skyrimSpecialEdition" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /McmRecorder.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | // created by vscode papyrus-lang papyrus.skyrimSpecialEdition.generateProject 3 | "folders": [ 4 | { 5 | "path": "." 6 | } 7 | ], 8 | "settings": { 9 | "papyrus.skyrimSpecialEdition.enabled": true, 10 | "papyrus.fallout4.enabled": false, 11 | "papyrus.skyrim.enabled": false 12 | } 13 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "pyro", 6 | "projectFile": "skyrimse.ppj", 7 | "gamePath": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Skyrim Special Edition\\", 8 | "problemMatcher": [ 9 | "$PapyrusCompiler" 10 | ], 11 | "label": "pyro: Compile Project (skyrimse.ppj)", 12 | "group": { 13 | "kind": "build", 14 | "isDefault": true 15 | } 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_Logging.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_Logging hidden 2 | 3 | function Log(string text) global 4 | Debug.Trace("[MCM Recorder] " + text) 5 | endFunction 6 | 7 | function LogContainer(string text, int jcontainer) global 8 | Log(text + ":\n" + ToJson(jcontainer)) 9 | endFunction 10 | 11 | string function ToJson(int jcontainer) global 12 | string filepath = McmRecorder_RecordingFiles.PathToRecordings() + "/" + ".temp" + "/temp.json" 13 | JValue.writeToFile(jcontainer, filepath) 14 | return MiscUtil.ReadFromFile(filepath) 15 | endFunction 16 | 17 | function ConsoleOut(string text) global 18 | MiscUtil.PrintConsole(text) 19 | endFunction 20 | 21 | function DumpAll(string filename = "Data/McmRecorder/.debug/McmRecorderDataDump.json") global 22 | JValue.writeToFile(JDB.solveObj(".mcmRecorder"), filename) 23 | ConsoleOut("[DEBUG] Written to " + filename) 24 | endFunction 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Mrowr Purr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_RecordingInfo.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_RecordingInfo hidden 2 | 3 | int function Get(string recordingName) global 4 | return McmRecorder_RecordingFiles.GetRecordingInfo(recordingName) 5 | endFunction 6 | 7 | ; TODO remove the recordingName parameter 8 | function Save(string recordingName, int recordingInfo) global 9 | McmRecorder_RecordingFiles.SaveRecordingInfoFile(recordingName, recordingInfo) 10 | endFunction 11 | 12 | bool function IsAutorun(int recordingInfo) global 13 | return JMap.getStr(recordingInfo, "autorun") == "true" 14 | endFunction 15 | 16 | bool function IsVrGesture(int recordingInfo) global 17 | return JMap.getStr(recordingInfo, "gesture") == "true" 18 | endFunction 19 | 20 | bool function SetIsVrGesture(int recordingInfo, bool enabled = true) global 21 | if enabled 22 | JMap.setStr(recordingInfo, "gesture", "true") 23 | else 24 | JMap.removeKey(recordingInfo, "gesture") 25 | endIf 26 | Save(GetName(recordingInfo), recordingInfo) 27 | endFunction 28 | 29 | string function GetName(int recordingInfo) global 30 | return JMap.getStr(recordingInfo, "name") 31 | endFunction 32 | 33 | ; TODO support replacements like $STEP_COUNT$ or something. 34 | string function GetWelcomeMessage(int recordingInfo) global 35 | return JMap.getStr(recordingInfo, "welcome") 36 | endFunction 37 | 38 | string function GetCompleteMessage(int recordingInfo) global 39 | return JMap.getStr(recordingInfo, "complete") 40 | endFunction 41 | -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_Config.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_Config hidden 2 | {Responsible for checking MCM Recorder configuration settings 3 | 4 | Settings are defined in Data\McmRecorder.json} 5 | 6 | function ReloadConfig() global 7 | ; TODO 8 | endFunction 9 | 10 | bool function IsSkyrimVR() global 11 | return Game.GetModByName("SkyrimVR.esm") != 255 12 | endFunction 13 | 14 | bool function IsSkyrimLE() global 15 | return MiscUtil.FileExists("TESV.exe") 16 | endFunction 17 | 18 | bool function ShowNotifications() global 19 | return JDB.solveStr(McmRecorder_JDB.JdbPath_Config_ShowNotifications()) != "false" 20 | endFunction 21 | 22 | bool function SetShowNotifications(bool value) global 23 | if value 24 | JDB.solveStrSetter(McmRecorder_JDB.JdbPath_Config_ShowNotifications(), "true", createMissingKeys = true) 25 | else 26 | JDB.solveStrSetter(McmRecorder_JDB.JdbPath_Config_ShowNotifications(), "false", createMissingKeys = true) 27 | endIf 28 | endFunction 29 | 30 | bool function ShowMessageBoxes() global 31 | return JDB.solveStr(McmRecorder_JDB.JdbPath_Config_ShowMessageBoxes()) != "false" 32 | endFunction 33 | 34 | bool function SetShowMessageBoxes(bool value) global 35 | if value 36 | JDB.solveStrSetter(McmRecorder_JDB.JdbPath_Config_ShowMessageBoxes(), "true", createMissingKeys = true) 37 | else 38 | JDB.solveStrSetter(McmRecorder_JDB.JdbPath_Config_ShowMessageBoxes(), "false", createMissingKeys = true) 39 | endIf 40 | endFunction 41 | -------------------------------------------------------------------------------- /skyrimse.ppj: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | @ImportsFolder\SKSE 20 | @ImportsFolder\SkyUI_SDK 21 | @ImportsFolder\PapyrusUtil 22 | @ImportsFolder\JContainers 23 | @ImportsFolder\UIExtensions 24 | @ImportsFolder\VRIK 25 | C:\Program Files (x86)\Steam\steamapps\common\Skyrim Special Edition\Data\Scripts\Source 26 | 27 | 28 | ./Scripts/Source 29 | 30 | 31 | 32 | @ModName.esp 33 | *.pex 34 | *.psc 35 | 36 | 37 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | - [ ] Show when dependencies are not installed 2 | - [ ] PW's not working mods 3 | - [ ] Pause while running 4 | - [ ] Add better VR support by customizing the MCM menu when you're in VR! 5 | - [ ] Set verbosity (whether messages show up ) 6 | - [ ] Cancel while running 7 | - [ ] Choose whether to run a step or not! 8 | - [ ] Choose a BATCH of steps to run! 9 | - [ ] Look into supporting MCMs which were built using MCM Helper (reportedly these don't work) 10 | 11 | - [x] Trigger automatically via SKSE 12 | - [x] Autorun after RaceMenu close 13 | - [x] Welcome Message 14 | - [x] Complete Message 15 | - [x] Extract all global functions to a private API (keep them there for now) 16 | - [x] Track the Nth cuz that's cool 17 | - [x] Trigger automatically via autorun: true 18 | - [x] BUG When you record but there are no recordings it still says 'Choose a recording' - also DISABLE this text 19 | - [x] Show console messages for recording actions (and play actions!) 20 | - [x] Branch logic on VR and use PapyrusUtil for SSE <---- 21 | - [x] Keyboard shortcuts - include in the Recording.json 22 | - [x] VR guestures - include in the Recording.json 23 | 24 | 25 | - Track the Nth index for selectors which have duplicates on the page 26 | - Can use SKSE mod events to run Recordings, Steps, or Actions 27 | - Can put recording name(s) into a config file which will AUTOMATICALLY run the recordings! 28 | - Can detect is a field is on the page easily (and get it) 29 | 30 | TODO: MCM translation files 31 | 32 | BUG: in VR when you click 'begin recording' if says 'Choose Recording To Play' even if there are none 33 | -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_VRIK.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_VRIK 2 | 3 | string function GetModEventNameForRecording(string recordingName) global 4 | return "McmRecorder_VrGesture_PlayRecording:" + recordingName 5 | endFunction 6 | 7 | string function GetRecordingNameFromModEvent(string modEventName) global 8 | return StringUtil.Substring(modEventName, StringUtil.GetLength("McmRecorder_VrGesture_PlayRecording:")) 9 | endFunction 10 | 11 | bool function IsVrikInstalled() global 12 | return Game.GetModByName("vrik.esp") != 255 13 | endFunction 14 | 15 | function RegisterVrikGesturesForRecordings() global 16 | string[] recordingNames = McmRecorder_RecordingFiles.GetRecordingNames() 17 | int i = 0 18 | while i < recordingNames.Length 19 | string recordingName = recordingNames[i] 20 | int recordingInfo = McmRecorder_RecordingInfo.Get(recordingName) 21 | bool isGesture = McmRecorder_RecordingInfo.IsVrGesture(recordingInfo) 22 | if isGesture 23 | RegisterVrikGestureForRecording(recordingName) 24 | endIf 25 | i += 1 26 | endWhile 27 | endFunction 28 | 29 | function RegisterVrikGestureForRecording(string recordingName) global 30 | VRIK.VrikAddGestureAction(GetModEventNameForRecording(recordingName), "MCM Recording: " + recordingName) 31 | ListenForVriKGestureForRecording(recordingName) 32 | endFunction 33 | 34 | function UnregisterVrikGestureForRecording(string recordingName) global 35 | ; TODO - Looks like there's no way to remove a VRIK gesture action????? 36 | StopListeningForVriKGestureForRecording(recordingName) 37 | endFunction 38 | 39 | function ListenForVriKGesturesForRecordings() global 40 | string[] recordingNames = McmRecorder_RecordingFiles.GetRecordingNames() 41 | int i = 0 42 | while i < recordingNames.Length 43 | string recordingName = recordingNames[i] 44 | int recordingInfo = McmRecorder_RecordingInfo.Get(recordingName) 45 | bool isGesture = McmRecorder_RecordingInfo.IsVrGesture(recordingInfo) 46 | if isGesture 47 | ListenForVriKGestureForRecording(recordingName) 48 | endIf 49 | i += 1 50 | endWhile 51 | endFunction 52 | 53 | function ListenForVriKGestureForRecording(string recordingName) global 54 | McmRecorder.GetMcmRecorderInstance().ListenForVriKGestureForRecording(recordingName) 55 | endFunction 56 | 57 | function StopListeningForVriKGestureForRecording(string recordingName) global 58 | McmRecorder.GetMcmRecorderInstance().StopListeningForVriKGestureForRecording(recordingName) 59 | endFunction 60 | -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_JDB.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_JDB hidden 2 | {Get JDB paths for JDB data stored by MCM Recorder} 3 | 4 | string function JdbPath_CurrentRecordingName() global 5 | return ".mcmRecorder.currentRecording.recordingName" 6 | endFunction 7 | 8 | string function JdbPath_CurrentRecordingModName() global 9 | return ".mcmRecorder.currentRecording.currentModName" 10 | endFunction 11 | 12 | string function JdbPath_CurrentRecordingRecordingStep() global 13 | return ".mcmRecorder.currentRecording.currentModStep" 14 | endFunction 15 | 16 | string function JdbPath_McmOptions() global 17 | return ".mcmRecorder.mcmOptions" 18 | endFunction 19 | 20 | string function JdbPath_McmOptions_MarkForReset() global 21 | return ".mcmRecorder.McmOptionsShouldBeReset" 22 | endFunction 23 | 24 | string function JdbPath_IsPlayingRecording() global 25 | return ".mcmRecorder.isPlayingRecording" 26 | endFunction 27 | 28 | string function JdbPath_PlayingRecordingName() global 29 | return ".mcmRecorder.playingRecording.name" 30 | endFunction 31 | 32 | string function JdbPath_PlayingRecordingSteps() global 33 | return ".mcmRecorder.playingRecording.steps" 34 | endFunction 35 | 36 | string function JdbPath_PlayingStepFilename() global 37 | return ".mcmRecorder.playingRecording.stepFilename" 38 | endFunction 39 | 40 | string function JdbPath_PlayingStepIndex() global 41 | return ".mcmRecorder.playingRecording.stepIndex" 42 | endFunction 43 | 44 | string function JdbPath_PlayingRecordingModName() global 45 | return ".mcmRecorder.playingRecording.modName" 46 | endFunction 47 | 48 | string function JdbPath_PlayingRecordingModPageName() global 49 | return ".mcmRecorder.playingRecording.pageName" 50 | endFunction 51 | 52 | string function JdbPath_PlayingRecordingModsPlayed() global 53 | return ".mcmRecorder.playingRecording.modsPlayed" 54 | endFunction 55 | 56 | string function JdbPath_CurrentlySkippingModName() global 57 | return ".mcmRecorder.playingRecording.modCurrentlySkipping" 58 | endFunction 59 | 60 | string function JdbPath_AutorunHistory() global 61 | return ".mcmRecorder.autorunHistory" 62 | endFunction 63 | 64 | string function JdbPath_MCM_KeyboardShortcuts_ShortcutInfos() global 65 | return ".mcmRecorder.mcm.keyboardShortcuts.shortcutInfos" 66 | endFunction 67 | 68 | string function JdbPath_MCM_KeyboardShortcuts_ShortcutOptions() global 69 | return ".mcmRecorder.mcm.keyboardShortcuts.shortcutOptions" 70 | endFunction 71 | 72 | string function JdbPath_Config_ShowNotifications() global 73 | return ".mcmRecorder.config.notifications" 74 | endFunction 75 | 76 | string function JdbPath_Config_ShowMessageBoxes() global 77 | return ".mcmRecorder.config.messageboxes" 78 | endFunction 79 | 80 | string function JdbPathPart(string part) global 81 | string[] parts = StringUtil.Split(part, ".") 82 | string sanitized = "" 83 | int i = 0 84 | while i < parts.Length 85 | if i == 0 86 | sanitized += parts[i] 87 | else 88 | sanitized += "_" + parts[i] 89 | endIf 90 | i += 1 91 | endWhile 92 | return sanitized 93 | endFunction 94 | -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_UI.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_UI 2 | 3 | function Notification(string text) global 4 | if McmRecorder_Config.ShowNotifications() 5 | Debug.Notification("[McmRecorder] " + text) 6 | endIf 7 | endFunction 8 | 9 | function MessageBox(string text) global 10 | if McmRecorder_Config.ShowMessageBoxes() 11 | Debug.MessageBox(text) 12 | endIf 13 | endFunction 14 | 15 | string function Options_Continue() global 16 | return "Continue" 17 | endFunction 18 | 19 | string function Options_TryAgain() global 20 | return "Try again" 21 | endFunction 22 | 23 | string function Options_SkipThisMod() global 24 | return "Skip this mod" 25 | endFunction 26 | 27 | function FinishedMessage(string recordingName) global 28 | int recording = McmRecorder_RecordingInfo.Get(recordingName) 29 | string finishedMessage = McmRecorder_RecordingInfo.GetCompleteMessage(recording) 30 | if ! finishedMessage 31 | finishedMessage = recordingName + " has finished playing." 32 | endIf 33 | MessageBox(finishedMessage) 34 | endFunction 35 | 36 | function WelcomeMessage(string recordingName) global 37 | int recording = McmRecorder_RecordingInfo.Get(recordingName) 38 | string welcomeMessage = McmRecorder_RecordingInfo.GetWelcomeMessage(recording) 39 | if welcomeMessage 40 | MessageBox(welcomeMessage) 41 | endIf 42 | endFunction 43 | 44 | function OpenSystemMenuDuringRecordingMessage(string recordingName) global 45 | int stepCount = JMap.count(McmRecorder_Player.GetCurrentlyPlayingSteps()) 46 | int stepIndex = McmRecorder_Player.GetCurrentlyPlayingStepIndex() 47 | string stepName = McmRecorder_Player.GetCurrentlyPlayingStepFilename() 48 | string text = recordingName + " setup currently in progress." 49 | text += "\n\nCurrently playing step " + stepName + " ("+ (stepIndex + 1) + "/" + stepCount + ")" 50 | text += "\n\nPlease exit the menu to allow this recording to continue." 51 | MessageBox(text) 52 | endFunction 53 | 54 | string function GetUserResponseForNotFoundSelector(string modName, string pageName, string selector) global 55 | string description = "Could not find MCM option:\n\nMod name: " + modName 56 | if pageName 57 | description += "\nPage name: " + pageName 58 | endIf 59 | description += "\nField name: " + selector 60 | description += "\n\nWhich of the following would you like to do?" 61 | description += "\n- Continue this mod and move on to the next MCM field" 62 | description += "\n- Try finding this MCM field again" 63 | description += "\n- Skip this mod and move on to configuring the next one" 64 | McmRecorder recorder = McmRecorder.GetMcmRecorderInstance() 65 | recorder.McmRecorder_MessageText.SetName(description) 66 | int response = recorder.McmRecorder_Message_SelectorNotFound.Show() 67 | if response == 0 68 | return Options_Continue() 69 | elseIf response == 1 70 | return Options_TryAgain() 71 | elseIf response == 2 72 | return Options_SkipThisMod() 73 | endIf 74 | endFunction 75 | 76 | string function GetUserResponseForNotFoundMod(string modName) global 77 | string description = "Could not find MCM\n\nMod name: " + modName 78 | description += "\n\nWhich of the following would you like to do?" 79 | description += "\n- Try waiting longer for this mod to become available" 80 | description += "\n- Skip this mod and move on to configuring the next one" 81 | McmRecorder recorder = McmRecorder.GetMcmRecorderInstance() 82 | recorder.McmRecorder_MessageText.SetName(description) 83 | int response = recorder.McmRecorder_Message_ModNotFound.Show() 84 | if response == 0 85 | return Options_TryAgain() 86 | elseIf response == 1 87 | return Options_SkipThisMod() 88 | endIf 89 | endFunction -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_KeyboardShortcuts.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_KeyboardShortcuts 2 | {Responsible for MCM Recorder keyboard shortcuts and VR VRIK gestures} 3 | 4 | function AddRecording(string recordingName) global 5 | int recordingInfo = McmRecorder_RecordingInfo.Get(recordingName) 6 | int shortcut = JMap.getObj(recordingInfo, "shortcut") 7 | if ! shortcut 8 | shortcut = JMap.object() 9 | JMap.setObj(recordingInfo, "shortcut", shortcut) 10 | endIf 11 | JMap.setInt(shortcut, "key", -1) 12 | McmRecorder_RecordingInfo.Save(recordingName, recordingInfo) 13 | endFunction 14 | 15 | int function GetShortcutsInfos() global 16 | string[] recordingNames = McmRecorder_RecordingFiles.GetRecordingNames() 17 | int shortcutInfos = JArray.object() 18 | JDB.solveObjSetter(McmRecorder_JDB.JdbPath_MCM_KeyboardShortcuts_ShortcutInfos(), shortcutInfos, createMissingKeys = true) 19 | int i = 0 20 | while i < recordingNames.Length 21 | string recordingName = recordingNames[i] 22 | int recordingInfo = McmRecorder_RecordingInfo.Get(recordingName) 23 | int shortcut = JMap.getObj(recordingInfo, "shortcut") 24 | if shortcut 25 | int shortcutInfo = JMap.object() 26 | JMap.setStr(shortcutInfo, "recordingName", recordingName) 27 | JMap.setObj(shortcutInfo, "shortcut", shortcut) 28 | JArray.addObj(shortcutInfos, shortcutInfo) 29 | endIf 30 | i += 1 31 | endWhile 32 | return shortcutInfos 33 | endFunction 34 | 35 | int[] function GetAllKeyboardShortcutKeys() global 36 | int[] keycodes 37 | string[] recordingNames = McmRecorder_RecordingFiles.GetRecordingNames() 38 | int shortcutInfos = JArray.object() 39 | int i = 0 40 | while i < recordingNames.Length 41 | string recordingName = recordingNames[i] 42 | int recordingInfo = McmRecorder_RecordingInfo.Get(recordingName) 43 | int shortcut = JMap.getObj(recordingInfo, "shortcut") 44 | if shortcut 45 | int keycode = JMap.getInt(shortcut, "key") 46 | if keycodes.Find(keycode) == -1 47 | keycodes = Utility.ResizeIntArray(keycodes, keycodes.Length + 1) 48 | keycodes[keycodes.Length - 1] = keycode 49 | endIf 50 | endIf 51 | i += 1 52 | endWhile 53 | return keycodes 54 | endFunction 55 | 56 | function RunKeyboardShortcutIfAny(int pressedKey) global 57 | bool ctrlPressed = Input.IsKeyPressed(29) || Input.IsKeyPressed(157) 58 | bool altPressed = Input.IsKeyPressed(56) || Input.IsKeyPressed(184) 59 | bool shiftPressed = Input.IsKeyPressed(42) || Input.IsKeyPressed(54) 60 | bool found 61 | string[] recordingNames = McmRecorder_RecordingFiles.GetRecordingNames() 62 | int shortcutInfos = JArray.object() 63 | int i = 0 64 | while i < recordingNames.Length && (!found) 65 | string recordingName = recordingNames[i] 66 | int recordingInfo = McmRecorder_RecordingInfo.Get(recordingName) 67 | int shortcut = JMap.getObj(recordingInfo, "shortcut") 68 | if shortcut 69 | int keycode = JMap.getInt(shortcut, "key") 70 | bool ctrl = JMap.getStr(shortcut, "ctrl") == "true" 71 | bool alt = JMap.getStr(shortcut, "alt") == "true" 72 | bool shift = JMap.getStr(shortcut, "shift") == "true" 73 | if pressedKey == keycode && \ 74 | ctrlPressed == ctrl && \ 75 | altPressed == alt && \ 76 | shiftPressed == shift 77 | McmRecorder_Logging.ConsoleOut("[Keyboard Shortcut] " + recordingName + " Key:" + keycode + " Ctrl:" + ctrl + " Alt:" + alt + " Shift:" + shift) 78 | bool notifications = McmRecorder_Config.ShowNotifications() 79 | bool messageboxes = McmRecorder_Config.ShowMessageBoxes() 80 | McmRecorder_Config.SetShowNotifications(false) 81 | McmRecorder_Config.SetShowMessageBoxes(false) 82 | McmRecorder_Player.PlayRecording(recordingName) 83 | McmRecorder_Config.SetShowNotifications(notifications) 84 | McmRecorder_Config.SetShowMessageBoxes(messageboxes) 85 | found = true 86 | endIf 87 | endIf 88 | i += 1 89 | endWhile 90 | endFunction 91 | -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_RecordingFiles.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_RecordingFiles hidden 2 | {Responsible for managing the recording files found in Data\McmRecorder\} 3 | 4 | function SaveCurrentRecording(string recordingName, string modName) global 5 | JValue.writeToFile(McmRecorder_Recorder.GetCurrentRecordingSteps(), GetFileNameForRecordingAction(recordingName, modName)) 6 | endFunction 7 | 8 | function SaveRecordingInfoFile(string recordingName, int metaInfo) global 9 | JValue.writeToFile(metaInfo, PathToRecordingFolder(recordingName) + ".json") 10 | endFunction 11 | 12 | int function GetAllStepsForRecording(string recordingName) global 13 | return JValue.readFromDirectory(PathToRecordingFolder(recordingName)) 14 | endFunction 15 | 16 | string[] function GetRecordingNames() global 17 | if ! MiscUtil.FileExists(PathToRecordings()) 18 | string[] ret 19 | return ret 20 | endIf 21 | 22 | string[] fileNames 23 | 24 | if McmRecorder_Config.IsSkyrimVR() || McmRecorder_Config.IsSkyrimLE() 25 | fileNames = JMap.allKeysPArray(JValue.readFromDirectory(PathToRecordings())) 26 | else 27 | fileNames = MiscUtil.FilesInFolder(PathToRecordings()) 28 | endIf 29 | 30 | int i = 0 31 | while i < fileNames.Length 32 | fileNames[i] = StringUtil.Substring(fileNames[i], 0, StringUtil.Find(fileNames[i], ".json")) 33 | i += 1 34 | endWhile 35 | return fileNames 36 | endFunction 37 | 38 | string[] function GetRecordingStepFilenames(string recordingName) global 39 | if McmRecorder_Config.IsSkyrimVR() || McmRecorder_Config.IsSkyrimLE() 40 | return JMap.allKeysPArray(JValue.readFromDirectory(PathToRecordingFolder(recordingName))) 41 | else 42 | return MiscUtil.FilesInFolder(PathToRecordingFolder(recordingName)) 43 | endIf 44 | endFunction 45 | 46 | int function GetRecordingInfo(string recordingName) global 47 | return JValue.ReadFromFile(PathToRecordingFolder(recordingName) + ".json") 48 | endFunction 49 | 50 | int function GetRecordingStep(string recordingName, string stepName) global 51 | return JValue.readFromFile(PathToStepFile(recordingName, stepName)) 52 | endFunction 53 | 54 | string function GetRecordingDescription(string recordingName) global 55 | int info = GetRecordingInfo(recordingName) 56 | string[] stepNames = GetRecordingStepFilenames(recordingName) 57 | 58 | string recordingDescription = recordingName 59 | if JMap.getStr(info, "version") 60 | recordingDescription += " (" + JMap.getStr(info, "version") + ")" 61 | endIf 62 | if JMap.getStr(info, "author") 63 | recordingDescription += "\n~ by " + JMap.getStr(info, "author") + " ~" 64 | endIf 65 | recordingDescription += "\nSteps: " + stepNames.Length 66 | 67 | return recordingDescription 68 | endFunction 69 | 70 | string function PathToRecordings() global 71 | return "Data/McmRecorder" 72 | endFunction 73 | 74 | string function PathToRecordingFolder(string recordingName) global 75 | return PathToRecordings() + "/" + FileSystemPathPart(recordingName) 76 | endFunction 77 | 78 | string function PathToStepFile(string recordingName, string stepName) global 79 | if StringUtil.Find(stepName, ".json") 80 | return PathToRecordings() + "/" + FileSystemPathPart(recordingName) + "/" + stepName 81 | else 82 | return PathToRecordings() + "/" + FileSystemPathPart(recordingName) + "/" + stepName + ".json" 83 | endIf 84 | endFunction 85 | 86 | string function FileSystemPathPart(string part) global 87 | string[] parts = StringUtil.Split(part, "/") 88 | string sanitized = "" 89 | int i = 0 90 | while i < parts.Length 91 | if i == 0 92 | sanitized += parts[i] 93 | else 94 | sanitized += "_" + parts[i] 95 | endIf 96 | i += 1 97 | endWhile 98 | parts = StringUtil.Split(sanitized, "\\") 99 | sanitized = "" 100 | i = 0 101 | while i < parts.Length 102 | if i == 0 103 | sanitized += parts[i] 104 | else 105 | sanitized += "_" + parts[i] 106 | endIf 107 | i += 1 108 | endWhile 109 | return sanitized 110 | endFunction 111 | 112 | string function GetFileNameForRecordingAction(string recordingName, string modName) global 113 | string recordingFolder = PathToRecordingFolder(recordingName) 114 | int recordingStepNumber 115 | if McmRecorder_Config.IsSkyrimVR() || McmRecorder_Config.IsSkyrimLE() 116 | recordingStepNumber = JMap.allKeysPArray(JValue.readFromDirectory(recordingFolder)).Length 117 | else 118 | recordingStepNumber = MiscUtil.FilesInFolder(recordingFolder).Length 119 | endIf 120 | if modName != McmRecorder_Recorder.GetCurrentRecordingModName() 121 | recordingStepNumber += 1 122 | McmRecorder_Recorder.SetCurrentRecordingModName(modName) 123 | endIf 124 | string filenameNumericPrefix = recordingStepNumber 125 | while StringUtil.GetLength(filenameNumericPrefix) < 4 126 | filenameNumericPrefix = "0" + filenameNumericPrefix 127 | endWhile 128 | return PathToRecordingFolder(recordingName) + "/" + filenameNumericPrefix + "_" + FileSystemPathPart(modName) + ".json" 129 | endFunction 130 | -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder extends Quest hidden 2 | 3 | ; TODO separate out an McmRecorderPrivate so this can become just a public API intended for integration 4 | 5 | ; **PRIVATE** 6 | ; 7 | ; Primary instance of the SKI_ConfigManager from the SKI_ConfigManagerInstance quest 8 | SKI_ConfigManager property skiConfigManager auto 9 | 10 | ; **PRIVATE** 11 | ; 12 | ; Message used to ask the user if they want to run a full recording, view steps, or add to the recording. 13 | Message property McmRecorder_Message_RunRecordingOrViewSteps auto 14 | 15 | ; **PRIVATE** 16 | ; 17 | ; Message used to ask the user if they want to skip a field that wasn't found, retry it, or skip the entire mod. 18 | Message property McmRecorder_Message_SelectorNotFound auto 19 | 20 | ; **PRIVATE** 21 | ; 22 | ; Message used to ask the user if they want to skip a mod or wait for it to show up when a mod cannot be found 23 | Message property McmRecorder_Message_ModNotFound auto 24 | 25 | ; **PRIVATE** 26 | ; 27 | ; Form used to set dynamic text in all of the Message dialogs used by MCM Recorder. 28 | Form property McmRecorder_MessageText auto 29 | 30 | ; **PRIVATE** 31 | ; 32 | ; Stores the installed version of MCM Recorder. Used for performing version upgrades. 33 | string property CurrentlyInstalledVersion auto 34 | 35 | ; Returns the installed version of MCM Recorder 36 | string function GetVersion() global 37 | return "1.0.7" 38 | endFunction 39 | 40 | event OnInit() 41 | CurrentlyInstalledVersion = GetVersion() 42 | skiConfigManager = Quest.GetQuest("SKI_ConfigManagerInstance") as SKI_ConfigManager 43 | StartListenForKeyboardShortcuts() 44 | ListenForRaceMenuClose() 45 | McmRecorder_VRIK.RegisterVrikGesturesForRecordings() 46 | ListenForRecordingSkseModEvents() 47 | endEvent 48 | 49 | event SaveGameLoaded() 50 | RegisterForSingleUpdate(5) 51 | StartListenForKeyboardShortcuts() 52 | McmRecorder_VRIK.ListenForVriKGesturesForRecordings() 53 | ListenForRecordingSkseModEvents() 54 | endEvent 55 | 56 | function StartListenForKeyboardShortcuts() 57 | int[] keycodes = McmRecorder_KeyboardShortcuts.GetAllKeyboardShortcutKeys() 58 | int i = 0 59 | while i < keycodes.Length 60 | RegisterForKey(keycodes[i]) 61 | i += 1 62 | endWhile 63 | endFunction 64 | 65 | function StopListeningForKeyboardShortcuts() 66 | int[] keycodes = McmRecorder_KeyboardShortcuts.GetAllKeyboardShortcutKeys() 67 | int i = 0 68 | while i < keycodes.Length 69 | UnregisterForKey(keycodes[i]) 70 | i += 1 71 | endWhile 72 | endFunction 73 | 74 | event OnKeyDown(int keycode) 75 | if ! McmRecorder_Player.IsPlayingRecording() 76 | McmRecorder_KeyboardShortcuts.RunKeyboardShortcutIfAny(keycode) 77 | endIf 78 | endEvent 79 | 80 | ; **DO NOT USE THIS** 81 | ; 82 | ; Returns a **private** script containing the script fields and properties used by MCM Recorder 83 | McmRecorder function GetMcmRecorderInstance() global 84 | return Quest.GetQuest("McmRecorder") as McmRecorder 85 | endFunction 86 | 87 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 88 | ; Get SkyUI MCM Script Instance 89 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 90 | 91 | SKI_ConfigBase function GetMcmInstance(string modName) global 92 | McmRecorder recorder = McmRecorder.GetMcmRecorderInstance() 93 | int index = recorder.skiConfigManager.ModNames.Find(modName) 94 | return recorder.skiConfigManager.ModConfigs[index] 95 | endFunction 96 | 97 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 98 | ; Autorun 99 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 100 | 101 | function ListenForRaceMenuClose() 102 | RegisterForMenu("RaceSex Menu") 103 | endFunction 104 | 105 | event OnMenuClose(string menuName) 106 | if menuName == "RaceSex Menu" 107 | UnregisterForMenu("RaceSex Menu") 108 | AutorunRecordings() 109 | endIf 110 | endEvent 111 | 112 | function AutorunRecordings() 113 | string[] recordingNames = McmRecorder_RecordingFiles.GetRecordingNames() 114 | int i = 0 115 | while i < recordingNames.Length 116 | string recordingName = recordingNames[i] 117 | int recordingInfo = McmRecorder_RecordingInfo.Get(recordingName) 118 | if McmRecorder_RecordingInfo.IsAutorun(recordingInfo) && (! McmRecorder_Player.HasBeenAutorun(recordingName)) 119 | McmRecorder_Player.MarkHasBeenAutorun(recordingName) 120 | McmRecorder_Logging.Log("Autorun Recording " + recordingName) 121 | McmRecorder_Player.PlayRecording(recordingName, mcmLoadWaitTime = 30.0) 122 | endIf 123 | i += 1 124 | endWhile 125 | endFunction 126 | 127 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 128 | ; Pause and Cancel Running Recording 129 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 130 | 131 | function ListenForSystemMenuOpen() 132 | UnregisterForMenu("Journal Menu") 133 | RegisterForMenu("Journal Menu") ; Track when the menu opens so we can show a mesasge if a recording is playing 134 | endFunction 135 | 136 | function StopListeningForSystemMenuOpen() 137 | UnregisterForMenu("Journal Menu") 138 | endFunction 139 | 140 | event OnMenuOpen(string menuName) 141 | if menuName == "Journal Menu" 142 | if McmRecorder_Player.IsPlayingRecording() 143 | McmRecorder_UI.OpenSystemMenuDuringRecordingMessage(McmRecorder_Player.GetCurrentlyPlayingRecordingName()) 144 | endIf 145 | endIf 146 | endEvent 147 | 148 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 149 | ; VR Gestures 150 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 151 | 152 | function ListenForVriKGestureForRecording(string recordingName) 153 | string modEventName = McmRecorder_VRIK.GetModEventNameForRecording(recordingName) 154 | RegisterForModEvent(modEventName, "OnVrikGesture") 155 | endFunction 156 | 157 | function StopListeningForVriKGestureForRecording(string recordingName) 158 | string modEventName = McmRecorder_VRIK.GetModEventNameForRecording(recordingName) 159 | UnregisterForModEvent(modEventName) 160 | endFunction 161 | 162 | event OnVrikGesture(string eventName, string strArg, float fltArg, Form sender) 163 | string recordingName = McmRecorder_VRIK.GetRecordingNameFromModEvent(eventName) 164 | McmRecorder_Player.PlayRecording(recordingName, verbose = false) 165 | endEvent 166 | 167 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 168 | ; VR Gestures 169 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 170 | 171 | function ListenForRecordingSkseModEvents() 172 | RegisterForModEvent("McmRecorder_PlayRecording", "OnRecordingModEvent") 173 | endFunction 174 | 175 | event OnRecordingModEvent(string eventName, string recordingName, float fltArg, Form sender) 176 | McmRecorder_Player.PlayRecording(recordingName, verbose = false) 177 | endEvent 178 | -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_McmFields.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_McmFields hidden 2 | {Responsible for storage of and querying of individual fields on Mod Configuration Menu pages} 3 | 4 | ; TODO OptionsForModPage_ByState & GetConfigurationOptionByState ? O[tion IDs fine for stateful options? 5 | 6 | function TrackField(string modName, string pageName, string optionType, int optionId, string text, string strValue, float fltValue, string stateName, bool force = false) global 7 | if McmOptionsShouldBeReset() 8 | ResetMcmOptions() 9 | endIf 10 | 11 | if force || McmRecorder_Recorder.IsRecording() || McmRecorder_Player.IsPlayingRecording() 12 | int optionsOnModPageForType = OptionsForModPage_ByOptionType(modName, pageName, optionType) 13 | int option = JMap.object() 14 | JArray.addObj(optionsOnModPageForType, option) 15 | JIntMap.setObj(OptionsForModPage_ByOptionIds(modName, pageName), optionId, option) 16 | JMap.setInt(option, "id", optionId) 17 | JMap.setStr(option, "state", stateName) 18 | JMap.setStr(option, "type", optionType) 19 | JMap.setStr(option, "text", text) 20 | JMap.setStr(option, "strValue", strValue) 21 | JMap.setFlt(option, "fltValue", fltvalue) 22 | endIf 23 | endFunction 24 | 25 | function MarkMcmOptionsForReset() global 26 | JDB.solveIntSetter(McmRecorder_JDB.JdbPath_McmOptions_MarkForReset(), 1, createMissingKeys = true) 27 | endFunction 28 | 29 | function UnMarkMcmOptionsForReset() global 30 | JDB.solveIntSetter(McmRecorder_JDB.JdbPath_McmOptions_MarkForReset(), 0) 31 | endFunction 32 | 33 | bool function McmOptionsShouldBeReset() global 34 | return JDB.solveInt(McmRecorder_JDB.JdbPath_McmOptions_MarkForReset()) == 1 35 | endFunction 36 | 37 | function ResetMcmOptions() global 38 | UnMarkMcmOptionsForReset() 39 | JDB.solveObjSetter(McmRecorder_JDB.JdbPath_McmOptions(), JMap.object(), createMissingKeys = true) 40 | endFunction 41 | 42 | int function GetConfigurationOptionById(string modName, string pageName, int optionId) global 43 | return JIntMap.getObj(OptionsForModPage_ByOptionIds(modName, pageName), optionId) 44 | endFunction 45 | 46 | int function OptionsForModPage_ByOptionIds(string modName, string pageName) global 47 | return JMap.getObj(OptionsForModPage(modName, pageName), "byId") 48 | endFunction 49 | 50 | int function OptionsForModPage_ByOptionTypes(string modName, string pageName) global 51 | return JMap.getObj(OptionsForModPage(modName, pageName), "byType") 52 | endFunction 53 | 54 | int function OptionsForModPage_ByOptionType(string modName, string pageName, string optionType) global 55 | int byType = OptionsForModPage_ByOptionTypes(modName, pageName) 56 | int typeMap = JMap.getObj(byType, optionType) 57 | if ! typeMap 58 | typeMap = JArray.object() 59 | JMap.setObj(byType, optionType, typeMap) 60 | endIf 61 | return typeMap 62 | endFunction 63 | 64 | int function AllMcmOptions() global 65 | int options = JDB.solveObj(McmRecorder_JDB.JdbPath_McmOptions()) 66 | if ! options 67 | options = JMap.object() 68 | JDB.solveObjSetter(McmRecorder_JDB.JdbPath_McmOptions(), options, createMissingKeys = true) 69 | endIf 70 | return options 71 | endFunction 72 | 73 | int function OptionsForMod(string modName) global 74 | int allOptions = AllMcmOptions() 75 | int options = JMap.getObj(allOptions, modName) 76 | if ! options 77 | options = JMap.object() 78 | JMap.setObj(allOptions, modName, options) 79 | endIf 80 | return options 81 | endFunction 82 | 83 | int function OptionsForModPage(string modName, string pageName) global 84 | int modOptions = OptionsForMod(modName) 85 | if ! pageName 86 | pageName = "SKYUI_DEFAULT_PAGE" 87 | endIf 88 | int options = JMap.getObj(modOptions, pageName) 89 | if ! options 90 | options = JMap.object() 91 | JMap.setObj(modOptions, pageName, options) 92 | JMap.setObj(options, "byId", JIntMap.object()) 93 | JMap.setObj(options, "byType", JMap.object()) 94 | endIf 95 | return options 96 | endFunction 97 | 98 | ; Given the internal numerical identifier for field types used by SkyUI in mod configuration menus, 99 | ; return a text representation of the field type, e.g. "input" rather than 8 100 | string function GetOptionTypeName(int skyUiMcmOptiontype) global 101 | if skyUiMcmOptiontype == 0 102 | return "empty" 103 | elseIf skyUiMcmOptiontype == 1 104 | return "header" 105 | elseIf skyUiMcmOptiontype == 2 106 | return "text" 107 | elseIf skyUiMcmOptiontype == 3 108 | return "toggle" 109 | elseIf skyUiMcmOptiontype == 4 110 | return "slider" 111 | elseIf skyUiMcmOptiontype == 5 112 | return "menu" 113 | elseIf skyUiMcmOptiontype == 6 114 | return "color" 115 | elseIf skyUiMcmOptiontype == 7 116 | return "keymap" 117 | elseIf skyUiMcmOptiontype == 8 118 | return "input" 119 | else 120 | return "unknown" 121 | endIf 122 | endFunction 123 | 124 | int function GetSelectorIndex(string modName, string pageName, int optionId) global 125 | int option = GetConfigurationOptionById(modName, pageName, optionId) 126 | 127 | if ! option 128 | McmRecorder_Logging.Log("Did not find option with ID " + optionId) 129 | return -2 130 | endIf 131 | 132 | string selector = GetOptionSelector(option) 133 | string stateName = JMap.getStr(option, "state") 134 | 135 | int optionsToSearch = OptionsForModPage_ByOptionType(modName, pageName, JMap.getStr(option, "type")) 136 | int optionsToSearchCount = JArray.count(optionsToSearch) 137 | 138 | int index = -1 ; The index of the item to return (-1 means it's the only one with the selector on the page) 139 | int count = 0 140 | 141 | int i = 0 142 | while i < optionsToSearchCount && index == -1 143 | int optionOnPage = JArray.getObj(optionsToSearch, i) 144 | string optionOnPageSelector = GetOptionSelector(optionOnPage) 145 | 146 | if optionOnPageSelector == selector 147 | count += 1 148 | endIf 149 | 150 | if stateName 151 | if stateName == JMap.getStr(optionOnPage, "state") 152 | index = count ; This is this specific item 153 | endIf 154 | elseIf optionId == JMap.getInt(optionOnPage, "id") 155 | index = count ; This is this specific item 156 | endIf 157 | 158 | i += 1 159 | endWhile 160 | 161 | if count > 1 162 | return index 163 | else 164 | return -1 165 | endIf 166 | endFunction 167 | 168 | string function GetOptionSelector(int option) global 169 | string selector = JMap.getStr(option, "text") 170 | if ! selector && JMap.getStr(option, "type") == "text" ; Use the 'right' side 171 | selector = JMap.getStr(option, "strValue") 172 | endIf 173 | return selector 174 | endFunction 175 | 176 | string function GetWildcardMatcher(string selector) global 177 | int strLength = StringUtil.GetLength(selector) 178 | if StringUtil.Substring(selector, 0, 1) == "*" && StringUtil.Substring(selector, strLength - 1, 1) == "*" 179 | return StringUtil.Substring(selector, 1, strLength - 2) 180 | else 181 | return "" 182 | endIf 183 | endFunction 184 | -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_Recorder.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_Recorder hidden 2 | {Responsible for recording actions in Mod Configuration Menus} 3 | 4 | bool function IsRecording() global 5 | return GetCurrentRecordingName() 6 | endFunction 7 | 8 | bool function AnyRecordings() global 9 | return RecordingsCount() > 0 10 | endFunction 11 | 12 | int function RecordingsCount() global 13 | return McmRecorder_RecordingFiles.GetRecordingNames().Length 14 | endFunction 15 | 16 | function RecordAction(SKI_ConfigBase mcm, string modName, string pageName, string optionType, int optionId, string stateName = "", bool recordFloatValue = false, bool recordStringValue = false, bool recordOptionType = false, float fltValue = -1.0, string strValue = "", string[] menuOptions = None) global 17 | if IsRecording() && modName != "MCM Recorder" 18 | if modName != GetCurrentRecordingModName() 19 | ResetCurrentRecordingSteps() 20 | endIf 21 | 22 | int option = McmRecorder_McmFields.GetConfigurationOptionById(modName, pageName, optionId) 23 | 24 | if ! option 25 | McmRecorder_Logging.ConsoleOut("Could not get configuration option for " + modName + " " + pageName + " optionId " + optionId) 26 | McmRecorder_Logging.DumpAll("Data/McmRecorder/.debug/ProblemMessageDump.json") 27 | Debug.MessageBox("[McmRecorder] Problem! You clicked on an MCM field which we were not able to detect.") 28 | Debug.Notification("[McmRecorder] Problem! You clicked on an MCM field which we were not able to detect.") 29 | return 30 | endIf 31 | 32 | if optionType == "menu" && fltValue == -1 33 | return ; The menu was opened but then closed without choosing an option. We don't reproduce this behavior. 34 | endIf 35 | 36 | int mcmAction = JMap.object() 37 | JArray.addObj(GetCurrentRecordingSteps(), mcmAction) 38 | 39 | JMap.setStr(mcmAction, "mod", modName) 40 | 41 | if pageName != "SKYUI_DEFAULT_PAGE" 42 | JMap.setStr(mcmAction, "page", pageName) 43 | endIf 44 | 45 | string selector = JMap.getStr(option, "text") 46 | 47 | ; How many items on this page have the same 'selector'? 48 | int selectorIndex = McmRecorder_McmFields.GetSelectorIndex(modName, pageName, optionId) 49 | if selectorIndex > -1 50 | JMap.setInt(mcmAction, "index", selectorIndex) 51 | endIf 52 | 53 | string debugPrefix = "[Record Action] " + modName 54 | if pageName 55 | debugPrefix += ": " + pageName 56 | endIf 57 | debugPrefix += " (" + selector + ")" 58 | if selectorIndex > -1 59 | debugPrefix += " [" + selectorIndex + "]" 60 | endIf 61 | 62 | if optionType == "clickable" 63 | if JMap.getStr(option, "type") == "toggle" 64 | JMap.setStr(mcmAction, "option", selector) 65 | if JMap.getFlt(option, "fltValue") == 0 66 | JMap.setStr(mcmAction, "toggle", "on") 67 | McmRecorder_Logging.ConsoleOut(debugPrefix + " on") 68 | else 69 | JMap.setStr(mcmAction, "toggle", "off") 70 | McmRecorder_Logging.ConsoleOut(debugPrefix + " off") 71 | endIf 72 | else 73 | if selector 74 | JMap.setStr(mcmAction, "click", selector) 75 | McmRecorder_Logging.ConsoleOut(debugPrefix + " click") 76 | else 77 | JMap.setStr(mcmAction, "click", JMap.getStr(option, "strValue")) 78 | JMap.setStr(mcmAction, "side", "right") 79 | McmRecorder_Logging.ConsoleOut(debugPrefix + " click (right)") 80 | endIf 81 | endIf 82 | elseIf optionType == "menu" 83 | JMap.setStr(mcmAction, "option", selector) 84 | if stateName 85 | string previousState = mcm.GetState() 86 | mcm.GotoState(stateName) 87 | mcm.OnMenuOpenST() 88 | string selectedOptionText = menuOptions[fltValue as int] 89 | JMap.setStr(mcmAction, "choose", selectedOptionText) 90 | mcm.GotoState(previousState) 91 | McmRecorder_Logging.ConsoleOut(debugPrefix + " choose '" + selectedOptionText + "'") 92 | else 93 | mcm.OnOptionMenuOpen(optionId) 94 | string selectedOptionText = menuOptions[fltValue as int] 95 | JMap.setStr(mcmAction, "choose", selectedOptionText) 96 | McmRecorder_Logging.ConsoleOut(debugPrefix + " choose '" + selectedOptionText + "'") 97 | endIf 98 | elseIf optionType == "slider" 99 | JMap.setStr(mcmAction, "option", selector) 100 | JMap.setFlt(mcmAction, "slider", fltValue) 101 | McmRecorder_Logging.ConsoleOut(debugPrefix + " slider " + fltValue) 102 | elseIf optionType == "keymap" 103 | JMap.setStr(mcmAction, "option", selector) 104 | JMap.setInt(mcmAction, "shortcut", fltValue as int) 105 | McmRecorder_Logging.ConsoleOut(debugPrefix + " shortcut " + fltValue as int) 106 | elseIf optionType == "color" 107 | JMap.setStr(mcmAction, "option", selector) 108 | JMap.setInt(mcmAction, "color", fltValue as int) 109 | McmRecorder_Logging.ConsoleOut(debugPrefix + " color " + fltValue as int) 110 | elseIf optionType == "input" 111 | JMap.setStr(mcmAction, "option", selector) 112 | JMap.setStr(mcmAction, "text", strValue) 113 | McmRecorder_Logging.ConsoleOut(debugPrefix + " input '" + strValue + "'") 114 | endIf 115 | 116 | McmRecorder_RecordingFiles.SaveCurrentRecording(GetCurrentRecordingName(), modName) 117 | endIf 118 | endFunction 119 | 120 | function BeginRecording(string recordingName) global 121 | SetCurrentRecordingName(recordingName) 122 | SetCurrentRecordingModName("") 123 | ResetCurrentRecordingSteps() 124 | int metaFile = JMap.object() 125 | string authorName = Game.GetPlayer().GetActorBase().GetName() 126 | JMap.setStr(metaFile, "name", recordingName) 127 | JMap.setStr(metaFile, "version", "1.0.0") 128 | JMap.setStr(metaFile, "author", authorName) 129 | JMap.setStr(metaFile, "autorun", "false") 130 | McmRecorder_RecordingFiles.SaveRecordingInfoFile(recordingName, metaFile) 131 | endFunction 132 | 133 | function ContinueRecording(string recordingName) global 134 | SetCurrentRecordingName(recordingName) 135 | SetCurrentRecordingModName("") 136 | ResetCurrentRecordingSteps() 137 | endFunction 138 | 139 | function StopRecording() global 140 | SetCurrentRecordingName("") 141 | endFunction 142 | 143 | string function GetCurrentRecordingName() global 144 | return JDB.solveStr(McmRecorder_JDB.JdbPath_CurrentRecordingName()) 145 | endFunction 146 | 147 | function SetCurrentRecordingName(string recodingName) global 148 | JDB.solveStrSetter(McmRecorder_JDB.JdbPath_CurrentRecordingName(), recodingName, createMissingKeys = true) 149 | endFunction 150 | 151 | string function GetCurrentRecordingModName() global 152 | return JDB.solveStr(McmRecorder_JDB.JdbPath_CurrentRecordingMOdName()) 153 | endFunction 154 | 155 | function SetCurrentRecordingModName(string modName) global 156 | JDB.solveStrSetter(McmRecorder_JDB.JdbPath_CurrentRecordingModName(), modName , createMissingKeys = true) 157 | endFunction 158 | 159 | int function GetCurrentRecordingSteps() global 160 | return JDB.solveObj(McmRecorder_JDB.JdbPath_CurrentRecordingRecordingStep()) 161 | endFunction 162 | 163 | function SetCurrentRecordingSteps(int stepInfo) global 164 | JDB.solveObjSetter(McmRecorder_JDB.JdbPath_CurrentRecordingRecordingStep(), stepInfo, createMissingKeys = true) 165 | endFunction 166 | 167 | function ResetCurrentRecordingSteps() global 168 | SetCurrentRecordingSteps(JArray.object()) 169 | endFunction 170 | -------------------------------------------------------------------------------- /Scripts/Source/SKI_ConfigManager.psc: -------------------------------------------------------------------------------- 1 | scriptName SKI_ConfigManager extends SKI_QuestBase hidden 2 | 3 | ;-- Properties -------------------------------------- 4 | String property JOURNAL_MENU 5 | String function get() 6 | 7 | return "Journal Menu" 8 | endFunction 9 | endproperty 10 | String property MENU_ROOT 11 | String function get() 12 | 13 | return "_root.ConfigPanelFader.configPanel" 14 | endFunction 15 | endproperty 16 | 17 | ;-- Variables --------------------------------------- 18 | String[] _modNames 19 | Bool _locked = false 20 | Bool _lockInit = false 21 | Int _updateCounter = 0 22 | Bool _cleanupFlag = false 23 | Int _configCount = 0 24 | SKI_ConfigBase[] _modConfigs 25 | SKI_ConfigBase _activeConfig 26 | Int _curConfigID = 0 27 | Int _addCounter = 0 28 | 29 | string[] property ModNames 30 | string[] function get() 31 | return _modNames 32 | endFunction 33 | endProperty 34 | 35 | SKI_ConfigBase[] property ModConfigs 36 | SKI_ConfigBase[] function get() 37 | return _modConfigs 38 | endFunction 39 | endProperty 40 | 41 | ;-- Functions --------------------------------------- 42 | 43 | function OnGameReload() 44 | self.RegisterForModEvent("SKICP_modSelected", "OnModSelect") 45 | self.RegisterForModEvent("SKICP_pageSelected", "OnPageSelect") 46 | self.RegisterForModEvent("SKICP_optionHighlighted", "OnOptionHighlight") 47 | self.RegisterForModEvent("SKICP_optionSelected", "OnOptionSelect") 48 | self.RegisterForModEvent("SKICP_optionDefaulted", "OnOptionDefault") 49 | self.RegisterForModEvent("SKICP_keymapChanged", "OnKeymapChange") 50 | self.RegisterForModEvent("SKICP_sliderSelected", "OnSliderSelect") 51 | self.RegisterForModEvent("SKICP_sliderAccepted", "OnSliderAccept") 52 | self.RegisterForModEvent("SKICP_menuSelected", "OnMenuSelect") 53 | self.RegisterForModEvent("SKICP_menuAccepted", "OnMenuAccept") 54 | self.RegisterForModEvent("SKICP_colorSelected", "OnColorSelect") 55 | self.RegisterForModEvent("SKICP_colorAccepted", "OnColorAccept") 56 | self.RegisterForModEvent("SKICP_inputSelected", "OnInputSelect") 57 | self.RegisterForModEvent("SKICP_inputAccepted", "OnInputAccept") 58 | self.RegisterForModEvent("SKICP_dialogCanceled", "OnDialogCancel") 59 | self.RegisterForMenu(self.JOURNAL_MENU) 60 | _lockInit = true 61 | _cleanupFlag = true 62 | self.CleanUp() 63 | self.SendModEvent("SKICP_configManagerReady", "", 0.000000) 64 | _updateCounter = 0 65 | self.RegisterForSingleUpdate(5 as Float) 66 | endFunction 67 | 68 | function OnSliderSelect(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 69 | 70 | Int optionIndex = a_numArg as Int 71 | _activeConfig.RequestSliderDialogData(optionIndex) 72 | endFunction 73 | 74 | function CleanUp() 75 | 76 | self.GotoState("BUSY") 77 | _cleanupFlag = false 78 | _configCount = 0 79 | Int i = 0 80 | while i < _modConfigs.length 81 | if _modConfigs[i] == none || _modConfigs[i].GetFormID() == 0 82 | _modConfigs[i] = none 83 | _modNames[i] = "" 84 | else 85 | _configCount += 1 86 | endIf 87 | i += 1 88 | endWhile 89 | self.GotoState("") 90 | endFunction 91 | 92 | function OnMenuClose(String a_menuName) 93 | 94 | self.GotoState("") 95 | if _activeConfig 96 | _activeConfig.CloseConfig() 97 | endIf 98 | _activeConfig = none 99 | endFunction 100 | 101 | function OnInit() 102 | 103 | _modConfigs = new SKI_ConfigBase[128] 104 | _modNames = new String[128] 105 | self.OnGameReload() 106 | endFunction 107 | 108 | function OnKeymapChange(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 109 | 110 | Int optionIndex = a_numArg as Int 111 | Int keyCode = ui.GetInt(self.JOURNAL_MENU, self.MENU_ROOT + ".selectedKeyCode") 112 | String conflictControl = input.GetMappedControl(keyCode) 113 | String conflictName = "" 114 | Int i = 0 115 | while conflictControl == "" && i < _modConfigs.length 116 | if _modConfigs[i] != none 117 | conflictControl = _modConfigs[i].GetCustomControl(keyCode) 118 | if conflictControl != "" 119 | conflictName = _modNames[i] 120 | endIf 121 | endIf 122 | i += 1 123 | endWhile 124 | _activeConfig.RemapKey(optionIndex, keyCode, conflictControl, conflictName) 125 | ui.InvokeBool(self.JOURNAL_MENU, self.MENU_ROOT + ".unlock", true) 126 | endFunction 127 | 128 | function OnMenuSelect(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 129 | 130 | Int optionIndex = a_numArg as Int 131 | _activeConfig.RequestMenuDialogData(optionIndex) 132 | endFunction 133 | 134 | function OnUpdate() 135 | 136 | if _cleanupFlag 137 | self.CleanUp() 138 | endIf 139 | if _addCounter > 0 140 | debug.Notification("MCM: Registered " + _addCounter as String + " new menu(s).") 141 | _addCounter = 0 142 | endIf 143 | self.SendModEvent("SKICP_configManagerReady", "", 0.000000) 144 | if _updateCounter < 6 145 | _updateCounter += 1 146 | self.RegisterForSingleUpdate(5 as Float) 147 | else 148 | self.RegisterForSingleUpdate(30 as Float) 149 | endIf 150 | endFunction 151 | 152 | Int function GetVersion() 153 | 154 | return 4 155 | endFunction 156 | 157 | function OnOptionDefault(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 158 | 159 | Int optionIndex = a_numArg as Int 160 | _activeConfig.ResetOption(optionIndex) 161 | ui.InvokeBool(self.JOURNAL_MENU, self.MENU_ROOT + ".unlock", true) 162 | endFunction 163 | 164 | function OnOptionSelect(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 165 | Int optionIndex = a_numArg as Int 166 | _activeConfig.SelectOption(optionIndex) 167 | ui.InvokeBool(self.JOURNAL_MENU, self.MENU_ROOT + ".unlock", true) 168 | endFunction 169 | 170 | function OnInputSelect(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 171 | 172 | Int optionIndex = a_numArg as Int 173 | _activeConfig.RequestInputDialogData(optionIndex) 174 | endFunction 175 | 176 | function Log(String a_msg) 177 | 178 | debug.Trace(self as String + ": " + a_msg, 0) 179 | endFunction 180 | 181 | function OnDialogCancel(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 182 | 183 | ui.InvokeBool(self.JOURNAL_MENU, self.MENU_ROOT + ".unlock", true) 184 | endFunction 185 | 186 | function OnSliderAccept(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 187 | 188 | Float value = a_numArg 189 | _activeConfig.SetSliderValue(value) 190 | ui.InvokeBool(self.JOURNAL_MENU, self.MENU_ROOT + ".unlock", true) 191 | endFunction 192 | 193 | Int function NextID() 194 | 195 | Int startIdx = _curConfigID 196 | while _modConfigs[_curConfigID] != none 197 | _curConfigID += 1 198 | if _curConfigID >= 128 199 | _curConfigID = 0 200 | endIf 201 | if _curConfigID == startIdx 202 | return -1 203 | endIf 204 | endWhile 205 | return _curConfigID 206 | endFunction 207 | 208 | ; Skipped compiler generated GetState 209 | 210 | Int function UnregisterMod(SKI_ConfigBase a_menu) 211 | 212 | self.GotoState("BUSY") 213 | Int i = 0 214 | while i < _modConfigs.length 215 | if _modConfigs[i] == a_menu 216 | _modConfigs[i] = none 217 | _modNames[i] = "" 218 | _configCount -= 1 219 | self.GotoState("") 220 | return i 221 | endIf 222 | i += 1 223 | endWhile 224 | self.GotoState("") 225 | return -1 226 | endFunction 227 | 228 | function OnMenuAccept(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 229 | 230 | Int value = a_numArg as Int 231 | _activeConfig.SetMenuIndex(value) 232 | ui.InvokeBool(self.JOURNAL_MENU, self.MENU_ROOT + ".unlock", true) 233 | endFunction 234 | 235 | Int function RegisterMod(SKI_ConfigBase a_menu, String a_modName) 236 | 237 | self.GotoState("BUSY") 238 | if _configCount >= 128 239 | self.GotoState("") 240 | return -1 241 | endIf 242 | Int i = 0 243 | while i < _modConfigs.length 244 | if _modConfigs[i] == a_menu 245 | self.GotoState("") 246 | return i 247 | endIf 248 | i += 1 249 | endWhile 250 | Int configID = self.NextID() 251 | if configID == -1 252 | self.GotoState("") 253 | return -1 254 | endIf 255 | _modConfigs[configID] = a_menu 256 | _modNames[configID] = a_modName 257 | _configCount += 1 258 | _addCounter += 1 259 | self.GotoState("") 260 | return configID 261 | endFunction 262 | 263 | function OnMenuOpen(String a_menuName) 264 | self.GotoState("BUSY") 265 | _activeConfig = none 266 | ui.InvokeStringA(self.JOURNAL_MENU, self.MENU_ROOT + ".setModNames", _modNames) 267 | endFunction 268 | 269 | function OnModSelect(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 270 | 271 | Int configIndex = a_numArg as Int 272 | if configIndex > -1 273 | if _activeConfig 274 | _activeConfig.CloseConfig() 275 | endIf 276 | _activeConfig = _modConfigs[configIndex] 277 | _activeConfig.OpenConfig() 278 | endIf 279 | ui.InvokeBool(self.JOURNAL_MENU, self.MENU_ROOT + ".unlock", true) 280 | endFunction 281 | 282 | function OnColorAccept(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 283 | 284 | Int color = a_numArg as Int 285 | _activeConfig.SetColorValue(color) 286 | ui.InvokeBool(self.JOURNAL_MENU, self.MENU_ROOT + ".unlock", true) 287 | endFunction 288 | 289 | function OnInputAccept(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 290 | 291 | _activeConfig.SetInputText(a_strArg) 292 | ui.InvokeBool(self.JOURNAL_MENU, self.MENU_ROOT + ".unlock", true) 293 | endFunction 294 | 295 | function OnOptionHighlight(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 296 | 297 | Int optionIndex = a_numArg as Int 298 | _activeConfig.HighlightOption(optionIndex) 299 | endFunction 300 | 301 | ; Skipped compiler generated GotoState 302 | 303 | function ForceReset() 304 | 305 | self.Log("Forcing config manager reset...") 306 | self.SendModEvent("SKICP_configManagerReset", "", 0.000000) 307 | self.GotoState("BUSY") 308 | Int i = 0 309 | while i < _modConfigs.length 310 | _modConfigs[i] = none 311 | _modNames[i] = "" 312 | i += 1 313 | endWhile 314 | _curConfigID = 0 315 | _configCount = 0 316 | self.GotoState("") 317 | self.SendModEvent("SKICP_configManagerReady", "", 0.000000) 318 | endFunction 319 | 320 | function OnColorSelect(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 321 | 322 | Int optionIndex = a_numArg as Int 323 | _activeConfig.RequestColorDialogData(optionIndex) 324 | endFunction 325 | 326 | function OnPageSelect(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 327 | 328 | String page = a_strArg 329 | Int index = a_numArg as Int 330 | _activeConfig.SetPage(page, index) 331 | ui.InvokeBool(self.JOURNAL_MENU, self.MENU_ROOT + ".unlock", true) 332 | endFunction 333 | 334 | ;-- State ------------------------------------------- 335 | state BUSY 336 | 337 | function CleanUp() 338 | 339 | ; Empty function 340 | endFunction 341 | 342 | Int function RegisterMod(SKI_ConfigBase a_menu, String a_modName) 343 | 344 | return -2 345 | endFunction 346 | 347 | function ForceReset() 348 | 349 | ; Empty function 350 | endFunction 351 | 352 | Int function UnregisterMod(SKI_ConfigBase a_menu) 353 | 354 | return -2 355 | endFunction 356 | endState 357 | -------------------------------------------------------------------------------- /Scripts/Source/McmRecorderMCM.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorderMCM extends SKI_ConfigBase 2 | {**PRIVATE** Please do not edit.} 3 | 4 | McmRecorder Recorder 5 | 6 | int oid_Record 7 | int oid_Stop 8 | int[] oids_Recordings 9 | int oid_KeyboardShortcuts_RecordingSelectionMenu 10 | 11 | string[] menuOptions 12 | string[] recordings 13 | bool isPlayingRecording 14 | string currentlyPlayingRecordingName 15 | bool openRunOrPreviewStepsPrompt 16 | 17 | bool property IsSkyrimVR 18 | bool function get() 19 | return Game.GetModByName("SkyrimVR.esm") != 255 20 | endFunction 21 | endProperty 22 | 23 | bool property IsVrikInstalled 24 | bool function get() 25 | return McmRecorder_VRIK.IsVrikInstalled() 26 | endFunction 27 | endProperty 28 | 29 | bool property HasJcontainersInstalled 30 | bool function get() 31 | return JContainers.APIVersion() 32 | endFunction 33 | endProperty 34 | 35 | bool property HasPapyrusUtilInstalled 36 | bool function get() 37 | return PapyrusUtil.GetVersion() 38 | endFunction 39 | endProperty 40 | 41 | event OnConfigInit() 42 | ModName = "MCM Recorder" 43 | Recorder = (self as Quest) as McmRecorder 44 | endEvent 45 | 46 | event OnConfigOpen() 47 | if IsSkyrimVR && IsVrikInstalled 48 | Pages = new string[3] 49 | Pages[0] = "MCM Recordings" 50 | Pages[1] = "Keyboard Shortcuts" 51 | Pages[2] = "VR Gestures" 52 | else 53 | Pages = new string[2] 54 | Pages[0] = "MCM Recordings" 55 | Pages[1] = "Keyboard Shortcuts" 56 | endIf 57 | endEvent 58 | 59 | event OnPageReset(string page) 60 | bool papyrusUtilOK = HasPapyrusUtilInstalled 61 | bool jcontainersOK = HasJcontainersInstalled 62 | 63 | if papyrusUtilOK && jcontainersOK 64 | if page == "Keyboard Shortcuts" 65 | Render_KeyboardShortcuts() 66 | elseIf page == "VR Gestures" 67 | Render_VRGestures() 68 | else 69 | Render_Recordings() 70 | endIf 71 | else 72 | if ! papyrusUtilOK 73 | AddTextOption("PapyrusUtil not found", "FAILED", OPTION_FLAG_DISABLED) 74 | AddTextOption("(or incompatible version installed)", "", OPTION_FLAG_DISABLED) 75 | endIf 76 | if ! jcontainersOK 77 | AddTextOption("JContainers not found", "FAILED", OPTION_FLAG_DISABLED) 78 | AddTextOption("(or incompatible version installed)", "", OPTION_FLAG_DISABLED) 79 | endIf 80 | endIf 81 | endEvent 82 | 83 | function Render_Recordings() 84 | if McmRecorder_Recorder.IsRecording() 85 | oid_Stop = AddTextOption("Currently Recording!", "STOP RECORDING", OPTION_FLAG_NONE) 86 | AddTextOption(McmRecorder_Recorder.GetCurrentRecordingName(), "", OPTION_FLAG_DISABLED) 87 | else 88 | if IsSkyrimVR 89 | oid_Record = AddTextOption("Click to begin recording:", "BEGIN RECORDING", OPTION_FLAG_NONE) 90 | else 91 | oid_Record = AddInputOption("Click to begin recording:", "BEGIN RECORDING", OPTION_FLAG_NONE) 92 | endIf 93 | AddEmptyOption() 94 | endIf 95 | ListRecordings() 96 | endFunction 97 | 98 | function Render_KeyboardShortcuts() 99 | if McmRecorder_Recorder.AnyRecordings() 100 | ; Recording Choice Dropdown 101 | AddTextOption("Choose a recording to set keyboard shortcut", "", OPTION_FLAG_DISABLED) 102 | AddEmptyOption() 103 | oid_KeyboardShortcuts_RecordingSelectionMenu = AddMenuOption("", "[Choose Recording]", OPTION_FLAG_NONE) 104 | AddEmptyOption() 105 | 106 | ; Existing keyboard shortcuts 107 | Render_KeyboardShortcutInfos(McmRecorder_KeyboardShortcuts.GetShortcutsInfos()) 108 | else 109 | AddTextOption("No recordings available", "", OPTION_FLAG_DISABLED) 110 | endIf 111 | endFunction 112 | 113 | function Render_KeyboardShortcutInfos(int shortcutInfos) 114 | int shortcutOptions = JIntMap.object() 115 | JDB.solveObjSetter(McmRecorder_JDB.JdbPath_MCM_KeyboardShortcuts_ShortcutOptions(), shortcutOptions, createMissingKeys = true) 116 | int shortcutInfosCount = JArray.count(shortcutInfos) 117 | if shortcutInfosCount > 0 118 | AddEmptyOption() 119 | AddEmptyOption() 120 | int i = 0 121 | while i < shortcutInfosCount 122 | 123 | int shortcutInfo = JArray.getObj(shortcutInfos, i) 124 | string recordingName = JMap.getStr(shortcutInfo, "recordingName") 125 | int shortcut = JMap.getObj(shortcutInfo, "shortcut") 126 | int keycode = JMap.getInt(shortcut, "key") 127 | 128 | int keymap = JMap.object() 129 | JMap.setStr(keymap, "action", "keymap") 130 | JMap.setObj(keymap, "shortcutInfo", shortcutInfo) 131 | 132 | int ctrl = JMap.object() 133 | JMap.setStr(ctrl, "action", "ctrl") 134 | JMap.setObj(ctrl, "shortcutInfo", shortcutInfo) 135 | 136 | int alt = JMap.object() 137 | JMap.setStr(alt, "action", "alt") 138 | JMap.setObj(alt, "shortcutInfo", shortcutInfo) 139 | 140 | int shift = JMap.object() 141 | JMap.setStr(shift, "action", "shift") 142 | JMap.setObj(shift, "shortcutInfo", shortcutInfo) 143 | 144 | int delete = JMap.object() 145 | JMap.setStr(delete, "action", "delete") 146 | JMap.setObj(delete, "shortcutInfo", shortcutInfo) 147 | 148 | JIntMap.setObj(shortcutOptions, AddKeyMapOption(recordingName, keycode, OPTION_FLAG_NONE), keymap) 149 | JIntMap.setObj(shortcutOptions, AddTextOption("Click to delete Recording", "DELETE", OPTION_FLAG_NONE), delete) 150 | JIntMap.setObj(shortcutOptions, AddToggleOption("Ctrl", JMap.getStr(shortcut, "ctrl") == "true", OPTION_FLAG_NONE), ctrl) 151 | AddEmptyOption() 152 | JIntMap.setObj(shortcutOptions, AddToggleOption("Alt", JMap.getStr(shortcut, "alt") == "true", OPTION_FLAG_NONE), alt) 153 | AddEmptyOption() 154 | JIntMap.setObj(shortcutOptions, AddToggleOption("Shift", JMap.getStr(shortcut, "shift") == "true", OPTION_FLAG_NONE), shift) 155 | AddEmptyOption() 156 | AddEmptyOption() 157 | AddEmptyOption() 158 | i += 1 159 | endWhile 160 | endIf 161 | endFunction 162 | 163 | int function GetShortcutInfoAndActionForOptionsID(int optionId) 164 | int shortcutOptions = JDB.solveObj(McmRecorder_JDB.JdbPath_MCM_KeyboardShortcuts_ShortcutOptions()) 165 | if shortcutOptions 166 | return JIntMap.getObj(shortcutOptions, optionId) 167 | endIf 168 | endFunction 169 | 170 | function Render_VRGestures() 171 | recordings = McmRecorder_RecordingFiles.GetRecordingNames() 172 | if recordings.Length && ((! McmRecorder_Recorder.IsRecording()) || recordings.Length > 1) 173 | SetCursorFillMode(TOP_TO_BOTTOM) 174 | AddTextOption("Choose MCM Recordings", "", OPTION_FLAG_DISABLED) 175 | AddTextOption("These will become available as VRIK Gestures", "", OPTION_FLAG_DISABLED) 176 | AddTextOption("which can be configured via the VRIK MCM", "", OPTION_FLAG_DISABLED) 177 | AddEmptyOption() 178 | SetCursorFillMode(LEFT_TO_RIGHT) 179 | oids_Recordings = Utility.CreateIntArray(recordings.Length) 180 | int i = 0 181 | while i < recordings.Length 182 | string recordingName = recordings[i] 183 | if recordingName != McmRecorder_Recorder.GetCurrentRecordingName() 184 | string[] stepNames = McmRecorder_RecordingFiles.GetRecordingStepFilenames(recordingName) 185 | int recordingInfo = McmRecorder_RecordingFiles.GetRecordingInfo(recordingName) 186 | bool isGesture = McmRecorder_RecordingInfo.IsVrGesture(recordingInfo) 187 | oids_Recordings[i] = AddToggleOption(recordingName, isGesture, OPTION_FLAG_NONE) 188 | string authorText = "" 189 | if JMap.getStr(recordingInfo, "author") 190 | authorText = "by " + JMap.getStr(recordingInfo, "author") 191 | endIf 192 | AddTextOption(authorText, JMap.getStr(recordingInfo, "version"), OPTION_FLAG_DISABLED) 193 | endIf 194 | i += 1 195 | endWhile 196 | else 197 | AddTextOption("There are no recordings", "", OPTION_FLAG_DISABLED) 198 | endIf 199 | endFunction 200 | 201 | function ListRecordings() 202 | recordings = McmRecorder_RecordingFiles.GetRecordingNames() 203 | if recordings.Length && ((! McmRecorder_Recorder.IsRecording()) || recordings.Length > 1) 204 | AddEmptyOption() 205 | AddEmptyOption() 206 | AddTextOption("Choose a recording to play:", "", OPTION_FLAG_DISABLED) 207 | AddEmptyOption() 208 | oids_Recordings = Utility.CreateIntArray(recordings.Length) 209 | int i = 0 210 | while i < recordings.Length 211 | string recordingName = recordings[i] 212 | if recordingName != McmRecorder_Recorder.GetCurrentRecordingName() 213 | string[] stepNames = McmRecorder_RecordingFiles.GetRecordingStepFilenames(recordingName) 214 | oids_Recordings[i] = AddTextOption("", recordingName, OPTION_FLAG_NONE) 215 | int recordingInfo = McmRecorder_RecordingFiles.GetRecordingInfo(recordingName) 216 | string authorText = "" 217 | if JMap.getStr(recordingInfo, "author") 218 | authorText = "by " + JMap.getStr(recordingInfo, "author") 219 | endIf 220 | AddTextOption(authorText, JMap.getStr(recordingInfo, "version"), OPTION_FLAG_DISABLED) 221 | endIf 222 | i += 1 223 | endWhile 224 | endIf 225 | endFunction 226 | 227 | event OnOptionSelect(int optionId) 228 | if IsSkyrimVR && optionId == oid_Record ; SkyrimVR 229 | if ! McmRecorder_Recorder.IsRecording() 230 | McmRecorder_Recorder.BeginRecording(GetRandomRecordingName()) 231 | ForcePageReset() 232 | else 233 | McmRecorder_Recorder.StopRecording() ; For SkyrimVR these OIDs are the same 234 | ForcePageReset() 235 | endIf 236 | elseIf optionId == oid_Stop 237 | McmRecorder_Recorder.StopRecording() 238 | ForcePageReset() 239 | elseIf oids_Recordings.Find(optionId) > -1 240 | int recordingIndex = oids_Recordings.Find(optionId) 241 | string recordingName = recordings[recordingIndex] 242 | if CurrentPage == "VR Gestures" 243 | int recording = McmRecorder_RecordingInfo.Get(recordingName) 244 | bool isGesture = McmRecorder_RecordingInfo.IsVrGesture(recording) 245 | if isGesture 246 | McmRecorder_RecordingInfo.SetIsVrGesture(recording, false) 247 | SetToggleOptionValue(optionId, false, false) 248 | McmRecorder_VRIK.UnregisterVrikGestureForRecording(recordingName) 249 | else 250 | McmRecorder_RecordingInfo.SetIsVrGesture(recording, true) 251 | SetToggleOptionValue(optionId, true, false) 252 | McmRecorder_VRIK.RegisterVrikGestureForRecording(recordingName) 253 | endIf 254 | else 255 | PromptToRunRecordingOrPreviewSteps(recordingName) 256 | endIf 257 | else 258 | int shortcutInfoAndAction = GetShortcutInfoAndActionForOptionsID(optionId) 259 | if shortcutInfoAndAction 260 | string clickAction = JMap.getStr(shortcutInfoAndAction, "action") 261 | int shortcutInfo = JMap.getObj(shortcutInfoAndAction, "shortcutInfo") 262 | string recordingName = JMap.getStr(shortcutInfo, "recordingName") 263 | int recordingInfo = McmRecorder_RecordingInfo.Get(recordingName) 264 | int shortcut = JMap.getObj(recordingInfo, "shortcut") 265 | if clickAction == "ctrl" 266 | if JMap.getStr(shortcut, "ctrl") == "true" 267 | JMap.removeKey(shortcut, "ctrl") 268 | SetToggleOptionValue(optionId, false, false) 269 | else 270 | JMap.setStr(shortcut, "ctrl", "true") 271 | SetToggleOptionValue(optionId, true, false) 272 | endIf 273 | McmRecorder_RecordingInfo.Save(recordingName, recordingInfo) 274 | elseIf clickAction == "alt" 275 | if JMap.getStr(shortcut, "alt") == "true" 276 | JMap.removeKey(shortcut, "alt") 277 | SetToggleOptionValue(optionId, false, false) 278 | else 279 | JMap.setStr(shortcut, "alt", "true") 280 | SetToggleOptionValue(optionId, true, false) 281 | endIf 282 | McmRecorder_RecordingInfo.Save(recordingName, recordingInfo) 283 | elseIf clickAction == "shift" 284 | if JMap.getStr(shortcut, "shift") == "true" 285 | JMap.removeKey(shortcut, "shift") 286 | SetToggleOptionValue(optionId, false, false) 287 | else 288 | JMap.setStr(shortcut, "shift", "true") 289 | SetToggleOptionValue(optionId, true, false) 290 | endIf 291 | McmRecorder_RecordingInfo.Save(recordingName, recordingInfo) 292 | elseIf clickAction == "delete" 293 | if ShowMessage("Are you sure you would like to delete this shortcut?", true, "Yes", "Cancel") 294 | JMap.removeKey(recordingInfo, "shortcut") 295 | ForcePageReset() 296 | McmRecorder_RecordingInfo.Save(recordingName, recordingInfo) 297 | endIf 298 | endIf 299 | endIf 300 | endIf 301 | endEvent 302 | 303 | event OnOptionKeyMapChange(int optionId, int keyCode, string conflictControl, string conflictName) 304 | int shortcutInfoAndAction = GetShortcutInfoAndActionForOptionsID(optionId) 305 | if shortcutInfoAndAction 306 | int shortcutInfo = JMap.getObj(shortcutInfoAndAction, "shortcutInfo") 307 | string recordingName = JMap.getStr(shortcutInfo, "recordingName") 308 | int recordingInfo = McmRecorder_RecordingInfo.Get(recordingName) 309 | int shortcut = JMap.getObj(recordingInfo, "shortcut") 310 | JMap.setInt(shortcut, "key", keyCode) 311 | SetKeyMapOptionValue(optionId, keyCode, false) 312 | Recorder.StopListeningForKeyboardShortcuts() 313 | McmRecorder_RecordingInfo.Save(recordingName, recordingInfo) 314 | Recorder.StartListenForKeyboardShortcuts() 315 | endIf 316 | endEvent 317 | 318 | event OnOptionInputAccept(int optionId, string text) 319 | McmRecorder_Recorder.BeginRecording(text) 320 | ForcePageReset() 321 | Debug.MessageBox("Recording Started!\n\nYou can now interact with MCM menus and all interactions will be recorded.\n\nWhen you are finished, return to this page to stop the recording (or quit the game).\n\nRecordings are saved in simple text files inside of Data\\McmRecorder\\ which you can edit to tweak your recording without completely re-recording it :)") 322 | endEvent 323 | 324 | event OnOptionInputOpen(int optionId) 325 | if optionId == oid_Record 326 | SetInputDialogStartText(GetRandomRecordingName()) 327 | endIf 328 | endEvent 329 | 330 | event OnOptionMenuOpen(int optionId) 331 | if optionId == oid_KeyboardShortcuts_RecordingSelectionMenu 332 | menuOptions = new string[1] 333 | string[] recordingNames = McmRecorder_RecordingFiles.GetRecordingNames() 334 | int recordingsWithoutShortcutsCount = 0 335 | int i = 0 336 | while i < recordingNames.Length 337 | string recordingName = recordingNames[i] 338 | if ! JMap.getObj(McmRecorder_RecordingInfo.Get(recordingName), "shortcut") 339 | recordingsWithoutShortcutsCount += 1 340 | menuOptions = Utility.ResizeStringArray(menuOptions, recordingsWithoutShortcutsCount) 341 | menuOptions[menuOptions.Length - 1] = recordingName 342 | endIf 343 | i += 1 344 | endWhile 345 | SetMenuDialogOptions(menuOptions) 346 | endIf 347 | endEvent 348 | 349 | event OnOptionMenuAccept(int optionId, int index) 350 | if index == -1 351 | return 352 | endIf 353 | 354 | if optionId == oid_KeyboardShortcuts_RecordingSelectionMenu 355 | string recordingName = menuOptions[index] 356 | McmRecorder_KeyboardShortcuts.AddRecording(recordingName) 357 | ForcePageReset() 358 | endIf 359 | endEvent 360 | 361 | string function GetRandomRecordingName() 362 | string[] currentTimeParts = StringUtil.Split(Utility.GetCurrentRealTime(), ".") 363 | return "Recording_" + currentTimeParts[0] + "_" + currentTimeParts[1] 364 | endFunction 365 | 366 | ; TODO move to the McmRecorder maybe a global script for prompts 367 | function PromptToRunRecordingOrPreviewSteps(string recordingName) 368 | string recordingDescription = McmRecorder_RecordingFiles.GetRecordingDescription(recordingName) 369 | 370 | bool confirmation = true 371 | 372 | ; The ShowMessage prompt can not be interacted with via SkyrimVR so we simply show a prompt - not a confirmation dialog 373 | if ! IsSkyrimVR 374 | confirmation = ShowMessage(recordingDescription + "\n\nClose the MCM to continue\n\nYou will be prompted to play this recording\n\nYou will also be able to preview all recording steps or play individual one\n\nYou will also be given the opportunity to continue the recording\n\nWould you like to continue?", "No", "Yes", "Cancel") 375 | if confirmation 376 | Debug.MessageBox("Close the MCM to continue") 377 | endIf 378 | endIf 379 | 380 | if confirmation 381 | string[] stepNames = McmRecorder_RecordingFiles.GetRecordingStepFilenames(recordingName) 382 | string text = recordingDescription + "\n" 383 | int i = 0 384 | while i < stepNames.Length && i < 11 385 | if i == 10 386 | text += "\n- ..." 387 | else 388 | text += "\n- " + StringUtil.Substring(stepNames[i], 0, StringUtil.Find(stepNames[i], ".json")) 389 | endIf 390 | i += 1 391 | endWhile 392 | text += "\n\nWould you like to play this recording?" 393 | 394 | ; Inlined to extract method later 395 | Recorder.McmRecorder_MessageText.SetName(text) 396 | int responseId = Recorder.McmRecorder_Message_RunRecordingOrViewSteps.Show() 397 | string response 398 | if responseId == 0 399 | response = "Play Recording" 400 | elseIf responseId == 1 401 | response = "View Steps" 402 | elseIf responseId == 2 403 | response = "Add to Recording" 404 | elseIf responseId == 3 405 | response = "Cancel" 406 | endIf 407 | 408 | if response == "Play Recording" 409 | currentlyPlayingRecordingName = recordingName 410 | isPlayingRecording = true 411 | McmRecorder_Player.PlayRecording(recordingName) 412 | currentlyPlayingRecordingName = "" 413 | isPlayingRecording = false 414 | elseIf response == "View Steps" 415 | ShowStepSelectionUI(recordingName, stepNames) 416 | elseIf response == "Add to Recording" 417 | McmRecorder_Recorder.ContinueRecording(recordingName) 418 | Debug.MessageBox("Recording has been restarted!\n\nYou can now interact with MCM menus and all interactions will be recorded.\n\nWhen you are finished, return to the MCM Recorder mod configuration menu to stop the recording (or quit the game).\n\nRecordings are saved in simple text files inside of Data\\McmRecorder\\ which you can edit to tweak your recording without completely re-recording it :)") 419 | endIf 420 | endIf 421 | endFunction 422 | 423 | function ShowStepSelectionUI(string recordingName, string[] stepNames) 424 | UIListMenu menu = UIExtensions.GetMenu("UIListMenu") as UIListMenu 425 | menu.AddEntryItem("[" + recordingName + "]") 426 | menu.AddEntryItem(" ") 427 | 428 | int i = 0 429 | while i < stepNames.Length 430 | string stepName = stepNames[i] 431 | menu.AddEntryItem(stepName) 432 | i += 1 433 | endWhile 434 | 435 | menu.OpenMenu() 436 | 437 | int selection = menu.GetResultInt() 438 | 439 | if selection < 2 ; Go Back 440 | ; TODO open the previous prompt! This isn't working... 441 | ; PromptToRunRecordingOrPreviewSteps(recordingName) 442 | else 443 | int stepIndex = selection - 2 ; Subtract the top 3 entry items 444 | string stepName = stepNames[stepIndex] 445 | McmRecorder_Player.PlayStep(recordingName, stepName) 446 | Debug.MessageBox("MCM recording " + recordingName + " step " + StringUtil.Substring(stepName, 0, StringUtil.Find(stepName, ".json")) + " has finished playing.") 447 | endIf 448 | endFunction 449 | -------------------------------------------------------------------------------- /Scripts/Source/McmRecorder_Player.psc: -------------------------------------------------------------------------------- 1 | scriptName McmRecorder_Player hidden 2 | {Responsible for playback of recordings or can trigger individual actions} 3 | 4 | bool function IsPlayingRecording() global 5 | return JDB.solveInt(McmRecorder_JDB.JdbPath_IsPlayingRecording()) 6 | endFunction 7 | 8 | function PlayRecording(string recordingName, float waitTimeBetweenActions = 0.0, float mcmLoadWaitTime = 0.0, bool verbose = true) global 9 | ClearModsPlayed() 10 | SetCurrentPlayingRecordingModName("") 11 | SetCurrentPlayingRecordingModPageName("") 12 | 13 | SetIsPlayingRecording(true) 14 | SetCurrentlyPlayingRecordingName(recordingName) 15 | 16 | McmRecorder recorder = McmRecorder.GetMcmRecorderInstance() 17 | recorder.ListenForSystemMenuOpen() 18 | 19 | int steps = McmRecorder_RecordingFiles.GetAllStepsForRecording(recordingName) 20 | SetCurrentlyPlayingSteps(steps) 21 | 22 | string[] stepFiles = JMap.allKeysPArray(steps) 23 | 24 | if verbose 25 | McmRecorder_UI.WelcomeMessage(recordingName) 26 | McmRecorder_UI.Notification("Play " + recordingName + " (" + stepFiles.Length + " steps)") 27 | endIf 28 | 29 | int fileIndex = 0 30 | while fileIndex < stepFiles.Length 31 | ; File for a given step 32 | string filename = stepFiles[fileIndex] 33 | int recordingActions = JMap.getObj(steps, filename) 34 | JValue.retain(recordingActions) 35 | int actionCount = JArray.count(recordingActions) 36 | 37 | ; Set the current step being run... 38 | SetCurrentlyPlayingStepFilename(filename) 39 | SetCurrentlyPlayingStepIndex(fileIndex) 40 | 41 | ; Show notification for the current step being run 42 | if verbose 43 | McmRecorder_UI.Notification(filename + " (" + (fileIndex + 1) + "/" + stepFiles.Length + ")") 44 | endIf 45 | 46 | int i = 0 47 | while i < actionCount 48 | int recordingAction = JArray.getObj(recordingActions, i) 49 | PlayAction(recordingAction, StringUtil.Substring(filename, 0, StringUtil.Find(filename, ".json")), mcmLoadWaitTime = mcmLoadWaitTime) 50 | if waitTimeBetweenActions 51 | Utility.WaitMenuMode(waitTimeBetweenActions) 52 | endIf 53 | i += 1 54 | endWhile 55 | 56 | JValue.release(recordingActions) 57 | fileIndex += 1 58 | endWhile 59 | 60 | recorder.StopListeningForSystemMenuOpen() 61 | 62 | if verbose 63 | McmRecorder_UI.FinishedMessage(recordingName) 64 | endIf 65 | 66 | SetIsPlayingRecording(false) 67 | endFunction 68 | 69 | function PlayStep(string recordingName, string stepName, float waitTimeBetweenActions = 0.0) global 70 | SetCurrentPlayingRecordingModName("") 71 | SetCurrentPlayingRecordingModPageName("") 72 | SetIsPlayingRecording(true) ; XXX is this used? 73 | 74 | int stepInfo = McmRecorder_RecordingFiles.GetRecordingStep(recordingName, stepName) 75 | 76 | JValue.retain(stepInfo) 77 | 78 | McmRecorder_UI.Notification("Playing step " + stepName + " of recording " + recordingName) 79 | 80 | int actionCount = JArray.count(stepInfo) 81 | int i = 0 82 | while i < actionCount 83 | int recordingAction = JArray.getObj(stepInfo, i) 84 | PlayAction(recordingAction, stepName) 85 | if waitTimeBetweenActions 86 | Utility.WaitMenuMode(waitTimeBetweenActions) 87 | endIf 88 | i += 1 89 | endWhile 90 | 91 | JValue.release(stepInfo) 92 | 93 | SetIsPlayingRecording(false) 94 | endFunction 95 | 96 | function PlayAction(int actionInfo, string stepName, bool promptOnFailures = true, float mcmLoadWaitTime = 10.0) global 97 | string modName = JMap.getStr(actionInfo, "mod") 98 | string pageName = JMap.getStr(actionInfo, "page") 99 | 100 | string skippingModName = GetCurrentlySkippingModName() 101 | if skippingModName 102 | if modName == skippingModName 103 | return ; Skip! 104 | else 105 | SetCurrentlySkippingModName("") 106 | endIf 107 | endIf 108 | 109 | SKI_ConfigBase mcm = McmRecorder.GetMcmInstance(modName) 110 | 111 | if (! mcm) && mcmLoadWaitTime 112 | McmRecorder_Logging.ConsoleOut("[Play Action] MCM not loaded: " + modName + " (waiting...)") 113 | float startTime = Utility.GetCurrentRealTime() 114 | float lastNotification = startTime 115 | while (! mcm) && (Utility.GetCurrentRealTime() - startTime) < mcmLoadWaitTime 116 | float now = Utility.GetCurrentRealTime() 117 | if (now - lastNotification) >= 5.0 ; Make configurable, 5 secs waiting for MCM to load 118 | lastNotification = now 119 | McmRecorder_UI.Notification("Waiting for " + modName + " MCM to load") 120 | McmRecorder_Logging.ConsoleOut("[Play Action] MCM not loaded: " + modName + " (waiting...)") 121 | endIf 122 | Utility.WaitMenuMode(1.0) ; hard coded for now 123 | mcm = McmRecorder.GetMcmInstance(modName) 124 | endWhile 125 | if ! mcm 126 | if promptOnFailures 127 | string result = McmRecorder_UI.GetUserResponseForNotFoundMod(modName) 128 | if result == "Try again" 129 | PlayAction(actionInfo, stepName, promptOnFailures, mcmLoadWaitTime) 130 | elseIf result == "Skip this mod" 131 | SetCurrentlySkippingModName(modName) 132 | return 133 | endIf 134 | else 135 | SetCurrentlySkippingModName(modName) 136 | return 137 | endIf 138 | return 139 | endIf 140 | endIf 141 | 142 | if ! mcm 143 | Debug.Trace("MCM recorder could not load MCM menu for " + modName) 144 | return 145 | endIf 146 | 147 | if modName != GetCurrentPlayingRecordingModName() || pageName != GetCurrentPlayingRecordingModPageName() 148 | RefreshMcmPage(mcm, modName, pageName) 149 | endIf 150 | 151 | SetCurrentPlayingRecordingModName(modName) 152 | SetCurrentPlayingRecordingModPageName(pageName) 153 | 154 | string optionType 155 | string selector = JMap.getStr(actionInfo, "option") 156 | 157 | if JMap.hasKey(actionInfo, "click") 158 | optionType = "text" 159 | selector = JMap.getStr(actionInfo, "click") 160 | elseIf JMap.hasKey(actionInfo, "toggle") 161 | optionType = "toggle" 162 | elseIf JMap.hasKey(actionInfo, "choose") 163 | optionType = "menu" 164 | elseIf JMap.hasKey(actionInfo, "text") 165 | optionType = "input" 166 | elseIf JMap.hasKey(actionInfo, "shortcut") 167 | optionType = "keymap" 168 | elseIf JMap.hasKey(actionInfo, "color") 169 | optionType = "color" 170 | elseIf JMap.hasKey(actionInfo, "slider") 171 | optionType = "slider" 172 | else 173 | McmRecorder_UI.MessageBox("MCM recording step " + stepName + " has action of unknown or unsupported type: '" + optionType + "'\n" + McmRecorder_Logging.ToJson(actionInfo)) 174 | return 175 | endIf 176 | 177 | string wildcard = McmRecorder_McmFields.GetWildcardMatcher(selector) 178 | string side = JMap.getStr(actionInfo, "side", "left") 179 | string stateName = JMap.getStr(actionInfo, "state") 180 | int index = JMap.getInt(actionInfo, "index", -1) 181 | 182 | string debugPrefix = "[Play Action] " + modName 183 | if pageName 184 | debugPrefix += ": " + pageName 185 | endIf 186 | debugPrefix += " (" + selector + ")" 187 | if index > -1 188 | debugPrefix += " [" + index + "]" 189 | endIf 190 | 191 | float searchTimeout = JMap.getFlt(actionInfo, "timeout", 30.0) ; Default to wait for options to show up for a max of 30 seconds 192 | float searchInterval = JMap.getFlt(actionInfo, "interval", 0.5) ; Default to try twice per second 193 | float searchPageLoadTime = JMap.getFlt(actionInfo, "pageload", 5.0) ; Allow pages up to 5 seconds for an option to appear 194 | 195 | int option = FindOption(mcm, modName, pageName, optionType, selector, wildcard, index, side, searchTimeout, searchInterval, searchPageLoadTime) 196 | 197 | if option 198 | if ! stateName 199 | stateName = JMap.getStr(option, "state") 200 | endIf 201 | int optionId = JMap.getInt(option, "id") 202 | if stateName 203 | string previousState = mcm.GetState() 204 | mcm.GotoState(stateName) 205 | if optionType == "menu" 206 | string menuItem = JMap.getStr(actionInfo, "choose") 207 | McmRecorder_Logging.ConsoleOut(debugPrefix + " choose '" + menuItem + "'") 208 | mcm.OnMenuOpenST() 209 | string[] menuOptions = mcm.MostRecentlyConfiguredMenuDialogOptions 210 | int itemIndex = menuOptions.Find(menuItem) 211 | if itemIndex == -1 212 | McmRecorder_UI.MessageBox("Could not find " + menuItem + " menu item. Available options: " + menuOptions) 213 | else 214 | mcm.OnMenuAcceptST(itemIndex) 215 | endIf 216 | elseIf optionType == "keymap" 217 | int shortcut = JMap.getFlt(actionInfo, "shortcut") as int 218 | McmRecorder_Logging.ConsoleOut(debugPrefix + " shortcut " + shortcut) 219 | mcm.OnKeyMapChangeST(shortcut, "", "") 220 | elseIf optionType == "color" 221 | int colorCode = JMap.getInt(actionInfo, "color") 222 | McmRecorder_Logging.ConsoleOut(debugPrefix + " color " + colorCode) 223 | mcm.OnColorAcceptST(colorCode) 224 | elseIf optionType == "input" 225 | string inputValue = JMap.getStr(actionInfo, "text") 226 | McmRecorder_Logging.ConsoleOut(debugPrefix + " input '" + inputValue + "'") 227 | mcm.OnInputAcceptST(inputValue) 228 | elseIf optionType == "slider" 229 | float sliderValue = JMap.getFlt(actionInfo, "slider") 230 | McmRecorder_Logging.ConsoleOut(debugPrefix + " slider " + sliderValue) 231 | mcm.OnSliderAcceptST(sliderValue) 232 | elseIf optionType == "toggle" 233 | string turnOnOrOff = JMap.getStr(actionInfo, "toggle") 234 | bool currentlyEnabledOnPage = JMap.getFlt(option, "fltValue") == 1 235 | if currentlyEnabledOnPage && turnOnOrOff == "off" 236 | McmRecorder_Logging.ConsoleOut(debugPrefix + " toggle off") 237 | mcm.OnSelectST() ; Turn off 238 | elseIf (!currentlyEnabledOnPage) && turnOnOrOff == "on" 239 | McmRecorder_Logging.ConsoleOut(debugPrefix + " toggle on") 240 | mcm.OnSelectST() ; Turn on 241 | endIf 242 | elseIf optionType == "text" 243 | McmRecorder_Logging.ConsoleOut(debugPrefix + " click") 244 | mcm.OnSelectST() 245 | endIf 246 | mcm.GotoState(previousState) 247 | else 248 | if optionType == "menu" 249 | string menuItem = JMap.getStr(actionInfo, "choose") 250 | McmRecorder_Logging.ConsoleOut(debugPrefix + " choose '" + menuItem + "'") 251 | mcm.OnOptionMenuOpen(optionId) 252 | string[] menuOptions = mcm.MostRecentlyConfiguredMenuDialogOptions 253 | int itemIndex = menuOptions.Find(menuItem) 254 | if itemIndex == -1 255 | McmRecorder_UI.MessageBox("Could not find " + menuItem + " menu item. Available options: " + menuOptions) 256 | else 257 | mcm.OnOptionMenuAccept(optionId, itemIndex) 258 | endIf 259 | elseIf optionType == "slider" 260 | float sliderValue = JMap.getFlt(actionInfo, "slider") 261 | McmRecorder_Logging.ConsoleOut(debugPrefix + " slider " + sliderValue) 262 | mcm.OnOptionSliderAccept(optionId, sliderValue) 263 | elseIf optionType == "keymap" 264 | int shortcut = JMap.getInt(actionInfo, "shortcut") 265 | McmRecorder_Logging.ConsoleOut(debugPrefix + " shortcut " + shortcut) 266 | mcm.OnOptionKeyMapChange(optionId, shortcut, "", "") 267 | elseIf optionType == "color" 268 | int colorCode = JMap.getInt(actionInfo, "color") 269 | McmRecorder_Logging.ConsoleOut(debugPrefix + " color " + colorCode) 270 | mcm.OnOptionColorAccept(optionId, colorCode) 271 | elseIf optionType == "input" 272 | string inputValue = JMap.getStr(actionInfo, "text") 273 | McmRecorder_Logging.ConsoleOut(debugPrefix + " input '" + inputValue + "'") 274 | mcm.OnOptionInputAccept(optionId, inputValue) 275 | elseIf optionType == "toggle" 276 | string turnOnOrOff = JMap.getStr(actionInfo, "toggle") 277 | bool currentlyEnabledOnPage = JMap.getFlt(option, "fltValue") == 1 278 | if currentlyEnabledOnPage && turnOnOrOff == "off" 279 | McmRecorder_Logging.ConsoleOut(debugPrefix + " toggle off") 280 | mcm.OnOptionSelect(optionId) ; Turn off 281 | elseIf (!currentlyEnabledOnPage) && turnOnOrOff == "on" 282 | McmRecorder_Logging.ConsoleOut(debugPrefix + " toggle on") 283 | mcm.OnOptionSelect(optionId) ; Turn on 284 | endIf 285 | elseIf optionType == "text" 286 | McmRecorder_Logging.ConsoleOut(debugPrefix + " click") 287 | mcm.OnOptionSelect(optionId) 288 | endIf 289 | endIf 290 | elseIf promptOnFailures 291 | string response = McmRecorder_UI.GetUserResponseForNotFoundSelector(modName, pageName, selector) 292 | if response == "Try again" 293 | PlayAction(actionInfo, stepName, promptOnFailures) 294 | elseIf response == "Skip this mod" 295 | SetCurrentlySkippingModName(modName) 296 | endIf 297 | endIf 298 | endFunction 299 | 300 | function RefreshMcmPage(SKI_ConfigBase mcm, string modName, string pageName) global 301 | McmRecorder_McmFields.MarkMcmOptionsForReset() 302 | if HasModBeenPlayed(modName) 303 | mcm.CloseConfig() 304 | else 305 | AddModPlayed(modName) 306 | endIf 307 | mcm.OpenConfig() 308 | mcm.SetPage(pageName, mcm.Pages.Find(pageName)) 309 | endFunction 310 | 311 | function SetIsPlayingRecording(bool running = true) global 312 | JDB.solveIntSetter(McmRecorder_JDB.JdbPath_IsPlayingRecording(), running as int, createMissingKeys = true) 313 | endFunction 314 | 315 | function SetCurrentlyPlayingRecordingName(string recordingName) global 316 | JDB.solveStrSetter(McmRecorder_JDB.JdbPath_PlayingRecordingName(), recordingName, createMissingKeys = true) 317 | endFunction 318 | 319 | string function GetCurrentlyPlayingRecordingName() global 320 | return JDB.solveStr(McmRecorder_JDB.JdbPath_PlayingRecordingName()) 321 | endFunction 322 | 323 | function SetCurrentlyPlayingSteps(int steps) global 324 | JDB.solveObjSetter(McmRecorder_JDB.JdbPath_PlayingRecordingSteps(), steps, createMissingKeys = true) 325 | endFunction 326 | 327 | int function GetCurrentlyPlayingSteps() global 328 | return JDB.solveObj(McmRecorder_JDB.JdbPath_PlayingRecordingSteps()) 329 | endFunction 330 | 331 | function SetCurrentlyPlayingStepFilename(string stepFilename) global 332 | JDB.solveStrSetter(McmRecorder_JDB.JdbPath_PlayingStepFilename(), stepFilename, createMissingKeys = true) 333 | endFunction 334 | 335 | string function GetCurrentlyPlayingStepFilename() global 336 | return JDB.solveStr(McmRecorder_JDB.JdbPath_PlayingStepFilename()) 337 | endFunction 338 | 339 | function SetCurrentlyPlayingStepIndex(int stepIndex) global 340 | JDB.solveIntSetter(McmRecorder_JDB.JdbPath_PlayingStepIndex(), stepIndex, createMissingKeys = true) 341 | endFunction 342 | 343 | int function GetCurrentlyPlayingStepIndex() global 344 | return JDB.solveInt(McmRecorder_JDB.JdbPath_PlayingStepIndex()) 345 | endFunction 346 | 347 | string function GetCurrentPlayingRecordingModName() global 348 | return JDB.solveStr(McmRecorder_JDB.JdbPath_PlayingRecordingModName()) 349 | endFunction 350 | 351 | function SetCurrentPlayingRecordingModName(string modName) global 352 | JDB.solveStrSetter(McmRecorder_JDB.JdbPath_PlayingRecordingModName(), modName , createMissingKeys = true) 353 | endFunction 354 | 355 | string function GetCurrentPlayingRecordingModPageName() global 356 | return JDB.solveStr(McmRecorder_JDB.JdbPath_PlayingRecordingModPageName()) 357 | endFunction 358 | 359 | function SetCurrentPlayingRecordingModPageName(string pageName) global 360 | JDB.solveStrSetter(McmRecorder_JDB.JdbPath_PlayingRecordingModPageName(), pageName , createMissingKeys = true) 361 | endFunction 362 | 363 | int function GetModsPlayed() global 364 | int modsPlayed = JDB.solveObj(McmRecorder_JDB.JdbPath_PlayingRecordingModsPlayed()) 365 | if ! modsPlayed 366 | modsPlayed = JMap.object() 367 | JDB.solveObjSetter(McmRecorder_JDB.JdbPath_PlayingRecordingModsPlayed(), modsPlayed, createMissingKeys = true) 368 | endIf 369 | return modsPlayed 370 | endFunction 371 | 372 | function AddModPlayed(string modName) global 373 | JMap.setInt(GetModsPlayed(), modName, 1) 374 | endFunction 375 | 376 | bool function HasModBeenPlayed(string modName) global 377 | return JMap.getInt(GetModsPlayed(), modName) 378 | endFunction 379 | 380 | bool function ClearModsPlayed() global 381 | JDB.solveObjSetter(McmRecorder_JDB.JdbPath_PlayingRecordingModsPlayed(), 0) 382 | endFunction 383 | 384 | string function GetCurrentlySkippingModName() global 385 | return JDB.solveStr(McmRecorder_JDB.JdbPath_CurrentlySkippingModName()) 386 | endFunction 387 | 388 | int function GetAutorunHistory() global 389 | int history = JDB.solveObj(McmRecorder_JDB.JdbPath_AutorunHistory()) 390 | if ! history 391 | history = JMap.object() 392 | JDB.solveObjSetter(McmRecorder_JDB.JdbPath_AutorunHistory(), history, createMissingKeys = true) 393 | endIf 394 | return history 395 | endFunction 396 | 397 | bool function HasBeenAutorun(string recordingName) global 398 | return JMap.getInt(GetAutorunHistory(), recordingName) 399 | endFunction 400 | 401 | function MarkHasBeenAutorun(string recordingName) global 402 | JMap.setInt(GetAutorunHistory(), recordingName, 1) 403 | endFunction 404 | 405 | function SetCurrentlySkippingModName(string modName) global 406 | JDB.solveStrSetter(McmRecorder_JDB.JdbPath_CurrentlySkippingModName(), modName, createMissingKeys = true) 407 | endFunction 408 | 409 | int function FindOption(SKI_ConfigBase mcm, string modName, string pageName, string optionType, string selector, string wildcard, int index, string side, float searchTimeout, float searchInterval, float searchPageLoadTime) global 410 | int foundOption 411 | float startTime = Utility.GetCurrentRealTime() 412 | while (! foundOption) && (Utility.GetCurrentRealTime() - startTime) < searchTimeout 413 | foundOption = AttemptFindOption(mcm, modName, pageName, optionType, selector, wildcard, index, side, searchInterval, searchPageLoadTime) 414 | if ! foundOption ; Does this ever run? 415 | McmRecorder_UI.Notification(modName + ": " + pageName + " (search for " + selector + ")") 416 | Utility.WaitMenuMode(searchInterval) 417 | endIf 418 | endWhile 419 | return foundOption 420 | endFunction 421 | 422 | int function AttemptFindOption(SKI_ConfigBase mcm, string modName, string pageName, string optionType, string selector, string wildcard, int index, string side, float searchInterval, float searchPageLoadTime) global 423 | float startTime = Utility.GetCurrentRealTime() 424 | while (Utility.GetCurrentRealTime() - startTime) < searchPageLoadTime 425 | int options = McmRecorder_McmFields.OptionsForModPage_ByOptionType(modName, pageName, optionType) 426 | int optionsCount = JArray.count(options) 427 | int matchCount = 0 428 | int i = 0 429 | while i < optionsCount 430 | int option = JArray.getObj(options, i) 431 | 432 | string optionText = JMap.getStr(option, "text") 433 | if side == "right" 434 | optionText = JMap.getStr(option, "strValue") 435 | endIf 436 | 437 | bool matches 438 | if wildcard 439 | matches = StringUtil.Find(optionText, wildcard) > -1 440 | else 441 | matches = optionText == selector 442 | endIf 443 | 444 | if matches 445 | matchCount += 1 446 | if index > -1 ; This must be the Nth one on the page 447 | if index == matchCount 448 | return option 449 | endIf 450 | else 451 | return option 452 | endIf 453 | endIf 454 | 455 | i += 1 456 | endWhile 457 | 458 | Utility.WaitMenuMode(searchInterval) 459 | 460 | RefreshMcmPage(mcm, modName, pageName) ; Wasn't on the page! Let's refresh the page. 461 | 462 | string debugPrefix = "[Play Action] " + modName 463 | if pageName 464 | debugPrefix += ": " + pageName 465 | endIf 466 | debugPrefix += " (" + selector + ")" 467 | if index > -1 468 | debugPrefix += " [" + index + "]" 469 | endIf 470 | 471 | if (Utility.GetCurrentRealTime() - startTime) >= 4 ; Every 4 seconds print an update 472 | McmRecorder_Logging.ConsoleOut(debugPrefix + " Searching for MCM option...") 473 | McmRecorder_UI.Notification(modName + ": " + pageName + " (search for " + selector + ")") 474 | endIf 475 | endWhile 476 | 477 | return 0 478 | endFunction 479 | -------------------------------------------------------------------------------- /Scripts/Source/SKI_ConfigBase.psc: -------------------------------------------------------------------------------- 1 | scriptName SKI_ConfigBase extends SKI_QuestBase 2 | 3 | ;-- Properties -------------------------------------- 4 | string property PageNameOrDefault 5 | string function get() 6 | if _currentPage 7 | return _currentPage 8 | else 9 | return "SKYUI_DEFAULT_PAGE" 10 | endIf 11 | endFunction 12 | endProperty 13 | String property CurrentPage 14 | String function get() 15 | 16 | return _currentPage 17 | endFunction 18 | endproperty 19 | Int property OPTION_TYPE_INPUT 20 | Int function get() 21 | 22 | return 8 23 | endFunction 24 | endproperty 25 | Int property OPTION_TYPE_KEYMAP 26 | Int function get() 27 | 28 | return 7 29 | endFunction 30 | endproperty 31 | String property MENU_ROOT 32 | String function get() 33 | 34 | return "_root.ConfigPanelFader.configPanel" 35 | endFunction 36 | endproperty 37 | Int property LEFT_TO_RIGHT 38 | Int function get() 39 | 40 | return 1 41 | endFunction 42 | endproperty 43 | Int property OPTION_FLAG_WITH_UNMAP 44 | Int function get() 45 | 46 | return 4 47 | endFunction 48 | endproperty 49 | Int property OPTION_TYPE_HEADER 50 | Int function get() 51 | 52 | return 1 53 | endFunction 54 | endproperty 55 | Int property TOP_TO_BOTTOM 56 | Int function get() 57 | 58 | return 2 59 | endFunction 60 | endproperty 61 | Int property OPTION_TYPE_EMPTY 62 | Int function get() 63 | 64 | return 0 65 | endFunction 66 | endproperty 67 | Int property OPTION_TYPE_TOGGLE 68 | Int function get() 69 | 70 | return 3 71 | endFunction 72 | endproperty 73 | Int property STATE_RESET 74 | Int function get() 75 | 76 | return 1 77 | endFunction 78 | endproperty 79 | Int property OPTION_FLAG_DISABLED 80 | Int function get() 81 | 82 | return 1 83 | endFunction 84 | endproperty 85 | String[] property Pages auto 86 | Int property STATE_COLOR 87 | Int function get() 88 | 89 | return 4 90 | endFunction 91 | endproperty 92 | String property JOURNAL_MENU 93 | String function get() 94 | 95 | return "Journal Menu" 96 | endFunction 97 | endproperty 98 | Int property OPTION_TYPE_MENU 99 | Int function get() 100 | 101 | return 5 102 | endFunction 103 | endproperty 104 | Int property OPTION_FLAG_HIDDEN 105 | Int function get() 106 | 107 | return 2 108 | endFunction 109 | endproperty 110 | Int property OPTION_TYPE_TEXT 111 | Int function get() 112 | 113 | return 2 114 | endFunction 115 | endproperty 116 | Int property STATE_INPUT 117 | Int function get() 118 | 119 | return 5 120 | endFunction 121 | endproperty 122 | Int property STATE_MENU 123 | Int function get() 124 | 125 | return 3 126 | endFunction 127 | endproperty 128 | Int property OPTION_FLAG_NONE 129 | Int function get() 130 | 131 | return 0 132 | endFunction 133 | endproperty 134 | Int property OPTION_TYPE_SLIDER 135 | Int function get() 136 | 137 | return 4 138 | endFunction 139 | endproperty 140 | Int property OPTION_TYPE_COLOR 141 | Int function get() 142 | 143 | return 6 144 | endFunction 145 | endproperty 146 | String property ModName auto 147 | Int property STATE_SLIDER 148 | Int function get() 149 | 150 | return 2 151 | endFunction 152 | endproperty 153 | Int property STATE_DEFAULT 154 | Int function get() 155 | 156 | return 0 157 | endFunction 158 | endproperty 159 | 160 | ;-- Variables --------------------------------------- 161 | Bool _messageResult = false 162 | String _infoText 163 | Int _currentPageNum = 0 164 | String[] _textBuf 165 | Int _configID = -1 166 | SKI_ConfigManager _configManager 167 | Int[] _optionFlagsBuf 168 | String[] _strValueBuf 169 | String _inputStartText 170 | Int property _activeOption = -1 auto 171 | Int _state = 0 172 | String _currentPage = "" 173 | Bool _initialized = false 174 | Int[] _colorParams 175 | Float[] _sliderParams 176 | Int[] _menuParams 177 | Int _cursorFillMode = 1 178 | String[] _stateOptionMap 179 | Bool _waitForMessage = false 180 | Float[] _numValueBuf 181 | Int _cursorPosition = 0 182 | 183 | ;-- Functions --------------------------------------- 184 | 185 | function SetTitleText(String a_text) 186 | if ! McmRecorder_Player.IsPlayingRecording() 187 | ui.InvokeString(self.JOURNAL_MENU, self.MENU_ROOT + ".setTitleText", a_text) 188 | endIf 189 | endFunction 190 | 191 | function SetOptionFlagsST(Int a_flags, Bool a_noUpdate, String a_stateName) 192 | 193 | if _state == self.STATE_RESET 194 | self.Error("Cannot set option flags while in OnPageReset(). Pass flags to AddOption instead") 195 | return 196 | endIf 197 | Int index = self.GetStateOptionIndex(a_stateName) 198 | if index < 0 199 | self.Error("Cannot use SetOptionFlagsST outside a valid option state") 200 | return 201 | endIf 202 | self.SetOptionFlags(index, a_flags, a_noUpdate) 203 | endFunction 204 | 205 | function OnSelectST() 206 | {Called when a non-interactive state option has been selected} 207 | 208 | ; Empty function 209 | endFunction 210 | 211 | function OnHighlightST() 212 | {Called when highlighting a state option} 213 | 214 | ; Empty function 215 | endFunction 216 | 217 | function AddSliderOptionST(String a_stateName, String a_text, Float a_value, String a_formatString, Int a_flags) 218 | 219 | self.AddOptionST(a_stateName, self.OPTION_TYPE_SLIDER, a_text, a_formatString, a_value, a_flags) 220 | endFunction 221 | 222 | function OnConfigClose() 223 | {Called when this config menu is closed} 224 | 225 | ; Empty function 226 | endFunction 227 | 228 | function SetSliderDialogRange(Float a_minValue, Float a_maxValue) 229 | 230 | if _state != self.STATE_SLIDER 231 | self.Error("Cannot set slider dialog params while outside OnOptionSliderOpen()") 232 | return 233 | endIf 234 | _sliderParams[2] = a_minValue 235 | _sliderParams[3] = a_maxValue 236 | endFunction 237 | 238 | function SetKeyMapOptionValue(Int a_option, Int a_keyCode, Bool a_noUpdate) 239 | 240 | Int index = a_option % 256 241 | Int type = _optionFlagsBuf[index] % 256 242 | if type != self.OPTION_TYPE_KEYMAP 243 | Int pageIdx = a_option / 256 - 1 244 | if pageIdx != -1 245 | self.Error("Option type mismatch. Expected keymap option, page \"" + Pages[pageIdx] + "\", index " + index as String) 246 | else 247 | self.Error("Option type mismatch. Expected keymap option, page \"\", index " + index as String) 248 | endIf 249 | return 250 | endIf 251 | self.SetOptionNumValue(index, a_keyCode as Float, a_noUpdate) 252 | endFunction 253 | 254 | function SetKeyMapOptionValueST(Int a_keyCode, Bool a_noUpdate, String a_stateName) 255 | 256 | Int index = self.GetStateOptionIndex(a_stateName) 257 | if index < 0 258 | self.Error("Cannot use SetKeyMapOptionValueST outside a valid option state") 259 | return 260 | endIf 261 | self.SetKeyMapOptionValue(index, a_keyCode, a_noUpdate) 262 | endFunction 263 | 264 | function OnInit() 265 | 266 | self.OnGameReload() 267 | endFunction 268 | 269 | function OnOptionMenuAccept(Int a_option, Int a_index) 270 | {Called when a menu entry has been accepted} 271 | 272 | ; Empty function 273 | endFunction 274 | 275 | function OnOptionSliderOpen(Int a_option) 276 | {Called when a slider option has been selected} 277 | 278 | ; Empty function 279 | endFunction 280 | 281 | function OnPageReset(String a_page) 282 | {Called when a new page is selected, including the initial empty page} 283 | 284 | ; Empty function 285 | endFunction 286 | 287 | function SetTextOptionValueST(String a_value, Bool a_noUpdate, String a_stateName) 288 | 289 | Int index = self.GetStateOptionIndex(a_stateName) 290 | if index < 0 291 | self.Error("Cannot use SetTextOptionValueST outside a valid option state") 292 | return 293 | endIf 294 | self.SetTextOptionValue(index, a_value, a_noUpdate) 295 | endFunction 296 | 297 | function OnMenuOpenST() 298 | {Called when a menu state option has been selected} 299 | 300 | ; Empty function 301 | endFunction 302 | 303 | function OnConfigManagerReady(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 304 | 305 | SKI_ConfigManager newManager = a_sender as SKI_ConfigManager 306 | if _configManager == newManager || newManager == none 307 | return 308 | endIf 309 | _configID = newManager.RegisterMod(self, ModName) 310 | if _configID >= 0 311 | _configManager = newManager 312 | self.OnConfigRegister() 313 | debug.Trace(self as String + ": Registered " + ModName + " at MCM.", 0) 314 | endIf 315 | endFunction 316 | 317 | function SetInfoText(String a_text) 318 | 319 | _infoText = a_text 320 | endFunction 321 | 322 | function SetInputDialogStartText(String a_text) 323 | 324 | if _state != self.STATE_INPUT 325 | self.Error("Cannot set input dialog params while outside OnOptionInputOpen()") 326 | return 327 | endIf 328 | _inputStartText = a_text 329 | endFunction 330 | 331 | function Error(String a_msg) 332 | 333 | debug.Trace(self as String + " ERROR: " + a_msg, 0) 334 | endFunction 335 | 336 | function SetTextOptionValue(Int a_option, String a_value, Bool a_noUpdate) 337 | 338 | Int index = a_option % 256 339 | Int type = _optionFlagsBuf[index] % 256 340 | if type != self.OPTION_TYPE_TEXT 341 | Int pageIdx = a_option / 256 - 1 342 | if pageIdx != -1 343 | self.Error("Option type mismatch. Expected text option, page \"" + Pages[pageIdx] + "\", index " + index as String) 344 | else 345 | self.Error("Option type mismatch. Expected text option, page \"\", index " + index as String) 346 | endIf 347 | return 348 | endIf 349 | self.SetOptionStrValue(index, a_value, a_noUpdate) 350 | endFunction 351 | 352 | function SetInputText(String a_text) 353 | 354 | String optionState = _stateOptionMap[_activeOption % 256] 355 | if optionState != "" 356 | String oldState = self.GetState() 357 | self.GotoState(optionState) 358 | self.OnInputAcceptST(a_text) 359 | self.GotoState(oldState) 360 | McmRecorder_Recorder.RecordAction(self, ModName, PageNameOrDefault, "input", _activeOption, strValue = a_text, recordStringValue = true, recordOptionType = true) 361 | else 362 | self.OnOptionInputAccept(_activeOption, a_text) 363 | McmRecorder_Recorder.RecordAction(self, ModName, PageNameOrDefault, "input", _activeOption, strValue = a_text, recordStringValue = true, recordOptionType = true) 364 | endIf 365 | _activeOption = -1 366 | endFunction 367 | 368 | function AddInputOptionST(String a_stateName, String a_text, String a_value, Int a_flags) 369 | 370 | self.AddOptionST(a_stateName, self.OPTION_TYPE_INPUT, a_text, a_value, 0 as Float, a_flags) 371 | endFunction 372 | 373 | function AddKeyMapOptionST(String a_stateName, String a_text, Int a_keyCode, Int a_flags) 374 | self.AddOptionST(a_stateName, self.OPTION_TYPE_KEYMAP, a_text, none, a_keyCode as Float, a_flags) 375 | endFunction 376 | 377 | function OnKeyMapChangeST(Int a_keyCode, String a_conflictControl, String a_conflictName) 378 | {Called when a key has been remapped for this state option} 379 | 380 | ; Empty function 381 | endFunction 382 | 383 | function OnConfigManagerReset(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 384 | 385 | _configManager = none 386 | endFunction 387 | 388 | Int function AddInputOption(String a_text, String a_value, Int a_flags) 389 | 390 | return self.AddOption(self.OPTION_TYPE_INPUT, a_text, a_value, 0 as Float, a_flags) 391 | endFunction 392 | 393 | function SelectOption(Int a_index) 394 | String optionState = _stateOptionMap[a_index] 395 | if optionState != "" 396 | String oldState = self.GetState() 397 | self.GotoState(optionState) 398 | self.OnSelectST() 399 | self.GotoState(oldState) 400 | McmRecorder_Recorder.RecordAction(self, ModName, PageNameOrDefault, "clickable", (a_index + _currentPageNum * 256), stateName = optionState) 401 | else 402 | Int option = a_index + _currentPageNum * 256 403 | McmRecorder_Recorder.RecordAction(self, ModName, PageNameOrDefault, "clickable", option) 404 | self.OnOptionSelect(option) 405 | endIf 406 | endFunction 407 | 408 | function SetInputOptionValueST(String a_value, Bool a_noUpdate, String a_stateName) 409 | 410 | Int index = self.GetStateOptionIndex(a_stateName) 411 | if index < 0 412 | self.Error("Cannot use SetInputOptionValueST outside a valid option state") 413 | return 414 | endIf 415 | self.SetInputOptionValue(index, a_value, a_noUpdate) 416 | endFunction 417 | 418 | ; Skipped compiler generated GotoState 419 | 420 | function SetOptionFlags(Int a_option, Int a_flags, Bool a_noUpdate) 421 | 422 | if _state == self.STATE_RESET 423 | self.Error("Cannot set option flags while in OnPageReset(). Pass flags to AddOption instead") 424 | return 425 | endIf 426 | Int index = a_option % 256 427 | Int oldFlags = _optionFlagsBuf[index] 428 | oldFlags %= 256 429 | oldFlags += a_flags * 256 430 | Int[] params = new Int[2] 431 | params[0] = index 432 | params[1] = a_flags 433 | if ! McmRecorder_Player.IsPlayingRecording() 434 | ui.InvokeIntA(self.JOURNAL_MENU, self.MENU_ROOT + ".setOptionFlags", params) 435 | endIf 436 | if !a_noUpdate && ! McmRecorder_Player.IsPlayingRecording() 437 | ui.Invoke(self.JOURNAL_MENU, self.MENU_ROOT + ".invalidateOptionData") 438 | endIf 439 | endFunction 440 | 441 | function SetPage(String a_page, Int a_index) 442 | _currentPage = a_page 443 | _currentPageNum = 1 + a_index 444 | if ! McmRecorder_Player.IsPlayingRecording() 445 | if a_page != "" 446 | self.SetTitleText(a_page) 447 | else 448 | self.SetTitleText(ModName) 449 | endIf 450 | endIf 451 | self.ClearOptionBuffers() 452 | _state = self.STATE_RESET 453 | self.OnPageReset(a_page) 454 | _state = self.STATE_DEFAULT 455 | self.WriteOptionBuffers() 456 | endFunction 457 | 458 | function SetInputOptionValue(Int a_option, String a_value, Bool a_noUpdate) 459 | 460 | Int index = a_option % 256 461 | Int type = _optionFlagsBuf[index] % 256 462 | if type != self.OPTION_TYPE_INPUT 463 | Int pageIdx = a_option / 256 - 1 464 | if pageIdx != -1 465 | self.Error("Option type mismatch. Expected input option, page \"" + Pages[pageIdx] + "\", index " + index as String) 466 | else 467 | self.Error("Option type mismatch. Expected input option, page \"\", index " + index as String) 468 | endIf 469 | return 470 | endIf 471 | self.SetOptionStrValue(index, a_value, a_noUpdate) 472 | endFunction 473 | 474 | Int function AddHeaderOption(String a_text, Int a_flags) 475 | 476 | return self.AddOption(self.OPTION_TYPE_HEADER, a_text, none, 0 as Float, a_flags) 477 | endFunction 478 | 479 | function OnGameReload() 480 | if !_initialized 481 | _initialized = true 482 | _sliderParams = new Float[5] 483 | _menuParams = new Int[2] 484 | _colorParams = new Int[2] 485 | self.OnConfigInit() 486 | debug.Trace(self as String + " INITIALIZED", 0) 487 | endIf 488 | self.RegisterForModEvent("SKICP_configManagerReady", "OnConfigManagerReady") 489 | self.RegisterForModEvent("SKICP_configManagerReset", "OnConfigManagerReset") 490 | self.CheckVersion() 491 | endFunction 492 | 493 | function OnSliderOpenST() 494 | {Called when a slider state option has been selected} 495 | 496 | ; Empty function 497 | endFunction 498 | 499 | function SetColorDialogStartColor(Int a_color) 500 | 501 | if _state != self.STATE_COLOR 502 | self.Error("Cannot set color dialog params while outside OnOptionColorOpen()") 503 | return 504 | endIf 505 | _colorParams[0] = a_color 506 | endFunction 507 | 508 | String function GetCustomControl(Int a_keyCode) 509 | {Returns the name of a custom control mapped to given keyCode, or "" if the key is not in use by this config} 510 | 511 | return "" 512 | endFunction 513 | 514 | Int function GetVersion() 515 | {Returns version of this script} 516 | 517 | return 1 518 | endFunction 519 | 520 | function OnOptionSliderAccept(Int a_option, Float a_value) 521 | {Called when a new slider value has been accepted} 522 | 523 | ; Empty function 524 | endFunction 525 | 526 | Int function AddKeyMapOption(String a_text, Int a_keyCode, Int a_flags) 527 | 528 | return self.AddOption(self.OPTION_TYPE_KEYMAP, a_text, none, a_keyCode as Float, a_flags) 529 | endFunction 530 | 531 | function OnInputOpenST() 532 | {Called when a text input state option has been selected} 533 | 534 | ; Empty function 535 | endFunction 536 | 537 | function SetColorValue(Int a_color) 538 | 539 | String optionState = _stateOptionMap[_activeOption % 256] 540 | if optionState != "" 541 | String oldState = self.GetState() 542 | self.GotoState(optionState) 543 | self.OnColorAcceptST(a_color) 544 | self.GotoState(oldState) 545 | McmRecorder_Recorder.RecordAction(self, ModName, PageNameOrDefault, "color", _activeOption, stateName = optionState, recordOptionType = true, fltValue = a_color, recordFloatValue = true) 546 | else 547 | self.OnOptionColorAccept(_activeOption, a_color) 548 | McmRecorder_Recorder.RecordAction(self, ModName, PageNameOrDefault, "color", _activeOption, recordOptionType = true, fltValue = a_color, recordFloatValue = true) 549 | endIf 550 | _activeOption = -1 551 | endFunction 552 | 553 | function SetMenuDialogStartIndex(Int a_value) 554 | 555 | if _state != self.STATE_MENU 556 | self.Error("Cannot set menu dialog params while outside OnOptionMenuOpen()") 557 | return 558 | endIf 559 | _menuParams[0] = a_value 560 | endFunction 561 | 562 | function ForcePageReset() 563 | {Forces a full reset of the current page} 564 | 565 | if McmRecorder_Player.IsPlayingRecording() 566 | CloseConfig() 567 | OpenConfig() 568 | Debug.Trace("[McmRecorder] ForcePageReset() for page " + CurrentPage) 569 | SetPage(CurrentPage, Pages.Find(CurrentPage)) 570 | else 571 | ui.Invoke(self.JOURNAL_MENU, self.MENU_ROOT + ".forcePageReset") 572 | endIf 573 | endFunction 574 | 575 | function HighlightOption(Int a_index) 576 | 577 | _infoText = "" 578 | if a_index != -1 579 | String optionState = _stateOptionMap[a_index] 580 | if optionState != "" 581 | String oldState = self.GetState() 582 | self.GotoState(optionState) 583 | self.OnHighlightST() 584 | self.GotoState(oldState) 585 | else 586 | Int option = a_index + _currentPageNum * 256 587 | self.OnOptionHighlight(option) 588 | endIf 589 | endIf 590 | if ! McmRecorder_Player.IsPlayingRecording() 591 | ui.InvokeString(self.JOURNAL_MENU, self.MENU_ROOT + ".setInfoText", _infoText) 592 | endIf 593 | endFunction 594 | 595 | function SetOptionStrValue(Int a_index, String a_strValue, Bool a_noUpdate) 596 | 597 | if _state == self.STATE_RESET 598 | self.Error("Cannot modify option data while in OnPageReset()") 599 | return 600 | endIf 601 | String menu = self.JOURNAL_MENU 602 | String root = self.MENU_ROOT 603 | if ! McmRecorder_Player.IsPlayingRecording() 604 | ui.SetInt(menu, root + ".optionCursorIndex", a_index) 605 | ui.SetString(menu, root + ".optionCursor.strValue", a_strValue) 606 | endIf 607 | if !a_noUpdate && !McmRecorder_Player.IsPlayingRecording() 608 | ui.Invoke(menu, root + ".invalidateOptionData") 609 | endIf 610 | endFunction 611 | 612 | Int function GetStateOptionIndex(String a_stateName) 613 | 614 | if a_stateName == "" 615 | a_stateName = self.GetState() 616 | endIf 617 | if a_stateName == "" 618 | return -1 619 | endIf 620 | return _stateOptionMap.find(a_stateName, 0) 621 | endFunction 622 | 623 | function SetSliderDialogDefaultValue(Float a_value) 624 | 625 | if _state != self.STATE_SLIDER 626 | self.Error("Cannot set slider dialog params while outside OnOptionSliderOpen()") 627 | return 628 | endIf 629 | _sliderParams[1] = a_value 630 | endFunction 631 | 632 | function ResetOption(Int a_index) 633 | 634 | String optionState = _stateOptionMap[a_index] 635 | if optionState != "" 636 | String oldState = self.GetState() 637 | self.GotoState(optionState) 638 | self.OnDefaultST() 639 | self.GotoState(oldState) 640 | else 641 | Int option = a_index + _currentPageNum * 256 642 | self.OnOptionDefault(option) 643 | endIf 644 | endFunction 645 | 646 | function OnColorOpenST() 647 | {Called when a color state option has been selected} 648 | 649 | ; Empty function 650 | endFunction 651 | 652 | function RequestInputDialogData(Int a_index) 653 | 654 | _activeOption = a_index + _currentPageNum * 256 655 | _inputStartText = "" 656 | _state = self.STATE_INPUT 657 | String optionState = _stateOptionMap[a_index] 658 | if optionState != "" 659 | String oldState = self.GetState() 660 | self.GotoState(optionState) 661 | self.OnInputOpenST() 662 | self.GotoState(oldState) 663 | else 664 | self.OnOptionInputOpen(_activeOption) 665 | endIf 666 | _state = self.STATE_DEFAULT 667 | if ! McmRecorder_Player.IsPlayingRecording() 668 | ui.InvokeString(self.JOURNAL_MENU, self.MENU_ROOT + ".setInputDialogParams", _inputStartText) 669 | endIf 670 | endFunction 671 | 672 | function OnOptionInputOpen(Int a_option) 673 | {Called when a text input option has been selected} 674 | 675 | ; Empty function 676 | endFunction 677 | 678 | function SetMenuIndex(Int a_index) 679 | 680 | String optionState = _stateOptionMap[_activeOption % 256] 681 | if optionState != "" 682 | String oldState = self.GetState() 683 | self.GotoState(optionState) 684 | self.OnMenuAcceptST(a_index) 685 | self.GotoState(oldState) 686 | McmRecorder_Recorder.RecordAction(self, ModName, PageNameOrDefault, "menu", _activeOption, stateName = optionState, fltValue = a_index, recordFloatValue = true, recordOptionType = true, menuOptions = MostRecentlyConfiguredMenuDialogOptions) 687 | else 688 | self.OnOptionMenuAccept(_activeOption, a_index) 689 | McmRecorder_Recorder.RecordAction(self, ModName, PageNameOrDefault, "menu", _activeOption, fltValue = a_index, recordFloatValue = true, recordOptionType = true, menuOptions = MostRecentlyConfiguredMenuDialogOptions) 690 | endIf 691 | _activeOption = -1 692 | endFunction 693 | 694 | function OnOptionColorOpen(Int a_option) 695 | {Called when a color option has been selected} 696 | 697 | ; Empty function 698 | endFunction 699 | 700 | function SetSliderValue(Float a_value) 701 | 702 | String optionState = _stateOptionMap[_activeOption % 256] 703 | if optionState != "" 704 | String oldState = self.GetState() 705 | self.GotoState(optionState) 706 | self.OnSliderAcceptST(a_value) 707 | self.GotoState(oldState) 708 | McmRecorder_Recorder.RecordAction(self, ModName, PageNameOrDefault, "slider", _activeOption, stateName = optionState, fltValue = a_value, recordFloatValue = true, recordOptionType = true) 709 | else 710 | self.OnOptionSliderAccept(_activeOption, a_value) 711 | McmRecorder_Recorder.RecordAction(self, ModName, PageNameOrDefault, "slider", _activeOption, fltValue = a_value, recordFloatValue = true, recordOptionType = true) 712 | endIf 713 | _activeOption = -1 714 | endFunction 715 | 716 | function OnConfigRegister() 717 | {Called when this config menu registered at the control panel} 718 | 719 | ; Empty function 720 | endFunction 721 | 722 | function SetSliderOptionValueST(Float a_value, String a_formatString, Bool a_noUpdate, String a_stateName) 723 | 724 | Int index = self.GetStateOptionIndex(a_stateName) 725 | if index < 0 726 | self.Error("Cannot use SetSliderOptionValueST outside a valid option state") 727 | return 728 | endIf 729 | self.SetSliderOptionValue(index, a_value, a_formatString, a_noUpdate) 730 | endFunction 731 | 732 | function RequestMenuDialogData(Int a_index) 733 | 734 | _activeOption = a_index + _currentPageNum * 256 735 | _menuParams[0] = -1 736 | _menuParams[1] = -1 737 | _state = self.STATE_MENU 738 | String optionState = _stateOptionMap[a_index] 739 | if optionState != "" 740 | String oldState = self.GetState() 741 | self.GotoState(optionState) 742 | self.OnMenuOpenST() 743 | self.GotoState(oldState) 744 | else 745 | self.OnOptionMenuOpen(_activeOption) 746 | endIf 747 | _state = self.STATE_DEFAULT 748 | if ! McmRecorder_Player.IsPlayingRecording() 749 | ui.InvokeIntA(self.JOURNAL_MENU, self.MENU_ROOT + ".setMenuDialogParams", _menuParams) 750 | endIf 751 | endFunction 752 | 753 | function OnOptionSelect(Int a_option) 754 | {Called when a non-interactive option has been selected} 755 | 756 | ; Empty function 757 | endFunction 758 | 759 | function RequestSliderDialogData(Int a_index) 760 | 761 | _activeOption = a_index + _currentPageNum * 256 762 | _sliderParams[0] = 0 as Float 763 | _sliderParams[1] = 0 as Float 764 | _sliderParams[2] = 0 as Float 765 | _sliderParams[3] = 1 as Float 766 | _sliderParams[4] = 1 as Float 767 | _state = self.STATE_SLIDER 768 | String optionState = _stateOptionMap[a_index] 769 | if optionState != "" 770 | String oldState = self.GetState() 771 | self.GotoState(optionState) 772 | self.OnSliderOpenST() 773 | self.GotoState(oldState) 774 | else 775 | self.OnOptionSliderOpen(_activeOption) 776 | endIf 777 | _state = self.STATE_DEFAULT 778 | if ! McmRecorder_Player.IsPlayingRecording() 779 | ui.InvokeFloatA(self.JOURNAL_MENU, self.MENU_ROOT + ".setSliderDialogParams", _sliderParams) 780 | endIf 781 | endFunction 782 | 783 | function OnMessageDialogClose(String a_eventName, String a_strArg, Float a_numArg, Form a_sender) 784 | 785 | _messageResult = a_numArg as Bool 786 | _waitForMessage = false 787 | endFunction 788 | 789 | function SetOptionValues(Int a_index, String a_strValue, Float a_numValue, Bool a_noUpdate) 790 | 791 | if _state == self.STATE_RESET 792 | self.Error("Cannot modify option data while in OnPageReset()") 793 | return 794 | endIf 795 | String menu = self.JOURNAL_MENU 796 | String root = self.MENU_ROOT 797 | if ! McmRecorder_Player.IsPlayingRecording() 798 | ui.SetInt(menu, root + ".optionCursorIndex", a_index) 799 | ui.SetString(menu, root + ".optionCursor.strValue", a_strValue) 800 | ui.SetFloat(menu, root + ".optionCursor.numValue", a_numValue) 801 | endIf 802 | if !a_noUpdate && !McmRecorder_Player.IsPlayingRecording() 803 | ui.Invoke(menu, root + ".invalidateOptionData") 804 | endIf 805 | endFunction 806 | 807 | function SetOptionNumValue(Int a_index, Float a_numValue, Bool a_noUpdate) 808 | 809 | if _state == self.STATE_RESET 810 | self.Error("Cannot modify option data while in OnPageReset()") 811 | return 812 | endIf 813 | String menu = self.JOURNAL_MENU 814 | String root = self.MENU_ROOT 815 | if ! McmRecorder_Player.IsPlayingRecording() 816 | ui.SetInt(menu, root + ".optionCursorIndex", a_index) 817 | ui.SetFloat(menu, root + ".optionCursor.numValue", a_numValue) 818 | endIf 819 | if !a_noUpdate && !McmRecorder_Player.IsPlayingRecording() 820 | ui.Invoke(menu, root + ".invalidateOptionData") 821 | endIf 822 | endFunction 823 | 824 | function ClearOptionBuffers() 825 | if McmRecorder_Player.IsPlayingRecording() || McmRecorder_Recorder.IsRecording() 826 | McmRecorder_McmFields.MarkMcmOptionsForReset() 827 | endIf 828 | 829 | Int t = self.OPTION_TYPE_EMPTY 830 | Int i = 0 831 | while i < 128 832 | _optionFlagsBuf[i] = t 833 | _textBuf[i] = "" 834 | _strValueBuf[i] = "" 835 | _numValueBuf[i] = 0 as Float 836 | _stateOptionMap[i] = "" 837 | i += 1 838 | endWhile 839 | _cursorPosition = 0 840 | _cursorFillMode = self.LEFT_TO_RIGHT 841 | endFunction 842 | 843 | function WriteOptionBuffers() 844 | if McmRecorder_Player.IsPlayingRecording() 845 | return 846 | endIf 847 | 848 | String menu = self.JOURNAL_MENU 849 | String root = self.MENU_ROOT 850 | Int t = self.OPTION_TYPE_EMPTY 851 | Int i = 0 852 | Int optionCount = 0 853 | i = 0 854 | while i < 128 855 | if _optionFlagsBuf[i] != t 856 | optionCount = i + 1 857 | endIf 858 | i += 1 859 | endWhile 860 | if ! McmRecorder_Player.IsPlayingRecording() 861 | ui.InvokeIntA(menu, root + ".setOptionFlagsBuffer", _optionFlagsBuf) 862 | ui.InvokeStringA(menu, root + ".setOptionTextBuffer", _textBuf) 863 | ui.InvokeStringA(menu, root + ".setOptionStrValueBuffer", _strValueBuf) 864 | ui.InvokeFloatA(menu, root + ".setOptionNumValueBuffer", _numValueBuf) 865 | ui.InvokeInt(menu, root + ".flushOptionBuffers", optionCount) 866 | endIf 867 | endFunction 868 | 869 | function SetSliderOptionValue(Int a_option, Float a_value, String a_formatString, Bool a_noUpdate) 870 | 871 | Int index = a_option % 256 872 | Int type = _optionFlagsBuf[index] % 256 873 | if type != self.OPTION_TYPE_SLIDER 874 | Int pageIdx = a_option / 256 - 1 875 | if pageIdx != -1 876 | self.Error("Option type mismatch. Expected slider option, page \"" + Pages[pageIdx] + "\", index " + index as String) 877 | else 878 | self.Error("Option type mismatch. Expected slider option, page \"\", index " + index as String) 879 | endIf 880 | return 881 | endIf 882 | self.SetOptionValues(index, a_formatString, a_value, a_noUpdate) 883 | endFunction 884 | 885 | function AddOptionST(String a_stateName, Int a_optionType, String a_text, String a_strValue, Float a_numValue, Int a_flags) 886 | if _stateOptionMap.find(a_stateName, 0) != -1 887 | self.Error("State option name " + a_stateName + " is already in use") 888 | return 889 | endIf 890 | Int index = self.AddOption(a_optionType, a_text, a_strValue, a_numValue, a_flags, a_stateName) % 256 891 | if index < 0 892 | return 893 | endIf 894 | if _stateOptionMap[index] != "" 895 | self.Error("State option index " + index as String + " already in use") 896 | return 897 | endIf 898 | _stateOptionMap[index] = a_stateName 899 | endFunction 900 | 901 | function AddToggleOptionST(String a_stateName, String a_text, Bool a_checked, Int a_flags) 902 | 903 | self.AddOptionST(a_stateName, self.OPTION_TYPE_TOGGLE, a_text, none, (a_checked as Int) as Float, a_flags) 904 | endFunction 905 | 906 | function UnloadCustomContent() 907 | 908 | if ! McmRecorder_Player.IsPlayingRecording() 909 | ui.Invoke(self.JOURNAL_MENU, self.MENU_ROOT + ".unloadCustomContent") 910 | endIf 911 | endFunction 912 | 913 | function OpenConfig() 914 | 915 | _optionFlagsBuf = new Int[128] 916 | _textBuf = new String[128] 917 | _strValueBuf = new String[128] 918 | _numValueBuf = new Float[128] 919 | _stateOptionMap = new String[128] 920 | self.SetPage("", -1) 921 | self.OnConfigOpen() 922 | if ! McmRecorder_Player.IsPlayingRecording() 923 | ui.InvokeStringA(self.JOURNAL_MENU, self.MENU_ROOT + ".setPageNames", Pages) 924 | endIf 925 | endFunction 926 | 927 | Bool function ShowMessage(String a_message, Bool a_withCancel, String a_acceptLabel, String a_cancelLabel) 928 | if McmRecorder_Player.IsPlayingRecording() 929 | return true ; Automatically accept every dialog without actually showing it :) 930 | endIf 931 | 932 | if _waitForMessage 933 | self.Error("Called ShowMessage() while another message was already open") 934 | return false 935 | endIf 936 | _waitForMessage = true 937 | _messageResult = false 938 | String[] params = new String[3] 939 | params[0] = a_message 940 | params[1] = a_acceptLabel 941 | if a_withCancel 942 | params[2] = a_cancelLabel 943 | else 944 | params[2] = "" 945 | endIf 946 | self.RegisterForModEvent("SKICP_messageDialogClosed", "OnMessageDialogClose") 947 | if ! McmRecorder_Player.IsPlayingRecording() 948 | ui.InvokeStringA(self.JOURNAL_MENU, self.MENU_ROOT + ".showMessageDialog", params) 949 | endIf 950 | while _waitForMessage 951 | utility.WaitMenuMode(0.100000) 952 | endWhile 953 | self.UnregisterForModEvent("SKICP_messageDialogClosed") 954 | return _messageResult 955 | endFunction 956 | 957 | string[] property MostRecentlyConfiguredMenuDialogOptions auto 958 | 959 | function SetMenuDialogOptions(String[] a_options) 960 | MostRecentlyConfiguredMenuDialogOptions = a_options 961 | 962 | if _state != self.STATE_MENU 963 | self.Error("Cannot set menu dialog params while outside OnOptionMenuOpen()") 964 | return 965 | endIf 966 | if ! McmRecorder_Player.IsPlayingRecording() 967 | ui.InvokeStringA(self.JOURNAL_MENU, self.MENU_ROOT + ".setMenuDialogOptions", a_options) 968 | endIf 969 | endFunction 970 | 971 | function OnVersionUpdate(Int a_version) 972 | {Called when a version update of this script has been detected} 973 | 974 | ; Empty function 975 | endFunction 976 | 977 | function SetColorDialogDefaultColor(Int a_color) 978 | 979 | if _state != self.STATE_COLOR 980 | self.Error("Cannot set color dialog params while outside OnOptionColorOpen()") 981 | return 982 | endIf 983 | _colorParams[1] = a_color 984 | endFunction 985 | 986 | function OnSliderAcceptST(Float a_value) 987 | {Called when a new slider state value has been accepted} 988 | 989 | ; Empty function 990 | endFunction 991 | 992 | function OnInputAcceptST(String a_input) 993 | {Called when a new text input has been accepted for this state option} 994 | 995 | ; Empty function 996 | endFunction 997 | 998 | function LoadCustomContent(String a_source, Float a_x, Float a_y) 999 | 1000 | Float[] params = new Float[2] 1001 | params[0] = a_x 1002 | params[1] = a_y 1003 | if ! McmRecorder_Player.IsPlayingRecording() 1004 | ui.InvokeFloatA(self.JOURNAL_MENU, self.MENU_ROOT + ".setCustomContentParams", params) 1005 | ui.InvokeString(self.JOURNAL_MENU, self.MENU_ROOT + ".loadCustomContent", a_source) 1006 | endIf 1007 | endFunction 1008 | 1009 | function RequestColorDialogData(Int a_index) 1010 | 1011 | _activeOption = a_index + _currentPageNum * 256 1012 | _colorParams[0] = -1 1013 | _colorParams[1] = -1 1014 | _state = self.STATE_COLOR 1015 | String optionState = _stateOptionMap[a_index] 1016 | if optionState != "" 1017 | String oldState = self.GetState() 1018 | self.GotoState(optionState) 1019 | self.OnColorOpenST() 1020 | self.GotoState(oldState) 1021 | else 1022 | self.OnOptionColorOpen(_activeOption) 1023 | endIf 1024 | _state = self.STATE_DEFAULT 1025 | if ! McmRecorder_Player.IsPlayingRecording() 1026 | ui.InvokeIntA(self.JOURNAL_MENU, self.MENU_ROOT + ".setColorDialogParams", _colorParams) 1027 | endIf 1028 | endFunction 1029 | 1030 | function SetSliderDialogInterval(Float a_value) 1031 | 1032 | if _state != self.STATE_SLIDER 1033 | self.Error("Cannot set slider dialog params while outside OnOptionSliderOpen()") 1034 | return 1035 | endIf 1036 | _sliderParams[4] = a_value 1037 | endFunction 1038 | 1039 | Int function AddColorOption(String a_text, Int a_color, Int a_flags) 1040 | 1041 | return self.AddOption(self.OPTION_TYPE_COLOR, a_text, none, a_color as Float, a_flags) 1042 | endFunction 1043 | 1044 | function SetColorOptionValueST(Int a_color, Bool a_noUpdate, String a_stateName) 1045 | 1046 | Int index = self.GetStateOptionIndex(a_stateName) 1047 | if index < 0 1048 | self.Error("Cannot use SetColorOptionValueST outside a valid option state") 1049 | return 1050 | endIf 1051 | self.SetColorOptionValue(index, a_color, a_noUpdate) 1052 | endFunction 1053 | 1054 | function SetMenuOptionValueST(String a_value, Bool a_noUpdate, String a_stateName) 1055 | 1056 | Int index = self.GetStateOptionIndex(a_stateName) 1057 | if index < 0 1058 | self.Error("Cannot use SetMenuOptionValueST outside a valid option state") 1059 | return 1060 | endIf 1061 | self.SetMenuOptionValue(index, a_value, a_noUpdate) 1062 | endFunction 1063 | 1064 | ; Skipped compiler generated GetState 1065 | 1066 | Int function AddEmptyOption() 1067 | 1068 | return self.AddOption(self.OPTION_TYPE_EMPTY, none, none, 0 as Float, 0) 1069 | endFunction 1070 | 1071 | function SetMenuOptionValue(Int a_option, String a_value, Bool a_noUpdate) 1072 | 1073 | Int index = a_option % 256 1074 | Int type = _optionFlagsBuf[index] % 256 1075 | if type != self.OPTION_TYPE_MENU 1076 | Int pageIdx = a_option / 256 - 1 1077 | if pageIdx != -1 1078 | self.Error("Option type mismatch. Expected menu option, page \"" + Pages[pageIdx] + "\", index " + index as String) 1079 | else 1080 | self.Error("Option type mismatch. Expected menu option, page \"\", index " + index as String) 1081 | endIf 1082 | return 1083 | endIf 1084 | self.SetOptionStrValue(index, a_value, a_noUpdate) 1085 | endFunction 1086 | 1087 | function SetMenuDialogDefaultIndex(Int a_value) 1088 | 1089 | if _state != self.STATE_MENU 1090 | self.Error("Cannot set menu dialog params while outside OnOptionMenuOpen()") 1091 | return 1092 | endIf 1093 | _menuParams[1] = a_value 1094 | endFunction 1095 | 1096 | function AddTextOptionST(String a_stateName, String a_text, String a_value, Int a_flags) 1097 | 1098 | self.AddOptionST(a_stateName, self.OPTION_TYPE_TEXT, a_text, a_value, 0 as Float, a_flags) 1099 | endFunction 1100 | 1101 | function AddMenuOptionST(String a_stateName, String a_text, String a_value, Int a_flags) 1102 | 1103 | self.AddOptionST(a_stateName, self.OPTION_TYPE_MENU, a_text, a_value, 0 as Float, a_flags) 1104 | endFunction 1105 | 1106 | function OnColorAcceptST(Int a_color) 1107 | {Called when a new color has been accepted for this state option} 1108 | 1109 | ; Empty function 1110 | endFunction 1111 | 1112 | function OnOptionHighlight(Int a_option) 1113 | {Called when highlighting an option} 1114 | 1115 | ; Empty function 1116 | endFunction 1117 | 1118 | function OnOptionKeyMapChange(Int a_option, Int a_keyCode, String a_conflictControl, String a_conflictName) 1119 | {Called when a key has been remapped} 1120 | 1121 | ; Empty function 1122 | endFunction 1123 | 1124 | Int function AddTextOption(String a_text, String a_value, Int a_flags) 1125 | 1126 | return self.AddOption(self.OPTION_TYPE_TEXT, a_text, a_value, 0 as Float, a_flags) 1127 | endFunction 1128 | 1129 | function OnConfigInit() 1130 | {Called when this config menu is initialized} 1131 | 1132 | ; Empty function 1133 | endFunction 1134 | 1135 | function OnDefaultST() 1136 | {Called when resetting a state option to its default value} 1137 | 1138 | ; Empty function 1139 | endFunction 1140 | 1141 | Int function AddToggleOption(String a_text, Bool a_checked, Int a_flags) 1142 | 1143 | return self.AddOption(self.OPTION_TYPE_TOGGLE, a_text, none, (a_checked as Int) as Float, a_flags) 1144 | endFunction 1145 | 1146 | function SetSliderDialogStartValue(Float a_value) 1147 | 1148 | if _state != self.STATE_SLIDER 1149 | self.Error("Cannot set slider dialog params while outside OnOptionSliderOpen()") 1150 | return 1151 | endIf 1152 | _sliderParams[0] = a_value 1153 | endFunction 1154 | 1155 | function SetCursorFillMode(Int a_fillMode) 1156 | 1157 | if a_fillMode == self.LEFT_TO_RIGHT || a_fillMode == self.TOP_TO_BOTTOM 1158 | _cursorFillMode = a_fillMode 1159 | endIf 1160 | endFunction 1161 | 1162 | function OnConfigOpen() 1163 | {Called when this config menu is opened} 1164 | 1165 | ; Empty function 1166 | endFunction 1167 | 1168 | function RemapKey(Int a_index, Int a_keyCode, String a_conflictControl, String a_conflictName) 1169 | 1170 | String optionState = _stateOptionMap[a_index] 1171 | if optionState != "" 1172 | String oldState = self.GetState() 1173 | self.GotoState(optionState) 1174 | self.OnKeyMapChangeST(a_keyCode, a_conflictControl, a_conflictName) 1175 | self.GotoState(oldState) 1176 | McmRecorder_Recorder.RecordAction(self, ModName, PageNameOrDefault, "keymap", (a_index + _currentPageNum * 256), stateName = optionState, recordOptionType = true, fltValue = a_keyCode, recordFloatValue = true) 1177 | else 1178 | Int option = a_index + _currentPageNum * 256 1179 | self.OnOptionKeyMapChange(option, a_keyCode, a_conflictControl, a_conflictName) 1180 | McmRecorder_Recorder.RecordAction(self, ModName, PageNameOrDefault, "keymap", option, recordOptionType = true, fltValue = a_keyCode, recordFloatValue = true) 1181 | endIf 1182 | endFunction 1183 | 1184 | function SetToggleOptionValue(Int a_option, Bool a_checked, Bool a_noUpdate) 1185 | 1186 | Int index = a_option % 256 1187 | Int type = _optionFlagsBuf[index] % 256 1188 | if type != self.OPTION_TYPE_TOGGLE 1189 | Int pageIdx = a_option / 256 - 1 1190 | if pageIdx != -1 1191 | self.Error("Option type mismatch. Expected toggle option, page \"" + Pages[pageIdx] + "\", index " + index as String) 1192 | else 1193 | self.Error("Option type mismatch. Expected toggle option, page \"\", index " + index as String) 1194 | endIf 1195 | return 1196 | endIf 1197 | self.SetOptionNumValue(index, (a_checked as Int) as Float, a_noUpdate) 1198 | endFunction 1199 | 1200 | Int function AddOption(Int a_optionType, String a_text, String a_strValue, Float a_numValue, Int a_flags, string stateName = "") 1201 | if _state != self.STATE_RESET 1202 | self.Error("Cannot add option " + a_text + " outside of OnPageReset()") 1203 | return -1 1204 | endIf 1205 | Int pos = _cursorPosition 1206 | if pos == -1 1207 | return -1 1208 | endIf 1209 | 1210 | _optionFlagsBuf[pos] = a_optionType + a_flags * 256 1211 | _textBuf[pos] = a_text 1212 | _strValueBuf[pos] = a_strValue 1213 | _numValueBuf[pos] = a_numValue 1214 | _cursorPosition += _cursorFillMode 1215 | if _cursorPosition >= 128 1216 | _cursorPosition = -1 1217 | endIf 1218 | 1219 | int optionId = pos + _currentPageNum * 256 1220 | 1221 | string optionTypeName = McmRecorder_McmFields.GetOptionTypeName(a_optionType) 1222 | McmRecorder_McmFields.TrackField( \ 1223 | modName = ModName, \ 1224 | pageName = PageNameOrDefault, \ 1225 | optionType = optionTypeName, \ 1226 | optionId = optionId, \ 1227 | text = a_text, \ 1228 | strValue = a_strValue, \ 1229 | fltValue = a_numValue, \ 1230 | stateName = stateName \ 1231 | ) 1232 | return optionId 1233 | endFunction 1234 | 1235 | function CloseConfig() 1236 | 1237 | self.OnConfigClose() 1238 | self.ClearOptionBuffers() 1239 | _waitForMessage = false 1240 | _optionFlagsBuf = new Int[1] 1241 | _textBuf = new String[1] 1242 | _strValueBuf = new String[1] 1243 | _numValueBuf = new Float[1] 1244 | _stateOptionMap = new String[1] 1245 | endFunction 1246 | 1247 | function OnOptionColorAccept(Int a_option, Int a_color) 1248 | {Called when a new color has been accepted} 1249 | 1250 | ; Empty function 1251 | endFunction 1252 | 1253 | function OnMenuAcceptST(Int a_index) 1254 | {Called when a menu entry has been accepted for this state option} 1255 | 1256 | ; Empty function 1257 | endFunction 1258 | 1259 | Int function AddSliderOption(String a_text, Float a_value, String a_formatString, Int a_flags) 1260 | 1261 | return self.AddOption(self.OPTION_TYPE_SLIDER, a_text, a_formatString, a_value, a_flags) 1262 | endFunction 1263 | 1264 | function OnOptionDefault(Int a_option) 1265 | {Called when resetting an option to its default value} 1266 | 1267 | ; Empty function 1268 | endFunction 1269 | 1270 | function AddColorOptionST(String a_stateName, String a_text, Int a_color, Int a_flags) 1271 | 1272 | self.AddOptionST(a_stateName, self.OPTION_TYPE_COLOR, a_text, none, a_color as Float, a_flags) 1273 | endFunction 1274 | 1275 | function SetCursorPosition(Int a_position) 1276 | 1277 | if a_position < 128 1278 | _cursorPosition = a_position 1279 | endIf 1280 | endFunction 1281 | 1282 | Int function AddMenuOption(String a_text, String a_value, Int a_flags) 1283 | 1284 | return self.AddOption(self.OPTION_TYPE_MENU, a_text, a_value, 0 as Float, a_flags) 1285 | endFunction 1286 | 1287 | function OnOptionInputAccept(Int a_option, String a_input) 1288 | {Called when a new text input has been accepted} 1289 | 1290 | ; Empty function 1291 | endFunction 1292 | 1293 | function SetToggleOptionValueST(Bool a_checked, Bool a_noUpdate, String a_stateName) 1294 | 1295 | Int index = self.GetStateOptionIndex(a_stateName) 1296 | if index < 0 1297 | self.Error("Cannot use SetToggleOptionValueST outside a valid option state") 1298 | return 1299 | endIf 1300 | self.SetToggleOptionValue(index, a_checked, a_noUpdate) 1301 | endFunction 1302 | 1303 | function OnOptionMenuOpen(Int a_option) 1304 | {Called when a menu option has been selected} 1305 | 1306 | ; Empty function 1307 | endFunction 1308 | 1309 | function SetColorOptionValue(Int a_option, Int a_color, Bool a_noUpdate) 1310 | 1311 | Int index = a_option % 256 1312 | Int type = _optionFlagsBuf[index] % 256 1313 | if type != self.OPTION_TYPE_COLOR 1314 | Int pageIdx = a_option / 256 - 1 1315 | if pageIdx != -1 1316 | self.Error("Option type mismatch. Expected color option, page \"" + Pages[pageIdx] + "\", index " + index as String) 1317 | else 1318 | self.Error("Option type mismatch. Expected color option, page \"\", index " + index as String) 1319 | endIf 1320 | return 1321 | endIf 1322 | self.SetOptionNumValue(index, a_color as Float, a_noUpdate) 1323 | endFunction 1324 | --------------------------------------------------------------------------------