]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/default/waypoints.qc
Fix waypoints for warpzones with oblique or horizontal warp plane
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / default / waypoints.qc
index e545c6c85f8a450855ead5f5675aba6066390237..c3b67a378fef08ac1a7823ceeb7064f049ef86a3 100644 (file)
@@ -1,5 +1,7 @@
 #include "waypoints.qh"
 
+#include <server/defs.qh>
+#include <server/miscfunctions.qh>
 #include "cvars.qh"
 
 #include "bot.qh"
@@ -24,7 +26,14 @@ void waypoint_unreachable(entity pl)
                it.colormod = '0.5 0.5 0.5';
                it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
        });
+
        entity e2 = navigation_findnearestwaypoint(pl, false);
+       if(!e2)
+       {
+               LOG_INFOF("Can't find any waypoint nearby\n");
+               return;
+       }
+
        navigation_markroutes(pl, e2);
 
        int j = 0;
@@ -161,6 +170,8 @@ void waypoint_setupmodel(entity wp)
                        wp.colormod = '1 0 0';
                else if (wp.wpflags & WAYPOINTFLAG_GENERATED)
                        wp.colormod = '1 1 0';
+               else if (wp.wphardwired)
+                       wp.colormod = '0.5 0 1';
                else
                        wp.colormod = '1 1 1';
        }
