📄 npc_hydra.cpp
字号:
//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ==========
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#include "cbase.h"
#include "npc_hydra.h"
#include "ai_hull.h"
#include "saverestore_utlvector.h"
#include "physics_saverestore.h"
#include "vphysics/constraints.h"
#include "vcollide_parse.h"
#include "ragdoll_shared.h"
#include "physics_prop_ragdoll.h"
//-----------------------------------------------------------------------------
//
// CNPC_Hydra
//
#define HYDRA_MAX_LENGTH 500
LINK_ENTITY_TO_CLASS( npc_hydra, CNPC_Hydra );
//=========================================================
// Hydra activities
//=========================================================
int ACT_HYDRA_COWER;
int ACT_HYDRA_STAB;
//=========================================================
// Private conditions
//=========================================================
//==================================================
// AntlionConditions
//==================================================
enum
{
COND_HYDRA_SNAGGED = LAST_SHARED_CONDITION,
COND_HYDRA_STUCK,
COND_HYDRA_OVERSHOOT,
COND_HYDRA_OVERSTRETCH, // longer than max distance
COND_HYDRA_STRIKE, // head hit something
COND_HYDRA_NOSTUCK // no segments are stuck
};
//=========================================================
// Hydra schedules
//=========================================================
enum
{
SCHED_HYDRA_DEPLOY = LAST_SHARED_SCHEDULE,
SCHED_HYDRA_RETRACT,
SCHED_HYDRA_IDLE,
SCHED_HYDRA_STAB, // shoot out head and try to hit object
SCHED_HYDRA_PULLBACK, //
SCHED_HYDRA_TIGHTEN_SLACK, // snagged on something, tighten slack up to obstacle and try again from there
SCHED_HYDRA_RETREAT,
SCHED_HYDRA_THROW,
SCHED_HYDRA_RANGE_ATTACK
};
//=========================================================
// Hydra tasks
//=========================================================
enum
{
TASK_HYDRA_RETRACT = LAST_SHARED_TASK,
TASK_HYDRA_DEPLOY,
TASK_HYDRA_GET_OBJECT,
TASK_HYDRA_THROW_OBJECT,
TASK_HYDRA_PREP_STAB,
TASK_HYDRA_STAB,
TASK_HYDRA_PULLBACK,
TASK_HYDRA_SET_MAX_TENSION,
TASK_HYDRA_SET_BLEND_TENSION
};
//---------------------------------------------------------
// Custom Client entity
//---------------------------------------------------------
IMPLEMENT_SERVERCLASS_ST(CNPC_Hydra, DT_NPC_Hydra)
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 0 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 1 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 2 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 3 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 4 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 5 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 6 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 7 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 8 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 9 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 10 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 11 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 12 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 13 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 14 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 15 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 16 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 17 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 18 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 19 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 20 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 21 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 22 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 23 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 24 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 25 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 26 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 27 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 28 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 29 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 30 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO_NETWORKARRAYELEM( m_vecChain, 31 ), -1, SPROP_COORD ),
SendPropVector( SENDINFO( m_vecHeadDir ), -1, SPROP_NORMAL ),
SendPropFloat( SENDINFO( m_flRelaxedLength ), 12, 0, 0.0, HYDRA_MAX_LENGTH * 1.5 ),
END_SEND_TABLE()
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_Hydra )
DEFINE_AUTO_ARRAY( CNPC_Hydra, m_vecChain, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CNPC_Hydra, m_activeChain, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_Hydra, m_bHasStuckSegments, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_Hydra, m_flCurrentLength, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Hydra, m_vecHeadGoal, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CNPC_Hydra, m_flHeadGoalInfluence, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Hydra, m_vecHeadDir, FIELD_VECTOR ),
DEFINE_FIELD( CNPC_Hydra, m_flRelaxedLength, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Hydra, m_vecOutward, FIELD_VECTOR ),
DEFINE_UTLVECTOR( CNPC_Hydra, m_body, FIELD_EMBEDDED ),
DEFINE_FIELD( CNPC_Hydra, m_idealLength, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Hydra, m_idealSegmentLength, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Hydra, m_bExtendSoundActive, FIELD_BOOLEAN ),
DEFINE_SOUNDPATCH( CNPC_Hydra, m_pExtendTentacleSound ),
DEFINE_FIELD( CNPC_Hydra, m_seed, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Hydra, m_vecTarget, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CNPC_Hydra, m_vecTargetDir, FIELD_VECTOR ),
DEFINE_FIELD( CNPC_Hydra, m_flLastAdjustmentTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Hydra, m_flTaskStartTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Hydra, m_flTaskEndTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Hydra, m_flLengthTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Hydra, m_bStabbedEntity, FIELD_BOOLEAN ),
END_DATADESC()
//-------------------------------------
BEGIN_SIMPLE_DATADESC( HydraBone )
DEFINE_FIELD( HydraBone, vecPos, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( HydraBone, vecDelta, FIELD_VECTOR ),
DEFINE_FIELD( HydraBone, flIdealLength, FIELD_FLOAT ),
DEFINE_FIELD( HydraBone, flActualLength, FIELD_FLOAT ),
DEFINE_FIELD( HydraBone, bStuck, FIELD_BOOLEAN ),
DEFINE_FIELD( HydraBone, bOnFire, FIELD_BOOLEAN ),
DEFINE_FIELD( HydraBone, vecGoalPos, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( HydraBone, flGoalInfluence,FIELD_FLOAT ),
END_DATADESC()
//-------------------------------------
//static ConVar sv_hydraMinTension( "hydra_min_tension", "1", FCVAR_ARCHIVE | FCVAR_SERVER, "Hydra Slack" );
//static ConVar sv_hydraLengthTension( "hydra_length_tension", "1", FCVAR_ARCHIVE | FCVAR_SERVER, "Hydra Slack" );
static ConVar sv_hydraLength( "hydra_length", "100", FCVAR_ARCHIVE | FCVAR_SERVER, "Hydra Length" );
static ConVar sv_hydraSlack( "hydra_slack", "200", FCVAR_ARCHIVE | FCVAR_SERVER, "Hydra Slack" );
static ConVar sv_hydraSegmentLength( "hydra_segment_length", "30", FCVAR_ARCHIVE | FCVAR_SERVER, "Hydra Slack" );
static ConVar sv_hydraTest( "hydra_test", "1", FCVAR_ARCHIVE | FCVAR_SERVER, "Hydra Slack" );
static ConVar sv_hydraBendTension( "hydra_bend_tension", "0.4", FCVAR_ARCHIVE | FCVAR_SERVER, "Hydra Slack" );
static ConVar sv_hydraBendDelta( "hydra_bend_delta", "50", FCVAR_ARCHIVE | FCVAR_SERVER, "Hydra Slack" );
static ConVar sv_hydraGoalTension( "hydra_goal_tension", "0.5", FCVAR_ARCHIVE | FCVAR_SERVER, "Hydra Slack" );
static ConVar sv_hydraGoalDelta( "hydra_goal_delta", "400", FCVAR_ARCHIVE | FCVAR_SERVER, "Hydra Slack" );
static ConVar sv_hydraMomentum( "hydra_momentum", "0.5", FCVAR_ARCHIVE | FCVAR_SERVER, "Hydra Slack" );
static ConVar sv_hydraTestSpike( "sv_hydraTestSpike", "1", FCVAR_SERVER, "Hydra Test impaling code" );
//-------------------------------------
// Purpose: Initialize the custom schedules
//-------------------------------------
//-------------------------------------
void CNPC_Hydra::Precache()
{
engine->PrecacheModel( "models/Hydra.mdl" );
UTIL_PrecacheOther( "hydra_impale" );
BaseClass::Precache();
}
void CNPC_Hydra::Activate( void )
{
CPASAttenuationFilter filter( this );
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
m_pExtendTentacleSound = controller.SoundCreate( filter, entindex(), "NPC_Hydra.ExtendTentacle" );
controller.Play( m_pExtendTentacleSound, 1.0, 100 );
BaseClass::Activate();
}
//-----------------------------------------------------------------------------
// Purpose: Returns this monster's place in the relationship table.
//-----------------------------------------------------------------------------
Class_T CNPC_Hydra::Classify( void )
{
return CLASS_BARNACLE;
}
//-------------------------------------
#define HYDRA_OUTWARD_BIAS 16
#define HYDRA_INWARD_BIAS 30
void CNPC_Hydra::Spawn()
{
Precache();
BaseClass::Spawn();
SetModel( "models/Hydra.mdl" );
SetHullType(HULL_HUMAN);
SetHullSizeNormal();
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_STEP );
SetBloodColor( BLOOD_COLOR_RED );
m_fEffects = 0;
m_iHealth = 20;
m_flFieldOfView = -1.0;// indicates the width of this NPC's forward view cone ( as a dotproduct result )
m_NPCState = NPC_STATE_NONE;
// CapabilitiesAdd( bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP | bits_CAP_MOVE_GROUND | bits_CAP_MOVE_CLIMB );
// CapabilitiesAdd( bits_CAP_USE_WEAPONS );
// CapabilitiesAdd( bits_CAP_ANIMATEDFACE );
GetVectors( NULL, NULL, &m_vecOutward );
SetAbsAngles( QAngle( 0, 0, 0 ) );
m_vecChain.Set( 0, GetAbsOrigin( ) - m_vecOutward * 32 );
m_vecChain.Set( 1, GetAbsOrigin( ) + m_vecOutward * 16 );
m_vecHeadGoal = m_vecChain[1] + m_vecOutward * HYDRA_OUTWARD_BIAS;
m_vecHeadDir = Vector( 0, 0, 1 );
// init bones
HydraBone bone;
bone.vecPos = GetAbsOrigin( ) - m_vecOutward * HYDRA_INWARD_BIAS;
m_body.AddToTail( bone );
bone.vecPos = m_vecChain[1];
m_body.AddToTail( bone );
bone.vecPos = m_vecHeadGoal;
m_body.AddToTail( bone );
bone.vecPos = m_vecHeadGoal + m_vecHeadDir;
m_body.AddToTail( bone );
m_idealSegmentLength = sv_hydraSegmentLength.GetFloat();
for (int i = 2; i < CHAIN_LINKS; i++)
{
m_vecChain.Set( i, m_vecChain[i-1] );
}
m_seed = random->RandomFloat( 0.0, 2000.0 );
NPCInit();
m_takedamage = DAMAGE_NO;
}
//-------------------------------------
void CNPC_Hydra::RunAI( void )
{
CheckLength( );
AdjustLength( );
BaseClass::RunAI();
CalcGoalForces( );
MoveBody( );
int i;
for (i = 1; i < CHAIN_LINKS && i < m_body.Count(); i++)
{
m_vecChain.Set( i, m_body[i].vecPos );
#if 0
if (m_body[i].bStuck)
{
NDebugOverlay::Box(m_body[i].vecPos, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255, 0, 0, 20, .1);
}
else
{
NDebugOverlay::Box(m_body[i].vecPos, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0, 255, 0, 20, .1);
}
NDebugOverlay::Line( m_body[i].vecPos, m_body[i].vecPos + m_body[i].vecDelta, 0, 255, 0, true, .1);
NDebugOverlay::Line( m_body[i-1].vecPos, m_body[i].vecPos, 255, 255, 255, true, .1);
#endif
#if 0
char text[128];
Q_snprintf( text, sizeof( text ), "%d", i );
NDebugOverlay::Text( m_body[i].vecPos, text, false, 0.1 );
#endif
#if 0
char text[128];
Q_snprintf( text, sizeof( text ), "%4.0f", (m_body[i].vecPos - m_body[i-1].vecPos).Length() * 100 / m_idealSegmentLength - 100);
NDebugOverlay::Text( 0.5*(m_body[i-1].vecPos + m_body[i].vecPos), text, false, 0.1 );
#endif
}
//NDebugOverlay::Box(m_body[i].vecPos, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0, 255, 0, 20, .1);
//NDebugOverlay::Box( m_vecHeadGoal, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255, 255, 0, 20, .1);
for (; i < CHAIN_LINKS; i++)
{
m_vecChain.Set( i, m_vecChain[i-1] );
}
}
Vector CNPC_Hydra::TestPosition( float t )
{
// return GetAbsOrigin( ) + Vector( sin( (m_seed + t) * 2.3 ) * 15, cos( (m_seed + t) * 2.4 ) * 150, sin( ( m_seed + t ) * 1.8 ) * 50 ) + m_vecOutward * sv_hydraLength.GetFloat();;
t = (int)(t * 0.2);
#if 1
Vector tmp = Vector( sin( (m_seed + t) * 0.8 ) * 15, cos( (m_seed + t) * 0.9 ) * 150, sin( ( m_seed + t ) * 0.4 ) * 50 );
tmp += Vector( sin( (m_seed + t) * 1.0 ) * 4, cos( (m_seed + t) * 0.9 ) * 4, sin( ( m_seed + t ) * 1.1 ) * 6 );
tmp += GetAbsOrigin( ) + m_vecOutward * sv_hydraLength.GetFloat();
return tmp;
#else
Vector tmp;
tmp.Init;
CBaseEntity *pPlayer = (CBaseEntity *)UTIL_PlayerByIndex( 1 );
if ( pPlayer )
{
tmp = pPlayer->EyePosition( );
Vector delta = (tmp - GetAbsOrigin( ));
if (delta.Length() > 200)
{
tmp = GetAbsOrigin( ) + Vector( 0, 0, 200 );
}
m_vecHeadDir = (pPlayer->EyePosition( ) - m_body[m_body.Count()-2].vecPos);
VectorNormalize( m_vecHeadDir );
}
return tmp;
#endif
// m_vecHeadGoal = GetAbsOrigin( ) + Vector( sin( gpGlobals->curtime * 0.3 ) * 15, cos( gpGlobals->curtime * 0.4 ) * 150, sin( gpGlobals->curtime * 0.2 ) * 50 + dt );
}
//-----------------------------------------------------------------------------
// Purpose: Calculate the bone forces based on goal positions, bending rules, stretching rules, etc.
// Input :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Hydra::CalcGoalForces( )
{
int i;
int iFirst = 2;
int iLast = m_body.Count() - 1;
// keep head segment straight
m_body[iLast].vecGoalPos = m_vecHeadGoal; // + m_vecHeadDir * m_body[iLast-1].flActualLength;
m_body[iLast].flGoalInfluence = m_flHeadGoalInfluence;
m_body[iLast-1].vecGoalPos = m_vecHeadGoal - m_vecHeadDir * m_idealSegmentLength;
m_body[iLast-1].flGoalInfluence = 1.0; // m_flHeadGoalInfluence;
// momentum?
for (i = iFirst; i <= iLast; i++)
{
m_body[i].vecDelta = m_body[i].vecDelta * sv_hydraMomentum.GetFloat();
}
//Vector right, up;
//VectorVectors( m_vecHeadDir, right, up );
float flGoalSegmentLength = m_idealSegmentLength * ( m_idealLength / m_flCurrentLength);
// goal forces
#if 1
for (i = iFirst; i <= iLast; i++)
{
// Msg("(%d) %.2f\n", i, t );
float flInfluence = m_body[i].flGoalInfluence;
if (flInfluence > 0)
{
m_body[i].flGoalInfluence = 0.0;
Vector v0 = (m_body[i].vecGoalPos - m_body[i].vecPos);
float length = v0.Length();
if (length > sv_hydraGoalDelta.GetFloat())
{
v0 = v0 * sv_hydraGoalDelta.GetFloat() / length;
}
m_body[i].vecDelta += v0 * flInfluence * sv_hydraGoalTension.GetFloat();
// NDebugOverlay::Box(m_body[i].vecGoalPos, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255, 255, 0, flInfluence * 255, .1);
}
}
#endif
// bending forces
for (i = iFirst-1; i <= iLast - 1; i++)
{
// Msg("(%d) %.2f\n", i, t );
Vector v3 = m_body[i+1].vecPos - m_body[i-1].vecPos;
VectorNormalize( v3 );
Vector delta;
float length;
//NDebugOverlay::Line( m_body[i].vecPos + v3 * flGoalSegmentLength, m_body[i].vecPos - v3 * flGoalSegmentLength, 255, 0, 0, true, .1);
if (i+1 <= iLast)
{
// towards head
delta = (m_body[i].vecPos + v3 * flGoalSegmentLength - m_body[i+1].vecPos) * sv_hydraBendTension.GetFloat();
length = delta.Length();
if (length > sv_hydraBendDelta.GetFloat())
{
delta = delta * (sv_hydraBendDelta.GetFloat() / length);
}
m_body[i+1].vecDelta += delta;
//NDebugOverlay::Line( m_body[i+1].vecPos, m_body[i+1].vecPos + delta, 255, 0, 0, true, .1);
}
if (i-1 >= iFirst)
{
// towards tail
delta = (m_body[i].vecPos - v3 * flGoalSegmentLength - m_body[i-1].vecPos) * sv_hydraBendTension.GetFloat();
length = delta.Length();
if (length > sv_hydraBendDelta.GetFloat())
{
delta = delta * (sv_hydraBendDelta.GetFloat() / length);
}
m_body[i-1].vecDelta += delta * 0.8;
//NDebugOverlay::Line( m_body[i-1].vecPos, m_body[i-1].vecPos + delta, 255, 0, 0, true, .1);
}
}
m_body[0].vecDelta = Vector( 0, 0, 0 );
m_body[1].vecDelta = Vector( 0, 0, 0 );
// normal gravity forces
for (i = iFirst; i <= iLast; i++)
{
if (!m_body[i].bStuck)
{
m_body[i].vecDelta.z -= 3.84 * 0.2;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -