//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $Date: $ // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "ai_basenpc.h" #include "ai_default.h" #include "ai_hull.h" #include "ai_squadslot.h" #include "ai_squad.h" #include "bitstring.h" #include "entitylist.h" #include "ai_hint.h" #include "IEffects.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Purpose: If requested slot is available return true and take the slot // Otherwise return false // Input : // Output : //----------------------------------------------------------------------------- bool CAI_BaseNPC::OccupyStrategySlot( int squadSlotID ) { return OccupyStrategySlotRange( squadSlotID, squadSlotID ); } //----------------------------------------------------------------------------- bool CAI_BaseNPC::OccupyStrategySlotRange( int slotIDStart, int slotIDEnd ) { // If I'm not in a squad a I don't fill slots return ( !m_pSquad || m_pSquad->OccupyStrategySlotRange( GetEnemy(), slotIDStart, slotIDEnd, &m_iMySquadSlot ) ); } //----------------------------------------------------------------------------- // Returns true if all in the range are full //----------------------------------------------------------------------------- bool CAI_BaseNPC::IsStrategySlotRangeOccupied( int slotIDStart, int slotIDEnd ) { return m_pSquad && m_pSquad->IsStrategySlotRangeOccupied( GetEnemy(), slotIDStart, slotIDEnd ); } //========================================================= // HasStrategySlot //========================================================= bool CAI_BaseNPC::HasStrategySlot( int squadSlotID ) { // If I wasn't taking up a squad slot I'm done return (m_iMySquadSlot == squadSlotID); } bool CAI_BaseNPC::HasStrategySlotRange( int slotIDStart, int slotIDEnd ) { // If I wasn't taking up a squad slot I'm done if (m_iMySquadSlot < slotIDStart || m_iMySquadSlot > slotIDEnd) { return false; } return true; } //========================================================= // VacateSlot //========================================================= void CAI_BaseNPC::VacateStrategySlot(void) { if (m_pSquad) { m_pSquad->VacateStrategySlot(GetEnemy(), m_iMySquadSlot); m_iMySquadSlot = SQUAD_SLOT_NONE; } } //------------------------------------------------------------------------------ // Purpose : Is cover node valid // Input : // Output : //------------------------------------------------------------------------------ bool CAI_BaseNPC::IsValidCover( const Vector &vecCoverLocation, CAI_Hint const *pHint ) { // firstly, limit choices to hint groups string_t iszHint = GetHintGroup(); char *pszHint = (char *)STRING(iszHint); if ((iszHint != NULL_STRING) && (pszHint[0] != '\0')) { if (!pHint || pHint->GetGroup() != GetHintGroup()) { return false; } } /* // If I'm in a squad don't pick cover node it other squad member // is already nearby if (m_pSquad) { return m_pSquad->IsValidCover( vecCoverLocation, pHint ); } */ // UNDONE: Do we really need this test? // ---------------------------------------------------------------- // Make sure my hull can fit at this node before accepting it. // Could be another NPC there or it could be blocked // ---------------------------------------------------------------- // FIXME: shouldn't this see that if I crouch behind it it'll be safe? Vector startPos = vecCoverLocation; startPos.z -= GetHullMins().z; // Move hull bottom up to node Vector endPos = startPos; endPos.z += 0.01; trace_t tr; AI_TraceEntity( this, vecCoverLocation, endPos, GetAITraceMask(), &tr ); if (tr.startsolid) { return false; } return true; } //----------------------------------------------------------------------------- // Purpose: Is squad member in my way from shooting here // Input : // Output : //----------------------------------------------------------------------------- bool CAI_BaseNPC::IsValidShootPosition( const Vector &vecShootLocation, CAI_Node *pNode, CAI_Hint const *pHint ) { // limit choices to hint groups if (GetHintGroup() != NULL_STRING) { if (!pHint || pHint->GetGroup() != GetHintGroup()) { if ( ( vecShootLocation - GetAbsOrigin() ).Length2DSqr() > 1 ) return false; } } return true; } //----------------------------------------------------------------------------- bool CAI_BaseNPC::IsSquadmateInSpread( const Vector &sourcePos, const Vector &targetPos, float flSpread, float maxDistOffCenter ) { if( !m_pSquad ) return false; AISquadIter_t iter; CAI_BaseNPC *pSquadmate = m_pSquad->GetFirstMember( &iter ); while ( pSquadmate ) { // Ignore squadmates that can't take damage. This is primarily to ignore npc_enemyfinders. if ( pSquadmate->m_takedamage != DAMAGE_NO ) { if ( pSquadmate != this ) { if ( PointInSpread( pSquadmate, sourcePos, targetPos, pSquadmate->GetAbsOrigin(), flSpread, maxDistOffCenter ) ) return true; } } pSquadmate = m_pSquad->GetNextMember( &iter ); } return false; } //----------------------------------------------------------------------------- void CAI_BaseNPC::AddToSquad( string_t name ) { g_AI_SquadManager.FindCreateSquad( this, name ); } //----------------------------------------------------------------------------- void CAI_BaseNPC::SetSquad( CAI_Squad *pSquad ) { if ( m_pSquad == pSquad ) { return; } if ( m_pSquad && m_iMySquadSlot != SQUAD_SLOT_NONE) { VacateStrategySlot(); } m_pSquad = pSquad; } //----------------------------------------------------------------------------- void CAI_BaseNPC::RemoveFromSquad() { if ( m_pSquad ) { m_pSquad->RemoveFromSquad( this, false ); m_pSquad = NULL; } } //----------------------------------------------------------------------------- void CAI_BaseNPC::CheckSquad() { if( !IsInSquad() ) return; if( !GetSquad()->IsLeader(this) ) return; if( VPhysicsGetObject() != NULL && (VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) ) { // I AM the leader, and I'm currently being held. This will screw up all of my relationship checks // if I'm a manhack or a rollermine, so just bomb out and try next time. return; } AISquadIter_t iter; CAI_BaseNPC *pSquadmate = m_pSquad->GetFirstMember( &iter ); while ( pSquadmate ) { if( IRelationType(pSquadmate) < D_LI ) { bool bWarn = true; // Rollermines and manhacks set their Class to NONE when held by the player, which makes all of // their squadmates complain that an enemy is in the squad. Suppress this. if( pSquadmate->VPhysicsGetObject() != NULL ) { if (pSquadmate->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) { bWarn = false; } } if( bWarn ) { Warning( "ERROR: Squad '%s' has enemies in it!\n", GetSquad()->GetName() ); Warning( "%s doesn't like %s\n\n", GetDebugName(), pSquadmate->GetDebugName() ); } } pSquadmate = m_pSquad->GetNextMember( &iter ); } } //----------------------------------------------------------------------------- // Returns the number of weapons of this type currently owned by squad members. //----------------------------------------------------------------------------- int CAI_BaseNPC::NumWeaponsInSquad( const char *pszWeaponClassname ) { string_t iszWeaponClassname = FindPooledString( pszWeaponClassname ); if( !GetSquad() ) { if( GetActiveWeapon() && GetActiveWeapon()->m_iClassname == iszWeaponClassname ) { // I'm alone in my squad, but I do have this weapon. return 1; } return 0; } int count = 0; AISquadIter_t iter; CAI_BaseNPC *pSquadmate = m_pSquad->GetFirstMember( &iter ); while ( pSquadmate ) { if( pSquadmate->GetActiveWeapon() && pSquadmate->GetActiveWeapon()->m_iClassname == iszWeaponClassname ) { count++; } pSquadmate = m_pSquad->GetNextMember( &iter ); } return count; }