summaryrefslogtreecommitdiff
path: root/game/client/tf/c_baseobject.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/client/tf/c_baseobject.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'game/client/tf/c_baseobject.cpp')
-rw-r--r--game/client/tf/c_baseobject.cpp1248
1 files changed, 1248 insertions, 0 deletions
diff --git a/game/client/tf/c_baseobject.cpp b/game/client/tf/c_baseobject.cpp
new file mode 100644
index 0000000..c0eec13
--- /dev/null
+++ b/game/client/tf/c_baseobject.cpp
@@ -0,0 +1,1248 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Clients CBaseObject
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "c_baseobject.h"
+#include "c_tf_player.h"
+#include "hud.h"
+#include "c_tf_team.h"
+#include "engine/IEngineSound.h"
+#include "particles_simple.h"
+#include "functionproxy.h"
+#include "IEffects.h"
+#include "model_types.h"
+#include "particlemgr.h"
+#include "particle_collision.h"
+#include "c_tf_weapon_builder.h"
+#include "ivrenderview.h"
+#include "ObjectControlPanel.h"
+#include "engine/ivmodelinfo.h"
+#include "c_te_effect_dispatch.h"
+#include "toolframework_client.h"
+#include "tf_hud_building_status.h"
+#include "cl_animevent.h"
+#include "eventlist.h"
+#include "c_obj_sapper.h"
+#include "tf_gamerules.h"
+#include "tf_hud_spectator_extras.h"
+#include "tf_proxyentity.h"
+
+// NVNT for building forces
+#include "haptics/haptic_utils.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// forward declarations
+void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );
+
+#define MAX_VISIBLE_BUILDPOINT_DISTANCE (400 * 400)
+
+// Remove aliasing of name due to shared code
+#undef CBaseObject
+
+IMPLEMENT_AUTO_LIST( IBaseObjectAutoList );
+
+IMPLEMENT_CLIENTCLASS_DT(C_BaseObject, DT_BaseObject, CBaseObject)
+ RecvPropInt(RECVINFO(m_iHealth)),
+ RecvPropInt(RECVINFO(m_iMaxHealth)),
+ RecvPropInt(RECVINFO(m_bHasSapper)),
+ RecvPropInt(RECVINFO(m_iObjectType)),
+ RecvPropBool(RECVINFO(m_bBuilding)),
+ RecvPropBool(RECVINFO(m_bPlacing)),
+ RecvPropBool(RECVINFO(m_bCarried)),
+ RecvPropBool(RECVINFO(m_bCarryDeploy)),
+ RecvPropBool(RECVINFO(m_bMiniBuilding)),
+ RecvPropFloat(RECVINFO(m_flPercentageConstructed)),
+ RecvPropInt(RECVINFO(m_fObjectFlags)),
+ RecvPropEHandle(RECVINFO(m_hBuiltOnEntity)),
+ RecvPropInt( RECVINFO( m_bDisabled ) ),
+ RecvPropEHandle( RECVINFO( m_hBuilder ) ),
+ RecvPropVector( RECVINFO( m_vecBuildMaxs ) ),
+ RecvPropVector( RECVINFO( m_vecBuildMins ) ),
+ RecvPropInt( RECVINFO( m_iDesiredBuildRotations ) ),
+ RecvPropInt( RECVINFO( m_bServerOverridePlacement ) ),
+ RecvPropInt( RECVINFO(m_iUpgradeLevel) ),
+ RecvPropInt( RECVINFO(m_iUpgradeMetal) ),
+ RecvPropInt( RECVINFO(m_iUpgradeMetalRequired) ),
+ RecvPropInt( RECVINFO(m_iHighestUpgradeLevel) ),
+ RecvPropInt( RECVINFO(m_iObjectMode) ),
+ RecvPropBool( RECVINFO( m_bDisposableBuilding ) ),
+ RecvPropBool( RECVINFO( m_bWasMapPlaced ) ),
+ RecvPropBool( RECVINFO( m_bPlasmaDisable ) ),
+END_RECV_TABLE()
+
+ConVar cl_obj_test_building_damage( "cl_obj_test_building_damage", "-1", FCVAR_CHEAT, "debug building damage", true, -1, true, BUILDING_DAMAGE_LEVEL_CRITICAL );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_BaseObject::C_BaseObject( )
+{
+ m_YawPreviewState = YAW_PREVIEW_OFF;
+ m_bBuilding = false;
+ m_bPlacing = false;
+ m_flPercentageConstructed = 0;
+ m_fObjectFlags = 0;
+ m_iOldUpgradeLevel = 0;
+
+ m_flCurrentBuildRotation = 0;
+
+ m_damageLevel = BUILDING_DAMAGE_LEVEL_NONE;
+
+ m_iLastPlacementPosValid = -1;
+
+ m_iObjectMode = 0;
+
+ m_bCarryDeploy = false;
+ m_bOldCarryDeploy = false;
+
+ m_bMiniBuilding = false;
+ m_bDisposableBuilding = false;
+
+ m_vecBuildForward = vec3_origin;
+ m_flBuildDistance = 0.0f;
+
+ m_flInvisibilityPercent = 0.f;
+
+ m_bWasMapPlaced = false;
+
+ m_hDamageEffects = NULL;
+
+ m_bPlasmaDisable = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_BaseObject::~C_BaseObject( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ m_bServerOverridePlacement = true; // assume valid at the start
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::UpdateOnRemove( void )
+{
+ StopAnimGeneratedSounds();
+
+ DestroyBoneAttachments();
+
+ CTFHudSpectatorExtras *pSpectatorExtras = GET_HUDELEMENT( CTFHudSpectatorExtras );
+ if ( pSpectatorExtras )
+ {
+ pSpectatorExtras->RemoveEntity( entindex() );
+ }
+
+ BaseClass::UpdateOnRemove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::PreDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PreDataUpdate( updateType );
+
+ m_iOldHealth = m_iHealth;
+ m_hOldOwner = GetOwner();
+ m_bWasActive = ShouldBeActive();
+ m_bWasBuilding = m_bBuilding;
+ m_bOldDisabled = m_bDisabled;
+ m_bWasPlacing = m_bPlacing;
+
+ m_nObjectOldSequence = GetSequence();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::OnDataChanged( DataUpdateType_t updateType )
+{
+ if (updateType == DATA_UPDATE_CREATED)
+ {
+ CreateBuildPoints();
+ // NVNT if the local player created this send a created effect
+ if(IsOwnedByLocalPlayer() &&haptics)
+ {
+ haptics->ProcessHapticEvent(3, "Game", "Build", GetClassname());
+ }
+ }
+
+ BaseClass::OnDataChanged( updateType );
+
+ // did we just pick up the object?
+ if ( !m_bWasPlacing && m_bPlacing )
+ {
+ m_iLastPlacementPosValid = -1;
+ }
+
+ // Did we just finish building?
+ if ( m_bWasBuilding && !m_bBuilding )
+ {
+ FinishedBuilding();
+ }
+ else if ( !m_bWasBuilding && m_bBuilding )
+ {
+ ResetClientsideFrame();
+ }
+
+ // Did we just go active?
+ bool bShouldBeActive = ShouldBeActive();
+ if ( !m_bWasActive && bShouldBeActive )
+ {
+ OnGoActive();
+ }
+ else if ( m_bWasActive && !bShouldBeActive )
+ {
+ OnGoInactive();
+ }
+
+ if ( m_bDisabled != m_bOldDisabled )
+ {
+ if ( m_bDisabled )
+ {
+ OnStartDisabled();
+ }
+ else
+ {
+ OnEndDisabled();
+ }
+ }
+
+ if ( !IsBuilding() && m_iHealth != m_iOldHealth )
+ {
+ // recalc our damage particle state
+ BuildingDamageLevel_t damageLevel = CalculateDamageLevel();
+
+ if ( damageLevel != m_damageLevel )
+ {
+ UpdateDamageEffects( damageLevel );
+
+ m_damageLevel = damageLevel;
+ }
+ }
+
+ if ( m_bCarryDeploy != m_bOldCarryDeploy )
+ {
+ m_bOldCarryDeploy = m_bCarryDeploy;
+ if ( !m_bCarryDeploy )
+ {
+ // Update our damage effects when we're done redeploying.
+ UpdateDamageEffects( CalculateDamageLevel() );
+ }
+ }
+
+ if ( m_iHealth > m_iOldHealth && m_iHealth == m_iMaxHealth )
+ {
+ // If we were just fully healed, remove all decals
+ RemoveAllDecals();
+ }
+
+ if ( GetOwner() == C_TFPlayer::GetLocalTFPlayer() )
+ {
+ IGameEvent *event = gameeventmanager->CreateEvent( "building_info_changed" );
+ if ( event )
+ {
+ event->SetInt( "building_type", GetType() );
+ event->SetInt( "object_mode", GetObjectMode() );
+ gameeventmanager->FireEventClientSide( event );
+ }
+ }
+
+ if ( IsPlacing() && GetSequence() != m_nObjectOldSequence )
+ {
+ // Ignore server sequences while placing
+ OnPlacementStateChanged( m_iLastPlacementPosValid > 0 );
+ }
+
+ if ( m_iOldUpgradeLevel != m_iUpgradeLevel )
+ {
+ UpgradeLevelChanged();
+ m_iOldUpgradeLevel = m_iUpgradeLevel;
+ }
+ // NVNT building status
+ if(IsOwnedByLocalPlayer()) {
+ if(m_bWasBuilding!=m_bBuilding) {
+ if(m_bBuilding && haptics) {
+ haptics->ProcessHapticEvent(3, "Game", "Building", GetClassname());
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::SetDormant( bool bDormant )
+{
+ BaseClass::SetDormant( bDormant );
+ //ENTITY_PANEL_ACTIVATE( "analyzed_object", !bDormant );
+}
+
+#define TF_OBJ_BODYGROUPTURNON 1
+#define TF_OBJ_BODYGROUPTURNOFF 0
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : origin -
+// angles -
+// event -
+// *options -
+//-----------------------------------------------------------------------------
+void C_BaseObject::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
+{
+ switch ( event )
+ {
+ default:
+ {
+ BaseClass::FireEvent( origin, angles, event, options );
+ }
+ break;
+ case TF_OBJ_PLAYBUILDSOUND:
+ {
+ EmitSound( options );
+ }
+ break;
+ case TF_OBJ_ENABLEBODYGROUP:
+ {
+ int index_ = FindBodygroupByName( options );
+ if ( index_ >= 0 )
+ {
+ SetBodygroup( index_, TF_OBJ_BODYGROUPTURNON );
+ }
+ }
+ break;
+ case TF_OBJ_DISABLEBODYGROUP:
+ {
+ int index_ = FindBodygroupByName( options );
+ if ( index_ >= 0 )
+ {
+ SetBodygroup( index_, TF_OBJ_BODYGROUPTURNOFF );
+ }
+ }
+ break;
+ case TF_OBJ_ENABLEALLBODYGROUPS:
+ case TF_OBJ_DISABLEALLBODYGROUPS:
+ {
+ // Start at 1, because body 0 is the main .mdl body...
+ // Is this the way we want to do this?
+ int count = GetNumBodyGroups();
+ for ( int i = 1; i < count; i++ )
+ {
+ int subpartcount = GetBodygroupCount( i );
+ if ( subpartcount == 2 )
+ {
+ SetBodygroup( i,
+ ( event == TF_OBJ_ENABLEALLBODYGROUPS ) ?
+ TF_OBJ_BODYGROUPTURNON : TF_OBJ_BODYGROUPTURNOFF );
+ }
+ else
+ {
+ DevMsg( "TF_OBJ_ENABLE/DISABLEBODY GROUP: %s has a group with %i subparts, should be exactly 2\n",
+ GetClassname(), subpartcount );
+ }
+ }
+ }
+ break;
+ }
+}
+
+
+const char* C_BaseObject::GetStatusName() const
+{
+ return GetObjectInfo( GetType() )->m_AltModes[GetObjectMode()].pszStatusName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: placement state has changed, update the model
+//-----------------------------------------------------------------------------
+void C_BaseObject::OnPlacementStateChanged( bool bValidPlacement )
+{
+ if ( bValidPlacement )
+ {
+ // NVNT if the local player placed this send a created effect
+ if(IsOwnedByLocalPlayer()&&haptics)
+ {
+ haptics->ProcessHapticEvent(3, "Game", "Placed", GetClassname());
+ }
+ SetActivity( ACT_OBJ_PLACING );
+ }
+ else
+ {
+ SetActivity( ACT_OBJ_IDLE );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::Simulate( void )
+{
+ if ( IsPlacing() && !MustBeBuiltOnAttachmentPoint() )
+ {
+ int iValidPlacement = ( IsPlacementPosValid() && ServerValidPlacement() ) ? 1 : 0;
+
+ if ( m_iLastPlacementPosValid != iValidPlacement )
+ {
+ m_iLastPlacementPosValid = iValidPlacement;
+ OnPlacementStateChanged( m_iLastPlacementPosValid > 0 );
+ }
+
+ // We figure out our own placement pos, but we still leave it to the server to
+ // do collision with other entities and nobuild triggers, so that sets the
+ // placement animation
+
+ SetLocalOrigin( m_vecBuildOrigin );
+ InvalidateBoneCache();
+
+ // Clear out our origin and rotation interpolation history
+ // so we don't pop when we teleport in the actual position from the server
+
+ CInterpolatedVar< Vector > &interpolator = GetOriginInterpolator();
+ interpolator.ClearHistory();
+
+ CInterpolatedVar<QAngle> &rotInterpolator = GetRotationInterpolator();
+ rotInterpolator.ClearHistory();
+ }
+ else if ( !IsPlacing() && !IsCarried() && m_iLastPlacementPosValid == 0 )
+ {
+ // HACK HACK: This sentry has been placed, but was placed on the server before the client updated
+ // from the carry position to see that was a valid placement.
+ // It missed its chance to set the correct activity, so we're doing it now.
+ SetActivity( ACT_OBJ_RUNNING );
+
+ // Check if the activity was valid because it might have still been using the older placement model
+ if ( GetActivity() != ACT_INVALID )
+ {
+ // Remember to retest our placement, but don't keep forcing the running activity
+ m_iLastPlacementPosValid = -1;
+ }
+ }
+
+ BaseClass::Simulate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return false if the server is telling us we can't place right now
+// could be due to placing in a nobuild or respawn room
+//-----------------------------------------------------------------------------
+bool C_BaseObject::ServerValidPlacement( void )
+{
+ return m_bServerOverridePlacement;
+}
+
+bool C_BaseObject::WasLastPlacementPosValid( void )
+{
+ if ( MustBeBuiltOnAttachmentPoint() )
+ {
+ return ( !IsEffectActive(EF_NODRAW) );
+ }
+
+ return ( m_iLastPlacementPosValid > 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int C_BaseObject::DrawModel( int flags )
+{
+ int drawn;
+
+ // If we're a brush-built, map-defined object chain up to baseentity draw
+ if ( modelinfo->GetModelType( GetModel() ) == mod_brush )
+ {
+ drawn = CBaseEntity::DrawModel(flags);
+ }
+ else
+ {
+ drawn = BaseClass::DrawModel(flags);
+ }
+
+ HighlightBuildPoints( flags );
+
+ return drawn;
+}
+
+float C_BaseObject::GetReversesBuildingConstructionSpeed( void )
+{
+ if ( HasSapper() )
+ {
+ C_ObjectSapper *pSapper = dynamic_cast< C_ObjectSapper* >( FirstMoveChild() );
+ if ( pSapper )
+ {
+ return pSapper->GetReversesBuildingConstructionSpeed();
+ }
+ }
+
+ return 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::HighlightBuildPoints( int flags )
+{
+ C_TFPlayer *pLocal = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pLocal )
+ return;
+
+ if ( !GetNumBuildPoints() || !InLocalTeam() )
+ return;
+
+ C_TFWeaponBuilder *pBuilderWpn = dynamic_cast< C_TFWeaponBuilder * >( pLocal->GetActiveWeaponForSelection() );
+ if ( !pBuilderWpn )
+ return;
+ if ( !pBuilderWpn->IsPlacingObject() )
+ return;
+ C_BaseObject *pPlacementObj = pBuilderWpn->GetPlacementModel();
+ if ( !pPlacementObj || pPlacementObj == this )
+ return;
+
+ // Near enough?
+ if ( (GetAbsOrigin() - pLocal->GetAbsOrigin()).LengthSqr() < MAX_VISIBLE_BUILDPOINT_DISTANCE )
+ {
+ bool bRestoreModel = false;
+ Vector vecPrevAbsOrigin = pPlacementObj->GetAbsOrigin();
+ QAngle vecPrevAbsAngles = pPlacementObj->GetAbsAngles();
+
+ Vector orgColor;
+ render->GetColorModulation( orgColor.Base() );
+ float orgBlend = render->GetBlend();
+
+ bool bSameTeam = ( pPlacementObj->GetTeamNumber() == GetTeamNumber() );
+
+ if ( pPlacementObj->IsHostileUpgrade() && bSameTeam )
+ {
+ // Don't hilight hostile upgrades on friendly objects
+ return;
+ }
+ else if ( !bSameTeam )
+ {
+ // Don't hilight upgrades on enemy objects
+ return;
+ }
+
+ // Any empty buildpoints?
+ for ( int i = 0; i < GetNumBuildPoints(); i++ )
+ {
+ // Can this object build on this point?
+ if ( CanBuildObjectOnBuildPoint( i, pPlacementObj->GetType() ) )
+ {
+ Vector vecBPOrigin;
+ QAngle vecBPAngles;
+ if ( GetBuildPoint(i, vecBPOrigin, vecBPAngles) )
+ {
+ pPlacementObj->InvalidateBoneCaches();
+
+ Vector color( 0, 255, 0 );
+ render->SetColorModulation( color.Base() );
+ float frac = fmod( gpGlobals->curtime, 3 );
+ frac *= 2 * M_PI;
+ frac = cos( frac );
+ render->SetBlend( (175 + (int)( frac * 75.0f )) / 255.0 );
+
+ // FIXME: This truly sucks! The bone cache should use
+ // render location for this computation instead of directly accessing AbsAngles
+ // Necessary for bone cache computations to work
+ pPlacementObj->SetAbsOrigin( vecBPOrigin );
+ pPlacementObj->SetAbsAngles( vecBPAngles );
+
+ modelrender->DrawModel(
+ flags,
+ pPlacementObj,
+ pPlacementObj->GetModelInstance(),
+ pPlacementObj->index,
+ pPlacementObj->GetModel(),
+ vecBPOrigin,
+ vecBPAngles,
+ pPlacementObj->m_nSkin,
+ pPlacementObj->m_nBody,
+ pPlacementObj->m_nHitboxSet
+ );
+
+ bRestoreModel = true;
+ }
+ }
+ }
+
+ if ( bRestoreModel )
+ {
+ pPlacementObj->SetAbsOrigin(vecPrevAbsOrigin);
+ pPlacementObj->SetAbsAngles(vecPrevAbsAngles);
+ pPlacementObj->InvalidateBoneCaches();
+
+ render->SetColorModulation( orgColor.Base() );
+ render->SetBlend( orgBlend );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Builder preview...
+//-----------------------------------------------------------------------------
+void C_BaseObject::ActivateYawPreview( bool enable )
+{
+ m_YawPreviewState = enable ? YAW_PREVIEW_ON : YAW_PREVIEW_WAITING_FOR_UPDATE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::PreviewYaw( float yaw )
+{
+ m_fYawPreview = yaw;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_BaseObject::IsPreviewingYaw() const
+{
+ return m_YawPreviewState != YAW_PREVIEW_OFF;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+BuildingDamageLevel_t C_BaseObject::CalculateDamageLevel( void )
+{
+ float flPercentHealth = (float)m_iHealth / (float)m_iMaxHealth;
+
+ BuildingDamageLevel_t damageLevel = BUILDING_DAMAGE_LEVEL_NONE;
+
+ if ( flPercentHealth < 0.25 )
+ {
+ damageLevel = BUILDING_DAMAGE_LEVEL_CRITICAL;
+ }
+ else if ( flPercentHealth < 0.45 )
+ {
+ damageLevel = BUILDING_DAMAGE_LEVEL_HEAVY;
+ }
+ else if ( flPercentHealth < 0.65 )
+ {
+ damageLevel = BUILDING_DAMAGE_LEVEL_MEDIUM;
+ }
+ else if ( flPercentHealth < 0.85 )
+ {
+ damageLevel = BUILDING_DAMAGE_LEVEL_LIGHT;
+ }
+
+ if ( cl_obj_test_building_damage.GetInt() >= 0 )
+ {
+ damageLevel = (BuildingDamageLevel_t)cl_obj_test_building_damage.GetInt();
+ }
+
+ return damageLevel;
+}
+
+/*
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::Release( void )
+{
+ // Remove any reticles on this entity
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pPlayer )
+ {
+ pPlayer->Remove_Target( this );
+ }
+
+ BaseClass::Release();
+}
+*/
+
+//-----------------------------------------------------------------------------
+// Ownership:
+//-----------------------------------------------------------------------------
+C_TFPlayer *C_BaseObject::GetOwner()
+{
+ return m_hBuilder;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_BaseObject::IsOwnedByLocalPlayer() const
+{
+ if ( !m_hBuilder )
+ return false;
+
+ return ( m_hBuilder == C_TFPlayer::GetLocalTFPlayer() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add entity to visibile entities list
+//-----------------------------------------------------------------------------
+void C_BaseObject::AddEntity( void )
+{
+ // If set to invisible, skip. Do this before resetting the entity pointer so it has
+ // valid data to decide whether it's visible.
+ if ( !ShouldDraw() )
+ {
+ return;
+ }
+
+ // Update the entity position
+ //UpdatePosition();
+
+ // Yaw preview
+ if (m_YawPreviewState != YAW_PREVIEW_OFF)
+ {
+ // This piece of code makes it so we keep using the preview
+ // until we get a network update which matches the update value
+ if (m_YawPreviewState == YAW_PREVIEW_WAITING_FOR_UPDATE)
+ {
+ if (fmod( fabs(GetLocalAngles().y - m_fYawPreview), 360.0f) < 1.0f)
+ {
+ m_YawPreviewState = YAW_PREVIEW_OFF;
+ }
+ }
+
+ if (GetLocalOrigin().y != m_fYawPreview)
+ {
+ SetLocalAnglesDim( Y_INDEX, m_fYawPreview );
+ InvalidateBoneCache();
+ }
+ }
+
+ // Create flashlight effects, etc.
+ CreateLightEffects();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::Select( void )
+{
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+ pPlayer->SetSelectedObject( this );
+}
+
+void C_BaseObject::ResetClientsideFrame( void )
+{
+ SetCycle( GetReversesBuildingConstructionSpeed() != 0.0f ? 1.0f : 0.0f );
+}
+
+//-----------------------------------------------------------------------------
+// Sends client commands back to the server:
+//-----------------------------------------------------------------------------
+void C_BaseObject::SendClientCommand( const char *pCmd )
+{
+ char szbuf[128];
+ Q_snprintf( szbuf, sizeof( szbuf ), "objcmd %d %s", entindex(), pCmd );
+ engine->ClientCmd(szbuf);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a text description for the object target
+//-----------------------------------------------------------------------------
+const char *C_BaseObject::GetTargetDescription( void ) const
+{
+ return GetStatusName();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a text description for the object target (more verbose)
+//-----------------------------------------------------------------------------
+const char *C_BaseObject::GetIDString( void )
+{
+ m_szIDString[0] = 0;
+ RecalculateIDString();
+ return m_szIDString;
+}
+
+
+//-----------------------------------------------------------------------------
+// It's a valid ID target when it's building
+//-----------------------------------------------------------------------------
+bool C_BaseObject::IsValidIDTarget( void )
+{
+ return InSameTeam( C_TFPlayer::GetLocalTFPlayer() ) && m_bBuilding;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::RecalculateIDString( void )
+{
+ // Subclasses may have filled this out with a string
+ if ( !m_szIDString[0] )
+ {
+ Q_strncpy( m_szIDString, GetTargetDescription(), sizeof(m_szIDString) );
+ }
+
+ // Have I taken damage?
+ if ( m_iHealth < m_iMaxHealth )
+ {
+ char szHealth[ MAX_ID_STRING ];
+ if ( m_bBuilding )
+ {
+ Q_snprintf( szHealth, sizeof(szHealth), "\nConstruction at %.0f percent\nHealth at %.0f percent", (m_flPercentageConstructed * 100), ceil(((float)m_iHealth / (float)m_iMaxHealth) * 100) );
+ }
+ else
+ {
+ Q_snprintf( szHealth, sizeof(szHealth), "\nHealth at %.0f percent", ceil(((float)m_iHealth / (float)m_iMaxHealth) * 100) );
+ }
+ Q_strncat( m_szIDString, szHealth, sizeof(m_szIDString), COPY_ALL_CHARACTERS );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Player has waved his crosshair over this entity. Display appropriate hints.
+//-----------------------------------------------------------------------------
+void C_BaseObject::DisplayHintTo( C_BasePlayer *pPlayer )
+{
+ bool bHintPlayed = false;
+
+ C_TFPlayer *pTFPlayer = ToTFPlayer(pPlayer);
+ if ( InSameTeam( pPlayer ) )
+ {
+ // We're looking at a friendly object.
+
+ if ( HasSapper() )
+ {
+ bHintPlayed = pPlayer->HintMessage( HINT_OBJECT_HAS_SAPPER, true, true );
+ }
+
+ if ( pTFPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) )
+ {
+ // I'm an engineer.
+
+ // If I'm looking at a constructing object, let me know I can help build it (but not
+ // if I built it myself, since I've already got that hint from the wrench).
+ if ( !bHintPlayed && IsBuilding() && GetBuilder() != pTFPlayer )
+ {
+ bHintPlayed = pPlayer->HintMessage( HINT_ENGINEER_USE_WRENCH_ONOTHER, false, true );
+ }
+
+ // If it's damaged, I can repair it
+ if ( !bHintPlayed && !IsBuilding() && GetHealth() < GetMaxHealth() )
+ {
+ bHintPlayed = pPlayer->HintMessage( HINT_ENGINEER_REPAIR_OBJECT, false, true );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::GetGlowEffectColor( float *r, float *g, float *b )
+{
+ if ( TFGameRules() )
+ {
+ TFGameRules()->GetTeamGlowColor( GetTeamNumber(), *r, *g, *b );
+ }
+ else
+ {
+ *r = 0.76f;
+ *g = 0.76f;
+ *b = 0.76f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Does this object have a sapper on it
+//-----------------------------------------------------------------------------
+bool C_BaseObject::HasSapper( void )
+{
+ return m_bHasSapper;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_BaseObject::IsPlasmaDisabled( void )
+{
+ return m_bPlasmaDisable;
+}
+
+void C_BaseObject::OnStartDisabled()
+{
+}
+
+void C_BaseObject::OnEndDisabled()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::GetTargetIDString( OUT_Z_BYTECAP( iMaxLenInBytes ) wchar_t *sIDString, int iMaxLenInBytes, bool bSpectator )
+{
+ Assert( iMaxLenInBytes >= sizeof(sIDString[0]) );
+ sIDString[0] = '\0';
+
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+
+ if ( !pLocalPlayer )
+ return;
+
+ if ( pLocalPlayer->InSameDisguisedTeam( this ) || pLocalPlayer->IsPlayerClass( TF_CLASS_SPY ) || bSpectator )
+ {
+ wchar_t wszBuilderName[ MAX_PLAYER_NAME_LENGTH ];
+
+ const char *pszStatusName = GetStatusName();
+ const wchar_t *wszObjectName = g_pVGuiLocalize->Find( pszStatusName );
+
+ bool bHasMode = false;
+ const char *printFormatString = "#TF_playerid_object";
+
+ if ( IsMiniBuilding() && !IsDisposableBuilding() )
+ {
+ printFormatString = "#TF_playerid_object_mini";
+ }
+
+ const wchar_t *wszModeName = L"";
+ const CObjectInfo* pObjectInfo = GetObjectInfo( GetType() );
+ if ( pObjectInfo && (pObjectInfo->m_iNumAltModes > 0) )
+ {
+ const char *pszModeName = pObjectInfo->m_AltModes[GetObjectMode()].pszModeName;
+ wszModeName = g_pVGuiLocalize->Find( pszModeName );
+ printFormatString = "TF_playerid_object_mode";
+ bHasMode = true;
+ }
+
+ if ( !wszObjectName )
+ {
+ wszObjectName = L"";
+ }
+
+ C_BasePlayer *pBuilder = GetOwner();
+
+ if ( pBuilder )
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode( pBuilder->GetPlayerName(), wszBuilderName, sizeof(wszBuilderName) );
+ }
+ else
+ {
+ wszBuilderName[0] = '\0';
+ }
+
+ // building or live, show health
+ wchar_t * localizedString = g_pVGuiLocalize->Find( printFormatString );
+ if ( localizedString )
+ {
+ if ( bHasMode )
+ {
+ g_pVGuiLocalize->ConstructString( sIDString, iMaxLenInBytes, localizedString,
+ 3, wszObjectName, wszBuilderName, wszModeName );
+ }
+ else
+ {
+ g_pVGuiLocalize->ConstructString( sIDString, iMaxLenInBytes, localizedString,
+ 2, wszObjectName, wszBuilderName );
+ }
+ }
+
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::GetTargetIDDataString( OUT_Z_BYTECAP(iMaxLenInBytes) wchar_t *sDataString, int iMaxLenInBytes )
+{
+ Assert( iMaxLenInBytes >= sizeof(sDataString[0]) );
+ sDataString[0] = '\0';
+
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( !pLocalPlayer )
+ return;
+
+ // Sentryguns have models for each level, so we don't show it in their target ID.
+ bool bShowLevel = ( GetType() != OBJ_SENTRYGUN );
+
+ wchar_t wszLevel[32];
+ if ( bShowLevel )
+ {
+ _snwprintf( wszLevel, ARRAYSIZE(wszLevel) - 1, L"%d", m_iUpgradeLevel );
+ wszLevel[ ARRAYSIZE(wszLevel)-1 ] = '\0';
+ }
+
+ if ( m_iUpgradeLevel >= 3 )
+ {
+ if ( bShowLevel )
+ {
+ g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find("#TF_playerid_object_level"),
+ 1,
+ wszLevel );
+ }
+ return;
+ }
+
+ wchar_t wszBuilderName[ MAX_PLAYER_NAME_LENGTH ];
+ wchar_t wszObjectName[ 32 ];
+ wchar_t wszUpgradeProgress[ 32 ];
+
+ g_pVGuiLocalize->ConvertANSIToUnicode( GetStatusName(), wszObjectName, sizeof(wszObjectName) );
+
+ C_BasePlayer *pBuilder = GetOwner();
+
+ if ( pBuilder )
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode( pBuilder->GetPlayerName(), wszBuilderName, sizeof(wszBuilderName) );
+ }
+ else
+ {
+ wszBuilderName[0] = '\0';
+ }
+
+ // level 1 and 2 show upgrade progress
+ if ( !IsMiniBuilding() && !IsDisposableBuilding() )
+ {
+ _snwprintf( wszUpgradeProgress, ARRAYSIZE(wszUpgradeProgress) - 1, L"%d / %d", m_iUpgradeMetal, GetUpgradeMetalRequired() );
+ wszUpgradeProgress[ ARRAYSIZE(wszUpgradeProgress)-1 ] = '\0';
+ if ( bShowLevel )
+ {
+ g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find("#TF_playerid_object_upgrading_level"),
+ 2,
+ wszLevel,
+ wszUpgradeProgress );
+ }
+ else
+ {
+ g_pVGuiLocalize->ConstructString( sDataString, iMaxLenInBytes, g_pVGuiLocalize->Find("#TF_playerid_object_upgrading"),
+ 1,
+ wszUpgradeProgress );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int C_BaseObject::GetDisplayPriority( void )
+{
+ return GetObjectInfo( GetType() )->m_iDisplayPriority;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *C_BaseObject::GetHudStatusIcon( void )
+{
+ return GetObjectInfo( GetType() )->m_pHudStatusIcon;
+}
+
+ConVar cl_obj_fake_alert( "cl_obj_fake_alert", "0", 0, "", true, BUILDING_HUD_ALERT_NONE, true, MAX_BUILDING_HUD_ALERT_LEVEL-1 );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+BuildingHudAlert_t C_BaseObject::GetBuildingAlertLevel( void )
+{
+ float flHealthPercent = GetHealth() / GetMaxHealth();
+
+ BuildingHudAlert_t alertLevel = BUILDING_HUD_ALERT_NONE;
+
+ if ( HasSapper() )
+ {
+ alertLevel = BUILDING_HUD_ALERT_SAPPER;
+ }
+ else if ( !IsBuilding() && flHealthPercent < 0.33 )
+ {
+ alertLevel = BUILDING_HUD_ALERT_VERY_LOW_HEALTH;
+ }
+ else if ( !IsBuilding() && flHealthPercent < 0.66 )
+ {
+ alertLevel = BUILDING_HUD_ALERT_LOW_HEALTH;
+ }
+
+ BuildingHudAlert_t iFakeAlert = (BuildingHudAlert_t)cl_obj_fake_alert.GetInt();
+
+ if ( iFakeAlert > BUILDING_HUD_ALERT_NONE &&
+ iFakeAlert < MAX_BUILDING_HUD_ALERT_LEVEL )
+ {
+ alertLevel = iFakeAlert;
+ }
+
+ return alertLevel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ShadowType_t C_BaseObject::ShadowCastType( void )
+{
+ if ( GetInvisibilityLevel() == 1.f )
+ return SHADOWS_NONE;
+
+ return BaseClass::ShadowCastType();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float C_BaseObject::GetInvisibilityLevel( void )
+{
+#ifdef STAGING_ONLY
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ C_TFPlayer *pOwner = GetOwner();
+ if ( pLocalPlayer && pLocalPlayer->m_Shared.InCond( TF_COND_STEALTHED_PHASE ) && pLocalPlayer != pOwner )
+ return 1.f;
+#endif // STAGING_ONLY
+
+ return m_flInvisibilityPercent;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BaseObject::SetInvisibilityLevel( float flValue )
+{
+ m_flPrevInvisibilityPercent = m_flInvisibilityPercent;
+ m_flInvisibilityPercent = clamp( flValue, 0.f, 1.f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: find the anim events that may have started sounds, and stop them.
+//-----------------------------------------------------------------------------
+void C_BaseObject::StopAnimGeneratedSounds( void )
+{
+ MDLCACHE_CRITICAL_SECTION();
+
+ CStudioHdr *pStudioHdr = GetModelPtr();
+ if ( !pStudioHdr )
+ return;
+
+ mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( GetSequence() );
+ if ( seqdesc.numevents == 0 )
+ return;
+
+ float flCurrentCycle = GetCycle();
+ mstudioevent_t *pevent = GetEventIndexForSequence( seqdesc );
+
+ for (int i = 0; i < (int)seqdesc.numevents; i++)
+ {
+ if ( pevent[i].cycle < flCurrentCycle )
+ {
+ if ( pevent[i].event == CL_EVENT_SOUND || pevent[i].event == AE_CL_PLAYSOUND )
+ {
+ StopSound( entindex(), pevent[i].options );
+ }
+ }
+ }
+}
+
+//============================================================================================================
+// POWER PROXY
+//============================================================================================================
+class CObjectPowerProxy : public CResultProxy
+{
+public:
+ bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
+ void OnBind( void *pC_BaseEntity );
+
+private:
+ CFloatInput m_Factor;
+};
+
+bool CObjectPowerProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
+{
+ if (!CResultProxy::Init( pMaterial, pKeyValues ))
+ return false;
+
+ if (!m_Factor.Init( pMaterial, pKeyValues, "scale", 1 ))
+ return false;
+
+ return true;
+}
+
+void CObjectPowerProxy::OnBind( void *pRenderable )
+{
+ // Find the view angle between the player and this entity....
+ IClientRenderable *pRend = (IClientRenderable *)pRenderable;
+ C_BaseEntity *pEntity = pRend->GetIClientUnknown()->GetBaseEntity();
+ C_BaseObject *pObject = dynamic_cast<C_BaseObject*>(pEntity);
+ if (!pObject)
+ return;
+
+ SetFloatResult( m_Factor.GetFloat() );
+
+ if ( ToolsEnabled() )
+ {
+ ToolFramework_RecordMaterialParams( GetMaterial() );
+ }
+}
+
+EXPOSE_INTERFACE( CObjectPowerProxy, IMaterialProxy, "ObjectPower" IMATERIAL_PROXY_INTERFACE_VERSION );
+
+//-----------------------------------------------------------------------------
+// Control screen
+//-----------------------------------------------------------------------------
+class CBasicControlPanel : public CObjectControlPanel
+{
+ DECLARE_CLASS( CBasicControlPanel, CObjectControlPanel );
+
+public:
+ CBasicControlPanel( vgui::Panel *parent, const char *panelName );
+};
+
+
+DECLARE_VGUI_SCREEN_FACTORY( CBasicControlPanel, "basic_control_panel" );
+
+
+//-----------------------------------------------------------------------------
+// Constructor:
+//-----------------------------------------------------------------------------
+CBasicControlPanel::CBasicControlPanel( vgui::Panel *parent, const char *panelName )
+ : BaseClass( parent, "CBasicControlPanel" )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Used for spy invisiblity material
+//-----------------------------------------------------------------------------
+class CBuildingInvisProxy : public CBaseInvisMaterialProxy
+{
+public:
+ virtual void OnBind( C_BaseEntity *pBaseEntity ) OVERRIDE;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+//-----------------------------------------------------------------------------
+void CBuildingInvisProxy::OnBind( C_BaseEntity *pBaseEntity )
+{
+ if ( !m_pPercentInvisible )
+ return;
+
+ if ( !pBaseEntity->IsBaseObject() )
+ return;
+
+ C_BaseObject *pObject = static_cast< C_BaseObject* >( pBaseEntity );
+ if ( !pObject )
+ return;
+
+ CTFPlayer *pOwner = ToTFPlayer( pObject->GetOwner() );
+ if ( !pOwner )
+ {
+ m_pPercentInvisible->SetFloatValue( 0.0f );
+ return;
+ }
+
+ m_pPercentInvisible->SetFloatValue( pObject->GetInvisibilityLevel() );
+}
+
+EXPOSE_INTERFACE( CBuildingInvisProxy, IMaterialProxy, "building_invis" IMATERIAL_PROXY_INTERFACE_VERSION );