Jump to content

Power Armor furniture animation additive subgraph issues; or, a method to instantly remove an actor from PA?


Recommended Posts

I swear I have never met another more frustrating issue while modding a Bethesda game.

So, I had the idea to add in dimmable headlamps for Power Armor helmets. Not too hard of a problem: add a new craftable OMOD for each helmet, then write a bunch of Papyrus that will swap that headlamp with other OMODs of varying brightness levels when an aid item/hotkey is used.

Straightforward enough, until I found that there's an engine bug where the game only properly loads the first headlamp spotlight you equip after entering a suit of Power Armor. All headlamps lights after the first one don't load the gobo (the texture the light filters through to project an image), and the color of the light is not loaded properly either (it'll be tinted purple for some reason).

Simple reproduction steps to see this bug in a vanilla game:

 

  • Get two PA helmets
  • Mod one helmet with the Vault Boy headlamp, and the other helmet with Bright or something
  • Put the Bright headlamp helmet on the PA frame, then get in it
  • Turn on the headlamp
  • Open your inventory and equip the Vault Boy headlamp helmet
  • Close your inventory and turn on the headlamp
    You'll notice that the really annoying vault boy overlay (bogo) is missing, and the light is sort of blue-greenish.
    And in reverse:
  • Exit and then re-enter the PA frame
  • Turn on the headlamp: notice that the Vault Boy gobo will have loaded this time, and the color is correct
  • Equip the other helmet and then turn it on
    This time the Bright headlamp will be missing its gobo (some concentric rings, to simulate an imperfect spotlight reflector/lens assembly), and the color will be distinctly purple instead of yellow.

There's a thread here where the author eventually diagnoses this issue as the gobo loading improperly, and notes that it doesn't happen if the light is not set to use a gobo, but it seems that this is not true for headlamps.



Anyway, not totally insurmountable: forcing an exit/reentry of the PA frame will reload the light and make it display properly. Here is where the real problem starts.

Turns out that getting the player out and back into their PA quickly is almost possible, but every avenue I've tried has been a dead end:

  • The first thing I tried was to simply wearer.SwitchToPowerArmor(None), wearer.SwitchToPowerArmor(PAFrame).
    (Skimming over the boring bits, like finding the PA frame furniture object--also, wearer is my local variable for the actor currently wearing the helmet with an adjustable headlamp)
    So, just kick the player out of their frame, then immediately force them back into it. Was a decent idea, but the second SwitchToPowerArmor() call won't work while the player is playing the exit animation, which is like 3.5 seconds. Adding a wait works, but feels absolutely horrible, because switching from the lowest to highest lamp setting takes up to 15 seconds of watching that damn animation.
  • Next I thought to do wearer.SwitchToPowerArmor(None), wearer.MoveTo(wearer), wearer.SwitchToPowerArmor(PAFrame).
    The first time I tried this it worked fine, the wearer.MoveTo(wearer) call would cancel the exit animation, allowing us to complete the whole cycle in just a few frames. A little clunky, but it works...or, it did, until I relaunched the game. After that, moving the player to themselves now always triggers a loading screen. I have no idea why this started, but I cannot get this method to work anymore, even in an unmodded game. I also tried placing something at the player's feet and then MoveTo()ing them to that, or MoveTo()ing to the PA frame, but these also trigger loading screens. I also tried fiddling with the fMinPlayerMoveToDistForLoadScreen ini setting, but it seems to do nothing.

    I have no explanation for why this worked in the first place. Moving on...
  • Next I attempted to find some way to cancel an ongoing animation, like playing a different idle with a script, and trying every Papyrus function I could think of that might also interrupt animations. No dice here, nothing seems to be able to do this except for MoveTo().
  • Right, so, maybe there's some way to artificially unequip a PA frame without SwitchToPowerArmor(None)? Maybe...wearer.UnequipAll()? Uh, no, oh god no, now my player character requires an SCP designation. Moving on...
  • Okay, so what if I make the animation shorter? Easy enough, I just have to find the animation files (in Meshes\Actors\Character\Animations\Furniture\PowerArmor\Neutral: ExitToStand.hkx, ExitToStandCombat.hkx, and QuickExitToStand.hkx), unpack them, hack the duration value to 0, repack and replace. Hacky, but works great as a proof of concept: moving the player out of and then back into their PA is back to only taking a few frames, which is kludgy, but workable.

    But, obviously, this creates a new problem: since this is an animation replacer, the PA exit animation is always instant. Not a good side-effect.

    Moving on...
  • After a little research, it's pretty simple to conditionally use different sets of animations, based on actor keywords, by setting up an animation subgraph. Perfect! Right?
    The process doesn't seem too difficult: in this case, all I (apparently) need to do is:
    • Make a new keyword
    • Duplicate HumanRaceSubGraphData
    • Set it to be Additive to HumanRaceSubGraphData
    • Make a (near) duplicate of the already-existing subgraph entry for Meshes\Actors\Character\Behavoirs\FurnitureNoMirrorBehavior.hkx with a target keyword of FurnitureTypePowerArmor, with some slight modifications: add my new keyword into the Actor Keywords filter, and add a new path pointing to where I've put my custom ExitToStand.hkx/etc animations--like so [image]
    • Confirm that the entry does, in fact, add (the UI is dodgy)
    • Save
    • Run the CK with these arguments to build anim text data:
      CreationKit.exe -GenerateAnimInfo:IDEK_ExitAnimTest.esp .\Data .\Data
    According to the information I've found about this process, this should be all I have to do. Now, if I just add my keyword to an actor with a script, they'll use my instant exit animations, and I can turn the animation set back to vanilla by removing the keyword again.

    Unfortunately, though, this doesn't seem to work, at all. Instead of using a different animation set, this prevents the actor from playing any PA furniture-related animations. If the player character is already in a suit of PA, attempting to exit shows the message You can't exit your armor here. Attempting to get into a PA frame shows You cannot use this at this time. Removing the keyword for the new subgraph returns things to normal.

    Thinking that this might be because of my crappy hacked animations, I tried setting the animation paths up as an exact duplicate of the vanilla one, except with my actor keyword, and rebuilt anim text data again. Theoretically, then, actors with my keyword should use my new subgraph, but they'll use the exact same animation set as the vanilla subgraph. Same issue. Removing my additive subgraph Race record and moving the (still near-duplicate) subgraph entry as a direct edit into HumanRaceSubGraphData also does not work. #!@%!#?!

So, here's where I'm at. I cannot find an adequate solution to any of these problems:

  • Lights and their gobos not loading properly
  • Reloading an incorrectly loaded spotlight without restarting the game, or exiting and reentering the PA frame
  • Removing an actor from their Power Armor without having to watch the exit animation
  • Cancelling or interrupting a PA exit animation
  • Conditionally replacing the PA exit animations

Since the last one is the most complicated, but also the most promising, I made a little test mod here in case anyone would be willing to experiment with it. It adds an additive subgraph that's active when an actor has a particular keyword, which can be toggled by the Aid item that the mod also adds to your inventory on install. Theoretically, using the item should allow you to instantly exit a PA frame. Using the item again should restore the normal animation. In practice, using the item prevents PA animations from working at all.

Hopefully someone with more knowledge on how subgraphs work can educate me on what I'm doing wrong.
Or, if anyone can think of a solution to any of the other problems I just listed, any one of those would work too, and I would be greatly appreciative.

If nothing else, maybe future generations will find this post and save themselves the bother of attempting such an undertaking.

Thanks for reading my dissertation about an exercise in frustration.

Link to comment
Share on other sites

One thine I noticed when I did a POC on PA previously, if you messed up the animation, u can't enter the PA ...

 

I take a look at your esp file, it looks ok but somehow I m not able to generate subgraph metadata. Not sure y ??? ... I took ur modded animation files and dump them under my POC mod (basically to change the sound of entering PA) that I have done previously. Instead of adding the keyword to actor, I add them to the target but that's due to different needs and it shouldn't matter. There are two PA near the bridge (in-game) with the added keyword. There is a holoptape that can be used to control the sound but u have to add it manually thru console. Search for "*PA". Your modded 'quick exit' animation is added for 'sound A'. Sound B will be normal exit animation.

 

Maybe u can take a look and it may solve your problem?

https://drive.google.com/file/d/18ymt2TZsHrCXx7zLOHkl3YqMHb_yTzEz/view

Link to comment
Share on other sites

Yep, your implementation works as intended. Thanks for that, really.

 

I've noticed that:

  • The CK will look into archives during the GenerateAnimInfo process, so if there is already existing AnimTextData in a .ba2, the CK will not attempt to regenerate it, even if it needs to
  • Related to the above, it the CK also tends not to regenerate AnimTextData if loose files already exist. If you keep your mods cleanly separated with MO2 like I do, just nuking the entire folder works. If you've got any mods installed directly to your Data folder that aren't packed into archives, this would become much more difficult.

 

Also, I figured out what I was doing wrong, turns out I am a f*#@ing idiot. There was a subtle typo in a file path that took me a solid three days to discover.

Actors\Character\Behavoirs\FurnitureNoMirrorBehavior.hkx

 

9equaNV.jpg

 

I'm sure I looked at that typo a hundred times and never noticed it. Here's an implementation of the earlier test mod that works properly after fixing the typo and regenerating AnimTextData.

 

At least someone might find something else in my post useful some day.

Link to comment
Share on other sites

Maybe you wanna try these 'exit animations? They were properly done cutting down just 2 frames with all the proper annotations. I think 2 frames r the lowest u can go. By just edit and set duration to 0, u miss out the annotations which can be important to the animation. One of them unhide the pipboy ...

 

 

https://drive.google.com/file/d/1f_hPV9M98yFBLJm-KxLU_hXQezhjG8fW/view

Link to comment
Share on other sites

Sure, I might as well, thanks. My intended use case always involves immediately shoving the player back into the PA frame, but I was still concerned that my 0 duration hack might screw up the animation graph state (or something) somehow, and it can't hurt to nip a future problem in the bud. All of my released mods are open sourced, so maybe someone else might want to rip the asset for some other purpose in the future.

 

Now I face a new problem, which is that the game doesn't like to reevaluate which animation set it ought to be using very often. In practice, the animation set used on exiting PA tends to be equal to whatever set of keywords the player had when entering the PA, which I suppose means that getting into PA probably immediately forces the game to reevaluate which subgraph to use. AttemptAnimationSetSwitch() doesn't help, as far as I can tell. If only I could see into the future...

 

Well, at least it's progress, and I get to fight something slightly different for a while. Hmm...

Link to comment
Share on other sites

Entering PA probably reload the subgraph but I know it removes or unregistered whatever 'RegisterForAnimationEvent' you have done. 'AttemptAnimationSetSwitch()' is not something that is immediate. However actions like 'fast' travel', exit from Pipboy and drawing of weapon will cause the subgraph to be loaded immediately. So if u triggered a keyword changed say thru a holotape, since u need to exist the Pipboy, u will see the new animation. But if u triggered that say thru consuming an item, if u consume it thru Pipboy, again u need to exist the Pipbo, everything is fine. However, if u 'favorite' the item and consume thru the favorite menu, u will not see the new animation till u perform those actions that I mentioned earlier ...

After entering PA, its probably falls under 'PA race' and its subgraph and thus AttemptAnimationSetSwitch() may not work for that.

Link to comment
Share on other sites

It seems that the PA frame is very resistant to reloading subgraphs, at least with regards to the exit animation. I tried getting into the PA frame, adding a keyword that should switch subgraphs, calling AttemptAnimationSetSwitch(), then opening/closing the pip-boy menu (well, sort of, since the PA interface is different), drawing and holstering a weapon, then fast travelling, but even then the subgraph that was active when I entered the PA frame is still used. At the moment I have two different subgraphs, where one has my conditional keyword on the actor, and another where it's on the target, and I tried the same process by tweaking either the furniture's keywords, or the actor's keywords, but that turns out to make no difference. Saving and then reloading works, but is obviously not a viable solution.

 

Bah, there has to be some solution that will work, right? The struggle continues.

 

Perhaps there's some unused animation the FurnitureNoMirrorBehavior graph points to that I can "inject" into the Meshes\Actors\Character\Animations\Furniture\PowerArmor\Neutral path, then set up a condition to use that animation in the Idle Animations menu, rather than fuss around with subgraphs. The game is much snappier about selecting from animations within a subgraph than it is switching subgraphs. I doubt this idea is viable, though.

Link to comment
Share on other sites

Eureka!

 

So, the PA furniture exit animations are ExitToStand.hkx, ExitToStandCombat.hkx, and QuickExitToStand.hkx. I had the idea to look closely at the existing idle animations, and it occurred to me that only the first two of those animations are ever actually played, because the condition functions on the idle animation record for QuickExitToStand.hkx are set to be impossible to fulfill for Power Armor. Since it's never used anyway, I can co-opt it for my own purposes by reconfiguring the condition functions like so, and then replacing the vanilla animation with your custom, 2-frame variant.

 

By doing this, it provides me a way to force the player to reenter their power armor in just a few frames, which is an acceptable workaround for the ugly visual bug that had been plaguing me. It took me three weeks to find this.

 

The visual bug in question makes an appearance around 2:20, so it's not 100% fixed, but without this workaround you always get the bug, and to me it just looks absolutely awful.

 

Unfortunately this does mean that only one mod in a given load order can exploit this particular quirk without a manual patch, but I'd be surprised if there are any other mods that make use of it anyway.

Actually I realized that this is a perfect use case for record injection, see the spoiler.

 

Also, here's a more detailed description, which I wrote as a PM, but I'm putting up publicly for anyone else who stumbles across this thread:

 

Here's the whole script I'm using to do it: https://idek.chir.uno/fo4/AdjustableHeadlampScript.psc

Look in UpdateHeadlamp().

(That GetFormFromFile() is there just as a temporary alternative to a property, it's faster to do that and just reloadscript "idek:ipa:AdjustableHeadlampScript" than to have to tweak the plugin file when I'm trying different things--that and the HotLoadPlugin (hlp) commands are a massive time-saver if you don't know about them already)

 

Here are the important parts trimmed out and annotated:

float t
ObjectReference c
 
; add keyword and force player out of armor
wearer.AddKeyword(FastPowerArmorExitAnimation)
 
; seems like the animation condition functions take a moment to update,
; I find it fails sometimes without a delay at least this long
Utility.Wait(0.1)
 
; kick the player out of their armor
wearer.SwitchToPowerArmor(None)
 
t = Utility.GetCurrentRealTime()
 
; get the player\'s armor furniture object
int i = 0
while (i < 10)
    c = self.GetContainer()
    Util.Trace(self + " in container " + c)
    if (c == wearer)
        Utility.Wait(0.05)
        i += 1
    else
        i = 100
    endif
endwhile
; The wiki mentions that you could do this instead:
; c = wearer.GetLinkedRef(LinkPowerArmorKeyword)
; SwitchToPowerArmor(None) will silently fail if there isn\'t some clear space directly behind the player,
; though, so you\'d need some different method of error checking
 
; wait at least 0.2s since we last recorded the time
Utility.Wait(Math.Min(0.2, Math.Max(0, t + 0.2 - Utility.GetCurrentRealTime())))
 
; remove the quick eject keyword and return to the PA
wearer.ResetKeyword(FastPowerArmorExitAnimation)
wearer.SwitchToPowerArmor(c)
 
; If the quick-eject thing didn\'t work right, we\'ll be sitting through the full 3.5s animation right now:
; the furniture object appears as soon as SwitchToPowerArmor(None) does anything, so we can\'t be
; 100% sure that we\'re fully out of the armor and finished with the animation right now.
; this bit checks for that and keeps trying to shove us back into the PA for up to 5s
Utility.Wait(0.1)
int i = 0
while (i < 10)
    c = self.GetContainer()
    if (c != wearer)
        ; Util.Trace(self + " ...reequip failed--wrong animation? Waiting a moment and retrying...")
        Utility.Wait(0.5)
        
        wearer.SwitchToPowerArmor(c)
        i += 1
    else
        i = 100
    endif
endwhile
The way I'm doing it for the headlamp thing requires that I attach this script to each helmet base object, which I would avoid if I could but it's the best way to do it in this case. Forcing the reequip with an "external" script like a RefAlias or Quest is certainly possible, but requires the script to be written a little differently to work.

 

To allow for an arbitrary number of mods to exploit this system without conflicting, I strongly recommend utilizing record injection, so we can share a common keyword, and edit to the idle animation record.

 

The easiest way to do this is to grab this pseudo-mod: https://idek.chir.uno/fo4/CommonFastExitInjection.esp (also attached to this post)

Then Copy as Override... both of the records from that plugin into your own plugin. This will give you the FastPowerArmorExitAnimation keyword to use in your script, and set up the edit to FurnitureNoMirrorExitToStandCombat properly.

 

To do the same thing from scratch, in xEdit, you'll want to make a new keyword, and give it the FormID of 006D6DD5 (chosen arbitrarily by RNG). I recommend naming it FastPowerArmorExitAnimation.

Then, make an override for the FurnitureNoMirrorExitToStandCombat idle animation record, and set it up like this.

 

Next you override the unused vanilla animation at Meshes\Actors\Character\Animations\Furniture\PowerArmor\Neutral\QuickExitToStand.hkx

with langnao's cut-down 2-frame animation.

Then, as long as the player has our injected keyword, they'll use the near-instant exit animation, and that code snippet above will work to cycle the player out of and back into the PA frame in about 0.3s or so.

 

Note that I do not recommend leaving this keyword on the player longer than you have to, because it could potentially interfere with unrelated animations without a much more complicated set of condition functions on the idle animation record. If you want a more general, longer-term animation replacer, the cleanest way to implement it is with animation subgraphs, which I explored earlier in the thread.

 

 

Edit: The mod this was for been released here: https://www.nexusmods.com/fallout4/mods/45572

Link to comment
Share on other sites

  • Recently Browsing   0 members

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