@name A* @inputs [From To]:vector IgnoredProps:array Active @persist AllowFlight HeightOffset VisualizeNodes @persist [UniqueName]:string ActiveCount CoreNumber Directions:array [TP FM]:vector @persist [GridSize]:vector [GT]:gtable Active MoveCost ST ErasedPath SlopeMul V @outputs PathFound PathTraced PathFailed Path:array if (first()) { UniqueName = "Gho1st" MoveCost = 20 AllowFlight = 0 HeightOffset = 32 GridSize = vec(50, 50, 32) SlopeMul = 3 VisualizeNodes = 0 IsInitailized = 0 GT = gTable(UniqueName + ".NA*") ActiveCount = GT["ActiveCount", number] + 1 CoreNumber = ActiveCount GT["ActiveCount", number] = ActiveCount Directions[1, vector] = vec(1, 0, 0) Directions[2, vector] = vec(1, 1, 0) Directions[3, vector] = vec(0, 1, 0) Directions[4, vector] = vec(-1, 1, 0) Directions[5, vector] = vec(-1, 0, 0) Directions[6, vector] = vec(-1, -1, 0) Directions[7, vector] = vec(0, -1, 0) Directions[8, vector] = vec(1, -1, 0) if (AllowFlight) { } if (CoreNumber == 1) { GT["OnCore", number] = 1 } runOnLast(1) runOnTick(1) setName("A* Core #" + CoreNumber) function vector roundPos(Vec : vector) { return round(Vec / GridSize) * GridSize } function number checkSpace(Pos : vector) { rangerFilter(IgnoredProps) local R = rangerOffsetHull(Pos, Pos, GridSize) return R:hit() } function string posString(Pos:vector) { return Pos:x() + "x" + Pos:y() + "x" + Pos:z() } holoCreate(-1) holoAlpha(-1, 0) } if (Active && ~Active || Active && (changed(From) || changed(To))) { GT["GridData", table] = table() GT["Open", table] = table() GT["PathFound", number] = 0 local CurVecPos = roundPos(From) local CurPos = posString(CurVecPos) local Open = GT["Open", table] Open[CurPos, number] = 0 GT["Open", table] = Open local GridData = GT["GridData", table][CurPos, table] GridData["Parent", string] = CurPos GridData["Pos", vector] = CurVecPos GridData["Closed", number] = 1 GT["GridData", table][CurPos, table] = GridData GT["CurNode", string] = CurPos #print(GT["Open", array][1, string]) #print(GT["CurNode", string] + "CC") ErasedPath = 0 PathTraced = 0 PathFound = 0 PathFailed = 0 ST = curtime() TP = roundPos(To) FM = CurVecPos } if (CoreNumber == 1) { GT["Active", number] = Active GT["IgnoredProps", array] = IgnoredProps GT["From", vector] = From GT["To", vector] = To GT["ST", number] = ST GT["PathFound", number] = PathFound GT["PathFailed", number] = PathFailed } else { Active = GT["Active", number] IgnoredProps = GT["IgnoredProps", array] From = GT["From", vector] To = GT["To", vector] ST = GT["ST", number] PathFound = GT["PathFound", number] PathFailed = GT["PathFailed", number] } if (PathFound && CoreNumber == 1 && !PathTraced) { if (!ErasedPath) { ErasedPath = 1 Path:clear() } FM = roundPos(From) while (perf()) { local Data = GT["GridData", table][GT["LastNode", string], table] Path:unshiftVector(Data["Pos", vector]) GT["LastNode", string] = Data["Parent", string] Data = GT["GridData", table][GT["LastNode", string], table] if (Data["Parent", string] == GT["LastNode", string]) { PathTraced = 1 Path:remove(Path:count()) break } } } if (Active && !PathFound && !PathFailed) { ActiveCount = gGetNum(UniqueName + ".NA*-Active") while (perf() && !PathFound && !PathFailed) { local CurNode = GT["GridData", table][GT["CurNode", string], table] local CurPos = CurNode["Pos", vector] GT["Open", table]:remove(GT["CurNode", string]) for (I = 1, Directions:count()) { local Dir = Directions[I, vector] local CheckPos = CurPos + (GridSize * Dir) rangerFilter(IgnoredProps) rangerFilter(players()) local RD = rangerOffset(9999, CheckPos + vec(0, 0, HeightOffset), vec(0, 0, -1)) local RDX = abs(RD:hitNormal():x()) local RDY = abs(RD:hitNormal():y()) local AddHeight = (((RDX + RDY) * SlopeMul) / 2) * GridSize:z() RD = rangerOffset(GridSize:z() * 2 + (GridSize:z() * (RDX + RDY)), CheckPos, vec(0, 0, -1)) local Z = RD:pos():z() + GridSize:z() holoPos(-1, RD:pos()) Z += AddHeight CheckPos = roundPos(CheckPos:setZ(Z)) local CheckPosStr = posString(CheckPos) local Closed = GT["GridData", table][CheckPosStr, table]["Closed", number] if (CheckPosStr == posString(roundPos(To))) { GT["PathFound", number] = 1 PathFound = 1 local Data = table() Data["Parent", string] = GT["CurNode", string] GT["GridData", table][CheckPosStr, table] = Data GT["LastNode", string] = CheckPosStr break } if (!Closed & RD:hit() & !holoEntity(-1):isUnderWater()) { if (!checkSpace(CheckPos)) { local NewData = table() local GMultiplier = Dir:length() local G = CurNode["G", number] + (MoveCost * GMultiplier) local H = CurPos:distance(roundPos(To)) local F = G + H NewData["Closed", number] = 1 NewData["Pos", vector] = CheckPos NewData["Parent", string] = GT["CurNode", string] NewData["G", number] = G GT["GridData", table][CheckPosStr, table] = NewData GT["Open", table][CheckPosStr, number] = F if (VisualizeNodes) { V++ if (!holoEntity(V)) { holoCreate(V) } #print(V) holoPos(V, CheckPos) holoScale(V, GridSize / 12) holoAlpha(V, 50) holoAng(V, ang(0, 0, 0)) if (V == 10) {V = 0} } } } } if (GT["Open", table]:count() == 0) { PathFailed = 1 break } #Check for new node local MinIndex = GT["Open", table]:values():minIndex() local KeyIndex = GT["Open", table]:keys()[MinIndex, string] GT["CurNode", string] = KeyIndex } } if (last()) { GT["ActiveCount", number] = GT["ActiveCount", number] - 1 }