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/shared/tf/halloween/eyeball_boss/teleport_vortex.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/shared/tf/halloween/eyeball_boss/teleport_vortex.cpp')
| -rw-r--r-- | game/shared/tf/halloween/eyeball_boss/teleport_vortex.cpp | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/game/shared/tf/halloween/eyeball_boss/teleport_vortex.cpp b/game/shared/tf/halloween/eyeball_boss/teleport_vortex.cpp new file mode 100644 index 0000000..53d720d --- /dev/null +++ b/game/shared/tf/halloween/eyeball_boss/teleport_vortex.cpp @@ -0,0 +1,611 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "tf_gamerules.h" +#include "teleport_vortex.h" + +//----------------------------------------------------------------------------- + +#ifdef CLIENT_DLL + + #include "c_tf_fx.h" + + inline float SCurve( float t ) + { + t = clamp( t, 0.0f, 1.0f ); + return t * t * (3 - 2*t); + } + +#else + + #include "tf_fx.h" + #include "tf_team.h" + #include "tf_weapon_sniperrifle.h" + +#endif + +//----------------------------------------------------------------------------- + +ConVar vortex_float_osc_speed( "vortex_float_osc_speed", "2.0", FCVAR_HIDDEN | FCVAR_CHEAT | FCVAR_REPLICATED ); +ConVar vortex_float_amp( "vortex_float_amp", "5.0", FCVAR_HIDDEN | FCVAR_CHEAT | FCVAR_REPLICATED ); +ConVar vortex_fade_fraction_denom( "vortex_fade_fraction_denom", "10.0", FCVAR_HIDDEN | FCVAR_CHEAT | FCVAR_REPLICATED ); +ConVar vortex_book_offset( "vortex_book_offset", "5.0", FCVAR_HIDDEN | FCVAR_CHEAT | FCVAR_REPLICATED ); + +//----------------------------------------------------------------------------- + +IMPLEMENT_NETWORKCLASS_ALIASED( TeleportVortex, DT_TeleportVortex ) + +BEGIN_NETWORK_TABLE( CTeleportVortex, DT_TeleportVortex ) + #if defined( CLIENT_DLL ) + RecvPropInt( RECVINFO( m_iState ) ), + #else + SendPropInt( SENDINFO( m_iState ), 4, SPROP_UNSIGNED | SPROP_CHANGES_OFTEN ), + #endif +END_NETWORK_TABLE() + +LINK_ENTITY_TO_CLASS( teleport_vortex, CTeleportVortex ); + +BEGIN_DATADESC( CTeleportVortex ) +END_DATADESC() + +//----------------------------------------------------------------------------- + +#define VORTEX_PARTICLE_EFFECT_EYEBALL_MOVED "eyeboss_tp_vortex" +#define VORTEX_PARTICLE_EFFECT_EYEBALL_DIED "eyeboss_aura_angry" + +#define VORTEX_BOOK_MODEL "models/props_halloween/bombonomicon.mdl" + +#define VORTEX_SOUND_EYEBALL_MOVED "Halloween.TeleportVortex.EyeballMovedVortex" +#define VORTEX_SOUND_EYEBALL_DIED "Halloween.TeleportVortex.EyeballDiedVortex" +#define VORTEX_SOUND_BOOK_SPAWN "Halloween.TeleportVortex.BookSpawn" +#define VORTEX_SOUND_BOOK_EXIT "Halloween.TeleportVortex.BookExit" + +#define VORTEX_OPEN_OPEN_ANIM "flip_stimulated" + +//----------------------------------------------------------------------------- + +CTeleportVortex::CTeleportVortex() +#ifdef GAME_DLL +: m_iAutoSetupVortex( -1 ) +#endif +{ +} + +CTeleportVortex::~CTeleportVortex() +{ +#ifdef CLIENT_DLL + DestroyParticleEffect(); +#endif +} + +#ifdef CLIENT_DLL +void CTeleportVortex::DestroyParticleEffect() +{ + if ( m_pVortexEffect ) + { + ParticleProp()->StopEmission( m_pVortexEffect ); + m_pVortexEffect = NULL; + } +} +#else +bool CTeleportVortex::KeyValue( const char *szKeyname, const char *szValue ) +{ + if ( !V_strnicmp( szKeyname, "type", 4 ) ) + { + m_iAutoSetupVortex = atoi( szValue ); + } + else + { + return BaseClass::KeyValue( szKeyname, szValue ); + } + + return true; +} +#endif + +void CTeleportVortex::Spawn( void ) +{ + Precache(); + BaseClass::Spawn(); + + AddEffects( EF_NODRAW ); + SetMoveType( MOVETYPE_NONE ); + SetSolid( SOLID_BBOX ); + CollisionProp()->SetCollisionBounds( Vector( -50, -50, -50 ), Vector( 50, 50, 50 ) ); + SetModelName( NULL_STRING ); + SetSolidFlags( FSOLID_TRIGGER ); + SetCollisionGroup( COLLISION_GROUP_WEAPON ); + SetRenderMode( kRenderTransAlpha ); + SetRenderColorA( 255 ); + UseClientSideAnimation(); + + m_lifeTimer.Start( 5.0f ); + +#ifdef CLIENT_DLL + m_flScale = 1.0f; + m_iOldState = VORTEXSTATE_INACTIVE; + m_pVortexEffect = NULL; + + ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_ALWAYS ); + + AddToLeafSystem( RENDER_GROUP_TWOPASS ); +#else + // Default to purgatory + m_pszWhere = "spawn_purgatory"; + + SetThink( &CTeleportVortex::VortexThink ); + SetNextThink( gpGlobals->curtime ); + + m_nSoundCounter = 0; + + m_bSplitTeam = false; + + if ( m_iAutoSetupVortex >= 0 ) + { + SetupVortex( m_iAutoSetupVortex != 0 ); + } +#endif +} + + +//----------------------------------------------------------------------------- + +void CTeleportVortex::Precache( void ) +{ + BaseClass::Precache(); + + PrecacheModel( VORTEX_BOOK_MODEL ); + + // We deliberately allow late precaches here. + bool bAllowPrecache = CBaseAnimating::IsPrecacheAllowed(); + CBaseAnimating::SetAllowPrecache( true ); + +#if GAME_DLL + PrecacheScriptSound( VORTEX_SOUND_EYEBALL_MOVED ); + PrecacheScriptSound( VORTEX_SOUND_EYEBALL_DIED ); + PrecacheScriptSound( VORTEX_SOUND_BOOK_SPAWN ); + PrecacheScriptSound( VORTEX_SOUND_BOOK_EXIT ); +#endif + + PrecacheParticleSystem( VORTEX_PARTICLE_EFFECT_EYEBALL_MOVED ); + PrecacheParticleSystem( VORTEX_PARTICLE_EFFECT_EYEBALL_DIED ); + + CBaseAnimating::SetAllowPrecache( bAllowPrecache ); +} + +//----------------------------------------------------------------------------- + +#ifdef GAME_DLL + +void CTeleportVortex::Touch( CBaseEntity *pOther ) +{ + BaseClass::Touch( pOther ); + + if ( pOther && pOther->IsPlayer() ) + { + if ( m_bSplitTeam ) + { + const char* pszTeam = pOther->GetTeamNumber() == TF_TEAM_RED ? "_red" : "_blue"; + SendPlayerToTheUnderworld( ToTFPlayer( pOther ), CFmtStr( "%s%s", m_pszWhere.Get(), pszTeam ) ); + } + else + { + SendPlayerToTheUnderworld( ToTFPlayer( pOther ), m_pszWhere ); + } + } +} + + +//----------------------------------------------------------------------------- + +int CTeleportVortex::UpdateTransmitState( void ) +{ + return SetTransmitState( FL_EDICT_PVSCHECK ); +} + +//----------------------------------------------------------------------------- + +void CTeleportVortex::StartTouch( CBaseEntity *pOther ) +{ + CBaseAnimating::StartTouch( pOther ); +} + +//----------------------------------------------------------------------------- + +void CTeleportVortex::SetupVortex( bool bIsDeathVortex, bool bSplitTeam /*= false*/ ) +{ + m_bSplitTeam = bSplitTeam; + if ( bIsDeathVortex ) + { + m_iState = VORTEXSTATE_ACTIVE_EYEBALL_DIED; + m_pszWhere = "spawn_loot"; + RemoveEffects( EF_NODRAW ); + SetModel( VORTEX_BOOK_MODEL ); + } + else + { + m_iState = VORTEXSTATE_ACTIVE_EYEBALL_MOVED; + m_pszWhere = "spawn_purgatory"; + AddEffects( EF_NODRAW ); + } +} + +void CTeleportVortex::VortexThink() +{ + if ( m_lifeTimer.IsElapsed() ) + { + UTIL_Remove( this ); + } + else + { + StudioFrameAdvance(); + + if ( m_iState == VORTEXSTATE_ACTIVE_EYEBALL_DIED ) + { + if ( m_nSoundCounter == 0 && ShouldDoBookRampIn() ) + { + EmitSound( VORTEX_SOUND_BOOK_SPAWN ); + ++m_nSoundCounter; + } + else if ( m_nSoundCounter == 1 && ShouldDoBookRampOut() ) + { + EmitSound( VORTEX_SOUND_BOOK_EXIT ); + ++m_nSoundCounter; + } + } + + SetNextThink( gpGlobals->curtime ); + } + + // suck nearby players into the vortex + CUtlVector< CTFPlayer * > playerVector; + CollectPlayers( &playerVector, TF_TEAM_RED, COLLECT_ONLY_LIVING_PLAYERS ); + CollectPlayers( &playerVector, TF_TEAM_BLUE, COLLECT_ONLY_LIVING_PLAYERS, APPEND_PLAYERS ); + + const float suctionRange = 500.0f; + + for( int i=0; i<playerVector.Count(); ++i ) + { + CTFPlayer *player = playerVector[i]; + + if ( player->GetFlags() & FL_ONGROUND ) + { + // not airborne + continue; + } + + Vector toVortex = WorldSpaceCenter() - player->WorldSpaceCenter(); + float range = toVortex.NormalizeInPlace(); + + if ( range > suctionRange ) + { + // too far + continue; + } + + if ( player->IsLookingTowards( WorldSpaceCenter() ) ) + { + if ( player->IsLineOfSightClear( WorldSpaceCenter(), CBaseCombatCharacter::IGNORE_ACTORS, player ) ) + { + player->ApplyAbsVelocityImpulse( 30.0f * toVortex ); + } + } + } +} + + +//----------------------------------------------------------------------------------------------------- +void SendPlayerToTheUnderworld( CTFPlayer *teleportingPlayer, const char *where ) +{ + if ( !teleportingPlayer ) + { + return; + } + + CUtlVector< CBaseEntity * > spawnVector; + + CBaseEntity *spawnPoint = NULL; + while( ( spawnPoint = gEntList.FindEntityByClassname( spawnPoint, "info_target" ) ) != NULL ) + { + if ( FStrEq( STRING( spawnPoint->GetEntityName() ), where ) ) + { + spawnVector.AddToTail( spawnPoint ); + } + } + + if ( spawnVector.Count() == 0 ) + { + Warning( "SendPlayerToTheUnderworld: No info_target entities named '%s' found!\n", where ); + return; + } + + // collect enemies that could block our spawning + CUtlVector< CTFPlayer * > enemyVector; + CollectPlayers( &enemyVector, GetEnemyTeam( teleportingPlayer->GetTeamNumber() ), COLLECT_ONLY_LIVING_PLAYERS ); + + const float nearRange = 25.0f; + + // collect spots without players overlapping them + CUtlVector< CBaseEntity * > openSpawnVector; + for( int i=0; i<spawnVector.Count(); ++i ) + { + int p; + + for( p=0; p<enemyVector.Count(); ++p ) + { + if ( ( spawnVector[i]->GetAbsOrigin() - enemyVector[p]->GetAbsOrigin() ).IsLengthLessThan( nearRange ) ) + { + // a player is occupying this spawn + break; + } + } + + if ( p == enemyVector.Count() ) + { + // no players are near this spawn point + openSpawnVector.AddToTail( spawnVector[i] ); + } + } + + CBaseEntity *teleportDestination = NULL; + + if ( openSpawnVector.Count() == 0 ) + { + // there are no free spawns - pick one and telefrag enemies standing there + int which = RandomInt( 0, spawnVector.Count()-1 ); + + teleportDestination = spawnVector[ which ]; + + for( int p=0; p<enemyVector.Count(); ++p ) + { + if ( ( teleportDestination->GetAbsOrigin() - enemyVector[p]->GetAbsOrigin() ).IsLengthLessThan( nearRange ) ) + { + // telefrag! + enemyVector[p]->TakeDamage( CTakeDamageInfo( teleportingPlayer, teleportingPlayer, 1000, DMG_CRUSH, TF_DMG_CUSTOM_TELEFRAG ) ); + } + } + } + else + { + // pick an open destination at random + int which = RandomInt( 0, openSpawnVector.Count()-1 ); + + teleportDestination = openSpawnVector[ which ]; + } + + if ( teleportDestination ) + { + if ( teleportingPlayer->GetTeam() ) + { + UTIL_LogPrintf( "HALLOWEEN: \"%s<%i><%s><%s>\" purgatory_teleport \"%s\"\n", + teleportingPlayer->GetPlayerName(), + teleportingPlayer->GetUserID(), + teleportingPlayer->GetNetworkIDString(), + teleportingPlayer->GetTeam()->GetName(), + where ); + } + + teleportingPlayer->Teleport( &teleportDestination->GetAbsOrigin(), &teleportDestination->GetAbsAngles(), &vec3_origin ); + + // When fighting Merasmus, give players full health on teleport + if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_LAKESIDE ) ) + { + if ( teleportingPlayer->IsAlive() ) + { + teleportingPlayer->TakeHealth( teleportingPlayer->GetMaxHealth(), DMG_GENERIC ); + teleportingPlayer->m_Shared.HealthKitPickupEffects(); + teleportingPlayer->m_Shared.RemoveCond( TF_COND_HALLOWEEN_BOMB_HEAD ); + } + } + + if ( FStrEq( where, "spawn_loot" ) ) + { + CReliableBroadcastRecipientFilter filter; + UTIL_SayText2Filter( filter, teleportingPlayer, false, "#TF_Halloween_Loot_Island", teleportingPlayer->GetPlayerName() ); + } + + teleportingPlayer->m_Shared.InstantlySniperUnzoom(); + + color32 fadeColor = { 255, 255, 255, 100 }; + UTIL_ScreenFade( teleportingPlayer, fadeColor, 0.25, 0.4, FFADE_IN ); + } +} + + +#else + +//----------------------------------------------------------------------------- + +void CTeleportVortex::ClientThink() +{ + // Fade in and out for first and last fraction of time + const float flDuration = m_lifeTimer.GetCountdownDuration(); + const float flRampTime = flDuration / vortex_fade_fraction_denom.GetInt(); + const float flElapsed = m_lifeTimer.GetElapsedTime(); + + float t = 1.0f; + if ( ShouldDoBookRampIn() ) + { + t = SCurve( flElapsed / flRampTime ); + } + else if ( ShouldDoBookRampOut() ) + { + t = SCurve( 1.0f - ( flElapsed - flDuration + flRampTime ) / flRampTime ); + } + +// SetRenderColorA( t * 255 ); + + m_flScale = t; +} + +//----------------------------------------------------------------------------- + +void CTeleportVortex::BuildTransformations( CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed ) +{ + // Translate root bone towards the player and oscillate a bit - if in the scale in/out period, translate up directly from the underworld, or down towards it. + const Vector vecBob( vortex_book_offset.GetFloat(), 0.0f, vortex_float_amp.GetFloat() * sinf( vortex_float_osc_speed.GetFloat() * gpGlobals->curtime ) ); + pos[0] = vecBob + Vector( 0.0f, 0.0f, Lerp( m_flScale, -500.0f, 0.0f ) ); + + matrix3x4_t mRootBoneRotation; + + // If the local player exists and is alive, render the book so that it's facing him + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + if ( pLocalPlayer && pLocalPlayer->IsAlive() ) + { + const Vector vecBook = GetAbsOrigin(); + const Vector vecPlayerPos = pLocalPlayer->GetAbsOrigin(); + Vector vecForward = vecPlayerPos - vecBook; + if ( VectorNormalize( vecForward ) > 0.1f ) + { + // Calculate a matrix based on the forward direction + VectorMatrix( vecForward, mRootBoneRotation ); + } + } + else + { + // Use whatever rotation is in the root bone already + QuaternionMatrix( q[0], mRootBoneRotation ); + } + + // Convert the matrix to a quaternion and slam the root bone rotation + MatrixQuaternion( mRootBoneRotation, q[0] ); + + // Let the base class actually build the global transforms + BaseClass::BuildTransformations( pStudioHdr, pos, q, cameraTransform, boneMask, boneComputed ); +} + +//----------------------------------------------------------------------------- + +void CTeleportVortex::PlayBookAnimation( const char *pAnimName ) +{ + int iAnimSequence = LookupSequence( pAnimName ); + if ( iAnimSequence ) + { + SetSequence( iAnimSequence ); + SetPlaybackRate( 1.0f ); + SetCycle( 0 ); + ResetSequenceInfo(); + } +} + +//----------------------------------------------------------------------------- + +void CTeleportVortex::OnDataChanged(DataUpdateType_t updateType) +{ + BaseClass::OnDataChanged( updateType ); + + if ( m_iState != m_iOldState ) + { + if ( m_iState != VORTEXSTATE_INACTIVE ) + { + // Stop any existing particle effect if necessary + AssertMsg( !m_pVortexEffect, "Particle effect should not be active!" ); + + // Create the particle effect and play a sound + const char *pszEffectName; + const char *pszSound; + if ( m_iState == VORTEXSTATE_ACTIVE_EYEBALL_MOVED ) + { + pszEffectName = VORTEX_PARTICLE_EFFECT_EYEBALL_MOVED; + pszSound = VORTEX_SOUND_EYEBALL_MOVED; + } + else + { + pszEffectName = VORTEX_PARTICLE_EFFECT_EYEBALL_DIED; + pszSound = VORTEX_SOUND_EYEBALL_DIED; + + PlayBookAnimation( VORTEX_OPEN_OPEN_ANIM ); + } + m_pVortexEffect = ParticleProp()->Create( pszEffectName, PATTACH_ABSORIGIN ); + EmitSound( pszSound ); + } + + m_iOldState = m_iState; + } +} + +#endif + +float CTeleportVortex::GetRampTime() +{ + const float flDuration = m_lifeTimer.GetCountdownDuration(); + return flDuration / vortex_fade_fraction_denom.GetInt(); +} + +bool CTeleportVortex::ShouldDoBookRampIn() +{ + return m_lifeTimer.GetElapsedTime() <= GetRampTime(); +} + +bool CTeleportVortex::ShouldDoBookRampOut() +{ + const float flDuration = m_lifeTimer.GetCountdownDuration(); + return m_lifeTimer.GetElapsedTime() >= flDuration - GetRampTime(); +} + +#ifdef CLIENT_DLL + #define CHightower_TeleportVortex C_Hightower_TeleportVortex +#endif +class CHightower_TeleportVortex : public CTeleportVortex +{ + DECLARE_DATADESC(); + DECLARE_NETWORKCLASS(); + DECLARE_CLASS( CHightower_TeleportVortex, CTeleportVortex ); + +public: + virtual void Spawn() + { + BaseClass::Spawn(); +#ifdef GAME_DLL + m_nWinningTeam = TF_TEAM_COUNT; // Invalid + SetupVortex( false, false ); + m_lifeTimer.Start( m_flDuration ); +#endif + } + + virtual void Touch( CBaseEntity *pOther ) + { +#ifdef GAME_DLL + const char *pszTarget = CFmtStr( "%s_%s", m_pszDestinationBaseName, ( pOther->GetTeamNumber() == m_nWinningTeam ) ? "winner" : "loser" ); + SendPlayerToTheUnderworld( ToTFPlayer( pOther ), pszTarget ); +#endif + } + +#ifdef GAME_DLL + void SetAdvantageTeam( inputdata_t &inputdata ) + { + m_nWinningTeam = FStrEq( inputdata.value.String(), "red" ) ? TF_TEAM_RED : TF_TEAM_BLUE; + } +#endif +private: + +#ifdef GAME_DLL + int m_nWinningTeam; + const char* m_pszDestinationBaseName; + float m_flDuration; +#endif +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( Hightower_TeleportVortex, DT_Hightower_TeleportVortex ) + +BEGIN_NETWORK_TABLE( CHightower_TeleportVortex, DT_Hightower_TeleportVortex ) + #if defined( CLIENT_DLL ) + RecvPropInt( RECVINFO( m_iState ) ), + #else + SendPropInt( SENDINFO( m_iState ), 4, SPROP_UNSIGNED | SPROP_CHANGES_OFTEN ), + #endif +END_NETWORK_TABLE() + +LINK_ENTITY_TO_CLASS( hightower_teleport_vortex, CHightower_TeleportVortex ); + +BEGIN_DATADESC( CHightower_TeleportVortex ) +#ifdef GAME_DLL + DEFINE_INPUTFUNC( FIELD_STRING, "SetAdvantageTeam", SetAdvantageTeam ), + DEFINE_KEYFIELD( m_flDuration, FIELD_FLOAT, "lifetime" ), + DEFINE_KEYFIELD( m_pszDestinationBaseName, FIELD_STRING, "target_base_name" ), +#endif +END_DATADESC()
\ No newline at end of file |