You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
922 lines
28 KiB
922 lines
28 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose: Clients CBaseObject
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
#include "cbase.h"
|
||
|
#include "c_baseobject.h"
|
||
|
#include "c_basetfplayer.h"
|
||
|
#include "hud.h"
|
||
|
#include "c_tfteam.h"
|
||
|
#include "engine/IEngineSound.h"
|
||
|
#include "particles_simple.h"
|
||
|
#include "functionproxy.h"
|
||
|
#include "IEffects.h"
|
||
|
#include "c_hint_events.h"
|
||
|
#include "model_types.h"
|
||
|
#include "particlemgr.h"
|
||
|
#include "particle_collision.h"
|
||
|
#include "env_objecteffects.h"
|
||
|
#include "basetfvehicle.h"
|
||
|
#include "c_weapon_builder.h"
|
||
|
#include "ivrenderview.h"
|
||
|
#include "ObjectControlPanel.h"
|
||
|
#include "engine/ivmodelinfo.h"
|
||
|
#include "c_te_effect_dispatch.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
#define MAX_VISIBLE_BUILDPOINT_DISTANCE (400 * 400)
|
||
|
|
||
|
// Remove aliasing of name due to shared code
|
||
|
#undef CBaseObject
|
||
|
|
||
|
IMPLEMENT_CLIENTCLASS_DT(C_BaseObject, DT_BaseObject, CBaseObject)
|
||
|
RecvPropInt(RECVINFO(m_iHealth)),
|
||
|
RecvPropInt(RECVINFO(m_iMaxHealth)),
|
||
|
RecvPropInt(RECVINFO(m_bHasSapper)),
|
||
|
RecvPropInt(RECVINFO(m_iObjectType)),
|
||
|
RecvPropInt(RECVINFO(m_bBuilding)),
|
||
|
RecvPropInt(RECVINFO(m_bPlacing)),
|
||
|
RecvPropFloat(RECVINFO(m_flPercentageConstructed)),
|
||
|
RecvPropInt(RECVINFO(m_fObjectFlags)),
|
||
|
RecvPropInt(RECVINFO(m_bDeteriorating)),
|
||
|
RecvPropEHandle(RECVINFO(m_hBuiltOnEntity)),
|
||
|
RecvPropInt(RECVINFO( m_takedamage ) ),
|
||
|
RecvPropInt( RECVINFO( m_bDisabled ) ),
|
||
|
RecvPropEHandle( RECVINFO( m_hBuilder ) ),
|
||
|
END_RECV_TABLE()
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
C_BaseObject::C_BaseObject( )
|
||
|
{
|
||
|
m_flDamageFlash = 0;
|
||
|
m_YawPreviewState = YAW_PREVIEW_OFF;
|
||
|
m_bBuilding = false;
|
||
|
m_bPlacing = false;
|
||
|
m_flPercentageConstructed = 0;
|
||
|
m_flNextEffect = 0;
|
||
|
m_bOldSapper = m_bHasSapper = false;
|
||
|
m_fObjectFlags = 0;
|
||
|
m_bDeteriorating = false;
|
||
|
m_ThermalMaterial.Init("player/thermal/thermal",TEXTURE_GROUP_CLIENT_EFFECTS);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
C_BaseObject::~C_BaseObject( void )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseObject::PreDataUpdate( DataUpdateType_t updateType )
|
||
|
{
|
||
|
BaseClass::PreDataUpdate( updateType );
|
||
|
|
||
|
m_iOldHealth = m_iHealth;
|
||
|
m_bOldSapper = m_bHasSapper;
|
||
|
m_hOldOwner = GetOwner();
|
||
|
m_bWasActive = ShouldBeActive();
|
||
|
m_bWasBuilding = m_bBuilding;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseObject::OnDataChanged( DataUpdateType_t updateType )
|
||
|
{
|
||
|
if (updateType == DATA_UPDATE_CREATED)
|
||
|
{
|
||
|
if ( !IS_MINIMAP_PANEL_DEFINED( ) && !(m_fObjectFlags & OF_SUPPRESS_APPEAR_ON_MINIMAP) )
|
||
|
{
|
||
|
CONSTRUCT_MINIMAP_PANEL( "minimap_object", MINIMAP_OBJECTS );
|
||
|
}
|
||
|
|
||
|
CreateBuildPoints();
|
||
|
}
|
||
|
|
||
|
BaseClass::OnDataChanged( updateType );
|
||
|
|
||
|
// Did we just finish building?
|
||
|
if ( m_bWasBuilding && !m_bBuilding )
|
||
|
{
|
||
|
FinishedBuilding();
|
||
|
}
|
||
|
|
||
|
// Did we just go active?
|
||
|
bool bShouldBeActive = ShouldBeActive();
|
||
|
if ( !m_bWasActive && bShouldBeActive )
|
||
|
{
|
||
|
OnGoActive();
|
||
|
}
|
||
|
else if ( m_bWasActive && !bShouldBeActive )
|
||
|
{
|
||
|
OnGoInactive();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool C_BaseObject::OffsetObjectOrigin( Vector& origin )
|
||
|
{
|
||
|
if ( !m_bBuilding )
|
||
|
return false;
|
||
|
|
||
|
if ( inv_demo.GetBool() )
|
||
|
return false;
|
||
|
|
||
|
Vector vecWorldMins, vecWorldMaxs;
|
||
|
CollisionProp()->WorldSpaceAABB( &vecWorldMins, &vecWorldMaxs );
|
||
|
float flSize = vecWorldMaxs.z - vecWorldMins.z;
|
||
|
origin.z -= (flSize * (1 - m_flPercentageConstructed));
|
||
|
|
||
|
// If we're building, fake sliding the object out of the ground
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
const char* C_BaseObject::GetStatusName() const
|
||
|
{
|
||
|
return GetObjectInfo( GetType() )->m_pStatusName;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int C_BaseObject::DrawModel( int flags )
|
||
|
{
|
||
|
Vector vRealOrigin = GetLocalOrigin();
|
||
|
Vector vOrigin = vRealOrigin;
|
||
|
bool needOriginReset = OffsetObjectOrigin( vOrigin );
|
||
|
if ( needOriginReset )
|
||
|
{
|
||
|
SetLocalOrigin( vOrigin );
|
||
|
InvalidateBoneCache();
|
||
|
}
|
||
|
|
||
|
int drawn;
|
||
|
C_BaseTFPlayer *pLocal = C_BaseTFPlayer::GetLocalPlayer();
|
||
|
if ( pLocal && pLocal->IsUsingThermalVision() )
|
||
|
{
|
||
|
modelrender->ForcedMaterialOverride( m_ThermalMaterial );
|
||
|
drawn = BaseClass::DrawModel(flags);
|
||
|
modelrender->ForcedMaterialOverride( NULL );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Restore faked origin
|
||
|
if ( needOriginReset )
|
||
|
{
|
||
|
SetLocalOrigin( vRealOrigin );
|
||
|
}
|
||
|
|
||
|
// If we were drawn, draw building effects if we're building, or damage effects if we're damaged
|
||
|
if ( drawn && (m_flNextEffect < gpGlobals->curtime) )
|
||
|
{
|
||
|
// Haxory LOD
|
||
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
|
||
|
if ( (GetAbsOrigin() - pPlayer->GetAbsOrigin()).LengthSqr() < lod_effect_distance.GetFloat() )
|
||
|
{
|
||
|
if ( IsBuilding() )
|
||
|
{
|
||
|
DrawBuildEffects();
|
||
|
}
|
||
|
if ( !m_bPlacing && !m_bBuilding )
|
||
|
{
|
||
|
if ( !HasPowerup( POWERUP_EMP ) )
|
||
|
{
|
||
|
DrawRunningEffects();
|
||
|
}
|
||
|
|
||
|
if ( GetHealth() < GetMaxHealth() )
|
||
|
{
|
||
|
DrawDamageEffects();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HighlightBuildPoints( flags );
|
||
|
|
||
|
return drawn;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseObject::HighlightBuildPoints( int flags )
|
||
|
{
|
||
|
C_BaseTFPlayer *pLocal = C_BaseTFPlayer::GetLocalPlayer();
|
||
|
if ( !pLocal )
|
||
|
return;
|
||
|
|
||
|
if ( !GetNumBuildPoints() || !InLocalTeam() )
|
||
|
return;
|
||
|
|
||
|
C_WeaponBuilder *pBuilderWpn = dynamic_cast< C_WeaponBuilder * >( 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();
|
||
|
|
||
|
// 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 );
|
||
|
|
||
|
// HACK: Fixup angles on the HL2 model we're using
|
||
|
if ( !strcmp( modelinfo->GetModelName( pPlacementObj->GetModel() ), "models/items/HealthKit.mdl" ) )
|
||
|
{
|
||
|
vecBPAngles.x += 90;
|
||
|
}
|
||
|
|
||
|
// 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 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Exit points for mounted vehicles....
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseObject::GetExitPoint( CBaseEntity *pPlayer, int nBuildPoint, Vector *pAbsPosition, QAngle *pAbsAngles )
|
||
|
{
|
||
|
Assert(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Overridden to allow for brush-built map defined objects
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool C_BaseObject::IsIdentityBrush( void )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// 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: This is called to get the initial builder yaw...
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float C_BaseObject::GetInitialBuilderYaw()
|
||
|
{
|
||
|
return GetAbsAngles().y;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseObject::PostDataUpdate( DataUpdateType_t updateType )
|
||
|
{
|
||
|
BaseClass::PostDataUpdate( updateType );
|
||
|
|
||
|
bool bNewEntity = (updateType == DATA_UPDATE_CREATED);
|
||
|
if ( bNewEntity )
|
||
|
{
|
||
|
m_flAttackTime = -1000;
|
||
|
}
|
||
|
|
||
|
// Determine if we're under attack
|
||
|
if ( !bNewEntity )
|
||
|
{
|
||
|
if ( m_iHealth < m_iOldHealth )
|
||
|
{
|
||
|
// Deteriorating objects don't play sounds
|
||
|
if ( !IsDeteriorating() )
|
||
|
{
|
||
|
m_flAttackTime = gpGlobals->curtime;
|
||
|
}
|
||
|
}
|
||
|
else if ( m_iHealth > m_iOldHealth && m_iHealth == m_iMaxHealth )
|
||
|
{
|
||
|
// If we were just fully healed, remove all decals
|
||
|
RemoveAllDecals();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( m_bHasSapper )
|
||
|
{
|
||
|
// Play a specific sound for a sapper...
|
||
|
if ( m_bOldSapper != m_bHasSapper )
|
||
|
{
|
||
|
// Don't create these for dragonsteeth
|
||
|
if ( InLocalTeam() && GetType() != OBJ_DRAGONSTEETH )
|
||
|
{
|
||
|
// Play a sound.
|
||
|
CLocalPlayerFilter filter;
|
||
|
EmitSound( filter, SOUND_FROM_LOCAL_PLAYER, "BaseObject.SapperDestroyingTeamBuilding" );
|
||
|
|
||
|
MinimapCreateTempTrace( "minimap_under_attack", MINIMAP_PERSONAL_ORDERS, GetAbsOrigin() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Notify the hint system of the object being built.
|
||
|
if ( bNewEntity && GetOwner() && ( GetOwner() == C_BasePlayer::GetLocalPlayer() ) )
|
||
|
{
|
||
|
C_HintEvent_ObjectBuiltByLocalPlayer event( this );
|
||
|
GlobalHintEvent( &event );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseObject::Release( void )
|
||
|
{
|
||
|
// Remove any reticles on this entity
|
||
|
C_BaseTFPlayer *pPlayer = C_BaseTFPlayer::GetLocalPlayer();
|
||
|
if ( pPlayer )
|
||
|
{
|
||
|
pPlayer->Remove_Target( this );
|
||
|
}
|
||
|
|
||
|
BaseClass::Release();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool C_BaseObject::IsUnderAttack( )
|
||
|
{
|
||
|
// It's under attack for the 3 seconds after the last attack time
|
||
|
return (gpGlobals->curtime - m_flAttackTime) < 5.0f;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Ownership:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
C_BaseTFPlayer *C_BaseObject::GetOwner()
|
||
|
{
|
||
|
return m_hBuilder;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool C_BaseObject::IsOwnedByLocalPlayer() const
|
||
|
{
|
||
|
if ( !m_hBuilder )
|
||
|
return false;
|
||
|
|
||
|
return ( m_hBuilder == C_BaseTFPlayer::GetLocalPlayer() );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// 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_BaseTFPlayer *pPlayer = C_BaseTFPlayer::GetLocalPlayer();
|
||
|
pPlayer->SetSelectedObject( this );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// 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)
|
||
|
//-----------------------------------------------------------------------------
|
||
|
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_BaseTFPlayer::GetLocalPlayer() ) && 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 ( IsDeteriorating() )
|
||
|
{
|
||
|
Q_snprintf( szHealth, sizeof(szHealth), "\nBUILDER LOST, DETERIORATING... %.0f percent", ceil(((float)m_iHealth / (float)m_iMaxHealth) * 100) );
|
||
|
}
|
||
|
else 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 );
|
||
|
}
|
||
|
|
||
|
if ( m_bHasSapper )
|
||
|
{
|
||
|
Q_strncat( m_szIDString, "\nUse it to remove the attached enemy object", sizeof(m_szIDString), COPY_ALL_CHARACTERS );
|
||
|
}
|
||
|
|
||
|
// If it's deteriorating, and I can buy it, tell me
|
||
|
C_BaseTFPlayer *pLocalPlayer = C_BaseTFPlayer::GetLocalPlayer();
|
||
|
if ( IsDeteriorating() && pLocalPlayer && ClassCanBuild( pLocalPlayer->PlayerClass(), GetType() ) )
|
||
|
{
|
||
|
char szBuy[ MAX_ID_STRING ];
|
||
|
int iCost = CalculateObjectCost( GetType(), pLocalPlayer->GetNumObjects( GetType() ), pLocalPlayer->GetTeamNumber() );
|
||
|
Q_snprintf( szBuy, sizeof(szBuy), "\nBUY THIS OBJECT FOR %d RESOURCES", iCost );
|
||
|
Q_strncat( m_szIDString, szBuy, sizeof(m_szIDString), COPY_ALL_CHARACTERS );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Effects created when the object's running
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseObject::DrawRunningEffects( void )
|
||
|
{
|
||
|
if ( !GetMaxHealth() )
|
||
|
return;
|
||
|
|
||
|
// Get the overall damage percentage
|
||
|
float flDamaged = 1.0 - ((float)GetHealth() / (float)GetMaxHealth());
|
||
|
|
||
|
// Damage attachment points
|
||
|
int iSmokeAttachment, iSparkAttachment;
|
||
|
Vector vecSmoke, vecSpark, vecSmokeDir, dir;
|
||
|
QAngle angSmoke, angSpark;
|
||
|
|
||
|
// Look for damage points
|
||
|
iSmokeAttachment = LookupRandomAttachment( "r_smoke" );
|
||
|
|
||
|
// Get the points
|
||
|
if ( GetAttachment( iSmokeAttachment, vecSmoke, angSmoke ) )
|
||
|
{
|
||
|
AngleVectors( angSmoke, &vecSmokeDir);
|
||
|
|
||
|
float r, g, b;
|
||
|
r = g = b = random->RandomFloat( 16, 92 );
|
||
|
|
||
|
// Smoke
|
||
|
CSmartPtr<CObjectSmokeParticles> pSmokeEmitter = CObjectSmokeParticles::Create( "DrawRunningEffects 1" );
|
||
|
pSmokeEmitter->SetSortOrigin( vecSmoke );
|
||
|
ObjectSmokeParticle *pParticle = (ObjectSmokeParticle *) pSmokeEmitter->AddParticle( sizeof(ObjectSmokeParticle), g_Mat_DustPuff[1], vecSmoke );
|
||
|
if ( pParticle )
|
||
|
{
|
||
|
pParticle->m_flLifetime = 0.0f;
|
||
|
pParticle->m_flDieTime = random->RandomFloat( 2.0f, 3.0f );
|
||
|
pParticle->m_uchStartSize = random->RandomFloat( 2, 3 );
|
||
|
pParticle->m_uchEndSize = random->RandomFloat( 5, 10 );
|
||
|
dir[0] = vecSmokeDir[0] + random->RandomFloat( -0.1f, 0.1f );
|
||
|
dir[1] = vecSmokeDir[1] + random->RandomFloat( -0.1f, 0.1f );
|
||
|
dir[2] = vecSmokeDir[2] + random->RandomFloat( -0.1f, 0.1f );
|
||
|
pParticle->m_vecVelocity = dir * random->RandomFloat( 30.0f, 40.0f );
|
||
|
pParticle->m_uchStartAlpha = random->RandomFloat( 128,255 );
|
||
|
pParticle->m_uchEndAlpha = 0;
|
||
|
pParticle->m_flRoll = random->RandomFloat( 180, 360 );
|
||
|
pParticle->m_flRollDelta = random->RandomFloat( -1, 1 );
|
||
|
pParticle->m_uchColor[0] = r;
|
||
|
pParticle->m_uchColor[1] = g;
|
||
|
pParticle->m_uchColor[2] = b;
|
||
|
pParticle->m_vecAcceleration = Vector(0,0,10);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Sparks
|
||
|
for ( float flSparks = flDamaged - 0.3; flSparks > 0; flSparks -= 0.3 )
|
||
|
{
|
||
|
// Get random spark attachment point
|
||
|
iSparkAttachment = LookupRandomAttachment( "r_spark" );
|
||
|
if ( GetAttachment( iSparkAttachment, vecSpark, angSpark ) )
|
||
|
{
|
||
|
g_pEffects->Sparks( vecSpark );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_flNextEffect = gpGlobals->curtime + random->RandomFloat( 0.05, 0.1 );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Effects created while the object's building itself
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseObject::DrawBuildEffects( void )
|
||
|
{
|
||
|
m_flNextEffect = gpGlobals->curtime + 10;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Effects created when the object's damaged
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_BaseObject::DrawDamageEffects( void )
|
||
|
{
|
||
|
if ( !GetMaxHealth() )
|
||
|
return;
|
||
|
|
||
|
// Get the overall damage percentage
|
||
|
float flDamaged = 1.0 - ((float)GetHealth() / (float)GetMaxHealth());
|
||
|
|
||
|
// Damage attachment points
|
||
|
int iSmokeAttachment, iFireAttachment, iSparkAttachment;
|
||
|
Vector vecSmoke, vecFire, vecSpark, vecSmokeDir, dir;
|
||
|
QAngle angSmoke, angFire, angSpark;
|
||
|
|
||
|
// HACK: Calculate a random origin
|
||
|
// This can go away when we require all objects to have damage attachment points
|
||
|
Vector vecOrigin;
|
||
|
CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &vecOrigin );
|
||
|
|
||
|
// Look for damage points
|
||
|
iSmokeAttachment = LookupRandomAttachment( "d_smoke" );
|
||
|
iFireAttachment = LookupRandomAttachment( "d_fire" );
|
||
|
|
||
|
// Get the points, and if we can't find 'em, use the random origin
|
||
|
if ( GetAttachment( iSmokeAttachment, vecSmoke, angSmoke ) )
|
||
|
{
|
||
|
AngleVectors( angSmoke, &vecSmokeDir );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vecSmoke = vecOrigin;
|
||
|
vecSmokeDir = Vector(0,0,1);
|
||
|
}
|
||
|
if ( !GetAttachment( iFireAttachment, vecFire, angFire ) )
|
||
|
{
|
||
|
vecFire = vecOrigin;
|
||
|
angFire = QAngle(0,0,0);
|
||
|
}
|
||
|
|
||
|
float r, g, b;
|
||
|
r = g = b = random->RandomFloat( 16, 92 );
|
||
|
|
||
|
// Smoke
|
||
|
CSmartPtr<CSimpleEmitter> pSmokeEmitter = CSimpleEmitter::Create( "DrawDamageEffects 1" );
|
||
|
pSmokeEmitter->SetSortOrigin( vecSmoke );
|
||
|
SimpleParticle *pParticle = (SimpleParticle *) pSmokeEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_DustPuff[1], vecSmoke );
|
||
|
if ( pParticle )
|
||
|
{
|
||
|
pParticle->m_flLifetime = 0.0f;
|
||
|
pParticle->m_flDieTime = random->RandomFloat( 2.0f, 3.0f );
|
||
|
pParticle->m_uchStartSize = MAX( 1, 20 * flDamaged );
|
||
|
pParticle->m_uchEndSize = MAX( 10, 80 * flDamaged );
|
||
|
dir[0] = vecSmokeDir[0] + random->RandomFloat( -0.2f, 0.2f );
|
||
|
dir[1] = vecSmokeDir[1] + random->RandomFloat( -0.2f, 0.2f );
|
||
|
dir[2] = vecSmokeDir[2] + random->RandomFloat( -0.2f, 0.2f );
|
||
|
pParticle->m_vecVelocity = dir * random->RandomFloat( 60.0f, 80.0f );
|
||
|
pParticle->m_uchStartAlpha = 255;
|
||
|
pParticle->m_uchEndAlpha = 0;
|
||
|
pParticle->m_flRoll = random->RandomFloat( 180, 360 );
|
||
|
pParticle->m_flRollDelta = random->RandomFloat( -1, 1 );
|
||
|
pParticle->m_uchColor[0] = r;
|
||
|
pParticle->m_uchColor[1] = g;
|
||
|
pParticle->m_uchColor[2] = b;
|
||
|
}
|
||
|
|
||
|
// If we're really hurt, start burning
|
||
|
if ( flDamaged > 0.25 )
|
||
|
{
|
||
|
CSmartPtr<CObjectFireParticles> pFireEmitter = CObjectFireParticles::Create( "DrawDamageEffects 1" );
|
||
|
pFireEmitter->SetSortOrigin( vecFire );
|
||
|
PMaterialHandle hSphereMaterial = pFireEmitter->GetPMaterial( "sprites/floorflame" );
|
||
|
ObjectFireParticle *pParticle = (ObjectFireParticle *) pFireEmitter->AddParticle( sizeof(ObjectFireParticle), hSphereMaterial, vecFire );
|
||
|
if ( pParticle )
|
||
|
{
|
||
|
pParticle->m_hParent = this;
|
||
|
pParticle->m_iAttachmentPoint = iFireAttachment;
|
||
|
|
||
|
pParticle->m_flLifetime = 0.0f;
|
||
|
pParticle->m_flDieTime = 1.0;
|
||
|
pParticle->m_uchStartSize = MAX( 5, 30 * (flDamaged - 0.25) );
|
||
|
pParticle->m_uchEndSize = pParticle->m_uchStartSize;
|
||
|
pParticle->m_vecVelocity = Vector(0,0,1);
|
||
|
pParticle->m_uchStartAlpha = 255;
|
||
|
pParticle->m_uchEndAlpha = 255;
|
||
|
pParticle->m_flRoll = 0;
|
||
|
pParticle->m_flRollDelta = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Sparks
|
||
|
for ( float flSparks = flDamaged - 0.3; flSparks > 0; flSparks -= 0.3 )
|
||
|
{
|
||
|
// Get random spark attachment point
|
||
|
iSparkAttachment = LookupRandomAttachment( "d_spark" );
|
||
|
if ( !GetAttachment( iSparkAttachment, vecSpark, angSpark ) )
|
||
|
{
|
||
|
vecSpark = vecOrigin;
|
||
|
angSpark = QAngle(0,0,0);
|
||
|
}
|
||
|
|
||
|
g_pEffects->Sparks( vecSpark );
|
||
|
}
|
||
|
|
||
|
m_flNextEffect = gpGlobals->curtime + random->RandomFloat( 0.2, 0.5 );
|
||
|
}
|
||
|
|
||
|
//============================================================================================================
|
||
|
// 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;
|
||
|
|
||
|
int iPowered = pObject->IsPowered();
|
||
|
|
||
|
SetFloatResult( iPowered * m_Factor.GetFloat() );
|
||
|
}
|
||
|
|
||
|
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" )
|
||
|
{
|
||
|
}
|