@@ -175,9 +186,8 @@ entity waypoint_spawn(vector m1, vector m2, float f)
 {
        if(!(f & WAYPOINTFLAG_PERSONAL))
        {
-               vector em1 = m1;
-               vector em2 = m2;
-               if (m1 == m2)
+               vector em1 = m1, em2 = m2;
+               if (!(f & WAYPOINTFLAG_GENERATED) && m1 == m2)
                {
                        em1 = m1 - '8 8 8';
                        em2 = m2 + '8 8 8';
@@ -213,7 +223,7 @@ entity waypoint_spawn(vector m1, vector m2, float f)
                        {
                                if(autocvar_developer)
                                {
-                                       LOG_INFO("A generated waypoint is stuck in solid at ", vtos(w.origin), "\n");
+                                       LOG_INFO("A generated waypoint is stuck in solid at ", vtos(w.origin));
                                        backtrace("Waypoint stuck");
                                }
                        }
@@ -260,8 +270,13 @@ void waypoint_spawn_fromeditor(entity pl)
 
        LABEL(add_wp);
        e = waypoint_spawn(org, org, 0);
+       if(!e)
+       {
+               LOG_INFOF("Couldn't spawn waypoint at %v\n", org);
+               return;
+       }
        waypoint_schedulerelink(e);
-       bprint(strcat("Waypoint spawned at ", vtos(org), "\n"));
+       bprint(strcat("Waypoint spawned at ", vtos(e.origin), "\n"));
        if(sym)
        {
                org = waypoint_getSymmetricalOrigin(e.origin, ctf_flags);
@@ -391,24 +406,78 @@ bool waypoint_islinked(entity from, entity to)
        return false;
 }
 
-float waypoint_getdistancecost_simple(float dist)
+void waypoint_updatecost_foralllinks()
+{
+       IL_EACH(g_waypoints, !(it.wpflags & WAYPOINTFLAG_TELEPORT),
+       {
+               if(it.wp00) it.wp00mincost = waypoint_getlinkcost(it, it.wp00);
+               if(it.wp01) it.wp01mincost = waypoint_getlinkcost(it, it.wp01);
+               if(it.wp02) it.wp02mincost = waypoint_getlinkcost(it, it.wp02);
+               if(it.wp03) it.wp03mincost = waypoint_getlinkcost(it, it.wp03);
+               if(it.wp04) it.wp04mincost = waypoint_getlinkcost(it, it.wp04);
+               if(it.wp05) it.wp05mincost = waypoint_getlinkcost(it, it.wp05);
+               if(it.wp06) it.wp06mincost = waypoint_getlinkcost(it, it.wp06);
+               if(it.wp07) it.wp07mincost = waypoint_getlinkcost(it, it.wp07);
+               if(it.wp08) it.wp08mincost = waypoint_getlinkcost(it, it.wp08);
+               if(it.wp09) it.wp09mincost = waypoint_getlinkcost(it, it.wp09);
+               if(it.wp10) it.wp10mincost = waypoint_getlinkcost(it, it.wp10);
+               if(it.wp11) it.wp11mincost = waypoint_getlinkcost(it, it.wp11);
+               if(it.wp12) it.wp12mincost = waypoint_getlinkcost(it, it.wp12);
+               if(it.wp13) it.wp13mincost = waypoint_getlinkcost(it, it.wp13);
+               if(it.wp14) it.wp14mincost = waypoint_getlinkcost(it, it.wp14);
+               if(it.wp15) it.wp15mincost = waypoint_getlinkcost(it, it.wp15);
+               if(it.wp16) it.wp16mincost = waypoint_getlinkcost(it, it.wp16);
+               if(it.wp17) it.wp17mincost = waypoint_getlinkcost(it, it.wp17);
+               if(it.wp18) it.wp18mincost = waypoint_getlinkcost(it, it.wp18);
+               if(it.wp19) it.wp19mincost = waypoint_getlinkcost(it, it.wp19);
+               if(it.wp20) it.wp20mincost = waypoint_getlinkcost(it, it.wp20);
+               if(it.wp21) it.wp21mincost = waypoint_getlinkcost(it, it.wp21);
+               if(it.wp22) it.wp22mincost = waypoint_getlinkcost(it, it.wp22);
+               if(it.wp23) it.wp23mincost = waypoint_getlinkcost(it, it.wp23);
+               if(it.wp24) it.wp24mincost = waypoint_getlinkcost(it, it.wp24);
+               if(it.wp25) it.wp25mincost = waypoint_getlinkcost(it, it.wp25);
+               if(it.wp26) it.wp26mincost = waypoint_getlinkcost(it, it.wp26);
+               if(it.wp27) it.wp27mincost = waypoint_getlinkcost(it, it.wp27);
+               if(it.wp28) it.wp28mincost = waypoint_getlinkcost(it, it.wp28);
+               if(it.wp29) it.wp29mincost = waypoint_getlinkcost(it, it.wp29);
+               if(it.wp30) it.wp30mincost = waypoint_getlinkcost(it, it.wp30);
+               if(it.wp31) it.wp31mincost = waypoint_getlinkcost(it, it.wp31);
+       });
+}
+
+float waypoint_getlinearcost(float dist)
 {
+       if(skill >= autocvar_bot_ai_bunnyhop_skilloffset)
+               return dist / (autocvar_sv_maxspeed * 1.25);
        return dist / autocvar_sv_maxspeed;
 }
+float waypoint_getlinearcost_underwater(float dist)
+{
+       // NOTE: this value is hardcoded on the engine too, see SV_WaterMove
+       return dist / (autocvar_sv_maxspeed * 0.7);
+}
 
-float waypoint_getdistancecost(vector from, vector to)
+float waypoint_gettravelcost(vector from, vector to, entity from_ent, entity to_ent)
 {
-       float c = waypoint_getdistancecost_simple(vlen(to - from));
+       bool submerged_from = navigation_check_submerged_state(from_ent, from);
+       bool submerged_to = navigation_check_submerged_state(to_ent, to);
+
+       if (submerged_from && submerged_to)
+               return waypoint_getlinearcost_underwater(vlen(to - from));
+
+       float c = waypoint_getlinearcost(vlen(to - from));
 
        float height = from.z - to.z;
-       if(height > 0 && autocvar_sv_gravity > 0)
+       if(height > jumpheight_vec.z && autocvar_sv_gravity > 0)
        {
                float height_cost = sqrt(height / (autocvar_sv_gravity / 2));
-               float xydist = vlen(vec2(to - from));
-               float xydist_cost = xydist / autocvar_sv_maxspeed;
-               if(max(height_cost, xydist_cost) < c)
-                       c = max(height_cost, xydist_cost);
+               c = waypoint_getlinearcost(vlen(vec2(to - from))); // xy distance cost
+               if(height_cost > c)
+                       c = height_cost;
        }
+
+       if (submerged_from || submerged_to)
+               return (c + waypoint_getlinearcost_underwater(vlen(to - from))) / 2;
        return c;
 }
 
@@ -418,19 +487,19 @@ float waypoint_getlinkcost(entity from, entity to)
        vector v2 = to.origin;
        if (from.wpisbox)
        {
-               vector m1 = to.absmin, m2 = to.absmax;
-               v1_x = bound(m1_x, v1_x, m2_x);
-               v1_y = bound(m1_y, v1_y, m2_y);
-               v1_z = bound(m1_z, v1_z, m2_z);
+               vector m1 = from.absmin, m2 = from.absmax;
+               v1.x = bound(m1.x, v2.x, m2.x);
+               v1.y = bound(m1.y, v2.y, m2.y);
+               v1.z = bound(m1.z, v2.z, m2.z);
        }
        if (to.wpisbox)
        {
-               vector m1 = from.absmin, m2 = from.absmax;
-               v2_x = bound(m1_x, v2_x, m2_x);
-               v2_y = bound(m1_y, v2_y, m2_y);
-               v2_z = bound(m1_z, v2_z, m2_z);
+               vector m1 = to.absmin, m2 = to.absmax;
+               v2.x = bound(m1.x, v1.x, m2.x);
+               v2.y = bound(m1.y, v1.y, m2.y);
+               v2.z = bound(m1.z, v1.z, m2.z);
        }
-       return waypoint_getdistancecost(v1, v2);
+       return waypoint_gettravelcost(v1, v2, from, to);
 }
 
 // add a new link to the spawnfunc_waypoint, replacing the furthest link it already has
@@ -490,15 +559,14 @@ void waypoint_addlink(entity from, entity to)
 // (SLOW!)
 void waypoint_think(entity this)
 {
-       vector sv, sm1, sm2, ev, em1, em2, dv;
+       vector sv = '0 0 0', sv2 = '0 0 0', ev = '0 0 0', ev2 = '0 0 0', dv;
+       float sv2_height = 0, ev2_height = 0;
 
        bot_calculate_stepheightvec();
 
        bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL);
 
        //dprint("waypoint_think wpisbox = ", ftos(this.wpisbox), "\n");
-       sm1 = this.origin + this.mins;
-       sm2 = this.origin + this.maxs;
        IL_EACH(g_waypoints, this != it,
        {
                if (boxesoverlap(this.absmin, this.absmax, it.absmin, it.absmax))
@@ -514,16 +582,10 @@ void waypoint_think(entity this)
                                ++relink_pvsculled;
                                continue;
                        }
-                       sv = it.origin;
-                       sv.x = bound(sm1_x, sv.x, sm2_x);
-                       sv.y = bound(sm1_y, sv.y, sm2_y);
-                       sv.z = bound(sm1_z, sv.z, sm2_z);
-                       ev = this.origin;
-                       em1 = it.origin + it.mins;
-                       em2 = it.origin + it.maxs;
-                       ev.x = bound(em1_x, ev.x, em2_x);
-                       ev.y = bound(em1_y, ev.y, em2_y);
-                       ev.z = bound(em1_z, ev.z, em2_z);
+
+                       SET_TRACEWALK_DESTCOORDS_2(this, it.origin, sv, sv2, sv2_height);
+                       SET_TRACEWALK_DESTCOORDS_2(it, this.origin, ev, ev2, ev2_height);
+
                        dv = ev - sv;
                        dv.z = 0;
                        if(vdist(dv, >=, 1050)) // max search distance in XY
@@ -531,35 +593,30 @@ void waypoint_think(entity this)
                                ++relink_lengthculled;
                                continue;
                        }
+
                        navigation_testtracewalk = 0;
-                       if (!this.wpisbox)
-                       {
-                               tracebox(sv - PL_MIN_CONST.z * '0 0 1', PL_MIN_CONST, PL_MAX_CONST, sv, false, this);
-                               if (!trace_startsolid)
-                               {
-                                       //dprint("sv deviation", vtos(trace_endpos - sv), "\n");
-                                       sv = trace_endpos + '0 0 1';
-                               }
-                       }
-                       if (!it.wpisbox)
-                       {
-                               tracebox(ev - PL_MIN_CONST.z * '0 0 1', PL_MIN_CONST, PL_MAX_CONST, ev, false, it);
-                               if (!trace_startsolid)
-                               {
-                                       //dprint("ev deviation", vtos(trace_endpos - ev), "\n");
-                                       ev = trace_endpos + '0 0 1';
-                               }
-                       }
+
                        //traceline(this.origin, it.origin, false, NULL);
                        //if (trace_fraction == 1)
-                       if (!this.wpisbox && tracewalk(this, sv, PL_MIN_CONST, PL_MAX_CONST, ev, MOVE_NOMONSTERS))
-                               waypoint_addlink(this, it);
-                       else
+                       if (this.wpisbox)
                                relink_walkculled += 0.5;
-                       if (!it.wpisbox && tracewalk(it, ev, PL_MIN_CONST, PL_MAX_CONST, sv, MOVE_NOMONSTERS))
-                               waypoint_addlink(it, this);
                        else
+                       {
+                               if (tracewalk(this, sv, PL_MIN_CONST, PL_MAX_CONST, ev2, ev2_height, MOVE_NOMONSTERS))
+                                       waypoint_addlink(this, it);
+                               else
+                                       relink_walkculled += 0.5;
+                       }
+
+                       if (it.wpisbox)
                                relink_walkculled += 0.5;
+                       else
+                       {
+                               if (tracewalk(it, ev, PL_MIN_CONST, PL_MAX_CONST, sv2, sv2_height, MOVE_NOMONSTERS))
+                                       waypoint_addlink(it, this);
+                               else
+                                       relink_walkculled += 0.5;
+                       }
                }
        });
        navigation_testtracewalk = 0;
