//========= 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