diff options
Diffstat (limited to 'game/server/hl2/npc_launcher.cpp')
| -rw-r--r-- | game/server/hl2/npc_launcher.cpp | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/game/server/hl2/npc_launcher.cpp b/game/server/hl2/npc_launcher.cpp new file mode 100644 index 0000000..9e94286 --- /dev/null +++ b/game/server/hl2/npc_launcher.cpp @@ -0,0 +1,413 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "ai_basenpc.h" +#include "ai_senses.h" +#include "ai_squad.h" +#include "grenade_homer.h" +#include "grenade_pathfollower.h" +#include "explode.h" +#include "ndebugoverlay.h" +#include "engine/IEngineSound.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define LAUNCHER_REST_TIME 3 + + +//------------------------------------ +// Spawnflags +//------------------------------------ +#define SF_LAUNCHER_CHECK_LOS (1 << 16) + + +//========================================================= +// >> CNPC_Launcher +//========================================================= +class CNPC_Launcher : public CAI_BaseNPC +{ + DECLARE_CLASS( CNPC_Launcher, CAI_BaseNPC ); + +public: + int m_nStartOn; + string_t m_sMissileModel; + string_t m_sLaunchSound; + string_t m_sFlySound; + int m_nSmokeTrail; + bool m_bSmokeLaunch; + int m_nLaunchDelay; + float m_flLaunchSpeed; + string_t m_sPathCornerName; // If following a path + float m_flHomingSpeed; + int m_nHomingStrength; + float m_flHomingDelay; // How long before homing starts + float m_flHomingRampUp; // How much time to ramp up to full homing + float m_flHomingDuration; // How long does homing last + float m_flHomingRampDown; // How long to ramp down to no homing + float m_flMissileGravity; + float m_flMinAttackDist; + float m_flMaxAttackDist; + float m_flSpinMagnitude; + float m_flSpinSpeed; + float m_flDamage; + float m_flDamageRadius; + + // ---------------- + // Outputs + // ---------------- + COutputEvent m_OnLaunch; // Triggered when missile is launched. + + // ---------------- + // Inputs + // ---------------- + void InputTurnOn( inputdata_t &inputdata ); + void InputTurnOff( inputdata_t &inputdata ); + void InputLOSCheckOn( inputdata_t &inputdata ); + void InputLOSCheckOff( inputdata_t &inputdata ); + void InputSetEnemy( inputdata_t &inputdata ); + void InputClearEnemy( inputdata_t &inputdata ); + void InputFireOnce( inputdata_t &inputdata ); + + void LauncherTurnOn(void); + + void Precache( void ); + void Spawn( void ); + Class_T Classify( void ); + bool IsValidEnemy(CBaseEntity *pTarget ); + void LaunchGrenade(CBaseEntity* pLauncher ); + void LauncherThink(void ); + bool FInViewCone( CBaseEntity *pEntity ); + + int DrawDebugTextOverlays(void); + + DECLARE_DATADESC(); +}; + + +BEGIN_DATADESC( CNPC_Launcher ) + + // Inputs + DEFINE_KEYFIELD( m_nStartOn, FIELD_INTEGER, "StartOn" ), + DEFINE_KEYFIELD( m_sMissileModel, FIELD_STRING, "MissileModel" ), + DEFINE_KEYFIELD( m_sLaunchSound, FIELD_STRING, "LaunchSound" ), + DEFINE_KEYFIELD( m_sFlySound, FIELD_STRING, "FlySound" ), + DEFINE_KEYFIELD( m_nSmokeTrail, FIELD_INTEGER, "SmokeTrail" ), + DEFINE_KEYFIELD( m_bSmokeLaunch, FIELD_BOOLEAN, "LaunchSmoke" ), + DEFINE_KEYFIELD( m_nLaunchDelay, FIELD_INTEGER, "LaunchDelay" ), + DEFINE_KEYFIELD( m_flLaunchSpeed, FIELD_FLOAT, "LaunchSpeed" ), + DEFINE_KEYFIELD( m_sPathCornerName, FIELD_STRING, "PathCornerName" ), + DEFINE_KEYFIELD( m_flHomingSpeed, FIELD_FLOAT, "HomingSpeed" ), + DEFINE_KEYFIELD( m_nHomingStrength, FIELD_INTEGER, "HomingStrength" ), + DEFINE_KEYFIELD( m_flHomingDelay, FIELD_FLOAT, "HomingDelay" ), + DEFINE_KEYFIELD( m_flHomingRampUp, FIELD_FLOAT, "HomingRampUp" ), + DEFINE_KEYFIELD( m_flHomingDuration, FIELD_FLOAT, "HomingDuration" ), + DEFINE_KEYFIELD( m_flHomingRampDown, FIELD_FLOAT, "HomingRampDown" ), + DEFINE_KEYFIELD( m_flGravity, FIELD_FLOAT, "Gravity" ), + DEFINE_KEYFIELD( m_flMinAttackDist, FIELD_FLOAT, "MinRange" ), + DEFINE_KEYFIELD( m_flMaxAttackDist, FIELD_FLOAT, "MaxRange" ), + DEFINE_KEYFIELD( m_flSpinMagnitude, FIELD_FLOAT, "SpinMagnitude" ), + DEFINE_KEYFIELD( m_flSpinSpeed, FIELD_FLOAT, "SpinSpeed" ), + DEFINE_KEYFIELD( m_flDamage, FIELD_FLOAT, "Damage" ), + DEFINE_KEYFIELD( m_flDamageRadius, FIELD_FLOAT, "DamageRadius" ), + DEFINE_FIELD( m_flMissileGravity, FIELD_FLOAT ), + + DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), + DEFINE_INPUTFUNC( FIELD_VOID, "LOSCheckOn", InputLOSCheckOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "LOSCheckOn", InputLOSCheckOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "FireOnce", InputFireOnce ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetEnemyEntity", InputSetEnemy ), + DEFINE_INPUTFUNC( FIELD_VOID, "ClearEnemyEntity", InputClearEnemy ), + + DEFINE_OUTPUT( m_OnLaunch, "OnLaunch" ), + + // Function Pointers + DEFINE_THINKFUNC( LauncherThink ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( npc_launcher, CNPC_Launcher ); + + +// =================== +// Input Functions +// =================== +void CNPC_Launcher::InputTurnOn( inputdata_t &inputdata ) +{ + LauncherTurnOn(); +} + +void CNPC_Launcher::InputTurnOff( inputdata_t &inputdata ) +{ + SetThink(NULL); +} + +void CNPC_Launcher::InputLOSCheckOn( inputdata_t &inputdata ) +{ + m_spawnflags |= SF_LAUNCHER_CHECK_LOS; +} + +void CNPC_Launcher::InputLOSCheckOff( inputdata_t &inputdata ) +{ + m_spawnflags &= ~SF_LAUNCHER_CHECK_LOS; +} + +void CNPC_Launcher::InputSetEnemy( inputdata_t &inputdata ) +{ + SetEnemy( inputdata.value.Entity().Get() ); +} + +void CNPC_Launcher::InputClearEnemy( inputdata_t &inputdata ) +{ + SetEnemy( NULL ); +} + +void CNPC_Launcher::InputFireOnce( inputdata_t &inputdata ) +{ + m_flNextAttack = 0; + + // If I using path following missiles just launch + if (m_sPathCornerName != NULL_STRING) + { + LaunchGrenade(NULL); + } + // Otherwise only launch if I have an enemy + else + { + LauncherThink(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Launcher::Precache( void ) +{ + // This is a dummy model that is never used! + PrecacheModel("models/player.mdl"); + PrecacheModel(STRING(m_sMissileModel)); + PrecacheScriptSound( STRING(m_sLaunchSound)); + PrecacheScriptSound( STRING(m_sFlySound)); + + UTIL_PrecacheOther( "grenade_homer"); + BaseClass::Precache(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Launcher::LauncherTurnOn(void) +{ + SetThink(&CNPC_Launcher::LauncherThink); + SetNextThink( gpGlobals->curtime ); + m_flNextAttack = 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Launcher::Spawn( void ) +{ + Precache(); + + // This is a dummy model that is never used! + SetModel( "models/player.mdl" ); + + UTIL_SetSize(this, vec3_origin, vec3_origin); + + m_takedamage = DAMAGE_NO; + + if (m_nHomingStrength > 100) + { + m_nHomingStrength = 100; + Warning("WARNING: NPC_Launcher Homing Strength must be between 0 and 100\n"); + } + + SetSolid( SOLID_NONE ); + SetMoveType( MOVETYPE_NONE ); + SetBloodColor( DONT_BLEED ); + AddEffects( EF_NODRAW ); + + AddFlag( FL_NPC ); + + CapabilitiesAdd( bits_CAP_SQUAD ); + + InitRelationshipTable(); + + if (m_nStartOn) + { + LauncherTurnOn(); + } + + // ------------------------------------------------------- + // If I form squads add me to a squad + // ------------------------------------------------------- + // @TODO (toml 12-05-02): RECONCILE WITH SAVE/RESTORE + if (!m_pSquad) + { + m_pSquad = g_AI_SquadManager.FindCreateSquad(this, m_SquadName); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Class_T CNPC_Launcher::Classify( void ) +{ + return CLASS_NONE; +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CNPC_Launcher::LaunchGrenade( CBaseEntity* pEnemy ) +{ + // If a path following missile, create a path following missile + if (m_sPathCornerName != NULL_STRING) + { + CGrenadePathfollower *pGrenade = CGrenadePathfollower::CreateGrenadePathfollower( m_sMissileModel, m_sFlySound, GetAbsOrigin(), vec3_angle, edict() ); + pGrenade->SetDamage(m_flDamage); + pGrenade->SetDamageRadius(m_flDamageRadius); + pGrenade->Launch(m_flLaunchSpeed,m_sPathCornerName); + } + else + { + Vector vUp; + AngleVectors( GetAbsAngles(), NULL, NULL, &vUp ); + Vector vLaunchVelocity = (vUp * m_flLaunchSpeed); + + CGrenadeHomer *pGrenade = CGrenadeHomer::CreateGrenadeHomer( m_sMissileModel, m_sFlySound, GetAbsOrigin(), vec3_angle, edict() ); + pGrenade->Spawn( ); + pGrenade->SetSpin(m_flSpinMagnitude,m_flSpinSpeed); + pGrenade->SetHoming((0.01*m_nHomingStrength),m_flHomingDelay,m_flHomingRampUp,m_flHomingDuration,m_flHomingRampDown); + pGrenade->SetDamage(m_flDamage); + pGrenade->SetDamageRadius(m_flDamageRadius); + pGrenade->Launch(this,pEnemy,vLaunchVelocity,m_flHomingSpeed,GetGravity(),m_nSmokeTrail); + } + + CPASAttenuationFilter filter( this, 0.3 ); + + EmitSound_t ep; + ep.m_nChannel = CHAN_WEAPON; + ep.m_pSoundName = STRING(m_sLaunchSound); + ep.m_SoundLevel = SNDLVL_NORM; + + EmitSound( filter, entindex(), ep ); + + if (m_bSmokeLaunch) + { + UTIL_Smoke(GetAbsOrigin(), random->RandomInt(20,30), random->RandomInt(10,15)); + } + m_flNextAttack = gpGlobals->curtime + LAUNCHER_REST_TIME; + +} + +//------------------------------------------------------------------------------ +// Purpose : Launcher sees 360 degrees +//------------------------------------------------------------------------------ +bool CNPC_Launcher::FInViewCone( CBaseEntity *pEntity ) +{ + return true; +} + +//------------------------------------------------------------------------------ +// Purpose : Override base class to check range and visibility +//------------------------------------------------------------------------------ +bool CNPC_Launcher::IsValidEnemy( CBaseEntity *pTarget ) +{ + // --------------------------------- + // Check range + // --------------------------------- + float flTargetDist = (GetAbsOrigin() - pTarget->GetAbsOrigin()).Length(); + if (flTargetDist < m_flMinAttackDist) + { + return false; + } + if (flTargetDist > m_flMaxAttackDist) + { + return false; + } + + if (!FBitSet (m_spawnflags, SF_LAUNCHER_CHECK_LOS)) + { + return true; + } + // ------------------------------------------------------ + // Make sure I can see the target from above my position + // ------------------------------------------------------ + trace_t tr; + + // Trace from launch position to target position. + // Use position above actual barral based on vertical launch speed + Vector vStartPos = GetAbsOrigin() + Vector(0,0,0.2*m_flLaunchSpeed); + Vector vEndPos = pTarget->GetAbsOrigin(); + AI_TraceLine( vStartPos, vEndPos, MASK_SHOT, pTarget, COLLISION_GROUP_NONE, &tr ); + + if (tr.fraction == 1.0) + { + return true; + } + return false; +} + +//------------------------------------------------------------------------------ +// Purpose : +//------------------------------------------------------------------------------ +void CNPC_Launcher::LauncherThink( void ) +{ + if (gpGlobals->curtime > m_flNextAttack) + { + // If enemy was set, fire at enemy + if (GetEnemy()) + { + LaunchGrenade(GetEnemy()); + m_OnLaunch.FireOutput(GetEnemy(), this); + m_flNextAttack = gpGlobals->curtime + m_nLaunchDelay; + } + // Otherwise look for enemy to fire at + else + { + GetSenses()->Look(m_flMaxAttackDist); + CBaseEntity* pBestEnemy = BestEnemy(); + + if (pBestEnemy) + { + LaunchGrenade(pBestEnemy); + m_OnLaunch.FireOutput(pBestEnemy, this); + m_flNextAttack = gpGlobals->curtime + m_nLaunchDelay; + } + } + } + SetNextThink( gpGlobals->curtime + 0.1f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CNPC_Launcher::DrawDebugTextOverlays(void) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + Q_snprintf(tempstr,sizeof(tempstr),"State: %s", (m_pfnThink) ? "On" : "Off" ); + EntityText(text_offset,tempstr,0); + text_offset++; + + Q_snprintf(tempstr,sizeof(tempstr),"LOS: %s", (FBitSet (m_spawnflags, SF_LAUNCHER_CHECK_LOS)) ? "On" : "Off" ); + EntityText(text_offset,tempstr,0); + text_offset++; + } + return text_offset; +} |