/*** * * Copyright (c) 1996-2002, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * * This source code contains proprietary and confidential information of * Valve LLC and its suppliers. Access to this code is restricted to * persons who have executed a written SDK license with Valve. Any access, * use or distribution of this code by or to any unlicensed person is illegal. * ****/ #if !OEM_BUILD && !HLDEMO_BUILD /* h_tentacle.cpp - silo of death tentacle monster (half life) */ #include "extdll.h" #include "util.h" #include "cbase.h" #include "monsters.h" #include "weapons.h" #include "soundent.h" #define ACT_T_IDLE 1010 #define ACT_T_TAP 1020 #define ACT_T_STRIKE 1030 #define ACT_T_REARIDLE 1040 class CTentacle : public CBaseMonster { public: CTentacle( void ); void Spawn(); void Precache(); void KeyValue( KeyValueData *pkvd ); int Save( CSave &save ); int Restore( CRestore &restore ); static TYPEDESCRIPTION m_SaveData[]; // Don't allow the tentacle to go across transitions!!! virtual int ObjectCaps( void ) { return CBaseMonster::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } void SetObjectCollisionBox( void ) { pev->absmin = pev->origin + Vector( -400, -400, 0 ); pev->absmax = pev->origin + Vector( 400, 400, 850 ); } void EXPORT Cycle( void ); void EXPORT CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); void EXPORT Start( void ); void EXPORT DieThink( void ); void EXPORT Test( void ); void EXPORT HitTouch( CBaseEntity *pOther ); float HearingSensitivity( void ) { return 2.0; }; int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); void HandleAnimEvent( MonsterEvent_t *pEvent ); void Killed( entvars_t *pevAttacker, int iGib ); MONSTERSTATE GetIdealState( void ) { return MONSTERSTATE_IDLE; }; int CanPlaySequence( BOOL fDisregardState ) { return TRUE; }; int Classify( void ); virtual int SizeForGrapple() { return GRAPPLE_FIXED; } int Level( float dz ); int MyLevel( void ); float MyHeight( 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; static const char *pHitSilo[]; static const char *pHitDirt[]; static const char *pHitWater[]; }; int CTentacle::g_fFlySound; int CTentacle::g_fSquirmSound; LINK_ENTITY_TO_CLASS( monster_tentacle, CTentacle ) // stike sounds #define TE_NONE -1 #define TE_SILO 0 #define TE_DIRT 1 #define TE_WATER 2 const char *CTentacle::pHitSilo[] = { "tentacle/te_strike1.wav", "tentacle/te_strike2.wav", }; const char *CTentacle::pHitDirt[] = { "player/pl_dirt1.wav", "player/pl_dirt2.wav", "player/pl_dirt3.wav", "player/pl_dirt4.wav", }; const char *CTentacle::pHitWater[] = { "player/pl_slosh1.wav", "player/pl_slosh2.wav", "player/pl_slosh3.wav", "player/pl_slosh4.wav", }; TYPEDESCRIPTION CTentacle::m_SaveData[] = { DEFINE_FIELD( CTentacle, m_flInitialYaw, FIELD_FLOAT ), DEFINE_FIELD( CTentacle, m_iGoalAnim, FIELD_INTEGER ), DEFINE_FIELD( CTentacle, m_iLevel, FIELD_INTEGER ), DEFINE_FIELD( CTentacle, m_iDir, FIELD_INTEGER ), DEFINE_FIELD( CTentacle, m_flFramerateAdj, FIELD_FLOAT ), DEFINE_FIELD( CTentacle, m_flSoundYaw, FIELD_FLOAT ), DEFINE_FIELD( CTentacle, m_iSoundLevel, FIELD_INTEGER ), DEFINE_FIELD( CTentacle, m_flSoundTime, FIELD_TIME ), DEFINE_FIELD( CTentacle, m_flSoundRadius, FIELD_FLOAT ), DEFINE_FIELD( CTentacle, m_iHitDmg, FIELD_INTEGER ), DEFINE_FIELD( CTentacle, m_flHitTime, FIELD_TIME ), DEFINE_FIELD( CTentacle, m_flTapRadius, FIELD_FLOAT ), DEFINE_FIELD( CTentacle, m_flNextSong, FIELD_TIME ), DEFINE_FIELD( CTentacle, m_iTapSound, FIELD_INTEGER ), DEFINE_FIELD( CTentacle, m_flMaxYaw, FIELD_FLOAT ), DEFINE_FIELD( CTentacle, m_vecPrevSound, FIELD_POSITION_VECTOR ), DEFINE_FIELD( CTentacle, m_flPrevSoundTime, FIELD_TIME ), }; IMPLEMENT_SAVERESTORE( CTentacle, CBaseMonster ) // 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; //========================================================= // Classify - indicates this monster's place in the // relationship table. //========================================================= int CTentacle::Classify( void ) { return CLASS_ALIEN_MONSTER; } // // Tentacle Spawn // void CTentacle::Spawn() { Precache(); pev->solid = SOLID_BBOX; pev->movetype = MOVETYPE_FLY; pev->effects = 0; pev->health = 75; pev->sequence = 0; SET_MODEL( ENT( pev ), "models/tentacle2.mdl" ); UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); pev->takedamage = DAMAGE_AIM; pev->flags |= FL_MONSTER; m_bloodColor = BLOOD_COLOR_GREEN; SetThink( &CTentacle::Start ); SetTouch( &CTentacle::HitTouch ); SetUse( &CTentacle::CommandUse ); pev->nextthink = gpGlobals->time + 0.2f; ResetSequenceInfo(); m_iDir = 1; pev->yaw_speed = 18; m_flInitialYaw = pev->angles.y; pev->ideal_yaw = m_flInitialYaw; g_fFlySound = FALSE; g_fSquirmSound = FALSE; m_iHitDmg = 20; if( m_flMaxYaw <= 0 ) m_flMaxYaw = 65; m_MonsterState = MONSTERSTATE_IDLE; // SetThink( &Test ); UTIL_SetOrigin( pev, pev->origin ); } void CTentacle::Precache() { PRECACHE_MODEL( "models/tentacle2.mdl" ); PRECACHE_SOUND( "ambience/flies.wav" ); PRECACHE_SOUND( "ambience/squirm2.wav" ); PRECACHE_SOUND( "tentacle/te_alert1.wav" ); PRECACHE_SOUND( "tentacle/te_alert2.wav" ); PRECACHE_SOUND( "tentacle/te_flies1.wav" ); PRECACHE_SOUND( "tentacle/te_move1.wav" ); PRECACHE_SOUND( "tentacle/te_move2.wav" ); PRECACHE_SOUND( "tentacle/te_roar1.wav" ); PRECACHE_SOUND( "tentacle/te_roar2.wav" ); PRECACHE_SOUND( "tentacle/te_search1.wav" ); PRECACHE_SOUND( "tentacle/te_search2.wav" ); PRECACHE_SOUND( "tentacle/te_sing1.wav" ); PRECACHE_SOUND( "tentacle/te_sing2.wav" ); PRECACHE_SOUND( "tentacle/te_squirm2.wav" ); PRECACHE_SOUND( "tentacle/te_strike1.wav" ); PRECACHE_SOUND( "tentacle/te_strike2.wav" ); PRECACHE_SOUND( "tentacle/te_swing1.wav" ); PRECACHE_SOUND( "tentacle/te_swing2.wav" ); PRECACHE_SOUND_ARRAY( pHitSilo ); PRECACHE_SOUND_ARRAY( pHitDirt ); PRECACHE_SOUND_ARRAY( pHitWater ); } CTentacle::CTentacle() { m_flMaxYaw = 65; m_iTapSound = 0; } void CTentacle::KeyValue( KeyValueData *pkvd ) { if( FStrEq( pkvd->szKeyName, "sweeparc" ) ) { m_flMaxYaw = atof( pkvd->szValue ) * 0.5f; pkvd->fHandled = TRUE; } else if( FStrEq( pkvd->szKeyName, "sound" ) ) { m_iTapSound = atoi( pkvd->szValue ); pkvd->fHandled = TRUE; } else CBaseMonster::KeyValue( pkvd ); } int CTentacle::Level( float dz ) { if( dz < 96 ) return 0; if( dz < 150 ) return 1; if( dz < 288 ) return 2; return 3; } float CTentacle::MyHeight() { switch( MyLevel() ) { case 1: return 136; case 2: return 190; case 3: return 328; } return 0; } int CTentacle::MyLevel() { switch( pev->sequence ) { 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 CTentacle::Test( void ) { pev->sequence = TENTACLE_ANIM_Floor_Strike; pev->framerate = 0; StudioFrameAdvance(); pev->nextthink = gpGlobals->time + 0.1f; } // // TentacleThink // void CTentacle::Cycle( void ) { // ALERT( at_console, "%s %.2f %d %d\n", STRING( pev->targetname ), pev->origin.z, m_MonsterState, m_IdealMonsterState ); pev->nextthink = gpGlobals-> time + 0.1f; // 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_MonsterState == MONSTERSTATE_SCRIPT || m_IdealMonsterState == MONSTERSTATE_SCRIPT ) { pev->angles.y = m_flInitialYaw; pev->ideal_yaw = m_flInitialYaw; ClearConditions( IgnoreConditions() ); MonsterThink(); m_iGoalAnim = TENTACLE_ANIM_Pit_Idle; return; } DispatchAnimEvents(); StudioFrameAdvance(); ChangeYaw( pev->yaw_speed ); CSound *pSound; Listen(); // Listen will set this if there's something in my sound list if( HasConditions( bits_COND_HEAR_SOUND ) ) pSound = PBestSound(); else pSound = NULL; if( pSound ) { Vector vecDir; if( gpGlobals->time - m_flPrevSoundTime < 0.5f ) { float dt = gpGlobals->time - m_flPrevSoundTime; vecDir = pSound->m_vecOrigin + ( pSound->m_vecOrigin - m_vecPrevSound ) / dt - pev->origin; } else { vecDir = pSound->m_vecOrigin - pev->origin; } m_flPrevSoundTime = gpGlobals->time; m_vecPrevSound = pSound->m_vecOrigin; m_flSoundYaw = UTIL_VecToYaw( vecDir ) - m_flInitialYaw; m_iSoundLevel = Level( vecDir.z ); if( m_flSoundYaw < -180 ) m_flSoundYaw += 360; if( m_flSoundYaw > 180 ) m_flSoundYaw -= 360; #if 0 // ALERT( at_console, "sound %d %.0f\n", m_iSoundLevel, m_flSoundYaw ); if( m_flSoundTime < gpGlobals->time ) { // play "I hear new something" sound const char *sound; switch( RANDOM_LONG( 0, 1 ) ) { case 0: sound = "tentacle/te_alert1.wav"; break; case 1: sound = "tentacle/te_alert2.wav"; break; } // UTIL_EmitAmbientSound( ENT( pev ), pev->origin + Vector( 0, 0, MyHeight() ), sound, 1.0, ATTN_NORM, 0, 100 ); } #endif m_flSoundTime = gpGlobals->time + RANDOM_FLOAT( 5.0f, 10.0f ); } // clip ideal_yaw float dy = m_flSoundYaw; switch( pev->sequence ) { 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; } pev->ideal_yaw = m_flInitialYaw + dy; if( m_fSequenceFinished ) { // ALERT( at_console, "%s done %d %d\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim ); if( pev->health <= 1 ) { m_iGoalAnim = TENTACLE_ANIM_Pit_Idle; if( pev->sequence == TENTACLE_ANIM_Pit_Idle ) { pev->health = 75; } } else if( m_flSoundTime > gpGlobals->time ) { if( m_flSoundYaw >= -( m_flMaxYaw + 30 ) && m_flSoundYaw <= ( m_flMaxYaw + 30 ) ) { // strike m_iGoalAnim = LookupActivity( ACT_T_STRIKE + m_iSoundLevel ); } else if( m_flSoundYaw >= -m_flMaxYaw * 2 && m_flSoundYaw <= m_flMaxYaw * 2 ) { // tap m_iGoalAnim = LookupActivity( ACT_T_TAP + m_iSoundLevel ); } else { // go into rear idle m_iGoalAnim = LookupActivity( ACT_T_REARIDLE + m_iSoundLevel ); } } else if( pev->sequence == TENTACLE_ANIM_Pit_Idle ) { // stay in pit until hear noise m_iGoalAnim = TENTACLE_ANIM_Pit_Idle; } else if( pev->sequence == m_iGoalAnim ) { if( MyLevel() >= 0 && gpGlobals->time < m_flSoundTime ) { if( RANDOM_LONG( 0, 9 ) < m_flSoundTime - gpGlobals->time ) { // continue stike m_iGoalAnim = LookupActivity( ACT_T_STRIKE + m_iSoundLevel ); } else { // tap m_iGoalAnim = LookupActivity( ACT_T_TAP + m_iSoundLevel ); } } else if( MyLevel() < 0 ) { m_iGoalAnim = LookupActivity( ACT_T_IDLE + 0 ); } else { if( m_flNextSong < gpGlobals->time ) { // play "I hear new something" sound const char *sound; switch( RANDOM_LONG( 0, 1 ) ) { case 0: sound = "tentacle/te_sing1.wav"; break; case 1: sound = "tentacle/te_sing2.wav"; break; } EMIT_SOUND( ENT( pev ), CHAN_VOICE, sound, 1.0, ATTN_NORM ); m_flNextSong = gpGlobals->time + RANDOM_FLOAT( 10, 20 ); } if( RANDOM_LONG( 0,15 ) == 0 ) { // idle on new level m_iGoalAnim = LookupActivity( ACT_T_IDLE + RANDOM_LONG( 0, 3 ) ); } else if( RANDOM_LONG( 0, 3 ) == 0 ) { // tap m_iGoalAnim = LookupActivity( ACT_T_TAP + MyLevel() ); } else { // idle m_iGoalAnim = LookupActivity( ACT_T_IDLE + MyLevel() ); } } if( m_flSoundYaw < 0 ) m_flSoundYaw += RANDOM_FLOAT( 2, 8 ); else m_flSoundYaw -= RANDOM_FLOAT( 2, 8 ); } pev->sequence = FindTransition( pev->sequence, m_iGoalAnim, &m_iDir ); if( m_iDir > 0 ) { pev->frame = 0; } else { m_iDir = -1; // just to safe pev->frame = 255; } ResetSequenceInfo(); m_flFramerateAdj = RANDOM_FLOAT( -0.2f, 0.2f ); pev->framerate = m_iDir * 1.0f + m_flFramerateAdj; switch( pev->sequence ) { case TENTACLE_ANIM_Floor_Tap: case TENTACLE_ANIM_Lev1_Tap: case TENTACLE_ANIM_Lev2_Tap: case TENTACLE_ANIM_Lev3_Tap: { Vector vecSrc; UTIL_MakeVectors( pev->angles ); TraceResult tr1, tr2; vecSrc = pev->origin + Vector( 0, 0, MyHeight() - 4 ); UTIL_TraceLine( vecSrc, vecSrc + gpGlobals->v_forward * 512, ignore_monsters, ENT( pev ), &tr1 ); vecSrc = pev->origin + Vector( 0, 0, MyHeight() + 8 ); UTIL_TraceLine( vecSrc, vecSrc + gpGlobals->v_forward * 512, ignore_monsters, ENT( pev ), &tr2 ); // ALERT( at_console, "%f %f\n", tr1.flFraction * 512, tr2.flFraction * 512 ); m_flTapRadius = SetBlending( 0, RANDOM_FLOAT( tr1.flFraction * 512, tr2.flFraction * 512 ) ); } break; default: m_flTapRadius = 336; // 400 - 64 break; } pev->view_ofs.z = MyHeight(); // ALERT( at_console, "seq %d\n", pev->sequence ); } if( m_flPrevSoundTime + 2.0f > gpGlobals->time ) { // 1.5 normal speed if hears sounds pev->framerate = m_iDir * 1.5f + m_flFramerateAdj; } else if( m_flPrevSoundTime + 5.0f > gpGlobals->time ) { // slowdown to normal pev->framerate = m_iDir + m_iDir * ( 5 - ( gpGlobals->time - m_flPrevSoundTime ) ) / 2 + m_flFramerateAdj; } } void CTentacle::CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { // ALERT( at_console, "%s triggered %d\n", STRING( pev->targetname ), useType ); switch( useType ) { case USE_OFF: pev->takedamage = DAMAGE_NO; SetThink( &CTentacle::DieThink ); m_iGoalAnim = TENTACLE_ANIM_Engine_Death1; break; case USE_ON: if( pActivator ) { // ALERT( at_console, "insert sound\n" ); CSoundEnt::InsertSound( bits_SOUND_WORLD, pActivator->pev->origin, 1024, 1.0 ); } break; case USE_SET: break; case USE_TOGGLE: pev->takedamage = DAMAGE_NO; SetThink( &CTentacle::DieThink ); m_iGoalAnim = TENTACLE_ANIM_Engine_Idle; break; } } void CTentacle::DieThink( void ) { pev->nextthink = gpGlobals-> time + 0.1f; DispatchAnimEvents(); StudioFrameAdvance(); ChangeYaw( 24 ); if( m_fSequenceFinished ) { if( pev->sequence == m_iGoalAnim ) { switch( m_iGoalAnim ) { case TENTACLE_ANIM_Engine_Idle: case TENTACLE_ANIM_Engine_Sway: case TENTACLE_ANIM_Engine_Swat: case TENTACLE_ANIM_Engine_Bob: m_iGoalAnim = TENTACLE_ANIM_Engine_Sway + RANDOM_LONG( 0, 2 ); break; case TENTACLE_ANIM_Engine_Death1: case TENTACLE_ANIM_Engine_Death2: case TENTACLE_ANIM_Engine_Death3: UTIL_Remove( this ); return; } } // ALERT( at_console, "%d : %d => ", pev->sequence, m_iGoalAnim ); pev->sequence = FindTransition( pev->sequence, m_iGoalAnim, &m_iDir ); // ALERT( at_console, "%d\n", pev->sequence ); if( m_iDir > 0 ) { pev->frame = 0; } else { pev->frame = 255; } ResetSequenceInfo(); float dy; switch( pev->sequence ) { 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: case TENTACLE_ANIM_Engine_Idle: case TENTACLE_ANIM_Engine_Sway: case TENTACLE_ANIM_Engine_Swat: case TENTACLE_ANIM_Engine_Bob: case TENTACLE_ANIM_Engine_Death1: case TENTACLE_ANIM_Engine_Death2: case TENTACLE_ANIM_Engine_Death3: pev->framerate = RANDOM_FLOAT( m_iDir - 0.2f, m_iDir + 0.2f ); dy = 180; break; default: pev->framerate = 1.5; dy = 0; break; } pev->ideal_yaw = m_flInitialYaw + dy; } } void CTentacle::HandleAnimEvent( MonsterEvent_t *pEvent ) { const char *sound; switch( pEvent->event ) { case 1: // bang { Vector vecSrc, vecAngles; GetAttachment( 0, vecSrc, vecAngles ); // Vector vecSrc = pev->origin + m_flTapRadius * Vector( cos( pev->angles.y * ( 3.14192653 / 180.0 ) ), sin( pev->angles.y * ( M_PI / 180.0 ) ), 0.0 ); // vecSrc.z += MyHeight(); switch( m_iTapSound ) { case TE_SILO: UTIL_EmitAmbientSound( ENT( pev ), vecSrc, RANDOM_SOUND_ARRAY( pHitSilo ), 1.0, ATTN_NORM, 0, 100 ); break; case TE_NONE: break; case TE_DIRT: UTIL_EmitAmbientSound( ENT( pev ), vecSrc, RANDOM_SOUND_ARRAY( pHitDirt ), 1.0, ATTN_NORM, 0, 100 ); break; case TE_WATER: UTIL_EmitAmbientSound( ENT( pev ), vecSrc, RANDOM_SOUND_ARRAY( pHitWater ), 1.0, ATTN_NORM, 0, 100 ); break; } gpGlobals->force_retouch++; } break; case 3: // start killing swing m_iHitDmg = 200; // UTIL_EmitAmbientSound( ENT( pev ), pev->origin + Vector( 0, 0, MyHeight() ), "tentacle/te_swing1.wav", 1.0, ATTN_NORM, 0, 100 ); break; case 4: // end killing swing m_iHitDmg = 25; break; case 5: // just "whoosh" sound // UTIL_EmitAmbientSound( ENT( pev ), pev->origin + Vector( 0, 0, MyHeight() ), "tentacle/te_swing2.wav", 1.0, ATTN_NORM, 0, 100 ); break; case 2: // tap scrape case 6: // light tap { Vector vecSrc = pev->origin + m_flTapRadius * Vector( cos( pev->angles.y * ( M_PI_F / 180.0f ) ), sin( pev->angles.y * ( M_PI_F / 180.0f ) ), 0.0f ); vecSrc.z += MyHeight(); float flVol = RANDOM_FLOAT( 0.3f, 0.5f ); switch( m_iTapSound ) { case TE_SILO: UTIL_EmitAmbientSound( ENT( pev ), vecSrc, RANDOM_SOUND_ARRAY( pHitSilo ), flVol, ATTN_NORM, 0, 100 ); break; case TE_NONE: break; case TE_DIRT: UTIL_EmitAmbientSound( ENT( pev ), vecSrc, RANDOM_SOUND_ARRAY( pHitDirt ), flVol, ATTN_NORM, 0, 100 ); break; case TE_WATER: UTIL_EmitAmbientSound( ENT( pev ), vecSrc, RANDOM_SOUND_ARRAY( pHitWater ), flVol, ATTN_NORM, 0, 100 ); break; } } break; case 7: // roar switch( RANDOM_LONG( 0, 1 ) ) { case 0: sound = "tentacle/te_roar1.wav"; break; case 1: sound = "tentacle/te_roar2.wav"; break; } UTIL_EmitAmbientSound( ENT( pev ), pev->origin + Vector( 0, 0, MyHeight() ), sound, 1.0, ATTN_NORM, 0, 100 ); break; case 8: // search switch( RANDOM_LONG( 0, 1 ) ) { case 0: sound = "tentacle/te_search1.wav"; break; case 1: sound = "tentacle/te_search2.wav"; break; } UTIL_EmitAmbientSound(ENT(pev), pev->origin + Vector( 0, 0, MyHeight()), sound, 1.0, ATTN_NORM, 0, 100); break; case 9: // swing switch( RANDOM_LONG( 0, 1 ) ) { case 0: sound = "tentacle/te_move1.wav"; break; case 1: sound = "tentacle/te_move2.wav"; break; } UTIL_EmitAmbientSound( ENT( pev ), pev->origin + Vector( 0, 0, MyHeight() ), sound, 1.0, ATTN_NORM, 0, 100 ); break; default: CBaseMonster::HandleAnimEvent( pEvent ); } } // // TentacleStart // // void CTentacle::Start( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) void CTentacle::Start( void ) { SetThink( &CTentacle::Cycle ); if( !g_fFlySound ) { EMIT_SOUND( ENT( pev ), CHAN_BODY, "ambience/flies.wav", 1, ATTN_NORM ); g_fFlySound = TRUE; //pev->nextthink = gpGlobals-> time + 0.1; } else if( !g_fSquirmSound ) { EMIT_SOUND( ENT( pev ), CHAN_BODY, "ambience/squirm2.wav", 1, ATTN_NORM ); g_fSquirmSound = TRUE; } pev->nextthink = gpGlobals->time + 0.1f; } void CTentacle::HitTouch( CBaseEntity *pOther ) { TraceResult tr = UTIL_GetGlobalTrace(); if( pOther->pev->modelindex == pev->modelindex ) return; if( m_flHitTime > gpGlobals->time ) return; // only look at the ones where the player hit me if( tr.pHit == NULL || tr.pHit->v.modelindex != pev->modelindex ) return; if( tr.iHitgroup >= 3 ) { pOther->TakeDamage( pev, pev, m_iHitDmg, DMG_CRUSH ); // ALERT( at_console, "wack %3d : ", m_iHitDmg ); } else if( tr.iHitgroup != 0 ) { pOther->TakeDamage( pev, pev, 20, DMG_CRUSH ); // ALERT( at_console, "tap %3d : ", 20 ); } else { return; // Huh? } m_flHitTime = gpGlobals->time + 0.5f; // ALERT( at_console, "%s : ", STRING( tr.pHit->v.classname ) ); // ALERT( at_console, "%.0f : %s : %d\n", pev->angles.y, STRING( pOther->pev->classname ), tr.iHitgroup ); } int CTentacle::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) { if( flDamage > pev->health ) { pev->health = 1; } else { pev->health -= flDamage; } return 1; } void CTentacle::Killed( entvars_t *pevAttacker, int iGib ) { m_iGoalAnim = TENTACLE_ANIM_Pit_Idle; return; } class CTentacleMaw : public CBaseMonster { public: void Spawn(); void Precache(); }; LINK_ENTITY_TO_CLASS( monster_tentaclemaw, CTentacleMaw ) // // Tentacle Spawn // void CTentacleMaw::Spawn() { Precache(); SET_MODEL( ENT( pev ), "models/maw.mdl" ); UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); pev->solid = SOLID_NOT; pev->movetype = MOVETYPE_STEP; pev->effects = 0; pev->health = 75; pev->yaw_speed = 8; pev->sequence = 0; pev->angles.x = 90; // ResetSequenceInfo( ); } void CTentacleMaw::Precache() { PRECACHE_MODEL( "models/maw.mdl" ); } #endif