Hoamaii Posted January 12, 2020 Share Posted January 12, 2020 Guys, I need a set of fresh eyes to help me figure out why exactly one script loops indefinitely while a fairly similar one doesn't... Here's the script I attached to a custom furniture in my test cell: ;============================ LOOPING SCRIPT ================================================================== Quest Property _HBT_PlayerPackageQuest Auto Actor Property PlayerRef Auto Event OnLoad() Self.BlockActivation(true) GoToState("Waiting") EndEvent State Waiting Event OnActivate(ObjectReference akActionRef) If akActionRef == PlayerRef Game.DisablePlayerControls(abMovement=true, abFighting=true, abCamSwitch=true, abLooking=false, abSneaking=true, abMenu=true, abActivate = true, abJournalTabs=true) Game.SetPlayerAIDriven(True) _HBT_PlayerPackageQuest.Start() GoToState("Sitting") EndIf EndEvent EndState State Sitting Event OnBeginState() RegisterForControl("Activate") Utility.Wait(1.5) GoToState("Exiting") EndEvent EndState State Exiting Event OnControlDown(String Control) If Control == "Activate" UnRegisterForControl("Activate") Self.Activate(PlayerRef, true) ; it loops whether I call self.activate here, or don't call it and then exit pressing forward _HBT_PlayerPackageQuest.Stop() Game.SetPlayerAIDriven(false) Game.EnablePlayerControls() Utility.Wait(5.9) ; time for the exit animation to play Debug.SendAnimationEvent(PlayerRef, "IdleForceDefaultState") Utility.Wait(3.5) GoToState("Waiting") EndIf EndEvent Event OnEndState() UnregisterForAllControls() UnregisterForAllKeys() Debug.Notification("back to waiting") EndEvent EndState ;========================= END LOOPING SCRIPT ===================================================================== This loops indefintely - no matter what other object I activate after that will trigger this script to run again in endless loops. In an attempt to figure out why, I added a side script to the same furniture to count "activations" - that script traces 3 activations each time the looping script runs: "count 0" when my package "Sit" procedure activates the furn, "count 1" when my script goes to "Sitting" state, and "count 2" OnControlDown(...) in the "Exiting" State. No idea what triggers activation in the "Sitting" state. The multiple calls for "UnregisterForControl" and "UnregisterForAllControls" are due to the fact that I first thought my script did no properly Unregister, and the strange thing is my log never complained about them when it should have. And obviously, the "onEndState" event runs because I do get my notification to show in game. Now this slight variation of the same script does not loop: ;============================ WORKING SCRIPT ================================================================== Quest Property _HBT_PlayerPackageQuest Auto Actor Property PlayerRef Auto Event OnLoad() Self.BlockActivation(true) GoToState("Waiting") EndEvent State Waiting Event OnActivate(ObjectReference akActionRef) ; counts for activate 0 in count side script If akActionRef == PlayerRef Game.DisablePlayerControls(abMovement=true, abFighting=true, abCamSwitch=true, abLooking=false, abSneaking=true, abMenu=true, abActivate = true, abJournalTabs=true) Game.SetPlayerAIDriven(True) _HBT_PlayerPackageQuest.Start() GoToState("Sitting") EndIf EndEvent EndState State Sitting ; strange: I get an activate count here too - count 1 Event OnBeginState() RegisterForControl("Activate") Utility.Wait(1.5) GoToState("Exiting") EndEvent EndState State Exiting Event OnControlDown(String Control) ; if I don't self.activate here, I get a count 3 when pressing forward to exit If Control == "Activate" UnRegisterForControl("Activate") ; Self.Activate(PlayerRef, true) ; loops again if I keep the self.Activate here, need to press forward to exit if I remove it. Debug.SendAnimationEvent(PlayerRef, "_HBr_BedrollFurn_Exit") ; remove the need to press forward which seems to feed the loop _HBT_PlayerPackageQuest.Stop() Game.SetPlayerAIDriven(false) Game.EnablePlayerControls() Utility.Wait(5.9) ; time for the exit animation to play Debug.SendAnimationEvent(PlayerRef, "IdleForceDefaultState") UnRegisterForControl("Activate") RegisterForSingleUpdate(3.5) EndIf EndEvent Event OnUpdate() UnregisterForUpdate() GoToState("Waiting") EndEvent Event OnEndState() UnregisterForUpdate() UnregisterForAllControls() UnregisterForAllKeys() Debug.Notification("Package stopped - back to waiting") EndEvent EndState ;========================= END WORKING SCRIPT ===================================================================== The only difference between this and the looping script is I use a "SingleUpdate" instead of calling "Wait(...)" - and I remove the "self.activate(...)" onControlDown(...) to replace it with an SAE event (but that never was enough to keep my first script from looping). Notice I still make multiple calls to Unregister for controls and keys - my log still does not complain about them either, and the reason I'm keeping them is I have no idea which call finally does the job!.. I have a hard time figuring out why my first script loops - or even why the scond script does not loop!.. My best guess is functions are running way faster than activations are registered and any activation in the Exiting state (either pressing the activate key or pressing "forward" which counts as an activation for furnitures) resulted in being simultaneously interpreted as activation in the "Waiting" state... And possibly calling "onUpdate" rather than "wait" stalls the script long enough to prevent that. Still, that doesn't explain why with the looping script, my PC could still exit the furniture and wander freely until activating any other random chair would cause the quest to start again...Nor why should pressing "forward" be interpreted as "activate" when I did not register for that control?!... Or why, when using console while the script is running, pressing the "enter" key after any command is also interpreted as activation... Feels like "DisablePlayerControls(...)" and RegisterForControl(...) are not really doing their jobs... It may also be worth mentioning that this is all tested in a test cell I coc to. Could anybody with better coding experience enlighten me about what I'm obviously missing here?.. Many thanks in advance!.. PS. On a side note: these scripts' only use is to test my custom furniture's direct in-game activation against package-induced activation - I don't intend to use any of this in the final mod. Link to comment Share on other sites More sharing options...
maxarturo Posted January 12, 2020 Share Posted January 12, 2020 I'm too tired to read all this, i'm going to sleep now... But, try putting a "State Busy" between each activation and optionaly a "Wait(Reactivation)", so that the exiting from the furniture is ensured first before any other activation is possible. Link to comment Share on other sites More sharing options...
maxarturo Posted January 13, 2020 Share Posted January 13, 2020 I encounter the same issue while trying to make a simple script that adds a "Blessing" when the Player sits on a specific throne. It seems that when you enter and exit, it will send the Activate event each time, in my case was adding the blessing when Player sits and when Player stands up, so i seperate the script into 2 STATES. Auto State WaitingPlayer Event OnActivate(ObjectReference akActionRef) Blessing.Cast(akActionRef, akActionRef) if akActionRef == Game.GetPlayer() AltarRemoveMsg.Show() BlessingMessage.Show() GoToState("StandingUp") EndIf EndEvent EndState State StandingUp Event OnActivate(ObjectReference akActionRef) if akActionRef == Game.GetPlayer() GoToState("WaitingPlayer") EndIf EndEvent EndState I hope that this can help you resolve your issue. Link to comment Share on other sites More sharing options...
ReDragon2013 Posted January 13, 2020 Share Posted January 13, 2020 maxarturo wrote:"I encounter the same issue while trying to make a simple script that adds a "Blessing" when the Player sits on a specific throne." Related to the info by this link https://www.creationkit.com/index.php?title=OnSit_-_ActorI have written a small effect script that could make the job. I am using EffectShader directly without spell cast here.xyzPlayerSittingEffect Scriptname xyzPlayerSittingEffect extends ActiveMagicEffect ; https://forums.nexusmods.com/index.php?/topic/8311813-looping-script-unsure-why/ EffectShader PROPERTY Blessing auto ; * run effect when ; IsInFurnitureState == 1.00 ; <furniture> == <throne> ; * stop effect when ; script handling ; -- EVENTs -- EVENT OnEffectStart(Actor akTarget, Actor akCaster) Debug.Trace(" OnEffectStart() - target = " +akTarget+ ", " +self) AltarRemoveMsg.show() Blessing.Play(akTarget as ObjectReference, -1) ; blessing, when player is sitting BlessingMessage.show() WHILE (TRUE) IF Game.IsLookingControlsEnabled() Blessing.Stop() RETURN ; - STOP - stop FX when player is leaving the throne ENDIF ; ---------------------- Utility.Wait(1.0) ENDWHILE ENDEVENT EVENT OnEffectFinish(Actor akTarget, Actor akCaster) Debug.Trace(" OnEffectFinish() - has been reached.. " +self) ENDEVENT Link to comment Share on other sites More sharing options...
ReDragon2013 Posted January 13, 2020 Share Posted January 13, 2020 (edited) Hoamaii look at this link: https://www.creationkit.com/index.php?title=IsFurnitureInUse_-_ObjectReference If we make your looping script a bit more compact, you will see next:xyzPackageScript Scriptname xyzPackageScript extends ObjectReference ; https://forums.nexusmods.com/index.php?/topic/8311813-looping-script-unsure-why/ Quest PROPERTY _HBT_PlayerPackageQuest auto ; quest ; Hoamaii wrote: ; "Notice I still make multiple calls to Unregister for controls and keys - my log ; still does not complain about them either, and the reason I'm keeping them is ; I have no idea which call finally does the job!" ; -- EVENTs -- EVENT OnLoad() self.BlockActivation(TRUE) gotoState("Waiting") ; ### STATE ### ENDEVENT ;============================== State Waiting ;============ EVENT OnActivate(ObjectReference akActionRef) IF (akActionRef == Game.GetPlayer() as ObjectReference) ELSE RETURN ; - STOP - not player activated ENDIF ;--------------------- Game.DisablePlayerControls(abMovement=true, abFighting=true, abCamSwitch=true, abLooking=false, abSneaking=true, abMenu=true, abActivate = true, abJournalTabs=true) Game.SetPlayerAIDriven(TRUE) _HBT_PlayerPackageQuest.Start() ;;; gotoState("Sitting") ; ### STATE ### RegisterForControl("Activate") ; SKSE required !!! Utility.Wait(1.5) gotoState("Exiting") ; ### STATE ### ENDEVENT ;======= endState ;============================== State Exiting ;============ EVENT OnControlDown(String Control) ; SKSE only !!! ; Skyrim wiki: "If only one key will be registered for the form, such differentiation is not necessary." ;; UnregisterForAllControls() UnRegisterForControl("Activate") ; SKSE required !!! ; it loops whether I call self.activate here, or do not call it and then exit pressing forward self.Activate(Game.GetPlayer() as ObjectReference, TRUE) _HBT_PlayerPackageQuest.Stop() Game.SetPlayerAIDriven(False) Game.EnablePlayerControls() Utility.Wait(5.9) ; time for the exit animation to play Debug.SendAnimationEvent(Game.GetPlayer() as ObjectReference, "IdleForceDefaultState") Utility.Wait(3.5) gotoState("Waiting") ; ### STATE ### ;;; UnregisterForAllControls() ;;; UnregisterForAllKeys() Debug.Notification("back to waiting") ENDEVENT ;======= endState EVENT OnActivate(ObjectReference akActionRef) IF (akActionRef == Game.GetPlayer() as ObjectReference) ELSE RETURN ; - STOP - not player activated ENDIF ;--------------------- Game.DisablePlayerControls(abMovement=true, abFighting=true, abCamSwitch=true, abLooking=false, abSneaking=true, abMenu=true, abActivate = true, abJournalTabs=true) RegisterForControl("Activate") ; SKSE required !!! ; --- Utility.Wait(1.5) ; *** Every code after wait() is paused, until object will be closed or player has leaving them *** ; --- gotoState("Exiting") ENDEVENTabove you'll see the culprit, OnBeginState() does not release the running OnActivate() thread. OnUpdate() runs parallel with own thread registered before. Edited January 13, 2020 by ReDragon2013 Link to comment Share on other sites More sharing options...
maxarturo Posted January 13, 2020 Share Posted January 13, 2020 Thanks for that ReDragon, but i resolved the issue with my little script, now the Spell Blessing is only added when the player sit on the specific throne. Link to comment Share on other sites More sharing options...
Hoamaii Posted January 13, 2020 Author Share Posted January 13, 2020 Thanks guys for taking the time to look at this!.. @ ReDragon2013: thanks a lot for your help - if I understand you correctly, after disabling player's controls, any further onActivate(...) event the furniture might receive would return false if "akactionRef != Game.GetPlayer()". And not accounting for potential onActivate events in my "Sitting" state was the culprit? Is that what you're saying? See, when I added my "ActivationCounting" script to my furn and realized it was receiving an activate event in the sitting state, I added an empty "onActivate(...)" event to that state - which made no difference whatsoever, it was still looping. Should I have conditioned that event even though at this stage of my testing, only the player can use that furniture? Sorry about that but English is not my native language, so I'm not sure I understand what you mean by "Every code after wait() is paused, until object will be closed or player has left them". What do you mean by "object will be closed"? What exactly is an "object" here? The line of code, is that it? Or something else? My understanding was Wait(5.5) pauses the script for 5.5" (and the state you're in as well), to resume the stack on the next line of code after that (thought that duration is not very precise in game, I know). So I was assumingt my script would remain in the same state as long as wait() was not done, pretty much like using a "while" loop will pause your code until one condition returns false - am I missing something here? Thanks again for your help, ReDragon2013!.. @Maxarturo: yep, had exactly the same issue you had when I was working on my Clearsky mod. Turned out some furniture receive 2 activate events when activated by the player, one on enter and one on exit (and even yet a 3rd one if you exited pressing "E" instead of forward) - and only one activate event when used by NPCs. So if they're designed to work for both, they need to be scripted. If I remember correctly, I used a bool to get my script working for both, but same logic as yours: adding conditions. That's what I started to do first with my looping script but that did not stop it from looping anyway... Then suddenly, one stupid version worked!.. That's when I decided I needed help... ;) Thanks Max! Link to comment Share on other sites More sharing options...
maxarturo Posted January 14, 2020 Share Posted January 14, 2020 (edited) "and even yet a 3rd one if you exited pressing "E" instead of forward" Ahhhhgggrrr....!!!!...!!!...!!! I checked this issue before leaving in the morning, and goddamn it !!???.... it does send 2 Activate Events when you press "E" while standing up from the throne. Stupid Beth progrmmers...!!@#$...!!!*&%@#...!!!*&^%#$@ Thanks for mention this, it didn't occur to me, not even once to press "E" while testing, and i thought i was done with this... Edited January 14, 2020 by maxarturo Link to comment Share on other sites More sharing options...
SurfsideNaturals Posted January 14, 2020 Share Posted January 14, 2020 (edited) Hey Maxaturo, Using states with furniture is tricky. Using get sit state can be useful. https://www.creationkit.com/index.php?title=GetSitState_-_Actor Event OnActivate(ObjectReference akActionRef) If (AkActionRef As Actor).GetSitState() > 2 Return EndIf Blessing.Cast(akActionRef, akActionRef) if akActionRef == Game.GetPlayer() Else Return EndIf AltarRemoveMsg.Show() BlessingMessage.Show() EndIf EndEvent If you were going to use states you could do something like this. Event OnActivate(ObjectReference akActionRef) Blessing.Cast(akActionRef, akActionRef) if akActionRef == Game.GetPlayer() AltarRemoveMsg.Show() BlessingMessage.Show() RegisterForSingleUpdate(60.0) GoToState("StandingUp") EndIf EndEvent State StandingUp Event OnActivate(ObjectReference akActionRef) if akActionRef == Game.GetPlayer() Else Blessing.Cast(akActionRef, akActionRef) EndIf EndEvent EndState Event OnUpdate() GoToState("") EndEvent Edited January 14, 2020 by SurfsideNaturals Link to comment Share on other sites More sharing options...
maxarturo Posted January 14, 2020 Share Posted January 14, 2020 Thanks SurfsideNaturals for that. But i've already made a script that i'm sure it'll work, i'll test it in around 1,5 hours when i get home. I just added a "State Busy", so this way the first activetion send will exit the player from the throne, and the second activation send will be in the "State Busy" so it'll do nothing. I know Hoamaii that you have already solved this issue, i'm posting this just in case someone else might encounter the same issue and needs it. Auto State WaitingPlayer Event OnActivate(ObjectReference akActionRef) if akActionRef == Game.GetPlayer() GoToState("Busy") Utility.Wait(1.5) Blessing.Cast(akActionRef, akActionRef) BlessingMessage.Show() GoToState("StandingUp") EndIf EndEvent EndState State StandingUp Event OnActivate(ObjectReference akActionRef) GoToState("Busy") if akActionRef == Game.GetPlayer() Utility.Wait(1.5) EndIf GoToState("WaitingPlayer") EndEvent EndState State Busy Event OnActivate(ObjectReference akActionRef) ;Do nothing. EndEvent EndState Link to comment Share on other sites More sharing options...
Recommended Posts