mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2024-12-23 01:59:43 +08:00
1189 lines
34 KiB
C++
1189 lines
34 KiB
C++
//========= 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();
|
|
}
|