Why isn't my script seeing this global variable?

Post » Wed Jun 20, 2012 5:41 pm

OK, I have used quest globals for a while now, and I know how they work.

Here is the global variable script:

Spoiler
Scriptname RedwoodsTools extends Quest{ Global functions used by Redwood's Mods }import gameimport utilityimport mathbool  Property MapRequestBool AutoLLNNavOrgScript Function GetLLNO(RedwoodsTools SelfIn) Global ; Should be completely unnecessary.	Return SelfIn.NavigationOrgItemEndFunctionFunction UpdateRequest()Debug.Notification("Map request Function Call")	If !MapRequestBool ; Will prevent multiple registrations		MapRequestBool = True		UnRegisterForUpdate()		RegisterForSingleUpdate(1)	Endif	Utility.Wait(5)	MapRequestBool = FalseEndFunctionbool Function IsBetween(Float B, Float A, Float C) global	if A > C &&  ( A > B && B > C)		return TRUE	elseif A < C && (A < B &&  B < C)		return TRUE	elseif A == C && A == B ; Special case,all equal		return TRUE	endif	return FALSEendFunctionFloat Function DistanceBetween(Objectreference A,ObjectReference :cool: global ; Returns the distance between 2 objectreferences	return A.GetDistance( :cool: ; Really just a way to call it without referencing one of the objects in the call.EndFunctionint function RollDice(int Number, int type) global ; Return the result of rolling number Dice with type sides each.	int count=0	int Value=http://forums.bethsoft.com/topic/1362677-why-isnt-my-script-seeing-this-global-variable/0	while count < Number		Value += RandomInt(1,type)		count += 1	endwhile	return Valueendfunctionfunction BounceItem(ObjectReference Item) global ; Add a random Havok Impulse to the item.	float X = (RollDice(1,360) as float)	float Y = (RollDice(1,360) as float)	float Z = (RollDice(1,360) as float)	float Force = (RollDice(1,99) as Float)	Item.ApplyHavokImpulse(X,Y,Z,Force)Endfunction; Variables for Linked List implementationActivator property NodeType Auto ; what type of object are we using as default nodes? Make sure to attach the linked list node script to said object type!FormList property RandomNodeType Auto ; Optional list of random object types for the nodes. Make sure the linked list node script is attatched to all such objects!LinkedListControls property LList Auto ; Dummy node used to call the node creation functions.ObjectReference property NodeAxis AutoActivator Property NodeAxisType Auto ; What type of object is the node axis?ObjectReference Property NodeAxisHideSpot Auto ; Where do we hide the node axis?LLNOrgItem Property NodeOrgItem Auto ; Tvar.NodeOrgItem = For the linked list testing spell, to handle list organization outside the effect.Activator Property NodeOrgItemType Auto ; = The item type for the above organizer item.LLNNavOrgScript Property NavigationOrgItem Auto; Tvar.NavigationOrgItem = The organizer item for Navigatiion node linked lists.Activator Property NavNodeOrgItemType Auto ; = The Item type for the above organizer item.\Bool Property MapRequest=False Auto ; Is there a ship trying to get a map and unable to find the map controller?LinkedListControls Property NavMapList Auto ; Gvar.NavMapList = List of maps for navigation control. Ordinary vanilla linked list.PyramidLLNControllerScript  Property RoomNodeControlItem Auto ; The Node Controller for the Pyramid Maze linked listsActivator Property RoomNodeControllerItemType Auto ; The Item Type for the above controller item.ObjectReference Function InitializeLL(ObjectReference NodeAxis,Activator NodeAxisType) Global	ObjectReference NewAxis	if !NodeAxis ; We haven't got a place to put node lists yet!		NewAxis = FindClosestReferenceOfTypeFromRef(NodeAxisType,Game.GetPlayer(),100000) ; Look for one.		if !NewAxis ; None in the cell!			NewAxis = Game.GetPlayer().PlaceAtMe(NodeAxisType,1,true) ; Emergency...make one!		endif	endif	return NewAxisEndFunctionLinkedListControls Function SetLList(ObjectReference NodeAxis,Activator NodeType) global ; Function to make a dummy node.	LinkedListControls NewLList	while !NewLList		NewLList = (NodeAxis.PlaceAtMe(NodeType,1,true) as LinkedListControls) ; Dummy Linked List node just to use to call functions.	endwhile	return NewLListEndFunctionEvent OnInit()	UnRegisterForUpdate()	RegisterForSingleUpdate(1)EndEventEvent OnUpdate() ; Checks periodically to make sure important global variables get filled when needed.	string Msg = "Quest OnUpdate:"	if !NodeAxis		MSG = MSG + " Initialize Node Axis"		NodeAxis = InitializeLL(NodeAxis,NodeAxisType)	else		MSG = MSG + " Node Axis Exists"	endif	if !LList		MSG = MSG + " Make Dummy Item"		LList = SetLList(NodeAxis,NodeType)	else		MSG = MSG + " Dummy Exists"	endif	if !NavigationOrgItem		MSG = MSG + " Make NavOrg"		NavigationOrgItem = (NodeAxis.PlaceAtme(NavNodeOrgItemType,1) as LLNNavOrgScript)	else		MSG = MSG + " NavOrg Exists"	endif	if !NodeAxis || !LList || !NavigationOrgItem		MSG = MSG + " ImpossibleCondition"	endif	RegisterForSingleUpdate(10.0)	Debug.Notification(MSG)EndEvent

And here is the OnInit part of the Script I'm having trouble with:

Scriptname MovingNavigationByTranslatesScript extends ObjectReferenceimport RedwoodsToolsimport UtilityRedwoodsTools Property Gvar Auto ; Global variable Quest Script. Works for every other script that uses it.; Attach this script to the object to be moved.Int Property MapToUse = 0 AutoLinkedListControls property MyMap AutoLinkedListControls Property CurrentInstruction AutoNavigationNodeDataScript property Direction autobool property Updating = False autoLLNNavOrgScript Property MapController  Auto ; The Linked list controller item for the maps.Event OnInit()	If !Updating		Updating = True ; Prevents this from running twice.		setMotionType(Motion_Keyframed, TRUE)		While !Gvar.NavigationOrgItem ; Wait until Orginizing item is initialized before proceeding.			Wait(1.0)Debug.Notification("Waiting for Nav Org Item") ; spams this, despite making 100% sure the variable was initilized.		endwhileDebug.Notification("Gvar.NavigationOrgItem Initialized.")		While !MapController			MapController = Gvar.NavigationOrgItem ; Never gets here.		EndWhile		while !MyMapDebug.Notification("Map Controller loaded. Getting Map for "+name)			MyMap = MapController.GetMap(MapToUse)			if !MyMap				MapController.MakeMaps()Debug.Notification("Trying to make maps for "+Name)				Wait(5.0)			Endif		endWhileDebug.Notification("Map Loaded for "+Name)		CurrentInstruction = MyMap.First		Direction = (CurrentInstruction.Data as NavigationNodeDataScript)		Gvar.MapRequestBool = False		Updating = False	EndifEndEvent

When I boot the game up from a completely clean save, the quest script goes through it's update once initializing the variables, then repeatedly verifying that they're still loaded, but the other script gets stuck in the while loop checking whether one of the three variables is initialized...at the same time that the quest's update is repeatedly verifying it.

All the other parts of my mod that use this variable script have worked just fine. Why doesn't this one?

I've tried everything obvious, and quite a few things that aren't. The Gvar property is correctly assigned to the quest, the script isn't running multiple threads (At least I'm pretty darn sure it isn't...) and it's not on any other objects, only on the global variable quest. I know that both scripts are running, and that the variables are loaded...so what could possibly be causing this?
User avatar
Jeff Turner
 
Posts: 3458
Joined: Tue Sep 04, 2007 5:35 pm

Post » Wed Jun 20, 2012 6:44 pm

Hi,
I am quite new to this and I may be mistaken. My guess is that Gvar in your script is `none'. That is, it is not pointing to the running quest instance. You can check this by adding "if Gvar == none; debug.messagebox...". I would guess that the other cases work because you have the queast as a property in the CreationKit which is filled upon saving. To be honest, I do not know how this works. If your script is included in the esp, perhaps simply initializing the variable to the same ID (fill a property to see the ID) might work? Alternatively, you could have a quest call your script with a valid reference, or store it in a GlobalVariable. Perhaps there are yet simpler ways that I am not aware of.
User avatar
Ronald
 
Posts: 3319
Joined: Sun Aug 05, 2007 12:16 am

Post » Wed Jun 20, 2012 10:33 pm

Hi,
I am quite new to this and I may be mistaken. My guess is that Gvar in your script is `none'. That is, it is not pointing to the running quest instance. You can check this by adding "if Gvar == none; debug.messagebox...". I would guess that the other cases work because you have the queast as a property in the CreationKit which is filled upon saving. To be honest, I do not know how this works. If your script is included in the esp, perhaps simply initializing the variable to the same ID (fill a property to see the ID) might work? Alternatively, you could have a quest call your script with a valid reference, or store it in a GlobalVariable. Perhaps there are yet simpler ways that I am not aware of.

Trust me, Gvar isn't none. It's definitely pointing at the quest. I assigned it directly in the properties section...checked that about a zillion times.
User avatar
ONLY ME!!!!
 
Posts: 3479
Joined: Tue Aug 28, 2007 12:16 pm

Post » Wed Jun 20, 2012 1:35 pm

Try turning on stack profiling and stepping through it? Thats always my plan B when things aren't working the way I expect. I'm not really seeing the purpose to them being globals when you're providing the reference to the quest anyway?

I thought the point to globals was so that you could have access to the functions without actually requiring a reference to the script?
User avatar
Alina loves Alexandra
 
Posts: 3456
Joined: Mon Jan 01, 2007 7:55 pm

Post » Wed Jun 20, 2012 3:26 pm

Try turning on stack profiling and stepping through it? Thats always my plan B when things aren't working the way I expect. I'm not really seeing the purpose to them being globals when you're providing the reference to the quest anyway?

Actually, I was wrong about one thing...for some unfathomable reason, Gvar is, in fact, coming back as "none" - which should be impossible. It's a quest script on a quest that starts enabled, and therefore always exists. I've never seen that happen before, ever.

The reason it's a global is, it has to be persistent and sharable between scripts...the only way to do that is to make it a global variable on a quest script.
User avatar
WYatt REed
 
Posts: 3409
Joined: Mon Jun 18, 2007 3:06 pm

Post » Wed Jun 20, 2012 8:19 am

I don't get it, whats not persistent and unshareable about it not being a global, but still being a linked quest?

Scriptname MyQuest extends Quest

Int Property MyVar1 Auto


Scriptname MyOtherQuest extends Quest

MyQuest Property myFirstQuest Auto

myFirstQuest.MyVar1 = 10

Neither of these are globals but their information is still persistent and shareable... Unless one of those scripts is not actually attached to anything...


From my understanding, globals should be in a script that doesn't extend anything, and doesn't have properties. That way you should be able to just directly use the global with the specified parameters and without the need of a property pointing to the object.
User avatar
alyssa ALYSSA
 
Posts: 3382
Joined: Mon Sep 25, 2006 8:36 pm

Post » Wed Jun 20, 2012 1:08 pm

You will note that the other script that is using the variable is not a quest.

Quest Scripts are the only way to make a global reference variable. They are not "Globals" within the quest...it's the QUEST that is being used to store the references so they can be used globally in other scripts. That are not quests.

In order to access the properties in that quest in another script, you need to make a property with a type equal to the scriptname, and point that property at the quest. The script is attached to the quest.
User avatar
Nathan Barker
 
Posts: 3554
Joined: Sun Jun 10, 2007 5:55 am

Post » Wed Jun 20, 2012 3:44 pm

I think we are confusing our terminology here, nothing about the quest is global except for the functions labeled global. The variables are merely stored within your quest and are referenced as children of the quest object. I was confused why you defined global functions within the quest because you can access them regardless of them being global as you are pointing to the quest object. Global functions are so you do not need the quest reference to use them, unless you are using them elsewhere where you do not need the quest object referenced, in which case would make sense, but it would be neater to separate the globals from the non-globals.

Anyway your problem is solved, carry on.
User avatar
glot
 
Posts: 3297
Joined: Mon Jul 17, 2006 1:41 pm

Post » Wed Jun 20, 2012 7:11 pm

I think we are confusing our terminology here, nothing about the quest is global except for the functions labeled global. The variables are merely stored within your quest and are referenced as children of the quest object. I was confused why you defined global functions within the quest because you can access them regardless of them being global as you are pointing to the quest object. Global functions are so you do not need the quest reference to use them, unless you are using them elsewhere where you do not need the quest object referenced, in which case would make sense, but it would be neater to separate the globals from the non-globals.

Anyway your problem is solved, carry on.

Actually it's not...the quest property is still coming back as "None" and I have no idea why, or how to fix it. I've never seen that happen, and I use this kind of thing all the time.

The global functions are in there because they have to be somewhere, and since I'm storing my variables there anyway, it was a convenient place to put them.
User avatar
Veronica Martinez
 
Posts: 3498
Joined: Tue Jun 20, 2006 9:43 am

Post » Wed Jun 20, 2012 10:22 am

Try renaming it, compiling, then updating the properties in the plugin.
User avatar
Mizz.Jayy
 
Posts: 3483
Joined: Sat Mar 03, 2007 5:56 pm

Post » Wed Jun 20, 2012 3:30 pm

Try renaming it, compiling, then updating the properties in the plugin.

Urk! A full week of struggling with this, and just doing that worked? What makes me worry now is, what was causing that not to work in the first place, so I can avoid causing it to happen again. That was painful!

The weird part is, all I did was change "Gvar" to "Tvar" everywhere in the script with Find-and-replace, save, exit CK, re-load CK, assign the property.
User avatar
Sian Ennis
 
Posts: 3362
Joined: Wed Nov 08, 2006 11:46 am

Post » Wed Jun 20, 2012 11:48 am

Urk! A full week of struggling with this, and just doing that worked? What makes me worry now is, what was causing that not to work in the first place, so I can avoid causing it to happen again. That was painful!

The weird part is, all I did was change "Gvar" to "Tvar" everywhere in the script with Find-and-replace, save, exit CK, re-load CK, assign the property.
Glad that it worked out. May I ask, where in the CK can you set properties of an arbitrary ObjectReference extension?

Regarding Gvar->Tvar, the immediate thought would be the "old mod version incompatibility" topic which you have posted in.
User avatar
Add Me
 
Posts: 3486
Joined: Thu Jul 05, 2007 8:21 am

Post » Wed Jun 20, 2012 11:41 am

Glad that it worked out. May I ask, where in the CK can you set properties of an arbitrary ObjectReference extension?

Regarding Gvar->Tvar, the immediate thought would be the "old mod version incompatibility" topic which you have posted in.

No, this isn't an old Mod/New Mod problem...the name "Gvar" is completely arbitrary and could be anything. When declaring a variable you can name it anything you want.

If you mean where do you assign values to the properties in a script on a base object, click the "properties" button next to the script with the script selected, and you can edit the values of all properties in the script. By default, a double-click pulls this up as well.

If you mean changing the properties on a particular instance of a base object, get it in the render window and double click on it, and go to the scripts tab (the very last one on the far right) and select the script and click the Properties button. this way you can assign different values to a specific instance of a base object without affecting the base object itself.
User avatar
MatthewJontully
 
Posts: 3517
Joined: Thu Mar 08, 2007 9:33 am

Post » Wed Jun 20, 2012 6:49 pm

Try to using states instead of simple boolean-guards, they were designed to handle such scenarios and do so beautifully.

A boolean-guard is just a race-condition waiting to happen, it requires two (or three) separate operations. And I haven't heard or read anywhere that reading from or writing to a variable is atomic. Actually, I have read just the opposite on the wiki.

Other than that ... (organized in a list for a better overview)
  • This is a wasted guard, it can't evaluate to false.
    bool property Updating = False autoEvent OnInit()	If !Updating ;...

  • This code I just don't understand :huh:
    LinkedListControls NewLListwhile (!NewLList)	NewLList = SomeCall()endwhilereturn NewLList
    What's wrong with "return SomeCall()"?

  • Is this necessary, did you tested this scenario?
    Event OnInit()	UnRegisterForUpdate() ; I mean this line here	RegisterForSingleUpdate(1)EndEvent
    It can only make sense, if you call Reset from somewhere else which is I guess what is going on. But it would mean that scripts don't properly clean up after themselves.

  • And what's up with that weird RollDice function?
    Pseudo-RNG's don't get more random by calling them repeatedly, they get more predictable.
User avatar
koumba
 
Posts: 3394
Joined: Thu Mar 22, 2007 8:39 pm

Post » Wed Jun 20, 2012 1:43 pm

Try to using states instead of simple boolean-guards, they were designed to handle such scenarios and do so beautifully.

A boolean-guard is just a race-condition waiting to happen, it requires two (or three) separate operations. And I haven't heard or read anywhere that reading from or writing to a variable is atomic. Actually, I have read just the opposite on the wiki.

Other than that ... (organized in a list for a better overview)
  • This is a wasted guard, it can't evaluate to false.
    		bool property Updating = False auto		Event OnInit()			If !Updating ;...		
  • This code I just don't understand :huh:
    		LinkedListControls NewLList		while (!NewLList)			NewLList = SomeCall()		endwhile		return NewLList		
    What's wrong with "return SomeCall()"?
  • Is this necessary, did you tested this scenario?
    		Event OnInit()			UnRegisterForUpdate() ; I mean this line here			RegisterForSingleUpdate(1)		EndEvent		
    It can only make sense, if you call Reset from somewhere else which is I guess what is going on. But it would mean that scripts don't properly clean up after themselves.
  • And what's up with that weird RollDice function?
    Pseudo-RNG's don't get more random by calling them repeatedly, they get more predictable.

1: There are some situations where OnInit can fire twice. the boolean is to kill the second firing so you don't get two threads.
2: It is waiting until it gets a valid return value from the call, which is dependant on what another script is doing. If you simply returned it, you could get a "None", and that code is there to prevent the possibility of that ever occurring.
3: See #1.
4: The dice functions are to simulate the rolling of multiple dice. http://apaladinincitadel.blogspot.com/2010/04/papers-and-paychecks-rpg.html - the result is a bell curve, where the median values are more common than the values at the extreme ends (In other words, very few 3D6 rolls will be 18 or 3, while 10 or 11 will be fairly common. It's just a way of altering the probibility from a flat equal chance for all results to a bell curve type result.)
User avatar
Jordan Moreno
 
Posts: 3462
Joined: Thu May 10, 2007 4:47 pm

Post » Wed Jun 20, 2012 6:07 pm

1, 3: So it is a race-condition.
2: That can't be right, because it would mean that the assignment operation can be finished before its expression has been evaluated.
4: Got it.
User avatar
James Hate
 
Posts: 3531
Joined: Sun Jun 24, 2007 5:55 am

Post » Wed Jun 20, 2012 12:45 pm

2: That can't be right, because it would mean that the assignment operation can be finished before its expression has been evaluated.

Nope. The function it's calling CAN return a "None" - All the loop is doing is saying "Wait until this function call returns something other than a "None" - at least I've had Papyrus mess up like that for reasons I've been unable to determine (in this case, it's a PlaceAtMe, which USUALLY works...but I've seen it not work before, so I want the code to wait until it has a grip on an actual object before proceeding.
User avatar
Julie Serebrekoff
 
Posts: 3359
Joined: Sun Dec 24, 2006 4:41 am

Post » Wed Jun 20, 2012 2:51 pm

Of course... should have got that a lot sooner.
User avatar
Nicole Elocin
 
Posts: 3390
Joined: Sun Apr 15, 2007 9:12 am


Return to V - Skyrim