diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/hl2/npc_combinecamera.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-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.cpp | 2376 |
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(); +} |