Jump to content

Need help adding hotkeys to MCM


Recommended Posts

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

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 by IsharaMeradin
Link to comment
Share on other sites

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 by ItsAlways710
Link to comment
Share on other sites

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 by ItsAlways710
Link to comment
Share on other sites

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

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

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 by ItsAlways710
Link to comment
Share on other sites

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 by IsharaMeradin
Link to comment
Share on other sites

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

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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...