#include "../../antilag.qh"
#include <common/constants.qh>
+#include <common/debug.qh>
+#include <common/mapobjects/trigger/jumppads.qh>
#include <common/net_linked.qh>
#include <common/physics/player.qh>
entity e2 = navigation_findnearestwaypoint(pl, false);
if(!e2)
{
- LOG_INFOF("Can't find any waypoint nearby\n");
+ LOG_INFO("Can't find any waypoint nearby\n");
return;
}
if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j);
}
+void waypoint_getSymmetricalAxis_cmd(entity caller, bool save, int arg_idx)
+{
+ vector v1 = stov(argv(arg_idx++));
+ vector v2 = stov(argv(arg_idx++));
+ vector mid = (v1 + v2) / 2;
+
+ float diffy = (v2.y - v1.y);
+ float diffx = (v2.x - v1.x);
+ if (v1.y == v2.y)
+ diffy = 0.000001;
+ if (v1.x == v2.x)
+ diffx = 0.000001;
+ float m = - diffx / diffy;
+ float q = - m * mid.x + mid.y;
+ if (fabs(m) <= 0.000001) m = 0;
+ if (fabs(q) <= 0.000001) q = 0;
+
+ string axis_str = strcat(ftos(m), " ", ftos(q));
+ if (save)
+ cvar_set("g_waypointeditor_symmetrical_axis", axis_str);
+ axis_str = strcat("\"", axis_str, "\"");
+ sprint(caller, strcat("Axis of symmetry based on input points: ", axis_str, "\n"));
+ if (save)
+ sprint(caller, sprintf(" ^3saved to %s\n", "g_waypointeditor_symmetrical_axis"));
+ if (save)
+ {
+ cvar_set("g_waypointeditor_symmetrical", "-2");
+ sprint(caller, strcat("g_waypointeditor_symmetrical", " has been set to ",
+ cvar_string("g_waypointeditor_symmetrical"), "\n"));
+ }
+}
+
+void waypoint_getSymmetricalOrigin_cmd(entity caller, bool save, int arg_idx)
+{
+ vector org = '0 0 0';
+ int ctf_flags = 0;
+ for (int i = 0; i < 6; i++)
+ {
+ if (argv(arg_idx + i) != "")
+ ctf_flags++;
+ }
+ if (ctf_flags < 2)
+ {
+ ctf_flags = 0;
+ org = vec2(havocbot_middlepoint);
+ if (argv(arg_idx) != "")
+ sprint(caller, "WARNING: Ignoring single input point\n");
+ if (havocbot_middlepoint_radius == 0)
+ {
+ sprint(caller, "Origin of symmetry can't be automatically determined\n");
+ return;
+ }
+ }
+ else
+ {
+ vector v1, v2, v3, v4, v5, v6;
+ for (int i = 1; i <= ctf_flags; i++)
+ {
+ if (i == 1) { v1 = stov(argv(arg_idx++)); org = v1 / ctf_flags; }
+ else if (i == 2) { v2 = stov(argv(arg_idx++)); org += v2 / ctf_flags; }
+ else if (i == 3) { v3 = stov(argv(arg_idx++)); org += v3 / ctf_flags; }
+ else if (i == 4) { v4 = stov(argv(arg_idx++)); org += v4 / ctf_flags; }
+ else if (i == 5) { v5 = stov(argv(arg_idx++)); org += v5 / ctf_flags; }
+ else if (i == 6) { v6 = stov(argv(arg_idx++)); org += v6 / ctf_flags; }
+ }
+ }
+
+ if (fabs(org.x) <= 0.000001) org.x = 0;
+ if (fabs(org.y) <= 0.000001) org.y = 0;
+ string org_str = strcat(ftos(org.x), " ", ftos(org.y));
+ if (save)
+ {
+ cvar_set("g_waypointeditor_symmetrical_origin", org_str);
+ cvar_set("g_waypointeditor_symmetrical_order", ftos(ctf_flags));
+ }
+ org_str = strcat("\"", org_str, "\"");
+
+ if (ctf_flags < 2)
+ sprint(caller, strcat("Origin of symmetry based on flag positions: ", org_str, "\n"));
+ else
+ sprint(caller, strcat("Origin of symmetry based on input points: ", org_str, "\n"));
+ if (save)
+ sprint(caller, sprintf(" ^3saved to %s\n", "g_waypointeditor_symmetrical_origin"));
+
+ if (ctf_flags < 2)
+ sprint(caller, "Order of symmetry: 0 (autodetected)\n");
+ else
+ sprint(caller, strcat("Order of symmetry: ", ftos(ctf_flags), "\n"));
+ if (save)
+ sprint(caller, sprintf(" ^3saved to %s\n", "g_waypointeditor_symmetrical_order"));
+
+ if (save)
+ {
+ if (ctf_flags < 2)
+ cvar_set("g_waypointeditor_symmetrical", "0");
+ else
+ cvar_set("g_waypointeditor_symmetrical", "-1");
+ sprint(caller, strcat("g_waypointeditor_symmetrical", " has been set to ",
+ cvar_string("g_waypointeditor_symmetrical"), "\n"));
+ }
+}
+
vector waypoint_getSymmetricalPoint(vector org, int ctf_flags)
{
vector new_org = org;
wp.colormod = '1 0 0';
else if (wp.wpflags & WAYPOINTFLAG_GENERATED)
wp.colormod = '1 1 0';
+ else if (wp.wpflags & WAYPOINTFLAG_NORELINK)
+ wp.colormod = '1 0.5 0'; // orange
else if (wp.wphardwired)
- wp.colormod = '0.5 0 1';
+ wp.colormod = '0.5 0 1'; // violet
else
wp.colormod = '1 1 1';
}
wp.model = "";
}
+entity waypoint_get(vector m1, vector m2)
+{
+ if (m1 == m2)
+ {
+ m1 -= '8 8 8';
+ m2 += '8 8 8';
+ }
+ IL_EACH(g_waypoints, boxesoverlap(m1, m2, it.absmin, it.absmax), { return it; });
+
+ return NULL;
+}
+
entity waypoint_spawn(vector m1, vector m2, float f)
{
if(!(f & (WAYPOINTFLAG_PERSONAL | WAYPOINTFLAG_GENERATED)) && m1 == m2)
{
- 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;
- });
+ entity wp_found = waypoint_get(m1, m2);
+ if (wp_found)
+ return wp_found;
}
// 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)
+ if((f & WAYPOINTFLAG_GENERATED) && !(f & (WAYPOINTFLAG_NORELINK | WAYPOINTFLAG_PERSONAL)) && m1 == m2)
{
IL_EACH(g_waypoints, boxesoverlap(m1, m2, it.absmin, it.absmax),
{
return w;
}
-void waypoint_spawn_fromeditor(entity pl)
+float trigger_push_get_push_time(entity this, vector endpos);
+void waypoint_addlink_for_custom_jumppad(entity wp_from, entity wp_to)
{
- entity e;
+ entity jp = NULL;
+ IL_EACH(g_jumppads, boxesoverlap(wp_from.absmin, wp_from.absmax, it.absmin, it.absmax),
+ {
+ jp = it;
+ break;
+ });
+ if (!jp)
+ return;
+
+ float cost = trigger_push_get_push_time(jp, wp_to.origin);
+ wp_from.wp00 = wp_to;
+ wp_from.wp00mincost = cost;
+ jp.nearestwaypoint = wp_from;
+ jp.nearestwaypointtimeout = -1;
+}
+
+bool start_wp_is_spawned;
+vector start_wp_origin;
+
+void waypoint_clear_start_wp(entity pl, bool warn)
+{
+ start_wp_is_spawned = false;
+ start_wp_origin = '0 0 0';
+ pl.wp_locked = NULL;
+ if (warn)
+ LOG_INFO("^xf80Start waypoint has been cleared.\n");
+}
+
+void waypoint_spawn_fromeditor(entity pl, bool at_crosshair, bool is_jump_wp)
+{
+ entity e = NULL, jp = NULL;
vector org = pl.origin;
+ if (at_crosshair)
+ {
+ crosshair_trace(pl);
+ org = trace_endpos - eZ * PL_MIN_CONST.z;
+ IL_EACH(g_jumppads, boxesoverlap(org + PL_MIN_CONST, org + PL_MAX_CONST, it.absmin, it.absmax),
+ {
+ jp = it;
+ break;
+ });
+ }
+ if (jp || is_jump_wp)
+ {
+ if (start_wp_is_spawned)
+ start_wp_is_spawned = false;
+ LOG_INFO("^xf80Spawning start waypoint...\n");
+ }
int ctf_flags = havocbot_symmetry_origin_order;
bool sym = ((autocvar_g_waypointeditor_symmetrical > 0 && ctf_flags >= 2)
|| (autocvar_g_waypointeditor_symmetrical < 0));
ctf_flags = 2;
int wp_num = ctf_flags;
- if(!PHYS_INPUT_BUTTON_CROUCH(pl))
+ if(!PHYS_INPUT_BUTTON_CROUCH(pl) && !at_crosshair && !is_jump_wp)
{
// snap waypoint to item's origin if close enough
IL_EACH(g_items, true,
});
}
+ vector start_org = '0 0 0';
+ if (start_wp_is_spawned)
+ {
+ LOG_INFO("^xf80Spawning destination waypoint...\n");
+ start_org = start_wp_origin;
+ }
+
+ // save org as it can be modified spawning symmetrycal waypoints
+ vector initial_origin = '0 0 0';
+ bool initial_origin_is_set = false;
+
LABEL(add_wp);
- e = waypoint_spawn(org, org, 0);
+
+ if (jp)
+ {
+ e = NULL;
+ IL_EACH(g_waypoints, it.wpflags & WAYPOINTFLAG_NORELINK
+ && boxesoverlap(org + PL_MIN_CONST, org + PL_MAX_CONST, it.absmin, it.absmax),
+ {
+ e = it; break;
+ });
+ if (!e)
+ e = waypoint_spawn(jp.absmin - PL_MAX_CONST + '1 1 1', jp.absmax - PL_MIN_CONST + '-1 -1 -1', WAYPOINTFLAG_TELEPORT | WAYPOINTFLAG_NORELINK);
+ if (!pl.wp_locked)
+ pl.wp_locked = e;
+ }
+ else if (is_jump_wp)
+ {
+ entity wp_found = waypoint_get(org, org);
+ if (wp_found && !(wp_found.wpflags & WAYPOINTFLAG_JUMP))
+ {
+ LOG_INFO("Error: can't spawn a jump waypoint over an existent waypoint of a different type\n");
+ return;
+ }
+ e = waypoint_spawn(org, org, WAYPOINTFLAG_JUMP | WAYPOINTFLAG_NORELINK);
+ if (!pl.wp_locked)
+ pl.wp_locked = e;
+ }
+ else
+ e = waypoint_spawn(org, org, 0);
if(!e)
{
LOG_INFOF("Couldn't spawn waypoint at %v\n", org);
+ if (start_wp_is_spawned)
+ waypoint_clear_start_wp(pl, true);
return;
}
- waypoint_schedulerelink(e);
+
+ if (!initial_origin_is_set)
+ {
+ initial_origin = e.origin;
+ initial_origin_is_set = true;
+ }
+
+ entity start_wp = NULL;
+ if (start_wp_is_spawned)
+ {
+ IL_EACH(g_waypoints, it.wpflags & WAYPOINTFLAG_NORELINK
+ && boxesoverlap(start_org, start_org, it.absmin, it.absmax),
+ {
+ start_wp = it; break;
+ });
+ if(!start_wp)
+ {
+ // should not happen
+ LOG_INFOF("Couldn't find start waypoint at %v\n", start_org);
+ waypoint_clear_start_wp(pl, true);
+ return;
+ }
+ waypoint_addlink(start_wp, e);
+ }
+
+ if (!(jp || is_jump_wp))
+ waypoint_schedulerelink(e);
bprint(strcat("Waypoint spawned at ", vtos(e.origin), "\n"));
- if(sym)
+
+ if (start_wp_is_spawned)
{
- org = waypoint_getSymmetricalPoint(e.origin, ctf_flags);
+ pl.wp_locked = NULL;
+ waypoint_schedulerelink(start_wp);
+ }
+
+ if (sym)
+ {
+ org = waypoint_getSymmetricalPoint(org, ctf_flags);
+ if (jp)
+ {
+ IL_EACH(g_jumppads, boxesoverlap(org + PL_MIN_CONST, org + PL_MAX_CONST, it.absmin, it.absmax),
+ {
+ jp = it; break;
+ });
+ }
+ if (start_wp_is_spawned)
+ start_org = waypoint_getSymmetricalPoint(start_org, ctf_flags);
if (vdist(org - pl.origin, >, 32))
{
if(wp_num > 2)
goto add_wp;
}
}
+ if (jp || is_jump_wp)
+ {
+ if (!start_wp_is_spawned)
+ {
+ // we've just created a custom jumppad waypoint
+ // the next one created by the user will be the destination waypoint
+ start_wp_is_spawned = true;
+ start_wp_origin = initial_origin;
+ }
+ }
+ else if (start_wp_is_spawned)
+ {
+ waypoint_clear_start_wp(pl, false);
+ }
}
void waypoint_remove(entity wp)
LABEL(remove_wp);
if (!e) return;
- if (e.wpflags & WAYPOINTFLAG_GENERATED) return;
+ if (e.wpflags & WAYPOINTFLAG_GENERATED)
+ {
+ if (start_wp_is_spawned)
+ waypoint_clear_start_wp(pl, true);
+ return;
+ }
if (e.wphardwired)
{
sym = false;
goto remove_wp;
}
+
+ if (start_wp_is_spawned)
+ waypoint_clear_start_wp(pl, true);
}
void waypoint_removelink(entity from, entity to)
{
- if (from == to || (from.wpflags & WAYPOINTFLAG_NORELINK))
+ if (from == to || (from.wpflags & WAYPOINTFLAG_NORELINK && !(from.wpflags & WAYPOINTFLAG_JUMP)))
return;
entity fromwp31_prev = from.wp31;
float height = from.z - to.z;
if(height > jumpheight_vec.z && autocvar_sv_gravity > 0)
{
- float height_cost = sqrt(height / (autocvar_sv_gravity / 2));
+ float height_cost;
+ if (boolean(from_ent.wpflags & WAYPOINTFLAG_JUMP))
+ height_cost = jumpheight_time + sqrt((height + jumpheight_vec.z) / (autocvar_sv_gravity / 2));
+ else
+ height_cost = sqrt(height / (autocvar_sv_gravity / 2));
c = waypoint_getlinearcost(vlen(vec2(to - from))); // xy distance cost
if(height_cost > c)
c = height_cost;
{
if (from == to || waypoint_islinked(from, to))
return;
- if (c == -1 && (from.wpflags & WAYPOINTFLAG_NORELINK))
+ if (c == -1 && (from.wpflags & WAYPOINTFLAG_NORELINK) && !(from.wpflags & WAYPOINTFLAG_JUMP))
return;
if(c == -1)
void waypoint_addlink(entity from, entity to)
{
- waypoint_addlink_customcost(from, to, -1);
+ if ((from.wpflags & WAYPOINTFLAG_NORELINK) && !(from.wpflags & (WAYPOINTFLAG_JUMP)))
+ waypoint_addlink_for_custom_jumppad(from, to);
+ else
+ waypoint_addlink_customcost(from, to, -1);
}
// relink this spawnfunc_waypoint
//traceline(this.origin, it.origin, false, NULL);
//if (trace_fraction == 1)
- if (this.wpisbox)
+ if (this.wpisbox || this.wpflags & WAYPOINTFLAG_JUMP)
relink_walkculled += 0.5;
else
{
relink_walkculled += 0.5;
}
- if (it.wpisbox)
+ if (it.wpisbox || it.wpflags & WAYPOINTFLAG_JUMP)
relink_walkculled += 0.5;
else
{
navigation_testtracewalk = 0;
this.wplinked = true;
this.dphitcontentsmask = dphitcontentsmask_save;
+
+ setthink(this, func_null);
+ this.nextthink = 0;
}
void waypoint_clearlinks(entity wp)
FOREACH_CLIENT(IS_PLAYER(it) && !it.isbot,
{
int display_type = 0;
+ if (wasfreed(it.wp_aimed))
+ it.wp_aimed = NULL;
+ if (wasfreed(it.wp_locked))
+ it.wp_locked = NULL;
if (PHYS_INPUT_BUTTON_USE(it))
it.wp_locked = it.wp_aimed;
entity head = it.wp_locked;