Jump to content

[LE] Looping script - unsure why?


Hoamaii

Recommended Posts

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

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

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

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_-_Actor

I 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

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")
ENDEVENT

above you'll see the culprit, OnBeginState() does not release the running OnActivate() thread. OnUpdate() runs parallel with own thread registered before.

Edited by ReDragon2013
Link to comment
Share on other sites

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

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

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

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

  • Recently Browsing   0 members

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