Okay, I see what you mean. Should I rather do a check on when a player moves using a for loop to see if there are enemies in his/her view and if there are, call the enemy' move proc?
mob
var
BossN
tmp/Boss

EnemyNPC
New()
. = ..()
spawn() move()

proc
move()
while(src)
var/Target

for(var/mob/P in oview(6))
if(Target) continue
if(P.client && !Frozen) Target = P
if(Target) step_towards(src, Target)
else
if(Boss || QuestNPC) SpAtt()
sleep(10)
sleep(5)

SpAtt()
for(var/mob/P in oview(7)) if(P.client && !target) Target = P

if(findtext(rank, "Mega") && target)
if(prob(25)) SpAttack1P()
else if(prob(50)) SpAttack2P()
else SpAttack1P()

Bump(mob/M)
if(ismob(M)) if(M.client) if(canattack) AttackP(M)



The way you programmed the AI is pretty bad :S You should reprogram the procs and simplify everything, I'm sure you could make something better.
In response to Raimo
Raimo wrote:
Okay, I see what you mean. Should I rather do a check on when a player moves using a for loop to see if there are enemies in his/her view and if there are, call the enemy' move proc?

Basically that's the idea, yep!

They can have a loop that runs then, of course (so they can be made to take steps toward the player, or attack if in range), but this loop should only be activated by the player "telling" it to.
In response to SuperSaiyanGokuX
SuperSaiyanGokuX wrote:
Basically that's the idea, yep!

They can have a loop that runs then, of course (so they can be made to take steps toward the player, or attack if in range), but this loop should only be activated by the player "telling" it to.

What do you think about Eternal's way on using a target var?
In response to Raimo
Raimo wrote:
SuperSaiyanGokuX wrote:
Basically that's the idea, yep!

They can have a loop that runs then, of course (so they can be made to take steps toward the player, or attack if in range), but this loop should only be activated by the player "telling" it to.

What do you think about Eternal's way on using a target var?

Well, now that it's been brought up, here's my point of view.

If you're going to the trouble of running a search for players in view, then sticking one of them into a variable (as your original code did with "player_found", and Eternal's is doing with "Target"), then you should be using that variable to its fullest. Why should the attack procedure run another check to find a target, when move() already did that?

I would use your move() proc to find and select a target, then pass that target along to the attack procs so they can use the target that was found.

Your move() proc should do the following:

if no Target
look for/find a Target
if Target was found
if within range of Target
Attack the Target
else
move towards Target


See the general idea in that plain English pseudo-code? Make the enemy select a Target, then have them go after or attack that Target, rather than have them look again to select a Target a second time in the attack proc.

You do this very simply by passing the Target (or player_found) through the call to the attack proc.
What way is more efficient, your first option or your second? Talking about CPU usage of DD itself.
Obviously, the fewer procedure calls/iterations, the better. So any system that reduces that number is more efficient.

However, these two concepts are not mutually exclusive! you can have it so that the move() loop is only running when it needs to be (when a player is nearby), and remove the second target check by finding a target once, and then passing it along.

Obviously, though, if you're activating their move() proc through player movement, you can probably just cut out the target search function entirely, and just pass that player into move() (and then on through to everything else.

One thing, though, is that you'd need to be careful that you're not activating the move() loop more than once at a time. Once the enemy has a target set, and is trying to chase/attack that target by their move() loop, then player steps should stop calling that enemy's move() proc.

One way to do that is to move the Target/player_found var into an actual var under the enemy mob type, so it will remain "permanent" (instead of being a temporary var within a proc). that way, you can check if the enemy has a target in your player's Move() proc, and only start the move() loop if they do not already have a target.

In fact, if you do this, then your move() loop can just use "while(target)" to run its course. Once there is a condition reached that the target is no longer the target (they moved out of range, or they were killed, basically), then you just set that var back to null, and the loop will stop.

This will also allow for on-the-fly target changes in a multiplayer environment. The enemy will go after whoever is their current target, and that can be changed if someone else wanders by.
Currently, I'm using a custom movement() proc to let players move. Anyway, I added a part in to check if enemies in the range of 7 tiles have found a player to attack, and if they already have, it breaks the for loop, but if they don't, it spawns their move() proc. Using that some mobs start attacking, other mobs keep standing in their place. Here is the movement() proc:
        movement()
if(usr.moving||usr.Frozen||usr.AFK)
return
src.moving++
while(1)
if(src.north)
if(!src.south)
if(src.east)
if(!src.west)
if(src.diagonals)
step(src,NORTHEAST)
else
step(src,NORTH)
else
if(src.west)
if(src.diagonals)
step(src,NORTHWEST)
else
step(src,NORTH)
else
if(src.south)
if(src.east)
if(!src.west)
if(src.diagonals)
step(src,SOUTHEAST)
else
step(src,SOUTH)
else
if(src.west)
if(src.diagonals)
step(src,SOUTHWEST)
else
step(src,SOUTH)
if(src.east)
if(!src.north&&!src.south&&!west)
step(src,EAST)
else
if(src.north&&src.south&&!west)
step(src,EAST)
else
if(src.west)
if(!src.north&&!src.south)
step(src,WEST)
else
if(src.north&&src.south)
step(src,WEST)

if(!src.north&&!src.south&&!src.east&&!src.west)
src.moving--
return
else
sleep(move_delay)
for(var/mob/EnemyNPC/E in ohearers(7,src))
if(E.player_found)
break
else
spawn() E.move()

And here is the current EnemyNPC AI:
mob
var
BossN
tmp/Boss=0
EnemyNPC
var
mob/player_found = null
proc
move()
if(!player_found)
for(var/mob/P in ohearers(7,src))
if(P.client && !src.Frozen)
player_found = P
break
if(src.Boss||src.QuestNPC)
src.SpAtt()
spawn(10)
move()
return //stop the regular loop
if(player_found)
step_towards(src,player_found)
if(get_dist(src,player_found)>7)
player_found = null
spawn(5)
move()

SpAtt()
src.target=player_found
switch(src.rank)
if("Mega","Mega Lv-2","Mega Lv-3")
random=rand(1,4)
if(random==1)
src.SpAttack1P()

if(random==2||random==3)
src.SpAttack2P()
else
src.SpAttack1P()
break //don't target everyone in range. just one.

Bump(mob/M)
if(ismob(M))
if(M.client && src.canattack)
src.AttackP(M)

Can anyone help me out please? Thanks in advance!
This is for the player.

mob/proc/Movement()
for()
. = 0
sleep(move_delay)
if(moving||Frozen||AFK)
continue
if(north) .+=1//NORTH
else if(south) .+=2//SOUTH
if(east) .+=4//EAST
else if(west) .+=8//WEST
if(.)
step(src,.)
moving++
for(var/mob/EnemyNPC/E in ohearers(7,src))
if(E.player_found)
continue
else
spawn()E.move()
else
moving--
I've used continue instead of break, now it works.
Sorry for the double post, but I managed to get DD's CPU usage from 45-50% to 2-3% with that. Thanks a lot guys, it really works great now.
Page: 1 2