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.
600 lines
14 KiB
600 lines
14 KiB
//========= 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
|
|
|