#include "../waypoints.qh"
#include <common/constants.qh>
+#include <common/net_linked.qh>
#include <common/physics/player.qh>
#include <common/state.qh>
-#include <common/items/all.qh>
+#include <common/items/_mod.qh>
+#include <common/triggers/teleporters.qh>
#include <common/triggers/trigger/jumppads.qh>
#include <lib/warpzone/common.qh>
if(bot_execute_commands(this))
return;
+ while(this.goalcurrent && wasfreed(this.goalcurrent))
+ {
+ navigation_poproute(this);
+ if(!this.goalcurrent)
+ this.bot_strategytime = 0;
+ }
+
if (bot_strategytoken == this)
if (!bot_strategytoken_taken)
{
// TODO: tracewalk() should take care of this job (better path finding under water)
// if we don't have a goal and we're under water look for a waypoint near the "shore" and push it
- if(IS_DEAD(this))
+ if(!(IS_DEAD(this)))
if(!this.goalcurrent)
if(this.waterlevel == WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER))
{
//heading = this.velocity;
//dprint(this.goalstack01.classname,etos(this.goalstack01),"\n");
if(
- this.goalstack01 != this && this.goalstack01 != NULL && ((this.aistatus & AI_STATUS_RUNNING) == 0) &&
+ this.goalstack01 != this && this.goalstack01 && !wasfreed(this.goalstack01) && ((this.aistatus & AI_STATUS_RUNNING) == 0) &&
!(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
)
next = ((this.goalstack01.absmin + this.goalstack01.absmax) * 0.5) - (this.origin + this.view_ofs);
if(this.goalcurrent.classname=="waypoint")
if (!(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL))
if(fabs(gco.z - this.origin.z) < this.maxs.z - this.mins.z)
- if(this.goalstack01!=NULL)
+ if(this.goalstack01 && !wasfreed(this.goalstack01))
{
gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5;
deviation = vectoangles(gno - this.origin) - vectoangles(gco - this.origin);
// Flying
PHYS_INPUT_BUTTON_HOOK(this) = true;
- if(this.navigation_jetpack_point.z - STAT(PL_MAX, NULL).z + STAT(PL_MIN, NULL).z < this.origin.z)
+ if(this.navigation_jetpack_point.z - STAT(PL_MAX, this).z + STAT(PL_MIN, this).z < this.origin.z)
{
this.movement_x = dir * v_forward * maxspeed;
this.movement_y = dir * v_right * maxspeed;
threshold = maxspeed * 0.2;
if(vdist(velxy, <, threshold))
{
- LOG_TRACE("Warning: ", this.netname, " got stuck on a jumppad (velocity in xy is ", vtos(velxy), "), trying to get out of it now\n");
+ LOG_TRACE("Warning: ", this.netname, " got stuck on a jumppad (velocity in xy is ", vtos(velxy), "), trying to get out of it now");
this.aistatus |= AI_STATUS_OUT_JUMPPAD;
}
return;
if(tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
{
// Remove dangerous dynamic goals from stack
- LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared\n");
+ LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared");
navigation_clearroute(this);
return;
}
if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) this.havocbot_ducktime=time+0.3/bound(0.1,skill+this.bot_dodgeskill,10);
}
+entity havocbot_gettarget(entity this, bool secondary)
+{
+ entity best = NULL;
+ vector eye = CENTER_OR_VIEWOFS(this);
+ IL_EACH(g_bot_targets, boolean((secondary) ? it.classname == "misc_breakablemodel" : it.classname != "misc_breakablemodel"),
+ {
+ vector v = CENTER_OR_VIEWOFS(it);
+ if(vdist(v - eye, <, autocvar_bot_ai_enemydetectionradius))
+ if(!best || vlen2(CENTER_OR_VIEWOFS(best) - eye) > vlen2(v - eye))
+ if(bot_shouldattack(this, it))
+ {
+ traceline(eye, v, true, this);
+ if (trace_ent == it || trace_fraction >= 1)
+ best = it;
+ }
+ });
+
+ return best;
+}
+
void havocbot_chooseenemy(entity this)
{
- entity head, best, head2;
- float rating, bestrating, hf;
- vector eye, v;
if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this))
{
this.enemy = NULL;
if (time < this.havocbot_chooseenemy_finished)
return;
this.havocbot_chooseenemy_finished = time + autocvar_bot_ai_enemydetectioninterval;
- eye = this.origin + this.view_ofs;
- best = NULL;
- bestrating = 100000000;
- head = head2 = findchainfloat(bot_attack, true);
+ vector eye = this.origin + this.view_ofs;
+ entity best = NULL;
+ float bestrating = 100000000;
// Backup hit flags
- hf = this.dphitcontentsmask;
+ int hf = this.dphitcontentsmask;
// Search for enemies, if no enemy can be seen directly try to look through transparent objects
{
scan_secondary_targets = false;
LABEL(scan_targets)
- for( ; head; head = head.chain)
+ IL_EACH(g_bot_targets, it.bot_attack,
{
if(!scan_secondary_targets)
{
- if(head.classname == "misc_breakablemodel")
+ if(it.classname == "misc_breakablemodel")
{
have_secondary_targets = true;
continue;
}
}
- else
- {
- if(head.classname != "misc_breakablemodel")
- continue;
- }
+ else if(it.classname != "misc_breakablemodel")
+ continue;
- v = (head.absmin + head.absmax) * 0.5;
- rating = vlen(v - eye);
- if (rating<autocvar_bot_ai_enemydetectionradius)
+ vector v = (it.absmin + it.absmax) * 0.5;
+ float rating = vlen2(v - eye);
+ if (vdist(v - eye, <, autocvar_bot_ai_enemydetectionradius))
if (bestrating > rating)
- if (bot_shouldattack(this, head))
+ if (bot_shouldattack(this, it))
{
traceline(eye, v, true, this);
- if (trace_ent == head || trace_fraction >= 1)
+ if (trace_ent == it || trace_fraction >= 1)
{
- best = head;
+ best = it;
bestrating = rating;
}
}
- }
+ });
if(!best && have_secondary_targets && !scan_secondary_targets)
{
scan_secondary_targets = true;
// restart the loop
- head = head2;
bestrating = 100000000;
goto scan_targets;
}
// Set flags to see through transparent objects
this.dphitcontentsmask |= DPCONTENTS_OPAQUE;
- head = head2;
scan_transparent = true;
}
// Step 4: Move to waypoint
if(this.havocbot_personal_waypoint==NULL)
{
- LOG_TRACE("Error: ", this.netname, " trying to walk to a non existent personal waypoint\n");
+ LOG_TRACE("Error: ", this.netname, " trying to walk to a non existent personal waypoint");
this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
return CMD_STATUS_ERROR;
}
bot_strategytoken_taken = true;
if(havocbot_moveto_refresh_route(this))
{
- LOG_TRACE(this.netname, " walking to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts)\n");
+ LOG_TRACE(this.netname, " walking to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts)");
this.havocbot_personal_waypoint_searchtime = time + 10;
this.havocbot_personal_waypoint_failcounter = 0;
}
this.havocbot_personal_waypoint_searchtime = time + 2;
if(this.havocbot_personal_waypoint_failcounter >= 30)
{
- LOG_TRACE("Warning: can't walk to the personal waypoint located at ", vtos(this.havocbot_personal_waypoint.origin),"\n");
+ LOG_TRACE("Warning: can't walk to the personal waypoint located at ", vtos(this.havocbot_personal_waypoint.origin));
this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
delete(this.havocbot_personal_waypoint);
return CMD_STATUS_ERROR;
}
else
- LOG_TRACE(this.netname, " can't walk to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts), trying later\n");
+ LOG_TRACE(this.netname, " can't walk to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts), trying later");
}
}
if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_REACHED)
{
// Step 5: Waypoint reached
- LOG_TRACE(this.netname, "'s personal waypoint reached\n");
+ LOG_TRACE(this.netname, "'s personal waypoint reached");
delete(this.havocbot_personal_waypoint);
this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_REACHED;
return CMD_STATUS_FINISHED;
// Wait until it is linked
if(!this.havocbot_personal_waypoint.wplinked)
{
- LOG_TRACE(this.netname, " waiting for personal waypoint to be linked\n");
+ LOG_TRACE(this.netname, " waiting for personal waypoint to be linked");
return CMD_STATUS_EXECUTING;
}
this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_GOING;
// Step 3: Route to waypoint
- LOG_TRACE(this.netname, " walking to its personal waypoint\n");
+ LOG_TRACE(this.netname, " walking to its personal waypoint");
return CMD_STATUS_EXECUTING;
}
wp = waypoint_spawnpersonal(this, pos);
if(wp==NULL)
{
- LOG_TRACE("Error: Can't spawn personal waypoint at ",vtos(pos),"\n");
+ LOG_TRACE("Error: Can't spawn personal waypoint at ",vtos(pos));
return CMD_STATUS_ERROR;
}
this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_LINKING;
// if pos is inside a teleport, then let's mark it as teleport waypoint
- FOREACH_ENTITY_CLASS("trigger_teleport", WarpZoneLib_BoxTouchesBrush(pos, pos, it, NULL),
+ IL_EACH(g_teleporters, WarpZoneLib_BoxTouchesBrush(pos, pos, it, NULL),
{
wp.wpflags |= WAYPOINTFLAG_TELEPORT;
this.lastteleporttime = 0;