pepperman35 Posted February 28 Share Posted February 28 Within a mod for Fallout 4, I have a jail which as twelve jail cells to house prisoners. These jail cells are identified with xmarkers located at the center of each cell. These markers are labeled FO4_CellMarker01 through FO4_CellMarker12. I have created six NPC prisoners which are located in a temporary holding cell called CoveTempHoldingCell. The six prisoners are labeled NPC_Prisoner01 through NPC_Prisoner06. I have created a Papyrus script that achieves the following: 1) When the player character enters a certain trigger, the six prisoners are randomly placed into the jail cells; and 2) When the player leaves the trigger, the six prisoners are returned to the temporary holding cell. In execution, I am noticing a significant dps drop when the player enters the trigger. Also, the npcs do not appear to be using the packages when placed by the script. If I hand place the npcs into various jail cells, the npcs use their packages with no issue. Any insight would be welcomed and appreciated. ================================================================ Scriptname FO4_JailCellScript extends ObjectReference ; Define properties for your NPC prisoners and cell markers Actor Property FO4_NPC_Prisoner01 Auto Actor Property FO4_NPC_Prisoner02 Auto Actor Property FO4_NPC_Prisoner03 Auto Actor Property FO4_NPC_Prisoner04 Auto Actor Property FO4_NPC_Prisoner05 Auto Actor Property FO4_NPC_Prisoner06 Auto ObjectReference[] Property JailCells Auto ObjectReference Property CoveTempMarker01 Auto ObjectReference Property CoveTempMarker02 Auto ObjectReference Property CoveTempMarker03 Auto ObjectReference Property CoveTempMarker04 Auto ObjectReference Property CoveTempMarker05 Auto ObjectReference Property CoveTempMarker06 Auto ; Define a trigger to detect player entry and exit Event OnTriggerEnter(ObjectReference akActionRef) If akActionRef == Game.GetPlayer() PlacePrisoners() EndIf EndEvent Event OnTriggerLeave(ObjectReference akActionRef) If akActionRef == Game.GetPlayer() ReturnPrisoners() EndIf EndEvent ; Function to randomly place prisoners in the cells Function PlacePrisoners() ; Initialize the prisoners array Actor[] prisoners = new Actor[6] prisoners[0] = FO4_NPC_Prisoner01 prisoners[1] = FO4_NPC_Prisoner02 prisoners[2] = FO4_NPC_Prisoner03 prisoners[3] = FO4_NPC_Prisoner04 prisoners[4] = FO4_NPC_Prisoner05 prisoners[5] = FO4_NPC_Prisoner06 ; Array to keep track of used cells Int[] usedCells = new Int[0] ; Debug.Notification("Placing prisoners...") ; Debug message While prisoners.Length > 0 Int cellIndex = Utility.RandomInt(0, JailCells.Length - 1) If usedCells.Find(cellIndex) == -1 prisoners[0].MoveTo(JailCells[cellIndex]) prisoners[0].EvaluatePackage() ; Force NPC to evaluate AI packages ; Debug.Notification("FO4_JailCellScript: Moved prisoner to cell: " + cellIndex) ; Debug message ; Add cell index to usedCells Int[] newUsedCells = new Int[usedCells.Length + 1] Int i = 0 While i < usedCells.Length newUsedCells = usedCells i += 1 EndWhile newUsedCells = cellIndex usedCells = newUsedCells ; Remove the first prisoner from the array Actor[] newPrisoners = new Actor[prisoners.Length - 1] i = 1 While i < prisoners.Length newPrisoners[i - 1] = prisoners i += 1 EndWhile prisoners = newPrisoners EndIf EndWhile EndFunction ; Function to return prisoners to the temporary holding cell Function ReturnPrisoners() FO4_NPC_Prisoner01.MoveTo(CoveTempMarker01) FO4_NPC_Prisoner02.MoveTo(CoveTempMarker02) FO4_NPC_Prisoner03.MoveTo(CoveTempMarker03) FO4_NPC_Prisoner04.MoveTo(CoveTempMarker04) FO4_NPC_Prisoner05.MoveTo(CoveTempMarker05) FO4_NPC_Prisoner06.MoveTo(CoveTempMarker06) ; Debug.Notification("FO4_JailCellScript: Prisoners returned to holding cell.") ; Debug message EndFunction Link to comment Share on other sites More sharing options...
SKKmods Posted February 28 Share Posted February 28 I have found that if actors are not 3d loaded after the moveto it can affect AI packages establishing even after forcing EvaluatePackage() You may need to add an OnLoad() EvaluatePackage() to their collection to work around that. Link to comment Share on other sites More sharing options...
LarannKiar Posted February 28 Share Posted February 28 You didn't meantion what kind of AI Package the NPCs should perform (i.e. AI Procedure >>Sit) but you probably want their AnimGraph managers to be initialized (and if Furnitures are involved, theirs too). Is3DLoaded() == True doesn't necessarily mean the AnimGraph is also available, however, after checking Is3DLoaded(), Utility.Wait(0.2) is usually enough for EvaluatePackage() to work as expected. (I added Bool Function HasAnimGraphManager(ObjectReference akReference) native global to Garden of Eden SE in v19.4 if you're interested). 1 Link to comment Share on other sites More sharing options...
pepperman35 Posted February 28 Author Share Posted February 28 The six prisoner NPCs have two simple AI packages: 1) Sandbox package with a 128 radius; and 2) Sleep package. Inside jail cell is 256 x 256 game units, and contains a chair, a bed, a toilet, and a sink. I have hand placed a few furniture markers in each of the cells (e.g., NPCNewspaperLeanLeft, etc). Thanks for the input SKKmods, and LarannKiar, your sage wisdom is invaluable. Link to comment Share on other sites More sharing options...
pepperman35 Posted March 10 Author Share Posted March 10 Don’t you just love the brute force approach to scripting. Anyways, here is what I have done: 1) Set up a quest; 2) moved the majority of the scripting to the quest; 3) created three scripts two of which reside on the quest while the other one resides on the default trigger. The AI packaging issue seems to be resolved but there is still a drop in FPS when entering the trigger. Seems odd for only moving six npcs. Here are the scripts for referenced. Still taking a look see at vanilla scripts to (e.g. DLC04MQ05QuestScrip) to see if I can implement the MoveTo better. Spoiler Scriptname FO4_PrisonerAIQuestScript extends Quest ; Updated: 09MAR2025:1451 ; Properties ReferenceAlias[] Property PrisonerAliases Auto Mandatory ReferenceAlias Property CoveReturnMarkerAlias Auto Mandatory ObjectReference[] Property JailCells Auto Mandatory ObjectReference Property FO4_PoliceOfficersEnable Auto Mandatory int PrisonerIndex = 0 bool isReturningPrisoners = false ; Flag to track prisoner movement state ; Function to shuffle the JailCells array manually Function ShuffleJailCells() int i = JailCells.Length while i > 1 i -= 1 int j = Utility.RandomInt(0, i) ObjectReference temp = JailCells JailCells = JailCells[j] JailCells[j] = temp endwhile EndFunction Function PlacePrisoners() Debug.Trace("[PrisonerAIQuest] Placing prisoners in jail cells...") ShuffleJailCells() ; Randomize cell assignment PrisonerIndex = 0 isReturningPrisoners = false ProcessPrisoners() ; Start the staggered process EndFunction Function ReturnPrisoners() Debug.Trace("[PrisonerAIQuest] Returning prisoners to holding area...") PrisonerIndex = 0 isReturningPrisoners = true ProcessPrisoners() ; Start the staggered process EndFunction Function ProcessPrisoners() while PrisonerIndex < PrisonerAliases.Length Actor prisoner = PrisonerAliases[PrisonerIndex].GetActorReference() if prisoner prisoner.DisableNoWait() if isReturningPrisoners prisoner.MoveTo(CoveReturnMarkerAlias.GetReference()) else prisoner.MoveTo(JailCells[PrisonerIndex]) endif prisoner.EnableNoWait() Debug.Trace("[PrisonerAIQuest] Processed prisoner " + PrisonerIndex) endif PrisonerIndex += 1 Utility.Wait(0.5) ; Stagger movement to prevent FPS drop endwhile ; **Ensure animation graph is ready before evaluating AI** WaitForAnimGraph() ; **AI Reset & Package Evaluation** EvaluatePrisonersAI() ; **Hard Reset AI via Quest Restart** ;Self.Stop() ;Utility.Wait(1.0) ;Self.Start() EndFunction Function WaitForAnimGraph() Debug.Trace("[PrisonerAIQuest] Waiting for animation graphs to initialize...") int i = 0 while i < PrisonerAliases.Length Actor prisoner = PrisonerAliases.GetActorReference() if prisoner int attempts = 0 while !prisoner.Is3DLoaded() && attempts < 10 Debug.Trace("[PrisonerAIQuest] Waiting for 3D load on prisoner " + i) Utility.Wait(0.5) attempts += 1 endwhile if prisoner.Is3DLoaded() Debug.Trace("[PrisonerAIQuest] Prisoner " + i + " 3D loaded. Wait a bit more to ensure anim graph is available") Utility.Wait(0.25) Debug.Trace("[PrisonerAIQuest] Animation Graph is now assumed to be available for " + Prisoner) else Debug.Trace("[PrisonerAIQuest] Warning: Prisoner " + i + " 3D failed to load! Animation Graph may NOT be loaded") endif endif i += 1 endwhile EndFunction Function EvaluatePrisonersAI() Debug.Trace("[PrisonerAIQuest] Evaluating prisoner AI packages...") int i = 0 while i < PrisonerAliases.Length Actor prisoner = PrisonerAliases.GetActorReference() if prisoner Package currentPackage = prisoner.GetCurrentPackage() Debug.Trace("[PrisonerAIQuest] Prisoner " + i + " current package: " + currentPackage) if currentPackage == None Debug.Trace("[PrisonerAIQuest] Prisoner " + i + " has no active package! Resetting AI.") prisoner.EvaluatePackage() else ; Force AI to "wake up" by temporarily disabling and re-enabling movement prisoner.SetRestrained(True) Utility.Wait(0.5) prisoner.SetRestrained(False) prisoner.EvaluatePackage() endif else Debug.Trace("[PrisonerAIQuest] Warning: Prisoner alias " + i + " is empty!") endif i += 1 endwhile EndFunction ; This function is used to enable some extras (police officer npcs) Function EnablePoliceOfficers() FO4_PoliceOfficersEnable.Enable() EndFunction ; This function is used to disable the extras (police officer npcs) Function DisablePoliceOfficers() FO4_PoliceOfficersEnable.Disable() EndFunction Spoiler Scriptname PrisonerAI_DebugScript extends Quest ; 03MAR2025 ; Array of prisoner aliases ReferenceAlias[] Property PrisonerAliases Auto Function EvaluatePrisonersAI() int prisonerCount = PrisonerAliases.Length Debug.Trace("[PrisonerAIDebug] Evaluating AI for " + prisonerCount + " prisoners.") int i = 0 while i < prisonerCount ReferenceAlias prisonerAlias = PrisonerAliases if prisonerAlias Actor prisoner = prisonerAlias.GetActorReference() if prisoner Debug.Trace("[PrisonerAIDebug] Evaluating prisoner [" + i + "]: ") ; Check if 3D is loaded (important for AI reevaluation) if prisoner.Is3DLoaded() Debug.Trace("[PrisonerAIDebug] Prisoner [" + i + "] is 3D loaded.") ; Log current package Package currentPackage = prisoner.GetCurrentPackage() if currentPackage Debug.Trace("[PrisonerAIDebug] Prisoner [" + i + "] current package: " + currentPackage) else Debug.Trace("[PrisonerAIDebug] Prisoner [" + i + "] has no active package.") endif ; Force AI reevaluation prisoner.EvaluatePackage() Debug.Trace("[PrisonerAIDebug] Prisoner [" + i + "] AI reevaluated.") else Debug.Trace("[PrisonerAIDebug] Prisoner [" + i + "] is NOT 3D loaded. Skipping AI evaluation.") endif else Debug.Trace("[PrisonerAIDebug] Prisoner alias [" + i + "] is empty or invalid.") endif else Debug.Trace("[PrisonerAIDebug] PrisonerAliases[" + i + "] is null.") endif i += 1 endwhile EndFunction ; Automatically trigger AI evaluation when the quest starts Event OnQuestInit() Debug.Trace("[PrisonerAIDebug] PrisonerAI_DebugScript initialized.") EvaluatePrisonersAI() EndEvent Spoiler Scriptname FO4_JailCellScript extends ObjectReference ; 09MARCH2025:1508 Quest Property PrisonerAIQuest Auto Mandatory Event OnTriggerEnter(ObjectReference akActionRef) If akActionRef == Game.GetPlayer() Debug.Trace("[JailCellTrigger] The player has entered the trigger area") (PrisonerAIQuest as FO4_PrisonerAIQuestScript).PlacePrisoners() (PrisonerAIQuest as FO4_PrisonerAIQuestScript).EnablePoliceOfficers() EndIf EndEvent Event OnTriggerLeave(ObjectReference akActionRef) If akActionRef == Game.GetPlayer() Debug.Trace("[JailCellTrigger] The player has left the trigger area") (PrisonerAIQuest as FO4_PrisonerAIQuestScript).DisablePoliceOfficers() EndIf EndEvent Link to comment Share on other sites More sharing options...
Recommended Posts