Open sulez745 opened 2 years ago
Hi, I’m the author Better Menu Controls and Better Saves.
I’ve been getting pretty regular reports of the hot keys in both mods not working for around 2 years at this point (the former using OnKeyDown and the latter OnControlDown), although I was never able to reproduce them.
The gist of the linked discussion is that a user of Better Menu Controls (with xOBSE 22.7) discovered that using IsKeyPressed3 instead (by using it to emulate OnKeyDown) does work. Also, a different user reported that the mod works for him as is with xOBSE 22.1, but not any newer version; this leads me to believe that the regression introduced in xOBSE 22.2 was never fully fixed.
I am now planning to rewrite my mods to use a different method of detecting the hot keys (as On{Key,Control}Down seem to be unreliable unfortunately), and so I was wondering whether the event handlers were still considered experimental? If so, I’ll just emulate the current behavior with IsKeyPressed3/IsControlPressed, but I much rather do things “the right way” if possible.
Maybe an issue with the disticion of Real and simulated presses.
FWIW both my mods work under the assumption that they’re recognizing real key presses, and none of the reports I’ve gotten suggest that the users were somehow simulating key presses.
Hi @dsemy I did try to test for this issue, but I'm unable to reproduce on my end. OnKeyDown should take all presses reported from IsKeyPressed3 so it's overall strange.
This issue affects all hotkeys or just some of them?
If people experience this issue for all hotkeys, I think it's a sort of strange mod interaction, maybe with some OBSE plugin. I recently fixed an issue with OBJA (a japanese localization plugin) that interfered with the Input system, I can't exclude there are others incompatibilities around.
Key and Control Events can be considered stable.
Hi @dsemy I did try to test for this issue, but I'm unable to reproduce on my end. OnKeyDown should take all presses reported from IsKeyPressed3 so it's overall strange.
This issue affects all hotkeys or just some of them?
IDK unfortunately, as I was also never affected by this. However it seems to be all of them from the reports I’ve been getting.
BTW, 2 reports did mention that spamming the hot keys for around 10 seconds made them work.
If people experience this issue for all hotkeys, I think it's a sort of strange mod interaction, maybe with some OBSE plugin. I recently fixed an issue with OBJA (a japanese localization plugin) that interfered with the Input system, I can't exclude there are others incompatibilities around.
I also think this is the case, as most reporters indicated they use multiple OBSE plugins, however I couldn’t make this issue appear even after installing all the OBSE plugins people affected by this issue were using simultaneously.
Key and Control Events can be considered stable.
Thanks, when I have the time I’ll update my mods to use them.
@dsemy I did notice that the script seems to not run for every frame. Maybe tweaking fQuestDelayTime. However I did noticed this with 21.8 too
I don't think that's the issue, otherwise people experiencing issues would have the hotkeys work at least sometimes, and replacing it with IsKeyDown3 shouldn't fix the issue.
In any case, switching to an event-based approach should make this unnecessary anyway right?
well yes, but I'm still interested to know why it happens
Well, I got around to doing some testing and with an uncapped frame rate (I get around 500~ fps average) the hotkeys still work almost every time I press them, so unless the reports are coming from people with insane frame rates I'm pretty confident this isn't the issue.
The problem appears to be twofold - setting fQuestDelayTime in the GetGameRestarted condition seems to only sometimes apply, which I discovered by finding that it seemingly appears at random whether a newly generated character will be able to use hotkeys or not (after editing the mod to use IsKeyPressed3)
I had to both change everything to use IsKeyPressed3 and also move the fQuestDelayTime assignment outside of the if statement.
EDIT: FWIW Here's how I changed the script.
scn BetterMenuControlsQuestSCRIPT ; Main script for Better Menu Controls. Handles all functions. All variables not documented here are documented in the BetterMenuControls.ini file.
; General menu handling.
short iGUpKey
short bGUpKey
short iGLeftKey
short bGLeftKey
short iGDownKey
short bGDownKey
short iGRightKey
short bGRightKey
short iGEnterKey
short bGEnterKey
short iGModKey
short bGModKey
short iGMenuModeKey ; Used to store the current menu mode key. (Journal key)
short bGMenuModeKey
short bGModKey ; Used to check if the player is currently pressing the mod key.
float fQuestDelayTime ; Used to control how often the script runs.
ref rGPlayerReference ; Used to store the player reference.
begin MenuMode
set fQuestDelayTime to 0.1
if GetGameRestarted
if IsModLoaded "LINK.esp" != 1
RunBatchScript "Data\BetterMenuControls.ini"
endif
let rGPlayerReference := playerRef
endif
if (iGMenuModeKey != GetControl 15)
let iGMenuModeKey := GetControl 15
endif
if (IsKeyPressed3 iGModKey)
let bGModKey := 0
else
let bGModKey := 1
endif
if (IsKeyPressed3 iGUpKey && bGUpKey == 0 && bGModKey)
MenuTapKey 200
endif
if (IsKeyPressed3 iGLeftKey && bGLeftKey == 0 && bGModKey)
MenuTapKey 203
endif
if (IsKeyPressed3 iGDownKey && bGDownKey == 0 && bGModKey)
MenuTapKey 208
endif
if (IsKeyPressed3 iGRightKey && bGRightKey == 0 && bGModKey)
MenuTapKey 205
endif
if (IsKeyPressed3 iGEnterKey && bGEnterKey == 0 && bGModKey)
MenuTapKey 28
endif
let bGUpKey := IsKeyPressed3 iGUpKey
let bGLeftKey := IsKeyPressed3 iGLeftKey
let bGDownKey := IsKeyPressed3 iGDownKey
let bGRightKey := IsKeyPressed3 iGRightKey
let bGEnterKey := IsKeyPressed3 iGEnterKey
end
; Journal menu handling.
short iJStatsKey
short bJStatsKey
short iJInventoryKey
short bJInventoryKey
short iJMagicKey
short bJMagicKey
short iJQuestLogKey
short bJQuestLogKey
short bJStats ; Used to check if the stats key was pressed in the journal menu.
short bJInventory ; Used to check if the inventory key was pressed in the journal menu.
short bJMagic ; Used to check if the magic key was pressed in the journal menu.
short bJQuestLog ; Used to check if the questlog key was pressed in the journal menu.
short bJFromInventory ; Used to check if the open menu is the inventory menu or it originates from it.
short bJExit ; Used to check if the player exited a menu originating from the inventory.
begin MenuMode 1
if (bJFromInventory != 1)
let bJFromInventory := 1
endif
if (IsKeyPressed3 iJStatsKey && bJStatsKey == 0)
let bJStats := 1
TapControl 15
endif
if (IsKeyPressed3 iJInventoryKey && bJInventoryKey == 0)
let bJInventory := 1
TapControl 15
endif
if (IsKeyPressed3 iJMagicKey && bJMagicKey == 0)
let bJMagic := 1
TapControl 15
endif
if (IsKeyPressed3 iJQuestLogKey && bJQuestLogKey == 0)
let bJQuestLog := 1
TapControl 15
endif
let bJStatsKey := IsKeyPressed3 iJStatsKey
let bJInventoryKey := IsKeyPressed3 iJInventoryKey
let bJMagicKey := IsKeyPressed3 iJMagicKey
let bJQuestLogKey := IsKeyPressed3 iJQuestLogKey
end
; GameMode handling.
begin GameMode
if (bJFromInventory && bJExit)
let bJFromInventory := 0
let bJExit := 0
TapControl 15
endif
if (bJFromInventory)
let bJFromInventory := 0
endif
if (bJExit)
let bJExit :=0
endif
if (IsKeyPressed3 iJStatsKey && bJStatsKey == 0 || bJStats)
let bJStats := 0
TapKey 59
endif
if (IsKeyPressed3 iJInventoryKey && bJInventoryKey == 0 || bJInventory)
let bJInventory := 0
TapKey 60
endif
if (IsKeyPressed3 iJMagicKey && bJMagicKey == 0 || bJMagic)
let bJMagic := 0
TapKey 61
endif
if (IsKeyPressed3 iJQuestLogKey && bJQuestLogKey == 0 || bJQuestLog)
let bJQuestLog := 0
TapKey 62
endif
let bJStatsKey := IsKeyPressed3 iJStatsKey
let bJInventoryKey := IsKeyPressed3 iJInventoryKey
let bJMagicKey := IsKeyPressed3 iJMagicKey
let bJQuestLogKey := IsKeyPressed3 iJQuestLogKey
end
; Container menu handling.
short iCBTakeAllKey
short bCBTakeAllKey
short iCBNegotiateKey
short bCBNegotiateKey
begin MenuMode 1008
if (IsKeyPressed3 iCBTakeAllKey && bCBTakeAllKey == 0 && IsBarterMenuActive != 1 && GetMenuFloatValue "cont_background\page_layout\cont_contents\cont_button_take_all\visible" 1008 == 2)
ClickMenuButton "#32" 1008
endif
if (IsKeyPressed3 iCBNegotiateKey && bCBNegotiateKey == 0 && IsBarterMenuActive)
ClickMenuButton "#34" 1008
endif
if (IsKeyPressed3 iGMenuModeKey && bGMenuModeKey == 0 && IsBarterMenuActive)
ClickMenuButton "#33" 1008
endif
let bCBTakeAllKey := IsKeyPressed3 iCBTakeAllKey
let bCBNegotiateKey := IsKeyPressed3 iCBNegotiateKey
let bGMenuModeKey := IsKeyPressed3 iGMenuModeKey
end
; Negotiate menu handling.
short iNOkKey
short bNOkKey
short iNCancelKey
short bNCancelKey
begin MenuMode 1025
if (IsKeyPressed3 iNOkKey && bNOkKey == 0)
ClickMenuButton "#6" 1025
endif
if (IsKeyPressed3 iNCancelKey && bNCancelKey == 0)
ClickMenuButton "#7" 1025
endif
let bNOkKey := IsKeyPressed3 iNOkKey
let bNCancelKey := IsKeyPressed3 iNCancelKey
end
; Repair menu handling.
short iRPRepairAllKey
short bRPRepairAllKey
short iRPExitKey
short bRPExitKey
begin MenuMode 1035
if (IsKeyPressed3 iRPRepairAllKey && bRPRepairAllKey == 0 && GetMenuFloatValue "rep_button_repair_all\visible" 1035 == 2)
ClickMenuButton "#16" 1035
endif
if (IsKeyPressed3 iRPExitKey && bRPExitKey == 0)
if (iRPExitKey == iGMenuModeKey)
let bJExit := 1
endif
ClickMenuButton "#2" 1035
endif
let bRPRepairAllKey := IsKeyPressed3 iRPRepairAllKey
let bRPExitKey := IsKeyPressed3 iRPExitKey
end
; MessageBox menu handling.
short iMBOkKey
short bMBOkKey
short iMBCancelKey
short bMBCancelKey
begin MenuMode 1001
if (IsKeyPressed3 iMBOkKey && bMBOkKey == 0)
ClickMenuButton "#4" 1001
endif
if (IsKeyPressed3 iMBCancelKey && bMBCancelKey == 0)
ClickMenuButton "#5" 1001
endif
let bMBOkKey := IsKeyPressed3 iMBOkKey
let bMBCancelKey := IsKeyPressed3 iMBCancelKey
end
; Quantity menu handling.
short iQOkKey
short bQOkKey
short iQCancelKey
short bQCancelKey
begin MenuMode 1016
if (IsKeyPressed3 iQOkKey && bQOkKey == 0)
ClickMenuButton "#6" 1016
endif
if (IsKeyPressed3 iQCancelKey && bQCancelKey == 0)
ClickMenuButton "#7" 1016
endif
let bQOkKey := IsKeyPressed3 iQOkKey
let bQCancelKey := IsKeyPressed3 iQCancelKey
end
; Dialog menu handling.
short iDExitKey
short bDExitKey
short iDPersuadeKey
short bDPersuadeKey
short iDBarterKey
short bDBarterKey
short iDSpellBarterKey
short bDSpellBarterKey
short iDRepairKey
short bDRepairKey
short iDTrainingKey
short bDTrainingKey
short iDRechargeKey
short bDRechargeKey
short bDVisibility ; Used to check if dialog topics are visible
ref rDReference ; Used to store the reference of the actor the player is in dialog with.
begin MenuMode 1009
if (rDReference != GetActiveMenuRef 1009)
let rDReference := GetActiveMenuRef 1009
endif
let bDVisibility := GetMenuFloatValue "dialog_topics\visible" 1009
if (IsKeyPressed3 iDExitKey && bDExitKey == 0 && bDVisibility == 2 && GetMenuFloatValue "dialog_topics\dialog_button_layout\dialog_goodbye\visible" 1009 == 2)
ClickMenuButton "#3" 1009
endif
if (IsKeyPressed3 iDPersuadeKey && bDPersuadeKey == 0 && bDVisibility == 2 && GetMenuFloatValue "dialog_topics\dialog_button_layout\dialog_persuade\visible" 1009 == 2)
ClickMenuButton "#7" 1009
endif
if (IsKeyPressed3 iDBarterKey && bDBarterKey == 0 && bDVisibility == 2 && GetMenuFloatValue "dialog_topics\dialog_button_layout\dialog_barter\visible" 1009 == 2)
ClickMenuButton "#8" 1009
endif
if (IsKeyPressed3 iDSpellBarterKey && bDSpellBarterKey == 0 && bDVisibility == 2 && GetMenuFloatValue "dialog_topics\dialog_button_layout\dialog_spell_buy\visible" 1009 == 2)
ClickMenuButton "#16" 1009
endif
if (IsKeyPressed3 iDRepairKey && bDRepairKey == 0 && bDVisibility == 2 && GetMenuFloatValue "dialog_topics\dialog_button_layout\dialog_repair\visible" 1009 == 2)
ClickMenuButton "#12" 1009
endif
if (IsKeyPressed3 iDTrainingKey && bDTrainingKey == 0 && bDVisibility == 2 && GetMenuFloatValue "dialog_topics\dialog_button_layout\dialog_training\visible" 1009 == 2)
ClickMenuButton "#9" 1009
endif
if (IsKeyPressed3 iDRechargeKey && bDRechargeKey == 0 && bDVisibility == 2 && GetMenuFloatValue "dialog_topics\dialog_button_layout\dialog_recharge\visible" 1009 == 2)
ClickMenuButton "#13" 1009
endif
let bDExitKey := IsKeyPressed3 iDExitKey
let bDPersuadeKey := IsKeyPressed3 iDPersuadeKey
let bDBarterKey := IsKeyPressed3 iDBarterKey
let bDSpellBarterKey := IsKeyPressed3 iDSpellBarterKey
let bDRepairKey := IsKeyPressed3 iDRepairKey
let bDTrainingKey := IsKeyPressed3 iDTrainingKey
let bDRechargeKey := IsKeyPressed3 iDRechargeKey
end
; Persuasion menu handling.
short iPBribeKey
short bPBribeKey
short iPRotateKey
short bPRotateKey
short iPStartKey
short bPStartKey
short iPExitKey
short bPExitKey
begin MenuMode 1034
if (IsKeyPressed3 iPBribeKey && bPBribeKey == 0)
ClickMenuButton "#8" 1034
endif
if (IsKeyPressed3 iPRotateKey && bPRotateKey == 0)
ClickMenuButton "#24" 1034
endif
if (IsKeyPressed3 iPStartKey && bPStartKey == 0)
ClickMenuButton "#26" 1034
endif
if (IsKeyPressed3 iPExitKey && bPExitKey == 0)
ClickMenuButton "#9" 1034
endif
let bPBribeKey := IsKeyPressed3 iPBribeKey
let bPRotateKey := IsKeyPressed3 iPRotateKey
let bPStartKey := IsKeyPressed3 iPStartKey
let bPExitKey := IsKeyPressed3 iPExitKey
end
; Alchemy menu handling.
short iACreateKey
short bACreateKey
short iAExitKey
short bAExitKey
short iDebug
begin MenuMode 1040
if (IsKeyPressed3 iACreateKey && bACreateKey == 0 && (GetMenuFloatValue "alchemy_background\alchemy_button_create\user1" 1040) == 2)
ClickMenuButton "#14" 1040
endif
if (IsKeyPressed3 iAExitKey && bAExitKey == 0)
if (iAExitKey == iGMenuModeKey)
let bJExit := 1
endif
ClickMenuButton "#15" 1040
endif
let bACreateKey := IsKeyPressed3 iACreateKey
let bAExitKey := IsKeyPressed3 iAExitKey
end
; Enchantment menu handling.
short iECreateKey
short bECreateKey
short iEExitKey
short bEExitKey
begin MenuMode 1042
if (IsKeyPressed3 iECreateKey && bECreateKey == 0)
ClickMenuButton "#14" 1042
endif
if (IsKeyPressed3 iEExitKey && bEExitKey == 0)
ClickMenuButton "#15" 1042
endif
let bECreateKey := IsKeyPressed3 iECreateKey
let bEExitKey := IsKeyPressed3 iEExitKey
end
; Recharge menu handling.
short iRCExitKey
short bRCExitKey
begin MenuMode 1049
if (IsKeyPressed3 iRCExitKey && bRCExitKey == 0)
if (iRCExitKey == iGMenuModeKey)
let bJExit := 1
endif
ClickMenuButton "#3" 1049
endif
let bRCExitKey := IsKeyPressed3 iRCExitKey
end
; SigilStone menu handling.
short iSCreateKey
short bSCreateKey
short iSExitKey
short bSExitKey
begin MenuMode 1048
if (IsKeyPressed3 iSCreateKey && bSCreateKey == 0)
ClickMenuButton "#14" 1048
endif
if (IsKeyPressed3 iSExitKey && bSExitKey == 0)
if (iSExitKey == iGMenuModeKey)
let bJExit := 1
endif
ClickMenuButton "#15" 1048
endif
let bSCreateKey := IsKeyPressed3 iSCreateKey
let bSExitKey := IsKeyPressed3 iSExitKey
end
; SpellPurchase menu handling.
short iSPNegotiateKey
short bSPNegotiateKey
short iSPExitKey
short bSPExitKey
begin MenuMode 1037
if (IsKeyPressed3 iSPNegotiateKey && bSPNegotiateKey == 0)
ClickMenuButton "#6" 1037
endif
if (IsKeyPressed3 iSPExitKey && bSPExitKey == 0)
ClickMenuButton "#2" 1037
endif
let bSPNegotiateKey := IsKeyPressed3 iSPNegotiateKey
let bSPExitKey := IsKeyPressed3 iSPExitKey
end
; SpellMaking menu handling.
short iSMCreateKey
short bSMCreateKey
short iSMExitKey
short bSMExitKey
begin MenuMode 1041
if (IsKeyPressed3 iSMCreateKey && bSMCreateKey == 0)
ClickMenuButton "#14" 1041
endif
if (IsKeyPressed3 iSMExitKey && bSMExitKey == 0)
ClickMenuButton "#15" 1041
endif
let bSMCreateKey := IsKeyPressed3 iSMCreateKey
let bSMExitKey := IsKeyPressed3 iSMExitKey
end
; Training menu handling.
short iTTrainKey
short bTTrainKey
short iTExitKey
short bTExitKey
begin MenuMode 1028
if (IsKeyPressed3 iTTrainKey && bTTrainKey == 0 && (GetMenuFloatValue "Training_background\train_button_train\user1" 1028) == 2 && (GetMenuFloatValue "Training_background\train_button_train\visible" 1028) == 2)
ClickMenuButton "#6" 1028
endif
if (IsKeyPressed3 iTExitKey && bTExitKey == 0)
ClickMenuButton "#7" 1028
endif
let bTTrainKey := IsKeyPressed3 iTTrainKey
let bTExitKey := IsKeyPressed3 iTExitKey
end
; SleepWait menu handling.
short iSWCancelKey
short bSWCancelKey
short iSWSleepWaitKey
short bSWSleepWaitKey
begin MenuMode 1012
if (IsKeyPressed3 iSWCancelKey && bSWCancelKey == 0)
ClickMenuButton "#6" 1012
endif
if (IsKeyPressed3 iSWSleepWaitKey && bSWSleepWaitKey == 0)
ClickMenuButton "#5" 1012
endif
let bSWCancelKey := IsKeyPressed3 iSWCancelKey
let bSWSleepWaitKey := IsKeyPressed3 iSWSleepWaitKey
end
; Levelup menu handling.
short iLUExitKey
short bLUExitKey
begin MenuMode 1027
if (IsKeyPressed3 iLUExitKey && bLUExitKey == 0)
ClickMenuButton "#1" 1027
endif
let bLUExitKey := IsKeyPressed3 iLUExitKey
end
; EffectSetting menu handling.
short iESCancelKey
short bESCancelKey
short iESOkKey
short bESOkKey
begin MenuMode 1043
if (IsKeyPressed3 iESOkKey && bESOkKey == 0)
ClickMenuButton "#21" 1043
endif
if (IsKeyPressed3 iESCancelKey && bESCancelKey == 0)
ClickMenuButton "#23" 1043
endif
let bESOkKey := IsKeyPressed3 iESOkKey
let bESCancelKey := IsKeyPressed3 iESCancelKey
end
; Generic menu handling. (QuestAdd/SkillPerk)
short iGNOkContinueKey
short bGNOkContinueKey
short iGNMakeActiveKey
short bGNMakeActiveKey
begin MenuMode 1011
if (IsKeyPressed3 iGNOkContinueKey && bGNOkContinueKey == 0)
ClickMenuButton "#1" 1011
endif
if (IsKeyPressed3 iGNMakeActiveKey && bGNMakeActiveKey == 0)
ClickMenuButton "#2" 1011
endif
let bGNOkContinueKey := IsKeyPressed3 iGNOkContinueKey
let bGNMakeActiveKey := IsKeyPressed3 iGNMakeActiveKey
end
; Book menu handling.
short iBTakeKey
short bBTakeKey
short iBExitKey
short bBExitKey
begin MenuMode 1026
if (IsKeyPressed3 iBTakeKey && bBTakeKey == 0 && GetMenuFloatValue "user3" 1026 == 2)
ClickMenuButton "#32" 1026
endif
if (IsKeyPressed3 iBExitKey && bBExitKey == 0)
if (iBExitKey == iGMenuModeKey && bJFromInventory)
let bJExit := 1
CloseAllMenus
else
ClickMenuButton "#31" 1026
endif
endif
let bBTakeKey := IsKeyPressed3 iBTakeKey
let bBExitKey := IsKeyPressed3 iBExitKey
end
; TextEdit menu handling.
short iTEBackKey
short bTEBackKey
short iTEOkKey
short bTEOkKey
begin MenuMode 1051
if (IsKeyPressed3 iTEBackKey && bTEBackKey == 0)
ClickMenuButton "#3" 1051
endif
if (IsKeyPressed3 iTEOkKey && bTEOkKey == 0)
ClickMenuButton "#2" 1051
endif
let bTEBackKey := IsKeyPressed3 iTEBackKey
let bTEOkKey := IsKeyPressed3 iTEOkKey
end
; Class menu handling.
short iCLCreateKey
short bCLCreateKey
short iCLDoneKey
short bCLDoneKey
begin MenuMode 1030
if (IsKeyPressed3 iCLCreateKey && bCLCreateKey == 0)
ClickMenuButton "#5" 1030
endif
if (IsKeyPressed3 iCLDoneKey && bCLDoneKey == 0)
ClickMenuButton "#4" 1030
endif
let bCLCreateKey := IsKeyPressed3 iCLCreateKey
let bCLDoneKey := IsKeyPressed3 iCLDoneKey
end
; RaceSex menu handling
short iRSDoneKey
short bRSDoneKey
begin MenuMode 1036
if (IsKeyPressed3 iRSDoneKey && bRSDoneKey == 0)
ClickMenuButton "#90" 1036
endif
let bRSDoneKey := IsKeyPressed3 iRSDoneKey
end
; Skills menu handling.
short iSKBackKey
short bSKBackKey
short iSKDoneKey
short bSKDoneKey
begin MenuMode 1032
if (IsKeyPressed3 iSKBackKey && bSKBackKey == 0)
ClickMenuButton "#5" 1032
endif
if (IsKeyPressed3 iSKDoneKey && bSKDoneKey == 0)
ClickMenuButton "#4" 1032
endif
let bSKBackKey := IsKeyPressed3 iSKBackKey
let bSKDoneKey := IsKeyPressed3 iSKDoneKey
end
;<CSEBlock>
;<CSECaretPos> 9267 </CSECaretPos>
;</CSEBlock>
There's something odd there.
fQuestDelayTime and all your mods variables are declared in a quest script, and so they are all stored in the savefile
GetGameRestarted is true only ONCE per game session, as soon as you reach the Main Menu, and will stay true until your script checks its value the first time, then it will become false for the rest of the game session, until you quit the game, return to desktop and launch the executable again. In your script you check GetGameRestarted in a MenuMode block, without filters, so it will run in the very Main Menu, where you initialize stuff at startup, and then, when a savefile is selected, the values stored in the savefile will overwrite all values you initialized with GetGameRestarted, leaving you scratching your head (exception to this: it won't happen in a new game or in a savefile where your mod is not already installed, as there are no stored values yet). Also, it will only initialize once per session and won't reinitialize again if you reload, unless you shut the game, so the next loadings in the same session will only apply the values stored in the savefile. What I mean is: unless you can store your configurations for the whole session, independent from savefiles, this initialization may not work as expected.
I would recommend GetGameLoaded, which is true ONCE per game load, as soon as it finishes loading a savefile or starting a new game (which is right before the loading screen fade out), and will stay true until your script checks its value the first time, then it will become false until you load a savefile again or return to Main Menu and start a new game. Being it true only after game is loaded (so AFTER the values stored in the savefile have been loaded), you can safely put your initialization here, ensuring your new values wins over the savefile's values. Also, being it true again after every load it can reinitialize again, cleaning the environment after every load and preventing old savefile's values from leaking.
An example is:
Quest script attached to quest MyModQuest
scn MyModQuestScript
float fQuestDelayTime
Begin GameMode ;only run when you're outside all menu and you're controlling your PG if GetGameLoaded ;true once, the very first frame after game finished loading and you're controlling your PG let fQuestDelayTime := 10 ; stuff endif ; whatever End
_Despite the game being considered "loaded" right before the loading screen fade out, a GetGameLoaded check would not work (or would not work "consistently") in a `MenuMode 1007` block_
---
For a more "professional" approach you can set 2 event handlers ONCE in the main menu and it will automatically fire the initializers after every game load or new game.
### Warning: as with GetGameLoaded, new/load game handlers fire just before the loading screen fade out, so before any GameLoaded block have a chance to run, which has 2 consequences:
- Your GameMode blocks will always find the environment already initialized by the handlers.
- If your mod needs to interoperate with other mods, these mods may not have initialized yet (especially if the use the above "GetGameLoaded" solution), so avoid commands like GetFormFromMod, GetVariable and similar in an PostLoadGame handler function. IsModLoaded is fine, but if RunBatchScript run stuff from LINK.esp, I'm not sure if LINK will be ready for that.
> Quest script attached to quest MyModQuest
scn MyModQuestScript
float fQuestDelayTime
;we only need to set these handlers once per game session, as soon as we hit the Main Menu (1044) Begin MenuMode 1044 if eval GetGameRestarted ;It's perfect for this task! if eval GetOBSEVersion < 22 ;Put the minimum version here MessageBox "Error: OBSE v0022 or later is required" ;and here else SetEventHandler "OnNewGame" MyModHandlerOnNewGame SetEventHandler "PostLoadGame" MyModHandlerPostLoadGame endif endif End
> Object script:
scn MyModHandlerOnNewGame
Begin _Function {}
;just call the same handler for "save loaded", but you can also add specific code for "new game" here
Call MyModHandlerPostLoadGame 1
End
> Object script:
scn MyModHandlerPostLoadGame
;fn argument: if 1, game has been successfully loaded short success
Begin _Function { success } if success let myQuest.fQuestDelayTime := 10 ; stuff endif End
I appreciate the information, but this isn’t related to this issue since it also happens in my Better Saves mod which does this initialization in a GameMode block.
In any case the next version of both mods shouldn’t even need to change to fQuestDelayTime since I’m planning to switch to an event-handler based implementation for detecting key presses.
This function is used by Better Menu Controls mod, but looks like it's bugged.
More info you can find here:
https://forums.nexusmods.com/index.php?/topic/6131833-better-menu-controls/page-11#entry114554443