summaryrefslogtreecommitdiff
path: root/game/server/hl1/hl1_npc_tentacle.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_tentacle.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_tentacle.cpp')
-rw-r--r--game/server/hl1/hl1_npc_tentacle.cpp1012
1 files changed, 1012 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_tentacle.cpp b/game/server/hl1/hl1_npc_tentacle.cpp
new file mode 100644
index 0000000..c4cc8d1
--- /dev/null
+++ b/game/server/hl1/hl1_npc_tentacle.cpp
@@ -0,0 +1,1012 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
+// events
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "ai_default.h"
+#include "ai_task.h"
+#include "ai_schedule.h"
+#include "ai_node.h"
+#include "ai_hull.h"
+#include "ai_hint.h"
+#include "ai_memory.h"
+#include "ai_route.h"
+#include "ai_motor.h"
+#include "ai_senses.h"
+#include "soundent.h"
+#include "game.h"
+#include "npcevent.h"
+#include "entitylist.h"
+#include "activitylist.h"
+#include "animation.h"
+#include "basecombatweapon.h"
+#include "IEffects.h"
+#include "vstdlib/random.h"
+#include "engine/IEngineSound.h"
+#include "ammodef.h"
+#include "hl1_ai_basenpc.h"
+#include "studio.h" //hitbox parsing
+#include "collisionutils.h" //ComputeSeparatingPlane
+
+#include "physics_bone_follower.h" //For BoneFollowerManager
+
+#define ACT_T_IDLE 1010
+Activity ACT_1010;
+Activity ACT_1011;
+Activity ACT_1012;
+Activity ACT_1013;
+
+#define ACT_T_TAP 1020
+Activity ACT_1020;
+Activity ACT_1021;
+Activity ACT_1022;
+Activity ACT_1023;
+
+#define ACT_T_STRIKE 1030
+Activity ACT_1030;
+Activity ACT_1031;
+Activity ACT_1032;
+Activity ACT_1033;
+
+#define ACT_T_REARIDLE 1040
+Activity ACT_1040;
+Activity ACT_1041;
+Activity ACT_1042;
+Activity ACT_1043;
+Activity ACT_1044;
+
+class CNPC_Tentacle : public CHL1BaseNPC
+{
+ DECLARE_CLASS( CNPC_Tentacle, CHL1BaseNPC );
+public:
+
+ CNPC_Tentacle();
+
+ void Spawn( );
+ void Precache( );
+ bool KeyValue( const char *szKeyName, const char *szValue );
+
+ bool QueryHearSound( CSound *pSound ) { return true; } // Tentacle isn't picky.
+
+
+ int Level( float dz );
+ int MyLevel( void );
+ float MyHeight( void );
+
+ // Don't allow the tentacle to go across transitions!!!
+ virtual int ObjectCaps( void ) { return CAI_BaseNPC::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
+
+ void Start ( void );
+ void Cycle ( void );
+ void HitTouch( CBaseEntity *pOther );
+
+ void HandleAnimEvent( animevent_t *pEvent );
+ float HearingSensitivity( void ) { return 2.0; };
+
+ virtual int OnTakeDamage( const CTakeDamageInfo &info );
+
+ bool CreateVPhysics( void );
+
+ void UpdateOnRemove( void );
+
+ float m_flInitialYaw;
+ int m_iGoalAnim;
+ int m_iLevel;
+ int m_iDir;
+ float m_flFramerateAdj;
+ float m_flSoundYaw;
+ int m_iSoundLevel;
+ float m_flSoundTime;
+ float m_flSoundRadius;
+ int m_iHitDmg;
+ float m_flHitTime;
+
+ float m_flTapRadius;
+
+ float m_flNextSong;
+ static int g_fFlySound;
+ static int g_fSquirmSound;
+
+ float m_flMaxYaw;
+ int m_iTapSound;
+
+ Vector m_vecPrevSound;
+ float m_flPrevSoundTime;
+
+ float MaxYawSpeed( void ) { return 18.0f; }
+
+ bool HeardAnything( void );
+
+ Class_T Classify ( void );
+
+ CBoneFollowerManager m_BoneFollowerManager;
+
+ DECLARE_DATADESC();
+ DEFINE_CUSTOM_AI;
+};
+
+// Crane bones that have physics followers
+static const char *pTentacleFollowerBoneNames[] =
+{
+ "Bone08",
+ "Bone09"
+};
+
+int CNPC_Tentacle::g_fFlySound;
+int CNPC_Tentacle::g_fSquirmSound;
+
+LINK_ENTITY_TO_CLASS( monster_tentacle, CNPC_Tentacle );
+
+// stike sounds
+#define TE_NONE -1
+#define TE_SILO 0
+#define TE_DIRT 1
+#define TE_WATER 2
+
+// animation sequence aliases
+typedef enum
+{
+ TENTACLE_ANIM_Pit_Idle,
+
+ TENTACLE_ANIM_rise_to_Temp1,
+ TENTACLE_ANIM_Temp1_to_Floor,
+ TENTACLE_ANIM_Floor_Idle,
+ TENTACLE_ANIM_Floor_Fidget_Pissed,
+ TENTACLE_ANIM_Floor_Fidget_SmallRise,
+ TENTACLE_ANIM_Floor_Fidget_Wave,
+ TENTACLE_ANIM_Floor_Strike,
+ TENTACLE_ANIM_Floor_Tap,
+ TENTACLE_ANIM_Floor_Rotate,
+ TENTACLE_ANIM_Floor_Rear,
+ TENTACLE_ANIM_Floor_Rear_Idle,
+ TENTACLE_ANIM_Floor_to_Lev1,
+
+ TENTACLE_ANIM_Lev1_Idle,
+ TENTACLE_ANIM_Lev1_Fidget_Claw,
+ TENTACLE_ANIM_Lev1_Fidget_Shake,
+ TENTACLE_ANIM_Lev1_Fidget_Snap,
+ TENTACLE_ANIM_Lev1_Strike,
+ TENTACLE_ANIM_Lev1_Tap,
+ TENTACLE_ANIM_Lev1_Rotate,
+ TENTACLE_ANIM_Lev1_Rear,
+ TENTACLE_ANIM_Lev1_Rear_Idle,
+ TENTACLE_ANIM_Lev1_to_Lev2,
+
+ TENTACLE_ANIM_Lev2_Idle,
+ TENTACLE_ANIM_Lev2_Fidget_Shake,
+ TENTACLE_ANIM_Lev2_Fidget_Swing,
+ TENTACLE_ANIM_Lev2_Fidget_Tut,
+ TENTACLE_ANIM_Lev2_Strike,
+ TENTACLE_ANIM_Lev2_Tap,
+ TENTACLE_ANIM_Lev2_Rotate,
+ TENTACLE_ANIM_Lev2_Rear,
+ TENTACLE_ANIM_Lev2_Rear_Idle,
+ TENTACLE_ANIM_Lev2_to_Lev3,
+
+ TENTACLE_ANIM_Lev3_Idle,
+ TENTACLE_ANIM_Lev3_Fidget_Shake,
+ TENTACLE_ANIM_Lev3_Fidget_Side,
+ TENTACLE_ANIM_Lev3_Fidget_Swipe,
+ TENTACLE_ANIM_Lev3_Strike,
+ TENTACLE_ANIM_Lev3_Tap,
+ TENTACLE_ANIM_Lev3_Rotate,
+ TENTACLE_ANIM_Lev3_Rear,
+ TENTACLE_ANIM_Lev3_Rear_Idle,
+
+ TENTACLE_ANIM_Lev1_Door_reach,
+
+ TENTACLE_ANIM_Lev3_to_Engine,
+ TENTACLE_ANIM_Engine_Idle,
+ TENTACLE_ANIM_Engine_Sway,
+ TENTACLE_ANIM_Engine_Swat,
+ TENTACLE_ANIM_Engine_Bob,
+ TENTACLE_ANIM_Engine_Death1,
+ TENTACLE_ANIM_Engine_Death2,
+ TENTACLE_ANIM_Engine_Death3,
+
+ TENTACLE_ANIM_none
+} TENTACLE_ANIM;
+
+BEGIN_DATADESC( CNPC_Tentacle )
+ DEFINE_FIELD( m_flInitialYaw, FIELD_FLOAT ),
+ DEFINE_FIELD( m_iGoalAnim, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iLevel, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iDir, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flFramerateAdj, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flSoundYaw, FIELD_FLOAT ),
+ DEFINE_FIELD( m_iSoundLevel, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flSoundTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flSoundRadius, FIELD_FLOAT ),
+ DEFINE_FIELD( m_iHitDmg, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flHitTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flTapRadius, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flNextSong, FIELD_TIME ),
+ DEFINE_FIELD( m_iTapSound, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flMaxYaw, FIELD_FLOAT ),
+ DEFINE_FIELD( m_vecPrevSound, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_flPrevSoundTime, FIELD_TIME ),
+
+ DEFINE_EMBEDDED( m_BoneFollowerManager ),
+
+ DEFINE_THINKFUNC( Start ),
+ DEFINE_THINKFUNC( Cycle ),
+ DEFINE_ENTITYFUNC( HitTouch ),
+END_DATADESC()
+
+Class_T CNPC_Tentacle::Classify ( void )
+{
+ return CLASS_ALIEN_MONSTER;
+}
+
+
+CNPC_Tentacle::CNPC_Tentacle()
+{
+ m_flMaxYaw = 65;
+ m_iTapSound = 0;
+}
+
+bool CNPC_Tentacle::KeyValue( const char *szKeyName, const char *szValue )
+{
+ if ( FStrEq( szKeyName, "sweeparc") )
+ {
+ m_flMaxYaw = atof( szValue ) / 2.0;
+ return true;
+ }
+ else if (FStrEq( szKeyName, "sound"))
+ {
+ m_iTapSound = atoi( szValue );
+ return true;
+ }
+ else
+ return BaseClass::KeyValue( szKeyName, szValue );
+
+ return false;
+}
+
+//
+// Tentacle Spawn
+//
+void CNPC_Tentacle::Spawn( )
+{
+ Precache( );
+
+ SetSolid( SOLID_BBOX );
+
+ //Necessary for TestCollision to be called for hitbox ray hits
+ AddSolidFlags( FSOLID_CUSTOMRAYTEST );
+
+ SetMoveType( MOVETYPE_NONE );
+ ClearEffects();
+ m_iHealth = 75;
+ SetSequence( 0 );
+
+ SetModel( "models/tentacle2.mdl" );
+ UTIL_SetSize( this, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
+
+ // Use our hitboxes to determine our render bounds
+ CollisionProp()->SetSurroundingBoundsType( USE_HITBOXES );
+
+ m_takedamage = DAMAGE_AIM;
+ AddFlag( FL_NPC );
+
+ m_bloodColor = BLOOD_COLOR_GREEN;
+
+ ResetSequenceInfo( );
+ m_iDir = 1;
+
+ SetThink( &CNPC_Tentacle::Start );
+ SetNextThink( gpGlobals->curtime + 0.2 );
+
+ SetTouch( &CNPC_Tentacle::HitTouch );
+
+ m_flInitialYaw = GetAbsAngles().y;
+ GetMotor()->SetIdealYawAndUpdate( m_flInitialYaw );
+
+ g_fFlySound = FALSE;
+ g_fSquirmSound = FALSE;
+
+ m_iHitDmg = 200;
+
+ if (m_flMaxYaw <= 0)
+ m_flMaxYaw = 65;
+
+ m_NPCState = NPC_STATE_IDLE;
+
+ UTIL_SetOrigin( this, GetAbsOrigin() );
+
+ CreateVPhysics();
+
+ AddEffects( EF_NOSHADOW );
+}
+
+void CNPC_Tentacle::UpdateOnRemove( void )
+{
+ m_BoneFollowerManager.DestroyBoneFollowers();
+ BaseClass::UpdateOnRemove();
+}
+
+void CNPC_Tentacle::Precache( )
+{
+ PrecacheModel("models/tentacle2.mdl");
+
+ PrecacheScriptSound( "Tentacle.Flies" );
+ PrecacheScriptSound( "Tentacle.Squirm" );
+ PrecacheScriptSound( "Tentacle.Sing" );
+ PrecacheScriptSound( "Tentacle.HitDirt" );
+ PrecacheScriptSound( "Tentacle.Swing" );
+ PrecacheScriptSound( "Tentacle.Search" );
+ PrecacheScriptSound( "Tentacle.Roar" );
+ PrecacheScriptSound( "Tentacle.Alert" );
+
+ BaseClass::Precache();
+}
+
+
+int CNPC_Tentacle::Level( float dz )
+{
+ if (dz < 216)
+ return 0;
+ if (dz < 408)
+ return 1;
+ if (dz < 600)
+ return 2;
+ return 3;
+}
+
+
+float CNPC_Tentacle::MyHeight( )
+{
+ switch ( MyLevel( ) )
+ {
+ case 1:
+ return 256;
+ case 2:
+ return 448;
+ case 3:
+ return 640;
+ }
+ return 0;
+}
+
+
+int CNPC_Tentacle::MyLevel( )
+{
+ switch( GetSequence() )
+ {
+ case TENTACLE_ANIM_Pit_Idle:
+ return -1;
+
+ case TENTACLE_ANIM_rise_to_Temp1:
+ case TENTACLE_ANIM_Temp1_to_Floor:
+ case TENTACLE_ANIM_Floor_to_Lev1:
+ return 0;
+
+ case TENTACLE_ANIM_Floor_Idle:
+ case TENTACLE_ANIM_Floor_Fidget_Pissed:
+ case TENTACLE_ANIM_Floor_Fidget_SmallRise:
+ case TENTACLE_ANIM_Floor_Fidget_Wave:
+ case TENTACLE_ANIM_Floor_Strike:
+ case TENTACLE_ANIM_Floor_Tap:
+ case TENTACLE_ANIM_Floor_Rotate:
+ case TENTACLE_ANIM_Floor_Rear:
+ case TENTACLE_ANIM_Floor_Rear_Idle:
+ return 0;
+
+ case TENTACLE_ANIM_Lev1_Idle:
+ case TENTACLE_ANIM_Lev1_Fidget_Claw:
+ case TENTACLE_ANIM_Lev1_Fidget_Shake:
+ case TENTACLE_ANIM_Lev1_Fidget_Snap:
+ case TENTACLE_ANIM_Lev1_Strike:
+ case TENTACLE_ANIM_Lev1_Tap:
+ case TENTACLE_ANIM_Lev1_Rotate:
+ case TENTACLE_ANIM_Lev1_Rear:
+ case TENTACLE_ANIM_Lev1_Rear_Idle:
+ return 1;
+
+ case TENTACLE_ANIM_Lev1_to_Lev2:
+ return 1;
+
+ case TENTACLE_ANIM_Lev2_Idle:
+ case TENTACLE_ANIM_Lev2_Fidget_Shake:
+ case TENTACLE_ANIM_Lev2_Fidget_Swing:
+ case TENTACLE_ANIM_Lev2_Fidget_Tut:
+ case TENTACLE_ANIM_Lev2_Strike:
+ case TENTACLE_ANIM_Lev2_Tap:
+ case TENTACLE_ANIM_Lev2_Rotate:
+ case TENTACLE_ANIM_Lev2_Rear:
+ case TENTACLE_ANIM_Lev2_Rear_Idle:
+ return 2;
+
+ case TENTACLE_ANIM_Lev2_to_Lev3:
+ return 2;
+
+ case TENTACLE_ANIM_Lev3_Idle:
+ case TENTACLE_ANIM_Lev3_Fidget_Shake:
+ case TENTACLE_ANIM_Lev3_Fidget_Side:
+ case TENTACLE_ANIM_Lev3_Fidget_Swipe:
+ case TENTACLE_ANIM_Lev3_Strike:
+ case TENTACLE_ANIM_Lev3_Tap:
+ case TENTACLE_ANIM_Lev3_Rotate:
+ case TENTACLE_ANIM_Lev3_Rear:
+ case TENTACLE_ANIM_Lev3_Rear_Idle:
+ return 3;
+
+ case TENTACLE_ANIM_Lev1_Door_reach:
+ return -1;
+ }
+ return -1;
+}
+
+void CNPC_Tentacle::Start( void )
+{
+ SetThink( &CNPC_Tentacle::Cycle );
+
+ CPASAttenuationFilter filter( this );
+
+ if ( !g_fFlySound )
+ {
+ EmitSound( filter, entindex(), "Tentacle.Flies" );
+ g_fFlySound = TRUE;
+ }
+ else if ( !g_fSquirmSound )
+ {
+ EmitSound( filter, entindex(), "Tentacle.Squirm" );
+ g_fSquirmSound = TRUE;
+ }
+
+ SetNextThink( gpGlobals->curtime + 0.1 );
+}
+
+bool CNPC_Tentacle::HeardAnything( void )
+{
+ if ( HasCondition( COND_HEAR_DANGER ) || // I remove a bunch of sounds from here on purpose. Talk to me if you're changing this!(sjb)
+ HasCondition( COND_HEAR_COMBAT ) ||
+ HasCondition( COND_HEAR_WORLD ) ||
+ HasCondition( COND_HEAR_PLAYER ) )
+ return true;
+
+ return false;
+}
+
+void CNPC_Tentacle::Cycle( void )
+{
+ //NDebugOverlay::Cross3D( EarPosition(), 32, 255, 0, 0, false, 0.1 );
+
+ // ALERT( at_console, "%s %.2f %d %d\n", STRING( pev->targetname ), pev->origin.z, m_MonsterState, m_IdealMonsterState );
+ SetNextThink( gpGlobals->curtime + 0.1 );
+
+ // ALERT( at_console, "%s %d %d %d %f %f\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim, m_iDir, pev->framerate, pev->health );
+
+ if ( m_NPCState == NPC_STATE_SCRIPT || GetIdealState() == NPC_STATE_SCRIPT)
+ {
+ SetAbsAngles( QAngle( GetAbsAngles().x, m_flInitialYaw, GetAbsAngles().z ) );
+ GetMotor()->SetIdealYaw( m_flInitialYaw );
+ RemoveIgnoredConditions();
+ NPCThink( );
+ m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
+ return;
+ }
+
+ StudioFrameAdvance();
+ DispatchAnimEvents( this );
+
+ GetMotor()->UpdateYaw( MaxYawSpeed() );
+
+ CSound *pSound = NULL;
+
+ GetSenses()->Listen();
+ m_BoneFollowerManager.UpdateBoneFollowers(this);
+
+ // Listen will set this if there's something in my sound list
+ if ( HeardAnything() )
+ pSound = GetSenses()->GetClosestSound( false, (SOUND_DANGER|SOUND_COMBAT|SOUND_WORLD|SOUND_PLAYER) );
+ else
+ pSound = NULL;
+
+ if ( pSound )
+ {
+ //NDebugOverlay::Line( EarPosition(), pSound->GetSoundOrigin(), 0, 255, 0, false, 0.2 );
+
+ 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
+ UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Alert", 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
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Tentacle.Sing" );
+
+ 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)
+ {
+ SetCycle( 0 );
+ }
+ else
+ {
+ m_iDir = -1; // just to safe
+ SetCycle( 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 )
+{
+ 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( GetSoundSourceIndex(), vecSrc, "Tentacle.HitSilo", 1.0, SNDLVL_GUNFIRE, 0, 100);
+ break;
+ case TE_NONE:
+ break;
+ case TE_DIRT:
+ UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitDirt", 1.0, SNDLVL_GUNFIRE, 0, 100);
+ break;
+ case TE_WATER:
+ UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitWater", 1.0, SNDLVL_GUNFIRE, 0, 100);
+ break;
+ }
+ }
+ break;
+
+ case 3: // start killing swing
+ m_iHitDmg = 200;
+ break;
+
+ case 4: // end killing swing
+ m_iHitDmg = 25;
+ break;
+
+ case 5: // just "whoosh" sound
+ 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( GetSoundSourceIndex(), vecSrc, "Tentacle.HitSilo", flVol, SNDLVL_GUNFIRE, 0, 100);
+ break;
+ case TE_NONE:
+ break;
+ case TE_DIRT:
+ UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitDirt", flVol, SNDLVL_GUNFIRE, 0, 100);
+ break;
+ case TE_WATER:
+ UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitWater", flVol, SNDLVL_GUNFIRE, 0, 100);
+ break;
+ }
+ }
+ break;
+
+
+ case 7: // roar
+ UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Roar", 1.0, SNDLVL_GUNFIRE, 0, 100);
+ break;
+
+ case 8: // search
+ UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Search", 1.0, SNDLVL_GUNFIRE, 0, 100);
+ break;
+
+ case 9: // swing
+ UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Swing", 1.0, SNDLVL_GUNFIRE, 0, 100);
+ break;
+ default:
+ BaseClass::HandleAnimEvent( pEvent );
+ }
+}
+
+void CNPC_Tentacle::HitTouch( CBaseEntity *pOther )
+{
+ 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.
+ if ( pOther->m_takedamage )
+ {
+ CTakeDamageInfo info( this, this, m_iHitDmg, DMG_CLUB );
+
+ Vector vDamageForce = pOther->GetAbsOrigin() - GetAbsOrigin();
+ VectorNormalize( vDamageForce );
+
+ CalculateMeleeDamageForce( &info, vDamageForce, 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( (int)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()