diff --git a/include/core.h b/include/core.h index d79b71a..2c3c5e7 100644 --- a/include/core.h +++ b/include/core.h @@ -524,6 +524,8 @@ const float TASKPRI_SHOOTBREAKABLE = 100.0f; const float TASKPRI_ESCAPEFROMBOMB = 100.0f; 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_PATH_INDEX = 8; @@ -745,6 +747,9 @@ private: int m_radioSelect; // radio entry 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_itemCheckTime; // time next search for items needs to be done 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 EyePosition (void) { return pev->origin + pev->view_ofs; }; + float GetThinkInterval (void); + // the main function that decides intervals of running bot ai void Think (void); @@ -1197,6 +1204,7 @@ public: void EnableChatterIcon (bool show); void DeleteSearchNodes (void); void VerifyBreakable (edict_t *touch); + void AvoidPlayersOnTheWay (edict_t *touch); void PushTask (TaskID id, float desire, int data, float time, bool canContinue); void RemoveCertainTask (TaskID id); @@ -1220,7 +1228,10 @@ public: void Kill (void); void Kick (bool keepQuota = false); + void ResetDoubleJumpState (void); + void StartDoubleJump (edict_t *ent); + int FindPlantedBomb(void); bool HasHostage (void); diff --git a/include/engine.h b/include/engine.h index 2f17ffa..3a6aaaf 100644 --- a/include/engine.h +++ b/include/engine.h @@ -97,8 +97,8 @@ private: int m_drawModels[DRAW_NUM]; // bot client command - bool m_isBotCommand; char m_arguments[256]; + bool m_isBotCommand; int m_argumentCount; edict_t *m_startEntity; @@ -218,7 +218,7 @@ public: // gets custom engine argv for client command inline const char *GetOverrideArgv (int num) { - return ExtractSingleField (m_arguments, num, false); + return ExtractSingleField (m_arguments, num); } // gets custom engine argc for client command @@ -298,8 +298,8 @@ public: } // static utility functions -public: - static const char *ExtractSingleField (const char *string, int id, bool terminate); +private: + const char *ExtractSingleField (const char *string, int id); }; // simplify access for console variables diff --git a/include/engine/util.h b/include/engine/util.h index 6618254..e0a387d 100644 --- a/include/engine/util.h +++ b/include/engine/util.h @@ -173,6 +173,7 @@ typedef struct hudtextparms_s #define VEC_DUCK_VIEW Vector( 0, 0, 12 ) #define SVC_TEMPENTITY 23 +#define SVC_CENTERPRINT 26 #define SVC_INTERMISSION 30 #define SVC_CDTRACK 32 #define SVC_WEAPONANIM 35 diff --git a/source/basecode.cpp b/source/basecode.cpp index 3ddb081..6f5ad8a 100644 --- a/source/basecode.cpp +++ b/source/basecode.cpp @@ -330,7 +330,7 @@ void Bot::AvoidGrenades (void) if ((ent->v.flags & FL_ONGROUND) == 0) { 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) { @@ -480,6 +480,30 @@ void Bot::VerifyBreakable (edict_t *touch) 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) { // 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; 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); @@ -2971,7 +2995,7 @@ void Bot::PeriodicThink (void) m_lastChatTime = engine.Time (); g_lastChatTime = engine.Time (); - char *pickedPhrase = const_cast (g_chatFactory[CHAT_DEAD].GetRandomElement ().GetBuffer ()); + auto pickedPhrase = g_chatFactory[CHAT_DEAD].GetRandomElement ().GetBuffer (); bool sayBufferExists = false; // search for last messages, sayed @@ -2983,7 +3007,7 @@ void Bot::PeriodicThink (void) if (!sayBufferExists) { - PrepareChatMessage (pickedPhrase); + PrepareChatMessage (const_cast (pickedPhrase)); PushMessageQueue (GAME_MSG_SAY_CMD); // add to ignore list @@ -4230,7 +4254,7 @@ void Bot::RunTask_Throw_SG (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 (); return; @@ -4246,24 +4270,25 @@ void Bot::RunTask_DoubleJump (void) m_moveSpeed = 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 ()) 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; - dest.z = 180.0f; + Vector src = pev->origin + Vector (0.0f, 0.0f, 45.0f); + Vector dest = src + g_pGlobals->v_up * 256.0f; 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; } @@ -4289,7 +4314,7 @@ void Bot::RunTask_DoubleJump (void) GetTask ()->data = destIndex; m_travelStartIndex = m_currentWaypointIndex; - // Always take the shortest path + // always take the shortest path FindShortestPath (m_currentWaypointIndex, destIndex); if (m_currentWaypointIndex == destIndex) @@ -4859,7 +4884,7 @@ void Bot::BotAI (void) SetIdealReactionTimes (); // 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 (); 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* // 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_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) { TaskComplete (); diff --git a/source/combat.cpp b/source/combat.cpp index 44cd54f..8fca03e 100644 --- a/source/combat.cpp +++ b/source/combat.cpp @@ -466,7 +466,7 @@ const Vector &Bot::GetAimPosition (void) } 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 ()) { @@ -505,12 +505,9 @@ float Bot::GetZOffset (float distance) bool shotgun = (m_currentWeapon == WEAPON_XM1014 || m_currentWeapon == WEAPON_M3); bool m249 = m_currentWeapon == WEAPON_M249; - const float BurstDistance = 300.0f; - const float DoubleBurstDistance = BurstDistance * 2; - float result = 3.5f; - if (distance < 2800.0f && distance > DoubleBurstDistance) + if (distance < 2800.0f && distance > MAX_SPRAY_DISTANCE_X2) { if (sniper) result = 1.5f; else if (zoomableRifle) result = 4.5f; @@ -520,7 +517,7 @@ float Bot::GetZOffset (float distance) else if (m249) result = 2.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; else if (zoomableRifle) result = 3.5f; @@ -530,7 +527,7 @@ float Bot::GetZOffset (float distance) else if (m249) result = -1.0f; else if (shotgun) result = 10.0f; } - else if (distance < BurstDistance) + else if (distance < MAX_SPRAY_DISTANCE) { if (sniper) result = 4.5f; 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)) return true; } - float offset = 0.0f; - const float SprayDistance = 250.0f; - if (distance < SprayDistance) + if (distance < MAX_SPRAY_DISTANCE) return false; - else if (distance < 2 * SprayDistance) + else if (distance < MAX_SPRAY_DISTANCE_X2) offset = 10.0f; else offset = 5.0f; @@ -704,10 +699,7 @@ bool Bot::DoFirePause (float distance) const float xPunch = DegreeToRadian (pev->punchangle.x); const float yPunch = DegreeToRadian (pev->punchangle.y); - float interval = m_thinkInterval; - - if ((g_gameFlags & GAME_LEGACY) && Math::FltZero (interval)) - interval = (1.0f / 30.0f) * Random.Float (0.95f, 1.05f); + float interval = GetThinkInterval (); // 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)) @@ -805,7 +797,7 @@ void Bot::FinishWeaponSelection (float distance, int index, int id, int choosen) } // 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) { @@ -1192,7 +1184,7 @@ void Bot::CombatFight (void) { 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_moveSpeed = -m_moveSpeed; diff --git a/source/engine.cpp b/source/engine.cpp index c6bb169..485250e 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -81,6 +81,7 @@ void Engine::ChatPrintf (const char *fmt, ...) MESSAGE_BEGIN (MSG_BROADCAST, FindMessageId (NETMSG_TEXTMSG)); WRITE_BYTE (HUD_PRINTTALK); + WRITE_STRING ("%s"); WRITE_STRING (string); MESSAGE_END (); } @@ -101,8 +102,7 @@ void Engine::CenterPrintf (const char *fmt, ...) } strcat (string, "\n"); - MESSAGE_BEGIN (MSG_BROADCAST, FindMessageId (NETMSG_TEXTMSG)); - WRITE_BYTE (HUD_PRINTCENTER); + MESSAGE_BEGIN (MSG_BROADCAST, SVC_CENTERPRINT); WRITE_STRING (string); MESSAGE_END (); } @@ -345,7 +345,7 @@ void Engine::IssueBotCommand (edict_t *ent, const char *fmt, ...) return; va_list ap; - static char string[256]; + char string[256]; va_start (ap, fmt); vsnprintf (string, SIZEOF_CHAR (string), fmt, ap); @@ -354,7 +354,7 @@ void Engine::IssueBotCommand (edict_t *ent, const char *fmt, ...) if (IsNullString (string)) return; - m_arguments[0] = 0x0; + m_arguments[0] = '\0'; m_argumentCount = 0; m_isBotCommand = true; @@ -407,16 +407,24 @@ void Engine::IssueBotCommand (edict_t *ent, const char *fmt, ...) } m_isBotCommand = false; - m_arguments[0] = 0x0; + m_arguments[0] = '\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 - static char field[256]; - field[0] = 0x0; + const int IterBufMax = 4; + + 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 length = strlen (string); @@ -452,19 +460,16 @@ const char *Engine::ExtractSingleField (const char *string, int id, bool termina int i = start; for (; i <= stop; i++) - field[i - start] = string[i]; + ptr[i - start] = string[i]; - field[i - start] = 0; + ptr[i - start] = 0; break; } count++; // we have parsed one field more } + String::TrimExternalBuffer (ptr); - if (terminate) - field[strlen (field) - 1] = 0; - - String::TrimExternalBuffer (field); - return field; + return ptr; } void Engine::IssueCmd (const char *fmt, ...) diff --git a/source/interface.cpp b/source/interface.cpp index efa818b..35888cf 100644 --- a/source/interface.cpp +++ b/source/interface.cpp @@ -118,7 +118,7 @@ int BotCommandHandler (edict_t *ent, const char *arg0, const char *arg1, const c } // 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, "%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 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_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_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 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); 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 save nocheck\t - save waypoints without checking.", 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 addbasic\t - creates basic waypoints on map.", self); engine.Printf ("%s wp find\t - show direction to specified waypoint.", self); @@ -497,6 +496,8 @@ void InitConfig (void) fp.Close (); } + engine.Printf ("INITING CHAT.CfG"); + // CHAT SYSTEM CONFIG INITIALIZATION if (OpenConfig ("chat.cfg", "Chat file not found.", &fp, true)) { @@ -506,7 +507,9 @@ void InitConfig (void) while (fp.GetBuffer (line, 255)) { 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) { @@ -552,8 +555,6 @@ void InitConfig (void) if (chatType != 3) line[79] = 0; - String::TrimExternalBuffer (line); - switch (chatType) { case 0: @@ -957,7 +958,12 @@ void Touch (edict_t *pentTouched, edict_t *pentOther) Bot *bot = bots.GetBot (pentOther); if (bot != nullptr) - bot->VerifyBreakable (pentTouched); + { + if (IsValidPlayer (pentTouched)) + bot->AvoidPlayersOnTheWay (pentTouched); + else + bot->VerifyBreakable (pentTouched); + } } if (g_gameFlags & GAME_METAMOD) RETURN_META (MRES_IGNORED); @@ -1614,20 +1620,12 @@ void ClientCommand (edict_t *ent) { case 1: case 2: - if (FindNearestPlayer (reinterpret_cast (&bot), client->ent, 300.0f, true, true, true)) + if (FindNearestPlayer (reinterpret_cast (&bot), client->ent, 450.0f, true, true, true)) { - if (!bot->m_hasC4 && !bot->HasHostage () ) + if (!bot->m_hasC4 && !bot->HasHostage ()) { if (selection == 1) - { - 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))); - } + bot->StartDoubleJump (client->ent); else if (selection == 2) bot->ResetDoubleJumpState (); } @@ -1637,7 +1635,7 @@ void ClientCommand (edict_t *ent) case 3: case 4: - if (FindNearestPlayer (reinterpret_cast (&bot), ent, 300.0f, true, true, true)) + if (FindNearestPlayer (reinterpret_cast (&bot), ent, 450.0f, true, true, true)) bot->DiscardWeaponForUser (ent, selection == 4 ? false : true); DisplayMenuToClient (ent, BOT_MENU_COMMANDS); diff --git a/source/manager.cpp b/source/manager.cpp index 2d21811..c63900b 100644 --- a/source/manager.cpp +++ b/source/manager.cpp @@ -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) { // this is bot destructor @@ -1139,6 +1147,9 @@ void Bot::NewRound (void) m_numFriendsLeft = 0; m_numEnemiesLeft = 0; + m_avoid = nullptr; + m_avoidTime = 0.0f; + for (i = 0; i < 5; i++) m_prevWptIndex[i] = -1; diff --git a/source/navigate.cpp b/source/navigate.cpp index 6c24a4c..ecc4985 100644 --- a/source/navigate.cpp +++ b/source/navigate.cpp @@ -320,54 +320,34 @@ void Bot::CheckCloseAvoidance (const Vector &dirNormal) if (m_seeEnemyTime + 1.5f < engine.Time ()) return; - edict_t *nearest = nullptr; - float nearestDist = 99999.0f; - int playerCount = 0; + if (m_avoidTime < engine.Time () || m_avoid == nullptr) + return; - 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) - continue; + if ((dirToPoint | g_pGlobals->v_right.Get2D ()) > 0.0f) + SetStrafeSpeed (dirNormal, pev->maxspeed); + else + SetStrafeSpeed (dirNormal, -pev->maxspeed); - float distance = (client.ent->v.origin - pev->origin).GetLength (); - - 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; - } + 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; TraceResult tr; - CheckCloseAvoidance (dirNormal); - // Standing still, no need to check? // 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) @@ -626,7 +604,7 @@ void Bot::CheckTerrain (float movedDistance, const Vector &dirNormal) if (IsOnFloor () || IsInWater ()) { pev->button |= IN_JUMP; - m_jumpStateTimer = Random.Float (2.0f, 3.0f); + m_jumpStateTimer = engine.Time () + Random.Float (0.7f, 1.5f); } break; @@ -648,6 +626,7 @@ void Bot::CheckTerrain (float movedDistance, const Vector &dirNormal) } } } + CheckCloseAvoidance (dirNormal); } bool Bot::DoWaypointNav (void) @@ -1187,7 +1166,7 @@ bool Bot::DoWaypointNav (void) } // 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; if (waypointDistance < desiredDistance)