ID:870512
 
(See the best response by Albro1.)
Code:
    Attack(var/mob/target in get_step(src, src.dir))
set category = "Skills"
if(bounds_dist(src, target) <= 16)
if(!Attacking && Chi >= 10)
Attacking = 1
flick("Attack", src)
var/hit = max(1, (50 * ((BP * Offense) / (target.BP * target.Defense))))
if(prob(hit))
var/Damage = max(1, round((BP * Strength) - (target.BP * (target.Durability/1.5))))
target.TakeDamage(Damage, src)
Spar(src, target)
else
flick("Dodge", target)
Chi -= 10
sleep(20/Speed) // Stopping consecutive attacks
Attacking = 0


Problem description:
This here is the Attack verb I wrote, all of the calculations work fine, but I changed it up not too long ago and things just keep going downhill.

My problem really is, I'm using pixel movement in my game and that allows multiple mobs to be on one tile. I'd like to have it attack the closest, and VISUALLY closest mob from it.

Like this, it doesn't even attack anything even though its selected.

Can anyone help me out or point me in the right direction with this?

Best response
You need to grab all the mobs in front of the user:
mob
proc/front(d) // d = distance in pixels
var x, y
if(dir & NORTH) // using bitflags makes accounting for diagonals much easier
y += d
if(dir & SOUTH)
y -= d
if(dir & EAST)
x += d
if(dir & WEST)
x -= d
return obounds(src, x, y)


And see which one is closest.
var/mob/m
for(var/mob/f in front(step_size)) // essentially the same as get_step
if(!m) m = f
if(bounds_dist(src, m) > bounds_dist(src, f)) m = f
// now attack m


Also, just a note for good practice: Don't use src in verbs unless it is necessary, which is not very often.
Thanks for the reply Albro.
There are a couple questions I have about what you posted, though.

What is dir & NORTH? and so on. Are you trying to check if the dir value is the same as NORTH? Im kinda unsure how that would work out.

Also, what is the purpose of var/mob/m? I can't really work out why that variable is needed.
dir & NORTH is a binary AND operation used for bitflags. Explained better here. Basically:
NORTH = 1
SOUTH = 2
EAST = 4
WEST = 8

NORTHEAST = 5
NORTHWEST = 9
SOUTHEAST = 6
SOUTHWEST = 10

Now, without confusing you too much:
dir = NORTH
if(dir & NORTH) // will return TRUE

dir = NORTHWEST
if(dir & NORTH) // will return TRUE
if(dir & WEST) // will return TRUE

dir = NORTH | WEST // same as NORTHWEST


So, by doing if(dir & NORTH), I'm effectively just checking to see if their direction involves NORTH at all.

As for var/mob/m - I'm using it in that example as a way to grab the closest mob to you. If loops through all mobs in front of you, and checks. If m isn't set to anything, then it sets it. If the distance between the current mob being looked at and src is shorter than the distance between m and src, it sets m to the current mob being looked at.

So, in the end, m is the closest mob to you.
That link isn't working for me, but I'll have to look into bitflags as that kinda condition is pretty foreign to me.

As far as m goes though;

m by default is null, so it will always be set to f.

How could the bounds between src,m and src,f be different if m is the same as f?
Links just aren't working for me tonight. It's fixed now.

As for the m thing:
// define m OUTSIDE of the loop.
var/mob/m
// now START the loop
for(var/mob/f in front(step_size))
// iteration start--

// this line is really only used on the first iteration
// since m starts off as null, we set it to f
if(!m) m = f

// now this line matters for every iteration except the first
if(bounds_dist(src, f) < bounds_dist(src, m))
// So if f (the mob we are looking at right now in this iteration) is closer than m (which is the mob it was set to LAST iteration), set it
m = f
// end iteration--


Basically m stores the closest mob. If the loop finds a mob that's closer than m, it sets m to that mob, then keeps looking. If it finds a mob even closer than the new m, it sets m to that mob. Once the loop ends, m will be set to the closest mob.
Hm... ok then. I tried it out, like this, I'll just post all the information regarding the changes.

Edit: Line 93 is where Find() uses obounds()

Find:
    Front(d)
var x, y
if(dir & NORTH)
y += d
if(dir & SOUTH)
y -= d
if(dir & EAST)
x += d
if(dir & WEST)
x -= d
return obounds(src, x, y)


Attack:
    Attack()
set category = "Skills"
var/mob/target
for(var/mob/f in Front(step_size))
if(!target)
target = f
if(bounds_dist(src, target) > bounds_dist(src, f))
target = f
if(!Attacking && Chi >= 10)
Attacking = 1
flick("Attack", src)
var/hit = max(1, (50 * ((BP * Offense) / (target.BP * target.Defense))))
if(prob(hit))
var/Damage = max(1, round((BP * Strength) - (target.BP * (target.Durability/1.5))))
target.TakeDamage(Damage, src)
Spar(src, target)
else
flick("Dodge", target)
Chi -= 10
sleep(20/Speed) // Stopping consecutive attacks
Attacking = 0



Runtime Error:
runtime error: Invalid bounds
proc name: Front (/mob/proc/Front)
source file: Procs.dm,93
usr: Kitsu (/mob/Player)
src: Kitsu (/mob/Player)
call stack:
Kitsu (/mob/Player): Front(16)
Kitsu (/mob/Player): Attack()
Did you do any debugging output? Try to see how far Front() is getting before the error occurs. You may also want to change var x,y to var x = 0, y = 0, just to give them a default numerical value.
Well, im checkin it out and I noticed something suspicious.
The proc is defining x and y, but x and y are also variables within the src of the proc, is it ok since they are local?

Also, initializing x and y fixed the problem seemingly.
It normally isn't the best to define a variable in a proc that is the same as the src's built in proc - simply for confusion's sake later. Technically though, think about it this way:
proc/whatever()
x = 2

whatever() is going to search for x in src since x isn't defined locally. However:
proc/whatever()
var/x
x = 2

whatever() finds the local x first, and defines it.

It's all about variable scoping. The procs start from within themselves and search outwards to their source.
Ahh, I thought that was the case here, lol. This is really working great though, except for the cases where if I was able to attack really fast, the mob wouldnt exist as I tried to calculate the damage, I just added in a couple of checks and its working just as intended;

Edit: Had to back up the sleep a level along with setting Attacking, it seemed to cause issues if the wait isn't activated every time Attack is used.

    Attack()
set category = "Skills"
var/mob/target
for(var/mob/f in Front(step_size))
if(!target)
target = f
if(bounds_dist(src, target) > bounds_dist(src, f))
target = f
if(!Attacking && Chi >= 10)
Attacking = 1
flick("Attack", src)
if(!target)
return
var/hit = max(1, (50 * ((BP * Offense) / (target.BP * target.Defense))))
if(prob(hit))
if(!target)
return
var/Damage = max(1, round((BP * Strength) - (target.BP * (target.Durability/1.5))))
target.TakeDamage(Damage, src)
Spar(src, target)
else
flick("Dodge", target)
Chi -= 10
sleep(20/Speed) // Stopping consecutive attacks
Attacking = 0
Good! I know there's quite a few responses, but please vote up the one you feel helped you the most!
Will do~
Done, lol.
Im running into a new issue I can't seem to figure out related to Attack now.. hell.
Time for a new topic, lol.

Thanks for your help Albro1.