Nav stuff

This commit is contained in:
Karim Abdel Hamid 2014-08-19 21:40:18 -07:00
parent 56accfdb9c
commit d191c2e886
10 changed files with 619 additions and 2 deletions

View file

@ -1,3 +1,3 @@
pushd %~dp0
devtools\bin\vpc.exe /hl2 /episodic +everything /mksln everything.sln
devtools\bin\vpc.exe /hl2 /episodic /2010 /navmesh +everything /mksln everything.sln
popd

View file

@ -1803,7 +1803,7 @@ void CTempEnts::MuzzleFlash( int type, ClientEntityHandle_t hEntity, int attachm
//-----------------------------------------------------------------------------
void CTempEnts::MuzzleFlash( const Vector& pos1, const QAngle& angles, int type, ClientEntityHandle_t hEntity, bool firstPerson )
{
#ifdef CSTRIKE_DLL
#ifndef CSTRIKE_DLL
return;

View file

@ -25,6 +25,11 @@
//@todo: bad dependency!
#include "ai_navigator.h"
// support for nav mesh
#include "nav_mesh.h"
#include "nav_pathfind.h"
#include "nav_area.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
@ -1426,6 +1431,23 @@ AI_Waypoint_t *CAI_Pathfinder::BuildRoute( const Vector &vStart, const Vector &v
buildFlags, goalTolerance);
}
// If the local fails, try a nav mesh route
// the nav mesh doesn't supports flying npc's, like the strider or gunship
if ( !pResult && curNavType != NAV_FLY )
{
/*CNavArea *closestArea = NULL;
CNavArea *startArea = TheNavMesh->GetNearestNavArea(vStart);
CNavArea *goalArea = TheNavMesh->GetNearestNavArea(vEnd);
ShortestPathCost costfunc;
if( NavAreaBuildPath( startArea, goalArea, &vEnd, costfunc, buildFlags, goalTolerance, &closestArea) )
{
AI_Waypoint_t *newway = new AI_Waypoint_t( vEnd, 0, curNavType, bits_WP_TO_GOAL, NO_NODE );
pResult = BuildNavRoute( startArea, closestArea, vEnd, newway, curNavType );
}*/
}
// If the fails, try a node route
if ( !pResult )
{
@ -1437,6 +1459,319 @@ AI_Waypoint_t *CAI_Pathfinder::BuildRoute( const Vector &vStart, const Vector &v
return pResult;
}
//-----------------------------------------------------------------------------
// Purpose: This is our cost func. This should likely do something different
// then return 0.0f...
//-----------------------------------------------------------------------------
class CNavArea;
class CNavLadder;
float checkCost( CNavArea *nav1, CNavArea *nav2, const CNavLadder *nav3 )
{
if(nav3) // ignore ladders
return 0.0f;
if( !nav1 || !nav2 )
return 0.0f;
// just take the lenght of the two centers. This should be enough.
return nav1->GetTotalCost() + (nav1->GetCenter() - nav2->GetCenter()).Length2D();
//return 0.0f;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Find path from startArea to goalArea via an A* search, using supplied cost heuristic.
* If cost functor returns -1 for an area, that area is considered a dead end.
* This doesn't actually build a path, but the path is defined by following parent
* pointers back from goalArea to startArea.
* If 'closestArea' is non-NULL, the closest area to the goal is returned (useful if the path fails).
* If 'goalArea' is NULL, will compute a path as close as possible to 'goalPos'.
* If 'goalPos' is NULL, will use the center of 'goalArea' as the goal position.
* Returns true if a path exists.
* Updated for hl2wars and hl2 npc's
*/
template< typename CostFunctor >
bool CAI_Pathfinder::NavAreaBuildPath( CNavArea *startArea, CNavArea *goalArea, const Vector *goalPos, CostFunctor &costFunc, int buildFlags, float goalTolerance, CNavArea **closestArea )
{
/*if (startArea == NULL)
return false;
if (goalArea == NULL && goalPos == NULL)
return false;
startArea->SetParent( NULL );
// if we are already in the goal area, build trivial path
if (startArea == goalArea)
{
goalArea->SetParent( NULL );
return true;
}
// determine actual goal position
Vector actualGoalPos = (goalPos) ? *goalPos : goalArea->GetCenter();
// start search
CNavArea::ClearSearchLists();
// compute estimate of path length
/// @todo Cost might work as "manhattan distance"
startArea->SetTotalCost( (startArea->GetCenter() - actualGoalPos).Length() );
float initCost = checkCost( startArea, NULL, NULL );
if (initCost < 0.0f)
return false;
startArea->SetCostSoFar( initCost );
startArea->AddToOpenList();
// keep track of the area we visit that is closest to the goal
if (closestArea)
*closestArea = startArea;
float closestAreaDist = startArea->GetTotalCost();
// do A* search
while( !CNavArea::IsOpenListEmpty() )
{
// get next area to check
CNavArea *area = CNavArea::PopOpenList();
// don't consider jump area's
if( area->GetAttributes() & NAV_MESH_JUMP)
continue;
// check if we have found the goal area or position
if (area == goalArea || (goalArea == NULL && goalPos && area->Contains( *goalPos )))
{
if (closestArea)
{
*closestArea = area;
}
return true;
}
// search adjacent areas
bool searchFloor = true;
int dir = NORTH;
const NavConnectVector *floorList = area->GetAdjacentAreas( NORTH );
int floorIter = 0;
bool ladderUp = true;
const NavLadderConnectVector *ladderList = NULL;
int ladderIter = NavLadderConnectList::InvalidIndex();
enum { AHEAD = 0, LEFT, RIGHT, BEHIND, NUM_TOP_DIRECTIONS };
int ladderTopDir = AHEAD;
while(true)
{
CNavArea *newArea;
NavTraverseType how;
const CNavLadder *ladder = NULL;
//
// Get next adjacent area - either on floor or via ladder
//
if (searchFloor)
{
// if exhausted adjacent connections in current direction, begin checking next direction
if (floorIter == floorList->InvalidIndex())
{
++dir;
if (dir == NUM_DIRECTIONS)
{
// checked all directions on floor - check ladders next
searchFloor = false;
ladderList = area->GetLadders( CNavLadder::LADDER_UP );
ladderIter = 0;
ladderTopDir = AHEAD;
}
else
{
// start next direction
floorList = area->GetAdjacentAreas( (NavDirType)dir );
floorIter = 0;
}
continue;
}
newArea = floorList->Element(floorIter).area;
how = (NavTraverseType)dir;
floorIter++;
}
else // search ladders
{
if (ladderIter == ladderList->InvalidIndex())
{
if (!ladderUp)
{
// checked both ladder directions - done
break;
}
else
{
// check down ladders
ladderUp = false;
ladderList = area->GetLadders( CNavLadder::LADDER_DOWN );
ladderIter = 0;
}
continue;
}
if (ladderUp)
{
ladder = ladderList->Element( ladderIter ).ladder;
// do not use BEHIND connection, as its very hard to get to when going up a ladder
if (ladderTopDir == AHEAD)
newArea = ladder->m_topForwardArea;
else if (ladderTopDir == LEFT)
newArea = ladder->m_topLeftArea;
else if (ladderTopDir == RIGHT)
newArea = ladder->m_topRightArea;
else
{
ladderIter++;
ladderTopDir = AHEAD;
continue;
}
how = GO_LADDER_UP;
++ladderTopDir;
}
else
{
newArea = ladderList->Element(ladderIter).ladder->m_bottomArea;
how = GO_LADDER_DOWN;
ladder = ladderList->Element(ladderIter).ladder;
ladderIter++;
}
if (newArea == NULL)
continue;
}
// don't backtrack
if (newArea == area)
continue;
// don't consider jump area's
if( newArea->GetAttributes() & NAV_MESH_JUMP)
continue;
float newCostSoFar = checkCost( newArea, area, ladder );
// check if cost functor says this area is a dead-end
if (newCostSoFar < 0.0f)
continue;
if ((newArea->IsOpen() || newArea->IsClosed()) && newArea->GetCostSoFar() <= newCostSoFar)
{
// this is a worse path - skip it
continue;
}
else
{
// compute estimate of distance left to go
float newCostRemaining = (newArea->GetCenter() - actualGoalPos).Length();
// track closest area to goal in case path fails
if (closestArea && newCostRemaining < closestAreaDist)
{
*closestArea = newArea;
closestAreaDist = newCostRemaining;
}
newArea->SetParent( area, how );
newArea->SetCostSoFar( newCostSoFar );
newArea->SetTotalCost( newCostSoFar + newCostRemaining );
if (newArea->IsClosed())
newArea->RemoveFromClosedList();
if (newArea->IsOpen())
{
// area already on open list, update the list order to keep costs sorted
newArea->UpdateOnOpenList();
}
else
{
newArea->AddToOpenList();
}
}
}
// we have searched this area
area->AddToClosedList();
}
return false;*/
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Builds a route to the given vecGoal using the navigation mesh result
//-----------------------------------------------------------------------------
AI_Waypoint_t *CAI_Pathfinder::BuildNavRoute( CNavArea *startArea, CNavArea *goalArea, const Vector &from, AI_Waypoint_t *waypoint, Navigation_t curNavType )
{
if( !goalArea )
return waypoint;
int nodeID, endFlags;
nodeID = NO_NODE;
endFlags = 0;
Vector center, center_portal, delta;
float hwidth, hwidthhull;
NavDirType dir;
Vector closestpoint;
if( goalArea->GetParent() )
{
center = goalArea->GetParent()->GetCenter();
dir = goalArea->ComputeDirection(&center);
goalArea->ComputePortal( goalArea->GetParent(), dir, &center_portal, &hwidth );
goalArea->ComputeClosestPointInPortal( goalArea->GetParent(), dir, goalArea->GetParent()->GetCenter(), &closestpoint );
// this point would be the closest route. But does or our hull fits?
trace_t trace;
CTraceFilterWorldOnly traceFilter;
AI_TraceHull( closestpoint, closestpoint, WorldAlignMins(), WorldAlignMaxs(), MASK_SOLID, &traceFilter, &trace );
if ( trace.fraction != 1.0f )
{
// move a bit to the center
delta = closestpoint - center_portal;
hwidthhull = GetOuter()->GetHullWidth() / 2.0f;
if( delta.IsLengthGreaterThan( hwidthhull ) )
{
closestpoint = closestpoint + ( (hwidthhull / delta.Length() ) * delta );
}
}
// TODO: try to make the connection look less forced by randomly moving it a bit.
closestpoint.z = goalArea->GetZ( closestpoint );
AI_Waypoint_t *newway = new AI_Waypoint_t( closestpoint, 0, curNavType, endFlags, nodeID );
if( waypoint )
newway->SetNext(waypoint);
return BuildNavRoute( startArea, goalArea->GetParent(), closestpoint, newway, curNavType );
}
else
{
return waypoint;
}
}
void CAI_Pathfinder::UnlockRouteNodes( AI_Waypoint_t *pPath )
{
CAI_Node *pNode;

View file

@ -80,6 +80,8 @@ public:
// --------------------------------
template< typename CostFunctor > bool NavAreaBuildPath( CNavArea *startArea, CNavArea *goalArea, const Vector *goalPos, CostFunctor &costFunc, int buildFlags, float goalTolerance, CNavArea **closestArea );
virtual AI_Waypoint_t *BuildNavRoute( CNavArea *startArea, CNavArea *goalArea, const Vector &from, AI_Waypoint_t *waypoint, Navigation_t curNavType );
virtual AI_Waypoint_t *BuildNodeRoute( const Vector &vStart, const Vector &vEnd, int buildFlags, float goalTolerance );
virtual AI_Waypoint_t *BuildLocalRoute( const Vector &vStart, const Vector &vEnd, CBaseEntity const *pTarget, int endFlags, int nodeID, int buildFlags, float goalTolerance);
virtual AI_Waypoint_t *BuildRadialRoute( const Vector &vStartPos, const Vector &vCenterPos, const Vector &vGoalPos, float flRadius, float flArc, float flStepDist, bool bClockwise, float goalTolerance, bool bAirRoute );

View file

@ -120,6 +120,7 @@ enum Class_T
CLASS_EARTH_FAUNA,
CLASS_HACKED_ROLLERMINE,
CLASS_COMBINE_HUNTER,
//CLASS_HUMAN_GRUNT,
NUM_AI_CLASSES
};

View file

@ -86,6 +86,7 @@ struct NavConnect
};
typedef CUtlVectorUltraConservative<NavConnect, CNavVectorAllocator> NavConnectVector;
typedef CUtlLinkedList<NavConnect, int> NavConnectList;
//-------------------------------------------------------------------------------------------------------------------
@ -103,6 +104,7 @@ union NavLadderConnect
}
};
typedef CUtlVectorUltraConservative<NavLadderConnect, CNavVectorAllocator> NavLadderConnectVector;
typedef CUtlLinkedList<NavLadderConnect, int> NavLadderConnectList;
//--------------------------------------------------------------------------------------------------------------

View file

@ -0,0 +1,277 @@
#include "cbase.h"
#include "ai_default.h"
#include "ai_task.h"
#include "ai_schedule.h"
#include "ai_hull.h"
#include "soundent.h"
#include "game.h"
#include "npcevent.h"
#include "entitylist.h"
#include "activitylist.h"
#include "ai_basenpc.h"
#include "ai_basehumanoid.h"
#include "ai_sentence.h"
#include "ai_baseactor.h"
#include "engine/IEngineSound.h"
#include "tier0/memdbgon.h"
//=========================================================
// Private activities
//=========================================================
//int ACT_MYCUSTOMACTIVITY = -1;
//Activity ACT_RUN_RIFLE;
Activity ACT_WALK_UNARMED;
//=========================================================
// Custom schedules
//=========================================================
enum
{
//SCHED_MYCUSTOMSCHEDULE = LAST_SHARED_SCHEDULE,
};
//=========================================================
// Custom tasks
//=========================================================
enum
{
//TASK_MYCUSTOMTASK = LAST_SHARED_TASK,
};
//=========================================================
// Custom Conditions
//=========================================================
enum
{
//COND_MYCUSTOMCONDITION = LAST_SHARED_CONDITION,
};
//=========================================================
//=========================================================
class CNPCHuman : public CAI_BaseHumanoid
{
DECLARE_CLASS( CNPCHuman, CAI_BaseHumanoid );
public:
void Precache( void );
void Spawn( void );
Class_T Classify( void );
int SelectSchedule( void );
void StartTask( const Task_t *pTask );
};
LINK_ENTITY_TO_CLASS( npc_hn_human, CNPCHuman );
//-----------------------------------------------------------------------------
// Purpose:
//
//
//-----------------------------------------------------------------------------
void CNPCHuman::Precache( void )
{
PrecacheModel( "models/humans/marine.mdl" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//
//
//-----------------------------------------------------------------------------
void CNPCHuman::Spawn( void )
{
Precache();
SetModel( "models/humans/marine.mdl" );
SetHullType(HULL_HUMAN);
SetHullSizeNormal();
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_STEP );
SetBloodColor( BLOOD_COLOR_RED );
SetFlexWeight("cheek_depth",RandomFloat(0,1));
SetFlexWeight("cheek_fat_max",RandomFloat(0,1));
SetFlexWeight("cheek_fat_min",RandomFloat(0,0.5));
SetFlexWeight("chin_butt",RandomFloat(0,1));
SetFlexWeight("chin_width",RandomFloat(0,1));
SetFlexWeight("face_d_min",RandomFloat(0,1));
SetFlexWeight("face_d_max",RandomFloat(0,1));
SetFlexWeight("head_height",RandomFloat(0,1));
SetFlexWeight("head_w_min",RandomFloat(0,1));
SetFlexWeight("head_w_max",RandomFloat(0,1));
SetFlexWeight("neck_size",RandomFloat(0,1));
SetFlexWeight("ears_angle",RandomFloat(0,1));
SetFlexWeight("ears_height",RandomFloat(0,1));
SetFlexWeight("eyes_ang_min",RandomFloat(0,1));
SetFlexWeight("eyes_ang_max",RandomFloat(0,1));
SetFlexWeight("eyes_height",RandomFloat(0,1));
SetFlexWeight("jaw_depth",RandomFloat(0,1));
SetFlexWeight("lowlip_size",RandomFloat(0,1));
SetFlexWeight("uplip_size",RandomFloat(0,1));
SetFlexWeight("mouth_w_min",RandomFloat(0,1));
SetFlexWeight("mouth_w_max",RandomFloat(0,1));
SetFlexWeight("mouth_h_min",RandomFloat(0,1));
SetFlexWeight("mouth_h_max",RandomFloat(0,1));
SetFlexWeight("mouth_depth",RandomFloat(0,1));
SetFlexWeight("nose_w_min",RandomFloat(0,1));
SetFlexWeight("nose_w_max",RandomFloat(0,1));
SetFlexWeight("nose_h_min",RandomFloat(0,1));
SetFlexWeight("nose_h_max",RandomFloat(0,1));
SetFlexWeight("nose_angle",RandomFloat(0,1));
SetFlexWeight("nose_d_max",RandomFloat(0,1));
SetFlexWeight("nost_height",RandomFloat(0,1));
SetFlexWeight("nost_width",RandomFloat(0,1));
SetFlexWeight("nose_tip",RandomFloat(0,1));
SetFlexWeight("hairline_puff",RandomFloat(0,1));
if (RandomInt(0,5)==4) {
SetBodygroup( 2, 1 );
SetFlexWeight("cigar_mouth",1);
}
else {
SetBodygroup( 2, 0 );
SetFlexWeight("cigar_mouth",0);
}
m_nSkin=RandomInt(0,14);
SetBodygroup( 3, RandomInt(0,1) );
//SetBodygroup( 4, RandomInt(0,1) );
m_iHealth = 40;
m_flFieldOfView = 0.5;
m_NPCState = NPC_STATE_NONE;
CapabilitiesClear();
CapabilitiesAdd( bits_CAP_MOVE_GROUND );
CapabilitiesAdd( bits_CAP_MOVE_JUMP );
CapabilitiesAdd( bits_CAP_MOVE_GROUND );
CapabilitiesAdd( bits_CAP_MOVE_CLIMB );
CapabilitiesAdd( bits_CAP_MOVE_CRAWL );
CapabilitiesAdd( bits_CAP_DUCK );
CapabilitiesAdd( bits_CAP_USE_WEAPONS );
CapabilitiesAdd( bits_CAP_RANGE_ATTACK_GROUP );
CapabilitiesAdd( bits_CAP_MELEE_ATTACK_GROUP );
CapabilitiesAdd( bits_CAP_INNATE_MELEE_ATTACK1 );
CapabilitiesAdd( bits_CAP_MOVE_SHOOT );
CapabilitiesAdd( bits_CAP_NO_HIT_SQUADMATES );
CapabilitiesAdd( bits_CAP_SQUAD );
CapabilitiesAdd( bits_CAP_USE );
CapabilitiesAdd( bits_CAP_DOORS_GROUP );
CapabilitiesAdd( bits_CAP_TURN_HEAD );
CapabilitiesAdd( bits_CAP_ANIMATEDFACE );
CapabilitiesAdd( bits_CAP_AIM_GUN );
CapabilitiesAdd( bits_CAP_MOVE_GROUND );
CBaseCombatWeapon *pWeapon = Weapon_Create( "weapon_shotgun" );
pWeapon->SetName( AllocPooledString(UTIL_VarArgs("%s_weapon", STRING(GetEntityName()))) );
pWeapon->AddEffects( EF_NOSHADOW );
Weapon_Equip( pWeapon );
NPCInit();
}
void CNPCHuman::StartTask( const Task_t *pTask )
{
switch (pTask->iTask) {
case TASK_RUN_PATH:
GetNavigator()->SetMovementActivity(ACT_RUN_RIFLE);
break;
case TASK_WALK_PATH:
GetNavigator()->SetMovementActivity(ACT_WALK_UNARMED);
break;
default:
BaseClass::StartTask(pTask);
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//
//
// Output :
//-----------------------------------------------------------------------------
Class_T CNPCHuman::Classify( void )
{
return CLASS_MILITARY;
}
int CNPCHuman::SelectSchedule( void )
{
if ( HasCondition( COND_HEAR_PHYSICS_DANGER ) )
{
return SCHED_FLINCH_PHYSICS;
}
// grunts place HIGH priority on running away from danger sounds.
if ( HasCondition(COND_HEAR_DANGER) )
{
CSound *pSound;
pSound = GetBestSound();
Assert( pSound != NULL );
if ( pSound)
{
if (pSound->m_iType & SOUND_DANGER)
{
// I hear something dangerous, probably need to take cover.
// dangerous sound nearby!, call it out
/*const char *pSentenceName = "COMBINE_DANGER";
CBaseEntity *pSoundOwner = pSound->m_hOwner;
if ( pSoundOwner )
{
CBaseGrenade *pGrenade = dynamic_cast<CBaseGrenade *>(pSoundOwner);
if ( pGrenade && pGrenade->GetThrower() )
{
if ( IRelationType( pGrenade->GetThrower() ) != D_LI )
{
// special case call out for enemy grenades
pSentenceName = "COMBINE_GREN";
}
}
}*/
//m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL );
// If the sound is approaching danger, I have no enemy, and I don't see it, turn to face.
if( !GetEnemy() && pSound->IsSoundType(SOUND_CONTEXT_DANGER_APPROACH) && pSound->m_hOwner && !FInViewCone(pSound->GetSoundReactOrigin()) )
{
GetMotor()->SetIdealYawToTarget( pSound->GetSoundReactOrigin() );
return SCHED_ALERT_FACE_BESTSOUND;
}
return SCHED_TAKE_COVER_FROM_BEST_SOUND;
}
}
}
switch ( m_NPCState )
{
case NPC_STATE_IDLE:
return SCHED_IDLE_STAND;
break;
case NPC_STATE_COMBAT:
if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
return SCHED_MELEE_ATTACK1;
if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
return SCHED_RANGE_ATTACK1;
return SCHED_CHASE_ENEMY;
break;
}
return SCHED_FAIL;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.