📄 hl1_npc_tentacle.cpp
字号:
GetMotor()->UpdateYaw( MaxYawSpeed() );
CSound *pSound = NULL;
GetSenses()->Listen();
m_BoneFollowerManager.UpdateBoneFollowers();
// Listen will set this if there's something in my sound list
if ( HeardAnything() )
pSound = GetSenses()->GetClosestSound( false );
else
pSound = NULL;
if ( pSound )
{
Vector vecDir;
if ( gpGlobals->curtime - m_flPrevSoundTime < 0.5 )
{
float dt = gpGlobals->curtime - m_flPrevSoundTime;
vecDir = pSound->GetSoundOrigin() + (pSound->GetSoundOrigin() - m_vecPrevSound) / dt - GetAbsOrigin();
}
else
{
vecDir = pSound->GetSoundOrigin() - GetAbsOrigin();
}
m_flPrevSoundTime = gpGlobals->curtime;
m_vecPrevSound = pSound->GetSoundOrigin();
m_flSoundYaw = VecToYaw ( vecDir ) - m_flInitialYaw;
m_iSoundLevel = Level( vecDir.z );
if (m_flSoundYaw < -180)
m_flSoundYaw += 360;
if (m_flSoundYaw > 180)
m_flSoundYaw -= 360;
// ALERT( at_console, "sound %d %.0f\n", m_iSoundLevel, m_flSoundYaw );
if (m_flSoundTime < gpGlobals->curtime)
{
// play "I hear new something" sound
char *sound = NULL;
switch( random->RandomInt( 0, 1 ) )
{
case 0: sound = "tentacle/te_alert1.wav"; break;
case 1: sound = "tentacle/te_alert2.wav"; break;
}
UTIL_EmitAmbientSound( this, GetAbsOrigin() + Vector( 0, 0, MyHeight()), sound, 1.0, SNDLVL_GUNFIRE, 0, 100);
}
m_flSoundTime = gpGlobals->curtime + random->RandomFloat( 5.0, 10.0 );
}
// clip ideal_yaw
float dy = m_flSoundYaw;
switch( GetSequence() )
{
case TENTACLE_ANIM_Floor_Rear:
case TENTACLE_ANIM_Floor_Rear_Idle:
case TENTACLE_ANIM_Lev1_Rear:
case TENTACLE_ANIM_Lev1_Rear_Idle:
case TENTACLE_ANIM_Lev2_Rear:
case TENTACLE_ANIM_Lev2_Rear_Idle:
case TENTACLE_ANIM_Lev3_Rear:
case TENTACLE_ANIM_Lev3_Rear_Idle:
if (dy < 0 && dy > -m_flMaxYaw)
dy = -m_flMaxYaw;
if (dy > 0 && dy < m_flMaxYaw)
dy = m_flMaxYaw;
break;
default:
if (dy < -m_flMaxYaw)
dy = -m_flMaxYaw;
if (dy > m_flMaxYaw)
dy = m_flMaxYaw;
}
GetMotor()->SetIdealYaw( m_flInitialYaw + dy );
if ( IsSequenceFinished() )
{
// ALERT( at_console, "%s done %d %d\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim );
if ( m_iHealth <= 1)
{
m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
if ( GetSequence() == TENTACLE_ANIM_Pit_Idle)
{
m_iHealth = 75;
}
}
else if ( m_flSoundTime > gpGlobals->curtime )
{
if (m_flSoundYaw >= -(m_flMaxYaw + 30) && m_flSoundYaw <= (m_flMaxYaw + 30))
{
// strike
switch ( m_iSoundLevel )
{
case 0:
m_iGoalAnim = SelectWeightedSequence ( ACT_1030 );
break;
case 1:
m_iGoalAnim = SelectWeightedSequence ( ACT_1031 );
break;
case 2:
m_iGoalAnim = SelectWeightedSequence ( ACT_1032 );
break;
case 3:
m_iGoalAnim = SelectWeightedSequence ( ACT_1033 );
break;
}
}
else if (m_flSoundYaw >= -m_flMaxYaw * 2 && m_flSoundYaw <= m_flMaxYaw * 2)
{
// tap
switch ( m_iSoundLevel )
{
case 0:
m_iGoalAnim = SelectWeightedSequence ( ACT_1020 );
break;
case 1:
m_iGoalAnim = SelectWeightedSequence ( ACT_1021 );
break;
case 2:
m_iGoalAnim = SelectWeightedSequence ( ACT_1022 );
break;
case 3:
m_iGoalAnim = SelectWeightedSequence ( ACT_1023 );
break;
}
}
else
{
// go into rear idle
switch ( m_iSoundLevel )
{
case 0:
m_iGoalAnim = SelectWeightedSequence ( ACT_1040 );
break;
case 1:
m_iGoalAnim = SelectWeightedSequence ( ACT_1041 );
break;
case 2:
m_iGoalAnim = SelectWeightedSequence ( ACT_1042 );
break;
case 3:
m_iGoalAnim = SelectWeightedSequence ( ACT_1043 );
break;
case 4:
m_iGoalAnim = SelectWeightedSequence ( ACT_1044 );
break;
}
}
}
else if ( GetSequence() == TENTACLE_ANIM_Pit_Idle)
{
// stay in pit until hear noise
m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
}
else if ( GetSequence() == m_iGoalAnim)
{
if ( MyLevel() >= 0 && gpGlobals->curtime < m_flSoundTime)
{
if ( random->RandomInt(0,9) < m_flSoundTime - gpGlobals->curtime )
{
// continue stike
switch ( m_iSoundLevel )
{
case 0:
m_iGoalAnim = SelectWeightedSequence ( ACT_1030 );
break;
case 1:
m_iGoalAnim = SelectWeightedSequence ( ACT_1031 );
break;
case 2:
m_iGoalAnim = SelectWeightedSequence ( ACT_1032 );
break;
case 3:
m_iGoalAnim = SelectWeightedSequence ( ACT_1033 );
break;
}
}
else
{
// tap
switch ( m_iSoundLevel )
{
case 0:
m_iGoalAnim = SelectWeightedSequence ( ACT_1020 );
break;
case 1:
m_iGoalAnim = SelectWeightedSequence ( ACT_1021 );
break;
case 2:
m_iGoalAnim = SelectWeightedSequence ( ACT_1022 );
break;
case 3:
m_iGoalAnim = SelectWeightedSequence ( ACT_1023 );
break;
}
}
}
else if ( MyLevel( ) < 0 )
{
m_iGoalAnim = SelectWeightedSequence( ACT_1010 );
}
else
{
if (m_flNextSong < gpGlobals->curtime)
{
// play "I hear new something" sound
char *sound = NULL;
switch( random->RandomInt( 0,1 ) )
{
case 0: sound = "tentacle/te_sing1.wav"; break;
case 1: sound = "tentacle/te_sing2.wav"; break;
}
CPASAttenuationFilter filter( this );
enginesound->EmitSound( filter, entindex(), CHAN_VOICE, sound, 1.0, SNDLVL_GUNFIRE);
m_flNextSong = gpGlobals->curtime + random->RandomFloat( 10, 20 );
}
if (random->RandomInt(0,15) == 0)
{
// idle on new level
switch ( random->RandomInt( 0, 3 ) )
{
case 0:
m_iGoalAnim = SelectWeightedSequence ( ACT_1010 );
break;
case 1:
m_iGoalAnim = SelectWeightedSequence ( ACT_1011 );
break;
case 2:
m_iGoalAnim = SelectWeightedSequence ( ACT_1012 );
break;
case 3:
m_iGoalAnim = SelectWeightedSequence ( ACT_1013 );
break;
}
}
else if ( random->RandomInt( 0, 3 ) == 0 )
{
// tap
switch ( MyLevel() )
{
case 0:
m_iGoalAnim = SelectWeightedSequence ( ACT_1020 );
break;
case 1:
m_iGoalAnim = SelectWeightedSequence ( ACT_1021 );
break;
case 2:
m_iGoalAnim = SelectWeightedSequence ( ACT_1022 );
break;
case 3:
m_iGoalAnim = SelectWeightedSequence ( ACT_1023 );
break;
}
}
else
{
// idle
switch ( MyLevel() )
{
case 0:
m_iGoalAnim = SelectWeightedSequence ( ACT_1010 );
break;
case 1:
m_iGoalAnim = SelectWeightedSequence ( ACT_1011 );
break;
case 2:
m_iGoalAnim = SelectWeightedSequence ( ACT_1012 );
break;
case 3:
m_iGoalAnim = SelectWeightedSequence ( ACT_1013 );
break;
}
}
}
if (m_flSoundYaw < 0)
m_flSoundYaw += random->RandomFloat( 2, 8 );
else
m_flSoundYaw -= random->RandomFloat( 2, 8 );
}
SetSequence( FindTransitionSequence( GetSequence(), m_iGoalAnim, &m_iDir ) );
if (m_iDir > 0)
{
m_flCycle = 0;
}
else
{
m_iDir = -1; // just to safe
m_flCycle = 1.0f;
}
ResetSequenceInfo( );
m_flFramerateAdj = random->RandomFloat( -0.2, 0.2 );
m_flPlaybackRate = m_iDir * 1.0 + m_flFramerateAdj;
switch( GetSequence() )
{
case TENTACLE_ANIM_Floor_Tap:
case TENTACLE_ANIM_Lev1_Tap:
case TENTACLE_ANIM_Lev2_Tap:
case TENTACLE_ANIM_Lev3_Tap:
{
Vector vecSrc, v_forward;
AngleVectors( GetAbsAngles(), &v_forward );
trace_t tr1, tr2;
vecSrc = GetAbsOrigin() + Vector( 0, 0, MyHeight() - 4);
UTIL_TraceLine( vecSrc, vecSrc + v_forward * 512, MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr1 );
vecSrc = GetAbsOrigin() + Vector( 0, 0, MyHeight() + 8);
UTIL_TraceLine( vecSrc, vecSrc + v_forward * 512, MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr2 );
// ALERT( at_console, "%f %f\n", tr1.flFraction * 512, tr2.flFraction * 512 );
m_flTapRadius = SetPoseParameter( 0, random->RandomFloat( tr1.fraction * 512, tr2.fraction * 512 ) );
}
break;
default:
m_flTapRadius = 336; // 400 - 64
break;
}
SetViewOffset( Vector( 0, 0, MyHeight() ) );
// ALERT( at_console, "seq %d\n", pev->sequence );
}
if (m_flPrevSoundTime + 2.0 > gpGlobals->curtime)
{
// 1.5 normal speed if hears sounds
m_flPlaybackRate = m_iDir * 1.5 + m_flFramerateAdj;
}
else if (m_flPrevSoundTime + 5.0 > gpGlobals->curtime)
{
// slowdown to normal
m_flPlaybackRate = m_iDir + m_iDir * (5 - (gpGlobals->curtime - m_flPrevSoundTime)) / 2 + m_flFramerateAdj;
}
}
void CNPC_Tentacle::HandleAnimEvent( animevent_t *pEvent )
{
char *sound = NULL;
switch( pEvent->event )
{
case 1: // bang
{
Vector vecSrc;
QAngle angAngles;
GetAttachment( "0", vecSrc, angAngles );
// Vector vecSrc = GetAbsOrigin() + m_flTapRadius * Vector( cos( GetAbsAngles().y * (3.14192653 / 180.0) ), sin( GetAbsAngles().y * (M_PI / 180.0) ), 0.0 );
// vecSrc.z += MyHeight( );
switch( m_iTapSound )
{
case TE_SILO:
UTIL_EmitAmbientSound( this, vecSrc, RANDOM_SOUND_ARRAY( pHitSilo ), 1.0, SNDLVL_GUNFIRE, 0, 100);
break;
case TE_NONE:
break;
case TE_DIRT:
UTIL_EmitAmbientSound( this, vecSrc, RANDOM_SOUND_ARRAY( pHitDirt ), 1.0, SNDLVL_GUNFIRE, 0, 100);
break;
case TE_WATER:
UTIL_EmitAmbientSound( this, vecSrc, RANDOM_SOUND_ARRAY( pHitWater ), 1.0, SNDLVL_GUNFIRE, 0, 100);
break;
}
}
break;
case 3: // start killing swing
m_iHitDmg = 200;
// UTIL_EmitAmbientSound(this, GetAbsOrigin() + Vector( 0, 0, MyHeight()), "tentacle/te_swing1.wav", 1.0, SNDLVL_NORM, 0, 100);
break;
case 4: // end killing swing
m_iHitDmg = 25;
break;
case 5: // just "whoosh" sound
// UTIL_EmitAmbientSound(this, GetAbsOrigin() + Vector( 0, 0, MyHeight()), "tentacle/te_swing2.wav", 1.0, SNDLVL_NORM, 0, 100);
break;
case 2: // tap scrape
case 6: // light tap
{
Vector vecSrc = GetAbsOrigin() + m_flTapRadius * Vector( cos( GetAbsAngles().y * (M_PI / 180.0) ), sin( GetAbsAngles().y * (M_PI / 180.0) ), 0.0 );
vecSrc.z += MyHeight( );
float flVol = random->RandomFloat( 0.3, 0.5 );
switch( m_iTapSound )
{
case TE_SILO:
UTIL_EmitAmbientSound( this, vecSrc, RANDOM_SOUND_ARRAY( pHitSilo ), flVol, SNDLVL_GUNFIRE, 0, 100);
break;
case TE_NONE:
break;
case TE_DIRT:
UTIL_EmitAmbientSound( this, vecSrc, RANDOM_SOUND_ARRAY( pHitDirt ), flVol, SNDLVL_GUNFIRE, 0, 100);
break;
case TE_WATER:
UTIL_EmitAmbientSound( this, vecSrc, RANDOM_SOUND_ARRAY( pHitWater ), flVol, SNDLVL_GUNFIRE, 0, 100);
break;
}
}
break;
case 7: // roar
switch( random->RandomInt(0,1) )
{
case 0: sound = "tentacle/te_roar1.wav"; break;
case 1: sound = "tentacle/te_roar2.wav"; break;
}
UTIL_EmitAmbientSound( this, GetAbsOrigin() + Vector( 0, 0, MyHeight()), sound, 1.0, SNDLVL_GUNFIRE, 0, 100);
break;
case 8: // search
switch( random->RandomInt(0,1) )
{
case 0: sound = "tentacle/te_search1.wav"; break;
case 1: sound = "tentacle/te_search2.wav"; break;
}
UTIL_EmitAmbientSound( this, GetAbsOrigin() + Vector( 0, 0, MyHeight()), sound, 1.0, SNDLVL_GUNFIRE, 0, 100);
break;
case 9: // swing
switch( random->RandomInt(0,1) )
{
case 0: sound = "tentacle/te_move1.wav"; break;
case 1: sound = "tentacle/te_move2.wav"; break;
}
UTIL_EmitAmbientSound( this, GetAbsOrigin() + Vector( 0, 0, MyHeight()), sound, 1.0, SNDLVL_GUNFIRE, 0, 100);
break;
default:
BaseClass::HandleAnimEvent( pEvent );
}
}
void CNPC_Tentacle::HitTouch( CBaseEntity *pOther )
{
trace_t tr = CBaseEntity::GetTouchTrace( );
if (m_flHitTime > gpGlobals->curtime)
return;
// only look at the ones where the player hit me
if( pOther == NULL || pOther->GetModelIndex() == GetModelIndex() || (pOther->GetSolidFlags() & FSOLID_TRIGGER) )
return;
//Right now the BoneFollower will always be hit in box 0, and
//will pass that to us. Make *any* touch by the physics objects a kill
//as the ragdoll only covers the top portion of the tentacle.
CTakeDamageInfo info( this, this, m_iHitDmg, DMG_CLUB );
CalculateMeleeDamageForce( &info, (pOther->GetAbsOrigin() - GetAbsOrigin()), pOther->GetAbsOrigin() );
pOther->TakeDamage( info );
m_flHitTime = gpGlobals->curtime + 0.5;
}
int CNPC_Tentacle::OnTakeDamage( const CTakeDamageInfo &info )
{
CTakeDamageInfo i = info;
//Don't allow the tentacle to die. Instead set health to 1, so we can do our own death and rebirth
if( i.GetDamage() > m_iHealth )
{
i.SetDamage( 0.0f );
m_iHealth = 1;
}
return BaseClass::OnTakeDamage( i );
}
bool CNPC_Tentacle::CreateVPhysics( void )
{
BaseClass::CreateVPhysics();
IPhysicsObject *pPhysics = VPhysicsGetObject();
if( pPhysics )
{
unsigned short flags = pPhysics->GetCallbackFlags();
flags |= CALLBACK_GLOBAL_TOUCH;
pPhysics->SetCallbackFlags( flags );
}
m_BoneFollowerManager.InitBoneFollowers( this, ARRAYSIZE(pTentacleFollowerBoneNames), pTentacleFollowerBoneNames );
return true;
}
//------------------------------------------------------------------------------
//
// Schedules
//
//------------------------------------------------------------------------------
AI_BEGIN_CUSTOM_NPC( monster_tentacle, CNPC_Tentacle )
DECLARE_ACTIVITY( ACT_1010 )
DECLARE_ACTIVITY( ACT_1011 )
DECLARE_ACTIVITY( ACT_1012 )
DECLARE_ACTIVITY( ACT_1013 )
DECLARE_ACTIVITY( ACT_1020 )
DECLARE_ACTIVITY( ACT_1021 )
DECLARE_ACTIVITY( ACT_1022 )
DECLARE_ACTIVITY( ACT_1023 )
DECLARE_ACTIVITY( ACT_1030 )
DECLARE_ACTIVITY( ACT_1031 )
DECLARE_ACTIVITY( ACT_1032 )
DECLARE_ACTIVITY( ACT_1033 )
DECLARE_ACTIVITY( ACT_1040 )
DECLARE_ACTIVITY( ACT_1041 )
DECLARE_ACTIVITY( ACT_1042 )
DECLARE_ACTIVITY( ACT_1043 )
DECLARE_ACTIVITY( ACT_1044 )
AI_END_CUSTOM_NPC()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -