//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: Base combat character with no AI // // $Workfile: $ // $Date: $ // //----------------------------------------------------------------------------- // $Log: $ // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "ai_hull.h" #include "ai_node.h" #include "ai_link.h" #include "ai_network.h" #include "ai_initutils.h" #include "bitstring.h" #include "ai_basenpc.h" #include "ai_navigator.h" #include "ai_moveprobe.h" #include "fmtstr.h" #include "game.h" #include "ai_networkmanager.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- CAI_Link *CAI_Node::GetLink( int destNodeId ) { // Now make sure this node still has a link to the destID for ( int link = 0; link < NumLinks(); link++ ) { // If we find the link the dynamic link is valid if ( m_Links[link]->DestNodeID(m_iID) == destNodeId ) { return m_Links[link]; } } return NULL; } //----------------------------------------------------------------------------- // Purpose: Add a link to this node // Input : // Output : //----------------------------------------------------------------------------- void CAI_Node::AddLink(CAI_Link *newLink) { if ( NumLinks() == AI_MAX_NODE_LINKS ) { DevMsg( "Node %d has too many links\n", m_iID ); return; } #ifdef _DEBUG for (int link=0;linkm_iDestID == m_iID || newLink->m_iSrcID == m_iID, "Added link to node that doesn't reference the node" ); #endif m_Links.AddToTail( newLink ); } //----------------------------------------------------------------------------- // Purpose: Returns link if node has a link to node of the given nNodeID. // Otherwise returns NULL // Input : // Output : //----------------------------------------------------------------------------- CAI_Link* CAI_Node::HasLink(int nNodeID) { for (int link=0;linkDestNodeID(m_iID) == nNodeID) { return m_Links[link]; } } return NULL; } //------------------------------------------------------------------------------ // Purpose : Called before GetShuffeledLinks to change the order in which // links are returned // Input : // Output : //------------------------------------------------------------------------------ void CAI_Node::ShuffleLinks(void) { m_iFirstShuffledLink++; if (m_iFirstShuffledLink >= NumLinks()) { m_iFirstShuffledLink = 0; } } //------------------------------------------------------------------------------ // Purpose : Used to get links in different order each time. // Call ShuffleLinks() first // Input : // Output : //------------------------------------------------------------------------------ CAI_Link* CAI_Node::GetShuffeledLink(int nNum) { int nLinkID = m_iFirstShuffledLink + nNum; if (nLinkID >= NumLinks()) { nLinkID -= NumLinks(); } return m_Links[nLinkID]; } //---------------------------------------------------------------------------------- // Purpose: Returns z value of floor below given point (up to fMaxDrop inches below) // Input : // Output : //---------------------------------------------------------------------------------- float GetFloorZ(const Vector &origin, float fMaxDrop) { // trace to the ground, then pop up 8 units and place node there to make it // easier for them to connect (think stairs, chairs, and bumps in the floor). // After the routing is done, push them back down. // trace_t tr; AI_TraceLine ( origin, origin - Vector ( 0, 0, fMaxDrop ), MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); // This trace is ONLY used if we hit an entity flagged with FL_WORLDBRUSH trace_t trEnt; AI_TraceLine ( origin, origin - Vector ( 0, 0, fMaxDrop ), MASK_NPCSOLID, NULL, COLLISION_GROUP_NONE, &trEnt ); // Did we hit something closer than the floor? if ( trEnt.fraction < tr.fraction ) { // If it was a world brush entity, copy the node location if ( trEnt.m_pEnt ) { CBaseEntity *e = trEnt.m_pEnt; if ( e && ( e->GetFlags() & FL_WORLDBRUSH ) ) { tr.endpos = trEnt.endpos; } } } return tr.endpos.z; } //----------------------------------------------------------------------------- // Purpose: Returns z value of floor below given point (up to 384 inches below) // Input : // Output : //----------------------------------------------------------------------------- float GetFloorZ(const Vector &origin) { return GetFloorZ(origin, 384); } //----------------------------------------------------------------------------- // Purpose: Returns distance of floor from the origin (up to 384 inches) // Input : // Output : //----------------------------------------------------------------------------- float GetFloorDistance(const Vector &origin) { return (origin.z - GetFloorZ(origin)); } //----------------------------------------------------------------------------- // Purpose: Climb nodes are centered over the climb surface, the must be // shifted away from the climb surface according to the hull size // of the NPC using the climb // Input : // Output : //----------------------------------------------------------------------------- Vector CAI_Node::GetPosition(int hull) const { if (m_eNodeType == NODE_CLIMB) { // Shift by the length of the hull and some small fudge float shift = (0.5*NAI_Hull::Length(hull)) + (NODE_CLIMB_OFFSET); Vector offsetDir = Vector(cos(DEG2RAD(m_flYaw)),sin(DEG2RAD(m_flYaw)),0); Vector origin; if (m_eNodeInfo & bits_NODE_CLIMB_OFF_FORWARD) { origin = m_vOrigin + (shift * offsetDir); } else if (m_eNodeInfo & bits_NODE_CLIMB_OFF_LEFT) { Vector upDir(0,0,1); Vector leftDir; CrossProduct( offsetDir, upDir, leftDir); origin = m_vOrigin - (2 * shift * leftDir) - (shift * offsetDir); } else if (m_eNodeInfo & bits_NODE_CLIMB_OFF_RIGHT) { Vector upDir(0,0,1); Vector leftDir; CrossProduct( offsetDir, upDir, leftDir); origin = m_vOrigin + (2 * shift * leftDir) - (shift * offsetDir); } else { origin = m_vOrigin - (shift * offsetDir); } return Vector( origin.x, origin.y, origin.z + m_flVOffset[hull] ); } else if (m_eNodeType == NODE_GROUND) { // this is the floor resting position of this hull, adjusted to account for mins.z return Vector( m_vOrigin.x, m_vOrigin.y, m_vOrigin.z + m_flVOffset[hull] ); } else { return m_vOrigin; } } //----------------------------------------------------------------------------- // Purpose: Constructor // Input : // Output : //----------------------------------------------------------------------------- CAI_Node::CAI_Node( int id, const Vector &origin, float yaw ) : m_Links( 0, 4 ) { m_vOrigin = origin; m_iID = id; for (int i = 0; i < NUM_HULLS; i++) { m_flVOffset[i] = 0.0; } m_eNodeType = NODE_GROUND; m_eNodeInfo = 0; m_iFirstShuffledLink = 0; m_pHint = NULL; m_flYaw = yaw; m_flNextUseTime = 0; m_zone = AI_NODE_ZONE_UNKNOWN; };