You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
573 lines
14 KiB
573 lines
14 KiB
//========= 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 ); |
|
}
|
|
|