xyz3lt Posted April 3, 2021 Share Posted April 3, 2021 Naturally since i am looking for a way to get actors from around player any way of scanning for ObjectRefrences around some ObjectReference will do.Ideally what i'd like is a Scan() function that returns array of Actors or ObjectReferences. Roughly what i am using right now. elt_Scanner.psc: Scriptname elt_Scanner extends Quest ReferenceAlias Property playerRef Auto Spell Property ScanSpell Auto Actor[] found_arr Actor[] return_arr float fScanStart Int found_C bool bRunning = false bool bInitialized = false Event OnInit() found_arr = new Actor[20] bInitialized = true endEvent Actor[] Function Scan() ;EP for Scripts that want to scan actors around player bRunning = true ScanSpell.Cast(playerRef.GetRef() as Actor) fScanStart = Utility.GetCurrentRealTime() RegisterForSingleUpdate(1) While bRunning == true ;bRunning will be set to false after 5 seconds Utility.Wait(1) ;Within those 5 seconds ActiveMagicEffect will push(akTarget) into found_arr endWhile return_arr = PapyrusUtil.ResizeActorArray(found_arr, found_C) ;New resized array filled with all found actors int i ;Clean found_arr While i < found_C found_arr[i] = none i+=1 endWhile found_C = 0 return return_arr ;Return newly created array filled with found actors endFunction Event onUpdate() if (fScanStart + 5) < Utility.GetCurrentRealTime() ;If 5 seconds since ScanStart have passed set end flag Display() bRunning = false else ;Else check in a second, now that i think one RegisterForSingleUpdate(5) would work better... RegisterForSingleUpdate(1) endif endEvent Function Push(Actor obj) if found_C < 20 found_arr[found_C] = obj found_C += 1 endif endFunction Function Display() Debug.Notification("Number of found actors: " + found_C) endFunction elt_ScannerEffect.psc: Scriptname elt_ScannerEffect extends activemagiceffect elt_Scanner Property Scanner auto Event OnEffectStart(Actor akTarget, Actor akCaster) Scanner.Push(akTarget) EndEvent Script above casts AOE on player and retrives actors this way, but it does take a little while, mostly because i don't know how to safely handle parallelism differently. I tried to use PapyrusUtils>MiscUtils>ScanCellNPCs but exactly as it says it only seems to scan a single cell, which is a problem in worldspace since cells are divided into squares.Also thought of getting 4 closest cells to player then GetNthForm for actors and handle distance manually with GetDistance. I didn't try it though.And also tried PowersOfThree>PO3_SKSEfunctions>FindAllReferencesOfFormType but that function seems to returns references as you would see them in CK render window. It worked very oddly, sometimes returning nothing and sometimes a lot. Some mod that i checked made use of a scriptCloak that add's keywords, then Actors are retrived trough ReferenceAlias'es. It's a bit too confusing for me to recreate from just looking. Here is PO3 script incase i used it incorrectly. elt_QuestScanner.psc: Scriptname elt_QuestScanner extends Quest import PO3_SKSEfunctions ObjectReference PlayerRef Event OnInit() PlayerRef = Game.GetPlayer() as ObjectReference RegisterForKey(62) Debug.Notification("F4 key Registered") endEvent ;PO3_SKSEfunctions.ObjectReference[] Function FindAllReferencesOfFormType(ObjectReference akRef, Int formType, float radius) Event OnKeyDown(Int KeyCode) ;Actor[] Scanned = MiscUtil.ScanCellNPCs(PlayerRef, 1000.0) ;ObjectReference[] Scanned = FindAllReferencesOfFormType(Game.GetPlayer() as ObjectReference, 43, 1000.0) ObjectReference[] Scanned = FindAllReferencesOfFormType(PlayerRef, 43, 1000.0) Debug.Notification("Scanned length: " + Scanned.Length) if Scanned ;not empty, notification with a string containing all found form43 objects Debug.Trace("Array not empty") int i = 0 string dbgString while i < Scanned.Length ;Debug.Notification(Scanned[i].GetBaseObject().GetName() + " detected") dbgString += Scanned[i].GetBaseObject().GetName() + " " i+=1 endwhile Debug.Notification(dbgString) else ;empty Debug.Trace("Array empty") endif EndEvent Link to comment Share on other sites More sharing options...
AnishaDawn Posted April 3, 2021 Share Posted April 3, 2021 (edited) You can use SKSE to do something similar. I wrote a quick and dirty example: Actor akCenter = player ; player variable containing Game.GetPlayer() isnt visible in this example Actor[] kActors = new Actor[128] Cell kCell = akCenter.GetParentCell() Int cellActors = kCell.GetNumRefs(43) Int i while i < cellActors Actor akActors = kCell.GetNthRef(i, 43) as actor if akActors != akCenter ; exclude the center of the search kActors[i] = akActors endif i += 1 endwhile Problems: Could be slow since it searches the entire cell checking every type that matches, so highly populated cells might slow it down.You will need to resize the array if the number of actors is over 128. I think, for example if you're near the edge of a cell and actors are in the next cell, this method will ignore them and catch only the ones in the parentcell. This works great for interiors though.You can modify this to make your own FindReferenceofXXXXX, like restricting the search to the radius around the player, rather than the full size of the cell if you want as well.Edit: I believe that FindAllReferencesOfFormType is actually using GetNumRefs underneath because the behavior you described is exactly how GetNumRefs operates. Cells might have 20 actors, and then some cells might have no actors. Now if you use the formtype to find objectreferences, it will return everything in the cell. Edited April 3, 2021 by AnishaDawn Link to comment Share on other sites More sharing options...
dylbill Posted April 3, 2021 Share Posted April 3, 2021 Papyrus Extender has some functions that can help with this: https://www.nexusmods.com/skyrim/mods/95017 ObjectReference[] function FindAllReferencesOfType(ObjectReference akRef, Form formOrList, float radius) global native ObjectReference[] function FindAllReferencesWithKeyword(ObjectReference akRef, Keyword[] keywordArray, float radius, bool matchAll) global native Actor[] function GetActorsByProcessingLevel(int Level) global nativeUse 0 for the actor function to get all loaded actors in your area. Link to comment Share on other sites More sharing options...
xyz3lt Posted April 3, 2021 Author Share Posted April 3, 2021 Big problem with GetNthRef / GetNumRefs is how cells work, inside a big cell like a ruin you can get a lot of hits, but add some GetDistance processing and you are good, meanwhile actors standing next to you inside worldspace like tamriel or whiterun may not be detected since they are technically in a different cell. For interiors i could just use ScanCellNPCs from MiscUtils as it does have built in distance, but for exteriors i'd have to check roughly 4 cells closest to player to get rid of "standing next to but not detected" issue. If it's faster then spell approach i'll try doing it anyways. As for PO3 it just acts strangely, maybe i misunderstood how it works, using script above, outside of jorrvaskr i can get at most 3 detections (player included) but usually 0 not even player, but inside even though there were only 2 npcs there i got 13 detections, including strange things like bandits and silver hand. Link to comment Share on other sites More sharing options...
dylbill Posted April 3, 2021 Share Posted April 3, 2021 Ah, I haven't tested the P03 actor function really, but have tested the ObjectReference function and they seem to work. There's also some function in MiscUtil from PapyrusUtil you may want to check too. Link to comment Share on other sites More sharing options...
xyz3lt Posted April 4, 2021 Author Share Posted April 4, 2021 (edited) Test script i am using. elt_QuestScanner.psc: Scriptname elt_QuestScanner extends Quest import PO3_SKSEfunctions ReferenceAlias Property playerRef Auto elt_Scanner Property Scanner Auto Event OnInit() RegisterForKey(62) RegisterForKey(64) RegisterForKey(65) RegisterForKey(66) Debug.Notification("F4 F6 F7 F8 keys Registered") endEvent ;PO3_SKSEfunctions.ObjectReference[] Function FindAllReferencesOfFormType(ObjectReference akRef, Int formType, float radius) Event OnKeyDown(Int KeyCode) if KeyCode == 62 Test62() elseif KeyCode == 64 Test64() elseif KeyCode == 65 Test65() elseif KeyCode == 66 Test66() endif EndEvent Function Test62() ObjectReference[] Scanned = FindAllReferencesOfFormType(PlayerRef.GetRef(), 43, 1000.0) dbgOutbput(Scanned) endFunction Function Test64() Actor[] Scanned = Scanner.Scan() dbgOutbput(Cast_ActorArray_to_ObjectReferenceArray(Scanned)) endFunction Function Test65() ObjectReference[] Scanned = FindAllReferencesOfType(PlayerRef.GetRef(), PlayerRef.GetRef(), 1000) dbgOutbput(Scanned) endFunction Function Test66() Actor[] Scanned = GetActorsByProcessingLevel(0) dbgOutbput(Cast_ActorArray_to_ObjectReferenceArray(Scanned)) endFunction ObjectReference[] Function Cast_ActorArray_to_ObjectReferenceArray(Actor[] aArr) ObjectReference[] retArr = PapyrusUtil.ObjRefArray(aArr.Length) int i while i < aArr.Length retArr[i] = aArr[i] as ObjectReference i+=1 endwhile return retArr endFunction Function dbgOutbput(ObjectReference[] dbgArr) Debug.Notification("Scanned length: " + dbgArr.Length) if dbgArr int i = 0 string dbgString while i < dbgArr.Length dbgString += dbgArr[i].GetBaseObject().GetName() + " " i+=1 endwhile Debug.Notification(dbgString) else ;empty Debug.Notification("Array empty") endif endFunction Test62 FindAllReferencesOfFormType works erratically detecting invisible things and sometimes nothing.Test64 using my spell scanner, works fine, but it does take 5 seconds (i could probably cut it down a bit).Test65 FindAllReferencesOfType doesn't ever return anything, clearly i am using it wrong and after a bit of testing i still don't have any idea how to make it return anything.Test66 GetActorsByProcessingLevel, it does work, returns 50 actors on average, with a bit of processing it coud easily be cut down to ~15, any idea how does it perform in comparison to spell? As for keywords search i didn't try it yet, i am not sure what keyword to look for, i could make a script cloak that adds/removes keywords but that couldn't possibly be performance friendly. Edit: As for MiscUtils ScanCellNPCs it only scans around you in current cell, works great for interiors but no so much for worldspace (square grid of cells).Edit2: somewhat more readable script Edited April 4, 2021 by xyz3lt Link to comment Share on other sites More sharing options...
dylbill Posted April 4, 2021 Share Posted April 4, 2021 Here is an explanation of processing level: https://geck.bethsoft.com/index.php?title=GetActorsByProcessingLevel 0 gets all loaded actors in the area. For FindAllReferencesOfType, it only finds object references of a specific form. So if you used: MiscObject Property Lockpick Auto Event OnInit() Form[] MyFormArray = PO3_SKSEfunctions.FindAllReferencesOfType(Game.GetPlayer(), Lockpick, 1000) EndEventIt would only find all of the lockpicks in 1000 units from the player. So if using an actorbase, it would only find that specific actor. Using a formlist instead there for me caused CTD. I would suggest using the keyword function. You can use ActorTypeNPC to get all humanoid actors. There's also a bunch of other ActorType keywords you can check for. Keyword Property ActorTypeNPC Auto Event OnInit() ObjectReference[] NPcs = PO3_SKSEfunctions.FindAllReferencesWithKeyword(Game.GetPlayer(), ActorTypeNPC, 1000, false) EndEventOr, if you're using Skyrim SE, you can maybe use SkyPal: https://www.nexusmods.com/skyrimspecialedition/mods/43925?tab=description which has a bunch of functions for getting and filtering object references. Link to comment Share on other sites More sharing options...
xyz3lt Posted April 4, 2021 Author Share Posted April 4, 2021 For now i'll try to improve SpellScanner, implement proper FindAllReferencesWithKeyword search keyword being ActorTypeNPC, implement skypal Grid() then filter it for actors then distance, and finally GetActorsByProcessingLevel with a bit of postprocessing, also using skypal filter for distance. Then test for performace impact of those 4 methods and that would be it. Thanks for all the help thats probably all i needed to know. Link to comment Share on other sites More sharing options...
dylbill Posted April 4, 2021 Share Posted April 4, 2021 No problem. Happy modding :) Link to comment Share on other sites More sharing options...
Recommended Posts