ID:505662
 
(See the best response by DarkCampainger.)
Code:
        StartTurn()
for(var/mob/I in view())
if(I==usr) continue
TargetList.Add(I)
activetarget = pick(TargetList)
world << "Target is [activetarget]."
while(src.isturn)
step_to(src,activetarget,0,0)
var/list/Xblock = block(src.x+1,src.x-1)
var/list/Yblock = block(src.y+1,src.y-1)
var/list/WholeBlock = new()
WholeBlock.Add(Xblock&&Yblock)
world << "Stepped again. AP is [src.AP.]"
for(var/turf/T in WholeBlock)
for(var/atom/A in T.contents)
if(activetarget == A)
Hurt(activetarget)
src.AP -= 5
sleep(10)


Problem description:

Could any savvy human being enlighten me as to a neat way of accomplishing something? Effectively what I'm trying to do is;

Have a mob ( simple AI ) iterate through a loop, while(src.isturn) - this will return 1 continually until the user's action-points deplete, ending their turn by setting isturn to 0, and thus halting the loop.
This resolves at present, but slowly, as their action-points slowly deplete whilst doing nothing [ standing next to the mob I want them to be moving into and thus draining AP. ]

Each time this code loops, I have the mob take a single step towards the target; the target will always be a mob, and this mob will always be immobile whilst the 'AI' is moving.
If the AI finds itself next to a mob in one of the 4 cardinal directions, it should Move() in the direction of the mob [ and thus Bump() it. ]

The problem I'm having is that step_to, even with a min=0 argument on it, does not ever actually Move() onto the tile the target argument specifies, and thus cannot call Bump() for instance.
In my case, mob/Bump() calls a proc, Hurt() that deals damage, amongst other things.

What I've attempted to do, as I couldn't think of a way to actually accomplish the above, is get a list of the five turfs around and including the one the mob is standing on, and then check this list for the target and directly call Hurt() on it, decreasing the AP directly. ( action points, it's turn-based and moving depletes them; normally Move() covers this as Move() is called in order to Bump() and thus attack. )

The logic of this code is faulty; it returns a warning that:
    for(var/turf/T in WholeBlock)
Movement.dm:73:warning: : statement has no effect


I appreciate that outright asking someone to fix it is totally unhelpful from a learning point of view, so I'd rather - if possible - someone could explain why this logic is faulty and how I might resolve it?

I feel somewhat foolish, in that I'm pretty certain having an enemy move towards a target and then Bump() it to smack it right in the face is something even the most newly-introduced programmer can do in My First RPG.dm. =/
Best response
I'm a bit short on time, but I can note a few things for you:

I'm not sure why you're getting that warning, but your indentation is wonky, so I would check that first.

You're using usr in a process, which can lead to unexpected bugs in unusual circumstances. You likely want src there (also, note that view() defaults to using usr)

step_to() is designed to avoid obstacles, and will not bump them, even if they are the target. A simple solution is to use step_towards() when within one tile (as determined by get_dist()), and step_to() otherwise. With this fix, the rest of my notes are unnecessary, but I would still like to clear a few things up.

Unless you defined your own process called block(), you're passing the wrong arguments. It expects two turfs, not two numbers.

You cannot combine two lists with the && (AND) logical operator. You want the + (addition) operator.

Instead of looping through all of the turfs and their contents in a plus-shape, it would have been faster to check that the distance to the target is <= 1, and that the direction to the target is a cardinal direction. However, if you use the step_towards() trick I mentioned before, you can go back to using Bump() and not worry about this part.

There is one other important note, and that is that using step_towards() will allow your enemy to attack diagonally. If you want to prevent that, you can use this little bitwise math trick (courtesy of Lummox JR long, long ago) to make the AI step to a cardinal direction:

        StartTurn()
var/list/TargetList = list()

for(var/mob/I in oview(src))
// Because we're using oview(), the 'center' (which we passed as src) won't be included
TargetList.Add(I)

activetarget = pick(TargetList)
world << "Target is [activetarget]."
while(src.isturn)
// Check if we're within attack range
if(get_dist(src, activetarget) <= 1)
// We need to check that we're attacking from a cardinal direction only

var/d = get_dir(src, activetarget) // Get direction to target

// Check if we're standing diagonally from the target with bitwise math
if(d & d-1)
// We are diagonal, so we need to step aside to be cardinal

// We can step vertically or horizontally, so we figure out both potential step directions
var/stepDirVert = d & (NORTH|SOUTH) // Would we be stepping NORTH or SOUTH? (more bitwise math)
var/stepDirHor = d & (EAST|WEST) // Would we be stepping EAST or WEST? (more bitwise math)

// Try stepping vertically first
var/success = step(src, stepDirVert, 0) // Step vertically to line-up with target

if(!success)
// If we failed to step vertically (something blocking us)
// Try stepping horizontally
step(src, stepDirHor, 0)
else
// We're standing cardinally from them, use step_towards() to trigger bump() and attack
step_towards(src, activetarget, 0)
else
// Otherwise move towards them
step_to(src,activetarget,0,0)
world << "Stepped again. AP is [src.AP.]"

sleep(10)


I'm guessing you're not familiar with bitwise math, so if you're interested, this looks like a decent explanation of it. Bitwise operators are like the logical operators, but operate on each bit in a number's binary representation individually. Because BYOND's directions are actually bit flags (NORTH = 1 (0001), SOUTH = 2 (0010), EAST = 4 (0100), WEST = 8 (1000)) we can use bitwise math to quickly operate on them (as opposed to using a long chain of if()s).

For example, this line:
if(d & d-1)
Could be written as:
if(d==NORTHEAST || d==NORTHWEST || d==SOUTHEAST || d==SOUTHWEST)


and this:
var/stepDirVert = d & (NORTH|SOUTH)
Could be written as (using the ? (ternary) operator, which is basically and embedded if() statement):
var/stepDirVert = (d==NORTHEAST || d==NORTHWEST) ? NORTH : SOUTH


Honestly, now that I've written it all out, I might as well have just used the more straight-forward if() statements... but enough rambling. Gotta get back to finals.