diff options
Diffstat (limited to 'game/server/tf2/tf_obj_tunnel.cpp')
| -rw-r--r-- | game/server/tf2/tf_obj_tunnel.cpp | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/game/server/tf2/tf_obj_tunnel.cpp b/game/server/tf2/tf_obj_tunnel.cpp new file mode 100644 index 0000000..c00261e --- /dev/null +++ b/game/server/tf2/tf_obj_tunnel.cpp @@ -0,0 +1,571 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "tf_obj.h" +#include "tf_obj_mapdefined.h" +#include "engine/IEngineSound.h" +#include "entityoutput.h" +#include "tf_shareddefs.h" +#include "triggers.h" +#include "shake.h" +#include "tf_player.h" +#include "tf_gamerules.h" + +#define TUNNEL_THINK_INTERVAL 0.1f +#define TUNNEL_FADE_TIME 1.0f +#define MAX_TUNNEL_DURATION 30.0f +// If tunneling takes longer than this, use a countdown +#define TUNNEL_DURATION_MESSAGE_NEEDED 3.0f + +// It takes this long to tunnel +static ConVar tf_tunnel_time( "tf_tunnel_time", "2", 0, "Takes this long to traverse a tunnel." ); + +class CObjectTunnel : public CObjectMapDefined +{ + DECLARE_CLASS( CObjectTunnel, CObjectMapDefined ); +public: + + DECLARE_SERVERCLASS(); + + virtual void Spawn( void ); + virtual void Killed( void ); + + int UpdateTransmitState(); + +private: +}; + +IMPLEMENT_SERVERCLASS_ST(CObjectTunnel, DT_ObjectTunnel) +END_SEND_TABLE(); + +int CObjectTunnel::UpdateTransmitState() +{ + return SetTransmitState( FL_EDICT_ALWAYS ); +} + +void CObjectTunnel::Spawn( void ) +{ + BaseClass::Spawn(); + + AddFlag( FL_NOTARGET ); + SetType( OBJ_TUNNEL ); +} + +LINK_ENTITY_TO_CLASS(obj_tunnel,CObjectTunnel); +LINK_ENTITY_TO_CLASS(obj_tunnel_prop,CObjectTunnel); + +//----------------------------------------------------------------------------- +// Purpose: Object has been blown up. Tunnels are never fully destroyed, so they stay on the minimap. +//----------------------------------------------------------------------------- +void CObjectTunnel::Killed( void ) +{ + m_bDying = true; + + RemoveAllSappers( this ); + + // Do an explosion. + CPASFilter filter( GetAbsOrigin() ); + te->Explosion( + filter, + 0.0, + &GetAbsOrigin(), + g_sModelIndexFireball, + 5.4, // radius + 15, + TE_EXPLFLAG_NODLIGHTS, + 256, + 200); + + // Become non-solid and invisible + VPhysicsDestroyObject(); + AddSolidFlags( FSOLID_NOT_SOLID ); + m_takedamage = DAMAGE_NO; + AddEffects( EF_NODRAW ); +} + +class CInfoTunnelExit : public CPointEntity +{ +public: + DECLARE_CLASS( CInfoTunnelExit, CPointEntity ); +private: +}; + +LINK_ENTITY_TO_CLASS(info_tunnel_exit,CInfoTunnelExit); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CObjectTunnelTrigger : public CBaseTrigger +{ + DECLARE_CLASS( CObjectTunnelTrigger, CBaseTrigger ); +public: + CObjectTunnelTrigger(); + + DECLARE_DATADESC(); + + virtual void Precache(); + virtual void Spawn(); + virtual void Activate(); + + virtual void StartTouch( CBaseEntity *pOther ); + + void SetActive( bool active ); + bool GetActive( void ) const; + + void InputSetActive( inputdata_t &inputdata ); + void InputSetInactive( inputdata_t &inputdata ); + void InputToggleActive( inputdata_t &inputdata ); + + void InputSetTarget( inputdata_t &inputdata ); + void InputSetTeleportDuration( inputdata_t &inputdata ); + void InputSetTeleportVelocity( inputdata_t &inputdata ); + + virtual void TunnelThink(); +private: + float GetTeleportDuration( void ); + + bool m_bActive; + CHandle< CInfoTunnelExit > m_hTunnelExit; + + COutputEvent OnTunnelTriggerStart; + COutputEvent OnTunnelTriggerEnd; + + struct TunnelPlayer + { + CHandle< CBaseTFPlayer > player; + Vector startpos; + float tunnelstarted; + float duration; + float teleporttime; + float fadeintime; + bool exitstarted; + float fadetime; + int iremaining; + int ilastremaining; + bool needremainigcounter; + }; + + CUtlVector< TunnelPlayer > m_Tunneling; + + void StartTunneling( CBaseTFPlayer *player ); + bool KeepTunneling( TunnelPlayer *tunnel ); + + + float m_flTeleportDuration; + float m_flTeleportVelocity; +}; + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CObjectTunnelTrigger::CObjectTunnelTrigger() +{ + m_flTeleportDuration = -1.0f; + m_flTeleportVelocity = 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *player - +//----------------------------------------------------------------------------- +void CObjectTunnelTrigger::StartTunneling( CBaseTFPlayer *player ) +{ + if ( !player ) + return; + + // Ignore if it's already in the list + int c = m_Tunneling.Count(); + for ( int i = 0 ; i < c; i++ ) + { + TunnelPlayer *tp = &m_Tunneling[ i ]; + if ( tp->player == player ) + { + return; + } + } + + TunnelPlayer tunnel; + tunnel.player = player; + tunnel.tunnelstarted = gpGlobals->curtime; + tunnel.duration = GetTeleportDuration(); + tunnel.teleporttime = tunnel.tunnelstarted + tunnel.duration; + tunnel.exitstarted = false; + tunnel.startpos = player->GetAbsOrigin(); + tunnel.iremaining = (int)tunnel.duration; + tunnel.ilastremaining = tunnel.iremaining; + tunnel.needremainigcounter = ( tunnel.iremaining > TUNNEL_DURATION_MESSAGE_NEEDED ) ? true : false; + + // Fade user screen to black + color32 black = {0,0,0,255}; + + float duration = tunnel.duration; + float fadeouttime = TUNNEL_FADE_TIME; + float holdtime = 0.0f; + if ( duration < 2 * TUNNEL_FADE_TIME ) + { + fadeouttime = duration * 0.5f; + } + else + { + fadeouttime = TUNNEL_FADE_TIME; + holdtime = duration - 2 * fadeouttime; + } + + tunnel.fadetime = fadeouttime; + tunnel.fadeintime = tunnel.tunnelstarted + fadeouttime + holdtime; + + UTIL_ScreenFade( player, black, fadeouttime, holdtime, FFADE_OUT | FFADE_STAYOUT | FFADE_PURGE ); + + m_Tunneling.AddToTail( tunnel ); + + player->SetMoveType( MOVETYPE_NONE ); + player->EnableControl( false ); + player->AddEffects( EF_NODRAW ); + player->AddSolidFlags( FSOLID_NOT_SOLID ); + + CPASAttenuationFilter filter( player, "ObjectTunnelTrigger.TeleportSound" ); + EmitSound( filter, player->entindex(), "ObjectTunnelTrigger.TeleportSound" ); + + OnTunnelTriggerStart.FireOutput( player, this ); +} + +float CObjectTunnelTrigger::GetTeleportDuration( void ) +{ + float duration = m_flTeleportDuration; + + if ( m_flTeleportVelocity > 0.0f && m_hTunnelExit != NULL ) + { + Vector delta = m_hTunnelExit->GetAbsOrigin() - GetAbsOrigin(); + float dist = delta.Length(); + duration = dist / m_flTeleportVelocity; + } + else if ( m_flTeleportDuration == -1.0f ) + { + Msg( "obj_tunnel_trigger: must set TeleportVelocity or TeleportDuration" ); + m_flTeleportDuration = tf_tunnel_time.GetFloat(); + } + + duration = MIN( duration, MAX_TUNNEL_DURATION ); + return duration; +} + +bool CObjectTunnelTrigger::KeepTunneling( TunnelPlayer *tunnel ) +{ + if ( !tunnel || ( tunnel->player == NULL ) ) + { + return false; + } + + float remaining = tunnel->teleporttime - gpGlobals->curtime + 0.5f; + remaining = MAX( 0.0f, remaining ); + + tunnel->iremaining = (int)( remaining ); + + if ( !tunnel->exitstarted ) + { + if ( gpGlobals->curtime > tunnel->fadeintime ) + { + tunnel->exitstarted = true; + // Fade user screen to black + color32 black = {0,0,0,255}; + UTIL_ScreenFade( tunnel->player, black, tunnel->fadetime, 0.0, FFADE_IN | FFADE_PURGE ); + + // Move to tunnel exit spot now that we're half-way through teleport + if ( m_hTunnelExit != NULL ) + { + tunnel->player->EnableControl( true ); + tunnel->player->RemoveEffects( EF_NODRAW ); + + // Change the player to non-solid before the teleport, so the physics system doesn't think he + // actually moved this distance: + int OriginalSolidFlags = tunnel->player->GetSolidFlags(); + tunnel->player->AddSolidFlags( FSOLID_NOT_SOLID); + + // Do a placement test to prevent the player from teleporting inside another player, the ground, or just to help + // prevent badly placed tunnels from causing stuck situations. + Vector vTarget = m_hTunnelExit->GetAbsOrigin(); + Vector vOriginal = vTarget; + + if ( !EntityPlacementTest( tunnel->player, vOriginal, vTarget, true ) ) + { + Warning("Couldn't place entity after tunnel teleport.\n"); + } + + + tunnel->player->Teleport( &vTarget /*m_hTunnelExit->GetAbsOrigin()*/, &m_hTunnelExit->GetAbsAngles(), NULL ); + tunnel->player->SnapEyeAngles( m_hTunnelExit->GetAbsAngles() ); + tunnel->player->SetAbsVelocity( vec3_origin ); + + // Restore the player's solid flags. + tunnel->player->SetSolidFlags(OriginalSolidFlags); + + } + } +// Can't quite do this because the player's weapons are still visible flying across the map even if +// he is hidden +#if 0 + else if ( gpGlobals->curtime > tunnel->tunnelstarted + tunnel->fadetime ) + { + float travel_time = tunnel->duration - 2 * tunnel->fadetime; + if ( travel_time > 0.0f ) + { + float f = ( gpGlobals->curtime - tunnel->tunnelstarted - tunnel->fadetime ) / travel_time; + f = clamp( f, 0.0f, 1.0f ); + if ( m_hTunnelExit != NULL ) + { + Vector delta = m_hTunnelExit->GetAbsOrigin() - tunnel->startpos; + Vector currentPos; + VectorMA( tunnel->startpos, f, delta, currentPos ); + + tunnel->player->Teleport( ¤tPos, NULL, NULL ); + } + } + } +#endif + } + + if ( tunnel->ilastremaining != tunnel->iremaining && + tunnel->needremainigcounter && + tunnel->iremaining >= 1 && + tunnel->player != NULL ) + { + // Counter + ClientPrint( tunnel->player, HUD_PRINTCENTER, UTIL_VarArgs("\nExiting tunnel in %d %s\n", tunnel->iremaining, tunnel->iremaining > 1 ? "seconds" : "second" ) ); + } + + tunnel->ilastremaining = tunnel->iremaining; + + // TODO: Play footstep or some other teleport sounds occasionaly to this player? + + bool done = ( gpGlobals->curtime > tunnel->teleporttime ) ? true : false; + if ( done ) + { + color32 black = {0,0,0,255}; + UTIL_ScreenFade( tunnel->player, black, 0.0f, 0.0f, FFADE_IN | FFADE_PURGE ); + + tunnel->player->SetMoveType( MOVETYPE_WALK ); + tunnel->player->EnableControl( true ); + tunnel->player->RemoveEffects( EF_NODRAW ); + tunnel->player->RemoveSolidFlags( FSOLID_NOT_SOLID ); + + // TODO: Play an exit sound?? + OnTunnelTriggerEnd.FireOutput( tunnel->player, this ); + } + + return !done; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CObjectTunnelTrigger::TunnelThink() +{ + // Make sure it's not already in the list + int c = m_Tunneling.Count(); + for ( int i = c - 1; i >= 0; i-- ) + { + TunnelPlayer *tp = &m_Tunneling[ i ]; + + if ( !KeepTunneling( tp ) ) + { + m_Tunneling.Remove( i ); + } + } + + SetNextThink( gpGlobals->curtime + TUNNEL_THINK_INTERVAL ); +} + +void CObjectTunnelTrigger::Precache() +{ + BaseClass::Precache(); + + PrecacheScriptSound( "ObjectTunnelTrigger.TeleportSound" ); + PrecacheScriptSound( "ObjectTunnelTrigger.DisabledSound" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CObjectTunnelTrigger::Spawn() +{ + Precache(); + + SetSolid( SOLID_BSP ); + AddSolidFlags( FSOLID_TRIGGER ); + SetMoveType( MOVETYPE_NONE ); + AddEffects( EF_NODRAW ); + SetModel( STRING( GetModelName() ) ); + AddFlag( FL_NOTARGET ); + + m_bActive = false; +} + +//----------------------------------------------------------------------------- +// Purpose: See if we've got a gather point specified +//----------------------------------------------------------------------------- +void CObjectTunnelTrigger::Activate( void ) +{ + BaseClass::Activate(); + + if (m_target != NULL_STRING) + { + CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, m_target ); + if ( pEnt && FClassnameIs( pEnt, "info_tunnel_exit" ) ) + { + m_hTunnelExit = static_cast< CInfoTunnelExit * >( pEnt ); + } + else + { + Msg( "CObjectTunnelTrigger::Activate, unable to connect tunnel to target %s\n", + STRING( m_target ) ); + } + } + else + { + Msg( "CObjectTunnelTrigger::Activate, missing target\n" ); + } + + SetActive( true ); + + SetThink( TunnelThink ); + SetNextThink( gpGlobals->curtime + TUNNEL_THINK_INTERVAL ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : active - +//----------------------------------------------------------------------------- +void CObjectTunnelTrigger::SetActive( bool active ) +{ + m_bActive = active; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CObjectTunnelTrigger::GetActive( void ) const +{ + return m_bActive; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CObjectTunnelTrigger::InputSetActive( inputdata_t &inputdata ) +{ + SetActive( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CObjectTunnelTrigger::InputSetInactive( inputdata_t &inputdata ) +{ + SetActive( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CObjectTunnelTrigger::InputToggleActive( inputdata_t &inputdata ) +{ + if ( m_bActive ) + { + SetActive( false ); + } + else + { + SetActive( true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CObjectTunnelTrigger::InputSetTarget( inputdata_t &inputdata ) +{ + CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, inputdata.value.String() ); + if ( pEnt && FClassnameIs( pEnt, "info_tunnel_exit" ) ) + { + m_hTunnelExit = static_cast< CInfoTunnelExit * >( pEnt ); + } + else + { + Msg( "CObjectTunnelTrigger::InputSetTarget: Couldn't find info_tunnel_exit named %s\n", + inputdata.value.String() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CObjectTunnelTrigger::InputSetTeleportDuration( inputdata_t &inputdata ) +{ + m_flTeleportDuration = inputdata.value.Float(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CObjectTunnelTrigger::InputSetTeleportVelocity( inputdata_t &inputdata ) +{ + m_flTeleportVelocity = inputdata.value.Float(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pOther - +//----------------------------------------------------------------------------- +void CObjectTunnelTrigger::StartTouch( CBaseEntity *pOther ) +{ + if ( !pOther || !pOther->IsPlayer() ) + return; + + // Only works for my team, of course + if ( !pOther->InSameTeam( this ) ) + return; + + if ( m_hTunnelExit == NULL ) + return; + + // It's been damaged to the point of being disabled + if ( !GetActive() ) + { + // Play a deny sound + CPASAttenuationFilter filter( pOther, "ObjectTunnelTrigger.DisabledSound" ); + EmitSound( filter, pOther->entindex(), "ObjectTunnelTrigger.DisabledSound" ); + return; + } + + StartTunneling( (CBaseTFPlayer *)pOther ); +} + +LINK_ENTITY_TO_CLASS(obj_tunnel_trigger,CObjectTunnelTrigger); + +BEGIN_DATADESC( CObjectTunnelTrigger ) + // inputs + DEFINE_INPUTFUNC( FIELD_VOID, "SetActive", InputSetActive ), + DEFINE_INPUTFUNC( FIELD_VOID, "SetInactive", InputSetInactive ), + DEFINE_INPUTFUNC( FIELD_VOID, "ToggleActive", InputToggleActive ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget", InputSetTarget ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTeleportDuration", InputSetTeleportDuration ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTeleportVelocity", InputSetTeleportVelocity ), + + // outputs + DEFINE_OUTPUT( OnTunnelTriggerStart, "OnTunnelTriggerStart" ), + DEFINE_OUTPUT( OnTunnelTriggerEnd, "OnTunnelTriggerEnd" ), + + // keyvalues + DEFINE_KEYFIELD_NOT_SAVED( m_flTeleportDuration, FIELD_FLOAT, "TeleportDuration" ), + DEFINE_KEYFIELD_NOT_SAVED( m_flTeleportVelocity, FIELD_FLOAT, "TeleportVelocity" ), +END_DATADESC() |