Jump to content

Reference animations from another mod


Sostrmnn

Recommended Posts

Hi

 

I haven't got much experience with CK but I'm trying to build myself a utility mod that can cycle through the idle animations from pinup poser (PP).

 

So far I've figured out how PP stores its animations and how its interface works. I've written some code that works with normal idles but gives me an error when I try to play one of the PP idles. I'm guessing the error is because I'm referencing an idle from another esp.

 

The error I get is:

Error: Cannot play a None idle on an actor

My code is:

Scriptname AutoPoserKBScript extends Quest  

Idle[] Property ListOfObjectsFLST Auto
Idle Property MyFNISspellc1 Auto   ;When I set this to "IdleSalute" it works. When it's set to "MyFNISspellc1" it fails.
Idle Property MyFNISspellc10 Auto  ;When I set this to "IdleLockPick" it works. When it's set to "MyFNISspellc10" it fails.
Actor Property PlayerREF Auto ; Least 'expensive' way to refer to the player

Event OnInit() ; This event will run once, when the script is initialized
    RegisterForKey(48) ;N
    RegisterForKey(49) ;B
    ;register idles
    ListOfObjectsFLST = new Idle[2]
    ListOfObjectsFLST[0] = MyFNISspellc1
    ListOfObjectsFLST[1] = MyFNISspellc10
EndEvent


Event OnKeyUp(int keyCode, float holdTime)
    if keyCode == 49
        Debug.Notification("KeyPressed N")
        if PlayerREF.PlayIdle(MyFNISspellc1) ;Error: Cannot play a None idle on an actor
            Debug.Notification("Playing idle c1")
        else
            Debug.Notification("failed")
        endif
    endif
    if keyCode == 48
        Debug.Notification("KeyPressed B")
        if PlayerREF.PlayIdle(MyFNISspellc10) ;Error: Cannot play a None idle on an actor
            Debug.Notification("Playing idle c10")
        else
            Debug.Notification("failed :(")
        endif
    endif
EndEvent

I will appreciate any help and insight on this.

Link to comment
Share on other sites

PP either needs to be listed as a master on your ESP so it can access PP's resources and auto-fill them in the script (be sure to select auto fill in the CK and that a form ID is assigned) - OR - You can pull the Idle resources in to your script using http://www.creationkit.com/GetFormFromFile_-_Game to avoid listing PP as a master (although it would still need PP installed to work correctly).

EDIT: Just in case you're not aware - for PP to be listed as a master, load it with Skyrim.esm and Update.esm when you load up your mod in the CK and save it. You can then go back with TES5Edit to see it listed in the header and/or remove the dependency if you decide to go with the GetFormFromFile route.

Edited by sevencardz
Link to comment
Share on other sites

PP either needs to be listed as a master on your ESP so it can access PP's resources and auto-fill them in the script (be sure to select auto fill in the CK and that a form ID is assigned) - OR - You can pull the Idle resources in to your script using http://www.creationkit.com/GetFormFromFile_-_Game to avoid listing PP as a master (although it would still need PP installed to work correctly).

 

EDIT: Just in case you're not aware - for PP to be listed as a master, load it with Skyrim.esm and Update.esm when you load up your mod in the CK and save it. You can then go back with TES5Edit to see it listed in the header and/or remove the dependency if you decide to go with the GetFormFromFile route.

 

I would like to use the first method you suggested. In the CK, when I open my mod I select "Skyrim.esm", "Update.esm" and "Pinup Poser.esp" together with my "testplugin1.esp". testplugin1.esp is set as the active file. I go to my quest properties and set the idle to one of the PP idles. I've double checked that the names are the same as what the real PP uses for its idles. I'm also using auto-fill as you suggested. A form ID is assigned.

 

If I save and load the game, I get the error in the Papyrus log. If I close CK and then open CK again, loading again as before, the form ID's on my quest are mysteriously set to "NONE".

 

Top part is before saving and closing. Bottom part is after reloading CK and opening same dialog:

http://i.imgur.com/kTjFEbu.png

 

You mention, "for PP to be listed as a master, load it with Skyrim.esm and Update.esm when you load up your mod in the CK and save it". I do select it when I load my mod in the CK but it's just listed as an ESP. Do I need to do something in TES5Edit to get it to be listed as ESM?

 

Edit: adding screenshot of the loading dialogs:

http://i.imgur.com/wh5Sv6V.png

 

I want to avoid doing the GetFormFromFile route because looking up all the form ID's for PP's idles will take me hours.

Edited by Sostrmnn
Link to comment
Share on other sites

I seem to have found a way to get this to work. Not 100% sure if it's overkill or not. I was searching google and found this thread http://forums.nexusmods.com/index.php?/topic/595124-reference-object-created-in-another-esp/ which referenced the following page http://cs.elderscrolls.com/index.php/De-Isolation_Tutorial

 

