Corrected usage of think interval for each bot.

Fixed double-jump task behaviour;.
Lowered CPU usage in player avoidance code.
Removed unused commands from 'yb help'.
Dirty fix for #44.
This commit is contained in:
jeefo 2017-02-06 22:31:54 +03:00
commit 62e9cccf7b
8 changed files with 152 additions and 120 deletions

View file

@ -524,6 +524,8 @@ const float TASKPRI_SHOOTBREAKABLE = 100.0f;
const float TASKPRI_ESCAPEFROMBOMB = 100.0f; const float TASKPRI_ESCAPEFROMBOMB = 100.0f;
const float MAX_GRENADE_TIMER = 2.34f; const float MAX_GRENADE_TIMER = 2.34f;
const float MAX_SPRAY_DISTANCE = 250.0f;
const float MAX_SPRAY_DISTANCE_X2 = MAX_SPRAY_DISTANCE * 2;
const int MAX_HOSTAGES = 8; const int MAX_HOSTAGES = 8;
const int MAX_PATH_INDEX = 8; const int MAX_PATH_INDEX = 8;
@ -745,6 +747,9 @@ private:
int m_radioSelect; // radio entry int m_radioSelect; // radio entry
float m_headedTime; float m_headedTime;
edict_t *m_avoid; // avoid players on our way
float m_avoidTime; // time to avoid players around
float m_blindRecognizeTime; // time to recognize enemy float m_blindRecognizeTime; // time to recognize enemy
float m_itemCheckTime; // time next search for items needs to be done float m_itemCheckTime; // time next search for items needs to be done
PickupType m_pickupType; // type of entity which needs to be used/picked up PickupType m_pickupType; // type of entity which needs to be used/picked up
@ -1177,6 +1182,8 @@ public:
inline Vector Center (void) { return (pev->absmax + pev->absmin) * 0.5; }; inline Vector Center (void) { return (pev->absmax + pev->absmin) * 0.5; };
inline Vector EyePosition (void) { return pev->origin + pev->view_ofs; }; inline Vector EyePosition (void) { return pev->origin + pev->view_ofs; };
float GetThinkInterval (void);
// the main function that decides intervals of running bot ai // the main function that decides intervals of running bot ai
void Think (void); void Think (void);
@ -1197,6 +1204,7 @@ public:
void EnableChatterIcon (bool show); void EnableChatterIcon (bool show);
void DeleteSearchNodes (void); void DeleteSearchNodes (void);
void VerifyBreakable (edict_t *touch); void VerifyBreakable (edict_t *touch);
void AvoidPlayersOnTheWay (edict_t *touch);
void PushTask (TaskID id, float desire, int data, float time, bool canContinue); void PushTask (TaskID id, float desire, int data, float time, bool canContinue);
void RemoveCertainTask (TaskID id); void RemoveCertainTask (TaskID id);
@ -1220,7 +1228,10 @@ public:
void Kill (void); void Kill (void);
void Kick (bool keepQuota = false); void Kick (bool keepQuota = false);
void ResetDoubleJumpState (void); void ResetDoubleJumpState (void);
void StartDoubleJump (edict_t *ent);
int FindPlantedBomb(void); int FindPlantedBomb(void);
bool HasHostage (void); bool HasHostage (void);

View file

@ -97,8 +97,8 @@ private:
int m_drawModels[DRAW_NUM]; int m_drawModels[DRAW_NUM];
// bot client command // bot client command
bool m_isBotCommand;
char m_arguments[256]; char m_arguments[256];
bool m_isBotCommand;
int m_argumentCount; int m_argumentCount;
edict_t *m_startEntity; edict_t *m_startEntity;
@ -218,7 +218,7 @@ public:
// gets custom engine argv for client command // gets custom engine argv for client command
inline const char *GetOverrideArgv (int num) inline const char *GetOverrideArgv (int num)
{ {
return ExtractSingleField (m_arguments, num, false); return ExtractSingleField (m_arguments, num);
} }
// gets custom engine argc for client command // gets custom engine argc for client command
@ -298,8 +298,8 @@ public:
} }
// static utility functions // static utility functions
public: private:
static const char *ExtractSingleField (const char *string, int id, bool terminate); const char *ExtractSingleField (const char *string, int id);
}; };
// simplify access for console variables // simplify access for console variables

View file

