#include "havocbot.qh"
+#include "roles.qh"
+
#include <server/defs.qh>
#include <server/miscfunctions.qh>
#include "../cvars.qh"
#include <lib/warpzone/common.qh>
-.float speed;
-
void havocbot_ai(entity this)
{
if(this.draggedby)
void havocbot_keyboard_movement(entity this, vector destorg)
{
- vector keyboard;
+ if(time <= this.havocbot_keyboardtime)
+ return;
- if (time > this.havocbot_keyboardtime)
+ float sk = skill + this.bot_moveskill;
+ this.havocbot_keyboardtime =
+ max(
+ this.havocbot_keyboardtime
+ + 0.05 / max(1, sk + this.havocbot_keyboardskill)
+ + random() * 0.025 / max(0.00025, skill + this.havocbot_keyboardskill)
+ , time);
+ vector keyboard = CS(this).movement / autocvar_sv_maxspeed;
+
+ float trigger = autocvar_bot_ai_keyboard_threshold;
+
+ // categorize forward movement
+ // at skill < 1.5 only forward
+ // at skill < 2.5 only individual directions
+ // at skill < 4.5 only individual directions, and forward diagonals
+ // at skill >= 4.5, all cases allowed
+ if (keyboard.x > trigger)
{
- float sk = skill + this.bot_moveskill;
- this.havocbot_keyboardtime =
- max(
- this.havocbot_keyboardtime
- + 0.05 / max(1, sk + this.havocbot_keyboardskill)
- + random() * 0.025 / max(0.00025, skill + this.havocbot_keyboardskill)
- , time);
- keyboard = CS(this).movement / autocvar_sv_maxspeed;
-
- float trigger = autocvar_bot_ai_keyboard_threshold;
-
- // categorize forward movement
- // at skill < 1.5 only forward
- // at skill < 2.5 only individual directions
- // at skill < 4.5 only individual directions, and forward diagonals
- // at skill >= 4.5, all cases allowed
- if (keyboard.x > trigger)
- {
- keyboard.x = 1;
- if (sk < 2.5)
- keyboard.y = 0;
- }
- else if (keyboard.x < -trigger && sk > 1.5)
- {
- keyboard.x = -1;
- if (sk < 4.5)
- keyboard.y = 0;
- }
- else
- {
- keyboard.x = 0;
- if (sk < 1.5)
- keyboard.y = 0;
- }
+ keyboard.x = 1;
+ if (sk < 2.5)
+ keyboard.y = 0;
+ }
+ else if (keyboard.x < -trigger && sk > 1.5)
+ {
+ keyboard.x = -1;
if (sk < 4.5)
- keyboard.z = 0;
-
- if (keyboard.y > trigger)
- keyboard.y = 1;
- else if (keyboard.y < -trigger)
- keyboard.y = -1;
- else
keyboard.y = 0;
+ }
+ else
+ {
+ keyboard.x = 0;
+ if (sk < 1.5)
+ keyboard.y = 0;
+ }
+ if (sk < 4.5)
+ keyboard.z = 0;
- if (keyboard.z > trigger)
- keyboard.z = 1;
- else if (keyboard.z < -trigger)
- keyboard.z = -1;
- else
- keyboard.z = 0;
+ if (keyboard.y > trigger)
+ keyboard.y = 1;
+ else if (keyboard.y < -trigger)
+ keyboard.y = -1;
+ else
+ keyboard.y = 0;
- // make sure bots don't get stuck if havocbot_keyboardtime is very high
- if (keyboard == '0 0 0')
- this.havocbot_keyboardtime = min(this.havocbot_keyboardtime, time + 0.2);
+ if (keyboard.z > trigger)
+ keyboard.z = 1;
+ else if (keyboard.z < -trigger)
+ keyboard.z = -1;
+ else
+ keyboard.z = 0;
- this.havocbot_keyboard = keyboard * autocvar_sv_maxspeed;
- if (this.havocbot_ducktime > time)
- PHYS_INPUT_BUTTON_CROUCH(this) = true;
+ // make sure bots don't get stuck if havocbot_keyboardtime is very high
+ if (keyboard == '0 0 0')
+ this.havocbot_keyboardtime = min(this.havocbot_keyboardtime, time + 0.2);
- keyboard = this.havocbot_keyboard;
- float blend = bound(0, vlen(destorg - this.origin) / autocvar_bot_ai_keyboard_distance, 1); // When getting close move with 360 degree
- //dprint("movement ", vtos(CS(this).movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n");
- CS(this).movement = CS(this).movement + (keyboard - CS(this).movement) * blend;
- }
+ this.havocbot_keyboard = keyboard * autocvar_sv_maxspeed;
+ if (this.havocbot_ducktime > time)
+ PHYS_INPUT_BUTTON_CROUCH(this) = true;
+
+ keyboard = this.havocbot_keyboard;
+ float blend = bound(0, vlen(destorg - this.origin) / autocvar_bot_ai_keyboard_distance, 1); // When getting close move with 360 degree
+ //dprint("movement ", vtos(CS(this).movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n");
+ CS(this).movement = CS(this).movement + (keyboard - CS(this).movement) * blend;
}
void havocbot_bunnyhop(entity this, vector dir)
{
- float bunnyhopdistance;
- vector deviation;
- float maxspeed;
-
// Don't jump when attacking
if(this.aistatus & AI_STATUS_ATTACKING)
return;
if(IS_PLAYER(this.goalcurrent))
return;
- maxspeed = autocvar_sv_maxspeed;
-
- if(this.aistatus & AI_STATUS_RUNNING && vdist(this.velocity, <, autocvar_sv_maxspeed * 0.75)
- || this.aistatus & AI_STATUS_DANGER_AHEAD)
+ if((this.aistatus & AI_STATUS_RUNNING) && vdist(this.velocity, <, autocvar_sv_maxspeed * 0.75)
+ || (this.aistatus & AI_STATUS_DANGER_AHEAD))
{
this.aistatus &= ~AI_STATUS_RUNNING;
PHYS_INPUT_BUTTON_JUMP(this) = false;
}
vector gco = get_closer_dest(this.goalcurrent, this.origin);
- bunnyhopdistance = vlen(this.origin - gco);
// Run only to visible goals
if(IS_ONGROUND(this))
- if(vdist(vec2(this.velocity), >=, autocvar_sv_maxspeed)) // if -really- running
+ if(vdist(vec2(this.velocity), >=, autocvar_sv_maxspeed))
if(checkpvs(this.origin + this.view_ofs, this.goalcurrent))
{
- this.bot_lastseengoal = this.goalcurrent;
+ this.bot_lastseengoal = this.goalcurrent;
- // seen it before
- if(this.bot_timelastseengoal)
+ // seen it before
+ if(this.bot_timelastseengoal)
+ {
+ // for a period of time
+ if(time - this.bot_timelastseengoal > autocvar_bot_ai_bunnyhop_firstjumpdelay)
{
- // for a period of time
- if(time - this.bot_timelastseengoal > autocvar_bot_ai_bunnyhop_firstjumpdelay)
- {
- float checkdistance;
- checkdistance = true;
+ bool checkdistance = true;
- // don't run if it is too close
- if(this.bot_canruntogoal==0)
- {
- if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_startdistance)
- this.bot_canruntogoal = 1;
- else
- this.bot_canruntogoal = -1;
- }
+ // don't run if it is too close
+ if(this.bot_canruntogoal==0)
+ {
+ if(vdist(this.origin - gco, >, autocvar_bot_ai_bunnyhop_startdistance))
+ this.bot_canruntogoal = 1;
+ else
+ this.bot_canruntogoal = -1;
+ }
- if(this.bot_canruntogoal != 1)
- return;
+ if(this.bot_canruntogoal != 1)
+ return;
- if(this.aistatus & AI_STATUS_ROAMING)
- 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 && !wasfreed(this.goalstack01))
+ if(this.aistatus & AI_STATUS_ROAMING)
+ 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 && !wasfreed(this.goalstack01))
+ {
+ vector gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5;
+ vector deviation = vectoangles(gno - this.origin) - vectoangles(gco - this.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(vlen2(this.origin - gco) < vlen2(this.origin - gno))
+ if(fabs(gno.z - gco.z) < this.maxs.z - this.mins.z)
{
- vector gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5;
- deviation = vectoangles(gno - this.origin) - vectoangles(gco - this.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(this.origin - gno))
- if(fabs(gno.z - gco.z) < this.maxs.z - this.mins.z)
+ if(vdist(gco - gno, >, autocvar_bot_ai_bunnyhop_startdistance))
+ if(checkpvs(this.origin + this.view_ofs, this.goalstack01))
{
- if(vdist(gco - gno, >, autocvar_bot_ai_bunnyhop_startdistance))
- if(checkpvs(this.origin + this.view_ofs, this.goalstack01))
- {
- checkdistance = false;
- }
+ checkdistance = false;
}
}
+ }
- if(checkdistance)
- {
- this.aistatus &= ~AI_STATUS_RUNNING;
- // increase stop distance in case the goal is on a slope or a lower platform
- if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance + (this.origin.z - gco.z))
- PHYS_INPUT_BUTTON_JUMP(this) = true;
- }
- else
- {
- this.aistatus |= AI_STATUS_RUNNING;
+ if(checkdistance)
+ {
+ this.aistatus &= ~AI_STATUS_RUNNING;
+ // increase stop distance in case the goal is on a slope or a lower platform
+ if(vdist(this.origin - gco, >, autocvar_bot_ai_bunnyhop_stopdistance + (this.origin.z - gco.z)))
PHYS_INPUT_BUTTON_JUMP(this) = true;
- }
+ }
+ else
+ {
+ this.aistatus |= AI_STATUS_RUNNING;
+ PHYS_INPUT_BUTTON_JUMP(this) = true;
}
}
- else
- {
- this.bot_timelastseengoal = time;
- }
+ }
+ else
+ {
+ this.bot_timelastseengoal = time;
+ }
}
else
{
this.bot_timelastseengoal = 0;
}
-
-#if 0
- // Release jump button
- if(!cvar("sv_pogostick"))
- if((IS_ONGROUND(this)) == 0)
- {
- if(this.velocity.z < 0 || vlen(this.velocity)<maxspeed)
- PHYS_INPUT_BUTTON_JUMP(this) = false;
-
- // Strafe
- if(this.aistatus & AI_STATUS_RUNNING)
- if(vlen(this.velocity)>maxspeed)
- {
- deviation = vectoangles(dir) - vectoangles(this.velocity);
- while (deviation.y < -180) deviation.y = deviation.y + 360;
- while (deviation.y > 180) deviation.y = deviation.y - 360;
-
- if(fabs(deviation.y)>10)
- CS(this).movement_x = 0;
-
- if(deviation.y>10)
- CS(this).movement_y = maxspeed * -1;
- else if(deviation.y<10)
- CS(this).movement_y = maxspeed;
-
- }
- }
-#endif
}
// return true when bot isn't getting closer to the current goal
#include "../bot.qh"
#include "../navigation.qh"
-.float bot_ratingscale;
-.float bot_ratingscale_time;
-.float max_armorvalue;
-.float havocbot_role_timeout;
-
-.void(entity this) havocbot_previous_role;
-.void(entity this) havocbot_role;
-
void havocbot_goalrating_waypoints(entity this, float ratingscale, vector org, float sradius)
{
// rate waypoints only if there's no alternative goal
void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius)
{
- float rating;
- vector o;
ratingscale = ratingscale * 0.0001;
IL_EACH(g_items, it.bot_pickup,
it.bot_pickup_respawning = true;
}
- o = (it.absmin + it.absmax) * 0.5;
+ vector o = (it.absmin + it.absmax) * 0.5;
if(vdist(o - org, >, sradius) || (it == this.ignoregoal && time < this.ignoregoaltime) )
continue;
it.bot_ratingscale_time = time;
it.bot_ratingscale = ratingscale;
- rating = it.bot_pickupevalfunc(this, it);
+ float rating = it.bot_pickupevalfunc(this, it);
if(rating > 0)
navigation_routerating(this, it, rating * ratingscale, 2000);
});
}
-#define BOT_RATING_ENEMY 2500
void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius)
{
if (autocvar_bot_nofire)
return;
// don't chase players if we're under water
- if(this.waterlevel>WATERLEVEL_WETFEET)
+ if(this.waterlevel > WATERLEVEL_WETFEET)
return;
ratingscale = ratingscale * 0.0001;
- float t;
FOREACH_CLIENT(IS_PLAYER(it) && bot_shouldattack(this, it), {
// TODO: Merge this logic with the bot_shouldattack function
if(vdist(it.origin - org, <, 100) || vdist(it.origin - org, >, sradius))
continue;
*/
- t = ((GetResource(this, RES_HEALTH) + GetResource(this, RES_ARMOR)) - (GetResource(it, RES_HEALTH) + GetResource(it, RES_ARMOR))) / 150;
+ float t = ((GetResource(this, RES_HEALTH) + GetResource(this, RES_ARMOR)) - (GetResource(it, RES_HEALTH) + GetResource(it, RES_ARMOR))) / 150;
t = bound(0, 1 + t, 3);
if (skill > 3)
{