Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

482 lines
13 KiB

#include "cbase.h"
#include "props.h"
#include "asw_sentry_base.h"
#include "asw_sentry_top.h"
#include "asw_player.h"
#include "asw_marine.h"
#include "asw_marine_skills.h"
#include "asw_marine_speech.h"
#include "asw_marine_resource.h"
#include "world.h"
#include "asw_util_shared.h"
#include "asw_fx_shared.h"
#include "asw_gamerules.h"
#include "asw_weapon_sentry_shared.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define SENTRY_BASE_MODEL "models/sentry_gun/sentry_base.mdl"
//#define SENTRY_BASE_MODEL "models/swarm/droneprops/DronePropIdle.mdl"
extern int g_sModelIndexFireball; // (in combatweapon.cpp) holds the index for the smoke cloud
ConVar asw_sentry_gun_type("asw_sentry_gun_type", "-1", FCVAR_CHEAT, "Force the type of sentry guns built to this. -1, the default, reads from the marine attributes.");
ConVar asw_sentry_infinite_ammo( "asw_sentry_infinite_ammo", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
LINK_ENTITY_TO_CLASS( asw_sentry_base, CASW_Sentry_Base );
PRECACHE_REGISTER( asw_sentry_base );
IMPLEMENT_SERVERCLASS_ST(CASW_Sentry_Base, DT_ASW_Sentry_Base)
SendPropBool(SENDINFO(m_bAssembled)),
SendPropBool(SENDINFO(m_bIsInUse)),
SendPropFloat(SENDINFO(m_fAssembleProgress)),
SendPropFloat(SENDINFO(m_fAssembleCompleteTime)),
SendPropInt(SENDINFO(m_iAmmo)),
SendPropInt(SENDINFO(m_iMaxAmmo)),
SendPropBool(SENDINFO(m_bSkillMarineHelping)),
SendPropInt(SENDINFO(m_nGunType)),
END_SEND_TABLE()
BEGIN_DATADESC( CASW_Sentry_Base )
DEFINE_THINKFUNC( AnimThink ),
DEFINE_FIELD( m_hSentryTop, FIELD_EHANDLE ),
DEFINE_FIELD( m_bAssembled, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bIsInUse, FIELD_BOOLEAN ),
DEFINE_FIELD( m_fAssembleProgress, FIELD_FLOAT ),
DEFINE_FIELD( m_fAssembleCompleteTime, FIELD_TIME ),
DEFINE_FIELD( m_hDeployer, FIELD_EHANDLE ),
END_DATADESC()
CASW_Sentry_Base::CASW_Sentry_Base()
{
m_iAmmo = -1;
m_fSkillMarineHelping = 0;
m_bSkillMarineHelping = false;
m_fDamageScale = 1.0f;
m_nGunType = kAUTOGUN;
m_bAlreadyTaken = false;
}
CASW_Sentry_Base::~CASW_Sentry_Base()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CASW_Sentry_Base::Spawn( void )
{
SetMoveType( MOVETYPE_NONE );
SetSolid( SOLID_BBOX );
SetCollisionGroup( ASW_COLLISION_GROUP_SENTRY );
Precache();
SetModel(SENTRY_BASE_MODEL);
BaseClass::Spawn();
AddEFlags( EFL_NO_DISSOLVE | EFL_NO_MEGAPHYSCANNON_RAGDOLL | EFL_NO_PHYSCANNON_INTERACTION );
SetCollisionBounds( Vector(-26,-26,0), Vector(26,26,60));
SetMaxHealth(300);
SetHealth(300);
m_takedamage = DAMAGE_YES;
SetThink( &CASW_Sentry_Base::AnimThink );
SetNextThink( gpGlobals->curtime + 0.1f );
// check for attaching to elevators
trace_t tr;
UTIL_TraceLine( GetAbsOrigin() + Vector(0, 0, 2),
GetAbsOrigin() - Vector(0, 0, 32), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction < 1.0f && tr.m_pEnt && !tr.m_pEnt->IsWorld() && !tr.m_pEnt->IsNPC() )
{
SetParent( tr.m_pEnt );
}
if ( m_iAmmo == -1 )
{
m_iAmmo = GetBaseAmmoForGunType( GetGunType() );
}
m_iMaxAmmo = m_iAmmo;
}
void CASW_Sentry_Base::PlayDeploySound()
{
EmitSound("ASW_Sentry.Deploy");
}
void CASW_Sentry_Base::Precache()
{
PrecacheModel( SENTRY_BASE_MODEL );
PrecacheScriptSound( "ASW_Sentry.SetupLoop" );
PrecacheScriptSound( "ASW_Sentry.SetupInterrupt" );
PrecacheScriptSound( "ASW_Sentry.SetupComplete" );
PrecacheScriptSound( "ASW_Sentry.Dismantled" );
BaseClass::Precache();
}
int CASW_Sentry_Base::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
return FL_EDICT_ALWAYS;
}
int CASW_Sentry_Base::UpdateTransmitState()
{
return SetTransmitState( FL_EDICT_FULLCHECK );
}
void CASW_Sentry_Base::AnimThink( void )
{
if (!m_bAssembled)
{
SetNextThink( gpGlobals->curtime + 0.1f );
StudioFrameAdvance();
m_bSkillMarineHelping = (m_fSkillMarineHelping >= gpGlobals->curtime - 0.2f);
}
else
{
m_bSkillMarineHelping = false;
}
}
// player has used this item
void CASW_Sentry_Base::ActivateUseIcon( CASW_Marine* pMarine, int nHoldType )
{
if (!m_bIsInUse && !m_bAssembled && nHoldType != ASW_USE_HOLD_RELEASE_FULL)
{
pMarine->StartUsing(this);
pMarine->GetMarineSpeech()->Chatter(CHATTER_USE);
}
else if (m_bAssembled && GetSentryTop())
{
if ( nHoldType == ASW_USE_HOLD_START )
{
pMarine->StartUsing(this);
pMarine->GetMarineSpeech()->Chatter(CHATTER_USE);
}
else if ( nHoldType == ASW_USE_HOLD_RELEASE_FULL )
{
pMarine->StopUsing();
if ( !m_bAlreadyTaken )
{
//Msg( "Disassembling sentry gun!\n" );
IGameEvent * event = gameeventmanager->CreateEvent( "sentry_dismantled" );
if ( event )
{
CBasePlayer *pPlayer = pMarine->GetCommander();
event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) );
event->SetInt( "entindex", entindex() );
gameeventmanager->FireEvent( event );
}
CASW_Weapon_Sentry *pWeapon = dynamic_cast<CASW_Weapon_Sentry*>( Create( GetWeaponNameForGunType( GetGunType() ), WorldSpaceCenter(), GetAbsAngles(), NULL ) );
pWeapon->SetSentryAmmo( m_iAmmo );
pMarine->TakeWeaponPickup( pWeapon );
EmitSound( "ASW_Sentry.Dismantled" );
UTIL_Remove( this );
m_bAlreadyTaken = true;
}
// TODO: just have the marine pick it up now and let that logic deal with the slot?
// TODO: Find an empty inv slot. Or default to 2nd.
// Drop whatever's in that slot currently
// Create a new sentry gun weapon with our ammo amount and give it to the marine
// Destroy ourselves
}
else if ( nHoldType == ASW_USE_RELEASE_QUICK )
{
pMarine->StopUsing();
pMarine->GetMarineSpeech()->Chatter(CHATTER_USE);
IGameEvent * event = gameeventmanager->CreateEvent( "sentry_rotated" );
if ( event )
{
CBasePlayer *pPlayer = pMarine->GetCommander();
event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) );
event->SetInt( "entindex", entindex() );
gameeventmanager->FireEvent( event );
}
// tell the top piece to turn to face the same way as pMarine is facing
GetSentryTop()->SetDeployYaw(pMarine->ASWEyeAngles().y);
GetSentryTop()->PlayTurnSound();
}
}
}
#define SENTRY_ASSEMBLE_TIME 7.0f // was 14.0
void CASW_Sentry_Base::MarineUsing(CASW_Marine* pMarine, float deltatime)
{
if (m_bIsInUse && !m_bAssembled && pMarine)
{
// check if any techs nearby have engineering skill to speed this up
float fSkillScale = MarineSkills()->GetHighestSkillValueNearby(pMarine->GetAbsOrigin(), ENGINEERING_AURA_RADIUS,
ASW_MARINE_SKILL_ENGINEERING, ASW_MARINE_SUBSKILL_ENGINEERING_SENTRY);
CASW_Marine *pSkillMarine = MarineSkills()->GetLastSkillMarine();
if ( fSkillScale> 0.0f && pSkillMarine && pSkillMarine->GetMarineResource())
{
pSkillMarine->m_fUsingEngineeringAura = gpGlobals->curtime;
m_fSkillMarineHelping = gpGlobals->curtime;
}
else
{
m_fSkillMarineHelping = 0;
}
if (fSkillScale < 1.0)
fSkillScale = 1.0f;
float fSetupAmount = (deltatime * (1.0f/SENTRY_ASSEMBLE_TIME)) * fSkillScale;
m_fAssembleProgress += fSetupAmount;
if (m_fAssembleProgress >= 1.0f)
{
m_fAssembleProgress = 1.0f;
pMarine->StopUsing();
m_bAssembled = true;
m_fAssembleCompleteTime = gpGlobals->curtime;
pMarine->GetMarineSpeech()->Chatter(CHATTER_SENTRY);
// spawn top half and activate it
CASW_Sentry_Top * RESTRICT pSentryTop = dynamic_cast<CASW_Sentry_Top*>( CreateEntityByName( GetEntityNameForGunType( GetGunType() ) ) );
m_hSentryTop = pSentryTop;
if ( pSentryTop )
{
pSentryTop->SetSentryBase( this );
const QAngle &angles = GetAbsAngles();
pSentryTop->SetAbsAngles( angles );
DispatchSpawn( pSentryTop );
}
}
}
}
void CASW_Sentry_Base::MarineStartedUsing(CASW_Marine* pMarine)
{
EmitSound( "ASW_Sentry.SetupLoop" );
if ( !m_bIsInUse && m_fAssembleProgress < 1.0f )
{
IGameEvent * event = gameeventmanager->CreateEvent( "sentry_start_building" );
if ( event )
{
CBasePlayer *pPlayer = pMarine->GetCommander();
event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) );
event->SetInt( "entindex", entindex() );
gameeventmanager->FireEvent( event );
}
}
m_bIsInUse = true;
}
void CASW_Sentry_Base::MarineStoppedUsing(CASW_Marine* pMarine)
{
if ( m_fAssembleProgress >= 1.0f )
{
EmitSound( "ASW_Sentry.SetupComplete" );
IGameEvent * event = gameeventmanager->CreateEvent( "sentry_complete" );
if ( event )
{
CBasePlayer *pPlayer = pMarine->GetCommander();
event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) );
event->SetInt( "entindex", entindex() );
event->SetInt( "marine", pMarine->entindex() );
gameeventmanager->FireEvent( event );
}
}
else
{
EmitSound( "ASW_Sentry.SetupInterrupt" );
IGameEvent * event = gameeventmanager->CreateEvent( "sentry_stop_building" );
if ( event )
{
CBasePlayer *pPlayer = pMarine->GetCommander();
event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) );
event->SetInt( "entindex", entindex() );
gameeventmanager->FireEvent( event );
}
}
m_bIsInUse = false;
}
CASW_Sentry_Top* CASW_Sentry_Base::GetSentryTop()
{
return assert_cast<CASW_Sentry_Top*>(m_hSentryTop.Get());
}
bool CASW_Sentry_Base::IsUsable(CBaseEntity *pUser)
{
return (pUser && pUser->GetAbsOrigin().DistTo(GetAbsOrigin()) < ASW_MARINE_USE_RADIUS); // near enough?
}
void CASW_Sentry_Base::OnFiredShots( int nNumShots )
{
if ( GetSentryTop() )
{
int nThreeQuarterAmmo = GetBaseAmmoForGunType( GetGunType() ) * 0.75f;
int nLowAmmo = GetBaseAmmoForGunType( GetGunType() ) / 5;
if ( m_iAmmo <= 0 )
{
GetSentryTop()->OnOutOfAmmo();
}
else if ( m_iAmmo <= nLowAmmo )
{
GetSentryTop()->OnLowAmmo();
}
else if ( m_iAmmo <= nThreeQuarterAmmo )
{
GetSentryTop()->OnUsedQuarterAmmo();
}
if( m_hDeployer )
m_hDeployer->OnWeaponFired( GetSentryTop(), nNumShots );
}
if ( !asw_sentry_infinite_ammo.GetBool() )
m_iAmmo -= nNumShots;
}
int CASW_Sentry_Base::OnTakeDamage( const CTakeDamageInfo &info )
{
// no friendly fire damage
CBaseEntity *pAttacker = info.GetAttacker();
if ( pAttacker && pAttacker->Classify() == CLASS_ASW_MARINE )
{
return 0;
}
else
{
return BaseClass::OnTakeDamage(info);
}
}
// explode if we die
void CASW_Sentry_Base::Event_Killed( const CTakeDamageInfo &info )
{
m_takedamage = DAMAGE_NO;
// explosion effect
Vector vecPos = GetAbsOrigin() + Vector(0, 0, 30);
trace_t tr;
UTIL_TraceLine ( vecPos, vecPos - Vector(0,0, 60), MASK_SHOT,
this, COLLISION_GROUP_NONE, &tr);
if ((tr.m_pEnt != GetWorldEntity()) || (tr.hitbox != 0))
{
// non-world needs smaller decals
if( tr.m_pEnt && !tr.m_pEnt->IsNPC() )
{
UTIL_DecalTrace( &tr, "SmallScorch" );
}
}
else
{
UTIL_DecalTrace( &tr, "Scorch" );
}
UTIL_ASW_ScreenShake( vecPos, 25.0, 150.0, 1.0, 750, SHAKE_START );
UTIL_ASW_GrenadeExplosion( vecPos, 400.0f );
EmitSound( "ASWGrenade.Explode" );
// damage to nearby things
ASWGameRules()->RadiusDamage ( CTakeDamageInfo( this, info.GetAttacker(), 150.0f, DMG_BLAST ), vecPos, 400.0f, CLASS_NONE, NULL );
if (GetSentryTop())
{
UTIL_Remove(GetSentryTop());
}
BaseClass::Event_Killed(info);
}
const char *CASW_Sentry_Base::GetEntityNameForGunType( GunType_t guntype )
{
AssertMsg1( static_cast<int>(guntype) >= 0, "Faulty guntype %d passed to CASW_Sentry_Base::GetEntityNameForGunType()\n", guntype );
if ( guntype < kGUNTYPE_MAX )
{
return sm_gunTypeToInfo[guntype].m_entityName;
}
else
{
Warning( "GetEntityNameForGunType called with unsupported GunType_t %d .. defaulting to machine gun.\n", guntype );
return sm_gunTypeToInfo[kAUTOGUN].m_entityName;
}
}
const char *CASW_Sentry_Base::GetWeaponNameForGunType( GunType_t guntype )
{
AssertMsg1( static_cast<int>(guntype) >= 0, "Faulty guntype %d passed to CASW_Sentry_Base::GetWeaponNameForGunType()\n", guntype );
if ( guntype < kGUNTYPE_MAX )
{
return sm_gunTypeToInfo[guntype].m_weaponName;
}
else
{
Warning( "GetWeaponNameForGunType called with unsupported GunType_t %d .. defaulting to machine gun.\n", guntype );
return sm_gunTypeToInfo[kAUTOGUN].m_weaponName;
}
}
int CASW_Sentry_Base::GetBaseAmmoForGunType( GunType_t guntype )
{
AssertMsg1( static_cast<int>(guntype) >= 0, "Faulty guntype %d passed to CASW_Sentry_Base::GetBaseAmmoForGunType()\n", guntype );
if ( guntype < kGUNTYPE_MAX )
{
return sm_gunTypeToInfo[guntype].m_nBaseAmmo;
}
else
{
Warning( "GetBaseAmmoForGunType called with unsupported GunType_t %d .. defaulting to machine gun.\n", guntype );
return sm_gunTypeToInfo[kAUTOGUN].m_nBaseAmmo;
}
}
CASW_Sentry_Base::GunType_t CASW_Sentry_Base::GetGunType( void ) const
{
// read the cvar
int nCvarGunType = asw_sentry_gun_type.GetInt();
if ( nCvarGunType >= 0 )
{
return static_cast<CASW_Sentry_Base::GunType_t>(nCvarGunType);
}
else
{
return (GunType_t) m_nGunType.Get();
}
}
/// This must exactly match the enum CASW_Sentry_Base::GunType_t
const CASW_Sentry_Base::SentryGunTypeInfo_t CASW_Sentry_Base::sm_gunTypeToInfo[CASW_Sentry_Base::kGUNTYPE_MAX] =
{
SentryGunTypeInfo_t("asw_sentry_top_machinegun", "asw_weapon_sentry", 450), // kAUTOGUN
SentryGunTypeInfo_t("asw_sentry_top_cannon", "asw_weapon_sentry_cannon", 25), // kCANNON
SentryGunTypeInfo_t("asw_sentry_top_flamer", "asw_weapon_sentry_flamer", 800), // kFLAME (ammo is stored as milliseconds of fire)
SentryGunTypeInfo_t("asw_sentry_top_icer", "asw_weapon_sentry_freeze", 800), // kICE
};