Cut nodes.

This commit is contained in:
Night Owl 2017-04-10 23:07:03 +05:00
parent e58740d9be
commit 1ceb22e151
3 changed files with 16 additions and 551 deletions

View File

@ -96,13 +96,13 @@ set (SVDLL_SOURCES
lights.cpp
maprules.cpp
# monstermaker.cpp
# monsters.cpp
monsters.cpp
# monsterstate.cpp
# mortar.cpp
# mp5.cpp
multiplay_gamerules.cpp
# nihilanth.cpp
nodes.cpp
# nodes.cpp
# osprey.cpp
pathcorner.cpp
plane.cpp

View File

@ -36,221 +36,4 @@ int CGraph::FindNearestNode( const Vector &vecOrigin, int afNodeTypes ) { return
/*********************************************************/
void CBaseMonster::ReportAIState( void ) { }
float CBaseMonster::ChangeYaw( int speed ) { return 0; }
void CBaseMonster::MakeIdealYaw( Vector vecTarget ) { }
void CBaseMonster::CorpseFallThink( void )
{
if( pev->flags & FL_ONGROUND )
{
SetThink( NULL );
SetSequenceBox();
UTIL_SetOrigin( pev, pev->origin );// link into world.
}
else
pev->nextthink = gpGlobals->time + 0.1;
}
// Call after animation/pose is set up
void CBaseMonster::MonsterInitDead( void )
{
InitBoneControllers();
pev->solid = SOLID_BBOX;
pev->movetype = MOVETYPE_TOSS;// so he'll fall to ground
pev->frame = 0;
ResetSequenceInfo();
pev->framerate = 0;
// Copy health
pev->max_health = pev->health;
pev->deadflag = DEAD_DEAD;
UTIL_SetSize( pev, g_vecZero, g_vecZero );
UTIL_SetOrigin( pev, pev->origin );
// Setup health counters, etc.
BecomeDead();
SetThink( &CBaseMonster::CorpseFallThink );
pev->nextthink = gpGlobals->time + 0.5;
}
BOOL CBaseMonster::ShouldFadeOnDeath( void )
{
return FALSE;
}
BOOL CBaseMonster::FCheckAITrigger( void )
{
return FALSE;
}
void CBaseMonster::KeyValue( KeyValueData *pkvd )
{
CBaseToggle::KeyValue( pkvd );
}
int CBaseMonster::IRelationship( CBaseEntity *pTarget )
{
static int iEnemy[14][14] =
{ // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN
/*NONE*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO },
/*MACHINE*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_DL, R_DL },
/*PLAYER*/ { R_NO ,R_DL ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_DL, R_DL },
/*HUMANPASSIVE*/{ R_NO ,R_NO ,R_AL ,R_AL ,R_HT ,R_FR ,R_NO ,R_HT ,R_DL ,R_FR ,R_NO ,R_AL, R_NO, R_NO },
/*HUMANMILITAR*/{ R_NO ,R_NO ,R_HT ,R_DL ,R_NO ,R_HT ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_HT, R_NO, R_NO },
/*ALIENMILITAR*/{ R_NO ,R_DL ,R_HT ,R_DL ,R_HT ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO },
/*ALIENPASSIVE*/{ R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO },
/*ALIENMONSTER*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO },
/*ALIENPREY */{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_FR ,R_NO ,R_DL, R_NO, R_NO },
/*ALIENPREDATO*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_HT ,R_DL ,R_NO ,R_DL, R_NO, R_NO },
/*INSECT*/ { R_FR ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR, R_NO, R_NO },
/*PLAYERALLY*/ { R_NO ,R_DL ,R_AL ,R_AL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_NO },
/*PBIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_DL },
/*ABIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_NO ,R_NO ,R_DL, R_DL, R_NO }
};
return iEnemy[Classify()][pTarget->Classify()];
}
//=========================================================
// Look - Base class monster function to find enemies or
// food by sight. iDistance is distance ( in units ) that the
// monster can see.
//
// Sets the sight bits of the m_afConditions mask to indicate
// which types of entities were sighted.
// Function also sets the Looker's m_pLink
// to the head of a link list that contains all visible ents.
// (linked via each ent's m_pLink field)
//
//=========================================================
void CBaseMonster::Look( int iDistance )
{
int iSighted = 0;
// DON'T let visibility information from last frame sit around!
ClearConditions( bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT );
m_pLink = NULL;
CBaseEntity *pSightEnt = NULL;// the current visible entity that we're dealing with
CBaseEntity *pList[100];
Vector delta = Vector( iDistance, iDistance, iDistance );
// Find only monsters/clients in box, NOT limited to PVS
int count = UTIL_EntitiesInBox( pList, 100, pev->origin - delta, pev->origin + delta, FL_CLIENT | FL_MONSTER );
for( int i = 0; i < count; i++ )
{
pSightEnt = pList[i];
if( pSightEnt != this && pSightEnt->pev->health > 0 )
{
// the looker will want to consider this entity
// don't check anything else about an entity that can't be seen, or an entity that you don't care about.
if( IRelationship( pSightEnt ) != R_NO && FInViewCone( pSightEnt ) && !FBitSet( pSightEnt->pev->flags, FL_NOTARGET ) && FVisible( pSightEnt ) )
{
if( pSightEnt->IsPlayer() )
{
// if we see a client, remember that (mostly for scripted AI)
iSighted |= bits_COND_SEE_CLIENT;
}
pSightEnt->m_pLink = m_pLink;
m_pLink = pSightEnt;
if( pSightEnt == m_hEnemy )
{
// we know this ent is visible, so if it also happens to be our enemy, store that now.
iSighted |= bits_COND_SEE_ENEMY;
}
// don't add the Enemy's relationship to the conditions. We only want to worry about conditions when
// we see monsters other than the Enemy.
switch( IRelationship( pSightEnt ) )
{
case R_NM:
iSighted |= bits_COND_SEE_NEMESIS;
break;
case R_HT:
iSighted |= bits_COND_SEE_HATE;
break;
case R_DL:
iSighted |= bits_COND_SEE_DISLIKE;
break;
case R_FR:
iSighted |= bits_COND_SEE_FEAR;
break;
case R_AL:
break;
default:
ALERT( at_aiconsole, "%s can't assess %s\n", STRING( pev->classname ), STRING(pSightEnt->pev->classname ) );
break;
}
}
}
}
SetConditions( iSighted );
}
//=========================================================
// BestVisibleEnemy - this functions searches the link
// list whose head is the caller's m_pLink field, and returns
// a pointer to the enemy entity in that list that is nearest the
// caller.
//
// !!!UNDONE - currently, this only returns the closest enemy.
// we'll want to consider distance, relationship, attack types, back turned, etc.
//=========================================================
CBaseEntity *CBaseMonster::BestVisibleEnemy( void )
{
CBaseEntity *pReturn;
CBaseEntity *pNextEnt;
int iNearest;
int iDist;
int iBestRelationship;
iNearest = 8192;// so first visible entity will become the closest.
pNextEnt = m_pLink;
pReturn = NULL;
iBestRelationship = R_NO;
while( pNextEnt != NULL )
{
if( pNextEnt->IsAlive() )
{
if( IRelationship( pNextEnt ) > iBestRelationship )
{
// this entity is disliked MORE than the entity that we
// currently think is the best visible enemy. No need to do
// a distance check, just get mad at this one for now.
iBestRelationship = IRelationship( pNextEnt );
iNearest = ( pNextEnt->pev->origin - pev->origin ).Length();
pReturn = pNextEnt;
}
else if( IRelationship( pNextEnt ) == iBestRelationship )
{
// this entity is disliked just as much as the entity that
// we currently think is the best visible enemy, so we only
// get mad at it if it is closer.
iDist = ( pNextEnt->pev->origin - pev->origin ).Length();
if( iDist <= iNearest )
{
iNearest = iDist;
iBestRelationship = IRelationship( pNextEnt );
pReturn = pNextEnt;
}
}
}
pNextEnt = pNextEnt->m_pLink;
}
return pReturn;
}

View File

@ -6,367 +6,49 @@
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
//=========================================================
// nodes.h
//=========================================================
//=========================================================
// DEFINE
//=========================================================
#define MAX_STACK_NODES 100
#define NO_NODE -1
#define MAX_NODE_HULLS 4
#define bits_NODE_LAND ( 1 << 0 ) // Land node, so nudge if necessary.
#define bits_NODE_AIR ( 1 << 1 ) // Air node, don't nudge.
#define bits_NODE_WATER ( 1 << 2 ) // Water node, don't nudge.
#define bits_NODE_GROUP_REALM (bits_NODE_LAND | bits_NODE_AIR | bits_NODE_WATER)
//=========================================================
// Instance of a node.
//=========================================================
class CNode
{
public:
Vector m_vecOrigin;// location of this node in space
Vector m_vecOriginPeek; // location of this node (LAND nodes are NODE_HEIGHT higher).
BYTE m_Region[3]; // Which of 256 regions do each of the coordinate belong?
int m_afNodeInfo;// bits that tell us more about this location
int m_cNumLinks; // how many links this node has
int m_iFirstLink;// index of this node's first link in the link pool.
// Where to start looking in the compressed routing table (offset into m_pRouteInfo).
// (4 hull sizes -- smallest to largest + fly/swim), and secondly, door capability.
//
int m_pNextBestNode[MAX_NODE_HULLS][2];
// Used in finding the shortest path. m_fClosestSoFar is -1 if not visited.
// Then it is the distance to the source. If another path uses this node
// and has a closer distance, then m_iPreviousNode is also updated.
//
float m_flClosestSoFar; // Used in finding the shortest path.
int m_iPreviousNode;
short m_sHintType;// there is something interesting in the world at this node's position
short m_sHintActivity;// there is something interesting in the world at this node's position
float m_flHintYaw;// monster on this node should face this yaw to face the hint.
};
//=========================================================
// CLink - A link between 2 nodes
//=========================================================
#define bits_LINK_SMALL_HULL ( 1 << 0 )// headcrab box can fit through this connection
#define bits_LINK_HUMAN_HULL ( 1 << 1 )// player box can fit through this connection
#define bits_LINK_LARGE_HULL ( 1 << 2 )// big box can fit through this connection
#define bits_LINK_FLY_HULL ( 1 << 3 )// a flying big box can fit through this connection
#define bits_LINK_DISABLED ( 1 << 4 )// link is not valid when the set
#define NODE_SMALL_HULL 0
#define NODE_HUMAN_HULL 1
#define NODE_LARGE_HULL 2
#define NODE_FLY_HULL 3
#ifndef NODES_H
#define NODES_H
#define NO_NODE -1
#define bits_NODE_GROUP_REALM 1
class CLink
{
public:
int m_iSrcNode;// the node that 'owns' this link ( keeps us from having to make reverse lookups )
int m_iDestNode;// the node on the other end of the link.
entvars_t *m_pLinkEnt;// the entity that blocks this connection (doors, etc)
// m_szLinkEntModelname is not necessarily NULL terminated (so we can store it in a more alignment-friendly 4 bytes)
char m_szLinkEntModelname[ 4 ];// the unique name of the brush model that blocks the connection (this is kept for save/restore)
int m_afLinkInfo;// information about this link
float m_flWeight;// length of the link line segment
};
typedef struct
{
int m_SortedBy[3];
int m_CheckedEvent;
} DIST_INFO;
typedef struct
{
Vector v;
short n; // Nearest node or -1 if no node found.
} CACHE_ENTRY;
//=========================================================
// CGraph
//=========================================================
#define GRAPH_VERSION (int)16// !!!increment this whever graph/node/link classes change, to obsolesce older disk files.
class CGraph
{
public:
// the graph has two flags, and should not be accessed unless both flags are TRUE!
BOOL m_fGraphPresent;// is the graph in memory?
BOOL m_fGraphPointersSet;// are the entity pointers for the graph all set?
BOOL m_fRoutingComplete; // are the optimal routes computed, yet?
CNode *m_pNodes;// pointer to the memory block that contains all node info
CLink *m_pLinkPool;// big list of all node connections
signed char *m_pRouteInfo; // compressed routing information the nodes use.
int m_cNodes;// total number of nodes
int m_cLinks;// total number of links
int m_nRouteInfo; // size of m_pRouteInfo in bytes.
CLink *m_pLinkPool;// big list of all node connections
// Tables for making nearest node lookup faster. SortedBy provided nodes in a
// order of a particular coordinate. Instead of doing a binary search, RangeStart
// and RangeEnd let you get to the part of SortedBy that you are interested in.
//
// Once you have a point of interest, the only way you'll find a closer point is
// if at least one of the coordinates is closer than the ones you have now. So we
// search each range. After the search is exhausted, we know we have the closest
// node.
//
#define CACHE_SIZE 128
#define NUM_RANGES 256
DIST_INFO *m_di; // This is m_cNodes long, but the entries don't correspond to CNode entries.
int m_RangeStart[3][NUM_RANGES];
int m_RangeEnd[3][NUM_RANGES];
float m_flShortest;
int m_iNearest;
int m_minX, m_minY, m_minZ, m_maxX, m_maxY, m_maxZ;
int m_minBoxX, m_minBoxY, m_minBoxZ, m_maxBoxX, m_maxBoxY, m_maxBoxZ;
int m_CheckedCounter;
float m_RegionMin[3], m_RegionMax[3]; // The range of nodes.
CACHE_ENTRY m_Cache[CACHE_SIZE];
int m_HashPrimes[16];
short *m_pHashLinks;
int m_nHashLinks;
// kinda sleazy. In order to allow variety in active idles for monster groups in a room with more than one node,
// we keep track of the last node we searched from and store it here. Subsequent searches by other monsters will pick
// up where the last search stopped.
int m_iLastActiveIdleSearch;
// another such system used to track the search for cover nodes, helps greatly with two monsters trying to get to the same node.
int m_iLastCoverSearch;
// functions to create the graph
int LinkVisibleNodes ( CLink *pLinkPool, FILE *file, int *piBadNode );
int RejectInlineLinks ( CLink *pLinkPool, FILE *file );
int FindShortestPath ( int *piPath, int iStart, int iDest, int iHull, int afCapMask);
int FindNearestNode ( const Vector &vecOrigin, CBaseEntity *pEntity );
int FindNearestNode ( const Vector &vecOrigin, int afNodeTypes );
//int FindNearestLink ( const Vector &vecTestPoint, int *piNearestLink, BOOL *pfAlongLine );
float PathLength( int iStart, int iDest, int iHull, int afCapMask );
int NextNodeInRoute( int iCurrentNode, int iDest, int iHull, int iCap );
enum NODEQUERY { NODEGRAPH_DYNAMIC, NODEGRAPH_STATIC };
// A static query means we're asking about the possiblity of handling this entity at ANY time
// A dynamic query means we're asking about it RIGHT NOW. So we should query the current state
int HandleLinkEnt ( int iNode, entvars_t *pevLinkEnt, int afCapMask, NODEQUERY queryType );
entvars_t* LinkEntForLink ( CLink *pLink, CNode *pNode );
void ShowNodeConnections ( int iNode );
void InitGraph( void );
int AllocNodes ( void );
int CheckNODFile(char *szMapName);
int FLoadGraph(char *szMapName);
int FSaveGraph(char *szMapName);
int FSetGraphPointers(void);
void CheckNode(Vector vecOrigin, int iNode);
void ShowNodeConnections ( int iNode );
int FindNearestNode ( const Vector &vecOrigin, CBaseEntity *pEntity );
int FindNearestNode ( const Vector &vecOrigin, int afNodeTypes );
void BuildRegionTables(void);
void ComputeStaticRoutingTables(void);
void TestRoutingTables(void);
void HashInsert(int iSrcNode, int iDestNode, int iKey);
void HashSearch(int iSrcNode, int iDestNode, int &iKey);
void HashChoosePrimes(int TableSize);
void BuildLinkLookups(void);
void SortNodes(void);
int HullIndex( const CBaseEntity *pEntity ); // what hull the monster uses
int NodeType( const CBaseEntity *pEntity ); // what node type the monster uses
inline int CapIndex( int afCapMask )
{
if (afCapMask & (bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE))
return 1;
return 0;
}
inline CNode &Node( int i )
{
#ifdef _DEBUG
if ( !m_pNodes || i < 0 || i > m_cNodes )
ALERT( at_error, "Bad Node!\n" );
#endif
return m_pNodes[i];
}
inline CLink &Link( int i )
{
#ifdef _DEBUG
if ( !m_pLinkPool || i < 0 || i > m_cLinks )
ALERT( at_error, "Bad link!\n" );
#endif
return m_pLinkPool[i];
}
inline CLink &NodeLink( int iNode, int iLink )
{
return Link( Node( iNode ).m_iFirstLink + iLink );
}
inline CLink &NodeLink( const CNode &node, int iLink )
{
return Link( node.m_iFirstLink + iLink );
}
inline int INodeLink ( int iNode, int iLink )
{
return NodeLink( iNode, iLink ).m_iDestNode;
}
#if 0
inline CNode &SourceNode( int iNode, int iLink )
{
return Node( NodeLink( iNode, iLink ).m_iSrcNode );
}
inline CNode &DestNode( int iNode, int iLink )
{
return Node( NodeLink( iNode, iLink ).m_iDestNode );
}
inline CNode *PNodeLink ( int iNode, int iLink )
{
return &DestNode( iNode, iLink );
}
#endif
};
//=========================================================
// Nodes start out as ents in the level. The node graph
// is built, then these ents are discarded.
//=========================================================
class CNodeEnt : public CBaseEntity
{
void Spawn( void );
void KeyValue( KeyValueData *pkvd );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
short m_sHintType;
short m_sHintActivity;
};
//=========================================================
// CStack - last in, first out.
//=========================================================
class CStack
{
public:
CStack( void );
void Push( int value );
int Pop( void );
int Top( void );
int Empty( void ) { return m_level==0; }
int Size( void ) { return m_level; }
void CopyToArray ( int *piArray );
private:
int m_stack[ MAX_STACK_NODES ];
int m_level;
};
//=========================================================
// CQueue - first in, first out.
//=========================================================
class CQueue
{
public:
CQueue( void );// constructor
inline int Full ( void ) { return ( m_cSize == MAX_STACK_NODES ); }
inline int Empty ( void ) { return ( m_cSize == 0 ); }
//inline int Tail ( void ) { return ( m_queue[ m_tail ] ); }
inline int Size ( void ) { return ( m_cSize ); }
void Insert( int, float );
int Remove( float & );
private:
int m_cSize;
struct tag_QUEUE_NODE
{
int Id;
float Priority;
} m_queue[ MAX_STACK_NODES ];
int m_head;
int m_tail;
};
//=========================================================
// CQueuePriority - Priority queue (smallest item out first).
//
//=========================================================
class CQueuePriority
{
public:
CQueuePriority( void );// constructor
inline int Full ( void ) { return ( m_cSize == MAX_STACK_NODES ); }
inline int Empty ( void ) { return ( m_cSize == 0 ); }
//inline int Tail ( float & ) { return ( m_queue[ m_tail ].Id ); }
inline int Size ( void ) { return ( m_cSize ); }
void Insert( int, float );
int Remove( float &);
private:
int m_cSize;
struct tag_HEAP_NODE
{
int Id;
float Priority;
} m_heap[ MAX_STACK_NODES ];
void Heap_SiftDown(int);
void Heap_SiftUp(void);
};
//=========================================================
// hints - these MUST coincide with the HINTS listed under
// info_node in the FGD file!
//=========================================================
enum
{
HINT_NONE = 0,
HINT_WORLD_DOOR,
HINT_WORLD_WINDOW,
HINT_WORLD_BUTTON,
HINT_WORLD_MACHINERY,
HINT_WORLD_LEDGE,
HINT_WORLD_LIGHT_SOURCE,
HINT_WORLD_HEAT_SOURCE,
HINT_WORLD_BLINKING_LIGHT,
HINT_WORLD_BRIGHT_COLORS,
HINT_WORLD_HUMAN_BLOOD,
HINT_WORLD_ALIEN_BLOOD,
HINT_TACTICAL_EXIT = 100,
HINT_TACTICAL_VANTAGE,
HINT_TACTICAL_AMBUSH,
HINT_STUKA_PERCH = 300,
HINT_STUKA_LANDING
};
extern CGraph WorldGraph;
#endif // NODES_H