Following the "de-isolation tutorial" I used Wrye Bash to "Esmify" the Pinup Poser.esp and then loaded Construction Kit and selected it together with Skyrim.esm and Update.esm. I set my testplugin1.esp as the active file and loaded. Then I went and auto-filled the properties on my quest. Saved. Went back to Wrye Bash and right click Pinup Poser.esp and "Espify" again. (This is apparently very important to do before launching the game). I then launched Skyrim (with CK still open and happily thinking that PP is an ESM) and I was able to play the idles from PP. Awesome! :blush:

 

Couple things I learned:

  • if you "Esmify" the esp file it does not change the extension, just tricks the CK into thinking that it's an ESM so that your ESP can become dependent on it.
  • you can ESMify the mod before you load CK and then ESPify it back before you run the game, now the CK will work correctly and the game will also run. The downside with this is that you have to remember to ESMify the mod again next time you open CK

 

Link to comment
Share on other sites

It's so much easier. :smile:

 

You have to make sure that PP is installed, and FNIS is run. Then simply go through all animations PP has defined inthe animation lists.

 

For example, open animation/Pinups/FNIS_Pinups_List.txt. THe first definition reads

b PUPStand1 Pinups_Stand1.hkx

This animation can be activated by

Debug.SendAnimationEvent(<actor>, "PUPStand1")

Usually, if the FNIS definition does NOT have the -a parameter, these animations are cyclic. You can terminate then by activating another animation, or with

Debug.SendAnimationEvent(<actor>, "IdleForceDefaultState")
Link to comment
Share on other sites

Wow, that's awesome. Waaaaaaaaaaaay easier! Thanks fore! :blush:

 

I ran into an issue with my approach anyway because CK forbids the huge amount of properties I tried to have on my script. I also found out that Papyrus won't compile if you have an array with size larger than 128. Wtf!! No wonder TES games are so buggy, their SDK is pretty crap. I'll work around the array limit by having separate arrays for each of the FNIS groups that are defined in PP.

 

Results of my handiwork thus far:

http://i.imgur.com/boXMsoc.gif

 

Code for anyone who's interested:

Scriptname AutoPoserKBScript extends Quest  

int Property activeAnimation auto
int Property animationCount auto
Actor Property PlayerREF Auto ; Least 'expensive' way to refer to the player

String[] animList

