mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-23 20:16:55 +01:00
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:
+77
-53
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
Reference in New Issue
Block a user