#include "havocbot.qh"
-#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()
}
else
{
- if not(self.jumppadcount)
+ if (!self.jumppadcount)
self.havocbot_role();
}
// if we don't have a goal and we're under water look for a waypoint near the "shore" and push it
if(self.deadflag != DEAD_NO)
if(self.goalcurrent==world)
- if(self.waterlevel==WATERLEVEL_SWIMMING || self.aistatus & AI_STATUS_OUT_WATER)
+ if(self.waterlevel==WATERLEVEL_SWIMMING || (self.aistatus & AI_STATUS_OUT_WATER))
{
// Look for the closest waypoint out of water
- local entity newgoal, head;
- local float bestdistance, distance;
+ entity newgoal, head;
+ float bestdistance, distance;
newgoal = world;
bestdistance = 10000;
if (self.bot_aimtarg)
{
self.aistatus |= AI_STATUS_ATTACKING;
- self.aistatus &~= AI_STATUS_ROAMING;
+ self.aistatus &= ~AI_STATUS_ROAMING;
if(self.weapons)
{
- weapon_action(self.weapon, WR_AIM);
+ WEP_ACTION(self.weapon, WR_AIM);
if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self))
{
self.BUTTON_ATCK = FALSE;
}
else
{
- if(self.bot_aimtarg.classname=="player")
+ if(IS_PLAYER(self.bot_aimtarg))
bot_aimdir(self.bot_aimtarg.origin + self.bot_aimtarg.view_ofs - self.origin - self.view_ofs , -1);
}
}
else if (self.goalcurrent)
{
self.aistatus |= AI_STATUS_ROAMING;
- self.aistatus &~= AI_STATUS_ATTACKING;
+ self.aistatus &= ~AI_STATUS_ATTACKING;
- local vector now,v,next;//,heading;
- local float aimdistance,skillblend,distanceblend,blend;
+ vector now,v,next;//,heading;
+ float aimdistance,skillblend,distanceblend,blend;
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");
if(
- self.goalstack01 != self && self.goalstack01 != world && self.aistatus & AI_STATUS_RUNNING == 0 &&
+ self.goalstack01 != self && self.goalstack01 != world && ((self.aistatus & AI_STATUS_RUNNING) == 0) &&
!(self.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
)
next = ((self.goalstack01.absmin + self.goalstack01.absmax) * 0.5) - (self.origin + self.view_ofs);
havocbot_movetogoal();
// if the bot is not attacking, consider reloading weapons
- if not(self.aistatus & AI_STATUS_ATTACKING)
+ if (!(self.aistatus & AI_STATUS_ATTACKING))
{
float i;
entity e;
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"))))
+ if ((self.weapons & WepSet_FromWeapon(i)) && (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 vector keyboard;
- local float blend, maxspeed;
+ vector keyboard;
+ float blend, maxspeed;
float sk;
sk = skill + self.bot_moveskill;
, time);
keyboard = self.movement * (1.0 / maxspeed);
- local float trigger, trigger1;
+ float trigger, trigger1;
blend = bound(0,sk*0.1,1);
trigger = autocvar_bot_ai_keyboard_threshold;
trigger1 = 0 - trigger;
blend = bound(0,vlen(destorg-self.origin)/autocvar_bot_ai_keyboard_distance,1); // When getting close move with 360 degree
//dprint("movement ", vtos(self.movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n");
self.movement = self.movement + (keyboard - self.movement) * blend;
-};
+}
void havocbot_bunnyhop(vector dir)
{
- local float bunnyhopdistance;
- local vector deviation;
- local float maxspeed;
+ float bunnyhopdistance;
+ vector deviation;
+ float maxspeed;
vector gco, gno;
- if(autocvar_g_midair)
- return;
-
- // Don't jump when using some weapons
- /*
+ // Don't jump when attacking
if(self.aistatus & AI_STATUS_ATTACKING)
- if(self.weapon == WEP_RIFLE)
return;
- if(self.goalcurrent.classname == "player")
+ if(IS_PLAYER(self.goalcurrent))
return;
- */
maxspeed = autocvar_sv_maxspeed;
if(self.aistatus & AI_STATUS_DANGER_AHEAD)
{
- self.aistatus &~= AI_STATUS_RUNNING;
+ self.aistatus &= ~AI_STATUS_RUNNING;
self.BUTTON_JUMP = FALSE;
self.bot_canruntogoal = 0;
self.bot_timelastseengoal = 0;
if(self.waterlevel > WATERLEVEL_WETFEET)
{
- self.aistatus &~= AI_STATUS_RUNNING;
+ self.aistatus &= ~AI_STATUS_RUNNING;
return;
}
// for a period of time
if(time - self.bot_timelastseengoal > autocvar_bot_ai_bunnyhop_firstjumpdelay)
{
- local float checkdistance;
+ float checkdistance;
checkdistance = TRUE;
// don't run if it is too close
if(self.aistatus & AI_STATUS_ROAMING)
if(self.goalcurrent.classname=="waypoint")
- if not(self.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)
+ if (!(self.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL))
if(fabs(gco_z - self.origin_z) < self.maxs_z - self.mins_z)
if(self.goalstack01!=world)
{
if(checkdistance)
{
- self.aistatus &~= AI_STATUS_RUNNING;
+ self.aistatus &= ~AI_STATUS_RUNNING;
if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance)
self.BUTTON_JUMP = TRUE;
}
self.bot_timelastseengoal = 0;
}
+#if 0
// Release jump button
if(!cvar("sv_pogostick"))
- if(self.flags & FL_ONGROUND == 0)
+ if((self.flags & FL_ONGROUND) == 0)
{
if(self.velocity_z < 0 || vlen(self.velocity)<maxspeed)
self.BUTTON_JUMP = FALSE;
}
}
-};
+#endif
+}
void havocbot_movetogoal()
{
- local vector destorg;
- local vector diff;
- local vector dir;
- local vector flatdir;
- local vector m1;
- local vector m2;
- local vector evadeobstacle;
- local vector evadelava;
- local float s;
- local float maxspeed;
- local vector gco;
- //local float dist;
- local vector dodge;
+ vector destorg;
+ vector diff;
+ vector dir;
+ vector flatdir;
+ vector m1;
+ vector m2;
+ vector evadeobstacle;
+ vector evadelava;
+ float s;
+ float maxspeed;
+ vector gco;
+ //float dist;
+ vector dodge;
//if (self.goalentity)
// te_lightning2(self, self.origin, (self.goalentity.absmin + self.goalentity.absmax) * 0.5);
self.movement = '0 0 0';
}
// Take off
- if not(self.aistatus & AI_STATUS_JETPACK_FLYING)
+ if (!(self.aistatus & AI_STATUS_JETPACK_FLYING))
{
// Brake almost completely so it can get a good direction
if(vlen(self.velocity)>10)
}
// Switch to normal mode
self.navigation_jetpack_goal = world;
- self.aistatus &~= AI_STATUS_JETPACK_LANDING;
- self.aistatus &~= AI_STATUS_JETPACK_FLYING;
+ self.aistatus &= ~AI_STATUS_JETPACK_LANDING;
+ self.aistatus &= ~AI_STATUS_JETPACK_FLYING;
return;
}
}
else if(checkpvs(self.origin,self.goalcurrent))
{
// If I can see the goal switch to landing code
- self.aistatus &~= AI_STATUS_JETPACK_FLYING;
+ self.aistatus &= ~AI_STATUS_JETPACK_FLYING;
self.aistatus |= AI_STATUS_JETPACK_LANDING;
return;
}
// Handling of jump pads
if(self.jumppadcount)
{
- // If got stuck on the jump pad try to reach the farthest visible item
+ // If got stuck on the jump pad try to reach the farthest visible waypoint
if(self.aistatus & AI_STATUS_OUT_JUMPPAD)
{
if(fabs(self.velocity_z)<50)
{
- local entity head, newgoal;
- local float distance, bestdistance;
+ entity head, newgoal = world;
+ float distance, bestdistance = 0;
- for (head = findchainfloat(bot_pickup, TRUE); head; head = head.chain)
+ for (head = findchain(classname, "waypoint"); head; head = head.chain)
{
- if(head.classname=="worldspawn")
- continue;
distance = vlen(head.origin - self.origin);
if(distance>1000)
self.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
navigation_clearroute();
navigation_routetogoal(newgoal, self.origin);
- self.aistatus &~= AI_STATUS_OUT_JUMPPAD;
+ self.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
}
}
else
{
if(self.velocity_z>0)
{
- local float threshold;
+ float threshold, sxy;
+ vector velxy = self.velocity; velxy_z = 0;
+ sxy = vlen(velxy);
threshold = maxspeed * 0.2;
- if(fabs(self.velocity_x) < threshold && fabs(self.velocity_y) < threshold)
+ if(sxy < threshold)
{
- dprint("Warning: ", self.netname, " got stuck on a jumppad, trying to get out of it now\n");
+ dprint("Warning: ", self.netname, " got stuck on a jumppad (velocity in xy is ", ftos(sxy), "), trying to get out of it now\n");
self.aistatus |= AI_STATUS_OUT_JUMPPAD;
}
return;
}
// Don't chase players while using a jump pad
- if(self.goalcurrent.classname=="player" || self.goalstack01.classname=="player")
+ if(IS_PLAYER(self.goalcurrent) || IS_PLAYER(self.goalstack01))
return;
}
}
else if(self.aistatus & AI_STATUS_OUT_JUMPPAD)
- self.aistatus &~= AI_STATUS_OUT_JUMPPAD;
+ self.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
- // If there is a trigger_hurt right below try to use the jetpack or make a rocketjump
+ // If there is a trigger_hurt right below try to use the jetpack or make a rocketjump // WEAPONTODO: move this to bot think!
if(skill>6)
- if not(self.flags & FL_ONGROUND)
+ if (!(self.flags & FL_ONGROUND))
{
tracebox(self.origin, self.mins, self.maxs, self.origin + '0 0 -65536', MOVE_NOMONSTERS, self);
if(tracebox_hits_trigger_hurt(self.origin, self.mins, self.maxs, trace_endpos ))
else
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;
+ vector xyvelocity = self.velocity; xyvelocity_z = 0;
+ float xyspeed = xyvelocity * dir;
if(xyspeed < (maxspeed / 2))
{
return;
}
- else if(self.health>autocvar_g_balance_rocketlauncher_damage*0.5)
+ else if(self.health>WEP_CVAR(devastator, damage)*0.5)
{
if(self.velocity_z < 0)
- if(client_hasweapon(self, WEP_ROCKET_LAUNCHER, TRUE, FALSE))
+ if(client_hasweapon(self, WEP_DEVASTATOR, TRUE, FALSE))
{
self.movement_x = maxspeed;
return;
}
- self.switchweapon = WEP_ROCKET_LAUNCHER;
+ self.switchweapon = WEP_DEVASTATOR;
self.v_angle_x = 90;
self.BUTTON_ATCK = TRUE;
- self.rocketjumptime = time + autocvar_g_balance_rocketlauncher_detonatedelay;
+ self.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay);
return;
}
}
else
{
if(self.aistatus & AI_STATUS_OUT_WATER)
- self.aistatus &~= AI_STATUS_OUT_WATER;
+ self.aistatus &= ~AI_STATUS_OUT_WATER;
// jump if going toward an obstacle that doesn't look like stairs we
// can walk up directly
}
// avoiding dangers and obstacles
- local vector dst_ahead, dst_down;
+ vector dst_ahead, dst_down;
makevectors(self.v_angle_y * '0 1 0');
dst_ahead = self.origin + self.view_ofs + (self.velocity * 0.4) + (v_forward * 32 * 3);
dst_down = dst_ahead + '0 0 -1500';
// Check for water/slime/lava and dangerous edges
// (only when the bot is on the ground or jumping intentionally)
- self.aistatus &~= AI_STATUS_DANGER_AHEAD;
+ self.aistatus &= ~AI_STATUS_DANGER_AHEAD;
- if(trace_fraction == 1 && self.jumppadcount == 0)
- if(self.flags & FL_ONGROUND || self.aistatus & AI_STATUS_RUNNING || self.BUTTON_JUMP == TRUE)
+ if(trace_fraction == 1 && self.jumppadcount == 0 && !self.goalcurrent.wphardwired )
+ if((self.flags & FL_ONGROUND) || (self.aistatus & AI_STATUS_RUNNING) || self.BUTTON_JUMP == TRUE)
{
// Look downwards
traceline(dst_ahead , dst_down, TRUE, world);
if(tracebox_hits_trigger_hurt(dst_ahead, self.mins, self.maxs, trace_endpos))
{
// 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;
+ dprint("bot ", self.netname, " avoided the goal ", self.goalcurrent.classname, " ", etos(self.goalcurrent), " because it led to a dangerous path; goal stack cleared\n");
+ navigation_clearroute();
+ return;
}
}
}
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.absmin + self.enemy.absmax ) * 0.5 ), TRUE, world);
- if(trace_ent.classname == "player")
+ if(IS_PLAYER(trace_ent))
dir = dir * bound(0,(skill+self.bot_dodgeskill)/7,1);
dir = normalize(dir + dodge + evadeobstacle + evadelava);
if ((dir * v_up) >= autocvar_sv_jumpvelocity*0.5 && (self.flags & FL_ONGROUND)) self.BUTTON_JUMP=1;
if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-skill-self.bot_dodgeskill)*0.1,1)) self.BUTTON_JUMP=TRUE;
if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-skill-self.bot_dodgeskill)*0.1,1)) self.havocbot_ducktime=time+0.3/bound(0.1,skill+self.bot_dodgeskill,10);
-};
+}
void havocbot_chooseenemy()
{
- local entity head, best, head2;
- local float rating, bestrating, i, f;
- local vector eye, v;
+ entity head, best, head2;
+ float rating, bestrating, i, hf;
+ vector eye, v;
if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self))
{
self.enemy = world;
if (self.health > 30)
{
// remain tracking him for a shot while (case he went after a small corner or pilar
- self.havocbot_chooseenemy_finished = time + autocvar_bot_ai_enemydetectioninterval;
+ self.havocbot_chooseenemy_finished = time + 0.5;
return;
}
// enemy isn't visible, or is far away, or we're injured severely
bestrating = 100000000;
head = head2 = findchainfloat(bot_attack, TRUE);
+ // Backup hit flags
+ hf = self.dphitcontentsmask;
+
// Search for enemies, if no enemy can be seen directly try to look through transparent objects
- for(;;)
+
+ self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
+
+ for(i = 0; ; ++i)
{
while (head)
{
break;
// Set flags to see through transparent objects
- f = self.dphitcontentsmask;
- self.dphitcontentsmask = DPCONTENTS_OPAQUE;
+ self.dphitcontentsmask |= DPCONTENTS_OPAQUE;
head = head2;
- ++i;
}
- // Restore hit flags if needed
- if(i)
- self.dphitcontentsmask = f;
+ // Restore hit flags
+ self.dphitcontentsmask = hf;
self.enemy = best;
self.havocbot_stickenemy = TRUE;
-};
+}
float havocbot_chooseweapon_checkreload(float new_weapon)
{
// 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;
+ float i, other_weapon_available = FALSE;
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))
+ if (WEP_ACTION(i, WR_CHECKAMMO1) + WEP_ACTION(i, WR_CHECKAMMO2))
other_weapon_available = TRUE;
}
if(other_weapon_available)
void havocbot_chooseweapon()
{
- local float i;
+ float i;
// ;)
- if(g_weaponarena == WEPBIT_TUBA)
+ if(g_weaponarena_weapons == WEPSET_TUBA)
{
self.switchweapon = WEP_TUBA;
return;
if(i < 1)
return;
- local float w;
- local float distance; distance=bound(10,vlen(self.origin-self.enemy.origin)-200,10000);
+ float w;
+ float distance; distance=bound(10,vlen(self.origin-self.enemy.origin)-200,10000);
// Should it do a weapon combo?
- local float af, ct, combo_time, combo;
+ float af, ct, combo_time, combo;
af = ATTACK_FINISHED(self);
ct = autocvar_bot_ai_weapon_combo_threshold;
}
}
}
-};
+}
void havocbot_aim()
{
- local vector selfvel, enemyvel;
+ vector selfvel, enemyvel;
// if(self.flags & FL_INWATER)
// return;
if (time < self.nextaim)
}
else
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()
{
float havocbot_moveto(vector pos)
{
- local entity wp;
+ entity wp;
if(self.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
{
if(self.havocbot_personal_waypoint==world)
{
dprint("Error: ", self.netname, " trying to walk to a non existent personal waypoint\n");
- self.aistatus &~= AI_STATUS_WAYPOINT_PERSONAL_GOING;
+ self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
return CMD_STATUS_ERROR;
}
if(self.havocbot_personal_waypoint_failcounter >= 30)
{
dprint("Warning: can't walk to the personal waypoint located at ", vtos(self.havocbot_personal_waypoint.origin),"\n");
- self.aistatus &~= AI_STATUS_WAYPOINT_PERSONAL_LINKING;
+ self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
remove(self.havocbot_personal_waypoint);
return CMD_STATUS_ERROR;
}
debuggoalstack();
// Heading
- local vector dir = ( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ) - (self.origin + self.view_ofs);
+ vector dir = ( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ) - (self.origin + self.view_ofs);
dir_z = 0;
bot_aimdir(dir, -1);
// Step 5: Waypoint reached
dprint(self.netname, "'s personal waypoint reached\n");
remove(self.havocbot_personal_waypoint);
- self.aistatus &~= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
+ self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_REACHED;
return CMD_STATUS_FINISHED;
}
}
self.havocbot_personal_waypoint_searchtime = time; // so we set the route next frame
- self.aistatus &~= AI_STATUS_WAYPOINT_PERSONAL_LINKING;
+ self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
self.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_GOING;
// Step 3: Route to waypoint
// LordHavoc: disabled because this is too expensive
return '0 0 0';
#if 0
- local entity head;
- local vector dodge, v, n;
- local float danger, bestdanger, vl, d;
+ entity head;
+ vector dodge, v, n;
+ float danger, bestdanger, vl, d;
dodge = '0 0 0';
bestdanger = -20;
// check for dangerous objects near bot or approaching bot
}
return dodge;
#endif
-};
+}