}
else
{
- if (!this.jumppadcount && !STAT(FROZEN, this))
+ if (!this.jumppadcount && !STAT(FROZEN, this)
+ && !(this.goalcurrent_prev && (this.goalcurrent_prev.wpflags & WAYPOINTFLAG_JUMP) && !IS_ONGROUND(this)))
+ {
+ // find a new goal
this.havocbot_role(this); // little too far down the rabbit hole
+ }
}
// if we don't have a goal and we're under water look for a waypoint near the "shore" and push it
{
if (this.goalcurrent)
navigation_clearroute(this);
+ this.enemy = NULL;
+ this.bot_aimtarg = NULL;
return;
}
if (skill > 6 && !(IS_ONGROUND(this)))
{
#define ROCKETJUMP_DAMAGE() WEP_CVAR(devastator, damage) * 0.8 \
- * ((this.strength_finished > time) ? autocvar_g_balance_powerup_strength_selfdamage : 1) \
- * ((this.invincible_finished > time) ? autocvar_g_balance_powerup_invincible_takedamage : 1)
+ * ((STAT(STRENGTH_FINISHED, this) > time) ? autocvar_g_balance_powerup_strength_selfdamage : 1) \
+ * ((STAT(INVINCIBLE_FINISHED, this) > time) ? autocvar_g_balance_powerup_invincible_takedamage : 1)
+
+ // save some CPU cycles by checking trigger_hurt after checking
+ // that something can be done to evade it (cheaper checks)
+ int action_for_trigger_hurt = 0;
+ if (this.items & IT_JETPACK)
+ action_for_trigger_hurt = 1;
+ else if (!this.jumppadcount && !waypoint_is_hardwiredlink(this.goalcurrent_prev, this.goalcurrent)
+ && !(this.goalcurrent_prev && this.goalcurrent_prev.wpflags & WAYPOINTFLAG_JUMP)
+ && GetResource(this, RES_HEALTH) + GetResource(this, RES_ARMOR) > ROCKETJUMP_DAMAGE())
+ {
+ action_for_trigger_hurt = 2;
+ }
+ else if (!this.goalcurrent)
+ action_for_trigger_hurt = 3;
- tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 -65536', MOVE_NOMONSTERS, this);
- if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos ))
- if(this.items & IT_JETPACK)
+ if (action_for_trigger_hurt)
+ {
+ tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 -65536', MOVE_NOMONSTERS, this);
+ if(!tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos))
+ action_for_trigger_hurt = 0;
+ }
+
+ if(action_for_trigger_hurt == 1) // jetpack
{
tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 65536', MOVE_NOMONSTERS, this);
if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos + '0 0 1' ))
return;
}
- else if(!this.jumppadcount && !waypoint_is_hardwiredlink(this.goalcurrent_prev, this.goalcurrent)
- && !(this.goalcurrent_prev && this.goalcurrent_prev.wpflags & WAYPOINTFLAG_JUMP)
- && GetResource(this, RES_HEALTH) + GetResource(this, RES_ARMOR) > ROCKETJUMP_DAMAGE())
+ else if(action_for_trigger_hurt == 2) // rocketjump
{
if(this.velocity.z < 0)
{
}
}
}
- else
+ else if(action_for_trigger_hurt == 3) // no goal
{
// If there is no goal try to move forward
- if(this.goalcurrent==NULL)
- CS(this).movement_x = maxspeed;
+ CS(this).movement_x = maxspeed;
}
}
dir = normalize(diff);
flatdir = (diff.z == 0) ? dir : normalize(vec2(diff));
- vector evadedanger = '0 0 0';
+ bool danger_detected = false;
+ vector do_break = '0 0 0';
//if (this.bot_dodgevector_time < time)
{
{
PHYS_INPUT_BUTTON_JUMP(this) = true;
this.bot_jump_time = time;
- // avoid changing route while bot is jumping a gap
- navigation_goalrating_timeout_extend_if_needed(this, 1.5);
}
}
else if (!this.goalstack01 || (this.goalcurrent.wpflags & (WAYPOINTFLAG_TELEPORT | WAYPOINTFLAG_LADDER)))
{
if (vlen2(flat_diff) < vlen2(offset))
{
- if (this.goalcurrent.wpflags & WAYPOINTFLAG_JUMP && this.goalstack01)
+ if ((this.goalcurrent.wpflags & WAYPOINTFLAG_JUMP) && this.goalstack01)
{
// oblique warpzones need a jump otherwise bots gets stuck
PHYS_INPUT_BUTTON_JUMP(this) = true;
bool unreachable = false;
s = CONTENT_SOLID;
- bool danger_detected = false;
if (trace_fraction == 1 && !this.jumppadcount
&& !waypoint_is_hardwiredlink(this.goalcurrent_prev, this.goalcurrent)
&& !(this.goalcurrent_prev && (this.goalcurrent_prev.wpflags & WAYPOINTFLAG_JUMP)))
//te_lightning2(NULL, dst_ahead, trace_endpos); // Draw "downwards" look
if(trace_endpos.z < this.origin.z + this.mins.z)
{
- s = pointcontents(trace_endpos + '0 0 1');
- if (s != CONTENT_SOLID)
- if (s == CONTENT_LAVA || s == CONTENT_SLIME)
+ if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
danger_detected = true;
- else if (s == CONTENT_SKY)
+ else if (trace_endpos.z < min(this.origin.z + this.mins.z, this.goalcurrent.origin.z) - 100)
danger_detected = true;
- else if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
+ else
{
- // the traceline check isn't enough but is good as optimization,
- // when not true (most of the time) this tracebox call is avoided
- tracebox(dst_ahead, this.mins, this.maxs, dst_down, true, this);
- if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
+ s = pointcontents(trace_endpos + '0 0 1');
+ if (s != CONTENT_SOLID)
{
- if (destorg.z > this.origin.z + jumpstepheightvec.z)
+ if (s == CONTENT_LAVA || s == CONTENT_SLIME)
+ danger_detected = true;
+ else if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
{
- // the goal is probably on an upper platform, assume bot can't get there
- unreachable = true;
+ // the traceline check isn't enough but is good as optimization,
+ // when not true (most of the time) this tracebox call is avoided
+ tracebox(dst_ahead, this.mins, this.maxs, dst_down, true, this);
+ if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
+ {
+ if (destorg.z > this.origin.z + jumpstepheightvec.z)
+ {
+ // the goal is probably on an upper platform, assume bot can't get there
+ unreachable = true;
+ }
+ else
+ danger_detected = true;
+ }
}
- else
- danger_detected = true;
}
}
}
}
- if (danger_detected && fabs(deviation.y) < 80
- && (fabs(deviation.y) > 5 || vdist(vec2(this.velocity), >, maxspeed * 1.5)))
- {
- evadedanger = normalize(this.velocity) * -1;
- evadedanger.z = 0;
- }
dir = flatdir;
makevectors(this.v_angle.y * '0 1 0');
// tracebox wouldn't work when bot is still on the ledge
traceline(this.origin, this.origin - '0 0 200', true, this);
if (this.origin.z - trace_endpos.z > 120)
- evadedanger = normalize(this.velocity) * -1;
+ do_break = normalize(this.velocity) * -1;
}
if(unreachable)
dodge = havocbot_dodge(this);
if (dodge)
dodge *= bound(0, 0.5 + (skill + this.bot_dodgeskill) * 0.1, 1);
- evadedanger *= bound(1, 3 - (skill + this.bot_dodgeskill), 3); // Noobs fear dangers a lot and take more distance from them
if (this.enemy)
{
traceline(this.origin, (this.enemy.absmin + this.enemy.absmax) * 0.5, true, NULL);
}
float ladder_zdir = 0;
- if(time < this.ladder_time)
+ if(this.ladder_entity)
{
if(this.goalcurrent.origin.z + this.goalcurrent.mins.z > this.origin.z + this.mins.z)
{
bot_aimdir(this, dir, 0);
}
+ vector evadedanger = '0 0 0';
if (!ladder_zdir)
{
dir *= dodge_enemy_factor;
- dir = normalize(dir + dodge + evadedanger);
+ if (danger_detected && vdist(this.velocity, >, maxspeed * 0.8) && this.goalcurrent_prev
+ && this.goalcurrent.classname == "waypoint")
+ {
+ vector p = this.origin + this.velocity * 0.2;
+ vector evadedanger = point_line_vec(p, vec2(this.goalcurrent_prev.origin) + eZ * p.z,
+ vec2(destorg - this.goalcurrent_prev.origin));
+ if (vdist(evadedanger, >, 20))
+ {
+ if (vdist(evadedanger, >, 40))
+ do_break = normalize(this.velocity) * -1;
+ evadedanger = normalize(evadedanger);
+ evadedanger *= bound(1, 3 - (skill + this.bot_dodgeskill), 3); // Noobs fear dangers a lot and take more distance from them
+ }
+ else
+ evadedanger = '0 0 0';
+ }
+ dir = normalize(dir + dodge + do_break + evadedanger);
}
makevectors(this.v_angle);
havocbot_keyboard_movement(this, destorg);
// Bunnyhop!
- if (!bunnyhop_forbidden && skill + this.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset)
+ if (!bunnyhop_forbidden && !evadedanger && !do_break && skill + this.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset)
havocbot_bunnyhop(this, dir);
if (dir * v_up >= autocvar_sv_jumpvelocity * 0.5 && IS_ONGROUND(this))
// Choose weapons for far distance
if ( distance > bot_distance_far ) {
- for(i=0; i < Weapons_COUNT && bot_weapons_far[i] != -1 ; ++i){
+ for(i=0; i < REGISTRY_COUNT(Weapons) && bot_weapons_far[i] != -1 ; ++i){
w = bot_weapons_far[i];
- if ( client_hasweapon(this, Weapons_from(w), weaponentity, true, false) )
+ if ( client_hasweapon(this, REGISTRY_GET(Weapons, w), weaponentity, true, false) )
{
if ((this.(weaponentity).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, weaponentity, w))
continue;
- this.(weaponentity).m_switchweapon = Weapons_from(w);
+ this.(weaponentity).m_switchweapon = REGISTRY_GET(Weapons, w);
return;
}
}
// Choose weapons for mid distance
if ( distance > bot_distance_close) {
- for(i=0; i < Weapons_COUNT && bot_weapons_mid[i] != -1 ; ++i){
+ for(i=0; i < REGISTRY_COUNT(Weapons) && bot_weapons_mid[i] != -1 ; ++i){
w = bot_weapons_mid[i];
- if ( client_hasweapon(this, Weapons_from(w), weaponentity, true, false) )
+ if ( client_hasweapon(this, REGISTRY_GET(Weapons, w), weaponentity, true, false) )
{
if ((this.(weaponentity).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, weaponentity, w))
continue;
- this.(weaponentity).m_switchweapon = Weapons_from(w);
+ this.(weaponentity).m_switchweapon = REGISTRY_GET(Weapons, w);
return;
}
}
}
// Choose weapons for close distance
- for(i=0; i < Weapons_COUNT && bot_weapons_close[i] != -1 ; ++i){
+ for(i=0; i < REGISTRY_COUNT(Weapons) && bot_weapons_close[i] != -1 ; ++i){
w = bot_weapons_close[i];
- if ( client_hasweapon(this, Weapons_from(w), weaponentity, true, false) )
+ if ( client_hasweapon(this, REGISTRY_GET(Weapons, w), weaponentity, true, false) )
{
if ((this.(weaponentity).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, weaponentity, w))
continue;
- this.(weaponentity).m_switchweapon = Weapons_from(w);
+ this.(weaponentity).m_switchweapon = REGISTRY_GET(Weapons, w);
return;
}
}