747 lines
20 KiB
C++
747 lines
20 KiB
C++
#include "cbase.h"
|
|
#ifdef CLIENT_DLL
|
|
#define CBaseAnimating C_BaseAnimating
|
|
#define CASW_Remote_Turret C_ASW_Remote_Turret
|
|
#include "c_asw_player.h"
|
|
#include "c_asw_marine.h"
|
|
#include "fx.h"
|
|
#include "soundenvelope.h"
|
|
#include "c_te_effect_dispatch.h"
|
|
#include "c_user_message_register.h"
|
|
#include "c_asw_fx.h"
|
|
#define CASW_Marine C_ASW_Marine
|
|
#else
|
|
#include "EntityFlame.h"
|
|
#include "asw_player.h"
|
|
#include "asw_marine.h"
|
|
#include "ai_network.h"
|
|
#include "ndebugoverlay.h"
|
|
#endif
|
|
#include "asw_gamerules.h"
|
|
#include "asw_util_shared.h"
|
|
#include "igamemovement.h"
|
|
#include "in_buttons.h"
|
|
#include "asw_remote_turret_shared.h"
|
|
#include "ammodef.h"
|
|
#include "fmtstr.h"
|
|
#include "effect_dispatch_data.h"
|
|
#include "particle_parse.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( ASW_Remote_Turret, DT_ASW_Remote_Turret )
|
|
|
|
BEGIN_NETWORK_TABLE( CASW_Remote_Turret, DT_ASW_Remote_Turret )
|
|
#ifdef CLIENT_DLL
|
|
RecvPropEHandle( RECVINFO( m_hUser ) ),
|
|
RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ),
|
|
RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ),
|
|
RecvPropBool( RECVINFO( m_bUpsideDown ) ),
|
|
RecvPropQAngles (RECVINFO(m_angDefault)),
|
|
RecvPropQAngles (RECVINFO(m_angViewLimit)),
|
|
#else
|
|
SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
|
|
SendPropEHandle( SENDINFO( m_hUser ) ),
|
|
SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11, SPROP_CHANGES_OFTEN ),
|
|
SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11, SPROP_CHANGES_OFTEN ),
|
|
SendPropBool( SENDINFO( m_bUpsideDown ) ),
|
|
SendPropQAngles (SENDINFO(m_angDefault), 10 ),
|
|
SendPropQAngles (SENDINFO(m_angViewLimit), 10 ),
|
|
#endif
|
|
END_NETWORK_TABLE()
|
|
|
|
LINK_ENTITY_TO_CLASS( asw_remote_turret, CASW_Remote_Turret );
|
|
PRECACHE_WEAPON_REGISTER(asw_remote_turret);
|
|
|
|
#define ASW_REMOTE_TURRET_MODEL "models/swarm/SentryGun/remoteturret.mdl"
|
|
|
|
ConVar asw_turret_fire_rate("asw_turret_fire_rate", "0.1f", FCVAR_CHEAT | FCVAR_REPLICATED, "Firing rate of remote controlled turrets");
|
|
ConVar asw_turret_turn_rate("asw_turret_turn_rate", "50.0f", FCVAR_CHEAT | FCVAR_REPLICATED, "Turning rate of remote controlled turrets");
|
|
extern ConVar asw_weapon_max_shooting_distance;
|
|
|
|
#ifndef CLIENT_DLL
|
|
|
|
BEGIN_DATADESC( CASW_Remote_Turret )
|
|
DEFINE_KEYFIELD(m_bUpsideDown, FIELD_BOOLEAN, "UpsideDown"),
|
|
DEFINE_KEYFIELD( m_angViewLimit, FIELD_VECTOR, "viewlimits" ),
|
|
END_DATADESC()
|
|
|
|
#else
|
|
ConVar asw_turret_debug_limits("asw_turret_debug_limits", "0", FCVAR_CHEAT, "Prints debug info about turret turning limits");
|
|
#endif // not client
|
|
|
|
CASW_Remote_Turret::CASW_Remote_Turret()
|
|
{
|
|
m_angEyeAngles.Init();
|
|
m_iAmmoType = GetAmmoDef()->Index("ASW_AG");
|
|
m_fNextFireTime = 0;
|
|
#ifdef CLIENT_DLL
|
|
m_pLoopingSound = NULL;
|
|
m_bLastPlaySound = false;
|
|
#else
|
|
m_hUser = false;
|
|
#endif
|
|
}
|
|
|
|
|
|
CASW_Remote_Turret::~CASW_Remote_Turret()
|
|
{
|
|
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
|
|
void CASW_Remote_Turret::Spawn()
|
|
{
|
|
BaseClass::Spawn();
|
|
Precache();
|
|
SetModel(ASW_REMOTE_TURRET_MODEL);
|
|
SetMoveType( MOVETYPE_NONE );
|
|
SetSolid(SOLID_BBOX);
|
|
SetCollisionGroup( COLLISION_GROUP_NONE );
|
|
m_takedamage = DAMAGE_NO;
|
|
m_iHealth = 100;
|
|
m_iMaxHealth = m_iHealth;
|
|
|
|
m_angDefault = GetAbsAngles();
|
|
if (m_angViewLimit.Get()[0] == 0)
|
|
m_angViewLimit.GetForModify()[0] = 60;
|
|
if (m_angViewLimit.Get()[1] == 0)
|
|
m_angViewLimit.GetForModify()[1] = 60;
|
|
}
|
|
|
|
void CASW_Remote_Turret::Precache()
|
|
{
|
|
PrecacheModel(ASW_REMOTE_TURRET_MODEL);
|
|
PrecacheScriptSound("ASW_Sentry.Fire");
|
|
PrecacheScriptSound("ASW_Sentry.Turn");
|
|
|
|
BaseClass::Precache();
|
|
}
|
|
|
|
#endif // not client
|
|
|
|
void CASW_Remote_Turret::SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
|
|
{
|
|
// only care about facing and firing
|
|
move->m_vecViewAngles = ucmd->viewangles;
|
|
// todo: clamp facing angle here?
|
|
move->m_nButtons = ucmd->buttons;
|
|
}
|
|
|
|
// asw
|
|
QAngle g_angTurret = QAngle(0,0,0);
|
|
bool g_bLastControllingTurret = false;
|
|
void CASW_Remote_Turret::SmoothTurretAngle(QAngle &ang)
|
|
{
|
|
float dt = MIN( 0.2, gpGlobals->frametime );
|
|
|
|
ang[YAW] = ASW_ClampYaw( asw_turret_turn_rate.GetFloat(), m_angEyeAngles[YAW], ang[YAW], dt );
|
|
ang[PITCH] = ASW_ClampYaw( asw_turret_turn_rate.GetFloat()*0.8f, m_angEyeAngles[PITCH], ang[PITCH], dt );
|
|
|
|
m_angEyeAngles = ang;//GetMarine()->EyeAngles()[YAW];
|
|
}
|
|
|
|
void CASW_Remote_Turret::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData )
|
|
{
|
|
QAngle angTemp = RealEyeAngles();
|
|
m_angEyeAngles = angTemp;
|
|
SetLocalAngles(m_angEyeAngles);
|
|
|
|
// check for firing
|
|
bool bAttack1, bAttack2, bReload;
|
|
GetButtons(bAttack1, bAttack2, bReload);
|
|
if (bAttack1 && gpGlobals->curtime >= m_fNextFireTime)
|
|
{
|
|
FireTurret(pPlayer);
|
|
}
|
|
}
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
const QAngle& CASW_Remote_Turret::RealEyeAngles()
|
|
{
|
|
bool bLocallyControlled = false;
|
|
if (m_hUser.Get())
|
|
{
|
|
C_ASW_Marine* pMarine = dynamic_cast<C_ASW_Marine*>(m_hUser.Get());
|
|
if (pMarine && pMarine->IsInhabited() && pMarine->GetCommander() == C_ASW_Player::GetLocalASWPlayer())
|
|
bLocallyControlled = true;
|
|
}
|
|
|
|
if (bLocallyControlled)
|
|
{
|
|
return C_ASW_Player::GetLocalASWPlayer()->EyeAngles();
|
|
}
|
|
else
|
|
{
|
|
return m_angEyeAngles;
|
|
}
|
|
}
|
|
|
|
const QAngle& CASW_Remote_Turret::EyeAngles()
|
|
{
|
|
return m_angEyeAngles;
|
|
}
|
|
|
|
const QAngle& CASW_Remote_Turret::GetRenderAngles()
|
|
{
|
|
bool bLocallyControlled = false;
|
|
if (m_hUser.Get())
|
|
{
|
|
C_ASW_Marine* pMarine = dynamic_cast<C_ASW_Marine*>(m_hUser.Get());
|
|
if (pMarine && pMarine->IsInhabited() && pMarine->GetCommander() == C_ASW_Player::GetLocalASWPlayer())
|
|
bLocallyControlled = true;
|
|
}
|
|
|
|
static QAngle ang;
|
|
ang = m_angEyeAngles;
|
|
if (bLocallyControlled)
|
|
{
|
|
ang = C_ASW_Player::GetLocalASWPlayer()->EyeAngles();
|
|
}
|
|
if (m_bUpsideDown)
|
|
ang[ROLL] = 180;
|
|
return ang;
|
|
}
|
|
|
|
int CASW_Remote_Turret::GetMuzzleAttachment( void )
|
|
{
|
|
return LookupAttachment( "muzzle" );
|
|
}
|
|
|
|
float CASW_Remote_Turret::GetMuzzleFlashScale( void )
|
|
{
|
|
return 1.5f;
|
|
}
|
|
|
|
void CASW_Remote_Turret::ProcessMuzzleFlashEvent()
|
|
{
|
|
// attach muzzle flash particle system effect
|
|
int iAttachment = GetMuzzleAttachment();
|
|
|
|
if ( iAttachment > 0 )
|
|
{
|
|
//#ifndef _DEBUG
|
|
float flScale = GetMuzzleFlashScale();
|
|
FX_MuzzleEffectAttached( flScale, GetRefEHandle(), iAttachment, NULL, false );
|
|
//#endif
|
|
}
|
|
BaseClass::ProcessMuzzleFlashEvent();
|
|
}
|
|
|
|
void CASW_Remote_Turret::OnRestore()
|
|
{
|
|
BaseClass::OnRestore();
|
|
SoundInit();
|
|
}
|
|
|
|
void CASW_Remote_Turret::OnDataChanged( DataUpdateType_t updateType )
|
|
{
|
|
SoundInit();
|
|
|
|
if ( updateType == DATA_UPDATE_CREATED )
|
|
{
|
|
SetNextClientThink(gpGlobals->curtime);
|
|
}
|
|
|
|
BaseClass::OnDataChanged( updateType );
|
|
}
|
|
|
|
void CASW_Remote_Turret::UpdateOnRemove()
|
|
{
|
|
BaseClass::UpdateOnRemove();
|
|
SoundShutdown();
|
|
}
|
|
|
|
void CASW_Remote_Turret::SoundInit()
|
|
{
|
|
// play an engine start sound!!
|
|
CPASAttenuationFilter filter( this );
|
|
|
|
m_pLoopingSound = CSoundEnvelopeController::GetController().SoundCreate( filter, entindex(), "ASW_Sentry.Turn" );
|
|
CSoundEnvelopeController::GetController().Play( m_pLoopingSound, 0.0, 100 );
|
|
}
|
|
|
|
void CASW_Remote_Turret::SoundShutdown()
|
|
{
|
|
if ( m_pLoopingSound )
|
|
{
|
|
CSoundEnvelopeController::GetController().SoundDestroy( m_pLoopingSound );
|
|
m_pLoopingSound = NULL;
|
|
}
|
|
}
|
|
|
|
void CASW_Remote_Turret::ClientThink(void)
|
|
{
|
|
float yawdiff = abs(AngleDiff(m_LastAngle[YAW], m_angEyeAngles[YAW]));
|
|
float pitchdiff = abs(AngleDiff(m_LastAngle[PITCH], m_angEyeAngles[PITCH]));
|
|
//bool bPlaySound = !AnglesAreEqual(m_LastAngle[YAW], m_angEyeAngles[YAW],1) ||
|
|
//!AnglesAreEqual(m_LastAngle[PITCH], m_angEyeAngles[PITCH],1);
|
|
bool bPlaySound = yawdiff > 0 || pitchdiff > 0;
|
|
|
|
//Msg("[C:%d:%f]CASW_Remote_Turret::ClientThink pos = %f, %f, %f\n", entindex(), gpGlobals->curtime, GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z);
|
|
|
|
//Msg("Lastyaw = %f current yaw=%f d=%f ", m_LastAngle[YAW], m_angEyeAngles[YAW], yawdiff);
|
|
//Msg("Lastp = %f currentp=%f d=%f\n", m_LastAngle[PITCH], m_angEyeAngles[PITCH], pitchdiff);
|
|
if (m_pLoopingSound && bPlaySound != m_bLastPlaySound)
|
|
{
|
|
if (bPlaySound)
|
|
{
|
|
//Msg("Lastyaw = %f current yaw=%f eq=%d ", m_LastAngle[YAW], m_angEyeAngles[YAW], AnglesAreEqual(m_LastAngle[YAW], m_angEyeAngles[YAW],1));
|
|
//Msg("Lastp = %f currentp=%f eq=%d\n", m_LastAngle[PITCH], m_angEyeAngles[PITCH], AnglesAreEqual(m_LastAngle[PITCH], m_angEyeAngles[PITCH],1));
|
|
float flVolume = CSoundEnvelopeController::GetController().SoundGetVolume( m_pLoopingSound );
|
|
if ( flVolume != 1.0f )
|
|
{
|
|
//Msg("Fading in\n");
|
|
CSoundEnvelopeController::GetController().SoundChangeVolume( m_pLoopingSound, 1.0, 0.01f );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Msg("Fading out\n");
|
|
CSoundEnvelopeController::GetController().SoundChangeVolume( m_pLoopingSound, 0.0, 0.01f );
|
|
}
|
|
}
|
|
m_bLastPlaySound = bPlaySound;
|
|
m_LastAngle = m_angEyeAngles;
|
|
|
|
if (m_hUser.Get())
|
|
{
|
|
SetNextClientThink(gpGlobals->curtime + 0.1f);
|
|
}
|
|
else
|
|
{
|
|
SetNextClientThink(gpGlobals->curtime + 1.0f);
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
const QAngle& CASW_Remote_Turret::RealEyeAngles()
|
|
{
|
|
if (m_hUser.Get())
|
|
{
|
|
CASW_Marine* pMarine = dynamic_cast<CASW_Marine*>(m_hUser.Get());
|
|
if (pMarine && pMarine->IsInhabited() && pMarine->GetCommander())
|
|
return pMarine->GetCommander()->EyeAngles();
|
|
}
|
|
return m_angEyeAngles;
|
|
}
|
|
|
|
const QAngle& CASW_Remote_Turret::EyeAngles()
|
|
{
|
|
|
|
return m_angEyeAngles;
|
|
}
|
|
#endif
|
|
|
|
Vector CASW_Remote_Turret::GetTurretCamPosition()
|
|
{
|
|
int iAttachment = LookupAttachment( "camera" );
|
|
if ( iAttachment > 0 )
|
|
{
|
|
Vector camOrigin;
|
|
QAngle camAngles;
|
|
|
|
if ( GetAttachment( iAttachment, camOrigin, camAngles ) )
|
|
{
|
|
return camOrigin;
|
|
}
|
|
}
|
|
//Msg("not using camera attach pos\n");
|
|
return GetAbsOrigin();
|
|
}
|
|
|
|
Vector CASW_Remote_Turret::GetTurretMuzzlePosition()
|
|
{
|
|
Vector vecMuzzleOffset(41.92, 0, 8.0);
|
|
Vector vecMuzzleOffsetTransformed(0,0,0);
|
|
matrix3x4_t matrix;
|
|
QAngle angFacing = EyeAngles();
|
|
if (m_bUpsideDown)
|
|
angFacing[ROLL] += 180;
|
|
AngleMatrix( angFacing, matrix );
|
|
VectorTransform(vecMuzzleOffset, matrix, vecMuzzleOffsetTransformed);
|
|
return vecMuzzleOffsetTransformed + GetAbsOrigin();
|
|
|
|
// using the attachment returns different results on the client/server, for some reason
|
|
/*
|
|
int iAttachment = LookupAttachment( "muzzle" );
|
|
if ( iAttachment > 0 )
|
|
{
|
|
Vector camOrigin;
|
|
QAngle camAngles;
|
|
|
|
if ( GetAttachment( iAttachment, camOrigin, camAngles ) )
|
|
{
|
|
return camOrigin;
|
|
}
|
|
}
|
|
return GetAbsOrigin();*/
|
|
}
|
|
|
|
void CASW_Remote_Turret::GetButtons(bool& bAttack1, bool& bAttack2, bool& bReload )
|
|
{
|
|
if (m_hUser.Get())
|
|
{
|
|
CASW_Marine* pMarine = dynamic_cast<CASW_Marine*>(m_hUser.Get());
|
|
if (pMarine && pMarine->IsInhabited() && pMarine->GetCommander())
|
|
{
|
|
bAttack1 = !!(pMarine->GetCommander()->m_nButtons & IN_ATTACK);
|
|
bAttack2 = !!(pMarine->GetCommander()->m_nButtons & IN_ATTACK2);
|
|
bReload = !!(pMarine->GetCommander()->m_nButtons & IN_RELOAD);
|
|
return;
|
|
}
|
|
}
|
|
|
|
bAttack1 = false;
|
|
bAttack2 = false;
|
|
bReload = false;
|
|
}
|
|
|
|
void CASW_Remote_Turret::FireTurret(CBasePlayer *pPlayer)
|
|
{
|
|
if (!pPlayer)
|
|
return;
|
|
|
|
Vector vecShootOrigin, vecShootDir;
|
|
vecShootOrigin = GetTurretMuzzlePosition();
|
|
#ifdef CLIENT_DLL
|
|
QAngle angFacing = EyeAngles();
|
|
#else
|
|
autoaim_params_t params;
|
|
params.m_fScale = 2.0f;
|
|
params.m_fMaxDist = asw_weapon_max_shooting_distance.GetFloat();
|
|
|
|
QAngle angFacing = EyeAngles() + AutoaimDeflection(vecShootOrigin, EyeAngles(), params);
|
|
|
|
#endif
|
|
AngleVectors(angFacing, &vecShootDir);
|
|
|
|
//Msg("%s: Firing bullets from %s ", IsServer() ? "S" : "C", VecToString(vecShootOrigin));
|
|
//Msg(" at angle %s\n", VecToString(angFacing));
|
|
|
|
FireBulletsInfo_t info(1, vecShootOrigin, vecShootDir, GetBulletSpread(), asw_weapon_max_shooting_distance.GetFloat(), m_iAmmoType);
|
|
info.m_flDamage = GetSentryDamage();
|
|
info.m_pAttacker = this;
|
|
info.m_iTracerFreq = 1;
|
|
FireBullets(info);
|
|
EmitSound("ASW_Sentry.Fire");
|
|
|
|
CEffectData data;
|
|
data.m_vOrigin = GetAbsOrigin();
|
|
CPASFilter filter( data.m_vOrigin );
|
|
DispatchParticleEffect( "muzzle_sentrygun", PATTACH_POINT_FOLLOW, this, "muzzle", false, -1, &filter );
|
|
|
|
//DoMuzzleFlash();
|
|
#ifdef CLIENT_DLL
|
|
//FX_MicroExplosion(vecShootOrigin, Vector(0,0,1));
|
|
#else
|
|
//NDebugOverlay::Box( vecShootOrigin, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0, 255, 0, 0, asw_turret_fire_rate.GetFloat() );
|
|
#endif
|
|
|
|
m_fNextFireTime = gpGlobals->curtime + asw_turret_fire_rate.GetFloat();
|
|
}
|
|
|
|
CBaseEntity* CASW_Remote_Turret::GetUser()
|
|
{
|
|
return m_hUser.Get();
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
|
|
QAngle CASW_Remote_Turret::AutoaimDeflection( Vector &vecSrc, const QAngle &eyeAngles, autoaim_params_t ¶ms )
|
|
{
|
|
float bestscore;
|
|
float score;
|
|
Vector bestdir;
|
|
CBaseEntity *bestent;
|
|
trace_t tr;
|
|
Vector v_forward, v_right, v_up;
|
|
|
|
AngleVectors( eyeAngles, &v_forward, &v_right, &v_up );
|
|
|
|
// try all possible entities
|
|
bestdir = v_forward;
|
|
bestscore = 0.0f;
|
|
bestent = NULL;
|
|
|
|
//Reset this data
|
|
params.m_bOnTargetNatural = false;
|
|
|
|
CBaseEntity *pIgnore = NULL;
|
|
CTraceFilterSkipTwoEntities traceFilter( this, pIgnore, COLLISION_GROUP_NONE );
|
|
|
|
UTIL_TraceLine( vecSrc, vecSrc + bestdir * MAX_COORD_FLOAT, MASK_SHOT, &traceFilter, &tr );
|
|
|
|
CBaseEntity *pEntHit = tr.m_pEnt;
|
|
|
|
if ( pEntHit && pEntHit->m_takedamage != DAMAGE_NO && pEntHit->GetHealth() > 0 )
|
|
{
|
|
// don't look through water
|
|
if (!((GetWaterLevel() != 3 && pEntHit->GetWaterLevel() == 3) || (GetWaterLevel() == 3 && pEntHit->GetWaterLevel() == 0)))
|
|
{
|
|
if( pEntHit->GetFlags() & FL_AIMTARGET )
|
|
{
|
|
// Player is already on target naturally, don't autoaim.
|
|
// Fill out the autoaim_params_t struct, though.
|
|
params.m_hAutoAimEntity.Set(pEntHit);
|
|
params.m_vecAutoAimDir = bestdir;
|
|
params.m_vecAutoAimPoint = tr.endpos;
|
|
params.m_bAutoAimAssisting = false;
|
|
params.m_bOnTargetNatural = true;
|
|
}
|
|
|
|
return vec3_angle;
|
|
}
|
|
}
|
|
|
|
int count = AimTarget_ListCount();
|
|
if ( count )
|
|
{
|
|
CBaseEntity **pList = (CBaseEntity **)stackalloc( sizeof(CBaseEntity *) * count );
|
|
AimTarget_ListCopy( pList, count );
|
|
|
|
for ( int i = 0; i < count; i++ )
|
|
{
|
|
Vector center;
|
|
Vector dir;
|
|
CBaseEntity *pEntity = pList[i];
|
|
|
|
// Don't shoot yourself
|
|
if ( pEntity == this )
|
|
continue;
|
|
|
|
if (!pEntity->IsAlive() || !pEntity->edict() )
|
|
continue;
|
|
|
|
//if ( !g_pGameRules->ShouldAutoAim( this, pEntity->edict() ) )
|
|
//continue;
|
|
|
|
// don't look through water
|
|
if ((GetWaterLevel() != 3 && pEntity->GetWaterLevel() == 3) || (GetWaterLevel() == 3 && pEntity->GetWaterLevel() == 0))
|
|
continue;
|
|
|
|
// don't autoaim at marines
|
|
if( pEntity->Classify() == CLASS_ASW_MARINE )
|
|
continue;
|
|
|
|
// Don't autoaim at the noisy bodytarget, this makes the autoaim crosshair wobble.
|
|
//center = pEntity->BodyTarget( vecSrc, false );
|
|
center = pEntity->WorldSpaceCenter();
|
|
|
|
dir = (center - vecSrc);
|
|
|
|
float dist = dir.Length2D();
|
|
VectorNormalize( dir );
|
|
|
|
// Skip if out of range.
|
|
if( dist > params.m_fMaxDist )
|
|
continue;
|
|
|
|
float dot = DotProduct (dir, v_forward );
|
|
|
|
// make sure it's in front of the player
|
|
if( dot < 0 )
|
|
continue;
|
|
|
|
score = GetAutoaimScore(vecSrc, v_forward, pEntity->GetAutoAimCenter(), pEntity, params.m_fScale);
|
|
|
|
if( score <= bestscore )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UTIL_TraceLine( vecSrc, center, MASK_SHOT, &traceFilter, &tr );
|
|
|
|
if (tr.fraction != 1.0 && tr.m_pEnt != pEntity )
|
|
{
|
|
// Msg( "hit %s, can't see %s\n", STRING( tr.u.ent->classname ), STRING( pEdict->classname ) );
|
|
continue;
|
|
}
|
|
|
|
// This is the best candidate so far.
|
|
bestscore = score;
|
|
bestent = pEntity;
|
|
bestdir = dir;
|
|
}
|
|
if ( bestent )
|
|
{
|
|
QAngle bestang;
|
|
|
|
VectorAngles( bestdir, bestang );
|
|
|
|
bestang -= eyeAngles;
|
|
|
|
// Autoaim detected a target for us. Aim automatically at its bodytarget.
|
|
params.m_hAutoAimEntity.Set(bestent);
|
|
params.m_vecAutoAimDir = bestdir;
|
|
params.m_vecAutoAimPoint = bestent->BodyTarget( vecSrc, false );
|
|
params.m_bAutoAimAssisting = true;
|
|
|
|
return bestang;
|
|
}
|
|
}
|
|
|
|
return QAngle( 0, 0, 0 );
|
|
}
|
|
|
|
float CASW_Remote_Turret::GetAutoaimScore( const Vector &eyePosition, const Vector &viewDir, const Vector &vecTarget, CBaseEntity *pTarget, float fScale )
|
|
{
|
|
float radiusSqr;
|
|
float targetRadiusSqr = Square( (pTarget->GetAutoAimRadius() * fScale) );
|
|
|
|
Vector vecNearestPoint = PointOnLineNearestPoint( eyePosition, eyePosition + viewDir * 8192, vecTarget );
|
|
Vector vecDiff = vecTarget - vecNearestPoint;
|
|
|
|
radiusSqr = vecDiff.LengthSqr();
|
|
|
|
if( radiusSqr <= targetRadiusSqr )
|
|
{
|
|
float score;
|
|
|
|
score = 1.0f - (radiusSqr / targetRadiusSqr);
|
|
|
|
Assert( score >= 0.0f && score <= 1.0f );
|
|
return score;
|
|
}
|
|
|
|
// 0 means no score- doesn't qualify for autoaim.
|
|
return 0.0f;
|
|
}
|
|
|
|
int CASW_Remote_Turret::UpdateTransmitState()
|
|
{
|
|
//if ( GetUser() )
|
|
{
|
|
return SetTransmitState( FL_EDICT_ALWAYS );
|
|
}
|
|
//else
|
|
//{
|
|
//return SetTransmitState( FL_EDICT_DONTSEND );
|
|
//}
|
|
}
|
|
|
|
// always send this entity to players (for now...)
|
|
int CASW_Remote_Turret::ShouldTransmit( const CCheckTransmitInfo *pInfo )
|
|
{
|
|
return FL_EDICT_ALWAYS;
|
|
}
|
|
|
|
void CASW_Remote_Turret::StopUsingTurret()
|
|
{
|
|
m_hUser = NULL;
|
|
UpdateTransmitState();
|
|
}
|
|
|
|
void CASW_Remote_Turret::StartedUsingTurret(CBaseEntity* pUser)
|
|
{
|
|
m_hUser = pUser;
|
|
UpdateTransmitState();
|
|
}
|
|
#else
|
|
// client
|
|
// clamp view angles
|
|
void CASW_Remote_Turret::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
|
|
{
|
|
QAngle angMax = m_angDefault.Get() + m_angViewLimit.Get();
|
|
QAngle angMin = m_angDefault.Get() - m_angViewLimit.Get();
|
|
|
|
// limit all 3 axes of rotation
|
|
if (asw_turret_debug_limits.GetBool()) Msg("Limiting turret:");
|
|
for (int i=0;i<3;i++)
|
|
{
|
|
float fDiff = UTIL_AngleDiff(pCmd->viewangles[i], m_angDefault.Get()[i]);
|
|
float fMinDiff = UTIL_AngleDiff(angMin[i], m_angDefault.Get()[i]);
|
|
float fMaxDiff = UTIL_AngleDiff(angMax[i], m_angDefault.Get()[i]);
|
|
if (asw_turret_debug_limits.GetBool())
|
|
Msg("%d: fdiff=%f mindiff=%f maxdiff=%f min=%f max=%f ang=%f def=%f", i, fDiff, fMinDiff, fMaxDiff,
|
|
angMin[i], angMax[i], pCmd->viewangles[i], m_angDefault.Get()[i]);
|
|
|
|
if (fMinDiff <= -180 || fMinDiff >= 180) // if 360 rotation is allowed, skip any clamping for this axis
|
|
continue;
|
|
|
|
if (fDiff < fMinDiff)
|
|
pCmd->viewangles[i] = angMin[i];
|
|
else if (fDiff > fMaxDiff)
|
|
pCmd->viewangles[i] = angMax[i];
|
|
}
|
|
if (asw_turret_debug_limits.GetBool()) Msg("\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
int CASW_Remote_Turret::GetSentryDamage()
|
|
{
|
|
if (!ASWGameRules())
|
|
return 10;
|
|
|
|
return 25.0f - ((ASWGameRules()->GetMissionDifficulty() - 5.0f) * 0.75f);
|
|
}
|
|
|
|
#ifdef CLIENT_DLL
|
|
extern void ASWDoParticleTracer( const char *pTracerEffectName, const Vector &vecStart, const Vector &vecEnd, bool bRedTracer, int iAttributeEffects = 0 );
|
|
#endif
|
|
|
|
void CASW_Remote_Turret::MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType )
|
|
{
|
|
#ifdef GAME_DLL
|
|
CBroadcastRecipientFilter filter;
|
|
if ( gpGlobals->maxClients <= 1 )
|
|
{
|
|
filter.SetIgnorePredictionCull( true );
|
|
}
|
|
const char* szTracer = "ASWRemoteTurretTracer";
|
|
UserMessageBegin( filter, szTracer );
|
|
WRITE_SHORT( entindex() );
|
|
WRITE_FLOAT( tr.endpos.x );
|
|
WRITE_FLOAT( tr.endpos.y );
|
|
WRITE_FLOAT( tr.endpos.z );
|
|
MessageEnd();
|
|
#else
|
|
ASWRemoteTurretTracer( tr.endpos );
|
|
#endif
|
|
}
|
|
|
|
#ifdef CLIENT_DLL
|
|
void __MsgFunc_ASWRemoteTurretTracer( bf_read &msg )
|
|
{
|
|
int iSentry = msg.ReadShort();
|
|
CASW_Remote_Turret *pSentry = dynamic_cast<CASW_Remote_Turret*>( ClientEntityList().GetEnt( iSentry ) );
|
|
|
|
Vector vecEnd;
|
|
vecEnd.x = msg.ReadFloat();
|
|
vecEnd.y = msg.ReadFloat();
|
|
vecEnd.z = msg.ReadFloat();
|
|
|
|
if ( pSentry )
|
|
{
|
|
pSentry->ASWRemoteTurretTracer( vecEnd );
|
|
}
|
|
}
|
|
USER_MESSAGE_REGISTER( ASWRemoteTurretTracer );
|
|
|
|
void CASW_Remote_Turret::ASWRemoteTurretTracer( const Vector &vecEnd )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
Vector vecStart;
|
|
QAngle vecAngles;
|
|
|
|
if ( IsDormant() )
|
|
return;
|
|
|
|
C_BaseAnimating::PushAllowBoneAccess( true, false, "remoteturret" );
|
|
|
|
// Get the muzzle origin
|
|
if ( !GetAttachment( GetMuzzleAttachment(), vecStart, vecAngles ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
ASWDoParticleTracer( "tracer_autogun", vecStart, vecEnd, false );
|
|
|
|
C_BaseAnimating::PopBoneAccess( "remoteturret" );
|
|
}
|
|
#endif |