ID:195021
 
//Title: Calculate the difference between two angles, keeping the correct sign
//Credit to: http://tinyurl.com/3kuumq2
//Contributed by: Kaiochao

/*
You might think that simply subtracting two angles would be good.
90-45=45, right? And 45-90 is -45. However, you might want 5-355 to be -10
instead of -350, and 355-5 to be 10 instead of 350. This code calculates the
smallest difference between two angles, while keeping the correct sign.

You're standing tall with both arms at your sides. You see an object in front of
you at a bearing of 300 degrees and point at it with your right arm. What do you
really do, though? Do you spin right 300 degrees and point, or do you simply
spin left by 30?

I came across the need for this code a long time ago, after playing a combat
flight simulator with heat-seeking missiles. I tried replicating this in a
simple top-down project where planes could rotate clockwise or counter-clockwise.
When I was programming the missile to follow planes, I didn't use this code, so
the missiles would often spin the wrong direction almost 360 degrees just to
adjust a few degrees the other way.

It really isn't that much code, and it was originally written in C#.
*/


proc/anglediff(firstAngle, secondAngle)
. = (secondAngle - firstAngle) % 360
if(. < -180) . += 360
if(. > 180) . -= 360

///*
//Testing Code/Sample Implementation:

// A pointer that points to a mob in its view.
// assuming we have an icon with 360 icon states for each angle
obj/pointer
icon = 'pointer.dmi'
var angle = 0, angle_to = 0
New() spawn
var mob/m, anglediff
for()
sleep(world.tick_lag)
m = locate()in view(src)

// arctan courtesy of Lummox JR, found in the Snippets Database
// "90-" because I prefer NORTH = 0 degrees and EAST = 90 degrees
// Get the angle from the pointer to the mob.
angle_to = 90 - arctan(m.x - x, m.y - y)

// Now, check how much the pointer has to turn to face the mob.
anglediff = anglediff(angle, angle_to)
if(abs(anglediff) > 5) // this pointer can rotate 5 degrees/tick
if(anglediff > 0) // 'angle' is clockwise from 'angle_to'
angle -= 5 // rotate counter-clockwise
else angle += 5
else angle = angle_to

// Now to normalize the angle. That is, keep it in 0-360
// (not including 360)
// Could be another snippet, but eh.
angle = round(angle, 1) % 360
while(angle < 0) angle += 360

icon_state = "[angle]"
//*/
Excellent. Maybe I should get around to uploading the fixes to jt_vectors, especially after that article someone wrote about the library.

I'd probably go ahead and normalise the angles modulo 360 for each, and convert the while loops to ifs, just so that someone can't deliberately feed bad values to the function to cause delays.
In response to Jtgibson
I updated the snippet with minor fixes.

It's also included in my own "handy procs library."
http://www.byond.com/developer/Kaiochao/ProcLib