diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index f5e4c25a300..febcc061e06 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -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--
diff --git a/code/defines/procs/AStar.dm b/code/defines/procs/AStar.dm
index 0af47ba12c6..acf759e4f9b 100644
--- a/code/defines/procs/AStar.dm
+++ b/code/defines/procs/AStar.dm
@@ -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
diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm
index 1e0694f4404..b0016ddf1ce 100644
--- a/code/game/machinery/bots/bots.dm
+++ b/code/game/machinery/bots/bots.dm
@@ -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("[src] beeps, \"[message]\"",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
diff --git a/code/game/machinery/bots/cleanbot.dm b/code/game/machinery/bots/cleanbot.dm
index 74751b7793d..396d657c96c 100644
--- a/code/game/machinery/bots/cleanbot.dm
+++ b/code/game/machinery/bots/cleanbot.dm
@@ -237,8 +237,9 @@ text("[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
diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm
index c5ebde6ba05..ae959bb42b0 100644
--- a/code/game/machinery/bots/ed209bot.dm
+++ b/code/game/machinery/bots/ed209bot.dm
@@ -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("[src] beeps, \"[message]\"",2)
- return
/obj/machinery/bot/ed209/explode()
walk_to(src,0)
diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm
index c2acf2f2ab9..78c418adb8c 100644
--- a/code/game/machinery/bots/floorbot.dm
+++ b/code/game/machinery/bots/floorbot.dm
@@ -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("[src] 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))
diff --git a/code/game/machinery/bots/medbot.dm b/code/game/machinery/bots/medbot.dm
index a377a8a7425..5096b6a1554 100644
--- a/code/game/machinery/bots/medbot.dm
+++ b/code/game/machinery/bots/medbot.dm
@@ -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")
diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm
index 0d633fe1b45..c2b4f9d25e1 100644
--- a/code/game/machinery/bots/secbot.dm
+++ b/code/game/machinery/bots/secbot.dm
@@ -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("[src] beeps, \"[message]\"",2)
- return
/obj/machinery/bot/secbot/explode()
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index c174b3817f1..f7410d589f0 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -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
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index 5a86a20259a..b213eff32d2 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -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
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 7a247f37614..4108711b507 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -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
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 6289b6dd255..07c144f1c08 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -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)
+
+////////////////////////////////////////////////////
\ No newline at end of file