Jump to content

I'm having trouble placing a turret for any given workshop using scripts


Recommended Posts

I'm using scripts to replace an existing turret with a new turret. The gist of it is:

OriginalTurret.Disable(abFadeOut = False)
ReplacementTurret = OriginalTurret.PlaceAtMe(akFormToPlace = ReplacementTurretForm, aiCount = 1, abForcePersist = False, abInitiallyDisabled = False, abDeleteWhenAble = False)
ReplacementTurret.SetLinkedRef(akLinkedRef = Workshop, apKeyword = WorkshopItemKeyword)
OriginalTurret.Delete()

You can also get the loose files to the mod as a whole here if you want the specifics (which includes the source files to the scripts). If you want to load it up in-game, it adds an activate option to the Machinegun Turret and Heavy Machinegun Turret (and adds marks I, III, V and VII to the workshop menu), which allows you to upgrade/downgrade between the turret marks. Available marks depend on player level requirements and encounter zone limitations, as with the vanilla game. So, I recommend you experiment somewhere high level (such as Nordhagen Beach) with a level 40+ player (player.modav experience 99999). This will allow you to experience the problem that I will be describing for yourself.

 

This implementation mostly works as intended, with one annoying flaw. The workshop defense rating will not update when you scrap any turret (that has been placed by this script) in workshop mode. So if I have a defense rating of 20, it will still be 20 when I scrap a turret. If I place the same turret using the workshop menu, it functions correctly. A stop-gap solution is to either place or destroy another object in workshop mode that produces resources (e.g. a mutfruit plant, a water pump, or a turret that wasn't placed with scripts).

 

The only proper solution I've found to this problem is to reload the workshop. Once you do this, any turrets that I place using scripts appear to function correctly (like any other turret). I achieve this at Sanctuary by running to the Museum of Freedom (where you first encounter Preston & friends), and then running back. I've used the Place Everywhere mod to remove the 5-second timeout to run back and forth in workshop mode. So, if you first scrap any turrets that have been placed using scripts before running back and forth in workshop mode, you can see at which exact point the defense rating is updated. This shows that it is updated when the workshop loads, which in the case of Sanctuary is when you're running back and reach the Red Rocket Truck Stop side of the Sanctuary Bridge.

 

This leads me to believe that there's some OnLoad() event in some script somewhere that somehow corrects whatever issue I'm encountering. I'm not entirely sure that this is the full extent of the bug, and therefore whether this solution fixes all of my problems. I've looked through all possible scripts that I think could be relevant, to either point me in the direction of the problem or a real solution. That is, the WorkshopObjectScript and workshopObjectActorScript which are attached to the turret, and the WorkshopScript that is attached to the workbench.

 

I've been experimenting with different solutions all day, and so far I've come up with a whole bunch of nothing. This is obviously a pretty specific question, so Google has come up with squat and looking at the source of Sim Settlements is like trying to find a needle in a haystack, not that the aforementioned vanilla scripts were much better. If anyone could offer any assistance, I'd very much appreciate it.

 

Link to comment
Share on other sites

If you don't get any better offers have a poke into the workshop events to see whats going in (this is a primer, not fully working standalone script):

Import WorkshopScript
WorkshopParent.RegisterForWorkshopEvents(self, bRegister = true)

Event WorkshopParentScript.WorkshopObjectBuilt(WorkshopParentScript akSender, Var[] akArgs)
	WorkshopObjectScript newWorkshopObject = akargs[0] as WorkshopObjectScript
	WorkshopScript workshopRef = akargs[1] as WorkshopScript
	safety =  WorkshopParent.GetRating(workshopRef, WorkshopParent.WorkshopRatingSafety)
	debug.trace(self + " WorkshopObjectBuilt " + workshopref + " " + newWorkshopObject + " " + safety  )
EndEvent

Event WorkshopParentScript.WorkshopObjectDestroyed(WorkshopParentScript akSender, Var[] akArgs)
	WorkshopObjectScript destroyedWorkshopObject = akargs[0] as WorkshopObjectScript
	WorkshopScript workshopRef = akargs[1] as WorkshopScript
	safety =  WorkshopParent.GetRating(workshopRef, WorkshopParent.WorkshopRatingSafety)
	debug.trace(self + " WorkshopObjectDestroyed " + workshopref + " " + destroyedWorkshopObject + " " + safety )
EndEvent

Edit: with enough debug.trace polling you can probably trap whatever event is kicking the WorkshopRatingSafety recalculation ...

Edited by SKK50
Link to comment
Share on other sites

That's a good place to look into next, thanks. I did also blank on WorkshopParentScript, so that will also be something I can look into.

I'd say that using scripts to spawn these turrets is skipping some of the workshop related events, which is what I spent a lot of yesterday looking into. If it's any of them, it's most likely to be the WorkshopObjectBuilt event, but the error is only evident once you destroy the turret (triggering the WorkshopObjectDestroyed event). Using debug.trace to take a look at workshop related events should clear some things up. For instance, does the WorkshopObjectBuilt event get triggered when a workshop object is spawned out of workshop mode using scripts?

I'm assuming that the WorkshopObjectDestroyed() event is triggered correctly when you scrap a turret in workshop mode that was spawned using scripts. Maybe a keyword (or something) is added to workshop objects when you create them in workshop mode that registers them to update the workshop resource ratings. As I mentioned in my first post, the OnLoad() event should be a big lead, but so far I've come up with nothing. Specifically, does the OnLoad() event trigger any subsequent workshop related events, perhaps as a fail-safe? For instance, I looked into how the DailyUpdate() function is triggered, etc.

Turrets are considered actors by the game (the same as settlers in a way), so it could be that I have to establish another link that's specific to workshop actors (hence the workshopObjectActorScript was of particular interest to me). I couldn't come up with anything, though. All I can say is that it adds another layer of complexity to the issue. Flora objects for instance would be much easier to troubleshoot (I would think), considering their forms are much simpler.

I'll go look into all of this soon, and update this thread with anything interesting that I find.

Edit 1: I had just been scrapping the turrets in my testing, which is still an issue, but apparently storing them instead does update the workshop defense rating. So, that's odd, given that both scrapping and storing the turrets trigger the WorkshopObjectDestroyed() event.

Edit 2: The only workaround I can think of at present is to use the RecalculateResources() function each time an affected turret is scrapped (via the WorkshopObjectDestroyed() event). Perhaps I could even set it up so that it is only in effect until the first time the turret is unloaded (given that the problem appears to be resolved once it is re-loaded). In saying that, the RecalculateResources() function is a trail to look down itself.

Edit 3: I think I've found the solution, which I've only quickly tested so far, but it worked right away. The OnWorkshopObjectPlaced() event doesn't trigger because I'm spawning them with scripts outside of workshop mode. I eventually familiarised myself with the various functions in WorkshopParentScript that are used in WorkshopScript (by the turret), which lead me to BuildObjectPUBLIC(). Calling this function on the turrets when I spawn them appears to fix this issue. Thanks again SKK50, your directions eventually lead me down the (hopefully) right path. I'll continue to post any further relevant information I come across.

Edit 4: The same applies for the deletion of a workshop object. So, I'm applying the RemoveObjectPUBLIC() function to the existing turret that I am deleting using scripts, as so:

OriginalTurret.Disable(abFadeOut = False)
WorkshopParent.RemoveObjectPUBLIC(RemovedObject = OriginalTurret, WorkshopRef = Workshop)
OriginalTurret.Delete()

ReplacementTurret = OriginalTurret.PlaceAtMe(akFormToPlace = ReplacementTurretForm, aiCount = 1, abForcePersist = False, abInitiallyDisabled = False, abDeleteWhenAble = False)
ReplacementTurret.SetLinkedRef(akLinkedRef = Workshop, apKeyword = WorkshopItemKeyword)
WorkshopParent.BuildObjectPUBLIC(NewObject = ReplacementTurret, WorkshopRef = Workshop)

This is far from set in stone, but appears to be working for now.

Edited by KernalsEgg
Link to comment
Share on other sites

LOL result, been there. Both Followers and Workshop have undocumented public/global functions that you start off trying to write yourself (cos you don't know they exist), generate hours of debug logs, get frustrated, eventually look in the base game scripts, find the actual functions, use 'em, kick yourself thinking I really should update creationkit.com for the next victim ... nah, too busy.

 

Learning. Learning never changes.

Link to comment
Share on other sites

Yeah, that describes too many of my experiences modding. It's a wonder we keep on coming back to it.

 

The WorkshopObjectActorScript also triggers the WorkshopObjectBuilt() event, which calls the EvaluatePackage() function. I also messaged kinggath, given his experience with this sort of thing, and he added that I should unlink the original turret (that is being deleted). In his words "BuildObjectPUBLIC and RemoveObjectPUBLIC are important for other things, but they don't affect the ratings", so I imagine that they were only indirectly correcting the issue that I was encountering.

 

In summary, the script now looks like this:

OriginalTurret.Disable(abFadeOut = False)
OriginalTurret.SetLinkedRef(akLinkedRef = None, apKeyword = WorkshopItemKeyword)
WorkshopParent.RemoveObjectPUBLIC(RemovedObject = OriginalTurret, WorkshopRef = Workshop)
OriginalTurret.Delete()

ReplacementTurret = OriginalTurret.PlaceAtMe(akFormToPlace = ReplacementTurretForm, aiCount = 1, abForcePersist = False, abInitiallyDisabled = False, abDeleteWhenAble = False)
ReplacementTurret.SetLinkedRef(akLinkedRef = Workshop, apKeyword = WorkshopItemKeyword)
WorkshopParent.BuildObjectPUBLIC(NewObject = ReplacementTurret, WorkshopRef = Workshop)
(ReplacementTurret As Actor).EvaluatePackage(abResetAI = True)

I'd consider this pretty final, but if anyone feels that we should add something, let us know.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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