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.
761 lines
20 KiB
761 lines
20 KiB
//========= 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"; |
|
} |
|
|
|
|
|
|