ItsAlways710 Posted December 3, 2021 Share Posted December 3, 2021 Any help would be appreciated. I have had the mod working well for a few weeks (in SE), and decided to add hotkeys. Now on a new game, after assigning hotkeys (and only after assigning hotkeys) they work, and you can play the game for a while, but after about 10 mins or so it locks up. No CTD, it just freezes. If you never assign a hotkey it works - well, I've only played it for about an hour that way, but it seems to work like it always did. I tried loading it on AE that I have on a different machine, and it loads and seems to work fine. Until you assign a hotkey and then you can't even exit the MCM Menu at all. I'm at a loss, but am pretty new to scripting. I'm assuming that it's in my OnKeyDown event or on my OnOptionKeyMapChange, they are at the bottom of my MCM script which is below - I'm new to scripting for skyrim so please be kind. Scriptname IA710_HandyCrafting_MCM_Script extends SKI_ConfigBase IA710_HandyCraftingScript Property HandyCrafting Auto ;These need to be filled with the formlists and container references. FormList Property IA710_flAllIngredients Auto {Holds all of the ingredients from the other lists.} ObjectReference Property IA710_cIngredientStorageRef Auto {Master container hold all the stored ingredients.} ObjectReference Property IA710_cTemporaryHoldingRef Auto {Container that holds Protected ingredients while the player is crafting.} ObjectReference Property IA710_cCappingChestRef Auto {Container tha holds the Capping ingredients list.} FormList[] Property IngredientsList Auto {Contains all the vanilla ingredient lists} ObjectReference[] Property IngredientContainerRef Auto {The containers for each of the corresponding ingredient lists} ; Miscelaneous properties Message Property IA710_mUninstallMessage Auto {Final instructions to follow to complete uninstallation.} Spell Property IA710_spFortifyCraftingWeight Auto {Used to remove the spell from the player, in case something went wrong.} String Property ThousandsSeperator = "," Auto {The thousands seperator used in formatting the item count numbers. Default: ,} ObjectReference Property IA710_cRetrieveStorageRef Auto {Temprarily Holds list specific ingredients while retrieving them} ObjectReference Property IA710_cBagofHoldingREF Auto {Container Ref that holds Bag of Holding stored items} Spell[] Property HandySpellsList Auto ; Handy Spell List is ; [0] = Handy Store and Retrieve Spell ; [1] = Handy Conjure Ghost Mount Spell ; [2] = Handy Teleport Spell ; [3] = Handy Bag of Holding Spell Actor PlayerRef ; To keep from making repeated calls to Game.GetPlayer() int current_version = 0 Int[] IngrdientEntryID ; ID numbers to access each of the vanilla ingredient lists/containers Int OpenCappingContainerID ; ID number to access the Capping container Int OpenStorageContainerID ; ID number to access the storage container Int StoreAllIngredientsID ; ID number to start the Store All Ingredients function Int IsEnabledID ; ID number to enable/disable Handy Crafting Int EnabledWeightBuffID ; ID number to enable/disable the carry weight buff Int EnableIngredientCappingID ; ID number to enable/disable ingredient Capping Int AlwaysShowMessagesID ; ID number to enable/disable container message always showing Int UninstallHandyCraftingID ; ID number to start uninstalling Handy Crafting Int RefreshListsID ; ID number to start the Refresh All of the Ingredient Lists Int AllowInDungeonsID ; ID number to allow Handy Crafting to work on crafting stations in dungeons/wilderness Int AddStoreIngredientsSpellID ; ID number to add/remove the "Handy Store and Retrieve" spell. Int AddHandyTeleportSpellID ; ID number to add/remove the "Handy Teleport" spell. Int AddHandyGhostMountSpellID ; ID number to add/remove the "Handy Conjure Ghost Mount" spell. Int AddHandyBOHSpellID ; ID number to add/remove the "Handy Bag of Holding" spell. Int RetrieveAllStoredID ; ID Number to Retrieve all items from all storage main and temporay storage, leaves capping, protected and custom added int[] HSKeyDefault int[] HSKeyMap int[] OID_Hotkey int HSLMax ; IngredientEntryID indices are ; 0 = Alchemy ; 1 = Carpetnry ; 2 = Cooking ; 3 = Enchanting ; 4 = Milling ; 5 = Smelting ; 6 = Smithing ; 7 = Tanning ; 8 = Tempering ; 9 = Protected Int Function GetVersion() Return 1 EndFunction String Function GetCustomControl(int keyCode) int idx = 0 while idx < HSLMax if (HSKeyMap[idx] == keyCode) return "Handy Crafting: " + HandySpellsList[idx].GetName() EndIf idx += 1 EndWhile return "" EndFunction String Function FormatNumber(Int aiNumToFormat) String FormattedNumber = aiNumToFormat If aiNumToFormat > 0 If StringUtil.GetLength(FormattedNumber) > 3 FormattedNumber = StringUtil.SubString(FormattedNumber, 0, StringUtil.GetLength(FormattedNumber) - 3) + ThousandsSeperator + StringUtil.SubString(FormattedNumber, StringUtil.GetLength(FormattedNumber) - 3, 3) If StringUtil.GetLength(FormattedNumber) > 7 FormattedNumber = StringUtil.SubString(FormattedNumber, 0, StringUtil.GetLength(FormattedNumber) - 7) + ThousandsSeperator + StringUtil.SubString(FormattedNumber, StringUtil.GetLength(FormattedNumber) - 7, 7) EndIf EndIf Return FormattedNumber Else Return "" EndIf EndFunction ; Closes the MCM and System menus. Brings the player back to game mode. Function CloseMCM() UI.Invoke("Journal Menu", "_root.QuestJournalFader.Menu_mc.ConfigPanelClose") UI.Invoke("Journal Menu", "_root.QuestJournalFader.Menu_mc.CloseMenu") Utility.Wait(0.5) EndFunction ; Refreshes a formlist with it's associated container ; Used to make sure the formlist contains valid objects Function RefreshList(FormList aflIngredientList, ObjectReference aorIngredientContainer) Int idx = aorIngredientContainer.GetNumItems() Form[] ContainerContents = Utility.CreateFormArray(idx) While idx > 0 idx -= 1 ContainerContents[idx] = aorIngredientContainer.GetNthForm(idx) EndWhile aflIngredientList.Revert() aflIngredientList.Addforms(ContainerContents) EndFunction ; Returns the total number of items inside a container Int Function MergeIngredientLists(FormList aflIngredientList) Int idx = IngredientsList.Length Form[] ConvertedFormlist aflIngredientList.Revert() While idx > 0 idx -= 1 ConvertedFormlist = IngredientsList[idx].ToArray() aflIngredientList.AddForms(ConvertedFormlist) EndWhile Return IA710_cIngredientStorageRef.GetItemCount(aflIngredientList) EndFunction event OnVersionUpdate(int version) if (version > current_version) Debug.Trace(self + ": Updating script to version " + version) OnConfigInit() endIf endEvent Event OnConfigInit() ; Pages = new string[3] Pages[0] = "List Maintenance" Pages[1] = "Configuration" Pages[2] = "Uninstall" PlayerRef = Game.GetPlayer() ; To keep from making repeated calls to Game.GetPlayer() IngrdientEntryID = Utility.CreateIntArray(IngredientsList.Length) ; Just in case I decide to add or remove ingredient lists Int idx = IngredientsList.Length - 1 ; Skip adding Protected Ingredients to the large list. Form[] ConvertedFormlist IA710_flAllIngredients.Revert() While idx > 0 idx -= 1 ConvertedFormlist = IngredientsList[idx].ToArray() IA710_flAllIngredients.AddForms(ConvertedFormlist) EndWhile PlayerRef.AddSpell(HandySpellsList[0], abVerbose = False) PlayerRef.AddSpell(HandySpellsList[1], abVerbose = False) PlayerRef.AddSpell(HandySpellsList[2], abVerbose = False) PlayerRef.AddSpell(HandySpellsList[3], abVerbose = False) ; Initialize Hotkey Stuff HSLMax = HandySpellsList.Length OID_Hotkey = Utility.CreateIntArray(HSLMax, 0) int[] tempkeyholding ; Unregister all keys UnregisterForAllKeys() ; Set Key Defaults If !HSKeyDefault || HSKeyDefault.Length != HSLMax HSKeyDefault = Utility.CreateIntArray(HSLMax, -1) endif ; if HSKeyMap doesn't exist, initialize it, ; if it already exists but is the wrong length, preserve the values we can, reinit and fill with old values if !HSKeyMap HSKeyMap = Utility.CreateIntArray(HSLMax, -1) elseIf HSKeyMap.Length != HSLMax TempKeyHolding = Utility.CreateIntArray(HSLMax, -1) idx = HSLMax while idx > 0 idx -= 1 if idx < HSKeyMap.length TempKeyHolding[idx] = HSKeyMap[idx] else TempKeyHolding[idx] = HSKeyDefault[idx] endif endWhile HSKeyMap = Utility.CreateIntArray(HSLMax, -1) HSKeyMap = TempKeyHolding endIf ; Register the hotkeys idx = HSLMax while idx > 0 idx -= 1 if HSKeyMap[idx] > 1 RegisterForKey(HSKeyMap[idx]) endIf endwhile current_version = GetVersion() Debug.Notification("Handy Crafting and Spells Initilization Complete") EndEvent Event OnGameReload() Int idx = IngredientsList.Length Form CurrentIngredient Parent.OnGameReload() While idx > 0 idx -= 1 RefreshList(IngredientsList[idx], IngredientContainerRef[idx]) EndWhile idx = IA710_cCappingChestRef.GetNumItems() HandyCrafting.CappedIngredientsNum HandyCrafting.CappedIngredientsRef = Utility.CreateFormArray(idx) HandyCrafting.CappedIngredientsQty = Utility.CreateIntArray(idx) While idx > 0 idx -= 1 CurrentIngredient = IA710_cCappingChestRef.GetNthForm(idx) HandyCrafting.CappedIngredientsRef[idx] = CurrentIngredient HandyCrafting.CappedIngredientsQty[idx] = IA710_cCappingChestRef.GetItemCount(CurrentIngredient) EndWhile EndEvent Event OnConfigOpen() Int idx = IngredientsList.Length - 1 ; Skip adding Protected Ingredients to the large list. Form[] ConvertedFormlist ; Merges all of the ingredient formlists into one large list. ; This is used to quickly store all ingreidents and display how many ingredients are in the storage container. IA710_flAllIngredients.Revert() While idx > 0 idx -= 1 ConvertedFormlist = IngredientsList[idx].ToArray() IA710_flAllIngredients.AddForms(ConvertedFormlist) EndWhile ; Some basic maintenance. PlayerRef.RemoveSpell(IA710_spFortifyCraftingWeight) ; Remove the carry weight buff if the player has it on him/her IA710_cTemporaryHoldingRef.RemoveAllItems(PlayerRef) ; This container should always be empty, but just to be safe IA710_cRetrieveStorageRef.RemoveAllItems(PlayerRef) ; This container should also always be empty, but just to be safe EndEvent Event OnPageReset(String a_Page) If (a_page == "") OnPageReset("List Maintenance") ; Load List Maintenance page ElseIf (a_page == "List Maintenance") SetCursorFillMode (LEFT_TO_RIGHT) SetTitleText("$The_HandyCrafting_Title") Utility.WaitMenuMode(0.1) ; Show all the ingredient lists on the left side of the page, maintenance and options on the right side. ; The two sides are mixed together, so good luck trying to make sense of it. :P - I created several pages and split it -IA710 AddHeaderOption("$Ingredients_Lists") AddHeaderOption("$Maintenance") IngrdientEntryID[0] = AddTextOption("$Alchemy_Ingredients", FormatNumber(IngredientContainerRef[0].GetNumItems())) OpenStorageContainerID = AddTextOption("$Open_Storage_Container", FormatNumber(IA710_cIngredientStorageRef.GetItemCount( IA710_flAllIngredients))) IngrdientEntryID[1] = AddTextOption("$Carpentry_Ingredients", FormatNumber(IngredientContainerRef[1].GetNumItems())) StoreAllIngredientsID = AddTextOption("$Store_All_Ingredients", "") IngrdientEntryID[2] = AddTextOption("$Cooking_Ingredients", FormatNumber(IngredientContainerRef[2].GetNumItems())) RetrieveAllStoredID = AddTextOption("$Retrieve_All_Items", "") SetCursorFillMode (TOP_TO_BOTTOM) IngrdientEntryID[3] = AddTextOption("$Enchanting_Ingredients", FormatNumber(IngredientContainerRef[3].GetNumItems())) IngrdientEntryID[4] = AddTextOption("$Milling_Ingredients", FormatNumber(IngredientContainerRef[4].GetNumItems())) IngrdientEntryID[5] = AddTextOption("$Smelting_Ingredients", FormatNumber(IngredientContainerRef[5].GetNumItems())) IngrdientEntryID[6] = AddTextOption("$Smithing_Ingredients", FormatNumber(IngredientContainerRef[6].GetNumItems())) IngrdientEntryID[7] = AddTextOption("$Tanning_Ingredients", FormatNumber(IngredientContainerRef[7].GetNumItems())) IngrdientEntryID[8] = AddTextOption("$Tempering_Ingredients", FormatNumber(IngredientContainerRef[8].GetNumItems())) IngrdientEntryID[9] = AddTextOption("$Protected_Ingredients", FormatNumber(IngredientContainerRef[9].GetNumItems())) OpenCappingContainerID = AddTextOption("$Capped_Ingredients", FormatNumber(IA710_cCappingChestRef.GetNumItems())) ElseIf (a_page == "Configuration") SetCursorFillMode (LEFT_TO_RIGHT) SetTitleText("$The_HandyCrafting_Title") AddHeaderOption("$Options") AddHeaderOption ("$Add_Spells_Header") IsEnabledID = AddToggleOption("$Enable_HandyCrafting", HandyCrafting.HandyCraftingEnabled) AddStoreIngredientsSpellID = AddToggleOption("$Add_Store_Ingredients_Power", PlayerRef.HasSpell(HandySpellsList[0])) AllowInDungeonsID = AddToggleOption("$Restricted_Mode", !HandyCrafting.AllowInDungeons) AddHandyGhostMountSpellID = AddToggleOption ("$Add_Handy_Mount_Spell", PlayerRef.HasSpell(HandySpellsList[1])) EnableIngredientCappingID = AddToggleOption("$Enable_Capping", HandyCrafting.EnableCapping) AddHandyTeleportSpellID = AddToggleOption("$Add_Handy_Teleport_Spell", PlayerRef.HasSpell(HandySpellsList[2])) EnabledWeightBuffID = AddToggleOption("$Increase_Carry_Weight_while_Crafting", HandyCrafting.CraftingWeightBuffEnabled) AddHandyBOHSpellID = AddToggleOption("$Add_Handy_BOH_Spell", PlayerRef.HasSpell(HandySpellsList[3])) AlwaysShowMessagesID = AddToggleOption("$Always_Show_Messages", HandyCrafting.AlwaysShowMessages) SetCursorFillMode(TOP_TO_BOTTOM) AddEmptyOption() AddHeaderOption("$Add_Handy_Hotkeys") int idx = 0 while (idx < HSLMax) OID_Hotkey[idx] = AddKeyMapOption(HandySpellsList[idx].GetName() + " Hotkey", HSKeyMap[idx]) idx += 1 endwhile ElseIf (a_page == "Uninstall") SetCursorFillMode (TOP_TO_BOTTOM) SetTitleText("$The_HandyCrafting_Title") AddHeaderOption("$Uninstall") UninstallHandyCraftingID = AddTextOption("$Uninstall_HandyCrafting", "") AddEmptyOption() AddHeaderOption("Troubleshooting") RefreshListsID = AddTextOption("Reinitialize and Refresh all lists", "") EndIf EndEvent Event OnOptionHighlight(Int OptionID) If OptionID == IngrdientEntryID[0] SetInfoText("$Alchemy_Description") ElseIf OptionID == IngrdientEntryID[1] SetInfoText("$Carpentry_Description") ElseIf OptionID == IngrdientEntryID[2] SetInfoText("$Cooking_Description") ElseIf OptionID == IngrdientEntryID[3] SetInfoText("$Enchanting_Description") ElseIf OptionID == IngrdientEntryID[4] SetInfoText("$Milling_Description") ElseIf OptionID == IngrdientEntryID[5] SetInfoText("$Smelting_Description") ElseIf OptionID == IngrdientEntryID[6] SetInfoText("$Smithing_Description") ElseIf OptionID == IngrdientEntryID[7] SetInfoText("$Tanning_Description") ElseIf OptionID == IngrdientEntryID[8] SetInfoText("$Tempering_Description") ElseIf OptionID == IngrdientEntryID[9] SetInfoText("$Protected_Description") ElseIf OptionID == OpenCappingContainerID SetInfoText("$Capped_Description") ElseIf OptionID == OpenStorageContainerID SetInfoText("$Open_Storage_Container_Description") ElseIf OptionID == StoreAllIngredientsID SetInfoText("$Store_All_Ingredients_Description") ElseIf OptionID == IsEnabledID SetInfoText("$Enable/Disable_Description") ElseIF OptionID == AllowInDungeonsID SetInfoText("$Restricted_Mode_Description") ElseIf OptionID == EnableIngredientCappingID SetInfoText("$Enable_Capping_Description") ElseIf OptionID == AlwaysShowMessagesID SetInfoText("$Always_Show_Messages_Description") Elseif OptionID == EnabledWeightBuffID SetInfoText("$Enable_Weight_Buff_Description") ElseIf OptionID == UninstallHandyCraftingID SetInfoText("$Uninstall_Description") ElseIf OptionID == AddStoreIngredientsSpellID SetInfoText("$Add_Store_Ingedients_Power_Description") ElseIf OptionID == AddHandyTeleportSpellID SetInfoText("$Add_Handy_Teleport_Spell_Description") ElseIf OptionID == AddHandyGhostMountSpellID SetInfoText("$Add_Handy_Mount_Spell_Description") ElseIf OptionID == AddHandyBOHSpellID SetInfoText("$Add_Handy_BOH_Spell_Description") ElseIf OptionID == RetrieveAllStoredID SetInfoText("$Retrieve_All_Items_Description") Else int idx = 0 while idx < HSLMax If (OptionID == OID_Hotkey[idx]) SetInfoText("$Set_Handy_Hotkey") EndIf idx += 1 EndWhile EndIf EndEvent Event OnOptionDefault(Int OptionID) If OptionID == IsEnabledID HandyCrafting.HandyCraftingEnabled = True SetToggleOptionValue(OptionID, HandyCrafting.HandyCraftingEnabled) ElseIf OptionID == AllowInDungeonsID HandyCrafting.AllowInDungeons = True SetToggleOptionValue(OptionID, !HandyCrafting.AllowInDungeons) ElseIf OptionID == EnabledWeightBuffID HandyCrafting.CraftingWeightBuffEnabled = True SetToggleOptionValue(OptionID, HandyCrafting.CraftingWeightBuffEnabled) ElseIf OptionID == EnableIngredientCappingID HandyCrafting.EnableCapping = True SetToggleOptionValue(OptionID, HandyCrafting.EnableCapping) ElseIf OptionID == AlwaysShowMessagesID HandyCrafting.AlwaysShowMessages = True SetToggleOptionValue(OptionID, HandyCrafting.AlwaysShowMessages) ElseIf OptionID == AddStoreIngredientsSpellID If !PlayerRef.HasSpell(HandySpellsList[0]) PlayerRef.AddSpell(HandySpellsList[0]) EndIf SetToggleOptionValue(AddStoreIngredientsSpellID, True) ElseIf OptionID == AddHandyTeleportSpellID If !PlayerRef.HasSpell(HandySpellsList[2]) PlayerRef.AddSpell(HandySpellsList[2]) EndIf SetToggleOptionValue(AddHandyTeleportSpellID, True) ElseIf OptionID == AddHandyGhostMountSpellID If !PlayerRef.HasSpell(HandySpellsList[1]) PlayerRef.AddSpell(HandySpellsList[1]) EndIf SetToggleOptionValue(AddHandyGhostMountSpellID, True) ElseIf OptionID == AddHandyBOHSpellID If !PlayerRef.HasSpell(HandySpellsList[3]) PlayerRef.AddSpell(HandySpellsList[3]) EndIf SetToggleOptionValue(AddHandyBOHSpellID, True) Else int idx = 0 while idx < HSLMax If (OptionID == OID_Hotkey[idx]) UnregisterForKey(HSKeyMap[idx]) SetKeyMapOptionValue(OptionID, -1) HSKeyMap[idx] = -1 EndIf idx += 1 EndWhile EndIf EndEvent Event OnOptionSelect(Int OptionID) Int idx Form IngredientToCap Int QuantityToCap Form CurrentIngredient If OptionID == UninstallHandyCraftingID ; Handle the uninstall option If ShowMessage("$Confirm_Uninstall") CloseMCM() HandyCrafting.ShutDown() UnregisterForAllKeys() PlayerRef.RemoveSpell(HandySpellsList[2]) PlayerRef.RemoveSpell(HandySpellsList[3]) IA710_cBagofHoldingREF.RemoveAllItems(PlayerRef) PlayerRef.RemoveSpell(HandySpellsList[1]) PlayerRef.RemoveSpell(HandySpellsList[0]) IA710_cRetrieveStorageRef.RemoveAllItems(PlayerRef) IA710_cIngredientStorageRef.RemoveAllItems(PlayerRef) IA710_cTemporaryHoldingRef.RemoveAllItems(PlayerRef) idx = IngredientContainerRef.Length While idx > 0 idx -= 1 IngredientContainerRef[idx].RemoveAllItems() EndWhile IA710_mUninstallMessage.Show() Self.Stop() EndIf ; Handle Troubleshooting ElseIf OptionID == RefreshListsID OnConfigInit() idx = IngredientsList.Length While idx > 0 idx -= 1 RefreshList(IngredientsList[idx], IngredientContainerRef[idx]) EndWhile idx = IA710_cCappingChestRef.GetNumItems() HandyCrafting.CappedIngredientsNum HandyCrafting.CappedIngredientsRef = Utility.CreateFormArray(idx) HandyCrafting.CappedIngredientsQty = Utility.CreateIntArray(idx) While idx > 0 idx -= 1 CurrentIngredient = IA710_cCappingChestRef.GetNthForm(idx) HandyCrafting.CappedIngredientsRef[idx] = CurrentIngredient HandyCrafting.CappedIngredientsQty[idx] = IA710_cCappingChestRef.GetItemCount(CurrentIngredient) EndWhile OnConfigOpen() ShowMessage("Reinitialized MCM and Refreshed all lists.", False) ; Handle the toggled options ElseIf OptionID == IsEnabledID HandyCrafting.HandyCraftingEnabled = !HandyCrafting.HandyCraftingEnabled SetToggleOptionValue(OptionID, HandyCrafting.HandyCraftingEnabled) ElseIf OptionID == AllowInDungeonsID ; Handle the Allow in Dungeons/Wilderness option HandyCrafting.AllowInDungeons = !HandyCrafting.AllowInDungeons SetToggleOptionValue(OptionID, !HandyCrafting.AllowInDungeons) ElseIf OptionID == EnabledWeightBuffID HandyCrafting.CraftingWeightBuffEnabled = !HandyCrafting.CraftingWeightBuffEnabled SetToggleOptionValue(OptionID, HandyCrafting.CraftingWeightBuffEnabled) ElseIf OptionID == EnableIngredientCappingID HandyCrafting.EnableCapping = !HandyCrafting.EnableCapping SetToggleOptionValue(OptionID, HandyCrafting.EnableCapping) ElseIf OptionID == AlwaysShowMessagesID HandyCrafting.AlwaysShowMessages = !HandyCrafting.AlwaysShowMessages SetToggleOptionValue(OptionID, HandyCrafting.AlwaysShowMessages) ElseIf OptionID == AddStoreIngredientsSpellID If !PlayerRef.HasSpell(HandySpellsList[0]) PlayerRef.AddSpell(HandySpellsList[0], abVerbose = False) Else PlayerRef.RemoveSpell(HandySpellsList[0]) IA710_cRetrieveStorageRef.RemoveAllItems(PlayerRef) EndIf SetToggleOptionValue(AddStoreIngredientsSpellID, PlayerRef.HasSpell(HandySpellsList[0])) ElseIf OptionID == AddHandyTeleportSpellID If !PlayerRef.HasSpell(HandySpellsList[2]) PlayerRef.AddSpell(HandySpellsList[2], abVerbose = False) Else PlayerRef.RemoveSpell(HandySpellsList[2]) EndIf SetToggleOptionValue(AddHandyTeleportSpellID, PlayerRef.HasSpell(HandySpellsList[2])) ElseIf OptionID == AddHandyGhostMountSpellID If !PlayerRef.HasSpell(HandySpellsList[1]) PlayerRef.AddSpell(HandySpellsList[1], abVerbose = False) Else PlayerRef.RemoveSpell(HandySpellsList[1]) EndIf SetToggleOptionValue(AddHandyGhostMountSpellID, PlayerRef.HasSpell(HandySpellsList[1])) ElseIf OptionID == AddHandyBOHSpellID If !PlayerRef.HasSpell(HandySpellsList[3]) PlayerRef.AddSpell(HandySpellsList[3], abVerbose = False) Else PlayerRef.RemoveSpell(HandySpellsList[3]) IA710_cBagofHoldingREF.RemoveAllItems(PlayerRef) EndIf SetToggleOptionValue(AddHandyBOHSpellID, PlayerRef.HasSpell(HandySpellsList[3])) ElseIf OptionID == OpenCappingContainerID ; Handle accessing the Capping container CloseMCM() IA710_cCappingChestRef.AddInventoryEventFilter(IA710_flAllIngredients) IA710_cCappingChestRef.Activate(PlayerRef) ElseIf OptionID == OpenStorageContainerID ; Handle accessing the storage container CloseMCM() IA710_cIngredientStorageRef.Activate(PlayerRef) ElseIf OptionID == StoreAllIngredientsID ; Handle the Store All Ingredients option ; Retreive all the items in the storage container IA710_cIngredientStorageRef.RemoveAllItems(PlayerRef) ; Store any Protected ingredients in the temporary container PlayerRef.RemoveItem(IngredientsList[9], PlayerRef.GetItemCount(IngredientsList[9]), abSilent = True, akOtherContainer = IA710_cTemporaryHoldingRef) ; Search through the capped ingredients list for excess ingredients in the player's inventory idx = IA710_cCappingChestRef.GetNumItems() While idx > 0 idx -= 1 IngredientToCap = IA710_cCappingChestRef.GetNthForm(idx) QuantityToCap = PlayerRef.GetItemCount(IngredientToCap) - IA710_cCappingChestRef.GetItemCount(IngredientToCap) ; Set any excess ingredients aside in the temporary holding container If QuantityToCap > 0 PlayerRef.RemoveItem(IngredientToCap, QuantityToCap, abSilent = True, akOtherContainer = IA710_cTemporaryHoldingRef) EndIf EndWhile ; Store all the remaining ingredients PlayerRef.RemoveItem(IA710_flAllIngredients, PlayerRef.GetItemCount(IA710_flAllIngredients), abSilent = True, akOtherContainer = IA710_cIngredientStorageRef) ; Retreive the Protected items from the temporary holding container IA710_cTemporaryHoldingRef.RemoveAllItems(PlayerRef) ShowMessage("$Ingredients_Stored", False) SetTextOptionValue(OpenStorageContainerID, FormatNumber(IA710_cIngredientStorageRef.GetItemCount(IA710_flAllIngredients))) ElseIf OptionID == RetrieveAllStoredID IA710_cBagofHoldingREF.RemoveAllItems(PlayerRef) IA710_cRetrieveStorageRef.RemoveAllItems(PlayerRef) IA710_cIngredientStorageRef.RemoveAllItems(PlayerRef) IA710_cTemporaryHoldingRef.RemoveAllItems(PlayerRef) SetTextOptionValue(OpenStorageContainerID, FormatNumber(IA710_cIngredientStorageRef.GetItemCount(IA710_flAllIngredients))) ShowMessage("$All_Items_Retrieved", False) ; Handle accessing the ingredient containers Else idx = IngredientsList.Length While idx > 0 idx -= 1 If OptionID == IngrdientEntryID[idx] CloseMCM() IngredientContainerRef[idx].Activate(PlayerRef) idx = 0 EndIf EndWhile EndIf EndEvent event OnOptionKeyMapChange(int option, int keyCode, string conflictControl, string conflictName) {Called when a key has been remapped} int idx = 0 while idx < HSLMax If (option == OID_Hotkey[idx]) bool continue = true if (keyCode != 1 && conflictControl != "") string msg if (conflictName != "") msg = "This key is already mapped to:\n\"" + conflictControl + "\"\n(" + conflictName + ")\n\nAre you sure you want to continue?" else msg = "This key is already mapped to:\n\"" + conflictControl + "\"\n\nAre you sure you want to continue?" endIf continue = ShowMessage(msg, true, "$Yes", "$No") endIf If continue UnregisterForKey(HSKeyMap[idx]) if keyCode != 1 RegisterForKey(keyCode) HSKeyMap[idx] = keyCode EndIf SetKeyMapOptionValue(option, keyCode) EndIf EndIf idx += 1 EndWhile endEvent Event OnKeyDown(int keycode) if (keyCode != 1 && !PlayerRef.IsOnMount() && !Utility.IsInMenuMode() && Game.IsMenuControlsEnabled() && !UI.IsTextInputEnabled()) int idx = 0 while idx < HSLMax If (keyCode == HSKeyMap[idx]) ; Debug.Notification("HCaS Casting: " + HandySpellsList[idx].GetName()) HandySpellsList[idx].Cast(PlayerRef) EndIf idx += 1 endWhile EndIf EndEvent It's a WIP so please forgive coding inconsistencies, any and all help would be appreciated! Thank you!IA710 Link to comment Share on other sites More sharing options...
IsharaMeradin Posted December 3, 2021 Share Posted December 3, 2021 (edited) How many total key binds are you registering?I ask because if it is a good number the while loop can take a decent amount of time to get through. Granted only one will match and execute the code but as it is now, it goes through all entries in the array even if the necessary code has already ran. I would recommend setting the idx variable to a value that is outside the bounds so that the while loop exits as soon as the desired code has been executed. It can be as simple as idx = HSLMax Also you may want to consider using states or a bool condition to prevent the event from piling up if multiple valid keys are pressed sequentially. Let it process one press and tell the user that it is busy while the processing is going on. Once done, open it back up for another press. And for clutter purposes, take advantage of the fact that registering for a key press means that all scripts attached to the object can receive the event. Thus shift the OnKeyDown event onto a separate script. And here is a function you can adapt as needed to help prevent unnecessary processing of the key press at undesired points: Bool Function SafeProcess() If (!Utility.IsInMenuMode()) \ && (!UI.IsMenuOpen("Dialogue Menu")) \ && (!UI.IsMenuOpen("Console")) \ && (!UI.IsMenuOpen("Crafting Menu")) \ && (!UI.IsMenuOpen("MessageBoxMenu")) \ && (!UI.IsMenuOpen("ContainerMenu")) \ && (!UI.IsTextInputEnabled()) \ && (!PlayerRef.IsInCombat()) ;IsInMenuMode to block when game is paused with menus open ;Dialogue Menu check to block when dialog is open ;Console check to block when console is open - console does not trigger IsInMenuMode and thus needs its own check ;Crafting Menu check to block when crafting menus are open - game is not paused so IsInMenuMode does not work ;MessageBoxMenu check to block when message boxes are open - while they pause the game, they do not trigger IsInMenuMode ;ContainerMenu check to block when containers are accessed - while they pause the game, they do not trigger IsInMenuMode ;IsTextInputEnabled check to block when editable text fields are open ;IsInCombat check to block when combat is going on. Return True Else Debug.Notification("IMS: Unable to process Mod Activation Key press at this time.") Return False EndIf EndFunction EDIT: Where does the OnGameReload event come from? I am not familiar with that one. It is not on the CK wiki (Skyrim or FO4) nor is it in the MCM API documentation. Edited December 3, 2021 by IsharaMeradin Link to comment Share on other sites More sharing options...
ItsAlways710 Posted December 3, 2021 Author Share Posted December 3, 2021 (edited) IsharaMeradin - I've used so many of your scripting examples, it's a pleasure and honor to meet you, thank you for the assistance! My responses below, I will have time to try some of your suggestions tomorrow evening I hope, if so I will let you know how it goes. ---How many total key binds are you registering? Four right now, will most likely never be more than 6 possible, but I only have to have one actually registered for it to freeze, and it freezes at random times, not when doing a keypress, I could just be standing there doing nothing at all, In AE just assigning a key will lock me in the MCM. I hit escape, get to the main MCM menu, then escape again and it flashes the Handy Crafting MCM and then kicks me to the main MCM, and I can't select anything. Weird. --- I would recommend setting the idx variable to a value that is outside the bounds so that the while loop exits as soon as the desired code has been executed. It can be as simple as idx = HSLMax I have used that elsewhere in the script, I'll change it to that and see if that helps. I had seen it used the way I did and didn't fully think that through, thank you! --- Also you may want to consider using states or a bool condition to prevent the event from piling up if multiple valid keys are pressed sequentially. Let it process one press and tell the user that it is busy while the processing is going on. Once done, open it back up for another press. I will try that - I tried to include on in the OnKeyDown event, but I like yours better, and thank you very much for the sample! Thank you!IA710 EDIT - Just saw your edit, that event came from the original mod, KR4FT, I asked Kae Arby the original author about it and he didn't mention where he had found it, just that he used it to rebuild a dynamic formlist to avoid a corruption issue that can occur if a mod with items added to the list is removed. I should ask him where he came up with it, but it does work, so I never questioned it, maybe I should.... Edited December 3, 2021 by ItsAlways710 Link to comment Share on other sites More sharing options...
ItsAlways710 Posted December 3, 2021 Author Share Posted December 3, 2021 (edited) How many total key binds are you registering?I ask because if it is a good number the while loop can take a decent amount of time to get through. Granted only one will match and execute the code but as it is now, it goes through all entries in the array even if the necessary code has already ran. I would recommend setting the idx variable to a value that is outside the bounds so that the while loop exits as soon as the desired code has been executed. It can be as simple as idx = HSLMax Also you may want to consider using states or a bool condition to prevent the event from piling up if multiple valid keys are pressed sequentially. Let it process one press and tell the user that it is busy while the processing is going on. Once done, open it back up for another press. And for clutter purposes, take advantage of the fact that registering for a key press means that all scripts attached to the object can receive the event. Thus shift the OnKeyDown event onto a separate script. And here is a function you can adapt as needed to help prevent unnecessary processing of the key press at undesired points: Bool Function SafeProcess() If (!Utility.IsInMenuMode()) \ && (!UI.IsMenuOpen("Dialogue Menu")) \ && (!UI.IsMenuOpen("Console")) \ && (!UI.IsMenuOpen("Crafting Menu")) \ && (!UI.IsMenuOpen("MessageBoxMenu")) \ && (!UI.IsMenuOpen("ContainerMenu")) \ && (!UI.IsTextInputEnabled()) \ && (!PlayerRef.IsInCombat()) ;IsInMenuMode to block when game is paused with menus open ;Dialogue Menu check to block when dialog is open ;Console check to block when console is open - console does not trigger IsInMenuMode and thus needs its own check ;Crafting Menu check to block when crafting menus are open - game is not paused so IsInMenuMode does not work ;MessageBoxMenu check to block when message boxes are open - while they pause the game, they do not trigger IsInMenuMode ;ContainerMenu check to block when containers are accessed - while they pause the game, they do not trigger IsInMenuMode ;IsTextInputEnabled check to block when editable text fields are open ;IsInCombat check to block when combat is going on. Return True Else Debug.Notification("IMS: Unable to process Mod Activation Key press at this time.") Return False EndIf EndFunction EDIT: Where does the OnGameReload event come from? I am not familiar with that one. It is not on the CK wiki (Skyrim or FO4) nor is it in the MCM API documentation. Hi! I found in SkyUI where the OnGameReload event comes from, it's actually referenced by OnPlayerLoadGame(), here is the link. https://github.com/schlangster/skyui/blob/master/dist/Data/Scripts/Source/SKI_PlayerLoadGameAlias.psc#L6 IA710 EDIT: Appears to be used throughout SKYUI's Source: https://github.com/schlangster/skyui/search?p=1&q=ongamereload but I can't really find other references to it. Maybe I should switch to OnPlayerLoadGame()? Edited December 3, 2021 by ItsAlways710 Link to comment Share on other sites More sharing options...
IsharaMeradin Posted December 3, 2021 Share Posted December 3, 2021 OnGameReload is not mentioned on the MCM API Reference page but I finally found it mentioned on the MCM Advanced Features page. I always used OnPlayerLoadGame instead. ^^ you beat me to it. Before you proceed and believe there is an error with your mod. Given all the updating going on with the recent AE stuff, test a third party mod that has key binds and see if it too has problems. Link to comment Share on other sites More sharing options...
ItsAlways710 Posted December 3, 2021 Author Share Posted December 3, 2021 J OnGameReload is not mentioned on the MCM API Reference page but I finally found it mentioned on the MCM Advanced Features page. I always used OnPlayerLoadGame instead. ^^ you beat me to it. Before you proceed and believe there is an error with your mod. Given all the updating going on with the recent AE stuff, test a third party mod that has key binds and see if it too has problems. Just tried Player Equipment Manager on my AE install, nothing enabled but required mods, and it has the same "Stuck in the MCM" issue after assigning a hotkey, so assuming that is an AE issue (or an issue with my AE install) and will wait to try to resolve it. But that doesn't explain the issues I'm having in SE 1.5.97 that I kept frozen on my other machine because I was developing this mod. Player Equipment Manager works just fine on that version of Skyrim and SKSE 2.0.20 so I should be able to get my mod to work fine on it right? -IA710 Link to comment Share on other sites More sharing options...
ItsAlways710 Posted December 3, 2021 Author Share Posted December 3, 2021 (edited) Was going through some things I had tested, and I had tested making idx=HSLMax in one iteration, but I had only tested it on AE, which is obviously broken (on my install at least), so I will try moving the OnKeyDown to it's own seperate script with the Bool you provided - pretty sure that I just reference the MCM script and call all the values from it (except the bool, I'll throw it in the OnKeyDown script), so it shouldn't need any properties other than the MCM Script. I'll let you know how that goes, may not have a chance until late this evening, we'll see. -IA710 EDIT: and only test on SE 1.5.97 for the time being LOL Edited December 3, 2021 by ItsAlways710 Link to comment Share on other sites More sharing options...
IsharaMeradin Posted December 3, 2021 Share Posted December 3, 2021 (edited) Slight confusion here... Consider something like this: Event OnKeyDown(int keycode) if (keyCode != 1 && SafeProcess() == True) int idx = 0 while idx < HSLMax GoToState("Busy") ; prevent the event from piling up with multiple key presses If (keyCode == HSKeyMap[idx]) ; Debug.Notification("HCaS Casting: " + HandySpellsList[idx].GetName()) HandySpellsList[idx].Cast(PlayerRef) idx = HSLMax ; got what we need - get out GoToState("") ; return to the empty state EndIf idx += 1 endWhile EndIf EndEvent Bool Function SafeProcess() If (!Utility.IsInMenuMode()) \ && (!UI.IsMenuOpen("Dialogue Menu")) \ && (!UI.IsMenuOpen("Console")) \ && (!UI.IsMenuOpen("Crafting Menu")) \ && (!UI.IsMenuOpen("MessageBoxMenu")) \ && (!UI.IsMenuOpen("ContainerMenu")) \ && (!UI.IsTextInputEnabled()) \ && (!PlayerRef.IsInCombat()) \ && (!PlayerRef.IsOnMount()) ;IsInMenuMode to block when game is paused with menus open ;Dialogue Menu check to block when dialog is open ;Console check to block when console is open - console does not trigger IsInMenuMode and thus needs its own check ;Crafting Menu check to block when crafting menus are open - game is not paused so IsInMenuMode does not work ;MessageBoxMenu check to block when message boxes are open - while they pause the game, they do not trigger IsInMenuMode ;ContainerMenu check to block when containers are accessed - while they pause the game, they do not trigger IsInMenuMode ;IsTextInputEnabled check to block when editable text fields are open ;IsInCombat check to block when combat is going on. ;IsOnMount check to block when riding a horse Return True Else Debug.Notification("Unable to process mod hot key at this time.") Return False EndIf EndFunction State Busy Event OnKeyDown(int keycode) ;do nothing EndEvent EndState EDIT: And I lost the second half of the post for some reason.... Ugh Part of you problem may stem from the fact that you are processing things as the user selects choices. This is unnecessary as the user can turn things on and off multiple times within the same menu session. Push the work off until the menu is closed. Use the OnConfigClose event to register for a single update. Then in the OnUpdate event go through your option values and do whatever work necessary based upon the user's final choices. You can look at the MCM script on my Inventory Management System mod for SSE to get an understanding of what I am saying in this second part. Edited December 3, 2021 by IsharaMeradin Link to comment Share on other sites More sharing options...
ItsAlways710 Posted December 4, 2021 Author Share Posted December 4, 2021 Confusion is a normal state for me unfortunately, and now I am more confused then ever. Replaced my OnKeyDown with the above, but removed the !PlayerRef.isInCombat as I want a couple of the spells to work in combat and don't care if the other two are cast. Other than that it looks great to me. Testing - game freezes no matter what, new game, existing game, minimal load order (SKSE, Skyui and this), huge load order, whether I assign a hotkey or not, it freezes after about 8-15 minutes. I can be sitting on a mount with the world spinning (so no keys have been pressed in forever and being on a mount should preclude anything from happening in the first place, and no keys are mapped anyways). UGH - Just realized as I was typing this that I used a slightly different script that I was playing with earlier, let me put these changes in the exact script we are discussing and see what happens so we are talking about the exact same code... not much is different though, so I think the results are going to be the same. I'm going to take out the registerkeys that's in the onconfiginit though just so I can make sure when I test a no keys ever mapped test it will absolutely be true. I looked over your example of part two of your reply and, well, my head still hurts LOL (thousands of lines, you are a BEAST LOL) - but I see what you are doing I think, although I am doing so little and I'm not positive how capable I would be at implementing it - I think I could do the key registration pretty easy, and do like a unregisterallkeys in the OnConfigOpen event, so then no keys are mapped at all while in menu, then they all get registered at once using the mechanism you describe above with OnConfigClose and OnUpdate, do you think that would be enough? A lot of the other options only get touched once... Going to recompile using the code sample I gave you originally and your suggestion and start testing again. I'm not sure where else to look and I feel pretty stupid right now LOL. Still wondering if putting the OnKeyDown in a different script would help, although I'm not totally sure what I have to do in order to do that, just add a script to the quest and put your example in it (add the MCM script to it for all the variables)? Would it even help making it a separate script? Thank you again for all your help. -IA710 Link to comment Share on other sites More sharing options...
IsharaMeradin Posted December 4, 2021 Share Posted December 4, 2021 Only reason for a separate script would be for organization purposes. I.e. you want the MCM stuff kept separate from non-MCM stuff But yeah all it would require would be adding a second script to the same quest as the MCM. Registering on the MCM script would allow the other script to catch the key press related events. Link to comment Share on other sites More sharing options...
Recommended Posts