Jump to content

Did I just stumble upon an easier way to call properties from a seperate script? Papyrus experts, help me understand what's going on here.


Recommended Posts

So, a little mod I made a few weeks back uses the RentRoomScript from the base game. Unsurprisingly, it controls the innkeeper room rental system. I dissected it and added onto it for use with my mod (that just lets you rent rooms for longer/customize prices). I didn't give it much thought beyond "hey, it works".

 

Today I went in to try to fix a small detail and decided to peek in on the script just to come to grips with how it actually works beyond me copy and pasting it and getting good results. Here's the script: apologize for the size.

 

Scriptname RentRoomScript extends Actor  Conditional
{script for anyone who rents a room}

ObjectReference Property Bed  Auto  
{bed to rent}

WIFunctionsScript Property WI Auto
{Pointer to WIFunctionsScript attached to WI quest}

; rent room or clear rental
function RentRoom(DialogueGenericScript pQuestScript)
	Bed.SetActorOwner(Game.GetPlayer().GetActorBase())
	RegisterForSingleUpdateGameTime (pQuestScript.RentHours)
	Game.GetPlayer().RemoveItem(pQuestScript.Gold, pQuestScript.RoomRentalCost.GetValueInt())
	; used to conditionalize innkeeper dialogue
	SetActorValue("Variable09", 1.0)
	
	WI.ShowPlayerRoom(self, Bed)
endFunction

function ClearRoom()
; 	debug.trace(self + " ClearRoom called on RentRoomScript - room rental expired")
	; clear ownership - either rental expired or I died
	Bed.SetActorOwner((self as Actor).GetActorBase())
	UnregisterForUpdateGameTime()
	; used to conditionalize innkeeper dialogue
	SetActorValue("Variable09", 0.0)
endFunction

; when this is called, reset the ownership on the bed
event OnUpdateGameTime()
	ClearRoom()
endEvent

; if I die, clear the room rental as well, to stop the timer
Event OnDeath(Actor akKiller)
	ClearRoom()
endEvent

; Extended Stay Edits 

function RentRoom3Day(ModExtendedStayVariables pQuestScript)
	Bed.SetActorOwner(Game.GetPlayer().GetActorBase())
	RegisterForSingleUpdateGameTime (pQuestScript.RentHours3Day)
	Game.GetPlayer().RemoveItem(pQuestScript.Gold, pQuestScript.RoomRentalCost3DayGlobal.GetValueInt())
	; used to conditionalize innkeeper dialogue
	SetActorValue("Variable09", 1.0)
	
	WI.ShowPlayerRoom(self, Bed)
endFunction

function RentRoom7Day(ModExtendedStayVariables pQuestScript)
	Bed.SetActorOwner(Game.GetPlayer().GetActorBase())
	RegisterForSingleUpdateGameTime (pQuestScript.RentHours7Day)
	Game.GetPlayer().RemoveItem(pQuestScript.Gold, pQuestScript.RoomRentalCost7DayGlobal.GetValueInt())
	; used to conditionalize innkeeper dialogue
	SetActorValue("Variable09", 1.0)
	
	WI.ShowPlayerRoom(self, Bed)
endFunction

function RentRoom30Day(ModExtendedStayVariables pQuestScript)
	Bed.SetActorOwner(Game.GetPlayer().GetActorBase())
	RegisterForSingleUpdateGameTime (pQuestScript.RentHours30Day)
	Game.GetPlayer().RemoveItem(pQuestScript.Gold, pQuestScript.RoomRentalCost30DayGlobal.GetValueInt())
	; used to conditionalize innkeeper dialogue
	SetActorValue("Variable09", 1.0)
	
	WI.ShowPlayerRoom(self, Bed)
endFunction



Specifically, I want to talk about the RentRoom() function and how it seemingly calls a function from a script attached to an external quest, without declaring that quest as a property.

 

