diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl1/hl1_npc_tentacle.cpp | |
| download | archived-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.cpp | 1012 |
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() |