summaryrefslogtreecommitdiff
path: root/game/server/hl1/hl1_npc_barnacle.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl1/hl1_npc_barnacle.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/hl1/hl1_npc_barnacle.cpp')
-rw-r--r--game/server/hl1/hl1_npc_barnacle.cpp516
1 files changed, 516 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_barnacle.cpp b/game/server/hl1/hl1_npc_barnacle.cpp
new file mode 100644
index 0000000..05524cb
--- /dev/null
+++ b/game/server/hl1/hl1_npc_barnacle.cpp
@@ -0,0 +1,516 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: barnacle - stationary ceiling mounted 'fishing' monster
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "hl1_npc_barnacle.h"
+#include "npcevent.h"
+#include "gib.h"
+#include "ai_default.h"
+#include "activitylist.h"
+#include "hl2_player.h"
+#include "vstdlib/random.h"
+#include "physics_saverestore.h"
+#include "vcollide_parse.h"
+#include "engine/IEngineSound.h"
+
+ConVar sk_barnacle_health( "sk_barnacle_health","25");
+
+//-----------------------------------------------------------------------------
+// Private activities.
+//-----------------------------------------------------------------------------
+static int ACT_EAT = 0;
+
+//-----------------------------------------------------------------------------
+// Interactions
+//-----------------------------------------------------------------------------
+int g_interactionBarnacleVictimDangle = 0;
+int g_interactionBarnacleVictimReleased = 0;
+int g_interactionBarnacleVictimGrab = 0;
+
+LINK_ENTITY_TO_CLASS( monster_barnacle, CNPC_Barnacle );
+IMPLEMENT_CUSTOM_AI( monster_barnacle, CNPC_Barnacle );
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize the custom schedules
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CNPC_Barnacle::InitCustomSchedules(void)
+{
+ INIT_CUSTOM_AI(CNPC_Barnacle);
+
+ ADD_CUSTOM_ACTIVITY(CNPC_Barnacle, ACT_EAT);
+
+ g_interactionBarnacleVictimDangle = CBaseCombatCharacter::GetInteractionID();
+ g_interactionBarnacleVictimReleased = CBaseCombatCharacter::GetInteractionID();
+ g_interactionBarnacleVictimGrab = CBaseCombatCharacter::GetInteractionID();
+}
+
+
+BEGIN_DATADESC( CNPC_Barnacle )
+
+ DEFINE_FIELD( m_flAltitude, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flKillVictimTime, FIELD_TIME ),
+ DEFINE_FIELD( m_cGibs, FIELD_INTEGER ),// barnacle loads up on gibs each time it kills something.
+ DEFINE_FIELD( m_fLiftingPrey, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flTongueAdj, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flIgnoreTouchesUntil, FIELD_TIME ),
+
+ // Function pointers
+ DEFINE_THINKFUNC( BarnacleThink ),
+ DEFINE_THINKFUNC( WaitTillDead ),
+END_DATADESC()
+
+
+//=========================================================
+// Classify - indicates this monster's place in the
+// relationship table.
+//=========================================================
+Class_T CNPC_Barnacle::Classify ( void )
+{
+ return CLASS_ALIEN_MONSTER;
+}
+
+//=========================================================
+// HandleAnimEvent - catches the monster-specific messages
+// that occur when tagged animation frames are played.
+//
+// Returns number of events handled, 0 if none.
+//=========================================================
+void CNPC_Barnacle::HandleAnimEvent( animevent_t *pEvent )
+{
+ switch( pEvent->event )
+ {
+ case BARNACLE_AE_PUKEGIB:
+ CGib::SpawnRandomGibs( this, 1, GIB_HUMAN );
+ break;
+ default:
+ BaseClass::HandleAnimEvent( pEvent );
+ break;
+ }
+}
+
+//=========================================================
+// Spawn
+//=========================================================
+void CNPC_Barnacle::Spawn()
+{
+ Precache( );
+
+ SetModel( "models/barnacle.mdl" );
+ UTIL_SetSize( this, Vector(-16, -16, -32), Vector(16, 16, 0) );
+
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_NOT_STANDABLE );
+ SetMoveType( MOVETYPE_NONE );
+ SetBloodColor( BLOOD_COLOR_GREEN );
+ m_iHealth = sk_barnacle_health.GetFloat();
+ m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
+ m_NPCState = NPC_STATE_NONE;
+ m_flKillVictimTime = 0;
+ m_cGibs = 0;
+ m_fLiftingPrey = FALSE;
+ m_takedamage = DAMAGE_YES;
+
+ InitBoneControllers();
+ InitTonguePosition();
+
+ // set eye position
+ SetDefaultEyeOffset();
+
+ SetActivity ( ACT_IDLE );
+
+ SetThink ( &CNPC_Barnacle::BarnacleThink );
+ SetNextThink( gpGlobals->curtime + 0.5f );
+ //Do not have a shadow
+ AddEffects( EF_NOSHADOW );
+
+ m_flIgnoreTouchesUntil = gpGlobals->curtime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+int CNPC_Barnacle::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
+{
+ CTakeDamageInfo info = inputInfo;
+ if ( info.GetDamageType() & DMG_CLUB )
+ {
+ info.SetDamage( m_iHealth );
+ }
+
+ return BaseClass::OnTakeDamage_Alive( info );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize tongue position when first spawned
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CNPC_Barnacle::InitTonguePosition( void )
+{
+ CBaseEntity *pTouchEnt;
+ float flLength;
+
+ pTouchEnt = TongueTouchEnt( &flLength );
+ m_flAltitude = flLength;
+
+ Vector origin;
+ QAngle angle;
+
+ GetAttachment( "TongueEnd", origin, angle );
+
+ m_flTongueAdj = origin.z - GetAbsOrigin().z;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_Barnacle::BarnacleThink ( void )
+{
+ CBaseEntity *pTouchEnt;
+ float flLength;
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ if (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI)
+ {
+ // AI Disabled, don't do anything
+ }
+ else if ( GetEnemy() != NULL )
+ {
+// barnacle has prey.
+
+ if ( !GetEnemy()->IsAlive() )
+ {
+ // someone (maybe even the barnacle) killed the prey. Reset barnacle.
+ m_fLiftingPrey = FALSE;// indicate that we're not lifting prey.
+ SetEnemy( NULL );
+ return;
+ }
+
+ CBaseCombatCharacter* pVictim = GetEnemyCombatCharacterPointer();
+ Assert( pVictim );
+
+ if ( m_fLiftingPrey )
+ {
+
+ if ( GetEnemy() != NULL && pVictim->m_lifeState == LIFE_DEAD )
+ {
+ // crap, someone killed the prey on the way up.
+ SetEnemy( NULL );
+ m_fLiftingPrey = FALSE;
+ return;
+ }
+
+ // still pulling prey.
+ Vector vecNewEnemyOrigin = GetEnemy()->GetLocalOrigin();
+ vecNewEnemyOrigin.x = GetLocalOrigin().x;
+ vecNewEnemyOrigin.y = GetLocalOrigin().y;
+
+ // guess as to where their neck is
+ // FIXME: remove, ask victim where their neck is
+ vecNewEnemyOrigin.x -= 6 * cos(GetEnemy()->GetLocalAngles().y * M_PI/180.0);
+ vecNewEnemyOrigin.y -= 6 * sin(GetEnemy()->GetLocalAngles().y * M_PI/180.0);
+
+ m_flAltitude -= BARNACLE_PULL_SPEED;
+ vecNewEnemyOrigin.z += BARNACLE_PULL_SPEED;
+
+ if ( fabs( GetLocalOrigin().z - ( vecNewEnemyOrigin.z + GetEnemy()->GetViewOffset().z ) ) < BARNACLE_BODY_HEIGHT )
+ {
+ // prey has just been lifted into position ( if the victim origin + eye height + 8 is higher than the bottom of the barnacle, it is assumed that the head is within barnacle's body )
+ m_fLiftingPrey = FALSE;
+
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Barnacle.Bite");
+
+ // Take a while to kill the player
+ m_flKillVictimTime = gpGlobals->curtime + 10;
+
+ if ( pVictim )
+ {
+ pVictim->DispatchInteraction( g_interactionBarnacleVictimDangle, NULL, this );
+ SetActivity ( (Activity)ACT_EAT );
+ }
+ }
+
+ CBaseEntity *pEnemy = GetEnemy();
+
+ trace_t trace;
+ UTIL_TraceEntity( pEnemy, pEnemy->GetAbsOrigin(), vecNewEnemyOrigin, MASK_SOLID_BRUSHONLY, pEnemy, COLLISION_GROUP_NONE, &trace );
+
+ if( trace.fraction != 1.0 )
+ {
+ // The victim cannot be moved from their current origin to this new origin. So drop them.
+ SetEnemy( NULL );
+ m_fLiftingPrey = FALSE;
+
+ if( pEnemy->MyCombatCharacterPointer() )
+ {
+ pEnemy->MyCombatCharacterPointer()->DispatchInteraction( g_interactionBarnacleVictimReleased, NULL, this );
+ }
+
+ // Ignore touches long enough to let the victim move away.
+ m_flIgnoreTouchesUntil = gpGlobals->curtime + 1.5;
+
+ SetActivity( ACT_IDLE );
+
+ return;
+ }
+
+ UTIL_SetOrigin ( GetEnemy(), vecNewEnemyOrigin );
+ }
+ else
+ {
+ // prey is lifted fully into feeding position and is dangling there.
+
+ if ( m_flKillVictimTime != -1 && gpGlobals->curtime > m_flKillVictimTime )
+ {
+ // kill!
+ if ( pVictim )
+ {
+ // DMG_CRUSH added so no physics force is generated
+ pVictim->TakeDamage( CTakeDamageInfo( this, this, pVictim->m_iHealth, DMG_SLASH | DMG_ALWAYSGIB | DMG_CRUSH ) );
+ m_cGibs = 3;
+ }
+
+ return;
+ }
+
+ // bite prey every once in a while
+ if ( pVictim && ( random->RandomInt( 0, 49 ) == 0 ) )
+ {
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Barnacle.Chew" );
+
+ if ( pVictim )
+ {
+ pVictim->DispatchInteraction( g_interactionBarnacleVictimDangle, NULL, this );
+ }
+ }
+ }
+ }
+ else
+ {
+// barnacle has no prey right now, so just idle and check to see if anything is touching the tongue.
+
+ // If idle and no nearby client, don't think so often. Client should be out of PVS and not within 50 feet.
+ if ( !UTIL_FindClientInPVS(edict()) )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
+
+ if( pPlayer )
+ {
+ Vector vecDist = pPlayer->GetAbsOrigin() - GetAbsOrigin();
+
+ if( vecDist.Length2DSqr() >= Square(600.0f) )
+ {
+ SetNextThink( gpGlobals->curtime + 1.5f );
+ }
+ }
+ }
+
+ if ( IsActivityFinished() )
+ {// this is done so barnacle will fidget.
+ SetActivity ( ACT_IDLE );
+ }
+
+ if ( m_cGibs && random->RandomInt(0,99) == 1 )
+ {
+ // cough up a gib.
+ CGib::SpawnRandomGibs( this, 1, GIB_HUMAN );
+ m_cGibs--;
+
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Barnacle.Chew" );
+ }
+
+ pTouchEnt = TongueTouchEnt( &flLength );
+
+ //NDebugOverlay::Box( GetAbsOrigin() - Vector( 0, 0, flLength ), Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255,0,0, 0, 0.1 );
+
+ if ( pTouchEnt != NULL )
+ {
+ // tongue is fully extended, and is touching someone.
+ CBaseCombatCharacter* pBCC = (CBaseCombatCharacter *)pTouchEnt;
+
+ // FIXME: humans should return neck position
+ Vector vecGrabPos = pTouchEnt->GetAbsOrigin();
+
+ if ( pBCC && pBCC->DispatchInteraction( g_interactionBarnacleVictimGrab, &vecGrabPos, this ) )
+ {
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Barnacle.Alert" );
+
+ SetSequenceByName ( "attack1" );
+
+ SetEnemy( pTouchEnt );
+
+ pTouchEnt->SetMoveType( MOVETYPE_FLY );
+ pTouchEnt->SetAbsVelocity( vec3_origin );
+ pTouchEnt->SetBaseVelocity( vec3_origin );
+ Vector origin = GetAbsOrigin();
+ origin.z = pTouchEnt->GetAbsOrigin().z;
+ pTouchEnt->SetLocalOrigin( origin );
+
+ m_fLiftingPrey = TRUE;// indicate that we should be lifting prey.
+ m_flKillVictimTime = -1;// set this to a bogus time while the victim is lifted.
+
+ m_flAltitude = (GetAbsOrigin().z - vecGrabPos.z);
+ }
+ }
+ else
+ {
+ // calculate a new length for the tongue to be clear of anything else that moves under it.
+ if ( m_flAltitude < flLength )
+ {
+ // if tongue is higher than is should be, lower it kind of slowly.
+ m_flAltitude += BARNACLE_PULL_SPEED;
+ }
+ else
+ {
+ m_flAltitude = flLength;
+ }
+
+ }
+
+ }
+
+ // ALERT( at_console, "tounge %f\n", m_flAltitude + m_flTongueAdj );
+ //NDebugOverlay::Box( GetAbsOrigin() - Vector( 0, 0, m_flAltitude ), Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255,255,255, 0, 0.1 );
+
+ SetBoneController( 0, -(m_flAltitude + m_flTongueAdj) );
+ StudioFrameAdvance();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_Barnacle::Event_Killed( const CTakeDamageInfo &info )
+{
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ m_takedamage = DAMAGE_NO;
+ m_lifeState = LIFE_DEAD;
+ if ( GetEnemy() != NULL )
+ {
+ CBaseCombatCharacter *pVictim = GetEnemyCombatCharacterPointer();
+
+ if ( pVictim )
+ {
+ pVictim->DispatchInteraction( g_interactionBarnacleVictimReleased, NULL, this );
+ }
+ }
+
+ CGib::SpawnRandomGibs( this, 4, GIB_HUMAN );
+
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Barnacle.Die" );
+
+ SetActivity ( ACT_DIESIMPLE );
+ SetBoneController( 0, 0 );
+
+ StudioFrameAdvance();
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ SetThink ( &CNPC_Barnacle::WaitTillDead );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_Barnacle::WaitTillDead ( void )
+{
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ StudioFrameAdvance();
+ DispatchAnimEvents ( this );
+
+ if ( IsActivityFinished() )
+ {
+ // death anim finished.
+ StopAnimation();
+ SetThink ( NULL );
+ }
+}
+
+//=========================================================
+// Precache - precaches all resources this monster needs
+//=========================================================
+void CNPC_Barnacle::Precache()
+{
+ PrecacheModel("models/barnacle.mdl");
+
+ PrecacheScriptSound( "Barnacle.Bite" );
+ PrecacheScriptSound( "Barnacle.Chew" );
+ PrecacheScriptSound( "Barnacle.Alert" );
+ PrecacheScriptSound( "Barnacle.Die" );
+
+ BaseClass::Precache();
+}
+
+//=========================================================
+// TongueTouchEnt - does a trace along the barnacle's tongue
+// to see if any entity is touching it. Also stores the length
+// of the trace in the int pointer provided.
+//=========================================================
+#define BARNACLE_CHECK_SPACING 8
+CBaseEntity *CNPC_Barnacle::TongueTouchEnt ( float *pflLength )
+{
+ trace_t tr;
+ float length;
+
+ // trace once to hit architecture and see if the tongue needs to change position.
+ UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() - Vector ( 0 , 0 , 2048 ),
+ MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
+
+ length = fabs( GetAbsOrigin().z - tr.endpos.z );
+ // Pull it up a tad
+ length -= 16;
+ if ( pflLength )
+ {
+ *pflLength = length;
+ }
+
+ // Don't try to touch any prey.
+ if ( m_flIgnoreTouchesUntil > gpGlobals->curtime )
+ return NULL;
+
+ Vector delta = Vector( BARNACLE_CHECK_SPACING, BARNACLE_CHECK_SPACING, 0 );
+ Vector mins = GetAbsOrigin() - delta;
+ Vector maxs = GetAbsOrigin() + delta;
+ maxs.z = GetAbsOrigin().z;
+
+ // Take our current tongue's length or a point higher if we hit a wall
+ // NOTENOTE: (this relieves the need to know if the tongue is currently moving)
+ mins.z -= MIN( m_flAltitude, length );
+
+ CBaseEntity *pList[10];
+ int count = UTIL_EntitiesInBox( pList, 10, mins, maxs, (FL_CLIENT|FL_NPC) );
+ if ( count )
+ {
+ for ( int i = 0; i < count; i++ )
+ {
+ CBaseCombatCharacter *pVictim = ToBaseCombatCharacter( pList[ i ] );
+
+ bool bCanHurt = false;
+
+ if ( IRelationType( pList[i] ) == D_HT || IRelationType( pList[i] ) == D_FR )
+ bCanHurt = true;
+
+ if ( pList[i] != this && bCanHurt == true && pVictim->m_lifeState == LIFE_ALIVE )
+ {
+ return pList[i];
+ }
+ }
+ }
+
+ return NULL;
+}