//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "basehlcombatweapon.h" #include "engine/IEngineSound.h" #include "npcevent.h" #include "in_buttons.h" #include "antlion_maker.h" #include "grenade_bugbait.h" #include "gamestats.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" // // Bug Bait Weapon // class CWeaponBugBait : public CBaseHLCombatWeapon { DECLARE_CLASS( CWeaponBugBait, CBaseHLCombatWeapon ); public: DECLARE_SERVERCLASS(); CWeaponBugBait( void ); void Spawn( void ); void FallInit( void ); int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); void Drop( const Vector &vecVelocity ); void BugbaitStickyTouch( CBaseEntity *pOther ); void OnPickedUp( CBaseCombatCharacter *pNewOwner ); bool Deploy( void ); bool Holster( CBaseCombatWeapon *pSwitchingTo ); void ItemPostFrame( void ); void Precache( void ); void PrimaryAttack( void ); void SecondaryAttack( void ); void ThrowGrenade( CBasePlayer *pPlayer ); bool HasAnyAmmo( void ) { return true; } bool Reload( void ); void SetSporeEmitterState( bool state = true ); bool ShouldDisplayHUDHint() { return true; } DECLARE_DATADESC(); protected: bool m_bDrawBackFinished; bool m_bRedraw; bool m_bEmitSpores; EHANDLE m_hSporeTrail; }; IMPLEMENT_SERVERCLASS_ST(CWeaponBugBait, DT_WeaponBugBait) END_SEND_TABLE() LINK_ENTITY_TO_CLASS( weapon_bugbait, CWeaponBugBait ); #ifndef HL2MP PRECACHE_WEAPON_REGISTER( weapon_bugbait ); #endif BEGIN_DATADESC( CWeaponBugBait ) DEFINE_FIELD( m_hSporeTrail, FIELD_EHANDLE ), DEFINE_FIELD( m_bRedraw, FIELD_BOOLEAN ), DEFINE_FIELD( m_bEmitSpores, FIELD_BOOLEAN ), DEFINE_FIELD( m_bDrawBackFinished, FIELD_BOOLEAN ), DEFINE_FUNCTION( BugbaitStickyTouch ), END_DATADESC() //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CWeaponBugBait::CWeaponBugBait( void ) { m_bDrawBackFinished = false; m_bRedraw = false; m_hSporeTrail = NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponBugBait::Spawn( void ) { BaseClass::Spawn(); // Increase the bugbait's pickup volume. It spawns inside the antlion guard's body, // and playtesters seem to be wary about moving into the body. SetSize( Vector( -4, -4, -4), Vector(4, 4, 4) ); CollisionProp()->UseTriggerBounds( true, 100 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponBugBait::FallInit( void ) { // Bugbait shouldn't be physics, because it musn't roll/move away from it's spawnpoint. // The game will break if the player can't pick it up, so it must stay still. SetModel( GetWorldModel() ); VPhysicsDestroyObject(); SetMoveType( MOVETYPE_FLYGRAVITY ); SetSolid( SOLID_BBOX ); AddSolidFlags( FSOLID_TRIGGER ); SetPickupTouch(); SetThink( &CBaseCombatWeapon::FallThink ); SetNextThink( gpGlobals->curtime + 0.1f ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponBugBait::Precache( void ) { BaseClass::Precache(); UTIL_PrecacheOther( "npc_grenade_bugbait" ); PrecacheScriptSound( "Weapon_Bugbait.Splat" ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponBugBait::Drop( const Vector &vecVelocity ) { BaseClass::Drop( vecVelocity ); // On touch, stick & stop moving. Increase our thinktime a bit so we don't stomp the touch for a bit SetNextThink( gpGlobals->curtime + 3.0 ); SetTouch( &CWeaponBugBait::BugbaitStickyTouch ); m_hSporeTrail = SporeExplosion::CreateSporeExplosion(); if ( m_hSporeTrail ) { SporeExplosion *pSporeExplosion = (SporeExplosion *)m_hSporeTrail.Get(); QAngle angles; VectorAngles( Vector(0,0,1), angles ); pSporeExplosion->SetAbsAngles( angles ); pSporeExplosion->SetAbsOrigin( GetAbsOrigin() ); pSporeExplosion->SetParent( this ); pSporeExplosion->m_flSpawnRate = 16.0f; pSporeExplosion->m_flParticleLifetime = 0.5f; pSporeExplosion->SetRenderColor( 0.0f, 0.5f, 0.25f, 0.15f ); pSporeExplosion->m_flStartSize = 32; pSporeExplosion->m_flEndSize = 48; pSporeExplosion->m_flSpawnRadius = 4; pSporeExplosion->SetLifetime( 9999 ); } } //----------------------------------------------------------------------------- // Purpose: Stick to the world when we touch it //----------------------------------------------------------------------------- void CWeaponBugBait::BugbaitStickyTouch( CBaseEntity *pOther ) { if ( !pOther->IsWorld() ) return; // Stop moving, wait for pickup SetMoveType( MOVETYPE_NONE ); SetThink( NULL ); SetPickupTouch(); } //----------------------------------------------------------------------------- // Purpose: // Input : *pPicker - //----------------------------------------------------------------------------- void CWeaponBugBait::OnPickedUp( CBaseCombatCharacter *pNewOwner ) { BaseClass::OnPickedUp( pNewOwner ); if ( m_hSporeTrail ) { UTIL_Remove( m_hSporeTrail ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponBugBait::PrimaryAttack( void ) { if ( m_bRedraw ) return; CBaseCombatCharacter *pOwner = GetOwner(); if ( pOwner == NULL ) return; CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); if ( pPlayer == NULL ) return; SendWeaponAnim( ACT_VM_HAULBACK ); m_flTimeWeaponIdle = FLT_MAX; m_flNextPrimaryAttack = FLT_MAX; m_iPrimaryAttacks++; gamestats->Event_WeaponFired( pPlayer, true, GetClassname() ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponBugBait::SecondaryAttack( void ) { // Squeeze! CPASAttenuationFilter filter( this ); EmitSound( filter, entindex(), "Weapon_Bugbait.Splat" ); if ( CGrenadeBugBait::ActivateBugbaitTargets( GetOwner(), GetAbsOrigin(), true ) == false ) { g_AntlionMakerManager.BroadcastFollowGoal( GetOwner() ); } SendWeaponAnim( ACT_VM_SECONDARYATTACK ); m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); if ( pOwner ) { m_iSecondaryAttacks++; gamestats->Event_WeaponFired( pOwner, false, GetClassname() ); } } //----------------------------------------------------------------------------- // Purpose: // Input : *pPlayer - //----------------------------------------------------------------------------- void CWeaponBugBait::ThrowGrenade( CBasePlayer *pPlayer ) { Vector vForward, vRight, vUp, vThrowPos, vThrowVel; pPlayer->EyeVectors( &vForward, &vRight, &vUp ); vThrowPos = pPlayer->EyePosition(); vThrowPos += vForward * 18.0f; vThrowPos += vRight * 12.0f; pPlayer->GetVelocity( &vThrowVel, NULL ); vThrowVel += vForward * 1000; CGrenadeBugBait *pGrenade = BugBaitGrenade_Create( vThrowPos, vec3_angle, vThrowVel, QAngle(600,random->RandomInt(-1200,1200),0), pPlayer ); if ( pGrenade != NULL ) { // If the shot is clear to the player, give the missile a grace period trace_t tr; UTIL_TraceLine( pPlayer->EyePosition(), pPlayer->EyePosition() + ( vForward * 128 ), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction == 1.0 ) { pGrenade->SetGracePeriod( 0.1f ); } } m_bRedraw = true; } //----------------------------------------------------------------------------- // Purpose: // Input : *pEvent - // *pOperator - //----------------------------------------------------------------------------- void CWeaponBugBait::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) { CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); switch( pEvent->event ) { case EVENT_WEAPON_SEQUENCE_FINISHED: m_bDrawBackFinished = true; break; case EVENT_WEAPON_THROW: ThrowGrenade( pOwner ); break; default: BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); break; } } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CWeaponBugBait::Reload( void ) { if ( ( m_bRedraw ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) ) { //Redraw the weapon SendWeaponAnim( ACT_VM_DRAW ); //Update our times m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); //Mark this as done m_bRedraw = false; } return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponBugBait::ItemPostFrame( void ) { CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); if ( pOwner == NULL ) return; // See if we're cocked and ready to throw if ( m_bDrawBackFinished ) { if ( ( pOwner->m_nButtons & IN_ATTACK ) == false ) { SendWeaponAnim( ACT_VM_THROW ); m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); m_bDrawBackFinished = false; } } else { //See if we're attacking if ( ( pOwner->m_nButtons & IN_ATTACK ) && ( m_flNextPrimaryAttack < gpGlobals->curtime ) ) { PrimaryAttack(); } else if ( ( pOwner->m_nButtons & IN_ATTACK2 ) && ( m_flNextSecondaryAttack < gpGlobals->curtime ) ) { SecondaryAttack(); } } if ( m_bRedraw ) { if ( IsViewModelSequenceFinished() ) { Reload(); } } WeaponIdle(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CWeaponBugBait::Deploy( void ) { CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); if ( pOwner == NULL ) return false; /* if ( m_hSporeTrail == NULL ) { m_hSporeTrail = SporeTrail::CreateSporeTrail(); m_hSporeTrail->m_bEmit = true; m_hSporeTrail->m_flSpawnRate = 100.0f; m_hSporeTrail->m_flParticleLifetime = 2.0f; m_hSporeTrail->m_flStartSize = 1.0f; m_hSporeTrail->m_flEndSize = 4.0f; m_hSporeTrail->m_flSpawnRadius = 8.0f; m_hSporeTrail->m_vecEndColor = Vector( 0, 0, 0 ); CBaseViewModel *vm = pOwner->GetViewModel(); if ( vm != NULL ) { m_hSporeTrail->FollowEntity( vm ); } } */ m_bRedraw = false; m_bDrawBackFinished = false; return BaseClass::Deploy(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CWeaponBugBait::Holster( CBaseCombatWeapon *pSwitchingTo ) { m_bRedraw = false; m_bDrawBackFinished = false; return BaseClass::Holster( pSwitchingTo ); } //----------------------------------------------------------------------------- // Purpose: // Input : true - //----------------------------------------------------------------------------- void CWeaponBugBait::SetSporeEmitterState( bool state ) { m_bEmitSpores = state; }