Jump to content
Our Domain Name Has Changed! Read more... ×
Sclerocephalus

Mysterious Tatos - another arrow in the knee ?

Recommended Posts

[07/17/2016 - 08:28:11AM] error: Unable to call MoveTo - no native object bound to the script object, or object is of incorrect type
stack:
	[<nullptr form> (FF000B03)].ObjectReference.MoveTo() - "<native>" Line ?
	[ (00066F2B)].workshopobjectscript.UpdatePosition() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 743
	[ (00066F2B)].workshopobjectscript.AssignActor() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 329
	[WorkshopParent (0002058E)].workshopparentscript.AssignActorToObject() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopparentscript.psc" Line 1540
	[WorkshopParent (0002058E)].workshopparentscript.TryToAssignResourceType() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopparentscript.psc" Line 2801
	[WorkshopParent (0002058E)].workshopparentscript.ResetWorkshop() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopparentscript.psc" Line 3479
	
	<truncated stack>
[07/17/2016 - 08:28:11AM] error: Unable to call Enable - no native object bound to the script object, or object is of incorrect type
stack:
	[<nullptr form> (FF000B03)].ObjectReference.Enable() - "<native>" Line ?
	[ (00066F2B)].workshopobjectscript.UpdatePosition() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 745
	[ (00066F2B)].workshopobjectscript.AssignActor() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 329
	[WorkshopParent (0002058E)].workshopparentscript.AssignActorToObject() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopparentscript.psc" Line 1540
	[WorkshopParent (0002058E)].workshopparentscript.TryToAssignResourceType() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopparentscript.psc" Line 2801
	[WorkshopParent (0002058E)].workshopparentscript.ResetWorkshop() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopparentscript.psc" Line 3479
	
	<truncated stack>

00066F2B is the reference of a tato at Abernathy Farm. This is one of 16 tatos at Abernathy Farm that were fixed in UFO4P  1.0.4 because they were missing a link to the workbench (as a result, they could not be repaired when damaged and settlers could not be succesffully assigned to work on them). There are no errors from any of the other 15 tatos though.

 

The respective lines in WorkshopObjectScript look as follows:

	if myDamageHelperRef
		myDamageHelperRef.Moveto(self)
		; make sure enabled
		myDamageHelperRef.Enable()
	endif

The damage helpers are markers that are created by the OnInit() event of WorkshopObjectScript (OnInit() calls the HandleCreation() function and this creates a marker via a PlaceAtMe() command). The reference of this marker is stored in the MyDamageHelperRef variable.

 

MyDamageHelperRef is obviously not 'none' (otherwise, MoveTo() and Enable() would not even be called), but it behaves like a 'none' when Papyrus tries to run a member function of ObjectReference script on it. In other words, the MyDamageHelperRef variable on WorkshopObjectScript of this tato stores a broken pointer.

 

Why ?

 

And why does this only happen to this tato ?

 

And now it gets weird ....

 

This error was thrown in my previous playthrough by a tato at Oberland Station (you probably guessed it already: this tato was fixed in UFO4P 1.0.4 as well, for the same reason as the tatos at Abernathy Farm):

[07/12/2016 - 06:24:28PM] error: Unable to call MoveTo - no native object bound to the script object, or object is of incorrect type
stack:
	[ (FF000B27)].ObjectReference.MoveTo() - "<native>" Line ?
	[ (00159FA6)].workshopobjectscript.UpdatePosition() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 743
	[ (00159FA6)].workshopobjectscript.AssignActor() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 329
	[WorkshopParent (0002058E)].workshopparentscript.AssignActorToObject() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopparentscript.psc" Line 1545
	[WorkshopParent (0002058E)].workshopparentscript.TryToAssignResourceType() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopparentscript.psc" Line 2806
	[WorkshopParent (0002058E)].workshopparentscript.ResetWorkshop() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopparentscript.psc" Line 3484
	<truncated stack>
[07/12/2016 - 06:24:28PM] error: Unable to call Enable - no native object bound to the script object, or object is of incorrect type
stack:
	[ (FF000B27)].ObjectReference.Enable() - "<native>" Line ?
	[ (00159FA6)].workshopobjectscript.UpdatePosition() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 745
	[ (00159FA6)].workshopobjectscript.AssignActor() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 329
	[WorkshopParent (0002058E)].workshopparentscript.AssignActorToObject() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopparentscript.psc" Line 1545
	[WorkshopParent (0002058E)].workshopparentscript.TryToAssignResourceType() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopparentscript.psc" Line 2806
	[WorkshopParent (0002058E)].workshopparentscript.ResetWorkshop() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopparentscript.psc" Line 3484
	<truncated stack>

Is this a mere incidence or does this really only happen to the crops fixed by UFO4P ?

 

If so, why ? And why do not all of them throw errors then ?

 

There is no difference in setup between crops that were properly linked in the base game and crops that got this link added by UFO4P. I already went into the CK to check for mysterious flags that might have been overlooked but there aren't any.

Share this post


Link to post
Share on other sites

Is it possible the game created the forms initially but then because they weren't persistent or something they eventually got culled by the code and now they only exist in a table of deleted references? Then suddenly we fix them and now it's trying to use them despite being deleted?

 

Have you tried completely removing the crops in question and replacing them with new tato plants?

Share this post


Link to post
Share on other sites

Is it possible the game created the forms initially but then because they weren't persistent or something they eventually got culled by the code and now they only exist in a table of deleted references?

 

That might explain the issue with the tato at Oberland station.

 

However, the Abernathy Farm tato threw its error on a fresh playthrough (on the first visit to that location), with UFO4P 1.0.4c installed from the beginning.

[After the discovery that certain AI packages may corrupt your saves - and I had been running such a package for weeks - I could not use that playthrough anymore, so I started a new one]

Share this post


Link to post
Share on other sites

Do the tatos work at all if you try to interact with them? Will settlers assign to them properly? If they do, then I'd be inclined to consider it a bogus error being thrown.

Share this post


Link to post
Share on other sites

To find out how many crops are really borked, I modified the OnLoad() event of WorkshopObjectScript as follows (this code will also delete any invalid markers and replace them with valid ones):

event OnLoad()
	; create link on load if I have an owner
	if AssignedActorLinkKeyword
		Actor myOwner = GetActorRefOwner()
		if myOwner
			myOwner.SetLinkedRef(self, AssignedActorLinkKeyword)
		endif
	endif

	;EDIT: Added to check for broken pointers in the myDamageHelperRef variable, and to replace them:
	if myDamageHelperRef
		bool bRepairDamageHelper = True
		if myDamageHelperRef.GetBaseObject() && myDamageHelperRef.GetBaseObject() == DamageHelper
			 bRepairDamageHelper = False
 		endIf
 		if bRepairDamageHelper
 			WorkshopParent.wsTrace(self + "		Invalid damage helper")
 			WorkshopParent.wsTrace(self + "		     deleting invalid damage helper")
 			myDamageHelperRef.SetLinkedRef(None)
 			myDamageHelperRef.Delete()
 			WorkshopParent.wsTrace(self + "		     creating new damage helper")
 			myDamageHelperRef = PlaceAtMe(DamageHelper)
 			myDamageHelperRef.SetLinkedRef(self)
 		endif
	 endIf
EndEvent

The check for GetBaseObject() is a bogus check: I needed a member function of ObjectReference Script that doesn't actually do anything in case it works (the comparison of the base object with the DamageHelper property was added to see whether crops are linked to unrelated objects; this isn't the case though). If it works, the bool is set to false and the repair code will be skipped. In case it doesn't, papyrus throws an error and skips the whole If structure, but it continues to process the script; in that case, the bool is still true and the repair code starts running.

 

I then went back to Abernathy Farm and inspected the logs.

 

Papyrus Log:

