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_scanner.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_scanner.cpp')
| -rw-r--r-- | mp/src/game/server/hl2/npc_scanner.cpp | 6058 |
1 files changed, 3029 insertions, 3029 deletions
diff --git a/mp/src/game/server/hl2/npc_scanner.cpp b/mp/src/game/server/hl2/npc_scanner.cpp index eb1c6c46..031ce6d9 100644 --- a/mp/src/game/server/hl2/npc_scanner.cpp +++ b/mp/src/game/server/hl2/npc_scanner.cpp @@ -1,3029 +1,3029 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "npcevent.h"
-#include "soundenvelope.h"
-#include "ai_hint.h"
-#include "ai_moveprobe.h"
-#include "ai_squad.h"
-#include "beam_shared.h"
-#include "globalstate.h"
-#include "soundent.h"
-#include "npc_citizen17.h"
-#include "gib.h"
-#include "spotlightend.h"
-#include "IEffects.h"
-#include "items.h"
-#include "ai_route.h"
-#include "player_pickup.h"
-#include "weapon_physcannon.h"
-#include "hl2_player.h"
-#include "npc_scanner.h"
-#include "materialsystem/imaterialsystemhardwareconfig.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-//-----------------------------------------------------------------------------
-// Singleton interfaces
-//-----------------------------------------------------------------------------
-extern IMaterialSystemHardwareConfig *g_pMaterialSystemHardwareConfig;
-
-//-----------------------------------------------------------------------------
-// Parameters for how the scanner relates to citizens.
-//-----------------------------------------------------------------------------
-#define SCANNER_CIT_INSPECT_DELAY 10 // Check for citizens this often
-#define SCANNER_CIT_INSPECT_GROUND_DIST 500 // How far to look for citizens to inspect
-#define SCANNER_CIT_INSPECT_FLY_DIST 1500 // How far to look for citizens to inspect
-
-#define SCANNER_CIT_INSPECT_LENGTH 5 // How long does the inspection last
-#define SCANNER_HINT_INSPECT_LENGTH 5 // How long does the inspection last
-#define SCANNER_SOUND_INSPECT_LENGTH 5 // How long does the inspection last
-
-#define SCANNER_HINT_INSPECT_DELAY 15 // Check for hint nodes this often
-
-#define SPOTLIGHT_WIDTH 32
-
-#define SCANNER_SPOTLIGHT_NEAR_DIST 64
-#define SCANNER_SPOTLIGHT_FAR_DIST 256
-#define SCANNER_SPOTLIGHT_FLY_HEIGHT 72
-#define SCANNER_NOSPOTLIGHT_FLY_HEIGHT 72
-
-#define SCANNER_FLASH_MIN_DIST 900 // How far does flash effect enemy
-#define SCANNER_FLASH_MAX_DIST 1200 // How far does flash effect enemy
-
-#define SCANNER_FLASH_MAX_VALUE 240 // How bright is maximum flash
-
-#define SCANNER_PHOTO_NEAR_DIST 64
-#define SCANNER_PHOTO_FAR_DIST 128
-
-#define SCANNER_FOLLOW_DIST 128
-
-#define SCANNER_NUM_GIBS 6 // Number of gibs in gib file
-
-// Strider Scout Scanners
-#define SCANNER_SCOUT_MAX_SPEED 150
-
-ConVar sk_scanner_health( "sk_scanner_health","0");
-ConVar g_debug_cscanner( "g_debug_cscanner", "0" );
-
-//-----------------------------------------------------------------------------
-// Private activities.
-//-----------------------------------------------------------------------------
-static int ACT_SCANNER_SMALL_FLINCH_ALERT = 0;
-static int ACT_SCANNER_SMALL_FLINCH_COMBAT = 0;
-static int ACT_SCANNER_INSPECT = 0;
-static int ACT_SCANNER_WALK_ALERT = 0;
-static int ACT_SCANNER_WALK_COMBAT = 0;
-static int ACT_SCANNER_FLARE = 0;
-static int ACT_SCANNER_RETRACT = 0;
-static int ACT_SCANNER_FLARE_PRONGS = 0;
-static int ACT_SCANNER_RETRACT_PRONGS = 0;
-static int ACT_SCANNER_FLARE_START = 0;
-
-//-----------------------------------------------------------------------------
-// Interactions
-//-----------------------------------------------------------------------------
-int g_interactionScannerInspect = 0;
-int g_interactionScannerInspectBegin = 0;
-int g_interactionScannerInspectHandsUp = 0;
-int g_interactionScannerInspectShowArmband = 0;//<<TEMP>>still to be completed
-int g_interactionScannerInspectDone = 0;
-int g_interactionScannerSupportEntity = 0;
-int g_interactionScannerSupportPosition = 0;
-
-//-----------------------------------------------------------------------------
-// Animation events
-//------------------------------------------------------------------------
-int AE_SCANNER_CLOSED;
-
-//-----------------------------------------------------------------------------
-// Attachment points
-//-----------------------------------------------------------------------------
-#define SCANNER_ATTACHMENT_LIGHT "light"
-#define SCANNER_ATTACHMENT_FLASH 1
-#define SCANNER_ATTACHMENT_LPRONG 2
-#define SCANNER_ATTACHMENT_RPRONG 3
-
-//-----------------------------------------------------------------------------
-// Other defines.
-//-----------------------------------------------------------------------------
-#define SCANNER_MAX_BEAMS 4
-
-BEGIN_DATADESC( CNPC_CScanner )
-
- DEFINE_SOUNDPATCH( m_pEngineSound ),
-
- DEFINE_EMBEDDED( m_KilledInfo ),
- DEFINE_FIELD( m_flGoalOverrideDistance, FIELD_FLOAT ),
- DEFINE_FIELD( m_bPhotoTaken, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_vInspectPos, FIELD_VECTOR ),
- DEFINE_FIELD( m_fInspectEndTime, FIELD_TIME ),
- DEFINE_FIELD( m_fCheckCitizenTime, FIELD_TIME ),
- DEFINE_FIELD( m_fCheckHintTime, FIELD_TIME ),
- DEFINE_KEYFIELD( m_bShouldInspect, FIELD_BOOLEAN, "ShouldInspect" ),
- DEFINE_KEYFIELD( m_bOnlyInspectPlayers, FIELD_BOOLEAN, "OnlyInspectPlayers" ),
- DEFINE_KEYFIELD( m_bNeverInspectPlayers,FIELD_BOOLEAN, "NeverInspectPlayers" ),
- DEFINE_FIELD( m_fNextPhotographTime, FIELD_TIME ),
-// DEFINE_FIELD( m_pEyeFlash, FIELD_CLASSPTR ),
- DEFINE_FIELD( m_vSpotlightTargetPos, FIELD_POSITION_VECTOR ),
- DEFINE_FIELD( m_vSpotlightCurrentPos, FIELD_POSITION_VECTOR ),
-// don't save (recreated after restore/transition)
-// DEFINE_FIELD( m_hSpotlight, FIELD_EHANDLE ),
-// DEFINE_FIELD( m_hSpotlightTarget, FIELD_EHANDLE ),
- DEFINE_FIELD( m_vSpotlightDir, FIELD_VECTOR ),
- DEFINE_FIELD( m_vSpotlightAngVelocity, FIELD_VECTOR ),
- DEFINE_FIELD( m_flSpotlightCurLength, FIELD_FLOAT ),
- DEFINE_FIELD( m_fNextSpotlightTime, FIELD_TIME ),
- DEFINE_FIELD( m_nHaloSprite, FIELD_INTEGER ),
- DEFINE_FIELD( m_fNextFlySoundTime, FIELD_TIME ),
- DEFINE_FIELD( m_nFlyMode, FIELD_INTEGER ),
- DEFINE_FIELD( m_nPoseTail, FIELD_INTEGER ),
- DEFINE_FIELD( m_nPoseDynamo, FIELD_INTEGER ),
- DEFINE_FIELD( m_nPoseFlare, FIELD_INTEGER ),
- DEFINE_FIELD( m_nPoseFaceVert, FIELD_INTEGER ),
- DEFINE_FIELD( m_nPoseFaceHoriz, FIELD_INTEGER ),
-
- DEFINE_FIELD( m_bIsClawScanner, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bIsOpen, FIELD_BOOLEAN ),
-
- // DEFINE_FIELD( m_bHasSpoken, FIELD_BOOLEAN ),
-
- DEFINE_FIELD( m_pSmokeTrail, FIELD_CLASSPTR ),
- DEFINE_FIELD( m_flFlyNoiseBase, FIELD_FLOAT ),
- DEFINE_FIELD( m_flEngineStallTime, FIELD_TIME ),
-
- DEFINE_FIELD( m_vecDiveBombDirection, FIELD_VECTOR ),
- DEFINE_FIELD( m_flDiveBombRollForce, FIELD_FLOAT ),
-
- DEFINE_KEYFIELD( m_flSpotlightMaxLength, FIELD_FLOAT, "SpotlightLength"),
- DEFINE_KEYFIELD( m_flSpotlightGoalWidth, FIELD_FLOAT, "SpotlightWidth"),
-
- // Physics Influence
- DEFINE_FIELD( m_hPhysicsAttacker, FIELD_EHANDLE ),
- DEFINE_FIELD( m_flLastPhysicsInfluenceTime, FIELD_TIME ),
-
- DEFINE_KEYFIELD( m_bNoLight, FIELD_BOOLEAN, "SpotlightDisabled" ),
-
- DEFINE_INPUTFUNC( FIELD_VOID, "DisableSpotlight", InputDisableSpotlight ),
- DEFINE_INPUTFUNC( FIELD_STRING, "InspectTargetPhoto", InputInspectTargetPhoto ),
- DEFINE_INPUTFUNC( FIELD_STRING, "InspectTargetSpotlight", InputInspectTargetSpotlight ),
- DEFINE_INPUTFUNC( FIELD_INTEGER, "InputShouldInspect", InputShouldInspect ),
- DEFINE_INPUTFUNC( FIELD_STRING, "SetFollowTarget", InputSetFollowTarget ),
- DEFINE_INPUTFUNC( FIELD_VOID, "ClearFollowTarget", InputClearFollowTarget ),
-
- DEFINE_INPUTFUNC( FIELD_STRING, "DeployMine", InputDeployMine ),
- DEFINE_INPUTFUNC( FIELD_STRING, "EquipMine", InputEquipMine ),
-
- DEFINE_OUTPUT( m_OnPhotographPlayer, "OnPhotographPlayer" ),
- DEFINE_OUTPUT( m_OnPhotographNPC, "OnPhotographNPC" ),
-
-END_DATADESC()
-
-
-LINK_ENTITY_TO_CLASS(npc_cscanner, CNPC_CScanner);
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-CNPC_CScanner::CNPC_CScanner()
-{
-#ifdef _DEBUG
- m_vInspectPos.Init();
- m_vSpotlightTargetPos.Init();
- m_vSpotlightCurrentPos.Init();
- m_vSpotlightDir.Init();
- m_vSpotlightAngVelocity.Init();
-#endif
- m_bShouldInspect = true;
- m_bOnlyInspectPlayers = false;
- m_bNeverInspectPlayers = false;
-
- char szMapName[256];
- Q_strncpy(szMapName, STRING(gpGlobals->mapname), sizeof(szMapName) );
- Q_strlower(szMapName);
-
- if( !Q_strnicmp( szMapName, "d3_c17", 6 ) )
- {
- // Streetwar scanners are claw scanners
- m_bIsClawScanner = true;
- }
- else
- {
- m_bIsClawScanner = false;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::Spawn(void)
-{
- // Check for user error
- if (m_flSpotlightMaxLength <= 0)
- {
- DevMsg("CNPC_CScanner::Spawn: Invalid spotlight length <= 0, setting to 500\n");
- m_flSpotlightMaxLength = 500;
- }
-
- if (m_flSpotlightGoalWidth <= 0)
- {
- DevMsg("CNPC_CScanner::Spawn: Invalid spotlight width <= 0, setting to 100\n");
- m_flSpotlightGoalWidth = 100;
- }
-
- if (m_flSpotlightGoalWidth > MAX_BEAM_WIDTH )
- {
- DevMsg("CNPC_CScanner::Spawn: Invalid spotlight width %.1f (max %.1f).\n", m_flSpotlightGoalWidth, MAX_BEAM_WIDTH );
- m_flSpotlightGoalWidth = MAX_BEAM_WIDTH;
- }
-
- Precache();
-
- if( m_bIsClawScanner )
- {
- SetModel( "models/shield_scanner.mdl");
- }
- else
- {
- SetModel( "models/combine_scanner.mdl");
- }
-
- m_iHealth = sk_scanner_health.GetFloat();
- m_iMaxHealth = m_iHealth;
-
- // ------------------------------------
- // Init all class vars
- // ------------------------------------
- m_vInspectPos = vec3_origin;
- m_fInspectEndTime = 0;
- m_fCheckCitizenTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_DELAY;
- m_fCheckHintTime = gpGlobals->curtime + SCANNER_HINT_INSPECT_DELAY;
- m_fNextPhotographTime = 0;
-
- m_vSpotlightTargetPos = vec3_origin;
- m_vSpotlightCurrentPos = vec3_origin;
-
- m_hSpotlight = NULL;
- m_hSpotlightTarget = NULL;
-
- AngleVectors( GetLocalAngles(), &m_vSpotlightDir );
- m_vSpotlightAngVelocity = vec3_origin;
-
- m_pEyeFlash = 0;
- m_fNextSpotlightTime = 0;
- m_nFlyMode = SCANNER_FLY_PATROL;
- m_vCurrentBanking = m_vSpotlightDir;
- m_flSpotlightCurLength = m_flSpotlightMaxLength;
-
- m_nPoseTail = LookupPoseParameter( "tail_control" );
- m_nPoseDynamo = LookupPoseParameter( "dynamo_wheel" );
- m_nPoseFlare = LookupPoseParameter( "alert_control" );
- m_nPoseFaceVert = LookupPoseParameter( "flex_vert" );
- m_nPoseFaceHoriz = LookupPoseParameter( "flex_horz" );
-
- // --------------------------------------------
-
- CapabilitiesAdd( bits_CAP_INNATE_MELEE_ATTACK1 );
-
- m_bPhotoTaken = false;
-
- BaseClass::Spawn();
-
- // Watch for this error state
- if ( m_bOnlyInspectPlayers && m_bNeverInspectPlayers )
- {
- Assert( 0 );
- Warning( "ERROR: Scanner set to never and always inspect players!\n" );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::Activate()
-{
- BaseClass::Activate();
-
- // Have to do this here because sprites do not go across level transitions
- m_pEyeFlash = CSprite::SpriteCreate( "sprites/blueflare1.vmt", GetLocalOrigin(), FALSE );
- m_pEyeFlash->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation );
- m_pEyeFlash->SetAttachment( this, LookupAttachment( SCANNER_ATTACHMENT_LIGHT ) );
- m_pEyeFlash->SetBrightness( 0 );
- m_pEyeFlash->SetScale( 1.4 );
-}
-
-//------------------------------------------------------------------------------
-// Purpose: Override to split in two when attacked
-//------------------------------------------------------------------------------
-int CNPC_CScanner::OnTakeDamage_Alive( const CTakeDamageInfo &info )
-{
- // Turn off my spotlight when shot
- SpotlightDestroy();
- m_fNextSpotlightTime = gpGlobals->curtime + 2.0f;
-
- return (BaseClass::OnTakeDamage_Alive( info ));
-}
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CNPC_CScanner::Gib( void )
-{
- if ( IsMarkedForDeletion() )
- return;
-
- // Spawn all gibs
- if( m_bIsClawScanner )
- {
- CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib1.mdl");
- CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib2.mdl");
- CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib3.mdl");
- CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib4.mdl");
- CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib5.mdl");
- CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib6.mdl");
- }
- else
- {
- CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib01.mdl" );
- CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib02.mdl" );
- CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib04.mdl" );
- CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib05.mdl" );
- }
-
- // Add a random chance of spawning a battery...
- if ( !HasSpawnFlags(SF_NPC_NO_WEAPON_DROP) && random->RandomFloat( 0.0f, 1.0f) < 0.3f )
- {
- CItem *pBattery = (CItem*)CreateEntityByName("item_battery");
- if ( pBattery )
- {
- pBattery->SetAbsOrigin( GetAbsOrigin() );
- pBattery->SetAbsVelocity( GetAbsVelocity() );
- pBattery->SetLocalAngularVelocity( GetLocalAngularVelocity() );
- pBattery->ActivateWhenAtRest();
- pBattery->Spawn();
- }
- }
-
- DeployMine();
-
- BaseClass::Gib();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pInflictor -
-// pAttacker -
-// flDamage -
-// bitsDamageType -
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::Event_Killed( const CTakeDamageInfo &info )
-{
- // Copy off the takedamage info that killed me, since we're not going to call
- // up into the base class's Event_Killed() until we gib. (gibbing is ultimate death)
- m_KilledInfo = info;
-
- DeployMine();
-
- ClearInspectTarget();
-
- // Interrupt whatever schedule I'm on
- SetCondition(COND_SCHEDULE_DONE);
-
- // Remove spotlight
- SpotlightDestroy();
-
- // Remove sprite
- UTIL_Remove(m_pEyeFlash);
- m_pEyeFlash = NULL;
-
- // If I have an enemy and I'm up high, do a dive bomb (unless dissolved)
- if ( !m_bIsClawScanner && GetEnemy() != NULL && (info.GetDamageType() & DMG_DISSOLVE) == false )
- {
- Vector vecDelta = GetLocalOrigin() - GetEnemy()->GetLocalOrigin();
- if ( ( vecDelta.z > 120 ) && ( vecDelta.Length() > 360 ) )
- {
- // If I'm divebombing, don't take any more damage. It will make Event_Killed() be called again.
- // This is especially bad if someone machineguns the divebombing scanner.
- AttackDivebomb();
- return;
- }
- }
-
- Gib();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Tells use whether or not the NPC cares about a given type of hint node.
-// Input : sHint -
-// Output : TRUE if the NPC is interested in this hint type, FALSE if not.
-//-----------------------------------------------------------------------------
-bool CNPC_CScanner::FValidateHintType(CAI_Hint *pHint)
-{
- return( pHint->HintType() == HINT_WORLD_WINDOW );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : Type -
-//-----------------------------------------------------------------------------
-int CNPC_CScanner::TranslateSchedule( int scheduleType )
-{
- switch ( scheduleType )
- {
- case SCHED_IDLE_STAND:
- {
- return SCHED_SCANNER_PATROL;
- }
-
- case SCHED_SCANNER_PATROL:
- return SCHED_CSCANNER_PATROL;
- }
- return BaseClass::TranslateSchedule(scheduleType);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : idealActivity -
-// *pIdealWeaponActivity -
-// Output : int
-//-----------------------------------------------------------------------------
-Activity CNPC_CScanner::NPC_TranslateActivity( Activity eNewActivity )
-{
- if( !m_bIsClawScanner )
- {
- return BaseClass::NPC_TranslateActivity( eNewActivity );
- }
-
- // The claw scanner came along a little late and doesn't have the activities
- // of the city scanner. So Just pick between these three
- if( eNewActivity == ACT_DISARM )
- {
- // Closing up.
- return eNewActivity;
- }
-
- if( m_bIsOpen )
- {
- return ACT_IDLE_ANGRY;
- }
- else
- {
- return ACT_IDLE;
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::HandleAnimEvent( animevent_t *pEvent )
-{
- if( pEvent->event == AE_SCANNER_CLOSED )
- {
- m_bIsOpen = false;
- SetActivity( ACT_IDLE );
- return;
- }
-
- BaseClass::HandleAnimEvent( pEvent );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-char *CNPC_CScanner::GetEngineSound( void )
-{
- if( m_bIsClawScanner )
- return "NPC_SScanner.FlyLoop";
-
- return "NPC_CScanner.FlyLoop";
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Plays the engine sound.
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::NPCThink(void)
-{
- if (!IsAlive())
- {
- SetActivity((Activity)ACT_SCANNER_RETRACT_PRONGS);
- StudioFrameAdvance( );
- SetNextThink( gpGlobals->curtime + 0.1f );
- }
- else
- {
- BaseClass::NPCThink();
- SpotlightUpdate();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::Precache(void)
-{
- // Model
- if( m_bIsClawScanner )
- {
- PrecacheModel("models/shield_scanner.mdl");
-
- PrecacheModel("models/gibs/Shield_Scanner_Gib1.mdl");
- PrecacheModel("models/gibs/Shield_Scanner_Gib2.mdl");
- PrecacheModel("models/gibs/Shield_Scanner_Gib3.mdl");
- PrecacheModel("models/gibs/Shield_Scanner_Gib4.mdl");
- PrecacheModel("models/gibs/Shield_Scanner_Gib5.mdl");
- PrecacheModel("models/gibs/Shield_Scanner_Gib6.mdl");
-
- PrecacheScriptSound( "NPC_SScanner.Shoot");
- PrecacheScriptSound( "NPC_SScanner.Alert" );
- PrecacheScriptSound( "NPC_SScanner.Die" );
- PrecacheScriptSound( "NPC_SScanner.Combat" );
- PrecacheScriptSound( "NPC_SScanner.Idle" );
- PrecacheScriptSound( "NPC_SScanner.Pain" );
- PrecacheScriptSound( "NPC_SScanner.TakePhoto" );
- PrecacheScriptSound( "NPC_SScanner.AttackFlash" );
- PrecacheScriptSound( "NPC_SScanner.DiveBombFlyby" );
- PrecacheScriptSound( "NPC_SScanner.DiveBomb" );
- PrecacheScriptSound( "NPC_SScanner.DeployMine" );
-
- PrecacheScriptSound( "NPC_SScanner.FlyLoop" );
- UTIL_PrecacheOther( "combine_mine" );
- }
- else
- {
- PrecacheModel("models/combine_scanner.mdl");
-
- PrecacheModel("models/gibs/scanner_gib01.mdl" );
- PrecacheModel("models/gibs/scanner_gib02.mdl" );
- PrecacheModel("models/gibs/scanner_gib02.mdl" );
- PrecacheModel("models/gibs/scanner_gib04.mdl" );
- PrecacheModel("models/gibs/scanner_gib05.mdl" );
-
- PrecacheScriptSound( "NPC_CScanner.Shoot");
- PrecacheScriptSound( "NPC_CScanner.Alert" );
- PrecacheScriptSound( "NPC_CScanner.Die" );
- PrecacheScriptSound( "NPC_CScanner.Combat" );
- PrecacheScriptSound( "NPC_CScanner.Idle" );
- PrecacheScriptSound( "NPC_CScanner.Pain" );
- PrecacheScriptSound( "NPC_CScanner.TakePhoto" );
- PrecacheScriptSound( "NPC_CScanner.AttackFlash" );
- PrecacheScriptSound( "NPC_CScanner.DiveBombFlyby" );
- PrecacheScriptSound( "NPC_CScanner.DiveBomb" );
- PrecacheScriptSound( "NPC_CScanner.DeployMine" );
-
- PrecacheScriptSound( "NPC_CScanner.FlyLoop" );
- }
-
- // Sprites
- m_nHaloSprite = PrecacheModel("sprites/light_glow03.vmt");
- PrecacheModel( "sprites/glow_test02.vmt" );
-
- BaseClass::Precache();
-}
-
-//------------------------------------------------------------------------------
-// Purpose: Request help inspecting from other squad members
-//------------------------------------------------------------------------------
-void CNPC_CScanner::RequestInspectSupport(void)
-{
- if (m_pSquad)
- {
- AISquadIter_t iter;
- for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
- {
- if (pSquadMember != this)
- {
- if (GetTarget())
- {
- pSquadMember->DispatchInteraction(g_interactionScannerSupportEntity,((void *)((CBaseEntity*)GetTarget())),this);
- }
- else
- {
- pSquadMember->DispatchInteraction(g_interactionScannerSupportPosition,((void *)m_vInspectPos.Base()),this);
- }
- }
- }
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-bool CNPC_CScanner::IsValidInspectTarget(CBaseEntity *pEntity)
-{
- // If a citizen, make sure he can be inspected again
- if (pEntity->Classify() == CLASS_CITIZEN_PASSIVE)
- {
- if (((CNPC_Citizen*)pEntity)->GetNextScannerInspectTime() > gpGlobals->curtime)
- {
- return false;
- }
- }
-
- // Make sure no other squad member has already chosen to
- // inspect this entity
- if (m_pSquad)
- {
- AISquadIter_t iter;
- for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
- {
- if (pSquadMember->GetTarget() == pEntity)
- {
- return false;
- }
- }
- }
-
- // Do not inspect friendly targets
- if ( IRelationType( pEntity ) == D_LI )
- return false;
-
- return true;
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-CBaseEntity* CNPC_CScanner::BestInspectTarget(void)
-{
- if ( !m_bShouldInspect )
- return NULL;
-
- CBaseEntity* pBestEntity = NULL;
- float fBestDist = MAX_COORD_RANGE;
- float fTestDist;
-
- CBaseEntity *pEntity = NULL;
-
- // If I have a spotlight, search from the spotlight position
- // otherwise search from my position
- Vector vSearchOrigin;
- float fSearchDist;
- if (m_hSpotlightTarget != NULL)
- {
- vSearchOrigin = m_hSpotlightTarget->GetAbsOrigin();
- fSearchDist = SCANNER_CIT_INSPECT_GROUND_DIST;
- }
- else
- {
- vSearchOrigin = WorldSpaceCenter();
- fSearchDist = SCANNER_CIT_INSPECT_FLY_DIST;
- }
-
- if ( m_bOnlyInspectPlayers )
- {
- CBasePlayer *pPlayer = AI_GetSinglePlayer();
- if ( !pPlayer )
- return NULL;
-
- if ( !pPlayer->IsAlive() || (pPlayer->GetFlags() & FL_NOTARGET) )
- return NULL;
-
- return WorldSpaceCenter().DistToSqr( pPlayer->EyePosition() ) <= (fSearchDist * fSearchDist) ? pPlayer : NULL;
- }
-
- CUtlVector<CBaseEntity *> candidates;
- float fSearchDistSq = fSearchDist * fSearchDist;
- int i;
-
- // Inspect players unless told otherwise
- if ( m_bNeverInspectPlayers == false )
- {
- // Players
- for ( i = 1; i <= gpGlobals->maxClients; i++ )
- {
- CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
-
- if ( pPlayer )
- {
- if ( vSearchOrigin.DistToSqr(pPlayer->GetAbsOrigin()) < fSearchDistSq )
- {
- candidates.AddToTail( pPlayer );
- }
- }
- }
- }
-
- // NPCs
- CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
-
- for ( i = 0; i < g_AI_Manager.NumAIs(); i++ )
- {
- if ( ppAIs[i] != this && vSearchOrigin.DistToSqr(ppAIs[i]->GetAbsOrigin()) < fSearchDistSq )
- {
- candidates.AddToTail( ppAIs[i] );
- }
- }
-
- for ( i = 0; i < candidates.Count(); i++ )
- {
- pEntity = candidates[i];
- Assert( pEntity != this && (pEntity->MyNPCPointer() || pEntity->IsPlayer() ) );
-
- CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
- if ( ( pNPC && pNPC->Classify() == CLASS_CITIZEN_PASSIVE ) || pEntity->IsPlayer() )
- {
- if ( pEntity->GetFlags() & FL_NOTARGET )
- continue;
-
- if ( pEntity->IsAlive() == false )
- continue;
-
- // Ensure it's within line of sight
- if ( !FVisible( pEntity ) )
- continue;
-
- fTestDist = ( GetAbsOrigin() - pEntity->EyePosition() ).Length();
- if ( fTestDist < fBestDist )
- {
- if ( IsValidInspectTarget( pEntity ) )
- {
- fBestDist = fTestDist;
- pBestEntity = pEntity;
- }
- }
- }
- }
- return pBestEntity;
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose: Clears any previous inspect target and set inspect target to
-// the given entity and set the durection of the inspection
-//------------------------------------------------------------------------------
-void CNPC_CScanner::SetInspectTargetToEnt(CBaseEntity *pEntity, float fInspectDuration)
-{
- ClearInspectTarget();
- SetTarget(pEntity);
-
- m_fInspectEndTime = gpGlobals->curtime + fInspectDuration;
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose: Clears any previous inspect target and set inspect target to
-// the given hint node and set the durection of the inspection
-//------------------------------------------------------------------------------
-void CNPC_CScanner::SetInspectTargetToHint(CAI_Hint *pHint, float fInspectDuration)
-{
- ClearInspectTarget();
-
- float yaw = pHint->Yaw();
- // --------------------------------------------
- // Figure out the location that the hint hits
- // --------------------------------------------
- Vector vHintDir = UTIL_YawToVector( yaw );
-
- Vector vHintOrigin;
- pHint->GetPosition( this, &vHintOrigin );
-
- Vector vHintEnd = vHintOrigin + (vHintDir * 512);
-
- trace_t tr;
- AI_TraceLine ( vHintOrigin, vHintEnd, MASK_BLOCKLOS, this, COLLISION_GROUP_NONE, &tr);
-
- if ( g_debug_cscanner.GetBool() )
- {
- NDebugOverlay::Line( vHintOrigin, tr.endpos, 255, 0, 0, true, 4.0f );
- NDebugOverlay::Cross3D( tr.endpos, -Vector(8,8,8), Vector(8,8,8), 255, 0, 0, true, 4.0f );
- }
-
- if (tr.fraction == 1.0f )
- {
- DevMsg("ERROR: Scanner hint node not facing a surface!\n");
- }
- else
- {
- SetHintNode( pHint );
- m_vInspectPos = tr.endpos;
- pHint->Lock( this );
-
- m_fInspectEndTime = gpGlobals->curtime + fInspectDuration;
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose: Clears any previous inspect target and set inspect target to
-// the given position and set the durection of the inspection
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-void CNPC_CScanner::SetInspectTargetToPos(const Vector &vInspectPos, float fInspectDuration)
-{
- ClearInspectTarget();
- m_vInspectPos = vInspectPos;
-
- m_fInspectEndTime = gpGlobals->curtime + fInspectDuration;
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose: Clears out any previous inspection targets
-//------------------------------------------------------------------------------
-void CNPC_CScanner::ClearInspectTarget(void)
-{
- if ( GetIdealState() != NPC_STATE_SCRIPT )
- {
- SetTarget( NULL );
- }
-
- ClearHintNode( SCANNER_HINT_INSPECT_LENGTH );
- m_vInspectPos = vec3_origin;
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose: Returns true if there is a position to be inspected.
-//------------------------------------------------------------------------------
-bool CNPC_CScanner::HaveInspectTarget( void )
-{
- if ( GetTarget() != NULL )
- return true;
-
- if ( m_vInspectPos != vec3_origin )
- return true;
-
- return false;
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-Vector CNPC_CScanner::InspectTargetPosition(void)
-{
- // If we have a target, return an adjust position
- if ( GetTarget() != NULL )
- {
- Vector vEyePos = GetTarget()->EyePosition();
-
- // If in spotlight mode, aim for ground below target unless is client
- if ( m_nFlyMode == SCANNER_FLY_SPOT && !(GetTarget()->GetFlags() & FL_CLIENT) )
- {
- Vector vInspectPos;
- vInspectPos.x = vEyePos.x;
- vInspectPos.y = vEyePos.y;
- vInspectPos.z = GetFloorZ( vEyePos );
-
- // Let's take three-quarters between eyes and ground
- vInspectPos.z += ( vEyePos.z - vInspectPos.z ) * 0.75f;
-
- return vInspectPos;
- }
- else
- {
- // Otherwise aim for eyes
- return vEyePos;
- }
- }
- else if ( m_vInspectPos != vec3_origin )
- {
- return m_vInspectPos;
- }
- else
- {
- DevMsg("InspectTargetPosition called with no target!\n");
-
- return m_vInspectPos;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::InputShouldInspect( inputdata_t &inputdata )
-{
- m_bShouldInspect = ( inputdata.value.Int() != 0 );
-
- if ( !m_bShouldInspect )
- {
- if ( GetEnemy() == GetTarget() )
- SetEnemy(NULL);
- ClearInspectTarget();
- SetTarget(NULL);
- SpotlightDestroy();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::DeployMine()
-{
- CBaseEntity *child;
- // iterate through all children
- for ( child = FirstMoveChild(); child != NULL; child = child->NextMovePeer() )
- {
- if( FClassnameIs( child, "combine_mine" ) )
- {
- child->SetParent( NULL );
- child->SetAbsVelocity( GetAbsVelocity() );
- child->SetOwnerEntity( this );
-
- ScannerEmitSound( "DeployMine" );
-
- IPhysicsObject *pPhysObj = child->VPhysicsGetObject();
- if( pPhysObj )
- {
- // Make sure the mine's awake
- pPhysObj->Wake();
- }
-
- if( m_bIsClawScanner )
- {
- // Fold up.
- SetActivity( ACT_DISARM );
- }
-
- return;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-float CNPC_CScanner::GetMaxSpeed()
-{
- if( IsStriderScout() )
- {
- return SCANNER_SCOUT_MAX_SPEED;
- }
-
- return BaseClass::GetMaxSpeed();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::InputDeployMine(inputdata_t &inputdata)
-{
- DeployMine();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::InputEquipMine(inputdata_t &inputdata)
-{
- CBaseEntity *child;
- // iterate through all children
- for ( child = FirstMoveChild(); child != NULL; child = child->NextMovePeer() )
- {
- if( FClassnameIs( child, "combine_mine" ) )
- {
- // Already have a mine!
- return;
- }
- }
-
- CBaseEntity *pEnt;
-
- pEnt = CreateEntityByName( "combine_mine" );
- bool bPlacedMine = false;
-
- if( m_bIsClawScanner )
- {
- Vector vecOrigin;
- QAngle angles;
- int attachment;
-
- attachment = LookupAttachment( "claw" );
-
- if( attachment > -1 )
- {
- GetAttachment( attachment, vecOrigin, angles );
-
- pEnt->SetAbsOrigin( vecOrigin );
- pEnt->SetAbsAngles( angles );
- pEnt->SetOwnerEntity( this );
- pEnt->SetParent( this, attachment );
-
- m_bIsOpen = true;
- SetActivity( ACT_IDLE_ANGRY );
- bPlacedMine = true;
- }
- }
-
-
- if( !bPlacedMine )
- {
- Vector vecMineLocation = GetAbsOrigin();
- vecMineLocation.z -= 32.0;
-
- pEnt->SetAbsOrigin( vecMineLocation );
- pEnt->SetAbsAngles( GetAbsAngles() );
- pEnt->SetOwnerEntity( this );
- pEnt->SetParent( this );
- }
-
- pEnt->Spawn();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Tells the scanner to go photograph an entity.
-// Input : String name or classname of the entity to inspect.
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::InputInspectTargetPhoto(inputdata_t &inputdata)
-{
- m_vLastPatrolDir = vec3_origin;
- m_bPhotoTaken = false;
- InspectTarget( inputdata, SCANNER_FLY_PHOTO );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Tells the scanner to go spotlight an entity.
-// Input : String name or classname of the entity to inspect.
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::InputInspectTargetSpotlight(inputdata_t &inputdata)
-{
- InspectTarget( inputdata, SCANNER_FLY_SPOT );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Tells the scanner to go photo or spotlight an entity.
-// Input : String name or classname of the entity to inspect.
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::InspectTarget( inputdata_t &inputdata, ScannerFlyMode_t eFlyMode )
-{
- CBaseEntity *pEnt = gEntList.FindEntityGeneric( NULL, inputdata.value.String(), this, inputdata.pActivator );
-
- if ( pEnt != NULL )
- {
- // Set and begin to inspect our target
- SetInspectTargetToEnt( pEnt, SCANNER_CIT_INSPECT_LENGTH );
-
- m_nFlyMode = eFlyMode;
- SetCondition( COND_CSCANNER_HAVE_INSPECT_TARGET );
-
- // Stop us from any other navigation we were doing
- GetNavigator()->ClearGoal();
- }
- else
- {
- DevMsg( "InspectTarget: target %s not found!\n", inputdata.value.String() );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CNPC_CScanner::MovingToInspectTarget( void )
-{
- // If we're flying to a photograph target and the photo isn't yet taken, we're still moving to it
- if ( m_nFlyMode == SCANNER_FLY_PHOTO && m_bPhotoTaken == false )
- return true;
-
- // If we're still on a path, then we're still moving
- if ( HaveInspectTarget() && GetNavigator()->IsGoalActive() )
- return true;
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::GatherConditions( void )
-{
- BaseClass::GatherConditions();
-
- // Clear out our old conditions
- ClearCondition( COND_CSCANNER_INSPECT_DONE );
- ClearCondition( COND_CSCANNER_HAVE_INSPECT_TARGET );
- ClearCondition( COND_CSCANNER_SPOT_ON_TARGET );
- ClearCondition( COND_CSCANNER_CAN_PHOTOGRAPH );
-
- // We don't do any of these checks if we have an enemy
- if ( GetEnemy() )
- return;
-
- // --------------------------------------
- // COND_CSCANNER_INSPECT_DONE
- //
- // If my inspection over
- // ---------------------------------------------------------
-
- // Refresh our timing if we're still moving to our inspection target
- if ( MovingToInspectTarget() )
- {
- m_fInspectEndTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_LENGTH;
- }
-
- // Update our follow times
- if ( HaveInspectTarget() && gpGlobals->curtime > m_fInspectEndTime && m_nFlyMode != SCANNER_FLY_FOLLOW )
- {
- SetCondition ( COND_CSCANNER_INSPECT_DONE );
-
- m_fCheckCitizenTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_DELAY;
- m_fCheckHintTime = gpGlobals->curtime + SCANNER_HINT_INSPECT_DELAY;
- ClearInspectTarget();
- }
-
- // ----------------------------------------------------------
- // If I heard a sound and I don't have an enemy, inspect it
- // ----------------------------------------------------------
- if ( ( HasCondition( COND_HEAR_COMBAT ) || HasCondition( COND_HEAR_DANGER ) ) && m_nFlyMode != SCANNER_FLY_FOLLOW )
- {
- CSound *pSound = GetBestSound();
-
- if ( pSound )
- {
- // Chase an owner if we can
- if ( pSound->m_hOwner != NULL )
- {
- // Don't inspect sounds of things we like
- if ( IRelationType( pSound->m_hOwner ) != D_LI )
- {
- // Only bother if we can see it
- if ( FVisible( pSound->m_hOwner ) )
- {
- SetInspectTargetToEnt( pSound->m_hOwner, SCANNER_SOUND_INSPECT_LENGTH );
- }
- }
- }
- else
- {
- // Otherwise chase the specific sound
- Vector vSoundPos = pSound->GetSoundOrigin();
- SetInspectTargetToPos( vSoundPos, SCANNER_SOUND_INSPECT_LENGTH );
- }
-
- m_nFlyMode = (random->RandomInt(0,2)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO;
- }
- }
-
- // --------------------------------------
- // COND_CSCANNER_HAVE_INSPECT_TARGET
- //
- // Look for a nearby citizen or player to hassle.
- // ---------------------------------------------------------
-
- // Check for citizens to inspect
- if ( gpGlobals->curtime > m_fCheckCitizenTime && HaveInspectTarget() == false )
- {
- CBaseEntity *pBestEntity = BestInspectTarget();
-
- if ( pBestEntity != NULL )
- {
- SetInspectTargetToEnt( pBestEntity, SCANNER_CIT_INSPECT_LENGTH );
- m_nFlyMode = (random->RandomInt(0,3)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO;
- SetCondition ( COND_CSCANNER_HAVE_INSPECT_TARGET );
- }
- }
-
- // Check for hints to inspect
- if ( gpGlobals->curtime > m_fCheckHintTime && HaveInspectTarget() == false )
- {
- SetHintNode( CAI_HintManager::FindHint( this, HINT_WORLD_WINDOW, 0, SCANNER_CIT_INSPECT_FLY_DIST ) );
-
- if ( GetHintNode() )
- {
- m_fCheckHintTime = gpGlobals->curtime + SCANNER_HINT_INSPECT_DELAY;
-
- m_nFlyMode = (random->RandomInt(0,2)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO;
-
- SetInspectTargetToHint( GetHintNode(), SCANNER_HINT_INSPECT_LENGTH );
-
- SetCondition ( COND_CSCANNER_HAVE_INSPECT_TARGET );
- }
- }
-
- // --------------------------------------
- // COND_CSCANNER_SPOT_ON_TARGET
- //
- // True when spotlight is on target ent
- // --------------------------------------
-
- if ( m_hSpotlightTarget != NULL && HaveInspectTarget() && m_hSpotlightTarget->GetSmoothedVelocity().Length() < 25 )
- {
- // If I have a target entity, check my spotlight against the
- // actual position of the entity
- if (GetTarget())
- {
- float fInspectDist = (m_vSpotlightTargetPos - m_vSpotlightCurrentPos).Length();
- if ( fInspectDist < 100 )
- {
- SetCondition( COND_CSCANNER_SPOT_ON_TARGET );
- }
- }
- // Otherwise just check by beam direction
- else
- {
- Vector vTargetDir = SpotlightTargetPos() - GetLocalOrigin();
- VectorNormalize(vTargetDir);
- float dotpr = DotProduct(vTargetDir, m_vSpotlightDir);
- if (dotpr > 0.95)
- {
- SetCondition( COND_CSCANNER_SPOT_ON_TARGET );
- }
- }
- }
-
- // --------------------------------------------
- // COND_CSCANNER_CAN_PHOTOGRAPH
- //
- // True when can photograph target ent
- // --------------------------------------------
-
- ClearCondition( COND_CSCANNER_CAN_PHOTOGRAPH );
-
- if ( m_nFlyMode == SCANNER_FLY_PHOTO )
- {
- // Make sure I have something to photograph and I'm ready to photograph and I'm not moving to fast
- if ( gpGlobals->curtime > m_fNextPhotographTime && HaveInspectTarget() && GetCurrentVelocity().LengthSqr() < (64*64) )
- {
- // Check that I'm in the right distance range
- float fInspectDist = (InspectTargetPosition() - GetAbsOrigin()).Length2D();
-
- // See if we're within range
- if ( fInspectDist > SCANNER_PHOTO_NEAR_DIST && fInspectDist < SCANNER_PHOTO_FAR_DIST )
- {
- // Make sure we're looking at the target
- if ( UTIL_AngleDiff( GetAbsAngles().y, VecToYaw( InspectTargetPosition() - GetAbsOrigin() ) ) < 4.0f )
- {
- trace_t tr;
- AI_TraceLine ( GetAbsOrigin(), InspectTargetPosition(), MASK_BLOCKLOS, GetTarget(), COLLISION_GROUP_NONE, &tr);
-
- if ( tr.fraction == 1.0f )
- {
- SetCondition( COND_CSCANNER_CAN_PHOTOGRAPH );
- }
- }
- }
- }
- }
-}
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CNPC_CScanner::PrescheduleThink(void)
-{
- BaseClass::PrescheduleThink();
-
- // Go back to idling if we're done
- if ( GetIdealActivity() == ACT_SCANNER_FLARE_START )
- {
- if ( IsSequenceFinished() )
- {
- SetIdealActivity( (Activity) ACT_IDLE );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Overridden because if the player is a criminal, we hate them.
-// Input : pTarget - Entity with which to determine relationship.
-// Output : Returns relationship value.
-//-----------------------------------------------------------------------------
-Disposition_t CNPC_CScanner::IRelationType(CBaseEntity *pTarget)
-{
- // If it's the player and they are a criminal, we hates them
- if ( pTarget && pTarget->Classify() == CLASS_PLAYER )
- {
- if ( GlobalEntity_GetState("gordon_precriminal") == GLOBAL_ON )
- return D_NU;
- }
-
- return BaseClass::IRelationType( pTarget );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pTask -
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::RunTask( const Task_t *pTask )
-{
- switch ( pTask->iTask )
- {
- case TASK_CSCANNER_PHOTOGRAPH:
- {
- if ( IsWaitFinished() )
- {
- // If light was on turn it off
- if ( m_pEyeFlash->GetBrightness() > 0 )
- {
- m_pEyeFlash->SetBrightness( 0 );
-
- // I'm done with this target
- if ( gpGlobals->curtime > m_fInspectEndTime )
- {
- ClearInspectTarget();
- TaskComplete();
- }
- // Otherwise take another picture
- else
- {
- SetWait( 5.0f, 10.0f );
- }
- }
- // If light was off, take another picture
- else
- {
- TakePhoto();
- SetWait( 0.1f );
- }
- }
- break;
- }
- case TASK_CSCANNER_ATTACK_PRE_FLASH:
- {
- AttackPreFlash();
-
- if ( IsWaitFinished() )
- {
- TaskComplete();
- }
- break;
- }
- case TASK_CSCANNER_ATTACK_FLASH:
- {
- if (IsWaitFinished())
- {
- AttackFlashBlind();
- TaskComplete();
- }
- break;
- }
- default:
- {
- BaseClass::RunTask(pTask);
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Gets the appropriate next schedule based on current condition
-// bits.
-//-----------------------------------------------------------------------------
-int CNPC_CScanner::SelectSchedule(void)
-{
- // Turn our flash off in case we were interrupted while it was on.
- if ( m_pEyeFlash )
- {
- m_pEyeFlash->SetBrightness( 0 );
- }
-
- // ----------------------------------------------------
- // If I'm dead, go into a dive bomb
- // ----------------------------------------------------
- if ( m_iHealth <= 0 )
- {
- m_flSpeed = SCANNER_MAX_DIVE_BOMB_SPEED;
- return SCHED_SCANNER_ATTACK_DIVEBOMB;
- }
-
- // -------------------------------
- // If I'm in a script sequence
- // -------------------------------
- if ( m_NPCState == NPC_STATE_SCRIPT )
- return(BaseClass::SelectSchedule());
-
- // -------------------------------
- // Flinch
- // -------------------------------
- if ( HasCondition(COND_LIGHT_DAMAGE) || HasCondition(COND_HEAVY_DAMAGE) )
- {
- if ( IsHeldByPhyscannon( ) )
- return SCHED_SMALL_FLINCH;
-
- if ( m_NPCState == NPC_STATE_IDLE )
- return SCHED_SMALL_FLINCH;
-
- if ( m_NPCState == NPC_STATE_ALERT )
- {
- if ( m_iHealth < ( 3 * sk_scanner_health.GetFloat() / 4 ))
- return SCHED_TAKE_COVER_FROM_ORIGIN;
-
- if ( SelectWeightedSequence( ACT_SMALL_FLINCH ) != -1 )
- return SCHED_SMALL_FLINCH;
- }
- else
- {
- if ( random->RandomInt( 0, 10 ) < 4 )
- return SCHED_SMALL_FLINCH;
- }
- }
-
- // I'm being held by the physcannon... struggle!
- if ( IsHeldByPhyscannon( ) )
- return SCHED_SCANNER_HELD_BY_PHYSCANNON;
-
- // ----------------------------------------------------------
- // If I have an enemy
- // ----------------------------------------------------------
- if ( GetEnemy() != NULL && GetEnemy()->IsAlive() && m_bShouldInspect )
- {
- // Always chase the enemy
- SetInspectTargetToEnt( GetEnemy(), 9999 );
-
- // Patrol if the enemy has vanished
- if ( HasCondition( COND_LOST_ENEMY ) )
- return SCHED_SCANNER_PATROL;
-
- // Chase via route if we're directly blocked
- if ( HasCondition( COND_SCANNER_FLY_BLOCKED ) )
- return SCHED_SCANNER_CHASE_ENEMY;
-
- // Attack if it's time
- if ( gpGlobals->curtime < m_flNextAttack )
- return SCHED_CSCANNER_SPOTLIGHT_HOVER;
-
- // Melee attack if possible
- if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
- {
- if ( random->RandomInt(0,1) )
- return SCHED_CSCANNER_ATTACK_FLASH;
-
- // TODO: a schedule where he makes an alarm sound?
- return SCHED_SCANNER_CHASE_ENEMY;
- }
-
- // If I'm far from the enemy, stay up high and approach in spotlight mode
- float fAttack2DDist = ( GetEnemyLKP() - GetAbsOrigin() ).Length2D();
-
- if ( fAttack2DDist > SCANNER_ATTACK_FAR_DIST )
- return SCHED_CSCANNER_SPOTLIGHT_HOVER;
-
- // Otherwise fly in low for attack
- return SCHED_SCANNER_ATTACK_HOVER;
- }
-
- // ----------------------------------------------------------
- // If I have something to inspect
- // ----------------------------------------------------------
- if ( HaveInspectTarget() )
- {
- // Pathfind to our goal
- if ( HasCondition( COND_SCANNER_FLY_BLOCKED ) )
- return SCHED_CSCANNER_MOVE_TO_INSPECT;
-
- // If I was chasing, pick with photographing or spotlighting
- if ( m_nFlyMode == SCANNER_FLY_CHASE )
- {
- m_nFlyMode = (random->RandomInt(0,1)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO;
- }
-
- // Handle spotlight
- if ( m_nFlyMode == SCANNER_FLY_SPOT )
- {
- if (HasCondition( COND_CSCANNER_SPOT_ON_TARGET ))
- {
- if (GetTarget())
- {
- RequestInspectSupport();
-
- CAI_BaseNPC *pNPC = GetTarget()->MyNPCPointer();
- // If I'm leading the inspection, so verbal inspection
- if (pNPC && pNPC->GetTarget() == this)
- {
- return SCHED_CSCANNER_SPOTLIGHT_INSPECT_CIT;
- }
-
- return SCHED_CSCANNER_SPOTLIGHT_HOVER;
- }
-
- return SCHED_CSCANNER_SPOTLIGHT_INSPECT_POS;
- }
-
- return SCHED_CSCANNER_SPOTLIGHT_HOVER;
- }
-
- // Handle photographing
- if ( m_nFlyMode == SCANNER_FLY_PHOTO )
- {
- if ( HasCondition( COND_CSCANNER_CAN_PHOTOGRAPH ))
- return SCHED_CSCANNER_PHOTOGRAPH;
-
- return SCHED_CSCANNER_PHOTOGRAPH_HOVER;
- }
-
- // Handle following after a target
- if ( m_nFlyMode == SCANNER_FLY_FOLLOW )
- {
- //TODO: Randomly make noise, photograph, etc
- return SCHED_SCANNER_FOLLOW_HOVER;
- }
-
- // Handle patrolling
- if ( ( m_nFlyMode == SCANNER_FLY_PATROL ) || ( m_nFlyMode == SCANNER_FLY_FAST ) )
- return SCHED_SCANNER_PATROL;
- }
-
- // Default to patrolling around
- return SCHED_SCANNER_PATROL;
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CNPC_CScanner::SpotlightDestroy(void)
-{
- if ( m_hSpotlight )
- {
- UTIL_Remove(m_hSpotlight);
- m_hSpotlight = NULL;
-
- UTIL_Remove(m_hSpotlightTarget);
- m_hSpotlightTarget = NULL;
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CNPC_CScanner::SpotlightCreate(void)
-{
- // Make sure we don't already have one
- if ( m_hSpotlight != NULL )
- return;
-
- // Can we create a spotlight yet?
- if ( gpGlobals->curtime < m_fNextSpotlightTime )
- return;
-
- // If I have an enemy, start spotlight on my enemy
- if (GetEnemy() != NULL)
- {
- Vector vEnemyPos = GetEnemyLKP();
- Vector vTargetPos = vEnemyPos;
- vTargetPos.z = GetFloorZ(vEnemyPos);
- m_vSpotlightDir = vTargetPos - GetLocalOrigin();
- VectorNormalize(m_vSpotlightDir);
- }
- // If I have an target, start spotlight on my target
- else if (GetTarget() != NULL)
- {
- Vector vTargetPos = GetTarget()->GetLocalOrigin();
- vTargetPos.z = GetFloorZ(GetTarget()->GetLocalOrigin());
- m_vSpotlightDir = vTargetPos - GetLocalOrigin();
- VectorNormalize(m_vSpotlightDir);
- }
- // Other wise just start looking down
- else
- {
- m_vSpotlightDir = Vector(0,0,-1);
- }
-
- trace_t tr;
- AI_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + m_vSpotlightDir * 2024, MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr );
-
- m_hSpotlightTarget = (CSpotlightEnd*)CreateEntityByName( "spotlight_end" );
- m_hSpotlightTarget->Spawn();
- m_hSpotlightTarget->SetLocalOrigin( tr.endpos );
- m_hSpotlightTarget->SetOwnerEntity( this );
- // YWB: Because the scanner only moves the target during think, make sure we interpolate over 0.1 sec instead of every tick!!!
- m_hSpotlightTarget->SetSimulatedEveryTick( false );
-
- // Using the same color as the beam...
- m_hSpotlightTarget->SetRenderColor( 255, 255, 255 );
- m_hSpotlightTarget->m_Radius = m_flSpotlightMaxLength;
-
- m_hSpotlight = CBeam::BeamCreate( "sprites/glow_test02.vmt", SPOTLIGHT_WIDTH );
- // Set the temporary spawnflag on the beam so it doesn't save (we'll recreate it on restore)
- m_hSpotlight->AddSpawnFlags( SF_BEAM_TEMPORARY );
- m_hSpotlight->SetColor( 255, 255, 255 );
- m_hSpotlight->SetHaloTexture( m_nHaloSprite );
- m_hSpotlight->SetHaloScale( 32 );
- m_hSpotlight->SetEndWidth( m_hSpotlight->GetWidth() );
- m_hSpotlight->SetBeamFlags( (FBEAM_SHADEOUT|FBEAM_NOTILE) );
- m_hSpotlight->SetBrightness( 32 );
- m_hSpotlight->SetNoise( 0 );
- m_hSpotlight->EntsInit( this, m_hSpotlightTarget );
- m_hSpotlight->SetHDRColorScale( 0.75f ); // Scale this back a bit on HDR maps
- // attach to light
- m_hSpotlight->SetStartAttachment( LookupAttachment( SCANNER_ATTACHMENT_LIGHT ) );
-
- m_vSpotlightAngVelocity = vec3_origin;
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-Vector CNPC_CScanner::SpotlightTargetPos(void)
-{
- // ----------------------------------------------
- // If I have an enemy
- // ----------------------------------------------
- if (GetEnemy() != NULL)
- {
- // If I can see my enemy aim for him
- if (HasCondition(COND_SEE_ENEMY))
- {
- // If its client aim for his eyes
- if (GetEnemy()->GetFlags() & FL_CLIENT)
- {
- m_vSpotlightTargetPos = GetEnemy()->EyePosition();
- }
- // Otherwise same for his feet
- else
- {
- m_vSpotlightTargetPos = GetEnemy()->GetLocalOrigin();
- m_vSpotlightTargetPos.z = GetFloorZ(GetEnemy()->GetLocalOrigin());
- }
- }
- // Otherwise aim for last known position if I can see LKP
- else
- {
- Vector vLKP = GetEnemyLKP();
- m_vSpotlightTargetPos.x = vLKP.x;
- m_vSpotlightTargetPos.y = vLKP.y;
- m_vSpotlightTargetPos.z = GetFloorZ(vLKP);
- }
- }
- // ----------------------------------------------
- // If I have an inspect target
- // ----------------------------------------------
- else if (HaveInspectTarget())
- {
- m_vSpotlightTargetPos = InspectTargetPosition();
- }
- else
- {
- // This creates a nice patrol spotlight sweep
- // in the direction that I'm travelling
- m_vSpotlightTargetPos = GetCurrentVelocity();
- m_vSpotlightTargetPos.z = 0;
- VectorNormalize( m_vSpotlightTargetPos );
- m_vSpotlightTargetPos *= 5;
-
- float noiseScale = 2.5;
- const Vector &noiseMod = GetNoiseMod();
- m_vSpotlightTargetPos.x += noiseScale*sin(noiseMod.x * gpGlobals->curtime + noiseMod.x);
- m_vSpotlightTargetPos.y += noiseScale*cos(noiseMod.y* gpGlobals->curtime + noiseMod.y);
- m_vSpotlightTargetPos.z -= fabs(noiseScale*cos(noiseMod.z* gpGlobals->curtime + noiseMod.z) );
- m_vSpotlightTargetPos = GetLocalOrigin()+m_vSpotlightTargetPos * 2024;
- }
-
- return m_vSpotlightTargetPos;
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-Vector CNPC_CScanner::SpotlightCurrentPos(void)
-{
- Vector vTargetDir = SpotlightTargetPos() - GetLocalOrigin();
- VectorNormalize(vTargetDir);
-
- if (!m_hSpotlight)
- {
- DevMsg("Spotlight pos. called w/o spotlight!\n");
- return vec3_origin;
- }
- // -------------------------------------------------
- // Beam has momentum relative to it's ground speed
- // so sclae the turn rate based on its distance
- // from the beam source
- // -------------------------------------------------
- float fBeamDist = (m_hSpotlightTarget->GetLocalOrigin() - GetLocalOrigin()).Length();
-
- float fBeamTurnRate = atan(50/fBeamDist);
- Vector vNewAngVelocity = fBeamTurnRate * (vTargetDir - m_vSpotlightDir);
-
- float myDecay = 0.4;
- m_vSpotlightAngVelocity = (myDecay * m_vSpotlightAngVelocity + (1-myDecay) * vNewAngVelocity);
-
- // ------------------------------
- // Limit overall angular speed
- // -----------------------------
- if (m_vSpotlightAngVelocity.Length() > 1)
- {
-
- Vector velDir = m_vSpotlightAngVelocity;
- VectorNormalize(velDir);
- m_vSpotlightAngVelocity = velDir * 1;
- }
-
- // ------------------------------
- // Calculate new beam direction
- // ------------------------------
- m_vSpotlightDir = m_vSpotlightDir + m_vSpotlightAngVelocity;
- m_vSpotlightDir = m_vSpotlightDir;
- VectorNormalize(m_vSpotlightDir);
-
-
- // ---------------------------------------------
- // Get beam end point. Only collide with
- // solid objects, not npcs
- // ---------------------------------------------
- trace_t tr;
- Vector vTraceEnd = GetAbsOrigin() + (m_vSpotlightDir * 2 * m_flSpotlightMaxLength);
- AI_TraceLine ( GetAbsOrigin(), vTraceEnd, MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr);
-
- return (tr.endpos);
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose: Update the direction and position of my spotlight
-//------------------------------------------------------------------------------
-void CNPC_CScanner::SpotlightUpdate(void)
-{
- //FIXME: JDW - E3 Hack
- if ( m_bNoLight )
- {
- if ( m_hSpotlight )
- {
- SpotlightDestroy();
- }
-
- return;
- }
-
- if ((m_nFlyMode != SCANNER_FLY_SPOT) &&
- (m_nFlyMode != SCANNER_FLY_PATROL) &&
- (m_nFlyMode != SCANNER_FLY_FAST))
- {
- if ( m_hSpotlight )
- {
- SpotlightDestroy();
- }
- return;
- }
-
- // If I don't have a spotlight attempt to create one
-
- if ( m_hSpotlight == NULL )
- {
- SpotlightCreate();
-
- if ( m_hSpotlight== NULL )
- return;
- }
-
- // Calculate the new homing target position
- m_vSpotlightCurrentPos = SpotlightCurrentPos();
-
- // ------------------------------------------------------------------
- // If I'm not facing the spotlight turn it off
- // ------------------------------------------------------------------
- Vector vSpotDir = m_vSpotlightCurrentPos - GetAbsOrigin();
- VectorNormalize(vSpotDir);
-
- Vector vForward;
- AngleVectors( GetAbsAngles(), &vForward );
-
- float dotpr = DotProduct( vForward, vSpotDir );
-
- if ( dotpr < 0.0 )
- {
- // Leave spotlight off for a while
- m_fNextSpotlightTime = gpGlobals->curtime + 3.0f;
-
- SpotlightDestroy();
- return;
- }
-
- // --------------------------------------------------------------
- // Update spotlight target velocity
- // --------------------------------------------------------------
- Vector vTargetDir = (m_vSpotlightCurrentPos - m_hSpotlightTarget->GetLocalOrigin());
- float vTargetDist = vTargetDir.Length();
-
- Vector vecNewVelocity = vTargetDir;
- VectorNormalize(vecNewVelocity);
- vecNewVelocity *= (10 * vTargetDist);
-
- // If a large move is requested, just jump to final spot as we
- // probably hit a discontinuity
- if (vecNewVelocity.Length() > 200)
- {
- VectorNormalize(vecNewVelocity);
- vecNewVelocity *= 200;
- m_hSpotlightTarget->SetLocalOrigin( m_vSpotlightCurrentPos );
- }
- m_hSpotlightTarget->SetAbsVelocity( vecNewVelocity );
-
- m_hSpotlightTarget->m_vSpotlightOrg = GetAbsOrigin();
-
- // Avoid sudden change in where beam fades out when cross disconinuities
- m_hSpotlightTarget->m_vSpotlightDir = m_hSpotlightTarget->GetLocalOrigin() - m_hSpotlightTarget->m_vSpotlightOrg;
- float flBeamLength = VectorNormalize( m_hSpotlightTarget->m_vSpotlightDir );
- m_flSpotlightCurLength = (0.80*m_flSpotlightCurLength) + (0.2*flBeamLength);
-
- // Fade out spotlight end if past max length.
- if (m_flSpotlightCurLength > 2*m_flSpotlightMaxLength)
- {
- m_hSpotlightTarget->SetRenderColorA( 0 );
- m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength);
- }
- else if (m_flSpotlightCurLength > m_flSpotlightMaxLength)
- {
- m_hSpotlightTarget->SetRenderColorA( (1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength)) );
- m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength);
- }
- else
- {
- m_hSpotlightTarget->SetRenderColorA( 1.0 );
- m_hSpotlight->SetFadeLength(m_flSpotlightCurLength);
- }
-
- // Adjust end width to keep beam width constant
- float flNewWidth = SPOTLIGHT_WIDTH * ( flBeamLength/m_flSpotlightMaxLength);
-
- m_hSpotlight->SetWidth(flNewWidth);
- m_hSpotlight->SetEndWidth(flNewWidth);
-
- m_hSpotlightTarget->m_flLightScale = 0.0;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Called just before we are deleted.
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::UpdateOnRemove( void )
-{
- SpotlightDestroy();
- BaseClass::UpdateOnRemove();
-}
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CNPC_CScanner::TakePhoto(void)
-{
- ScannerEmitSound( "TakePhoto" );
-
- m_pEyeFlash->SetScale( 1.4 );
- m_pEyeFlash->SetBrightness( 255 );
- m_pEyeFlash->SetColor(255,255,255);
-
- Vector vRawPos = InspectTargetPosition();
- Vector vLightPos = vRawPos;
-
- // If taking picture of entity, aim at feet
- if ( GetTarget() )
- {
- if ( GetTarget()->IsPlayer() )
- {
- m_OnPhotographPlayer.FireOutput( GetTarget(), this );
- BlindFlashTarget( GetTarget() );
- }
-
- if ( GetTarget()->MyNPCPointer() != NULL )
- {
- m_OnPhotographNPC.FireOutput( GetTarget(), this );
- GetTarget()->MyNPCPointer()->DispatchInteraction( g_interactionScannerInspectBegin, NULL, this );
- }
- }
-
- SetIdealActivity( (Activity) ACT_SCANNER_FLARE_START );
-
- m_bPhotoTaken = true;
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CNPC_CScanner::AttackPreFlash(void)
-{
- ScannerEmitSound( "TakePhoto" );
-
- // If off turn on, if on turn off
- if (m_pEyeFlash->GetBrightness() == 0)
- {
- m_pEyeFlash->SetScale( 0.5 );
- m_pEyeFlash->SetBrightness( 255 );
- m_pEyeFlash->SetColor(255,0,0);
- }
- else
- {
- m_pEyeFlash->SetBrightness( 0 );
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CNPC_CScanner::AttackFlash(void)
-{
- ScannerEmitSound( "AttackFlash" );
- m_pEyeFlash->SetScale( 1.8 );
- m_pEyeFlash->SetBrightness( 255 );
- m_pEyeFlash->SetColor(255,255,255);
-
- if (GetEnemy() != NULL)
- {
- Vector pos = GetEnemyLKP();
- CBroadcastRecipientFilter filter;
- te->DynamicLight( filter, 0.0, &pos, 200, 200, 255, 0, 300, 0.2, 50 );
-
- if (GetEnemy()->IsPlayer())
- {
- m_OnPhotographPlayer.FireOutput(GetTarget(), this);
- }
- else if( GetEnemy()->MyNPCPointer() )
- {
- m_OnPhotographNPC.FireOutput(GetTarget(), this);
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pTarget -
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::BlindFlashTarget( CBaseEntity *pTarget )
-{
- // Tell all the striders this person is here!
- CAI_BaseNPC ** ppAIs = g_AI_Manager.AccessAIs();
- int nAIs = g_AI_Manager.NumAIs();
-
- if( IsStriderScout() )
- {
- for ( int i = 0; i < nAIs; i++ )
- {
- if( FClassnameIs( ppAIs[ i ], "npc_strider" ) )
- {
- ppAIs[ i ]->UpdateEnemyMemory( pTarget, pTarget->GetAbsOrigin(), this );
- }
- }
- }
-
- // Only bother with player
- if ( pTarget->IsPlayer() == false )
- return;
-
- // Scale the flash value by how closely the player is looking at me
- Vector vFlashDir = GetAbsOrigin() - pTarget->EyePosition();
- VectorNormalize(vFlashDir);
-
- Vector vFacing;
- AngleVectors( pTarget->EyeAngles(), &vFacing );
-
- float dotPr = DotProduct( vFlashDir, vFacing );
-
- // Not if behind us
- if ( dotPr > 0.5f )
- {
- // Make sure nothing in the way
- trace_t tr;
- AI_TraceLine ( GetAbsOrigin(), pTarget->EyePosition(), MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr );
-
- if ( tr.startsolid == false && tr.fraction == 1.0)
- {
- color32 white = { 255, 255, 255, SCANNER_FLASH_MAX_VALUE * dotPr };
-
- if ( ( g_pMaterialSystemHardwareConfig != NULL ) && ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) )
- {
- white.a = ( byte )( ( float )white.a * 0.9f );
- }
-
- float flFadeTime = ( IsX360() ) ? 0.5f : 3.0f;
- UTIL_ScreenFade( pTarget, white, flFadeTime, 0.5, FFADE_IN );
- }
- }
-}
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CNPC_CScanner::AttackFlashBlind(void)
-{
- if( GetEnemy() )
- {
- BlindFlashTarget( GetEnemy() );
- }
-
- m_pEyeFlash->SetBrightness( 0 );
-
- float fAttackDelay = random->RandomFloat(SCANNER_ATTACK_MIN_DELAY,SCANNER_ATTACK_MAX_DELAY);
-
- if( IsStriderScout() )
- {
- // Make strider scouts more snappy.
- fAttackDelay *= 0.5;
- }
-
- m_flNextAttack = gpGlobals->curtime + fAttackDelay;
- m_fNextSpotlightTime = gpGlobals->curtime + 1.0f;
-}
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CNPC_CScanner::AttackDivebomb( void )
-{
- if (m_hSpotlight)
- {
- SpotlightDestroy();
- }
-
- BaseClass::AttackDivebomb();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pTask -
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::StartTask( const Task_t *pTask )
-{
- switch (pTask->iTask)
- {
- case TASK_CSCANNER_GET_PATH_TO_INSPECT_TARGET:
- {
- // Must have somewhere to fly to
- if ( HaveInspectTarget() == false )
- {
- TaskFail( "No inspection target to fly to!\n" );
- return;
- }
-
- if ( GetTarget() )
- {
- //FIXME: Tweak
- //Vector idealPos = IdealGoalForMovement( InspectTargetPosition(), GetAbsOrigin(), 128.0f, 128.0f );
-
- AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin );
-
- if ( GetNavigator()->SetGoal( goal ) )
- {
- TaskComplete();
- return;
- }
- }
- else
- {
- AI_NavGoal_t goal( GOALTYPE_LOCATION, InspectTargetPosition() );
-
- if ( GetNavigator()->SetGoal( goal ) )
- {
- TaskComplete();
- return;
- }
- }
-
- // Don't try and inspect this target again for a few seconds
- CNPC_Citizen *pCitizen = dynamic_cast<CNPC_Citizen *>( GetTarget() );
- if ( pCitizen )
- {
- pCitizen->SetNextScannerInspectTime( gpGlobals->curtime + 5.0 );
- }
-
- TaskFail("No route to inspection target!\n");
- }
- break;
-
- case TASK_CSCANNER_SPOT_INSPECT_ON:
- {
- if (GetTarget() == NULL)
- {
- TaskFail(FAIL_NO_TARGET);
- }
- else
- {
- CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer();
- if (!pNPC)
- {
- TaskFail(FAIL_NO_TARGET);
- }
- else
- {
- pNPC->DispatchInteraction(g_interactionScannerInspectBegin,NULL,this);
-
- // Now we need some time to inspect
- m_fInspectEndTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_LENGTH;
- TaskComplete();
- }
- }
- break;
- }
- case TASK_CSCANNER_SPOT_INSPECT_WAIT:
- {
- if (GetTarget() == NULL)
- {
- TaskFail(FAIL_NO_TARGET);
- }
- else
- {
- CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer();
- if (!pNPC)
- {
- SetTarget( NULL );
- TaskFail(FAIL_NO_TARGET);
- }
- else
- {
- //<<TEMP>>//<<TEMP>> armband too!
- pNPC->DispatchInteraction(g_interactionScannerInspectHandsUp,NULL,this);
- }
- TaskComplete();
- }
- break;
- }
- case TASK_CSCANNER_SPOT_INSPECT_OFF:
- {
- if (GetTarget() == NULL)
- {
- TaskFail(FAIL_NO_TARGET);
- }
- else
- {
- CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer();
- if (!pNPC)
- {
- TaskFail(FAIL_NO_TARGET);
- }
- else
- {
- pNPC->DispatchInteraction(g_interactionScannerInspectDone,NULL,this);
-
- // Clear target entity and don't inspect again for a while
- SetTarget( NULL );
- m_fCheckCitizenTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_DELAY;
- TaskComplete();
- }
- }
- break;
- }
- case TASK_CSCANNER_CLEAR_INSPECT_TARGET:
- {
- ClearInspectTarget();
-
- TaskComplete();
- break;
- }
-
- case TASK_CSCANNER_SET_FLY_SPOT:
- {
- m_nFlyMode = SCANNER_FLY_SPOT;
- TaskComplete();
- break;
- }
-
- case TASK_CSCANNER_SET_FLY_PHOTO:
- {
- m_nFlyMode = SCANNER_FLY_PHOTO;
- m_bPhotoTaken = false;
-
- // Leave spotlight off for a while
- m_fNextSpotlightTime = gpGlobals->curtime + 2.0;
-
- TaskComplete();
- break;
- }
-
- case TASK_CSCANNER_PHOTOGRAPH:
- {
- TakePhoto();
- SetWait( 0.1 );
- break;
- }
-
- case TASK_CSCANNER_ATTACK_PRE_FLASH:
- {
- if( IsStriderScout() )
- {
- Vector vecScare = GetEnemy()->EarPosition();
- Vector vecDir = WorldSpaceCenter() - vecScare;
- VectorNormalize( vecDir );
- vecScare += vecDir * 64.0f;
-
- CSoundEnt::InsertSound( SOUND_DANGER, vecScare, 256, 1.0, this );
- }
-
- if (m_pEyeFlash)
- {
- AttackPreFlash();
- // Flash red for a while
- SetWait( 1.0f );
- }
- else
- {
- TaskFail("No Flash");
- }
- break;
- }
-
- case TASK_CSCANNER_ATTACK_FLASH:
- {
- AttackFlash();
- // Blinding occurs slightly later
- SetWait( 0.05 );
- break;
- }
-
- // Override to go to inspect target position whether or not is an entity
- case TASK_GET_PATH_TO_TARGET:
- {
- if (!HaveInspectTarget())
- {
- TaskFail(FAIL_NO_TARGET);
- }
- else if (GetHintNode())
- {
- Vector vNodePos;
- GetHintNode()->GetPosition(this,&vNodePos);
-
- GetNavigator()->SetGoal( vNodePos );
- }
- else
- {
- AI_NavGoal_t goal( (const Vector &)InspectTargetPosition() );
- goal.pTarget = GetTarget();
- GetNavigator()->SetGoal( goal );
- }
- break;
- }
- default:
- BaseClass::StartTask(pTask);
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-char *CNPC_CScanner::GetScannerSoundPrefix( void )
-{
- if( m_bIsClawScanner )
- return "NPC_SScanner";
-
- return "NPC_CScanner";
-}
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-float CNPC_CScanner::MinGroundDist( void )
-{
- if ( m_nFlyMode == SCANNER_FLY_SPOT && !GetHintNode() )
- {
- return SCANNER_SPOTLIGHT_FLY_HEIGHT;
- }
-
- return SCANNER_NOSPOTLIGHT_FLY_HEIGHT;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::AdjustScannerVelocity( void )
-{
- if ( m_bIsClawScanner )
- {
- m_vCurrentVelocity *= ( 1 + sin( ( gpGlobals->curtime + m_flFlyNoiseBase ) * 2.5f ) * .1 );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : flInterval -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CNPC_CScanner::OverrideMove( float flInterval )
-{
- // ----------------------------------------------
- // If dive bombing
- // ----------------------------------------------
- if (m_nFlyMode == SCANNER_FLY_DIVE)
- {
- MoveToDivebomb( flInterval );
- }
- else
- {
- Vector vMoveTargetPos(0,0,0);
- CBaseEntity *pMoveTarget = NULL;
-
- // The original line of code was, due to the accidental use of '|' instead of
- // '&', always true. Replacing with 'true' to suppress the warning without changing
- // the (long-standing) behavior.
- if ( true ) //!GetNavigator()->IsGoalActive() || ( GetNavigator()->GetCurWaypointFlags() | bits_WP_TO_PATHCORNER ) )
- {
- // Select move target
- if ( GetTarget() != NULL )
- {
- pMoveTarget = GetTarget();
- }
- else if ( GetEnemy() != NULL )
- {
- pMoveTarget = GetEnemy();
- }
-
- // Select move target position
- if ( HaveInspectTarget() )
- {
- vMoveTargetPos = InspectTargetPosition();
- }
- else if ( GetEnemy() != NULL )
- {
- vMoveTargetPos = GetEnemy()->GetAbsOrigin();
- }
- }
- else
- {
- vMoveTargetPos = GetNavigator()->GetCurWaypointPos();
- }
-
- ClearCondition( COND_SCANNER_FLY_CLEAR );
- ClearCondition( COND_SCANNER_FLY_BLOCKED );
-
- // See if we can fly there directly
- if ( pMoveTarget || HaveInspectTarget() )
- {
- trace_t tr;
- AI_TraceHull( GetAbsOrigin(), vMoveTargetPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
-
- float fTargetDist = (1.0f-tr.fraction)*(GetAbsOrigin() - vMoveTargetPos).Length();
-
- if ( ( tr.m_pEnt == pMoveTarget ) || ( fTargetDist < 50 ) )
- {
- if ( g_debug_cscanner.GetBool() )
- {
- NDebugOverlay::Line(GetLocalOrigin(), vMoveTargetPos, 0,255,0, true, 0);
- NDebugOverlay::Cross3D(tr.endpos,Vector(-5,-5,-5),Vector(5,5,5),0,255,0,true,0.1);
- }
-
- SetCondition( COND_SCANNER_FLY_CLEAR );
- }
- else
- {
- //HANDY DEBUG TOOL
- if ( g_debug_cscanner.GetBool() )
- {
- NDebugOverlay::Line(GetLocalOrigin(), vMoveTargetPos, 255,0,0, true, 0);
- NDebugOverlay::Cross3D(tr.endpos,Vector(-5,-5,-5),Vector(5,5,5),255,0,0,true,0.1);
- }
-
- SetCondition( COND_SCANNER_FLY_BLOCKED );
- }
- }
-
- // If I have a route, keep it updated and move toward target
- if ( GetNavigator()->IsGoalActive() )
- {
- if ( OverridePathMove( pMoveTarget, flInterval ) )
- {
- BlendPhyscannonLaunchSpeed();
- return true;
- }
- }
- else if (m_nFlyMode == SCANNER_FLY_SPOT)
- {
- MoveToSpotlight( flInterval );
- }
- // If photographing
- else if ( m_nFlyMode == SCANNER_FLY_PHOTO )
- {
- MoveToPhotograph( flInterval );
- }
- else if ( m_nFlyMode == SCANNER_FLY_FOLLOW )
- {
- MoveToSpotlight( flInterval );
- }
- // ----------------------------------------------
- // If attacking
- // ----------------------------------------------
- else if (m_nFlyMode == SCANNER_FLY_ATTACK)
- {
- if ( m_hSpotlight )
- {
- SpotlightDestroy();
- }
-
- MoveToAttack( flInterval );
- }
- // -----------------------------------------------------------------
- // If I don't have a route, just decelerate
- // -----------------------------------------------------------------
- else if (!GetNavigator()->IsGoalActive())
- {
- float myDecay = 9.5;
- Decelerate( flInterval, myDecay);
- }
- }
-
- MoveExecute_Alive( flInterval );
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Accelerates toward a given position.
-// Input : flInterval - Time interval over which to move.
-// vecMoveTarget - Position to move toward.
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::MoveToTarget( float flInterval, const Vector &vecMoveTarget )
-{
- // Don't move if stalling
- if ( m_flEngineStallTime > gpGlobals->curtime )
- return;
-
- // Look at our inspection target if we have one
- if ( GetEnemy() != NULL )
- {
- // Otherwise at our enemy
- TurnHeadToTarget( flInterval, GetEnemy()->EyePosition() );
- }
- else if ( HaveInspectTarget() )
- {
- TurnHeadToTarget( flInterval, InspectTargetPosition() );
- }
- else
- {
- // Otherwise face our motion direction
- TurnHeadToTarget( flInterval, vecMoveTarget );
- }
-
- // -------------------------------------
- // Move towards our target
- // -------------------------------------
- float myAccel;
- float myZAccel = 400.0f;
- float myDecay = 0.15f;
-
- Vector vecCurrentDir;
-
- // Get the relationship between my current velocity and the way I want to be going.
- vecCurrentDir = GetCurrentVelocity();
- VectorNormalize( vecCurrentDir );
-
- Vector targetDir = vecMoveTarget - GetAbsOrigin();
- float flDist = VectorNormalize(targetDir);
-
- float flDot;
- flDot = DotProduct( targetDir, vecCurrentDir );
-
- if( flDot > 0.25 )
- {
- // If my target is in front of me, my flight model is a bit more accurate.
- myAccel = 250;
- }
- else
- {
- // Have a harder time correcting my course if I'm currently flying away from my target.
- myAccel = 128;
- }
-
- if ( myAccel > flDist / flInterval )
- {
- myAccel = flDist / flInterval;
- }
-
- if ( myZAccel > flDist / flInterval )
- {
- myZAccel = flDist / flInterval;
- }
-
- MoveInDirection( flInterval, targetDir, myAccel, myZAccel, myDecay );
-
- // calc relative banking targets
- Vector forward, right, up;
- GetVectors( &forward, &right, &up );
-
- m_vCurrentBanking.x = targetDir.x;
- m_vCurrentBanking.z = 120.0f * DotProduct( right, targetDir );
- m_vCurrentBanking.y = 0;
-
- float speedPerc = SimpleSplineRemapVal( GetCurrentVelocity().Length(), 0.0f, GetMaxSpeed(), 0.0f, 1.0f );
-
- speedPerc = clamp( speedPerc, 0.0f, 1.0f );
-
- m_vCurrentBanking *= speedPerc;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : flInterval -
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::MoveToSpotlight( float flInterval )
-{
- if ( flInterval <= 0 )
- return;
-
- Vector vTargetPos;
-
- if ( HaveInspectTarget() )
- {
- vTargetPos = InspectTargetPosition();
- }
- else if ( GetEnemy() != NULL )
- {
- vTargetPos = GetEnemyLKP();
- }
- else
- {
- return;
- }
-
- //float flDesiredDist = SCANNER_SPOTLIGHT_NEAR_DIST + ( ( SCANNER_SPOTLIGHT_FAR_DIST - SCANNER_SPOTLIGHT_NEAR_DIST ) / 2 );
-
- float flIdealHeightDiff = SCANNER_SPOTLIGHT_NEAR_DIST;
- if( IsEnemyPlayerInSuit() )
- {
- flIdealHeightDiff *= 0.5;
- }
-
- Vector idealPos = IdealGoalForMovement( vTargetPos, GetAbsOrigin(), GetGoalDistance(), flIdealHeightDiff );
-
- MoveToTarget( flInterval, idealPos );
-
- //TODO: Re-implement?
-
- /*
- // ------------------------------------------------
- // Also keep my distance from other squad members
- // unless I'm inspecting
- // ------------------------------------------------
- if (m_pSquad &&
- gpGlobals->curtime > m_fInspectEndTime)
- {
- CBaseEntity* pNearest = m_pSquad->NearestSquadMember(this);
- if (pNearest)
- {
- Vector vNearestDir = (pNearest->GetLocalOrigin() - GetLocalOrigin());
- if (vNearestDir.Length() < SCANNER_SQUAD_FLY_DIST)
- {
- vNearestDir = pNearest->GetLocalOrigin() - GetLocalOrigin();
- VectorNormalize(vNearestDir);
- vFlyDirection -= 0.5*vNearestDir;
- }
- }
- }
-
- // ---------------------------------------------------------
- // Add evasion if I have taken damage recently
- // ---------------------------------------------------------
- if ((m_flLastDamageTime + SCANNER_EVADE_TIME) > gpGlobals->curtime)
- {
- vFlyDirection = vFlyDirection + VelocityToEvade(GetEnemyCombatCharacterPointer());
- }
- */
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : float
-//-----------------------------------------------------------------------------
-float CNPC_CScanner::GetGoalDistance( void )
-{
- if ( m_flGoalOverrideDistance != 0.0f )
- return m_flGoalOverrideDistance;
-
- switch ( m_nFlyMode )
- {
- case SCANNER_FLY_PHOTO:
- return ( SCANNER_PHOTO_NEAR_DIST + ( ( SCANNER_PHOTO_FAR_DIST - SCANNER_PHOTO_NEAR_DIST ) / 2 ) );
- break;
-
- case SCANNER_FLY_SPOT:
- {
- float goalDist = ( SCANNER_SPOTLIGHT_NEAR_DIST + ( ( SCANNER_SPOTLIGHT_FAR_DIST - SCANNER_SPOTLIGHT_NEAR_DIST ) / 2 ) );
- if( IsEnemyPlayerInSuit() )
- {
- goalDist *= 0.5;
- }
- return goalDist;
- }
- break;
-
- case SCANNER_FLY_FOLLOW:
- return ( SCANNER_FOLLOW_DIST );
- break;
- }
-
- return BaseClass::GetGoalDistance();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::MoveToPhotograph(float flInterval)
-{
- if ( HaveInspectTarget() == false )
- return;
-
- //float flDesiredDist = SCANNER_PHOTO_NEAR_DIST + ( ( SCANNER_PHOTO_FAR_DIST - SCANNER_PHOTO_NEAR_DIST ) / 2 );
-
- Vector idealPos = IdealGoalForMovement( InspectTargetPosition(), GetAbsOrigin(), GetGoalDistance(), 32.0f );
-
- MoveToTarget( flInterval, idealPos );
-
- //FIXME: Re-implement?
-
- /*
- // ------------------------------------------------
- // Also keep my distance from other squad members
- // unless I'm inspecting
- // ------------------------------------------------
- if (m_pSquad &&
- gpGlobals->curtime > m_fInspectEndTime)
- {
- CBaseEntity* pNearest = m_pSquad->NearestSquadMember(this);
- if (pNearest)
- {
- Vector vNearestDir = (pNearest->GetLocalOrigin() - GetLocalOrigin());
- if (vNearestDir.Length() < SCANNER_SQUAD_FLY_DIST)
- {
- vNearestDir = pNearest->GetLocalOrigin() - GetLocalOrigin();
- VectorNormalize(vNearestDir);
- vFlyDirection -= 0.5*vNearestDir;
- }
- }
- }
- */
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: This is a generic function (to be implemented by sub-classes) to
-// handle specific interactions between different types of characters
-// (For example the barnacle grabbing an NPC)
-// Input : Constant for the type of interaction
-// Output : true - if sub-class has a response for the interaction
-// false - if sub-class has no response
-//-----------------------------------------------------------------------------
-bool CNPC_CScanner::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* pSourceEnt)
-{
- // TODO:: - doing this by just an interrupt contition would be a lot better!
- if (interactionType == g_interactionScannerSupportEntity)
- {
- // Only accept help request if I'm not already busy
- if (GetEnemy() == NULL && !HaveInspectTarget())
- {
- // Only accept if target is a reasonable distance away
- CBaseEntity* pTarget = (CBaseEntity*)data;
- float fTargetDist = (pTarget->GetLocalOrigin() - GetLocalOrigin()).Length();
-
- if (fTargetDist < SCANNER_SQUAD_HELP_DIST)
- {
- float fInspectTime = (((CNPC_CScanner*)pSourceEnt)->m_fInspectEndTime - gpGlobals->curtime);
- SetInspectTargetToEnt(pTarget,fInspectTime);
-
- if (random->RandomInt(0,2)==0)
- {
- SetSchedule(SCHED_CSCANNER_PHOTOGRAPH_HOVER);
- }
- else
- {
- SetSchedule(SCHED_CSCANNER_SPOTLIGHT_HOVER);
- }
- return true;
- }
- }
- }
- else if (interactionType == g_interactionScannerSupportPosition)
- {
- // Only accept help request if I'm not already busy
- if (GetEnemy() == NULL && !HaveInspectTarget())
- {
- // Only accept if target is a reasonable distance away
- Vector vInspectPos;
- vInspectPos.x = ((Vector *)data)->x;
- vInspectPos.y = ((Vector *)data)->y;
- vInspectPos.z = ((Vector *)data)->z;
-
- float fTargetDist = (vInspectPos - GetLocalOrigin()).Length();
-
- if (fTargetDist < SCANNER_SQUAD_HELP_DIST)
- {
- float fInspectTime = (((CNPC_CScanner*)pSourceEnt)->m_fInspectEndTime - gpGlobals->curtime);
- SetInspectTargetToPos(vInspectPos,fInspectTime);
-
- if (random->RandomInt(0,2)==0)
- {
- SetSchedule(SCHED_CSCANNER_PHOTOGRAPH_HOVER);
- }
- else
- {
- SetSchedule(SCHED_CSCANNER_SPOTLIGHT_HOVER);
- }
- return true;
- }
- }
- }
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &inputdata -
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::InputDisableSpotlight( inputdata_t &inputdata )
-{
- m_bNoLight = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : float
-//-----------------------------------------------------------------------------
-float CNPC_CScanner::GetHeadTurnRate( void )
-{
- if ( GetEnemy() )
- return 800.0f;
-
- if ( HaveInspectTarget() )
- return 500.0f;
-
- return BaseClass::GetHeadTurnRate();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &inputdata -
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::InputSetFollowTarget( inputdata_t &inputdata )
-{
- InspectTarget( inputdata, SCANNER_FLY_FOLLOW );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &inputdata -
-//-----------------------------------------------------------------------------
-void CNPC_CScanner::InputClearFollowTarget( inputdata_t &inputdata )
-{
- SetInspectTargetToEnt( NULL, 0 );
-
- m_nFlyMode = SCANNER_FLY_PATROL;
-}
-
-//-----------------------------------------------------------------------------
-//
-// Schedules
-//
-//-----------------------------------------------------------------------------
-
-AI_BEGIN_CUSTOM_NPC( npc_cscanner, CNPC_CScanner )
- DECLARE_TASK(TASK_CSCANNER_SET_FLY_PHOTO)
- DECLARE_TASK(TASK_CSCANNER_SET_FLY_SPOT)
- DECLARE_TASK(TASK_CSCANNER_PHOTOGRAPH)
- DECLARE_TASK(TASK_CSCANNER_ATTACK_PRE_FLASH)
- DECLARE_TASK(TASK_CSCANNER_ATTACK_FLASH)
- DECLARE_TASK(TASK_CSCANNER_SPOT_INSPECT_ON)
- DECLARE_TASK(TASK_CSCANNER_SPOT_INSPECT_WAIT)
- DECLARE_TASK(TASK_CSCANNER_SPOT_INSPECT_OFF)
- DECLARE_TASK(TASK_CSCANNER_CLEAR_INSPECT_TARGET)
- DECLARE_TASK(TASK_CSCANNER_GET_PATH_TO_INSPECT_TARGET)
-
- DECLARE_CONDITION(COND_CSCANNER_HAVE_INSPECT_TARGET)
- DECLARE_CONDITION(COND_CSCANNER_INSPECT_DONE)
- DECLARE_CONDITION(COND_CSCANNER_CAN_PHOTOGRAPH)
- DECLARE_CONDITION(COND_CSCANNER_SPOT_ON_TARGET)
-
- DECLARE_ACTIVITY(ACT_SCANNER_SMALL_FLINCH_ALERT)
- DECLARE_ACTIVITY(ACT_SCANNER_SMALL_FLINCH_COMBAT)
- DECLARE_ACTIVITY(ACT_SCANNER_INSPECT)
- DECLARE_ACTIVITY(ACT_SCANNER_WALK_ALERT)
- DECLARE_ACTIVITY(ACT_SCANNER_WALK_COMBAT)
- DECLARE_ACTIVITY(ACT_SCANNER_FLARE)
- DECLARE_ACTIVITY(ACT_SCANNER_RETRACT)
- DECLARE_ACTIVITY(ACT_SCANNER_FLARE_PRONGS)
- DECLARE_ACTIVITY(ACT_SCANNER_RETRACT_PRONGS)
- DECLARE_ACTIVITY(ACT_SCANNER_FLARE_START)
-
- DECLARE_ANIMEVENT( AE_SCANNER_CLOSED )
-
- DECLARE_INTERACTION(g_interactionScannerInspect)
- DECLARE_INTERACTION(g_interactionScannerInspectBegin)
- DECLARE_INTERACTION(g_interactionScannerInspectDone)
- DECLARE_INTERACTION(g_interactionScannerInspectHandsUp)
- DECLARE_INTERACTION(g_interactionScannerInspectShowArmband)
- DECLARE_INTERACTION(g_interactionScannerSupportEntity)
- DECLARE_INTERACTION(g_interactionScannerSupportPosition)
-
- //=========================================================
- // > SCHED_CSCANNER_PATROL
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CSCANNER_PATROL,
-
- " Tasks"
- " TASK_CSCANNER_CLEAR_INSPECT_TARGET 0"
- " TASK_SCANNER_SET_FLY_PATROL 0"
- " TASK_SET_TOLERANCE_DISTANCE 32"
- " TASK_SET_ROUTE_SEARCH_TIME 5" // Spend 5 seconds trying to build a path if stuck
- " TASK_GET_PATH_TO_RANDOM_NODE 2000"
- " TASK_RUN_PATH 0"
- " TASK_WAIT_FOR_MOVEMENT 0"
- ""
- " Interrupts"
- " COND_GIVE_WAY"
- " COND_NEW_ENEMY"
- " COND_SEE_ENEMY"
- " COND_SEE_FEAR"
- " COND_HEAR_COMBAT"
- " COND_HEAR_DANGER"
- " COND_HEAR_PLAYER"
- " COND_LIGHT_DAMAGE"
- " COND_HEAVY_DAMAGE"
- " COND_PROVOKED"
- " COND_CSCANNER_HAVE_INSPECT_TARGET"
- " COND_SCANNER_GRABBED_BY_PHYSCANNON"
- )
-
- //=========================================================
- // > SCHED_CSCANNER_SPOTLIGHT_HOVER
- //
- // Hover above target entity, trying to get spotlight
- // on my target
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CSCANNER_SPOTLIGHT_HOVER,
-
- " Tasks"
- " TASK_CSCANNER_SET_FLY_SPOT 0"
- " TASK_SET_ACTIVITY ACTIVITY:ACT_WALK "
- " TASK_WAIT 1"
- ""
- " Interrupts"
- " COND_CSCANNER_SPOT_ON_TARGET"
- " COND_CSCANNER_INSPECT_DONE"
- " COND_SCANNER_FLY_BLOCKED"
- " COND_NEW_ENEMY"
- " COND_SCANNER_GRABBED_BY_PHYSCANNON"
- )
-
- //=========================================================
- // > SCHED_CSCANNER_SPOTLIGHT_INSPECT_POS
- //
- // Inspect a position once spotlight is on it
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CSCANNER_SPOTLIGHT_INSPECT_POS,
-
- " Tasks"
- " TASK_CSCANNER_SET_FLY_SPOT 0"
- " TASK_SET_ACTIVITY ACTIVITY:ACT_SCANNER_INSPECT"
- " TASK_SPEAK_SENTENCE 3" // Curious sound
- " TASK_WAIT 5"
- " TASK_CSCANNER_CLEAR_INSPECT_TARGET 0"
- ""
- " Interrupts"
- " COND_CSCANNER_INSPECT_DONE"
- " COND_HEAR_DANGER"
- " COND_HEAR_COMBAT"
- " COND_NEW_ENEMY"
- " COND_SCANNER_GRABBED_BY_PHYSCANNON"
- )
-
- //=========================================================
- // > SCHED_CSCANNER_SPOTLIGHT_INSPECT_CIT
- //
- // Inspect a citizen once spotlight is on it
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CSCANNER_SPOTLIGHT_INSPECT_CIT,
-
- " Tasks"
- " TASK_CSCANNER_SET_FLY_SPOT 0"
- " TASK_SET_ACTIVITY ACTIVITY:ACT_SCANNER_INSPECT"
- " TASK_SPEAK_SENTENCE 0" // Stop!
- " TASK_WAIT 1"
- " TASK_CSCANNER_SPOT_INSPECT_ON 0"
- " TASK_WAIT 2"
- " TASK_SPEAK_SENTENCE 1" // Hands on head or Show Armband!
- " TASK_WAIT 1"
- " TASK_CSCANNER_SPOT_INSPECT_WAIT 0"
- " TASK_WAIT 5"
- " TASK_SPEAK_SENTENCE 2" // Free to go!
- " TASK_WAIT 1"
- " TASK_CSCANNER_SPOT_INSPECT_OFF 0"
- " TASK_CSCANNER_CLEAR_INSPECT_TARGET 0"
- ""
- " Interrupts"
- " COND_NEW_ENEMY"
- " COND_SCANNER_GRABBED_BY_PHYSCANNON"
- )
-
- //=========================================================
- // > SCHED_CSCANNER_PHOTOGRAPH_HOVER
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CSCANNER_PHOTOGRAPH_HOVER,
-
- " Tasks"
- " TASK_CSCANNER_SET_FLY_PHOTO 0"
- " TASK_WAIT 2"
- ""
- " Interrupts"
- " COND_CSCANNER_INSPECT_DONE"
- " COND_CSCANNER_CAN_PHOTOGRAPH"
- " COND_SCANNER_FLY_BLOCKED"
- " COND_NEW_ENEMY"
- " COND_SCANNER_GRABBED_BY_PHYSCANNON"
- )
-
- //=========================================================
- // > SCHED_CSCANNER_PHOTOGRAPH
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CSCANNER_PHOTOGRAPH,
-
- " Tasks"
- " TASK_CSCANNER_SET_FLY_PHOTO 0"
- " TASK_CSCANNER_PHOTOGRAPH 0"
- ""
- " Interrupts"
- " COND_CSCANNER_INSPECT_DONE"
- " COND_NEW_ENEMY"
- " COND_ENEMY_DEAD"
- " COND_SCANNER_GRABBED_BY_PHYSCANNON"
- )
-
- //=========================================================
- // > SCHED_CSCANNER_ATTACK_FLASH
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CSCANNER_ATTACK_FLASH,
-
- " Tasks"
- " TASK_SCANNER_SET_FLY_ATTACK 0"
- " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
- " TASK_CSCANNER_ATTACK_PRE_FLASH 0 "
- " TASK_CSCANNER_ATTACK_FLASH 0"
- " TASK_WAIT 0.5"
- ""
- " Interrupts"
- " COND_NEW_ENEMY"
- " COND_ENEMY_DEAD"
- " COND_SCANNER_GRABBED_BY_PHYSCANNON"
- )
-
- //=========================================================
- // > SCHED_CSCANNER_MOVE_TO_INSPECT
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CSCANNER_MOVE_TO_INSPECT,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_SCANNER_PATROL"
- " TASK_SET_TOLERANCE_DISTANCE 128"
- " TASK_CSCANNER_GET_PATH_TO_INSPECT_TARGET 0"
- " TASK_RUN_PATH 0"
- " TASK_WAIT_FOR_MOVEMENT 0"
- ""
- " Interrupts"
- " COND_SCANNER_FLY_CLEAR"
- " COND_NEW_ENEMY"
- " COND_SCANNER_GRABBED_BY_PHYSCANNON"
- )
-
-AI_END_CUSTOM_NPC()
-
-//-----------------------------------------------------------------------------
-// Claw Scanner
-//
-// Scanner that always spawns as a claw scanner
-//-----------------------------------------------------------------------------
-
-class CNPC_ClawScanner : public CNPC_CScanner
-{
-DECLARE_CLASS( CNPC_ClawScanner, CNPC_CScanner );
-
-public:
- CNPC_ClawScanner();
- DECLARE_DATADESC();
-};
-
-BEGIN_DATADESC( CNPC_ClawScanner )
-END_DATADESC()
-
-
-LINK_ENTITY_TO_CLASS(npc_clawscanner, CNPC_ClawScanner);
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-CNPC_ClawScanner::CNPC_ClawScanner()
-{
- // override our superclass's setting
- BecomeClawScanner();
-}
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "npcevent.h" +#include "soundenvelope.h" +#include "ai_hint.h" +#include "ai_moveprobe.h" +#include "ai_squad.h" +#include "beam_shared.h" +#include "globalstate.h" +#include "soundent.h" +#include "npc_citizen17.h" +#include "gib.h" +#include "spotlightend.h" +#include "IEffects.h" +#include "items.h" +#include "ai_route.h" +#include "player_pickup.h" +#include "weapon_physcannon.h" +#include "hl2_player.h" +#include "npc_scanner.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Singleton interfaces +//----------------------------------------------------------------------------- +extern IMaterialSystemHardwareConfig *g_pMaterialSystemHardwareConfig; + +//----------------------------------------------------------------------------- +// Parameters for how the scanner relates to citizens. +//----------------------------------------------------------------------------- +#define SCANNER_CIT_INSPECT_DELAY 10 // Check for citizens this often +#define SCANNER_CIT_INSPECT_GROUND_DIST 500 // How far to look for citizens to inspect +#define SCANNER_CIT_INSPECT_FLY_DIST 1500 // How far to look for citizens to inspect + +#define SCANNER_CIT_INSPECT_LENGTH 5 // How long does the inspection last +#define SCANNER_HINT_INSPECT_LENGTH 5 // How long does the inspection last +#define SCANNER_SOUND_INSPECT_LENGTH 5 // How long does the inspection last + +#define SCANNER_HINT_INSPECT_DELAY 15 // Check for hint nodes this often + +#define SPOTLIGHT_WIDTH 32 + +#define SCANNER_SPOTLIGHT_NEAR_DIST 64 +#define SCANNER_SPOTLIGHT_FAR_DIST 256 +#define SCANNER_SPOTLIGHT_FLY_HEIGHT 72 +#define SCANNER_NOSPOTLIGHT_FLY_HEIGHT 72 + +#define SCANNER_FLASH_MIN_DIST 900 // How far does flash effect enemy +#define SCANNER_FLASH_MAX_DIST 1200 // How far does flash effect enemy + +#define SCANNER_FLASH_MAX_VALUE 240 // How bright is maximum flash + +#define SCANNER_PHOTO_NEAR_DIST 64 +#define SCANNER_PHOTO_FAR_DIST 128 + +#define SCANNER_FOLLOW_DIST 128 + +#define SCANNER_NUM_GIBS 6 // Number of gibs in gib file + +// Strider Scout Scanners +#define SCANNER_SCOUT_MAX_SPEED 150 + +ConVar sk_scanner_health( "sk_scanner_health","0"); +ConVar g_debug_cscanner( "g_debug_cscanner", "0" ); + +//----------------------------------------------------------------------------- +// Private activities. +//----------------------------------------------------------------------------- +static int ACT_SCANNER_SMALL_FLINCH_ALERT = 0; +static int ACT_SCANNER_SMALL_FLINCH_COMBAT = 0; +static int ACT_SCANNER_INSPECT = 0; +static int ACT_SCANNER_WALK_ALERT = 0; +static int ACT_SCANNER_WALK_COMBAT = 0; +static int ACT_SCANNER_FLARE = 0; +static int ACT_SCANNER_RETRACT = 0; +static int ACT_SCANNER_FLARE_PRONGS = 0; +static int ACT_SCANNER_RETRACT_PRONGS = 0; +static int ACT_SCANNER_FLARE_START = 0; + +//----------------------------------------------------------------------------- +// Interactions +//----------------------------------------------------------------------------- +int g_interactionScannerInspect = 0; +int g_interactionScannerInspectBegin = 0; +int g_interactionScannerInspectHandsUp = 0; +int g_interactionScannerInspectShowArmband = 0;//<<TEMP>>still to be completed +int g_interactionScannerInspectDone = 0; +int g_interactionScannerSupportEntity = 0; +int g_interactionScannerSupportPosition = 0; + +//----------------------------------------------------------------------------- +// Animation events +//------------------------------------------------------------------------ +int AE_SCANNER_CLOSED; + +//----------------------------------------------------------------------------- +// Attachment points +//----------------------------------------------------------------------------- +#define SCANNER_ATTACHMENT_LIGHT "light" +#define SCANNER_ATTACHMENT_FLASH 1 +#define SCANNER_ATTACHMENT_LPRONG 2 +#define SCANNER_ATTACHMENT_RPRONG 3 + +//----------------------------------------------------------------------------- +// Other defines. +//----------------------------------------------------------------------------- +#define SCANNER_MAX_BEAMS 4 + +BEGIN_DATADESC( CNPC_CScanner ) + + DEFINE_SOUNDPATCH( m_pEngineSound ), + + DEFINE_EMBEDDED( m_KilledInfo ), + DEFINE_FIELD( m_flGoalOverrideDistance, FIELD_FLOAT ), + DEFINE_FIELD( m_bPhotoTaken, FIELD_BOOLEAN ), + DEFINE_FIELD( m_vInspectPos, FIELD_VECTOR ), + DEFINE_FIELD( m_fInspectEndTime, FIELD_TIME ), + DEFINE_FIELD( m_fCheckCitizenTime, FIELD_TIME ), + DEFINE_FIELD( m_fCheckHintTime, FIELD_TIME ), + DEFINE_KEYFIELD( m_bShouldInspect, FIELD_BOOLEAN, "ShouldInspect" ), + DEFINE_KEYFIELD( m_bOnlyInspectPlayers, FIELD_BOOLEAN, "OnlyInspectPlayers" ), + DEFINE_KEYFIELD( m_bNeverInspectPlayers,FIELD_BOOLEAN, "NeverInspectPlayers" ), + DEFINE_FIELD( m_fNextPhotographTime, FIELD_TIME ), +// DEFINE_FIELD( m_pEyeFlash, FIELD_CLASSPTR ), + DEFINE_FIELD( m_vSpotlightTargetPos, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vSpotlightCurrentPos, FIELD_POSITION_VECTOR ), +// don't save (recreated after restore/transition) +// DEFINE_FIELD( m_hSpotlight, FIELD_EHANDLE ), +// DEFINE_FIELD( m_hSpotlightTarget, FIELD_EHANDLE ), + DEFINE_FIELD( m_vSpotlightDir, FIELD_VECTOR ), + DEFINE_FIELD( m_vSpotlightAngVelocity, FIELD_VECTOR ), + DEFINE_FIELD( m_flSpotlightCurLength, FIELD_FLOAT ), + DEFINE_FIELD( m_fNextSpotlightTime, FIELD_TIME ), + DEFINE_FIELD( m_nHaloSprite, FIELD_INTEGER ), + DEFINE_FIELD( m_fNextFlySoundTime, FIELD_TIME ), + DEFINE_FIELD( m_nFlyMode, FIELD_INTEGER ), + DEFINE_FIELD( m_nPoseTail, FIELD_INTEGER ), + DEFINE_FIELD( m_nPoseDynamo, FIELD_INTEGER ), + DEFINE_FIELD( m_nPoseFlare, FIELD_INTEGER ), + DEFINE_FIELD( m_nPoseFaceVert, FIELD_INTEGER ), + DEFINE_FIELD( m_nPoseFaceHoriz, FIELD_INTEGER ), + + DEFINE_FIELD( m_bIsClawScanner, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bIsOpen, FIELD_BOOLEAN ), + + // DEFINE_FIELD( m_bHasSpoken, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_pSmokeTrail, FIELD_CLASSPTR ), + DEFINE_FIELD( m_flFlyNoiseBase, FIELD_FLOAT ), + DEFINE_FIELD( m_flEngineStallTime, FIELD_TIME ), + + DEFINE_FIELD( m_vecDiveBombDirection, FIELD_VECTOR ), + DEFINE_FIELD( m_flDiveBombRollForce, FIELD_FLOAT ), + + DEFINE_KEYFIELD( m_flSpotlightMaxLength, FIELD_FLOAT, "SpotlightLength"), + DEFINE_KEYFIELD( m_flSpotlightGoalWidth, FIELD_FLOAT, "SpotlightWidth"), + + // Physics Influence + DEFINE_FIELD( m_hPhysicsAttacker, FIELD_EHANDLE ), + DEFINE_FIELD( m_flLastPhysicsInfluenceTime, FIELD_TIME ), + + DEFINE_KEYFIELD( m_bNoLight, FIELD_BOOLEAN, "SpotlightDisabled" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "DisableSpotlight", InputDisableSpotlight ), + DEFINE_INPUTFUNC( FIELD_STRING, "InspectTargetPhoto", InputInspectTargetPhoto ), + DEFINE_INPUTFUNC( FIELD_STRING, "InspectTargetSpotlight", InputInspectTargetSpotlight ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "InputShouldInspect", InputShouldInspect ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetFollowTarget", InputSetFollowTarget ), + DEFINE_INPUTFUNC( FIELD_VOID, "ClearFollowTarget", InputClearFollowTarget ), + + DEFINE_INPUTFUNC( FIELD_STRING, "DeployMine", InputDeployMine ), + DEFINE_INPUTFUNC( FIELD_STRING, "EquipMine", InputEquipMine ), + + DEFINE_OUTPUT( m_OnPhotographPlayer, "OnPhotographPlayer" ), + DEFINE_OUTPUT( m_OnPhotographNPC, "OnPhotographNPC" ), + +END_DATADESC() + + +LINK_ENTITY_TO_CLASS(npc_cscanner, CNPC_CScanner); + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CNPC_CScanner::CNPC_CScanner() +{ +#ifdef _DEBUG + m_vInspectPos.Init(); + m_vSpotlightTargetPos.Init(); + m_vSpotlightCurrentPos.Init(); + m_vSpotlightDir.Init(); + m_vSpotlightAngVelocity.Init(); +#endif + m_bShouldInspect = true; + m_bOnlyInspectPlayers = false; + m_bNeverInspectPlayers = false; + + char szMapName[256]; + Q_strncpy(szMapName, STRING(gpGlobals->mapname), sizeof(szMapName) ); + Q_strlower(szMapName); + + if( !Q_strnicmp( szMapName, "d3_c17", 6 ) ) + { + // Streetwar scanners are claw scanners + m_bIsClawScanner = true; + } + else + { + m_bIsClawScanner = false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CScanner::Spawn(void) +{ + // Check for user error + if (m_flSpotlightMaxLength <= 0) + { + DevMsg("CNPC_CScanner::Spawn: Invalid spotlight length <= 0, setting to 500\n"); + m_flSpotlightMaxLength = 500; + } + + if (m_flSpotlightGoalWidth <= 0) + { + DevMsg("CNPC_CScanner::Spawn: Invalid spotlight width <= 0, setting to 100\n"); + m_flSpotlightGoalWidth = 100; + } + + if (m_flSpotlightGoalWidth > MAX_BEAM_WIDTH ) + { + DevMsg("CNPC_CScanner::Spawn: Invalid spotlight width %.1f (max %.1f).\n", m_flSpotlightGoalWidth, MAX_BEAM_WIDTH ); + m_flSpotlightGoalWidth = MAX_BEAM_WIDTH; + } + + Precache(); + + if( m_bIsClawScanner ) + { + SetModel( "models/shield_scanner.mdl"); + } + else + { + SetModel( "models/combine_scanner.mdl"); + } + + m_iHealth = sk_scanner_health.GetFloat(); + m_iMaxHealth = m_iHealth; + + // ------------------------------------ + // Init all class vars + // ------------------------------------ + m_vInspectPos = vec3_origin; + m_fInspectEndTime = 0; + m_fCheckCitizenTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_DELAY; + m_fCheckHintTime = gpGlobals->curtime + SCANNER_HINT_INSPECT_DELAY; + m_fNextPhotographTime = 0; + + m_vSpotlightTargetPos = vec3_origin; + m_vSpotlightCurrentPos = vec3_origin; + + m_hSpotlight = NULL; + m_hSpotlightTarget = NULL; + + AngleVectors( GetLocalAngles(), &m_vSpotlightDir ); + m_vSpotlightAngVelocity = vec3_origin; + + m_pEyeFlash = 0; + m_fNextSpotlightTime = 0; + m_nFlyMode = SCANNER_FLY_PATROL; + m_vCurrentBanking = m_vSpotlightDir; + m_flSpotlightCurLength = m_flSpotlightMaxLength; + + m_nPoseTail = LookupPoseParameter( "tail_control" ); + m_nPoseDynamo = LookupPoseParameter( "dynamo_wheel" ); + m_nPoseFlare = LookupPoseParameter( "alert_control" ); + m_nPoseFaceVert = LookupPoseParameter( "flex_vert" ); + m_nPoseFaceHoriz = LookupPoseParameter( "flex_horz" ); + + // -------------------------------------------- + + CapabilitiesAdd( bits_CAP_INNATE_MELEE_ATTACK1 ); + + m_bPhotoTaken = false; + + BaseClass::Spawn(); + + // Watch for this error state + if ( m_bOnlyInspectPlayers && m_bNeverInspectPlayers ) + { + Assert( 0 ); + Warning( "ERROR: Scanner set to never and always inspect players!\n" ); + } +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CNPC_CScanner::Activate() +{ + BaseClass::Activate(); + + // Have to do this here because sprites do not go across level transitions + m_pEyeFlash = CSprite::SpriteCreate( "sprites/blueflare1.vmt", GetLocalOrigin(), FALSE ); + m_pEyeFlash->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation ); + m_pEyeFlash->SetAttachment( this, LookupAttachment( SCANNER_ATTACHMENT_LIGHT ) ); + m_pEyeFlash->SetBrightness( 0 ); + m_pEyeFlash->SetScale( 1.4 ); +} + +//------------------------------------------------------------------------------ +// Purpose: Override to split in two when attacked +//------------------------------------------------------------------------------ +int CNPC_CScanner::OnTakeDamage_Alive( const CTakeDamageInfo &info ) +{ + // Turn off my spotlight when shot + SpotlightDestroy(); + m_fNextSpotlightTime = gpGlobals->curtime + 2.0f; + + return (BaseClass::OnTakeDamage_Alive( info )); +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CNPC_CScanner::Gib( void ) +{ + if ( IsMarkedForDeletion() ) + return; + + // Spawn all gibs + if( m_bIsClawScanner ) + { + CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib1.mdl"); + CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib2.mdl"); + CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib3.mdl"); + CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib4.mdl"); + CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib5.mdl"); + CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/Shield_Scanner_Gib6.mdl"); + } + else + { + CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib01.mdl" ); + CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib02.mdl" ); + CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib04.mdl" ); + CGib::SpawnSpecificGibs( this, 1, 500, 250, "models/gibs/scanner_gib05.mdl" ); + } + + // Add a random chance of spawning a battery... + if ( !HasSpawnFlags(SF_NPC_NO_WEAPON_DROP) && random->RandomFloat( 0.0f, 1.0f) < 0.3f ) + { + CItem *pBattery = (CItem*)CreateEntityByName("item_battery"); + if ( pBattery ) + { + pBattery->SetAbsOrigin( GetAbsOrigin() ); + pBattery->SetAbsVelocity( GetAbsVelocity() ); + pBattery->SetLocalAngularVelocity( GetLocalAngularVelocity() ); + pBattery->ActivateWhenAtRest(); + pBattery->Spawn(); + } + } + + DeployMine(); + + BaseClass::Gib(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pInflictor - +// pAttacker - +// flDamage - +// bitsDamageType - +//----------------------------------------------------------------------------- +void CNPC_CScanner::Event_Killed( const CTakeDamageInfo &info ) +{ + // Copy off the takedamage info that killed me, since we're not going to call + // up into the base class's Event_Killed() until we gib. (gibbing is ultimate death) + m_KilledInfo = info; + + DeployMine(); + + ClearInspectTarget(); + + // Interrupt whatever schedule I'm on + SetCondition(COND_SCHEDULE_DONE); + + // Remove spotlight + SpotlightDestroy(); + + // Remove sprite + UTIL_Remove(m_pEyeFlash); + m_pEyeFlash = NULL; + + // If I have an enemy and I'm up high, do a dive bomb (unless dissolved) + if ( !m_bIsClawScanner && GetEnemy() != NULL && (info.GetDamageType() & DMG_DISSOLVE) == false ) + { + Vector vecDelta = GetLocalOrigin() - GetEnemy()->GetLocalOrigin(); + if ( ( vecDelta.z > 120 ) && ( vecDelta.Length() > 360 ) ) + { + // If I'm divebombing, don't take any more damage. It will make Event_Killed() be called again. + // This is especially bad if someone machineguns the divebombing scanner. + AttackDivebomb(); + return; + } + } + + Gib(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Tells use whether or not the NPC cares about a given type of hint node. +// Input : sHint - +// Output : TRUE if the NPC is interested in this hint type, FALSE if not. +//----------------------------------------------------------------------------- +bool CNPC_CScanner::FValidateHintType(CAI_Hint *pHint) +{ + return( pHint->HintType() == HINT_WORLD_WINDOW ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : Type - +//----------------------------------------------------------------------------- +int CNPC_CScanner::TranslateSchedule( int scheduleType ) +{ + switch ( scheduleType ) + { + case SCHED_IDLE_STAND: + { + return SCHED_SCANNER_PATROL; + } + + case SCHED_SCANNER_PATROL: + return SCHED_CSCANNER_PATROL; + } + return BaseClass::TranslateSchedule(scheduleType); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : idealActivity - +// *pIdealWeaponActivity - +// Output : int +//----------------------------------------------------------------------------- +Activity CNPC_CScanner::NPC_TranslateActivity( Activity eNewActivity ) +{ + if( !m_bIsClawScanner ) + { + return BaseClass::NPC_TranslateActivity( eNewActivity ); + } + + // The claw scanner came along a little late and doesn't have the activities + // of the city scanner. So Just pick between these three + if( eNewActivity == ACT_DISARM ) + { + // Closing up. + return eNewActivity; + } + + if( m_bIsOpen ) + { + return ACT_IDLE_ANGRY; + } + else + { + return ACT_IDLE; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_CScanner::HandleAnimEvent( animevent_t *pEvent ) +{ + if( pEvent->event == AE_SCANNER_CLOSED ) + { + m_bIsOpen = false; + SetActivity( ACT_IDLE ); + return; + } + + BaseClass::HandleAnimEvent( pEvent ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +char *CNPC_CScanner::GetEngineSound( void ) +{ + if( m_bIsClawScanner ) + return "NPC_SScanner.FlyLoop"; + + return "NPC_CScanner.FlyLoop"; +} + +//----------------------------------------------------------------------------- +// Purpose: Plays the engine sound. +//----------------------------------------------------------------------------- +void CNPC_CScanner::NPCThink(void) +{ + if (!IsAlive()) + { + SetActivity((Activity)ACT_SCANNER_RETRACT_PRONGS); + StudioFrameAdvance( ); + SetNextThink( gpGlobals->curtime + 0.1f ); + } + else + { + BaseClass::NPCThink(); + SpotlightUpdate(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CScanner::Precache(void) +{ + // Model + if( m_bIsClawScanner ) + { + PrecacheModel("models/shield_scanner.mdl"); + + PrecacheModel("models/gibs/Shield_Scanner_Gib1.mdl"); + PrecacheModel("models/gibs/Shield_Scanner_Gib2.mdl"); + PrecacheModel("models/gibs/Shield_Scanner_Gib3.mdl"); + PrecacheModel("models/gibs/Shield_Scanner_Gib4.mdl"); + PrecacheModel("models/gibs/Shield_Scanner_Gib5.mdl"); + PrecacheModel("models/gibs/Shield_Scanner_Gib6.mdl"); + + PrecacheScriptSound( "NPC_SScanner.Shoot"); + PrecacheScriptSound( "NPC_SScanner.Alert" ); + PrecacheScriptSound( "NPC_SScanner.Die" ); + PrecacheScriptSound( "NPC_SScanner.Combat" ); + PrecacheScriptSound( "NPC_SScanner.Idle" ); + PrecacheScriptSound( "NPC_SScanner.Pain" ); + PrecacheScriptSound( "NPC_SScanner.TakePhoto" ); + PrecacheScriptSound( "NPC_SScanner.AttackFlash" ); + PrecacheScriptSound( "NPC_SScanner.DiveBombFlyby" ); + PrecacheScriptSound( "NPC_SScanner.DiveBomb" ); + PrecacheScriptSound( "NPC_SScanner.DeployMine" ); + + PrecacheScriptSound( "NPC_SScanner.FlyLoop" ); + UTIL_PrecacheOther( "combine_mine" ); + } + else + { + PrecacheModel("models/combine_scanner.mdl"); + + PrecacheModel("models/gibs/scanner_gib01.mdl" ); + PrecacheModel("models/gibs/scanner_gib02.mdl" ); + PrecacheModel("models/gibs/scanner_gib02.mdl" ); + PrecacheModel("models/gibs/scanner_gib04.mdl" ); + PrecacheModel("models/gibs/scanner_gib05.mdl" ); + + PrecacheScriptSound( "NPC_CScanner.Shoot"); + PrecacheScriptSound( "NPC_CScanner.Alert" ); + PrecacheScriptSound( "NPC_CScanner.Die" ); + PrecacheScriptSound( "NPC_CScanner.Combat" ); + PrecacheScriptSound( "NPC_CScanner.Idle" ); + PrecacheScriptSound( "NPC_CScanner.Pain" ); + PrecacheScriptSound( "NPC_CScanner.TakePhoto" ); + PrecacheScriptSound( "NPC_CScanner.AttackFlash" ); + PrecacheScriptSound( "NPC_CScanner.DiveBombFlyby" ); + PrecacheScriptSound( "NPC_CScanner.DiveBomb" ); + PrecacheScriptSound( "NPC_CScanner.DeployMine" ); + + PrecacheScriptSound( "NPC_CScanner.FlyLoop" ); + } + + // Sprites + m_nHaloSprite = PrecacheModel("sprites/light_glow03.vmt"); + PrecacheModel( "sprites/glow_test02.vmt" ); + + BaseClass::Precache(); +} + +//------------------------------------------------------------------------------ +// Purpose: Request help inspecting from other squad members +//------------------------------------------------------------------------------ +void CNPC_CScanner::RequestInspectSupport(void) +{ + if (m_pSquad) + { + AISquadIter_t iter; + for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) ) + { + if (pSquadMember != this) + { + if (GetTarget()) + { + pSquadMember->DispatchInteraction(g_interactionScannerSupportEntity,((void *)((CBaseEntity*)GetTarget())),this); + } + else + { + pSquadMember->DispatchInteraction(g_interactionScannerSupportPosition,((void *)m_vInspectPos.Base()),this); + } + } + } + } +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +bool CNPC_CScanner::IsValidInspectTarget(CBaseEntity *pEntity) +{ + // If a citizen, make sure he can be inspected again + if (pEntity->Classify() == CLASS_CITIZEN_PASSIVE) + { + if (((CNPC_Citizen*)pEntity)->GetNextScannerInspectTime() > gpGlobals->curtime) + { + return false; + } + } + + // Make sure no other squad member has already chosen to + // inspect this entity + if (m_pSquad) + { + AISquadIter_t iter; + for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) ) + { + if (pSquadMember->GetTarget() == pEntity) + { + return false; + } + } + } + + // Do not inspect friendly targets + if ( IRelationType( pEntity ) == D_LI ) + return false; + + return true; +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +CBaseEntity* CNPC_CScanner::BestInspectTarget(void) +{ + if ( !m_bShouldInspect ) + return NULL; + + CBaseEntity* pBestEntity = NULL; + float fBestDist = MAX_COORD_RANGE; + float fTestDist; + + CBaseEntity *pEntity = NULL; + + // If I have a spotlight, search from the spotlight position + // otherwise search from my position + Vector vSearchOrigin; + float fSearchDist; + if (m_hSpotlightTarget != NULL) + { + vSearchOrigin = m_hSpotlightTarget->GetAbsOrigin(); + fSearchDist = SCANNER_CIT_INSPECT_GROUND_DIST; + } + else + { + vSearchOrigin = WorldSpaceCenter(); + fSearchDist = SCANNER_CIT_INSPECT_FLY_DIST; + } + + if ( m_bOnlyInspectPlayers ) + { + CBasePlayer *pPlayer = AI_GetSinglePlayer(); + if ( !pPlayer ) + return NULL; + + if ( !pPlayer->IsAlive() || (pPlayer->GetFlags() & FL_NOTARGET) ) + return NULL; + + return WorldSpaceCenter().DistToSqr( pPlayer->EyePosition() ) <= (fSearchDist * fSearchDist) ? pPlayer : NULL; + } + + CUtlVector<CBaseEntity *> candidates; + float fSearchDistSq = fSearchDist * fSearchDist; + int i; + + // Inspect players unless told otherwise + if ( m_bNeverInspectPlayers == false ) + { + // Players + for ( i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); + + if ( pPlayer ) + { + if ( vSearchOrigin.DistToSqr(pPlayer->GetAbsOrigin()) < fSearchDistSq ) + { + candidates.AddToTail( pPlayer ); + } + } + } + } + + // NPCs + CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); + + for ( i = 0; i < g_AI_Manager.NumAIs(); i++ ) + { + if ( ppAIs[i] != this && vSearchOrigin.DistToSqr(ppAIs[i]->GetAbsOrigin()) < fSearchDistSq ) + { + candidates.AddToTail( ppAIs[i] ); + } + } + + for ( i = 0; i < candidates.Count(); i++ ) + { + pEntity = candidates[i]; + Assert( pEntity != this && (pEntity->MyNPCPointer() || pEntity->IsPlayer() ) ); + + CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); + if ( ( pNPC && pNPC->Classify() == CLASS_CITIZEN_PASSIVE ) || pEntity->IsPlayer() ) + { + if ( pEntity->GetFlags() & FL_NOTARGET ) + continue; + + if ( pEntity->IsAlive() == false ) + continue; + + // Ensure it's within line of sight + if ( !FVisible( pEntity ) ) + continue; + + fTestDist = ( GetAbsOrigin() - pEntity->EyePosition() ).Length(); + if ( fTestDist < fBestDist ) + { + if ( IsValidInspectTarget( pEntity ) ) + { + fBestDist = fTestDist; + pBestEntity = pEntity; + } + } + } + } + return pBestEntity; +} + + +//------------------------------------------------------------------------------ +// Purpose: Clears any previous inspect target and set inspect target to +// the given entity and set the durection of the inspection +//------------------------------------------------------------------------------ +void CNPC_CScanner::SetInspectTargetToEnt(CBaseEntity *pEntity, float fInspectDuration) +{ + ClearInspectTarget(); + SetTarget(pEntity); + + m_fInspectEndTime = gpGlobals->curtime + fInspectDuration; +} + + +//------------------------------------------------------------------------------ +// Purpose: Clears any previous inspect target and set inspect target to +// the given hint node and set the durection of the inspection +//------------------------------------------------------------------------------ +void CNPC_CScanner::SetInspectTargetToHint(CAI_Hint *pHint, float fInspectDuration) +{ + ClearInspectTarget(); + + float yaw = pHint->Yaw(); + // -------------------------------------------- + // Figure out the location that the hint hits + // -------------------------------------------- + Vector vHintDir = UTIL_YawToVector( yaw ); + + Vector vHintOrigin; + pHint->GetPosition( this, &vHintOrigin ); + + Vector vHintEnd = vHintOrigin + (vHintDir * 512); + + trace_t tr; + AI_TraceLine ( vHintOrigin, vHintEnd, MASK_BLOCKLOS, this, COLLISION_GROUP_NONE, &tr); + + if ( g_debug_cscanner.GetBool() ) + { + NDebugOverlay::Line( vHintOrigin, tr.endpos, 255, 0, 0, true, 4.0f ); + NDebugOverlay::Cross3D( tr.endpos, -Vector(8,8,8), Vector(8,8,8), 255, 0, 0, true, 4.0f ); + } + + if (tr.fraction == 1.0f ) + { + DevMsg("ERROR: Scanner hint node not facing a surface!\n"); + } + else + { + SetHintNode( pHint ); + m_vInspectPos = tr.endpos; + pHint->Lock( this ); + + m_fInspectEndTime = gpGlobals->curtime + fInspectDuration; + } +} + + +//------------------------------------------------------------------------------ +// Purpose: Clears any previous inspect target and set inspect target to +// the given position and set the durection of the inspection +// Input : +// Output : +//------------------------------------------------------------------------------ +void CNPC_CScanner::SetInspectTargetToPos(const Vector &vInspectPos, float fInspectDuration) +{ + ClearInspectTarget(); + m_vInspectPos = vInspectPos; + + m_fInspectEndTime = gpGlobals->curtime + fInspectDuration; +} + + +//------------------------------------------------------------------------------ +// Purpose: Clears out any previous inspection targets +//------------------------------------------------------------------------------ +void CNPC_CScanner::ClearInspectTarget(void) +{ + if ( GetIdealState() != NPC_STATE_SCRIPT ) + { + SetTarget( NULL ); + } + + ClearHintNode( SCANNER_HINT_INSPECT_LENGTH ); + m_vInspectPos = vec3_origin; +} + + +//------------------------------------------------------------------------------ +// Purpose: Returns true if there is a position to be inspected. +//------------------------------------------------------------------------------ +bool CNPC_CScanner::HaveInspectTarget( void ) +{ + if ( GetTarget() != NULL ) + return true; + + if ( m_vInspectPos != vec3_origin ) + return true; + + return false; +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +Vector CNPC_CScanner::InspectTargetPosition(void) +{ + // If we have a target, return an adjust position + if ( GetTarget() != NULL ) + { + Vector vEyePos = GetTarget()->EyePosition(); + + // If in spotlight mode, aim for ground below target unless is client + if ( m_nFlyMode == SCANNER_FLY_SPOT && !(GetTarget()->GetFlags() & FL_CLIENT) ) + { + Vector vInspectPos; + vInspectPos.x = vEyePos.x; + vInspectPos.y = vEyePos.y; + vInspectPos.z = GetFloorZ( vEyePos ); + + // Let's take three-quarters between eyes and ground + vInspectPos.z += ( vEyePos.z - vInspectPos.z ) * 0.75f; + + return vInspectPos; + } + else + { + // Otherwise aim for eyes + return vEyePos; + } + } + else if ( m_vInspectPos != vec3_origin ) + { + return m_vInspectPos; + } + else + { + DevMsg("InspectTargetPosition called with no target!\n"); + + return m_vInspectPos; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CScanner::InputShouldInspect( inputdata_t &inputdata ) +{ + m_bShouldInspect = ( inputdata.value.Int() != 0 ); + + if ( !m_bShouldInspect ) + { + if ( GetEnemy() == GetTarget() ) + SetEnemy(NULL); + ClearInspectTarget(); + SetTarget(NULL); + SpotlightDestroy(); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_CScanner::DeployMine() +{ + CBaseEntity *child; + // iterate through all children + for ( child = FirstMoveChild(); child != NULL; child = child->NextMovePeer() ) + { + if( FClassnameIs( child, "combine_mine" ) ) + { + child->SetParent( NULL ); + child->SetAbsVelocity( GetAbsVelocity() ); + child->SetOwnerEntity( this ); + + ScannerEmitSound( "DeployMine" ); + + IPhysicsObject *pPhysObj = child->VPhysicsGetObject(); + if( pPhysObj ) + { + // Make sure the mine's awake + pPhysObj->Wake(); + } + + if( m_bIsClawScanner ) + { + // Fold up. + SetActivity( ACT_DISARM ); + } + + return; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float CNPC_CScanner::GetMaxSpeed() +{ + if( IsStriderScout() ) + { + return SCANNER_SCOUT_MAX_SPEED; + } + + return BaseClass::GetMaxSpeed(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CScanner::InputDeployMine(inputdata_t &inputdata) +{ + DeployMine(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CScanner::InputEquipMine(inputdata_t &inputdata) +{ + CBaseEntity *child; + // iterate through all children + for ( child = FirstMoveChild(); child != NULL; child = child->NextMovePeer() ) + { + if( FClassnameIs( child, "combine_mine" ) ) + { + // Already have a mine! + return; + } + } + + CBaseEntity *pEnt; + + pEnt = CreateEntityByName( "combine_mine" ); + bool bPlacedMine = false; + + if( m_bIsClawScanner ) + { + Vector vecOrigin; + QAngle angles; + int attachment; + + attachment = LookupAttachment( "claw" ); + + if( attachment > -1 ) + { + GetAttachment( attachment, vecOrigin, angles ); + + pEnt->SetAbsOrigin( vecOrigin ); + pEnt->SetAbsAngles( angles ); + pEnt->SetOwnerEntity( this ); + pEnt->SetParent( this, attachment ); + + m_bIsOpen = true; + SetActivity( ACT_IDLE_ANGRY ); + bPlacedMine = true; + } + } + + + if( !bPlacedMine ) + { + Vector vecMineLocation = GetAbsOrigin(); + vecMineLocation.z -= 32.0; + + pEnt->SetAbsOrigin( vecMineLocation ); + pEnt->SetAbsAngles( GetAbsAngles() ); + pEnt->SetOwnerEntity( this ); + pEnt->SetParent( this ); + } + + pEnt->Spawn(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Tells the scanner to go photograph an entity. +// Input : String name or classname of the entity to inspect. +//----------------------------------------------------------------------------- +void CNPC_CScanner::InputInspectTargetPhoto(inputdata_t &inputdata) +{ + m_vLastPatrolDir = vec3_origin; + m_bPhotoTaken = false; + InspectTarget( inputdata, SCANNER_FLY_PHOTO ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Tells the scanner to go spotlight an entity. +// Input : String name or classname of the entity to inspect. +//----------------------------------------------------------------------------- +void CNPC_CScanner::InputInspectTargetSpotlight(inputdata_t &inputdata) +{ + InspectTarget( inputdata, SCANNER_FLY_SPOT ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Tells the scanner to go photo or spotlight an entity. +// Input : String name or classname of the entity to inspect. +//----------------------------------------------------------------------------- +void CNPC_CScanner::InspectTarget( inputdata_t &inputdata, ScannerFlyMode_t eFlyMode ) +{ + CBaseEntity *pEnt = gEntList.FindEntityGeneric( NULL, inputdata.value.String(), this, inputdata.pActivator ); + + if ( pEnt != NULL ) + { + // Set and begin to inspect our target + SetInspectTargetToEnt( pEnt, SCANNER_CIT_INSPECT_LENGTH ); + + m_nFlyMode = eFlyMode; + SetCondition( COND_CSCANNER_HAVE_INSPECT_TARGET ); + + // Stop us from any other navigation we were doing + GetNavigator()->ClearGoal(); + } + else + { + DevMsg( "InspectTarget: target %s not found!\n", inputdata.value.String() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CNPC_CScanner::MovingToInspectTarget( void ) +{ + // If we're flying to a photograph target and the photo isn't yet taken, we're still moving to it + if ( m_nFlyMode == SCANNER_FLY_PHOTO && m_bPhotoTaken == false ) + return true; + + // If we're still on a path, then we're still moving + if ( HaveInspectTarget() && GetNavigator()->IsGoalActive() ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CScanner::GatherConditions( void ) +{ + BaseClass::GatherConditions(); + + // Clear out our old conditions + ClearCondition( COND_CSCANNER_INSPECT_DONE ); + ClearCondition( COND_CSCANNER_HAVE_INSPECT_TARGET ); + ClearCondition( COND_CSCANNER_SPOT_ON_TARGET ); + ClearCondition( COND_CSCANNER_CAN_PHOTOGRAPH ); + + // We don't do any of these checks if we have an enemy + if ( GetEnemy() ) + return; + + // -------------------------------------- + // COND_CSCANNER_INSPECT_DONE + // + // If my inspection over + // --------------------------------------------------------- + + // Refresh our timing if we're still moving to our inspection target + if ( MovingToInspectTarget() ) + { + m_fInspectEndTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_LENGTH; + } + + // Update our follow times + if ( HaveInspectTarget() && gpGlobals->curtime > m_fInspectEndTime && m_nFlyMode != SCANNER_FLY_FOLLOW ) + { + SetCondition ( COND_CSCANNER_INSPECT_DONE ); + + m_fCheckCitizenTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_DELAY; + m_fCheckHintTime = gpGlobals->curtime + SCANNER_HINT_INSPECT_DELAY; + ClearInspectTarget(); + } + + // ---------------------------------------------------------- + // If I heard a sound and I don't have an enemy, inspect it + // ---------------------------------------------------------- + if ( ( HasCondition( COND_HEAR_COMBAT ) || HasCondition( COND_HEAR_DANGER ) ) && m_nFlyMode != SCANNER_FLY_FOLLOW ) + { + CSound *pSound = GetBestSound(); + + if ( pSound ) + { + // Chase an owner if we can + if ( pSound->m_hOwner != NULL ) + { + // Don't inspect sounds of things we like + if ( IRelationType( pSound->m_hOwner ) != D_LI ) + { + // Only bother if we can see it + if ( FVisible( pSound->m_hOwner ) ) + { + SetInspectTargetToEnt( pSound->m_hOwner, SCANNER_SOUND_INSPECT_LENGTH ); + } + } + } + else + { + // Otherwise chase the specific sound + Vector vSoundPos = pSound->GetSoundOrigin(); + SetInspectTargetToPos( vSoundPos, SCANNER_SOUND_INSPECT_LENGTH ); + } + + m_nFlyMode = (random->RandomInt(0,2)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO; + } + } + + // -------------------------------------- + // COND_CSCANNER_HAVE_INSPECT_TARGET + // + // Look for a nearby citizen or player to hassle. + // --------------------------------------------------------- + + // Check for citizens to inspect + if ( gpGlobals->curtime > m_fCheckCitizenTime && HaveInspectTarget() == false ) + { + CBaseEntity *pBestEntity = BestInspectTarget(); + + if ( pBestEntity != NULL ) + { + SetInspectTargetToEnt( pBestEntity, SCANNER_CIT_INSPECT_LENGTH ); + m_nFlyMode = (random->RandomInt(0,3)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO; + SetCondition ( COND_CSCANNER_HAVE_INSPECT_TARGET ); + } + } + + // Check for hints to inspect + if ( gpGlobals->curtime > m_fCheckHintTime && HaveInspectTarget() == false ) + { + SetHintNode( CAI_HintManager::FindHint( this, HINT_WORLD_WINDOW, 0, SCANNER_CIT_INSPECT_FLY_DIST ) ); + + if ( GetHintNode() ) + { + m_fCheckHintTime = gpGlobals->curtime + SCANNER_HINT_INSPECT_DELAY; + + m_nFlyMode = (random->RandomInt(0,2)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO; + + SetInspectTargetToHint( GetHintNode(), SCANNER_HINT_INSPECT_LENGTH ); + + SetCondition ( COND_CSCANNER_HAVE_INSPECT_TARGET ); + } + } + + // -------------------------------------- + // COND_CSCANNER_SPOT_ON_TARGET + // + // True when spotlight is on target ent + // -------------------------------------- + + if ( m_hSpotlightTarget != NULL && HaveInspectTarget() && m_hSpotlightTarget->GetSmoothedVelocity().Length() < 25 ) + { + // If I have a target entity, check my spotlight against the + // actual position of the entity + if (GetTarget()) + { + float fInspectDist = (m_vSpotlightTargetPos - m_vSpotlightCurrentPos).Length(); + if ( fInspectDist < 100 ) + { + SetCondition( COND_CSCANNER_SPOT_ON_TARGET ); + } + } + // Otherwise just check by beam direction + else + { + Vector vTargetDir = SpotlightTargetPos() - GetLocalOrigin(); + VectorNormalize(vTargetDir); + float dotpr = DotProduct(vTargetDir, m_vSpotlightDir); + if (dotpr > 0.95) + { + SetCondition( COND_CSCANNER_SPOT_ON_TARGET ); + } + } + } + + // -------------------------------------------- + // COND_CSCANNER_CAN_PHOTOGRAPH + // + // True when can photograph target ent + // -------------------------------------------- + + ClearCondition( COND_CSCANNER_CAN_PHOTOGRAPH ); + + if ( m_nFlyMode == SCANNER_FLY_PHOTO ) + { + // Make sure I have something to photograph and I'm ready to photograph and I'm not moving to fast + if ( gpGlobals->curtime > m_fNextPhotographTime && HaveInspectTarget() && GetCurrentVelocity().LengthSqr() < (64*64) ) + { + // Check that I'm in the right distance range + float fInspectDist = (InspectTargetPosition() - GetAbsOrigin()).Length2D(); + + // See if we're within range + if ( fInspectDist > SCANNER_PHOTO_NEAR_DIST && fInspectDist < SCANNER_PHOTO_FAR_DIST ) + { + // Make sure we're looking at the target + if ( UTIL_AngleDiff( GetAbsAngles().y, VecToYaw( InspectTargetPosition() - GetAbsOrigin() ) ) < 4.0f ) + { + trace_t tr; + AI_TraceLine ( GetAbsOrigin(), InspectTargetPosition(), MASK_BLOCKLOS, GetTarget(), COLLISION_GROUP_NONE, &tr); + + if ( tr.fraction == 1.0f ) + { + SetCondition( COND_CSCANNER_CAN_PHOTOGRAPH ); + } + } + } + } + } +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CNPC_CScanner::PrescheduleThink(void) +{ + BaseClass::PrescheduleThink(); + + // Go back to idling if we're done + if ( GetIdealActivity() == ACT_SCANNER_FLARE_START ) + { + if ( IsSequenceFinished() ) + { + SetIdealActivity( (Activity) ACT_IDLE ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Overridden because if the player is a criminal, we hate them. +// Input : pTarget - Entity with which to determine relationship. +// Output : Returns relationship value. +//----------------------------------------------------------------------------- +Disposition_t CNPC_CScanner::IRelationType(CBaseEntity *pTarget) +{ + // If it's the player and they are a criminal, we hates them + if ( pTarget && pTarget->Classify() == CLASS_PLAYER ) + { + if ( GlobalEntity_GetState("gordon_precriminal") == GLOBAL_ON ) + return D_NU; + } + + return BaseClass::IRelationType( pTarget ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pTask - +//----------------------------------------------------------------------------- +void CNPC_CScanner::RunTask( const Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_CSCANNER_PHOTOGRAPH: + { + if ( IsWaitFinished() ) + { + // If light was on turn it off + if ( m_pEyeFlash->GetBrightness() > 0 ) + { + m_pEyeFlash->SetBrightness( 0 ); + + // I'm done with this target + if ( gpGlobals->curtime > m_fInspectEndTime ) + { + ClearInspectTarget(); + TaskComplete(); + } + // Otherwise take another picture + else + { + SetWait( 5.0f, 10.0f ); + } + } + // If light was off, take another picture + else + { + TakePhoto(); + SetWait( 0.1f ); + } + } + break; + } + case TASK_CSCANNER_ATTACK_PRE_FLASH: + { + AttackPreFlash(); + + if ( IsWaitFinished() ) + { + TaskComplete(); + } + break; + } + case TASK_CSCANNER_ATTACK_FLASH: + { + if (IsWaitFinished()) + { + AttackFlashBlind(); + TaskComplete(); + } + break; + } + default: + { + BaseClass::RunTask(pTask); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Gets the appropriate next schedule based on current condition +// bits. +//----------------------------------------------------------------------------- +int CNPC_CScanner::SelectSchedule(void) +{ + // Turn our flash off in case we were interrupted while it was on. + if ( m_pEyeFlash ) + { + m_pEyeFlash->SetBrightness( 0 ); + } + + // ---------------------------------------------------- + // If I'm dead, go into a dive bomb + // ---------------------------------------------------- + if ( m_iHealth <= 0 ) + { + m_flSpeed = SCANNER_MAX_DIVE_BOMB_SPEED; + return SCHED_SCANNER_ATTACK_DIVEBOMB; + } + + // ------------------------------- + // If I'm in a script sequence + // ------------------------------- + if ( m_NPCState == NPC_STATE_SCRIPT ) + return(BaseClass::SelectSchedule()); + + // ------------------------------- + // Flinch + // ------------------------------- + if ( HasCondition(COND_LIGHT_DAMAGE) || HasCondition(COND_HEAVY_DAMAGE) ) + { + if ( IsHeldByPhyscannon( ) ) + return SCHED_SMALL_FLINCH; + + if ( m_NPCState == NPC_STATE_IDLE ) + return SCHED_SMALL_FLINCH; + + if ( m_NPCState == NPC_STATE_ALERT ) + { + if ( m_iHealth < ( 3 * sk_scanner_health.GetFloat() / 4 )) + return SCHED_TAKE_COVER_FROM_ORIGIN; + + if ( SelectWeightedSequence( ACT_SMALL_FLINCH ) != -1 ) + return SCHED_SMALL_FLINCH; + } + else + { + if ( random->RandomInt( 0, 10 ) < 4 ) + return SCHED_SMALL_FLINCH; + } + } + + // I'm being held by the physcannon... struggle! + if ( IsHeldByPhyscannon( ) ) + return SCHED_SCANNER_HELD_BY_PHYSCANNON; + + // ---------------------------------------------------------- + // If I have an enemy + // ---------------------------------------------------------- + if ( GetEnemy() != NULL && GetEnemy()->IsAlive() && m_bShouldInspect ) + { + // Always chase the enemy + SetInspectTargetToEnt( GetEnemy(), 9999 ); + + // Patrol if the enemy has vanished + if ( HasCondition( COND_LOST_ENEMY ) ) + return SCHED_SCANNER_PATROL; + + // Chase via route if we're directly blocked + if ( HasCondition( COND_SCANNER_FLY_BLOCKED ) ) + return SCHED_SCANNER_CHASE_ENEMY; + + // Attack if it's time + if ( gpGlobals->curtime < m_flNextAttack ) + return SCHED_CSCANNER_SPOTLIGHT_HOVER; + + // Melee attack if possible + if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) ) + { + if ( random->RandomInt(0,1) ) + return SCHED_CSCANNER_ATTACK_FLASH; + + // TODO: a schedule where he makes an alarm sound? + return SCHED_SCANNER_CHASE_ENEMY; + } + + // If I'm far from the enemy, stay up high and approach in spotlight mode + float fAttack2DDist = ( GetEnemyLKP() - GetAbsOrigin() ).Length2D(); + + if ( fAttack2DDist > SCANNER_ATTACK_FAR_DIST ) + return SCHED_CSCANNER_SPOTLIGHT_HOVER; + + // Otherwise fly in low for attack + return SCHED_SCANNER_ATTACK_HOVER; + } + + // ---------------------------------------------------------- + // If I have something to inspect + // ---------------------------------------------------------- + if ( HaveInspectTarget() ) + { + // Pathfind to our goal + if ( HasCondition( COND_SCANNER_FLY_BLOCKED ) ) + return SCHED_CSCANNER_MOVE_TO_INSPECT; + + // If I was chasing, pick with photographing or spotlighting + if ( m_nFlyMode == SCANNER_FLY_CHASE ) + { + m_nFlyMode = (random->RandomInt(0,1)==0) ? SCANNER_FLY_SPOT : SCANNER_FLY_PHOTO; + } + + // Handle spotlight + if ( m_nFlyMode == SCANNER_FLY_SPOT ) + { + if (HasCondition( COND_CSCANNER_SPOT_ON_TARGET )) + { + if (GetTarget()) + { + RequestInspectSupport(); + + CAI_BaseNPC *pNPC = GetTarget()->MyNPCPointer(); + // If I'm leading the inspection, so verbal inspection + if (pNPC && pNPC->GetTarget() == this) + { + return SCHED_CSCANNER_SPOTLIGHT_INSPECT_CIT; + } + + return SCHED_CSCANNER_SPOTLIGHT_HOVER; + } + + return SCHED_CSCANNER_SPOTLIGHT_INSPECT_POS; + } + + return SCHED_CSCANNER_SPOTLIGHT_HOVER; + } + + // Handle photographing + if ( m_nFlyMode == SCANNER_FLY_PHOTO ) + { + if ( HasCondition( COND_CSCANNER_CAN_PHOTOGRAPH )) + return SCHED_CSCANNER_PHOTOGRAPH; + + return SCHED_CSCANNER_PHOTOGRAPH_HOVER; + } + + // Handle following after a target + if ( m_nFlyMode == SCANNER_FLY_FOLLOW ) + { + //TODO: Randomly make noise, photograph, etc + return SCHED_SCANNER_FOLLOW_HOVER; + } + + // Handle patrolling + if ( ( m_nFlyMode == SCANNER_FLY_PATROL ) || ( m_nFlyMode == SCANNER_FLY_FAST ) ) + return SCHED_SCANNER_PATROL; + } + + // Default to patrolling around + return SCHED_SCANNER_PATROL; +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CNPC_CScanner::SpotlightDestroy(void) +{ + if ( m_hSpotlight ) + { + UTIL_Remove(m_hSpotlight); + m_hSpotlight = NULL; + + UTIL_Remove(m_hSpotlightTarget); + m_hSpotlightTarget = NULL; + } +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CNPC_CScanner::SpotlightCreate(void) +{ + // Make sure we don't already have one + if ( m_hSpotlight != NULL ) + return; + + // Can we create a spotlight yet? + if ( gpGlobals->curtime < m_fNextSpotlightTime ) + return; + + // If I have an enemy, start spotlight on my enemy + if (GetEnemy() != NULL) + { + Vector vEnemyPos = GetEnemyLKP(); + Vector vTargetPos = vEnemyPos; + vTargetPos.z = GetFloorZ(vEnemyPos); + m_vSpotlightDir = vTargetPos - GetLocalOrigin(); + VectorNormalize(m_vSpotlightDir); + } + // If I have an target, start spotlight on my target + else if (GetTarget() != NULL) + { + Vector vTargetPos = GetTarget()->GetLocalOrigin(); + vTargetPos.z = GetFloorZ(GetTarget()->GetLocalOrigin()); + m_vSpotlightDir = vTargetPos - GetLocalOrigin(); + VectorNormalize(m_vSpotlightDir); + } + // Other wise just start looking down + else + { + m_vSpotlightDir = Vector(0,0,-1); + } + + trace_t tr; + AI_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + m_vSpotlightDir * 2024, MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr ); + + m_hSpotlightTarget = (CSpotlightEnd*)CreateEntityByName( "spotlight_end" ); + m_hSpotlightTarget->Spawn(); + m_hSpotlightTarget->SetLocalOrigin( tr.endpos ); + m_hSpotlightTarget->SetOwnerEntity( this ); + // YWB: Because the scanner only moves the target during think, make sure we interpolate over 0.1 sec instead of every tick!!! + m_hSpotlightTarget->SetSimulatedEveryTick( false ); + + // Using the same color as the beam... + m_hSpotlightTarget->SetRenderColor( 255, 255, 255 ); + m_hSpotlightTarget->m_Radius = m_flSpotlightMaxLength; + + m_hSpotlight = CBeam::BeamCreate( "sprites/glow_test02.vmt", SPOTLIGHT_WIDTH ); + // Set the temporary spawnflag on the beam so it doesn't save (we'll recreate it on restore) + m_hSpotlight->AddSpawnFlags( SF_BEAM_TEMPORARY ); + m_hSpotlight->SetColor( 255, 255, 255 ); + m_hSpotlight->SetHaloTexture( m_nHaloSprite ); + m_hSpotlight->SetHaloScale( 32 ); + m_hSpotlight->SetEndWidth( m_hSpotlight->GetWidth() ); + m_hSpotlight->SetBeamFlags( (FBEAM_SHADEOUT|FBEAM_NOTILE) ); + m_hSpotlight->SetBrightness( 32 ); + m_hSpotlight->SetNoise( 0 ); + m_hSpotlight->EntsInit( this, m_hSpotlightTarget ); + m_hSpotlight->SetHDRColorScale( 0.75f ); // Scale this back a bit on HDR maps + // attach to light + m_hSpotlight->SetStartAttachment( LookupAttachment( SCANNER_ATTACHMENT_LIGHT ) ); + + m_vSpotlightAngVelocity = vec3_origin; +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +Vector CNPC_CScanner::SpotlightTargetPos(void) +{ + // ---------------------------------------------- + // If I have an enemy + // ---------------------------------------------- + if (GetEnemy() != NULL) + { + // If I can see my enemy aim for him + if (HasCondition(COND_SEE_ENEMY)) + { + // If its client aim for his eyes + if (GetEnemy()->GetFlags() & FL_CLIENT) + { + m_vSpotlightTargetPos = GetEnemy()->EyePosition(); + } + // Otherwise same for his feet + else + { + m_vSpotlightTargetPos = GetEnemy()->GetLocalOrigin(); + m_vSpotlightTargetPos.z = GetFloorZ(GetEnemy()->GetLocalOrigin()); + } + } + // Otherwise aim for last known position if I can see LKP + else + { + Vector vLKP = GetEnemyLKP(); + m_vSpotlightTargetPos.x = vLKP.x; + m_vSpotlightTargetPos.y = vLKP.y; + m_vSpotlightTargetPos.z = GetFloorZ(vLKP); + } + } + // ---------------------------------------------- + // If I have an inspect target + // ---------------------------------------------- + else if (HaveInspectTarget()) + { + m_vSpotlightTargetPos = InspectTargetPosition(); + } + else + { + // This creates a nice patrol spotlight sweep + // in the direction that I'm travelling + m_vSpotlightTargetPos = GetCurrentVelocity(); + m_vSpotlightTargetPos.z = 0; + VectorNormalize( m_vSpotlightTargetPos ); + m_vSpotlightTargetPos *= 5; + + float noiseScale = 2.5; + const Vector &noiseMod = GetNoiseMod(); + m_vSpotlightTargetPos.x += noiseScale*sin(noiseMod.x * gpGlobals->curtime + noiseMod.x); + m_vSpotlightTargetPos.y += noiseScale*cos(noiseMod.y* gpGlobals->curtime + noiseMod.y); + m_vSpotlightTargetPos.z -= fabs(noiseScale*cos(noiseMod.z* gpGlobals->curtime + noiseMod.z) ); + m_vSpotlightTargetPos = GetLocalOrigin()+m_vSpotlightTargetPos * 2024; + } + + return m_vSpotlightTargetPos; +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +Vector CNPC_CScanner::SpotlightCurrentPos(void) +{ + Vector vTargetDir = SpotlightTargetPos() - GetLocalOrigin(); + VectorNormalize(vTargetDir); + + if (!m_hSpotlight) + { + DevMsg("Spotlight pos. called w/o spotlight!\n"); + return vec3_origin; + } + // ------------------------------------------------- + // Beam has momentum relative to it's ground speed + // so sclae the turn rate based on its distance + // from the beam source + // ------------------------------------------------- + float fBeamDist = (m_hSpotlightTarget->GetLocalOrigin() - GetLocalOrigin()).Length(); + + float fBeamTurnRate = atan(50/fBeamDist); + Vector vNewAngVelocity = fBeamTurnRate * (vTargetDir - m_vSpotlightDir); + + float myDecay = 0.4; + m_vSpotlightAngVelocity = (myDecay * m_vSpotlightAngVelocity + (1-myDecay) * vNewAngVelocity); + + // ------------------------------ + // Limit overall angular speed + // ----------------------------- + if (m_vSpotlightAngVelocity.Length() > 1) + { + + Vector velDir = m_vSpotlightAngVelocity; + VectorNormalize(velDir); + m_vSpotlightAngVelocity = velDir * 1; + } + + // ------------------------------ + // Calculate new beam direction + // ------------------------------ + m_vSpotlightDir = m_vSpotlightDir + m_vSpotlightAngVelocity; + m_vSpotlightDir = m_vSpotlightDir; + VectorNormalize(m_vSpotlightDir); + + + // --------------------------------------------- + // Get beam end point. Only collide with + // solid objects, not npcs + // --------------------------------------------- + trace_t tr; + Vector vTraceEnd = GetAbsOrigin() + (m_vSpotlightDir * 2 * m_flSpotlightMaxLength); + AI_TraceLine ( GetAbsOrigin(), vTraceEnd, MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr); + + return (tr.endpos); +} + + +//------------------------------------------------------------------------------ +// Purpose: Update the direction and position of my spotlight +//------------------------------------------------------------------------------ +void CNPC_CScanner::SpotlightUpdate(void) +{ + //FIXME: JDW - E3 Hack + if ( m_bNoLight ) + { + if ( m_hSpotlight ) + { + SpotlightDestroy(); + } + + return; + } + + if ((m_nFlyMode != SCANNER_FLY_SPOT) && + (m_nFlyMode != SCANNER_FLY_PATROL) && + (m_nFlyMode != SCANNER_FLY_FAST)) + { + if ( m_hSpotlight ) + { + SpotlightDestroy(); + } + return; + } + + // If I don't have a spotlight attempt to create one + + if ( m_hSpotlight == NULL ) + { + SpotlightCreate(); + + if ( m_hSpotlight== NULL ) + return; + } + + // Calculate the new homing target position + m_vSpotlightCurrentPos = SpotlightCurrentPos(); + + // ------------------------------------------------------------------ + // If I'm not facing the spotlight turn it off + // ------------------------------------------------------------------ + Vector vSpotDir = m_vSpotlightCurrentPos - GetAbsOrigin(); + VectorNormalize(vSpotDir); + + Vector vForward; + AngleVectors( GetAbsAngles(), &vForward ); + + float dotpr = DotProduct( vForward, vSpotDir ); + + if ( dotpr < 0.0 ) + { + // Leave spotlight off for a while + m_fNextSpotlightTime = gpGlobals->curtime + 3.0f; + + SpotlightDestroy(); + return; + } + + // -------------------------------------------------------------- + // Update spotlight target velocity + // -------------------------------------------------------------- + Vector vTargetDir = (m_vSpotlightCurrentPos - m_hSpotlightTarget->GetLocalOrigin()); + float vTargetDist = vTargetDir.Length(); + + Vector vecNewVelocity = vTargetDir; + VectorNormalize(vecNewVelocity); + vecNewVelocity *= (10 * vTargetDist); + + // If a large move is requested, just jump to final spot as we + // probably hit a discontinuity + if (vecNewVelocity.Length() > 200) + { + VectorNormalize(vecNewVelocity); + vecNewVelocity *= 200; + m_hSpotlightTarget->SetLocalOrigin( m_vSpotlightCurrentPos ); + } + m_hSpotlightTarget->SetAbsVelocity( vecNewVelocity ); + + m_hSpotlightTarget->m_vSpotlightOrg = GetAbsOrigin(); + + // Avoid sudden change in where beam fades out when cross disconinuities + m_hSpotlightTarget->m_vSpotlightDir = m_hSpotlightTarget->GetLocalOrigin() - m_hSpotlightTarget->m_vSpotlightOrg; + float flBeamLength = VectorNormalize( m_hSpotlightTarget->m_vSpotlightDir ); + m_flSpotlightCurLength = (0.80*m_flSpotlightCurLength) + (0.2*flBeamLength); + + // Fade out spotlight end if past max length. + if (m_flSpotlightCurLength > 2*m_flSpotlightMaxLength) + { + m_hSpotlightTarget->SetRenderColorA( 0 ); + m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength); + } + else if (m_flSpotlightCurLength > m_flSpotlightMaxLength) + { + m_hSpotlightTarget->SetRenderColorA( (1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength)) ); + m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength); + } + else + { + m_hSpotlightTarget->SetRenderColorA( 1.0 ); + m_hSpotlight->SetFadeLength(m_flSpotlightCurLength); + } + + // Adjust end width to keep beam width constant + float flNewWidth = SPOTLIGHT_WIDTH * ( flBeamLength/m_flSpotlightMaxLength); + + m_hSpotlight->SetWidth(flNewWidth); + m_hSpotlight->SetEndWidth(flNewWidth); + + m_hSpotlightTarget->m_flLightScale = 0.0; +} + +//----------------------------------------------------------------------------- +// Purpose: Called just before we are deleted. +//----------------------------------------------------------------------------- +void CNPC_CScanner::UpdateOnRemove( void ) +{ + SpotlightDestroy(); + BaseClass::UpdateOnRemove(); +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CNPC_CScanner::TakePhoto(void) +{ + ScannerEmitSound( "TakePhoto" ); + + m_pEyeFlash->SetScale( 1.4 ); + m_pEyeFlash->SetBrightness( 255 ); + m_pEyeFlash->SetColor(255,255,255); + + Vector vRawPos = InspectTargetPosition(); + Vector vLightPos = vRawPos; + + // If taking picture of entity, aim at feet + if ( GetTarget() ) + { + if ( GetTarget()->IsPlayer() ) + { + m_OnPhotographPlayer.FireOutput( GetTarget(), this ); + BlindFlashTarget( GetTarget() ); + } + + if ( GetTarget()->MyNPCPointer() != NULL ) + { + m_OnPhotographNPC.FireOutput( GetTarget(), this ); + GetTarget()->MyNPCPointer()->DispatchInteraction( g_interactionScannerInspectBegin, NULL, this ); + } + } + + SetIdealActivity( (Activity) ACT_SCANNER_FLARE_START ); + + m_bPhotoTaken = true; +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CNPC_CScanner::AttackPreFlash(void) +{ + ScannerEmitSound( "TakePhoto" ); + + // If off turn on, if on turn off + if (m_pEyeFlash->GetBrightness() == 0) + { + m_pEyeFlash->SetScale( 0.5 ); + m_pEyeFlash->SetBrightness( 255 ); + m_pEyeFlash->SetColor(255,0,0); + } + else + { + m_pEyeFlash->SetBrightness( 0 ); + } +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CNPC_CScanner::AttackFlash(void) +{ + ScannerEmitSound( "AttackFlash" ); + m_pEyeFlash->SetScale( 1.8 ); + m_pEyeFlash->SetBrightness( 255 ); + m_pEyeFlash->SetColor(255,255,255); + + if (GetEnemy() != NULL) + { + Vector pos = GetEnemyLKP(); + CBroadcastRecipientFilter filter; + te->DynamicLight( filter, 0.0, &pos, 200, 200, 255, 0, 300, 0.2, 50 ); + + if (GetEnemy()->IsPlayer()) + { + m_OnPhotographPlayer.FireOutput(GetTarget(), this); + } + else if( GetEnemy()->MyNPCPointer() ) + { + m_OnPhotographNPC.FireOutput(GetTarget(), this); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pTarget - +//----------------------------------------------------------------------------- +void CNPC_CScanner::BlindFlashTarget( CBaseEntity *pTarget ) +{ + // Tell all the striders this person is here! + CAI_BaseNPC ** ppAIs = g_AI_Manager.AccessAIs(); + int nAIs = g_AI_Manager.NumAIs(); + + if( IsStriderScout() ) + { + for ( int i = 0; i < nAIs; i++ ) + { + if( FClassnameIs( ppAIs[ i ], "npc_strider" ) ) + { + ppAIs[ i ]->UpdateEnemyMemory( pTarget, pTarget->GetAbsOrigin(), this ); + } + } + } + + // Only bother with player + if ( pTarget->IsPlayer() == false ) + return; + + // Scale the flash value by how closely the player is looking at me + Vector vFlashDir = GetAbsOrigin() - pTarget->EyePosition(); + VectorNormalize(vFlashDir); + + Vector vFacing; + AngleVectors( pTarget->EyeAngles(), &vFacing ); + + float dotPr = DotProduct( vFlashDir, vFacing ); + + // Not if behind us + if ( dotPr > 0.5f ) + { + // Make sure nothing in the way + trace_t tr; + AI_TraceLine ( GetAbsOrigin(), pTarget->EyePosition(), MASK_OPAQUE, this, COLLISION_GROUP_NONE, &tr ); + + if ( tr.startsolid == false && tr.fraction == 1.0) + { + color32 white = { 255, 255, 255, SCANNER_FLASH_MAX_VALUE * dotPr }; + + if ( ( g_pMaterialSystemHardwareConfig != NULL ) && ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) ) + { + white.a = ( byte )( ( float )white.a * 0.9f ); + } + + float flFadeTime = ( IsX360() ) ? 0.5f : 3.0f; + UTIL_ScreenFade( pTarget, white, flFadeTime, 0.5, FFADE_IN ); + } + } +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CNPC_CScanner::AttackFlashBlind(void) +{ + if( GetEnemy() ) + { + BlindFlashTarget( GetEnemy() ); + } + + m_pEyeFlash->SetBrightness( 0 ); + + float fAttackDelay = random->RandomFloat(SCANNER_ATTACK_MIN_DELAY,SCANNER_ATTACK_MAX_DELAY); + + if( IsStriderScout() ) + { + // Make strider scouts more snappy. + fAttackDelay *= 0.5; + } + + m_flNextAttack = gpGlobals->curtime + fAttackDelay; + m_fNextSpotlightTime = gpGlobals->curtime + 1.0f; +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CNPC_CScanner::AttackDivebomb( void ) +{ + if (m_hSpotlight) + { + SpotlightDestroy(); + } + + BaseClass::AttackDivebomb(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pTask - +//----------------------------------------------------------------------------- +void CNPC_CScanner::StartTask( const Task_t *pTask ) +{ + switch (pTask->iTask) + { + case TASK_CSCANNER_GET_PATH_TO_INSPECT_TARGET: + { + // Must have somewhere to fly to + if ( HaveInspectTarget() == false ) + { + TaskFail( "No inspection target to fly to!\n" ); + return; + } + + if ( GetTarget() ) + { + //FIXME: Tweak + //Vector idealPos = IdealGoalForMovement( InspectTargetPosition(), GetAbsOrigin(), 128.0f, 128.0f ); + + AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin ); + + if ( GetNavigator()->SetGoal( goal ) ) + { + TaskComplete(); + return; + } + } + else + { + AI_NavGoal_t goal( GOALTYPE_LOCATION, InspectTargetPosition() ); + + if ( GetNavigator()->SetGoal( goal ) ) + { + TaskComplete(); + return; + } + } + + // Don't try and inspect this target again for a few seconds + CNPC_Citizen *pCitizen = dynamic_cast<CNPC_Citizen *>( GetTarget() ); + if ( pCitizen ) + { + pCitizen->SetNextScannerInspectTime( gpGlobals->curtime + 5.0 ); + } + + TaskFail("No route to inspection target!\n"); + } + break; + + case TASK_CSCANNER_SPOT_INSPECT_ON: + { + if (GetTarget() == NULL) + { + TaskFail(FAIL_NO_TARGET); + } + else + { + CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer(); + if (!pNPC) + { + TaskFail(FAIL_NO_TARGET); + } + else + { + pNPC->DispatchInteraction(g_interactionScannerInspectBegin,NULL,this); + + // Now we need some time to inspect + m_fInspectEndTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_LENGTH; + TaskComplete(); + } + } + break; + } + case TASK_CSCANNER_SPOT_INSPECT_WAIT: + { + if (GetTarget() == NULL) + { + TaskFail(FAIL_NO_TARGET); + } + else + { + CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer(); + if (!pNPC) + { + SetTarget( NULL ); + TaskFail(FAIL_NO_TARGET); + } + else + { + //<<TEMP>>//<<TEMP>> armband too! + pNPC->DispatchInteraction(g_interactionScannerInspectHandsUp,NULL,this); + } + TaskComplete(); + } + break; + } + case TASK_CSCANNER_SPOT_INSPECT_OFF: + { + if (GetTarget() == NULL) + { + TaskFail(FAIL_NO_TARGET); + } + else + { + CAI_BaseNPC* pNPC = GetTarget()->MyNPCPointer(); + if (!pNPC) + { + TaskFail(FAIL_NO_TARGET); + } + else + { + pNPC->DispatchInteraction(g_interactionScannerInspectDone,NULL,this); + + // Clear target entity and don't inspect again for a while + SetTarget( NULL ); + m_fCheckCitizenTime = gpGlobals->curtime + SCANNER_CIT_INSPECT_DELAY; + TaskComplete(); + } + } + break; + } + case TASK_CSCANNER_CLEAR_INSPECT_TARGET: + { + ClearInspectTarget(); + + TaskComplete(); + break; + } + + case TASK_CSCANNER_SET_FLY_SPOT: + { + m_nFlyMode = SCANNER_FLY_SPOT; + TaskComplete(); + break; + } + + case TASK_CSCANNER_SET_FLY_PHOTO: + { + m_nFlyMode = SCANNER_FLY_PHOTO; + m_bPhotoTaken = false; + + // Leave spotlight off for a while + m_fNextSpotlightTime = gpGlobals->curtime + 2.0; + + TaskComplete(); + break; + } + + case TASK_CSCANNER_PHOTOGRAPH: + { + TakePhoto(); + SetWait( 0.1 ); + break; + } + + case TASK_CSCANNER_ATTACK_PRE_FLASH: + { + if( IsStriderScout() ) + { + Vector vecScare = GetEnemy()->EarPosition(); + Vector vecDir = WorldSpaceCenter() - vecScare; + VectorNormalize( vecDir ); + vecScare += vecDir * 64.0f; + + CSoundEnt::InsertSound( SOUND_DANGER, vecScare, 256, 1.0, this ); + } + + if (m_pEyeFlash) + { + AttackPreFlash(); + // Flash red for a while + SetWait( 1.0f ); + } + else + { + TaskFail("No Flash"); + } + break; + } + + case TASK_CSCANNER_ATTACK_FLASH: + { + AttackFlash(); + // Blinding occurs slightly later + SetWait( 0.05 ); + break; + } + + // Override to go to inspect target position whether or not is an entity + case TASK_GET_PATH_TO_TARGET: + { + if (!HaveInspectTarget()) + { + TaskFail(FAIL_NO_TARGET); + } + else if (GetHintNode()) + { + Vector vNodePos; + GetHintNode()->GetPosition(this,&vNodePos); + + GetNavigator()->SetGoal( vNodePos ); + } + else + { + AI_NavGoal_t goal( (const Vector &)InspectTargetPosition() ); + goal.pTarget = GetTarget(); + GetNavigator()->SetGoal( goal ); + } + break; + } + default: + BaseClass::StartTask(pTask); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +char *CNPC_CScanner::GetScannerSoundPrefix( void ) +{ + if( m_bIsClawScanner ) + return "NPC_SScanner"; + + return "NPC_CScanner"; +} + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +float CNPC_CScanner::MinGroundDist( void ) +{ + if ( m_nFlyMode == SCANNER_FLY_SPOT && !GetHintNode() ) + { + return SCANNER_SPOTLIGHT_FLY_HEIGHT; + } + + return SCANNER_NOSPOTLIGHT_FLY_HEIGHT; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CScanner::AdjustScannerVelocity( void ) +{ + if ( m_bIsClawScanner ) + { + m_vCurrentVelocity *= ( 1 + sin( ( gpGlobals->curtime + m_flFlyNoiseBase ) * 2.5f ) * .1 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flInterval - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CNPC_CScanner::OverrideMove( float flInterval ) +{ + // ---------------------------------------------- + // If dive bombing + // ---------------------------------------------- + if (m_nFlyMode == SCANNER_FLY_DIVE) + { + MoveToDivebomb( flInterval ); + } + else + { + Vector vMoveTargetPos(0,0,0); + CBaseEntity *pMoveTarget = NULL; + + // The original line of code was, due to the accidental use of '|' instead of + // '&', always true. Replacing with 'true' to suppress the warning without changing + // the (long-standing) behavior. + if ( true ) //!GetNavigator()->IsGoalActive() || ( GetNavigator()->GetCurWaypointFlags() | bits_WP_TO_PATHCORNER ) ) + { + // Select move target + if ( GetTarget() != NULL ) + { + pMoveTarget = GetTarget(); + } + else if ( GetEnemy() != NULL ) + { + pMoveTarget = GetEnemy(); + } + + // Select move target position + if ( HaveInspectTarget() ) + { + vMoveTargetPos = InspectTargetPosition(); + } + else if ( GetEnemy() != NULL ) + { + vMoveTargetPos = GetEnemy()->GetAbsOrigin(); + } + } + else + { + vMoveTargetPos = GetNavigator()->GetCurWaypointPos(); + } + + ClearCondition( COND_SCANNER_FLY_CLEAR ); + ClearCondition( COND_SCANNER_FLY_BLOCKED ); + + // See if we can fly there directly + if ( pMoveTarget || HaveInspectTarget() ) + { + trace_t tr; + AI_TraceHull( GetAbsOrigin(), vMoveTargetPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + + float fTargetDist = (1.0f-tr.fraction)*(GetAbsOrigin() - vMoveTargetPos).Length(); + + if ( ( tr.m_pEnt == pMoveTarget ) || ( fTargetDist < 50 ) ) + { + if ( g_debug_cscanner.GetBool() ) + { + NDebugOverlay::Line(GetLocalOrigin(), vMoveTargetPos, 0,255,0, true, 0); + NDebugOverlay::Cross3D(tr.endpos,Vector(-5,-5,-5),Vector(5,5,5),0,255,0,true,0.1); + } + + SetCondition( COND_SCANNER_FLY_CLEAR ); + } + else + { + //HANDY DEBUG TOOL + if ( g_debug_cscanner.GetBool() ) + { + NDebugOverlay::Line(GetLocalOrigin(), vMoveTargetPos, 255,0,0, true, 0); + NDebugOverlay::Cross3D(tr.endpos,Vector(-5,-5,-5),Vector(5,5,5),255,0,0,true,0.1); + } + + SetCondition( COND_SCANNER_FLY_BLOCKED ); + } + } + + // If I have a route, keep it updated and move toward target + if ( GetNavigator()->IsGoalActive() ) + { + if ( OverridePathMove( pMoveTarget, flInterval ) ) + { + BlendPhyscannonLaunchSpeed(); + return true; + } + } + else if (m_nFlyMode == SCANNER_FLY_SPOT) + { + MoveToSpotlight( flInterval ); + } + // If photographing + else if ( m_nFlyMode == SCANNER_FLY_PHOTO ) + { + MoveToPhotograph( flInterval ); + } + else if ( m_nFlyMode == SCANNER_FLY_FOLLOW ) + { + MoveToSpotlight( flInterval ); + } + // ---------------------------------------------- + // If attacking + // ---------------------------------------------- + else if (m_nFlyMode == SCANNER_FLY_ATTACK) + { + if ( m_hSpotlight ) + { + SpotlightDestroy(); + } + + MoveToAttack( flInterval ); + } + // ----------------------------------------------------------------- + // If I don't have a route, just decelerate + // ----------------------------------------------------------------- + else if (!GetNavigator()->IsGoalActive()) + { + float myDecay = 9.5; + Decelerate( flInterval, myDecay); + } + } + + MoveExecute_Alive( flInterval ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Accelerates toward a given position. +// Input : flInterval - Time interval over which to move. +// vecMoveTarget - Position to move toward. +//----------------------------------------------------------------------------- +void CNPC_CScanner::MoveToTarget( float flInterval, const Vector &vecMoveTarget ) +{ + // Don't move if stalling + if ( m_flEngineStallTime > gpGlobals->curtime ) + return; + + // Look at our inspection target if we have one + if ( GetEnemy() != NULL ) + { + // Otherwise at our enemy + TurnHeadToTarget( flInterval, GetEnemy()->EyePosition() ); + } + else if ( HaveInspectTarget() ) + { + TurnHeadToTarget( flInterval, InspectTargetPosition() ); + } + else + { + // Otherwise face our motion direction + TurnHeadToTarget( flInterval, vecMoveTarget ); + } + + // ------------------------------------- + // Move towards our target + // ------------------------------------- + float myAccel; + float myZAccel = 400.0f; + float myDecay = 0.15f; + + Vector vecCurrentDir; + + // Get the relationship between my current velocity and the way I want to be going. + vecCurrentDir = GetCurrentVelocity(); + VectorNormalize( vecCurrentDir ); + + Vector targetDir = vecMoveTarget - GetAbsOrigin(); + float flDist = VectorNormalize(targetDir); + + float flDot; + flDot = DotProduct( targetDir, vecCurrentDir ); + + if( flDot > 0.25 ) + { + // If my target is in front of me, my flight model is a bit more accurate. + myAccel = 250; + } + else + { + // Have a harder time correcting my course if I'm currently flying away from my target. + myAccel = 128; + } + + if ( myAccel > flDist / flInterval ) + { + myAccel = flDist / flInterval; + } + + if ( myZAccel > flDist / flInterval ) + { + myZAccel = flDist / flInterval; + } + + MoveInDirection( flInterval, targetDir, myAccel, myZAccel, myDecay ); + + // calc relative banking targets + Vector forward, right, up; + GetVectors( &forward, &right, &up ); + + m_vCurrentBanking.x = targetDir.x; + m_vCurrentBanking.z = 120.0f * DotProduct( right, targetDir ); + m_vCurrentBanking.y = 0; + + float speedPerc = SimpleSplineRemapVal( GetCurrentVelocity().Length(), 0.0f, GetMaxSpeed(), 0.0f, 1.0f ); + + speedPerc = clamp( speedPerc, 0.0f, 1.0f ); + + m_vCurrentBanking *= speedPerc; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flInterval - +//----------------------------------------------------------------------------- +void CNPC_CScanner::MoveToSpotlight( float flInterval ) +{ + if ( flInterval <= 0 ) + return; + + Vector vTargetPos; + + if ( HaveInspectTarget() ) + { + vTargetPos = InspectTargetPosition(); + } + else if ( GetEnemy() != NULL ) + { + vTargetPos = GetEnemyLKP(); + } + else + { + return; + } + + //float flDesiredDist = SCANNER_SPOTLIGHT_NEAR_DIST + ( ( SCANNER_SPOTLIGHT_FAR_DIST - SCANNER_SPOTLIGHT_NEAR_DIST ) / 2 ); + + float flIdealHeightDiff = SCANNER_SPOTLIGHT_NEAR_DIST; + if( IsEnemyPlayerInSuit() ) + { + flIdealHeightDiff *= 0.5; + } + + Vector idealPos = IdealGoalForMovement( vTargetPos, GetAbsOrigin(), GetGoalDistance(), flIdealHeightDiff ); + + MoveToTarget( flInterval, idealPos ); + + //TODO: Re-implement? + + /* + // ------------------------------------------------ + // Also keep my distance from other squad members + // unless I'm inspecting + // ------------------------------------------------ + if (m_pSquad && + gpGlobals->curtime > m_fInspectEndTime) + { + CBaseEntity* pNearest = m_pSquad->NearestSquadMember(this); + if (pNearest) + { + Vector vNearestDir = (pNearest->GetLocalOrigin() - GetLocalOrigin()); + if (vNearestDir.Length() < SCANNER_SQUAD_FLY_DIST) + { + vNearestDir = pNearest->GetLocalOrigin() - GetLocalOrigin(); + VectorNormalize(vNearestDir); + vFlyDirection -= 0.5*vNearestDir; + } + } + } + + // --------------------------------------------------------- + // Add evasion if I have taken damage recently + // --------------------------------------------------------- + if ((m_flLastDamageTime + SCANNER_EVADE_TIME) > gpGlobals->curtime) + { + vFlyDirection = vFlyDirection + VelocityToEvade(GetEnemyCombatCharacterPointer()); + } + */ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CNPC_CScanner::GetGoalDistance( void ) +{ + if ( m_flGoalOverrideDistance != 0.0f ) + return m_flGoalOverrideDistance; + + switch ( m_nFlyMode ) + { + case SCANNER_FLY_PHOTO: + return ( SCANNER_PHOTO_NEAR_DIST + ( ( SCANNER_PHOTO_FAR_DIST - SCANNER_PHOTO_NEAR_DIST ) / 2 ) ); + break; + + case SCANNER_FLY_SPOT: + { + float goalDist = ( SCANNER_SPOTLIGHT_NEAR_DIST + ( ( SCANNER_SPOTLIGHT_FAR_DIST - SCANNER_SPOTLIGHT_NEAR_DIST ) / 2 ) ); + if( IsEnemyPlayerInSuit() ) + { + goalDist *= 0.5; + } + return goalDist; + } + break; + + case SCANNER_FLY_FOLLOW: + return ( SCANNER_FOLLOW_DIST ); + break; + } + + return BaseClass::GetGoalDistance(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CScanner::MoveToPhotograph(float flInterval) +{ + if ( HaveInspectTarget() == false ) + return; + + //float flDesiredDist = SCANNER_PHOTO_NEAR_DIST + ( ( SCANNER_PHOTO_FAR_DIST - SCANNER_PHOTO_NEAR_DIST ) / 2 ); + + Vector idealPos = IdealGoalForMovement( InspectTargetPosition(), GetAbsOrigin(), GetGoalDistance(), 32.0f ); + + MoveToTarget( flInterval, idealPos ); + + //FIXME: Re-implement? + + /* + // ------------------------------------------------ + // Also keep my distance from other squad members + // unless I'm inspecting + // ------------------------------------------------ + if (m_pSquad && + gpGlobals->curtime > m_fInspectEndTime) + { + CBaseEntity* pNearest = m_pSquad->NearestSquadMember(this); + if (pNearest) + { + Vector vNearestDir = (pNearest->GetLocalOrigin() - GetLocalOrigin()); + if (vNearestDir.Length() < SCANNER_SQUAD_FLY_DIST) + { + vNearestDir = pNearest->GetLocalOrigin() - GetLocalOrigin(); + VectorNormalize(vNearestDir); + vFlyDirection -= 0.5*vNearestDir; + } + } + } + */ +} + + +//----------------------------------------------------------------------------- +// Purpose: This is a generic function (to be implemented by sub-classes) to +// handle specific interactions between different types of characters +// (For example the barnacle grabbing an NPC) +// Input : Constant for the type of interaction +// Output : true - if sub-class has a response for the interaction +// false - if sub-class has no response +//----------------------------------------------------------------------------- +bool CNPC_CScanner::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* pSourceEnt) +{ + // TODO:: - doing this by just an interrupt contition would be a lot better! + if (interactionType == g_interactionScannerSupportEntity) + { + // Only accept help request if I'm not already busy + if (GetEnemy() == NULL && !HaveInspectTarget()) + { + // Only accept if target is a reasonable distance away + CBaseEntity* pTarget = (CBaseEntity*)data; + float fTargetDist = (pTarget->GetLocalOrigin() - GetLocalOrigin()).Length(); + + if (fTargetDist < SCANNER_SQUAD_HELP_DIST) + { + float fInspectTime = (((CNPC_CScanner*)pSourceEnt)->m_fInspectEndTime - gpGlobals->curtime); + SetInspectTargetToEnt(pTarget,fInspectTime); + + if (random->RandomInt(0,2)==0) + { + SetSchedule(SCHED_CSCANNER_PHOTOGRAPH_HOVER); + } + else + { + SetSchedule(SCHED_CSCANNER_SPOTLIGHT_HOVER); + } + return true; + } + } + } + else if (interactionType == g_interactionScannerSupportPosition) + { + // Only accept help request if I'm not already busy + if (GetEnemy() == NULL && !HaveInspectTarget()) + { + // Only accept if target is a reasonable distance away + Vector vInspectPos; + vInspectPos.x = ((Vector *)data)->x; + vInspectPos.y = ((Vector *)data)->y; + vInspectPos.z = ((Vector *)data)->z; + + float fTargetDist = (vInspectPos - GetLocalOrigin()).Length(); + + if (fTargetDist < SCANNER_SQUAD_HELP_DIST) + { + float fInspectTime = (((CNPC_CScanner*)pSourceEnt)->m_fInspectEndTime - gpGlobals->curtime); + SetInspectTargetToPos(vInspectPos,fInspectTime); + + if (random->RandomInt(0,2)==0) + { + SetSchedule(SCHED_CSCANNER_PHOTOGRAPH_HOVER); + } + else + { + SetSchedule(SCHED_CSCANNER_SPOTLIGHT_HOVER); + } + return true; + } + } + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CNPC_CScanner::InputDisableSpotlight( inputdata_t &inputdata ) +{ + m_bNoLight = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CNPC_CScanner::GetHeadTurnRate( void ) +{ + if ( GetEnemy() ) + return 800.0f; + + if ( HaveInspectTarget() ) + return 500.0f; + + return BaseClass::GetHeadTurnRate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CNPC_CScanner::InputSetFollowTarget( inputdata_t &inputdata ) +{ + InspectTarget( inputdata, SCANNER_FLY_FOLLOW ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CNPC_CScanner::InputClearFollowTarget( inputdata_t &inputdata ) +{ + SetInspectTargetToEnt( NULL, 0 ); + + m_nFlyMode = SCANNER_FLY_PATROL; +} + +//----------------------------------------------------------------------------- +// +// Schedules +// +//----------------------------------------------------------------------------- + +AI_BEGIN_CUSTOM_NPC( npc_cscanner, CNPC_CScanner ) + DECLARE_TASK(TASK_CSCANNER_SET_FLY_PHOTO) + DECLARE_TASK(TASK_CSCANNER_SET_FLY_SPOT) + DECLARE_TASK(TASK_CSCANNER_PHOTOGRAPH) + DECLARE_TASK(TASK_CSCANNER_ATTACK_PRE_FLASH) + DECLARE_TASK(TASK_CSCANNER_ATTACK_FLASH) + DECLARE_TASK(TASK_CSCANNER_SPOT_INSPECT_ON) + DECLARE_TASK(TASK_CSCANNER_SPOT_INSPECT_WAIT) + DECLARE_TASK(TASK_CSCANNER_SPOT_INSPECT_OFF) + DECLARE_TASK(TASK_CSCANNER_CLEAR_INSPECT_TARGET) + DECLARE_TASK(TASK_CSCANNER_GET_PATH_TO_INSPECT_TARGET) + + DECLARE_CONDITION(COND_CSCANNER_HAVE_INSPECT_TARGET) + DECLARE_CONDITION(COND_CSCANNER_INSPECT_DONE) + DECLARE_CONDITION(COND_CSCANNER_CAN_PHOTOGRAPH) + DECLARE_CONDITION(COND_CSCANNER_SPOT_ON_TARGET) + + DECLARE_ACTIVITY(ACT_SCANNER_SMALL_FLINCH_ALERT) + DECLARE_ACTIVITY(ACT_SCANNER_SMALL_FLINCH_COMBAT) + DECLARE_ACTIVITY(ACT_SCANNER_INSPECT) + DECLARE_ACTIVITY(ACT_SCANNER_WALK_ALERT) + DECLARE_ACTIVITY(ACT_SCANNER_WALK_COMBAT) + DECLARE_ACTIVITY(ACT_SCANNER_FLARE) + DECLARE_ACTIVITY(ACT_SCANNER_RETRACT) + DECLARE_ACTIVITY(ACT_SCANNER_FLARE_PRONGS) + DECLARE_ACTIVITY(ACT_SCANNER_RETRACT_PRONGS) + DECLARE_ACTIVITY(ACT_SCANNER_FLARE_START) + + DECLARE_ANIMEVENT( AE_SCANNER_CLOSED ) + + DECLARE_INTERACTION(g_interactionScannerInspect) + DECLARE_INTERACTION(g_interactionScannerInspectBegin) + DECLARE_INTERACTION(g_interactionScannerInspectDone) + DECLARE_INTERACTION(g_interactionScannerInspectHandsUp) + DECLARE_INTERACTION(g_interactionScannerInspectShowArmband) + DECLARE_INTERACTION(g_interactionScannerSupportEntity) + DECLARE_INTERACTION(g_interactionScannerSupportPosition) + + //========================================================= + // > SCHED_CSCANNER_PATROL + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_CSCANNER_PATROL, + + " Tasks" + " TASK_CSCANNER_CLEAR_INSPECT_TARGET 0" + " TASK_SCANNER_SET_FLY_PATROL 0" + " TASK_SET_TOLERANCE_DISTANCE 32" + " TASK_SET_ROUTE_SEARCH_TIME 5" // Spend 5 seconds trying to build a path if stuck + " TASK_GET_PATH_TO_RANDOM_NODE 2000" + " TASK_RUN_PATH 0" + " TASK_WAIT_FOR_MOVEMENT 0" + "" + " Interrupts" + " COND_GIVE_WAY" + " COND_NEW_ENEMY" + " COND_SEE_ENEMY" + " COND_SEE_FEAR" + " COND_HEAR_COMBAT" + " COND_HEAR_DANGER" + " COND_HEAR_PLAYER" + " COND_LIGHT_DAMAGE" + " COND_HEAVY_DAMAGE" + " COND_PROVOKED" + " COND_CSCANNER_HAVE_INSPECT_TARGET" + " COND_SCANNER_GRABBED_BY_PHYSCANNON" + ) + + //========================================================= + // > SCHED_CSCANNER_SPOTLIGHT_HOVER + // + // Hover above target entity, trying to get spotlight + // on my target + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_CSCANNER_SPOTLIGHT_HOVER, + + " Tasks" + " TASK_CSCANNER_SET_FLY_SPOT 0" + " TASK_SET_ACTIVITY ACTIVITY:ACT_WALK " + " TASK_WAIT 1" + "" + " Interrupts" + " COND_CSCANNER_SPOT_ON_TARGET" + " COND_CSCANNER_INSPECT_DONE" + " COND_SCANNER_FLY_BLOCKED" + " COND_NEW_ENEMY" + " COND_SCANNER_GRABBED_BY_PHYSCANNON" + ) + + //========================================================= + // > SCHED_CSCANNER_SPOTLIGHT_INSPECT_POS + // + // Inspect a position once spotlight is on it + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_CSCANNER_SPOTLIGHT_INSPECT_POS, + + " Tasks" + " TASK_CSCANNER_SET_FLY_SPOT 0" + " TASK_SET_ACTIVITY ACTIVITY:ACT_SCANNER_INSPECT" + " TASK_SPEAK_SENTENCE 3" // Curious sound + " TASK_WAIT 5" + " TASK_CSCANNER_CLEAR_INSPECT_TARGET 0" + "" + " Interrupts" + " COND_CSCANNER_INSPECT_DONE" + " COND_HEAR_DANGER" + " COND_HEAR_COMBAT" + " COND_NEW_ENEMY" + " COND_SCANNER_GRABBED_BY_PHYSCANNON" + ) + + //========================================================= + // > SCHED_CSCANNER_SPOTLIGHT_INSPECT_CIT + // + // Inspect a citizen once spotlight is on it + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_CSCANNER_SPOTLIGHT_INSPECT_CIT, + + " Tasks" + " TASK_CSCANNER_SET_FLY_SPOT 0" + " TASK_SET_ACTIVITY ACTIVITY:ACT_SCANNER_INSPECT" + " TASK_SPEAK_SENTENCE 0" // Stop! + " TASK_WAIT 1" + " TASK_CSCANNER_SPOT_INSPECT_ON 0" + " TASK_WAIT 2" + " TASK_SPEAK_SENTENCE 1" // Hands on head or Show Armband! + " TASK_WAIT 1" + " TASK_CSCANNER_SPOT_INSPECT_WAIT 0" + " TASK_WAIT 5" + " TASK_SPEAK_SENTENCE 2" // Free to go! + " TASK_WAIT 1" + " TASK_CSCANNER_SPOT_INSPECT_OFF 0" + " TASK_CSCANNER_CLEAR_INSPECT_TARGET 0" + "" + " Interrupts" + " COND_NEW_ENEMY" + " COND_SCANNER_GRABBED_BY_PHYSCANNON" + ) + + //========================================================= + // > SCHED_CSCANNER_PHOTOGRAPH_HOVER + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_CSCANNER_PHOTOGRAPH_HOVER, + + " Tasks" + " TASK_CSCANNER_SET_FLY_PHOTO 0" + " TASK_WAIT 2" + "" + " Interrupts" + " COND_CSCANNER_INSPECT_DONE" + " COND_CSCANNER_CAN_PHOTOGRAPH" + " COND_SCANNER_FLY_BLOCKED" + " COND_NEW_ENEMY" + " COND_SCANNER_GRABBED_BY_PHYSCANNON" + ) + + //========================================================= + // > SCHED_CSCANNER_PHOTOGRAPH + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_CSCANNER_PHOTOGRAPH, + + " Tasks" + " TASK_CSCANNER_SET_FLY_PHOTO 0" + " TASK_CSCANNER_PHOTOGRAPH 0" + "" + " Interrupts" + " COND_CSCANNER_INSPECT_DONE" + " COND_NEW_ENEMY" + " COND_ENEMY_DEAD" + " COND_SCANNER_GRABBED_BY_PHYSCANNON" + ) + + //========================================================= + // > SCHED_CSCANNER_ATTACK_FLASH + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_CSCANNER_ATTACK_FLASH, + + " Tasks" + " TASK_SCANNER_SET_FLY_ATTACK 0" + " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" + " TASK_CSCANNER_ATTACK_PRE_FLASH 0 " + " TASK_CSCANNER_ATTACK_FLASH 0" + " TASK_WAIT 0.5" + "" + " Interrupts" + " COND_NEW_ENEMY" + " COND_ENEMY_DEAD" + " COND_SCANNER_GRABBED_BY_PHYSCANNON" + ) + + //========================================================= + // > SCHED_CSCANNER_MOVE_TO_INSPECT + //========================================================= + DEFINE_SCHEDULE + ( + SCHED_CSCANNER_MOVE_TO_INSPECT, + + " Tasks" + " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_SCANNER_PATROL" + " TASK_SET_TOLERANCE_DISTANCE 128" + " TASK_CSCANNER_GET_PATH_TO_INSPECT_TARGET 0" + " TASK_RUN_PATH 0" + " TASK_WAIT_FOR_MOVEMENT 0" + "" + " Interrupts" + " COND_SCANNER_FLY_CLEAR" + " COND_NEW_ENEMY" + " COND_SCANNER_GRABBED_BY_PHYSCANNON" + ) + +AI_END_CUSTOM_NPC() + +//----------------------------------------------------------------------------- +// Claw Scanner +// +// Scanner that always spawns as a claw scanner +//----------------------------------------------------------------------------- + +class CNPC_ClawScanner : public CNPC_CScanner +{ +DECLARE_CLASS( CNPC_ClawScanner, CNPC_CScanner ); + +public: + CNPC_ClawScanner(); + DECLARE_DATADESC(); +}; + +BEGIN_DATADESC( CNPC_ClawScanner ) +END_DATADESC() + + +LINK_ENTITY_TO_CLASS(npc_clawscanner, CNPC_ClawScanner); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CNPC_ClawScanner::CNPC_ClawScanner() +{ + // override our superclass's setting + BecomeClawScanner(); +} |