source-engine/game/server/hl1/hl1_weapon_tripmine.cpp
2022-04-16 12:05:19 +03:00

574 lines
14 KiB
C++

//========= 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 );
}