//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: Base combat character with no AI // // $NoKeywords: $ //=============================================================================// #ifndef BASECOMBATCHARACTER_H #define BASECOMBATCHARACTER_H #include #include "weapon_proficiency.h" #ifdef _WIN32 #pragma once #endif #include "cbase.h" #include "baseentity.h" #include "baseflex.h" #include "damagemodifier.h" #include "utllinkedlist.h" #include "ai_hull.h" #include "physics_impact_damage.h" class CScriptedTarget; typedef CHandle CBaseCombatWeaponHandle; // ------------------------------------- // Capability Bits // ------------------------------------- enum Capability_t { bits_CAP_MOVE_GROUND = 0x00000001, // walk/run bits_CAP_MOVE_JUMP = 0x00000002, // jump/leap bits_CAP_MOVE_FLY = 0x00000004, // can fly, move all around bits_CAP_MOVE_CLIMB = 0x00000008, // climb ladders bits_CAP_MOVE_SWIM = 0x00000010, // navigate in water // UNDONE - not yet implemented bits_CAP_MOVE_CRAWL = 0x00000020, // crawl // UNDONE - not yet implemented bits_CAP_MOVE_SHOOT = 0x00000040, // tries to shoot weapon while moving bits_CAP_SKIP_NAV_GROUND_CHECK = 0x00000080, // optimization - skips ground tests while computing navigation bits_CAP_USE = 0x00000100, // open doors/push buttons/pull levers //bits_CAP_HEAR = 0x00000200, // can hear forced sounds bits_CAP_AUTO_DOORS = 0x00000400, // can trigger auto doors bits_CAP_OPEN_DOORS = 0x00000800, // can open manual doors bits_CAP_TURN_HEAD = 0x00001000, // can turn head, always bone controller 0 bits_CAP_WEAPON_RANGE_ATTACK1 = 0x00002000, // can do a weapon range attack 1 bits_CAP_WEAPON_RANGE_ATTACK2 = 0x00004000, // can do a weapon range attack 2 bits_CAP_WEAPON_MELEE_ATTACK1 = 0x00008000, // can do a weapon melee attack 1 bits_CAP_WEAPON_MELEE_ATTACK2 = 0x00010000, // can do a weapon melee attack 2 bits_CAP_INNATE_RANGE_ATTACK1 = 0x00020000, // can do a innate range attack 1 bits_CAP_INNATE_RANGE_ATTACK2 = 0x00040000, // can do a innate range attack 1 bits_CAP_INNATE_MELEE_ATTACK1 = 0x00080000, // can do a innate melee attack 1 bits_CAP_INNATE_MELEE_ATTACK2 = 0x00100000, // can do a innate melee attack 1 bits_CAP_USE_WEAPONS = 0x00200000, // can use weapons (non-innate attacks) //bits_CAP_STRAFE = 0x00400000, // strafe ( walk/run sideways) bits_CAP_ANIMATEDFACE = 0x00800000, // has animated eyes/face bits_CAP_USE_SHOT_REGULATOR = 0x01000000, // Uses the shot regulator for range attack1 bits_CAP_FRIENDLY_DMG_IMMUNE = 0x02000000, // don't take damage from npc's that are D_LI bits_CAP_SQUAD = 0x04000000, // can form squads bits_CAP_DUCK = 0x08000000, // cover and reload ducking bits_CAP_NO_HIT_PLAYER = 0x10000000, // don't hit players bits_CAP_AIM_GUN = 0x20000000, // Use arms to aim gun, not just body bits_CAP_NO_HIT_SQUADMATES = 0x40000000, // none bits_CAP_SIMPLE_RADIUS_DAMAGE = 0x80000000, // Do not use robust radius damage model on this character. }; #define bits_CAP_DOORS_GROUP (bits_CAP_AUTO_DOORS | bits_CAP_OPEN_DOORS) #define bits_CAP_RANGE_ATTACK_GROUP (bits_CAP_WEAPON_RANGE_ATTACK1 | bits_CAP_WEAPON_RANGE_ATTACK2) #define bits_CAP_MELEE_ATTACK_GROUP (bits_CAP_WEAPON_MELEE_ATTACK1 | bits_CAP_WEAPON_MELEE_ATTACK2) class CBaseCombatWeapon; enum Disposition_t { D_ER, // Undefined - error D_HT, // Hate D_FR, // Fear D_LI, // Like D_NU // Neutral }; const int DEF_RELATIONSHIP_PRIORITY = INT_MIN; struct Relationship_t { EHANDLE entity; // Relationship to a particular entity Class_T classType; // Relationship to a class CLASS_NONE = not class based (Def. in baseentity.h) Disposition_t disposition; // D_HT (Hate), D_FR (Fear), D_LI (Like), D_NT (Neutral) int priority; // Relative importance of this relationship (higher numbers mean more important) DECLARE_SIMPLE_DATADESC(); }; //----------------------------------------------------------------------------- // Purpose: This should contain all of the combat entry points / functionality // that are common between NPCs and players //----------------------------------------------------------------------------- class CBaseCombatCharacter : public CBaseFlex { DECLARE_CLASS( CBaseCombatCharacter, CBaseFlex ); public: CBaseCombatCharacter(void); ~CBaseCombatCharacter(void); DECLARE_SERVERCLASS(); DECLARE_DATADESC(); DECLARE_PREDICTABLE(); public: void Spawn( void ) { BaseClass::Spawn(); SetBlocksLOS( false ); } virtual void Precache(); virtual const impactdamagetable_t &GetPhysicsImpactDamageTable( void ); int TakeHealth( float flHealth, int bitsDamageType ); void CauseDeath( const CTakeDamageInfo &info ); virtual bool FVisible ( CBaseEntity *pEntity, int traceMask = MASK_OPAQUE, CBaseEntity **ppBlocker = NULL ); virtual bool FVisible( const Vector &vecTarget, int traceMask = MASK_OPAQUE, CBaseEntity **ppBlocker = NULL ) { return BaseClass::FVisible( vecTarget, traceMask, ppBlocker ); } static void ResetVisibilityCache( CBaseCombatCharacter *pBCC = NULL ); virtual bool FInViewCone( CBaseEntity *pEntity ); virtual bool FInViewCone( const Vector &vecSpot ); virtual bool FInAimCone( CBaseEntity *pEntity ); virtual bool FInAimCone( const Vector &vecSpot ); virtual bool ShouldShootMissTarget( CBaseCombatCharacter *pAttacker ); virtual CBaseEntity *FindMissTarget( void ); // Do not call HandleInteraction directly, use DispatchInteraction bool DispatchInteraction( int interactionType, void *data, CBaseCombatCharacter* sourceEnt ) { return ( interactionType > 0 ) ? HandleInteraction( interactionType, data, sourceEnt ) : false; } virtual bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter* sourceEnt ); virtual QAngle BodyAngles(); virtual Vector BodyDirection2D( void ); virtual Vector BodyDirection3D( void ); virtual Vector HeadDirection2D( void ) { return BodyDirection2D( ); }; // No head motion so just return body dir virtual Vector HeadDirection3D( void ) { return BodyDirection2D( ); }; // No head motion so just return body dir virtual Vector EyeDirection2D( void ) { return HeadDirection2D( ); }; // No eye motion so just return head dir virtual Vector EyeDirection3D( void ) { return HeadDirection3D( ); }; // No eye motion so just return head dir virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ); // ----------------------- // Ammo // ----------------------- virtual int GiveAmmo( int iCount, int iAmmoIndex, bool bSuppressSound = false ); int GiveAmmo( int iCount, const char *szName, bool bSuppressSound = false ); void RemoveAmmo( int iCount, int iAmmoIndex ); void RemoveAmmo( int iCount, const char *szName ); void RemoveAllAmmo( ); int GetAmmoCount( int iAmmoIndex ) const; int GetAmmoCount( char *szName ) const; virtual Activity NPC_TranslateActivity( Activity baseAct ); // ----------------------- // Weapons // ----------------------- CBaseCombatWeapon* Weapon_Create( const char *pWeaponName ); virtual Activity Weapon_TranslateActivity( Activity baseAct, bool *pRequired = NULL ); void Weapon_SetActivity( Activity newActivity, float duration ); void Weapon_FrameUpdate( void ); void Weapon_HandleAnimEvent( animevent_t *pEvent ); CBaseCombatWeapon* Weapon_OwnsThisType( const char *pszWeapon, int iSubType = 0 ) const; // True if already owns a weapon of this class virtual bool Weapon_CanUse( CBaseCombatWeapon *pWeapon ); // True is allowed to use this class of weapon virtual void Weapon_Equip( CBaseCombatWeapon *pWeapon ); // Adds weapon to player virtual bool Weapon_EquipAmmoOnly( CBaseCombatWeapon *pWeapon ); // Adds weapon ammo to player, leaves weapon bool Weapon_Detach( CBaseCombatWeapon *pWeapon ); // Clear any pointers to the weapon. virtual void Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget = NULL, const Vector *pVelocity = NULL ); virtual bool Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex = 0 ); // Switch to given weapon if has ammo (false if failed) virtual Vector Weapon_ShootPosition( ); // gun position at current position/orientation bool Weapon_IsOnGround( CBaseCombatWeapon *pWeapon ); CBaseEntity* Weapon_FindUsable( const Vector &range ); // search for a usable weapon in this range virtual bool Weapon_CanSwitchTo(CBaseCombatWeapon *pWeapon); virtual bool Weapon_SlotOccupied( CBaseCombatWeapon *pWeapon ); virtual CBaseCombatWeapon *Weapon_GetSlot( int slot ) const; CBaseCombatWeapon *Weapon_GetWpnForAmmo( int iAmmoIndex ); // For weapon strip void Weapon_DropAll( bool bDisallowWeaponPickup = false ); virtual bool AddPlayerItem( CBaseCombatWeapon *pItem ) { return false; } virtual bool RemovePlayerItem( CBaseCombatWeapon *pItem ) { return false; } virtual bool CanBecomeServerRagdoll( void ) { return true; } // ----------------------- // Damage // ----------------------- // Don't override this for characters, override the per-life-state versions below virtual int OnTakeDamage( const CTakeDamageInfo &info ); // Override these to control how your character takes damage in different states virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info ); virtual int OnTakeDamage_Dying( const CTakeDamageInfo &info ); virtual int OnTakeDamage_Dead( const CTakeDamageInfo &info ); virtual void OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ) {} virtual void NotifyFriendsOfDamage( CBaseEntity *pAttackerEntity ) {} // utility function to calc damage force Vector CalcDamageForceVector( const CTakeDamageInfo &info ); virtual int BloodColor(); virtual Activity GetDeathActivity( void ); virtual bool CorpseGib( const CTakeDamageInfo &info ); virtual void CorpseFade( void ); // Called instead of GibNPC() when gibs are disabled virtual bool HasHumanGibs( void ); virtual bool HasAlienGibs( void ); virtual bool ShouldGib( const CTakeDamageInfo &info ) { return false; } // Always ragdoll, unless specified by the leaf class float GetDamageAccumulator() { return m_flDamageAccumulator; } int GetDamageCount( void ) { return m_iDamageCount; } // # of times NPC has been damaged. used for tracking 1-shot kills. // Character killed (only fired once) virtual void Event_Killed( const CTakeDamageInfo &info ); // Killed a character void InputKilledNPC( inputdata_t &inputdata ); virtual void OnKilledNPC( CBaseCombatCharacter *pKilled ) {}; // Exactly one of these happens immediately after killed (gibbed may happen later when the corpse gibs) // Character gibbed or faded out (violence controls) (only fired once) // returns true if gibs were spawned virtual bool Event_Gibbed( const CTakeDamageInfo &info ); // Character entered the dying state without being gibbed (only fired once) virtual void Event_Dying( void ); // character died and should become a ragdoll now // return true if converted to a ragdoll, false to use AI death virtual bool BecomeRagdoll( const CTakeDamageInfo &info, const Vector &forceVector ); virtual void FixupBurningServerRagdoll( CBaseEntity *pRagdoll ); virtual bool BecomeRagdollBoogie( CBaseEntity *pKiller, const Vector &forceVector, float duration, int flags ); CBaseEntity *FindHealthItem( const Vector &vecPosition, const Vector &range ); virtual CBaseEntity *CheckTraceHullAttack( float flDist, const Vector &mins, const Vector &maxs, int iDamage, int iDmgType, float forceScale = 1.0f, bool bDamageAnyNPC = false ); virtual CBaseEntity *CheckTraceHullAttack( const Vector &vStart, const Vector &vEnd, const Vector &mins, const Vector &maxs, int iDamage, int iDmgType, float flForceScale = 1.0f, bool bDamageAnyNPC = false ); virtual CBaseCombatCharacter *MyCombatCharacterPointer( void ) { return this; } // VPHYSICS virtual void VPhysicsShadowCollision( int index, gamevcollisionevent_t *pEvent ); virtual void VPhysicsUpdate( IPhysicsObject *pPhysics ); float CalculatePhysicsStressDamage( vphysics_objectstress_t *pStressOut, IPhysicsObject *pPhysics ); void ApplyStressDamage( IPhysicsObject *pPhysics, bool bRequireLargeObject ); virtual void PushawayTouch( CBaseEntity *pOther ) {} void SetImpactEnergyScale( float fScale ) { m_impactEnergyScale = fScale; } virtual void UpdateOnRemove( void ); virtual Disposition_t IRelationType( CBaseEntity *pTarget ); virtual int IRelationPriority( CBaseEntity *pTarget ); virtual void SetLightingOriginRelative( CBaseEntity *pLightingOrigin ); protected: Relationship_t *FindEntityRelationship( CBaseEntity *pTarget ); public: // Vehicle queries virtual bool IsInAVehicle( void ) { return false; } virtual IServerVehicle *GetVehicle( void ) { return NULL; } virtual CBaseEntity *GetVehicleEntity( void ) { return NULL; } // Blood color (see BLOOD_COLOR_* macros in baseentity.h) void SetBloodColor( int nBloodColor ); // Weapons.. CBaseCombatWeapon* GetActiveWeapon() const; int WeaponCount() const; CBaseCombatWeapon* GetWeapon( int i ) const; bool RemoveWeapon( CBaseCombatWeapon *pWeapon ); void RemoveAllWeapons(); WeaponProficiency_t GetCurrentWeaponProficiency() { return m_CurrentWeaponProficiency; } void SetCurrentWeaponProficiency( WeaponProficiency_t iProficiency ) { m_CurrentWeaponProficiency = iProficiency; } virtual WeaponProficiency_t CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ); virtual Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget = NULL ); virtual float GetSpreadBias( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ); virtual void DoMuzzleFlash(); // Interactions static void InitInteractionSystem(); // Relationships static void AllocateDefaultRelationships( ); static void SetDefaultRelationship( Class_T nClass, Class_T nClassTarget, Disposition_t nDisposition, int nPriority ); Disposition_t GetDefaultRelationshipDisposition( Class_T nClassTarget ); virtual void AddEntityRelationship( CBaseEntity *pEntity, Disposition_t nDisposition, int nPriority ); virtual bool RemoveEntityRelationship( CBaseEntity *pEntity ); virtual void AddClassRelationship( Class_T nClass, Disposition_t nDisposition, int nPriority ); // Nav hull type Hull_t GetHullType() const { return m_eHull; } void SetHullType( Hull_t hullType ) { m_eHull = hullType; } // FIXME: The following 3 methods are backdoor hack methods // This is a sort of hack back-door only used by physgun! void SetAmmoCount( int iCount, int iAmmoIndex ); // This is a hack to blat out the current active weapon... // Used by weapon_slam + game_ui void SetActiveWeapon( CBaseCombatWeapon *pNewWeapon ); void ClearActiveWeapon() { SetActiveWeapon( NULL ); } virtual void OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon ) {} // I can't use my current weapon anymore. Switch me to the next best weapon. bool SwitchToNextBestWeapon(CBaseCombatWeapon *pCurrent); // This is a hack to copy the relationship strings used by monstermaker void SetRelationshipString( string_t theString ) { m_RelationshipString = theString; } float GetNextAttack() const { return m_flNextAttack; } void SetNextAttack( float flWait ) { m_flNextAttack = flWait; } bool m_bForceServerRagdoll; public: // returns the last body region that took damage int LastHitGroup() const { return m_LastHitGroup; } protected: void SetLastHitGroup( int nHitGroup ) { m_LastHitGroup = nHitGroup; } public: CNetworkVar( float, m_flNextAttack ); // cannot attack again until this time protected: private: Hull_t m_eHull; protected: int m_bloodColor; // color of blood particless // ------------------- // combat ability data // ------------------- float m_flFieldOfView; // cosine of field of view for this character Vector m_HackedGunPos; // HACK until we can query end of gun string_t m_RelationshipString; // Used to load up relationship keyvalues float m_impactEnergyScale;// scale the amount of energy used to calculate damage this ent takes due to physics public: static int GetInteractionID(); // Returns the next interaction # private: // For weapon strip void ThrowDirForWeaponStrip( CBaseCombatWeapon *pWeapon, const Vector &vecForward, Vector *pVecThrowDir ); void DropWeaponForWeaponStrip( CBaseCombatWeapon *pWeapon, const Vector &vecForward, const QAngle &vecAngles, float flDiameter ); friend class CScriptedTarget; // needs to access GetInteractionID() static int m_lastInteraction; // Last registered interaction # static Relationship_t** m_DefaultRelationship; // attack/damage int m_LastHitGroup; // the last body region that took damage float m_flDamageAccumulator; // so very small amounts of damage do not get lost. int m_iDamageCount; // # of times NPC has been damaged. used for tracking 1-shot kills. // Weapon proficiency gets calculated each time an NPC changes his weapon, and then // cached off as the CurrentWeaponProficiency. WeaponProficiency_t m_CurrentWeaponProficiency; // --------------- // Relationships // --------------- CUtlVector m_Relationship; // Array of relationships // shared ammo slots CNetworkArrayForDerived( int, m_iAmmo, MAX_AMMO_SLOTS ); // Usable character items CNetworkArray( CBaseCombatWeaponHandle, m_hMyWeapons, MAX_WEAPONS ); CNetworkHandle( CBaseCombatWeapon, m_hActiveWeapon ); friend class CCleanupDefaultRelationShips; }; EXTERN_SEND_TABLE(DT_BaseCombatCharacter); void RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrc, float flRadius, int iClassIgnore, CBaseEntity *pEntityIgnore ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class CTraceFilterMelee : public CTraceFilterEntitiesOnly { public: // It does have a base, but we'll never network anything below here.. DECLARE_CLASS_NOBASE( CTraceFilterMelee ); CTraceFilterMelee( const IHandleEntity *passentity, int collisionGroup, CTakeDamageInfo *dmgInfo, float flForceScale, bool bDamageAnyNPC ) : m_pPassEnt(passentity), m_collisionGroup(collisionGroup), m_dmgInfo(dmgInfo), m_pHit(NULL), m_flForceScale(flForceScale), m_bDamageAnyNPC(bDamageAnyNPC) { } virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); public: const IHandleEntity *m_pPassEnt; int m_collisionGroup; CTakeDamageInfo *m_dmgInfo; CBaseEntity *m_pHit; float m_flForceScale; bool m_bDamageAnyNPC; }; #endif // BASECOMBATCHARACTER_H