Robbie922004 Posted October 21, 2016 Share Posted October 21, 2016 (edited) 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 October 21, 2016 by Robbie922004 Link to comment Share on other sites More sharing options...
lofgren Posted October 21, 2016 Share Posted October 21, 2016 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. Link to comment Share on other sites More sharing options...
Robbie922004 Posted October 21, 2016 Author Share Posted October 21, 2016 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 More sharing options...
lofgren Posted October 21, 2016 Share Posted October 21, 2016 (edited) 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 October 21, 2016 by lofgren Link to comment Share on other sites More sharing options...
Surilindur Posted October 21, 2016 Share Posted October 21, 2016 (edited) 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 QuestAnd 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 Questthat 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 Autothen 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 October 21, 2016 by Contrathetix Link to comment Share on other sites More sharing options...
Robbie922004 Posted October 21, 2016 Author Share Posted October 21, 2016 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 More sharing options...
Surilindur Posted October 21, 2016 Share Posted October 21, 2016 (edited) 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) endFunctionThe parameter declaration DialogueGenericScript pQuestScriptis 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 October 21, 2016 by Contrathetix Link to comment Share on other sites More sharing options...
lofgren Posted October 21, 2016 Share Posted October 21, 2016 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 More sharing options...
Robbie922004 Posted October 21, 2016 Author Share Posted October 21, 2016 (edited) 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 October 21, 2016 by Robbie922004 Link to comment Share on other sites More sharing options...
lofgren Posted October 21, 2016 Share Posted October 21, 2016 (edited) 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 October 21, 2016 by lofgren Link to comment Share on other sites More sharing options...
Recommended Posts