#include "role_ctf.qc"
#include "role_onslaught.qc"
#include "role_keyhunt.qc"
+#include "role_freezetag.qc"
+#include "role_keepaway.qc"
#include "roles.qc"
void havocbot_ai()
local vector now,v,next;//,heading;
local float aimdistance,skillblend,distanceblend,blend;
- next = now = self.goalcurrent.origin - (self.origin + self.view_ofs);
+ next = now = ( (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5) - (self.origin + self.view_ofs);
aimdistance = vlen(now);
//heading = self.velocity;
//dprint(self.goalstack01.classname,etos(self.goalstack01),"\n");
self.goalstack01 != self && self.goalstack01 != world && self.aistatus & AI_STATUS_RUNNING == 0 &&
!(self.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
)
- next = self.goalstack01.origin - (self.origin + self.view_ofs);
+ next = ((self.goalstack01.absmin + self.goalstack01.absmax) * 0.5) - (self.origin + self.view_ofs);
skillblend=bound(0,(skill+self.bot_moveskill-2.5)*0.5,1); //lower skill player can't preturn
distanceblend=bound(0,aimdistance/autocvar_bot_ai_keyboard_distance,1);
bot_aimdir(v, -1);
}
havocbot_movetogoal();
+
+ // if the bot is not attacking, consider reloading weapons
+ if not(self.aistatus & AI_STATUS_ATTACKING)
+ {
+ float i;
+ entity e;
+
+ // we are currently holding a weapon that's not fully loaded, reload it
+ if(skill >= 2) // bots can only reload the held weapon on purpose past this skill
+ if(self.clip_load < self.clip_size)
+ self.impulse = 20; // "press" the reload button, not sure if this is done right
+
+ // if we're not reloading a weapon, switch to any weapon in our invnetory that's not fully loaded to reload it next
+ // the code above executes next frame, starting the reloading then
+ if(skill >= 5) // bots can only look for unloaded weapons past this skill
+ if(self.clip_load >= 0) // only if we're not reloading a weapon already
+ {
+ for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+ {
+ e = get_weaponinfo(i);
+ if ((e.spawnflags & WEP_FLAG_RELOADABLE) && (self.weapon_load[i] < cvar(strcat("g_balance_", e.netname, "_reload_ammo"))))
+ self.switchweapon = i;
+ }
+ }
+ }
};
void havocbot_keyboard_movement(vector destorg)
local float trigger, trigger1;
blend = bound(0,sk*0.1,1);
- trigger = autocvar_bot_ai_keyboard_treshold;
+ trigger = autocvar_bot_ai_keyboard_threshold;
trigger1 = 0 - trigger;
// categorize forward movement
local float bunnyhopdistance;
local vector deviation;
local float maxspeed;
+ vector gco, gno;
if(autocvar_g_midair)
return;
// Don't jump when using some weapons
/*
if(self.aistatus & AI_STATUS_ATTACKING)
- if(self.weapon == WEP_SNIPERRIFLE)
+ if(self.weapon == WEP_RIFLE)
return;
if(self.goalcurrent.classname == "player")
self.bot_timelastseengoal = 0;
}
- bunnyhopdistance = vlen(self.origin - self.goalcurrent.origin);
+ gco = (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5;
+ bunnyhopdistance = vlen(self.origin - gco);
// Run only to visible goals
if(self.flags & FL_ONGROUND)
if(self.aistatus & AI_STATUS_ROAMING)
if(self.goalcurrent.classname=="waypoint")
if not(self.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)
- if(fabs(self.goalcurrent.origin_z - self.origin_z) < self.maxs_z - self.mins_z)
+ if(fabs(gco_z - self.origin_z) < self.maxs_z - self.mins_z)
if(self.goalstack01!=world)
{
- deviation = vectoangles(self.goalstack01.origin - self.origin) - vectoangles(self.goalcurrent.origin - self.origin);
+ gno = (self.goalstack01.absmin + self.goalstack01.absmax) * 0.5;
+ deviation = vectoangles(gno - self.origin) - vectoangles(gco - self.origin);
while (deviation_y < -180) deviation_y = deviation_y + 360;
while (deviation_y > 180) deviation_y = deviation_y - 360;
if(fabs(deviation_y) < 20)
- if(bunnyhopdistance < vlen(self.origin - self.goalstack01.origin))
- if(fabs(self.goalstack01.origin_z - self.goalcurrent.origin_z) < self.maxs_z - self.mins_z)
+ if(bunnyhopdistance < vlen(self.origin - gno))
+ if(fabs(gno_z - gco_z) < self.maxs_z - self.mins_z)
{
- if(vlen(self.goalcurrent.origin - self.goalstack01.origin) > autocvar_bot_ai_bunnyhop_startdistance)
+ if(vlen(gco - gno) > autocvar_bot_ai_bunnyhop_startdistance)
if(checkpvs(self.origin + self.view_ofs, self.goalstack01))
{
checkdistance = FALSE;
local vector evadelava;
local float s;
local float maxspeed;
+ local vector gco;
//local float dist;
local vector dodge;
//if (self.goalentity)
if(self.goalcurrent==self.navigation_jetpack_goal)
if(self.ammo_fuel)
{
- #ifdef DEBUG_BOT_GOALSTACK
+ if(autocvar_bot_debug_goalstack)
+ {
debuggoalstack();
te_wizspike(self.navigation_jetpack_point);
- #endif
+ }
// Take off
if not(self.aistatus & AI_STATUS_JETPACK_FLYING)
float db, v, d;
vector dxy;
- dxy = self.origin - self.goalcurrent.origin; dxy_z = 0;
+ dxy = self.origin - ( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ); dxy_z = 0;
d = vlen(dxy);
v = vlen(self.velocity - self.velocity_z * '0 0 1');
db = (pow(v,2) / (autocvar_g_jetpack_acceleration_side * 2)) + 100;
if(distance>1000)
continue;
- traceline(self.origin + self.view_ofs , head.origin, TRUE, world);
+ traceline(self.origin + self.view_ofs , ( ( head.absmin + head.absmax ) * 0.5 ), TRUE, world);
if(trace_fraction<1)
continue;
if(self.goalcurrent==world)
dir = v_forward;
else
- dir = normalize(self.goalcurrent.origin - self.origin);
+ dir = normalize(( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ) - self.origin);
local vector xyvelocity = self.velocity; xyvelocity_z = 0;
local float xyspeed = xyvelocity * dir;
return;
}
-#ifdef DEBUG_BOT_GOALSTACK
- debuggoalstack();
-#endif
+
+ if(autocvar_bot_debug_goalstack)
+ debuggoalstack();
m1 = self.goalcurrent.origin + self.goalcurrent.mins;
m2 = self.goalcurrent.origin + self.goalcurrent.maxs;
dir = normalize(diff);
flatdir = diff;flatdir_z = 0;
flatdir = normalize(flatdir);
+ gco = (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5;
//if (self.bot_dodgevector_time < time)
{
}
else
{
- if(self.velocity_z >= 0 && !(self.watertype == CONTENT_WATER && self.goalcurrent.origin_z < self.origin_z) &&
+ if(self.velocity_z >= 0 && !(self.watertype == CONTENT_WATER && gco_z < self.origin_z) &&
( !(self.waterlevel == WATERLEVEL_WETFEET && self.watertype == CONTENT_WATER) || self.aistatus & AI_STATUS_OUT_WATER))
self.BUTTON_JUMP = TRUE;
else
{
// Remove dangerous dynamic goals from stack
if (self.goalcurrent.classname == "player" || self.goalcurrent.classname == "droppedweapon")
+ {
navigation_poproute();
+ if(self.goalcurrent)
+ gco = (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5;
+ }
// try to stop
flatdir = '0 0 0';
evadeobstacle = normalize(self.velocity) * -1;
dodge = havocbot_dodge();
dodge = dodge * bound(0,0.5+(skill+self.bot_dodgeskill)*0.1,1);
evadelava = evadelava * bound(1,3-(skill+self.bot_dodgeskill),3); //Noobs fear lava a lot and take more distance from it
- traceline(self.origin, self.enemy.origin, TRUE, world);
+ traceline(self.origin, ( ( self.enemy.absmin + self.enemy.absmax ) * 0.5 ), TRUE, world);
if(trace_ent.classname == "player")
dir = dir * bound(0,(skill+self.bot_dodgeskill)/7,1);
// and not really really far away
// and we're not severely injured
// then keep tracking for a half second into the future
- traceline(self.origin+self.view_ofs, self.enemy.origin+self.enemy.view_ofs*0.5,FALSE,world);
+ traceline(self.origin+self.view_ofs, ( self.enemy.absmin + self.enemy.absmax ) * 0.5,FALSE,world);
if (trace_ent == self.enemy || trace_fraction == 1)
- if (vlen(self.enemy.origin - self.origin) < 1000)
+ if (vlen((( self.enemy.absmin + self.enemy.absmax ) * 0.5) - self.origin) < 1000)
if (self.health > 30)
{
// remain tracking him for a shot while (case he went after a small corner or pilar
// I want to do a second scan if no enemy was found or I don't have weapons
// TODO: Perform the scan when using the rifle (requires changes on the rifle code)
- if(best || self.weapons) // || self.weapon == WEP_SNIPERRIFLE
+ if(best || self.weapons) // || self.weapon == WEP_RIFLE
break;
if(i)
break;
self.havocbot_stickenemy = TRUE;
};
+float havocbot_chooseweapon_checkreload(float new_weapon)
+{
+ // bots under this skill cannot find unloaded weapons to reload idly when not in combat,
+ // so skip this for them, or they'll never get to reload their weapons at all.
+ // this also allows bots under this skill to be more stupid, and reload more often during combat :)
+ if(skill < 5)
+ return FALSE;
+
+ // if this weapon is scheduled for reloading, don't switch to it during combat
+ if (self.weapon_load[new_weapon] < 0)
+ {
+ local float i, other_weapon_available;
+ for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+ {
+ // if we are out of ammo for all other weapons, it's an emergency to switch to anything else
+ if (weapon_action(i, WR_CHECKAMMO1) + weapon_action(i, WR_CHECKAMMO2))
+ other_weapon_available = TRUE;
+ }
+ if(other_weapon_available)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
void havocbot_chooseweapon()
{
local float i;
if(i < 1)
return;
- // Workaround for rifle reloading (..)
- if(self.weapon == WEP_SNIPERRIFLE)
- if(i < autocvar_g_balance_sniperrifle_reloadtime + 1)
- return;
-
local float w;
local float distance; distance=bound(10,vlen(self.origin-self.enemy.origin)-200,10000);
if ( distance > bot_distance_far ) {
for(i=0; i < WEP_COUNT && bot_weapons_far[i] != -1 ; ++i){
w = bot_weapons_far[i];
- if ( client_hasweapon(self, w, TRUE, FALSE) ){
- if ( self.weapon == w && combo)
+ if ( client_hasweapon(self, w, TRUE, FALSE) )
+ {
+ if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w))
continue;
self.switchweapon = w;
return;
if ( distance > bot_distance_close) {
for(i=0; i < WEP_COUNT && bot_weapons_mid[i] != -1 ; ++i){
w = bot_weapons_mid[i];
- if ( client_hasweapon(self, w, TRUE, FALSE) ){
- if ( self.weapon == w && combo)
+ if ( client_hasweapon(self, w, TRUE, FALSE) )
+ {
+ if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w))
continue;
self.switchweapon = w;
return;
// Choose weapons for close distance
for(i=0; i < WEP_COUNT && bot_weapons_close[i] != -1 ; ++i){
w = bot_weapons_close[i];
- if ( client_hasweapon(self, w, TRUE, FALSE) ){
- if ( self.weapon == w && combo)
+ if ( client_hasweapon(self, w, TRUE, FALSE) )
+ {
+ if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w))
continue;
self.switchweapon = w;
return;
enemyvel = self.enemy.velocity;
if (!self.enemy.waterlevel)
enemyvel_z = 0;
- lag_additem(time + self.ping, 0, 0, self.enemy, self.origin, selfvel, self.enemy.origin, enemyvel);
+ lag_additem(time + self.ping, 0, 0, self.enemy, self.origin, selfvel, (self.enemy.absmin + self.enemy.absmax) * 0.5, enemyvel);
}
else
- lag_additem(time + self.ping, 0, 0, world, self.origin, selfvel, self.goalcurrent.origin, '0 0 0');
+ lag_additem(time + self.ping, 0, 0, world, self.origin, selfvel, ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5, '0 0 0');
};
float havocbot_moveto_refresh_route()
}
}
- #ifdef DEBUG_BOT_GOALSTACK
+ if(autocvar_bot_debug_goalstack)
debuggoalstack();
- #endif
// Heading
- local vector dir = self.goalcurrent.origin - (self.origin + self.view_ofs);
+ local vector dir = ( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ) - (self.origin + self.view_ofs);
dir_z = 0;
bot_aimdir(dir, -1);