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/client/tf/c_baseobject.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/tf/c_baseobject.cpp')
| -rw-r--r-- | game/client/tf/c_baseobject.cpp | 1248 |
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 ); |