You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
382 lines
12 KiB
382 lines
12 KiB
/*** |
|
* |
|
* Copyright (c) 1996-2002, Valve LLC. All rights reserved. |
|
* |
|
* This product contains software technology licensed from Id |
|
* 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. |
|
* |
|
****/ |
|
//========================================================= |
|
// nodes.h |
|
//========================================================= |
|
#pragma once |
|
#if !defined(NODES_H) |
|
#define 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 |
|
|
|
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_RETAIL 16 // Retail Half-Life graph version. Don't increment this |
|
#if XASH_64BIT |
|
#define _GRAPH_VERSION (16 * 10) |
|
#else |
|
#define _GRAPH_VERSION (16) // !!!increment this whenever graph/node/link classes change, to obsolesce older disk files. |
|
#endif |
|
#define GRAPH_VERSION (int)_GRAPH_VERSION |
|
#define GRAPH_VERSION_RETAIL (int)_GRAPH_VERSION_RETAIL |
|
|
|
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. |
|
|
|
// 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(const char *szMapName); |
|
int FLoadGraph(const char *szMapName); |
|
int FSaveGraph(const char *szMapName); |
|
int FSetGraphPointers(void); |
|
void CheckNode(Vector vecOrigin, int iNode); |
|
|
|
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 ) |
|
{ |
|
#if _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 ) |
|
{ |
|
#if _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
|
|
|