[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B2C)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F60)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B2C)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F62)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B2C)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F5F)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B13)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F5E)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B13)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F53)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F40)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B13)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F37)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F39)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B2C)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F36)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F35)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B26)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F2B)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F2A)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.GetBaseObject() - "" Line ?
[ (00066F29)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 136
[07/20/2016 - 10:22:07AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B26)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F2B)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:07AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B26)].ObjectReference.Delete() - "" Line ?
[ (00066F2B)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145
[07/20/2016 - 10:22:07AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B2C)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F5F)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:07AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B2C)].ObjectReference.Delete() - "" Line ?
[ (00066F5F)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145
[07/20/2016 - 10:22:07AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B13)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F5E)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:07AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B13)].ObjectReference.Delete() - "" Line ?
[ (00066F5E)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145
[07/20/2016 - 10:22:07AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B13)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F53)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:07AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B13)].ObjectReference.Delete() - "" Line ?
[ (00066F53)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145
[07/20/2016 - 10:22:07AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B2C)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F60)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:07AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B2C)].ObjectReference.Delete() - "" Line ?
[ (00066F60)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145
[07/20/2016 - 10:22:07AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F40)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:07AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.Delete() - "" Line ?
[ (00066F40)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145
[07/20/2016 - 10:22:07AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F39)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:07AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.Delete() - "" Line ?
[ (00066F39)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145
[07/20/2016 - 10:22:07AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B2C)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F36)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:07AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B2C)].ObjectReference.Delete() - "" Line ?
[ (00066F36)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145
[07/20/2016 - 10:22:07AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F35)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:07AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.Delete() - "" Line ?
[ (00066F35)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145
[07/20/2016 - 10:22:07AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B13)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F37)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:07AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B13)].ObjectReference.Delete() - "" Line ?
[ (00066F37)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145
[07/20/2016 - 10:22:07AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F2A)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:07AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.Delete() - "" Line ?
[ (00066F2A)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145
[07/20/2016 - 10:22:07AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B2C)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F62)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:08AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B2C)].ObjectReference.Delete() - "" Line ?
[ (00066F62)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145
[07/20/2016 - 10:22:08AM] error: Unable to call SetLinkedRef - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.SetLinkedRef() - "" Line ?
[ (00066F29)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 144
[07/20/2016 - 10:22:08AM] error: Unable to call Delete - no native object bound to the script object, or object is of incorrect type
stack:
[ (FF000B12)].ObjectReference.Delete() - "" Line ?
[ (00066F29)].workshopobjectscript.OnLoad() - "C:\Users\Dr. Peter Haas\AppData\Local\Temp\PapyrusTemp\workshopobjectscript.psc" Line 145

 

Workshop Log:

[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F2B)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F2B)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F4E)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F4E)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F29)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F34)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F4E)>] creating new damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F34)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F34)>] creating new damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F5F)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F5F)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F5E)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F5E)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F53)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F53)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F5F)>] creating new damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F53)>] creating new damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F60)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F60)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F40)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F40)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F60)>] creating new damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F39)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F39)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F40)>] creating new damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F36)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F36)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F35)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F35)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F39)>] creating new damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F36)>] creating new damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F35)>] creating new damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F37)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F37)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F2A)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F2A)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F62)>] Invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F62)>] deleting invalid damage helper
[07/20/2016 - 10:22:07AM] [workshopobjectscript < (00066F2A)>] creating new damage helper
[07/20/2016 - 10:22:08AM] [workshopobjectscript < (00066F37)>] creating new damage helper
[07/20/2016 - 10:22:08AM] [workshopobjectscript < (00066F62)>] creating new damage helper
[07/20/2016 - 10:22:08AM] [workshopobjectscript < (00066F42)>] Invalid damage helper
[07/20/2016 - 10:22:08AM] [workshopobjectscript < (00066F42)>] deleting invalid damage helper
[07/20/2016 - 10:22:08AM] [workshopobjectscript < (00066F2B)>] creating new damage helper
[07/20/2016 - 10:22:08AM] [workshopobjectscript < (00066F42)>] creating new damage helper
[07/20/2016 - 10:22:08AM] [workshopobjectscript < (00066F29)>] deleting invalid damage helper
[07/20/2016 - 10:22:08AM] [workshopobjectscript < (00066F5E)>] creating new damage helper
[07/20/2016 - 10:22:08AM] [workshopobjectscript < (00066F29)>] creating new damage helper

 

 

There were sixteen crops with invalid damage helpers, and in all sixteen cases, the pointers were broken.

These are the RefIDs of the crops:

 

00066F29, 00066F2A, 00066F2B, 00066F34, 00066F35, 00066F36, 00066F37, 00066F39, 00066F40, 00066F42, 00066F4E, 00066F53, 00066F5E, 00066F5F, 00066F60, 00066F62.

 

You may want to compare this with the tracker entry for bug #20563:

http://www.afkmods.com/index.php?/tracdown/issue/20563-cannot-repair-damaged-crops/

 

These are the crops that were fixed by UFO4P 1.0.3 - all of them and only these.

Share this post


Link to post
Share on other sites

One more thing: UFO4P did also add an invisible razorgrain to the tato field at Abernathy Farm. Just walk around in the southwestern part of the field while in workshop mode (you won't see it if you're not in workshop mode) and look around; it should be difficult to miss.

 

At first, I thought that a tato plant is now misidentified as a razorgrain, but that is not the case. The razograin just covers the tatos from being selected. You still can select them when you're close enough, and they will all properly highlight as tatos.

 

The razorgrain was still there after the script fixed the damage helpers. I still have to check however whether it will be gone after a reload.

Share this post


Link to post
Share on other sites

So did we miss something in linking these up to the workbench? Is it going to cause some kind of permanent breakage? Cause IMO, people shouldn't be staring at their logs all day anyway. If the tatos can be assigned to a settler, repaired when broken, etc, then I don't see the harm unless it's going to spin out of control into a source of huge bloat because it's generating refs over and over again or something.

 

If your script modification fixes it, then that seem like it should do the job to me.

 

As for the razorgrain, I have no idea how the hell that could even happen.

Share this post


Link to post
Share on other sites

So did we miss something in linking these up to the workbench?

 

 

I'm pretty sure that we did not miss anything. I did compare the fixed entries with the base game entries of properly linked crops many times and could not see any differences. I have no idea why this is happening.

 

 

Is it going to cause some kind of permanent breakage?

 

 

To understand the possible consequences, one needs to know what the damage helpers do. That's actually quite simple: they record damage and pass it on to the crops they're linked to. For this purpose, they all run the WorkshopFloraDamageHelperScript:

Scriptname WorkshopFloraDamageHelperScript extends ObjectReference Const
{script to pass damage to linked flora}

EVENT OnDestructionStageChanged(int aiOldStage, int aiCurrentStage)
	if IsDestroyed()
		debug.trace(self + " Destroyed - passing on to my linked ref (" + GetLinkedRef() + ")")
		; pass on destruction to linked ref
		GetLinkedRef().DamageObject(9999)
	endif
EndEvent

This makes much sense in respect of saving performance in collision detection. I did not have a look at the meshes, but I bet that the crops' collision meshes have been given a havok material that doesn't react to projectiles, whereas the markers' meshes do only react to projectiles. The crops' collision meshes, which probably represent their shapes quite properly would then be used only for less performance intensive tasks such as actor-object collisions, while the marker meshes are as simple as possible (spheres ?) to deal with the performance intensive stuff.

 

Some background knowledge on several aspects of collision detection procedures is probably needed now, so here we go:  collision detection generally includes two separate scans:

(1) a broad scan, where all tested objects are represented by simple geometric shapes  (primitives), usually boxes (for static objects, these are the bounding boxes) or spheres. Spheres are preferred for fast moving objects (e.g. all of the havok objects that can be sent flying around). This scan  serves as a filter: it sorts out any objects that can't be colliding and leaves only those that may possibly collide. Therefore, prerequisites for primitives are that they must include all of the respective objects. Thus, the broad scan may produce false positives (i.e. it may detect two objects as colliding when they actually don't, because their primitives are overly large) but it will never produce false negatives (i.e. overlook a collision because the primitives are too small).

