Skip to content

Commit

Permalink
Nav Mesh: Auto Walkable Seeds
Browse files Browse the repository at this point in the history
  • Loading branch information
caxanga334 committed Jul 9, 2024
1 parent 67e2133 commit d5a8080
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 27 deletions.
5 changes: 5 additions & 0 deletions extension/bot/interfaces/behavior.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,8 @@ QueryAnswerType IBehavior::ShouldAssistTeammate(CBaseBot* me, CBaseExtPlayer& te
{
return GetDecisionQueryResponder()->ShouldAssistTeammate(me, teammate);
}

QueryAnswerType IBehavior::ShouldSwitchToWeapon(CBaseBot* me, const CBotWeapon* weapon)
{
return GetDecisionQueryResponder()->ShouldSwitchToWeapon(me, weapon);
}
1 change: 1 addition & 0 deletions extension/bot/interfaces/behavior.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class IBehavior : public IBotInterface, public IDecisionQuery
Vector GetTargetAimPos(CBaseBot* me, edict_t* entity, CBaseExtPlayer* player = nullptr) override;
QueryAnswerType IsReady(CBaseBot* me) override;
QueryAnswerType ShouldAssistTeammate(CBaseBot* me, CBaseExtPlayer& teammate) override;
QueryAnswerType ShouldSwitchToWeapon(CBaseBot* me, const CBotWeapon* weapon) override;

private:

Expand Down
9 changes: 8 additions & 1 deletion extension/bot/interfaces/decisionquery.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ struct edict_t;
class CBaseBot;
class CKnownEntity;
class CBaseExtPlayer;
class Vector;
class CBotWeapon;

#include "mathlib.h"

Expand Down Expand Up @@ -59,6 +59,8 @@ class IDecisionQuery
virtual QueryAnswerType IsReady(CBaseBot* me);
// Should the bot help a specific teammate?
virtual QueryAnswerType ShouldAssistTeammate(CBaseBot* me, CBaseExtPlayer& teammate);
// Should the bot switch to this weapon?
virtual QueryAnswerType ShouldSwitchToWeapon(CBaseBot* me, const CBotWeapon* weapon);
private:

};
Expand Down Expand Up @@ -123,5 +125,10 @@ inline QueryAnswerType IDecisionQuery::ShouldAssistTeammate(CBaseBot* me, CBaseE
return ANSWER_UNDEFINED;
}

inline QueryAnswerType IDecisionQuery::ShouldSwitchToWeapon(CBaseBot* me, const CBotWeapon* weapon)
{
return ANSWER_UNDEFINED;
}

#endif // !SMNAV_BOT_DECISION_QUERY_H_

5 changes: 5 additions & 0 deletions extension/bot/interfaces/tasks.h
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,11 @@ class AITaskManager : public IEventListener, public IDecisionQuery
PROPAGATE_DECISION_WITH_2ARGS(ShouldAssistTeammate, me, teammate);
}

QueryAnswerType ShouldSwitchToWeapon(CBaseBot* me, const CBotWeapon* weapon) override
{
PROPAGATE_DECISION_WITH_2ARGS(ShouldSwitchToWeapon, me, weapon);
}

private:
friend class AITask<BotClass>;

Expand Down
9 changes: 7 additions & 2 deletions extension/bot/tf2/tasks/tf2bot_maintask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,13 @@ void CTF2BotMainTask::SelectBestWeaponForEnemy(CTF2Bot* me, const CKnownEntity*

if (best)
{
me->SelectWeaponByCommand(best->GetBaseCombatWeapon().GetClassname());
m_weaponswitchtimer.Start(2.0f); // found a valid weapon,
// start the timer even if the query disallows switch
m_weaponswitchtimer.Start(3.0f); // found a valid weapon

if (ShouldSwitchToWeapon(me, best) != ANSWER_NO)
{
me->SelectWeapon(best->GetEntity());
}
}
}

Expand Down
5 changes: 1 addition & 4 deletions extension/bot/tf2/tasks/tf2bot_maintask.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ class CTF2BotMainTask : public AITask<CTF2Bot>

