some optimizations to goal finding

This commit is contained in:
Dmitry 2015-06-09 22:16:08 +03:00
commit 73f21591f9
6 changed files with 130 additions and 144 deletions

View file

@ -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 <int> &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

View file

@ -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;
}

View file

@ -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

View file

@ -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);

View file

@ -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 ());

View file

@ -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 <int> offensiveWpts;
Array <int> defensiveWpts;
@ -91,8 +90,8 @@ int Bot::FindGoal (void)
goto TacticChoosen;
}
offensive = static_cast <int> (m_agressionLevel * 100);
defensive = static_cast <int> (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 <int> &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 <int> &goals = g_waypoint->m_goalPoints;
Array <int> 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 <float> (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;
}