4 #define medium spawnshieldtime
8 float edge_show(vector point,float fsize);
9 void mark_error(vector where,float lifetime);
10 void mark_info(vector where,float lifetime);
11 entity mark_misc(vector where,float lifetime);
13 void pathlib_showpath(entity start)
19 te_lightning1(e,e.origin,e.path_next.origin);
26 pathlib_showpath(self);
27 self.nextthink = time + 1;
30 void __showpath2_think()
32 mark_info(self.origin,1);
35 self.path_next.think = __showpath2_think;
36 self.path_next.nextthink = time + 0.15;
40 self.owner.think = __showpath2_think;
41 self.owner.nextthink = time + 0.15;
45 void pathlib_showpath2(entity path)
47 path.think = __showpath2_think;
48 path.nextthink = time;
53 void pathlib_deletepath(entity start)
57 e = findchainentity(owner, start);
66 float fsnap(float val,float fsize)
68 return rint(val / fsize) * fsize;
71 vector vsnap(vector point,float fsize)
75 vret.x = rint(point.x / fsize) * fsize;
76 vret.y = rint(point.y / fsize) * fsize;
77 vret.z = ceil(point.z / fsize) * fsize;
82 float walknode_stepsize;
83 vector walknode_stepup;
84 vector walknode_maxdrop;
85 vector walknode_boxup;
86 vector walknode_boxmax;
87 vector walknode_boxmin;
88 float pathlib_movenode_goodnode;
90 float floor_ok(vector point)
94 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
97 pc = pointcontents(point);
107 if(pointcontents(point - '0 0 1') != CONTENT_SOLID)
113 if(pointcontents(point - '0 0 1') == CONTENT_SOLID)
119 #define _pcheck(p) traceline(p+z_up,p-z_down,MOVE_WORLDONLY,self); if (!floor_ok(trace_endpos)) return 1
120 float edge_check(vector point,float fsize)
124 z_up = '0 0 1' * fsize;
125 z_down = '0 0 1' * fsize;
127 _pcheck(point + ('1 1 0' * fsize));
128 _pcheck(point + ('1 -1 0' * fsize));
129 _pcheck(point + ('1 0 0' * fsize));
131 _pcheck(point + ('0 1 0' * fsize));
132 _pcheck(point + ('0 -1 0' * fsize));
134 _pcheck(point + ('-1 0 0' * fsize));
135 _pcheck(point + ('-1 1 0' * fsize));
136 _pcheck(point + ('-1 -1 0' * fsize));
142 #define _pshow(p) mark_error(p,10)
143 float edge_show(vector point,float fsize)
146 _pshow(point + ('1 1 0' * fsize));
147 _pshow(point + ('1 -1 0' * fsize));
148 _pshow(point + ('1 0 0' * fsize));
150 _pshow(point + ('0 1 0' * fsize));
151 _pshow(point + ('0 -1 0' * fsize));
153 _pshow(point + ('-1 0 0' * fsize));
154 _pshow(point + ('-1 1 0' * fsize));
155 _pshow(point + ('-1 -1 0' * fsize));
161 var vector pathlib_movenode(vector start,vector end,float doedge);
162 vector pathlib_wateroutnode(vector start,vector end,float doedge)
166 pathlib_movenode_goodnode = 0;
168 end.x = fsnap(end.x, pathlib_gridsize);
169 end.y = fsnap(end.y, pathlib_gridsize);
171 traceline(end + ('0 0 0.25' * pathlib_gridsize),end - ('0 0 1' * pathlib_gridsize),MOVE_WORLDONLY,self);
174 if(pointcontents(end - '0 0 1') != CONTENT_SOLID)
177 for(surface = start ; surface.z < (end.z + 32); ++surface.z)
179 if(pointcontents(surface) == CONTENT_EMPTY)
183 if(pointcontents(surface + '0 0 1') != CONTENT_EMPTY)
186 tracebox(start + '0 0 64', walknode_boxmin,walknode_boxmax, end + '0 0 64', MOVE_WORLDONLY, self);
187 if(trace_fraction == 1)
188 pathlib_movenode_goodnode = 1;
190 if(fabs(surface.z - end.z) > 32)
191 pathlib_movenode_goodnode = 0;
196 vector pathlib_swimnode(vector start,vector end,float doedge)
198 pathlib_movenode_goodnode = 0;
200 if(pointcontents(start) != CONTENT_WATER)
203 end.x = fsnap(end.x, pathlib_gridsize);
204 end.y = fsnap(end.y, pathlib_gridsize);
206 if(pointcontents(end) == CONTENT_EMPTY)
207 return pathlib_wateroutnode( start, end, doedge);
209 tracebox(start, walknode_boxmin,walknode_boxmax, end, MOVE_WORLDONLY, self);
210 if(trace_fraction == 1)
211 pathlib_movenode_goodnode = 1;
216 vector pathlib_flynode(vector start,vector end)
218 pathlib_movenode_goodnode = 0;
220 end.x = fsnap(end.x, pathlib_gridsize);
221 end.y = fsnap(end.y, pathlib_gridsize);
223 tracebox(start, walknode_boxmin,walknode_boxmax, end, MOVE_WORLDONLY, self);
224 if(trace_fraction == 1)
225 pathlib_movenode_goodnode = 1;
230 vector pathlib_walknode(vector start,vector end,float doedge)
232 vector direction,point,last_point,s,e;
233 float steps, distance, i,laststep;
235 pathlib_movenode_goodnode = 0;
241 direction = normalize(s - e);
243 distance = vlen(start - end);
244 laststep = distance / walknode_stepsize;
245 steps = floor(laststep);
246 laststep = laststep - steps;
249 s = point + walknode_stepup;
250 e = point - walknode_maxdrop;
252 traceline(s, e,MOVE_WORLDONLY,self);
253 if(trace_fraction == 1.0)
256 if (floor_ok(trace_endpos) == 0)
259 last_point = trace_endpos;
261 for(i = 0; i < steps; ++i)
263 point = last_point + direction * walknode_stepsize;
265 s = point + walknode_stepup;
266 e = point - walknode_maxdrop;
267 traceline(s, e,MOVE_WORLDONLY,self);
268 if(trace_fraction == 1.0)
271 point = trace_endpos;
272 if (!floor_ok(trace_endpos))
275 tracebox(last_point + walknode_boxup, walknode_boxmin,walknode_boxmax, point + walknode_boxup, MOVE_WORLDONLY, self);
276 if(trace_fraction != 1.0)
280 if(edge_check(point,pathlib_edge_check_size))
286 point = last_point + direction * walknode_stepsize * laststep;
288 point.x = fsnap(point.x, pathlib_gridsize);
289 point.y = fsnap(point.y, pathlib_gridsize);
291 s = point + walknode_stepup;
292 e = point - walknode_maxdrop;
293 traceline(s, e,MOVE_WORLDONLY,self);
295 if(trace_fraction == 1.0)
298 point = trace_endpos;
300 if (!floor_ok(trace_endpos))
303 tracebox(last_point + walknode_boxup, walknode_boxmin,walknode_boxmax, point + walknode_boxup, MOVE_WORLDONLY, self);
304 if(trace_fraction != 1.0)
307 pathlib_movenode_goodnode = 1;
311 var float pathlib_cost(entity parent,vector to, float static_cost);
312 float pathlib_g_static(entity parent,vector to, float static_cost)
315 return parent.pathlib_node_g + static_cost * pathlib_movecost_waterfactor;
317 return parent.pathlib_node_g + static_cost;
320 float pathlib_g_static_water(entity parent,vector to, float static_cost)
323 return parent.pathlib_node_g + static_cost * pathlib_movecost_waterfactor;
325 return parent.pathlib_node_g + static_cost;
328 float pathlib_g_euclidean(entity parent,vector to, float static_cost)
330 return parent.pathlib_node_g + vlen(parent.origin - to);
332 float pathlib_g_euclidean_water(entity parent,vector to, float static_cost)
335 return parent.pathlib_node_g + vlen(parent.origin - to) * pathlib_movecost_waterfactor;
337 return parent.pathlib_node_g + vlen(parent.origin - to);
340 var float(vector from,vector to) pathlib_heuristic;
343 Manhattan Menas we expect to move up,down left or right
344 No diagonal moves espected. (like moving bewteen city blocks)
346 float pathlib_h_manhattan(vector a,vector b)
348 //h(n) = D * (abs(n.x-goal.x) + abs(n.y-goal.y))
352 h += fabs(a.y - b.y);
353 h *= pathlib_gridsize;
359 This heuristic consider both stright and disagonal moves
360 to have teh same cost.
362 float pathlib_h_diagonal(vector a,vector b)
364 //h(n) = D * max(abs(n.x-goal.x), abs(n.y-goal.y))
369 h = pathlib_movecost * max(x,y);
375 This heuristic only considers the stright line distance.
376 Will usualy mean a lower H then G meaning A* Will speand more
379 float pathlib_h_euclidean(vector a,vector b)
385 This heuristic consider both stright and disagonal moves,
386 But has a separate cost for diagonal moves.
388 float pathlib_h_diagonal2(vector a,vector b)
390 float h_diag,h_str,h,x,y;
393 h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y))
394 h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y))
395 h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n)))
404 h = pathlib_movecost_diag * h_diag;
405 h += pathlib_movecost * (h_str - 2 * h_diag);
411 This heuristic consider both stright and disagonal moves,
412 But has a separate cost for diagonal moves.
416 float pathlib_h_diagonal2sdp(vector preprev,vector prev,vector point,vector end)
418 float h_diag,h_str,h,x,y,z;
420 //h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y))
421 //h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y))
422 //h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n)))
424 x = fabs(point.x - end.x);
425 y = fabs(point.y - end.y);
426 z = fabs(point.z - end.z);
428 h_diag = min3(x,y,z);
431 h = pathlib_movecost_diag * h_diag;
432 h += pathlib_movecost * (h_str - 2 * h_diag);
437 d1 = normalize(preprev - point);
438 d2 = normalize(prev - point);
440 //bprint("pathlib_h_diagonal2sdp-M = ",ftos(m),"\n");
446 float pathlib_h_diagonal3(vector a,vector b)
448 float h_diag,h_str,h,x,y,z;
450 //h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y))
451 //h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y))
452 //h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n)))
458 h_diag = min3(x,y,z);
461 h = pathlib_movecost_diag * h_diag;
462 h += pathlib_movecost * (h_str - 2 * h_diag);
467 const float PATHLIB_NODEEXPIRE = 0.05;
468 float pathlib_scraplist_cnt;
472 #ifdef PATHLIB_USE_NODESCRAP
473 if(pathlib_scraplist_cnt)
475 n = findentity(world,owner,scraplist);
478 --pathlib_scraplist_cnt;
479 ++pathlib_recycle_cnt;
483 pathlib_scraplist_cnt = 0;
488 n.think = SUB_Remove;
489 n.nextthink = time + PATHLIB_NODEEXPIRE;
493 void dumpnode(entity n)
495 #ifdef PATHLIB_USE_NODESCRAP
496 ++pathlib_scraplist_cnt;
500 n.is_path_node = false;
503 //n.is_path_node = false;
504 n.think = SUB_Remove;
509 entity pathlib_mknode(vector where,entity parent)
514 node.is_path_node = true;
515 node.owner = openlist;
516 node.path_prev = parent;
518 setorigin(node, where);
522 node.medium = pointcontents(where);
527 var float pathlib_expandnode(entity node, vector start, vector goal);
528 float pathlib_expandnode_star(entity node, vector start, vector goal);
529 float pathlib_expandnode_box(entity node, vector start, vector goal);
531 var float pathlib_makenode(entity parent,vector start, vector to, vector goal,float cost);
532 float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector goal,float cost)
538 ++pathlib_searched_cnt;
540 if(inwater(parent.origin))
542 pathlib_expandnode = pathlib_expandnode_box;
543 pathlib_movenode = pathlib_swimnode;
550 pathlib_expandnode = pathlib_expandnode_box;
551 pathlib_movenode = pathlib_swimnode;
557 pathlib_expandnode = pathlib_expandnode_star;
558 pathlib_movenode = pathlib_walknode;
563 where = pathlib_movenode(parent.origin,to,0);
564 if (!pathlib_movenode_goodnode)
568 if(edge_check(where,pathlib_edge_check_size))
572 pathlib_h_diagonal2sdp(parent.path_prev.origin,parent.origin,where,goal);
574 h = pathlib_heuristic(where,goal);
575 g = pathlib_cost(parent,where,cost);
578 node = findradius(where,pathlib_gridsize * 0.75);
581 if(node.is_path_node == true)
584 if(node.owner == openlist)
586 if(node.pathlib_node_g > g)
588 node.pathlib_node_h = h;
589 node.pathlib_node_g = g;
590 node.pathlib_node_f = f;
591 node.path_prev = parent;
595 best_open_node = node;
596 else if(best_open_node.pathlib_node_f > node.pathlib_node_f)
597 best_open_node = node;
605 node = pathlib_mknode(where,parent);
606 node.pathlib_node_h = h;
607 node.pathlib_node_g = g;
608 node.pathlib_node_f = f;
611 best_open_node = node;
612 else if(best_open_node.pathlib_node_f > node.pathlib_node_f)
613 best_open_node = node;
618 entity pathlib_getbestopen()
625 ++pathlib_bestcash_hits;
626 pathlib_bestcash_saved += pathlib_open_cnt;
628 return best_open_node;
631 node = findchainentity(owner,openlist);
638 ++pathlib_bestopen_seached;
639 if(node.pathlib_node_f < bestnode.pathlib_node_f)
648 void pathlib_close_node(entity node,vector goal)
651 if(node.owner == closedlist)
653 dprint("Pathlib: Tried to close a closed node!\n");
657 if(node == best_open_node)
658 best_open_node = world;
660 ++pathlib_closed_cnt;
663 node.owner = closedlist;
665 if(vlen(node.origin - goal) <= pathlib_gridsize)
669 goalmove = pathlib_walknode(node.origin,goal,1);
670 if(pathlib_movenode_goodnode)
673 pathlib_foundgoal = true;
678 float pathlib_expandnode_star(entity node, vector start, vector goal)
690 point = where + v_forward * pathlib_gridsize;
691 nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost);
694 point = where - v_forward * pathlib_gridsize;
695 nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost);
698 point = where + v_right * pathlib_gridsize;
699 nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost);
702 point = where - v_right * pathlib_gridsize;
703 nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost);
706 point = where + v_forward * pathlib_gridsize + v_right * pathlib_gridsize;
707 nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost_diag);
710 point = where + v_forward * pathlib_gridsize - v_right * pathlib_gridsize;
711 nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost_diag);
714 point = where - v_forward * pathlib_gridsize + v_right * pathlib_gridsize;
715 nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost_diag);
718 point = where - v_forward * pathlib_gridsize - v_right * pathlib_gridsize;
719 nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost_diag);
721 return pathlib_open_cnt;
724 float pathlib_expandnode_box(entity node, vector start, vector goal)
728 for(v.z = node.origin.z - pathlib_gridsize; v.z <= node.origin.z + pathlib_gridsize; v.z += pathlib_gridsize)
729 for(v.y = node.origin.y - pathlib_gridsize; v.y <= node.origin.y + pathlib_gridsize; v.y += pathlib_gridsize)
730 for(v.x = node.origin.x - pathlib_gridsize; v.x <= node.origin.x + pathlib_gridsize; v.x += pathlib_gridsize)
732 if(vlen(v - node.origin))
733 pathlib_makenode(node,start,v,goal,pathlib_movecost);
736 return pathlib_open_cnt;
739 void pathlib_cleanup()
743 node = findfloat(world,is_path_node, true);
747 node = findfloat(node,is_path_node, true);
756 best_open_node = world;
761 var float buildpath_nodefilter(vector n,vector c,vector p);
762 float buildpath_nodefilter_directional(vector n,vector c,vector p)
766 d2 = normalize(p - c);
767 d1 = normalize(c - n);
769 if(vlen(d1-d2) < 0.25)
775 float buildpath_nodefilter_moveskip(vector n,vector c,vector p)
777 pathlib_walknode(p,n,1);
778 if(pathlib_movenode_goodnode)
784 entity path_build(entity next, vector where, entity prev, entity start)
789 if(buildpath_nodefilter)
790 if(buildpath_nodefilter(next.origin,where,prev.origin))
796 path.path_next = next;
798 setorigin(path,where);
801 path.classname = "path_end";
805 path.classname = "path_start";
807 path.classname = "path_node";
813 entity pathlib_astar(vector from,vector to)
815 entity path, start, end, open, n, ln;
816 float ptime, ftime, ctime;
818 ptime = gettime(GETTIME_REALTIME);
822 // Select water<->land capable node make/link
823 pathlib_makenode = pathlib_makenode_adaptive;
824 // Select XYZ cost estimate
825 pathlib_heuristic = pathlib_h_diagonal3;
826 // Select distance + waterfactor cost
827 pathlib_cost = pathlib_g_euclidean_water;
828 // Select star expander
829 pathlib_expandnode = pathlib_expandnode_star;
830 // Select walk simulation movement test
831 pathlib_movenode = pathlib_walknode;
832 // Filter final nodes by direction
833 buildpath_nodefilter = buildpath_nodefilter_directional;
835 // If the start is in water we need diffrent settings
838 // Select volumetric node expaner
839 pathlib_expandnode = pathlib_expandnode_box;
841 // Water movement test
842 pathlib_movenode = pathlib_swimnode;
849 closedlist = spawn();
854 pathlib_closed_cnt = 0;
855 pathlib_open_cnt = 0;
856 pathlib_made_cnt = 0;
857 pathlib_merge_cnt = 0;
858 pathlib_searched_cnt = 0;
859 pathlib_bestopen_seached = 0;
860 pathlib_bestcash_hits = 0;
861 pathlib_bestcash_saved = 0;
862 pathlib_recycle_cnt = 0;
864 pathlib_gridsize = 128;
865 pathlib_movecost = pathlib_gridsize;
866 pathlib_movecost_diag = vlen(('1 1 0' * pathlib_gridsize));
867 pathlib_movecost_waterfactor = 1.1;
868 pathlib_foundgoal = 0;
870 walknode_boxmax = self.maxs * 1.5;
871 walknode_boxmin = self.mins * 1.5;
873 pathlib_edge_check_size = (vlen(walknode_boxmin - walknode_boxmax) * 0.5);
875 walknode_boxup = '0 0 2' * self.maxs.z;
876 walknode_stepsize = 32;
877 walknode_stepup = '0 0 1' * walknode_stepsize;
878 walknode_maxdrop = '0 0 3' * walknode_stepsize;
880 from.x = fsnap(from.x,pathlib_gridsize);
881 from.y = fsnap(from.y,pathlib_gridsize);
883 to.x = fsnap(to.x,pathlib_gridsize);
884 to.y = fsnap(to.y,pathlib_gridsize);
886 dprint("AStar init. ", ftos(pathlib_scraplist_cnt), " nodes on scrap\n");
887 path = pathlib_mknode(from,world);
888 pathlib_close_node(path,to);
889 if(pathlib_foundgoal)
891 dprint("AStar: Goal found on first node!\n");
895 open.classname = "path_end";
896 setorigin(open,path.origin);
903 if(pathlib_expandnode(path,from,to) <= 0)
905 dprint("AStar path fail.\n");
911 best_open_node = pathlib_getbestopen();
913 pathlib_close_node(best_open_node,to);
914 if(inwater(n.origin))
915 pathlib_expandnode_box(n,from,to);
917 pathlib_expandnode_star(n,from,to);
919 while(pathlib_open_cnt)
921 best_open_node = pathlib_getbestopen();
923 pathlib_close_node(best_open_node,to);
925 if(inwater(n.origin))
926 pathlib_expandnode_box(n,from,to);
928 pathlib_expandnode(n,from,to);
930 if(pathlib_foundgoal)
932 dprint("Target found. Rebuilding and filtering path...\n");
933 ftime = gettime(GETTIME_REALTIME);
934 ptime = ftime - ptime;
936 start = path_build(world,path.origin,world,world);
937 end = path_build(world,goal_node.origin,world,start);
941 for(open = goal_node; open.path_prev != path; open = open.path_prev)
943 n = path_build(ln,open.origin,open.path_prev,start);
949 ftime = gettime(GETTIME_REALTIME) - ftime;
951 ctime = gettime(GETTIME_REALTIME);
953 ctime = gettime(GETTIME_REALTIME) - ctime;
957 pathlib_showpath2(start);
959 dprint("Time used - pathfinding: ", ftos(ptime),"\n");
960 dprint("Time used - rebuild & filter: ", ftos(ftime),"\n");
961 dprint("Time used - cleanup: ", ftos(ctime),"\n");
962 dprint("Time used - total: ", ftos(ptime + ftime + ctime),"\n");
963 dprint("Time used - # frames: ", ftos(ceil((ptime + ftime + ctime) / sys_frametime)),"\n\n");
964 dprint("Nodes - created: ", ftos(pathlib_made_cnt),"\n");
965 dprint("Nodes - open: ", ftos(pathlib_open_cnt),"\n");
966 dprint("Nodes - merged: ", ftos(pathlib_merge_cnt),"\n");
967 dprint("Nodes - closed: ", ftos(pathlib_closed_cnt),"\n");
968 dprint("Nodes - searched: ", ftos(pathlib_searched_cnt),"\n");
970 if(pathlib_recycle_cnt)
971 dprint("Nodes - make/reuse: ", ftos(pathlib_made_cnt / pathlib_recycle_cnt),"\n");
972 if(pathlib_recycle_cnt)
973 dprint("Nodes - reused: ", ftos(pathlib_recycle_cnt),"\n");
975 dprint("Nodes bestopen searched: ", ftos(pathlib_bestopen_seached),"\n");
976 dprint("Nodes bestcash - hits: ", ftos(pathlib_bestcash_hits),"\n");
977 dprint("Nodes bestcash - save: ", ftos(pathlib_bestcash_saved),"\n");
978 dprint("AStar done. ", ftos(pathlib_scraplist_cnt), " nodes on scrap\n\n");
984 dprint("A* Faild to find a path! Try a smaller gridsize.\n");