@@ -644,8 +701,27 @@ bool waypoint_load_links()
                return false;
        }
 
+       bool parse_comments = true;
+       float ver = 0;
+
        while ((s = fgets(file)))
        {
+               if(parse_comments)
+               {
+                       if(substring(s, 0, 2) == "//")
+                       {
+                               if(substring(s, 2, 17) == "WAYPOINT_VERSION ")
+                                       ver = stof(substring(s, 19, -1));
+                               continue;
+                       }
+                       else
+                       {
+                               if(ver < WAYPOINT_VERSION)
+                                       return false;
+                               parse_comments = false;
+                       }
+               }
+
                tokens = tokenizebyseparator(s, "*");
 
                if (tokens!=2)
@@ -768,7 +844,7 @@ void waypoint_load_or_remove_links_hardwired(bool removal_mode)
                        if(!found)
                        {
                                if(!removal_mode)
-                                       LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_from_pos), ". Path skipped\n"));
+                                       LOG_INFO("NOTICE: Can not find waypoint at ", vtos(wp_from_pos), ". Path skipped");
                                continue;
                        }
                }
@@ -790,7 +866,7 @@ void waypoint_load_or_remove_links_hardwired(bool removal_mode)
                if(!found)
                {
                        if(!removal_mode)
-                               LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_to_pos), ". Path skipped\n"));
+                               LOG_INFO("NOTICE: Can not find waypoint at ", vtos(wp_to_pos), ". Path skipped");
                        continue;
                }
 
