Jump to content

Photo

Need Help with Scripting (running a script via dialog, papyrus fragments, and inventory management on an NPC)

papyrus script dialogue

  • Please log in to reply
10 replies to this topic

#1
TravelerHD

TravelerHD

    Stranger

  • Members
  • Pip
  • 5 posts

Here is my scenario:
 
1. I've created a potential custom follower NPC. "ArmorStormcloakOutfit" is her Default Outfit, and she has some steel armor in her inventory.
2. When I ask her to follow me and she agrees, I want her to equip the steel armor in that's in her inventory and for her Default Outfit to switch to "ArmorImperialLightOutfit".
 
I figured that hooking the script into the Character > Quest > Generic > DialogueFavorGeneric > Dialog Views > DialogFavorTopicsView > "Follow Me. I need your help." > "Lead the way." response is what I need to do, but I'm not sure how to actually trigger what I need from there. I've learned how to call very simple papyrus fragments, but I'm unable to call full functions. I tried referencing an external script and calling the function as explained in Method One here in the Creation Kit wiki, but although everything compiles absolutely nothing happens (I tried creating a function that runs a simple debug message box). 
 
Along with my trouble getting scripts to actually run, I'm also not 100% sure if the function I'll ultimately use is written correctly (I haven't had a chance to run and troubleshoot it). If anyone sees errors in it let me know.
 

Scriptname MyFollowerStartScript extends Quest

Actor Property pMyCustomFollower Auto

Function CustomFollowerStartFunction(ObjectReference akSpeakerRef)
	Actor akSpeaker = akSpeakerRef as Actor
	If (akSpeaker == pMyCustomFollower)
		If (akSpeaker.GetItemCount(ArmorSteelCuirassA) >= 1)
			akSpeaker.EquipItem(ArmorSteelCuirassA)
		EndIf
		If (akSpeaker.GetItemCount(ArmorSteelBootA) >= 1)
			akSpeaker.EquipItem(ArmorSteelBootA)
		EndIf

		akSpeaker.SetOutfit(ArmorImperialLightOutfit)
	EndIf
EndFunction

 
I'm familiar with typical desktop programming but I'm really new to Skyrim scripting. Any help would be appreciated.


Edited by TravelerHD, 29 October 2020 - 08:19 PM.


#2
TravelerHD

TravelerHD

    Stranger

  • Members
  • Pip
  • 5 posts

Update 1: for anyone following or stumbling upon this via a search, I figured out how to call a function. Kinda surprised I didn't figure it out earlier. Here's the details.
 
For my example the function I want to call in entirety is called "MyCustomFollwerStartFunction". You put it in the End papyrus fragment like so:
xk6TiCc.png

 

For the script itself, it should look like this:

3txm54K.png

 

I haven't tested the script code at the bottom of my post yet. I'll update this comment or the post if I succeed or fail.

 

 

Update 2: I have my function in place and it seems to be running well. After some testing and consideration I decided to do away with the equip calls, so my full script just looks like this:

;BEGIN FRAGMENT CODE - Do not edit anything between this and the end comment
;NEXT FRAGMENT INDEX 1
Scriptname TIF__000B0EE9 Extends TopicInfo Hidden

;BEGIN FRAGMENT Fragment_0
Function Fragment_0(ObjectReference akSpeakerRef)
Actor akSpeaker = akSpeakerRef as Actor
;BEGIN CODE
(pDialogueFollower as DialogueFollowerScript).SetFollower(akspeaker)
MyCustomFollowerStartFunction(akspeaker)
;END CODE
EndFunction
;END FRAGMENT

;END FRAGMENT CODE - Do not edit anything between this and the begin comment

Quest Property pDialogueFollower  Auto  
Actor Property pMyCustomFollower  Auto  
Outfit Property pSelectedStormcloakOutfit  Auto  

Function MyCustomFollowerStartFunction(Actor aMyCustomerFollower)
	string asMyCustomFollower = aMyCustomerFollower as string
	string psMyCustomFollower = pMyCustomFollower as string

	If (psMyCustomFollower == asMyCustomFollower) 
		aMyCustomerFollower.SetOutfit(pSelectedStormcloakOutfit)
	EndIf
EndFunction

Casting/parsing the actor property of my follower (pMyCustomFollower) and the actor property of the current NPC in the dialogue (aMyCustomerFollower) to strings is probably unnecessary, but it's left over from testing and I've done enough programming over the years to know that if it isn't broken, don't fix it lol. I should probably settle on a variable naming convention too; my names are a bit of a mess. But the function should be somewhat readable.

 

For some reason I was having problems simply using ArmorImperialLightOutfit in my SetOutfit() call, so I had to declare ArmorImperialLightOutfit as a property named pSelectedStormclockOutfit. That seemed to fix the issue.


Edited by TravelerHD, 30 October 2020 - 03:01 AM.


#3
dylbill

dylbill

    Faithful poster

  • Premium Member
  • 1,919 posts

Huh, I didn't know you could do that. What I do is set it up the way you originally had it, and declare the quest script that holds the function as a property. 

 

So, in your fragment you would add a MyFollowerStartScript StartScriptRef property (You can change StartScriptRef to something else if you wish.) When filling the property choose the quest that MyFollowerStartScript is attached to. Then in your fragment you would write 

StartScriptRef.CustomFollowerStartFunction(akSpeaker)


#4
TravelerHD

TravelerHD

    Stranger

  • Members
  • Pip
  • 5 posts

Yeah, that's definitely a much cleaner and proper way to do it. Unfortunately I could never get it working. I think I failed to assign the right value in the Property window of the CK for my script property.



#5
dylbill

dylbill

    Faithful poster

  • Premium Member
  • 1,919 posts

Ah. Well when adding the property in the fragment, the type should be MyFollowerStartScript. You can name it whatever you like. After adding it, click on the property, then edit value and pick object. If your script is only attached to one quest, only that quest should show up as an option.



#6
maxarturo

maxarturo

    What does this even mean?

  • Supporter
  • PipPipPipPip
  • 2,165 posts
Hello mates.
On a related subject.
 
Can the name of the script be set as a 'String', or some other property ?, i know that it cannot be set a 'String', but i would like to optimize one of my pair of puzzles, to minimize the scripts from 13 to 4, in such a way that all 4 puzzles would be also handle by the primary scene's 'Master Controller'.
 
They all use the type of function:
(Object01 as Script's A Name).FunctionA()
* calling / executing functions from other scripts.
 
* I've try:
(Object01 as (MyScript)).FunctionA()
'MyScript' set as string property, and some other approaches but none of them would even compile... and i did try plenty.
 
I've some scripts that do more or less the same things with some of those in the 'pair of puzzles' do and i used them in multiple applications, but if could just add the script's name as a 'String Property' it would minimize the use of some unnecessary scripts in my current pair of puzzles and just end up with 2 global scripts, (scripts that can be used on multiple applications and circistanses and by other 'Master Controllers' + sub controllers).
 
Thanks in advance.


#7
dylbill

dylbill

    Faithful poster

  • Premium Member
  • 1,919 posts

Hmmm, I don't think that's possible unfortunately. What you could do instead is handle it all in the master script function and pass in your object references instead of doing it with individual scripts. Something else to consider, you could also use states in a second master script, and put your functions in the OnBeginState event.

 

I assume your objects with the scripts are activators? If so, you can put your activator object references in an array, and make a string array that corresponds to go to the correct state. 

 

So in your master state script you would do something like this: 

 

Scriptname StateFunctionsScript extends Quest 

ObjectReference[] Property MyActivorRefs Auto 
String[] Property MyStateStrings Auto 

Event OnInit() 
    MyStateStrings = New String[3]
    MyStateStrings[0] = "State1"
    MyStateStrings[1] = "State2"
    MyStateStrings[2] = "State3"
EndEvent 

State State1 
    Event OnBeginState()
        SomeFunction()
    EndEvent 
EndState 

State State2
    Event OnBeginState()
        SomeOtherFunction()
    EndEvent 
EndState 

State State3
    Event OnBeginState()
       AnotherFunction()
    EndEvent 
EndState 

And then in your activator script you would do this. 

Scriptname MyActivatorScript extends ObjectReference 

StateFunctionsScript Property QuestScript Auto

Event OnActivate(ObjectReference akActionRef)
    Int Index = QuestScript.MyActivorRefs.Find(Self) 
    QuestScript.GoToState(QuestScript.MyStateStrings[Index])
EndEvent

Edited by dylbill, 30 October 2020 - 05:32 PM.


#8
maxarturo

maxarturo

    What does this even mean?

  • Supporter
  • PipPipPipPip
  • 2,165 posts
Thanks Iroh.
 
My 'Master Controller' already has around 15 states with a bunch of functions (more or less the way you posted it, but a little bit more complicated), and i just wanted to reduce the use of scripts in those puzzles / cell by using those 2 scripts that i use for different things (they work together and i used them to death).
 
Not that the whole thing is not working as it is now, but i'm from those kind of persons that i prefer having everything together, i don't get lost or confused with one huge script handling a lot of things or doing a bunch of different things, on the contrary, it makes my life easier !.
 
Thank you for your reply and have a nice weekend.


#9
dylbill

dylbill

    Faithful poster

  • Premium Member
  • 1,919 posts

No problem, you too! Happy modding.



#10
sornan

sornan

    Fan

  • Members
  • PipPipPip
  • 308 posts

Yea, the whole scripting system with Skyrim is a bit awkward, as it seems it was not designed to be very dynamic in itself, rather it's set up to be used in bits and chunks put into fragments often, making it hard to see what is doing what when so much is only visible panel by panel in various areas of the editor. There are ways around this system, but those ways around are not optimized. As it seems some of the things you guys are doing, and me as well, using quest scripts and trying to minimize the use of fragments that can just be hard to keep track of. I've found the usage of OnUpdate essential for me, so I can use looping quest scripts that can kick off immediately and not being dependent on a specific event before getting potentially lengthy code to run.

 

One of the things I had discovered some time ago is that quests added that have quest scripts attached to them run those scripts immediately (on a clean save), even if the quest is not start game enabled - honestly I am a bit confused as to why they did that, the scripts should only run once the quest is started. And bothering to stop the scripts and then restart them is really just wasting time fighting the system, so for me I have to add an initial condition to ensure the core code does not run until it should for each quest script.

 

Oblivion had some limitations and issues like this too, although the quest script system actually revolved around looping scripts, and as I recall had by default a lot of the game objects and references directly accessible in the scripts, where here we have to manually put in the OnUpdate event to get looping code, and also have to manually input objects or references into code along with having to at least confirm those additions with the button interface of the script properties.

 

I mean, it's all workable, just with some limitations and workarounds.

 

If anyone has worked with scripts in the Armed Assault series of games, that would be in my opinion the ideal script system. Scripts are under the control of the mission designer completely, fully accessible via custom folders - so everything is in view, scripts are executed and shutdown via code from anywhere, and looping code can be easily done in any script that is executed by just running a while-do loop. Scripts would run in full from the start, and portions of that code running being dependent on conditions put in by the coder purely, not event based. Global variables were also by default accessible by all scripts, yet in a game like Oblivion or Skyrim, where there is so much potential 'Global' data and so many scripts running, I can see how maybe having to declare such data in each script might reduce performance issues.

Either way sure would be nice to see a more dynamic and more easily accessible and visible script system in the next TES game, but probably not lol.

 

I will note, despite my occasional gripes with the script system for Skyrim, I admit the Scenes and Dialog functions they put in make things tons easier than in Oblivion, where it took forever sometimes to get some scenes and lengthy dialog to work out.


Edited by sornan, 31 October 2020 - 09:21 PM.






Also tagged with one or more of these keywords: papyrus, script, dialogue

Page loaded in: 1.226 seconds