aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/hl2/npc_combinecamera.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/hl2/npc_combinecamera.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/hl2/npc_combinecamera.cpp')
-rw-r--r--mp/src/game/server/hl2/npc_combinecamera.cpp2376
1 files changed, 1188 insertions, 1188 deletions
diff --git a/mp/src/game/server/hl2/npc_combinecamera.cpp b/mp/src/game/server/hl2/npc_combinecamera.cpp
index 5ac48e57..26d1f07e 100644
--- a/mp/src/game/server/hl2/npc_combinecamera.cpp
+++ b/mp/src/game/server/hl2/npc_combinecamera.cpp
@@ -1,1188 +1,1188 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Security cameras will track a default target (if they have one)
-// until they either acquire an enemy to track or are told to track
-// an entity via an input. If they lose their target they will
-// revert to tracking their default target. They acquire enemies
-// using the relationship table just like any other NPC.
-//
-// Cameras have two zones of awareness, an inner zone formed by the
-// intersection of an inner FOV and an inner radius. The camera is
-// fully aware of entities in the inner zone and will acquire enemies
-// seen there.
-//
-// The outer zone of awareness is formed by the intersection of an
-// outer FOV and an outer radius. The camera is only vaguely aware
-// of entities in the outer zone and will flash amber when enemies
-// are there, but will otherwise ignore them.
-//
-// They can be made angry via an input, at which time they sound an
-// alarm and snap a few pictures of whatever they are tracking. They
-// can also be set to become angry anytime they acquire an enemy.
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "ai_basenpc.h"
-#include "ai_senses.h"
-#include "ai_memory.h"
-#include "engine/IEngineSound.h"
-#include "ammodef.h"
-#include "Sprite.h"
-#include "hl2/hl2_player.h"
-#include "soundenvelope.h"
-#include "explode.h"
-#include "IEffects.h"
-#include "animation.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-// Debug visualization
-ConVar g_debug_combine_camera("g_debug_combine_camera", "0");
-
-#define COMBINE_CAMERA_MODEL "models/combine_camera/combine_camera.mdl"
-#define COMBINE_CAMERA_GLOW_SPRITE "sprites/glow1.vmt"
-#define COMBINE_CAMERA_FLASH_SPRITE "sprites/light_glow03.vmt"
-#define COMBINE_CAMERA_BC_YAW "aim_yaw"
-#define COMBINE_CAMERA_BC_PITCH "aim_pitch"
-
-#define COMBINE_CAMERA_SPREAD VECTOR_CONE_2DEGREES
-#define COMBINE_CAMERA_MAX_WAIT 5
-#define COMBINE_CAMERA_PING_TIME 1.0f
-
-// Spawnflags
-#define SF_COMBINE_CAMERA_BECOMEANGRY 0x00000020
-#define SF_COMBINE_CAMERA_IGNOREENEMIES 0x00000040
-#define SF_COMBINE_CAMERA_STARTINACTIVE 0x00000080
-
-// Heights
-#define COMBINE_CAMERA_RETRACT_HEIGHT 24
-#define COMBINE_CAMERA_DEPLOY_HEIGHT 64
-
-
-// Activities
-int ACT_COMBINE_CAMERA_OPEN;
-int ACT_COMBINE_CAMERA_CLOSE;
-int ACT_COMBINE_CAMERA_OPEN_IDLE;
-int ACT_COMBINE_CAMERA_CLOSED_IDLE;
-int ACT_COMBINE_CAMERA_FIRE;
-
-
-const float CAMERA_CLICK_INTERVAL = 0.5f;
-const float CAMERA_MOVE_INTERVAL = 1.0f;
-
-
-//
-// The camera has two FOVs - a wide one for becoming slightly aware of someone,
-// a narrow one for becoming totally aware of them.
-//
-const float CAMERA_FOV_WIDE = 0.5;
-const float CAMERA_FOV_NARROW = 0.707;
-
-
-// Camera states
-enum cameraState_e
-{
- CAMERA_SEARCHING,
- CAMERA_AUTO_SEARCHING,
- CAMERA_ACTIVE,
- CAMERA_DEAD,
-};
-
-
-// Eye states
-enum eyeState_t
-{
- CAMERA_EYE_IDLE, // Nothing abnormal in the inner or outer viewcone, dim green.
- CAMERA_EYE_SEEKING_TARGET, // Something in the outer viewcone, flashes amber as it converges on the target.
- CAMERA_EYE_FOUND_TARGET, // Something in the inner viewcone, bright amber.
- CAMERA_EYE_ANGRY, // Found a target that we don't like: angry, bright red.
- CAMERA_EYE_DORMANT, // Not active
- CAMERA_EYE_DEAD, // Completely invisible
- CAMERA_EYE_DISABLED, // Turned off, must be reactivated before it'll deploy again (completely invisible)
- CAMERA_EYE_HAPPY, // Found a target that we like: go green for a second
-};
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-class CNPC_CombineCamera : public CAI_BaseNPC
-{
- DECLARE_CLASS(CNPC_CombineCamera, CAI_BaseNPC);
-public:
-
- CNPC_CombineCamera();
- ~CNPC_CombineCamera();
-
- void Precache();
- void Spawn();
- Vector HeadDirection2D();
-
- int DrawDebugTextOverlays();
-
- void Deploy();
- void ActiveThink();
- void SearchThink();
- void DeathThink();
-
- void InputToggle(inputdata_t &inputdata);
- void InputEnable(inputdata_t &inputdata);
- void InputDisable(inputdata_t &inputdata);
- void InputSetAngry(inputdata_t &inputdata);
- void InputSetIdle(inputdata_t &inputdata);
-
- void DrawDebugGeometryOverlays(void);
-
- float MaxYawSpeed();
-
- int OnTakeDamage(const CTakeDamageInfo &inputInfo);
-
- Class_T Classify() { return (m_bEnabled) ? CLASS_MILITARY : CLASS_NONE; }
-
- bool IsValidEnemy( CBaseEntity *pEnemy );
- bool FVisible(CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL);
-
- Vector EyeOffset(Activity nActivity)
- {
- Vector vecEyeOffset(0,0,-64);
- GetEyePosition(GetModelPtr(), vecEyeOffset);
- return vecEyeOffset;
- }
-
- Vector EyePosition()
- {
- return GetAbsOrigin() + EyeOffset(GetActivity());
- }
-
-protected:
-
- CBaseEntity *GetTarget();
- bool UpdateFacing();
- void TrackTarget(CBaseEntity *pTarget);
-
- bool PreThink(cameraState_e state);
- void SetEyeState(eyeState_t state);
- void MaintainEye();
- void Ping();
- void Toggle();
- void Enable();
- void Disable();
- void SetHeight(float height);
-
- CBaseEntity *MaintainEnemy();
- void SetAngry(bool bAngry);
-
-protected:
- int m_iAmmoType;
- int m_iMinHealthDmg;
-
- int m_nInnerRadius; // The camera will only lock onto enemies that are within the inner radius.
- int m_nOuterRadius; // The camera will flash amber when enemies are within the outer radius, but outside the inner radius.
-
- bool m_bActive; // The camera is deployed and looking for targets
- bool m_bAngry; // The camera has gotten angry at someone and sounded an alarm.
- bool m_bBlinkState;
- bool m_bEnabled; // Denotes whether the camera is able to deploy or not
-
- string_t m_sDefaultTarget;
-
- EHANDLE m_hEnemyTarget; // Entity we acquired as an enemy.
-
- float m_flPingTime;
- float m_flClickTime; // Time to take next picture while angry.
- int m_nClickCount; // Counts pictures taken since we last became angry.
- float m_flMoveSoundTime;
- float m_flTurnOffEyeFlashTime;
- float m_flEyeHappyTime;
-
- QAngle m_vecGoalAngles;
-
- CSprite *m_pEyeGlow;
- CSprite *m_pEyeFlash;
-
- DECLARE_DATADESC();
-};
-
-
-BEGIN_DATADESC(CNPC_CombineCamera)
-
- DEFINE_FIELD(m_iAmmoType, FIELD_INTEGER),
- DEFINE_KEYFIELD(m_iMinHealthDmg, FIELD_INTEGER, "minhealthdmg"),
- DEFINE_KEYFIELD(m_nInnerRadius, FIELD_INTEGER, "innerradius"),
- DEFINE_KEYFIELD(m_nOuterRadius, FIELD_INTEGER, "outerradius"),
- DEFINE_FIELD(m_bActive, FIELD_BOOLEAN),
- DEFINE_FIELD(m_bAngry, FIELD_BOOLEAN),
- DEFINE_FIELD(m_bBlinkState, FIELD_BOOLEAN),
- DEFINE_FIELD(m_bEnabled, FIELD_BOOLEAN),
- DEFINE_KEYFIELD(m_sDefaultTarget, FIELD_STRING, "defaulttarget"),
- DEFINE_FIELD(m_hEnemyTarget, FIELD_EHANDLE),
- DEFINE_FIELD(m_flPingTime, FIELD_TIME),
- DEFINE_FIELD(m_flClickTime, FIELD_TIME),
- DEFINE_FIELD(m_nClickCount, FIELD_INTEGER ),
- DEFINE_FIELD(m_flMoveSoundTime, FIELD_TIME),
- DEFINE_FIELD(m_flTurnOffEyeFlashTime, FIELD_TIME),
- DEFINE_FIELD(m_flEyeHappyTime, FIELD_TIME),
- DEFINE_FIELD(m_vecGoalAngles, FIELD_VECTOR),
- DEFINE_FIELD(m_pEyeGlow, FIELD_CLASSPTR),
- DEFINE_FIELD(m_pEyeFlash, FIELD_CLASSPTR),
-
- DEFINE_THINKFUNC(Deploy),
- DEFINE_THINKFUNC(ActiveThink),
- DEFINE_THINKFUNC(SearchThink),
- DEFINE_THINKFUNC(DeathThink),
-
- // Inputs
- DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle),
- DEFINE_INPUTFUNC(FIELD_VOID, "Enable", InputEnable),
- DEFINE_INPUTFUNC(FIELD_VOID, "Disable", InputDisable),
- DEFINE_INPUTFUNC(FIELD_VOID, "SetAngry", InputSetAngry),
- DEFINE_INPUTFUNC(FIELD_VOID, "SetIdle", InputSetIdle),
-
-END_DATADESC()
-
-LINK_ENTITY_TO_CLASS(npc_combine_camera, CNPC_CombineCamera);
-
-
-//-----------------------------------------------------------------------------
-// Constructor
-//-----------------------------------------------------------------------------
-CNPC_CombineCamera::CNPC_CombineCamera()
-{
- m_bActive = false;
- m_pEyeGlow = NULL;
- m_pEyeFlash = NULL;
- m_iAmmoType = -1;
- m_iMinHealthDmg = 0;
- m_flPingTime = 0;
- m_bBlinkState = false;
- m_bEnabled = false;
-
- m_vecGoalAngles.Init();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-CNPC_CombineCamera::~CNPC_CombineCamera()
-{
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Precache
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::Precache()
-{
- PrecacheModel(COMBINE_CAMERA_MODEL);
- PrecacheModel(COMBINE_CAMERA_GLOW_SPRITE);
- PrecacheModel(COMBINE_CAMERA_FLASH_SPRITE);
-
- // Activities
- ADD_CUSTOM_ACTIVITY(CNPC_CombineCamera, ACT_COMBINE_CAMERA_OPEN);
- ADD_CUSTOM_ACTIVITY(CNPC_CombineCamera, ACT_COMBINE_CAMERA_CLOSE);
- ADD_CUSTOM_ACTIVITY(CNPC_CombineCamera, ACT_COMBINE_CAMERA_CLOSED_IDLE);
- ADD_CUSTOM_ACTIVITY(CNPC_CombineCamera, ACT_COMBINE_CAMERA_OPEN_IDLE);
- ADD_CUSTOM_ACTIVITY(CNPC_CombineCamera, ACT_COMBINE_CAMERA_FIRE);
-
- PrecacheScriptSound( "NPC_CombineCamera.Move" );
- PrecacheScriptSound( "NPC_CombineCamera.BecomeIdle" );
- PrecacheScriptSound( "NPC_CombineCamera.Active" );
- PrecacheScriptSound( "NPC_CombineCamera.Click" );
- PrecacheScriptSound( "NPC_CombineCamera.Ping" );
- PrecacheScriptSound( "NPC_CombineCamera.Angry" );
- PrecacheScriptSound( "NPC_CombineCamera.Die" );
-
- BaseClass::Precache();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Spawn the entity
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::Spawn()
-{
- Precache();
-
- SetModel(COMBINE_CAMERA_MODEL);
-
- m_pEyeFlash = CSprite::SpriteCreate(COMBINE_CAMERA_FLASH_SPRITE, GetLocalOrigin(), FALSE);
- m_pEyeFlash->SetTransparency(kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation);
- m_pEyeFlash->SetAttachment(this, 2);
- m_pEyeFlash->SetBrightness(0);
- m_pEyeFlash->SetScale(1.0);
-
- BaseClass::Spawn();
-
- m_HackedGunPos = Vector(0, 0, 12.75);
- SetViewOffset(EyeOffset(ACT_IDLE));
- m_flFieldOfView = CAMERA_FOV_WIDE;
- m_takedamage = DAMAGE_YES;
- m_iHealth = 50;
- m_bloodColor = BLOOD_COLOR_MECH;
-
- SetSolid(SOLID_BBOX);
- AddSolidFlags(FSOLID_NOT_STANDABLE);
-
- SetHeight(COMBINE_CAMERA_RETRACT_HEIGHT);
-
- AddFlag(FL_AIMTARGET);
-
- SetPoseParameter(COMBINE_CAMERA_BC_YAW, 0);
- SetPoseParameter(COMBINE_CAMERA_BC_PITCH, 0);
-
- m_iAmmoType = GetAmmoDef()->Index("Pistol");
-
- // Create our eye sprite
- m_pEyeGlow = CSprite::SpriteCreate(COMBINE_CAMERA_GLOW_SPRITE, GetLocalOrigin(), false);
- m_pEyeGlow->SetTransparency(kRenderWorldGlow, 255, 0, 0, 128, kRenderFxNoDissipation);
- m_pEyeGlow->SetAttachment(this, 2);
-
- // Set our enabled state
- m_bEnabled = ((m_spawnflags & SF_COMBINE_CAMERA_STARTINACTIVE) == false);
-
- // Make sure the radii are sane.
- if (m_nOuterRadius <= 0)
- {
- m_nOuterRadius = 300;
- }
-
- if (m_nInnerRadius <= 0)
- {
- m_nInnerRadius = 450;
- }
-
- if (m_nOuterRadius < m_nInnerRadius)
- {
- V_swap(m_nOuterRadius, m_nInnerRadius);
- }
-
- // Do we start active?
- if (m_bEnabled)
- {
- Deploy();
- }
- else
- {
- SetEyeState(CAMERA_EYE_DISABLED);
- }
-
- //Adrian: No shadows on these guys.
- AddEffects( EF_NOSHADOW );
-
- // Stagger our starting times
- SetNextThink( gpGlobals->curtime + random->RandomFloat(0.1f, 0.3f) );
-
- // Don't allow us to skip animation setup because our attachments are critical to us!
- SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-CBaseEntity *CNPC_CombineCamera::GetTarget()
-{
- return m_hEnemyTarget;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CNPC_CombineCamera::OnTakeDamage(const CTakeDamageInfo &inputInfo)
-{
- if (!m_takedamage)
- return 0;
-
- CTakeDamageInfo info = inputInfo;
-
- if (m_bActive == false)
- info.ScaleDamage(0.1f);
-
- // If attacker can't do at least the min required damage to us, don't take any damage from them
- if (info.GetDamage() < m_iMinHealthDmg)
- return 0;
-
- m_iHealth -= info.GetDamage();
-
- if (m_iHealth <= 0)
- {
- m_iHealth = 0;
- m_takedamage = DAMAGE_NO;
-
- RemoveFlag(FL_NPC); // why are they set in the first place???
-
- // FIXME: This needs to throw a ragdoll gib or something other than animating the retraction -- jdw
-
- ExplosionCreate(GetAbsOrigin(), GetLocalAngles(), this, 100, 100, false);
- SetThink(&CNPC_CombineCamera::DeathThink);
-
- StopSound("Alert");
-
- m_OnDamaged.FireOutput(info.GetInflictor(), this);
-
- SetNextThink( gpGlobals->curtime + 0.1f );
-
- return 0;
- }
-
- return 1;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Deploy and start searching for targets.
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::Deploy()
-{
- m_vecGoalAngles = GetAbsAngles();
-
- SetNextThink( gpGlobals->curtime );
-
- SetEyeState(CAMERA_EYE_IDLE);
- m_bActive = true;
-
- SetHeight(COMBINE_CAMERA_DEPLOY_HEIGHT);
- SetIdealActivity((Activity) ACT_COMBINE_CAMERA_OPEN_IDLE);
- m_flPlaybackRate = 0;
- SetThink(&CNPC_CombineCamera::SearchThink);
-
- EmitSound("NPC_CombineCamera.Move");
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the speed at which the camera can face a target
-//-----------------------------------------------------------------------------
-float CNPC_CombineCamera::MaxYawSpeed()
-{
- if (m_hEnemyTarget)
- return 180.0f;
-
- return 60.0f;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Causes the camera to face its desired angles
-//-----------------------------------------------------------------------------
-bool CNPC_CombineCamera::UpdateFacing()
-{
- bool bMoved = false;
- matrix3x4_t localToWorld;
-
- GetAttachment(LookupAttachment("eyes"), localToWorld);
-
- Vector vecGoalDir;
- AngleVectors(m_vecGoalAngles, &vecGoalDir );
-
- Vector vecGoalLocalDir;
- VectorIRotate(vecGoalDir, localToWorld, vecGoalLocalDir);
-
- QAngle vecGoalLocalAngles;
- VectorAngles(vecGoalLocalDir, vecGoalLocalAngles);
-
- // Update pitch
- float flDiff = AngleNormalize(UTIL_ApproachAngle( vecGoalLocalAngles.x, 0.0, 0.1f * MaxYawSpeed()));
-
- int iPose = LookupPoseParameter(COMBINE_CAMERA_BC_PITCH);
- SetPoseParameter(iPose, GetPoseParameter(iPose) + (flDiff / 1.5f));
-
- if (fabs(flDiff) > 0.1f)
- {
- bMoved = true;
- }
-
- // Update yaw
- flDiff = AngleNormalize(UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1f * MaxYawSpeed()));
-
- iPose = LookupPoseParameter(COMBINE_CAMERA_BC_YAW);
- SetPoseParameter(iPose, GetPoseParameter(iPose) + (flDiff / 1.5f));
-
- if (fabs(flDiff) > 0.1f)
- {
- bMoved = true;
- }
-
- if (bMoved && (m_flMoveSoundTime < gpGlobals->curtime))
- {
- EmitSound("NPC_CombineCamera.Move");
- m_flMoveSoundTime = gpGlobals->curtime + CAMERA_MOVE_INTERVAL;
- }
-
- // You're going to make decisions based on this info. So bump the bone cache after you calculate everything
- InvalidateBoneCache();
-
- return bMoved;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-Vector CNPC_CombineCamera::HeadDirection2D()
-{
- Vector vecMuzzle, vecMuzzleDir;
-
- GetAttachment("eyes", vecMuzzle, &vecMuzzleDir );
- vecMuzzleDir.z = 0;
- VectorNormalize(vecMuzzleDir);
-
- return vecMuzzleDir;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CNPC_CombineCamera::FVisible(CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker)
-{
- CBaseEntity *pHitEntity = NULL;
- if ( BaseClass::FVisible( pEntity, traceMask, &pHitEntity ) )
- return true;
-
- // If we hit something that's okay to hit anyway, still fire
- if ( pHitEntity && pHitEntity->MyCombatCharacterPointer() )
- {
- if (IRelationType(pHitEntity) == D_HT)
- return true;
- }
-
- if (ppBlocker)
- {
- *ppBlocker = pHitEntity;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Enemies are only valid if they're inside our radius
-//-----------------------------------------------------------------------------
-bool CNPC_CombineCamera::IsValidEnemy( CBaseEntity *pEnemy )
-{
- Vector vecDelta = pEnemy->GetAbsOrigin() - GetAbsOrigin();
- float flDist = vecDelta.Length();
- if ( (flDist > m_nOuterRadius) || !FInViewCone(pEnemy) )
- return false;
-
- return BaseClass::IsValidEnemy( pEnemy );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Called when we have no scripted target. Looks for new enemies to track.
-//-----------------------------------------------------------------------------
-CBaseEntity *CNPC_CombineCamera::MaintainEnemy()
-{
- if (HasSpawnFlags(SF_COMBINE_CAMERA_IGNOREENEMIES))
- return NULL;
-
- GetSenses()->Look(m_nOuterRadius);
-
- CBaseEntity *pEnemy = BestEnemy();
- if (pEnemy)
- {
- // See if our best enemy is too far away to care about.
- Vector vecDelta = pEnemy->GetAbsOrigin() - GetAbsOrigin();
- float flDist = vecDelta.Length();
- if (flDist < m_nOuterRadius)
- {
- if (FInViewCone(pEnemy))
- {
- // dvs: HACK: for checking multiple view cones
- float flSaveFieldOfView = m_flFieldOfView;
- m_flFieldOfView = CAMERA_FOV_NARROW;
-
- // Is the target visible?
- bool bVisible = FVisible(pEnemy);
- m_flFieldOfView = flSaveFieldOfView;
- if ( bVisible )
- return pEnemy;
- }
- }
- }
-
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Think while actively tracking a target.
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::ActiveThink()
-{
- // Allow descended classes a chance to do something before the think function
- if (PreThink(CAMERA_ACTIVE))
- return;
-
- // No active target, look for suspicious characters.
- CBaseEntity *pTarget = MaintainEnemy();
- if ( !pTarget )
- {
- // Nobody suspicious. Go back to being idle.
- m_hEnemyTarget = NULL;
- EmitSound("NPC_CombineCamera.BecomeIdle");
- SetAngry(false);
- SetThink(&CNPC_CombineCamera::SearchThink);
- SetNextThink( gpGlobals->curtime );
- return;
- }
-
- // Examine the target until it reaches our inner radius
- if ( pTarget != m_hEnemyTarget )
- {
- Vector vecDelta = pTarget->GetAbsOrigin() - GetAbsOrigin();
- float flDist = vecDelta.Length();
- if ( (flDist < m_nInnerRadius) && FInViewCone(pTarget) )
- {
- m_OnFoundEnemy.Set(pTarget, pTarget, this);
-
- // If it's a citizen, it's ok. If it's the player, it's not ok.
- if ( pTarget->IsPlayer() )
- {
- SetEyeState(CAMERA_EYE_FOUND_TARGET);
-
- if (HasSpawnFlags(SF_COMBINE_CAMERA_BECOMEANGRY))
- {
- SetAngry(true);
- }
- else
- {
- EmitSound("NPC_CombineCamera.Active");
- }
-
- m_OnFoundPlayer.Set(pTarget, pTarget, this);
- m_hEnemyTarget = pTarget;
- }
- else
- {
- SetEyeState(CAMERA_EYE_HAPPY);
- m_flEyeHappyTime = gpGlobals->curtime + 2.0;
-
- // Now forget about this target forever
- AddEntityRelationship( pTarget, D_NU, 99 );
- }
- }
- else
- {
- // If we get angry automatically, we get un-angry automatically
- if ( HasSpawnFlags(SF_COMBINE_CAMERA_BECOMEANGRY) && m_bAngry )
- {
- SetAngry(false);
- }
- m_hEnemyTarget = NULL;
-
- // We don't quite see this guy, but we sense him.
- SetEyeState(CAMERA_EYE_SEEKING_TARGET);
- }
- }
-
- // Update our think time
- SetNextThink( gpGlobals->curtime + 0.1f );
-
- TrackTarget(pTarget);
- MaintainEye();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pTarget -
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::TrackTarget( CBaseEntity *pTarget )
-{
- if (!pTarget)
- return;
-
- // Calculate direction to target
- Vector vecMid = EyePosition();
- Vector vecMidTarget = pTarget->BodyTarget(vecMid);
- Vector vecDirToTarget = vecMidTarget - vecMid;
-
- // We want to look at the target's eyes so we don't jitter
- Vector vecDirToTargetEyes = pTarget->WorldSpaceCenter() - vecMid;
- VectorNormalize(vecDirToTargetEyes);
-
- QAngle vecAnglesToTarget;
- VectorAngles(vecDirToTargetEyes, vecAnglesToTarget);
-
- // Draw debug info
- if (g_debug_combine_camera.GetBool())
- {
- NDebugOverlay::Cross3D(vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05);
- NDebugOverlay::Cross3D(pTarget->WorldSpaceCenter(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05);
- NDebugOverlay::Line(vecMid, pTarget->WorldSpaceCenter(), 0, 255, 0, false, 0.05);
-
- NDebugOverlay::Cross3D(vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05);
- NDebugOverlay::Cross3D(vecMidTarget, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05);
- NDebugOverlay::Line(vecMid, vecMidTarget, 0, 255, 0, false, 0.05f);
- }
-
- Vector vecMuzzle, vecMuzzleDir;
- QAngle vecMuzzleAng;
-
- GetAttachment("eyes", vecMuzzle, &vecMuzzleDir);
-
- SetIdealActivity((Activity) ACT_COMBINE_CAMERA_OPEN_IDLE);
-
- m_vecGoalAngles.y = vecAnglesToTarget.y;
- m_vecGoalAngles.x = vecAnglesToTarget.x;
-
- // Turn to face
- UpdateFacing();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::MaintainEye()
-{
- // Angry cameras take a few pictures of their target.
- if ((m_bAngry) && (m_nClickCount <= 3))
- {
- if ((m_flClickTime != 0) && (m_flClickTime < gpGlobals->curtime))
- {
- m_pEyeFlash->SetScale(1.0);
- m_pEyeFlash->SetBrightness(255);
- m_pEyeFlash->SetColor(255,255,255);
-
- EmitSound("NPC_CombineCamera.Click");
-
- m_flTurnOffEyeFlashTime = gpGlobals->curtime + 0.1;
- m_flClickTime = gpGlobals->curtime + CAMERA_CLICK_INTERVAL;
- }
- else if ((m_flTurnOffEyeFlashTime != 0) && (m_flTurnOffEyeFlashTime < gpGlobals->curtime))
- {
- m_flTurnOffEyeFlashTime = 0;
- m_pEyeFlash->SetBrightness( 0, 0.25f );
- m_nClickCount++;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Target doesn't exist or has eluded us, so search for one
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::SearchThink()
-{
- // Allow descended classes a chance to do something before the think function
- if (PreThink(CAMERA_SEARCHING))
- return;
-
- SetNextThink( gpGlobals->curtime + 0.05f );
-
- SetIdealActivity((Activity) ACT_COMBINE_CAMERA_OPEN_IDLE);
-
- if ( !GetTarget() )
- {
- // Try to acquire a new target
- if (MaintainEnemy())
- {
- SetThink( &CNPC_CombineCamera::ActiveThink );
- return;
- }
- }
-
- // Display that we're scanning
- m_vecGoalAngles.x = 15.0f;
- m_vecGoalAngles.y = GetAbsAngles().y + (sin(gpGlobals->curtime * 2.0f) * 45.0f);
-
- // Turn and ping
- UpdateFacing();
- Ping();
-
- SetEyeState(CAMERA_EYE_IDLE);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Allows a generic think function before the others are called
-// Input : state - which state the camera is currently in
-//-----------------------------------------------------------------------------
-bool CNPC_CombineCamera::PreThink(cameraState_e state)
-{
- CheckPVSCondition();
-
- MaintainActivity();
- StudioFrameAdvance();
-
- // If we're disabled, shut down
- if ( !m_bEnabled )
- {
- SetIdealActivity((Activity) ACT_COMBINE_CAMERA_CLOSED_IDLE);
- SetNextThink( gpGlobals->curtime + 0.1f );
- return true;
- }
-
- // Do not interrupt current think function
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Sets the state of the glowing eye attached to the camera
-// Input : state - state the eye should be in
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::SetEyeState(eyeState_t state)
-{
- // Must have a valid eye to affect
- if (m_pEyeGlow == NULL)
- return;
-
- if (m_bAngry)
- {
- m_pEyeGlow->SetColor(255, 0, 0);
- m_pEyeGlow->SetBrightness(164, 0.1f);
- m_pEyeGlow->SetScale(0.4f, 0.1f);
- return;
- }
-
- // If we're switching to IDLE, and we're still happy, use happy instead
- if ( state == CAMERA_EYE_IDLE && m_flEyeHappyTime > gpGlobals->curtime )
- {
- state = CAMERA_EYE_HAPPY;
- }
-
- // Set the state
- switch (state)
- {
- default:
- case CAMERA_EYE_IDLE:
- {
- m_pEyeGlow->SetColor(0, 255, 0);
- m_pEyeGlow->SetBrightness(164, 0.1f);
- m_pEyeGlow->SetScale(0.4f, 0.1f);
- break;
- }
-
- case CAMERA_EYE_SEEKING_TARGET:
- {
- // Toggle our state
- m_bBlinkState = !m_bBlinkState;
-
- // Amber
- m_pEyeGlow->SetColor(255, 128, 0);
-
- if (m_bBlinkState)
- {
- // Fade up and scale up
- m_pEyeGlow->SetScale(0.25f, 0.1f);
- m_pEyeGlow->SetBrightness(164, 0.1f);
- }
- else
- {
- // Fade down and scale down
- m_pEyeGlow->SetScale(0.2f, 0.1f);
- m_pEyeGlow->SetBrightness(64, 0.1f);
- }
-
- break;
- }
-
- case CAMERA_EYE_FOUND_TARGET:
- {
- if (!m_bAngry)
- {
- // Amber
- m_pEyeGlow->SetColor(255, 128, 0);
-
- // Fade up and scale up
- m_pEyeGlow->SetScale(0.45f, 0.1f);
- m_pEyeGlow->SetBrightness(220, 0.1f);
- }
- else
- {
- m_pEyeGlow->SetColor(255, 0, 0);
- m_pEyeGlow->SetBrightness(164, 0.1f);
- m_pEyeGlow->SetScale(0.4f, 0.1f);
- }
-
- break;
- }
-
- case CAMERA_EYE_DORMANT: // Fade out and scale down
- {
- m_pEyeGlow->SetColor(0, 255, 0);
- m_pEyeGlow->SetScale(0.1f, 0.5f);
- m_pEyeGlow->SetBrightness(64, 0.5f);
- break;
- }
-
- case CAMERA_EYE_DEAD: // Fade out slowly
- {
- m_pEyeGlow->SetColor(255, 0, 0);
- m_pEyeGlow->SetScale(0.1f, 3.0f);
- m_pEyeGlow->SetBrightness(0, 3.0f);
- break;
- }
-
- case CAMERA_EYE_DISABLED:
- {
- m_pEyeGlow->SetColor(0, 255, 0);
- m_pEyeGlow->SetScale(0.1f, 1.0f);
- m_pEyeGlow->SetBrightness(0, 1.0f);
- break;
- }
-
- case CAMERA_EYE_HAPPY:
- {
- m_pEyeGlow->SetColor(0, 255, 0);
- m_pEyeGlow->SetBrightness(255, 0.1f);
- m_pEyeGlow->SetScale(0.5f, 0.1f);
- break;
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Make a pinging noise so the player knows where we are
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::Ping()
-{
- // See if it's time to ping again
- if (m_flPingTime > gpGlobals->curtime)
- return;
-
- // Ping!
- EmitSound("NPC_CombineCamera.Ping");
- m_flPingTime = gpGlobals->curtime + COMBINE_CAMERA_PING_TIME;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Toggle the camera's state
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::Toggle()
-{
- if (m_bEnabled)
- {
- Disable();
- }
- else
- {
- Enable();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Enable the camera and deploy
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::Enable()
-{
- m_bEnabled = true;
- SetThink(&CNPC_CombineCamera::Deploy);
- SetNextThink( gpGlobals->curtime + 0.05f );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Retire the camera until enabled again
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::Disable()
-{
- m_bEnabled = false;
- m_hEnemyTarget = NULL;
- SetNextThink( gpGlobals->curtime + 0.1f );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Toggle the camera's state via input function
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::InputToggle(inputdata_t &inputdata)
-{
- Toggle();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler to enable the camera.
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::InputEnable(inputdata_t &inputdata)
-{
- Enable();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler to disable the camera.
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::InputDisable(inputdata_t &inputdata)
-{
- Disable();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: When we become angry, we make an angry sound and start photographing
-// whatever target we are tracking.
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::SetAngry(bool bAngry)
-{
- if ((bAngry) && (!m_bAngry))
- {
- m_bAngry = true;
- m_nClickCount = 0;
- m_flClickTime = gpGlobals->curtime + 0.4;
- EmitSound("NPC_CombineCamera.Angry");
- SetEyeState(CAMERA_EYE_ANGRY);
- }
- else if ((!bAngry) && (m_bAngry))
- {
- m_bAngry = false;
-
- // make sure the flash is off (we might be in mid-flash)
- m_pEyeFlash->SetBrightness(0);
- SetEyeState(GetTarget() ? CAMERA_EYE_SEEKING_TARGET : CAMERA_EYE_IDLE);
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::InputSetAngry(inputdata_t &inputdata)
-{
- SetAngry(true);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::InputSetIdle(inputdata_t &inputdata)
-{
- SetAngry(false);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::DeathThink()
-{
- if (PreThink(CAMERA_DEAD))
- return;
-
- // Level out our angles
- m_vecGoalAngles = GetAbsAngles();
- SetNextThink( gpGlobals->curtime + 0.1f );
-
- if (m_lifeState != LIFE_DEAD)
- {
- m_lifeState = LIFE_DEAD;
-
- EmitSound("NPC_CombineCamera.Die");
-
- // lots of smoke
- Vector pos;
- CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &pos );
-
- CBroadcastRecipientFilter filter;
-
- te->Smoke(filter, 0.0, &pos, g_sModelIndexSmoke, 2.5, 10);
-
- g_pEffects->Sparks(pos);
-
- SetActivity((Activity) ACT_COMBINE_CAMERA_CLOSE);
- }
-
- StudioFrameAdvance();
-
- if (IsActivityFinished() && (UpdateFacing() == false))
- {
- SetHeight(COMBINE_CAMERA_RETRACT_HEIGHT);
-
- m_flPlaybackRate = 0;
- SetThink(NULL);
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : height -
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::SetHeight(float height)
-{
- Vector forward, right, up;
- AngleVectors(GetLocalAngles(), &forward, &right, &up);
-
- Vector mins = (forward * -16.0f) + (right * -16.0f);
- Vector maxs = (forward * 16.0f) + (right * 16.0f) + (up * -height);
-
- if (mins.x > maxs.x)
- {
- V_swap(mins.x, maxs.x);
- }
-
- if (mins.y > maxs.y)
- {
- V_swap(mins.y, maxs.y);
- }
-
- if (mins.z > maxs.z)
- {
- V_swap(mins.z, maxs.z);
- }
-
- SetCollisionBounds(mins, maxs);
-
- UTIL_SetSize(this, mins, maxs);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw any debug text overlays
-//-----------------------------------------------------------------------------
-int CNPC_CombineCamera::DrawDebugTextOverlays(void)
-{
- int text_offset = BaseClass::DrawDebugTextOverlays();
-
- if (m_debugOverlays & OVERLAY_TEXT_BIT)
- {
- char tempstr[512];
-
- Q_snprintf( tempstr, sizeof( tempstr ),"Enemy : %s", m_hEnemyTarget ? m_hEnemyTarget->GetDebugName() : "<none>");
- EntityText(text_offset,tempstr,0);
- text_offset++;
- }
- return text_offset;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CombineCamera::DrawDebugGeometryOverlays(void)
-{
- // ------------------------------
- // Draw viewcone if selected
- // ------------------------------
- if ((m_debugOverlays & OVERLAY_NPC_VIEWCONE_BIT))
- {
- float flViewRange = acos(CAMERA_FOV_NARROW);
- Vector vEyeDir = EyeDirection2D( );
- Vector vLeftDir, vRightDir;
- float fSin, fCos;
- SinCos( flViewRange, &fSin, &fCos );
-
- vLeftDir.x = vEyeDir.x * fCos - vEyeDir.y * fSin;
- vLeftDir.y = vEyeDir.x * fSin + vEyeDir.y * fCos;
- vLeftDir.z = vEyeDir.z;
- fSin = sin(-flViewRange);
- fCos = cos(-flViewRange);
- vRightDir.x = vEyeDir.x * fCos - vEyeDir.y * fSin;
- vRightDir.y = vEyeDir.x * fSin + vEyeDir.y * fCos;
- vRightDir.z = vEyeDir.z;
-
- NDebugOverlay::BoxDirection(EyePosition(), Vector(0,0,-40), Vector(200,0,40), vLeftDir, 255, 255, 0, 50, 0 );
- NDebugOverlay::BoxDirection(EyePosition(), Vector(0,0,-40), Vector(200,0,40), vRightDir, 255, 255, 0, 50, 0 );
- NDebugOverlay::Box(EyePosition(), -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, 128, 0 );
- }
-
- BaseClass::DrawDebugGeometryOverlays();
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Security cameras will track a default target (if they have one)
+// until they either acquire an enemy to track or are told to track
+// an entity via an input. If they lose their target they will
+// revert to tracking their default target. They acquire enemies
+// using the relationship table just like any other NPC.
+//
+// Cameras have two zones of awareness, an inner zone formed by the
+// intersection of an inner FOV and an inner radius. The camera is
+// fully aware of entities in the inner zone and will acquire enemies
+// seen there.
+//
+// The outer zone of awareness is formed by the intersection of an
+// outer FOV and an outer radius. The camera is only vaguely aware
+// of entities in the outer zone and will flash amber when enemies
+// are there, but will otherwise ignore them.
+//
+// They can be made angry via an input, at which time they sound an
+// alarm and snap a few pictures of whatever they are tracking. They
+// can also be set to become angry anytime they acquire an enemy.
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "ai_basenpc.h"
+#include "ai_senses.h"
+#include "ai_memory.h"
+#include "engine/IEngineSound.h"
+#include "ammodef.h"
+#include "Sprite.h"
+#include "hl2/hl2_player.h"
+#include "soundenvelope.h"
+#include "explode.h"
+#include "IEffects.h"
+#include "animation.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// Debug visualization
+ConVar g_debug_combine_camera("g_debug_combine_camera", "0");
+
+#define COMBINE_CAMERA_MODEL "models/combine_camera/combine_camera.mdl"
+#define COMBINE_CAMERA_GLOW_SPRITE "sprites/glow1.vmt"
+#define COMBINE_CAMERA_FLASH_SPRITE "sprites/light_glow03.vmt"
+#define COMBINE_CAMERA_BC_YAW "aim_yaw"
+#define COMBINE_CAMERA_BC_PITCH "aim_pitch"
+
+#define COMBINE_CAMERA_SPREAD VECTOR_CONE_2DEGREES
+#define COMBINE_CAMERA_MAX_WAIT 5
+#define COMBINE_CAMERA_PING_TIME 1.0f
+
+// Spawnflags
+#define SF_COMBINE_CAMERA_BECOMEANGRY 0x00000020
+#define SF_COMBINE_CAMERA_IGNOREENEMIES 0x00000040
+#define SF_COMBINE_CAMERA_STARTINACTIVE 0x00000080
+
+// Heights
+#define COMBINE_CAMERA_RETRACT_HEIGHT 24
+#define COMBINE_CAMERA_DEPLOY_HEIGHT 64
+
+
+// Activities
+int ACT_COMBINE_CAMERA_OPEN;
+int ACT_COMBINE_CAMERA_CLOSE;
+int ACT_COMBINE_CAMERA_OPEN_IDLE;
+int ACT_COMBINE_CAMERA_CLOSED_IDLE;
+int ACT_COMBINE_CAMERA_FIRE;
+
+
+const float CAMERA_CLICK_INTERVAL = 0.5f;
+const float CAMERA_MOVE_INTERVAL = 1.0f;
+
+
+//
+// The camera has two FOVs - a wide one for becoming slightly aware of someone,
+// a narrow one for becoming totally aware of them.
+//
+const float CAMERA_FOV_WIDE = 0.5;
+const float CAMERA_FOV_NARROW = 0.707;
+
+
+// Camera states
+enum cameraState_e
+{
+ CAMERA_SEARCHING,
+ CAMERA_AUTO_SEARCHING,
+ CAMERA_ACTIVE,
+ CAMERA_DEAD,
+};
+
+
+// Eye states
+enum eyeState_t
+{
+ CAMERA_EYE_IDLE, // Nothing abnormal in the inner or outer viewcone, dim green.
+ CAMERA_EYE_SEEKING_TARGET, // Something in the outer viewcone, flashes amber as it converges on the target.
+ CAMERA_EYE_FOUND_TARGET, // Something in the inner viewcone, bright amber.
+ CAMERA_EYE_ANGRY, // Found a target that we don't like: angry, bright red.
+ CAMERA_EYE_DORMANT, // Not active
+ CAMERA_EYE_DEAD, // Completely invisible
+ CAMERA_EYE_DISABLED, // Turned off, must be reactivated before it'll deploy again (completely invisible)
+ CAMERA_EYE_HAPPY, // Found a target that we like: go green for a second
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CNPC_CombineCamera : public CAI_BaseNPC
+{
+ DECLARE_CLASS(CNPC_CombineCamera, CAI_BaseNPC);
+public:
+
+ CNPC_CombineCamera();
+ ~CNPC_CombineCamera();
+
+ void Precache();
+ void Spawn();
+ Vector HeadDirection2D();
+
+ int DrawDebugTextOverlays();
+
+ void Deploy();
+ void ActiveThink();
+ void SearchThink();
+ void DeathThink();
+
+ void InputToggle(inputdata_t &inputdata);
+ void InputEnable(inputdata_t &inputdata);
+ void InputDisable(inputdata_t &inputdata);
+ void InputSetAngry(inputdata_t &inputdata);
+ void InputSetIdle(inputdata_t &inputdata);
+
+ void DrawDebugGeometryOverlays(void);
+
+ float MaxYawSpeed();
+
+ int OnTakeDamage(const CTakeDamageInfo &inputInfo);
+
+ Class_T Classify() { return (m_bEnabled) ? CLASS_MILITARY : CLASS_NONE; }
+
+ bool IsValidEnemy( CBaseEntity *pEnemy );
+ bool FVisible(CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL);
+
+ Vector EyeOffset(Activity nActivity)
+ {
+ Vector vecEyeOffset(0,0,-64);
+ GetEyePosition(GetModelPtr(), vecEyeOffset);
+ return vecEyeOffset;
+ }
+
+ Vector EyePosition()
+ {
+ return GetAbsOrigin() + EyeOffset(GetActivity());
+ }
+
+protected:
+
+ CBaseEntity *GetTarget();
+ bool UpdateFacing();
+ void TrackTarget(CBaseEntity *pTarget);
+
+ bool PreThink(cameraState_e state);
+ void SetEyeState(eyeState_t state);
+ void MaintainEye();
+ void Ping();
+ void Toggle();
+ void Enable();
+ void Disable();
+ void SetHeight(float height);
+
+ CBaseEntity *MaintainEnemy();
+ void SetAngry(bool bAngry);
+
+protected:
+ int m_iAmmoType;
+ int m_iMinHealthDmg;
+
+ int m_nInnerRadius; // The camera will only lock onto enemies that are within the inner radius.
+ int m_nOuterRadius; // The camera will flash amber when enemies are within the outer radius, but outside the inner radius.
+
+ bool m_bActive; // The camera is deployed and looking for targets
+ bool m_bAngry; // The camera has gotten angry at someone and sounded an alarm.
+ bool m_bBlinkState;
+ bool m_bEnabled; // Denotes whether the camera is able to deploy or not
+
+ string_t m_sDefaultTarget;
+
+ EHANDLE m_hEnemyTarget; // Entity we acquired as an enemy.
+
+ float m_flPingTime;
+ float m_flClickTime; // Time to take next picture while angry.
+ int m_nClickCount; // Counts pictures taken since we last became angry.
+ float m_flMoveSoundTime;
+ float m_flTurnOffEyeFlashTime;
+ float m_flEyeHappyTime;
+
+ QAngle m_vecGoalAngles;
+
+ CSprite *m_pEyeGlow;
+ CSprite *m_pEyeFlash;
+
+ DECLARE_DATADESC();
+};
+
+
+BEGIN_DATADESC(CNPC_CombineCamera)
+
+ DEFINE_FIELD(m_iAmmoType, FIELD_INTEGER),
+ DEFINE_KEYFIELD(m_iMinHealthDmg, FIELD_INTEGER, "minhealthdmg"),
+ DEFINE_KEYFIELD(m_nInnerRadius, FIELD_INTEGER, "innerradius"),
+ DEFINE_KEYFIELD(m_nOuterRadius, FIELD_INTEGER, "outerradius"),
+ DEFINE_FIELD(m_bActive, FIELD_BOOLEAN),
+ DEFINE_FIELD(m_bAngry, FIELD_BOOLEAN),
+ DEFINE_FIELD(m_bBlinkState, FIELD_BOOLEAN),
+ DEFINE_FIELD(m_bEnabled, FIELD_BOOLEAN),
+ DEFINE_KEYFIELD(m_sDefaultTarget, FIELD_STRING, "defaulttarget"),
+ DEFINE_FIELD(m_hEnemyTarget, FIELD_EHANDLE),
+ DEFINE_FIELD(m_flPingTime, FIELD_TIME),
+ DEFINE_FIELD(m_flClickTime, FIELD_TIME),
+ DEFINE_FIELD(m_nClickCount, FIELD_INTEGER ),
+ DEFINE_FIELD(m_flMoveSoundTime, FIELD_TIME),
+ DEFINE_FIELD(m_flTurnOffEyeFlashTime, FIELD_TIME),
+ DEFINE_FIELD(m_flEyeHappyTime, FIELD_TIME),
+ DEFINE_FIELD(m_vecGoalAngles, FIELD_VECTOR),
+ DEFINE_FIELD(m_pEyeGlow, FIELD_CLASSPTR),
+ DEFINE_FIELD(m_pEyeFlash, FIELD_CLASSPTR),
+
+ DEFINE_THINKFUNC(Deploy),
+ DEFINE_THINKFUNC(ActiveThink),
+ DEFINE_THINKFUNC(SearchThink),
+ DEFINE_THINKFUNC(DeathThink),
+
+ // Inputs
+ DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle),
+ DEFINE_INPUTFUNC(FIELD_VOID, "Enable", InputEnable),
+ DEFINE_INPUTFUNC(FIELD_VOID, "Disable", InputDisable),
+ DEFINE_INPUTFUNC(FIELD_VOID, "SetAngry", InputSetAngry),
+ DEFINE_INPUTFUNC(FIELD_VOID, "SetIdle", InputSetIdle),
+
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS(npc_combine_camera, CNPC_CombineCamera);
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CNPC_CombineCamera::CNPC_CombineCamera()
+{
+ m_bActive = false;
+ m_pEyeGlow = NULL;
+ m_pEyeFlash = NULL;
+ m_iAmmoType = -1;
+ m_iMinHealthDmg = 0;
+ m_flPingTime = 0;
+ m_bBlinkState = false;
+ m_bEnabled = false;
+
+ m_vecGoalAngles.Init();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CNPC_CombineCamera::~CNPC_CombineCamera()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Precache
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::Precache()
+{
+ PrecacheModel(COMBINE_CAMERA_MODEL);
+ PrecacheModel(COMBINE_CAMERA_GLOW_SPRITE);
+ PrecacheModel(COMBINE_CAMERA_FLASH_SPRITE);
+
+ // Activities
+ ADD_CUSTOM_ACTIVITY(CNPC_CombineCamera, ACT_COMBINE_CAMERA_OPEN);
+ ADD_CUSTOM_ACTIVITY(CNPC_CombineCamera, ACT_COMBINE_CAMERA_CLOSE);
+ ADD_CUSTOM_ACTIVITY(CNPC_CombineCamera, ACT_COMBINE_CAMERA_CLOSED_IDLE);
+ ADD_CUSTOM_ACTIVITY(CNPC_CombineCamera, ACT_COMBINE_CAMERA_OPEN_IDLE);
+ ADD_CUSTOM_ACTIVITY(CNPC_CombineCamera, ACT_COMBINE_CAMERA_FIRE);
+
+ PrecacheScriptSound( "NPC_CombineCamera.Move" );
+ PrecacheScriptSound( "NPC_CombineCamera.BecomeIdle" );
+ PrecacheScriptSound( "NPC_CombineCamera.Active" );
+ PrecacheScriptSound( "NPC_CombineCamera.Click" );
+ PrecacheScriptSound( "NPC_CombineCamera.Ping" );
+ PrecacheScriptSound( "NPC_CombineCamera.Angry" );
+ PrecacheScriptSound( "NPC_CombineCamera.Die" );
+
+ BaseClass::Precache();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Spawn the entity
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::Spawn()
+{
+ Precache();
+
+ SetModel(COMBINE_CAMERA_MODEL);
+
+ m_pEyeFlash = CSprite::SpriteCreate(COMBINE_CAMERA_FLASH_SPRITE, GetLocalOrigin(), FALSE);
+ m_pEyeFlash->SetTransparency(kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation);
+ m_pEyeFlash->SetAttachment(this, 2);
+ m_pEyeFlash->SetBrightness(0);
+ m_pEyeFlash->SetScale(1.0);
+
+ BaseClass::Spawn();
+
+ m_HackedGunPos = Vector(0, 0, 12.75);
+ SetViewOffset(EyeOffset(ACT_IDLE));
+ m_flFieldOfView = CAMERA_FOV_WIDE;
+ m_takedamage = DAMAGE_YES;
+ m_iHealth = 50;
+ m_bloodColor = BLOOD_COLOR_MECH;
+
+ SetSolid(SOLID_BBOX);
+ AddSolidFlags(FSOLID_NOT_STANDABLE);
+
+ SetHeight(COMBINE_CAMERA_RETRACT_HEIGHT);
+
+ AddFlag(FL_AIMTARGET);
+
+ SetPoseParameter(COMBINE_CAMERA_BC_YAW, 0);
+ SetPoseParameter(COMBINE_CAMERA_BC_PITCH, 0);
+
+ m_iAmmoType = GetAmmoDef()->Index("Pistol");
+
+ // Create our eye sprite
+ m_pEyeGlow = CSprite::SpriteCreate(COMBINE_CAMERA_GLOW_SPRITE, GetLocalOrigin(), false);
+ m_pEyeGlow->SetTransparency(kRenderWorldGlow, 255, 0, 0, 128, kRenderFxNoDissipation);
+ m_pEyeGlow->SetAttachment(this, 2);
+
+ // Set our enabled state
+ m_bEnabled = ((m_spawnflags & SF_COMBINE_CAMERA_STARTINACTIVE) == false);
+
+ // Make sure the radii are sane.
+ if (m_nOuterRadius <= 0)
+ {
+ m_nOuterRadius = 300;
+ }
+
+ if (m_nInnerRadius <= 0)
+ {
+ m_nInnerRadius = 450;
+ }
+
+ if (m_nOuterRadius < m_nInnerRadius)
+ {
+ V_swap(m_nOuterRadius, m_nInnerRadius);
+ }
+
+ // Do we start active?
+ if (m_bEnabled)
+ {
+ Deploy();
+ }
+ else
+ {
+ SetEyeState(CAMERA_EYE_DISABLED);
+ }
+
+ //Adrian: No shadows on these guys.
+ AddEffects( EF_NOSHADOW );
+
+ // Stagger our starting times
+ SetNextThink( gpGlobals->curtime + random->RandomFloat(0.1f, 0.3f) );
+
+ // Don't allow us to skip animation setup because our attachments are critical to us!
+ SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseEntity *CNPC_CombineCamera::GetTarget()
+{
+ return m_hEnemyTarget;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CNPC_CombineCamera::OnTakeDamage(const CTakeDamageInfo &inputInfo)
+{
+ if (!m_takedamage)
+ return 0;
+
+ CTakeDamageInfo info = inputInfo;
+
+ if (m_bActive == false)
+ info.ScaleDamage(0.1f);
+
+ // If attacker can't do at least the min required damage to us, don't take any damage from them
+ if (info.GetDamage() < m_iMinHealthDmg)
+ return 0;
+
+ m_iHealth -= info.GetDamage();
+
+ if (m_iHealth <= 0)
+ {
+ m_iHealth = 0;
+ m_takedamage = DAMAGE_NO;
+
+ RemoveFlag(FL_NPC); // why are they set in the first place???
+
+ // FIXME: This needs to throw a ragdoll gib or something other than animating the retraction -- jdw
+
+ ExplosionCreate(GetAbsOrigin(), GetLocalAngles(), this, 100, 100, false);
+ SetThink(&CNPC_CombineCamera::DeathThink);
+
+ StopSound("Alert");
+
+ m_OnDamaged.FireOutput(info.GetInflictor(), this);
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ return 0;
+ }
+
+ return 1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Deploy and start searching for targets.
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::Deploy()
+{
+ m_vecGoalAngles = GetAbsAngles();
+
+ SetNextThink( gpGlobals->curtime );
+
+ SetEyeState(CAMERA_EYE_IDLE);
+ m_bActive = true;
+
+ SetHeight(COMBINE_CAMERA_DEPLOY_HEIGHT);
+ SetIdealActivity((Activity) ACT_COMBINE_CAMERA_OPEN_IDLE);
+ m_flPlaybackRate = 0;
+ SetThink(&CNPC_CombineCamera::SearchThink);
+
+ EmitSound("NPC_CombineCamera.Move");
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the speed at which the camera can face a target
+//-----------------------------------------------------------------------------
+float CNPC_CombineCamera::MaxYawSpeed()
+{
+ if (m_hEnemyTarget)
+ return 180.0f;
+
+ return 60.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Causes the camera to face its desired angles
+//-----------------------------------------------------------------------------
+bool CNPC_CombineCamera::UpdateFacing()
+{
+ bool bMoved = false;
+ matrix3x4_t localToWorld;
+
+ GetAttachment(LookupAttachment("eyes"), localToWorld);
+
+ Vector vecGoalDir;
+ AngleVectors(m_vecGoalAngles, &vecGoalDir );
+
+ Vector vecGoalLocalDir;
+ VectorIRotate(vecGoalDir, localToWorld, vecGoalLocalDir);
+
+ QAngle vecGoalLocalAngles;
+ VectorAngles(vecGoalLocalDir, vecGoalLocalAngles);
+
+ // Update pitch
+ float flDiff = AngleNormalize(UTIL_ApproachAngle( vecGoalLocalAngles.x, 0.0, 0.1f * MaxYawSpeed()));
+
+ int iPose = LookupPoseParameter(COMBINE_CAMERA_BC_PITCH);
+ SetPoseParameter(iPose, GetPoseParameter(iPose) + (flDiff / 1.5f));
+
+ if (fabs(flDiff) > 0.1f)
+ {
+ bMoved = true;
+ }
+
+ // Update yaw
+ flDiff = AngleNormalize(UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1f * MaxYawSpeed()));
+
+ iPose = LookupPoseParameter(COMBINE_CAMERA_BC_YAW);
+ SetPoseParameter(iPose, GetPoseParameter(iPose) + (flDiff / 1.5f));
+
+ if (fabs(flDiff) > 0.1f)
+ {
+ bMoved = true;
+ }
+
+ if (bMoved && (m_flMoveSoundTime < gpGlobals->curtime))
+ {
+ EmitSound("NPC_CombineCamera.Move");
+ m_flMoveSoundTime = gpGlobals->curtime + CAMERA_MOVE_INTERVAL;
+ }
+
+ // You're going to make decisions based on this info. So bump the bone cache after you calculate everything
+ InvalidateBoneCache();
+
+ return bMoved;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Vector CNPC_CombineCamera::HeadDirection2D()
+{
+ Vector vecMuzzle, vecMuzzleDir;
+
+ GetAttachment("eyes", vecMuzzle, &vecMuzzleDir );
+ vecMuzzleDir.z = 0;
+ VectorNormalize(vecMuzzleDir);
+
+ return vecMuzzleDir;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEntity -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CNPC_CombineCamera::FVisible(CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker)
+{
+ CBaseEntity *pHitEntity = NULL;
+ if ( BaseClass::FVisible( pEntity, traceMask, &pHitEntity ) )
+ return true;
+
+ // If we hit something that's okay to hit anyway, still fire
+ if ( pHitEntity && pHitEntity->MyCombatCharacterPointer() )
+ {
+ if (IRelationType(pHitEntity) == D_HT)
+ return true;
+ }
+
+ if (ppBlocker)
+ {
+ *ppBlocker = pHitEntity;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Enemies are only valid if they're inside our radius
+//-----------------------------------------------------------------------------
+bool CNPC_CombineCamera::IsValidEnemy( CBaseEntity *pEnemy )
+{
+ Vector vecDelta = pEnemy->GetAbsOrigin() - GetAbsOrigin();
+ float flDist = vecDelta.Length();
+ if ( (flDist > m_nOuterRadius) || !FInViewCone(pEnemy) )
+ return false;
+
+ return BaseClass::IsValidEnemy( pEnemy );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when we have no scripted target. Looks for new enemies to track.
+//-----------------------------------------------------------------------------
+CBaseEntity *CNPC_CombineCamera::MaintainEnemy()
+{
+ if (HasSpawnFlags(SF_COMBINE_CAMERA_IGNOREENEMIES))
+ return NULL;
+
+ GetSenses()->Look(m_nOuterRadius);
+
+ CBaseEntity *pEnemy = BestEnemy();
+ if (pEnemy)
+ {
+ // See if our best enemy is too far away to care about.
+ Vector vecDelta = pEnemy->GetAbsOrigin() - GetAbsOrigin();
+ float flDist = vecDelta.Length();
+ if (flDist < m_nOuterRadius)
+ {
+ if (FInViewCone(pEnemy))
+ {
+ // dvs: HACK: for checking multiple view cones
+ float flSaveFieldOfView = m_flFieldOfView;
+ m_flFieldOfView = CAMERA_FOV_NARROW;
+
+ // Is the target visible?
+ bool bVisible = FVisible(pEnemy);
+ m_flFieldOfView = flSaveFieldOfView;
+ if ( bVisible )
+ return pEnemy;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Think while actively tracking a target.
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::ActiveThink()
+{
+ // Allow descended classes a chance to do something before the think function
+ if (PreThink(CAMERA_ACTIVE))
+ return;
+
+ // No active target, look for suspicious characters.
+ CBaseEntity *pTarget = MaintainEnemy();
+ if ( !pTarget )
+ {
+ // Nobody suspicious. Go back to being idle.
+ m_hEnemyTarget = NULL;
+ EmitSound("NPC_CombineCamera.BecomeIdle");
+ SetAngry(false);
+ SetThink(&CNPC_CombineCamera::SearchThink);
+ SetNextThink( gpGlobals->curtime );
+ return;
+ }
+
+ // Examine the target until it reaches our inner radius
+ if ( pTarget != m_hEnemyTarget )
+ {
+ Vector vecDelta = pTarget->GetAbsOrigin() - GetAbsOrigin();
+ float flDist = vecDelta.Length();
+ if ( (flDist < m_nInnerRadius) && FInViewCone(pTarget) )
+ {
+ m_OnFoundEnemy.Set(pTarget, pTarget, this);
+
+ // If it's a citizen, it's ok. If it's the player, it's not ok.
+ if ( pTarget->IsPlayer() )
+ {
+ SetEyeState(CAMERA_EYE_FOUND_TARGET);
+
+ if (HasSpawnFlags(SF_COMBINE_CAMERA_BECOMEANGRY))
+ {
+ SetAngry(true);
+ }
+ else
+ {
+ EmitSound("NPC_CombineCamera.Active");
+ }
+
+ m_OnFoundPlayer.Set(pTarget, pTarget, this);
+ m_hEnemyTarget = pTarget;
+ }
+ else
+ {
+ SetEyeState(CAMERA_EYE_HAPPY);
+ m_flEyeHappyTime = gpGlobals->curtime + 2.0;
+
+ // Now forget about this target forever
+ AddEntityRelationship( pTarget, D_NU, 99 );
+ }
+ }
+ else
+ {
+ // If we get angry automatically, we get un-angry automatically
+ if ( HasSpawnFlags(SF_COMBINE_CAMERA_BECOMEANGRY) && m_bAngry )
+ {
+ SetAngry(false);
+ }
+ m_hEnemyTarget = NULL;
+
+ // We don't quite see this guy, but we sense him.
+ SetEyeState(CAMERA_EYE_SEEKING_TARGET);
+ }
+ }
+
+ // Update our think time
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ TrackTarget(pTarget);
+ MaintainEye();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pTarget -
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::TrackTarget( CBaseEntity *pTarget )
+{
+ if (!pTarget)
+ return;
+
+ // Calculate direction to target
+ Vector vecMid = EyePosition();
+ Vector vecMidTarget = pTarget->BodyTarget(vecMid);
+ Vector vecDirToTarget = vecMidTarget - vecMid;
+
+ // We want to look at the target's eyes so we don't jitter
+ Vector vecDirToTargetEyes = pTarget->WorldSpaceCenter() - vecMid;
+ VectorNormalize(vecDirToTargetEyes);
+
+ QAngle vecAnglesToTarget;
+ VectorAngles(vecDirToTargetEyes, vecAnglesToTarget);
+
+ // Draw debug info
+ if (g_debug_combine_camera.GetBool())
+ {
+ NDebugOverlay::Cross3D(vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05);
+ NDebugOverlay::Cross3D(pTarget->WorldSpaceCenter(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05);
+ NDebugOverlay::Line(vecMid, pTarget->WorldSpaceCenter(), 0, 255, 0, false, 0.05);
+
+ NDebugOverlay::Cross3D(vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05);
+ NDebugOverlay::Cross3D(vecMidTarget, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05);
+ NDebugOverlay::Line(vecMid, vecMidTarget, 0, 255, 0, false, 0.05f);
+ }
+
+ Vector vecMuzzle, vecMuzzleDir;
+ QAngle vecMuzzleAng;
+
+ GetAttachment("eyes", vecMuzzle, &vecMuzzleDir);
+
+ SetIdealActivity((Activity) ACT_COMBINE_CAMERA_OPEN_IDLE);
+
+ m_vecGoalAngles.y = vecAnglesToTarget.y;
+ m_vecGoalAngles.x = vecAnglesToTarget.x;
+
+ // Turn to face
+ UpdateFacing();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::MaintainEye()
+{
+ // Angry cameras take a few pictures of their target.
+ if ((m_bAngry) && (m_nClickCount <= 3))
+ {
+ if ((m_flClickTime != 0) && (m_flClickTime < gpGlobals->curtime))
+ {
+ m_pEyeFlash->SetScale(1.0);
+ m_pEyeFlash->SetBrightness(255);
+ m_pEyeFlash->SetColor(255,255,255);
+
+ EmitSound("NPC_CombineCamera.Click");
+
+ m_flTurnOffEyeFlashTime = gpGlobals->curtime + 0.1;
+ m_flClickTime = gpGlobals->curtime + CAMERA_CLICK_INTERVAL;
+ }
+ else if ((m_flTurnOffEyeFlashTime != 0) && (m_flTurnOffEyeFlashTime < gpGlobals->curtime))
+ {
+ m_flTurnOffEyeFlashTime = 0;
+ m_pEyeFlash->SetBrightness( 0, 0.25f );
+ m_nClickCount++;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Target doesn't exist or has eluded us, so search for one
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::SearchThink()
+{
+ // Allow descended classes a chance to do something before the think function
+ if (PreThink(CAMERA_SEARCHING))
+ return;
+
+ SetNextThink( gpGlobals->curtime + 0.05f );
+
+ SetIdealActivity((Activity) ACT_COMBINE_CAMERA_OPEN_IDLE);
+
+ if ( !GetTarget() )
+ {
+ // Try to acquire a new target
+ if (MaintainEnemy())
+ {
+ SetThink( &CNPC_CombineCamera::ActiveThink );
+ return;
+ }
+ }
+
+ // Display that we're scanning
+ m_vecGoalAngles.x = 15.0f;
+ m_vecGoalAngles.y = GetAbsAngles().y + (sin(gpGlobals->curtime * 2.0f) * 45.0f);
+
+ // Turn and ping
+ UpdateFacing();
+ Ping();
+
+ SetEyeState(CAMERA_EYE_IDLE);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allows a generic think function before the others are called
+// Input : state - which state the camera is currently in
+//-----------------------------------------------------------------------------
+bool CNPC_CombineCamera::PreThink(cameraState_e state)
+{
+ CheckPVSCondition();
+
+ MaintainActivity();
+ StudioFrameAdvance();
+
+ // If we're disabled, shut down
+ if ( !m_bEnabled )
+ {
+ SetIdealActivity((Activity) ACT_COMBINE_CAMERA_CLOSED_IDLE);
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ return true;
+ }
+
+ // Do not interrupt current think function
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the state of the glowing eye attached to the camera
+// Input : state - state the eye should be in
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::SetEyeState(eyeState_t state)
+{
+ // Must have a valid eye to affect
+ if (m_pEyeGlow == NULL)
+ return;
+
+ if (m_bAngry)
+ {
+ m_pEyeGlow->SetColor(255, 0, 0);
+ m_pEyeGlow->SetBrightness(164, 0.1f);
+ m_pEyeGlow->SetScale(0.4f, 0.1f);
+ return;
+ }
+
+ // If we're switching to IDLE, and we're still happy, use happy instead
+ if ( state == CAMERA_EYE_IDLE && m_flEyeHappyTime > gpGlobals->curtime )
+ {
+ state = CAMERA_EYE_HAPPY;
+ }
+
+ // Set the state
+ switch (state)
+ {
+ default:
+ case CAMERA_EYE_IDLE:
+ {
+ m_pEyeGlow->SetColor(0, 255, 0);
+ m_pEyeGlow->SetBrightness(164, 0.1f);
+ m_pEyeGlow->SetScale(0.4f, 0.1f);
+ break;
+ }
+
+ case CAMERA_EYE_SEEKING_TARGET:
+ {
+ // Toggle our state
+ m_bBlinkState = !m_bBlinkState;
+
+ // Amber
+ m_pEyeGlow->SetColor(255, 128, 0);
+
+ if (m_bBlinkState)
+ {
+ // Fade up and scale up
+ m_pEyeGlow->SetScale(0.25f, 0.1f);
+ m_pEyeGlow->SetBrightness(164, 0.1f);
+ }
+ else
+ {
+ // Fade down and scale down
+ m_pEyeGlow->SetScale(0.2f, 0.1f);
+ m_pEyeGlow->SetBrightness(64, 0.1f);
+ }
+
+ break;
+ }
+
+ case CAMERA_EYE_FOUND_TARGET:
+ {
+ if (!m_bAngry)
+ {
+ // Amber
+ m_pEyeGlow->SetColor(255, 128, 0);
+
+ // Fade up and scale up
+ m_pEyeGlow->SetScale(0.45f, 0.1f);
+ m_pEyeGlow->SetBrightness(220, 0.1f);
+ }
+ else
+ {
+ m_pEyeGlow->SetColor(255, 0, 0);
+ m_pEyeGlow->SetBrightness(164, 0.1f);
+ m_pEyeGlow->SetScale(0.4f, 0.1f);
+ }
+
+ break;
+ }
+
+ case CAMERA_EYE_DORMANT: // Fade out and scale down
+ {
+ m_pEyeGlow->SetColor(0, 255, 0);
+ m_pEyeGlow->SetScale(0.1f, 0.5f);
+ m_pEyeGlow->SetBrightness(64, 0.5f);
+ break;
+ }
+
+ case CAMERA_EYE_DEAD: // Fade out slowly
+ {
+ m_pEyeGlow->SetColor(255, 0, 0);
+ m_pEyeGlow->SetScale(0.1f, 3.0f);
+ m_pEyeGlow->SetBrightness(0, 3.0f);
+ break;
+ }
+
+ case CAMERA_EYE_DISABLED:
+ {
+ m_pEyeGlow->SetColor(0, 255, 0);
+ m_pEyeGlow->SetScale(0.1f, 1.0f);
+ m_pEyeGlow->SetBrightness(0, 1.0f);
+ break;
+ }
+
+ case CAMERA_EYE_HAPPY:
+ {
+ m_pEyeGlow->SetColor(0, 255, 0);
+ m_pEyeGlow->SetBrightness(255, 0.1f);
+ m_pEyeGlow->SetScale(0.5f, 0.1f);
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Make a pinging noise so the player knows where we are
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::Ping()
+{
+ // See if it's time to ping again
+ if (m_flPingTime > gpGlobals->curtime)
+ return;
+
+ // Ping!
+ EmitSound("NPC_CombineCamera.Ping");
+ m_flPingTime = gpGlobals->curtime + COMBINE_CAMERA_PING_TIME;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle the camera's state
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::Toggle()
+{
+ if (m_bEnabled)
+ {
+ Disable();
+ }
+ else
+ {
+ Enable();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Enable the camera and deploy
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::Enable()
+{
+ m_bEnabled = true;
+ SetThink(&CNPC_CombineCamera::Deploy);
+ SetNextThink( gpGlobals->curtime + 0.05f );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Retire the camera until enabled again
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::Disable()
+{
+ m_bEnabled = false;
+ m_hEnemyTarget = NULL;
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle the camera's state via input function
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::InputToggle(inputdata_t &inputdata)
+{
+ Toggle();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handler to enable the camera.
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::InputEnable(inputdata_t &inputdata)
+{
+ Enable();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handler to disable the camera.
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::InputDisable(inputdata_t &inputdata)
+{
+ Disable();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: When we become angry, we make an angry sound and start photographing
+// whatever target we are tracking.
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::SetAngry(bool bAngry)
+{
+ if ((bAngry) && (!m_bAngry))
+ {
+ m_bAngry = true;
+ m_nClickCount = 0;
+ m_flClickTime = gpGlobals->curtime + 0.4;
+ EmitSound("NPC_CombineCamera.Angry");
+ SetEyeState(CAMERA_EYE_ANGRY);
+ }
+ else if ((!bAngry) && (m_bAngry))
+ {
+ m_bAngry = false;
+
+ // make sure the flash is off (we might be in mid-flash)
+ m_pEyeFlash->SetBrightness(0);
+ SetEyeState(GetTarget() ? CAMERA_EYE_SEEKING_TARGET : CAMERA_EYE_IDLE);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::InputSetAngry(inputdata_t &inputdata)
+{
+ SetAngry(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::InputSetIdle(inputdata_t &inputdata)
+{
+ SetAngry(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::DeathThink()
+{
+ if (PreThink(CAMERA_DEAD))
+ return;
+
+ // Level out our angles
+ m_vecGoalAngles = GetAbsAngles();
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ if (m_lifeState != LIFE_DEAD)
+ {
+ m_lifeState = LIFE_DEAD;
+
+ EmitSound("NPC_CombineCamera.Die");
+
+ // lots of smoke
+ Vector pos;
+ CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &pos );
+
+ CBroadcastRecipientFilter filter;
+
+ te->Smoke(filter, 0.0, &pos, g_sModelIndexSmoke, 2.5, 10);
+
+ g_pEffects->Sparks(pos);
+
+ SetActivity((Activity) ACT_COMBINE_CAMERA_CLOSE);
+ }
+
+ StudioFrameAdvance();
+
+ if (IsActivityFinished() && (UpdateFacing() == false))
+ {
+ SetHeight(COMBINE_CAMERA_RETRACT_HEIGHT);
+
+ m_flPlaybackRate = 0;
+ SetThink(NULL);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : height -
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::SetHeight(float height)
+{
+ Vector forward, right, up;
+ AngleVectors(GetLocalAngles(), &forward, &right, &up);
+
+ Vector mins = (forward * -16.0f) + (right * -16.0f);
+ Vector maxs = (forward * 16.0f) + (right * 16.0f) + (up * -height);
+
+ if (mins.x > maxs.x)
+ {
+ V_swap(mins.x, maxs.x);
+ }
+
+ if (mins.y > maxs.y)
+ {
+ V_swap(mins.y, maxs.y);
+ }
+
+ if (mins.z > maxs.z)
+ {
+ V_swap(mins.z, maxs.z);
+ }
+
+ SetCollisionBounds(mins, maxs);
+
+ UTIL_SetSize(this, mins, maxs);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw any debug text overlays
+//-----------------------------------------------------------------------------
+int CNPC_CombineCamera::DrawDebugTextOverlays(void)
+{
+ int text_offset = BaseClass::DrawDebugTextOverlays();
+
+ if (m_debugOverlays & OVERLAY_TEXT_BIT)
+ {
+ char tempstr[512];
+
+ Q_snprintf( tempstr, sizeof( tempstr ),"Enemy : %s", m_hEnemyTarget ? m_hEnemyTarget->GetDebugName() : "<none>");
+ EntityText(text_offset,tempstr,0);
+ text_offset++;
+ }
+ return text_offset;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_CombineCamera::DrawDebugGeometryOverlays(void)
+{
+ // ------------------------------
+ // Draw viewcone if selected
+ // ------------------------------
+ if ((m_debugOverlays & OVERLAY_NPC_VIEWCONE_BIT))
+ {
+ float flViewRange = acos(CAMERA_FOV_NARROW);
+ Vector vEyeDir = EyeDirection2D( );
+ Vector vLeftDir, vRightDir;
+ float fSin, fCos;
+ SinCos( flViewRange, &fSin, &fCos );
+
+ vLeftDir.x = vEyeDir.x * fCos - vEyeDir.y * fSin;
+ vLeftDir.y = vEyeDir.x * fSin + vEyeDir.y * fCos;
+ vLeftDir.z = vEyeDir.z;
+ fSin = sin(-flViewRange);
+ fCos = cos(-flViewRange);
+ vRightDir.x = vEyeDir.x * fCos - vEyeDir.y * fSin;
+ vRightDir.y = vEyeDir.x * fSin + vEyeDir.y * fCos;
+ vRightDir.z = vEyeDir.z;
+
+ NDebugOverlay::BoxDirection(EyePosition(), Vector(0,0,-40), Vector(200,0,40), vLeftDir, 255, 255, 0, 50, 0 );
+ NDebugOverlay::BoxDirection(EyePosition(), Vector(0,0,-40), Vector(200,0,40), vRightDir, 255, 255, 0, 50, 0 );
+ NDebugOverlay::Box(EyePosition(), -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, 128, 0 );
+ }
+
+ BaseClass::DrawDebugGeometryOverlays();
+}