inline bool CTF2BotMainTask::AllowedToSwitchWeapon()
{
if (!m_weaponswitchtimer.HasStarted())
return true;

if (m_weaponswitchtimer.IsElapsed())
if (!m_weaponswitchtimer.HasStarted() || m_weaponswitchtimer.IsElapsed())
return true;

return false;
Expand Down
1 change: 0 additions & 1 deletion extension/bot/tf2/tasks/tf2bot_tactical.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#include <bot/tf2/tasks/medic/tf2bot_medic_main_task.h>
#include <bot/tf2/tasks/engineer/tf2bot_engineer_main.h>
#include <bot/tf2/tasks/sniper/tf2bot_task_sniper_move_to_sniper_spot.h>
#include <bot/tf2/tasks/spy/tf2bot_task_spy_infiltrate.h>
#include "scenario/payload/tf2bot_task_push_payload.h"
#include "scenario/mvm/tf2bot_mvm_idle.h"

Expand Down
7 changes: 7 additions & 0 deletions extension/concommands_debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,13 @@ CON_COMMAND_F(sm_snap_my_angles, "sNAP", FCVAR_CHEAT)
player.SnapEyeAngles(angles);
}

CON_COMMAND_F(sm_navbot_debug_entlist, "Debugs the entity list.", FCVAR_CHEAT)
{
UtilHelpers::ForEveryEntity([](int index, edict_t* edict, CBaseEntity* entity) {
Msg("Entity #%i: %s (%p <%p>) \n", index, gamehelpers->GetEntityClassname(entity) ? gamehelpers->GetEntityClassname(entity) : "NULL CLASSNAME", entity, edict);
});
}

#if SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_BMS

CON_COMMAND_F(sm_get_entity_size, "Returns the entity size", FCVAR_CHEAT)
Expand Down
14 changes: 14 additions & 0 deletions extension/mods/tf2/nav/tfnavmesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ ConVar sm_tf_nav_show_mvm_attributes("sm_tf_nav_show_mvm_attributes", "0", FCVAR
CTFNavMesh::CTFNavMesh() : CNavMesh()
{
m_spawnroomupdatetimer.Start(NAV_SPAWNROOM_UPDATE_INTERVAL);

AddWalkableEntity("item_ammopack_full");
AddWalkableEntity("item_ammopack_medium");
AddWalkableEntity("item_ammopack_small");
AddWalkableEntity("item_healthkit_full");
AddWalkableEntity("item_healthkit_medium");
AddWalkableEntity("item_healthkit_small");
AddWalkableEntity("item_teamflag");
AddWalkableEntity("func_capturezone", true);
AddWalkableEntity("func_regenerate", true);
AddWalkableEntity("team_control_point");
AddWalkableEntity("trigger_capture_area", true);
AddWalkableEntity("trigger_timer_door", true);
AddWalkableEntity("info_powerup_spawn");
}

CTFNavMesh::~CTFNavMesh()
Expand Down
49 changes: 32 additions & 17 deletions extension/navmesh/nav_generate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <sdkports/debugoverlay_shared.h>
#include <sdkports/sdk_traces.h>
#include <sdkports/sdk_utils.h>
#include <entities/baseentity.h>
#include "nav_mesh.h"
#include "nav_node.h"
#include "nav_pathfind.h"
Expand Down Expand Up @@ -3194,26 +3195,40 @@ void CNavMesh::CreateNavAreasFromNodes( void )
// adds walkable positions for any/all positions a mod specifies
void CNavMesh::AddWalkableSeeds( void )
{
// TO-DO
/*
CUtlLinkedList<edict_t*> spawns;
FOR_EACH_VEC(this->m_spawnNames, i) {
findEntWithMatchingName(m_spawnNames[i], spawns);
}
FOR_EACH_LL(spawns, i)
{
// snap it to the sampling grid
Vector pos = spawns[i]->GetCollideable()->GetCollisionOrigin();
pos.x = TheNavMesh->SnapToGrid( pos.x );
pos.y = TheNavMesh->SnapToGrid( pos.y );
UtilHelpers::ForEveryEntity([this](int index, edict_t* edict, CBaseEntity* entity) {
const char* classname = gamehelpers->GetEntityClassname(entity);

Vector normal;
if ( FindGroundForNode( &pos, &normal ) )
if (classname != nullptr && classname[0] != '\0')
{
AddWalkableSeed( pos, normal );
auto it = m_walkableEntities.find(classname);

if (it != m_walkableEntities.end())
{
entities::HBaseEntity be(entity);

Vector pos;

if (it->second)
{
pos = be.WorldSpaceCenter();
}
else
{
pos = be.GetAbsOrigin();
}

pos.x = SnapToGrid(pos.x);
pos.y = SnapToGrid(pos.y);

Vector normal;

if (FindGroundForNode(&pos, &normal))
{
AddWalkableSeed(pos, normal);
}
}
}
}
*/
});
}