Event OnInit() ; This event will run once, when the script is initialized
    RegisterForKey(48) ;B
    RegisterForKey(49) ;N
    activeAnimation = 1
    ;register idles
    animationCount = 128 ;Remember to update size of animList array
    animList = new String[128] ;Remember to update animationCount
    animList[0] = "PUP2Stand1"
    animList[1] = "PUP2Stand2"
    animList[2] = "PUP2Stand3"
    animList[3] = "PUP2Stand4"
    animList[4] = "PUP2Stand5"
    animList[5] = "PUP2Stand6"
    animList[6] = "PUP2Stand7"
    animList[7] = "PUP2Stand8"
    animList[8] = "PUP2Stand9"
    animList[9] = "PUP2Stand10"
    animList[10] = "PUP2Stand11"
    animList[11] = "PUP2Stand12"
    animList[12] = "PUP2Stand13"
    animList[13] = "PUP2Stand14"
    animList[14] = "PUP2Stand15"
    animList[15] = "PUP2Stand16"
    animList[16] = "PUP2Stand17"
    animList[17] = "PUP2Stand18"
    animList[18] = "PUP2Stand19"
    animList[19] = "PUP2Stand20"
    animList[20] = "PUP2Stand21"
    animList[21] = "PUP2Stand22"
    animList[22] = "PUP2Stand23"
    animList[23] = "PUP2Stand24"
    animList[24] = "PUP2Stand25"
    animList[25] = "PUP2Stand26"
    animList[26] = "PUP2Stand27"
    animList[27] = "PUP2Stand28"
    animList[28] = "PUP2Stand29"
    animList[29] = "PUP2Stand30"
    animList[30] = "PUP2Stand31"
    animList[31] = "PUP2Stand32"
    animList[32] = "PUP2Stand33"
    animList[33] = "PUP2Stand34"
    animList[34] = "PUP2Stand35"
    animList[35] = "PUP2Stand36"
    animList[36] = "PUP2Stand37"
    animList[37] = "PUP2Stand38"
    animList[38] = "PUP2Stand39"
    animList[39] = "PUP2Stand40"
    animList[40] = "PUP2Stand41"
    animList[41] = "PUP2Stand42"
    animList[42] = "PUP2Stand43"
    animList[43] = "PUP2Stand44"
    animList[44] = "PUP2Stand45"
    animList[45] = "PUP2Stand46"
    animList[46] = "PUP2Stand47"
    animList[47] = "PUP2Stand48"
    animList[48] = "PUP2Stand49"
    animList[49] = "PUP2Stand50"
    animList[50] = "PUP2Stand51"
    animList[51] = "PUP2Stand52"
    animList[52] = "PUP2Stand53"
    animList[53] = "PUP2Stand54"
    animList[54] = "PUP2Stand55"
    animList[55] = "PUP2Stand56"
    animList[56] = "PUP2Stand57"
    animList[57] = "PUP2Stand58"
    animList[58] = "PUP2Stand59"
    animList[59] = "PUP2Stand60"
    animList[60] = "PUP2Stand61"
    animList[61] = "PUP2Stand62"
    animList[62] = "PUP2Stand63"
    animList[63] = "PUP2Stand64"
    animList[64] = "PUP2Stand65"
    animList[65] = "PUP2Stand66"
    animList[66] = "PUP2Stand67"
    animList[67] = "PUP2Stand68"
    animList[68] = "PUP2Stand69"
    animList[69] = "PUP2Stand70"
    animList[70] = "PUP2Stand71"
    animList[71] = "PUP2Stand72"
    animList[72] = "PUP2Stand73"
    animList[73] = "PUP2Stand74"
    animList[74] = "PUP2Stand75"
    animList[75] = "PUP2Stand76"
    animList[76] = "PUP2Stand77"
    animList[77] = "PUP2Stand78"
    animList[78] = "PUP2Stand79"
    animList[79] = "PUP2Stand80"
    animList[80] = "PUP2Stand81"
    animList[81] = "PUPStand1"
    animList[82] = "PUPStand2"
    animList[83] = "PUPStand3"
    animList[84] = "PUPStand4"
    animList[85] = "PUPStand5"
    animList[86] = "PUPStand6"
    animList[87] = "PUPStand7"
    animList[88] = "PUPStand8"
    animList[89] = "PUPStand9"
    animList[90] = "PUPStand10"
    animList[91] = "PUPStand11"
    animList[92] = "PUPStand12"
    animList[93] = "PUPStand13"
    animList[94] = "PUPStand14"
    animList[95] = "PUPStand15"
    animList[96] = "PUPStand16"
    animList[97] = "PUPStand17"
    animList[98] = "PUPStand18"
    animList[99] = "PUPStand19"
    animList[100] = "PUPStand20"
    animList[101] = "PUPStand21"
    animList[102] = "PUPStand22"
    animList[103] = "PUPStand23"
    animList[104] = "PUPStand24"
    animList[105] = "PUPStand25"
    animList[106] = "PUPStand26"
    animList[107] = "PUPStand27"
    animList[108] = "PUPStand28"
    animList[109] = "PUPStand29"
    animList[110] = "PUPStand30"
    animList[111] = "PUPStand31"
    animList[112] = "PUPStand32"
    animList[113] = "PUPStand33"
    animList[114] = "PUPStand34"
    animList[115] = "PUPStand35"
    animList[116] = "PUPStand36"
    animList[117] = "PUPStand37"
    animList[118] = "PUPStand38"
    animList[119] = "PUPStand39"
    animList[120] = "PUPStand40"
    animList[121] = "PUPStand41"
    animList[122] = "PUPStand42"
    animList[123] = "PUPStand43"
    animList[124] = "PUPStand44"
    animList[125] = "PUPStand45"
    animList[126] = "PUPStand46"
    animList[127] = "PUPStand47"
EndEvent


Event OnKeyUp(int keyCode, float holdTime)
    if keyCode == 49 ;N
        activeAnimation = (activeAnimation + 1) % animationCount
    endif
    if keyCode == 48 ;B
        activeAnimation = activeAnimation - 1
        if activeAnimation < 0 ; modulus on negative numbers doesn't work as expected in papyrus
            activeAnimation = animationCount - 1
        endif
    endif
    Debug.Trace("activeAnimation = " + activeAnimation)
    Debug.Notification("Playing idle: " + activeAnimation)
    Debug.SendAnimationEvent(PlayerREF, animList[activeAnimation])
EndEvent

Thank you to sevencardz and fore for the help! :thumbsup:

Link to comment
Share on other sites

You can make this much smaller, without the need for so many big arrays. Remember they will be included in the saves and cause save bloats.

 

This works, because PPs animations are nicely grouped. Define groups without numbers, and keep the the upper bound in a string variable. From what I have seen works for all animations except the paired ones.

animGroup[0] = "PinupsStand"
animGroup[1] = "PinupsSit"
animGroup[2] = "PinupsLay"
...............................
animGroupLen = "162027027........................"

Then you make a nested loop and read the upper bound for each group with

