Jump to content

Dynamic object replacement


senterpat

Recommended Posts

Hello, I'm hoping someone can help with this, as the only way I can figure to do it is a ton of FO4edit edits.

 

I need to find and replace potion objects as cells are loaded that are placed in the game world. I used GetNextRef in FNV to accomplish this, but cannot find any documentation on a similar command for FO4.

 

The only other way I can think to accomplish this is to replace all the references with a dummy object and then set its Leveled List in FO4edit. There are thousands of references, so would prefer to find a scripted way. Also this method won't support mod added locations, so I would have to make more patches.

Link to comment
Share on other sites

What your asking is fairly complex, so I trust your OK with the fundamental quest and script structures:

 

Create an Xmarker at the player either quest alias or PlaceAtMe()

 

Area Scan FindAllReferencesOfType() max radius 10K units loaded area

Do stuff with the results

Xmarker MoveTo() Player

RegisterForDistanceGreaterThanEvent() from the Xmarker, typically half the scan radius for overlap

 

OnDistanceGreaterThan() event trigger Area Scan

Link to comment
Share on other sites

So you are suggesting using RegisterForDistanceGreaterThanEvent on the player, process everything it gives you, drop an xMarker at the position, and then redo the whole thing as soon as the player leaves a certain area around the xmarker? Yes, that sounds like it should work. However, what happens if the player enters an interior or changes worldspaces? Will the event fire, too? If not, you'd need something to catch that, too. Maybe onCellDetach on the marker could work?

 

 

 

If you want to do it in xEdit instead, you can look at my search&replace script in here: https://www.nexusmods.com/fallout4/mods/28898

The way I made it you need to make a replacement map file, where you write which editor IDs should be replaced by which other editor IDs.

Link to comment
Share on other sites

Its the well proven basis for all SKK dynamic detection systems used by tens of thousands of satisfied customers in stuff like AutoUnlock, Combat Settlers, Dogmeat Sniffs Glue, Combat Stalkers, 4-76 NPC replacement, 4-76 Item replacement ...

 

The trick is to ensure variation in the distance events so they don't all trigger at the same time, tripping the SKK Script Lag detector

 

Previously used OnLocationChange, but the named boundaries are rather random from 1K to 20K units apart, and there are over 30 50 base game registrations for that event already all triggering at the same time. /research

Link to comment
Share on other sites

  • 2 weeks later...

So I've managed to get this working to some extent, but now I've hit another road block, the item that is replacing the found potion is a leveled list, and you can't add a Leveled Item directly to the world, in FNV I used a container reference to work out the leveled item, then used GetInventoryObject to get the new item and place it in the world. I can't find a function to do this in FO4 tho. Any ideas?

Link to comment
Share on other sites

PlaceAtMe can be used with level lists no problem:

ObjectReference ThisItem = pPlayerREF.PlaceAtMe(pLL_Deliverer, aiCount = 1, abForcePersist = true, abInitiallyDisabled = false, abDeleteWhenAble = false)

ObjectReference ThisItem = ObjectToSpawnAt.PlaceAtMe(pLGND_PossibleLegendaryItemBaseLists, aiCount = 1, abForcePersist = true, abInitiallyDisabled = false, abDeleteWhenAble = false)

You may need to avoid bounding collisions either:

 

(a) spawn at the replaced item abInitiallyDisabled = true, then .Disable() the replaced item and .Enable() the replacement item

 

(b) .Disable() the replaced item before spawning the replacement.

Link to comment
Share on other sites

Awesome thanks for all the help SKK, this sort of script is a bit more complex then any I've attempted, and I just want to make sure I didn't miss anything, I used your script as a reference (hope you don't mind) and was wondering if you could look it over and make sure it's not wasteful or anything. Either way, thanks for all the help!


Function FindStuff
	Self.UnregisterForDistanceEvents(pPlayerREF as ScriptObject, Alias_LocationChangeMarker.GetReference() as ScriptObject)
	Self.UnRegisterForPlayerTeleport()
	ObjectReference[] FoundStuff = pPlayerREF.FindAllReferencesOfType(CigaretteCarton, fDetectionDistance)
	int currentElement = 0
		while (currentElement < FoundStuff.Length)
			FoundStuff[currentElement].Disable()
			ObjectReference NewObject = FoundStuff[currentElement].Placeatme(CigaretteCartonReplacer, 1, True, false, false)
			FoundStuff[currentElement].Delete()
			currentElement += 1
		endWhile
			ObjectReference[] FoundStuff = pPlayerREF.FindAllReferencesOfType(CigarettePack, fDetectionDistance)
	int currentElement = 0
		while (currentElement < FoundStuff.Length)
			FoundStuff[currentElement].Disable()
			ObjectReference NewObject = FoundStuff[currentElement].Placeatme(CigarettePackReplacer, 1, True, false, false)
			FoundStuff[currentElement].Delete()
			currentElement += 1
		endWhile
	Self.SwitchEnabled()
EndFunction


Function SwitchEnabled()
		Self.RegisterForPlayerTeleport()
		Alias_LocationChangeMarker.GetReference().MoveTo(pPlayerREF, 0, 0, 0, True)
		Self.RegisterForDistanceGreaterThanEvent(pPlayerREF as ScriptObject, Alias_LocationChangeMarker.GetReference() as ScriptObject, fMovementDistance)
EndFunction

Event Actor.OnLocationChange(Actor akSender, Location akOldLoc, Location akNewLoc)
	Self.UnregisterForAllEvents()
	Self.RegisterForRemoteEvent((pPlayerREF as Actor) as ScriptObject, "OnPlayerLoadGame")
	Self.SwitchEnabled()
EndEvent

Event Actor.OnLocationChange(Actor akSender, Location akOldLoc, Location akNewLoc)
	Self.UnregisterForAllEvents()
	Self.RegisterForRemoteEvent((pPlayerREF as Actor) as ScriptObject, "OnPlayerLoadGame")
	Self.SwitchEnabled()
EndEvent

Event OnQuestInit()
	Self.RegisterForRemoteEvent((pPlayerREF as Actor) as ScriptObject, "OnPlayerLoadGame")
	Self.RegisterForCustomEvent((pFollowers as followersscript) as ScriptObject, "followersscript_CompanionChange")
	Self.SwitchEnabled()
EndEvent

Event OnDistanceGreaterThan(ObjectReference akObj1, ObjectReference akObj2, float afDistance)
	If ((pPlayerREF as Actor).IsOnMount() == True)
		Alias_LocationChangeMarker.GetReference().MoveTo(pPlayerREF, 0, 0, 0, True)
		Self.RegisterForDistanceGreaterThanEvent(pPlayerREF as ScriptObject, Alias_LocationChangeMarker.GetReference() as ScriptObject, fMovementDistance)
	Else
		Self.FindStuff()
	EndIf
EndEvent

Event Actor.OnPlayerLoadGame(Actor akSender)
		Self.RegisterForRemoteEvent((pPlayerREF as Actor) as ScriptObject, "OnPlayerLoadGame")
		Self.SwitchEnabled()
	EndIf
EndEvent
Link to comment
Share on other sites

Hey that script looks familiar ! Don't have a stack of time, but a quick scan looks lovely.

 

Except tiny detail you cant declare int currentElement twice in the same scope. Second one will compile error.

 

+ Don't forget to create a LocationChangeMarker quest reference alias that creates a persistent xmarker at the player.

 

++ fMovementDistance is best at around 50% fDetectionDistance for good overlap and minimum overhead. Challenge is the default uGridsToLoad max active radius is 10,240 so *everyone* uses that to detect and 5,120 to move. Try and jitter those numbers.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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