--- /dev/null
+#include "pathlib.qh"
+#include "utility.qh"
+
+void pathlib_deletepath(entity start)
+{
+ entity e;
+
+ e = findchainentity(owner, start);
+ while(e)
+ {
+ e.think = SUB_Remove;
+ e.nextthink = time;
+ e = e.chain;
+ }
+}
+
+//#define PATHLIB_NODEEXPIRE 0.05
+const float PATHLIB_NODEEXPIRE = 20;
+
+void dumpnode(entity n)
+{
+ n.is_path_node = false;
+ n.think = SUB_Remove;
+ n.nextthink = time;
+}
+
+entity pathlib_mknode(vector where,entity parent)
+{
+ entity node;
+
+ node = pathlib_nodeatpoint(where);
+ if(node)
+ {
+ #ifdef TURRET_DEBUG
+ mark_error(where, 60);
+ #endif
+ return node;
+ }
+
+ node = spawn();
+
+ node.think = SUB_Remove;
+ node.nextthink = time + PATHLIB_NODEEXPIRE;
+ node.is_path_node = true;
+ node.owner = openlist;
+ node.path_prev = parent;
+
+
+ setsize(node, '0 0 0', '0 0 0');
+
+ setorigin(node, where);
+ node.medium = pointcontents(where);
+ pathlib_showsquare(where, 1 ,15);
+
+ ++pathlib_made_cnt;
+ ++pathlib_open_cnt;
+
+ return node;
+}
+
+float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector goal,float cost)
+{
+ entity node;
+ float h,g,f,doedge = 0;
+ vector where;
+
+ ++pathlib_searched_cnt;
+
+ if(inwater(parent.origin))
+ {
+ LOG_TRACE("FromWater\n");
+ pathlib_expandnode = pathlib_expandnode_box;
+ pathlib_movenode = pathlib_swimnode;
+ }
+ else
+ {
+ if(inwater(to))
+ {
+ LOG_TRACE("ToWater\n");
+ pathlib_expandnode = pathlib_expandnode_box;
+ pathlib_movenode = pathlib_walknode;
+ }
+ else
+ {
+ LOG_TRACE("LandToLoand\n");
+ //if(edge_check(parent.origin))
+ // return 0;
+
+ pathlib_expandnode = pathlib_expandnode_star;
+ pathlib_movenode = pathlib_walknode;
+ doedge = 1;
+ }
+ }
+
+ node = pathlib_nodeatpoint(to);
+ if(node)
+ {
+ LOG_TRACE("NodeAtPoint\n");
+ ++pathlib_merge_cnt;
+
+ if(node.owner == openlist)
+ {
+ h = pathlib_heuristic(node.origin,goal);
+ g = pathlib_cost(parent,node.origin,cost);
+ f = g + h;
+
+ if(node.pathlib_node_g > g)
+ {
+ node.pathlib_node_h = h;
+ node.pathlib_node_g = g;
+ node.pathlib_node_f = f;
+
+ node.path_prev = parent;
+ }
+
+ if (!best_open_node)
+ best_open_node = node;
+ else if(best_open_node.pathlib_node_f > node.pathlib_node_f)
+ best_open_node = node;
+ }
+
+ return 1;
+ }
+
+ where = pathlib_movenode(parent.origin, to, 0);
+
+ if (!pathlib_movenode_goodnode)
+ {
+ //pathlib_showsquare(where, 0 ,30);
+ //pathlib_showsquare(parent.origin, 1 ,30);
+ LOG_TRACE("pathlib_movenode_goodnode = 0\n");
+ return 0;
+ }
+
+ //pathlib_showsquare(where, 1 ,30);
+
+ if(pathlib_nodeatpoint(where))
+ {
+ LOG_TRACE("NAP WHERE :",vtos(where),"\n");
+ LOG_TRACE("not NAP TO:",vtos(to),"\n");
+ LOG_TRACE("NAP-NNAP:",ftos(vlen(to-where)),"\n\n");
+ return 0;
+ }
+
+
+ if(doedge)
+ if (!tile_check(where))
+ {
+ LOG_TRACE("tile_check fail\n");
+ pathlib_showsquare(where, 0 ,30);
+ return 0;
+ }
+
+
+ h = pathlib_heuristic(where,goal);
+ g = pathlib_cost(parent,where,cost);
+ f = g + h;
+
+ /*
+ node = findradius(where,pathlib_gridsize * 0.5);
+ while(node)
+ {
+ if(node.is_path_node == true)
+ {
+ ++pathlib_merge_cnt;
+ if(node.owner == openlist)
+ {
+ if(node.pathlib_node_g > g)
+ {
+ //pathlib_movenode(where,node.origin,0);
+ //if(pathlib_movenode_goodnode)
+ //{
+ //mark_error(node.origin + '0 0 128',30);
+ node.pathlib_node_h = h;
+ node.pathlib_node_g = g;
+ node.pathlib_node_f = f;
+ node.path_prev = parent;
+ //}
+ }
+
+ if (!best_open_node)
+ best_open_node = node;
+ else if(best_open_node.pathlib_node_f > node.pathlib_node_f)
+ best_open_node = node;
+ }
+
+ return 1;
+ }
+ node = node.chain;
+ }
+ */
+
+ node = pathlib_mknode(where, parent);
+ node.pathlib_node_h = h;
+ node.pathlib_node_g = g;
+ node.pathlib_node_f = f;
+
+ if (!best_open_node)
+ best_open_node = node;
+ else if(best_open_node.pathlib_node_f > node.pathlib_node_f)
+ best_open_node = node;
+
+ return 1;
+}
+
+entity pathlib_getbestopen()
+{
+ entity node;
+ entity bestnode;
+
+ if(best_open_node)
+ {
+ ++pathlib_bestcash_hits;
+ pathlib_bestcash_saved += pathlib_open_cnt;
+
+ return best_open_node;
+ }
+
+ node = findchainentity(owner,openlist);
+ if(!node)
+ return world;
+
+ bestnode = node;
+ while(node)
+ {
+ ++pathlib_bestopen_seached;
+ if(node.pathlib_node_f < bestnode.pathlib_node_f)
+ bestnode = node;
+
+ node = node.chain;
+ }
+
+ return bestnode;
+}
+
+void pathlib_close_node(entity node,vector goal)
+{
+
+ if(node.owner == closedlist)
+ {
+ LOG_TRACE("Pathlib: Tried to close a closed node!\n");
+ return;
+ }
+
+ if(node == best_open_node)
+ best_open_node = world;
+
+ ++pathlib_closed_cnt;
+ --pathlib_open_cnt;
+
+ node.owner = closedlist;
+
+ if(vlen(node.origin - goal) <= pathlib_gridsize)
+ {
+ vector goalmove;
+
+ goalmove = pathlib_walknode(node.origin,goal,1);
+ if(pathlib_movenode_goodnode)
+ {
+ goal_node = node;
+ pathlib_foundgoal = true;
+ }
+ }
+}
+
+void pathlib_cleanup()
+{
+ best_open_node = world;
+
+ //return;
+
+ entity node;
+
+ node = findfloat(world,is_path_node, true);
+ while(node)
+ {
+ /*
+ node.owner = openlist;
+ node.pathlib_node_g = 0;
+ node.pathlib_node_h = 0;
+ node.pathlib_node_f = 0;
+ node.path_prev = world;
+ */
+
+ dumpnode(node);
+ node = findfloat(node,is_path_node, true);
+ }
+
+ if(openlist)
+ remove(openlist);
+
+ if(closedlist)
+ remove(closedlist);
+
+ openlist = world;
+ closedlist = world;
+
+}
+
+float Cosine_Interpolate(float a, float b, float x)
+{
+ float ft,f;
+
+ ft = x * 3.1415927;
+ f = (1 - cos(ft)) * 0.5;
+
+ return a*(1-f) + b*f;
+}
+
+float buildpath_nodefilter_directional(vector n,vector c,vector p)
+{
+ vector d1,d2;
+
+ d2 = normalize(p - c);
+ d1 = normalize(c - n);
+
+ if(vlen(d1-d2) < 0.25)
+ {
+ //mark_error(c,30);
+ return 1;
+ }
+ //mark_info(c,30);
+ return 0;
+}
+
+float buildpath_nodefilter_moveskip(vector n,vector c,vector p)
+{
+ pathlib_walknode(p,n,1);
+ if(pathlib_movenode_goodnode)
+ return 1;
+
+ return 0;
+}
+
+float buildpath_nodefilter_none(vector n,vector c,vector p)
+{
+ return 0;
+}
+
+entity path_build(entity next, vector where, entity prev, entity start)
+{
+ entity path;
+
+ if(prev && next)
+ if(buildpath_nodefilter)
+ if(buildpath_nodefilter(next.origin,where,prev.origin))
+ return next;
+
+
+ path = spawn();
+ path.owner = start;
+ path.path_next = next;
+
+ setorigin(path,where);
+
+ if(!next)
+ path.classname = "path_end";
+ else
+ {
+ if(!prev)
+ path.classname = "path_start";
+ else
+ path.classname = "path_node";
+ }
+
+ return path;
+}
+
+entity pathlib_astar(vector from,vector to)
+{SELFPARAM();
+ entity path, start, end, open, n, ln;
+ float ptime, ftime, ctime;
+
+ ptime = gettime(GETTIME_REALTIME);
+ pathlib_starttime = ptime;
+
+ pathlib_cleanup();
+
+ // Select water<->land capable node make/link
+ pathlib_makenode = pathlib_makenode_adaptive;
+
+ // Select XYZ cost estimate
+ //pathlib_heuristic = pathlib_h_diagonal3;
+ pathlib_heuristic = pathlib_h_diagonal;
+
+ // Select distance + waterfactor cost
+ pathlib_cost = pathlib_g_euclidean_water;
+
+ // Select star expander
+ pathlib_expandnode = pathlib_expandnode_star;
+
+ // Select walk simulation movement test
+ pathlib_movenode = pathlib_walknode;
+
+ // Filter final nodes by direction
+ buildpath_nodefilter = buildpath_nodefilter_directional;
+
+ // Filter tiles with cross pattern
+ tile_check = tile_check_cross;
+
+ // If the start is in water we need diffrent settings
+ if(inwater(from))
+ {
+ // Select volumetric node expaner
+ pathlib_expandnode = pathlib_expandnode_box;
+
+ // Water movement test
+ pathlib_movenode = pathlib_swimnode;
+ }
+
+ if (!openlist)
+ openlist = spawn();
+
+ if (!closedlist)
+ closedlist = spawn();
+
+ pathlib_closed_cnt = 0;
+ pathlib_open_cnt = 0;
+ pathlib_made_cnt = 0;
+ pathlib_merge_cnt = 0;
+ pathlib_searched_cnt = 0;
+ pathlib_bestopen_seached = 0;
+ pathlib_bestcash_hits = 0;
+ pathlib_bestcash_saved = 0;
+
+ pathlib_gridsize = 128;
+ pathlib_movecost = pathlib_gridsize;
+ pathlib_movecost_diag = vlen(('1 1 0' * pathlib_gridsize));
+ pathlib_movecost_waterfactor = 25000000;
+ pathlib_foundgoal = 0;
+
+ movenode_boxmax = self.maxs * 1.1;
+ movenode_boxmin = self.mins * 1.1;
+
+ movenode_stepsize = pathlib_gridsize * 0.25;
+
+ tile_check_size = pathlib_gridsize * 0.5;
+ tile_check_up = '0 0 2' * pathlib_gridsize;
+ tile_check_up = '0 0 128';
+ tile_check_down = '0 0 3' * pathlib_gridsize;
+ tile_check_down = '0 0 256';
+
+ //movenode_stepup = '0 0 128';
+ movenode_stepup = '0 0 36';
+ movenode_maxdrop = '0 0 512';
+ //movenode_maxdrop = '0 0 512';
+ movenode_boxup = '0 0 72';
+
+ from.x = fsnap(from.x, pathlib_gridsize);
+ from.y = fsnap(from.y, pathlib_gridsize);
+ //from_z += 32;
+
+ to.x = fsnap(to.x, pathlib_gridsize);
+ to.y = fsnap(to.y, pathlib_gridsize);
+ //to_z += 32;
+
+ LOG_TRACE("AStar init\n");
+ path = pathlib_mknode(from, world);
+ pathlib_close_node(path, to);
+ if(pathlib_foundgoal)
+ {
+ LOG_TRACE("AStar: Goal found on first node!\n");
+
+ open = spawn();
+ open.owner = open;
+ open.classname = "path_end";
+ setorigin(open,path.origin);
+
+ pathlib_cleanup();
+
+ return open;
+ }
+
+ if(pathlib_expandnode(path, from, to) <= 0)
+ {
+ LOG_TRACE("AStar path fail.\n");
+ pathlib_cleanup();
+
+ return world;
+ }
+
+ best_open_node = pathlib_getbestopen();
+ n = best_open_node;
+ pathlib_close_node(best_open_node, to);
+ if(inwater(n.origin))
+ pathlib_expandnode_box(n, from, to);
+ else
+ pathlib_expandnode_star(n, from, to);
+
+ while(pathlib_open_cnt)
+ {
+ if((gettime(GETTIME_REALTIME) - pathlib_starttime) > pathlib_maxtime)
+ {
+ LOG_TRACE("Path took to long to compute!\n");
+ LOG_TRACE("Nodes - created: ", ftos(pathlib_made_cnt),"\n");
+ LOG_TRACE("Nodes - open: ", ftos(pathlib_open_cnt),"\n");
+ LOG_TRACE("Nodes - merged: ", ftos(pathlib_merge_cnt),"\n");
+ LOG_TRACE("Nodes - closed: ", ftos(pathlib_closed_cnt),"\n");
+
+ pathlib_cleanup();
+ return world;
+ }
+
+ best_open_node = pathlib_getbestopen();
+ n = best_open_node;
+ pathlib_close_node(best_open_node,to);
+
+ if(inwater(n.origin))
+ pathlib_expandnode_box(n,from,to);
+ else
+ pathlib_expandnode(n,from,to);
+
+ if(pathlib_foundgoal)
+ {
+ LOG_TRACE("Target found. Rebuilding and filtering path...\n");
+ ftime = gettime(GETTIME_REALTIME);
+ ptime = ftime - ptime;
+
+ start = path_build(world,path.origin,world,world);
+ end = path_build(world,goal_node.origin,world,start);
+ ln = end;
+
+ open = goal_node;
+ for(open = goal_node; open.path_prev != path; open = open.path_prev)
+ {
+ n = path_build(ln,open.origin,open.path_prev,start);
+ ln.path_prev = n;
+ ln = n;
+ }
+ start.path_next = n;
+ n.path_prev = start;
+ ftime = gettime(GETTIME_REALTIME) - ftime;
+
+ ctime = gettime(GETTIME_REALTIME);
+ pathlib_cleanup();
+ ctime = gettime(GETTIME_REALTIME) - ctime;
+
+
+#ifdef DEBUGPATHING
+ pathlib_showpath2(start);
+
+ LOG_TRACE("Time used - pathfinding: ", ftos(ptime),"\n");
+ LOG_TRACE("Time used - rebuild & filter: ", ftos(ftime),"\n");
+ LOG_TRACE("Time used - cleanup: ", ftos(ctime),"\n");
+ LOG_TRACE("Time used - total: ", ftos(ptime + ftime + ctime),"\n");
+ LOG_TRACE("Time used - # frames: ", ftos(ceil((ptime + ftime + ctime) / sys_frametime)),"\n\n");
+ LOG_TRACE("Nodes - created: ", ftos(pathlib_made_cnt),"\n");
+ LOG_TRACE("Nodes - open: ", ftos(pathlib_open_cnt),"\n");
+ LOG_TRACE("Nodes - merged: ", ftos(pathlib_merge_cnt),"\n");
+ LOG_TRACE("Nodes - closed: ", ftos(pathlib_closed_cnt),"\n");
+ LOG_TRACE("Nodes - searched: ", ftos(pathlib_searched_cnt),"\n");
+ LOG_TRACE("Nodes bestopen searched: ", ftos(pathlib_bestopen_seached),"\n");
+ LOG_TRACE("Nodes bestcash - hits: ", ftos(pathlib_bestcash_hits),"\n");
+ LOG_TRACE("Nodes bestcash - save: ", ftos(pathlib_bestcash_saved),"\n");
+ LOG_TRACE("AStar done.\n");
+#endif
+ return start;
+ }
+ }
+
+ LOG_TRACE("A* Faild to find a path! Try a smaller gridsize.\n");
+
+ pathlib_cleanup();
+
+ return world;
+}