Tracking a bunch of quests in one quest script

Post » Mon Nov 19, 2012 4:44 am

Basically what I'm wanting to do is make a display room that enables objects based on quests the player has completed. For example, if the player becomes Thane of The Rift, it would enable a Riften banner in the display room.

I think I understand the basics of how to do this, but as a novice scripter, I am sometimes confused on how to use else, if/endiff and elseif in a script that checks for multiple things. I can write it in English, but I need help with the actual code.

(In English it would be: "if Favor Quest 250 is at stage 25 or higher, enable my banner and check the next quest. If the next quest is at stage XX or higher enable the banner or object I placed for that quest, and keep going through the other quests I want to check doing the same thing...")

I think I need to just create a quest that I will start at the stage where my display room becomes available to the player. The script on the quest will need to run to check what the player has done so fay, and enable the stuff he has already completed, and then track future progress from there. So, is this on the right track?

Scriptname BalokDisplayRoomQuestScript extends QuestQuest property Favor250 auto ;MarcarthQuest property Favor252 auto ;SolitudeQuest property Favor253 auto ;WhiterunObjectReference Property MyMarcarthBanner  AutoObjectReference Property MySolitudeBanner  AutoObjectReference Property MyWhiterunBanner  AutoEvent OnInit()			RegisterForSingleUpdate(120)EndEventEvent OnUpdate()			if (Favor250.GetStageDone(25))			   MyMarcarthBanner.Enable()			elseif				(Favor252.GetStageDone(25))  				MySolitudeBanner.Enable()			elseif		           (Favor253.GetStageDone(25))  			       MyWhiterunBanner.Enable()			endifEndEvent

I will be doing this for WAY more than those 3 quests, and I will probably be able to use "if (QuestXXX.IsCompleted())" as well, but this is the gist of what I'm wanting to do.
User avatar
Veronica Flores
 
Posts: 3308
Joined: Mon Sep 11, 2006 5:26 pm

Post » Sun Nov 18, 2012 4:40 pm

Arrays to the rescue!
ScriptName BalokDisplayRoomDoorScript Extends ObjectReferenceObjectReference[] Property RefArray AutoQuest[] Property QuestArray AutoEvent OnCellAttach()	Int iIndex = QuestArray.Length	While iIndex > 0		iIndex -= 1		If QuestArray[iIndex].GetStageDone(25)			RefArray[iIndex].Enable()		EndIf	EndWhileEndEvent
Just make sure the indices correspond and you're golden. I'd do it http://www.creationkit.com/OnCellAttach_-_ObjectReference instead though, attaching the script to the inside door REFR or something so it won't run until it matters.
User avatar
Eduardo Rosas
 
Posts: 3381
Joined: Thu Oct 18, 2007 3:15 pm

Post » Mon Nov 19, 2012 5:33 am

Thanks Justin, but I'm in uncharted waters with arrays. Can you be a little more detailed? Searching for QuestArray on the wiki produced no results.
User avatar
Caroline flitcroft
 
Posts: 3412
Joined: Sat Nov 25, 2006 7:05 am

Post » Mon Nov 19, 2012 12:21 am

Thanks Justin, but I'm in uncharted waters with arrays. Can you be a little more detailed? Searching for QuestArray on the wiki produced no results.
Compile the above, attach it to an object in that room from within the render/cell window, then check out the properties. If you have ten quests and ten references to enable, add ten elements to each array and point them to the ten quests/references you want to enable. If element 0 of QuestArray is Favor250, element 0 of RefArray needs to be MyMarcarthBanner etc. So long as the indices match between the two arrays (basically indexed lists), that While loop will do what you're after. In the event you have more than 128 quests, you'd want FormLists which you can do the same thing with, just a little differently.

http://www.creationkit.com/Arrays_(Papyrus)
User avatar
Latisha Fry
 
Posts: 3399
Joined: Sat Jun 24, 2006 6:42 am

