Jump to content

Skyrim Papyrus - Exit Furnitur & Put Actor Behind it


Recommended Posts

I have tried to wrap my head around making a papyrus script to move a actor (player or NPC) behind a chair when they exit it so not as to get stuck against a table that is in front of it.

I've tried GetPosition, GetAngle, SetPosition  & SetAngle but can't seem to grasp how to grab the enter / exit event.  I thought to track GetSitting of the ActorReference to then SetPosition when that state is registered as "standing",  but nothing I have poorly conjured has compiled.

 

Scriptname ExitChairBehind extends ObjectReference

Event OnActivate(ObjectReference akActionRef)
;Get the Actor who activated
Actor akActivator = akActionRef.GetActorReference()
;Get Rotation of the furniture
Float fObjectAngle = GetAngle()
;Get Current XYZ pos of Furniture
Float fObjectPos = GetPosition()

;Get Activated Actor Sitting State
ActorID.GetSitting()

endEvent

Event OnActivate(Objectreference akActionRef)
If GetSitting = 0
AkActor SetPosition(-1, -1, -1)
;SetPosition should be relative to the furniture position, and opposite its current Z rotation to X or Y Position. 
 
EndEvent


This isn't a completed script as I've tried to scrub out stuff that was just not correct.  I realize this is very complicated for someone who isn't good at coding.  So any help would be appreciated, or if someone can write this, even better. I am frustrated at this point.

Link to comment
53 minutes ago, Nexussfire said:

I have tried to wrap my head around making a papyrus script to move a actor (player or NPC) behind a chair when they exit it so not as to get stuck against a table that is in front of it.

I've tried GetPosition, GetAngle, SetPosition  & SetAngle but can't seem to grasp how to grab the enter / exit event.  I thought to track GetSitting of the ActorReference to then SetPosition when that state is registered as "standing",  but nothing I have poorly conjured has compiled.

 

Scriptname ExitChairBehind extends ObjectReference

Event OnActivate(ObjectReference akActionRef)
;Get the Actor who activated
Actor akActivator = akActionRef.GetActorReference()
;Get Rotation of the furniture
Float fObjectAngle = GetAngle()
;Get Current XYZ pos of Furniture
Float fObjectPos = GetPosition()

;Get Activated Actor Sitting State
ActorID.GetSitting()

endEvent

Event OnActivate(Objectreference akActionRef)
If GetSitting = 0
AkActor SetPosition(-1, -1, -1)
;SetPosition should be relative to the furniture position, and opposite its current Z rotation to X or Y Position. 
 
EndEvent


This isn't a completed script as I've tried to scrub out stuff that was just not correct.  I realize this is very complicated for someone who isn't good at coding.  So any help would be appreciated, or if someone can write this, even better. I am frustrated at this point.

 

Problem:  Actor gets stuck in front of chair when standing.

 

Solution 1) Use the correct kind of chair for the area.  Chairs have up to three approach/exit positions (left, right, front).  Behind is not one of them.  Sounds like a "front" type chair is being used where a left/right chair would be needed instead.  Look at the chair names in CK or xEdit and you'll see "Chair01L" meaning left-side entry/exit, "Chair01R", etc.  If it has no letter, it is usually using all three approaches.  Replacing this chair with one that has the correct approach angles (and doesn't have front) may fix the issue.

 

Solution 2) Re-navmesh the area or tweak the table position.  Assuming the chair isn't the problem then in general the AI can find its own way around (may teleport if it can't) if the area is set up right.  That you are having this issue is a sign that the area itself is messed up in relation to the furniture.

 

"Solution?" 3) Create a script-based (potentially performance-reducing) "band-aid" to fix something better solved with a non-scripted solution.

 

Given that I don't think its a great solution, I'm only going to point out some of the issues with the script:

- Two OnActivate() Events likely won't trigger correctly.

- "If GetSitting" should be == 0 and would need to be called on the actor, not the furniture/objectreference.

- If the fObjectAngle etc need to be called by a separate function/event, they need to be initially defined outside of it, so put "float fObjectAngle" etc under the scriptname section and feed their values from the event.

