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.
559 lines
15 KiB
559 lines
15 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose: Client's CObjectSentrygun
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
#include "cbase.h"
|
||
|
#include "bone_setup.h"
|
||
|
#include "CommanderOverlay.h"
|
||
|
#include "c_baseobject.h"
|
||
|
#include "C_Obj_SentryGun.h"
|
||
|
#include "tf_shareddefs.h"
|
||
|
#include "c_basetfplayer.h"
|
||
|
#include "ObjectControlPanel.h"
|
||
|
#include <vgui_controls/Button.h>
|
||
|
|
||
|
inline float UTIL_AngleMod(float a)
|
||
|
{
|
||
|
return anglemod(a);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Base Sentrygun
|
||
|
//-----------------------------------------------------------------------------
|
||
|
BEGIN_RECV_TABLE_NOBASE(C_ObjectSentrygun, DT_SentrygunTeamOnlyVars)
|
||
|
RecvPropInt(RECVINFO( m_iAmmo )),
|
||
|
END_RECV_TABLE()
|
||
|
|
||
|
IMPLEMENT_CLIENTCLASS_DT(C_ObjectSentrygun, DT_ObjectSentrygun, CObjectSentrygun)
|
||
|
RecvPropInt( RECVINFO( m_iBaseTurnRate ) ),
|
||
|
RecvPropEHandle(RECVINFO(m_hEnemy)),
|
||
|
RecvPropDataTable( "teamonly", 0, 0, &REFERENCE_RECV_TABLE( DT_SentrygunTeamOnlyVars )),
|
||
|
RecvPropInt(RECVINFO(m_bTurtled)),
|
||
|
RecvPropInt( RECVINFO( m_nAnimationParity ) ),
|
||
|
RecvPropInt( RECVINFO( m_nOrientationParity ) ),
|
||
|
END_RECV_TABLE()
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
C_ObjectSentrygun::C_ObjectSentrygun()
|
||
|
{
|
||
|
m_fBoneXRotator = 0;
|
||
|
m_fBoneYRotator = 0;
|
||
|
m_iAmmo = 0;
|
||
|
m_bTurtled = false;
|
||
|
m_flStartedTurtlingAt = 0;
|
||
|
m_flStartedUnTurtlingAt = 0;
|
||
|
|
||
|
SetViewOffset( Vector(0,0,22) );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::SetDormant( bool bDormant )
|
||
|
{
|
||
|
BaseClass::SetDormant( bDormant );
|
||
|
ENTITY_PANEL_ACTIVATE( "sentrygun", !bDormant );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int C_ObjectSentrygun::DrawModel( int flags )
|
||
|
{
|
||
|
float flRealOriginZ = GetLocalOrigin().z;
|
||
|
|
||
|
// If we're turtling, slide the model into the ground
|
||
|
if ( m_bTurtled )
|
||
|
{
|
||
|
// How far down are we?
|
||
|
float flTime = MIN( gpGlobals->curtime - m_flStartedTurtlingAt, SENTRY_TURTLE_TIME );
|
||
|
float flPercent = 1 - (SENTRY_TURTLE_TIME - flTime) / SENTRY_TURTLE_TIME;
|
||
|
|
||
|
// FIXME: This is totally wrong!!!
|
||
|
Vector vNewOrigin = GetLocalOrigin();
|
||
|
vNewOrigin.z -= (CollisionProp()->OBBSize().z * flPercent);
|
||
|
SetLocalOrigin( vNewOrigin );
|
||
|
InvalidateBoneCache();
|
||
|
}
|
||
|
else if ( !m_bTurtled )
|
||
|
{
|
||
|
if ( m_flStartedUnTurtlingAt )
|
||
|
{
|
||
|
float flTime = MIN( gpGlobals->curtime - m_flStartedUnTurtlingAt, SENTRY_TURTLE_TIME );
|
||
|
float flPercent = (SENTRY_TURTLE_TIME - flTime) / SENTRY_TURTLE_TIME;
|
||
|
|
||
|
// FIXME: This is totally wrong!!!
|
||
|
Vector vNewOrigin = GetLocalOrigin();
|
||
|
vNewOrigin.z -= (CollisionProp()->OBBSize().z * flPercent);
|
||
|
SetLocalOrigin( vNewOrigin );
|
||
|
InvalidateBoneCache();
|
||
|
|
||
|
// Fully unturtled?
|
||
|
if ( flTime >= SENTRY_TURTLE_TIME )
|
||
|
{
|
||
|
m_flStartedUnTurtlingAt = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int drawn = BaseClass::DrawModel( flags );
|
||
|
SetLocalOriginDim( Z_INDEX, flRealOriginZ );
|
||
|
|
||
|
return drawn;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::PreDataUpdate( DataUpdateType_t updateType )
|
||
|
{
|
||
|
BaseClass::PreDataUpdate( updateType );
|
||
|
|
||
|
m_bLastTurtled = m_bTurtled;
|
||
|
m_nLastAnimationParity = m_nAnimationParity;
|
||
|
m_angPrevLocalAngles = GetLocalAngles();
|
||
|
m_nPrevOrientationParity = m_nOrientationParity;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::PostDataUpdate( DataUpdateType_t updateType )
|
||
|
{
|
||
|
BaseClass::PostDataUpdate( updateType );
|
||
|
|
||
|
if ( m_bLastTurtled != m_bTurtled )
|
||
|
{
|
||
|
if ( m_bTurtled )
|
||
|
{
|
||
|
m_flStartedTurtlingAt = gpGlobals->curtime;
|
||
|
m_flStartedUnTurtlingAt = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_flStartedUnTurtlingAt = gpGlobals->curtime;
|
||
|
m_flStartedTurtlingAt = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( m_nLastAnimationParity != m_nAnimationParity )
|
||
|
{
|
||
|
SetCycle( 0.0f );
|
||
|
}
|
||
|
|
||
|
bool changed = false;
|
||
|
QAngle angleDiff;
|
||
|
angleDiff = ( GetAbsAngles() - m_angPrevLocalAngles );
|
||
|
for (int i = 0;i < 3; i++ )
|
||
|
{
|
||
|
angleDiff[i] = UTIL_AngleMod( angleDiff[ i ] );
|
||
|
}
|
||
|
|
||
|
if ( angleDiff.Length() > 0.1f )
|
||
|
{
|
||
|
changed = true;
|
||
|
}
|
||
|
if ( updateType == DATA_UPDATE_CREATED || changed )
|
||
|
{
|
||
|
// Orient it
|
||
|
m_vecCurAngles.y = UTIL_AngleMod( GetLocalAngles().y );
|
||
|
RecomputeOrientation();
|
||
|
}
|
||
|
else if ( m_nPrevOrientationParity != m_nOrientationParity )
|
||
|
{
|
||
|
if ( changed )
|
||
|
{
|
||
|
RecomputeOrientation();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::OnDataChanged( DataUpdateType_t updateType )
|
||
|
{
|
||
|
BaseClass::OnDataChanged( updateType );
|
||
|
|
||
|
if ( updateType == DATA_UPDATE_CREATED )
|
||
|
{
|
||
|
// Start thinking (Baseclass stops it)
|
||
|
SetNextClientThink( CLIENT_THINK_ALWAYS );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::FinishedBuilding( void )
|
||
|
{
|
||
|
BaseClass::FinishedBuilding();
|
||
|
|
||
|
EmitSound( "ObjectSentrygun.Activate" );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::GetBoneControllers(float controllers[MAXSTUDIOBONECTRLS])
|
||
|
{
|
||
|
studiohdr_t *pModel = modelinfo->GetStudiomodel( GetModel() );
|
||
|
|
||
|
// When yaw preview is on,
|
||
|
if (!IsPreviewingYaw())
|
||
|
{
|
||
|
Studio_SetController(pModel, 0, m_fBoneXRotator, controllers[0]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Bone rotation == 0 here to make it exactly match the preview
|
||
|
Studio_SetController(pModel, 0, 0, controllers[0]);
|
||
|
}
|
||
|
|
||
|
Studio_SetController(pModel, 1, m_fBoneYRotator, controllers[1]);
|
||
|
Studio_SetController(pModel, 2, m_fBoneYRotator, controllers[2]);
|
||
|
Studio_SetController(pModel, 3, m_fBoneYRotator, controllers[3]);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: This is called to get the initial builder yaw...
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float C_ObjectSentrygun::GetInitialBuilderYaw()
|
||
|
{
|
||
|
// Take the current rotation into account
|
||
|
return GetAbsAngles().y + m_fBoneXRotator;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Called when a rotation happens
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_ObjectSentrygun::RecomputeOrientation( )
|
||
|
{
|
||
|
m_iRightBound = UTIL_AngleMod( m_vecCurAngles.y - 50);
|
||
|
m_iLeftBound = UTIL_AngleMod( m_vecCurAngles.y + 50);
|
||
|
if ( m_iRightBound > m_iLeftBound )
|
||
|
{
|
||
|
m_iRightBound = m_iLeftBound;
|
||
|
m_iLeftBound = UTIL_AngleMod( m_vecCurAngles.y - 50);
|
||
|
}
|
||
|
|
||
|
// Start it rotating
|
||
|
m_vecGoalAngles.y = m_iRightBound;;
|
||
|
m_vecGoalAngles.x = m_vecCurAngles.x = 0;
|
||
|
|
||
|
m_fBoneXRotator = 0.0f;
|
||
|
m_fBoneYRotator = 0.0f;
|
||
|
|
||
|
m_bTurningRight = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Handle movement of the turret
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool C_ObjectSentrygun::MoveTurret(void)
|
||
|
{
|
||
|
bool bMoved = 0;
|
||
|
|
||
|
float turnrate = (float)(m_iBaseTurnRate) * 10.0f;
|
||
|
turnrate *= gpGlobals->frametime;
|
||
|
|
||
|
// any x movement?
|
||
|
if ( m_vecCurAngles.x != m_vecGoalAngles.x )
|
||
|
{
|
||
|
float flDir = m_vecGoalAngles.x > m_vecCurAngles.x ? 1 : -1 ;
|
||
|
|
||
|
m_vecCurAngles.x += 0.1 * (turnrate * 5) * flDir;
|
||
|
|
||
|
// if we started below the goal, and now we're past, peg to goal
|
||
|
if (flDir == 1)
|
||
|
{
|
||
|
if (m_vecCurAngles.x > m_vecGoalAngles.x)
|
||
|
m_vecCurAngles.x = m_vecGoalAngles.x;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (m_vecCurAngles.x < m_vecGoalAngles.x)
|
||
|
m_vecCurAngles.x = m_vecGoalAngles.x;
|
||
|
}
|
||
|
|
||
|
m_fBoneYRotator = m_vecCurAngles.x;
|
||
|
|
||
|
bMoved = 1;
|
||
|
}
|
||
|
|
||
|
if ( m_vecCurAngles.y != m_vecGoalAngles.y )
|
||
|
{
|
||
|
float flDir = m_vecGoalAngles.y > m_vecCurAngles.y ? 1 : -1 ;
|
||
|
float flDist = fabs(m_vecGoalAngles.y - m_vecCurAngles.y);
|
||
|
bool bReversed = false;
|
||
|
|
||
|
if (flDist > 180)
|
||
|
{
|
||
|
flDist = 360 - flDist;
|
||
|
flDir = -flDir;
|
||
|
bReversed = true;
|
||
|
}
|
||
|
|
||
|
if (m_hEnemy == NULL )
|
||
|
{
|
||
|
if (flDist > 30)
|
||
|
{
|
||
|
if (m_fTurnRate < turnrate * 20)
|
||
|
{
|
||
|
m_fTurnRate += turnrate;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Slow down
|
||
|
if ( m_fTurnRate > (turnrate * 5) )
|
||
|
m_fTurnRate -= turnrate;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// When tracking enemies, move faster and don't slow
|
||
|
if (flDist > 30)
|
||
|
{
|
||
|
if (m_fTurnRate < turnrate * 30)
|
||
|
{
|
||
|
m_fTurnRate += turnrate * 3;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_vecCurAngles.y += 0.1 * m_fTurnRate * flDir;
|
||
|
|
||
|
// if we passed over the goal, peg right to it now
|
||
|
if (flDir == -1)
|
||
|
{
|
||
|
if ( (bReversed == false && m_vecGoalAngles.y > m_vecCurAngles.y) || (bReversed == true && m_vecGoalAngles.y < m_vecCurAngles.y) )
|
||
|
m_vecCurAngles.y = m_vecGoalAngles.y;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( (bReversed == false && m_vecGoalAngles.y < m_vecCurAngles.y) || (bReversed == true && m_vecGoalAngles.y > m_vecCurAngles.y) )
|
||
|
m_vecCurAngles.y = m_vecGoalAngles.y;
|
||
|
}
|
||
|
|
||
|
if (m_vecCurAngles.y < 0)
|
||
|
m_vecCurAngles.y += 360;
|
||
|
else if (m_vecCurAngles.y >= 360)
|
||
|
m_vecCurAngles.y -= 360;
|
||
|
|
||
|
if (flDist < (0.05 * turnrate))
|
||
|
m_vecCurAngles.y = m_vecGoalAngles.y;
|
||
|
|
||
|
m_fBoneXRotator = m_vecCurAngles.y - UTIL_AngleMod( GetAbsAngles().y );
|
||
|
|
||
|
bMoved = 1;
|
||
|
}
|
||
|
|
||
|
if ( !bMoved || !m_fTurnRate )
|
||
|
m_fTurnRate = turnrate;
|
||
|
|
||
|
if ( bMoved )
|
||
|
{
|
||
|
NetworkStateChanged();
|
||
|
}
|
||
|
|
||
|
return bMoved;
|
||
|
}
|
||
|
|
||
|
void C_ObjectSentrygun::ClientThink( void )
|
||
|
{
|
||
|
// Turtling sentryguns don't think
|
||
|
if ( IsTurtled() )
|
||
|
return;
|
||
|
|
||
|
if ( IsPlacing() || IsBuilding() )
|
||
|
return;
|
||
|
|
||
|
|
||
|
if ( m_hEnemy != NULL )
|
||
|
{
|
||
|
// Figure out where we're firing at
|
||
|
Vector vecMid = EyePosition();
|
||
|
Vector vecFireTarget = m_hEnemy->WorldSpaceCenter(); // + vecMid; // BodyTarget( vecMid );
|
||
|
Vector vecDirToEnemy = vecFireTarget - vecMid;
|
||
|
QAngle angToTarget;
|
||
|
VectorAngles(vecDirToEnemy, angToTarget);
|
||
|
|
||
|
angToTarget.y = UTIL_AngleMod( angToTarget.y );
|
||
|
if (angToTarget.x < -180)
|
||
|
angToTarget.x += 360;
|
||
|
if (angToTarget.x > 180)
|
||
|
angToTarget.x -= 360;
|
||
|
|
||
|
// now all numbers should be in [1...360]
|
||
|
// pin to turret limitations to [-50...50]
|
||
|
if (angToTarget.x > 50)
|
||
|
angToTarget.x = 50;
|
||
|
else if (angToTarget.x < -50)
|
||
|
angToTarget.x = -50;
|
||
|
|
||
|
m_vecGoalAngles.y = angToTarget.y;
|
||
|
m_vecGoalAngles.x = angToTarget.x;
|
||
|
|
||
|
MoveTurret();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Rotate
|
||
|
if ( !MoveTurret() )
|
||
|
{
|
||
|
// Play a sound occasionally
|
||
|
if ( random->RandomFloat(0, 1) < 0.02 )
|
||
|
{
|
||
|
EmitSound( "ObjectSentrygun.Idle" );
|
||
|
}
|
||
|
|
||
|
// Switch rotation direction
|
||
|
if (m_bTurningRight)
|
||
|
{
|
||
|
m_bTurningRight = false;
|
||
|
m_vecGoalAngles.y = m_iLeftBound;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_bTurningRight = true;
|
||
|
m_vecGoalAngles.y = m_iRightBound;
|
||
|
}
|
||
|
|
||
|
// Randomly look up and down a bit
|
||
|
if ( random->RandomFloat(0, 1) < 0.3 )
|
||
|
{
|
||
|
m_vecGoalAngles.x = (int)random->RandomFloat(-10,10);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Control screen
|
||
|
//-----------------------------------------------------------------------------
|
||
|
class CSentrygunControlPanel : public CRotatingObjectControlPanel
|
||
|
{
|
||
|
DECLARE_CLASS( CSentrygunControlPanel, CRotatingObjectControlPanel );
|
||
|
|
||
|
public:
|
||
|
CSentrygunControlPanel( vgui::Panel *parent, const char *panelName );
|
||
|
virtual bool Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData );
|
||
|
virtual void OnTick();
|
||
|
virtual void OnCommand( const char *command );
|
||
|
|
||
|
void AddAmmo( void );
|
||
|
|
||
|
private:
|
||
|
vgui::Label *m_pAmmoLabel;
|
||
|
};
|
||
|
|
||
|
|
||
|
DECLARE_VGUI_SCREEN_FACTORY( CSentrygunControlPanel, "sentrygun_control_panel" );
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Constructor:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CSentrygunControlPanel::CSentrygunControlPanel( vgui::Panel *parent, const char *panelName )
|
||
|
: BaseClass( parent, "CSentrygunControlPanel" )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Initialization
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CSentrygunControlPanel::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData )
|
||
|
{
|
||
|
m_pAmmoLabel = new vgui::Label( this, "AmmoReadout", "" );
|
||
|
|
||
|
if (!BaseClass::Init(pKeyValues, pInitData))
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Frame-based update
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CSentrygunControlPanel::OnTick()
|
||
|
{
|
||
|
BaseClass::OnTick();
|
||
|
|
||
|
C_BaseObject *pObj = GetOwningObject();
|
||
|
if (!pObj)
|
||
|
return;
|
||
|
|
||
|
Assert( dynamic_cast<C_ObjectSentrygun*>(pObj) );
|
||
|
C_ObjectSentrygun *pSentrygun = static_cast<C_ObjectSentrygun*>(pObj);
|
||
|
|
||
|
char buf[256];
|
||
|
int iAmmo = pSentrygun->GetAmmoLeft();
|
||
|
if (iAmmo > 0)
|
||
|
{
|
||
|
Q_snprintf( buf, sizeof( buf ), "%d rounds left", iAmmo );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Q_snprintf( buf, sizeof( buf ), "OUT OF AMMO" );
|
||
|
}
|
||
|
m_pAmmoLabel->SetText( buf );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Handle ammo input to the sentrygun
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CSentrygunControlPanel::AddAmmo( void )
|
||
|
{
|
||
|
C_BaseObject *pObj = GetOwningObject();
|
||
|
if (pObj)
|
||
|
{
|
||
|
pObj->SendClientCommand( "addammo" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Button click handlers
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CSentrygunControlPanel::OnCommand( const char *command )
|
||
|
{
|
||
|
if (!Q_strnicmp(command, "AddAmmo", 7))
|
||
|
{
|
||
|
AddAmmo();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
BaseClass::OnCommand(command);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//======================================================================================================
|
||
|
// SENTRYGUN TYPES
|
||
|
//======================================================================================================
|
||
|
// Purpose: Plasma sentrygun
|
||
|
//-----------------------------------------------------------------------------
|
||
|
IMPLEMENT_CLIENTCLASS_DT(C_ObjectSentrygunPlasma, DT_ObjectSentrygunPlasma, CObjectSentrygunPlasma)
|
||
|
END_RECV_TABLE()
|
||
|
|
||
|
C_ObjectSentrygunPlasma::C_ObjectSentrygunPlasma()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Rocket launcher sentrygun
|
||
|
//-----------------------------------------------------------------------------
|
||
|
IMPLEMENT_CLIENTCLASS_DT(C_ObjectSentrygunRocketlauncher, DT_ObjectSentrygunRocketlauncher, CObjectSentrygunRocketlauncher)
|
||
|
END_RECV_TABLE()
|
||
|
|
||
|
C_ObjectSentrygunRocketlauncher::C_ObjectSentrygunRocketlauncher()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|