]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/lib/pathlib/main.qc
Bots: move supporting code into bot directory
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / lib / pathlib / main.qc
diff --git a/qcsrc/server/bot/lib/pathlib/main.qc b/qcsrc/server/bot/lib/pathlib/main.qc
new file mode 100644 (file)
index 0000000..39e23bd
--- /dev/null
@@ -0,0 +1,566 @@
+#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;
+}