llde / xOBSE

Oblivion Script extender source
251 stars 36 forks source link

OnKeyDown is bugged #172

Open sulez745 opened 2 years ago

sulez745 commented 2 years ago

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

ghost commented 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.

llde commented 1 year ago

Maybe an issue with the disticion of Real and simulated presses.

ghost commented 1 year ago

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.

llde commented 1 year ago

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.

ghost commented 1 year ago

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.

llde commented 1 year ago

@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

ghost commented 1 year ago

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?

llde commented 1 year ago

well yes, but I'm still interested to know why it happens

ghost commented 1 year ago

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.

elizagamedev commented 1 year ago

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>
Forlini91 commented 1 year ago

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

ghost commented 1 year ago

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.