]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/default/waypoints.qc
Merge branch 'terencehill/bot_waypoints' into terencehill/bot_ai
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / default / waypoints.qc
index 50af1d5198c6d38e0d0f5e78d314bdec9ad39476..fa82d926b63e92776cfd293dbebccf15ba20ad04 100644 (file)
@@ -179,19 +179,12 @@ void waypoint_setupmodel(entity wp)
                wp.model = "";
 }
 
-// create a new spawnfunc_waypoint and automatically link it to other waypoints, and link
-// them back to it as well
-// (suitable for spawnfunc_waypoint editor)
 entity waypoint_spawn(vector m1, vector m2, float f)
 {
-       if(!(f & WAYPOINTFLAG_PERSONAL))
+       if(!(f & (WAYPOINTFLAG_PERSONAL | WAYPOINTFLAG_GENERATED)) && m1 == m2)
        {
-               vector em1 = m1, em2 = m2;
-               if (!(f & WAYPOINTFLAG_GENERATED) && m1 == m2)
-               {
-                       em1 = m1 - '8 8 8';
-                       em2 = m2 + '8 8 8';
-               }
+               vector em1 = m1 - '8 8 8';
+               vector em2 = m2 + '8 8 8';
                IL_EACH(g_waypoints, boxesoverlap(em1, em2, it.absmin, it.absmax),
                {
                        return it;
@@ -223,7 +216,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");
                                }
                        }
@@ -559,8 +552,8 @@ void waypoint_addlink(entity from, entity to)
 // (SLOW!)
 void waypoint_think(entity this)
 {
-       vector sv, sv2, ev, ev2, dv;
-       float sv2_height, ev2_height;
+       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();
 
@@ -583,8 +576,12 @@ void waypoint_think(entity this)
                                continue;
                        }
 
-                       SET_TRACEWALK_DESTCOORDS_2(this, it.origin, sv, sv2, sv2_height);
-                       SET_TRACEWALK_DESTCOORDS_2(it, this.origin, ev, ev2, ev2_height);
+                       sv = set_tracewalk_dest_2(this, it.origin);
+                       sv2 = tracewalk_dest;
+                       sv2_height = tracewalk_dest_height;
+                       ev = set_tracewalk_dest_2(it, this.origin);
+                       ev2 = tracewalk_dest;
+                       ev2_height = tracewalk_dest_height;
 
                        dv = ev - sv;
                        dv.z = 0;
@@ -682,33 +679,76 @@ void waypoint_schedulerelinkall()
        waypoint_load_links_hardwired();
 }
 
+#define GET_GAMETYPE_EXTENSION() ((g_race) ? ".race" : "")
+
 // Load waypoint links from file
 bool waypoint_load_links()
 {
-       string filename, s;
+       string s;
        float file, tokens, c = 0, found;
        entity wp_from = NULL, wp_to;
        vector wp_to_pos, wp_from_pos;
-       filename = strcat("maps/", mapname);
-       filename = strcat(filename, ".waypoints.cache");
+
+       string gt_ext = GET_GAMETYPE_EXTENSION();
+
+       string filename = sprintf("maps/%s.waypoints.cache", strcat(mapname, gt_ext));
        file = fopen(filename, FILE_READ);
 
+       if (gt_ext != "" && file < 0)
+       {
+               // if race waypoint file doesn't exist load the default one
+               filename = sprintf("maps/%s.waypoints.cache", mapname);
+               file = fopen(filename, FILE_READ);
+       }
+
        if (file < 0)
        {
-               LOG_TRACE("waypoint links load from ");
-               LOG_TRACE(filename);
-               LOG_TRACE(" failed");
+               LOG_TRACE("waypoint links load from ", filename, " failed");
+               waypoint_schedulerelinkall();
                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)
+                               {
+                                       LOG_TRACE("waypoint links for this map are outdated.");
+                                       if (g_assault)
+                                       {
+                                               LOG_TRACE("Assault waypoint links need to be manually updated in the editor");
+                                       }
+                                       else
+                                       {
+                                               LOG_TRACE("automatically updating...");
+                                               waypoint_schedulerelinkall();
+                                               fclose(file);
+                                               return false;
+                                       }
+                               }
+                               parse_comments = false;
+                       }
+               }
+
                tokens = tokenizebyseparator(s, "*");
 
                if (tokens!=2)
                {
                        // bad file format
                        fclose(file);
+                       waypoint_schedulerelinkall(); // link all the autogenerated waypoints (teleporters)
                        return false;
                }
 
@@ -736,7 +776,6 @@ bool waypoint_load_links()
                                LOG_TRACE("waypoint_load_links: couldn't find 'from' waypoint at ", vtos(wp_from_pos));
                                continue;
                        }
-
                }
 
                // Search "to" waypoint
@@ -765,7 +804,19 @@ bool waypoint_load_links()
 
        fclose(file);
 
-       LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.cache");
+       LOG_TRACE("loaded ", ftos(c), " waypoint links from ", filename);
+
+       bool scheduled = false;
+       IL_EACH(g_waypoints, it.wpflags & WAYPOINTFLAG_ITEM,
+       {
+               if (!it.wp00)
+               {
+                       waypoint_schedulerelink(it);
+                       scheduled = true;
+               }
+       });
+       if (scheduled)
+               return false;
 
        botframe_cachedwaypointlinks = true;
        return true;