(2) a narrow scan, which checks the actual collision meshes of all object pairs returned by the broad scan. This scan is using performance intensive algorithms based on the separating axes theorem. [NB: It's probably worth mentioning here that these algorithms may include mesh partitioning so as to obtain strictly convex chunks when a mesh has concave portions. Worth mentioning because there are some people claiming on the internet that this is what the chunks of a compressed mesh shape are all about. That is nonsense (and easily verified, by the way: make a block with an eight sided hole in it and run it through NifTools; you'll need to separate this into eight similar chunks to make sure each chunk is convex, but they'll look nowhere similar to the chunks in the compressed mesh shape you get from it).]

 

A good overview of collision detection concepts is given here (note however that there is no clear distinction drawn between broad scan and narrow scan):

http://www.metanetsoftware.com/technique/tutorialA.html#section5

 

Detection algorithms in the broad scan are very simple (compared to those used in the narrow scan) and thus very fast, but it still makes a big difference whether slow moving or fast moving objects are considered. Take actor-object collisions as an example: actors are slow moving: they make a limited progress per frame, so the broad scan needs to consider only objects within a small radius. This radius increases with the speed at which an object is moving, and the number of objects to be included increases with the square of that radius (or even with the cube, when havok objects are considered that can be kicked around in all directions). Moreover, a fast moving object that is at a certain position in one frame may be at a position far away in the next frame, and there may be objects between those positions with which the moving object should have collided. To detect those collisions properly, a pre-scan is performed on each fast moving object per frame, which checks the pathway travelled by that object between two frames. It is obvious that the workload increases tremendously the faster objects are moving. In addition, there's rarely one fast moving object in a scene at a time (in Fallout 4, you easily have dozens). This is why spheres are the preferred shapes here, since they reduce calculations to the simplest possible collision detection algorithm of checking whether the distance between two objects' centers is smaller or larger than the sum of the spheres' radii. The drawback of spheres is that they have maximum volume at given object dimensions, meaning that they represent the actual shape of an object most inexactly, so the broad scan returns more false positives and some of the performance gained by using spheres for the broad scan will be wasted by the calculations on those false positives in the narrow scan. However, if the objects are really fast moving, the narrow scan may be skipped entirely: if things are going really fast in the game, you wouldn't notice the difference it would make when collisions were calculated from the real objects' shapes.

 

But let's come back to the crops: if the marker is missing, they should become indestructible.

 

However, we only knew so far that the myDamageHelperRef variables held invalid references. What if the markers are still there (and still properly linked to the crops) ? Tried this and could indeed damage all tatos by gunfire, except for the southernmost one in the westernmost row (which didn't accept any damage at all). I could repair them without problems (and yes, I also can assign all of them to actors, even the one that didn't accept any damage), but I couldn't damage them again. This is no surprise however, because the damage helper is reset from WorkshopObjectScript, and this needs a valid reference in myDamageHelperRef for doing that.

 

The consequence for my initial fix is that we should try to reuse the existing markers, if possible, and only create new ones if it's not possible (so we don't litter the game world with more unused stuff that may never get cleaned up). Since the damage helpers are created by a PlaceAtMe command, they should be easy to identify as the closest reference of 'DamageHelper' type. So I improved the code, combined it with the damage helper creation procedure and packed it in a separate function. If myDamageHelperRef is broken, the function now checks whether there is a helper marker that is still linked to the crop, and if so, will repair the broken pointer by putting the reference of that marker in myDamageHelperRef. Only if no marker is found, it will create a new one:

function UFO4P_ValidateDamageHelper()
	if DamageHelper
		if myDamageHelperRef
			if myDamageHelperRef.GetBaseObject()
				return
			endIf
			WorkshopParent.wsTrace(self + "		Invalid myDamageHelperRef")
			ObjectReference myClosestDamageHelperRef = Game.FindClosestReferenceOfTypeFromRef (DamageHelper, self, 50.0)
			if myClosestDamageHelperRef
				ObjectReference myClosestDamageHelperLinkedRef = myClosestDamageHelperRef.GetLinkedRef()
				if myClosestDamageHelperLinkedRef
					if myClosestDamageHelperLinkedRef == self
						WorkshopParent.wsTrace(self + "		     Found damage helper linked to me, repairing myDamageHelperRef")
						myDamageHelperRef = myClosestDamageHelperRef
						return						
					endIf
				else
					;myClosestDamageHelperRef is not linked to anything -> delete:
					myClosestDamageHelperRef.Delete()
				endIf
			endIf
			WorkshopParent.wsTrace(self + "		     Found no damage helper linked to me, creating new one")
		elseIf workshopID >= 0
                        ;only log this when the object is at a workshop (crops in the wilderness will create new damage helpers on every cell reset anyway)
			WorkshopParent.wsTrace(self + "		Creating damage helper")
		endIf
		myDamageHelperRef = PlaceAtMe(DamageHelper)
		myDamageHelperRef.SetLinkedRef(self)
	endIf
endFunction 

This is now called from the OnLoad() event (to catch broken links and to repair them) and from the HandleCreation() function, which in turn is called from The Oninit() event and from a custom event that fires when the crop has been 'built' by the player.

 

Good news is that the pointers apparently won't break again. Meanwhile, I had Abernathy Farm unload and reload more than ten times, but the errors never re-appeared. This fix is permanent (and retro-active).

 

Bad news is about the razorgrain: this is a serious issue, since trying to manipulate it in any way while you are in workshop mode repeatedly leads to an immediate crash (even when you just try to select it). Nonetheless, I made some progress here that will hopefully enable us to fix it.

 

It's noteworthy to add here that we have some tracker entries on an obscure bug with invisible crops that apparently remain behind when a crop is stored in the workbench. Authors of those entries also report that manipulation of  those invisible crops may lead to crashes. This sounds familiar ...

 

