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.
921 lines
28 KiB
921 lines
28 KiB
//========= 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" ) |
|
{ |
|
}
|
|
|