Expand Down
16 changes: 16 additions & 0 deletions extension/navmesh/nav_mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ CNavMesh::CNavMesh( void )
ListenForGameEvent("round_start");
ListenForGameEvent("dod_round_start");
ListenForGameEvent("teamplay_round_start");

// Default walkable entities for generation
AddWalkableEntity("info_player_start");
AddWalkableEntity("info_player_teamspawn");
}

//--------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -2914,6 +2918,18 @@ void CNavMesh::CommandNavMarkWalkable( void )

static ConCommand sm_nav_mark_walkable( "sm_nav_mark_walkable", CommandNavMarkWalkable, "Mark the current location as a walkable position. These positions are used as seed locations when sampling the map to generate a Navigation Mesh.", FCVAR_GAMEDLL | FCVAR_CHEAT );

static void CommandNavSeedWalkableSpots()
{
TheNavMesh->CommandNavSeedWalkableSpots();
Msg("Walkable spots added!\n");
}

void CNavMesh::CommandNavSeedWalkableSpots(void)
{
AddWalkableSeeds();
}

static ConCommand sm_nav_seed_walkable_spots("sm_nav_seed_walkable_spots", CommandNavSeedWalkableSpots, "Automatically adds walkable positions.", FCVAR_GAMEDLL | FCVAR_CHEAT);

//--------------------------------------------------------------------------------------------------------------
void CommandNavClearWalkableMarks( void )
Expand Down
11 changes: 9 additions & 2 deletions extension/navmesh/nav_mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,12 @@ class CNavMesh : public CEventListenerHelper
void BeginAnalysis( bool quitWhenFinished = false ); // re-analyze an existing Mesh. Determine Hiding Spots, Encounter Spots, etc.

bool IsGenerating( void ) const { return m_generationMode != GENERATE_NONE; } // return true while a Navigation Mesh is being generated
void addPlayerSpawnName(const char *name) { m_spawnNames.AddToTail(name); } // adds the name of a spawn entitie
/**
* @brief Adds an entity classname to the list of entities to be used for generating walkable spots
* @param name Entity classname
* @param useCenter if true, use the entity WorldSpaceCenter instead of AbsOrigin for the walkable spot.
*/
void AddWalkableEntity(const char* name, bool useCenter = false) { m_walkableEntities.emplace(name, useCenter); }
void AddWalkableSeed( const Vector &pos, const Vector &normal ); // add given walkable position to list of seed positions for map sampling
virtual void AddWalkableSeeds( void ); // adds walkable positions for any/all positions a mod specifies
void ClearWalkableSeeds( void ) { m_walkableSeeds.RemoveAll(); } // erase all walkable seed positions
Expand Down Expand Up @@ -535,6 +540,7 @@ class CNavMesh : public CEventListenerHelper
void CommandNavSaveSelected( const CCommand &args ); // Save selected set to disk
void CommandNavMergeMesh( const CCommand &args ); // Merge a saved selected set into the current mesh
void CommandNavMarkWalkable( void );
void CommandNavSeedWalkableSpots(void);
void CommandNavConnectSpecialLink(int32_t linktype);
void CommandNavDisconnectSpecialLink(int32_t linktype);
void CommandNavSetLinkOrigin();
Expand Down Expand Up @@ -945,7 +951,8 @@ class CNavMesh : public CEventListenerHelper
float m_generationStartTime;
Extent m_simplifyGenerationExtent;

CUtlVector<CUtlString> m_spawnNames; // list of spawn names
std::unordered_map<std::string, bool> m_walkableEntities; // List of entities class names to generate walkable seeds

struct WalkableSeedSpot
{
Vector pos;
Expand Down
24 changes: 24 additions & 0 deletions extension/util/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,30 @@ namespace UtilHelpers
}
}

/**
* @brief Runs a function on every entity.
* @tparam T A function with the following parameters: (int index, edict_t* edict, CBaseEntity* entity)
* @param functor Function to call.
*/
template <typename T>
inline void ForEveryEntity(T functor)
{
CBaseHandle next = g_EntList->FirstHandle();

do
{
CBaseEntity* pEntity = UtilHelpers::GetBaseEntityFromCBaseHandle(next);
edict_t* pEdict = UtilHelpers::GetEdictFromCBaseHandle(next);
int index = next.GetEntryIndex();

functor(index, pEdict, pEntity);

next = g_EntList->NextHandle(next);

} while (next.IsValid());

}

/**
* @brief Calls ClientCommandKeyValues for the given client. Throws an exception if the engine branch doesn't support keyvalue commands.
* @param client Client that will be sending the command.
Expand Down

0 comments on commit d5a8080

Please sign in to comment.