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.
762 lines
20 KiB
762 lines
20 KiB
![]()
4 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose: Client's CObjectSentrygun
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
#include "cbase.h"
|
||
|
#include "c_tf_player.h"
|
||
|
#include "vgui_bitmapbutton.h"
|
||
|
#include "vgui/ILocalize.h"
|
||
|
#include "tf_fx_muzzleflash.h"
|
||
|
#include "eventlist.h"
|
||
|
#include "hintsystem.h"
|
||
|
#include <vgui_controls/ProgressBar.h>
|
||
|
#include "igameevents.h"
|
||
|
|
||
|
#include "c_obj_sentrygun.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
using namespace vgui;
|
||
|
|
||
|
static void RecvProxy_BooleanToShieldLevel( const CRecvProxyData *pData, void *pStruct, void *pOut )
|
||
|
{
|
||
|
// convert old boolean "m_bShielded" to uint32 "m_nShieldLevel"
|
||
|
*(uint32*)pOut = ( pData->m_Value.m_Int != 0 ) ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SentryRocket, DT_TFProjectile_SentryRocket )
|
||
|
|
||
|
BEGIN_NETWORK_TABLE( C_TFProjectile_SentryRocket, DT_TFProjectile_SentryRocket )
|
||
|
END_NETWORK_TABLE()
|
||
|
|
||
|
BEGIN_NETWORK_TABLE_NOBASE( C_ObjectSentrygun, DT_SentrygunLocalData )
|
||
|
RecvPropInt( RECVINFO(m_iKills) ),
|
||
|
RecvPropInt( RECVINFO(m_iAssists) ),
|
||
|
END_NETWORK_TABLE()
|
||
|
|
||
|
IMPLEMENT_CLIENTCLASS_DT(C_ObjectSentrygun, DT_ObjectSentrygun, CObjectSentrygun)
|
||
|
RecvPropInt( RECVINFO(m_iAmmoShells) ),
|
||
|
RecvPropInt( RECVINFO(m_iAmmoRockets) ),
|
||
|
RecvPropInt( RECVINFO(m_iState) ),
|
||
|
RecvPropBool( RECVINFO(m_bPlayerControlled) ),
|
||
|
RecvPropInt( RECVINFO(m_nShieldLevel) ),
|
||
|
RecvPropInt( RECVINFO_NAME(m_nShieldLevel, m_bShielded), 0, RecvProxy_BooleanToShieldLevel ), // for demo compatibility only
|
||
|
RecvPropEHandle( RECVINFO( m_hEnemy ) ),
|
||
|
RecvPropEHandle( RECVINFO( m_hAutoAimTarget ) ),
|
||
|
RecvPropDataTable( "SentrygunLocalData", 0, 0, &REFERENCE_RECV_TABLE( DT_SentrygunLocalData ) ),
|
||
|
END_RECV_TABLE()
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
C_ObjectSentrygun::C_ObjectSentrygun()
|
||
|
{
|
||
|
m_iMaxAmmoShells = SENTRYGUN_MAX_SHELLS_1;
|
||
|
m_bPlayerControlled = false;
|
||
|
m_bOldPlayerControlled = false;
|
||
|
m_nShieldLevel = SHIELD_NONE;
|
||
|
m_nOldShieldLevel = SHIELD_NONE;
|
||
|
m_hLaserBeamEffect = NULL;
|
||
|
m_pTempShield = NULL;
|
||
|
m_bNearMiss = false;
|
||
|
m_flNextNearMissCheck = 0.f;
|
||
|
|
||
|
m_iOldModelIndex = 0;
|
||
|
m_bOldCarried = false;
|
||
|
m_bRecreateShield = false;
|
||
|
m_bRecreateLaserBeam = false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::UpdateOnRemove( void )
|
||
|
{
|
||
|
DestroyLaserBeam();
|
||
|
DestroyShield();
|
||
|
DestroySiren();
|
||
|
|
||
|
BaseClass::UpdateOnRemove();
|
||
|
}
|
||
|
|
||
|
|
||
|
void C_ObjectSentrygun::GetAmmoCount( int &iShells, int &iMaxShells, int &iRockets, int & iMaxRockets )
|
||
|
{
|
||
|
iShells = m_iAmmoShells;
|
||
|
iMaxShells = m_iMaxAmmoShells;
|
||
|
iRockets = m_iAmmoRockets;
|
||
|
iMaxRockets = SENTRYGUN_MAX_ROCKETS;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::UpgradeLevelChanged()
|
||
|
{
|
||
|
switch( m_iUpgradeLevel )
|
||
|
{
|
||
|
case 1:
|
||
|
{
|
||
|
VectorCopy( SENTRYGUN_EYE_OFFSET_LEVEL_1, m_vecViewOffset );
|
||
|
m_iMaxAmmoShells = SENTRYGUN_MAX_SHELLS_1;
|
||
|
break;
|
||
|
}
|
||
|
case 2:
|
||
|
{
|
||
|
VectorCopy( SENTRYGUN_EYE_OFFSET_LEVEL_2, m_vecViewOffset );
|
||
|
m_iMaxAmmoShells = SENTRYGUN_MAX_SHELLS_2;
|
||
|
break;
|
||
|
}
|
||
|
case 3:
|
||
|
{
|
||
|
VectorCopy( SENTRYGUN_EYE_OFFSET_LEVEL_3, m_vecViewOffset );
|
||
|
m_iMaxAmmoShells = SENTRYGUN_MAX_SHELLS_3;
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
Assert( 0 );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CreateLaserBeam();
|
||
|
|
||
|
// Because the bounding box size changes when upgrading, force the shadow to be reprojected using the new bounds
|
||
|
g_pClientShadowMgr->AddToDirtyShadowList( this, true );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::OnPreDataChanged( DataUpdateType_t updateType )
|
||
|
{
|
||
|
BaseClass::OnPreDataChanged( updateType );
|
||
|
|
||
|
m_iOldBodygroups = GetBody();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::OnDataChanged( DataUpdateType_t updateType )
|
||
|
{
|
||
|
BaseClass::OnDataChanged( updateType );
|
||
|
|
||
|
// intercept bodygroup sets from the server
|
||
|
// we aren't clientsideanimating, but we don't want the server setting our
|
||
|
// bodygroup while we are placing
|
||
|
if ( m_iOldBodygroups != GetBody() )
|
||
|
{
|
||
|
if ( IsPlacing() )
|
||
|
{
|
||
|
m_nBody = m_iOldBodygroups;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( GetModelIndex() != m_iOldModelIndex )
|
||
|
{
|
||
|
m_iOldModelIndex = GetModelIndex();
|
||
|
|
||
|
if ( IsMiniBuilding() )
|
||
|
{
|
||
|
CStudioHdr *pStudiohdr = GetModelPtr();
|
||
|
int bodyGroup = FindBodygroupByName( "mini_sentry_light" );
|
||
|
if ( bodyGroup < pStudiohdr->numbodyparts() )
|
||
|
{
|
||
|
mstudiobodyparts_t *pbodypart = pStudiohdr->pBodypart( bodyGroup );
|
||
|
if ( pbodypart->base > 0 )
|
||
|
{
|
||
|
SetBodygroup( bodyGroup, 1 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( m_bPlayerControlled != m_bOldPlayerControlled || m_bRecreateLaserBeam )
|
||
|
{
|
||
|
if ( m_bPlayerControlled )
|
||
|
{
|
||
|
CreateLaserBeam();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DestroyLaserBeam();
|
||
|
}
|
||
|
m_bOldPlayerControlled = m_bPlayerControlled;
|
||
|
m_bRecreateLaserBeam = false;
|
||
|
}
|
||
|
|
||
|
if ( m_nShieldLevel != m_nOldShieldLevel || m_bRecreateShield )
|
||
|
{
|
||
|
if ( m_nShieldLevel > 0 )
|
||
|
{
|
||
|
CreateShield();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DestroyShield();
|
||
|
}
|
||
|
m_nOldShieldLevel = m_nShieldLevel;
|
||
|
m_bRecreateShield = false;
|
||
|
}
|
||
|
|
||
|
if ( IsCarried() != m_bOldCarried )
|
||
|
{
|
||
|
m_bOldCarried = IsCarried();
|
||
|
if ( IsCarried() )
|
||
|
{
|
||
|
DestroySiren();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ShouldBeActive() && !IsDisabled() && IsMiniBuilding() && !m_hSirenEffect )
|
||
|
{
|
||
|
CreateSiren();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::OnGoActive( void )
|
||
|
{
|
||
|
CreateSiren();
|
||
|
|
||
|
BaseClass::OnGoActive();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::OnGoInactive( void )
|
||
|
{
|
||
|
DestroySiren();
|
||
|
|
||
|
BaseClass::OnGoInactive();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::OnStartDisabled( void )
|
||
|
{
|
||
|
DestroySiren();
|
||
|
|
||
|
BaseClass::OnStartDisabled();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::OnEndDisabled( void )
|
||
|
{
|
||
|
CreateSiren();
|
||
|
|
||
|
BaseClass::OnEndDisabled();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::CreateLaserBeam( void )
|
||
|
{
|
||
|
if ( !m_bPlayerControlled )
|
||
|
return;
|
||
|
|
||
|
DestroyLaserBeam();
|
||
|
|
||
|
int iAttachment = LookupAttachment( "laser_origin" );
|
||
|
m_hLaserBeamEffect = ParticleProp()->Create( "laser_sight_beam", PATTACH_POINT_FOLLOW, iAttachment );
|
||
|
if ( m_hLaserBeamEffect )
|
||
|
{
|
||
|
m_hLaserBeamEffect->SetSortOrigin( m_hLaserBeamEffect->GetRenderOrigin() );
|
||
|
}
|
||
|
|
||
|
SetNextClientThink( CLIENT_THINK_ALWAYS );
|
||
|
|
||
|
if ( m_hLaserBeamEffect )
|
||
|
{
|
||
|
if ( GetTeamNumber() == TF_TEAM_BLUE )
|
||
|
{
|
||
|
m_hLaserBeamEffect->SetControlPoint( 2, Vector( 0, 0, 255 ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_hLaserBeamEffect->SetControlPoint( 2, Vector( 255, 0, 0 ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::DestroyLaserBeam( void )
|
||
|
{
|
||
|
if ( m_hLaserBeamEffect )
|
||
|
{
|
||
|
SetNextClientThink( CLIENT_THINK_NEVER );
|
||
|
ParticleProp()->StopEmissionAndDestroyImmediately( m_hLaserBeamEffect );
|
||
|
m_hLaserBeamEffect = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::SetDormant( bool bDormant )
|
||
|
{
|
||
|
if ( IsDormant() && !bDormant )
|
||
|
{
|
||
|
// Make sure our shield is where we are. We may have moved since last seen.
|
||
|
if ( m_pTempShield )
|
||
|
{
|
||
|
m_bRecreateShield = true;
|
||
|
m_bRecreateLaserBeam = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BaseClass::SetDormant( bDormant );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::CreateShield( void )
|
||
|
{
|
||
|
DestroyShield();
|
||
|
|
||
|
model_t *pModel = (model_t *) engine->LoadModel( "models/buildables/sentry_shield.mdl" );
|
||
|
m_pTempShield = tempents->SpawnTempModel( pModel, GetAbsOrigin(), GetAbsAngles(), Vector(0, 0, 0), 1, FTENT_NEVERDIE );
|
||
|
if ( m_pTempShield )
|
||
|
{
|
||
|
m_pTempShield->ChangeTeam( GetTeamNumber() );
|
||
|
m_pTempShield->m_nSkin = ( GetTeamNumber() == TF_TEAM_RED ) ? 0 : 1;
|
||
|
//m_pTempShield->m_nRenderFX = kRenderFxDistort;
|
||
|
}
|
||
|
|
||
|
m_hShieldEffect = ParticleProp()->Create( "turret_shield", PATTACH_ABSORIGIN_FOLLOW, 0, Vector( 0,0,30) );
|
||
|
if ( !m_hShieldEffect )
|
||
|
return;
|
||
|
if ( GetTeamNumber() == TF_TEAM_BLUE )
|
||
|
{
|
||
|
m_hShieldEffect->SetControlPoint( 1, Vector(50,150,255) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_hShieldEffect->SetControlPoint( 1, Vector(255,50,50) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::DestroyShield( void )
|
||
|
{
|
||
|
if ( m_pTempShield )
|
||
|
{
|
||
|
m_pTempShield->flags = FTENT_FADEOUT;
|
||
|
m_pTempShield->die = gpGlobals->curtime;
|
||
|
m_pTempShield->fadeSpeed = 1.0f;
|
||
|
m_pTempShield = NULL;
|
||
|
}
|
||
|
|
||
|
if ( m_hShieldEffect )
|
||
|
{
|
||
|
ParticleProp()->StopEmission( m_hShieldEffect );
|
||
|
m_hShieldEffect = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::CreateSiren( void )
|
||
|
{
|
||
|
if ( !IsMiniBuilding() )
|
||
|
return;
|
||
|
|
||
|
if ( IsCarried() )
|
||
|
return;
|
||
|
|
||
|
if ( m_hSirenEffect )
|
||
|
return;
|
||
|
|
||
|
const char* flashlightName = "cart_flashinglight";
|
||
|
if ( GetTeamNumber() == TF_TEAM_RED )
|
||
|
{
|
||
|
flashlightName = "cart_flashinglight_red";
|
||
|
}
|
||
|
m_hSirenEffect = ParticleProp()->Create( flashlightName, PATTACH_POINT_FOLLOW, "siren" );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::DestroySiren( void )
|
||
|
{
|
||
|
if ( m_hSirenEffect )
|
||
|
{
|
||
|
ParticleProp()->StopEmission( m_hSirenEffect );
|
||
|
m_hSirenEffect = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::ClientThink( void )
|
||
|
{
|
||
|
if ( m_hLaserBeamEffect && m_hEnemy && GetBuilder() )
|
||
|
{
|
||
|
QAngle vecAngles;
|
||
|
Vector vecMuzzleOrigin;
|
||
|
int iAttachment = 0;
|
||
|
switch ( GetUpgradeLevel() )
|
||
|
{
|
||
|
case 1:
|
||
|
iAttachment = LookupAttachment( "muzzle" );
|
||
|
break;
|
||
|
case 2:
|
||
|
iAttachment = LookupAttachment( "muzzle_l" );
|
||
|
break;
|
||
|
case 3:
|
||
|
iAttachment = LookupAttachment( "rocket_l" );
|
||
|
break;
|
||
|
}
|
||
|
GetAttachment( iAttachment, vecMuzzleOrigin, vecAngles );
|
||
|
|
||
|
Vector vForward;
|
||
|
AngleVectors( vecAngles, &vForward );
|
||
|
|
||
|
Vector vEnd = m_hEnemy->WorldSpaceCenter();
|
||
|
if ( m_hAutoAimTarget )
|
||
|
{
|
||
|
vEnd = m_hAutoAimTarget->GetAbsOrigin() + m_hAutoAimTarget->GetClassEyeHeight()*0.75f;
|
||
|
}
|
||
|
|
||
|
trace_t trace;
|
||
|
CTraceFilterIgnoreTeammatesAndTeamObjects filter( GetBuilder(), COLLISION_GROUP_NONE, GetBuilder()->GetTeamNumber() );
|
||
|
UTIL_TraceLine( vecMuzzleOrigin, vEnd, MASK_SOLID, &filter, &trace );
|
||
|
|
||
|
Vector vecInterpBeamPos;
|
||
|
InterpolateVector( gpGlobals->frametime * 25.f, m_vecLaserBeamPos, trace.endpos, vecInterpBeamPos );
|
||
|
|
||
|
m_hLaserBeamEffect->SetControlPoint( 1, vecInterpBeamPos );
|
||
|
m_vecLaserBeamPos = vecInterpBeamPos;
|
||
|
|
||
|
// Perform a near-miss check.
|
||
|
// This works pretty well as a threat indicator for the arrow, let's try it for our laser.
|
||
|
if ( gpGlobals->curtime > m_flNextNearMissCheck )
|
||
|
{
|
||
|
// CheckNearMiss( vecMuzzleOrigin, m_hEnemy->GetAbsOrigin() );
|
||
|
m_flNextNearMissCheck = gpGlobals->curtime + 0.2f;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::CheckNearMiss( Vector vecStart, Vector vecEnd )
|
||
|
{
|
||
|
// Check against the local player. If the laser sweeps near him, play the near miss sound...
|
||
|
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
|
||
|
if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
|
||
|
return;
|
||
|
|
||
|
// Can't hear near miss sounds from friendly guns.
|
||
|
// if ( pLocalPlayer->GetTeamNumber() == GetTeamNumber() )
|
||
|
// return;
|
||
|
|
||
|
Vector vecPlayerPos = pLocalPlayer->GetAbsOrigin();
|
||
|
Vector vecClosestPoint;
|
||
|
float dist;
|
||
|
CalcClosestPointOnLineSegment( vecPlayerPos, vecStart, vecEnd, vecClosestPoint, &dist );
|
||
|
dist = vecPlayerPos.DistTo( vecClosestPoint );
|
||
|
if ( dist > 120 )
|
||
|
{
|
||
|
StopSound( "Building_Sentrygun.ShaftLaserPass" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !m_bNearMiss )
|
||
|
{
|
||
|
// We're good for a near miss!
|
||
|
float soundlen = 0;
|
||
|
EmitSound_t params;
|
||
|
params.m_flSoundTime = 0;
|
||
|
params.m_pSoundName = "Building_Sentrygun.ShaftLaserPass";
|
||
|
params.m_pflSoundDuration = &soundlen;
|
||
|
params.m_flVolume = 1.f - (dist / 120.f);
|
||
|
CSingleUserRecipientFilter localFilter( pLocalPlayer );
|
||
|
EmitSound( localFilter, pLocalPlayer->entindex(), params );
|
||
|
|
||
|
m_bNearMiss = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::DisplayHintTo( C_BasePlayer *pPlayer )
|
||
|
{
|
||
|
bool bHintPlayed = false;
|
||
|
|
||
|
C_TFPlayer *pTFPlayer = ToTFPlayer(pPlayer);
|
||
|
if ( InSameTeam( pPlayer ) )
|
||
|
{
|
||
|
// We're looking at a friendly object.
|
||
|
if ( pTFPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) )
|
||
|
{
|
||
|
// If the sentrygun can be upgraded, and I can afford it, let me know
|
||
|
if ( GetHealth() == GetMaxHealth() && GetUpgradeLevel() < 3 )
|
||
|
{
|
||
|
if ( pTFPlayer->GetBuildResources() >= SENTRYGUN_UPGRADE_COST )
|
||
|
{
|
||
|
bHintPlayed = pTFPlayer->HintMessage( HINT_ENGINEER_UPGRADE_SENTRYGUN, false, true );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bHintPlayed = pTFPlayer->HintMessage( HINT_ENGINEER_METAL_TO_UPGRADE, false, true );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !bHintPlayed )
|
||
|
{
|
||
|
BaseClass::DisplayHintTo( pPlayer );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *C_ObjectSentrygun::GetHudStatusIcon( void )
|
||
|
{
|
||
|
const char *pszResult;
|
||
|
|
||
|
switch( m_iUpgradeLevel )
|
||
|
{
|
||
|
case 1:
|
||
|
default:
|
||
|
pszResult = "obj_status_sentrygun_1";
|
||
|
break;
|
||
|
case 2:
|
||
|
pszResult = "obj_status_sentrygun_2";
|
||
|
break;
|
||
|
case 3:
|
||
|
pszResult = "obj_status_sentrygun_3";
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return pszResult;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
BuildingHudAlert_t C_ObjectSentrygun::GetBuildingAlertLevel( void )
|
||
|
{
|
||
|
BuildingHudAlert_t baseAlertLevel = BaseClass::GetBuildingAlertLevel();
|
||
|
|
||
|
// Just warn on low shells.
|
||
|
|
||
|
float flShellPercent = (float)m_iAmmoShells / (float)m_iMaxAmmoShells;
|
||
|
|
||
|
BuildingHudAlert_t alertLevel = BUILDING_HUD_ALERT_NONE;
|
||
|
|
||
|
if ( !IsCarried() )
|
||
|
{
|
||
|
if ( !IsBuilding() && flShellPercent < 0.25 )
|
||
|
{
|
||
|
alertLevel = BUILDING_HUD_ALERT_VERY_LOW_AMMO;
|
||
|
}
|
||
|
else if ( !IsBuilding() && flShellPercent < 0.50 )
|
||
|
{
|
||
|
alertLevel = BUILDING_HUD_ALERT_LOW_AMMO;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return MAX( baseAlertLevel, alertLevel );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: During placement, only use the smaller bbox for shadow calc, don't include the range bodygroup
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType )
|
||
|
{
|
||
|
if ( IsPlacing() )
|
||
|
{
|
||
|
mins = CollisionProp()->OBBMins();
|
||
|
maxs = CollisionProp()->OBBMaxs();
|
||
|
|
||
|
// HACK: The collision prop bounding box doesn't quite cover the blueprint model, so we bloat it a little
|
||
|
Vector bbBloat( 10.0f, 10.0f, 0.0f );
|
||
|
mins -= bbBloat;
|
||
|
maxs += bbBloat;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BaseClass::GetShadowRenderBounds( mins, maxs, shadowType );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Re-calc our damage particles when we get a new model
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CStudioHdr *C_ObjectSentrygun::OnNewModel( void )
|
||
|
{
|
||
|
CStudioHdr *hdr = BaseClass::OnNewModel();
|
||
|
|
||
|
UpdateDamageEffects( m_damageLevel );
|
||
|
|
||
|
// Reset Bodygroups
|
||
|
for ( int i = GetNumBodyGroups()-1; i >= 0; i-- )
|
||
|
{
|
||
|
SetBodygroup( i, 0 );
|
||
|
}
|
||
|
|
||
|
m_iPlacementBodygroup = FindBodygroupByName( "sentry1_range" );
|
||
|
m_iPlacementBodygroup_Mini = FindBodygroupByName( "sentry1_range_mini" );
|
||
|
|
||
|
return hdr;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Damage level has changed, update our effects
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::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 = "sentrydamage_1";
|
||
|
break;
|
||
|
case BUILDING_DAMAGE_LEVEL_MEDIUM:
|
||
|
pszEffect = "sentrydamage_2";
|
||
|
break;
|
||
|
case BUILDING_DAMAGE_LEVEL_HEAVY:
|
||
|
pszEffect = "sentrydamage_3";
|
||
|
break;
|
||
|
case BUILDING_DAMAGE_LEVEL_CRITICAL:
|
||
|
pszEffect = "sentrydamage_4";
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( Q_strlen(pszEffect) > 0 )
|
||
|
{
|
||
|
switch( m_iUpgradeLevel )
|
||
|
{
|
||
|
case 1:
|
||
|
case 2:
|
||
|
m_hDamageEffects = ParticleProp()->Create( pszEffect, PATTACH_POINT_FOLLOW, "build_point_0" );
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
m_hDamageEffects = ParticleProp()->Create( pszEffect, PATTACH_POINT_FOLLOW, "sentrydamage" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: placement state has changed, update the model
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::OnPlacementStateChanged( bool bValidPlacement )
|
||
|
{
|
||
|
if ( bValidPlacement && ( m_iPlacementBodygroup >= 0 ) && ( m_iPlacementBodygroup_Mini >= 0 ) )
|
||
|
{
|
||
|
if ( IsMiniBuilding() )
|
||
|
{
|
||
|
SetBodygroup( m_iPlacementBodygroup, 0 );
|
||
|
SetBodygroup( m_iPlacementBodygroup_Mini, 1 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetBodygroup( m_iPlacementBodygroup, 1 );
|
||
|
SetBodygroup( m_iPlacementBodygroup_Mini, 0 );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetBodygroup( m_iPlacementBodygroup, 0 );
|
||
|
SetBodygroup( m_iPlacementBodygroup_Mini, 0 );
|
||
|
}
|
||
|
|
||
|
BaseClass::OnPlacementStateChanged( bValidPlacement );
|
||
|
}
|
||
|
|
||
|
void C_ObjectSentrygun::DebugDamageParticles( void )
|
||
|
{
|
||
|
Msg( "Health %d\n", GetHealth() );
|
||
|
|
||
|
BuildingDamageLevel_t damageLevel = CalculateDamageLevel();
|
||
|
Msg( "Damage Level %d\n", (int)damageLevel );
|
||
|
|
||
|
if ( m_hDamageEffects )
|
||
|
{
|
||
|
Msg( "m_hDamageEffects is valid\n" );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Msg( "m_hDamageEffects is NULL\n" );
|
||
|
}
|
||
|
|
||
|
// print all particles owned by particleprop
|
||
|
ParticleProp()->DebugPrintEffects();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
|
||
|
{
|
||
|
BaseClass::BuildTransformations( hdr, pos, q, cameraTransform, boneMask, boneComputed );
|
||
|
|
||
|
if ( !IsMiniBuilding() )
|
||
|
return;
|
||
|
|
||
|
if ( IsBuilding() || IsPlacing() )
|
||
|
return;
|
||
|
|
||
|
|
||
|
//Vector position;
|
||
|
//for ( int i=0; i<8; ++i )
|
||
|
//{
|
||
|
// matrix3x4_t &transform = GetBoneForWrite( i );
|
||
|
// MatrixGetColumn( transform, 3, position );
|
||
|
// MatrixSetColumn( Vector(0,0,-4) + position, 3, transform );
|
||
|
//}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char* C_ObjectSentrygun::GetStatusName() const
|
||
|
{
|
||
|
if ( IsDisposableBuilding() )
|
||
|
{
|
||
|
return "#TF_Object_Sentry_Disp";
|
||
|
}
|
||
|
|
||
|
return "#TF_Object_Sentry";
|
||
|
}
|
||
|
|
||
|
|