- GetPosition() needs to be linked to something to work, so "self.Getposition()" should return the position of whatever objectreference the script gets attached to.

 

tl;dr/Conclusion:  Don't script this.  The issue is the chair.  Or the navmesh.  Or both.

Edited by Seijin8
Link to comment

One side has the chairs fairly close to the table in this example screenshot. They are custom chairs, I've changed the entry flags to Left / Right. The issue is with that is the exit does not happen at all, but rather just makes the exiting actor (player at the moment) just stand on the chair.  Front before would exit correctly, however even  with a LOT of unnatural space between chair & table it'd be easy to be stuck between them.

This is fairly specific use case. A simple solution to what I want is to place X markers for each chair and then move actors to them when they get up. Which requires scripting.  I know benches are usually used for this kind of arrangement in the game but that's not really the aesthetic I want here, and why I am asking for help on scripting.

If its really not possible via script then I will forgo the chairs I guess.

 

ChairTableArrangement.jpg

Edited by Nexussfire
Link to comment
22 minutes ago, Nexussfire said:

One side has the chairs fairly close to the table in this example screenshot. They are custom chairs, I've changed the entry flags to Left / Right. The issue is with that is the exit does not happen at all, but rather just makes the exiting actor (player at the moment) just stand on the chair.  Front before would exit correctly, however even  with a LOT of unnatural space between chair & table it'd be easy to be stuck between them.

This is fairly specific use case. A simple solution to what I want is to place X markers for each chair and then move actors to them when they get up. Which requires scripting.  I know benches are usually used for this kind of arrangement in the game but that's not really the aesthetic I want here, and why I am asking for help on scripting.

If its really not possible via script then I will forgo the chairs I guess.

 

 

 

Fair enough on the setup.

 

IRL the chairs get moved.  In game, that really isn't a thing.  One solution is to use the bench entry animation for the middle chairs (look like the rest can be accessed normally from the sides) and ignore the actor clipping through it.  I dunno if that is more or less realistic than chairs that are indestructibly linked to the spacetime continuum or not.

 

Seeing what you're shooting for, adding a linkedref XMarker to the chair simplifies the scripting, but honestly if you're going to script things anyway...

 

1) Start with the middle chairs turned sideways, say using only a right entry.

2) Make a triggerbox around the chair with a script that watches for NPCs entering and then checks periodically if they are sitting down.

3) Once the actor finishes the sitting animation, rotate the chair 90° to face the table

-- I don't know if the actor will automatically turn with the chair.  If not, maybe either manually rotate them too or use actor.moveto(Chair) to reset them on the moved chair.  May need to experiment with this.

4) Triggerbox now looks to see if the actor is getting up (should still try to exit sideways)

5) If the actor is moving, turn the chair -90° to original position and do the same trick again if they are getting stuck.  They should now have a way to exit smoothly without hitting the table or getting stuck.

 

Keep in mind "actor.moveto(furniture)" results in them "using" the furniture.  If used on a bed they go right to the laying part of the animation, skipping the enter animation.  Same with chairs.

Edited by Seijin8
Link to comment
Quote

-- I don't know if the actor will automatically turn with the chair.  If not, maybe either manually rotate them too or use actor.moveto(Chair) to reset them on the moved chair.  May need to experiment with this.



For rotating the actor sitting I believe there is
 

; Look 90 degrees to the left
Game.SetSittingRotation(-90.0)



I haven't the slightest clue how to do any of that, however. My understanding of writing correct Papyrus is primitive. I get what the functions are for the most part but putting it all together right isn't something I am doing.

Seems like more work to rotate the chairs in place? I was trying to figure out what the listen event is for furniture being used is. It is a marker that creates a animated idle state so I thought it'd be easiest to get the exit event from the marker, wait a few seconds for the anim to play and then just teleport actor to Xmarker reference via Script Property that lets me pick which marker to move them to.

There is

IsFurnitureMarkerInUse

Example
; Is the first marker on the bed in use (or someone has reserved it)?
if Bed.IsFurnitureMarkerInUse(0)
  Debug.Trace("Bed marker 0 is being used")
endIf



Though I am not certain on how to call on this without having the chairs constantly script ticking away to see if anything has used them recently, which from what I've read would be a rather dirty script.