@@ -804,6 +880,8 @@ void waypoint_load_or_remove_links_hardwired(bool removal_mode)
                waypoint_addlink(wp_from, wp_to);
                wp_from.wphardwired = true;
                wp_to.wphardwired = true;
+               waypoint_setupmodel(wp_from);
+               waypoint_setupmodel(wp_to);
        }
 
        fclose(file);
@@ -812,9 +890,6 @@ void waypoint_load_or_remove_links_hardwired(bool removal_mode)
                LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.hardwired");
 }
 
-void waypoint_load_links_hardwired() { waypoint_load_or_remove_links_hardwired(false); }
-void waypoint_remove_links_hardwired() { waypoint_load_or_remove_links_hardwired(true); }
-
 entity waypoint_get_link(entity w, float i)
 {
        switch(i)
@@ -865,10 +940,12 @@ void waypoint_save_links()
        int file = fopen(filename, FILE_WRITE);
        if (file < 0)
        {
-               LOG_INFOF("waypoint link save to %s failed\n", filename);
+               LOG_INFOF("waypoint link save to %s failed", filename);
                return;
        }
 
+       fputs(file, strcat("//", "WAYPOINT_VERSION ", ftos_decimals(WAYPOINT_VERSION, 2), "\n"));
+
        int c = 0;
        IL_EACH(g_waypoints, true,
        {
@@ -886,7 +963,7 @@ void waypoint_save_links()
        fclose(file);
        botframe_cachedwaypointlinks = true;
 
-       LOG_INFOF("saved %d waypoint links to maps/%s.waypoints.cache\n", c, mapname);
+       LOG_INFOF("saved %d waypoint links to maps/%s.waypoints.cache", c, mapname);
 
        waypoint_load_links_hardwired();
 }
@@ -901,10 +978,16 @@ void waypoint_saveall()
                waypoint_save_links(); // save anyway?
                botframe_loadedforcedlinks = false;
 
-               LOG_INFOF("waypoint links: save to %s failed\n", filename);
+               LOG_INFOF("waypoint links: save to %s failed", filename);
                return;
        }
 
+       // add 3 comments to not break compatibility with older Xonotic versions
+       // (they are read as a waypoint with origin '0 0 0' and flag 0 though)
+       fputs(file, strcat("//", "WAYPOINT_VERSION ", ftos_decimals(WAYPOINT_VERSION, 2), "\n"));
+       fputs(file, strcat("//", "\n"));
+       fputs(file, strcat("//", "\n"));
+
        int c = 0;
        IL_EACH(g_waypoints, true,
        {
@@ -924,7 +1007,7 @@ void waypoint_saveall()
        waypoint_save_links();
        botframe_loadedforcedlinks = false;
 
-       LOG_INFOF("saved %d waypoints to maps/%s.waypoints\n", c, mapname);
+       LOG_INFOF("saved %d waypoints to maps/%s.waypoints", c, mapname);
 }
 
 // load waypoints from file
@@ -938,49 +1021,73 @@ float waypoint_loadall()
        filename = strcat("maps/", mapname);
        filename = strcat(filename, ".waypoints");
        file = fopen(filename, FILE_READ);
-       if (file >= 0)
+
+       bool parse_comments = true;
+       float ver = 0;
+
+       if (file < 0)
+       {
+               LOG_TRACE("waypoint load from ", filename, " failed");
+               return 0;
+       }
+
+       while ((s = fgets(file)))
        {
-               while ((s = fgets(file)))
+               if(parse_comments)
                {
-                       m1 = stov(s);
-                       s = fgets(file);
-                       if (!s)
-                               break;
-                       m2 = stov(s);
-                       s = fgets(file);
-                       if (!s)
-                               break;
-                       fl = stof(s);
-                       waypoint_spawn(m1, m2, fl);
-                       if (m1 == m2)
-                               cwp = cwp + 1;
+                       if(substring(s, 0, 2) == "//")
+                       {
+                               if(substring(s, 2, 17) == "WAYPOINT_VERSION ")
+                                       ver = stof(substring(s, 19, -1));
+                               continue;
+                       }
                        else
-                               cwb = cwb + 1;
+                       {
+                               if(floor(ver) < floor(WAYPOINT_VERSION))
+                                       LOG_TRACE("waypoints for this map are outdated");
+                               parse_comments = false;
+                       }
                }
-               fclose(file);
-               LOG_TRACE("loaded ", ftos(cwp), " waypoints and ", ftos(cwb), " wayboxes from maps/", mapname, ".waypoints");
-       }
-       else
-       {
-               LOG_TRACE("waypoint load from ", filename, " failed");
+               m1 = stov(s);
+               s = fgets(file);
+               if (!s)
+                       break;
+               m2 = stov(s);
+               s = fgets(file);
+               if (!s)
+                       break;
+               fl = stof(s);
+               waypoint_spawn(m1, m2, fl);
+               if (m1 == m2)
+                       cwp = cwp + 1;
+               else
+                       cwb = cwb + 1;
        }
+       fclose(file);
+       LOG_TRACE("loaded ", ftos(cwp), " waypoints and ", ftos(cwb), " wayboxes from maps/", mapname, ".waypoints");
+
        return cwp + cwb;
 }
 
-vector waypoint_fixorigin(vector position)
+#define waypoint_fixorigin(position, tracetest_ent) \
+       waypoint_fixorigin_down_dir(position, tracetest_ent, '0 0 -1')
+
+vector waypoint_fixorigin_down_dir(vector position, entity tracetest_ent, vector down_dir)
 {
-       tracebox(position + '0 0 1' * (1 - PL_MIN_CONST.z), PL_MIN_CONST, PL_MAX_CONST, position + '0 0 -512', MOVE_NOMONSTERS, NULL);
+       tracebox(position + '0 0 1', PL_MIN_CONST, PL_MAX_CONST, position + down_dir * 3000, MOVE_NOMONSTERS, tracetest_ent);
+       if(trace_startsolid)
+               tracebox(position + '0 0 1' * (1 - PL_MIN_CONST.z / 2), PL_MIN_CONST, PL_MAX_CONST, position + down_dir * 3000, MOVE_NOMONSTERS, tracetest_ent);
+       if(trace_startsolid)
+               tracebox(position + '0 0 1' * (1 - PL_MIN_CONST.z), PL_MIN_CONST, PL_MAX_CONST, position + down_dir * 3000, MOVE_NOMONSTERS, tracetest_ent);
        if(trace_fraction < 1)
                position = trace_endpos;
-       //traceline(position, position + '0 0 -512', MOVE_NOMONSTERS, NULL);
-       //print("position is ", ftos(trace_endpos_z - position_z), " above solid\n");
        return position;
 }
 
 void waypoint_spawnforitem_force(entity e, vector org)
 {
        // Fix the waypoint altitude if necessary
-       org = waypoint_fixorigin(org);
+       org = waypoint_fixorigin(org, NULL);
 
        // don't spawn an item spawnfunc_waypoint if it already exists
        IL_EACH(g_waypoints, true,
@@ -1029,17 +1136,25 @@ void waypoint_spawnforteleporter_boxes(entity e, int teleport_flag, vector org1,
        e.nearestwaypointtimeout = -1;
 }
 
-void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken)
+void waypoint_spawnforteleporter_wz(entity e, vector org, vector destination, float timetaken, vector down_dir, entity tracetest_ent)
 {
-       org = waypoint_fixorigin(org);
-       destination = waypoint_fixorigin(destination);
+       // warpzones with oblique warp plane rely on down_dir to snap waypoints
+       // to the ground without leaving the warp plane
+       // warpzones with horizontal warp plane (down_dir.x == -1) generate
+       // destination waypoint snapped to the ground (leaving warpzone), source
+       // waypoint in the center of the warp plane
+       if(down_dir.x != -1)
+               org = waypoint_fixorigin_down_dir(org, tracetest_ent, down_dir);
+       if(down_dir.x == -1)
+               down_dir = '0 0 -1';
+       destination = waypoint_fixorigin_down_dir(destination, tracetest_ent, down_dir);
        waypoint_spawnforteleporter_boxes(e, WAYPOINTFLAG_TELEPORT, org, org, destination, destination, timetaken);
 }
 
-void waypoint_spawnforteleporter(entity e, vector destination, float timetaken)
+void waypoint_spawnforteleporter(entity e, vector destination, float timetaken, entity tracetest_ent)
 {
-       destination = waypoint_fixorigin(destination);
-       waypoint_spawnforteleporter_boxes(e, WAYPOINTFLAG_TELEPORT, e.absmin, e.absmax, destination, destination, timetaken);
+       destination = waypoint_fixorigin(destination, tracetest_ent);
+       waypoint_spawnforteleporter_boxes(e, WAYPOINTFLAG_TELEPORT, e.absmin - PL_MAX_CONST + '1 1 1', e.absmax - PL_MIN_CONST + '-1 -1 -1', destination, destination, timetaken);
 }
 
 entity waypoint_spawnpersonal(entity this, vector position)
@@ -1049,7 +1164,7 @@ entity waypoint_spawnpersonal(entity this, vector position)
        // drop the waypoint to a proper location:
        //   first move it up by a player height
        //   then move it down to hit the floor with player bbox size
-       position = waypoint_fixorigin(position);
+       position = waypoint_fixorigin(position, this);
 
        w = waypoint_spawn(position, position, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_PERSONAL);
        w.nearestwaypoint = NULL;
@@ -1178,7 +1293,7 @@ float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .en
 
                // if wp -> porg, then OK
                float maxdist;
-               if(navigation_waypoint_will_link(wp.origin, porg, p, walkfromwp, 1050))
+               if(navigation_waypoint_will_link(wp.origin, porg, p, porg, 0, wp.origin, 0, walkfromwp, 1050))
                {
                        // we may find a better one
                        maxdist = vlen(wp.origin - porg);
@@ -1194,8 +1309,8 @@ float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .en
                {
                        float d = vlen(wp.origin - it.origin) + vlen(it.origin - porg);
                        if(d < bestdist)
-                       if(navigation_waypoint_will_link(wp.origin, it.origin, p, walkfromwp, 1050))
-                       if(navigation_waypoint_will_link(it.origin, porg, p, walkfromwp, 1050))
+                       if(navigation_waypoint_will_link(wp.origin, it.origin, p, it.origin, 0, wp.origin, 0, walkfromwp, 1050))
+                       if(navigation_waypoint_will_link(it.origin, porg, p, porg, 0, it.origin, 0, walkfromwp, 1050))
                        {
                                bestdist = d;
                                p.(fld) = it;
@@ -1203,7 +1318,7 @@ float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .en
                });
                if(bestdist < maxdist)
                {
-                       LOG_INFO("update chain to new nearest WP ", etos(p.(fld)), "\n");
+                       LOG_INFO("update chain to new nearest WP ", etos(p.(fld)));
                        return 0;
                }
 
@@ -1249,7 +1364,7 @@ float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .en
 
                if(wp)
                {
-                       if(!navigation_waypoint_will_link(wp.origin, o, p, walkfromwp, 1050))
+                       if(!navigation_waypoint_will_link(wp.origin, o, p, o, 0, wp.origin, 0, walkfromwp, 1050))
                        {
                                // we cannot walk from wp.origin to o
                                // get closer to tmax
@@ -1275,14 +1390,14 @@ float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .en
                // if we get here, o is valid regarding waypoints
                // check if o is connected right to the player
                // we break if it succeeds, as that means o is a good waypoint location
-               if(navigation_waypoint_will_link(o, porg, p, walkfromwp, 1050))
+               if(navigation_waypoint_will_link(o, porg, p, porg, 0, o, 0, walkfromwp, 1050))
                        break;
 
                // o is no good, we need to get closer to the player
                tmax = t;
        }
 
-       LOG_INFO("spawning a waypoint for connecting to ", etos(wp), "\n");
+       LOG_INFO("spawning a waypoint for connecting to ", etos(wp));
        botframe_autowaypoints_createwp(o, p, fld, 0);
        return 1;
 }
@@ -1298,7 +1413,7 @@ void botframe_autowaypoints_fix(entity p, float walkfromwp, .entity fld)
        if(r != -1)
                return;
 
-       LOG_INFO("emergency: got no good nearby WP to build a link from, starting a new chain\n");
+       LOG_INFO("emergency: got no good nearby WP to build a link from, starting a new chain");
        if(!botframe_autowaypoints_fixdown(p.origin))
                return; // shouldn't happen, caught above
        botframe_autowaypoints_createwp(trace_endpos, p, fld, WAYPOINTFLAG_PROTECTED);
@@ -1372,7 +1487,7 @@ LABEL(next)
 
        IL_EACH(g_waypoints, !(it.wpflags & (WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END)),
        {
-               LOG_INFOF("Removed a waypoint at %v. Try again for more!\n", it.origin);
+               LOG_INFOF("Removed a waypoint at %v. Try again for more!", it.origin);
                te_explosion(it.origin);
                waypoint_remove(it);
                break;
@@ -1386,12 +1501,12 @@ LABEL(next)
 
 void botframe_autowaypoints()
 {
-       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && !IS_DEAD(it), LAMBDA(
+       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && !IS_DEAD(it), {
                // going back is broken, so only fix waypoints to walk TO the player
                //botframe_autowaypoints_fix(p, false, botframe_autowaypoints_lastwp0);
                botframe_autowaypoints_fix(it, true, botframe_autowaypoints_lastwp1);
                //te_explosion(p.botframe_autowaypoints_lastwp0.origin);
-       ));
+       });
 
        if (autocvar_g_waypointeditor_auto >= 2) {
                botframe_deleteuselesswaypoints();