ID:257773
 
//Title: Get Closest Objects
//Credit to: DivineOpeanut
//Contributed by: DivineOpeanut

/*
A simple but flexible proc for finding the closest objects to a given center.
*/



proc
// ---------------------------------------------------------------------------------
// Args:
// center: an object on the map
// oftype: the object type searched for (/atom/movable by default)
// group: only objects in the group[] will be searched (world.contents by default)
// num_return: the amount of objects to return (1 by default)
// dist_method: the proc used to measure distance. if no proc is specified,
// get_dist() will be used.
//
// Returns:
// A list of objects (or object) closest to [center].
// ---------------------------------------------------------------------------------

get_closest_objects(center, oftype=/atom/movable, list/group=world.contents, num_return=1, dist_method)
var/closest_objects[] = list()

for (var/cycle in 1 to ((group.len < num_return) ? group.len : num_return))
// world << "Cycle [1]:"
var/closest_object
var/closest_dist

for (var/object in group)
if (oftype && !istype(object,oftype)) continue
if (object in closest_objects) continue
var/dist_to = (dist_method ? call(dist_method)(center,object) : get_dist(center,object))
// world << "[object] found in [object:x], [object:y], [object:z] with distance [dist_to]."
if(!closest_object || dist_to < closest_dist)
closest_object = object
closest_dist = dist_to
// world << "[object] is now closest_object."

if(!closest_object)
// world << "No closest_object found. Breaking loop and returning."
break

closest_objects += closest_object
// world << "Added closest_object to closest_objects. closest_objects is now of length [closest_objects.len]."

return closest_objects


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

world
maxx = 15
maxy = 15
maxz = 1

New()
. = ..()
Populate() // populate the world with objects

proc
Populate()
var/amount = 20
var/populate[] = list(/obj/red,/obj/green,/obj/blue)
for (var/obj in 1 to amount)
var/obj_type = pick(populate)
var/obj/new_obj = new obj_type()
do
var/coord_x = rand(1,world.maxx)
var/coord_y = rand(1,world.maxy)
var/coord_z = rand(1,world.maxz)
var/new_loc = locate(coord_x,coord_y,coord_z)
new_obj.Move(new_loc)
while(!new_obj.loc)

obj
density = 1

var/font_color

// marks the object for twenty ticks
proc/Mark()
var/init_text = "[initial(text)]"
text = "<font color=[font_color]>[text]</font>"
spawn(20) text = "[init_text]"

red
text = "R"
font_color = "red"

green
text = "G"
font_color = "green"

blue
text = "B"
font_color = "blue"

mob/verb
mark_objects()
var/center = usr
var/oftype = input(usr,"Which type of object?") as anything in list(/obj/red,/obj/green,/obj/blue,/obj)

// This is just one of the many things you can do with the "group" arg. If this variable is set
// to 1, 5, or 8, objects that are further than that number of tiles from [center] will not be \
// searched.

var/group[] = input(usr,"What maximum range?") as anything in list("1","5","8","any range")
switch(group)
if("1") group = orange(1,usr)
if("5") group = orange(5,usr)
if("8") group = orange(8,usr)
if("any range") group = world.contents - center

var/num_return = input(usr,"How many objects at most should be marked?") as num
var/dist_method = input(usr,"Which proc to measure distance?") as anything in list(/proc/normal_dist,/proc/random_dist)

var/objects[] = get_closest_objects(center,oftype,group,num_return,dist_method)
for (var/obj/mark in objects)
world << "object found at [mark.x], [mark.y], [mark.z]"
mark.Mark()

mark_objects_quick()
set desc = "Searches for objects of type /obj/red, in group setting \"any range,\" num_return 3 and dist_method /proc/normal_dist"

var/center = usr
var/oftype = /obj/red
var/group[] = world.contents - center
var/num_return = 3
var/dist_method = /proc/normal_dist

var/objects[] = get_closest_objects(center,oftype,group,num_return,dist_method)
for (var/obj/mark in objects)
world << "object found at [mark.x], [mark.y], [mark.z]"
mark.Mark()


proc/normal_dist(atom/A,atom/B)
var/dx = A.x - B.x
var/dy = A.y - B.y
return sqrt(dx*dx + dy*dy)

proc/random_dist(atom/A,atom/B)
return rand(1,sqrt(world.maxx*world.maxy))

//*/


Author's notes: Consider this snippet in "beta" status. I did not find much time to test it, so some bugs might not have been covered. Please report any problems to me, thanks!