When a crop is stored in a workbench, it gets deleted (the procedure is the same as for scrapping objects; crops can't be scrapped in the base game though). Work flow is interesting here: the crop disappears immediately, while you're in workshop mode. Unfortunately, workshop mode is like a black box: this mode is not papyrus-scripted; everything happening here is happening at engine level, so we can only speculate on what it's doing. I assume however, that the crop is only disabled at this point. Subsequently, an OnWorkshopObjectDestroyed event fires on WorkshopScript, which then calls the RemoveObjectPUBLIC function on WorkshopParentScript. This unassigns the object from any actors and from the workshop and finally calls the HandleDeletion() function on its WorkshopObjectScript to delete all markers.

 

A look at how the marker gets deleted and comparing this with its creation was ... well, rewarding (say, it was a good place to dig into). This is the vanilla code for creating a marker (in the HandleCreation() function):

	if DamageHelper && !myDamageHelperRef
		myDamageHelperRef = PlaceAtMe(DamageHelper)
		; link to me
		myDamageHelperRef.SetLinkedRef(self)
	endif

[NB: the check always returns 'false' when there's anything  in myDamageHelperRef, i.e. even if the pointer is broken. Therefore, my fix replaces this code with a call of the new function (see above), so as to make sure that myDamageHelperRef is always properly evaluated]

 

And this is the vanilla code for deleting a marker (in the HandleDeletion() function):

	if myDamageHelperRef
		myDamageHelperRef.Delete()
	endif

I see two issues here:

(1) The damage helper is not unlinked from 'self' (i.e. the crop) before it is deleted. Links make references persistent, so I wonder whether deletion actually works. Maybe it does, but testing this is a waste of time when you can simply prevent this from becoming an issue by unlinking the marker from the crop before deleting it.

(2) Even worse: myDamagHelperRef is never cleared. This will not automatically become 'none' just because the reference it points at is deleted. Instead, it might be ending up at pointing into limbo. On the other hand, it could also represent an obstacle to the deletion of the marker, since its reference remains stored in a script variable.

 

This needs to be fixed, irrespective of a possible relation to the invisible crop issue (I propose that we create a separate tracker entry for this one and only close the invisible crop entries when we're sure that it fixed those as well):

	if myDamageHelperRef
		;UFO4P 1.0.x Bug #yyyyy: Unlink from self before deleting:
		myDamageHelperRef.SetLinkedRef(none)
		myDamageHelperRef.Delete()
		;UFO4P 1.0.x Bug #yyyyy: Clear variable:
		myDamageHelperRef = none
	endif

On the other hand, a relation to the invisible crop issue is more than likely: after all, what could be left behind if it is not the crop ? Also, detection of what object is in the player's field of view is probably faster when simple shape markers are checked instead of the actual objects, so  I could imagine that workshop mode selects the markers and then highlights the linked crop (if any). Thus, the invisible crops may just be orphaned markers.

 

I therefore tried to find out the exact position of the invisible razorgrain, and it appears that it is centered on the northernmost tato in the westernmost row. If it's a wrong marker at the position of an existing crop, I can fix it. I'll just have to write a function that checks this for all crops at a given workshop (this is probably best done by the workshop reset, and it should probably run only once on every workshop). There still remains a problem though: if this is really the solution to the invisible crop bug, affected games will have orphaned markers now in places where there is no crop, and it will be difficult to catch them (unless F4SE is used), so be prepared that we may not be able to provide a retro-active solution.

Share this post


Link to post
Share on other sites

I added the following function to WorkshopParentScript:

function UFO4P_CheckDamageHelpers (WorkshopObjectScript theCrop)
	wsTrace (self + " UFO4P_CheckDamageHelpers - Checking helper markers for " + theCrop)
	int MarkerCount = UFO4P_WorkshopFloraDamageHelpers.GetSize()
	int i = 0
	while i < MarkerCount
		MovableStatic theMarker = UFO4P_WorkshopFloraDamageHelpers.GetAt(i) As MovableStatic
		if theMarker != theCrop.DamageHelper
			ObjectReference MarkerRef = Game.FindClosestReferenceOfTypeFromRef (theMarker, theCrop, 200.0)
			if MarkerRef
				wsTrace (self + "     Found Marker; Form = " + theMarker + "; Ref = " + MarkerRef)
				bool bDeleteMarker = true
				bool bUnlinkMarker = true
				ObjectReference MarkerLinkedRef = MarkerRef.GetLinkedRef()
				if MarkerLinkedRef == none
					wsTrace (self + "     Marker not linked to any object - deleting ...")
					bUnlinkMarker = false
				elseIf MarkerLinkedRef.GetParentCell() == None || !MarkerLinkedRef.GetParentCell().IsLoaded()
					wsTrace (self + "     Marker linked to an object in an unloaded cell - deleting ...")
				else
					WorkshopObjectScript theLinkedObject = MarkerLinkedRef As WorkshopObjectScript
					if theLinkedObject == None
						wsTrace (self + "     Marker not linked to a workshop object - deleting ...")
					elseIf !theLinkedObject.GetBaseObject() As Flora
						wsTrace (self + "     Marker not linked to a crop - deleting ...")
					elseIf theMarker != theLinkedObject.DamageHelper
						wsTrace (self + "     Marker not linked to a matching crop - deleting ...")
					else
						bDeleteMarker = false
					endIf
				endIf
				if bDeleteMarker
					if bUnlinkMarker
						MarkerRef.SetLinkedRef (none)
					endIf
					MarkerRef.Delete()
				endIf
			endIf
		endif
		i += 1
	endWhile
	wsTrace (self + " UFO4P_CheckDamageHelpers - DONE")
endFunction 

This function only checks markers that do not match the tested crop (otherwise; it would only find the marker that belongs to the crop anyway). During a workshop reset, this runs on every crop. I also added a hidden bool to WorkshopScript that starts 'true' and tells WorkshopParentScript to run the clean-up function. If this is done, that bool is set to 'false', so there will be only one clean-up on every workshop. This is because the procedure draws a lot of performance; also, once the marker deletion procedure on WorkshopObjectScript has been fixed, it should not be needed anymore.

 

EDIT: In case a modder runs into a similar issue, he can simply reset the hidden bool on the respective WorkshopScript, and the cleanup will run again on that workshop the next time it resets.

 

The function checks various scenarios for mis-configured markers, and when it ran on Abernathy Farm, it really found two that didn't belong there and deleted them. I immediately reloaded and went into workshop mode to look for the invisible razorgrain - and it was gone !

 

So we have proof now that the invisible crops are really just orphaned damage helpers.

 

If you would have to guess what was wrong with those markers, what would you think ?

 

Solution is here:

[07/23/2016 - 06:16:35PM] [workshopparentscript ] UFO4P_CheckDamageHelpers - Checking helper markers for [workshopobjectscript < (00066F62)>]
[07/23/2016 - 06:16:36PM] [workshopparentscript ] Found Marker; Form = [MovableStatic < (0023D7D8)>]; Ref = [ObjectReference < (FF000AEC)>]
[07/23/2016 - 06:16:36PM] [workshopparentscript ] Marker linked to an object in an unloaded cell - deleting ...
[07/23/2016 - 06:16:36PM] [workshopparentscript ] UFO4P_CheckDamageHelpers - DONE

[07/23/2016 - 06:16:41PM] [workshopparentscript ] UFO4P_CheckDamageHelpers - Checking helper markers for [workshopobjectscript < (00066F53)>]
[07/23/2016 - 06:16:42PM] [workshopparentscript ] Found Marker; Form = [MovableStatic < (0023D7D8)>]; Ref = [ObjectReference < (FF000AF3)>]
[07/23/2016 - 06:16:42PM] [workshopparentscript ] Marker linked to an object in an unloaded cell - deleting ...
[07/23/2016 - 06:16:42PM] [workshopparentscript ] UFO4P_CheckDamageHelpers - DONE

Share this post


Link to post
Share on other sites

I don't know if this helps or anything, but I was surprised to learn awhile ago that disabled objects:

  • remain in place in the world;
  • are merely set to invisible, noncollidable, and inactive (for actors); and
  • can be manipulated with Papyrus.

The latter I can understand. The former two don't meet my definition of "disabled," but okay.

 

So, for example, Auto Loot was finding disabled Bobby Pin Boxes and repeatedly looting them. Activating a Bobby Pin Box only disabled the box, so the FindAllReferences* functions, which don't care about enable state, were generating an infinite loop.

 

I also discovered when I implemented "auto disable on loot" that some objects, especially some dead actors, cannot be disabled via Papyrus, so beware of using Disable() to try to fix anything. Note that the same objects that Papyrus cannot disable can be disabled with the in-game console. Makes no sense, but there it is.

Share this post


Link to post
Share on other sites

While I'm always grateful for any additional helpful information (and yours is certainly interesting), I'm somehow completely missing the point of your post, so forgive me when I have to ask what you're referring to.

 

This thread is actually not about fixing something by disabling or deleting it. It's about fixing the deletion of something.

Share this post


Link to post
Share on other sites

You mentioned that the invisible razorgrain was "only disabled at this point," and it occurred to me I should say something about disabled objects, if only for future consideration.

Share this post


Link to post
Share on other sites

You mentioned that the invisible razorgrain was "only disabled at this point," and it occurred to me I should say something about disabled objects, if only for future consideration.

 

Oh, I see.

 

That is probably misleading. What I wanted to be kept in mind is that no object on which Delete() is called gets actually deleted until the cell unloads. Only then, it's technically removed. Otherwise, deleting an object from a script would be impossible.

 

Take the code from WorkshopObjectScript as an example:

if myDamageHelperRef
  myDamageHelperRef.SetLinkedRef(none)
  myDamageHelperRef.Delete()
  myDamageHelperRef = none
endif

The reference is stored in the myDamageHelperRef variable. If I would have to make sure that the object is not persistent in any way before I call Delete() on it, I would have to set myDamageHelperRef to none first, but on what do I call Delete() then ?

 

Fortunately, that's not how it works. You still have to make sure that the reference is not persistent, but it's sufficient for this to be done until the cell unloads, so you can call Delete() on it earlier (at this point, the object is technically only 'marked for deletion').

 

Now back to the crops: when you store them in the workbench, they disappear instantly, but technically, they can't be deleted at this moment, so my guess is that the black box code of workshop mode disables them deliberately. The important consequence of the object not being actually deleted is that the script that runs on it (and all of its properties) is still accsessible.

Share this post


Link to post
Share on other sites

As you may know, an integrity check of the damage helpers for all existing crops went live with UFO4P 1.0.5. The procedure only runs once on every workshop. In the same UFO4P version, we also fixed the damage helper deletion procedure, so the game should have stopped to generate 'invisible crops' (i.e. orphaned damage helper markers) from then on.

 

Though, there still remained the problem of invalid markers that were created before this fix went live. As we know now, removing a planted crop by storing it in the workbench could cause an orphaned marker to remain behind, and there appear to be players who have been using this option (and probably are still using it) quite extensively.  In the worst case, trying to manipulate an 'invisible crop' while in workshop mode may cause an immediate CTD (presumably because the crop the marker was linked to is deleted once the workshop unloads and the link on the marker is pointing into limbo upon return), so this is still a serious issue we need to care about.

 

Unlike the misconfigured crops, where the faulty markers were always at or very near the position of an existing crop and therefore easy to find, orphaned markers left behind after removal of the crop are significantly more difficult to spot. Eventually, I conceived a cleanup procedure that places an arbitrary grid on the workshop (well, actually only a portion of it, to keep the run time of the cleanup procedure as low as possible) and runs a search for markers on every grid point. The following idea was used to refine this procedure: if an arbitrary grid (i.e. with search points not necessarily placed on any existing crops) is able to detect all valid markers (i.e. those on the existing crops), it also should have a good chance at spotting any invalid ones (that are likely positioned somewhere inbetween).

 

For this purpose, a new hidden bool property and a few new variables have been added to WorkshopScript:

;-----------------------------------------------------------
;	Added by UFO4P 1.0.x for Bug #yyyyy:
;-----------------------------------------------------------

;This tells WorkshopParentScript to start a timer for running a damage helper cleanup on the entire workshop after the next reset of this workshop has finished
;running (or after the reset following the next reset if UFO4P_CleanupDamageHelpers_WorkObjects is 'true' at the same time). Once this has been done, the bool
;is set to 'false'.
bool property UFO4P_CleanupDamageHelpers_Workshop = true auto hidden

;Helper bool for setting appropriate start values to the position helper variables. This is always reset to 'false' when the cleanup function has finished
;running, so the position helpers will be re-initialized in case the cleanup needs to be run again (e.g. when the location unloads before it finishes running).
bool UFO4P_PositionHelpersInitialized = false

;Position helpers to outline a farm area at a workshop location which is then searched for orphaned damage helpers (this is to reduce workload; alternatively
;the entire workshop location would have to be searched)
Float UFO4P_xMin
Float UFO4P_xMax
Float UFO4P_yMin
Float UFO4P_yMax
Float UFO4P_zMin
Float UFO4P_zMax

;Number of crops at this workshop (if needed, the value is set by WorkshopParentScript after a reset has run on this workshop):
int UFO4P_CropCount = 0

;TimerID to handle the damage helper cleanup:
int UFO4P_DamageHelperCleanupTimerID = 95

The UFO4P_CleanupDamageHelpers_Workshop bool tells WorkshopParentScript that a cleanup will have to be run on this workshop, so the workshop reset will collect the necessary data from the crops when it runs for the next time. Once that reset has finished running, it resets the bool to 'false' and starts the cleanup procedure via timer.

 

The necessary modifications in the ResetWorkshop() function of WorkshopParentScript add to the code that was already added for UFO4P 1.0.5 (note that the middle part appears twice in that function, because the ResetWorkshop() function runs separate loops on damaged and undamaged crops):

	;UFO4P 1.0.5 Bug #21039: Get bool from workshop, for faster access:
	bool bCleanupDamageHelpers_WorkObjects = workshopRef.UFO4P_CleanupDamageHelpers_WorkObjects
	;UFO4P 1.0.x Bug #yyyyy: Get bool from workshop, for faster access. Also evaluate bDamageHelperTasks, to allow for quick checks:
	bool bCleanupDamageHelpers_Workshop = workshopRef.UFO4P_CleanupDamageHelpers_Workshop
	bool bDamageHelperTasks = bCleanupDamageHelpers_WorkObjects || bCleanupDamageHelpers_Workshop
	
	;UFO4P 1.0.x Bug #yyyyy:
	int iCropCount = 0

------------------------

			;UFO4P 1.0.5 Bug #21039 && UFO4P 1.0.x Bug #yyyyy: if damage helper related tasks have to be carried out, and resourceRef is a crop, check the respective
			;bools and call the appropriate functions:
			if bDamageHelperTasks && resourceRef.GetBaseObject() as Flora
				;UFO4P 1.0.5 Bug #21039: call WorkshopObjectScript to check the damage helpers at the position of this workshop object
				if bCleanupDamageHelpers_WorkObjects
					resourceRef.UFO4P_CleanupDamageHelpersAtWorkObject(UFO4P_WorkshopFloraDamageHelpers)
				;UFO4P 1.0.x Bug #yyyyy: update crop count and call WorkshopScript to update the position helpers (this will be delayed to the next reset if damage
				;helpers are cleaned up for the individual crops in this reset):
				elseIf bCleanupDamageHelpers_Workshop
					iCropCount += 1
					workshopRef.UFO4P_UpdatePositionHelpers (resourceRef.X, resourceRef.Y, resourceRef.Z)
				endIf
			endif

------------------------

	;UFO4P 1.0.5 Bug #21039 && UFO4P 1.0.x Bug #yyyyy:
	if bDamageHelperTasks
		;UFO4P 1.0.5 Bug #21039: If damage helpers had to be cleaned up for the crops at this workshop, this will have been done now. Thus set the respective
		;bool on WorkshopScript to 'false':
		if bCleanupDamageHelpers_WorkObjects
			workshopRef.UFO4P_CleanupDamageHelpers_WorkObjects = false

		;UFO4P 1.0.x Bug #yyyyy: If damage helpers need to be cleaned up for the entire workshop, start a timer on WorkshopScript and set the bool to 'false' (so
		;this will not be run again in case the next workshop reset starts before the cleanup function finishes running). The timer event will then run the cleanup
		;function from a separate thread on the respective workshop, so it won't delay nor interfere with other workshop-related activities.
		;Note1: this will be delayed to the next reset if damage helpers were cleaned up for the individual crops in this reset
		;Note2: the cleanup will not be started when there have been less than two crops found at the workshop, because no search area can be outlined in that case
		;(the bool is still set to 'false' though because cleaning up a workshop with no crops would be futile anyway).
		elseIf bCleanupDamageHelpers_Workshop
			if iCropCount > 1
				wsTrace (self + " UFO4P_DamageHelperCleanup - Starting timer for marker cleanup at " + workshopRef)
				workshopRef.UFO4P_SetCropCount (iCropCount)
				workshopRef.StartTimer (3.0, UFO4P_DamageHelperCleanupTimerID)
			endIf
			workshopRef.UFO4P_CleanupDamageHelpers_Workshop = false
		endif
	endIf

Notes:

  1. Since this cleanup procedure is a workshop-specific task, it runs from WorkshopScript. Since it may take a rather long time to complete (up to 90 seconds for my largest settlements), it is started via timer so as to run on a separate thread. This thread is not locked as it doesn't touch any workshop-specific sensitive data.
  2. Due to the long time it needs to complete, the ResetWorkshop() function must set the tracking bool to 'false' before the cleanup actually starts running. Otherwise, a subsequent reset could start running on the same workshop before the cleanup finishes running and would inevitably start another thread to run the cleanup again.

 

Now to the actual cleanup procedure (on WorkshopScript):

function UFO4P_SetCropCount (int newValue)
	UFO4P_CropCount = newValue
endFunction

function UFO4P_UpdatePositionHelpers (float cropX, float cropY, float cropZ)
	if UFO4P_PositionHelpersInitialized == false
		UFO4P_xMin = cropX
		UFO4P_xMax = cropX
		UFO4P_yMin = cropY
		UFO4P_yMax = cropY
		UFO4P_zMin = cropZ
		UFO4P_zMax = cropZ
		UFO4P_PositionHelpersInitialized = true
	else
		UFO4P_xMin = Math.Min (UFO4P_xMin, cropX)
		UFO4P_xMax = Math.Max (UFO4P_xMax, cropX)
		UFO4P_yMin = Math.Min (UFO4P_yMin, cropY)
		UFO4P_yMax = Math.Max (UFO4P_yMax, cropY)
		UFO4P_zMin = Math.Min (UFO4P_zMin, cropZ)
		UFO4P_zMax = Math.Max (UFO4P_zMax, cropZ)
	endIf
endFunction

function UFO4P_CleanupDamageHelpersAtWorkshop (formList MarkerList)

	float deltaX = UFO4P_xMax - UFO4P_xMin
	float deltaY = UFO4P_yMax - UFO4P_yMin
	float deltaZ = UFO4P_zMax - UFO4P_zMin	
	float stepWidth = 80.0
	
	float zPos = UFO4P_zMin + 0.5 * deltaZ
	float searchRadius = 0.5 * Math.sqrt (2 * stepWidth * stepWidth + deltaZ * deltaZ)

	UFO4P_xMin += 0.5 * stepWidth
	UFO4P_xMax += 0.5 * stepWidth
	UFO4P_yMin += 0.5 * stepWidth
	UFO4P_yMax += 0.5 * stepWidth

	workshopParent.wsTrace (self + " UFO4P_CleanupDamageHelpersAtWorkshop - Starting cleanup ...")
	workshopParent.wsTrace (self + "    Box dimensions: deltaX = " + deltaX + "; deltaY = " + deltaY + "; deltaZ = " + deltaZ)
	workshopParent.wsTrace (self + "    Number of crops = " + UFO4P_CropCount)
	workshopParent.wsTrace (self + "    Step width = " + stepWidth + "; Single point search radius = " + searchRadius)

	;Array to store checked markers, so they won't be checked more than once even if they are found several times
	ObjectReference[] RefList = New ObjectReference[0]
	;Total number of marker refs found (this counts on when RefList has to be cleared)
	int MarkerCount = 0
	;Number of marker refs currently stored in RefList
	int MarkerIndex = 0
	
	float xPos = UFO4P_xMin
	while xPos < UFO4P_xMax && myLocation.IsLoaded()
		float yPos = UFO4P_yMin
		while yPos < UFO4P_yMax && myLocation.IsLoaded()
			ObjectReference MarkerRef = Game.FindClosestReferenceOfAnyTypeInList (MarkerList, xPos, yPos, zPos, searchRadius)
			if MarkerRef && !MarkerRef.IsDeleted() && RefList.Find(MarkerRef) < 0
				workshopParent.wsTrace (self + "     Found marker " + MarkerCount + "; Form = " + MarkerRef.GetBaseObject() + "; Ref = " + MarkerRef)
				if MarkerRef.IsDeleted()
					;if found marker is already marked for deletion, unlink it from any object for safety:
					MarkerRef.SetLinkedRef (none)
					workshopParent.wsTrace (self + "     Marker is already deleted")
				else
					if MarkerIndex >= 128
						RefList.Clear()
						MarkerIndex = 0
					endIf
					RefList.Add(MarkerRef)
					MarkerCount += 1
					MarkerIndex += 1

					bool bDeleteMarker = true
					ObjectReference MarkerLinkedRef = MarkerRef.GetLinkedRef()
					if MarkerLinkedRef == none
						workshopParent.wsTrace (self + "     Marker not linked to any object - deleting ...")
					elseIf MarkerLinkedRef.IsDeleted()
						workshopParent.wsTrace (self + "     Marker linked to deleted object - deleting ...")
					elseIf MarkerLinkedRef.GetParentCell() == None || !MarkerLinkedRef.GetParentCell().IsLoaded()
						workshopParent.wsTrace (self + "     Marker linked to an object in an unloaded cell - deleting ...")
					else 
						WorkshopObjectScript theLinkedObject = MarkerLinkedRef As WorkshopObjectScript
						if !theLinkedObject || !theLinkedObject.DamageHelper || theLinkedObject.DamageHelper != MarkerRef.GetBaseObject()
							workshopParent.wsTrace (self + "     Marker not linked to a matching workshop object - deleting ...")
						elseIf theLinkedObject.GetMyDamageHelperRef() != MarkerRef
							workshopParent.wsTrace (self + "     The workshop object this marker is linked to has a different ref specified in myDamageHelperRef - deleting marker ...")
						else
							bDeleteMarker = false
						endIf
					endIf

					if bDeleteMarker
						MarkerRef.SetLinkedRef (none)
						MarkerRef.Delete()					
					endIf
				endif

			endIf
			yPos += stepWidth
		endWhile
		xPos += stepWidth
	endwhile

	;clear array for safety
	RefList.Clear()

	;reset the helper bool to make sure that the position helpers get re-initialized in case this function ever runs again:
	UFO4P_PositionHelpersInitialized = false
	
	if !myLocation.IsLoaded()
		workshopParent.wsTrace (self + " UFO4P_CleanupDamageHelpersAtWorkshop - Workshop location unloaded; Cleanup stopped before completion.")
		;Reseet bool to 'true' to run the cleanup again after the next reset of this workshop:
		UFO4P_CleanupDamageHelpers_Workshop = true
	else
		workshopParent.wsTrace (self + " UFO4P_CleanupDamageHelpersAtWorkshop - DONE")
	endIf

endFunction

Notes:

  1. The crop count was used for tracking purposes during the test phase and can be thrown out when this goes live.
  2. The UFO4P_UpdatePositionHelpers() function is called by the ResetWorkshop() function on every crop.  The final values represent the dimensions of the search box for the cleanup to work on.
  3. StepWidth is the preset distance between two search points on the two-dimensional grid (i.e. plan view, fixed z position) in both the x and y directions. Higher values increase the chance for missing markers in the search, while lower values will increase the run time of the cleanup procedure considerably. Testing has shown that the optimal value is 80.0.
  4. SearchPointRadius: To make sure that the 'search spheres' positioned at the corners of a box with edge lengths a, b and c include the complete volume of the box, two spheres positioned on opposite corners of that box must touch at its center. That is, the radius of the sphere has to be equal to a half of the box's diagonal, or SearchPointRadius = 0.5 * sqrt (a^2 + b^2 + c^2)
  5. The maximum distance of the z positions is problematic as a large difference will result in an overly large SearchPointRadius, and this slows everything down. Fortunately, deltaZ turned out to be rather small in most cases, always well below 100. The only settlement with a risk for running a very long cleanup procedure appears to be Spectacle Island, in case there are separate farm plots in several places of the island with grossly different z positions.
  6. Since a fair number of checks need to be run on every marker found, storing all found markers in an array to check for duplicate hits before running the integrity checks turned out to reduce the run time of the cleanup procedure considerably. Due to the size limit, I may have to clear the array, but it still does its job because the procedure scans the search area by row. It only becomes a problem when a farm plot has more than 127 crops planted in a row but this is very unlikely to happen.
  7. Due to the long run times, there is a risk that the player leaves the location before it has finished running. If the location unloads, the search procedure won't find anything, so it will check this in every loop cycle. If the location unloads, it will bail out and reset the tracking bool to 'true'. This makes sure that one complete cleanup will run on every workshop.

The workshop log will print a summary, such as this one for County Crossing (there will be additional messages printed if an invalid marker is spotted and removed):

[11/22/2016 - 11:50:51AM]  [workshopparentscript <WorkshopParent (0002058E)>] UFO4P_DamageHelperCleanup - Starting timer for marker cleanup at [workshopscript < (0009B1DB)>]
[11/22/2016 - 11:50:54AM]  [workshopscript < (0009B1DB)>] UFO4P_CleanupDamageHelpersAtWorkshop - Starting cleanup ...
[11/22/2016 - 11:50:54AM]  [workshopscript < (0009B1DB)>]    Box dimensions: deltaX = 1112.195313; deltaY = 1317.822266; deltaZ = 11.681641
[11/22/2016 - 11:50:54AM]  [workshopscript < (0009B1DB)>]    Number of crops = 29
[11/22/2016 - 11:50:54AM]  [workshopscript < (0009B1DB)>]    Step width = 80.000000; Single point search radius = 56.869282
[11/22/2016 - 11:50:55AM]  [workshopscript < (0009B1DB)>]     Found marker 0; Form = [MovableStatic < (0023D900)>]; Ref = [ObjectReference < (FF01993F)>]
[11/22/2016 - 11:50:56AM]  [workshopscript < (0009B1DB)>]     Found marker 1; Form = [MovableStatic < (0023D900)>]; Ref = [ObjectReference < (FF0198FA)>]
[11/22/2016 - 11:50:57AM]  [workshopscript < (0009B1DB)>]     Found marker 2; Form = [MovableStatic < (0023D900)>]; Ref = [ObjectReference < (FF019987)>]
[11/22/2016 - 11:50:58AM]  [workshopscript < (0009B1DB)>]     Found marker 3; Form = [MovableStatic < (0023D900)>]; Ref = [ObjectReference < (FF0199D7)>]
[11/22/2016 - 11:50:59AM]  [workshopscript < (0009B1DB)>]     Found marker 4; Form = [MovableStatic < (0023D900)>]; Ref = [ObjectReference < (FF019A23)>]
[11/22/2016 - 11:51:00AM]  [workshopscript < (0009B1DB)>]     Found marker 5; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF04E747)>]
[11/22/2016 - 11:51:01AM]  [workshopscript < (0009B1DB)>]     Found marker 6; Form = [MovableStatic < (0023D900)>]; Ref = [ObjectReference < (FF019A4F)>]
[11/22/2016 - 11:51:01AM]  [workshopscript < (0009B1DB)>]     Found marker 7; Form = [MovableStatic < (0023D900)>]; Ref = [ObjectReference < (FF019A99)>]
[11/22/2016 - 11:51:02AM]  [workshopscript < (0009B1DB)>]     Found marker 8; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF04E7EC)>]
[11/22/2016 - 11:51:03AM]  [workshopscript < (0009B1DB)>]     Found marker 9; Form = [MovableStatic < (0023D900)>]; Ref = [ObjectReference < (FF019AF6)>]
[11/22/2016 - 11:51:04AM]  [workshopscript < (0009B1DB)>]     Found marker 10; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF0095CA)>]
[11/22/2016 - 11:51:05AM]  [workshopscript < (0009B1DB)>]     Found marker 11; Form = [MovableStatic < (0023D900)>]; Ref = [ObjectReference < (FF019B22)>]
[11/22/2016 - 11:51:06AM]  [workshopscript < (0009B1DB)>]     Found marker 12; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF04E6D2)>]
[11/22/2016 - 11:51:06AM]  [workshopscript < (0009B1DB)>]     Found marker 13; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF00A158)>]
[11/22/2016 - 11:51:07AM]  [workshopscript < (0009B1DB)>]     Found marker 14; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF00A045)>]
[11/22/2016 - 11:51:08AM]  [workshopscript < (0009B1DB)>]     Found marker 15; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF04E5F2)>]
[11/22/2016 - 11:51:08AM]  [workshopscript < (0009B1DB)>]     Found marker 16; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF00A323)>]
[11/22/2016 - 11:51:09AM]  [workshopscript < (0009B1DB)>]     Found marker 17; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF00A0F2)>]
[11/22/2016 - 11:51:10AM]  [workshopscript < (0009B1DB)>]     Found marker 18; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF04E6D9)>]
[11/22/2016 - 11:51:10AM]  [workshopscript < (0009B1DB)>]     Found marker 19; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF009568)>]
[11/22/2016 - 11:51:11AM]  [workshopscript < (0009B1DB)>]     Found marker 20; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF00A08E)>]
[11/22/2016 - 11:51:12AM]  [workshopscript < (0009B1DB)>]     Found marker 21; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF00967E)>]
[11/22/2016 - 11:51:13AM]  [workshopscript < (0009B1DB)>]     Found marker 22; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF00A274)>]
[11/22/2016 - 11:51:14AM]  [workshopscript < (0009B1DB)>]     Found marker 23; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF00956F)>]
[11/22/2016 - 11:51:14AM]  [workshopscript < (0009B1DB)>]     Found marker 24; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF00A2A5)>]
[11/22/2016 - 11:51:15AM]  [workshopscript < (0009B1DB)>]     Found marker 25; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF00A11C)>]
[11/22/2016 - 11:51:16AM]  [workshopscript < (0009B1DB)>]     Found marker 26; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF009FD9)>]
[11/22/2016 - 11:51:17AM]  [workshopscript < (0009B1DB)>]     Found marker 27; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF00A271)>]
[11/22/2016 - 11:51:19AM]  [workshopscript < (0009B1DB)>]     Found marker 28; Form = [MovableStatic < (0023D7DA)>]; Ref = [ObjectReference < (FF009F09)>]
[11/22/2016 - 11:51:20AM]  [workshopscript < (0009B1DB)>] UFO4P_CleanupDamageHelpersAtWorkshop - DONE

