Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

393 lines
12 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Client's CObjectSentrygun
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "c_baseobject.h"
#include "c_tf_player.h"
#include "vgui/ILocalize.h"
#include "c_obj_dispenser.h"
// NVNT haptics system interface
#include "c_tf_haptics.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
using namespace vgui;
//-----------------------------------------------------------------------------
// Purpose: RecvProxy that converts the Team's player UtlVector to entindexes
//-----------------------------------------------------------------------------
void RecvProxy_HealingList( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
C_ObjectDispenser *pDispenser = (C_ObjectDispenser*)pStruct;
CBaseHandle *pHandle = (CBaseHandle*)(&(pDispenser->m_hHealingTargets[pData->m_iElement]));
RecvProxy_IntToEHandle( pData, pStruct, pHandle );
// update the heal beams
pDispenser->m_bUpdateHealingTargets = true;
}
void RecvProxyArrayLength_HealingArray( void *pStruct, int objectID, int currentArrayLength )
{
C_ObjectDispenser *pDispenser = (C_ObjectDispenser*)pStruct;
if ( pDispenser->m_hHealingTargets.Size() != currentArrayLength )
pDispenser->m_hHealingTargets.SetSize( currentArrayLength );
// update the heal beams
pDispenser->m_bUpdateHealingTargets = true;
}
//-----------------------------------------------------------------------------
// Purpose: Dispenser object
//-----------------------------------------------------------------------------
IMPLEMENT_CLIENTCLASS_DT(C_ObjectDispenser, DT_ObjectDispenser, CObjectDispenser)
RecvPropInt( RECVINFO( m_iState ) ),
RecvPropInt( RECVINFO( m_iAmmoMetal ) ),
RecvPropInt( RECVINFO( m_iMiniBombCounter ) ),
RecvPropArray2(
RecvProxyArrayLength_HealingArray,
RecvPropInt( "healing_array_element", 0, SIZEOF_IGNORE, 0, RecvProxy_HealingList ),
MAX_PLAYERS,
0,
"healing_array"
)
END_RECV_TABLE()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
C_ObjectDispenser::C_ObjectDispenser()
{
m_bUpdateHealingTargets = false;
m_bPlayingSound = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
C_ObjectDispenser::~C_ObjectDispenser()
{
StopSound( "Building_Dispenser.Heal" );
// NVNT see if local player is in the list of targets
// temp. fix if dispener is destroyed will stop all healers.
if(m_bPlayingSound)
{
if(tfHaptics.healingDispenserCount>0) {
tfHaptics.healingDispenserCount --;
if(tfHaptics.healingDispenserCount==0 && !tfHaptics.wasBeingHealedMedic)
tfHaptics.isBeingHealed = false;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : updateType -
//-----------------------------------------------------------------------------
void C_ObjectDispenser::OnDataChanged( DataUpdateType_t updateType )
{
BaseClass::OnDataChanged( updateType );
#ifdef STAGING_ONLY
if ( updateType == DATA_UPDATE_CREATED )
{
SetNextClientThink( CLIENT_THINK_ALWAYS );
}
#endif // STAGING_ONLY
if ( m_bUpdateHealingTargets )
{
UpdateEffects();
m_bUpdateHealingTargets = false;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_ObjectDispenser::ClientThink()
{
BaseClass::ClientThink();
#ifdef STAGING_ONLY
C_TFPlayer *pTFOwner = GetOwner();
if ( pTFOwner && pTFOwner->m_Shared.IsEnteringOrExitingFullyInvisible() )
{
UpdateEffects();
}
#endif // STAGING_ONLY
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_ObjectDispenser::SetInvisibilityLevel( float flValue )
{
if ( IsEnteringOrExitingFullyInvisible( flValue ) )
{
UpdateEffects();
}
BaseClass::SetInvisibilityLevel( flValue );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_ObjectDispenser::UpdateEffects( void )
{
C_TFPlayer *pOwner = GetOwner();
if ( GetInvisibilityLevel() == 1.f || ( pOwner && pOwner->m_Shared.IsFullyInvisible() ) )
{
StopEffects( true );
return;
}
StopEffects();
// Now add any new targets
for ( int i = 0; i < m_hHealingTargets.Count(); i++ )
{
C_BaseEntity *pTarget = m_hHealingTargets[i].Get();
// Loops through the healing targets, and make sure we have an effect for each of them
if ( pTarget )
{
// don't want to show this effect for stealthed spies
C_TFPlayer *pPlayer = dynamic_cast< C_TFPlayer * >( pTarget );
if ( pPlayer && ( pPlayer->m_Shared.IsStealthed() || pPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) ) )
continue;
bool bHaveEffect = false;
for ( int targets = 0; targets < m_hHealingTargetEffects.Count(); targets++ )
{
if ( m_hHealingTargetEffects[targets].pTarget == pTarget )
{
bHaveEffect = true;
break;
}
}
if ( bHaveEffect )
continue;
// NVNT if the dispenser has started to heal the local player
// notify the haptics system
if(pTarget==C_BasePlayer::GetLocalPlayer())
{
tfHaptics.healingDispenserCount++;
if(!tfHaptics.wasBeingHealedMedic) {
tfHaptics.isBeingHealed = true;
}
}
const char *pszEffectName;
if ( GetTeamNumber() == TF_TEAM_RED )
{
pszEffectName = "dispenser_heal_red";
}
else
{
pszEffectName = "dispenser_heal_blue";
}
CNewParticleEffect *pEffect;
// if we don't have a model, attach at the origin, otherwise use attachment 'heal_origin'
if ( FBitSet( GetObjectFlags(), OF_DOESNT_HAVE_A_MODEL ) )
{
// offset the origin to player's chest
if ( FBitSet( GetObjectFlags(), OF_PLAYER_DESTRUCTION ) )
{
pEffect = ParticleProp()->Create( pszEffectName, PATTACH_ABSORIGIN_FOLLOW, NULL, Vector( 0, 0, 50 ) );
}
else
{
pEffect = ParticleProp()->Create( pszEffectName, PATTACH_ABSORIGIN_FOLLOW );
}
}
else
{
pEffect = ParticleProp()->Create( pszEffectName, PATTACH_POINT_FOLLOW, "heal_origin" );
}
ParticleProp()->AddControlPoint( pEffect, 1, pTarget, PATTACH_ABSORIGIN_FOLLOW, NULL, Vector(0,0,50) );
int iIndex = m_hHealingTargetEffects.AddToTail();
m_hHealingTargetEffects[iIndex].pTarget = pTarget;
m_hHealingTargetEffects[iIndex].pEffect = pEffect;
// Start the sound over again every time we start a new beam
StopSound( "Building_Dispenser.Heal" );
CLocalPlayerFilter filter;
EmitSound( filter, entindex(), "Building_Dispenser.Heal" );
m_bPlayingSound = true;
}
}
// Stop the sound if we're not healing anyone
if ( m_bPlayingSound && m_hHealingTargets.Count() == 0 )
{
m_bPlayingSound = false;
// stop the sound
StopSound( "Building_Dispenser.Heal" );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_ObjectDispenser::StopEffects( bool bRemoveAll /* = false */ )
{
// Find all the targets we've stopped healing
bool bStillHealing[MAX_DISPENSER_HEALING_TARGETS] = { 0 };
for ( int i = 0; i < m_hHealingTargetEffects.Count(); i++ )
{
bStillHealing[i] = false;
// Are we still healing this target?
if ( !bRemoveAll )
{
for ( int target = 0; target < m_hHealingTargets.Count(); target++ )
{
if ( m_hHealingTargets[target] && m_hHealingTargets[target] == m_hHealingTargetEffects[i].pTarget )
{
bStillHealing[i] = true;
break;
}
}
}
}
// Now remove all the dead effects
for ( int i = m_hHealingTargetEffects.Count()-1; i >= 0; i-- )
{
if ( !bStillHealing[i] )
{
// NVNT if the healing target of this dispenser is the local player.
// inform the haptics system interface we are no longer healing.
if(m_hHealingTargetEffects[i].pTarget==C_BasePlayer::GetLocalPlayer())
{
if(tfHaptics.healingDispenserCount>0) {
tfHaptics.healingDispenserCount --;
if(tfHaptics.healingDispenserCount==0 && !tfHaptics.wasBeingHealedMedic)
tfHaptics.isBeingHealed = false;
}
}
ParticleProp()->StopEmission( m_hHealingTargetEffects[i].pEffect );
m_hHealingTargetEffects.Remove(i);
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Damage level has changed, update our effects
//-----------------------------------------------------------------------------
void C_ObjectDispenser::UpdateDamageEffects( BuildingDamageLevel_t damageLevel )
{
if ( m_hDamageEffects )
{
m_hDamageEffects->StopEmission( false, false );
m_hDamageEffects = NULL;
}
const char *pszEffect = "";
switch( damageLevel )
{
case BUILDING_DAMAGE_LEVEL_LIGHT:
pszEffect = "dispenserdamage_1";
break;
case BUILDING_DAMAGE_LEVEL_MEDIUM:
pszEffect = "dispenserdamage_2";
break;
case BUILDING_DAMAGE_LEVEL_HEAVY:
pszEffect = "dispenserdamage_3";
break;
case BUILDING_DAMAGE_LEVEL_CRITICAL:
pszEffect = "dispenserdamage_4";
break;
default:
break;
}
if ( Q_strlen(pszEffect) > 0 )
{
m_hDamageEffects = ParticleProp()->Create( pszEffect, PATTACH_ABSORIGIN );
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int C_ObjectDispenser::GetMaxMetal( void )
{
return DISPENSER_MAX_METAL_AMMO;
}
//-----------------------------------------------------------------------------
// Control screen
//-----------------------------------------------------------------------------
DECLARE_VGUI_SCREEN_FACTORY( CDispenserControlPanel, "screen_obj_dispenser_blue" );
DECLARE_VGUI_SCREEN_FACTORY( CDispenserControlPanel_Red, "screen_obj_dispenser_red" );
//-----------------------------------------------------------------------------
// Constructor:
//-----------------------------------------------------------------------------
CDispenserControlPanel::CDispenserControlPanel( vgui::Panel *parent, const char *panelName )
: BaseClass( parent, "CDispenserControlPanel" )
{
m_pAmmoProgress = new RotatingProgressBar( this, "MeterArrow" );
}
//-----------------------------------------------------------------------------
// Deactivates buttons we can't afford
//-----------------------------------------------------------------------------
void CDispenserControlPanel::OnTickActive( C_BaseObject *pObj, C_TFPlayer *pLocalPlayer )
{
BaseClass::OnTickActive( pObj, pLocalPlayer );
Assert( dynamic_cast< C_ObjectDispenser* >( pObj ) );
m_hDispenser = static_cast< C_ObjectDispenser* >( pObj );
float flProgress = m_hDispenser ? m_hDispenser->GetMetalAmmoCount() / (float)m_hDispenser->GetMaxMetal() : 0.f;
m_pAmmoProgress->SetProgress( flProgress );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CDispenserControlPanel::IsVisible( void )
{
if ( m_hDispenser )
{
#ifdef STAGING_ONLY
if ( m_hDispenser->IsMiniBuilding() )
return false;
#endif // STAGING_ONLY
if ( m_hDispenser->GetInvisibilityLevel() == 1.f )
return false;
}
return BaseClass::IsVisible();
}
IMPLEMENT_CLIENTCLASS_DT(C_ObjectCartDispenser, DT_ObjectCartDispenser, CObjectCartDispenser)
END_RECV_TABLE()