function RentRoom(DialogueGenericScript pQuestScript)
	Bed.SetActorOwner(Game.GetPlayer().GetActorBase())
	RegisterForSingleUpdateGameTime (pQuestScript.RentHours)
	Game.GetPlayer().RemoveItem(pQuestScript.Gold, pQuestScript.RoomRentalCost.GetValueInt())
	; used to conditionalize innkeeper dialogue
	SetActorValue("Variable09", 1.0)
	
	WI.ShowPlayerRoom(self, Bed)
endFunction
function RentRoom(DialogueGenericScript pQuestScript) seems to be the only reference to the script it's calling properties from. This is not a method I have found documented anywhere else, but it works.

 

Some context: The room rental script is attached to NPCs, and is called in dialogue to rent rooms. DialogueGenericScript is a script that contains nothing but variables (like room rental time and cost) and is attached to the generic dialogue quest. These variables are successfully being called.

 

I used the same thing on the custom RentRoom3day(), 7day, and 30day functions and it works.

 

function RentRoom30Day(ModExtendedStayVariables pQuestScript)

 

My variables script (ModExtendedStayVariables) isn't even attached to the same quest as the DialogueGenericScript is! Mine's on a custom quest, completely separate. And yet somehow, without declaring my script as a property, it's still successfully calling and using properties in my custom functions to determine price/hours rented.

 

Every other method I've seen for calling properties from other scripts requires declaring that script as a property, but this seems to do it on its own just by including the name of the script in the function and giving using it as a property (in this case, pQuestScript). The only script being pointed to is the WIFunctionsScript which does not contain variables used.

 

I am baffled. Anybody know what the deal with this is? Don't get me wrong, I'm glad it works, but it's driving me crazy that I don't know WHY it works.

 

EDIT: Oh and here's a fragment from dialogue that calls the RentRoom() functions added by the mod just in case it's relevant.

 

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

;BEGIN FRAGMENT Fragment_2
Function Fragment_2(ObjectReference akSpeakerRef)
Actor akSpeaker = akSpeakerRef as Actor
;BEGIN CODE
(akspeaker as RentRoomScript).RentRoom30Day(GetOwningQuest() as MODExtendedStayVariables)
;END CODE
EndFunction
;END FRAGMENT

;END FRAGMENT CODE - Do not edit anything between this and the begin comment
Edited by Robbie922004
Link to comment
Share on other sites

The script is not declared as a property because it doesn't refer to a specific object, but to any object that is passed to it. In this case, it refers to the script attached to akSpeaker, whoever akSpeaker may happen to be.

The variables script it's pulling properties from isn't attached to akSpeaker, it's only attached to the DialogueGeneric quest.

 

It makes sense in the papyrus fragment because it's calling the variable script from the quest the dialogue that fires the fragment is attached to. I'm just not sure how in the main RentRoomScript (which is attached to actors but has no variables) it can compile without declaring the variables script as a property.

Link to comment
Share on other sites

You're wrong. The RentRoomScript is attached to every innkeeper.

 

Premature ejaculation.

 

The variables are coming from the MODExtendedStayVariables, which is being called by casting GetOwningQuest() to it. This method is used extensively in the vanilla game.

 

The RentRoomScript can compile because it does have variables. The parameter pQuestScript is a variable.

Edited by lofgren
Link to comment
Share on other sites

I am not an expert, but from what I have noticed, you can use a property of the base type and cast it:

Quest Property SomeQuest Auto

(SomeQuest as SomeQuestScript).SomeFunction() ; <-- where SomeQuestScript extends Quest

And you can also use the derived script name as the type (since the 'type' is just a script name, Quest, ObjectReference, Form, etc. all are scripts)

SomeQuestScript Property SomeQuest Auto

SomeQuest.SomeFunction() ; <-- where SomeQuest IS SomeQuestScript (the type, no need to cast)

And you can also use a derived script as the type, and cast it as another script derived from a same parent/base/something - a sort of 'sibling':

SomeQuestScript Property SomeQuest Auto