Also I have been searching for the relevant syntax on this page https://www.creationkit.com/index.php?title=List_of_Papyrus_Functions

Edited by Nexussfire
Link to comment
5 hours ago, Nexussfire said:



For rotating the actor sitting I believe there is
 

; Look 90 degrees to the left
Game.SetSittingRotation(-90.0)



I haven't the slightest clue how to do any of that, however. My understanding of writing correct Papyrus is primitive. I get what the functions are for the most part but putting it all together right isn't something I am doing.

Seems like more work to rotate the chairs in place? I was trying to figure out what the listen event is for furniture being used is. It is a marker that creates a animated idle state so I thought it'd be easiest to get the exit event from the marker, wait a few seconds for the anim to play and then just teleport actor to Xmarker reference via Script Property that lets me pick which marker to move them to.

There is

IsFurnitureMarkerInUse

Example
; Is the first marker on the bed in use (or someone has reserved it)?
if Bed.IsFurnitureMarkerInUse(0)
  Debug.Trace("Bed marker 0 is being used")
endIf



Though I am not certain on how to call on this without having the chairs constantly script ticking away to see if anything has used them recently, which from what I've read would be a rather dirty script.

Also I have been searching for the relevant syntax on this page https://www.creationkit.com/index.php?title=List_of_Papyrus_Functions

 

 

Okay, keeping in mind I just woke up and haven't had my full measure of caffeine yet... the general papyrus for it would be something like this:

 

Scriptname ChairRotationScript extends ObjectReference
;attach this to a triggerbox

ObjectReference property ChairToRot auto ; the chair

Actor aSitting ; whoever comes into the triggerbox
bool bRot = false ; whether or not the chair has been rotated
float fT = 1.5 ; arbitrary time (in seconds) for script cycles, easier to adjust here than everywhere
float fRot = 500.0 ; catches original rotation amount, this nonsense value is a stand-in saying it hasn't been set yet ; could also just feed this manually
float fWind ; the angleZ we want the chair to rotate to

Event OnTriggerEnter(ObjectReference TriggerRef)
    aSitting = TriggerRef as Actor
    If aSitting
        If fRot != 500.0
            fRot = ChairToRot.GetAngleZ()
            fWind = (fRot - 90.0)
            If fWind < 0.0
                fWind += 360.0 ; should make sure the values are between 0 and 359.999
            EndIf
        EndIf
        RegisterForSingleUpdate(fT)
    EndIf
EndEvent

Event OnTriggerLeave(ObjectReference TriggerRef)
    If aSitting == TriggerRef
        UnregisterForUpdate()
        If bRot
            Rotchair(false, false)
        EndIf
    EndIf
EndEvent

Event OnUpdate()
    If bRot ; already rotated
        If aSitting.GetSitState() != 3 ; no longer sitting
            RotChair(false)
        EndIf
    Else ; not rotated
        If aSitting.GetSitState() == 3 ; is sitting
            RotChair(true)
        EndIf
    EndIf
    RegisterForSingleUpdate(fT)
EndEvent

Function RotChair(bool bWindLeft, bool bMoveActor = true)
    If bWindLeft
        If ChairToRot.GetAngleZ() == fRot
            ChairToRot.SetAngle(0.0, 0.0, fWind)
            ;If in testing, the actor automatically moves to the chair's new rotation, this can be deleted
            Utility.Wait(0.5)
            aSitting.MoveTo(ChairToRot)
            ;end deletion segment
        EndIf
        bRot = true
    Else
        If ChairToRot.GetAngleZ() == (fWind)
            ChairToRot.SetAngle(0.0, 0.0, fWind)
            ;If in testing, the actor automatically moves to the chair's new rotation, this can be deleted
            If bMoveActor
                Utility.Wait(0.5)
                aSitting.MoveTo(ChairToRot)
            EndIf
            ;end deletion segment
        EndIf
        bRot = false
    EndIf
EndFunction

 

This will need extensive testing of course.  No doubt there's a simpler way to do this, too.

 

EDIT: My code sucks...

 

Edited by Seijin8
Link to comment
55 minutes ago, Seijin8 said:

 

 