@ -330,7 +330,7 @@ void Bot::AvoidGrenades (void)
if ((ent->v.flags & FL_ONGROUND) == 0) if ((ent->v.flags & FL_ONGROUND) == 0)
{ {
float distance = (ent->v.origin - pev->origin).GetLength (); float distance = (ent->v.origin - pev->origin).GetLength ();
float distanceMoved = ((ent->v.origin + ent->v.velocity * m_frameInterval) - pev->origin).GetLength (); float distanceMoved = ((ent->v.origin + ent->v.velocity * GetThinkInterval ()) - pev->origin).GetLength ();
if (distanceMoved < distance && distance < 500.0f) if (distanceMoved < distance && distance < 500.0f)
{ {
@ -480,6 +480,30 @@ void Bot::VerifyBreakable (edict_t *touch)
PushTask (TASK_SHOOTBREAKABLE, TASKPRI_SHOOTBREAKABLE, -1, 0.0f, false); PushTask (TASK_SHOOTBREAKABLE, TASKPRI_SHOOTBREAKABLE, -1, 0.0f, false);
} }
void Bot::AvoidPlayersOnTheWay (edict_t *touch)
{
auto task = GetTaskId ();
if (task == TASK_PLANTBOMB || task == TASK_DEFUSEBOMB)
return;
int ownId = GetIndex ();
int otherId = engine.IndexOfEntity (touch);
if (ownId < otherId)
return;
if (m_avoid != nullptr)
{
int currentId = engine.IndexOfEntity (m_avoid);
if (currentId < otherId)
return;
}
m_avoid = touch;
m_avoidTime = engine.Time () + 0.6f;
}
edict_t *Bot::FindBreakable (void) edict_t *Bot::FindBreakable (void)
{ {
// this function checks if bot is blocked by a shoot able breakable in his moving direction // this function checks if bot is blocked by a shoot able breakable in his moving direction
@ -837,7 +861,7 @@ void Bot::FindItem (void)
m_itemIgnore = ent; m_itemIgnore = ent;
allowPickup = false; allowPickup = false;
if (!m_defendedBomb && m_difficulty >= 2 && Random.Int (0, 100) < 80) if (!m_defendedBomb && m_difficulty >= 2 && Random.Int (0, 100) < 75 && pev->health < 80)
{ {
int index = FindDefendWaypoint (entityOrigin); int index = FindDefendWaypoint (entityOrigin);
@ -2971,7 +2995,7 @@ void Bot::PeriodicThink (void)
m_lastChatTime = engine.Time (); m_lastChatTime = engine.Time ();
g_lastChatTime = engine.Time (); g_lastChatTime = engine.Time ();
char *pickedPhrase = const_cast <char *> (g_chatFactory[CHAT_DEAD].GetRandomElement ().GetBuffer ()); auto pickedPhrase = g_chatFactory[CHAT_DEAD].GetRandomElement ().GetBuffer ();
bool sayBufferExists = false; bool sayBufferExists = false;
// search for last messages, sayed // search for last messages, sayed
@ -2983,7 +3007,7 @@ void Bot::PeriodicThink (void)
if (!sayBufferExists) if (!sayBufferExists)
{ {
PrepareChatMessage (pickedPhrase); PrepareChatMessage (const_cast <char *> (pickedPhrase));
PushMessageQueue (GAME_MSG_SAY_CMD); PushMessageQueue (GAME_MSG_SAY_CMD);
// add to ignore list // add to ignore list
@ -4230,7 +4254,7 @@ void Bot::RunTask_Throw_SG (void)
void Bot::RunTask_DoubleJump (void) void Bot::RunTask_DoubleJump (void)
{ {
if (!IsAlive (m_doubleJumpEntity) || (m_aimFlags & AIM_ENEMY) || (m_travelStartIndex != -1 && GetTask ()->time + (waypoints.GetTravelTime (pev->maxspeed, waypoints.GetPath (m_travelStartIndex)->origin, m_doubleJumpOrigin) + 11.0) < engine.Time ())) if (!IsAlive (m_doubleJumpEntity) || (m_aimFlags & AIM_ENEMY) || (m_travelStartIndex != -1 && GetTask ()->time + (waypoints.GetTravelTime (pev->maxspeed, waypoints.GetPath (m_travelStartIndex)->origin, m_doubleJumpOrigin) + 11.0f) < engine.Time ()))
{ {
ResetDoubleJumpState (); ResetDoubleJumpState ();
return; return;
@ -4246,24 +4270,25 @@ void Bot::RunTask_DoubleJump (void)
m_moveSpeed = 0.0f; m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f; m_strafeSpeed = 0.0f;
bool inJump = (m_doubleJumpEntity->v.button & IN_JUMP) || (m_doubleJumpEntity->v.oldbuttons & IN_JUMP);
if (m_duckForJump < engine.Time ()) if (m_duckForJump < engine.Time ())
pev->button |= IN_DUCK; pev->button |= IN_DUCK;
else if (inJump && !(pev->oldbuttons & IN_JUMP))
pev->button |= IN_JUMP;
MakeVectors (Vector::GetZero ()); MakeVectors (Vector (0.0f, pev->angles.y, 0.0f));
Vector dest = EyePosition () + g_pGlobals->v_forward * 500.0f; Vector src = pev->origin + Vector (0.0f, 0.0f, 45.0f);
dest.z = 180.0f; Vector dest = src + g_pGlobals->v_up * 256.0f;
TraceResult tr; TraceResult tr;
engine.TestLine (EyePosition (), dest, TRACE_IGNORE_GLASS, GetEntity (), &tr); engine.TestLine (src, dest, TRACE_IGNORE_NONE, GetEntity (), &tr);
if (tr.flFraction < 1.0f && tr.pHit == m_doubleJumpEntity) if (tr.flFraction < 1.0f && tr.pHit == m_doubleJumpEntity && inJump)
{ {
if (m_doubleJumpEntity->v.button & IN_JUMP) m_duckForJump = engine.Time () + Random.Float (3.0f, 5.0f);
{ GetTask ()->time = engine.Time ();
m_duckForJump = engine.Time () + Random.Float (3.0f, 5.0f);
GetTask ()->time = engine.Time ();
}
} }
return; return;
} }
@ -4289,7 +4314,7 @@ void Bot::RunTask_DoubleJump (void)
GetTask ()->data = destIndex; GetTask ()->data = destIndex;
m_travelStartIndex = m_currentWaypointIndex; m_travelStartIndex = m_currentWaypointIndex;
// Always take the shortest path // always take the shortest path
FindShortestPath (m_currentWaypointIndex, destIndex); FindShortestPath (m_currentWaypointIndex, destIndex);
if (m_currentWaypointIndex == destIndex) if (m_currentWaypointIndex == destIndex)
@ -4859,7 +4884,7 @@ void Bot::BotAI (void)
SetIdealReactionTimes (); SetIdealReactionTimes ();
// calculate 2 direction vectors, 1 without the up/down component // calculate 2 direction vectors, 1 without the up/down component
const Vector &dirOld = m_destOrigin - (pev->origin + pev->velocity * m_frameInterval); const Vector &dirOld = m_destOrigin - (pev->origin + pev->velocity * GetThinkInterval ());
const Vector &dirNormal = dirOld.Normalize2D (); const Vector &dirNormal = dirOld.Normalize2D ();
m_moveAngles = dirOld.ToAngles (); m_moveAngles = dirOld.ToAngles ();
@ -5499,7 +5524,7 @@ void Bot::DiscardWeaponForUser (edict_t *user, bool discardC4)
// this function, asks bot to discard his current primary weapon (or c4) to the user that requsted it with /drop* // this function, asks bot to discard his current primary weapon (or c4) to the user that requsted it with /drop*
// command, very useful, when i'm don't have money to buy anything... ) // command, very useful, when i'm don't have money to buy anything... )
if (IsAlive (user) && m_moneyAmount >= 2000 && HasPrimaryWeapon () && (user->v.origin - pev->origin).GetLength () <= 240.0f) if (IsAlive (user) && m_moneyAmount >= 2000 && HasPrimaryWeapon () && (user->v.origin - pev->origin).GetLength () <= 450.0f)
{ {
m_aimFlags |= AIM_ENTITY; m_aimFlags |= AIM_ENTITY;
m_lookAt = user->v.origin; m_lookAt = user->v.origin;
@ -5530,6 +5555,17 @@ void Bot::DiscardWeaponForUser (edict_t *user, bool discardC4)
} }
} }
void Bot::StartDoubleJump (edict_t *ent)
{
ResetDoubleJumpState ();
m_doubleJumpOrigin = ent->v.origin;
m_doubleJumpEntity = ent;
PushTask (TASK_DOUBLEJUMP, TASKPRI_DOUBLEJUMP, -1, engine.Time (), true);
TeamSayText (FormatBuffer ("Ok %s, i will help you!", STRING (ent->v.netname)));
}
void Bot::ResetDoubleJumpState (void) void Bot::ResetDoubleJumpState (void)
{ {
TaskComplete (); TaskComplete ();

View file

@ -466,7 +466,7 @@ const Vector &Bot::GetAimPosition (void)
} }
m_lastEnemyOrigin = targetOrigin; m_lastEnemyOrigin = targetOrigin;
} }
const Vector &velocity = UsesSniper () ? Vector::GetZero () : 1.0f * m_frameInterval * (m_enemy->v.velocity - pev->velocity); const Vector &velocity = UsesSniper () ? Vector::GetZero () : 1.0f * GetThinkInterval () * (m_enemy->v.velocity - pev->velocity);
if (m_difficulty < 3 && !randomize.IsZero ()) if (m_difficulty < 3 && !randomize.IsZero ())
{ {
@ -505,12 +505,9 @@ float Bot::GetZOffset (float distance)
bool shotgun = (m_currentWeapon == WEAPON_XM1014 || m_currentWeapon == WEAPON_M3); bool shotgun = (m_currentWeapon == WEAPON_XM1014 || m_currentWeapon == WEAPON_M3);
bool m249 = m_currentWeapon == WEAPON_M249; bool m249 = m_currentWeapon == WEAPON_M249;
const float BurstDistance = 300.0f;
const float DoubleBurstDistance = BurstDistance * 2;
float result = 3.5f; float result = 3.5f;
if (distance < 2800.0f && distance > DoubleBurstDistance) if (distance < 2800.0f && distance > MAX_SPRAY_DISTANCE_X2)
{ {
if (sniper) result = 1.5f; if (sniper) result = 1.5f;
else if (zoomableRifle) result = 4.5f; else if (zoomableRifle) result = 4.5f;
@ -520,7 +517,7 @@ float Bot::GetZOffset (float distance)
else if (m249) result = 2.5f; else if (m249) result = 2.5f;
else if (shotgun) result = 10.5f; else if (shotgun) result = 10.5f;
} }
else if (distance > BurstDistance && distance <= DoubleBurstDistance) else if (distance > MAX_SPRAY_DISTANCE && distance <= MAX_SPRAY_DISTANCE_X2)
{ {
if (sniper) result = 2.5f; if (sniper) result = 2.5f;
else if (zoomableRifle) result = 3.5f; else if (zoomableRifle) result = 3.5f;
@ -530,7 +527,7 @@ float Bot::GetZOffset (float distance)
else if (m249) result = -1.0f; else if (m249) result = -1.0f;
else if (shotgun) result = 10.0f; else if (shotgun) result = 10.0f;
} }
else if (distance < BurstDistance) else if (distance < MAX_SPRAY_DISTANCE)
{ {
if (sniper) result = 4.5f; if (sniper) result = 4.5f;
else if (zoomableRifle) result = -5.0f; else if (zoomableRifle) result = -5.0f;
@ -690,13 +687,11 @@ bool Bot::DoFirePause (float distance)
if (GetShootingConeDeviation (GetEntity (), &m_enemyOrigin) > 0.92f && IsEnemyProtectedByShield (m_enemy)) if (GetShootingConeDeviation (GetEntity (), &m_enemyOrigin) > 0.92f && IsEnemyProtectedByShield (m_enemy))
return true; return true;
} }
float offset = 0.0f; float offset = 0.0f;
const float SprayDistance = 250.0f;
if (distance < SprayDistance) if (distance < MAX_SPRAY_DISTANCE)
return false; return false;
else if (distance < 2 * SprayDistance) else if (distance < MAX_SPRAY_DISTANCE_X2)
offset = 10.0f; offset = 10.0f;
else else
offset = 5.0f; offset = 5.0f;
@ -704,10 +699,7 @@ bool Bot::DoFirePause (float distance)
const float xPunch = DegreeToRadian (pev->punchangle.x); const float xPunch = DegreeToRadian (pev->punchangle.x);
const float yPunch = DegreeToRadian (pev->punchangle.y); const float yPunch = DegreeToRadian (pev->punchangle.y);
float interval = m_thinkInterval; float interval = GetThinkInterval ();
if ((g_gameFlags & GAME_LEGACY) && Math::FltZero (interval))
interval = (1.0f / 30.0f) * Random.Float (0.95f, 1.05f);
// check if we need to compensate recoil // check if we need to compensate recoil
if (tanf (A_sqrtf (fabsf (xPunch * xPunch) + fabsf (yPunch * yPunch))) * distance > offset + 30.0f + ((100 - (m_difficulty * 25)) / 100.f)) if (tanf (A_sqrtf (fabsf (xPunch * xPunch) + fabsf (yPunch * yPunch))) * distance > offset + 30.0f + ((100 - (m_difficulty * 25)) / 100.f))
@ -805,7 +797,7 @@ void Bot::FinishWeaponSelection (float distance, int index, int id, int choosen)
} }
// need to care for burst fire? // need to care for burst fire?
if (distance < 256.0f || m_blindTime > engine.Time ()) if (distance < MAX_SPRAY_DISTANCE || m_blindTime > engine.Time ())
{ {
if (id == WEAPON_KNIFE) if (id == WEAPON_KNIFE)
{ {
@ -1192,7 +1184,7 @@ void Bot::CombatFight (void)
{ {
MakeVectors (pev->v_angle); MakeVectors (pev->v_angle);
if (IsDeadlyDrop (pev->origin + (g_pGlobals->v_forward * m_moveSpeed * 0.2f) + (g_pGlobals->v_right * m_strafeSpeed * 0.2f) + (pev->velocity * m_frameInterval))) if (IsDeadlyDrop (pev->origin + (g_pGlobals->v_forward * m_moveSpeed * 0.2f) + (g_pGlobals->v_right * m_strafeSpeed * 0.2f) + (pev->velocity * GetThinkInterval ())))
{ {
m_strafeSpeed = -m_strafeSpeed; m_strafeSpeed = -m_strafeSpeed;
m_moveSpeed = -m_moveSpeed; m_moveSpeed = -m_moveSpeed;

View file

@ -345,7 +345,7 @@ void Engine::IssueBotCommand (edict_t *ent, const char *fmt, ...)
return; return;
va_list ap; va_list ap;
static char string[256]; char string[256];
va_start (ap, fmt); va_start (ap, fmt);
vsnprintf (string, SIZEOF_CHAR (string), fmt, ap); vsnprintf (string, SIZEOF_CHAR (string), fmt, ap);
@ -354,7 +354,7 @@ void Engine::IssueBotCommand (edict_t *ent, const char *fmt, ...)
if (IsNullString (string)) if (IsNullString (string))
return; return;
m_arguments[0] = 0x0; m_arguments[0] = '\0';
m_argumentCount = 0; m_argumentCount = 0;
m_isBotCommand = true; m_isBotCommand = true;
@ -407,16 +407,24 @@ void Engine::IssueBotCommand (edict_t *ent, const char *fmt, ...)
} }
m_isBotCommand = false; m_isBotCommand = false;
m_arguments[0] = 0x0; m_arguments[0] = '\0';
m_argumentCount = 0; m_argumentCount = 0;
} }
const char *Engine::ExtractSingleField (const char *string, int id, bool terminate) const char *Engine::ExtractSingleField (const char *string, int id)
{ {
// this function gets and returns a particular field in a string where several strings are concatenated // this function gets and returns a particular field in a string where several strings are concatenated
static char field[256]; const int IterBufMax = 4;
field[0] = 0x0;
static char arg[IterBufMax][256];
static int iter = -1;
if (iter > IterBufMax - 1)
iter = 0;
char *ptr = arg[++iter];
ptr[0] = 0;
int pos = 0, count = 0, start = 0, stop = 0; int pos = 0, count = 0, start = 0, stop = 0;
int length = strlen (string); int length = strlen (string);
@ -452,19 +460,16 @@ const char *Engine::ExtractSingleField (const char *string, int id, bool termina
int i = start; int i = start;
for (; i <= stop; i++) for (; i <= stop; i++)
field[i - start] = string[i]; ptr[i - start] = string[i];
field[i - start] = 0; ptr[i - start] = 0;
break; break;
} }
count++; // we have parsed one field more count++; // we have parsed one field more
} }
String::TrimExternalBuffer (ptr);
if (terminate) return ptr;
field[strlen (field) - 1] = 0;
String::TrimExternalBuffer (field);
return field;
} }
void Engine::IssueCmd (const char *fmt, ...) void Engine::IssueCmd (const char *fmt, ...)

View file

@ -118,7 +118,7 @@ int BotCommandHandler (edict_t *ent, const char *arg0, const char *arg1, const c
} }
// display some sort of help information // display some sort of help information
else if (A_stricmp (arg0, "?") == 0 || A_stricmp (arg0, "help") == 0) else if (strcmp (arg0, "?") == 0 || strcmp (arg0, "help") == 0)
{ {
engine.ClientPrintf (ent, "Bot Commands:"); engine.ClientPrintf (ent, "Bot Commands:");
engine.ClientPrintf (ent, "%s version\t - display version information.", self); engine.ClientPrintf (ent, "%s version\t - display version information.", self);
@ -131,7 +131,7 @@ int BotCommandHandler (edict_t *ent, const char *arg0, const char *arg1, const c
engine.ClientPrintf (ent, "%s votemap\t - allows dead bots to vote for specific map.", self); engine.ClientPrintf (ent, "%s votemap\t - allows dead bots to vote for specific map.", self);
engine.ClientPrintf (ent, "%s cmenu\t - displaying bots command menu.", self); engine.ClientPrintf (ent, "%s cmenu\t - displaying bots command menu.", self);
if (A_stricmp (arg1, "full") == 0 || A_stricmp (arg1, "f") == 0 || A_stricmp (arg1, "?") == 0) if (strcmp (arg1, "full") == 0 || strcmp (arg1, "?") == 0)
{ {
engine.ClientPrintf (ent, "%s add_t\t - creates one random bot to terrorist team.", self); engine.ClientPrintf (ent, "%s add_t\t - creates one random bot to terrorist team.", self);
engine.ClientPrintf (ent, "%s add_ct\t - creates one random bot to ct team.", self); engine.ClientPrintf (ent, "%s add_ct\t - creates one random bot to ct team.", self);
@ -140,8 +140,6 @@ int BotCommandHandler (edict_t *ent, const char *arg0, const char *arg1, const c
engine.ClientPrintf (ent, "%s kill_t\t - kills all bots on terrorist team.", self); engine.ClientPrintf (ent, "%s kill_t\t - kills all bots on terrorist team.", self);
engine.ClientPrintf (ent, "%s kill_ct\t - kills all bots on ct team.", self); engine.ClientPrintf (ent, "%s kill_ct\t - kills all bots on ct team.", self);
engine.ClientPrintf (ent, "%s list\t - display list of bots currently playing.", self); engine.ClientPrintf (ent, "%s list\t - display list of bots currently playing.", self);
engine.ClientPrintf (ent, "%s order\t - execute specific command on specified bot.", self);
engine.ClientPrintf (ent, "%s time\t - displays current time on server.", self);
engine.ClientPrintf (ent, "%s deletewp\t - erase waypoint file from hard disk (permanently).", self); engine.ClientPrintf (ent, "%s deletewp\t - erase waypoint file from hard disk (permanently).", self);
if (!engine.IsDedicatedServer ()) if (!engine.IsDedicatedServer ())
@ -151,6 +149,7 @@ int BotCommandHandler (edict_t *ent, const char *arg0, const char *arg1, const c
engine.Printf ("%s wp on noclip\t - enable noclip cheat", self); engine.Printf ("%s wp on noclip\t - enable noclip cheat", self);
engine.Printf ("%s wp save nocheck\t - save waypoints without checking.", self); engine.Printf ("%s wp save nocheck\t - save waypoints without checking.", self);
engine.Printf ("%s wp add\t - open menu for waypoint creation.", self); engine.Printf ("%s wp add\t - open menu for waypoint creation.", self);
engine.Printf ("%s wp delete\t - delete waypoint nearest to host edict.", self);
engine.Printf ("%s wp menu\t - open main waypoint menu.", self); engine.Printf ("%s wp menu\t - open main waypoint menu.", self);
engine.Printf ("%s wp addbasic\t - creates basic waypoints on map.", self); engine.Printf ("%s wp addbasic\t - creates basic waypoints on map.", self);
engine.Printf ("%s wp find\t - show direction to specified waypoint.", self); engine.Printf ("%s wp find\t - show direction to specified waypoint.", self);
@ -497,6 +496,8 @@ void InitConfig (void)
fp.Close (); fp.Close ();
} }
engine.Printf ("INITING CHAT.CfG");
// CHAT SYSTEM CONFIG INITIALIZATION // CHAT SYSTEM CONFIG INITIALIZATION
if (OpenConfig ("chat.cfg", "Chat file not found.", &fp, true)) if (OpenConfig ("chat.cfg", "Chat file not found.", &fp, true))
{ {
@ -506,7 +507,9 @@ void InitConfig (void)
while (fp.GetBuffer (line, 255)) while (fp.GetBuffer (line, 255))
{ {
SKIP_COMMENTS (); SKIP_COMMENTS ();
strncpy (section, Engine::ExtractSingleField (line, 0, 1), SIZEOF_CHAR (section));
String::TrimExternalBuffer (line);
strncpy (section, line, SIZEOF_CHAR (section));
if (strcmp (section, "[KILLED]") == 0) if (strcmp (section, "[KILLED]") == 0)
{ {
@ -552,8 +555,6 @@ void InitConfig (void)
if (chatType != 3) if (chatType != 3)
line[79] = 0; line[79] = 0;
String::TrimExternalBuffer (line);
switch (chatType) switch (chatType)
{ {
case 0: case 0:
@ -957,7 +958,12 @@ void Touch (edict_t *pentTouched, edict_t *pentOther)
Bot *bot = bots.GetBot (pentOther); Bot *bot = bots.GetBot (pentOther);
if (bot != nullptr) if (bot != nullptr)
bot->VerifyBreakable (pentTouched); {
if (IsValidPlayer (pentTouched))
bot->AvoidPlayersOnTheWay (pentTouched);
else
bot->VerifyBreakable (pentTouched);
}
} }
if (g_gameFlags & GAME_METAMOD) if (g_gameFlags & GAME_METAMOD)
RETURN_META (MRES_IGNORED); RETURN_META (MRES_IGNORED);
@ -1614,20 +1620,12 @@ void ClientCommand (edict_t *ent)
{ {
case 1: case 1:
case 2: case 2:
if (FindNearestPlayer (reinterpret_cast <void **> (&bot), client->ent, 300.0f, true, true, true)) if (FindNearestPlayer (reinterpret_cast <void **> (&bot), client->ent, 450.0f, true, true, true))
{ {
if (!bot->m_hasC4 && !bot->HasHostage () ) if (!bot->m_hasC4 && !bot->HasHostage ())
{ {
if (selection == 1) if (selection == 1)
{ bot->StartDoubleJump (client->ent);
bot->ResetDoubleJumpState ();
bot->m_doubleJumpOrigin = client->ent->v.origin;
bot->m_doubleJumpEntity = client->ent;
bot->PushTask (TASK_DOUBLEJUMP, TASKPRI_DOUBLEJUMP, -1, engine.Time (), true);
bot->TeamSayText (FormatBuffer ("Ok %s, i will help you!", STRING (ent->v.netname)));
}
else if (selection == 2) else if (selection == 2)
bot->ResetDoubleJumpState (); bot->ResetDoubleJumpState ();
} }
@ -1637,7 +1635,7 @@ void ClientCommand (edict_t *ent)
case 3: case 3:
case 4: case 4:
if (FindNearestPlayer (reinterpret_cast <void **> (&bot), ent, 300.0f, true, true, true)) if (FindNearestPlayer (reinterpret_cast <void **> (&bot), ent, 450.0f, true, true, true))
bot->DiscardWeaponForUser (ent, selection == 4 ? false : true); bot->DiscardWeaponForUser (ent, selection == 4 ? false : true);
DisplayMenuToClient (ent, BOT_MENU_COMMANDS); DisplayMenuToClient (ent, BOT_MENU_COMMANDS);

View file

@ -1036,6 +1036,14 @@ void Bot::ReleaseUsedName (void)
} }
} }
float Bot::GetThinkInterval (void)
{
if (Math::FltZero (m_thinkInterval))
return m_frameInterval;
return m_thinkInterval;
}
Bot::~Bot (void) Bot::~Bot (void)
{ {
// this is bot destructor // this is bot destructor
@ -1139,6 +1147,9 @@ void Bot::NewRound (void)
m_numFriendsLeft = 0; m_numFriendsLeft = 0;
m_numEnemiesLeft = 0; m_numEnemiesLeft = 0;
m_avoid = nullptr;
m_avoidTime = 0.0f;
for (i = 0; i < 5; i++) for (i = 0; i < 5; i++)
m_prevWptIndex[i] = -1; m_prevWptIndex[i] = -1;

View file

@ -320,54 +320,34 @@ void Bot::CheckCloseAvoidance (const Vector &dirNormal)
if (m_seeEnemyTime + 1.5f < engine.Time ()) if (m_seeEnemyTime + 1.5f < engine.Time ())
return; return;
edict_t *nearest = nullptr; if (m_avoidTime < engine.Time () || m_avoid == nullptr)
float nearestDist = 99999.0f; return;
int playerCount = 0;
for (int i = 0; i < engine.MaxClients (); i++) MakeVectors (m_moveAngles); // use our movement angles
float interval = GetThinkInterval ();
// try to predict where we should be next frame
Vector moved = pev->origin + g_pGlobals->v_forward * m_moveSpeed * interval;
moved += g_pGlobals->v_right * m_strafeSpeed * interval;
moved += pev->velocity * interval;
float nearestDistance = (m_avoid->v.origin - pev->origin).GetLength2D ();
float nextFrameDistance = ((m_avoid->v.origin + m_avoid->v.velocity * interval) - pev->origin).GetLength2D ();
// is player that near now or in future that we need to steer away?
if ((m_avoid->v.origin - moved).GetLength2D () <= 48.0f || (nearestDistance <= 56.0f && nextFrameDistance < nearestDistance))
{ {
const Client &client = g_clients[i]; // to start strafing, we have to first figure out if the target is on the left side or right side
const Vector &dirToPoint = (pev->origin - m_avoid->v.origin).Get2D ();
if (!(client.flags & (CF_USED | CF_ALIVE)) || client.ent == GetEntity () || client.team != m_team) if ((dirToPoint | g_pGlobals->v_right.Get2D ()) > 0.0f)
continue; SetStrafeSpeed (dirNormal, pev->maxspeed);
else
SetStrafeSpeed (dirNormal, -pev->maxspeed);
float distance = (client.ent->v.origin - pev->origin).GetLength (); if (nearestDistance < 56.0f && (dirToPoint | g_pGlobals->v_forward.Get2D ()) < 0.0f)
m_moveSpeed = -pev->maxspeed;
if (distance < nearestDist && distance < pev->maxspeed)
{
nearestDist = distance;
nearest = client.ent;
playerCount++;
}
}
if (playerCount < 4 && IsValidPlayer (nearest))
{
MakeVectors (m_moveAngles); // use our movement angles
// try to predict where we should be next frame
Vector moved = pev->origin + g_pGlobals->v_forward * m_moveSpeed * m_frameInterval;
moved += g_pGlobals->v_right * m_strafeSpeed * m_frameInterval;
moved += pev->velocity * m_frameInterval;
float nearestDistance = (nearest->v.origin - pev->origin).GetLength2D ();
float nextFrameDistance = ((nearest->v.origin + nearest->v.velocity * m_frameInterval) - pev->origin).GetLength2D ();
// is player that near now or in future that we need to steer away?
if ((nearest->v.origin - moved).GetLength2D () <= 48.0f || (nearestDistance <= 56.0f && nextFrameDistance < nearestDistance))
{
// to start strafing, we have to first figure out if the target is on the left side or right side
const Vector &dirToPoint = (pev->origin - nearest->v.origin).Get2D ();
if ((dirToPoint | g_pGlobals->v_right.Get2D ()) > 0.0f)
SetStrafeSpeed (dirNormal, pev->maxspeed);
else
SetStrafeSpeed (dirNormal, -pev->maxspeed);
if (nearestDistance < 56.0f && (dirToPoint | g_pGlobals->v_forward.Get2D ()) < 0.0f)
m_moveSpeed = -pev->maxspeed;
}
} }
} }
@ -376,8 +356,6 @@ void Bot::CheckTerrain (float movedDistance, const Vector &dirNormal)
m_isStuck = false; m_isStuck = false;
TraceResult tr; TraceResult tr;
CheckCloseAvoidance (dirNormal);
// Standing still, no need to check? // Standing still, no need to check?
// FIXME: doesn't care for ladder movement (handled separately) should be included in some way // FIXME: doesn't care for ladder movement (handled separately) should be included in some way
if ((m_moveSpeed >= 10.0f || m_strafeSpeed >= 10.0f) && m_lastCollTime < engine.Time () && m_seeEnemyTime + 0.8f < engine.Time () && GetTaskId () != TASK_ATTACK) if ((m_moveSpeed >= 10.0f || m_strafeSpeed >= 10.0f) && m_lastCollTime < engine.Time () && m_seeEnemyTime + 0.8f < engine.Time () && GetTaskId () != TASK_ATTACK)
@ -626,7 +604,7 @@ void Bot::CheckTerrain (float movedDistance, const Vector &dirNormal)
if (IsOnFloor () || IsInWater ()) if (IsOnFloor () || IsInWater ())
{ {
pev->button |= IN_JUMP; pev->button |= IN_JUMP;
m_jumpStateTimer = Random.Float (2.0f, 3.0f); m_jumpStateTimer = engine.Time () + Random.Float (0.7f, 1.5f);
} }
break; break;
@ -648,6 +626,7 @@ void Bot::CheckTerrain (float movedDistance, const Vector &dirNormal)
} }
} }
} }
CheckCloseAvoidance (dirNormal);
} }
bool Bot::DoWaypointNav (void) bool Bot::DoWaypointNav (void)
@ -1187,7 +1166,7 @@ bool Bot::DoWaypointNav (void)
} }
// needs precise placement - check if we get past the point // needs precise placement - check if we get past the point
if (desiredDistance < 16.0f && waypointDistance < 30.0f && (pev->origin + (pev->velocity * m_frameInterval) - m_waypointOrigin).GetLength () > waypointDistance) if (desiredDistance < 16.0f && waypointDistance < 30.0f && (pev->origin + (pev->velocity * GetThinkInterval ()) - m_waypointOrigin).GetLength () > waypointDistance)
desiredDistance = waypointDistance + 1.0f; desiredDistance = waypointDistance + 1.0f;
if (waypointDistance < desiredDistance) if (waypointDistance < desiredDistance)