//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #ifndef NPC_BARNACLE_H #define NPC_BARNACLE_H #ifdef _WIN32 #pragma once #endif #include "ai_basenpc.h" #include "physics_prop_ragdoll.h" #include "studio.h" class CNPC_Barnacle; #define BARNACLE_PULL_SPEED 80 #define BARNACLE_KILL_VICTIM_DELAY \ 5 // how many seconds after pulling prey in to gib them. // Tongue #define BARNACLE_TONGUE_POINTS 8 #define BARNACLE_MIN_PULL_TIME 3.0f #define NUM_BARNACLE_GIBS 4 #define SF_BARNACLE_CHEAP_DEATH (1 << 16) // Don't spawn as many gibs #define SF_BARNACLE_AMBUSH \ (1 << 17) // Start with tongue retracted and wait for input. // when true, causes the barnacle's visible tongue to offset // from the physical one when pulling the player. #define BARNACLE_USE_TONGUE_OFFSET 1 //----------------------------------------------------------------------------- // Purpose: This is the entity we place at the top & bottom of the tongue, to // create a vphysics spring //----------------------------------------------------------------------------- class CBarnacleTongueTip : public CBaseAnimating { DECLARE_CLASS(CBarnacleTongueTip, CBaseAnimating); public: DECLARE_DATADESC(); virtual void Spawn(void); virtual void Precache(void); virtual void UpdateOnRemove(); virtual void VPhysicsUpdate(IPhysicsObject *pPhysics); virtual int UpdateTransmitState(void); bool CreateSpring(CBaseAnimating *pTongueRoot); static CBarnacleTongueTip *CreateTongueTip(CNPC_Barnacle *pBarnacle, CBaseAnimating *pTongueRoot, const Vector &vecOrigin, const QAngle &vecAngles); static CBarnacleTongueTip *CreateTongueRoot(const Vector &vecOrigin, const QAngle &vecAngles); IPhysicsSpring *m_pSpring; private: CHandle m_hBarnacle; }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class CNPC_Barnacle : public CAI_BaseNPC { DECLARE_CLASS(CNPC_Barnacle, CAI_BaseNPC); public: DECLARE_SERVERCLASS(); DECLARE_DATADESC(); CNPC_Barnacle(); ~CNPC_Barnacle(); void Spawn(void); virtual void Activate(void); void Precache(void); Class_T Classify(void); virtual void ComputeWorldSpaceSurroundingBox(Vector *pVecWorldMins, Vector *pVecWorldMaxs); virtual void HandleAnimEvent(animevent_t *pEvent); void Event_Killed(const CTakeDamageInfo &info); int OnTakeDamage_Alive(const CTakeDamageInfo &info); void PlayerHasIlluminatedNPC(CBasePlayer *pPlayer, float flDot); // The tongue's vphysics updated void OnTongueTipUpdated(); private: void SetAltitude(float flAltitude); void SpawnDeathGibs(void); void InitTonguePosition(void); CBaseEntity *TongueTouchEnt(float *pflLength); void BarnacleThink(void); void SwallowPrey(void); void WaitTillDead(void); void AttachTongueToTarget(CBaseEntity *pTouchEnt, Vector vecGrabPos); CRagdollProp *AttachRagdollToTongue(CBaseAnimating *pAnimating); void RemoveRagdoll(bool bDestroyRagdoll); void LostPrey(bool bRemoveRagdoll); void BitePrey(void); // Updates the tongue length void UpdateTongue(void); // Spit out the prey; add physics force! void SpitPrey(); void SprayBlood(); // What type of enemy do we have? bool IsEnemyAPlayer(); bool IsEnemyARagdoll(); bool IsEnemyAPhysicsObject(); bool IsEnemyAnNPC(); bool CanPickup(CBaseCombatCharacter *pBCC); // Allows the ragdoll to settle before biting it bool WaitForRagdollToSettle(float flBiteZOffset); // Allows the physics prop to settle before biting it bool WaitForPhysicsObjectToSettle(float flBiteZOffset); // Play a scream right before biting void PlayLiftingScream(float flBiteZOffset); // Pulls the prey upward toward the mouth void PullEnemyTorwardsMouth(bool bAdjustEnemyOrigin); // Lift the prey stuck to our tongue up towards our mouth void LiftPrey(void); void LiftPlayer(float flBiteZOffset); void LiftRagdoll(float flBiteZOffset); void LiftPhysicsObject(float flBiteZOffset); void LiftNPC(float flBiteZOffset); void UpdatePlayerConstraint(void); void InputDropTongue(inputdata_t &inputdata); void InputSetDropTongueSpeed(inputdata_t &inputdata); void DropTongue(void); #if HL2_EPISODIC /// Decides whether something should poison the barnacle upon eating static bool IsPoisonous(CBaseEntity *pVictim); void InputLetGo(inputdata_t &inputdata); COutputEHANDLE m_OnGrab, m_OnRelease; const impactdamagetable_t &GetPhysicsImpactDamageTable(void); #endif CNetworkVar(float, m_flAltitude); int m_cGibs; // barnacle loads up on gibs each time it kills something. bool m_bLiftingPrey; // true when the prey's on the tongue and being lifted // to the mouth bool m_bSwallowingPrey; // if it's a human, true while the barnacle chews // it and swallows it whole. float m_flDigestFinish; // time at which we've finished digesting something // we chewed float m_flVictimHeight; int m_iGrabbedBoneIndex; bool m_bPlayedPullSound; bool m_bPlayerWasStanding; static const char *m_szGibNames[NUM_BARNACLE_GIBS]; // Tongue spline points CNetworkVar(Vector, m_vecRoot); CNetworkVar(Vector, m_vecTip); CNetworkVar(Vector, m_vecTipDrawOffset); // Tongue tip & root CHandle m_hTongueRoot; CHandle m_hTongueTip; CHandle m_hRagdoll; matrix3x4_t m_pRagdollBones[MAXSTUDIOBONES]; IPhysicsConstraint *m_pConstraint; float m_flRestUnitsAboveGround; int m_nSpitAttachment; EHANDLE m_hLastSpitEnemy; int m_nShakeCount; float m_flNextBloodTime; #ifndef _XBOX int m_nBloodColor; #endif Vector m_vecBloodPos; float m_flBarnaclePullSpeed; float m_flLocalTimer; Vector m_vLastEnemyPos; float m_flLastPull; CSimpleSimTimer m_StuckTimer; bool m_bSwallowingBomb; #ifdef HL2_EPISODIC bool m_bSwallowingPoison; #endif #if BARNACLE_USE_TONGUE_OFFSET // Static because only one barnacle can be holding the player // at a time, and because it's not really a big deal if it // resets to zero after reload. const static Vector m_svPlayerHeldTipOffset; #endif DEFINE_CUSTOM_AI; }; //----------------------------------------------------------------------------- // What type of enemy do we have? //----------------------------------------------------------------------------- inline bool CNPC_Barnacle::IsEnemyAPlayer() { return GetEnemy() && GetEnemy()->IsPlayer(); } inline bool CNPC_Barnacle::IsEnemyARagdoll() { return m_hRagdoll != NULL; } inline bool CNPC_Barnacle::IsEnemyAPhysicsObject() { return !m_hRagdoll && GetEnemy() && !GetEnemy()->IsPlayer() && !GetEnemy()->MyNPCPointer() && (GetEnemy()->GetMoveType() == MOVETYPE_VPHYSICS); } inline bool CNPC_Barnacle::IsEnemyAnNPC() { return !IsEnemyARagdoll() && (GetEnemy()->MyNPCPointer() != NULL); } #endif // NPC_BARNACLE_H