Post » Mon Nov 19, 2012 5:05 am

HOLY CRAP! That is a thing of beauty! I do have one question though, and it's probably a dumb one, lol. But do I need to edit it for each index like this:

If QuestArray[0].GetStageDone(25)		    RefArray[0].Enable()EndIfIf QuestArray[2].GetStageDone(50)		    RefArray[2].Enable()EndIfIf QuestArray[3].GetStageDone(75)		    RefArray[3].Enable()EndIf

Otherwise, what do I do if I need to check something other than GetStageDone and stage 25?
User avatar
Julie Serebrekoff
 
Posts: 3359
Joined: Sun Dec 24, 2006 4:41 am

Post » Sun Nov 18, 2012 11:31 pm

Arrays are cool stuff. You'd not have to write an If/ElseIf for each index unless checking different stuff or doing different stuff for each quest. If the pattern in the OP is uniform for each quest, the while loop will walk through 'em all as it is. If checking different stuff in each instance, you'd have to write an 'If/EsleIf' for each indexed quest.
User avatar
Dalton Greynolds
 
Posts: 3476
Joined: Thu Oct 18, 2007 5:12 pm

Post » Sun Nov 18, 2012 10:11 pm

OK, I'm back on this after a couple days off for the holiday, and something is not working.

EDIT: As these things usually go, after I posted this I figured out why the OnCellAttach was not working. I had made a quick copy of my plugin as a backup right there in the Data folder, and the game in its infinite wisdom automatically checked it off in my load order and it was overriding my working plugin, lol. However, now that I can see the cell is loading (duh, I'm in it aren't I?) my banner is not enabling. During this test, the first check for QuestArray[0] would return false, but the 2nd check for QuestArray[1] should return true as I completed the Thane section of MQ104 (stage 160 is set by Balgruf's dialog when he makes you Thane at the end of MQ104 "Dragon Rising"). Both of these checks would enable the same banner, RefArray[0].

Scriptname BalokDisplayRoomDoorScript extends ObjectReference  ObjectReference[] Property RefArray AutoQuest[] Property QuestArray AutoEvent OnCellAttach()Debug.Notification("Cell has attached")		Int iIndex = QuestArray.Length		While iIndex > 0				iIndex -= 1				If QuestArray[0].GetStageDone(25)					RefArray[0].Enable()										ElseIf QuestArray[1].GetStageDone(160)			   	 RefArray[0].Enable()				EndIf		EndWhileEndEvent
User avatar
Invasion's
 
Posts: 3546
Joined: Fri Aug 18, 2006 6:09 pm

Post » Mon Nov 19, 2012 12:56 am

If QuestArray[0].GetStageDone(25)RefArray[0].Enable()
should be:

If QuestArray[iIndex].GetStageDone(25)RefArray[iIndex].Enable()


ElseIf QuestArray[1].GetStageDone(160)RefArray[0].Enable()

should be:

ElseIf QuestArray[iIndex].GetStageDone(160)RefArray[iIndex].Enable()



At least I think so, if I understood what Justin taught you ...
User avatar
ijohnnny
 
Posts: 3412
Joined: Sun Oct 22, 2006 12:15 am

Post » Sun Nov 18, 2012 10:01 pm

Thanks h4, but if that were so, how would it distinguish between my arrays if they are all simply iIndex? Does it somehow just keep a running order of them in the script from top to bottom?

Good news is that with further testing, I've been able to coc into my main tower, use the console to setstage mq104 160, then enter my display room and that seems to be working, but setstage favor253 25 still does not. The earlier tests were not working because of the way scripts are saved in the save game I guess.
User avatar
ONLY ME!!!!
 
Posts: 3479
Joined: Tue Aug 28, 2007 12:16 pm

Post » Sun Nov 18, 2012 8:29 pm

When you set up array property elements in the CK, you can set their order by moving them up/down. Just set all the arrays up so the indices correspond with each other, then iIndex will line up across the arrays.

If the stage to check for varies, add an Int array whose elements will also line up.

ScriptName BalokDisplayRoomDoorScript Extends ObjectReferenceInt[] Property iStageArray AutoObjectReference[] Property kRefArray AutoQuest[] Property kQuestArray AutoEvent OnCellAttach()	Int iIndex = kQuestArray.Length ; How many quests? Other arrays are of the same length. If 10 quests, iIndex will start equaling ten	While iIndex > 0 ; Walk through elements		iIndex -= 1 ; Element 9 is tenth member. 0 is first		If kQuestArray[iIndex].GetStageDone(iStageArray[iIndex]) ; Since the indices correspond, the right stage will be checked for each quest			kRefArray[iIndex].Enable() ; as with the other arrays, the indices correspond so the correct ObjectReference will be enabled		EndIf	EndWhileEndEvent
User avatar
Tammie Flint
 
Posts: 3336
Joined: Mon Aug 14, 2006 12:12 am

Post » Sun Nov 18, 2012 10:37 pm

When you set up array property elements in the CK, you can set their order by moving them up/down. Just set all the arrays up so the indices correspond with each other, then iIndex will line up across the arrays.

If the stage to check for varies, add an Int array whose elements will also line up.

OK, I think I follow you. So adding more quests to track the stages of would look like this? (Like I said in my OP, I get tripped up sometimes by if, elseif and else and what to use where.)

ScriptName BalokDisplayRoomDoorScript Extends ObjectReferenceInt[] Property iStageArray AutoObjectReference[] Property RefArray AutoQuest[] Property QuestArray AutoEvent OnCellAttach()		Int iIndex = QuestArray.Length ; How many quests? Other arrays are of the same length.		While iIndex > 0				iIndex -= 1				If QuestArray[iIndex].GetStageDone(iStageArray[iIndex]) ;my 0 index						RefArray[iIndex].Enable()										ElseIf QuestArray[iIndex].GetStageDone(iStageArray[iIndex]) ;my 1 index						RefArray[iIndex].Enable()										ElseIf QuestArray[iIndex].GetStageDone(iStageArray[iIndex]) ;my 2 index						RefArray[iIndex].Enable()										ElseIf QuestArray[iIndex].GetStageDone(iStageArray[iIndex]) ;my 3 index						RefArray[iIndex].Enable()										ElseIf QuestArray[iIndex].GetStageDone(iStageArray[iIndex]) ;my 4 index						RefArray[iIndex].Enable()										EndIf		EndWhileEndEvent

Then I just set the quest properties, stage int's and my enable objects as I want them? Somehow this seems just too damn easy!
User avatar
Latino HeaT
 
Posts: 3402
Joined: Thu Nov 08, 2007 6:21 pm

Post » Sun Nov 18, 2012 9:44 pm

You won't need the 'ElseIf'. The loop below will suffice. Each 'ElseIf' in what you just posted would be checking the same stuff 'cause iIndex won't be changed until the next iteration of the While loop.

Int[] Property iStageArray AutoObjectReference[] Property kRefArray AutoQuest[] Property kQuestArray AutoEvent OnCellAttach()	Int iIndex = kQuestArray.Length	While iIndex > 0		iIndex -= 1		If kQuestArray[iIndex].GetStageDone(iStageArray[iIndex]) ; Your EVERY index			kRefArray[iIndex].Enable()		EndIf	EndWhileEndEvent
User avatar
Ells
 
Posts: 3430
Joined: Thu Aug 10, 2006 9:03 pm

Post » Mon Nov 19, 2012 3:50 am

You won't need the 'ElseIf'. The loop below will suffice. Each 'ElseIf' in what you just posted would be checking the same stuff 'cause iIndex won't be changed until the next iteration of the While loop.

:facepalm:

OK, I think I get it now. When it checks [iIndex] it checks EVERYTHING in that index! (You gotta draw me a picture sometimes, lol.)

I'm sure I'll have more questions as I go through this, but one thing I just thought about was that this display room already has around 20 weapon displays, and will have probably 10 to 12 mannequins in there as well. I'm using Amethyst Deceiver's weapon display scripts, and will be trying to use his/her? mannequin scripts as well. But, am I heading for trouble having all this stuff enable OnCellAttach? I'm not only wanting the banners, but I'll likely be placing all kinds of stuff like the Thief Guild rewards. I was hoping to also place a miniature of each of the Lord Stones that would enable when the player found each of them as well. TBH, I'm still working up all the stuff I'd like to add, so there will probably be more. Do I need to be concerned with all this stuff going on when this cell loads? I think in terms of objects, it won't be a big deal, but I seem to remember there being a problem with how much papyrus activity you can safely run. (http://www.gamesas.com/topic/1372057-universal-item-display-script-weapons-shields-potions-etc-mini-tutorial/page__view__findpost__p__20733704)
User avatar
Pete Schmitzer
 
Posts: 3387
Joined: Fri Sep 14, 2007 8:20 am

Post » Mon Nov 19, 2012 4:57 am

Once you get arrays, they are great and you can do the stuff Justin is talking about, in great depth.

Arrays are a list that is indexed - like a book - with a pointer (a key) to the contents of each record. You get at the records contained in an array by using the "key". Each entry is stored with a key (usually a sequential number).

And, if you can control the order that things are stored in an array, you can do multiple-array-pivots (or something similarly named) that let you have multiple dimensions of information, all stored with the same KEY.

Here's a stupidly simple example

declare AnArrayOfMyFriends()AnArrayOfMyFriends[0] = "Bill"AnArrayOfMyFriends[1] = "Sally"AnArrayOfMyFriends[2] = "Jane"

declare AnArrayOfMyFriendsBirthdays()AnArrayOfMyFriendsBirthdays[0] = "June 1st 1990"AnArrayOfMyFriendsBirthdays[1] = "May 21st 1974"AnArrayOfMyFriendsBirthdays[2] = "November 3rd 1982"

So, because we control the order - the index values (the keys) - that things are stored against in the arrays, we know that the two arrays tie together ... but really they are just two seperate arrays.

Because we put the stuff in, in a regular way, we can make use of identical-keys to pull it back out

So, because we stored "Bill" with a KEY of 0 in the first array, we added Bill's birthday to the second array with the very same key (0).


This is how Justin's example works.

You have two - or more!! - arrays that you have filled with related information. But you have filled them in such a way that you can use the same "key" to get at the related information in each of the arrays.

So:

my-mates-name = AnArrayOfMyFriends[0]my-mates-birthday = AnArrayOfMyFriendsBirthdays[0]

Will return Bill's name AND Bill's birthday.

Justin then goes a bit futher and uses a COUNTER to step through the "outside" array (iIndex) and also read the "inside" array

Arrays are quick to process ... but if you have hundreds (with lots of data in them) to loop through, then yeah, you will slow up the game (though threads may help you some)



(having read back through all that ... I'm not sure I'm helping!!!? Sorry if I'm not)



http://www.cplusplus.com/doc/tutorial/arrays/ for the theory, have a read of that (it is c++, but that doesn't matter ... arrays work the same way - nearly - in all languages). Oh and please ignore multi-dimensional arrays ... or you will hurt your head very badly indeed!!
User avatar
Devils Cheek
 
Posts: 3561
Joined: Sun Aug 13, 2006 10:24 pm

Post » Sun Nov 18, 2012 7:12 pm

Arrays are quick to process ... but if you have hundreds (with lots of data in them) to loop through, then yeah, you will slow up the game (though threads may help you some)

I'll probably end up with around 50 to 75 with 3 sets of data, checking a quest, a stage and enabling something based on those parameters.
User avatar
phil walsh
 
Posts: 3317
Joined: Wed May 16, 2007 8:46 pm

Post » Sun Nov 18, 2012 6:21 pm

I'll probably end up with around 50 to 75 with 3 sets of data, checking a quest, a stage and enabling something based on those parameters.
It'll chug a little bit, I think.

Depends when you are doing it (and how often).

If other intensive stuff is going on, at the same time - combat or whatever - then you may see an effect on your script (and//or you may see any effect on the processing time of the game itself, but I doubt that for the numbers you are talking about)

I guess trying to activate the script when you know things are quiet would be best, to be as safe as possible (so check for InCombat and things like that, if you can)?
User avatar
Ernesto Salinas
 
Posts: 3399
Joined: Sat Nov 03, 2007 2:19 pm

Post » Sun Nov 18, 2012 9:38 pm

The While loop, even if the arrays are maxed out at 128, is still faster than a single Game.GetPlayer()

Spoiler
[09/04/2012 - 07:43:34PM] Code Comparer log opened (PC)[09/04/2012 - 07:43:34PM] Skyrim Version: 1.7.7.0[09/04/2012 - 07:43:34PM] SKSE Version: 1.051100[09/04/2012 - 07:43:46PM] Calibration Complete for 10^3 iterations: 0.010130 for each empty test[09/04/2012 - 07:43:56PM] === 'GetPlayer' ===[09/04/2012 - 07:43:56PM] Started 'GetPlayer' at: 34.803001 | Iterations to complete: 1000[09/04/2012 - 07:43:56PM] Finished 'GetPlayer' at: 45.130867 | Iterations completed: 1000[09/04/2012 - 07:43:56PM] Time elapsed (Raw) for 'GetPlayer': 10.337997[09/04/2012 - 07:43:56PM] Time elapsed (Calibrated) for 'GetPlayer': 10.327868[09/04/2012 - 07:43:56PM] Approximate time for each iteration (Raw): 0.010338[09/04/2012 - 07:43:56PM] Approximate time for each iteration (Calibrated): 0.010328[09/04/2012 - 07:43:56PM] === 'While Loop Posted Above' ===[09/04/2012 - 07:43:56PM] Started 'loop' at: 45.160999 | Iterations to complete: 1000[09/04/2012 - 07:43:56PM] Finished 'loop' at: 45.161869 | Iterations completed: 1000[09/04/2012 - 07:43:56PM] Time elapsed (Raw) for 'loop': 0.011002[09/04/2012 - 07:43:56PM] Time elapsed (Calibrated) for 'loop': 0.000872[09/04/2012 - 07:43:56PM] Approximate time for each iteration (Raw): 0.000011[09/04/2012 - 07:43:56PM] Approximate time for each iteration (Calibrated): 0.000001[09/04/2012 - 07:44:06PM] Log closed
User avatar
Bellismydesi
 
Posts: 3360
Joined: Sun Jun 18, 2006 7:25 am

Post » Mon Nov 19, 2012 2:47 am

The While loop, even if the arrays are maxed out at 128, is still faster than a single Game.GetPlayer()

Frightening stat :dry:
User avatar
Dan Wright
 
Posts: 3308
Joined: Mon Jul 16, 2007 8:40 am

Post » Mon Nov 19, 2012 2:41 am

Yeah. I'm sure I'm deranged or something, but even if I'm only referring to the player once in a script I still tend to add a PlayerREF property. IIRC, Game.GetForm(0x00000014) and Game.GetFormFromFile(0x00000014, "Skyrim.ESM") are both substantially faster then GetPlayer, but not to the extent a PlayerREF property is.

Ah... There it is:
Spoiler
[09/03/2012 - 12:13:39AM] Code Comparer log opened (PC)[09/03/2012 - 12:13:39AM] Code Comparer Version: 1.000000[09/03/2012 - 12:13:39AM] Skyrim Version: 1.7.7.0[09/03/2012 - 12:13:39AM] SKSE Version: 1.051100[09/03/2012 - 12:13:51AM] Calibration Complete for 10^4 iterations: 0.041440 for each empty test[09/03/2012 - 12:13:51AM] === 'GetForm' ===[09/03/2012 - 12:13:51AM] Started 'GetForm' at: 34.956001 | Iterations to complete: 10000[09/03/2012 - 12:13:51AM] Finished 'GetForm' at: 35.067562 | Iterations completed: 10000[09/03/2012 - 12:13:51AM] Time elapsed (Raw) for 'GetForm': 0.153000[09/03/2012 - 12:13:51AM] Time elapsed (Calibrated) for 'GetForm': 0.111560[09/03/2012 - 12:13:51AM] Approximate time for each iteration (Raw): 0.000015[09/03/2012 - 12:13:51AM] Approximate time for each iteration (Calibrated): 0.000011[09/03/2012 - 12:14:01AM] Calibration Complete for 10^4 iterations: 0.041820 for each empty test[09/03/2012 - 12:14:01AM] === 'GetFormFromFile' ===[09/03/2012 - 12:14:01AM] Started 'GetFormFromFile' at: 45.202000 | Iterations to complete: 10000[09/03/2012 - 12:14:01AM] Finished 'GetFormFromFile' at: 45.315178 | Iterations completed: 10000[09/03/2012 - 12:14:01AM] Time elapsed (Raw) for 'GetFormFromFile': 0.154999[09/03/2012 - 12:14:01AM] Time elapsed (Calibrated) for 'GetFormFromFile': 0.113179[09/03/2012 - 12:14:01AM] Approximate time for each iteration (Raw): 0.000015[09/03/2012 - 12:14:01AM] Approximate time for each iteration (Calibrated): 0.000011[09/03/2012 - 12:14:15AM] Calibration Complete for 10^4 iterations: 0.041470 for each empty test[09/03/2012 - 12:15:57AM] === 'getplayer' ===[09/03/2012 - 12:15:57AM] Started 'getplayer' at: 59.305000 | Iterations to complete: 10000[09/03/2012 - 12:15:57AM] Finished 'getplayer' at: 161.638519 | Iterations completed: 10000[09/03/2012 - 12:15:57AM] Time elapsed (Raw) for 'getplayer': 102.374992[09/03/2012 - 12:15:57AM] Time elapsed (Calibrated) for 'getplayer': 102.333519[09/03/2012 - 12:15:57AM] Approximate time for each iteration (Raw): 0.010237[09/03/2012 - 12:15:57AM] Approximate time for each iteration (Calibrated): 0.010233[09/03/2012 - 12:16:07AM] Calibration Complete for 10^4 iterations: 0.041450 for each empty test[09/03/2012 - 12:16:08AM] === 'PlayerRef' ===[09/03/2012 - 12:16:08AM] Started 'PlayerRef' at: 171.867996 | Iterations to complete: 10000[09/03/2012 - 12:16:08AM] Finished 'PlayerRef' at: 171.868561 | Iterations completed: 10000[09/03/2012 - 12:16:08AM] Time elapsed (Raw) for 'PlayerRef': 0.042007[09/03/2012 - 12:16:08AM] Time elapsed (Calibrated) for 'PlayerRef': 0.000558[09/03/2012 - 12:16:08AM] Approximate time for each iteration (Raw): 0.000004[09/03/2012 - 12:16:08AM] Approximate time for each iteration (Calibrated): 0.000000[09/03/2012 - 12:16:19AM] Log closed
User avatar
kasia
 
Posts: 3427
Joined: Sun Jun 18, 2006 10:46 pm

Post » Mon Nov 19, 2012 5:49 am

Very interesting thread. I've got to give it another read-through to actually digest the array stuff. Thats amazing about how long game.getplayer takes, I think I'm going to join in on your PlayerREF property habit from now on

- Hypno
User avatar
maria Dwyer
 
Posts: 3422
Joined: Sat Jan 27, 2007 11:24 am


Return to V - Skyrim