From 1c0355a19de015f94b5f75868f8128a74f387651 Mon Sep 17 00:00:00 2001 From: Menshin Date: Tue, 29 Sep 2015 23:26:47 +0200 Subject: [PATCH 1/3] * Made the Priority Queue insertion use dichotomic search to find the position of the inserted element * Added a generic (array based) heap data structure * Changed A* to use a heap open set instead of a priority queue * Fixed A* not checking for already processed turfs * Fixed A* number of traversed nodes not being updated on new route * Changed the heuristic proc for every A* calls to /turf/proc/Distance_cardinal, since A* now only progress in cardinal directions --- code/__DATASTRUCTURES/heap.dm | 75 +++++++++++++++++++ code/__DATASTRUCTURES/priority_queue.dm | 40 +++++++--- code/game/machinery/bots/cleanbot.dm | 2 +- code/game/machinery/bots/floorbot.dm | 4 +- .../mob/living/simple_animal/parrot.dm | 2 +- code/orphaned procs/AStar.dm | 58 +++++++------- tgstation.dme | 7 +- 7 files changed, 144 insertions(+), 44 deletions(-) create mode 100644 code/__DATASTRUCTURES/heap.dm diff --git a/code/__DATASTRUCTURES/heap.dm b/code/__DATASTRUCTURES/heap.dm new file mode 100644 index 00000000000..7246eff8771 --- /dev/null +++ b/code/__DATASTRUCTURES/heap.dm @@ -0,0 +1,75 @@ + +////////////////////// +//Heap object +////////////////////// + +/Heap + var/list/L + var/cmp + +/Heap/New(compare) + L = new() + cmp = compare + +/Heap/proc/IsEmpty() + return !L.len + +//Insert and place at its position a new node in the heap +/Heap/proc/Insert(atom/A) + + L.Add(A) + Swim(L.len) + +//removes and returns the first element of the heap +//(i.e the max or the min dependant on the comparison function) +/Heap/proc/Pop() + if(!L.len) + return 0 + . = L[1] + + L[1] = L[L.len] + L.Cut(L.len) + + Sink(1) + +//Get a node up to its right position in the heap +/Heap/proc/Swim(var/index) + var/parent = round(index * 0.5) + + while(parent > 0 && (call(cmp)(L[index],L[parent]) > 0)) + L.Swap(index,parent) + index = parent + parent = round(index * 0.5) + +//Get a node down to its right position in the heap +/Heap/proc/Sink(var/index) + var/g_child = GetGreaterChild(index) + + while(g_child > 0 && (call(cmp)(L[index],L[g_child]) < 0)) + L.Swap(index,g_child) + index = g_child + g_child = GetGreaterChild(index) + +//Returns the greater (relative to the comparison proc) of a node children +//or 0 if there's no child +/Heap/proc/GetGreaterChild(var/index) + if(index * 2 > L.len) + return 0 + + if(index * 2 + 1 > L.len) + return index * 2 + + if(call(cmp)(L[index * 2],L[index * 2 + 1]) < 0) + return index * 2 + 1 + else + return index * 2 + +//Replaces a given node so it verify the heap condition +/Heap/proc/ReSort(atom/A) + var/index = L.Find(A) + + Swim(index) + Sink(index) + +/Heap/proc/List() + . = L.Copy() diff --git a/code/__DATASTRUCTURES/priority_queue.dm b/code/__DATASTRUCTURES/priority_queue.dm index 2983924e0a1..8689a19f4e4 100644 --- a/code/__DATASTRUCTURES/priority_queue.dm +++ b/code/__DATASTRUCTURES/priority_queue.dm @@ -15,23 +15,42 @@ /PriorityQueue/proc/IsEmpty() return !L.len +//return the index the element should be in the priority queue using dichotomic search +/PriorityQueue/proc/FindElementIndex(atom/A) + var/i = 1 + var/j = L.len + var/mid + + while(i < j) + mid = round((i+j)/2) + + if(call(cmp)(L[mid],A) < 0) + i = mid + 1 + else + j = mid + + if(i == 1 || i == L.len) //edge cases + return (call(cmp)(L[i],A) > 0) ? i : i+1 + else + return i + + //add an element in the list, -//immediatly ordering it to its position using Insertion sort +//immediatly ordering it to its position using dichotomic search /PriorityQueue/proc/Enqueue(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-- + if(!L.len) + L.Add(A) + return + + L.Insert(FindElementIndex(A),A) //removes and returns the first element in the queue /PriorityQueue/proc/Dequeue() if(!L.len) return 0 . = L[1] + Remove(.) - return . //removes an element /PriorityQueue/proc/Remove(atom/A) @@ -39,12 +58,11 @@ //returns a copy of the elements list /PriorityQueue/proc/List() - var/list/ret = L.Copy() - return ret + . = L.Copy() //return the position of an element or 0 if not found /PriorityQueue/proc/Seek(atom/A) - return L.Find(A) + . = L.Find(A) //return the element at the i_th position /PriorityQueue/proc/Get(i) diff --git a/code/game/machinery/bots/cleanbot.dm b/code/game/machinery/bots/cleanbot.dm index 00f14c4a61b..5c915439f13 100644 --- a/code/game/machinery/bots/cleanbot.dm +++ b/code/game/machinery/bots/cleanbot.dm @@ -166,7 +166,7 @@ text("[on ? "On" : "Off"]")) if(target) if(!path || path.len == 0) //No path, need a new one //Try to produce a path to the target, and ignore airlocks to which it has access. - path = get_path_to(loc, target.loc, src, /turf/proc/Distance, 0, 30, id=botcard) + path = get_path_to(loc, target.loc, src, /turf/proc/Distance_cardinal, 0, 30, id=botcard) if (!bot_move(target)) add_to_ignore(target) target = null diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm index 7f107e9892b..1ed19deb7f9 100644 --- a/code/game/machinery/bots/floorbot.dm +++ b/code/game/machinery/bots/floorbot.dm @@ -260,9 +260,9 @@ if(path.len == 0) if(!istype(target, /turf/)) var/turf/TL = get_turf(target) - path = get_path_to(loc, TL, src, /turf/proc/Distance, 0, 30, id=botcard) + path = get_path_to(loc, TL, src, /turf/proc/Distance_cardinal, 0, 30, id=botcard) else - path = get_path_to(loc, target, src, /turf/proc/Distance, 0, 30, id=botcard) + path = get_path_to(loc, target, src, /turf/proc/Distance_cardinal, 0, 30, id=botcard) if(!bot_move(target)) add_to_ignore(target) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index f600071a076..76d8c13e570 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -619,7 +619,7 @@ if((C.l_hand && C.l_hand.w_class <= 2) || (C.r_hand && C.r_hand.w_class <= 2)) item = C if(item) - if(!AStar(loc, get_turf(item), src, /turf/proc/Distance)) + if(!AStar(loc, get_turf(item), src, /turf/proc/Distance_cardinal)) item = null continue return item diff --git a/code/orphaned procs/AStar.dm b/code/orphaned procs/AStar.dm index da152bdc8a0..5835e56afab 100644 --- a/code/orphaned procs/AStar.dm +++ b/code/orphaned procs/AStar.dm @@ -23,29 +23,29 @@ Mintargetdist: Minimum distance to the target before path returns, could be used near a target, but not right to it - for an AI mob with a gun, for example. Minnodedist: Minimum number of nodes to return in the path, could be used to give a path a minimum length to avoid portals or something i guess?? Not that they're counted right now but w/e. + + + Modified to provide ID argument - supplied to 'adjacent' proc, defaults to null + Used for checking if route exists through a door which can be opened + + 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 */ -// Modified to provide ID argument - supplied to 'adjacent' proc, defaults to null -// Used for checking if route exists through a door which can be opened - -// 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 - - ////////////////////// //PathNode object ////////////////////// @@ -79,6 +79,10 @@ length to avoid portals or something i guess?? Not that they're counted right no /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 + //search if there's a PathNode that points to turf T in the Priority Queue /proc/SeekTurf(var/PriorityQueue/Queue, turf/T) var/i = 1 @@ -99,7 +103,7 @@ length to avoid portals or something i guess?? Not that they're counted right no //the actual algorithm /proc/AStar(start, end, atom, dist, maxnodes, maxnodedepth = 30, mintargetdist, minnodedist, id=null, turf/exclude=null) - var/PriorityQueue/open = new /PriorityQueue(/proc/PathWeightCompare) //the open list, ordered using the PathWeightCompare proc, from lower f to higher + 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 @@ -110,13 +114,13 @@ length to avoid portals or something i guess?? Not that they're counted right no return 0 //initialization - open.Enqueue(new /PathNode(start,null,0,call(start,dist)(end),0)) + 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.Dequeue() //get the lower f turf in 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 @@ -135,6 +139,7 @@ length to avoid portals or something i guess?? Not that they're counted right no while(cur.prevNode) cur = cur.prevNode path.Add(cur.source) + break //IMPLEMENTATION TO FINISH @@ -148,17 +153,18 @@ length to avoid portals or something i guess?? Not that they're counted right no //var/list/L = call(cur.source,adjacent)(id,closed) var/list/L = cur.source.reachableAdjacentTurfs(atom, id) for(var/turf/T in L) - if(T == exclude) + 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.Enqueue(new /PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1)) + 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 } diff --git a/tgstation.dme b/tgstation.dme index 38771327bb6..fd595770f6c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -17,6 +17,7 @@ #include "code\_compile_options.dm" #include "code\hub.dm" #include "code\world.dm" +#include "code\__DATASTRUCTURES\heap.dm" #include "code\__DATASTRUCTURES\linked_lists.dm" #include "code\__DATASTRUCTURES\priority_queue.dm" #include "code\__DATASTRUCTURES\stacks.dm" @@ -834,9 +835,9 @@ #include "code\modules\admin\NewBan.dm" #include "code\modules\admin\newbanjob.dm" #include "code\modules\admin\player_panel.dm" -#include "code\modules\admin\secrets.dm" -#include "code\modules\admin\stickyban.dm" -#include "code\modules\admin\sql_notes.dm" +#include "code\modules\admin\secrets.dm" +#include "code\modules\admin\sql_notes.dm" +#include "code\modules\admin\stickyban.dm" #include "code\modules\admin\topic.dm" #include "code\modules\admin\watchlist.dm" #include "code\modules\admin\DB ban\functions.dm" From e6b2f37182f1cc86789592c3119f7cd124f3e204 Mon Sep 17 00:00:00 2001 From: Menshin Date: Thu, 1 Oct 2015 19:52:32 +0200 Subject: [PATCH 2/3] * (Re)Made A* adjacent turfs checking proc generic * Wrote a /reachableAdjacentAtmosTurfs proc that checks if a turf can be accessed via atmos * Made airborne disease spreading only via atmos * A* generic turfs checking proc can now take into account unsimulated turfs using an argument * Fixes ling stings not working in space and floorbot not repairing hull breaches --- code/datums/diseases/_disease.dm | 2 +- code/datums/diseases/dna_spread.dm | 2 +- .../gamemodes/changeling/powers/tiny_prick.dm | 4 +- code/game/machinery/bots/floorbot.dm | 4 +- code/orphaned procs/AStar.dm | 112 ++++++------------ 5 files changed, 41 insertions(+), 83 deletions(-) diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm index 6b098310e6d..941ec60f498 100644 --- a/code/datums/diseases/_disease.dm +++ b/code/datums/diseases/_disease.dm @@ -122,7 +122,7 @@ var/list/diseases = typesof(/datum/disease) - /datum/disease if(isturf(source.loc)) for(var/mob/living/carbon/C in oview(spread_range, source)) if(isturf(C.loc)) - if(AStar(source.loc, C.loc, null, /turf/proc/Distance, spread_range)) + if(AStar(source.loc, C.loc, null, /turf/proc/Distance, spread_range, adjacent = (spread_flags & AIRBORNE) ? /turf/proc/reachableAdjacentAtmosTurfs : /turf/proc/reachableAdjacentTurfs)) C.ContractDisease(src) diff --git a/code/datums/diseases/dna_spread.dm b/code/datums/diseases/dna_spread.dm index 1ee18603a61..14f35b7bef1 100644 --- a/code/datums/diseases/dna_spread.dm +++ b/code/datums/diseases/dna_spread.dm @@ -10,7 +10,7 @@ viable_mobtypes = list(/mob/living/carbon/human) var/datum/dna/original_dna = null var/transformed = 0 - desc = "This disease transplants the genetic code of the intial vector into new hosts." + desc = "This disease transplants the genetic code of the initial vector into new hosts." severity = MEDIUM diff --git a/code/game/gamemodes/changeling/powers/tiny_prick.dm b/code/game/gamemodes/changeling/powers/tiny_prick.dm index 24fbc8ec07d..cf8f609f219 100644 --- a/code/game/gamemodes/changeling/powers/tiny_prick.dm +++ b/code/game/gamemodes/changeling/powers/tiny_prick.dm @@ -38,9 +38,7 @@ return if(!isturf(user.loc)) return - if(get_dist(user, target) > (user.mind.changeling.sting_range)) - return //sanity check as AStar is still throwing insane stunts - if(!AStar(user.loc, target.loc, null, /turf/proc/Distance, user.mind.changeling.sting_range)) + if(!AStar(user.loc, target.loc, null, /turf/proc/Distance, user.mind.changeling.sting_range, simulated_only = 0)) return //hope this ancient magic still works if(target.mind && target.mind.changeling) sting_feedback(user,target) diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm index 1ed19deb7f9..677406692a3 100644 --- a/code/game/machinery/bots/floorbot.dm +++ b/code/game/machinery/bots/floorbot.dm @@ -260,9 +260,9 @@ if(path.len == 0) if(!istype(target, /turf/)) var/turf/TL = get_turf(target) - path = get_path_to(loc, TL, src, /turf/proc/Distance_cardinal, 0, 30, id=botcard) + path = get_path_to(loc, TL, src, /turf/proc/Distance_cardinal, 0, 30, id=botcard, simulated_only = 0) else - path = get_path_to(loc, target, src, /turf/proc/Distance_cardinal, 0, 30, id=botcard) + path = get_path_to(loc, target, src, /turf/proc/Distance_cardinal, 0, 30, id=botcard, simulated_only = 0) if(!bot_move(target)) add_to_ignore(target) diff --git a/code/orphaned procs/AStar.dm b/code/orphaned procs/AStar.dm index 5835e56afab..065f6edd267 100644 --- a/code/orphaned procs/AStar.dm +++ b/code/orphaned procs/AStar.dm @@ -3,47 +3,25 @@ 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, adjacent turf proc, distance proc) -For the adjacent turf proc i wrote: -/turf/proc/AdjacentTurfs -And for the distance one i wrote: -/turf/proc/Distance -So an example use might be: - -src.path_list = AStar(src.loc, target.loc, /turf/proc/AdjacentTurfs, /turf/proc/Distance) - -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. +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. -Minnodedist: Minimum number of nodes to return in the path, could be used to give a path a minimum -length to avoid portals or something i guess?? Not that they're counted right now but w/e. +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 - Modified to provide ID argument - supplied to 'adjacent' proc, defaults to null - Used for checking if route exists through a door which can be opened +Actual Adjacent procs : - Also added 'exclude' turf to avoid travelling over; defaults to null + /turf/proc/reachableAdjacentTurfs : returns reachable turfs in cardinal directions (uses simulated_only) - Currently, there's four main ways to call AStar + /turf/proc/reachableAdjacentAtmosTurfs : returns turfs in cardinal directions reachable via atmos - 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 */ ////////////////////// @@ -83,26 +61,15 @@ length to avoid portals or something i guess?? Not that they're counted right no /proc/HeapPathWeightCompare(PathNode/a, PathNode/b) return b.f - a.f -//search if there's a PathNode that points to turf T in the Priority Queue -/proc/SeekTurf(var/PriorityQueue/Queue, 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 - //wrapper that returns an empty list if A* failed to find a path -/proc/get_path_to(start, end, atom, dist, maxnodes, maxnodedepth = 30, mintargetdist, minnodedist, id=null, turf/exclude=null) - var/list/path = AStar(start, end, atom, dist, maxnodes, maxnodedepth, mintargetdist, minnodedist,id, exclude) +/proc/get_path_to(start, end, atom, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) + var/list/path = AStar(start, end, atom, dist, maxnodes, maxnodedepth, mintargetdist, adjacent,id, exclude, simulated_only) if(!path) path = list() return path //the actual algorithm -/proc/AStar(start, end, atom, dist, maxnodes, maxnodedepth = 30, mintargetdist, minnodedist, id=null, turf/exclude=null) +/proc/AStar(start, end, atom, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) 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 @@ -136,22 +103,15 @@ length to avoid portals or something i guess?? Not that they're counted right no if(cur.source == end || closeenough) path = new() path.Add(cur.source) + while(cur.prevNode) cur = cur.prevNode path.Add(cur.source) break - //IMPLEMENTATION TO FINISH - //do we really need this minnodedist ??? - /*if(minnodedist && maxnodedepth) - if(call(cur.source,minnodedist)(end) + cur.nt >= maxnodedepth) - continue - */ - //get adjacents turfs using the adjacent proc, checking for access with id - //var/list/L = call(cur.source,adjacent)(id,closed) - var/list/L = cur.source.reachableAdjacentTurfs(atom, id) + var/list/L = call(cur.source,adjacent)(atom,id, simulated_only) for(var/turf/T in L) if(T == exclude || T in closed) continue @@ -186,33 +146,33 @@ length to avoid portals or something i guess?? Not that they're counted right no return path -/turf/proc/reachableAdjacentTurfs(atom, ID) +//Returns adjacent turfs in cardinal directions that are reachable +//simulated_only controls whether only simulated turfs are considered or not +/turf/proc/reachableAdjacentTurfs(atom, ID, simulated_only) var/list/L = new() var/turf/simulated/T - if(ID) - for(var/dir in cardinal) - T = get_step(src,dir) - if(!istype(T) || T.density) - continue - if(!LinkBlockedWithAccess(T, ID)) - L.Add(T) - else - for(var/dir in cardinal) - if(dir & atmos_adjacent_turfs) - T = get_step(src,dir) - if(!istype(T)) - continue - if(!LinkBlocked(atom, T)) - L.Add(T) + + for(var/dir in cardinal) + T = get_step(src,dir) + if(simulated_only && !istype(T)) + continue + if(!T.density && !LinkBlockedWithAccess(T, ID)) + L.Add(T) return L -/turf/proc/LinkBlocked(atom, turf/T) - if(istype(atom, /atom/movable)) - for(var/obj/O in T) - if(!O.CanPass(atom, T, 1)) - return 1 - return 0 - return 0 +//Returns adjacent turfs in cardinal directions that are reachable via atmos +/turf/proc/reachableAdjacentAtmosTurfs() + var/list/L = new() + var/turf/simulated/T + + for(var/dir in cardinal) + if(dir & atmos_adjacent_turfs) + T = get_step(src,dir) + if(!istype(T)) + continue + if(CanAtmosPass(T)) + L.Add(T) + return L /turf/proc/LinkBlockedWithAccess(turf/T, obj/item/weapon/card/id/ID) var/adir = get_dir(src, T) From 70e1ed3820aef787c6d672ab4308197c101f1639 Mon Sep 17 00:00:00 2001 From: Menshin Date: Fri, 2 Oct 2015 22:54:53 +0200 Subject: [PATCH 3/3] Tweaked the "max path length" logic in A*. --- code/game/gamemodes/changeling/powers/tiny_prick.dm | 2 +- code/orphaned procs/AStar.dm | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/code/game/gamemodes/changeling/powers/tiny_prick.dm b/code/game/gamemodes/changeling/powers/tiny_prick.dm index cf8f609f219..08c2bbfe5c9 100644 --- a/code/game/gamemodes/changeling/powers/tiny_prick.dm +++ b/code/game/gamemodes/changeling/powers/tiny_prick.dm @@ -39,7 +39,7 @@ if(!isturf(user.loc)) return if(!AStar(user.loc, target.loc, null, /turf/proc/Distance, user.mind.changeling.sting_range, simulated_only = 0)) - return //hope this ancient magic still works + return if(target.mind && target.mind.changeling) sting_feedback(user,target) take_chemical_cost(user.mind.changeling) diff --git a/code/orphaned procs/AStar.dm b/code/orphaned procs/AStar.dm index 065f6edd267..e00d43e9e8e 100644 --- a/code/orphaned procs/AStar.dm +++ b/code/orphaned procs/AStar.dm @@ -70,6 +70,13 @@ Actual Adjacent procs : //the actual algorithm /proc/AStar(start, end, atom, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) + + 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 @@ -135,10 +142,6 @@ Actual Adjacent procs : 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++) @@ -197,4 +200,4 @@ Actual Adjacent procs : for(var/obj/machinery/door/D in T) if(!D.CanAStarPass(ID, dir)) return 1 - return 0 \ No newline at end of file + return 0