@@ -773,14 +824,23 @@ bool waypoint_load_links()
 
 void waypoint_load_or_remove_links_hardwired(bool removal_mode)
 {
-       string filename, s;
+       string s;
        float file, tokens, c = 0, found;
        entity wp_from = NULL, wp_to;
        vector wp_to_pos, wp_from_pos;
-       filename = strcat("maps/", mapname);
-       filename = strcat(filename, ".waypoints.hardwired");
+
+       string gt_ext = GET_GAMETYPE_EXTENSION();
+
+       string filename = sprintf("maps/%s.waypoints.hardwired", strcat(mapname, gt_ext));
        file = fopen(filename, FILE_READ);
 
+       if (gt_ext != "" && file < 0)
+       {
+               // if race waypoint file doesn't exist load the default one
+               filename = sprintf("maps/%s.waypoints.hardwired", mapname);
+               file = fopen(filename, FILE_READ);
+       }
+
        botframe_loadedforcedlinks = true;
 
        if (file < 0)
@@ -825,7 +885,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;
                        }
                }
@@ -847,7 +907,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;
                }
 
@@ -867,8 +927,8 @@ void waypoint_load_or_remove_links_hardwired(bool removal_mode)
 
        fclose(file);
 
-       if(!removal_mode)
-               LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.hardwired");
+       LOG_TRACE(((removal_mode) ? "unloaded " : "loaded "),
+               ftos(c), " waypoint links from maps/", mapname, ".waypoints.hardwired");
 }
 
 entity waypoint_get_link(entity w, float i)
@@ -917,14 +977,18 @@ void waypoint_save_links()
        // temporarily remove hardwired links so they don't get saved among normal links
        waypoint_remove_links_hardwired();
 
-       string filename = sprintf("maps/%s.waypoints.cache", mapname);
+       string gt_ext = GET_GAMETYPE_EXTENSION();
+
+       string filename = sprintf("maps/%s.waypoints.cache", strcat(mapname, gt_ext));
        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,
        {
@@ -940,9 +1004,10 @@ 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 %s", c, filename);
 
        waypoint_load_links_hardwired();
 }
@@ -950,17 +1015,25 @@ void waypoint_save_links()
 // save waypoints to gamedir/data/maps/mapname.waypoints
 void waypoint_saveall()
 {
-       string filename = sprintf("maps/%s.waypoints", mapname);
+       string gt_ext = GET_GAMETYPE_EXTENSION();
+
+       string filename = sprintf("maps/%s.waypoints", strcat(mapname, gt_ext));
        int file = fopen(filename, FILE_WRITE);
        if (file < 0)
        {
                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,
        {
@@ -980,52 +1053,90 @@ 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 %s", c, filename);
 }
 
 // load waypoints from file
 float waypoint_loadall()
 {
-       string filename, s;
+       string s;
        float file, cwp, cwb, fl;
        vector m1, m2;
        cwp = 0;
        cwb = 0;
-       filename = strcat("maps/", mapname);
-       filename = strcat(filename, ".waypoints");
+
+       string gt_ext = GET_GAMETYPE_EXTENSION();
+
+       string filename = sprintf("maps/%s.waypoints", strcat(mapname, gt_ext));
        file = fopen(filename, FILE_READ);
-       if (file >= 0)
+
+       if (gt_ext != "" && file < 0)
        {
-               while ((s = fgets(file)))
-               {
-                       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");
+               // if race waypoint file doesn't exist load the default one
+               filename = sprintf("maps/%s.waypoints", mapname);
+               file = fopen(filename, FILE_READ);
        }
-       else
+
+       if (file < 0)
        {
                LOG_TRACE("waypoint load from ", filename, " failed");
+               return 0;
        }
+
+       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(floor(ver) < floor(WAYPOINT_VERSION))
+                               {
+                                       LOG_TRACE("waypoints for this map are outdated");
+                                       LOG_TRACE("please update them in the editor");
+                               }
+                               parse_comments = false;
+                       }
+               }
+               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, entity tracetest_ent)
+#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, tracetest_ent);
+       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;
        return position;
@@ -1083,10 +1194,18 @@ void waypoint_spawnforteleporter_boxes(entity e, int teleport_flag, vector org1,
        e.nearestwaypointtimeout = -1;
 }
 
-void waypoint_spawnforteleporter_wz(entity e, vector org, vector destination, float timetaken, entity tracetest_ent)
+void waypoint_spawnforteleporter_wz(entity e, vector org, vector destination, float timetaken, vector down_dir, entity tracetest_ent)
 {
-       org = waypoint_fixorigin(org, tracetest_ent);
-       destination = waypoint_fixorigin(destination, tracetest_ent);
+       // 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);
 }
 
@@ -1232,7 +1351,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, wp.origin, 0, 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);
@@ -1248,8 +1367,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, wp.origin, 0, walkfromwp, 1050))
-                       if(navigation_waypoint_will_link(it.origin, porg, p, it.origin, 0, 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;
@@ -1257,7 +1376,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;
                }
 
@@ -1303,7 +1422,7 @@ float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .en
 
                if(wp)
                {
-                       if(!navigation_waypoint_will_link(wp.origin, o, p, wp.origin, 0, 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
@@ -1329,14 +1448,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, o, 0, 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;
 }
@@ -1352,7 +1471,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);
@@ -1426,7 +1545,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;