Refined the implementation of the AStar algorithm :

*Reorganised and commented used procs for better visibility/maintenance
*Redid the PriorityQueue class
*Changed the euclidian distance to the byond one (so that we don't calculate float and square root when it's not necessary)
*Made the AStar algorithm use the closed list instead of just filling it
*Some optimization in the open list sorting : last entered is first in case of f ties, slightly less tiles are checked now
*Changes the order directions are checked when adding adjacents turfs to make for more 'realistic' path (read less 'drunk pathing' with lots of diagonals)
*Fixed some turfs densities not being check for diagonal movement
*Closed firedoors are now correctly seens as blocked by the algorithm
*Cleanbots now only moves in cardinal directions, like other bots.
*Updated the info text at the beginning of the file a bit
*Absolutely unrelated : simplified the turf.Bless() proc

Conflicts:
	code/__HELPERS/unsorted.dm
	code/defines/procs/AStar.dm
	code/game/machinery/bots/bots.dm
	code/game/machinery/bots/cleanbot.dm
	code/game/machinery/doors/firedoor.dm
	code/game/machinery/doors/windowdoor.dm
	code/game/turfs/turf.dm
This commit is contained in:
Menshin
2014-07-13 15:50:15 -04:00
committed by ZomgPonies
parent 0d3e45ac8b
commit bd6b5a6870
12 changed files with 396 additions and 310 deletions
+77 -53
View File
@@ -150,25 +150,82 @@ Turf and target are seperate in case you want to teleport some distance from a t
return destination
///////////////////
//A* helpers procs
///////////////////
// Returns true if a link between A and B is blocked
// Movement through doors allowed if ID has access
/proc/LinkBlockedWithAccess(turf/A, turf/B, obj/item/weapon/card/id/ID)
/proc/LinkBlocked(turf/A, turf/B)
if(A == null || B == null) return 1
var/adir = get_dir(A,B)
var/rdir = get_dir(B,A)
if((adir & (NORTH|SOUTH)) && (adir & (EAST|WEST))) // diagonal
var/iStep = get_step(A,adir&(NORTH|SOUTH))
if(!LinkBlocked(A,iStep) && !LinkBlocked(iStep,B)) return 0
if(adir & (adir-1)) // diagonal
var/turf/iStep = get_step(A,adir&(NORTH|SOUTH))
if(!iStep.density && !LinkBlockedWithAccess(A,iStep, ID) && !LinkBlockedWithAccess(iStep,B,ID))
return 0
var/turf/pStep = get_step(A,adir&(EAST|WEST))
if(!pStep.density && !LinkBlockedWithAccess(A,pStep,ID) && !LinkBlockedWithAccess(pStep,B,ID))
return 0
return 1
if(DirBlockedWithAccess(A,adir, ID))
return 1
if(DirBlockedWithAccess(B,rdir, ID))
return 1
for(var/obj/O in B)
if(O.density && !istype(O, /obj/machinery/door) && !(O.flags & ON_BORDER))
return 1
return 0
// Returns true if direction is blocked from loc
// Checks doors against access with given ID
/proc/DirBlockedWithAccess(turf/loc,var/dir,var/obj/item/weapon/card/id/ID)
for(var/obj/structure/window/D in loc)
if(!D.density) continue
if(D.dir == SOUTHWEST) return 1 //full-tile window
if(D.dir == dir) return 1 //matching border window
for(var/obj/machinery/door/D in loc)
if(!D.CanAStarPass(ID,dir))
return 1
return 0
// Returns true if a link between A and B is blocked
// Movement through doors allowed if door is open
/proc/LinkBlocked(turf/A, turf/B)
if(A == null || B == null)
return 1
var/adir = get_dir(A,B)
var/rdir = get_dir(B,A)
if(adir & (adir-1)) //diagonal
var/turf/iStep = get_step(A,adir & (NORTH|SOUTH)) //check the north/south component
if(!iStep.density && !LinkBlocked(A,iStep) && !LinkBlocked(iStep,B))
return 0
var/turf/pStep = get_step(A,adir & (EAST|WEST)) //check the east/west component
if(!pStep.density && !LinkBlocked(A,pStep) && !LinkBlocked(pStep,B))
return 0
var/pStep = get_step(A,adir&(EAST|WEST))
if(!LinkBlocked(A,pStep) && !LinkBlocked(pStep,B)) return 0
return 1
if(DirBlocked(A,adir)) return 1
if(DirBlocked(B,rdir)) return 1
for(var/obj/O in B)
if(O.density && !istype(O, /obj/machinery/door) && !(O.flags & ON_BORDER))
return 1
return 0
// Returns true if direction is blocked from loc
// Checks if doors are open
/proc/DirBlocked(turf/loc,var/dir)
for(var/obj/structure/window/D in loc)
if(!D.density) continue
@@ -176,18 +233,12 @@ Turf and target are seperate in case you want to teleport some distance from a t
if(D.dir == dir) return 1
for(var/obj/machinery/door/D in loc)
if(!D.density) continue
if(istype(D, /obj/machinery/door/window))
if((dir & SOUTH) && (D.dir & (EAST|WEST))) return 1
if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return 1
else return 1 // it's a real, air blocking door
if(!D.density)//if the door is open
continue
else return 1 // if closed, it's a real, air blocking door
return 0
/proc/TurfBlockedNonWindow(turf/loc)
for(var/obj/O in loc)
if(O.density && !istype(O, /obj/structure/window))
return 1
return 0
/////////////////////////////////////////////////////////////////////////
/proc/sign(x)
return x!=0?x/abs(x):0
@@ -1555,41 +1606,6 @@ proc/check_target_facings(mob/living/initator, mob/living/target)
return 3
/proc/texttospeechstrip(var/t_in)
var/t_out = ""
for(var/i=1, i<=length(t_in), i++)
var/ascii_char = text2ascii(t_in,i)
switch(ascii_char)
// A .. Z
if(65 to 90) //Uppercase Letters
if(lentext(t_out) <= 150)
t_out += ascii2text(ascii_char)
// a .. z
if(97 to 122) //Lowercase Letters
if(lentext(t_out) <= 150)
t_out += ascii2text(ascii_char)
// 0 .. 9
if(48 to 57) //Numbers
if(lentext(t_out) <= 150)
t_out += ascii2text(ascii_char)
// ` , - . ! ? : '
if(39,44,45,46,33,63,58,96,60,62) //Common name punctuation
if(lentext(t_out) <= 150)
t_out += ascii2text(ascii_char)
//Space
if(32)
if(lentext(t_out) <= 150)
t_out += ascii2text(ascii_char)
return t_out
/var/lastspeak = ""
/mob/proc/texttospeech(var/text, var/speed, var/pitch, var/accent, var/voice, var/echo)
text = texttospeechstrip(text)
lastspeak = text
ext_python("voice.py", "\"[accent]\" \"[voice]\" \"[pitch]\" \"[echo]\" \"[speed]\" \"[text]\" \"[src.ckey]\"")
atom/proc/GetTypeInAllContents(typepath)
var/list/processing_list = list(src)
var/list/processed = list()
@@ -1609,4 +1625,12 @@ atom/proc/GetTypeInAllContents(typepath)
processed |= A
return found
return found
/proc/random_step(atom/movable/AM, steps, chance)
var/initial_chance = chance
while(steps > 0)
if(prob(chance))
step(AM, pick(alldirs))
chance = max(chance - (initial_chance / steps), 0)
steps--
+189 -136
View File
@@ -1,5 +1,3 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
/*
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
@@ -14,10 +12,6 @@ So an example use might be:
src.path_list = AStar(src.loc, target.loc, /turf/proc/AdjacentTurfs, /turf/proc/Distance)
Note: The path is returned starting at the END node, so i wrote reverselist to reverse it for ease of use.
src.path_list = reverselist(src.pathlist)
Then to start on the path, all you need to do it:
Step_to(src, src.path_list[1])
src.path_list -= src.path_list[1] or equivilent to remove that node from the list.
@@ -36,150 +30,209 @@ length to avoid portals or something i guess?? Not that they're counted right no
// Also added 'exclude' turf to avoid travelling over; defaults to null
//Currently, there's four main ways to call AStar
//
// 1) adjacent = "/turf/proc/AdjacentTurfsWithAccess" and distance = "/turf/proc/Distance"
// Seeks a path moving in all directions (including diagonal) and checking for the correct id to get through doors
//
// 2) adjacent = "/turf/proc/CardinalTurfsWithAccess" and distance = "/turf/proc/Distance_cardinal"
// Seeks a path moving only in cardinal directions and checking if for the correct id to get through doors
// Used by most bots, including Beepsky
//
// 3) adjacent = "/turf/proc/AdjacentTurfs" and distance = "/turf/proc/Distance"
// Same as 1), but don't check for ID. Can get only get through open doors
//
// 4) adjacent = "/turf/proc/AdjacentTurfsSpace" and distance = "/turf/proc/Distance"
// Same as 1), but check all turf, including unsimulated
PriorityQueue
var/L[]
var/cmp
New(compare)
L = new()
cmp = compare
proc
IsEmpty()
return !L.len
Enqueue(d)
var/i
var/j
L.Add(d)
i = L.len
j = i>>1
while(i > 1 && call(cmp)(L[j],L[i]) > 0)
L.Swap(i,j)
i = j
j >>= 1
//////////////////////
//PriorityQueue object
//////////////////////
Dequeue()
if(!L.len) return 0
. = L[1]
Remove(1)
//an ordered list, using the cmp proc to weight the list elements
/PriorityQueue
var/list/L //the actual queue
var/cmp //the weight function used to order the queue
Remove(i)
if(i > L.len) return 0
L.Swap(i,L.len)
L.Cut(L.len)
if(i < L.len)
_Fix(i)
_Fix(i)
var/child = i + i
var/item = L[i]
while(child <= L.len)
if(child + 1 <= L.len && call(cmp)(L[child],L[child + 1]) > 0)
child++
if(call(cmp)(item,L[child]) > 0)
L[i] = L[child]
i = child
else
break
child = i + i
L[i] = item
List()
var/ret[] = new()
var/copy = L.Copy()
while(!IsEmpty())
ret.Add(Dequeue())
L = copy
return ret
RemoveItem(i)
var/ind = L.Find(i)
if(ind)
Remove(ind)
PathNode
var/datum/source
var/PathNode/prevNode
var/f
var/g
var/h
var/nt // Nodes traversed
New(s,p,pg,ph,pnt)
source = s
prevNode = p
g = pg
h = ph
f = g + h
source.bestF = f
nt = pnt
/PriorityQueue/New(compare)
L = new()
cmp = compare
datum
var/bestF
proc
PathWeightCompare(PathNode/a, PathNode/b)
return a.f - b.f
/PriorityQueue/proc/IsEmpty()
return !L.len
AStar(start,end,adjacent,dist,maxnodes,maxnodedepth = 30,mintargetdist,minnodedist,id=null, var/turf/exclude=null)
//add an element in the list,
//immediatly ordering it to its position using Insertion sort
/PriorityQueue/proc/Enqueue(var/atom/A)
var/i
L.Add(A)
i = L.len -1
while(i > 0 && call(cmp)(L[i],A) >= 0) //place the element at it's right position using the compare proc
L.Swap(i,i+1) //last inserted element being first in case of ties (optimization)
i--
// world << "A*: [start] [end] [adjacent] [dist] [maxnodes] [maxnodedepth] [mintargetdist], [minnodedist] [id]"
var/PriorityQueue/open = new /PriorityQueue(/proc/PathWeightCompare)
var/closed[] = new()
var/path[]
start = get_turf(start)
if(!start) return 0
//removes and returns the first element in the queue
/PriorityQueue/proc/Dequeue()
if(!L.len)
return 0
. = L[1]
Remove(.)
return .
open.Enqueue(new /PathNode(start,null,0,call(start,dist)(end)))
//removes an element
/PriorityQueue/proc/Remove(var/atom/A)
return L.Remove(A)
while(!open.IsEmpty() && !path)
{
var/PathNode/cur = open.Dequeue()
closed.Add(cur.source)
//returns a copy of the elements list
/PriorityQueue/proc/List()
var/list/ret = L.Copy()
return ret
var/closeenough
if(mintargetdist)
closeenough = call(cur.source,dist)(end) <= mintargetdist
//return the position of an element or 0 if not found
/PriorityQueue/proc/Seek(var/atom/A)
return L.Find(A)
if(cur.source == end || closeenough)
path = new()
//return the element at the i_th position
/PriorityQueue/proc/Get(var/i)
if(i > L.len || i < 1)
return 0
return L[i]
//replace the passed element at it's right position using the cmp proc
/PriorityQueue/proc/ReSort(var/atom/A)
var/i = Seek(A)
if(i == 0)
return
while(i < L.len && call(cmp)(L[i],L[i+1]) > 0)
L.Swap(i,i+1)
i++
while(i > 1 && call(cmp)(L[i],L[i-1]) <= 0) //last inserted element being first in case of ties (optimization)
L.Swap(i,i-1)
i--
//////////////////////
//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
//search if there's a PathNode that points to turf T in the Priority Queue
proc/SeekTurf(var/PriorityQueue/Queue, var/turf/T)
var/i = 1
var/PathNode/PN
while(i < Queue.L.len + 1)
PN = Queue.L[i]
if(PN.source == T)
return i
i++
return 0
//the actual algorithm
proc/AStar(start,end,adjacent,dist,maxnodes,maxnodedepth = 30,mintargetdist,minnodedist,id=null, var/turf/exclude=null)
var/PriorityQueue/open = new /PriorityQueue(/proc/PathWeightCompare) //the open list, ordered using the PathWeightCompare proc, from lower f to higher
var/list/closed = new() //the closed list
var/list/path = null //the returned path, if any
var/PathNode/cur //current processed turf
//sanitation
start = get_turf(start)
if(!start)
return 0
//initialization
open.Enqueue(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.Dequeue() //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)
while(cur.prevNode)
cur = cur.prevNode
path.Add(cur.source)
break
break
var/L[] = call(cur.source,adjacent)(id)
if(minnodedist && maxnodedepth)
if(call(cur.source,minnodedist)(end) + cur.nt >= maxnodedepth)
continue
else if(maxnodedepth)
if(cur.nt >= maxnodedepth)
continue
//IMPLEMENTATION TO FINISH
//do we really need this minnodedist ???
/*if(minnodedist && maxnodedepth)
if(call(cur.source,minnodedist)(end) + cur.nt >= maxnodedepth)
continue
*/
for(var/datum/d in L)
if(d == exclude)
continue
var/ng = cur.g + call(cur.source,dist)(d)
if(d.bestF)
if(ng + call(d,dist)(end) < d.bestF)
for(var/i = 1; i <= open.L.len; i++)
var/PathNode/n = open.L[i]
if(n.source == d)
open.Remove(i)
break
else
continue
//get adjacents turfs using the adjacent proc, checking for access with id
var/list/L = call(cur.source,adjacent)(id,closed)
open.Enqueue(new /PathNode(d,cur,ng,call(d,dist)(end),cur.nt+1))
if(maxnodes && open.L.len > maxnodes)
open.L.Cut(open.L.len)
}
for(var/turf/T in L)
if(T == exclude)
continue
var/PathNode/temp
while(!open.IsEmpty())
temp = open.Dequeue()
temp.source.bestF = 0
while(closed.len)
temp = closed[closed.len]
temp.bestF = 0
closed.Cut(closed.len)
var/newg = cur.g + call(cur.source,dist)(T)
if(!T.PNode) //is not already in open list, so add it
open.Enqueue(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()
open.ReSort(T.PNode)//reorder the changed element in the list
if(path)
for(var/i = 1; i <= path.len/2; i++)
path.Swap(i,path.len-i+1)
}
return path
//cleaning after us
for(var/PathNode/PN in open.L)
PN.source.PNode = null
for(var/turf/T in closed)
T.PNode = null
//if the path is longer than maxnodes, then don't return it
if(path && maxnodes && path.len > (maxnodes + 1))
return 0
//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
+6 -65
View File
@@ -157,69 +157,10 @@
src.add_hiddenprint(user)
src.attack_hand(user)
/******************************************************************/
// Navigation procs
// Used for A-star pathfinding
/obj/machinery/bot/proc/speak(var/message)
if((!src.on) || (!message))
return
for(var/mob/O in hearers(src, null))
O.show_message("<span class='game say'><span class='name'>[src]</span> beeps, \"[message]\"</span>",2)
return
// Returns the surrounding cardinal turfs with open links
// Including through doors openable with the ID
/turf/proc/CardinalTurfsWithAccess(var/obj/item/weapon/card/id/ID)
var/L[] = new()
// for(var/turf/simulated/t in oview(src,1))
for(var/d in cardinal)
var/turf/simulated/T = get_step(src, d)
if(istype(T) && !T.density)
if(!LinkBlockedWithAccess(src, T, ID))
L.Add(T)
return L
// Returns true if a link between A and B is blocked
// Movement through doors allowed if ID has access
/proc/LinkBlockedWithAccess(turf/A, turf/B, obj/item/weapon/card/id/ID)
if(A == null || B == null) return 1
var/adir = get_dir(A,B)
var/rdir = get_dir(B,A)
if((adir & (NORTH|SOUTH)) && (adir & (EAST|WEST))) // diagonal
var/iStep = get_step(A,adir&(NORTH|SOUTH))
if(!LinkBlockedWithAccess(A,iStep, ID) && !LinkBlockedWithAccess(iStep,B,ID))
return 0
var/pStep = get_step(A,adir&(EAST|WEST))
if(!LinkBlockedWithAccess(A,pStep,ID) && !LinkBlockedWithAccess(pStep,B,ID))
return 0
return 1
if(DirBlockedWithAccess(A,adir, ID))
return 1
if(DirBlockedWithAccess(B,rdir, ID))
return 1
for(var/obj/O in B)
if(O.density && !istype(O, /obj/machinery/door) && !(O.flags & ON_BORDER))
return 1
return 0
// Returns true if direction is blocked from loc
// Checks doors against access with given ID
/proc/DirBlockedWithAccess(turf/loc,var/dir,var/obj/item/weapon/card/id/ID)
for(var/obj/structure/window/D in loc)
if(!D.density) continue
if(D.is_fulltile()) return 1
if(D.dir == dir) return 1
for(var/obj/machinery/door/D in loc)
if(!D.density) continue
if(istype(D, /obj/machinery/door/window))
if( dir & D.dir ) return !D.check_access(ID)
//if((dir & SOUTH) && (D.dir & (EAST|WEST))) return !D.check_access(ID)
//if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return !D.check_access(ID)
else return !D.check_access(ID) // it's a real, air blocking door
return 0
+3 -2
View File
@@ -237,8 +237,9 @@ text("<A href='?src=\ref[src];operation=oddbutton'>[src.oddbutton ? "Yes" : "No"
if(target && path.len == 0)
spawn(0)
if(!src || !target) return
src.path = AStar(src.loc, src.target.loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id=botcard)
if (!path) path = list()
src.path = AStar(src.loc, src.target.loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 30)
if(!src.path)
src.path = list()
if(src.path.len == 0)
src.oldtarget = src.target
target.targeted_by = null
-4
View File
@@ -709,10 +709,6 @@ Auto Patrol: []"},
M:loc = T
*/
/obj/machinery/bot/ed209/proc/speak(var/message)
for(var/mob/O in hearers(src, null))
O.show_message("<span class='game say'><span class='name'>[src]</span> beeps, \"[message]\"",2)
return
/obj/machinery/bot/ed209/explode()
walk_to(src,0)
-4
View File
@@ -120,10 +120,6 @@ var/global/list/floorbot_targets=list()
return
/obj/machinery/bot/floorbot/proc/speak(var/message)
for(var/mob/O in hearers(src, null))
O.show_message("<span class='game say'><span class='name'>[src]</span> beeps, \"[message]\"",2)
return
/obj/machinery/bot/floorbot/attackby(var/obj/item/W , mob/user as mob)
if(istype(W, /obj/item/stack/tile/plasteel))
-5
View File
@@ -446,11 +446,6 @@
return
/obj/machinery/bot/medbot/proc/speak(var/message)
if((!src.on) || (!message))
return
visible_message("[src] beeps, \"[message]\"")
return
/obj/machinery/bot/medbot/bullet_act(var/obj/item/projectile/Proj)
if(Proj.flag == "taser")
+11 -17
View File
@@ -83,17 +83,16 @@
/obj/machinery/bot/secbot
New()
..()
src.icon_state = "secbot[src.on]"
spawn(3)
src.botcard = new /obj/item/weapon/card/id(src)
var/datum/job/detective/J = new/datum/job/detective
src.botcard.access = J.get_access()
if(radio_controller)
radio_controller.add_object(src, control_freq, filter = RADIO_SECBOT)
radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS)
/obj/machinery/bot/secbot/New()
..()
src.icon_state = "secbot[src.on]"
spawn(3)
src.botcard = new /obj/item/weapon/card/id(src)
var/datum/job/detective/J = new /datum/job/detective
src.botcard.access = J.get_access()
if(radio_controller)
radio_controller.add_object(src, control_freq, filter = RADIO_SECBOT)
radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS)
/obj/machinery/bot/secbot/turn_on()
@@ -252,7 +251,7 @@ Auto Patrol: []"},
src.target = null
src.last_found = world.time
src.frustration = 0
src.mode = 0
src.mode = SECBOT_IDLE
walk_to(src,0)
if(target) // make sure target exists
@@ -394,7 +393,6 @@ Auto Patrol: []"},
// perform a single patrol step
/obj/machinery/bot/secbot/proc/patrol_step()
if(loc == patrol_target) // reached target
@@ -667,10 +665,6 @@ Auto Patrol: []"},
M:loc = T
*/
/obj/machinery/bot/secbot/proc/speak(var/message)
for(var/mob/O in hearers(src, null))
O.show_message("<span class='game say'><span class='name'>[src]</span> beeps, \"[message]\"",2)
return
/obj/machinery/bot/secbot/explode()
+4
View File
@@ -99,6 +99,10 @@
return !density
//used in the AStar algorithm to determinate if the turf the door is on is passable
/obj/machinery/door/proc/CanAStarPass(var/obj/item/weapon/card/id/ID)
return !density || check_access(ID)
/obj/machinery/door/proc/bumpopen(mob/user as mob)
if(operating) return
if(user.last_airflow > world.time - vsc.airflow_delay) //Fakkit
+4
View File
@@ -276,3 +276,7 @@
/obj/machinery/door/firedoor/multi_tile/triple
icon = 'icons/obj/doors/DoorHazard3x1.dmi'
width = 3
//used in the AStar algorithm to determinate if the turf the door is on is passable
/obj/machinery/door/firedoor/CanAStarPass()
return !density
+5
View File
@@ -74,6 +74,11 @@
else
return 1
//used in the AStar algorithm to determinate if the turf the door is on is passable
/obj/machinery/door/window/CanAStarPass(var/obj/item/weapon/card/id/ID, var/to_dir)
return !density || (dir != to_dir) || check_access(ID)
/obj/machinery/door/window/CheckExit(atom/movable/mover as mob|obj, turf/target as turf)
if(istype(mover) && mover.checkpass(PASSGLASS))
return 1
+97 -24
View File
@@ -26,6 +26,8 @@
var/has_resources
var/list/resources
var/PathNode/PNode = null //associated PathNode in the A* algorithm
/turf/New()
..()
for(var/atom/movable/AM as mob|obj in src)
@@ -336,31 +338,102 @@
return
flags |= NOJAUNT
/turf/proc/AdjacentTurfs()
var/L[] = new()
for(var/turf/simulated/t in oview(src,1))
if(!t.density)
if(!LinkBlocked(src, t) && !TurfBlockedNonWindow(t))
L.Add(t)
return L
/turf/proc/Distance(turf/t)
if(get_dist(src,t) == 1)
var/cost = (src.x - t.x) * (src.x - t.x) + (src.y - t.y) * (src.y - t.y)
cost *= (pathweight+t.pathweight)/2
return cost
else
return get_dist(src,t)
/turf/proc/AdjacentTurfsSpace()
var/L[] = new()
for(var/turf/t in oview(src,1))
if(!t.density)
if(!LinkBlocked(src, t) && !TurfBlockedNonWindow(t))
L.Add(t)
/////////////////////////////////////////////////////////////////////////
// Navigation procs
// Used for A-star pathfinding
////////////////////////////////////////////////////////////////////////
///////////////////////////
//Cardinal only movements
///////////////////////////
// Returns the surrounding cardinal turfs with open links
// Including through doors openable with the ID
/turf/proc/CardinalTurfsWithAccess(var/obj/item/weapon/card/id/ID)
var/list/L = new()
var/turf/simulated/T
for(var/dir in cardinal)
T = get_step(src, dir)
if(istype(T) && !T.density)
if(!LinkBlockedWithAccess(src, T, ID))
L.Add(T)
return L
// This Distance proc assumes that only cardinal movement is
// Returns the surrounding cardinal turfs with open links
// Don't check for ID, doors passable only if open
/turf/proc/CardinalTurfs()
var/list/L = new()
var/turf/simulated/T
for(var/dir in cardinal)
T = get_step(src, dir)
if(istype(T) && !T.density)
if(!LinkBlocked(src, T))
L.Add(T)
return L
///////////////////////////
//All directions movements
///////////////////////////
// Returns the surrounding simulated turfs with open links
// Including through doors openable with the ID
/turf/proc/AdjacentTurfsWithAccess(var/obj/item/weapon/card/id/ID = null,var/list/closed)//check access if one is passed
var/list/L = new()
var/turf/simulated/T
for(var/dir in list(NORTHWEST,NORTHEAST,SOUTHEAST,SOUTHWEST,NORTH,EAST,SOUTH,WEST)) //arbitrarily ordered list to favor non-diagonal moves in case of ties
T = get_step(src,dir)
if(T in closed) //turf already proceeded in A*
continue
if(istype(T) && !T.density)
if(!LinkBlockedWithAccess(src, T, ID))
L.Add(T)
return L
//Idem, but don't check for ID and goes through open doors
/turf/proc/AdjacentTurfs(var/list/closed)
var/list/L = new()
var/turf/simulated/T
for(var/dir in list(NORTHWEST,NORTHEAST,SOUTHEAST,SOUTHWEST,NORTH,EAST,SOUTH,WEST)) //arbitrarily ordered list to favor non-diagonal moves in case of ties
T = get_step(src,dir)
if(T in closed) //turf already proceeded by A*
continue
if(istype(T) && !T.density)
if(!LinkBlocked(src, T))
L.Add(T)
return L
// check for all turfs, including unsimulated ones
/turf/proc/AdjacentTurfsSpace(var/obj/item/weapon/card/id/ID = null, var/list/closed)//check access if one is passed
var/list/L = new()
var/turf/T
for(var/dir in list(NORTHWEST,NORTHEAST,SOUTHEAST,SOUTHWEST,NORTH,EAST,SOUTH,WEST)) //arbitrarily ordered list to favor non-diagonal moves in case of ties
T = get_step(src,dir)
if(T in closed) //turf already proceeded by A*
continue
if(istype(T) && !T.density)
if(!ID)
if(!LinkBlocked(src, T))
L.Add(T)
else
if(!LinkBlockedWithAccess(src, T, ID))
L.Add(T)
return L
//////////////////////////////
//Distance procs
//////////////////////////////
//Distance associates with all directions movement
/turf/proc/Distance(var/turf/T)
return get_dist(src,T)
// This Distance proc assumes that only cardinal movement is
// possible. It results in more efficient (CPU-wise) pathing
// for bots and anything else that only moves in cardinal dirs.
/turf/proc/Distance_cardinal(turf/t)
if(!src || !t) return 0
return abs(src.x - t.x) + abs(src.y - t.y)
/turf/proc/Distance_cardinal(turf/T)
if(!src || !T) return 0
return abs(src.x - T.x) + abs(src.y - T.y)
////////////////////////////////////////////////////