+}
+
+void waypoint_removelink(entity from, entity to)
+{
+ if (from == to || (from.wpflags & WAYPOINTFLAG_NORELINK))
+ return;
+
+ bool found = false;
+ if (!found && from.wp00 == to) found = true; if (found) {from.wp00 = from.wp01; from.wp00mincost = from.wp01mincost;}
+ if (!found && from.wp01 == to) found = true; if (found) {from.wp01 = from.wp02; from.wp01mincost = from.wp02mincost;}
+ if (!found && from.wp02 == to) found = true; if (found) {from.wp02 = from.wp03; from.wp02mincost = from.wp03mincost;}
+ if (!found && from.wp03 == to) found = true; if (found) {from.wp03 = from.wp04; from.wp03mincost = from.wp04mincost;}
+ if (!found && from.wp04 == to) found = true; if (found) {from.wp04 = from.wp05; from.wp04mincost = from.wp05mincost;}
+ if (!found && from.wp05 == to) found = true; if (found) {from.wp05 = from.wp06; from.wp05mincost = from.wp06mincost;}
+ if (!found && from.wp06 == to) found = true; if (found) {from.wp06 = from.wp07; from.wp06mincost = from.wp07mincost;}
+ if (!found && from.wp07 == to) found = true; if (found) {from.wp07 = from.wp08; from.wp07mincost = from.wp08mincost;}
+ if (!found && from.wp08 == to) found = true; if (found) {from.wp08 = from.wp09; from.wp08mincost = from.wp09mincost;}
+ if (!found && from.wp09 == to) found = true; if (found) {from.wp09 = from.wp10; from.wp09mincost = from.wp10mincost;}
+ if (!found && from.wp10 == to) found = true; if (found) {from.wp10 = from.wp11; from.wp10mincost = from.wp11mincost;}
+ if (!found && from.wp11 == to) found = true; if (found) {from.wp11 = from.wp12; from.wp11mincost = from.wp12mincost;}
+ if (!found && from.wp12 == to) found = true; if (found) {from.wp12 = from.wp13; from.wp12mincost = from.wp13mincost;}
+ if (!found && from.wp13 == to) found = true; if (found) {from.wp13 = from.wp14; from.wp13mincost = from.wp14mincost;}
+ if (!found && from.wp14 == to) found = true; if (found) {from.wp14 = from.wp15; from.wp14mincost = from.wp15mincost;}
+ if (!found && from.wp15 == to) found = true; if (found) {from.wp15 = from.wp16; from.wp15mincost = from.wp16mincost;}
+ if (!found && from.wp16 == to) found = true; if (found) {from.wp16 = from.wp17; from.wp16mincost = from.wp17mincost;}
+ if (!found && from.wp17 == to) found = true; if (found) {from.wp17 = from.wp18; from.wp17mincost = from.wp18mincost;}
+ if (!found && from.wp18 == to) found = true; if (found) {from.wp18 = from.wp19; from.wp18mincost = from.wp19mincost;}
+ if (!found && from.wp19 == to) found = true; if (found) {from.wp19 = from.wp20; from.wp19mincost = from.wp20mincost;}
+ if (!found && from.wp20 == to) found = true; if (found) {from.wp20 = from.wp21; from.wp20mincost = from.wp21mincost;}
+ if (!found && from.wp21 == to) found = true; if (found) {from.wp21 = from.wp22; from.wp21mincost = from.wp22mincost;}
+ if (!found && from.wp22 == to) found = true; if (found) {from.wp22 = from.wp23; from.wp22mincost = from.wp23mincost;}
+ if (!found && from.wp23 == to) found = true; if (found) {from.wp23 = from.wp24; from.wp23mincost = from.wp24mincost;}
+ if (!found && from.wp24 == to) found = true; if (found) {from.wp24 = from.wp25; from.wp24mincost = from.wp25mincost;}
+ if (!found && from.wp25 == to) found = true; if (found) {from.wp25 = from.wp26; from.wp25mincost = from.wp26mincost;}
+ if (!found && from.wp26 == to) found = true; if (found) {from.wp26 = from.wp27; from.wp26mincost = from.wp27mincost;}
+ if (!found && from.wp27 == to) found = true; if (found) {from.wp27 = from.wp28; from.wp27mincost = from.wp28mincost;}
+ if (!found && from.wp28 == to) found = true; if (found) {from.wp28 = from.wp29; from.wp28mincost = from.wp29mincost;}
+ if (!found && from.wp29 == to) found = true; if (found) {from.wp29 = from.wp30; from.wp29mincost = from.wp30mincost;}
+ if (!found && from.wp30 == to) found = true; if (found) {from.wp30 = from.wp31; from.wp30mincost = from.wp31mincost;}
+ if (found) {from.wp31 = NULL; from.wp31mincost = 10000000;}
+}
+
+bool waypoint_islinked(entity from, entity to)
+{
+ if (from.wp00 == to) return true;if (from.wp01 == to) return true;if (from.wp02 == to) return true;if (from.wp03 == to) return true;
+ if (from.wp04 == to) return true;if (from.wp05 == to) return true;if (from.wp06 == to) return true;if (from.wp07 == to) return true;
+ if (from.wp08 == to) return true;if (from.wp09 == to) return true;if (from.wp10 == to) return true;if (from.wp11 == to) return true;
+ if (from.wp12 == to) return true;if (from.wp13 == to) return true;if (from.wp14 == to) return true;if (from.wp15 == to) return true;
+ if (from.wp16 == to) return true;if (from.wp17 == to) return true;if (from.wp18 == to) return true;if (from.wp19 == to) return true;
+ if (from.wp20 == to) return true;if (from.wp21 == to) return true;if (from.wp22 == to) return true;if (from.wp23 == to) return true;
+ if (from.wp24 == to) return true;if (from.wp25 == to) return true;if (from.wp26 == to) return true;if (from.wp27 == to) return true;
+ if (from.wp28 == to) return true;if (from.wp29 == to) return true;if (from.wp30 == to) return true;if (from.wp31 == to) return true;
+ return false;
+}
+
+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_gettravelcost(vector from, vector to, entity from_ent, entity to_ent)
+{
+ 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 > jumpheight_vec.z && autocvar_sv_gravity > 0)
+ {
+ float 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 (submerged_from || submerged_to)
+ return (c + waypoint_getlinearcost_underwater(vlen(to - from))) / 2;
+ return c;
+}
+
+float waypoint_getlinkcost(entity from, entity to)
+{
+ vector v1 = from.origin;
+ vector v2 = to.origin;
+ if (from.wpisbox)
+ {
+ 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 = 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_gettravelcost(v1, v2, from, to);
+}
+
+// add a new link to the spawnfunc_waypoint, replacing the furthest link it already has
+// if c == -1 automatically determine cost of the link
+void waypoint_addlink_customcost(entity from, entity to, float c)
+{
+ if (from == to || waypoint_islinked(from, to))
+ return;
+ if (c == -1 && (from.wpflags & WAYPOINTFLAG_NORELINK))
+ return;
+
+ if(c == -1)
+ c = waypoint_getlinkcost(from, to);