diff --git a/include/core.h b/include/core.h index 0f86a79..92e8791 100644 --- a/include/core.h +++ b/include/core.h @@ -704,9 +704,11 @@ struct Client edict_t *ent; // pointer to actual edict Vector origin; // position in the world Vector soundPosition; // position sound was played + int team; // bot team int realTeam; // real bot team in free for all mode (csdm) int flags; // client flags + float hearingDistance; // distance this sound is heared float timeSoundLasting; // time sound is played/heared float maxTimeSoundLasting; // max time sound is played/heared (to divide the difference between that above one and the current one) @@ -1000,6 +1002,7 @@ private: int FindCoverWaypoint (float maxDistance); int FindDefendWaypoint (Vector origin); int FindGoal (void); + void FilterGoals (const Array &goals, int *result); void FindItem (void); void CheckTerrain (float movedDistance, const Vector &dir, const Vector &dirNormal); @@ -1153,6 +1156,7 @@ public: int m_actMessageIndex; // current processed message int m_pushMessageIndex; // offset for next pushed message + int m_goalFailed; // if bot can't reach several times in a row int m_prevGoalIndex; // holds destination goal waypoint int m_chosenGoalIndex; // used for experience, same as above float m_goalValue; // ranking value for this waypoint diff --git a/include/corelib.h b/include/corelib.h index 7cf966b..077077b 100644 --- a/include/corelib.h +++ b/include/corelib.h @@ -1204,7 +1204,7 @@ public: // Returns: // Number of allocated items. // - int GetSize (void) + int GetSize (void) const { return m_itemSize; } @@ -1216,7 +1216,7 @@ public: // Returns: // Number of elements. // - int GetElementNumber (void) + int GetElementNumber (void) const { return m_itemCount; } diff --git a/source/basecode.cpp b/source/basecode.cpp index 6030b4e..71f1b34 100644 --- a/source/basecode.cpp +++ b/source/basecode.cpp @@ -2678,7 +2678,7 @@ void Bot::CheckRadioCommands (void) { if (g_timeNextBombUpdate < GetWorldTime ()) { - float minDistance = FLT_MAX; + float minDistance = 4096.0f; // find nearest bomb waypoint to player IterateArray (g_waypoint->m_goalPoints, i) @@ -3202,25 +3202,10 @@ void Bot::RunTask (void) m_aimFlags |= AIM_NAVPOINT; // user forced a waypoint as a goal? - if (yb_debug_goal.GetInt () != -1) + if (yb_debug_goal.GetInt () != -1 && GetTask ()->data != yb_debug_goal.GetInt ()) { - // check if we reached it - if (((m_currentPath->origin - pev->origin).SkipZ ()).GetLengthSquared () < 16 && GetTask ()->data == yb_debug_goal.GetInt ()) - { - m_moveSpeed = 0.0; - m_strafeSpeed = 0.0; - - m_checkTerrain = false; - m_moveToGoal = false; - - return; // we can safely return here - } - - if (GetTask ()->data != yb_debug_goal.GetInt ()) - { - DeleteSearchNodes (); - GetTask ()->data = yb_debug_goal.GetInt (); - } + DeleteSearchNodes (); + GetTask ()->data = yb_debug_goal.GetInt (); } // bots rushing with knife, when have no enemy (thanks for idea to nicebot project) @@ -3300,15 +3285,14 @@ void Bot::RunTask (void) MakeVectors (pev->v_angle); - m_timeCamping = GetWorldTime () + Random.Float (10.0f, 30.0f); - StartTask (TASK_CAMP, TASKPRI_CAMP, -1, m_timeCamping, true); + StartTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + Random.Float (20.0f, 40.0f), true); m_camp = Vector (m_currentPath->campStartX, m_currentPath->campStartY, 0.0f); m_aimFlags |= AIM_CAMP; m_campDirection = 0; // tell the world we're camping - if (Random.Long (0, 100) < 95) + if (Random.Long (0, 100) < 80) RadioMessage (Radio_InPosition); m_moveToGoal = false; @@ -3349,8 +3333,7 @@ void Bot::RunTask (void) ChatterMessage (Chatter_GoingToGuardVIPSafety); // play info about that } } - - if ((g_mapType & MAP_DE) && ((m_currentPath->flags & FLAG_GOAL) || m_inBombZone) && m_seeEnemyTime + 3.0 < GetWorldTime ()) + else if ((g_mapType & MAP_DE) && ((m_currentPath->flags & FLAG_GOAL) || m_inBombZone) && m_seeEnemyTime + 1.5f < GetWorldTime ()) { // is it a terrorist carrying the bomb? if (m_hasC4) @@ -3368,12 +3351,12 @@ void Bot::RunTask (void) } else if (m_team == TEAM_CF) { - if (!g_bombPlanted && GetNearbyFriendsNearPosition (pev->origin, 360) < 3 && Random.Long (0, 100) < 85 && GetTaskId () == TASK_NORMAL && m_fearLevel > m_agressionLevel / 2) + if (!g_bombPlanted && GetNearbyFriendsNearPosition (pev->origin, 360.0f) < 3 && Random.Long (0, 100) < 85 && m_personality != PERSONALITY_RUSHER) { int index = FindDefendWaypoint (m_currentPath->origin); - StartTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + Random.Float (45.0, 60.0), true); // push camp task on to stack - StartTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, GetWorldTime () + Random.Float (10.0, 15.0), true); // push move command + StartTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + Random.Float (25.0, 40.0), true); // push camp task on to stack + StartTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, index, GetWorldTime () + Random.Float (5.0f, 11.0f), true); // push move command if (g_waypoint->GetPath (index)->vis.crouch <= g_waypoint->GetPath (index)->vis.stand) m_campButtons |= IN_DUCK; @@ -3715,7 +3698,7 @@ void Bot::RunTask (void) m_idealReactionTime /= 2; m_navTimeset = GetWorldTime (); - m_timeCamping = GetWorldTime(); + m_timeCamping = GetWorldTime(); m_moveSpeed = 0; m_strafeSpeed = 0.0; @@ -5180,7 +5163,7 @@ void Bot::BotAI (void) sprintf (aimFlags, "%s%s%s%s%s%s%s%s", m_aimFlags & AIM_NAVPOINT ? " NavPoint" : "", m_aimFlags & AIM_CAMP ? " CampPoint" : "", - m_aimFlags & AIM_PREDICT_PATH ? " predictPath" : "", + m_aimFlags & AIM_PREDICT_PATH ? " PredictPath" : "", m_aimFlags & AIM_LAST_ENEMY ? " LastEnemy" : "", m_aimFlags & AIM_ENTITY ? " Entity" : "", m_aimFlags & AIM_ENEMY ? " Enemy" : "", @@ -5443,7 +5426,7 @@ void Bot::CollectGoalExperience (int damage, int team) // gets called each time a bot gets damaged by some enemy. tries to achieve a statistic about most/less dangerous // waypoints for a destination goal used for pathfinding - if ((g_numWaypoints < 1) || g_waypointsChanged || (m_chosenGoalIndex < 0) || (m_prevGoalIndex < 0)) + if (g_numWaypoints < 1 || g_waypointsChanged || m_chosenGoalIndex < 0 || m_prevGoalIndex < 0) return; // only rate goal waypoint if bot died because of the damage diff --git a/source/interface.cpp b/source/interface.cpp index dde528d..b68c17e 100644 --- a/source/interface.cpp +++ b/source/interface.cpp @@ -997,11 +997,13 @@ void Touch (edict_t *pentTouched, edict_t *pentOther) // the two entities both have velocities, for example two players colliding, this function // is called twice, once for each entity moving. - Bot *touched = g_botManager->GetBot (pentTouched); - - if (touched != NULL) - touched->VerifyBreakable (pentOther); + if (!IsEntityNull (pentTouched) && (pentTouched->v.flags & FL_FAKECLIENT)) + { + Bot *touched = g_botManager->GetBot (pentTouched); + if (touched != NULL) + touched->VerifyBreakable (pentOther); + } if (g_isMetamod) RETURN_META (MRES_IGNORED); diff --git a/source/manager.cpp b/source/manager.cpp index 621e04d..897f708 100644 --- a/source/manager.cpp +++ b/source/manager.cpp @@ -931,6 +931,7 @@ void Bot::NewRound (void) m_currentWaypointIndex = -1; m_currentPath = NULL; m_currentTravelFlags = 0; + m_goalFailed = 0; m_desiredVelocity = nullvec; m_prevGoalIndex = -1; m_chosenGoalIndex = -1; @@ -940,11 +941,8 @@ void Bot::NewRound (void) m_duckDefuse = false; m_duckDefuseCheckTime = 0.0; - m_prevWptIndex[0] = -1; - m_prevWptIndex[1] = -1; - m_prevWptIndex[2] = -1; - m_prevWptIndex[3] = -1; - m_prevWptIndex[4] = -1; + for (int i = 0; i < 5; i++) + m_prevWptIndex[i] = -1; m_navTimeset = GetWorldTime (); m_team = GetTeam (GetEntity ()); diff --git a/source/navigate.cpp b/source/navigate.cpp index 82d4d26..b843434 100644 --- a/source/navigate.cpp +++ b/source/navigate.cpp @@ -50,16 +50,15 @@ int Bot::FindGoal (void) } int tactic; - // path finding behaviour depending on map type - int offensive; - int defensive; - int goalDesire; + float offensive; + float defensive; - int forwardDesire; - int campDesire; - int backoffDesire; - int tacticChoice; + float goalDesire; + float forwardDesire; + float campDesire; + float backoffDesire; + float tacticChoice; Array offensiveWpts; Array defensiveWpts; @@ -91,8 +90,8 @@ int Bot::FindGoal (void) goto TacticChoosen; } - offensive = static_cast (m_agressionLevel * 100); - defensive = static_cast (m_fearLevel * 100); + offensive = m_agressionLevel * 100; + defensive = m_fearLevel * 100; if (g_mapType & (MAP_AS | MAP_CS)) { @@ -103,8 +102,17 @@ int Bot::FindGoal (void) } else if (m_team == TEAM_CF) { - defensive -= 25.0f; - offensive += 25.0f; + // on hostage maps force more bots to save hostages + if (g_mapType & MAP_CS) + { + defensive -= 25.0f - m_difficulty * 0.5f; + offensive += 25.0f + m_difficulty * 5.0f; + } + else // on AS leave as is + { + defensive -= 25.0f; + offensive += 25.0f; + } } } else if ((g_mapType & MAP_DE) && m_team == TEAM_CF) @@ -128,10 +136,10 @@ int Bot::FindGoal (void) return m_chosenGoalIndex = FindDefendWaypoint (g_waypoint->GetBombPosition ()); } - goalDesire = Random.Long (0, 100 + (offensive * 0.5)) + offensive; - forwardDesire = Random.Long (0, 100) + offensive; - campDesire = Random.Long (0, 100) + defensive; - backoffDesire = Random.Long (0, 100) + defensive; + goalDesire = Random.Float (0.0f, 100.0f) + offensive; + forwardDesire = Random.Float (0.0f, 100.0f) + offensive; + campDesire = Random.Float (0.0f, 100.0f) + defensive; + backoffDesire = Random.Float (0.0f, 100.0f) + defensive; if (!UsesCampGun ()) campDesire = 0; @@ -158,41 +166,17 @@ TacticChoosen: int goalChoices[4] = {-1, -1, -1, -1}; if (tactic == 0 && !defensiveWpts.IsEmpty ()) // careful goal - { - for (int i = 0; i < 4; i++) - { - goalChoices[i] = defensiveWpts.GetRandomElement (); - InternalAssert (goalChoices[i] >= 0 && goalChoices[i] < g_numWaypoints); - } - } + FilterGoals (defensiveWpts, goalChoices); else if (tactic == 1 && !g_waypoint->m_campPoints.IsEmpty ()) // camp waypoint goal { // pickup sniper points if possible for sniping bots - if (!g_waypoint->m_sniperPoints.IsEmpty () && ((pev->weapons & (1 << WEAPON_AWP)) || (pev->weapons & (1 << WEAPON_SCOUT)) || (pev->weapons & (1 << WEAPON_G3SG1)) || (pev->weapons & (1 << WEAPON_SG550)))) - { - for (int i = 0; i < 4; i++) - { - goalChoices[i] = g_waypoint->m_sniperPoints.GetRandomElement (); - InternalAssert (goalChoices[i] >= 0 && goalChoices[i] < g_numWaypoints); - } - } + if (!g_waypoint->m_sniperPoints.IsEmpty () && UsesSniper ()) + FilterGoals (g_waypoint->m_sniperPoints, goalChoices); else - { - for (int i = 0; i < 4; i++) - { - goalChoices[i] = g_waypoint->m_campPoints.GetRandomElement (); - InternalAssert (goalChoices[i] >= 0 && goalChoices[i] < g_numWaypoints); - } - } + FilterGoals (g_waypoint->m_campPoints, goalChoices); } else if (tactic == 2 && !offensiveWpts.IsEmpty ()) // offensive goal - { - for (int i = 0; i < 4; i++) - { - goalChoices[i] = offensiveWpts.GetRandomElement (); - InternalAssert (goalChoices[i] >= 0 && goalChoices[i] < g_numWaypoints); - } - } + FilterGoals (offensiveWpts, goalChoices); else if (tactic == 3 && !g_waypoint->m_goalPoints.IsEmpty ()) // map goal waypoint { // forcee bomber to select closest goal, if round-start goal was reset by something @@ -231,13 +215,7 @@ TacticChoosen: } } else - { - for (int i = 0; i < 4; i++) - { - goalChoices[i] = g_waypoint->m_goalPoints.GetRandomElement (); - InternalAssert (goalChoices[i] >= 0 && goalChoices[i] < g_numWaypoints); - } - } + FilterGoals (g_waypoint->m_goalPoints, goalChoices); } if (m_currentWaypointIndex == -1 || m_currentWaypointIndex >= g_numWaypoints) @@ -259,31 +237,42 @@ TacticChoosen: if (testIndex < 0) break; - if (m_team == TEAM_TF) - { - if ((g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + goalChoices[i])->team0Value < (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + goalChoices[i + 1])->team0Value) - { - goalChoices[i + 1] = goalChoices[i]; - goalChoices[i] = testIndex; + int goal1 = m_team == TEAM_TF ? (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + goalChoices[i])->team0Value : (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + goalChoices[i])->team1Value; + int goal2 = m_team == TEAM_TF ? (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + goalChoices[i + 1])->team0Value : (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + goalChoices[i + 1])->team1Value; - isSorting = true; - } - } - else + if (goal1 < goal2) { - if ((g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + goalChoices[i])->team1Value < (g_experienceData + (m_currentWaypointIndex * g_numWaypoints) + goalChoices[i + 1])->team1Value) - { - goalChoices[i + 1] = goalChoices[i]; - goalChoices[i] = testIndex; + goalChoices[i + 1] = goalChoices[i]; + goalChoices[i] = testIndex; - isSorting = true; - } + isSorting = true; } } } while (isSorting); - // return and store goal - return m_chosenGoalIndex = goalChoices[0]; + return m_chosenGoalIndex = goalChoices[0]; // return and store goal +} + +void Bot::FilterGoals (const Array &goals, int *result) +{ + // this function filters the goals, so new goal is not bot's old goal, and array of goals doesn't contains duplicate goals + + int totalGoals = goals.GetElementNumber (); + int searchCount = 0; + + for (int index = 0; index < 4; index++) + { + int rand = goals.GetRandomElement (); + + if (searchCount <= 8 && (m_prevGoalIndex == rand || ((result[0] == rand || result[1] == rand || result[2] == rand || result[3] == rand) && totalGoals > 4)) && !IsPointOccupied (rand)) + { + if (index > 0) + index--; + + continue; + } + result[index] = rand; + } } bool Bot::GoalIsValid (void) @@ -665,8 +654,8 @@ bool Bot::DoWaypointNav (void) // pressing the jump button gives the illusion of the bot actual jmping. if (IsOnFloor () || IsOnLadder ()) { - if (m_desiredVelocity != nullvec) - pev->velocity = m_desiredVelocity + m_desiredVelocity * 0.15; // cheating i know, but something changed in recent cs updates... + if (m_desiredVelocity.x != 0.0f && m_desiredVelocity.y != 0.0f) + pev->velocity = m_desiredVelocity + m_desiredVelocity * 0.11f; pev->button |= IN_JUMP; @@ -1126,13 +1115,8 @@ bool Bot::DoWaypointNav (void) } // needs precise placement - check if we get past the point - if (desiredDistance < 16.0 && waypointDistance < 30) - { - Vector nextFrameOrigin = pev->origin + (pev->velocity * m_frameInterval); - - if ((nextFrameOrigin - m_waypointOrigin).GetLength () > waypointDistance) - desiredDistance = waypointDistance + 1.0; - } + if (desiredDistance < 16.0 && waypointDistance < 30 && (pev->origin + (pev->velocity * m_frameInterval) - m_waypointOrigin).GetLength () > waypointDistance) + desiredDistance = waypointDistance + 1.0; if (waypointDistance < desiredDistance) { @@ -1279,9 +1263,7 @@ public: if (m_size >= m_heapSize) { m_heapSize += 100; - m_heap = (Node *) realloc (m_heap, sizeof (Node) * m_heapSize); - - ServerPrint ("REALLOCATING"); + m_heap = (Node *)realloc (m_heap, sizeof (Node) * m_heapSize); } m_heap[m_size].pri = pri; @@ -1954,7 +1936,29 @@ void Bot::GetValidWaypoint (void) } } DeleteSearchNodes (); - FindWaypoint (); + + if (m_goalFailed > 1) + { + DebugMsg ("GOAL FAILED!"); + int newGoal = FindGoal (); + + m_prevGoalIndex = newGoal; + m_chosenGoalIndex = newGoal; + + // remember index + GetTask ()->data = newGoal; + + // do pathfinding if it's not the current waypoint + if (newGoal != m_currentWaypointIndex) + FindPath (m_currentWaypointIndex, newGoal, m_pathType); + + m_goalFailed = 0; + } + else + { + FindWaypoint (); + m_goalFailed++; + } m_waypointOrigin = m_currentPath->origin; } @@ -1962,6 +1966,9 @@ void Bot::GetValidWaypoint (void) void Bot::ChangeWptIndex (int waypointIndex) { + if (waypointIndex == -1) + return; + m_prevWptIndex[4] = m_prevWptIndex[3]; m_prevWptIndex[3] = m_prevWptIndex[2]; m_prevWptIndex[2] = m_prevWptIndex[1]; @@ -1971,19 +1978,14 @@ void Bot::ChangeWptIndex (int waypointIndex) m_navTimeset = GetWorldTime (); m_currentPath = g_waypoint->GetPath (m_currentWaypointIndex); - - // get the current waypoint flags - if (m_currentWaypointIndex != -1) - m_waypointFlags = m_currentPath->flags; - else - m_waypointFlags = 0; + m_waypointFlags = m_currentPath->flags; } int Bot::ChooseBombWaypoint (void) { // this function finds the best goal (bomb) waypoint for CTs when searching for a planted bomb. - Array &goals = g_waypoint->m_goalPoints; + Array goals = g_waypoint->m_goalPoints; if (goals.IsEmpty ()) return Random.Long (0, g_numWaypoints - 1); // reliability check @@ -2332,7 +2334,7 @@ bool Bot::HeadTowardWaypoint (void) m_minSpeed = pev->maxspeed; // only if we in normal task and bomb is not planted - if (m_currentPath->flags & (FLAG_GOAL | FLAG_SNIPER | FLAG_CAMP) && GetTaskId () == TASK_NORMAL && !g_bombPlanted && m_personality != PERSONALITY_RUSHER && !m_hasC4 && !m_isVIP && m_loosedBombWptIndex == -1 && !HasHostage ()) + if (GetTaskId () == TASK_NORMAL && m_timeCamping + 30.0f < GetWorldTime () && !g_bombPlanted && m_personality != PERSONALITY_RUSHER && !m_hasC4 && !m_isVIP && m_loosedBombWptIndex == -1 && !HasHostage ()) { m_campButtons = 0; @@ -2358,22 +2360,17 @@ bool Bot::HeadTowardWaypoint (void) break; } - if (m_baseAgressionLevel < kills && GetTaskId () != TASK_MOVETOPOSITION && HasPrimaryWeapon ()) + if (m_baseAgressionLevel < kills && HasPrimaryWeapon ()) { - DebugMsg ("pushing camp on to stack"); - StartTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + (m_fearLevel * (g_timeRoundMid - GetWorldTime ()) * 0.5), true); // push camp task on to stack - - StartTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, FindDefendWaypoint (g_waypoint->GetPath (waypoint)->origin), 0.0, true); - - if (m_difficulty >= 3) - pev->button |= IN_DUCK; + StartTask (TASK_CAMP, TASKPRI_CAMP, -1, GetWorldTime () + Random.Float (m_difficulty * 0.5, m_difficulty) * 5, true); + StartTask (TASK_MOVETOPOSITION, TASKPRI_MOVETOPOSITION, FindDefendWaypoint (g_waypoint->GetPath (waypoint)->origin), GetWorldTime () + Random.Float (3.0f, 10.0f), true); } } else if (g_botsCanPause && !IsOnLadder () && !IsInWater () && !m_currentTravelFlags && IsOnFloor ()) { if (static_cast (kills) == m_baseAgressionLevel) m_campButtons |= IN_DUCK; - else if (Random.Long (1, 100) > (m_difficulty * 25 + Random.Long (1, 20))) + else if (Random.Long (1, 100) > m_difficulty * 25) m_minSpeed = GetWalkSpeed (); } } @@ -2430,7 +2427,6 @@ bool Bot::HeadTowardWaypoint (void) if (willJump && m_currentWeapon != WEAPON_KNIFE && m_currentWeapon != WEAPON_SCOUT && !m_isReloading && !UsesPistol () && (jumpDistance > 210 || (destination.z + 32.0f > src.z && jumpDistance > 150.0f) || ((destination - src).GetLength2D () < 60 && jumpDistance > 60)) && IsEntityNull (m_enemy)) SelectWeaponByName ("weapon_knife"); // draw out the knife if we needed - // bot not already on ladder but will be soon? if ((g_waypoint->GetPath (destIndex)->flags & FLAG_LADDER) && !IsOnLadder ()) { @@ -3292,8 +3288,11 @@ bool Bot::IsPointOccupied (int index) continue; // check if this waypoint is already used - if (IsAlive (bot->GetEntity ()) && (bot->m_currentWaypointIndex == index || bot->GetTask ()->data == index || (g_waypoint->GetPath (index)->origin - bot->pev->origin).GetLength2D () < 120.0f)) - return true; + if (IsAlive (bot->GetEntity ())) + { + if ((GetShootingConeDeviation (bot->GetEntity (), &pev->origin) >= 0.7 ? bot->m_prevWptIndex[0] : m_currentWaypointIndex) == index || bot->GetTask ()->data == index || (g_waypoint->GetPath (index)->origin - bot->pev->origin).GetLength2D () < 96.0) + return true; + } } return false; }