int upperBound = StringUtil.substring(animGroupLen, outerIndex * 3, 3) as int
Link to comment
Share on other sites

 

You can make this much smaller, without the need for so many big arrays. Remember they will be included in the saves and cause save bloats.

 

This works, because PPs animations are nicely grouped. Define groups without numbers, and keep the the upper bound in a string variable. From what I have seen works for all animations except the paired ones.

animGroup[0] = "PinupsStand"
animGroup[1] = "PinupsSit"
animGroup[2] = "PinupsLay"
...............................
animGroupLen = "162027027........................"

Then you make a nested loop and read the upper bound for each group with

int upperBound = StringUtil.substring(animGroupLen, outerIndex * 3, 3) as int

 

 

That's a very good point! I will rewrite it as you suggest. ;D

 

I've been having fun with this helper functionality, much more convenient than going through the menus (for me). Quite chuffed :)

Link to comment
Share on other sites

In case anyone wants the final code, here it is. It doesn't work for paired animations but that's not something I'm interested in at the moment - might look at it later if I feel like figuring out how to write spells.

Scriptname AutoPoserKBScript extends Quest  

Actor Property PlayerREF Auto ; Least 'expensive' way to refer to the player

String[] animGroup
String animGroupCounts
int animGroupCount
int activeAnimation ;activeAnimation is 1-based because of the way the animations are named
int activeGroup
int activeGroupCount

Event OnInit() ; This event will run once, when the script is initialized
    Debug.Trace("AutoPoserKBScript initialising ===================")
    RegisterForKey(47) ;V
    RegisterForKey(48) ;B
    RegisterForKey(49) ;N
    activeAnimation = 1
    ;Group names
    animGroupCount = 20        ; <--+-- Update these values together
    animGroup = new String[20] ; <--/
    animGroup[0] = "APose1HSword"
    animGroup[1] = "APoseBow"
    animGroup[2] = "APoseCBow"
    animGroup[3] = "APoseDual"
    animGroup[4] = "APoseFist"
    animGroup[5] = "APoseZwei"
    animGroup[6] = "PPair"
    animGroup[7] = "PUPStand"
    animGroup[8] = "PUP2Stand"
    animGroup[9] = "PUP3Kneel"
    animGroup[10] = "PUP3Lay"
    animGroup[11] = "PUP3Sit"
    animGroup[12] = "PUP4Stand"
    animGroup[13] = "PUP5Kneel"
    animGroup[14] = "PUP5Lay"
    animGroup[15] = "PUP5Sit"
    animGroup[16] = "PUPMKneel"
    animGroup[17] = "PUPMLay"
    animGroup[18] = "PUPMSit"
    animGroup[19] = "PUPMStand"
    ;Group counts, for reference:
    ;APose1HSword: 18
    ;APoseBow: 5
    ;APoseCBow: 4
    ;APoseDual: 18
    ;APoseFist: 18
    ;APoseZwei: 18
    ;PPair: 40
    ;PUPStand: 81
    ;PUP2Stand: 81
    ;PUP3Kneel: 27
    ;PUP3Lay: 27
    ;PUP3Sit: 27
    ;PUP4Stand: 81
    ;PUP5Kneel: 27
    ;PUP5Lay: 27
    ;PUP5Sit: 27
    ;PUPMKneel: 9
    ;PUPMLay: 9
    ;PUPMSit: 9
    ;PUPMStand: 54
    animGroupCounts = "1805041818184081812727278127272709090954"
    activeGroup = 7
    activeGroupCount = StringUtil.substring(animGroupCounts, activeGroup * 2, 2) as int
EndEvent


Event OnKeyUp(int keyCode, float holdTime)
    if keyCode == 47 ;V
        activeGroup = (activeGroup + 1) % animGroupCount
        activeGroupCount = StringUtil.substring(animGroupCounts, activeGroup * 2, 2) as int
        activeAnimation = 1
        Debug.Notification("Active group: " + animGroup[activeGroup] + " activeGroupCount = " + activeGroupCount)
    endif
    if keyCode == 48 ;B
        activeAnimation = activeAnimation - 1
        if activeAnimation <= 0
            activeAnimation = activeGroupCount
        endif
    endif
    if keyCode == 49 ;N
        activeAnimation = activeAnimation + 1
        if activeAnimation > activeGroupCount
            activeAnimation = 1
        endif
    endif
    Debug.Trace("activeGroup = " + activeGroup + " activeAnimation = " + activeAnimation + " Playing idle: " + animGroup[activeGroup] + activeAnimation)
    Debug.Notification("Playing idle: " + animGroup[activeGroup] + activeAnimation)
    Debug.SendAnimationEvent(PlayerREF, animGroup[activeGroup] + activeAnimation)
EndEvent

 

Link to comment
Share on other sites

  • Recently Browsing   0 members

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