mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-22 14:16:50 +00:00
601 lines
14 KiB
C++
601 lines
14 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Satchel charge
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "npcevent.h"
|
|
#include "hl1_basecombatweapon_shared.h"
|
|
//#include "basecombatweapon.h"
|
|
//#include "basecombatcharacter.h"
|
|
#ifdef CLIENT_DLL
|
|
#include "c_baseplayer.h"
|
|
#else
|
|
#include "player.h"
|
|
#endif
|
|
//#include "AI_BaseNPC.h"
|
|
//#include "player.h"
|
|
#include "gamerules.h"
|
|
#include "in_buttons.h"
|
|
#ifndef CLIENT_DLL
|
|
#include "soundent.h"
|
|
#include "game.h"
|
|
#endif
|
|
#include "vstdlib/random.h"
|
|
#include "engine/IEngineSound.h"
|
|
#include "hl1mp_weapon_satchel.h"
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CWeaponSatchel
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
#define SATCHEL_VIEW_MODEL "models/v_satchel.mdl"
|
|
#define SATCHEL_WORLD_MODEL "models/w_satchel.mdl"
|
|
#define SATCHELRADIO_VIEW_MODEL "models/v_satchel_radio.mdl"
|
|
#define SATCHELRADIO_WORLD_MODEL "models/w_satchel.mdl" // this needs fixing if we do multiplayer
|
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSatchel, DT_WeaponSatchel );
|
|
|
|
BEGIN_NETWORK_TABLE( CWeaponSatchel, DT_WeaponSatchel )
|
|
#ifdef CLIENT_DLL
|
|
RecvPropInt( RECVINFO( m_iRadioViewIndex ) ),
|
|
RecvPropInt( RECVINFO( m_iRadioWorldIndex ) ),
|
|
RecvPropInt( RECVINFO( m_iSatchelViewIndex ) ),
|
|
RecvPropInt( RECVINFO( m_iSatchelWorldIndex ) ),
|
|
RecvPropInt( RECVINFO( m_iChargeReady ) ),
|
|
#else
|
|
SendPropInt( SENDINFO( m_iRadioViewIndex ) ),
|
|
SendPropInt( SENDINFO( m_iRadioWorldIndex ) ),
|
|
SendPropInt( SENDINFO( m_iSatchelViewIndex ) ),
|
|
SendPropInt( SENDINFO( m_iSatchelWorldIndex ) ),
|
|
SendPropInt( SENDINFO( m_iChargeReady ) ),
|
|
#endif
|
|
END_NETWORK_TABLE()
|
|
|
|
LINK_ENTITY_TO_CLASS( weapon_satchel, CWeaponSatchel );
|
|
|
|
PRECACHE_WEAPON_REGISTER( weapon_satchel );
|
|
|
|
//IMPLEMENT_SERVERCLASS_ST( CWeaponSatchel, DT_WeaponSatchel )
|
|
//END_SEND_TABLE()
|
|
|
|
|
|
BEGIN_PREDICTION_DATA( CWeaponSatchel )
|
|
#ifdef CLIENT_DLL
|
|
DEFINE_PRED_FIELD( m_iRadioViewIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
|
|
DEFINE_PRED_FIELD( m_iRadioWorldIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
|
|
DEFINE_PRED_FIELD( m_iSatchelViewIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
|
|
DEFINE_PRED_FIELD( m_iSatchelWorldIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ),
|
|
DEFINE_PRED_FIELD( m_iChargeReady, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
|
#endif
|
|
END_PREDICTION_DATA()
|
|
|
|
|
|
BEGIN_DATADESC( CWeaponSatchel )
|
|
DEFINE_FIELD( m_iChargeReady, FIELD_INTEGER ),
|
|
|
|
// DEFINE_FIELD( m_iRadioViewIndex, FIELD_INTEGER ),
|
|
// DEFINE_FIELD( m_iRadioWorldIndex, FIELD_INTEGER ),
|
|
// DEFINE_FIELD( m_iSatchelViewIndex, FIELD_INTEGER ),
|
|
// DEFINE_FIELD( m_iSatchelWorldIndex, FIELD_INTEGER ),
|
|
END_DATADESC()
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructor
|
|
//-----------------------------------------------------------------------------
|
|
CWeaponSatchel::CWeaponSatchel( void )
|
|
{
|
|
m_bReloadsSingly = false;
|
|
m_bFiresUnderwater = true;
|
|
}
|
|
|
|
void CWeaponSatchel::Equip( CBaseCombatCharacter *pOwner )
|
|
{
|
|
BaseClass::Equip( pOwner );
|
|
|
|
m_iChargeReady = 0; // this satchel charge weapon now forgets that any satchels are deployed by it.
|
|
}
|
|
|
|
bool CWeaponSatchel::HasAnyAmmo( void )
|
|
{
|
|
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
|
if ( !pPlayer )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
|
|
{
|
|
// player is carrying some satchels
|
|
return true;
|
|
}
|
|
|
|
if ( HasChargeDeployed() )
|
|
{
|
|
// player isn't carrying any satchels, but has some out
|
|
return true;
|
|
}
|
|
|
|
return BaseClass::HasAnyAmmo();
|
|
}
|
|
|
|
bool CWeaponSatchel::CanDeploy( void )
|
|
{
|
|
if ( HasAnyAmmo() )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CWeaponSatchel::Precache( void )
|
|
{
|
|
m_iSatchelViewIndex = PrecacheModel( SATCHEL_VIEW_MODEL );
|
|
m_iSatchelWorldIndex = PrecacheModel( SATCHEL_WORLD_MODEL );
|
|
m_iRadioViewIndex = PrecacheModel( SATCHELRADIO_VIEW_MODEL );
|
|
m_iRadioWorldIndex = PrecacheModel( SATCHELRADIO_WORLD_MODEL );
|
|
|
|
#ifndef CLIENT_DLL
|
|
UTIL_PrecacheOther( "monster_satchel" );
|
|
#endif
|
|
|
|
BaseClass::Precache();
|
|
}
|
|
|
|
void CWeaponSatchel::ItemPostFrame( void )
|
|
{
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
|
if (!pOwner)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
|
|
{
|
|
// If the firing button was just pressed, reset the firing time
|
|
if ( pOwner->m_afButtonPressed & IN_ATTACK )
|
|
{
|
|
m_flNextPrimaryAttack = gpGlobals->curtime;
|
|
}
|
|
|
|
PrimaryAttack();
|
|
}
|
|
|
|
BaseClass::ItemPostFrame();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CWeaponSatchel::PrimaryAttack( void )
|
|
{
|
|
switch ( m_iChargeReady )
|
|
{
|
|
case 0:
|
|
{
|
|
Throw();
|
|
}
|
|
break;
|
|
case 1:
|
|
{
|
|
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
|
if ( !pPlayer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
|
|
|
|
#ifndef CLIENT_DLL
|
|
CBaseEntity *pSatchel = NULL;
|
|
|
|
while ( (pSatchel = gEntList.FindEntityByClassname( pSatchel, "monster_satchel" ) ) != NULL)
|
|
{
|
|
if ( pSatchel->GetOwnerEntity() == pPlayer )
|
|
{
|
|
pSatchel->Use( pPlayer, pPlayer, USE_ON, 0 );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
m_iChargeReady = 2;
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
|
|
SetWeaponIdleTime( gpGlobals->curtime + 0.5 );
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
// we're reloading, don't allow fire
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CWeaponSatchel::SecondaryAttack( void )
|
|
{
|
|
if ( m_iChargeReady != 2 )
|
|
{
|
|
Throw();
|
|
}
|
|
}
|
|
|
|
void CWeaponSatchel::Throw( void )
|
|
{
|
|
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
|
if ( !pPlayer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
|
|
{
|
|
Vector vecForward;
|
|
pPlayer->EyeVectors( &vecForward );
|
|
|
|
Vector vecSrc = pPlayer->WorldSpaceCenter();
|
|
Vector vecThrow = vecForward * 274 + pPlayer->GetAbsVelocity();
|
|
|
|
#ifndef CLIENT_DLL
|
|
CBaseEntity *pSatchel = Create( "monster_satchel", vecSrc, QAngle( 0, 0, 90 ), pPlayer );
|
|
if ( pSatchel )
|
|
{
|
|
pSatchel->SetAbsVelocity( vecThrow );
|
|
QAngle angVel = pSatchel->GetLocalAngularVelocity();
|
|
angVel.y = 400;
|
|
pSatchel->SetLocalAngularVelocity( angVel );
|
|
|
|
ActivateRadioModel();
|
|
|
|
SendWeaponAnim( ACT_VM_DRAW );
|
|
|
|
// player "shoot" animation
|
|
pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
|
|
|
m_iChargeReady = 1;
|
|
|
|
pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
|
|
|
|
}
|
|
#endif
|
|
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
|
|
}
|
|
}
|
|
|
|
void CWeaponSatchel::WeaponIdle( void )
|
|
{
|
|
if ( !HasWeaponIdleTimeElapsed() )
|
|
return;
|
|
|
|
switch( m_iChargeReady )
|
|
{
|
|
case 0:
|
|
case 1:
|
|
SendWeaponAnim( ACT_VM_FIDGET );
|
|
break;
|
|
case 2:
|
|
{
|
|
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
|
|
|
if ( pPlayer && (pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0) )
|
|
{
|
|
m_iChargeReady = 0;
|
|
if ( !pPlayer->SwitchToNextBestWeapon( pPlayer->GetActiveWeapon() ) )
|
|
{
|
|
Holster();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
ActivateSatchelModel();
|
|
|
|
SendWeaponAnim( ACT_VM_DRAW );
|
|
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
|
|
m_iChargeReady = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) );// how long till we do this again.
|
|
}
|
|
|
|
bool CWeaponSatchel::Deploy( void )
|
|
{
|
|
SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) );
|
|
|
|
if ( HasChargeDeployed() )
|
|
{
|
|
ActivateRadioModel();
|
|
}
|
|
else
|
|
{
|
|
ActivateSatchelModel();
|
|
}
|
|
|
|
bool bRet = BaseClass::Deploy();
|
|
if ( bRet )
|
|
{
|
|
//
|
|
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
|
if ( pPlayer )
|
|
{
|
|
pPlayer->SetNextAttack( gpGlobals->curtime + 1.0 );
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
|
|
}
|
|
|
|
bool CWeaponSatchel::Holster( CBaseCombatWeapon *pSwitchingTo )
|
|
{
|
|
if ( !BaseClass::Holster( pSwitchingTo ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
|
if ( pPlayer )
|
|
{
|
|
pPlayer->SetNextAttack( gpGlobals->curtime + 0.5 );
|
|
|
|
if ( (pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0) && !HasChargeDeployed() )
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
SetThink( &CWeaponSatchel::DestroyItem );
|
|
SetNextThink( gpGlobals->curtime + 0.1 );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CWeaponSatchel::ActivateSatchelModel( void )
|
|
{
|
|
m_iViewModelIndex = m_iSatchelViewIndex;
|
|
m_iWorldModelIndex = m_iSatchelWorldIndex;
|
|
SetModel( GetViewModel() );
|
|
}
|
|
|
|
void CWeaponSatchel::ActivateRadioModel( void )
|
|
{
|
|
m_iViewModelIndex = m_iRadioViewIndex;
|
|
m_iWorldModelIndex = m_iRadioWorldIndex;
|
|
SetModel( GetViewModel() );
|
|
}
|
|
|
|
const char *CWeaponSatchel::GetViewModel( int ) const
|
|
{
|
|
if ( m_iViewModelIndex == m_iSatchelViewIndex )
|
|
{
|
|
return SATCHEL_VIEW_MODEL;
|
|
}
|
|
else
|
|
{
|
|
return SATCHELRADIO_VIEW_MODEL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
const char *CWeaponSatchel::GetWorldModel( void ) const
|
|
{
|
|
if ( m_iViewModelIndex == m_iSatchelViewIndex )
|
|
{
|
|
return SATCHEL_WORLD_MODEL;
|
|
}
|
|
else
|
|
{
|
|
return SATCHELRADIO_WORLD_MODEL;
|
|
}
|
|
}
|
|
|
|
void CWeaponSatchel::OnRestore( void )
|
|
{
|
|
BaseClass::OnRestore();
|
|
if ( HasChargeDeployed() )
|
|
{
|
|
ActivateRadioModel();
|
|
}
|
|
else
|
|
{
|
|
ActivateSatchelModel();
|
|
}
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
//-----------------------------------------------------------------------------
|
|
// CSatchelCharge
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#define SATCHEL_CHARGE_MODEL "models/w_satchel.mdl"
|
|
|
|
|
|
extern ConVar sk_plr_dmg_satchel;
|
|
|
|
|
|
BEGIN_DATADESC( CSatchelCharge )
|
|
DEFINE_FIELD( m_flNextBounceSoundTime, FIELD_TIME ),
|
|
DEFINE_FIELD( m_bInAir, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_vLastPosition, FIELD_POSITION_VECTOR ),
|
|
|
|
// Function Pointers
|
|
DEFINE_ENTITYFUNC( SatchelTouch ),
|
|
DEFINE_THINKFUNC( SatchelThink ),
|
|
DEFINE_USEFUNC( SatchelUse ),
|
|
END_DATADESC()
|
|
|
|
LINK_ENTITY_TO_CLASS( monster_satchel, CSatchelCharge );
|
|
|
|
//=========================================================
|
|
// Deactivate - do whatever it is we do to an orphaned
|
|
// satchel when we don't want it in the world anymore.
|
|
//=========================================================
|
|
void CSatchelCharge::Deactivate( void )
|
|
{
|
|
AddSolidFlags( FSOLID_NOT_SOLID );
|
|
UTIL_Remove( this );
|
|
}
|
|
|
|
|
|
void CSatchelCharge::Spawn( void )
|
|
{
|
|
Precache( );
|
|
// motor
|
|
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_SLIDE );
|
|
SetSolid( SOLID_BBOX );
|
|
|
|
SetModel( SATCHEL_CHARGE_MODEL );
|
|
|
|
UTIL_SetSize( this, Vector( -4, -4, 0), Vector(4, 4, 8) );
|
|
|
|
SetTouch( &CSatchelCharge::SatchelTouch );
|
|
SetUse( &CSatchelCharge::SatchelUse );
|
|
SetThink( &CSatchelCharge::SatchelThink );
|
|
SetNextThink( gpGlobals->curtime + 0.1f );
|
|
|
|
m_flDamage = sk_plr_dmg_satchel.GetFloat();
|
|
m_DmgRadius = m_flDamage * 2.5;
|
|
m_takedamage = DAMAGE_NO;
|
|
m_iHealth = 1;
|
|
|
|
SetGravity( UTIL_ScaleForGravity( 560 ) ); // slightly lower gravity
|
|
SetFriction( 0.97 ); //used in SatchelTouch to slow us when sliding
|
|
SetSequence( LookupSequence( "onback" ) );
|
|
|
|
m_bInAir = false;
|
|
m_flNextBounceSoundTime = 0;
|
|
|
|
m_vLastPosition = vec3_origin;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input :
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
void CSatchelCharge::SatchelUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
|
{
|
|
SetThink( &CBaseGrenade::Detonate );
|
|
SetNextThink( gpGlobals->curtime );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input :
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
void CSatchelCharge::SatchelTouch( CBaseEntity *pOther )
|
|
{
|
|
Assert( pOther );
|
|
if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS) || GetWaterLevel() > 0 )
|
|
return;
|
|
|
|
StudioFrameAdvance( );
|
|
|
|
UpdateSlideSound();
|
|
|
|
if ( m_bInAir )
|
|
{
|
|
BounceSound();
|
|
m_bInAir = false;
|
|
}
|
|
|
|
// add a bit of static friction
|
|
SetAbsVelocity( GetAbsVelocity() * GetFriction() );
|
|
SetLocalAngularVelocity( GetLocalAngularVelocity() * GetFriction() );
|
|
}
|
|
|
|
void CSatchelCharge::UpdateSlideSound( void )
|
|
{
|
|
// HACKHACK - On ground isn't always set, so look for ground underneath
|
|
trace_t tr;
|
|
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,10), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
|
|
|
|
if ( !(tr.fraction < 1.0) )
|
|
{
|
|
m_bInAir = true;
|
|
}
|
|
}
|
|
|
|
void CSatchelCharge::SatchelThink( void )
|
|
{
|
|
UpdateSlideSound();
|
|
|
|
StudioFrameAdvance( );
|
|
SetNextThink( gpGlobals->curtime + 0.1f );
|
|
|
|
if (!IsInWorld())
|
|
{
|
|
UTIL_Remove( this );
|
|
return;
|
|
}
|
|
|
|
Vector vecNewVel = GetAbsVelocity();
|
|
|
|
if ( GetWaterLevel() > 0 )
|
|
{
|
|
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
|
|
vecNewVel *= 0.8;
|
|
SetLocalAngularVelocity( GetLocalAngularVelocity() * 0.9 );
|
|
|
|
vecNewVel.z = 0;
|
|
SetGravity( -0.2 );
|
|
}
|
|
else if ( GetWaterLevel() == 0 )
|
|
{
|
|
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_SLIDE );
|
|
|
|
SetGravity( 1.0 );
|
|
}
|
|
|
|
SetAbsVelocity( vecNewVel );
|
|
}
|
|
|
|
void CSatchelCharge::Precache( void )
|
|
{
|
|
PrecacheModel( SATCHEL_CHARGE_MODEL );
|
|
PrecacheScriptSound( "SatchelCharge.Bounce" );
|
|
}
|
|
|
|
void CSatchelCharge::BounceSound( void )
|
|
{
|
|
if ( gpGlobals->curtime > m_flNextBounceSoundTime )
|
|
{
|
|
EmitSound( "SatchelCharge.Bounce" );
|
|
|
|
m_flNextBounceSoundTime = gpGlobals->curtime + 0.1;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructor
|
|
// Input :
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
CSatchelCharge::CSatchelCharge(void)
|
|
{
|
|
m_vLastPosition.Init();
|
|
}
|
|
|
|
#endif
|