((SomeQuestScript as Quest) as OtherScriptThatExtendsQuestScript).OtherFunction()

But you cannot cast one derived script as another derived script (a 'sibling' of sorts) directly. That does not work.

 

Then, when filling the property in the Creation Kit, if you have a script

ScriptName _AAA_Flying_Pigs_Quest Extends Quest

that you attach to a quest (in the Creation Kit), and then have another script

ScriptName _AAA_Flying_Pigs_Actor Extends Actor

_AAA_Flying_Pigs_Quest Property FlyingPigQuest Auto

then the Creation Kit will only offer you quests that have _AAA_Flying_Pigs_Quest script attached to them (any quests that have it attached would do).

 

Hopefully that makes some sense. :rolleyes:

 

For example:

 

SomeQuestScript.psc

 

 

ScriptName SomeQuestScript Extends Quest
 
Function SomeFunction()
EndFunction

 

 

 

 

OtherQuestScript.psc

 

 

ScriptName OtherQuestScript Extends Quest
 
Function OtherFunction()
EndFunction

 

 

 

 

SomeObjectReferenceScript.psc

 

 

ScriptName SomeObjectReferenceScript Extends ObjectReference
 
Quest Property MyQuest Auto
SomeQuestScript Property SomeQuest Auto
OtherQuestScript Property OtherQuest Auto
 
Event OnActivate(ObjectReference akActionRef)
 
    ;/
        with property of type Quest Script, so a cast is needed
        since Quest Script does not have those functions
    /;
    (MyQuest as SomeQuestScript).SomeFunction()
    (MyQuest as OtherQuestScript).OtherFunction()
 
    ;/
        with property types being the actual derived scripts already,
        so no need to cast anything
    /;
    SomeQuest.SomeFunction()
    OtherQuest.OtherFunction()
 
    ;/
        this is interesting, the property is a derived script,
        so to call functions from ANOTHER derived script (a sort of 'sibling')
        the cast goes like:

        derived -> base -> another derived (the 'sibling' of sorts)
    /;
    ((SomeQuest as Quest) as OtherQuestScript).OtherFunction()
    ((OtherQuest as Quest) as SomeQuestScript).SomeFunction()
 
    ;/
        However these below DO NOT WORK
 
        (SomeQuest as OtherQuestScript).OtherFunction()
        (OtherQuest as SomeQuestScript).SomeFunction()
 
        since a direct cast cannot be done (they are 'siblings' of sorts)
    /;

EndEvent

 

 

 

 

The 'type' - Quest, Actor, Form, ObjectReference, etc. - is just a script. They are listed here: http://www.creationkit.com/index.php?title=Script_Objects

 

If someone spots anything fishy in that, feel free to correct. :blush:

 

Edit: Umm... I think misunderstood the question. Oops. :psyduck:

Edited by Contrathetix
Link to comment
Share on other sites

You're wrong. The RentRoomScript is attached to every innkeeper.

 

Premature ejaculation.

 

The variables are coming from the MODExtendedStayVariables, which is being called by casting GetOwningQuest() to it. This method is used extensively in the vanilla game.

 

The RentRoomScript can compile because it does have variables. The parameter pQuestScript is a variable.

I'm not sure I follow. It's only being cast as an owning quest in the fragment in the dialogue, not in the RentRoomScript() itself. Every other time I've tried to include a variable without declaring it properly (read: wrote it wrong) the script refuses to compile. So I'm unsure how RentRoomScript can correctly compile without explicitly declaring ModExtendedStayVariables (or DialogueGenericScript) as properties that are used for the pQuestScript variable.

Link to comment
Share on other sites

I think I am trying starting (oops, cannot type) to sort of understand the question now. Sorry for getting confused. :blush: So in this part:

