//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: RPG // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "npcevent.h" #include "in_buttons.h" #include "hl1mp_basecombatweapon_shared.h" #include "hl1mp_weapon_rpg.h" #ifdef CLIENT_DLL #include "hl1/c_hl1mp_player.h" #include "model_types.h" #include "beamdraw.h" #include "fx_line.h" #include "view.h" #else #include "basecombatcharacter.h" #include "movie_explosion.h" #include "hl1mp_player.h" #include "rope.h" #include "soundent.h" #include "vstdlib/random.h" #include "engine/IEngineSound.h" #include "explode.h" #include "util.h" #include "te_effect_dispatch.h" #include "shake.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern ConVar sk_plr_dmg_rpg; void TE_BeamFollow( IRecipientFilter& filter, float delay, int iEntIndex, int modelIndex, int haloIndex, float life, float width, float endWidth, float fadeLength,float r, float g, float b, float a ); #define RPG_LASER_SPRITE "sprites/redglow_mp1" class CLaserDot : public CBaseEntity { DECLARE_CLASS( CLaserDot, CBaseEntity ); public: CLaserDot( void ); ~CLaserDot( void ); static CLaserDot *Create( const Vector &origin, CBaseEntity *pOwner = NULL, bool bVisibleDot = true ); void SetTargetEntity( CBaseEntity *pTarget ) { m_hTargetEnt = pTarget; } CBaseEntity *GetTargetEntity( void ) { return m_hTargetEnt; } void SetLaserPosition( const Vector &origin, const Vector &normal ); Vector GetChasePosition(); void TurnOn( void ); void TurnOff( void ); bool IsOn() const { return m_bIsOn; } void Toggle( void ); int ObjectCaps() { return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; } void MakeInvisible( void ); #ifdef CLIENT_DLL virtual bool IsTransparent( void ) { return true; } virtual RenderGroup_t GetRenderGroup( void ) { return RENDER_GROUP_TRANSLUCENT_ENTITY; } virtual int DrawModel( int flags ); virtual void OnDataChanged( DataUpdateType_t updateType ); virtual bool ShouldDraw( void ) { return (IsEffectActive(EF_NODRAW)==false); } CMaterialReference m_hSpriteMaterial; #endif protected: Vector m_vecSurfaceNormal; EHANDLE m_hTargetEnt; bool m_bVisibleLaserDot; // bool m_bIsOn; CNetworkVar( bool, m_bIsOn ); DECLARE_NETWORKCLASS(); DECLARE_DATADESC(); public: CLaserDot *m_pNext; }; #ifndef CLIENT_DLL //============================================================================= // RPG Rocket //============================================================================= BEGIN_DATADESC( CRpgRocket ) DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ), DEFINE_FIELD( m_vecAbsVelocity, FIELD_VECTOR ), DEFINE_FIELD( m_flIgniteTime, FIELD_TIME ), //DEFINE_FIELD( m_iTrail, FIELD_INTEGER ), // Function Pointers DEFINE_ENTITYFUNC( RocketTouch ), DEFINE_THINKFUNC( IgniteThink ), DEFINE_THINKFUNC( SeekThink ), END_DATADESC() LINK_ENTITY_TO_CLASS( rpg_rocket, CRpgRocket ); IMPLEMENT_SERVERCLASS_ST(CRpgRocket, DT_RpgRocket) END_SEND_TABLE() CRpgRocket::CRpgRocket() { } //----------------------------------------------------------------------------- // Purpose: // // //----------------------------------------------------------------------------- void CRpgRocket::Precache( void ) { PrecacheModel( "models/rpgrocket.mdl" ); PrecacheModel( "sprites/animglow01.vmt" ); PrecacheScriptSound( "Weapon_RPG.RocketIgnite" ); m_iTrail = PrecacheModel("sprites/smoke.vmt"); } //----------------------------------------------------------------------------- // Purpose: // // //----------------------------------------------------------------------------- void CRpgRocket::Spawn( void ) { Precache(); SetSolid( SOLID_BBOX ); SetModel("models/rpgrocket.mdl"); UTIL_SetSize( this, -Vector(0,0,0), Vector(0,0,0) ); SetTouch( &CRpgRocket::RocketTouch ); SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); SetThink( &CRpgRocket::IgniteThink ); SetNextThink( gpGlobals->curtime + 0.4f ); QAngle angAngs; Vector vecFwd; angAngs = GetAbsAngles(); angAngs.x -= 30; AngleVectors( angAngs, &vecFwd ); SetAbsVelocity( vecFwd * 250 ); SetGravity( UTIL_ScaleForGravity( 400 ) ); // use a lower gravity for rockets m_flDamage = sk_plr_dmg_rpg.GetFloat(); m_DmgRadius = m_flDamage * 2.5; } //----------------------------------------------------------------------------- // Purpose: // Input : *pOther - //----------------------------------------------------------------------------- void CRpgRocket::RocketTouch( CBaseEntity *pOther ) { if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) ) return; if ( m_hOwner != NULL ) { m_hOwner->NotifyRocketDied(); } StopSound( "Weapon_RPG.RocketIgnite" ); ExplodeTouch( pOther ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CRpgRocket::IgniteThink( void ) { SetMoveType( MOVETYPE_FLY ); AddEffects( EF_DIMLIGHT ); EmitSound( "Weapon_RPG.RocketIgnite" ); SetThink( &CRpgRocket::SeekThink ); SetNextThink( gpGlobals->curtime + 0.1f ); CBroadcastRecipientFilter filter; TE_BeamFollow( filter, 0.0, entindex(), m_iTrail, 0, 4, 5, 5, 0, 224, 224, 255, 255 ); m_flIgniteTime = gpGlobals->curtime; } #define RPG_HOMING_SPEED 0.125f //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CRpgRocket::SeekThink( void ) { CBaseEntity *pOther = NULL; Vector vecTarget; Vector vecFwd; Vector vecDir; float flDist, flMax, flDot; trace_t tr; AngleVectors( GetAbsAngles(), &vecFwd ); vecTarget = vecFwd; flMax = 4096; // Examine all entities within a reasonable radius while ( (pOther = gEntList.FindEntityByClassname( pOther, "laser_spot" ) ) != NULL) { CLaserDot *pDot = dynamic_cast(pOther); // if ( pDot->IsActive() ) if ( pDot->IsOn() ) { UTIL_TraceLine( GetAbsOrigin(), pDot->GetAbsOrigin(), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction >= 0.90 ) { vecDir = pDot->GetAbsOrigin() - GetAbsOrigin(); flDist = VectorLength( vecDir ); VectorNormalize( vecDir ); flDot = DotProduct( vecFwd, vecDir ); if ( (flDot > 0) && (flDist * (1 - flDot) < flMax) ) { flMax = flDist * (1 - flDot); vecTarget = vecDir; } } } } QAngle vecAng; VectorAngles( vecTarget, vecAng ); SetAbsAngles( vecAng ); // this acceleration and turning math is totally wrong, but it seems to respond well so don't change it. float flSpeed = GetAbsVelocity().Length(); if ( gpGlobals->curtime - m_flIgniteTime < 1.0 ) { SetAbsVelocity( GetAbsVelocity() * 0.2 + vecTarget * (flSpeed * 0.8 + 400) ); if ( GetWaterLevel() == 3 ) { // go slow underwater if ( GetAbsVelocity().Length() > 300 ) { Vector vecVel = GetAbsVelocity(); VectorNormalize( vecVel ); SetAbsVelocity( vecVel * 300 ); } UTIL_BubbleTrail( GetAbsOrigin() - GetAbsVelocity() * 0.1, GetAbsOrigin(), 4 ); } else { if ( GetAbsVelocity().Length() > 2000 ) { Vector vecVel = GetAbsVelocity(); VectorNormalize( vecVel ); SetAbsVelocity( vecVel * 2000 ); } } } else { if ( IsEffectActive( EF_DIMLIGHT ) ) { ClearEffects(); } SetAbsVelocity( GetAbsVelocity() * 0.2 + vecTarget * flSpeed * 0.798 ); if ( GetWaterLevel() == 0 && GetAbsVelocity().Length() < 1500 ) { Detonate(); } } SetNextThink( gpGlobals->curtime + 0.1f ); } void CRpgRocket::Detonate( void ) { StopSound( "Weapon_RPG.RocketIgnite" ); BaseClass::Detonate(); } //----------------------------------------------------------------------------- // Purpose: // // Input : &vecOrigin - // &vecAngles - // NULL - // // Output : CRpgRocket //----------------------------------------------------------------------------- CRpgRocket *CRpgRocket::Create( const Vector &vecOrigin, const QAngle &angAngles, CBasePlayer *pentOwner ) { CRpgRocket *pRocket = (CRpgRocket *)CreateEntityByName( "rpg_rocket" ); UTIL_SetOrigin( pRocket, vecOrigin ); pRocket->SetAbsAngles( angAngles ); pRocket->Spawn(); pRocket->SetOwnerEntity( pentOwner ); return pRocket; } #endif // endif #ifndef CLIENT_DLL //============================================================================= // Laser Dot //============================================================================= IMPLEMENT_NETWORKCLASS_ALIASED( LaserDot, DT_LaserDot ) BEGIN_NETWORK_TABLE( CLaserDot, DT_LaserDot ) #ifdef CLIENT_DLL RecvPropBool( RECVINFO( m_bIsOn ) ), #else SendPropBool( SENDINFO( m_bIsOn ) ), #endif END_NETWORK_TABLE() #ifndef CLIENT_DLL // a list of laser dots to search quickly CEntityClassList g_LaserDotList; template <> CLaserDot *CEntityClassList::m_pClassList = NULL; CLaserDot *GetLaserDotList() { return g_LaserDotList.m_pClassList; } #endif LINK_ENTITY_TO_CLASS( laser_spot, CLaserDot ); BEGIN_DATADESC( CLaserDot ) DEFINE_FIELD( m_vecSurfaceNormal, FIELD_VECTOR ), DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ), DEFINE_FIELD( m_bVisibleLaserDot, FIELD_BOOLEAN ), DEFINE_FIELD( m_bIsOn, FIELD_BOOLEAN ), //DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ), // don't save - regenerated by constructor END_DATADESC() //----------------------------------------------------------------------------- // Finds missiles in cone //----------------------------------------------------------------------------- CBaseEntity *CreateLaserDot( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot ) { return CLaserDot::Create( origin, pOwner, bVisibleDot ); } void SetLaserDotTarget( CBaseEntity *pLaserDot, CBaseEntity *pTarget ) { CLaserDot *pDot = assert_cast< CLaserDot* >(pLaserDot ); pDot->SetTargetEntity( pTarget ); } void EnableLaserDot( CBaseEntity *pLaserDot, bool bEnable ) { CLaserDot *pDot = assert_cast< CLaserDot* >(pLaserDot ); if ( bEnable ) { pDot->TurnOn(); } else { pDot->TurnOff(); } } CLaserDot::CLaserDot( void ) { m_hTargetEnt = NULL; m_bIsOn = true; #ifndef CLIENT_DLL g_LaserDotList.Insert( this ); #endif } CLaserDot::~CLaserDot( void ) { #ifndef CLIENT_DLL g_LaserDotList.Remove( this ); #endif } //----------------------------------------------------------------------------- // Purpose: // Input : &origin - // Output : CLaserDot //----------------------------------------------------------------------------- CLaserDot *CLaserDot::Create( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot ) { #ifndef CLIENT_DLL CLaserDot *pLaserDot = (CLaserDot *) CBaseEntity::Create( "laser_spot", origin, QAngle(0,0,0) ); if ( pLaserDot == NULL ) return NULL; pLaserDot->m_bVisibleLaserDot = bVisibleDot; pLaserDot->SetMoveType( MOVETYPE_NONE ); pLaserDot->AddSolidFlags( FSOLID_NOT_SOLID ); pLaserDot->AddEffects( EF_NOSHADOW ); UTIL_SetSize( pLaserDot, -Vector(6,6,6), Vector(6,6,6) ); pLaserDot->SetOwnerEntity( pOwner ); pLaserDot->AddEFlags( EFL_FORCE_CHECK_TRANSMIT ); if ( !bVisibleDot ) { pLaserDot->MakeInvisible(); } return pLaserDot; #else return NULL; #endif } void CLaserDot::SetLaserPosition( const Vector &origin, const Vector &normal ) { SetAbsOrigin( origin ); m_vecSurfaceNormal = normal; } Vector CLaserDot::GetChasePosition() { return GetAbsOrigin() - m_vecSurfaceNormal * 10; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLaserDot::TurnOn( void ) { m_bIsOn = true; RemoveEffects(EF_NODRAW); if ( m_bVisibleLaserDot ) { //BaseClass::TurnOn(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLaserDot::TurnOff( void ) { m_bIsOn = false; AddEffects(EF_NODRAW); if ( m_bVisibleLaserDot ) { //BaseClass::TurnOff(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CLaserDot::MakeInvisible( void ) { } #ifdef CLIENT_DLL //----------------------------------------------------------------------------- // Purpose: Draw our sprite //----------------------------------------------------------------------------- int CLaserDot::DrawModel( int flags ) { color32 color={255,255,255,255}; Vector vecAttachment, vecDir; QAngle angles; float scale; Vector endPos; C_HL1MP_Player *pOwner = ToHL1MPPlayer( GetOwnerEntity() ); if ( pOwner != NULL && pOwner->IsDormant() == false ) { // Always draw the dot in front of our faces when in first-person if ( pOwner->IsLocalPlayer() ) { // Take our view position and orientation vecAttachment = CurrentViewOrigin(); vecDir = CurrentViewForward(); } else { // Take the eye position and direction vecAttachment = pOwner->EyePosition(); QAngle angles = pOwner->EyeAngles(); AngleVectors( angles, &vecDir ); } trace_t tr; UTIL_TraceLine( vecAttachment, vecAttachment + ( vecDir * MAX_TRACE_LENGTH ), MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr ); // Backup off the hit plane endPos = tr.endpos + ( tr.plane.normal * 4.0f ); } else { // Just use our position if we can't predict it otherwise endPos = GetAbsOrigin(); } // Randomly flutter scale = 16.0f + random->RandomFloat( -4.0f, 4.0f ); // Draw our laser dot in space CMatRenderContextPtr pRenderContext( materials ); pRenderContext->Bind( m_hSpriteMaterial, this ); DrawSprite( endPos, scale, scale, color ); return 1; } //----------------------------------------------------------------------------- // Purpose: Setup our sprite reference //----------------------------------------------------------------------------- void CLaserDot::OnDataChanged( DataUpdateType_t updateType ) { if ( updateType == DATA_UPDATE_CREATED ) { m_hSpriteMaterial.Init( RPG_LASER_SPRITE, TEXTURE_GROUP_CLIENT_EFFECTS ); } } #endif //CLIENT_DLL //============================================================================= // RPG Weapon //============================================================================= LINK_ENTITY_TO_CLASS( weapon_rpg, CWeaponRPG ); PRECACHE_WEAPON_REGISTER( weapon_rpg ); //IMPLEMENT_SERVERCLASS_ST( CWeaponRPG, DT_WeaponRPG ) //END_SEND_TABLE() IMPLEMENT_NETWORKCLASS_ALIASED( WeaponRPG, DT_WeaponRPG ) BEGIN_DATADESC( CWeaponRPG ) DEFINE_FIELD( m_bIntialStateUpdate, FIELD_BOOLEAN ), DEFINE_FIELD( m_bGuiding, FIELD_BOOLEAN ), #ifndef CLIENT_DLL DEFINE_FIELD( m_hLaserDot, FIELD_EHANDLE ), #endif DEFINE_FIELD( m_hMissile, FIELD_EHANDLE ), DEFINE_FIELD( m_bLaserDotSuspended, FIELD_BOOLEAN ), DEFINE_FIELD( m_flLaserDotReviveTime, FIELD_TIME ), END_DATADESC() BEGIN_NETWORK_TABLE( CWeaponRPG, DT_WeaponRPG ) #ifdef CLIENT_DLL RecvPropBool( RECVINFO( m_bIntialStateUpdate ) ), RecvPropBool( RECVINFO( m_bGuiding ) ), RecvPropBool( RECVINFO( m_bLaserDotSuspended ) ), // RecvPropEHandle( RECVINFO( m_hMissile ), RecvProxy_MissileDied ), // RecvPropVector( RECVINFO( m_vecLaserDot ) ), #else SendPropBool( SENDINFO( m_bIntialStateUpdate ) ), SendPropBool( SENDINFO( m_bGuiding ) ), SendPropBool( SENDINFO( m_bLaserDotSuspended ) ), // SendPropEHandle( SENDINFO( m_hMissile ) ), // SendPropVector( SENDINFO( m_vecLaserDot ) ), #endif END_NETWORK_TABLE() BEGIN_PREDICTION_DATA( CWeaponRPG ) #ifdef CLIENT_DLL DEFINE_PRED_FIELD( m_bIntialStateUpdate, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_bGuiding, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_bLaserDotSuspended, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_flLaserDotReviveTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), #endif END_PREDICTION_DATA() //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CWeaponRPG::CWeaponRPG( void ) { m_bReloadsSingly = false; m_bFiresUnderwater = true; m_bGuiding = true; m_bIntialStateUpdate = false; m_bLaserDotSuspended = false; } CWeaponRPG::~CWeaponRPG() { #ifndef CLIENT_DLL if ( m_hLaserDot != NULL ) { UTIL_Remove( m_hLaserDot ); m_hLaserDot = NULL; } #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponRPG::ItemPostFrame( void ) { BaseClass::ItemPostFrame(); CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); if ( pPlayer == NULL ) return; //If we're pulling the weapon out for the first time, wait to draw the laser if ( m_bIntialStateUpdate ) { if ( GetActivity() != ACT_VM_DRAW ) { if ( IsGuiding() && !m_bLaserDotSuspended ) { #ifndef CLIENT_DLL if ( m_hLaserDot != NULL ) { m_hLaserDot->TurnOn(); } #endif } m_bIntialStateUpdate = false; } else { return; } } //Player has toggled guidance state if ( pPlayer->m_afButtonPressed & IN_ATTACK2 ) { if ( IsGuiding() ) { StopGuiding(); } else { StartGuiding(); } } //Move the laser UpdateSpot(); } void CWeaponRPG::Drop( const Vector &vecVelocity ) { StopGuiding(); #ifndef CLIENT_DLL if ( m_hLaserDot != NULL ) { UTIL_Remove( m_hLaserDot ); m_hLaserDot = NULL; } #endif BaseClass::Drop( vecVelocity ); } int CWeaponRPG::GetDefaultClip1( void ) const { if ( g_pGameRules->IsMultiplayer() ) { // more default ammo in multiplay. return BaseClass::GetDefaultClip1() * 2; } else { return BaseClass::GetDefaultClip1(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponRPG::Precache( void ) { #ifndef CLIENT_DLL UTIL_PrecacheOther( "laser_spot" ); UTIL_PrecacheOther( "rpg_rocket" ); #endif // PrecacheModel( RPG_LASER_SPRITE ); PrecacheModel( "sprites/redglow_mp1.vmt" ); BaseClass::Precache(); } bool CWeaponRPG::Deploy( void ) { m_bIntialStateUpdate = true; m_bLaserDotSuspended = false; CreateLaserPointer(); #ifndef CLIENT_DLL if ( m_hLaserDot != NULL ) { m_hLaserDot->TurnOff(); } #endif if ( m_iClip1 <= 0 ) { return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_RPG_DRAW_UNLOADED, (char*)GetAnimPrefix() ); } return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_DRAW, (char*)GetAnimPrefix() ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponRPG::PrimaryAttack( void ) { // Can't have an active missile out if ( m_hMissile != NULL ) return; // Can't be reloading if ( GetActivity() == ACT_VM_RELOAD ) return; if ( m_iClip1 <= 0 ) { if ( !m_bFireOnEmpty ) { Reload(); } else { WeaponSound( EMPTY ); m_flNextPrimaryAttack = gpGlobals->curtime + 0.2; } } Vector vecOrigin; Vector vecForward; CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); if ( pOwner == NULL ) return; SendWeaponAnim( ACT_VM_PRIMARYATTACK ); WeaponSound( SINGLE ); #ifndef CLIENT_DLL CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 400, 0.2 ); #endif pOwner->DoMuzzleFlash(); #ifndef CLIENT_DLL // Register a muzzleflash for the AI pOwner->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 ); #endif Vector vForward, vRight, vUp; pOwner->EyeVectors( &vForward, &vRight, &vUp ); Vector muzzlePoint = pOwner->Weapon_ShootPosition() + vForward * 16.0f + vRight * 8.0f + vUp * -8.0f; #ifndef CLIENT_DLL QAngle vecAngles; VectorAngles( vForward, vecAngles ); CRpgRocket * pMissile = CRpgRocket::Create( muzzlePoint, vecAngles, pOwner ); pMissile->m_hOwner = this; pMissile->SetAbsVelocity( pMissile->GetAbsVelocity() + vForward * DotProduct( pOwner->GetAbsVelocity(), vForward ) ); m_hMissile = pMissile; #endif pOwner->ViewPunch( QAngle( -5, 0, 0 ) ); m_iClip1--; m_flNextPrimaryAttack = gpGlobals->curtime + 1.5; SetWeaponIdleTime( 1.5 ); UpdateSpot(); } void CWeaponRPG::WeaponIdle( void ) { CBaseCombatCharacter *pOwner = GetOwner(); if ( pOwner == NULL ) return; if ( !HasWeaponIdleTimeElapsed() ) return; int iAnim; float flRand = random->RandomFloat( 0, 1 ); if ( flRand <= 0.75 || IsGuiding() ) { if ( m_iClip1 <= 0 ) iAnim = ACT_RPG_IDLE_UNLOADED; else iAnim = ACT_VM_IDLE; } else { if ( m_iClip1 <= 0 ) iAnim = ACT_RPG_FIDGET_UNLOADED; else iAnim = ACT_VM_FIDGET; } SendWeaponAnim( iAnim ); } void CWeaponRPG::NotifyRocketDied( void ) { m_hMissile = NULL; // Can't be reloading if ( GetActivity() == ACT_VM_RELOAD ) return; // Reload(); } bool CWeaponRPG::Reload( void ) { CBaseCombatCharacter *pOwner = GetOwner(); #if 0 if ( pOwner == NULL ) return false; if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 ) return false; WeaponSound( RELOAD ); SendWeaponAnim( ACT_VM_RELOAD ); #ifndef CLIENT_DLL if ( m_hLaserDot != NULL ) { m_hLaserDot->TurnOff(); } #endif m_bLaserDotSuspended = true; m_flLaserDotReviveTime = gpGlobals->curtime + 2.1; m_flNextPrimaryAttack = gpGlobals->curtime + 2.1; m_flNextSecondaryAttack = gpGlobals->curtime + 2.1; return true; #endif // Can't be reloading if ( GetActivity() == ACT_VM_RELOAD ) return false; if ( pOwner == NULL ) return false; if ( m_iClip1 > 0 ) return false; if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 ) return false; // because the RPG waits to autoreload when no missiles are active while the LTD is on, the // weapons code is constantly calling into this function, but is often denied because // a) missiles are in flight, but the LTD is on // or // b) player is totally out of ammo and has nothing to switch to, and should be allowed to // shine the designator around // // Set the next attack time into the future so that WeaponIdle will get called more often // than reload, allowing the RPG LTD to be updated if ( ( m_hMissile != NULL ) && IsGuiding() ) { // no reloading when there are active missiles tracking the designator. // ward off future autoreload attempts by setting next attack time into the future for a bit. return false; } #ifndef CLIENT_DLL if ( m_hLaserDot != NULL ) { m_hLaserDot->TurnOff(); } #endif m_bLaserDotSuspended = true; m_flLaserDotReviveTime = gpGlobals->curtime + 2.1; m_flNextSecondaryAttack = gpGlobals->curtime + 2.1; return DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD ); } bool CWeaponRPG::Holster( CBaseCombatWeapon *pSwitchingTo ) { // can't put away while guiding a missile. if ( IsGuiding() && ( m_hMissile != NULL ) ) return false; // StopGuiding(); #ifndef CLIENT_DLL if ( m_hLaserDot != NULL ) { m_hLaserDot->TurnOff(); UTIL_Remove( m_hLaserDot ); m_hLaserDot = NULL; } #endif m_bLaserDotSuspended = false; return BaseClass::Holster( pSwitchingTo ); } void CWeaponRPG::UpdateSpot( void ) { CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); if ( pPlayer == NULL ) return; CreateLaserPointer(); #ifndef CLIENT_DLL if ( m_hLaserDot == NULL ) return; #endif if ( IsGuiding() && m_bLaserDotSuspended && ( m_flLaserDotReviveTime <= gpGlobals->curtime ) ) { #ifndef CLIENT_DLL m_hLaserDot->TurnOn(); #endif m_bLaserDotSuspended = false; } //Move the laser dot, if active trace_t tr; Vector muzzlePos = pPlayer->Weapon_ShootPosition(); Vector forward; AngleVectors( pPlayer->EyeAngles() + pPlayer->m_Local.m_vecPunchAngle, &forward ); Vector endPos = muzzlePos + ( forward * MAX_TRACE_LENGTH ); // Trace out for the endpoint UTIL_TraceLine( muzzlePos, endPos, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); // Move the laser sprite Vector laserPos = tr.endpos + ( tr.plane.normal * 2.0f ); #ifndef CLIENT_DLL m_hLaserDot->SetLaserPosition( laserPos, tr.plane.normal ); #endif } void CWeaponRPG::CreateLaserPointer( void ) { #ifndef CLIENT_DLL if ( m_hLaserDot != NULL ) return; m_hLaserDot = CLaserDot::Create( GetAbsOrigin(), GetOwner() ); if ( !IsGuiding() ) { if ( m_hLaserDot ) { m_hLaserDot->TurnOff(); } } #endif } bool CWeaponRPG::IsGuiding( void ) { return m_bGuiding; } void CWeaponRPG::StartGuiding( void ) { m_bGuiding = true; #ifndef CLIENT_DLL if ( m_hLaserDot != NULL ) { m_hLaserDot->TurnOn(); } #endif UpdateSpot(); } void CWeaponRPG::StopGuiding( void ) { m_bGuiding = false; #ifndef CLIENT_DLL if ( m_hLaserDot != NULL ) { m_hLaserDot->TurnOff(); } #endif }