ID:1979825
 
(See the best response by Lummox JR.)
Code:
mob
Enemies
var
aggro_dist = 7
keep_dist = 1
chase_speed = 2

turf/aggro_loc
turf/home_loc

next_attack = 0

//helper functions
proc
foundTarget(var/mob/player/c)
if(!src.target)
src.target = c
aggro_loc = src.loc
src.chaseState()

lostTarget()
var
rng = range(aggro_loc,aggro_dist)
mob/player/trg
mdist = aggro_dist+1
d

//search for combatants within range
for(var/mob/player/c in rng)
d = get_dist(src,c)
if(d<mdist||(d==mdist&&rand(1)))
mdist = d
trg = c

//if we found anything, chase, if not, reset
if(trg)
src.target = trg
chaseState()
else
resetState()

fight()
//You can put whatever you want in here.
//just make sure you set next_attack when you have made an attack.

next_attack = world.time + 10

//state functions
proc
chaseState()
set waitfor=0
var/d = get_dist(src,target)
while(d>keep_dist)
//if the target is out of range of dead, bail out.
if(get_dist(aggro_loc,src)>aggro_dist||src.target.health.current<=0)
src.lostTarget()
return 0
//if the path is blocked, take a random step
. = step(src,get_dir(src,target))
if(!.)
step_rand(src)
sleep(chase_speed)
d = get_dist(src,target)
attackState()
return 1

attackState()
set waitfor=0
var/d
while(src.target.health.current>0)
d = get_dist(src,target)

//if the target is too far away, chase
if(d>src.keep_dist)
chaseState()
return

//if the target is too close, avoid
if(d<src.keep_dist)
//if the path is blocked, take a random step
. = step_away(src,target)
if(!.)
step_rand(src)

//if we are eligible to attack, do it.
if(world.time>=next_attack) attack()

sleep(chase_speed)

//when the loop is done, we've lost the target
src.lostTarget()

resetState()
set waitfor=0
var
//allow us longer than it should take to get home via distance
returntime = world.time + get_dist(src,home_loc) * (3 + chase_speed)
while(world.time<returntime&&src.loc!=home_loc)
//if the path is blocked, take a random step
. = step(src,get_dir(src,home_loc))
if(!.)
step_rand(src)
sleep(chase_speed)

//teleport if we can't get home
if(src.loc!=home_loc)
src.loc = home_loc
src.target = null
src.aggro_loc = null
src.dir = 2



Problem description: Greetings, after tempering with the snippet, no matter how many tiny changes I make, I can't seem to solve this issue.

runtime error: Maximum recursion level reached (perhaps there is an infinite loop)
To avoid this safety check, set world.loop_checks=0.
proc name: lostTarget (/mob/Enemies/proc/lostTarget)
usr: Luchasi (/mob/player)
src: (83)Dummy (/mob/Enemies)
call stack:
(83)Dummy (/mob/Enemies): lostTarget()
(83)Dummy (/mob/Enemies): attackState()
(83)Dummy (/mob/Enemies): chaseState()

This usually occurs after one the following arguments return a false:
//from the chaseState()
if(get_dist(aggro_loc,src)>aggro_dist||src.target.health.current<=0)



In addition I would like to know:

-How can I allow the ai to continue chasing until the player exists its view.
For one thing, you should be using spawn() in all the places where the procs are calling each other. E.g., spawn(-1) chaseState() instead of just chaseState(). You're getting recursion in part because one proc calls another, which calls another, which calls another, and so on.

The critical problem in this code appears to be lostTarget(), which is looping through potential targets but does not check their health to see if they're valid. So your mob is detecting a target in lostTarget() that happens to be dead; that calls chaseState(). Because the distance appears to be within keep_dist, chaseState() is immediately calling attackState(). Then attackState() sees that your target is dead, and calls lostTarget().

The recursion likely wouldn't be much of an issue here if not for the fact that lostTarget() is allowing the infinite loop to happen. In fact I think that with setting waitfor=0 as you are, the recursion stops being an issue as soon as any of the procs sleeps. The problem is, the procs are never sleeping because lostTarget() is repeatedly picking a bad target.
In response to Lummox JR
Hello and thank you for respond. I really appreciate it.

            lostTarget()
set waitfor = 0
var
rng = range(aggro_loc,aggro_dist)
mob/player/trg
mdist = aggro_dist+1
d

//search for combatants within range
for(var/mob/player/c in rng)
d = get_dist(src,c)
if(d<mdist||(d==mdist&&rand(1)))
mdist = d
trg = c

//if we found anything, chase, if not, reset
if(trg && trg.health.current > 0)
src.target = trg
spawn(-1) chaseState()
else
spawn(-1) resetState()

fight()
//You can put whatever you want in here.
//just make sure you set next_attack when you have made an attack.

next_attack = world.time + 10


Not sure if that was the way to go, but by adding"if(trg && trg.health.current > 0)," the issue appears to be solved. I still have a few more questions.

-How can I allow the ai to continue chasing until the player exists its view. And if the player returns back to ais view, how can I stop it's return state and get the ai to chase the player once more.

I would assume it has to with the "aggro_dist" variable. It needs to be modified to something along the lines of "oview()".


~Regards
Best response
If you want them to keep chasing the player, I would suggest making another distance var, like chase_dist, that's higher than aggro_dist. In chaseState() you'd change all cases of aggro_dist to chase_dist.

BTW, the modification you made isn't quite correct. As it stands now, lostTarget() will bail out of the loop as soon as it finds any mob, dead or not, and if the mob is dead it'll go back to resetState(). That fixes the loop, but it's not the behavior you want. This is the change you'd need:

                //search for combatants within range
for(var/mob/player/c in rng)
// skip over dead mobs
if(c.health.current <= 0) continue
d = get_dist(src,c)
if(d<mdist || (d==mdist && rand(1)))
mdist = d
trg = c

//if we found anything, chase, if not, reset
if(trg)
src.target = trg
spawn(-1) chaseState()
else
spawn(-1) resetState()
Thank you so much Lummox JR.