📄 npc_barnacle.cpp
字号:
//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: barnacle - stationary ceiling mounted 'fishing' monster
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================
#include "cbase.h"
#include "physics_prop_ragdoll.h"
#include "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 "vphysics/constraints.h"
#include "studio.h"
#include "bone_setup.h"
ConVar sk_barnacle_health( "sk_barnacle_health","0");
//-----------------------------------------------------------------------------
// Private activities.
//-----------------------------------------------------------------------------
int ACT_BARNACLE_SLURP; // Pulling the tongue up with prey on the end
int ACT_BARNACLE_BITE_HUMAN; // Biting the head of a humanoid
int ACT_BARNACLE_CHEW_HUMAN; // Slowly swallowing the humanoid
int ACT_BARNACLE_BARF_HUMAN; // Spitting out human legs & gibs
int ACT_BARNACLE_TONGUE_WRAP; // Wrapping the tongue around a target
//-----------------------------------------------------------------------------
// Interactions
//-----------------------------------------------------------------------------
int g_interactionBarnacleVictimDangle = 0;
int g_interactionBarnacleVictimReleased = 0;
int g_interactionBarnacleVictimGrab = 0;
LINK_ENTITY_TO_CLASS( npc_barnacle, CNPC_Barnacle );
// Tongue Spring constants
#define BARNACLE_TONGUE_SPRING_CONSTANT_HANGING 10000
#define BARNACLE_TONGUE_SPRING_CONSTANT_LIFTING 10000
#define BARNACLE_TONGUE_SPRING_CONSTANT_LOWERING 7000
#define BARNACLE_TONGUE_SPRING_DAMPING 20
//-----------------------------------------------------------------------------
// Purpose: Constructor
// Input :
// Output :
//-----------------------------------------------------------------------------
CNPC_Barnacle::CNPC_Barnacle(void)
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CNPC_Barnacle::~CNPC_Barnacle( void )
{
// Destroy the ragdoll->tongue tip constraint
if ( m_pConstraint )
{
physenv->DestroyConstraint( m_pConstraint );
m_pConstraint = NULL;
}
}
BEGIN_DATADESC( CNPC_Barnacle )
DEFINE_FIELD( CNPC_Barnacle, m_flAltitude, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Barnacle, m_cGibs, FIELD_INTEGER ),// barnacle loads up on gibs each time it kills something.
DEFINE_FIELD( CNPC_Barnacle, m_fTongueExtended, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_Barnacle, m_bLiftingPrey, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_Barnacle, m_bSwallowingPrey, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_Barnacle, m_flDigestFinish, FIELD_TIME ),
DEFINE_FIELD( CNPC_Barnacle, m_bPlayedPullSound, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_Barnacle, m_flVictimHeight, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Barnacle, m_iGrabbedBoneIndex, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_Barnacle, m_vecRoot, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CNPC_Barnacle, m_vecTip, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CNPC_Barnacle, m_hTongueRoot, FIELD_EHANDLE ),
DEFINE_FIELD( CNPC_Barnacle, m_hTongueTip, FIELD_EHANDLE ),
DEFINE_FIELD( CNPC_Barnacle, m_hRagdoll, FIELD_EHANDLE ),
DEFINE_AUTO_ARRAY( CNPC_Barnacle, m_pRagdollBones, FIELD_MATRIX3X4_WORLDSPACE ),
DEFINE_PHYSPTR( CNPC_Barnacle, m_pConstraint ),
// Function pointers
DEFINE_THINKFUNC( CNPC_Barnacle, BarnacleThink ),
DEFINE_THINKFUNC( CNPC_Barnacle, WaitTillDead ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CNPC_Barnacle, DT_Barnacle )
SendPropFloat( SENDINFO( m_flAltitude ), 0, SPROP_NOSCALE),
SendPropVector( SENDINFO( m_vecRoot ), 0, SPROP_COORD ),
SendPropVector( SENDINFO( m_vecTip ), 0, SPROP_COORD ),
END_SEND_TABLE()
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
Class_T CNPC_Barnacle::Classify ( void )
{
return CLASS_BARNACLE;
}
//-----------------------------------------------------------------------------
// Purpose: Initialize absmin & absmax to the appropriate box
//-----------------------------------------------------------------------------
void CNPC_Barnacle::SetObjectCollisionBox( void )
{
BaseClass::SetObjectCollisionBox();
// Extend our bounding box downwards the length of the tongue
Vector vecAbsMins = GetAbsMins();
vecAbsMins.z -= m_flAltitude;
SetAbsMins( vecAbsMins );
}
//=========================================================
// 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;
case BARNACLE_AE_BITE:
BitePrey();
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_cGibs = 0;
m_bLiftingPrey = false;
m_bSwallowingPrey = false;
m_flDigestFinish = 0;
m_takedamage = DAMAGE_YES;
m_pConstraint = NULL;
InitBoneControllers();
InitTonguePosition();
// set eye position
SetDefaultEyeOffset();
SetActivity( ACT_IDLE );
SetThink ( BarnacleThink );
SetNextThink( gpGlobals->curtime + 0.5f );
Relink();
//Do not have a shadow
m_fEffects |= EF_NOSHADOW;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Barnacle::Activate( void )
{
BaseClass::Activate();
// Create our tongue tips
m_hTongueRoot = CBarnacleTongueTip::CreateTongueRoot( m_vecRoot, QAngle(90,0,0) );
m_hTongueTip = CBarnacleTongueTip::CreateTongueTip( m_hTongueRoot, m_vecTip, QAngle(0,0,0) );
Assert( m_hTongueRoot && m_hTongueTip );
}
//-----------------------------------------------------------------------------
// 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 );
float flTongueAdj = origin.z - GetAbsOrigin().z;
m_vecRoot = origin - Vector(0,0,flTongueAdj);
m_vecTip.Set( m_vecRoot.Get() - Vector(0,0,(float)m_flAltitude) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Barnacle::BarnacleThink ( void )
{
CBaseEntity *pTouchEnt;
float flLength;
SetNextThink( gpGlobals->curtime + 0.1f );
UpdateTongue();
// AI Disabled, don't do anything?
if ( CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI )
return;
// Do we have an enemy?
if ( GetEnemy() )
{
if ( m_bLiftingPrey )
{
LiftPrey();
}
}
else if ( m_hRagdoll )
{
// Slowly swallowing the ragdoll?
if ( m_bSwallowingPrey )
{
SwallowPrey();
}
// Stay bloated as we digest
else if ( m_flDigestFinish )
{
// Still digesting him>
if ( m_flDigestFinish > gpGlobals->curtime )
{
if ( IsActivityFinished() )
{
SetActivity( ACT_IDLE );
}
// bite prey every once in a while
if ( random->RandomInt(0,49) == 0 )
{
EmitSound( "NPC_Barnacle.Digest" );
}
}
else
{
// Finished digesting
LostPrey( true ); // Remove all evidence
m_flDigestFinish = 0;
}
}
}
else
{
// Were we lifting prey?
if ( m_bSwallowingPrey || m_bLiftingPrey )
{
// Something removed our prey.
LostPrey( false );
}
// 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
if ( !UTIL_FindClientInPVS( edict() ) )
{
SetNextThink( gpGlobals->curtime + random->RandomFloat(1,1.5) ); // Stagger a bit to keep barnacles from thinking on the same frame
}
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--;
EmitSound( "NPC_Barnacle.Digest" );
}
pTouchEnt = TongueTouchEnt( &flLength );
if ( pTouchEnt != NULL && m_fTongueExtended )
{
// tongue is fully extended, and is touching someone.
CBaseCombatCharacter *pBCC = dynamic_cast<CBaseCombatCharacter *>(pTouchEnt);
// FIXME: humans should return neck position
Vector vecGrabPos = pTouchEnt->EyePosition();
if ( pBCC && pBCC->HandleInteraction( g_interactionBarnacleVictimGrab, &vecGrabPos, this ) )
{
AttachTongueToTarget( pTouchEnt, vecGrabPos );
}
}
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;
m_fTongueExtended = false;
}
else
{
// Restore the hanging spring constant
m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_HANGING );
m_flAltitude = flLength;
m_fTongueExtended = true;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -