sqwarmed/sdk_src/game/server/swarm/asw_aoegrenade_projectile.cpp

465 lines
13 KiB
C++

#include "cbase.h"
#include "asw_aoegrenade_projectile.h"
#include "asw_marine.h"
#include "Sprite.h"
#include "SpriteTrail.h"
#include "soundent.h"
#include "te_effect_dispatch.h"
#include "IEffects.h"
#include "decals.h"
#include "asw_shareddefs.h"
#include "particle_parse.h"
#include "triggers.h"
#include "world.h"
#include "asw_game_resource.h"
#include "asw_marine_resource.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define AOEGREN_MODEL "models/items/Mine/mine.mdl"
//-----------------------------------------------------------------------------
// Purpose: SendProxy that converts the AOEGren list UtlVector to entindices
//-----------------------------------------------------------------------------
void SendProxy_AOEGrenList( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
{
CASW_AOEGrenade_Projectile *pAOEGren = (CASW_AOEGrenade_Projectile*)pStruct;
// If this assertion fails, then SendProxyArrayLength_AOEGrenArray must have failed.
Assert( iElement < pAOEGren->m_hAOETargets.Count() );
CBaseEntity *pEnt = pAOEGren->m_hAOETargets[iElement].Get();
EHANDLE hOther = pEnt;
SendProxy_EHandleToInt( pProp, pStruct, &hOther, pOut, iElement, objectID );
}
int SendProxyArrayLength_AOEGrenArray( const void *pStruct, int objectID )
{
CASW_AOEGrenade_Projectile *pAOEGren = (CASW_AOEGrenade_Projectile*)pStruct;
return pAOEGren->m_hAOETargets.Count();
}
class CASW_AOEGrenade_TouchTrigger : public CBaseTrigger
{
DECLARE_CLASS( CASW_AOEGrenade_TouchTrigger, CBaseTrigger );
public:
CASW_AOEGrenade_TouchTrigger() {}
void Spawn( void )
{
BaseClass::Spawn();
// SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS means "only marines" in ASW
AddSpawnFlags( SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS );
InitTrigger();
}
virtual void StartTouch( CBaseEntity *pEntity )
{
CASW_AOEGrenade_Projectile *pParent = dynamic_cast<CASW_AOEGrenade_Projectile *>(GetOwnerEntity());
if ( pParent )
{
pParent->StartTouch( pEntity );
}
}
virtual void EndTouch( CBaseEntity *pEntity )
{
CASW_AOEGrenade_Projectile *pParent = dynamic_cast<CASW_AOEGrenade_Projectile *>(GetOwnerEntity());
if ( pParent )
{
pParent->EndTouch( pEntity );
}
}
};
LINK_ENTITY_TO_CLASS( asw_aoegrenade_touch_trigger, CASW_AOEGrenade_TouchTrigger );
BEGIN_DATADESC( CASW_AOEGrenade_Projectile )
DEFINE_FUNCTION( AOEGrenadeTouch ),
DEFINE_FUNCTION( AOEGrenadeThink ),
// Fields
DEFINE_FIELD( m_pMainGlow, FIELD_EHANDLE ),
DEFINE_FIELD( m_pGlowTrail, FIELD_EHANDLE ),
DEFINE_FIELD( m_flTimeBurnOut, FIELD_TIME ),
DEFINE_FIELD( m_flTimePulse, FIELD_TIME ),
DEFINE_FIELD( m_flLastDoAOE, FIELD_TIME ),
DEFINE_FIELD( m_flScale, FIELD_FLOAT ),
DEFINE_FIELD( m_nBounces, FIELD_INTEGER ),
DEFINE_FIELD( m_flDuration, FIELD_FLOAT ),
DEFINE_FIELD( m_bFading, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bSettled, FIELD_BOOLEAN ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CASW_AOEGrenade_Projectile, DT_ASW_AOEGrenade_Projectile )
SendPropArray2(
SendProxyArrayLength_AOEGrenArray,
SendPropInt( "aoegren_array_element", 0, SIZEOF_IGNORE, NUM_NETWORKED_EHANDLE_BITS, SPROP_UNSIGNED, SendProxy_AOEGrenList),
MAX_PLAYERS,
0,
"aoetarget_array"
),
SendPropFloat( SENDINFO( m_flTimeBurnOut ), 0, SPROP_NOSCALE ),
//SendPropFloat( SENDINFO( m_flTimePulse ), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO( m_flScale ), 0, SPROP_NOSCALE ),
SendPropBool( SENDINFO( m_bSettled ) ),
SendPropFloat( SENDINFO( m_flRadius ), 0, SPROP_NOSCALE ),
END_SEND_TABLE()
// aoegrenades maintain a linked list of themselves, for quick checking for autoaim
CASW_AOEGrenade_Projectile* g_pHeadAOEGrenade = NULL;
CASW_AOEGrenade_Projectile::CASW_AOEGrenade_Projectile()
{
m_flScale = 1.0f;
m_flNextDamage = gpGlobals->curtime;
m_lifeState = LIFE_ALIVE;
m_iHealth = 100;
m_flDuration = 20.0f;
m_flRadius = 128.0f;
SetModelName( MAKE_STRING( AOEGREN_MODEL ) );
}
CASW_AOEGrenade_Projectile::~CASW_AOEGrenade_Projectile( void )
{
if ( m_hTouchTrigger.Get() )
{
UTIL_Remove( m_hTouchTrigger );
}
int iSize = m_hAOETargets.Count();
for ( int i = iSize - 1; i >= 0; i-- )
{
EHANDLE hEntity = m_hAOETargets[i];
StopAOE( hEntity.Get() );
}
}
int CASW_AOEGrenade_Projectile::Restore( IRestore &restore )
{
int result = BaseClass::Restore( restore );
return result;
}
void CASW_AOEGrenade_Projectile::Spawn( void )
{
Precache( );
SetModel( GetModelName().ToCStr() );
UTIL_SetSize( this, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ) );
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_SOLID );
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
//m_iDamage = 0;
m_takedamage = DAMAGE_NO;
SetFriction( 0.6f );
SetGravity( UTIL_ScaleForGravity( GetGrenadeGravity() ) );
m_flTimeBurnOut = gpGlobals->curtime + GetBurnDuration();
m_flTimePulse = -1;
AddEffects( EF_NOSHADOW|EF_NORECEIVESHADOW );
AddFlag( FL_OBJECT );
SetCollisionGroup( ASW_COLLISION_GROUP_IGNORE_NPCS );
// Tumble in air
QAngle vecAngVelocity( 0, random->RandomFloat ( -100, -500 ), 0 );
SetLocalAngularVelocity( vecAngVelocity );
SetTouch( &CASW_AOEGrenade_Projectile::AOEGrenadeTouch );
SetThink( &CASW_AOEGrenade_Projectile::AOEGrenadeThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
void CASW_AOEGrenade_Projectile::Precache( void )
{
PrecacheModel( GetModelName().ToCStr() );
PrecacheScriptSound( "ASW_BuffGrenade.GrenadeActivate" );
PrecacheScriptSound( "ASW_BuffGrenade.ActiveLoop" );
PrecacheScriptSound( "ASW_BuffGrenade.StartBuff" );
PrecacheScriptSound( "ASW_BuffGrenade.BuffLoop" );
PrecacheModel( "swarm/sprites/whiteglow1.vmt" );
PrecacheModel( "swarm/sprites/greylaser1.vmt" );
BaseClass::Precache();
}
const Vector& CASW_AOEGrenade_Projectile::GetEffectOrigin()
{
static Vector s_vecEffectPos;
Vector forward, right, up;
AngleVectors(GetAbsAngles(), &forward, &right, &up);
s_vecEffectPos = GetAbsOrigin() + up * 5;
return s_vecEffectPos;
}
unsigned int CASW_AOEGrenade_Projectile::PhysicsSolidMaskForEntity( void ) const
{
return MASK_NPCSOLID;
}
void CASW_AOEGrenade_Projectile::AOEGrenadeTouch( CBaseEntity *pOther )
{
Assert( pOther );
if ( !pOther->IsSolid() )
return;
// don't touch npcs
if ( pOther->IsNPC() )
return;
// Change our flight characteristics
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
SetGravity( UTIL_ScaleForGravity( 640 ) );
m_nBounces++;
//After the first bounce, smacking into whoever fired the flare is fair game
//SetOwnerEntity( NULL );
// Slow down
Vector vecNewVelocity = GetAbsVelocity();
vecNewVelocity.x *= 0.8f;
vecNewVelocity.y *= 0.8f;
SetAbsVelocity( vecNewVelocity );
//Stopped?
if ( GetAbsVelocity().Length() < 128.0f )
{
LayFlat();
SetAbsVelocity( vec3_origin );
SetMoveType( MOVETYPE_NONE );
//RemoveSolidFlags( FSOLID_NOT_SOLID );
//AddSolidFlags( FSOLID_TRIGGER );
//SetTouch( &CASW_Flare_Projectile::FlareBurnTouch );
m_hTouchTrigger = CBaseEntity::Create( "asw_aoegrenade_touch_trigger", GetAbsOrigin(), vec3_angle, this );
int radius = GetEffectRadius();
UTIL_SetSize( m_hTouchTrigger.Get(), Vector(-radius,-radius,-radius), Vector(radius,radius,radius) );
m_hTouchTrigger->SetSolid( SOLID_BBOX );
// call StartTouch() on all marines in radius
CASW_Game_Resource *pGameResource = ASWGameResource();
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
{
CASW_Marine_Resource *pMR = pGameResource->GetMarineResource(i);
if (!pMR)
continue;
CASW_Marine *pMarine = pMR->GetMarineEntity();
if (!pMarine)
continue;
if( GetDistanceToEntity( pMarine ) < m_flRadius )
{
CBaseEntity *pEntity = pMarine;
StartTouch( pEntity );
}
}
//NDebugOverlay::Box( GetAbsOrigin(), Vector(-radius,-radius,-radius), Vector(radius,radius,radius), 0, 0, 255, 200, 5.5 );
Assert( m_hTouchTrigger.Get() );
m_bSettled = true;
}
}
void CASW_AOEGrenade_Projectile::AOEGrenadeThink( void )
{
if ( m_flTimeBurnOut != -1.0f )
{
//Burned out
if ( m_flTimeBurnOut < gpGlobals->curtime )
{
OnBurnout();
UTIL_Remove( this );
return;
}
}
//Act differently underwater
if ( GetWaterLevel() > 1 )
{
UTIL_Bubbles( GetEffectOrigin() + Vector( -2, -2, -2 ), GetEffectOrigin() + Vector( 2, 2, 2 ), 1 );
}
if ( !m_bSettled && GetAbsVelocity().Length() < 128.0f && GetGroundEntity() )
{
AOEGrenadeTouch( GetGroundEntity() );
}
GiveEffectToEntitesInRadius();
//Next update
SetNextThink( gpGlobals->curtime + 0.1f );
}
void CASW_AOEGrenade_Projectile::GiveEffectToEntitesInRadius( void )
{
bool bDoAOE = ( gpGlobals->curtime > m_flLastDoAOE + GetDoAOEDelayTime() );
// for each player in touching list
int iSize = m_hTouchingEntities.Count();
for ( int i = iSize-1; i >= 0; i-- )
{
EHANDLE hEntity = m_hTouchingEntities[i];
CBaseEntity *pEntity = hEntity.Get();
if ( !pEntity )
continue;
if ( !IsAOETarget( pEntity ) )
{
// if they're on the list, but we aren't buffing them, do so
StartAOE( pEntity );
}
if ( bDoAOE )
{
DoAOE( pEntity );
}
}
if ( bDoAOE )
{
m_flLastDoAOE = gpGlobals->curtime;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CASW_AOEGrenade_Projectile::StartTouch( CBaseEntity *pEntity )
{
if ( !ShouldTouchEntity( pEntity ) )
return;
// add to touching entities
EHANDLE hEntity = pEntity;
if( m_hTouchingEntities.Find( hEntity ) == -1 )
{
m_hTouchingEntities.AddToTail( hEntity );
// try to start healing them
StartAOE( pEntity );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CASW_AOEGrenade_Projectile::EndTouch( CBaseEntity *pEntity )
{
// remove from touching entities
EHANDLE hEntity = pEntity;
m_hTouchingEntities.FindAndRemove( hEntity );
// remove from healing list
StopAOE( pEntity );
}
//-----------------------------------------------------------------------------
// Purpose: Add the buff to this target
//-----------------------------------------------------------------------------
void CASW_AOEGrenade_Projectile::StartAOE( CBaseEntity *pEntity )
{
AddAOETarget( pEntity );
}
//-----------------------------------------------------------------------------
// Purpose: Stop healing this target
//-----------------------------------------------------------------------------
bool CASW_AOEGrenade_Projectile::StopAOE( CBaseEntity *pEntity )
{
bool bFound = false;
EHANDLE hEntity = pEntity;
bFound = m_hAOETargets.FindAndRemove( hEntity );
NetworkStateChanged();
return bFound;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CASW_AOEGrenade_Projectile::AddAOETarget( CBaseEntity *pEntity )
{
Assert( pEntity );
// add to tail
EHANDLE hEntity = pEntity;
m_hAOETargets.AddToTail( hEntity );
NetworkStateChanged();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CASW_AOEGrenade_Projectile::RemoveAOETarget( CBaseEntity *pEntity )
{
Assert( pEntity );
// remove
EHANDLE hEntity = pEntity;
m_hAOETargets.FindAndRemove( hEntity );
NetworkStateChanged();
}
//-----------------------------------------------------------------------------
// Purpose: Are we buffing this target already
//-----------------------------------------------------------------------------
bool CASW_AOEGrenade_Projectile::IsAOETarget( CBaseEntity *pEntity )
{
Assert( pEntity );
EHANDLE hEntity = pEntity;
return m_hAOETargets.HasElement( hEntity );
}
void CASW_AOEGrenade_Projectile::LayFlat()
{
QAngle angFacing = GetAbsAngles();
//if (angFacing[PITCH] > 0 && angFacing[PITCH] < 180.0f)
angFacing[PITCH] = 0; //90
//else
// angFacing[PITCH] = 270;
SetAbsAngles(angFacing);
//Msg("Laying flat to %f, %f, %f\n", angFacing[PITCH], angFacing[YAW], angFacing[ROLL]);
}
//extern ConVar asw_aoegrenade_autoaim_radius;
void CASW_AOEGrenade_Projectile::DrawDebugGeometryOverlays()
{
/*
// draw arrows showing the extent of our autoaim
for (int i=0;i<360;i+=45)
{
float flBaseSize = 10;
float flHeight = asw_aoegrenade_autoaim_radius.GetFloat();
Vector vBasePos = GetAbsOrigin() + Vector( 0, 0, 5 );
QAngle angles( 0, 0, 0 );
Vector vForward, vRight, vUp;
angles[YAW] = i;
AngleVectors( angles, &vForward, &vRight, &vUp );
NDebugOverlay::Triangle( vBasePos+vRight*flBaseSize/2, vBasePos-vRight*flBaseSize/2, vBasePos+vForward*flHeight, 0, 255, 0, 255, false, 10 );
}
*/
BaseClass::DrawDebugGeometryOverlays();
}