Scripting - Getting closest 'Actor' to Player

Post » Wed Jun 20, 2012 8:28 pm

Hello all,
I have been wracking my brain trying to come up with a stable way to find the closest enemy to the player with a temporary spell and attached script on the player.
The spell lasts 2 mins and is a constant effect script type.
I have looked for hours on the boards, used code snippets from papyrus masters like Cipscis and RandomNoob and Redwood Elf, I've also play tested for about 10 times longer trying to create a decent script but the below is the best I could come up with and (by its nature of using a 'Random' function) although it does work most of the time there is a failure rate.

Scriptname LFBeginCombat extends ActiveMagicEffect{Script to run for Begin Combat instance.}Spell Property spThisSpell Auto		   ; This spell.Spell Property spSpellToCast Auto		  ; Reference of the spell too cast.Actor Property PlayerRef Auto		   ; Reference the Player, told this was faster than Game.GetPlayer() all the time.Actor TargetRef			   ; Reference for the Target.Actor CasterRef			   ; The casting reference point.Actor TargetRefStoreOne			 ; Just a hold reference for one of the actors found.Float flDistanceStore = 0.0			; Stored Target Reference distance to player.Float flDistanceTarget = 0.0		   ; Last Target Reference distance to player.Bool boUpdating = False			 ; Just to make sure it doesn't try to run initialisation twice.Int intCount = 0			  ; Number of times the while loop has run.Int intMaxSearch = 10			 ; Max number of times to run the while loop.Int intSearchRadius = 2000			; Search Radius to 2000 units.Bool boStopUpdate = False			; Bool to stop the updating.[/color]EVENT OnEffectStart(Actor akTarget, Actor akCaster)IF !boUpdating			  ; Make sure an Initialisation hasn't already started.  RegisterForSingleUpdate(2)		  ; Run this update once in three seconds time.  boUpdating = TrueELSE  Debug.Notification("Script tried to initialise twice!")   ; WTFENDIFEndEVENTEVENT OnUpdate()Debug.Trace("Update Started")IF !boStopUpdate && PlayerRef.IsInCombat()	   ; Really need to learn when to use STATES!IF TargetRef == None || TargetRef == PlayerRef || TargetRef.IsDead() || !TargetRef.IsInCombat()   FunctionFindNearestEnemy()		 ; My rubbish version of finding nearest enemy to player.   Debug.Trace("Last target was " + intCount + " " + TargetRef)ENDIFELSEIF boStopUpdate  PlayerRef.DispelSpell(spThisSpell)		; Dispell this spell if all else fails.  ReturnENDIFIF TargetRef != None && TargetRef != PlayerRef && !TargetRef.IsDead() && TargetRef.IsInCombat() && PlayerRef.HasLOS(TargetRef)  Debug.Notification("I've just seen my opponent!")  Debug.Trace("I saw my opponent! " + TargetRef + " " + intCount)  spSpellToCast.Cast(TargetRef, TargetRef); TargetRef.DoCombatSpellApply(spSpellToCast, TargetRef)   ; Same effect doesn't have any improvment over RemoteCast() or Cast().boStopUpdate = True  Utility.Wait(0.5)			; Wait just half a sec to dispell this spell effect.  PlayerRef.DispelSpell(spThisSpell)  ReturnENDIFIF PlayerRef.IsInCombat()  RegisterForSingleUpdate(0.5)		 ; Make updating faster while player is in combat.ELSE  RegisterForSingleUpdate(2)ENDIFEndEVENTFUNCTION FunctionFindNearestEnemy() ; Trying to find nearest actor to player who is in players line of sight and in combat with player.ObjectReference ObjPlayerRef = (PlayerRef As ObjectReference)  ; Setup an object reference for the player.Bool boKeepWhileing = True		   ; Is this really necessary too?TargetRef = Game.FindRandomActorFromRef(ObjPlayerRef, intSearchRadius)WHILE intCount < intMaxSearch && boKeepWhileingIF TargetRef == None || TargetRef == PlayerRef || TargetRef.IsDead() || !TargetRef.IsInCombat()   TargetRef = Game.FindRandomActorFromRef(ObjPlayerRef, intSearchRadius)   intCount += 1  ELSE   boKeepWhileing = False   Return  EndIF  IF TargetRefStoreOne != None && TargetRef != None && TargetRef != PlayerRef && TargetRefStoreOne != PlayerRef   flDistanceStore = TargetRefStoreOne.GetDistance(PlayerRef) ; Lots of checks to make sure the random actor wasn't the bloody Player!   flDistanceTarget = TargetRef.GetDistance(PlayerRef)IF flDistanceStore >= flDistanceTarget	TargetRefStoreOne = TargetRef	Debug.Trace("New TargetRef won " + flDistanceTarget + " units away " + TargetRef + ". StoreRef " + flDistanceStore + " units away")   ELSE	TargetRef = TargetRefStoreOne	Debug.Trace("Old Store Ref closer " + flDistanceStore + " units away " + TargetRefStoreOne + ". New TargetRef " + flDistanceTarget + " units away")   ENDIFELSEIF TargetRefStoreOne == None && TargetRef != PlayerRef   TargetRefStoreOne = TargetRef   Debug.Trace("Set the TargetRefStoreOne to the TargetRef as it was none")  ENDIFENDWHILE; Should I have a "Return" somewhere here for the closest enemy?Debug.Notification(" I didn't find a valid opponent in " + intMaxSearch + " searches")EndFUNCTIONSTATE Busy				; Empty state for when script is busy... I think


(Man the formatting on this is really bad when cut and paste from Notepad++ how does one get good tab indents and spaces? Arg edited like 6 times!)

One of the posts I saw by Borgurt1337 used a cloak type spell attaching scripts dynamically to actors around the player which sounded like a good idea but I had trouble getting certain other effects or spells to fire off the actor I chose to be the closes to the player. I am so surprised there is no GetClosestActorToActor() function. I want to later expand this to include ally's for my mod.

Any help or advice would be greatly appreciated.

Leon
User avatar
Eilidh Brian
 
Posts: 3504
Joined: Mon Jun 19, 2006 10:45 am

Post » Thu Jun 21, 2012 2:20 am

There is a find closest actor function. Why not UAE that instead of find random actor? Just use a while loop to keep trying until an actor that meets the conditions is found. Checking if it returns the player should be a condition too because it will return self if self is an actor
User avatar
Causon-Chambers
 
Posts: 3503
Joined: Sun Oct 15, 2006 11:47 pm

Post » Wed Jun 20, 2012 9:48 pm

@ Elseagoat: I tried those but findclosestactor won't help as the player moves so I can't just pick an arbitrary location, I can't use findclosestactor to ref because it always returns the player. Random only works because it might not always hit the player.
User avatar
rheanna bruining
 
Posts: 3415
Joined: Fri Dec 22, 2006 11:00 am

Post » Wed Jun 20, 2012 7:06 pm

I'm flattered you think I'm a papyrus master, but there are lots more people here better than me. :blush:

What are the problems you have with the way Borgut1337 used a cloak spell to attach scripts on actors? I thought it worked so great I even wrote a tutorial for it on the wiki.
User avatar
Auguste Bartholdi
 
Posts: 3521
Joined: Tue Jun 13, 2006 11:20 am

Post » Thu Jun 21, 2012 6:36 am

I'm not a Script master? http://i2.kym-cdn.com/entries/icons/original/000/003/617/okayguy.jpg :(
User avatar
Claire
 
Posts: 3329
Joined: Tue Oct 24, 2006 4:01 pm


Return to V - Skyrim