function RentRoom(DialogueGenericScript pQuestScript)
    Bed.SetActorOwner(Game.GetPlayer().GetActorBase())
    RegisterForSingleUpdateGameTime (pQuestScript.RentHours)
    Game.GetPlayer().RemoveItem(pQuestScript.Gold, pQuestScript.RoomRentalCost.GetValueInt())
    ; used to conditionalize innkeeper dialogue
    SetActorValue("Variable09", 1.0)

    WI.ShowPlayerRoom(self, Bed)
endFunction

The parameter declaration

DialogueGenericScript pQuestScript

is actually also a variable declaration. The contents of DialogueGenericScript is this (apparently):

 

 

Scriptname DialogueGenericScript extends Quest  Conditional
GlobalVariable Property RoomRentalCost  Auto 
MiscObject Property Gold  Auto 

float Property RentHours = 24.0  Auto 
{standard room rental time in hours of game time}

 

 

where everything is defined that is used from it.

 

The "variable type" (being DialogueGenericScript) is just a script name, like Quest, Form, SKI_ConfigBase and the like. It is not a "type" like the ones in Creation Kit, as funny as it sounds.

 

Edit: Considering what the function looks like:

function RentRoom30Day(ModExtendedStayVariables pQuestScript)

And how it is called, and how the parameter is passed:

(akspeaker as RentRoomScript).RentRoom30Day(GetOwningQuest() as MODExtendedStayVariables)

It could be that the owning quest of the dialogue topic (GetOwningQuest()) has MODExtendedStayVariables script attached to it, and that the properties of the script have been filled on that quest in the Creation Kit.

Edited by Contrathetix
Link to comment
Share on other sites

Contrathetix, it sounds like you've got it.

 

A parameter is just a variable that is passed into a function when it is called. For example if I write a function:

function LookAtMe(actor you, actor me)

then within the function I can call:

You.HasLOS(Me)

as long as when I call the function from my script, I am sure to fill the variables:

LookAtMe(Brynjolf, PlayerREF)

You can do this with any script, including scripts you write yourself. This is basically what is happening with the script in question.

Link to comment
Share on other sites

Interesting. So it seems like it's indeed possible to call properties from a script without fully declaring the property in the usual way (Script Property YourScript Auto) just by including the script name in your function. This seems like a much simpler way of doing things, no? Saves a whole step just by doing something like: YourFunction(YourScript YourVariable)

 

What was tripping me up is that it seems like it's automatically grabbing the correct script without declaring it using only the script name, which is something I've not seen before. The only time I've gotten something along these lines to compile in the past required proper declaration via the usual method.

 

Now I have to wonder why this method isn't more commonly used or better documented. Seems a lot more direct than other methods used to call properties from other scripts.

 

Edit: Considering what the function looks like:

 


function RentRoom30Day(ModExtendedStayVariables pQuestScript)
And how it is called, and how the parameter is passed:


(akspeaker as RentRoomScript).RentRoom30Day(GetOwningQuest() as MODExtendedStayVariables)
It could be that the owning quest of the dialogue topic (GetOwningQuest()) has MODExtendedStayVariables script attached to it, and the properties of the script have been filled on that quest in the Creation Kit.

 

This is correct. The owning quest for the custom dialogue branches is a custom quest that has ModExtendedStayVariables attached, and the properties are filled out. I wasn't confused as to why the papyrus fragments in dialogue were working (I wrote them) because of the GetOwningQuest() function being called. I use these variables much like the vanilla room rental uses DialogueGenericScript to set the cost for room rental and the time that you're allowed to stay. And in the case of prices, they're globals that can be changed to let users customize prices.

 

I was just unsure as to why RentRoomScript would work in the way that it was, or even compile, without declaring the script it's yanking properties from. But if that's a valid declaration, that clears everything up.

Edited by Robbie922004
Link to comment
Share on other sites

Somewhere along the way you have to declare the object by name. You're not really saving a step or anything. If it seems simpler, that's just your subjective opinion based on what feels more intuitive to you. If it feels more intuitive then feel free to use it but there's nothing inherently better about it one way or the other.

Edited by lofgren
Link to comment
Share on other sites

  • Recently Browsing   0 members

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