//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Implements the tripmine grenade. // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "beam_shared.h" #include "shake.h" #include "grenade_tripmine.h" #include "vstdlib/random.h" #include "engine/IEngineSound.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern const char* g_pModelNameLaser; ConVar sk_plr_dmg_tripmine ( "sk_plr_dmg_tripmine","0"); ConVar sk_npc_dmg_tripmine ( "sk_npc_dmg_tripmine","0"); ConVar sk_tripmine_radius ( "sk_tripmine_radius","0"); LINK_ENTITY_TO_CLASS( npc_tripmine, CTripmineGrenade ); BEGIN_DATADESC( CTripmineGrenade ) DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ), DEFINE_FIELD( m_flPowerUp, FIELD_TIME ), DEFINE_FIELD( m_vecDir, FIELD_VECTOR ), DEFINE_FIELD( m_vecEnd, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_flBeamLength, FIELD_FLOAT ), DEFINE_FIELD( m_pBeam, FIELD_CLASSPTR ), DEFINE_FIELD( m_posOwner, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_angleOwner, FIELD_VECTOR ), // Function Pointers DEFINE_FUNCTION( WarningThink ), DEFINE_FUNCTION( PowerupThink ), DEFINE_FUNCTION( BeamBreakThink ), DEFINE_FUNCTION( DelayDeathThink ), END_DATADESC() CTripmineGrenade::CTripmineGrenade() { m_vecDir.Init(); m_vecEnd.Init(); m_posOwner.Init(); m_angleOwner.Init(); } void CTripmineGrenade::Spawn( void ) { Precache( ); // motor SetMoveType( MOVETYPE_FLY ); SetSolid( SOLID_BBOX ); AddSolidFlags( FSOLID_NOT_SOLID ); SetModel( "models/Weapons/w_slam.mdl" ); m_flCycle = 0; m_nBody = 3; m_flDamage = sk_plr_dmg_tripmine.GetFloat(); m_DmgRadius = sk_tripmine_radius.GetFloat(); ResetSequenceInfo( ); m_flPlaybackRate = 0; UTIL_SetSize(this, Vector( -4, -4, -2), Vector(4, 4, 2)); m_flPowerUp = gpGlobals->curtime + 2.0; SetThink( PowerupThink ); SetNextThink( gpGlobals->curtime + 0.2 ); m_takedamage = DAMAGE_YES; m_iHealth = 1; EmitSound( "TripmineGrenade.Charge" ); // Tripmine sits at 90 on wall so rotate back to get m_vecDir QAngle angles = GetLocalAngles(); angles.x -= 90; AngleVectors( angles, &m_vecDir ); m_vecEnd = GetLocalOrigin() + m_vecDir * 2048; } void CTripmineGrenade::Precache( void ) { PrecacheModel("models/Weapons/w_slam.mdl"); PrecacheScriptSound( "TripmineGrenade.Charge" ); PrecacheScriptSound( "TripmineGrenade.PowerUp" ); PrecacheScriptSound( "TripmineGrenade.StopSound" ); PrecacheScriptSound( "TripmineGrenade.Activate" ); PrecacheScriptSound( "TripmineGrenade.ShootRope" ); PrecacheScriptSound( "TripmineGrenade.Hook" ); } void CTripmineGrenade::WarningThink( void ) { // set to power up SetThink( PowerupThink ); SetNextThink( gpGlobals->curtime + 1.0f ); } void CTripmineGrenade::PowerupThink( void ) { if (gpGlobals->curtime > m_flPowerUp) { MakeBeam( ); RemoveSolidFlags( FSOLID_NOT_SOLID ); m_bIsLive = true; // play enabled sound EmitSound( "TripmineGrenade.PowerUp" );; } SetNextThink( gpGlobals->curtime + 0.1f ); } void CTripmineGrenade::KillBeam( void ) { if ( m_pBeam ) { UTIL_Remove( m_pBeam ); m_pBeam = NULL; } } void CTripmineGrenade::MakeBeam( void ) { trace_t tr; UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); m_flBeamLength = tr.fraction; // If I hit a living thing, send the beam through me so it turns on briefly // and then blows the living thing up CBaseEntity *pEntity = tr.m_pEnt; CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity ); // Draw length is not the beam length if entity is in the way float drawLength = tr.fraction; if (pBCC) { SetOwnerEntity( pBCC ); UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); m_flBeamLength = tr.fraction; SetOwnerEntity( NULL ); } // set to follow laser spot SetThink( BeamBreakThink ); // Delay first think slightly so beam has time // to appear if person right in front of it SetNextThink( gpGlobals->curtime + 1.0f ); Vector vecTmpEnd = GetLocalOrigin() + m_vecDir * 2048 * drawLength; m_pBeam = CBeam::BeamCreate( g_pModelNameLaser, 1.0 ); m_pBeam->PointEntInit( vecTmpEnd, this ); m_pBeam->SetColor( 0, 214, 198 ); m_pBeam->SetScrollRate( 25.6 ); m_pBeam->SetBrightness( 64 ); } void CTripmineGrenade::BeamBreakThink( void ) { // See if I can go solid yet (has dropper moved out of way?) if (IsSolidFlagSet( FSOLID_NOT_SOLID )) { trace_t tr; Vector vUpBit = GetAbsOrigin(); vUpBit.z += 5.0; UTIL_TraceEntity( this, GetAbsOrigin(), vUpBit, MASK_SHOT, &tr ); if ( !tr.startsolid && (tr.fraction == 1.0) ) { RemoveSolidFlags( FSOLID_NOT_SOLID ); } } trace_t tr; // NOT MASK_SHOT because we want only simple hit boxes UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); // ALERT( at_console, "%f : %f\n", tr.flFraction, m_flBeamLength ); // respawn detect. if ( !m_pBeam ) { MakeBeam( ); if ( tr.m_pEnt ) m_hOwner = tr.m_pEnt; // reset owner too } CBaseEntity *pEntity = tr.m_pEnt; CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity ); if (pBCC || fabs( m_flBeamLength - tr.fraction ) > 0.001) { m_iHealth = 0; Event_Killed( CTakeDamageInfo( (CBaseEntity*)m_hOwner, this, 100, GIB_NORMAL ) ); return; } SetNextThink( gpGlobals->curtime + 0.1f ); } int CTripmineGrenade::OnTakeDamage_Alive( const CTakeDamageInfo &info ) { if (gpGlobals->curtime < m_flPowerUp && info.GetDamage() < m_iHealth) { // disable // Create( "weapon_tripmine", GetLocalOrigin() + m_vecDir * 24, GetAngles() ); SetThink( SUB_Remove ); SetNextThink( gpGlobals->curtime + 0.1f ); KillBeam(); return FALSE; } return BaseClass::OnTakeDamage_Alive( info ); } //----------------------------------------------------------------------------- // Purpose: // Input : // Output : //----------------------------------------------------------------------------- void CTripmineGrenade::Event_Killed( const CTakeDamageInfo &info ) { m_takedamage = DAMAGE_NO; SetThink( DelayDeathThink ); SetNextThink( gpGlobals->curtime + 0.5 ); EmitSound( "TripmineGrenade.StopSound" ); } void CTripmineGrenade::DelayDeathThink( void ) { KillBeam(); trace_t tr; UTIL_TraceLine ( GetAbsOrigin() + m_vecDir * 8, GetAbsOrigin() - m_vecDir * 64, MASK_SOLID, this, COLLISION_GROUP_NONE, & tr); UTIL_ScreenShake( GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START ); Explode( &tr, DMG_BLAST ); }