mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 09:42:29 +00:00
Like seriously, don't use spaces in file names, this is a codebase, they're annoying. Hopefully Github will show these renames correctly.
181 lines
5.7 KiB
Plaintext
181 lines
5.7 KiB
Plaintext
/*
|
|
A Star pathfinding algorithm
|
|
Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of
|
|
windows along the route into account.
|
|
Use:
|
|
your_list = AStar(start location, end location, moving atom, distance proc, max nodes, maximum node depth, minimum distance to target, adjacent proc, atom id, turfs to exclude, check only simulated)
|
|
|
|
Optional extras to add on (in order):
|
|
Distance proc : the distance used in every A* calculation (length of path and heuristic)
|
|
MaxNodes: The maximum number of nodes the returned path can be (0 = infinite)
|
|
Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite)
|
|
Mintargetdist: Minimum distance to the target before path returns, could be used to get
|
|
near a target, but not right to it - for an AI mob with a gun, for example.
|
|
Adjacent proc : returns the turfs to consider around the actually processed node
|
|
Simulated only : whether to consider unsimulated turfs or not (used by some Adjacent proc)
|
|
|
|
Also added 'exclude' turf to avoid travelling over; defaults to null
|
|
|
|
Actual Adjacent procs :
|
|
|
|
/turf/proc/reachableAdjacentTurfs : returns reachable turfs in cardinal directions (uses simulated_only)
|
|
|
|
/turf/proc/reachableAdjacentAtmosTurfs : returns turfs in cardinal directions reachable via atmos
|
|
|
|
*/
|
|
|
|
//////////////////////
|
|
//PathNode object
|
|
//////////////////////
|
|
|
|
//A* nodes variables
|
|
/PathNode
|
|
var/turf/source //turf associated with the PathNode
|
|
var/PathNode/prevNode //link to the parent PathNode
|
|
var/f //A* Node weight (f = g + h)
|
|
var/g //A* movement cost variable
|
|
var/h //A* heuristic variable
|
|
var/nt //count the number of Nodes traversed
|
|
|
|
/PathNode/New(s,p,pg,ph,pnt)
|
|
source = s
|
|
prevNode = p
|
|
g = pg
|
|
h = ph
|
|
f = g + h
|
|
source.PNode = src
|
|
nt = pnt
|
|
|
|
/PathNode/proc/calc_f()
|
|
f = g + h
|
|
|
|
//////////////////////
|
|
//A* procs
|
|
//////////////////////
|
|
|
|
//the weighting function, used in the A* algorithm
|
|
/proc/PathWeightCompare(PathNode/a, PathNode/b)
|
|
return a.f - b.f
|
|
|
|
//reversed so that the Heap is a MinHeap rather than a MaxHeap
|
|
/proc/HeapPathWeightCompare(PathNode/a, PathNode/b)
|
|
return b.f - a.f
|
|
|
|
//wrapper that returns an empty list if A* failed to find a path
|
|
/proc/get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1)
|
|
var/list/path = AStar(caller, end, dist, maxnodes, maxnodedepth, mintargetdist, adjacent,id, exclude, simulated_only)
|
|
if(!path)
|
|
path = list()
|
|
return path
|
|
|
|
//the actual algorithm
|
|
/proc/AStar(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1)
|
|
|
|
//sanitation
|
|
var/start = get_turf(caller)
|
|
if(!start)
|
|
return 0
|
|
|
|
if(maxnodes)
|
|
//if start turf is farther than maxnodes from end turf, no need to do anything
|
|
if(call(start, dist)(end) > maxnodes)
|
|
return 0
|
|
maxnodedepth = maxnodes //no need to consider path longer than maxnodes
|
|
|
|
var/Heap/open = new /Heap(/proc/HeapPathWeightCompare) //the open list
|
|
var/list/closed = new() //the closed list
|
|
var/list/path = null //the returned path, if any
|
|
var/PathNode/cur //current processed turf
|
|
|
|
//initialization
|
|
open.Insert(new /PathNode(start,null,0,call(start,dist)(end),0))
|
|
|
|
//then run the main loop
|
|
while(!open.IsEmpty() && !path)
|
|
//get the lower f node on the open list
|
|
cur = open.Pop() //get the lower f turf in the open list
|
|
closed.Add(cur.source) //and tell we've processed it
|
|
|
|
//if we only want to get near the target, check if we're close enough
|
|
var/closeenough
|
|
if(mintargetdist)
|
|
closeenough = call(cur.source,dist)(end) <= mintargetdist
|
|
|
|
//if too many steps, abandon that path
|
|
if(maxnodedepth && (cur.nt > maxnodedepth))
|
|
continue
|
|
|
|
//found the target turf (or close enough), let's create the path to it
|
|
if(cur.source == end || closeenough)
|
|
path = new()
|
|
path.Add(cur.source)
|
|
|
|
while(cur.prevNode)
|
|
cur = cur.prevNode
|
|
path.Add(cur.source)
|
|
|
|
break
|
|
|
|
//get adjacents turfs using the adjacent proc, checking for access with id
|
|
var/list/L = call(cur.source,adjacent)(caller,id, simulated_only)
|
|
for(var/turf/T in L)
|
|
if(T == exclude || (T in closed))
|
|
continue
|
|
|
|
var/newg = cur.g + call(cur.source,dist)(T)
|
|
if(!T.PNode) //is not already in open list, so add it
|
|
open.Insert(new /PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1))
|
|
else //is already in open list, check if it's a better way from the current turf
|
|
if(newg < T.PNode.g)
|
|
T.PNode.prevNode = cur
|
|
T.PNode.g = newg
|
|
T.PNode.calc_f()
|
|
T.PNode.nt = cur.nt + 1
|
|
open.ReSort(T.PNode)//reorder the changed element in the list
|
|
CHECK_TICK
|
|
|
|
|
|
//cleaning after us
|
|
for(var/PathNode/PN in open.L)
|
|
PN.source.PNode = null
|
|
for(var/turf/T in closed)
|
|
T.PNode = null
|
|
|
|
//reverse the path to get it from start to finish
|
|
if(path)
|
|
for(var/i = 1; i <= path.len/2; i++)
|
|
path.Swap(i,path.len-i+1)
|
|
|
|
return path
|
|
|
|
//Returns adjacent turfs in cardinal directions that are reachable
|
|
//simulated_only controls whether only simulated turfs are considered or not
|
|
/turf/proc/reachableAdjacentTurfs(caller, ID, simulated_only)
|
|
var/list/L = new()
|
|
var/turf/T
|
|
|
|
for(var/dir in cardinal)
|
|
T = get_step(src,dir)
|
|
if(simulated_only && !istype(T))
|
|
continue
|
|
if(!T.density && !LinkBlockedWithAccess(T,caller, ID))
|
|
L.Add(T)
|
|
return L
|
|
|
|
//Returns adjacent turfs in cardinal directions that are reachable via atmos
|
|
/turf/proc/reachableAdjacentAtmosTurfs()
|
|
return atmos_adjacent_turfs
|
|
|
|
/turf/proc/LinkBlockedWithAccess(turf/T, caller, ID)
|
|
var/adir = get_dir(src, T)
|
|
var/rdir = get_dir(T, src)
|
|
|
|
for(var/obj/structure/window/W in src)
|
|
if(!W.CanAStarPass(ID, adir))
|
|
return 1
|
|
for(var/obj/O in T)
|
|
if(!O.CanAStarPass(ID, rdir, caller))
|
|
return 1
|
|
|
|
return 0
|