diff options
Diffstat (limited to 'game/server/hl1/hl1_npc_barnacle.cpp')
| -rw-r--r-- | game/server/hl1/hl1_npc_barnacle.cpp | 516 |
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; +} |