Auto Targeting

by Red Hall Dev
Super efficient automatic targeting
ID:1890627
 
Breif Description

Automatically target enemies on your screen with minimal possible CPU at maximum speed of target acquisition.

Mechanical Description

Rather than using the brute force approach of constantly scanning for enemies this will instead only update what target is selected when either you or another mob in your view changes location.

In the case of a mob being deleted it will remember who saw it, move to a null location and then tell all who had it targeted to update their target.

In the case of a mob being teleported you can simply call relocate() before it is teleported. Now it will do the same as being deleted. It will move to a null location until you are ready for it to be moved to the new location. When it is relocated it will let every mob that had it targeted know to update.
Last second update for release, during debugging I removed random walking. It's back in.
Just a quick proofread for everyone's benefit:

Implicit usr abuse in the oview():
updateTargetsForOthers()
for(var/mob/M in oview()) // should be oview(src) like you have everywhere else
M.updateTarget()

Minor changes (not important):
paintTarget()
if(target != null) del(target) // if(!target)
target = image('Icons.dmi',currentTarget,"Target",) // extra comma
src << target

Simple alternative (also not too important):
var/list/lastSeen = new/list()
for(var/mob/M in oview(src))
lastSeen += M

// equivalent:
var lastSeen[] = ohearers(src)
(although filtering lastSeen wasn't necessary in the first place, since the second for() loop already filters for mobs)

Don't forget the return value (this is actually important):
Move()
..() // should be ". = ..()"
updateTarget()
updateTargetsForOthers()


About the concept: You didn't really explain what this demo is showing, but I'm guessing it's to always display a targeting reticle on the enemy mob nearest the player.

In terms of efficiency, if many combatants (e.g. players and their targets) are moving around often, this can actually be a lot worse than a well-managed loop. For example, if just your player was moving constantly, it wouldn't be any different from a "brute force approach of constantly scanning". Add in enemy movements causing you to update potentially multiple times in the same frame and it's technically worse than if you were just checking periodically every few frames. With that in mind, and if auto-target is only active for short periods of time, I'd say a loop would be best, since the only times a player might activate auto-targeting are when there are (presumably moving) enemies nearby.
In response to Kaiochao
Implicit usr abuse in the oview():

As much as that sentence should not exist.. It's true. I'd like to do a feature request to change it's default to src but that's a lot of work.. And I already have a current feature request so I'll take it 1 at a time.

Minor changes (not important):

I agree, there's no reason not to and it will make the whole process more error tolerant.

Simple alternative (also not too important):
Cool idea, I hadn't thought of that. It's built in so almost certainly a faster way to do it.

Don't forget the return value (this is actually important):

Gud point. Thank you.

Loop is probably best

So there's a couple of things I would mention here.

My thoughts on the loop scenario
The longer the delay in the loop, the longer a wait the player has until their next target is acquired. If the delay is super small like world.tick_lag you can make it obviously unnoticeable but hugely wasteful of resources due to all of the false checks. Or.. You can set the delay high and introduce a lag to your target acquisition but reduce false checks.

Of course there is some reasonable middleground between these but for shooters which are usually fast paced I'm assuming developers would really like instant target acquisition and would like to minimize false checks, negating them completely being an ultimate goal.

I'm also going to add that the number of false hits increases by a factor of N for each player. In a game with 20 players there's a massive amount of false checks occurring which waste CPU.

My thoughts on the on-change scenario
In this case because I'm only updating when something moves I will never have a false check scenario. Also remember that enemies need to target players too (not in this demo but almost certainly in your game) and they can use the same process.

Even in the situation of 40 users all on the same screen really because you're not getting false checks you're basically always going to use less CPU than a loop unless that loop is extremely slow in which case you have laggy targeting which is not nice for your players.
In response to Kaiochao
Also, I'll add those edits in just a moment. Thanks for mentioning them.