Okay, keeping in mind I just woke up and haven't had my full measure of caffeine yet... the general papyrus for it would be something like this:

 

Scriptname ChairRotationScript extends ObjectReference
;attach this to a triggerbox

ObjectReference property ChairToRot auto ; the chair

Actor aSitting ; whoever comes into the triggerbox
bool bRot = false ; whether or not the chair has been rotated
float fT = 1.5 ; arbitrary time (in seconds) for script cycles, easier to adjust here than everywhere
float fRot = 500.0 ; catches original rotation amount, this nonsense value is a stand-in saying it hasn't been set yet ; could also just feed this manually
float fWind ; the angleZ we want the chair to rotate to

Event OnTriggerEnter(ObjectReference TriggerRef)
    aSitting = TriggerRef as Actor
    If aSitting
        If fRot != 500.0
            fRot = ChairToRot.GetAngleZ()
            fWind = (fRot - 90.0)
            If fWind < 0.0
                fWind += 360.0 ; should make sure the values are between 0 and 359.999
            EndIf
        EndIf
        RegisterForSingleUpdate(fT)
    EndIf
EndEvent

Event OnTriggerLeave(ObjectReference TriggerRef)
    If aSitting == TriggerRef
        UnregisterForUpdate()
        If bRot
            Rotchair(false, false)
        EndIf
    EndIf
EndEvent

Event OnUpdate()
    If bRot ; already rotated
        If aSitting.GetSitState() != 3 ; no longer sitting
            RotChair(false)
        EndIf
    Else ; not rotated
        If aSitting.GetSitState() == 3 ; is sitting
            RotChair(true)
        EndIf
    EndIf
    RegisterForSingleUpdate(fT)
EndEvent

Function RotChair(bool bWindLeft, bool bMoveActor = true)
    If bWindLeft
        If ChairToRot.GetAngleZ() == fRot
            ChairToRot.SetAngle(0.0, 0.0, fWind)
            ;If in testing, the actor automatically moves to the chair's new rotation, this can be deleted
            Utility.Wait(0.5)
            aSitting.MoveTo(ChairToRot)
            ;end deletion segment
        EndIf
        bRot = true
    Else
        If ChairToRot.GetAngleZ() == (fWind)
            ChairToRot.SetAngle(0.0, 0.0, fWind)
            ;If in testing, the actor automatically moves to the chair's new rotation, this can be deleted
            If bMoveActor
                Utility.Wait(0.5)
                aSitting.MoveTo(ChairToRot)
            EndIf
            ;end deletion segment
        EndIf
        bRot = false
    EndIf
EndFunction

 

This will need extensive testing of course.  No doubt there's a simpler way to do this, too.

 

EDIT: My code sucks...

 

Well your code compiles which is more than mine does so not really.


However I do not see anything happening as intended when said compiled script is applied to a triggerbox and linking the relative chair in script properties.  The character sometimes is facing to the left or the right more after a few stumbles out of it, but the chair itself never moves / rotates.  And the exit still just boots the character to standing on top of the chair.  This is such a over-complicated process.

What I think will need to happen is I will need to attach the chairs to a RotateHelper  (Specifically : DLC2DunFahlbtharzRotateHelper90CW)  Which will force move any static object attached to it like an animated object. I have no idea what will happen doing this to a furniture object, may do nothing... or the game might have an aneurysm.  But I would still need a script with an event that paused the animation to get into the chair, rotate it 90*, then play the anim to sit, then play the rotatehelper to "close" again from open.

Thank you for providing some help, I appreciate it.



Edit: The exit animation from left or right seems to be related to the furniture marker I used for the custom chairs and the arms of the chair being in the way. Not much of a fix. So it is Front Anim only that works correctly.  I may forgo the chairs and just put a bench down.

Edited by Nexussfire
Link to comment
20 minutes ago, Nexussfire said:

I may forgo the chairs and just put a bench down.

 

Yeah, a lot of modding for me turns into that.  Amount of work needed to get effect is vastly over what I'm trying to fix.  Generally settle for a less than optimal solution and stew on it over time until something better pops into mind or I forget about it.

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

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

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. For more information, see our Privacy Policy & Terms of Use