I have been running this since mid august with the line for resetting the tracking bool commented out, i.e. my game is running the cleanups permanently after every workshop reset - just for testing purposes of course, since the procedure is not intended to be run on any workshop more than once. Although it may take a long time to complete, there is no noticeable drop in performance: after all, it runs on a single, separate thread and does not call external scripts to perform any lengthy tasks.

 

The procedure is doing now what it was intended to do: it can reliably detect all valid markers (no matter whether the grid points are placed on existing crops or not) and also spotted and removed a couple of invalid ones.

 

 

VOLUNTEERS NEEDED

 

Since I have very rarely removed any crops by storing them in the workbench in any of my playthroughs, there weren't many invalid markers to clean up. Not that I did not create some deliberately and not that I did not vary the crop positiions in my farm plots: there's still the risk of my solution being too closely focused on how I play the game (i.e. how I imagine a farm plot could be conceived). I have to make sure however that the procedure works in anyone's game, and in this specific case, I cannot be sure that it does when others don't have tested it.

 

To make sure that the procedure works (or, at least, removes a major fraction of invalid markers in anyone's game), I need some people that do have this problem in their games to test my approach (and log the results while they are doing so).

 

Depending on the outcome, I could refine the procedure before we go live. In the worst case, it might turn out that it only spots a minor fraction of invalid markers and we would have to live with the fact that we cannot provide an appropriate retroactive solution. Even then however, we would know at least that we tried what we could on this issue, close the tickets and move on to work on other bugs.

Share this post


Link to post
Share on other sites

bumping this because I have a relevant report

 

at the nuka workd red rocket there are a small farm, carrots, groud, razorgrain...

 

After storing and scrapping everything, it comes to my attention that the razorgrains are still there, only invisible. They can be interacted in the workshop mode and there's more: you can keep storing them as much as you like. It's a endless supply of razorgrains!

 

some screens

 

OgkrnYf.jpg

XtuPtdF.jpg

Share this post


Link to post
Share on other sites

The same problem as that of the kawaksallas, but when you move the watermelon at the wall of the house in the Sanctuary Hills. Watermelon become invisible.

Share this post


Link to post
Share on other sites

Oh Beth, you never-ending source of fun and frustration ...

 

First of all, my previous approach belongs to the category "Why make it simple, if you can make it overly complicated ?".

 

The references of invalid damage helpers may not be known to the crops but that doesn't mean that we need to invent complicated search algorithms to spot them. After all, all damage helpers are running a script (to deal their damage to the crops), Which means that I only have to add an OnLoad event to that script to let them validate themselves:

Scriptname WorkshopFloraDamageHelperScript extends ObjectReference Const

:
:
:

Event OnLoad()
	if !IsDisabled()
		UFO4P_ValidateMe()
	endif 
EndEvent

function UFO4P_ValidateMe()

	bool bDeleteMe = true
	ObjectReference myLinkedRef = GetLinkedRef()
	int workshopID = -1
	
	if myLinkedRef
		workshopObjectScript myLinkedObject = myLinkedRef as WorkshopObjectScript
		if myLinkedObject
			workshopID = myLinkedObject.workshopID
			ObjectReference myLinkedObjectDamageHelperRef = myLinkedObject.UFO4P_GetMyDamageHelperRef()
			if myLinkedObjectDamageHelperRef && myLinkedObjectDamageHelperRef == self
				if self.GetBaseObject() != myLinkedObject.DamageHelper
					myLinkedObject.UFO4P_ClearMyDamageHelperRef()
					TraceResult ("Linked object has a different damage helper base object specified. Deleting ...", workshopID)
				elseif myLinkedObject.GetDistance(self) > 10.0
					myLinkedObject.UFO4P_ClearMyDamageHelperRef()
					TraceResult ("Not placed at the position of the linked object. Deleting ...", workshopID)
				else
					bDeleteMe = false
					TraceResult("OK", workshopID)
				endif
			else
				TraceResult ("Linked object has a different damage helper reference specified. Deleting ...", workshopID)
			endif
		else
			TraceResult ("Not linked to a workshop object. Deleting ...", workshopID)
		endif
	else
		TraceResult ("Not linked to any object. Deleting ...", workshopID)
	endif

	if bDeleteMe
		DisableNoWait()
		SetLinkedRef(none)
		Delete()
	endif

endFunction

function TraceResult (string HelperStatus, int wsID)
	debug.OpenUserLog("DamageHelperLog")
	debug.TraceUser("DamageHelperLog", self + ": WorkshopID = " + wsID + ", Status = " + HelperStatus)
endFunction 

This also works when damge helpers are duplicated in the same place. Since they are created by a PlaceAtMe command at the position of the crop, this is likely the case for most invalid helpers, and trying to find them via a FindClosestReference type function would fail anyway since whether that function returns the valid helper or an invalid one would be entirely random.

 

With the modified script active, I ran from Lexington to Sanctuary (via Drumlin Diner, Concord and Red Rocket Station) and got 642 damage helpers logged, of which only 140 very valid ones! Ooops!

 

Now, what's interesting here is that less than a dozen of the invalid ones were at workshops. Most of them are at wilderness crops, and it appears that those crops don't just have one invalid marker!

 

I have no idea where they come from. Could it be that the damage helpers survive a cell reset because they have a linked reference ?

 

EDIT: Forgot to say that all invalid helpers were invalid for the same reason: the crops they were linked to had a different reference stored in their myDamageHelperRef variables.

Share this post


Link to post
Share on other sites

The most important news first: This new approach works as expected and will remove all invalid helpers permanently.

 

Meanwhile, I simplified the code a little, since it is only for removing markers that are either not linked to anything, not linked to a crop or to a crop that has a different marker reference specified in its myDamageHelperRef variable (i.e. crops that created new helpers for whatever reason which made any previously existing ones invalid). Misconfigurations of valid markers are checked by the crops anyway and we don't need to run that code twice:

Event OnLoad()
	if !IsDisabled()
		UFO4P_ValidateMe()
	endif 
EndEvent

function UFO4P_ValidateMe()

	ObjectReference myLinkedRef = GetLinkedRef()
	int workshopID = -1

	debug.OpenUserLog("DamageHelperLog")
	
	if myLinkedRef
		workshopObjectScript myLinkedObject = myLinkedRef as WorkshopObjectScript
		if myLinkedObject
			workshopID = myLinkedObject.workshopID
			ObjectReference myLinkedObjectDamageHelperRef = myLinkedObject.UFO4P_GetMyDamageHelperRef()
			if myLinkedObjectDamageHelperRef && myLinkedObjectDamageHelperRef == self
				debug.TraceUser("DamageHelperLog", "Valid helper found. Ref = " + self + ", LinkedRef = " + myLinkedRef + ", WorkshopID = " + workshopID)
				return
			endIf
		endif
	endif

	debug.TraceUser("DamageHelperLog", "Invalid helper found. Ref = " + self + ", LinkedRef = " + myLinkedRef + ", WorkshopID = " + workshopID + ". Deleting ...")
	
	DisableNoWait()
	SetLinkedRef(none)
	Delete()

endFunction 

With this code in place, large parts of the clenaup procedure added to WorkshopObjectScript in UFo4P 1.0.5 become obsolete: there is no need for the crops to check for unrelated markers (this was added back then to catch as many invalid damage helpers as possible when we still tried to track them from the crops, but now that they validate themselves, this code is superfluous) and there's also no need for trying to repair a broken pointer (as this costs more performance than creating a new marker and letting the old one delete itself on next load).

As already mentioned above, I discovered another related bug though: wilderness crops are piling up a large number of invalid damage helpers over time, and it appears that this is in fact a result of cell resets: the myDamageHelperRef variable is reset to its default value ( i.e. 'none'), which lets WorkshopObjectScript create a new helper, but the previous helper still exists (apparently even after a cell reset) because it never was unlinked from the crop.

Get this one for example: This log is from two crops that were spotted in the area between Easy City Downs and Revere Beach: they both had 27 (TWENTY-SEVEN !) invalid damage helpers linked to them:

[01/14/2017 - 09:15:09PM] DamageHelperLog log opened (PC-64)
[01/14/2017 - 09:15:09PM] Valid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF073234)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF03E96C)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF03E97A)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF03D5CD)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF03C86E)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF03D621)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF03C6BF)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF0393D0)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF0393CF)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF036CD3)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF037078)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF02D821)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF037079)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF02D81D)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF026249)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF026223)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF01DE8E)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF01DD84)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF01D70C)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF01D499)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF01C863)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF01C844)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF01B496)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF036CAE)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF019318)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF019745)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF019744)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF0192FE)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF0191CA)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF018940)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF0191E0)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF0181BD)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF01B3FB)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF0181A8)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF016B9E)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF016B74)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF016B73)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF017E7D)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF069AA7)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF009D06)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF009C99)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF013744)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF016B9D)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF013828)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF05F463)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF0468BD)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF04C8B7)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Valid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF073235)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF04669B)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF06E3AD)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:09PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF06E3AE)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:10PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF0564BA)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:10PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF04CF35)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:10PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF056510)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:10PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF05F945)>], LinkedRef = [workshopobjectscript < (00087411)>], WorkshopID = -1. Deleting ...
[01/14/2017 - 09:15:10PM] Invalid helper found. Ref = [WorkshopFloraDamageHelperScript < (FF069A63)>], LinkedRef = [workshopobjectscript < (00087410)>], WorkshopID = -1. Deleting ...

This brings save game bloat to a new level!

To fix this, I cnsidered to add some code to WorkshopObjectScript that lets all crops that are not at a workshop location (i.e. workshopID = -1) delete their damage helpers on unload, and create a new one on load.  However, this would cost at least as much performance as letting the helpers alone and delete themselves from the modified OnLoad event of WorkshopFloraDamageHelperScript once they become invalid. After all, the modified WorkshopFloraDamageHelperScript will be running on all helpers at all time and performs the necessary checks anyway.

Which means that the script modification will fix that bug too.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Support us on Patreon!

×