|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Tripmine
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "npcevent.h"
#include "hl1_basecombatweapon_shared.h"
#include "basecombatcharacter.h"
#include "ai_basenpc.h"
#include "player.h"
#include "gamerules.h"
#include "in_buttons.h"
#include "soundent.h"
#include "game.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "hl1_player.h"
#include "hl1_basegrenade.h"
#include "beam_shared.h"
extern ConVar sk_plr_dmg_tripmine;
//-----------------------------------------------------------------------------
// CWeaponTripMine
//-----------------------------------------------------------------------------
#define TRIPMINE_MODEL "models/w_tripmine.mdl"
class CWeaponTripMine : public CBaseHL1CombatWeapon { DECLARE_CLASS( CWeaponTripMine, CBaseHL1CombatWeapon ); public:
CWeaponTripMine( void );
void Spawn( void ); void Precache( void ); void Equip( CBaseCombatCharacter *pOwner ); void PrimaryAttack( void ); void WeaponIdle( void ); bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
DECLARE_SERVERCLASS(); DECLARE_DATADESC();
private: int m_iGroundIndex; int m_iPickedUpIndex; };
LINK_ENTITY_TO_CLASS( weapon_tripmine, CWeaponTripMine );
PRECACHE_WEAPON_REGISTER( weapon_tripmine );
IMPLEMENT_SERVERCLASS_ST( CWeaponTripMine, DT_WeaponTripMine ) END_SEND_TABLE()
BEGIN_DATADESC( CWeaponTripMine ) END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWeaponTripMine::CWeaponTripMine( void ) { m_bReloadsSingly = false; m_bFiresUnderwater = true; }
void CWeaponTripMine::Spawn( void ) { BaseClass::Spawn();
m_iWorldModelIndex = m_iGroundIndex; SetModel( TRIPMINE_MODEL );
SetActivity( ACT_TRIPMINE_GROUND ); ResetSequenceInfo( ); m_flPlaybackRate = 0;
if ( !g_pGameRules->IsDeathmatch() ) { UTIL_SetSize( this, Vector(-16, -16, 0), Vector(16, 16, 28) ); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponTripMine::Precache( void ) { BaseClass::Precache();
m_iGroundIndex = PrecacheModel( TRIPMINE_MODEL ); m_iPickedUpIndex = PrecacheModel( GetWorldModel() );
UTIL_PrecacheOther( "monster_tripmine" ); }
void CWeaponTripMine::Equip( CBaseCombatCharacter *pOwner ) { m_iWorldModelIndex = m_iPickedUpIndex;
BaseClass::Equip( pOwner ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponTripMine::PrimaryAttack( void ) { CHL1_Player *pPlayer = ToHL1Player( GetOwner() ); if ( !pPlayer ) { return; }
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ) return;
Vector vecAiming = pPlayer->GetAutoaimVector( 0 ); Vector vecSrc = pPlayer->Weapon_ShootPosition( );
trace_t tr;
UTIL_TraceLine( vecSrc, vecSrc + vecAiming * 64, MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction < 1.0 ) { CBaseEntity *pEntity = tr.m_pEnt; if ( pEntity && !( pEntity->GetFlags() & FL_CONVEYOR ) ) { QAngle angles; VectorAngles( tr.plane.normal, angles );
CBaseEntity::Create( "monster_tripmine", tr.endpos + tr.plane.normal * 2, angles, pPlayer );
pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
pPlayer->SetAnimation( PLAYER_ATTACK1 ); if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ) { if ( !pPlayer->SwitchToNextBestWeapon( pPlayer->GetActiveWeapon() ) ) Holster(); } else { SendWeaponAnim( ACT_VM_DRAW ); SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) ); }
m_flNextPrimaryAttack = gpGlobals->curtime + 0.5; SetWeaponIdleTime( gpGlobals->curtime ); // MO curtime correct ?
} } else { SetWeaponIdleTime( m_flTimeWeaponIdle = gpGlobals->curtime + random->RandomFloat( 10, 15 ) ); } }
void CWeaponTripMine::WeaponIdle( void ) { CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( !pPlayer ) { return; }
if ( !HasWeaponIdleTimeElapsed() ) return;
int iAnim;
if ( random->RandomFloat( 0, 1 ) <= 0.75 ) { iAnim = ACT_VM_IDLE; } else { iAnim = ACT_VM_FIDGET; }
SendWeaponAnim( iAnim ); }
bool CWeaponTripMine::Holster( CBaseCombatWeapon *pSwitchingTo ) { CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); if ( !pPlayer ) { return false; }
if ( !BaseClass::Holster( pSwitchingTo ) ) { return false; }
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ) { SetThink( &CWeaponTripMine::DestroyItem ); SetNextThink( gpGlobals->curtime + 0.1 ); }
pPlayer->SetNextAttack( gpGlobals->curtime + 0.5 );
return true; }
//-----------------------------------------------------------------------------
// CTripmineGrenade
//-----------------------------------------------------------------------------
#define TRIPMINE_BEAM_SPRITE "sprites/laserbeam.vmt"
class CTripmineGrenade : public CHL1BaseGrenade { DECLARE_CLASS( CTripmineGrenade, CHL1BaseGrenade ); public: CTripmineGrenade(); void Spawn( void ); void Precache( void );
int OnTakeDamage_Alive( const CTakeDamageInfo &info ); void WarningThink( void ); void PowerupThink( void ); void BeamBreakThink( void ); void DelayDeathThink( void ); void Event_Killed( const CTakeDamageInfo &info );
DECLARE_DATADESC();
private: void MakeBeam( void ); void KillBeam( void );
private: float m_flPowerUp; Vector m_vecDir; Vector m_vecEnd; float m_flBeamLength;
CHandle<CBaseEntity> m_hRealOwner; CHandle<CBeam> m_hBeam;
CHandle<CBaseEntity> m_hStuckOn; Vector m_posStuckOn; QAngle m_angStuckOn;
int m_iLaserModel; };
LINK_ENTITY_TO_CLASS( monster_tripmine, CTripmineGrenade );
BEGIN_DATADESC( CTripmineGrenade ) 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_hBeam, FIELD_EHANDLE ),
DEFINE_FIELD( m_hRealOwner, FIELD_EHANDLE ), DEFINE_FIELD( m_hStuckOn, FIELD_EHANDLE ), DEFINE_FIELD( m_posStuckOn, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_angStuckOn, FIELD_VECTOR ), //DEFINE_FIELD( m_iLaserModel, FIELD_INTEGER ),
// Function Pointers
DEFINE_THINKFUNC( WarningThink ), DEFINE_THINKFUNC( PowerupThink ), DEFINE_THINKFUNC( BeamBreakThink ), DEFINE_THINKFUNC( DelayDeathThink ), END_DATADESC()
CTripmineGrenade::CTripmineGrenade() { m_vecDir.Init(); m_vecEnd.Init(); }
void CTripmineGrenade::Spawn( void ) { Precache( ); // motor
SetMoveType( MOVETYPE_FLY ); SetSolid( SOLID_BBOX ); AddSolidFlags( FSOLID_NOT_SOLID ); SetModel( TRIPMINE_MODEL );
// Don't collide with the player (the beam will still be tripped by one, however)
SetCollisionGroup( COLLISION_GROUP_WEAPON );
SetCycle( 0 ); SetSequence( SelectWeightedSequence( ACT_TRIPMINE_WORLD ) ); ResetSequenceInfo(); m_flPlaybackRate = 0; UTIL_SetSize( this, Vector( -8, -8, -8), Vector(8, 8, 8) );
m_flDamage = sk_plr_dmg_tripmine.GetFloat(); m_DmgRadius = m_flDamage * 2.5;
if ( m_spawnflags & 1 ) { // power up quickly
m_flPowerUp = gpGlobals->curtime + 1.0; } else { // power up in 2.5 seconds
m_flPowerUp = gpGlobals->curtime + 2.5; } SetThink( &CTripmineGrenade::PowerupThink ); SetNextThink( gpGlobals->curtime + 0.2 );
m_takedamage = DAMAGE_YES;
m_iHealth = 1;
if ( GetOwnerEntity() != NULL ) { // play deploy sound
EmitSound( "TripmineGrenade.Deploy" ); EmitSound( "TripmineGrenade.Charge" );
m_hRealOwner = GetOwnerEntity(); } AngleVectors( GetAbsAngles(), &m_vecDir ); m_vecEnd = GetAbsOrigin() + m_vecDir * MAX_TRACE_LENGTH; }
void CTripmineGrenade::Precache( void ) { PrecacheModel( TRIPMINE_MODEL ); m_iLaserModel = PrecacheModel( TRIPMINE_BEAM_SPRITE );
PrecacheScriptSound( "TripmineGrenade.Deploy" ); PrecacheScriptSound( "TripmineGrenade.Charge" ); PrecacheScriptSound( "TripmineGrenade.Activate" );
}
void CTripmineGrenade::WarningThink( void ) { // set to power up
SetThink( &CTripmineGrenade::PowerupThink ); SetNextThink( gpGlobals->curtime + 1.0f ); }
void CTripmineGrenade::PowerupThink( void ) { if ( m_hStuckOn == NULL ) { trace_t tr; CBaseEntity *pOldOwner = GetOwnerEntity();
// don't explode if the player is standing in front of the laser
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + m_vecDir * 32, MASK_SHOT, NULL, COLLISION_GROUP_NONE, &tr );
if( tr.m_pEnt && pOldOwner && ( tr.m_pEnt == pOldOwner ) && pOldOwner->IsPlayer() ) { m_flPowerUp += 0.1; //delay the arming
SetNextThink( gpGlobals->curtime + 0.1f ); return; }
// find out what we've been stuck on
SetOwnerEntity( NULL ); UTIL_TraceLine( GetAbsOrigin() + m_vecDir * 8, GetAbsOrigin() - m_vecDir * 32, MASK_SHOT, pOldOwner, COLLISION_GROUP_NONE, &tr );
if ( tr.startsolid ) { SetOwnerEntity( pOldOwner ); m_flPowerUp += 0.1; SetNextThink( gpGlobals->curtime + 0.1f ); return; } if ( tr.fraction < 1.0 ) { SetOwnerEntity( tr.m_pEnt ); m_hStuckOn = tr.m_pEnt; m_posStuckOn = m_hStuckOn->GetAbsOrigin(); m_angStuckOn = m_hStuckOn->GetAbsAngles(); } else { // somehow we've been deployed on nothing, or something that was there, but now isn't.
// remove ourselves
StopSound( "TripmineGrenade.Deploy" ); StopSound( "TripmineGrenade.Charge" ); SetThink( &CBaseEntity::SUB_Remove ); SetNextThink( gpGlobals->curtime + 0.1f ); // ALERT( at_console, "WARNING:Tripmine at %.0f, %.0f, %.0f removed\n", pev->origin.x, pev->origin.y, pev->origin.z );
KillBeam(); return; } } else if ( (m_posStuckOn != m_hStuckOn->GetAbsOrigin()) || (m_angStuckOn != m_hStuckOn->GetAbsAngles()) ) { // what we were stuck on has moved, or rotated. Create a tripmine weapon and drop to ground
StopSound( "TripmineGrenade.Deploy" ); StopSound( "TripmineGrenade.Charge" ); CBaseEntity *pMine = Create( "weapon_tripmine", GetAbsOrigin() + m_vecDir * 24, GetAbsAngles() ); pMine->AddSpawnFlags( SF_NORESPAWN );
SetThink( &CBaseEntity::SUB_Remove ); SetNextThink( gpGlobals->curtime + 0.1f ); KillBeam(); return; }
if ( gpGlobals->curtime > m_flPowerUp ) { MakeBeam( ); RemoveSolidFlags( FSOLID_NOT_SOLID );
m_bIsLive = true;
// play enabled sound
EmitSound( "TripmineGrenade.Activate" ); }
SetNextThink( gpGlobals->curtime + 0.1f ); }
void CTripmineGrenade::KillBeam( void ) { if ( m_hBeam ) { UTIL_Remove( m_hBeam ); m_hBeam = NULL; } }
void CTripmineGrenade::MakeBeam( void ) { trace_t tr;
UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
m_flBeamLength = tr.fraction;
// set to follow laser spot
SetThink( &CTripmineGrenade::BeamBreakThink );
SetNextThink( gpGlobals->curtime + 1.0f );
Vector vecTmpEnd = GetAbsOrigin() + m_vecDir * MAX_TRACE_LENGTH * m_flBeamLength;
m_hBeam = CBeam::BeamCreate( TRIPMINE_BEAM_SPRITE, 1.0 ); m_hBeam->PointEntInit( vecTmpEnd, this ); m_hBeam->SetColor( 0, 214, 198 ); m_hBeam->SetScrollRate( 25.5 ); m_hBeam->SetBrightness( 64 ); m_hBeam->AddSpawnFlags( SF_BEAM_TEMPORARY ); // so it won't save and come back to haunt us later..
}
void CTripmineGrenade::BeamBreakThink( void ) { bool bBlowup = false; 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_hBeam ) { MakeBeam();
trace_t stuckOnTrace; Vector forward; GetVectors( &forward, NULL, NULL );
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - forward * 12.0f, MASK_SOLID, this, COLLISION_GROUP_NONE, &stuckOnTrace );
if ( stuckOnTrace.m_pEnt ) { m_hStuckOn = stuckOnTrace.m_pEnt; // reset stuck on ent too
} }
CBaseEntity *pEntity = tr.m_pEnt; CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity );
if ( pBCC || fabs( m_flBeamLength - tr.fraction ) > 0.001 ) { bBlowup = true; } else { if ( m_hStuckOn == NULL ) bBlowup = true; else if ( m_posStuckOn != m_hStuckOn->GetAbsOrigin() ) bBlowup = true; else if ( m_angStuckOn != m_hStuckOn->GetAbsAngles() ) bBlowup = true; }
if ( bBlowup ) { SetOwnerEntity( m_hRealOwner ); m_iHealth = 0; Event_Killed( CTakeDamageInfo( this, m_hRealOwner, 100, GIB_NORMAL ) ); return; }
SetNextThink( gpGlobals->curtime + 0.1 ); } /*
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( &CBaseEntity::SUB_Remove ); SetNextThink( gpGlobals->curtime + 0.1f ); KillBeam(); return 0; } return BaseClass::OnTakeDamage_Alive( info ); }*/
void CTripmineGrenade::Event_Killed( const CTakeDamageInfo &info ) { m_takedamage = DAMAGE_NO;
if ( info.GetAttacker() && ( info.GetAttacker()->GetFlags() & FL_CLIENT ) ) { // some client has destroyed this mine, he'll get credit for any kills
SetOwnerEntity( info.GetAttacker() ); }
SetThink( &CTripmineGrenade::DelayDeathThink ); SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1, 0.3 ) );
StopSound( "TripmineGrenade.Charge" ); }
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);
Explode( &tr, DMG_BLAST ); }
|