if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j);
}
-vector waypoint_getSymmetricalOrigin(vector org, int ctf_flags)
+vector waypoint_getSymmetricalPoint(vector org, int ctf_flags)
{
vector new_org = org;
if (fabs(autocvar_g_waypointeditor_symmetrical) == 1)
}
else if (fabs(autocvar_g_waypointeditor_symmetrical) == 2)
{
- float m = havocbot_symmetryaxis_equation.x;
- float q = havocbot_symmetryaxis_equation.y;
+ float m = havocbot_symmetry_axis_m;
+ float q = havocbot_symmetry_axis_q;
if (autocvar_g_waypointeditor_symmetrical == -2)
{
m = autocvar_g_waypointeditor_symmetrical_axis.x;
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;
});
}
+ // spawn only one destination waypoint for teleports teleporting player to the exact same spot
+ // otherwise links loaded from file would be applied only to the first destination
+ // waypoint since link format doesn't specify waypoint entities but just positions
+ if((f & WAYPOINTFLAG_GENERATED) && !(f & WAYPOINTFLAG_NORELINK) && m1 == m2)
+ {
+ IL_EACH(g_waypoints, boxesoverlap(m1, m2, it.absmin, it.absmax),
+ {
+ return it;
+ });
+ }
entity w = new(waypoint);
IL_PUSH(g_waypoints, w);
{
entity e;
vector org = pl.origin;
- int ctf_flags = havocbot_symmetryaxis_equation.z;
+ int ctf_flags = havocbot_symmetry_origin_order;
bool sym = ((autocvar_g_waypointeditor_symmetrical > 0 && ctf_flags >= 2)
|| (autocvar_g_waypointeditor_symmetrical < 0));
- int order = ctf_flags;
if(autocvar_g_waypointeditor_symmetrical_order >= 2)
- {
- order = autocvar_g_waypointeditor_symmetrical_order;
- ctf_flags = order;
- }
+ ctf_flags = autocvar_g_waypointeditor_symmetrical_order;
+ if (sym && ctf_flags < 2)
+ ctf_flags = 2;
+ int wp_num = ctf_flags;
if(!PHYS_INPUT_BUTTON_CROUCH(pl))
{
{
vector item_org = (it.absmin + it.absmax) * 0.5;
item_org.z = it.absmin.z - PL_MIN_CONST.z;
- if(vlen(item_org - org) < 30)
+ if (vlen(item_org - org) < 20)
{
org = item_org;
break;
bprint(strcat("Waypoint spawned at ", vtos(e.origin), "\n"));
if(sym)
{
- org = waypoint_getSymmetricalOrigin(e.origin, ctf_flags);
+ org = waypoint_getSymmetricalPoint(e.origin, ctf_flags);
if (vdist(org - pl.origin, >, 32))
{
- if(order > 2)
- order--;
+ if(wp_num > 2)
+ wp_num--;
else
sym = false;
goto add_wp;
void waypoint_remove(entity wp)
{
- // tell all waypoints linked to wp that they need to relink
IL_EACH(g_waypoints, it != wp,
{
if (waypoint_islinked(it, wp))
{
entity e = navigation_findnearestwaypoint(pl, false);
- int ctf_flags = havocbot_symmetryaxis_equation.z;
+ int ctf_flags = havocbot_symmetry_origin_order;
bool sym = ((autocvar_g_waypointeditor_symmetrical > 0 && ctf_flags >= 2)
|| (autocvar_g_waypointeditor_symmetrical < 0));
- int order = ctf_flags;
if(autocvar_g_waypointeditor_symmetrical_order >= 2)
- {
- order = autocvar_g_waypointeditor_symmetrical_order;
- ctf_flags = order;
- }
+ ctf_flags = autocvar_g_waypointeditor_symmetrical_order;
+ if (sym && ctf_flags < 2)
+ ctf_flags = 2;
+ int wp_num = ctf_flags;
LABEL(remove_wp);
if (!e) return;
entity wp_sym = NULL;
if (sym)
{
- vector org = waypoint_getSymmetricalOrigin(e.origin, ctf_flags);
+ vector org = waypoint_getSymmetricalPoint(e.origin, ctf_flags);
FOREACH_ENTITY_CLASS("waypoint", !(it.wpflags & WAYPOINTFLAG_GENERATED), {
if(vdist(org - it.origin, <, 3))
{
if (sym && wp_sym)
{
e = wp_sym;
- if(order > 2)
- order--;
+ if(wp_num > 2)
+ wp_num--;
else
sym = false;
goto remove_wp;
}
float waypoint_getlinearcost_underwater(float dist)
{
- // NOTE: this value is hardcoded on the engine too, see SV_WaterMove
+ // NOTE: underwater speed factor is hardcoded in the engine too, see SV_WaterMove
return dist / (autocvar_sv_maxspeed * 0.7);
}
bot_calculate_stepheightvec();
+ int dphitcontentsmask_save = this.dphitcontentsmask;
+ this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
+
bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL);
//dprint("waypoint_think wpisbox = ", ftos(this.wpisbox), "\n");
relink_walkculled += 0.5;
else
{
- if (tracewalk(it, ev, PL_MIN_CONST, PL_MAX_CONST, sv2, sv2_height, MOVE_NOMONSTERS))
+ if (tracewalk(this, ev, PL_MIN_CONST, PL_MAX_CONST, sv2, sv2_height, MOVE_NOMONSTERS))
waypoint_addlink(it, this);
else
relink_walkculled += 0.5;
});
navigation_testtracewalk = 0;
this.wplinked = true;
+ this.dphitcontentsmask = dphitcontentsmask_save;
}
void waypoint_clearlinks(entity wp)
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;
+ string links_time = string_null;
while ((s = fgets(file)))
{
{
if(substring(s, 2, 17) == "WAYPOINT_VERSION ")
ver = stof(substring(s, 19, -1));
+ else if(substring(s, 2, 14) == "WAYPOINT_TIME ")
+ links_time = substring(s, 16, -1);
continue;
}
else
{
- if(ver < WAYPOINT_VERSION)
- return false;
+ if(ver < WAYPOINT_VERSION || links_time != waypoint_time)
+ {
+ if (links_time != waypoint_time)
+ LOG_TRACE("waypoint links for this map are not made for these waypoints.");
+ else
+ 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;
}
}
{
// bad file format
fclose(file);
+ waypoint_schedulerelinkall(); // link all the autogenerated waypoints (teleporters)
return false;
}
LOG_TRACE("waypoint_load_links: couldn't find 'from' waypoint at ", vtos(wp_from_pos));
continue;
}
-
}
// Search "to" waypoint
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;
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)
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)
// 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)
{
}
fputs(file, strcat("//", "WAYPOINT_VERSION ", ftos_decimals(WAYPOINT_VERSION, 2), "\n"));
+ if (waypoint_time != "")
+ fputs(file, strcat("//", "WAYPOINT_TIME ", waypoint_time, "\n"));
int c = 0;
IL_EACH(g_waypoints, true,
entity link = waypoint_get_link(it, j);
if(link)
{
+ // NOTE: vtos rounds vector components to 1 decimal place
string s = strcat(vtos(it.origin), "*", vtos(link.origin), "\n");
fputs(file, s);
++c;
}
});
fclose(file);
+
botframe_cachedwaypointlinks = true;
- LOG_INFOF("saved %d waypoint links to maps/%s.waypoints.cache", c, mapname);
+ LOG_INFOF("saved %d waypoint links to %s", c, filename);
waypoint_load_links_hardwired();
}
// 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)
{
return;
}
- // add 3 comments to not break compatibility with older Xonotic versions
+ float sym = autocvar_g_waypointeditor_symmetrical;
+ string sym_str = ftos(sym);
+ if (sym == -1 || (sym == 1 && autocvar_g_waypointeditor_symmetrical_order >= 2))
+ {
+ if (sym == 1)
+ {
+ sym_str = cons(sym_str, "-");
+ sym_str = cons(sym_str, "-");
+ }
+ else
+ {
+ sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_origin.x));
+ sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_origin.y));
+ }
+ if (autocvar_g_waypointeditor_symmetrical_order >= 2)
+ sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_order));
+ }
+ else if (autocvar_g_waypointeditor_symmetrical == -2)
+ {
+ sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_axis.x));
+ sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_axis.y));
+ }
+
+ // a group of 3 comments doesn't 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"));
+ fputs(file, strcat("//", "WAYPOINT_SYMMETRY ", sym_str, "\n"));
+
+ strcpy(waypoint_time, strftime(true, "%Y-%m-%d %H:%M:%S"));
+ fputs(file, strcat("//", "WAYPOINT_TIME ", waypoint_time, "\n"));
+ //fputs(file, strcat("//", "\n"));
+ //fputs(file, strcat("//", "\n"));
+ //fputs(file, strcat("//", "\n"));
int c = 0;
IL_EACH(g_waypoints, true,
continue;
string s;
+ // NOTE: vtos rounds vector components to 1 decimal place
s = strcat(vtos(it.origin + it.mins), "\n");
s = strcat(s, vtos(it.origin + it.maxs));
s = strcat(s, "\n");
waypoint_save_links();
botframe_loadedforcedlinks = false;
- LOG_INFOF("saved %d waypoints to maps/%s.waypoints", 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);
- bool parse_comments = true;
- float ver = 0;
+ if (gt_ext != "" && file < 0)
+ {
+ // if race waypoint file doesn't exist load the default one
+ filename = sprintf("maps/%s.waypoints", mapname);
+ file = fopen(filename, FILE_READ);
+ }
if (file < 0)
{
return 0;
}
+ bool parse_comments = true;
+ float ver = 0;
+ float sym = 0;
+ float sym_param1 = 0, sym_param2 = 0, sym_param3 = 0;
+
while ((s = fgets(file)))
{
if(parse_comments)
{
if(substring(s, 2, 17) == "WAYPOINT_VERSION ")
ver = stof(substring(s, 19, -1));
+ else if(substring(s, 2, 18) == "WAYPOINT_SYMMETRY ")
+ {
+ int tokens = tokenizebyseparator(substring(s, 20, -1), " ");
+ if (tokens) { sym = stof(argv(0)); }
+ if (tokens > 1) { sym_param1 = stof(argv(1)); }
+ if (tokens > 2) { sym_param2 = stof(argv(2)); }
+ if (tokens > 3) { sym_param3 = stof(argv(3)); }
+ }
+ else if(substring(s, 2, 14) == "WAYPOINT_TIME ")
+ strcpy(waypoint_time, substring(s, 16, -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;
}
}
fclose(file);
LOG_TRACE("loaded ", ftos(cwp), " waypoints and ", ftos(cwb), " wayboxes from maps/", mapname, ".waypoints");
+ if (autocvar_g_waypointeditor && autocvar_g_waypointeditor_symmetrical_allowload)
+ {
+ cvar_set("g_waypointeditor_symmetrical", ftos(sym));
+ if (sym == 1 && sym_param3 < 2)
+ cvar_set("g_waypointeditor_symmetrical_order", "0"); // make sure this is reset if not loaded
+ if (sym == -1 || (sym == 1 && sym_param3 >= 2))
+ {
+ string params;
+ if (sym == 1)
+ params = cons("-", "-");
+ else
+ {
+ params = cons(ftos(sym_param1), ftos(sym_param2));
+ cvar_set("g_waypointeditor_symmetrical_origin", params);
+ }
+ cvar_set("g_waypointeditor_symmetrical_order", ftos(sym_param3));
+ LOG_INFO("Waypoint editor: loaded symmetry ", ftos(sym), " with origin ", params, " and order ", ftos(sym_param3));
+ }
+ else if (sym == -2)
+ {
+ string params = strcat(ftos(sym_param1), " ", ftos(sym_param2));
+ cvar_set("g_waypointeditor_symmetrical_axis", params);
+ LOG_INFO("Waypoint editor: loaded symmetry ", ftos(sym), " with axis ", params);
+ }
+ else
+ LOG_INFO("Waypoint editor: loaded symmetry ", ftos(sym));
+ LOG_INFO(strcat("g_waypointeditor_symmetrical", " has been set to ", cvar_string("g_waypointeditor_symmetrical")));
+ }
+
return cwp + cwb;
}
vector waypoint_fixorigin_down_dir(vector position, entity tracetest_ent, vector down_dir)
{
- tracebox(position + '0 0 1', PL_MIN_CONST, PL_MAX_CONST, position + down_dir * 3000, MOVE_NOMONSTERS, tracetest_ent);
+ vector endpos = position + down_dir * 3000;
+ tracebox(position + '0 0 1', PL_MIN_CONST, PL_MAX_CONST, endpos, 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);
+ tracebox(position + '0 0 1' * (1 - PL_MIN_CONST.z / 2), PL_MIN_CONST, PL_MAX_CONST, endpos, 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);
+ tracebox(position + '0 0 1' * (1 - PL_MIN_CONST.z), PL_MIN_CONST, PL_MAX_CONST, endpos, MOVE_NOMONSTERS, tracetest_ent);
if(trace_fraction < 1)
position = trace_endpos;
return position;
{
int display_type = 0;
entity head = navigation_findnearestwaypoint(it, false);
+ it.nearestwaypoint = head; // mainly useful for debug
+ it.nearestwaypointtimeout = time + 2; // while I'm at it...
if (IS_ONGROUND(it) || it.waterlevel > WATERLEVEL_NONE)
display_type = 1; // default
else if(head && (head.wphardwired))