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_ichthyosaur.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_ichthyosaur.cpp')
| -rw-r--r-- | mp/src/game/server/hl2/npc_ichthyosaur.cpp | 3116 |
1 files changed, 1558 insertions, 1558 deletions
diff --git a/mp/src/game/server/hl2/npc_ichthyosaur.cpp b/mp/src/game/server/hl2/npc_ichthyosaur.cpp index 72290476..52d26750 100644 --- a/mp/src/game/server/hl2/npc_ichthyosaur.cpp +++ b/mp/src/game/server/hl2/npc_ichthyosaur.cpp @@ -1,1558 +1,1558 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Ichthyosaur - buh bum... buh bum...
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "ai_basenpc.h"
-#include "ai_task.h"
-#include "ai_default.h"
-#include "ai_schedule.h"
-#include "ai_hull.h"
-#include "ai_interactions.h"
-#include "ai_navigator.h"
-#include "ai_motor.h"
-#include "activitylist.h"
-#include "game.h"
-#include "npcevent.h"
-#include "player.h"
-#include "entitylist.h"
-#include "soundenvelope.h"
-#include "shake.h"
-#include "ndebugoverlay.h"
-#include "vstdlib/random.h"
-#include "engine/IEngineSound.h"
-#include "movevars_shared.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-ConVar sk_ichthyosaur_health( "sk_ichthyosaur_health", "0" );
-ConVar sk_ichthyosaur_melee_dmg( "sk_ichthyosaur_melee_dmg", "0" );
-
-#define ICHTHYOSAUR_MODEL "models/ichthyosaur.mdl"
-
-#define ICH_HEIGHT_PREFERENCE 16.0f
-#define ICH_DEPTH_PREFERENCE 8.0f
-
-#define ICH_WAYPOINT_DISTANCE 64.0f
-
-#define ICH_AE_BITE 11
-#define ICH_AE_BITE_START 12
-
-#define ICH_SWIM_SPEED_WALK 150
-#define ICH_SWIM_SPEED_RUN 500
-
-#define ICH_MIN_TURN_SPEED 4.0f
-#define ICH_MAX_TURN_SPEED 30.0f
-
-#define ENVELOPE_CONTROLLER (CSoundEnvelopeController::GetController())
-
-#define FEELER_COLLISION 0
-#define FEELER_COLLISION_VISUALIZE (FEELER_COLLISION&&0)
-
-enum IchthyosaurMoveType_t
-{
- ICH_MOVETYPE_SEEK = 0, // Fly through the target without stopping.
- ICH_MOVETYPE_ARRIVE // Slow down and stop at target.
-};
-
-//
-// CNPC_Ichthyosaur
-//
-
-class CNPC_Ichthyosaur : public CAI_BaseNPC
-{
-public:
- DECLARE_CLASS( CNPC_Ichthyosaur, CAI_BaseNPC );
- DECLARE_DATADESC();
-
- CNPC_Ichthyosaur( void ) {}
-
- int SelectSchedule( void );
- int MeleeAttack1Conditions( float flDot, float flDist );
- int OnTakeDamage_Alive( const CTakeDamageInfo &info );
- int TranslateSchedule( int type );
-
- void Precache( void );
- void Spawn( void );
- void MoveFlyExecute( CBaseEntity *pTargetEnt, const Vector & vecDir, float flDistance, float flInterval );
- void HandleAnimEvent( animevent_t *pEvent );
- void PrescheduleThink( void );
- bool OverrideMove( float flInterval );
- void StartTask( const Task_t *pTask );
- void RunTask( const Task_t *pTask );
- void TranslateNavGoal( CBaseEntity *pEnemy, Vector &chasePosition );
- float GetDefaultNavGoalTolerance();
-
- float MaxYawSpeed( void );
-
- Class_T Classify( void ) { return CLASS_ANTLION; } //FIXME: No classification for various wildlife?
-
- bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
-
-private:
-
- bool SteerAvoidObstacles( Vector &Steer, const Vector &Velocity, const Vector &Forward, const Vector &Right, const Vector &Up );
- bool Beached( void );
-
- void DoMovement( float flInterval, const Vector &MoveTarget, int eMoveType );
- void SteerArrive( Vector &Steer, const Vector &Target );
- void SteerSeek( Vector &Steer, const Vector &Target );
- void ClampSteer( Vector &SteerAbs, Vector &SteerRel, Vector &forward, Vector &right, Vector &up );
- void AddSwimNoise( Vector *velocity );
-
- void Bite( void );
- void EnsnareVictim( CBaseEntity *pVictim );
- void ReleaseVictim( void );
- void DragVictim( float moveDist );
-
- void SetPoses( Vector moveRel, float speed );
-
- //void IchTouch( CBaseEntity *pOther );
-
- float GetGroundSpeed( void );
-
-#if FEELER_COLLISION
- Vector DoProbe( const Vector &Probe );
- Vector m_LastSteer;
-#endif
-
- CBaseEntity *m_pVictim;
-
- static const Vector m_vecAccelerationMax;
- static const Vector m_vecAccelerationMin;
-
- Vector m_vecLastMoveTarget;
-
- float m_flNextBiteTime;
- float m_flHoldTime;
- float m_flSwimSpeed;
- float m_flTailYaw;
- float m_flTailPitch;
-
- float m_flNextPingTime;
- float m_flNextGrowlTime;
-
- bool m_bHasMoveTarget;
- bool m_bIgnoreSurface;
-
- //CSoundPatch *m_pSwimSound;
- //CSoundPatch *m_pVoiceSound;
-
- DEFINE_CUSTOM_AI;
-};
-
-//Acceleration definitions
-const Vector CNPC_Ichthyosaur::m_vecAccelerationMax = Vector( 256, 1024, 512 );
-const Vector CNPC_Ichthyosaur::m_vecAccelerationMin = Vector( -256, -1024, -512 );
-
-//Data description
-BEGIN_DATADESC( CNPC_Ichthyosaur )
-
-// Silence classcheck
-// DEFINE_FIELD( m_LastSteer, FIELD_VECTOR ),
-
- DEFINE_FIELD( m_pVictim, FIELD_CLASSPTR ),
- DEFINE_FIELD( m_vecLastMoveTarget, FIELD_VECTOR ),
- DEFINE_FIELD( m_flNextBiteTime, FIELD_FLOAT ),
- DEFINE_FIELD( m_flHoldTime, FIELD_FLOAT ),
- DEFINE_FIELD( m_flSwimSpeed, FIELD_FLOAT ),
- DEFINE_FIELD( m_flTailYaw, FIELD_FLOAT ),
- DEFINE_FIELD( m_flTailPitch, FIELD_FLOAT ),
- DEFINE_FIELD( m_flNextPingTime, FIELD_FLOAT ),
- DEFINE_FIELD( m_flNextGrowlTime, FIELD_FLOAT ),
- DEFINE_FIELD( m_bHasMoveTarget, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bIgnoreSurface, FIELD_BOOLEAN ),
-
- //DEFINE_FUNCTION( IchTouch ),
-
-END_DATADESC()
-
-//Schedules
-enum IchSchedules
-{
- SCHED_ICH_CHASE_ENEMY = LAST_SHARED_SCHEDULE,
- SCHED_ICH_PATROL_RUN,
- SCHED_ICH_PATROL_WALK,
- SCHED_ICH_DROWN_VICTIM,
- SCHED_ICH_MELEE_ATTACK1,
- SCHED_ICH_THRASH,
-};
-
-//Tasks
-enum IchTasks
-{
- TASK_ICH_GET_PATH_TO_RANDOM_NODE = LAST_SHARED_TASK,
- TASK_ICH_GET_PATH_TO_DROWN_NODE,
- TASK_ICH_THRASH_PATH,
-};
-
-//Activities
-int ACT_ICH_THRASH;
-int ACT_ICH_BITE_HIT;
-int ACT_ICH_BITE_MISS;
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::InitCustomSchedules( void )
-{
- INIT_CUSTOM_AI( CNPC_Ichthyosaur );
-
- //Interaction REGISTER_INTERACTION( g_interactionAntlionAttacked );
-
- //Schedules
- ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_CHASE_ENEMY );
- ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_PATROL_RUN );
- ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_PATROL_WALK );
- ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_DROWN_VICTIM );
- ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_MELEE_ATTACK1 );
- ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_THRASH );
-
- //Tasks
- ADD_CUSTOM_TASK( CNPC_Ichthyosaur, TASK_ICH_GET_PATH_TO_RANDOM_NODE );
- ADD_CUSTOM_TASK( CNPC_Ichthyosaur, TASK_ICH_GET_PATH_TO_DROWN_NODE );
- ADD_CUSTOM_TASK( CNPC_Ichthyosaur, TASK_ICH_THRASH_PATH );
-
- //Conditions ADD_CUSTOM_CONDITION( CNPC_CombineGuard, COND_ANTLIONGRUB_HEARD_SQUEAL );
-
- //Activities
- ADD_CUSTOM_ACTIVITY( CNPC_Ichthyosaur, ACT_ICH_THRASH );
- ADD_CUSTOM_ACTIVITY( CNPC_Ichthyosaur, ACT_ICH_BITE_HIT );
- ADD_CUSTOM_ACTIVITY( CNPC_Ichthyosaur, ACT_ICH_BITE_MISS );
-
- AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_CHASE_ENEMY );
- AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_PATROL_RUN );
- AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_PATROL_WALK );
- AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_DROWN_VICTIM );
- AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_MELEE_ATTACK1 );
- AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_THRASH );
-}
-
-LINK_ENTITY_TO_CLASS( npc_ichthyosaur, CNPC_Ichthyosaur );
-IMPLEMENT_CUSTOM_AI( npc_ichthyosaur, CNPC_Ichthyosaur );
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::Precache( void )
-{
- PrecacheModel( ICHTHYOSAUR_MODEL );
-
- PrecacheScriptSound( "NPC_Ichthyosaur.Bite" );
- PrecacheScriptSound( "NPC_Ichthyosaur.BiteMiss" );
- PrecacheScriptSound( "NPC_Ichthyosaur.AttackGrowl" );
-
- BaseClass::Precache();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::Spawn( void )
-{
- Precache();
-
- SetModel( ICHTHYOSAUR_MODEL );
-
- SetHullType(HULL_LARGE_CENTERED);
- SetHullSizeNormal();
- SetDefaultEyeOffset();
-
- SetNavType( NAV_FLY );
- m_NPCState = NPC_STATE_NONE;
- SetBloodColor( BLOOD_COLOR_RED );
- m_iHealth = sk_ichthyosaur_health.GetFloat();
- m_iMaxHealth = m_iHealth;
- m_flFieldOfView = -0.707; // 270 degrees
- SetDistLook( 1024 );
-
- SetSolid( SOLID_BBOX );
- AddSolidFlags( FSOLID_NOT_STANDABLE );
- SetMoveType( MOVETYPE_STEP );
- AddFlag( FL_FLY | FL_STEPMOVEMENT );
-
- m_flGroundSpeed = ICH_SWIM_SPEED_RUN;
-
- m_bIgnoreSurface = false;
-
- m_flSwimSpeed = 0.0f;
- m_flTailYaw = 0.0f;
- m_flTailPitch = 0.0f;
-
- m_flNextBiteTime = gpGlobals->curtime;
- m_flHoldTime = gpGlobals->curtime;
- m_flNextPingTime = gpGlobals->curtime;
- m_flNextGrowlTime = gpGlobals->curtime;
-
-#if FEELER_COLLISION
-
- Vector forward;
-
- GetVectors( &forward, NULL, NULL );
-
- m_vecCurrentVelocity = forward * m_flGroundSpeed;
-
-#endif
-
- //SetTouch( IchTouch );
-
- CapabilitiesClear();
- CapabilitiesAdd( bits_CAP_MOVE_FLY | bits_CAP_INNATE_MELEE_ATTACK1 );
-
- NPCInit();
-
- //m_pSwimSound = ENVELOPE_CONTROLLER.SoundCreate( edict(), CHAN_BODY, "xxxCONVERTTOGAMESOUNDS!!!npc/ichthyosaur/ich_amb1wav", ATTN_NORM );
- //m_pVoiceSound = ENVELOPE_CONTROLLER.SoundCreate( edict(), CHAN_VOICE, "xxxCONVERTTOGAMESOUNDS!!!npc/ichthyosaur/water_breathwav", ATTN_IDLE );
-
- //ENVELOPE_CONTROLLER.Play( m_pSwimSound, 1.0f, 100 );
- //ENVELOPE_CONTROLLER.Play( m_pVoiceSound,1.0f, 100 );
-
- BaseClass::Spawn();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pOther -
-//-----------------------------------------------------------------------------
-/*
-void CNPC_Ichthyosaur::IchTouch( CBaseEntity *pOther )
-{
-}
-*/
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CNPC_Ichthyosaur::SelectSchedule( void )
-{
- if ( m_NPCState == NPC_STATE_COMBAT )
- {
- if ( m_flHoldTime > gpGlobals->curtime )
- return SCHED_ICH_DROWN_VICTIM;
-
- if ( m_flNextBiteTime > gpGlobals->curtime )
- return SCHED_PATROL_RUN;
-
- if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
- return SCHED_MELEE_ATTACK1;
-
- return SCHED_CHASE_ENEMY;
- }
-
- return BaseClass::SelectSchedule();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Handles movement towards the last move target.
-// Input : flInterval -
-//-----------------------------------------------------------------------------
-bool CNPC_Ichthyosaur::OverrideMove( float flInterval )
-{
- m_flGroundSpeed = GetGroundSpeed();
-
- if ( m_bHasMoveTarget )
- {
- DoMovement( flInterval, m_vecLastMoveTarget, ICH_MOVETYPE_ARRIVE );
- }
- else
- {
- DoMovement( flInterval, GetLocalOrigin(), ICH_MOVETYPE_ARRIVE );
- }
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &probe -
-// Output : Vector
-//-----------------------------------------------------------------------------
-#if FEELER_COLLISION
-
-Vector CNPC_Ichthyosaur::DoProbe( const Vector &probe )
-{
- trace_t tr;
- float fraction = 1.0f;
- bool collided = false;
- Vector normal = Vector( 0, 0, -1 );
-
- float waterLevel = UTIL_WaterLevel( GetAbsOrigin(), GetAbsOrigin().z, GetAbsOrigin().z+150 );
-
- waterLevel -= GetAbsOrigin().z;
- waterLevel /= 150;
-
- if ( waterLevel < 1.0f )
- {
- collided = true;
- fraction = waterLevel;
- }
-
- AI_TraceHull( GetAbsOrigin(), probe, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
-
- if ( ( collided == false ) || ( tr.fraction < fraction ) )
- {
- fraction = tr.fraction;
- normal = tr.plane.normal;
- }
-
- if ( ( fraction < 1.0f ) && ( GetEnemy() == NULL || tr.u.ent != GetEnemy()->pev ) )
- {
-#if FEELER_COLLISION_VISUALIZE
- NDebugOverlay::Line( GetLocalOrigin(), probe, 255, 0, 0, false, 0.1f );
-#endif
-
- Vector probeDir = probe - GetLocalOrigin();
-
- Vector normalToProbeAndWallNormal = probeDir.Cross( normal );
- Vector steeringVector = normalToProbeAndWallNormal.Cross( probeDir );
-
- Vector velDir = GetAbsVelocity();
- VectorNormalize( velDir );
-
- float steeringForce = m_flGroundSpeed * ( 1.0f - fraction ) * normal.Dot( velDir );
-
- if ( steeringForce < 0.0f )
- {
- steeringForce = -steeringForce;
- }
-
- velDir = steeringVector;
- VectorNormalize( velDir );
-
- steeringVector = steeringForce * velDir;
-
- return steeringVector;
- }
-
-#if FEELER_COLLISION_VISUALIZE
- NDebugOverlay::Line( GetLocalOrigin(), probe, 0, 255, 0, false, 0.1f );
-#endif
-
- return Vector( 0.0f, 0.0f, 0.0f );
-}
-
-#endif
-
-//-----------------------------------------------------------------------------
-// Purpose: Move the victim of a drag along with us
-// Input : moveDist - our amount of travel
-//-----------------------------------------------------------------------------
-
-#define DRAG_OFFSET 50.0f
-
-void CNPC_Ichthyosaur::DragVictim( float moveDist )
-{
- Vector mins, maxs;
- float width;
-
- mins = WorldAlignMins();
- maxs = WorldAlignMaxs();
- width = ( maxs.y - mins.y ) * 0.5f;
-
- Vector forward, up;
- GetVectors( &forward, NULL, &up );
-
- Vector newPos = GetAbsOrigin() + ( (forward+(up*0.25f)) * ( moveDist + width + DRAG_OFFSET ) );
-
- trace_t tr;
- AI_TraceEntity( this, m_pVictim->GetAbsOrigin(), newPos, MASK_NPCSOLID, &tr );
-
- if ( ( tr.fraction == 1.0f ) && ( tr.m_pEnt != this ) )
- {
- UTIL_SetOrigin( m_pVictim, tr.endpos );
- }
- else
- {
- ReleaseVictim();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Determines the pose parameters for the bending of the body and tail speed
-// Input : moveRel - the dot products for the deviation off of each direction (f,r,u)
-// speed - speed of the fish
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::SetPoses( Vector moveRel, float speed )
-{
- float movePerc, moveBase;
-
- //Find out how fast we're moving in our animations boundaries
- if ( GetIdealActivity() == ACT_WALK )
- {
- moveBase = 0.5f;
- movePerc = moveBase * ( speed / ICH_SWIM_SPEED_WALK );
- }
- else
- {
- moveBase = 1.0f;
- movePerc = moveBase * ( speed / ICH_SWIM_SPEED_RUN );
- }
-
- Vector tailPosition;
- float flSwimSpeed = movePerc;
-
- //Forward deviation
- if ( moveRel.x > 0 )
- {
- flSwimSpeed *= moveBase + (( moveRel.x / m_vecAccelerationMax.x )*moveBase);
- }
- else if ( moveRel.x < 0 )
- {
- flSwimSpeed *= moveBase - (( moveRel.x / m_vecAccelerationMin.x )*moveBase);
- }
-
- //Vertical deviation
- if ( moveRel.z > 0 )
- {
- tailPosition[PITCH] = -90.0f * ( moveRel.z / m_vecAccelerationMax.z );
- }
- else if ( moveRel.z < 0 )
- {
- tailPosition[PITCH] = 90.0f * ( moveRel.z / m_vecAccelerationMin.z );
- }
- else
- {
- tailPosition[PITCH] = 0.0f;
- }
-
- //Lateral deviation
- if ( moveRel.y > 0 )
- {
- tailPosition[ROLL] = 25 * moveRel.y / m_vecAccelerationMax.y;
- tailPosition[YAW] = -1.0f * moveRel.y / m_vecAccelerationMax.y;
- }
- else if ( moveRel.y < 0 )
- {
- tailPosition[ROLL] = -25 * moveRel.y / m_vecAccelerationMin.y;
- tailPosition[YAW] = moveRel.y / m_vecAccelerationMin.y;
- }
- else
- {
- tailPosition[ROLL] = 0.0f;
- tailPosition[YAW] = 0.0f;
- }
-
- //Clamp
- flSwimSpeed = clamp( flSwimSpeed, 0.25f, 1.0f );
- tailPosition[YAW] = clamp( tailPosition[YAW], -90.0f, 90.0f );
- tailPosition[PITCH] = clamp( tailPosition[PITCH], -90.0f, 90.0f );
-
- //Blend
- m_flTailYaw = ( m_flTailYaw * 0.8f ) + ( tailPosition[YAW] * 0.2f );
- m_flTailPitch = ( m_flTailPitch * 0.8f ) + ( tailPosition[PITCH] * 0.2f );
- m_flSwimSpeed = ( m_flSwimSpeed * 0.8f ) + ( flSwimSpeed * 0.2f );
-
- //Pose the body
- SetPoseParameter( 0, m_flSwimSpeed );
- SetPoseParameter( 1, m_flTailYaw );
- SetPoseParameter( 2, m_flTailPitch );
-
- //FIXME: Until the sequence info is reset properly after SetPoseParameter
- if ( ( GetActivity() == ACT_RUN ) || ( GetActivity() == ACT_WALK ) )
- {
- ResetSequenceInfo();
- }
-
- //Face our current velocity
- GetMotor()->SetIdealYawAndUpdate( UTIL_AngleMod( CalcIdealYaw( GetAbsOrigin() + GetAbsVelocity() ) ), AI_KEEP_YAW_SPEED );
-
- float pitch = 0.0f;
-
- if ( speed != 0.0f )
- {
- pitch = -RAD2DEG( asin( GetAbsVelocity().z / speed ) );
- }
-
- //FIXME: Framerate dependant
- QAngle angles = GetLocalAngles();
-
- angles.x = (angles.x * 0.8f) + (pitch * 0.2f);
- angles.z = (angles.z * 0.9f) + (tailPosition[ROLL] * 0.1f);
-
- SetLocalAngles( angles );
-}
-
-#define LATERAL_NOISE_MAX 2.0f
-#define LATERAL_NOISE_FREQ 1.0f
-
-#define VERTICAL_NOISE_MAX 2.0f
-#define VERTICAL_NOISE_FREQ 1.0f
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : velocity -
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::AddSwimNoise( Vector *velocity )
-{
- Vector right, up;
-
- GetVectors( NULL, &right, &up );
-
- float lNoise, vNoise;
-
- lNoise = LATERAL_NOISE_MAX * sin( gpGlobals->curtime * LATERAL_NOISE_FREQ );
- vNoise = VERTICAL_NOISE_MAX * sin( gpGlobals->curtime * VERTICAL_NOISE_FREQ );
-
- (*velocity) += ( right * lNoise ) + ( up * vNoise );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : flInterval -
-// &m_LastMoveTarget -
-// eMoveType -
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::DoMovement( float flInterval, const Vector &MoveTarget, int eMoveType )
-{
- // dvs: something is setting this bit, causing us to stop moving and get stuck that way
- Forget( bits_MEMORY_TURNING );
-
- Vector Steer, SteerAvoid, SteerRel;
- Vector forward, right, up;
-
- //Get our orientation vectors.
- GetVectors( &forward, &right, &up);
-
- if ( ( GetActivity() == ACT_MELEE_ATTACK1 ) && ( GetEnemy() != NULL ) )
- {
- SteerSeek( Steer, GetEnemy()->GetAbsOrigin() );
- }
- else
- {
- //If we are approaching our goal, use an arrival steering mechanism.
- if ( eMoveType == ICH_MOVETYPE_ARRIVE )
- {
- SteerArrive( Steer, MoveTarget );
- }
- else
- {
- //Otherwise use a seek steering mechanism.
- SteerSeek( Steer, MoveTarget );
- }
- }
-
-#if FEELER_COLLISION
-
- Vector f, u, l, r, d;
-
- float probeLength = GetAbsVelocity().Length();
-
- if ( probeLength < 150 )
- probeLength = 150;
-
- if ( probeLength > 500 )
- probeLength = 500;
-
- f = DoProbe( GetLocalOrigin() + (probeLength * forward) );
- r = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward+right)) );
- l = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward-right)) );
- u = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward+up)) );
- d = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward-up)) );
-
- SteerAvoid = f+r+l+u+d;
-
- //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+SteerAvoid, 255, 255, 0, false, 0.1f );
-
- if ( SteerAvoid.LengthSqr() )
- {
- Steer = (SteerAvoid*0.5f);
- }
-
- m_vecVelocity = m_vecVelocity + (Steer*0.5f);
-
- VectorNormalize( m_vecVelocity );
-
- SteerRel.x = forward.Dot( m_vecVelocity );
- SteerRel.y = right.Dot( m_vecVelocity );
- SteerRel.z = up.Dot( m_vecVelocity );
-
- m_vecVelocity *= m_flGroundSpeed;
-
-#else
-
- //See if we need to avoid any obstacles.
- if ( SteerAvoidObstacles( SteerAvoid, GetAbsVelocity(), forward, right, up ) )
- {
- //Take the avoidance vector
- Steer = SteerAvoid;
- }
-
- //Clamp our ideal steering vector to within our physical limitations.
- ClampSteer( Steer, SteerRel, forward, right, up );
-
- ApplyAbsVelocityImpulse( Steer * flInterval );
-
-#endif
-
- Vector vecNewVelocity = GetAbsVelocity();
- float flLength = vecNewVelocity.Length();
-
- //Clamp our final speed
- if ( flLength > m_flGroundSpeed )
- {
- vecNewVelocity *= ( m_flGroundSpeed / flLength );
- flLength = m_flGroundSpeed;
- }
-
- Vector workVelocity = vecNewVelocity;
-
- AddSwimNoise( &workVelocity );
-
- // Pose the fish properly
- SetPoses( SteerRel, flLength );
-
- //Drag our victim before moving
- if ( m_pVictim != NULL )
- {
- DragVictim( (workVelocity*flInterval).Length() );
- }
-
- //Move along the current velocity vector
- if ( WalkMove( workVelocity * flInterval, MASK_NPCSOLID ) == false )
- {
- //Attempt a half-step
- if ( WalkMove( (workVelocity*0.5f) * flInterval, MASK_NPCSOLID) == false )
- {
- //Restart the velocity
- //VectorNormalize( m_vecVelocity );
- vecNewVelocity *= 0.5f;
- }
- else
- {
- //Cut our velocity in half
- vecNewVelocity *= 0.5f;
- }
- }
-
- SetAbsVelocity( vecNewVelocity );
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Gets a steering vector to arrive at a target location with a
-// relatively small velocity.
-// Input : Steer - Receives the ideal steering vector.
-// Target - Target position at which to arrive.
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::SteerArrive(Vector &Steer, const Vector &Target)
-{
- Vector Offset = Target - GetLocalOrigin();
- float fTargetDistance = Offset.Length();
-
- float fIdealSpeed = m_flGroundSpeed * (fTargetDistance / ICH_WAYPOINT_DISTANCE);
- float fClippedSpeed = MIN( fIdealSpeed, m_flGroundSpeed );
-
- Vector DesiredVelocity( 0, 0, 0 );
-
- if ( fTargetDistance > ICH_WAYPOINT_DISTANCE )
- {
- DesiredVelocity = (fClippedSpeed / fTargetDistance) * Offset;
- }
-
- Steer = DesiredVelocity - GetAbsVelocity();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Gets a steering vector to move towards a target position as quickly
-// as possible.
-// Input : Steer - Receives the ideal steering vector.
-// Target - Target position to seek.
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::SteerSeek( Vector &Steer, const Vector &Target )
-{
- Vector offset = Target - GetLocalOrigin();
-
- VectorNormalize( offset );
-
- Vector DesiredVelocity = m_flGroundSpeed * offset;
-
- Steer = DesiredVelocity - GetAbsVelocity();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &Steer -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CNPC_Ichthyosaur::SteerAvoidObstacles(Vector &Steer, const Vector &Velocity, const Vector &Forward, const Vector &Right, const Vector &Up)
-{
- trace_t tr;
-
- bool collided = false;
- Vector dir = Velocity;
- float speed = VectorNormalize( dir );
-
- //Look ahead one second and avoid whatever is in our way.
- AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + (dir*speed), GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
-
- Vector forward;
-
- GetVectors( &forward, NULL, NULL );
-
- //If we're hitting our enemy, just continue on
- if ( ( GetEnemy() != NULL ) && ( tr.m_pEnt == GetEnemy() ) )
- return false;
-
- if ( tr.fraction < 1.0f )
- {
- CBaseEntity *pBlocker = tr.m_pEnt;
-
- if ( ( pBlocker != NULL ) && ( pBlocker->MyNPCPointer() != NULL ) )
- {
- DevMsg( 2, "Avoiding an NPC\n" );
-
- Vector HitOffset = tr.endpos - GetAbsOrigin();
-
- Vector SteerUp = CrossProduct( HitOffset, Velocity );
- Steer = CrossProduct( SteerUp, Velocity );
- VectorNormalize( Steer );
-
- /*Vector probeDir = tr.endpos - GetAbsOrigin();
- Vector normalToProbeAndWallNormal = probeDir.Cross( tr.plane.normal );
-
- Steer = normalToProbeAndWallNormal.Cross( probeDir );
- VectorNormalize( Steer );*/
-
- if ( tr.fraction > 0 )
- {
- Steer = (Steer * Velocity.Length()) / tr.fraction;
- //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+Steer, 255, 0, 0, false, 0.1f );
- }
- else
- {
- Steer = (Steer * 1000 * Velocity.Length());
- //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+Steer, 255, 0, 0, false, 0.1f );
- }
- }
- else
- {
- if ( ( pBlocker != NULL ) && ( pBlocker == GetEnemy() ) )
- {
- DevMsg( "Avoided collision\n" );
- return false;
- }
-
- DevMsg( 2, "Avoiding the world\n" );
-
- Vector steeringVector = tr.plane.normal;
-
- if ( tr.fraction == 0.0f )
- return false;
-
- Steer = steeringVector * ( Velocity.Length() / tr.fraction );
-
- //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+Steer, 255, 0, 0, false, 0.1f );
- }
-
- //return true;
- collided = true;
- }
-
- //Try to remain 8 feet above the ground.
- AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector(0, 0, -ICH_HEIGHT_PREFERENCE), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
-
- if ( tr.fraction < 1.0f )
- {
- Steer += Vector( 0, 0, m_vecAccelerationMax.z / tr.fraction );
- collided = true;
- }
-
- //Stay under the surface
- if ( m_bIgnoreSurface == false )
- {
- float waterLevel = ( UTIL_WaterLevel( GetAbsOrigin(), GetAbsOrigin().z, GetAbsOrigin().z+ICH_DEPTH_PREFERENCE ) - GetAbsOrigin().z ) / ICH_DEPTH_PREFERENCE;
-
- if ( waterLevel < 1.0f )
- {
- Steer += -Vector( 0, 0, m_vecAccelerationMax.z / waterLevel );
- collided = true;
- }
- }
-
- return collided;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Clamps the desired steering vector based on the limitations of this
-// vehicle.
-// Input : SteerAbs - The vector indicating our ideal steering vector. Receives
-// the clamped steering vector in absolute (x,y,z) coordinates.
-// SteerRel - Receives the clamped steering vector in relative (forward, right, up)
-// coordinates.
-// forward - Our current forward vector.
-// right - Our current right vector.
-// up - Our current up vector.
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::ClampSteer(Vector &SteerAbs, Vector &SteerRel, Vector &forward, Vector &right, Vector &up)
-{
- float fForwardSteer = DotProduct(SteerAbs, forward);
- float fRightSteer = DotProduct(SteerAbs, right);
- float fUpSteer = DotProduct(SteerAbs, up);
-
- if (fForwardSteer > 0)
- {
- fForwardSteer = MIN(fForwardSteer, m_vecAccelerationMax.x);
- }
- else
- {
- fForwardSteer = MAX(fForwardSteer, m_vecAccelerationMin.x);
- }
-
- if (fRightSteer > 0)
- {
- fRightSteer = MIN(fRightSteer, m_vecAccelerationMax.y);
- }
- else
- {
- fRightSteer = MAX(fRightSteer, m_vecAccelerationMin.y);
- }
-
- if (fUpSteer > 0)
- {
- fUpSteer = MIN(fUpSteer, m_vecAccelerationMax.z);
- }
- else
- {
- fUpSteer = MAX(fUpSteer, m_vecAccelerationMin.z);
- }
-
- SteerAbs = (fForwardSteer*forward) + (fRightSteer*right) + (fUpSteer*up);
-
- SteerRel.x = fForwardSteer;
- SteerRel.y = fRightSteer;
- SteerRel.z = fUpSteer;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pTargetEnt -
-// vecDir -
-// flDistance -
-// flInterval -
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::MoveFlyExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flDistance, float flInterval )
-{
- IchthyosaurMoveType_t eMoveType = ( GetNavigator()->CurWaypointIsGoal() ) ? ICH_MOVETYPE_ARRIVE : ICH_MOVETYPE_SEEK;
-
- m_flGroundSpeed = GetGroundSpeed();
-
- Vector moveGoal = GetNavigator()->GetCurWaypointPos();
-
- //See if we can move directly to our goal
- if ( ( GetEnemy() != NULL ) && ( GetNavigator()->GetGoalTarget() == (CBaseEntity *) GetEnemy() ) )
- {
- trace_t tr;
- Vector goalPos = GetEnemy()->GetAbsOrigin() + ( GetEnemy()->GetSmoothedVelocity() * 0.5f );
-
- AI_TraceHull( GetAbsOrigin(), goalPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, GetEnemy(), COLLISION_GROUP_NONE, &tr );
-
- if ( tr.fraction == 1.0f )
- {
- moveGoal = tr.endpos;
- }
- }
-
- //Move
- DoMovement( flInterval, moveGoal, eMoveType );
-
- //Save the info from that run
- m_vecLastMoveTarget = moveGoal;
- m_bHasMoveTarget = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CNPC_Ichthyosaur::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker )
-{
- // don't look through water
- if ( GetWaterLevel() != pEntity->GetWaterLevel() )
- return false;
-
- return BaseClass::FVisible( pEntity, traceMask, ppBlocker );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get our conditions for a melee attack
-// Input : flDot -
-// flDist -
-// Output : int
-//-----------------------------------------------------------------------------
-int CNPC_Ichthyosaur::MeleeAttack1Conditions( float flDot, float flDist )
-{
- Vector predictedDir = ( (GetEnemy()->GetAbsOrigin()+(GetEnemy()->GetSmoothedVelocity())) - GetAbsOrigin() );
- float flPredictedDist = VectorNormalize( predictedDir );
-
- Vector vBodyDir;
- GetVectors( &vBodyDir, NULL, NULL );
-
- float flPredictedDot = DotProduct( predictedDir, vBodyDir );
-
- if ( flPredictedDot < 0.8f )
- return COND_NOT_FACING_ATTACK;
-
- if ( ( flPredictedDist > ( GetAbsVelocity().Length() * 0.5f) ) && ( flDist > 128.0f ) )
- return COND_TOO_FAR_TO_ATTACK;
-
- return COND_CAN_MELEE_ATTACK1;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEvent -
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::HandleAnimEvent( animevent_t *pEvent )
-{
- switch ( pEvent->event )
- {
- case ICH_AE_BITE:
- Bite();
- break;
-
- case ICH_AE_BITE_START:
- {
- EmitSound( "NPC_Ichthyosaur.AttackGrowl" );
- }
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::Bite( void )
-{
- //Don't allow another bite too soon
- if ( m_flNextBiteTime > gpGlobals->curtime )
- return;
-
- CBaseEntity *pHurt;
-
- //FIXME: E3 HACK - Always damage bullseyes if we're scripted to hit them
- if ( ( GetEnemy() != NULL ) && ( GetEnemy()->Classify() == CLASS_BULLSEYE ) )
- {
- pHurt = GetEnemy();
- }
- else
- {
- pHurt = CheckTraceHullAttack( 108, Vector(-32,-32,-32), Vector(32,32,32), 0, DMG_CLUB );
- }
-
- //Hit something
- if ( pHurt != NULL )
- {
- CTakeDamageInfo info( this, this, sk_ichthyosaur_melee_dmg.GetInt(), DMG_CLUB );
-
- if ( pHurt->IsPlayer() )
- {
- CBasePlayer *pPlayer = ToBasePlayer( pHurt );
-
- if ( pPlayer )
- {
- if ( ( ( m_flHoldTime < gpGlobals->curtime ) && ( pPlayer->m_iHealth < (pPlayer->m_iMaxHealth*0.5f)) ) || ( pPlayer->GetWaterLevel() < 1 ) )
- {
- //EnsnareVictim( pHurt );
- }
- else
- {
- info.SetDamage( sk_ichthyosaur_melee_dmg.GetInt() * 3 );
- }
- CalculateMeleeDamageForce( &info, GetAbsVelocity(), pHurt->GetAbsOrigin() );
- pHurt->TakeDamage( info );
-
- color32 red = {64, 0, 0, 255};
- UTIL_ScreenFade( pPlayer, red, 0.5, 0, FFADE_IN );
-
- //Disorient the player
- QAngle angles = pPlayer->GetLocalAngles();
-
- angles.x += random->RandomInt( 60, 25 );
- angles.y += random->RandomInt( 60, 25 );
- angles.z = 0.0f;
-
- pPlayer->SetLocalAngles( angles );
-
- pPlayer->SnapEyeAngles( angles );
- }
- }
- else
- {
- CalculateMeleeDamageForce( &info, GetAbsVelocity(), pHurt->GetAbsOrigin() );
- pHurt->TakeDamage( info );
- }
-
- m_flNextBiteTime = gpGlobals->curtime + random->RandomFloat( 2.0f, 4.0f );
-
- //Bubbles!
- UTIL_Bubbles( pHurt->GetAbsOrigin()+Vector(-32.0f,-32.0f,-32.0f), pHurt->GetAbsOrigin()+Vector(32.0f,32.0f,0.0f), random->RandomInt( 16, 32 ) );
-
- // Play a random attack hit sound
- EmitSound( "NPC_Ichthyosaur.Bite" );
-
- if ( GetActivity() == ACT_MELEE_ATTACK1 )
- {
- SetActivity( (Activity) ACT_ICH_BITE_HIT );
- }
-
- return;
- }
-
- //Play the miss animation and sound
- if ( GetActivity() == ACT_MELEE_ATTACK1 )
- {
- SetActivity( (Activity) ACT_ICH_BITE_MISS );
- }
-
- //Miss sound
- EmitSound( "NPC_Ichthyosaur.BiteMiss" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CNPC_Ichthyosaur::Beached( void )
-{
- trace_t tr;
- Vector testPos;
-
- testPos = GetAbsOrigin() - Vector( 0, 0, ICH_DEPTH_PREFERENCE );
-
- AI_TraceHull( GetAbsOrigin(), testPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
-
- return ( tr.fraction < 1.0f );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::PrescheduleThink( void )
-{
- BaseClass::PrescheduleThink();
-
- //Ambient sounds
- /*
- if ( random->RandomInt( 0, 20 ) == 10 )
- {
- if ( random->RandomInt( 0, 1 ) )
- {
- ENVELOPE_CONTROLLER.SoundChangeVolume( m_pSwimSound, random->RandomFloat( 0.0f, 0.5f ), 1.0f );
- }
- else
- {
- ENVELOPE_CONTROLLER.SoundChangeVolume( m_pVoiceSound, random->RandomFloat( 0.0f, 0.5f ), 1.0f );
- }
- }
- */
-
- //Pings
- if ( m_flNextPingTime < gpGlobals->curtime )
- {
- m_flNextPingTime = gpGlobals->curtime + random->RandomFloat( 3.0f, 8.0f );
- }
-
- //Growls
- if ( ( m_NPCState == NPC_STATE_COMBAT || m_NPCState == NPC_STATE_ALERT ) && ( m_flNextGrowlTime < gpGlobals->curtime ) )
- {
- m_flNextGrowlTime = gpGlobals->curtime + random->RandomFloat( 2.0f, 6.0f );
- }
-
- //Randomly emit bubbles
- if ( random->RandomInt( 0, 10 ) == 0 )
- {
- UTIL_Bubbles( GetAbsOrigin()+(GetHullMins()*0.5f), GetAbsOrigin()+(GetHullMaxs()*0.5f), 1 );
- }
-
- //Check our water level
- if ( GetWaterLevel() != 3 )
- {
- if ( GetWaterLevel() < 2 )
- {
- DevMsg( 2, "Came out of water\n" );
-
- if ( Beached() )
- {
- SetSchedule( SCHED_ICH_THRASH );
-
- Vector vecNewVelocity = GetAbsVelocity();
- vecNewVelocity[2] = 8.0f;
- SetAbsVelocity( vecNewVelocity );
- }
- }
- else
- {
- //TODO: Wake effects
- }
- }
-
- //If we have a victim, update them
- if ( m_pVictim != NULL )
- {
- //See if it's time to release the victim
- if ( m_flHoldTime < gpGlobals->curtime )
- {
- ReleaseVictim();
- return;
- }
-
- Bite();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pevInflictor -
-// *pAttacker -
-// flDamage -
-// bitsDamageType -
-//-----------------------------------------------------------------------------
-int CNPC_Ichthyosaur::OnTakeDamage_Alive( const CTakeDamageInfo &info )
-{
- //Release the player if he's struck us while being held
- if ( m_flHoldTime > gpGlobals->curtime )
- {
- ReleaseVictim();
-
- //Don't give them as much time to flee
- m_flNextBiteTime = gpGlobals->curtime + 2.0f;
-
- SetSchedule( SCHED_ICH_THRASH );
- }
-
- return BaseClass::OnTakeDamage_Alive( info );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::EnsnareVictim( CBaseEntity *pVictim )
-{
- CBaseCombatCharacter* pBCC = (CBaseCombatCharacter *) pVictim;
-
- if ( pBCC && pBCC->DispatchInteraction( g_interactionBarnacleVictimGrab, NULL, this ) )
- {
- if ( pVictim->IsPlayer() )
- {
- CBasePlayer *pPlayer = dynamic_cast< CBasePlayer * >((CBaseEntity *) pVictim);
-
- if ( pPlayer )
- {
- m_flHoldTime = MAX( gpGlobals->curtime+3.0f, pPlayer->PlayerDrownTime() - 2.0f );
- }
- }
- else
- {
- m_flHoldTime = gpGlobals->curtime + 4.0f;
- }
-
- m_pVictim = pVictim;
- m_pVictim->AddSolidFlags( FSOLID_NOT_SOLID );
-
- SetSchedule( SCHED_ICH_DROWN_VICTIM );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::ReleaseVictim( void )
-{
- CBaseCombatCharacter *pBCC = (CBaseCombatCharacter *) m_pVictim;
-
- pBCC->DispatchInteraction( g_interactionBarnacleVictimReleased, NULL, this );
-
- m_pVictim->RemoveSolidFlags( FSOLID_NOT_SOLID );
-
- m_pVictim = NULL;
- m_flNextBiteTime = gpGlobals->curtime + 8.0f;
- m_flHoldTime = gpGlobals->curtime - 0.1f;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : speed to move at
-//-----------------------------------------------------------------------------
-float CNPC_Ichthyosaur::GetGroundSpeed( void )
-{
- if ( m_flHoldTime > gpGlobals->curtime )
- return ICH_SWIM_SPEED_WALK/2.0f;
-
- if ( GetIdealActivity() == ACT_WALK )
- return ICH_SWIM_SPEED_WALK;
-
- if ( GetIdealActivity() == ACT_ICH_THRASH )
- return ICH_SWIM_SPEED_WALK;
-
- return ICH_SWIM_SPEED_RUN;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : type -
-// Output : int
-//-----------------------------------------------------------------------------
-int CNPC_Ichthyosaur::TranslateSchedule( int type )
-{
- if ( type == SCHED_CHASE_ENEMY ) return SCHED_ICH_CHASE_ENEMY;
- //if ( type == SCHED_IDLE_STAND ) return SCHED_PATROL_WALK;
- if ( type == SCHED_PATROL_RUN ) return SCHED_ICH_PATROL_RUN;
- if ( type == SCHED_PATROL_WALK ) return SCHED_ICH_PATROL_WALK;
- if ( type == SCHED_MELEE_ATTACK1 ) return SCHED_ICH_MELEE_ATTACK1;
-
- return BaseClass::TranslateSchedule( type );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pTask -
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::StartTask( const Task_t *pTask )
-{
- switch ( pTask->iTask )
- {
- case TASK_ICH_THRASH_PATH:
- GetNavigator()->SetMovementActivity( (Activity) ACT_ICH_THRASH );
- TaskComplete();
- break;
-
- case TASK_ICH_GET_PATH_TO_RANDOM_NODE:
- {
- if ( GetEnemy() == NULL || !GetNavigator()->SetRandomGoal( GetEnemy()->GetLocalOrigin(), pTask->flTaskData ) )
- {
- if (!GetNavigator()->SetRandomGoal( pTask->flTaskData ) )
- {
- TaskFail(FAIL_NO_REACHABLE_NODE);
- return;
- }
- }
-
- TaskComplete();
- }
- break;
-
- case TASK_ICH_GET_PATH_TO_DROWN_NODE:
- {
- Vector drownPos = GetLocalOrigin() - Vector( 0, 0, pTask->flTaskData );
-
- if ( GetNavigator()->SetGoal( drownPos, AIN_CLEAR_TARGET ) == false )
- {
- TaskFail( FAIL_NO_ROUTE );
- return;
- }
-
- TaskComplete();
- }
- break;
-
- case TASK_MELEE_ATTACK1:
- m_flPlaybackRate = 1.0f;
- BaseClass::StartTask(pTask);
- break;
-
- default:
- BaseClass::StartTask(pTask);
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pTask -
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::RunTask( const Task_t *pTask )
-{
- switch ( pTask->iTask )
- {
- case TASK_ICH_GET_PATH_TO_RANDOM_NODE:
- return;
- break;
-
- case TASK_ICH_GET_PATH_TO_DROWN_NODE:
- return;
- break;
-
- default:
- BaseClass::RunTask(pTask);
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : desired yaw speed
-//-----------------------------------------------------------------------------
-float CNPC_Ichthyosaur::MaxYawSpeed( void )
-{
- if ( GetIdealActivity() == ACT_MELEE_ATTACK1 )
- return 16.0f;
-
- if ( GetIdealActivity() == ACT_ICH_THRASH )
- return 16.0f;
-
- //Ramp up the yaw speed as we increase our speed
- return ICH_MIN_TURN_SPEED + ( (ICH_MAX_TURN_SPEED-ICH_MIN_TURN_SPEED) * ( fabs(GetAbsVelocity().Length()) / ICH_SWIM_SPEED_RUN ) );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEnemy -
-// &chasePosition -
-// &tolerance -
-//-----------------------------------------------------------------------------
-void CNPC_Ichthyosaur::TranslateNavGoal( CBaseEntity *pEnemy, Vector &chasePosition )
-{
- Vector offset = pEnemy->EyePosition() - pEnemy->GetAbsOrigin();
- chasePosition += offset;
-}
-
-float CNPC_Ichthyosaur::GetDefaultNavGoalTolerance()
-{
- return GetHullWidth()*2.0f;
-}
-
-
-//-----------------------------------------------------------------------------
-//
-// Schedules
-//
-//-----------------------------------------------------------------------------
-
-//==================================================
-// SCHED_ICH_CHASE_ENEMY
-//==================================================
-
-AI_DEFINE_SCHEDULE
-(
- SCHED_ICH_CHASE_ENEMY,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ICH_PATROL_WALK"
- " TASK_SET_TOLERANCE_DISTANCE 64"
- " TASK_SET_GOAL GOAL:ENEMY"
- " TASK_GET_PATH_TO_GOAL PATH:TRAVEL"
- " TASK_RUN_PATH 0"
- " TASK_WAIT_FOR_MOVEMENT 0"
- ""
- " Interrupts"
- " COND_NEW_ENEMY"
- " COND_ENEMY_DEAD"
- " COND_ENEMY_UNREACHABLE"
- " COND_CAN_MELEE_ATTACK1"
- " COND_TOO_CLOSE_TO_ATTACK"
- " COND_LOST_ENEMY"
- " COND_TASK_FAILED"
-);
-
-//==================================================
-// SCHED_ICH_PATROL_RUN
-//==================================================
-
-AI_DEFINE_SCHEDULE
-(
- SCHED_ICH_PATROL_RUN,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE"
- " TASK_SET_TOLERANCE_DISTANCE 64"
- " TASK_SET_ROUTE_SEARCH_TIME 4"
- " TASK_ICH_GET_PATH_TO_RANDOM_NODE 200"
- " TASK_RUN_PATH 0"
- " TASK_WAIT_FOR_MOVEMENT 0"
- ""
- " Interrupts"
- " COND_CAN_MELEE_ATTACK1"
- " COND_GIVE_WAY"
- " COND_NEW_ENEMY"
- " COND_LIGHT_DAMAGE"
- " COND_HEAVY_DAMAGE"
-);
-
-//==================================================
-// SCHED_ICH_PATROL_WALK
-//==================================================
-
-AI_DEFINE_SCHEDULE
-(
- SCHED_ICH_PATROL_WALK,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE"
- " TASK_SET_TOLERANCE_DISTANCE 64"
- " TASK_SET_ROUTE_SEARCH_TIME 4"
- " TASK_ICH_GET_PATH_TO_RANDOM_NODE 200"
- " TASK_WALK_PATH 0"
- " TASK_WAIT_FOR_MOVEMENT 0"
- ""
- " Interrupts"
- " COND_CAN_MELEE_ATTACK1"
- " COND_GIVE_WAY"
- " COND_NEW_ENEMY"
- " COND_LIGHT_DAMAGE"
- " COND_HEAVY_DAMAGE"
-);
-
-//==================================================
-// SCHED_ICH_DROWN_VICTIM
-//==================================================
-
-AI_DEFINE_SCHEDULE
-(
- SCHED_ICH_DROWN_VICTIM,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE"
- " TASK_SET_TOLERANCE_DISTANCE 64"
- " TASK_SET_ROUTE_SEARCH_TIME 4"
- " TASK_ICH_GET_PATH_TO_DROWN_NODE 256"
- " TASK_WALK_PATH 0"
- " TASK_WAIT_FOR_MOVEMENT 0"
- ""
- " Interrupts"
- " COND_NEW_ENEMY"
- " COND_LIGHT_DAMAGE"
- " COND_HEAVY_DAMAGE"
-);
-
-//=========================================================
-// SCHED_ICH_MELEE_ATTACK1
-//=========================================================
-
-AI_DEFINE_SCHEDULE
-(
- SCHED_ICH_MELEE_ATTACK1,
-
- " Tasks"
- " TASK_ANNOUNCE_ATTACK 1"
- " TASK_MELEE_ATTACK1 0"
- ""
- " Interrupts"
- " COND_NEW_ENEMY"
- " COND_ENEMY_DEAD"
- " COND_ENEMY_OCCLUDED"
-);
-
-//==================================================
-// SCHED_ICH_THRASH
-//==================================================
-
-AI_DEFINE_SCHEDULE
-(
- SCHED_ICH_THRASH,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE"
- " TASK_SET_TOLERANCE_DISTANCE 64"
- " TASK_SET_ROUTE_SEARCH_TIME 4"
- " TASK_ICH_GET_PATH_TO_RANDOM_NODE 64"
- " TASK_ICH_THRASH_PATH 0"
- " TASK_WAIT_FOR_MOVEMENT 0"
- ""
- " Interrupts"
-);
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Ichthyosaur - buh bum... buh bum... +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "ai_basenpc.h" +#include "ai_task.h" +#include "ai_default.h" +#include "ai_schedule.h" +#include "ai_hull.h" +#include "ai_interactions.h" +#include "ai_navigator.h" +#include "ai_motor.h" +#include "activitylist.h" +#include "game.h" +#include "npcevent.h" +#include "player.h" +#include "entitylist.h" +#include "soundenvelope.h" +#include "shake.h" +#include "ndebugoverlay.h" +#include "vstdlib/random.h" +#include "engine/IEngineSound.h" +#include "movevars_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +ConVar sk_ichthyosaur_health( "sk_ichthyosaur_health", "0" ); +ConVar sk_ichthyosaur_melee_dmg( "sk_ichthyosaur_melee_dmg", "0" ); + +#define ICHTHYOSAUR_MODEL "models/ichthyosaur.mdl" + +#define ICH_HEIGHT_PREFERENCE 16.0f +#define ICH_DEPTH_PREFERENCE 8.0f + +#define ICH_WAYPOINT_DISTANCE 64.0f + +#define ICH_AE_BITE 11 +#define ICH_AE_BITE_START 12 + +#define ICH_SWIM_SPEED_WALK 150 +#define ICH_SWIM_SPEED_RUN 500 + +#define ICH_MIN_TURN_SPEED 4.0f +#define ICH_MAX_TURN_SPEED 30.0f + +#define ENVELOPE_CONTROLLER (CSoundEnvelopeController::GetController()) + +#define FEELER_COLLISION 0 +#define FEELER_COLLISION_VISUALIZE (FEELER_COLLISION&&0) + +enum IchthyosaurMoveType_t +{ + ICH_MOVETYPE_SEEK = 0, // Fly through the target without stopping. + ICH_MOVETYPE_ARRIVE // Slow down and stop at target. +}; + +// +// CNPC_Ichthyosaur +// + +class CNPC_Ichthyosaur : public CAI_BaseNPC +{ +public: + DECLARE_CLASS( CNPC_Ichthyosaur, CAI_BaseNPC ); + DECLARE_DATADESC(); + + CNPC_Ichthyosaur( void ) {} + + int SelectSchedule( void ); + int MeleeAttack1Conditions( float flDot, float flDist ); + int OnTakeDamage_Alive( const CTakeDamageInfo &info ); + int TranslateSchedule( int type ); + + void Precache( void ); + void Spawn( void ); + void MoveFlyExecute( CBaseEntity *pTargetEnt, const Vector & vecDir, float flDistance, float flInterval ); + void HandleAnimEvent( animevent_t *pEvent ); + void PrescheduleThink( void ); + bool OverrideMove( float flInterval ); + void StartTask( const Task_t *pTask ); + void RunTask( const Task_t *pTask ); + void TranslateNavGoal( CBaseEntity *pEnemy, Vector &chasePosition ); + float GetDefaultNavGoalTolerance(); + + float MaxYawSpeed( void ); + + Class_T Classify( void ) { return CLASS_ANTLION; } //FIXME: No classification for various wildlife? + + bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ); + +private: + + bool SteerAvoidObstacles( Vector &Steer, const Vector &Velocity, const Vector &Forward, const Vector &Right, const Vector &Up ); + bool Beached( void ); + + void DoMovement( float flInterval, const Vector &MoveTarget, int eMoveType ); + void SteerArrive( Vector &Steer, const Vector &Target ); + void SteerSeek( Vector &Steer, const Vector &Target ); + void ClampSteer( Vector &SteerAbs, Vector &SteerRel, Vector &forward, Vector &right, Vector &up ); + void AddSwimNoise( Vector *velocity ); + + void Bite( void ); + void EnsnareVictim( CBaseEntity *pVictim ); + void ReleaseVictim( void ); + void DragVictim( float moveDist ); + + void SetPoses( Vector moveRel, float speed ); + + //void IchTouch( CBaseEntity *pOther ); + + float GetGroundSpeed( void ); + +#if FEELER_COLLISION + Vector DoProbe( const Vector &Probe ); + Vector m_LastSteer; +#endif + + CBaseEntity *m_pVictim; + + static const Vector m_vecAccelerationMax; + static const Vector m_vecAccelerationMin; + + Vector m_vecLastMoveTarget; + + float m_flNextBiteTime; + float m_flHoldTime; + float m_flSwimSpeed; + float m_flTailYaw; + float m_flTailPitch; + + float m_flNextPingTime; + float m_flNextGrowlTime; + + bool m_bHasMoveTarget; + bool m_bIgnoreSurface; + + //CSoundPatch *m_pSwimSound; + //CSoundPatch *m_pVoiceSound; + + DEFINE_CUSTOM_AI; +}; + +//Acceleration definitions +const Vector CNPC_Ichthyosaur::m_vecAccelerationMax = Vector( 256, 1024, 512 ); +const Vector CNPC_Ichthyosaur::m_vecAccelerationMin = Vector( -256, -1024, -512 ); + +//Data description +BEGIN_DATADESC( CNPC_Ichthyosaur ) + +// Silence classcheck +// DEFINE_FIELD( m_LastSteer, FIELD_VECTOR ), + + DEFINE_FIELD( m_pVictim, FIELD_CLASSPTR ), + DEFINE_FIELD( m_vecLastMoveTarget, FIELD_VECTOR ), + DEFINE_FIELD( m_flNextBiteTime, FIELD_FLOAT ), + DEFINE_FIELD( m_flHoldTime, FIELD_FLOAT ), + DEFINE_FIELD( m_flSwimSpeed, FIELD_FLOAT ), + DEFINE_FIELD( m_flTailYaw, FIELD_FLOAT ), + DEFINE_FIELD( m_flTailPitch, FIELD_FLOAT ), + DEFINE_FIELD( m_flNextPingTime, FIELD_FLOAT ), + DEFINE_FIELD( m_flNextGrowlTime, FIELD_FLOAT ), + DEFINE_FIELD( m_bHasMoveTarget, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bIgnoreSurface, FIELD_BOOLEAN ), + + //DEFINE_FUNCTION( IchTouch ), + +END_DATADESC() + +//Schedules +enum IchSchedules +{ + SCHED_ICH_CHASE_ENEMY = LAST_SHARED_SCHEDULE, + SCHED_ICH_PATROL_RUN, + SCHED_ICH_PATROL_WALK, + SCHED_ICH_DROWN_VICTIM, + SCHED_ICH_MELEE_ATTACK1, + SCHED_ICH_THRASH, +}; + +//Tasks +enum IchTasks +{ + TASK_ICH_GET_PATH_TO_RANDOM_NODE = LAST_SHARED_TASK, + TASK_ICH_GET_PATH_TO_DROWN_NODE, + TASK_ICH_THRASH_PATH, +}; + +//Activities +int ACT_ICH_THRASH; +int ACT_ICH_BITE_HIT; +int ACT_ICH_BITE_MISS; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::InitCustomSchedules( void ) +{ + INIT_CUSTOM_AI( CNPC_Ichthyosaur ); + + //Interaction REGISTER_INTERACTION( g_interactionAntlionAttacked ); + + //Schedules + ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_CHASE_ENEMY ); + ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_PATROL_RUN ); + ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_PATROL_WALK ); + ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_DROWN_VICTIM ); + ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_MELEE_ATTACK1 ); + ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_THRASH ); + + //Tasks + ADD_CUSTOM_TASK( CNPC_Ichthyosaur, TASK_ICH_GET_PATH_TO_RANDOM_NODE ); + ADD_CUSTOM_TASK( CNPC_Ichthyosaur, TASK_ICH_GET_PATH_TO_DROWN_NODE ); + ADD_CUSTOM_TASK( CNPC_Ichthyosaur, TASK_ICH_THRASH_PATH ); + + //Conditions ADD_CUSTOM_CONDITION( CNPC_CombineGuard, COND_ANTLIONGRUB_HEARD_SQUEAL ); + + //Activities + ADD_CUSTOM_ACTIVITY( CNPC_Ichthyosaur, ACT_ICH_THRASH ); + ADD_CUSTOM_ACTIVITY( CNPC_Ichthyosaur, ACT_ICH_BITE_HIT ); + ADD_CUSTOM_ACTIVITY( CNPC_Ichthyosaur, ACT_ICH_BITE_MISS ); + + AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_CHASE_ENEMY ); + AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_PATROL_RUN ); + AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_PATROL_WALK ); + AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_DROWN_VICTIM ); + AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_MELEE_ATTACK1 ); + AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_THRASH ); +} + +LINK_ENTITY_TO_CLASS( npc_ichthyosaur, CNPC_Ichthyosaur ); +IMPLEMENT_CUSTOM_AI( npc_ichthyosaur, CNPC_Ichthyosaur ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::Precache( void ) +{ + PrecacheModel( ICHTHYOSAUR_MODEL ); + + PrecacheScriptSound( "NPC_Ichthyosaur.Bite" ); + PrecacheScriptSound( "NPC_Ichthyosaur.BiteMiss" ); + PrecacheScriptSound( "NPC_Ichthyosaur.AttackGrowl" ); + + BaseClass::Precache(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::Spawn( void ) +{ + Precache(); + + SetModel( ICHTHYOSAUR_MODEL ); + + SetHullType(HULL_LARGE_CENTERED); + SetHullSizeNormal(); + SetDefaultEyeOffset(); + + SetNavType( NAV_FLY ); + m_NPCState = NPC_STATE_NONE; + SetBloodColor( BLOOD_COLOR_RED ); + m_iHealth = sk_ichthyosaur_health.GetFloat(); + m_iMaxHealth = m_iHealth; + m_flFieldOfView = -0.707; // 270 degrees + SetDistLook( 1024 ); + + SetSolid( SOLID_BBOX ); + AddSolidFlags( FSOLID_NOT_STANDABLE ); + SetMoveType( MOVETYPE_STEP ); + AddFlag( FL_FLY | FL_STEPMOVEMENT ); + + m_flGroundSpeed = ICH_SWIM_SPEED_RUN; + + m_bIgnoreSurface = false; + + m_flSwimSpeed = 0.0f; + m_flTailYaw = 0.0f; + m_flTailPitch = 0.0f; + + m_flNextBiteTime = gpGlobals->curtime; + m_flHoldTime = gpGlobals->curtime; + m_flNextPingTime = gpGlobals->curtime; + m_flNextGrowlTime = gpGlobals->curtime; + +#if FEELER_COLLISION + + Vector forward; + + GetVectors( &forward, NULL, NULL ); + + m_vecCurrentVelocity = forward * m_flGroundSpeed; + +#endif + + //SetTouch( IchTouch ); + + CapabilitiesClear(); + CapabilitiesAdd( bits_CAP_MOVE_FLY | bits_CAP_INNATE_MELEE_ATTACK1 ); + + NPCInit(); + + //m_pSwimSound = ENVELOPE_CONTROLLER.SoundCreate( edict(), CHAN_BODY, "xxxCONVERTTOGAMESOUNDS!!!npc/ichthyosaur/ich_amb1wav", ATTN_NORM ); + //m_pVoiceSound = ENVELOPE_CONTROLLER.SoundCreate( edict(), CHAN_VOICE, "xxxCONVERTTOGAMESOUNDS!!!npc/ichthyosaur/water_breathwav", ATTN_IDLE ); + + //ENVELOPE_CONTROLLER.Play( m_pSwimSound, 1.0f, 100 ); + //ENVELOPE_CONTROLLER.Play( m_pVoiceSound,1.0f, 100 ); + + BaseClass::Spawn(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pOther - +//----------------------------------------------------------------------------- +/* +void CNPC_Ichthyosaur::IchTouch( CBaseEntity *pOther ) +{ +} +*/ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CNPC_Ichthyosaur::SelectSchedule( void ) +{ + if ( m_NPCState == NPC_STATE_COMBAT ) + { + if ( m_flHoldTime > gpGlobals->curtime ) + return SCHED_ICH_DROWN_VICTIM; + + if ( m_flNextBiteTime > gpGlobals->curtime ) + return SCHED_PATROL_RUN; + + if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) ) + return SCHED_MELEE_ATTACK1; + + return SCHED_CHASE_ENEMY; + } + + return BaseClass::SelectSchedule(); +} + +//----------------------------------------------------------------------------- +// Purpose: Handles movement towards the last move target. +// Input : flInterval - +//----------------------------------------------------------------------------- +bool CNPC_Ichthyosaur::OverrideMove( float flInterval ) +{ + m_flGroundSpeed = GetGroundSpeed(); + + if ( m_bHasMoveTarget ) + { + DoMovement( flInterval, m_vecLastMoveTarget, ICH_MOVETYPE_ARRIVE ); + } + else + { + DoMovement( flInterval, GetLocalOrigin(), ICH_MOVETYPE_ARRIVE ); + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &probe - +// Output : Vector +//----------------------------------------------------------------------------- +#if FEELER_COLLISION + +Vector CNPC_Ichthyosaur::DoProbe( const Vector &probe ) +{ + trace_t tr; + float fraction = 1.0f; + bool collided = false; + Vector normal = Vector( 0, 0, -1 ); + + float waterLevel = UTIL_WaterLevel( GetAbsOrigin(), GetAbsOrigin().z, GetAbsOrigin().z+150 ); + + waterLevel -= GetAbsOrigin().z; + waterLevel /= 150; + + if ( waterLevel < 1.0f ) + { + collided = true; + fraction = waterLevel; + } + + AI_TraceHull( GetAbsOrigin(), probe, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); + + if ( ( collided == false ) || ( tr.fraction < fraction ) ) + { + fraction = tr.fraction; + normal = tr.plane.normal; + } + + if ( ( fraction < 1.0f ) && ( GetEnemy() == NULL || tr.u.ent != GetEnemy()->pev ) ) + { +#if FEELER_COLLISION_VISUALIZE + NDebugOverlay::Line( GetLocalOrigin(), probe, 255, 0, 0, false, 0.1f ); +#endif + + Vector probeDir = probe - GetLocalOrigin(); + + Vector normalToProbeAndWallNormal = probeDir.Cross( normal ); + Vector steeringVector = normalToProbeAndWallNormal.Cross( probeDir ); + + Vector velDir = GetAbsVelocity(); + VectorNormalize( velDir ); + + float steeringForce = m_flGroundSpeed * ( 1.0f - fraction ) * normal.Dot( velDir ); + + if ( steeringForce < 0.0f ) + { + steeringForce = -steeringForce; + } + + velDir = steeringVector; + VectorNormalize( velDir ); + + steeringVector = steeringForce * velDir; + + return steeringVector; + } + +#if FEELER_COLLISION_VISUALIZE + NDebugOverlay::Line( GetLocalOrigin(), probe, 0, 255, 0, false, 0.1f ); +#endif + + return Vector( 0.0f, 0.0f, 0.0f ); +} + +#endif + +//----------------------------------------------------------------------------- +// Purpose: Move the victim of a drag along with us +// Input : moveDist - our amount of travel +//----------------------------------------------------------------------------- + +#define DRAG_OFFSET 50.0f + +void CNPC_Ichthyosaur::DragVictim( float moveDist ) +{ + Vector mins, maxs; + float width; + + mins = WorldAlignMins(); + maxs = WorldAlignMaxs(); + width = ( maxs.y - mins.y ) * 0.5f; + + Vector forward, up; + GetVectors( &forward, NULL, &up ); + + Vector newPos = GetAbsOrigin() + ( (forward+(up*0.25f)) * ( moveDist + width + DRAG_OFFSET ) ); + + trace_t tr; + AI_TraceEntity( this, m_pVictim->GetAbsOrigin(), newPos, MASK_NPCSOLID, &tr ); + + if ( ( tr.fraction == 1.0f ) && ( tr.m_pEnt != this ) ) + { + UTIL_SetOrigin( m_pVictim, tr.endpos ); + } + else + { + ReleaseVictim(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Determines the pose parameters for the bending of the body and tail speed +// Input : moveRel - the dot products for the deviation off of each direction (f,r,u) +// speed - speed of the fish +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::SetPoses( Vector moveRel, float speed ) +{ + float movePerc, moveBase; + + //Find out how fast we're moving in our animations boundaries + if ( GetIdealActivity() == ACT_WALK ) + { + moveBase = 0.5f; + movePerc = moveBase * ( speed / ICH_SWIM_SPEED_WALK ); + } + else + { + moveBase = 1.0f; + movePerc = moveBase * ( speed / ICH_SWIM_SPEED_RUN ); + } + + Vector tailPosition; + float flSwimSpeed = movePerc; + + //Forward deviation + if ( moveRel.x > 0 ) + { + flSwimSpeed *= moveBase + (( moveRel.x / m_vecAccelerationMax.x )*moveBase); + } + else if ( moveRel.x < 0 ) + { + flSwimSpeed *= moveBase - (( moveRel.x / m_vecAccelerationMin.x )*moveBase); + } + + //Vertical deviation + if ( moveRel.z > 0 ) + { + tailPosition[PITCH] = -90.0f * ( moveRel.z / m_vecAccelerationMax.z ); + } + else if ( moveRel.z < 0 ) + { + tailPosition[PITCH] = 90.0f * ( moveRel.z / m_vecAccelerationMin.z ); + } + else + { + tailPosition[PITCH] = 0.0f; + } + + //Lateral deviation + if ( moveRel.y > 0 ) + { + tailPosition[ROLL] = 25 * moveRel.y / m_vecAccelerationMax.y; + tailPosition[YAW] = -1.0f * moveRel.y / m_vecAccelerationMax.y; + } + else if ( moveRel.y < 0 ) + { + tailPosition[ROLL] = -25 * moveRel.y / m_vecAccelerationMin.y; + tailPosition[YAW] = moveRel.y / m_vecAccelerationMin.y; + } + else + { + tailPosition[ROLL] = 0.0f; + tailPosition[YAW] = 0.0f; + } + + //Clamp + flSwimSpeed = clamp( flSwimSpeed, 0.25f, 1.0f ); + tailPosition[YAW] = clamp( tailPosition[YAW], -90.0f, 90.0f ); + tailPosition[PITCH] = clamp( tailPosition[PITCH], -90.0f, 90.0f ); + + //Blend + m_flTailYaw = ( m_flTailYaw * 0.8f ) + ( tailPosition[YAW] * 0.2f ); + m_flTailPitch = ( m_flTailPitch * 0.8f ) + ( tailPosition[PITCH] * 0.2f ); + m_flSwimSpeed = ( m_flSwimSpeed * 0.8f ) + ( flSwimSpeed * 0.2f ); + + //Pose the body + SetPoseParameter( 0, m_flSwimSpeed ); + SetPoseParameter( 1, m_flTailYaw ); + SetPoseParameter( 2, m_flTailPitch ); + + //FIXME: Until the sequence info is reset properly after SetPoseParameter + if ( ( GetActivity() == ACT_RUN ) || ( GetActivity() == ACT_WALK ) ) + { + ResetSequenceInfo(); + } + + //Face our current velocity + GetMotor()->SetIdealYawAndUpdate( UTIL_AngleMod( CalcIdealYaw( GetAbsOrigin() + GetAbsVelocity() ) ), AI_KEEP_YAW_SPEED ); + + float pitch = 0.0f; + + if ( speed != 0.0f ) + { + pitch = -RAD2DEG( asin( GetAbsVelocity().z / speed ) ); + } + + //FIXME: Framerate dependant + QAngle angles = GetLocalAngles(); + + angles.x = (angles.x * 0.8f) + (pitch * 0.2f); + angles.z = (angles.z * 0.9f) + (tailPosition[ROLL] * 0.1f); + + SetLocalAngles( angles ); +} + +#define LATERAL_NOISE_MAX 2.0f +#define LATERAL_NOISE_FREQ 1.0f + +#define VERTICAL_NOISE_MAX 2.0f +#define VERTICAL_NOISE_FREQ 1.0f + +//----------------------------------------------------------------------------- +// Purpose: +// Input : velocity - +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::AddSwimNoise( Vector *velocity ) +{ + Vector right, up; + + GetVectors( NULL, &right, &up ); + + float lNoise, vNoise; + + lNoise = LATERAL_NOISE_MAX * sin( gpGlobals->curtime * LATERAL_NOISE_FREQ ); + vNoise = VERTICAL_NOISE_MAX * sin( gpGlobals->curtime * VERTICAL_NOISE_FREQ ); + + (*velocity) += ( right * lNoise ) + ( up * vNoise ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flInterval - +// &m_LastMoveTarget - +// eMoveType - +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::DoMovement( float flInterval, const Vector &MoveTarget, int eMoveType ) +{ + // dvs: something is setting this bit, causing us to stop moving and get stuck that way + Forget( bits_MEMORY_TURNING ); + + Vector Steer, SteerAvoid, SteerRel; + Vector forward, right, up; + + //Get our orientation vectors. + GetVectors( &forward, &right, &up); + + if ( ( GetActivity() == ACT_MELEE_ATTACK1 ) && ( GetEnemy() != NULL ) ) + { + SteerSeek( Steer, GetEnemy()->GetAbsOrigin() ); + } + else + { + //If we are approaching our goal, use an arrival steering mechanism. + if ( eMoveType == ICH_MOVETYPE_ARRIVE ) + { + SteerArrive( Steer, MoveTarget ); + } + else + { + //Otherwise use a seek steering mechanism. + SteerSeek( Steer, MoveTarget ); + } + } + +#if FEELER_COLLISION + + Vector f, u, l, r, d; + + float probeLength = GetAbsVelocity().Length(); + + if ( probeLength < 150 ) + probeLength = 150; + + if ( probeLength > 500 ) + probeLength = 500; + + f = DoProbe( GetLocalOrigin() + (probeLength * forward) ); + r = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward+right)) ); + l = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward-right)) ); + u = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward+up)) ); + d = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward-up)) ); + + SteerAvoid = f+r+l+u+d; + + //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+SteerAvoid, 255, 255, 0, false, 0.1f ); + + if ( SteerAvoid.LengthSqr() ) + { + Steer = (SteerAvoid*0.5f); + } + + m_vecVelocity = m_vecVelocity + (Steer*0.5f); + + VectorNormalize( m_vecVelocity ); + + SteerRel.x = forward.Dot( m_vecVelocity ); + SteerRel.y = right.Dot( m_vecVelocity ); + SteerRel.z = up.Dot( m_vecVelocity ); + + m_vecVelocity *= m_flGroundSpeed; + +#else + + //See if we need to avoid any obstacles. + if ( SteerAvoidObstacles( SteerAvoid, GetAbsVelocity(), forward, right, up ) ) + { + //Take the avoidance vector + Steer = SteerAvoid; + } + + //Clamp our ideal steering vector to within our physical limitations. + ClampSteer( Steer, SteerRel, forward, right, up ); + + ApplyAbsVelocityImpulse( Steer * flInterval ); + +#endif + + Vector vecNewVelocity = GetAbsVelocity(); + float flLength = vecNewVelocity.Length(); + + //Clamp our final speed + if ( flLength > m_flGroundSpeed ) + { + vecNewVelocity *= ( m_flGroundSpeed / flLength ); + flLength = m_flGroundSpeed; + } + + Vector workVelocity = vecNewVelocity; + + AddSwimNoise( &workVelocity ); + + // Pose the fish properly + SetPoses( SteerRel, flLength ); + + //Drag our victim before moving + if ( m_pVictim != NULL ) + { + DragVictim( (workVelocity*flInterval).Length() ); + } + + //Move along the current velocity vector + if ( WalkMove( workVelocity * flInterval, MASK_NPCSOLID ) == false ) + { + //Attempt a half-step + if ( WalkMove( (workVelocity*0.5f) * flInterval, MASK_NPCSOLID) == false ) + { + //Restart the velocity + //VectorNormalize( m_vecVelocity ); + vecNewVelocity *= 0.5f; + } + else + { + //Cut our velocity in half + vecNewVelocity *= 0.5f; + } + } + + SetAbsVelocity( vecNewVelocity ); + +} + +//----------------------------------------------------------------------------- +// Purpose: Gets a steering vector to arrive at a target location with a +// relatively small velocity. +// Input : Steer - Receives the ideal steering vector. +// Target - Target position at which to arrive. +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::SteerArrive(Vector &Steer, const Vector &Target) +{ + Vector Offset = Target - GetLocalOrigin(); + float fTargetDistance = Offset.Length(); + + float fIdealSpeed = m_flGroundSpeed * (fTargetDistance / ICH_WAYPOINT_DISTANCE); + float fClippedSpeed = MIN( fIdealSpeed, m_flGroundSpeed ); + + Vector DesiredVelocity( 0, 0, 0 ); + + if ( fTargetDistance > ICH_WAYPOINT_DISTANCE ) + { + DesiredVelocity = (fClippedSpeed / fTargetDistance) * Offset; + } + + Steer = DesiredVelocity - GetAbsVelocity(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Gets a steering vector to move towards a target position as quickly +// as possible. +// Input : Steer - Receives the ideal steering vector. +// Target - Target position to seek. +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::SteerSeek( Vector &Steer, const Vector &Target ) +{ + Vector offset = Target - GetLocalOrigin(); + + VectorNormalize( offset ); + + Vector DesiredVelocity = m_flGroundSpeed * offset; + + Steer = DesiredVelocity - GetAbsVelocity(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &Steer - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CNPC_Ichthyosaur::SteerAvoidObstacles(Vector &Steer, const Vector &Velocity, const Vector &Forward, const Vector &Right, const Vector &Up) +{ + trace_t tr; + + bool collided = false; + Vector dir = Velocity; + float speed = VectorNormalize( dir ); + + //Look ahead one second and avoid whatever is in our way. + AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + (dir*speed), GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); + + Vector forward; + + GetVectors( &forward, NULL, NULL ); + + //If we're hitting our enemy, just continue on + if ( ( GetEnemy() != NULL ) && ( tr.m_pEnt == GetEnemy() ) ) + return false; + + if ( tr.fraction < 1.0f ) + { + CBaseEntity *pBlocker = tr.m_pEnt; + + if ( ( pBlocker != NULL ) && ( pBlocker->MyNPCPointer() != NULL ) ) + { + DevMsg( 2, "Avoiding an NPC\n" ); + + Vector HitOffset = tr.endpos - GetAbsOrigin(); + + Vector SteerUp = CrossProduct( HitOffset, Velocity ); + Steer = CrossProduct( SteerUp, Velocity ); + VectorNormalize( Steer ); + + /*Vector probeDir = tr.endpos - GetAbsOrigin(); + Vector normalToProbeAndWallNormal = probeDir.Cross( tr.plane.normal ); + + Steer = normalToProbeAndWallNormal.Cross( probeDir ); + VectorNormalize( Steer );*/ + + if ( tr.fraction > 0 ) + { + Steer = (Steer * Velocity.Length()) / tr.fraction; + //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+Steer, 255, 0, 0, false, 0.1f ); + } + else + { + Steer = (Steer * 1000 * Velocity.Length()); + //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+Steer, 255, 0, 0, false, 0.1f ); + } + } + else + { + if ( ( pBlocker != NULL ) && ( pBlocker == GetEnemy() ) ) + { + DevMsg( "Avoided collision\n" ); + return false; + } + + DevMsg( 2, "Avoiding the world\n" ); + + Vector steeringVector = tr.plane.normal; + + if ( tr.fraction == 0.0f ) + return false; + + Steer = steeringVector * ( Velocity.Length() / tr.fraction ); + + //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+Steer, 255, 0, 0, false, 0.1f ); + } + + //return true; + collided = true; + } + + //Try to remain 8 feet above the ground. + AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector(0, 0, -ICH_HEIGHT_PREFERENCE), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + + if ( tr.fraction < 1.0f ) + { + Steer += Vector( 0, 0, m_vecAccelerationMax.z / tr.fraction ); + collided = true; + } + + //Stay under the surface + if ( m_bIgnoreSurface == false ) + { + float waterLevel = ( UTIL_WaterLevel( GetAbsOrigin(), GetAbsOrigin().z, GetAbsOrigin().z+ICH_DEPTH_PREFERENCE ) - GetAbsOrigin().z ) / ICH_DEPTH_PREFERENCE; + + if ( waterLevel < 1.0f ) + { + Steer += -Vector( 0, 0, m_vecAccelerationMax.z / waterLevel ); + collided = true; + } + } + + return collided; +} + + +//----------------------------------------------------------------------------- +// Purpose: Clamps the desired steering vector based on the limitations of this +// vehicle. +// Input : SteerAbs - The vector indicating our ideal steering vector. Receives +// the clamped steering vector in absolute (x,y,z) coordinates. +// SteerRel - Receives the clamped steering vector in relative (forward, right, up) +// coordinates. +// forward - Our current forward vector. +// right - Our current right vector. +// up - Our current up vector. +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::ClampSteer(Vector &SteerAbs, Vector &SteerRel, Vector &forward, Vector &right, Vector &up) +{ + float fForwardSteer = DotProduct(SteerAbs, forward); + float fRightSteer = DotProduct(SteerAbs, right); + float fUpSteer = DotProduct(SteerAbs, up); + + if (fForwardSteer > 0) + { + fForwardSteer = MIN(fForwardSteer, m_vecAccelerationMax.x); + } + else + { + fForwardSteer = MAX(fForwardSteer, m_vecAccelerationMin.x); + } + + if (fRightSteer > 0) + { + fRightSteer = MIN(fRightSteer, m_vecAccelerationMax.y); + } + else + { + fRightSteer = MAX(fRightSteer, m_vecAccelerationMin.y); + } + + if (fUpSteer > 0) + { + fUpSteer = MIN(fUpSteer, m_vecAccelerationMax.z); + } + else + { + fUpSteer = MAX(fUpSteer, m_vecAccelerationMin.z); + } + + SteerAbs = (fForwardSteer*forward) + (fRightSteer*right) + (fUpSteer*up); + + SteerRel.x = fForwardSteer; + SteerRel.y = fRightSteer; + SteerRel.z = fUpSteer; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pTargetEnt - +// vecDir - +// flDistance - +// flInterval - +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::MoveFlyExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flDistance, float flInterval ) +{ + IchthyosaurMoveType_t eMoveType = ( GetNavigator()->CurWaypointIsGoal() ) ? ICH_MOVETYPE_ARRIVE : ICH_MOVETYPE_SEEK; + + m_flGroundSpeed = GetGroundSpeed(); + + Vector moveGoal = GetNavigator()->GetCurWaypointPos(); + + //See if we can move directly to our goal + if ( ( GetEnemy() != NULL ) && ( GetNavigator()->GetGoalTarget() == (CBaseEntity *) GetEnemy() ) ) + { + trace_t tr; + Vector goalPos = GetEnemy()->GetAbsOrigin() + ( GetEnemy()->GetSmoothedVelocity() * 0.5f ); + + AI_TraceHull( GetAbsOrigin(), goalPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, GetEnemy(), COLLISION_GROUP_NONE, &tr ); + + if ( tr.fraction == 1.0f ) + { + moveGoal = tr.endpos; + } + } + + //Move + DoMovement( flInterval, moveGoal, eMoveType ); + + //Save the info from that run + m_vecLastMoveTarget = moveGoal; + m_bHasMoveTarget = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEntity - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CNPC_Ichthyosaur::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker ) +{ + // don't look through water + if ( GetWaterLevel() != pEntity->GetWaterLevel() ) + return false; + + return BaseClass::FVisible( pEntity, traceMask, ppBlocker ); +} + +//----------------------------------------------------------------------------- +// Purpose: Get our conditions for a melee attack +// Input : flDot - +// flDist - +// Output : int +//----------------------------------------------------------------------------- +int CNPC_Ichthyosaur::MeleeAttack1Conditions( float flDot, float flDist ) +{ + Vector predictedDir = ( (GetEnemy()->GetAbsOrigin()+(GetEnemy()->GetSmoothedVelocity())) - GetAbsOrigin() ); + float flPredictedDist = VectorNormalize( predictedDir ); + + Vector vBodyDir; + GetVectors( &vBodyDir, NULL, NULL ); + + float flPredictedDot = DotProduct( predictedDir, vBodyDir ); + + if ( flPredictedDot < 0.8f ) + return COND_NOT_FACING_ATTACK; + + if ( ( flPredictedDist > ( GetAbsVelocity().Length() * 0.5f) ) && ( flDist > 128.0f ) ) + return COND_TOO_FAR_TO_ATTACK; + + return COND_CAN_MELEE_ATTACK1; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEvent - +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::HandleAnimEvent( animevent_t *pEvent ) +{ + switch ( pEvent->event ) + { + case ICH_AE_BITE: + Bite(); + break; + + case ICH_AE_BITE_START: + { + EmitSound( "NPC_Ichthyosaur.AttackGrowl" ); + } + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::Bite( void ) +{ + //Don't allow another bite too soon + if ( m_flNextBiteTime > gpGlobals->curtime ) + return; + + CBaseEntity *pHurt; + + //FIXME: E3 HACK - Always damage bullseyes if we're scripted to hit them + if ( ( GetEnemy() != NULL ) && ( GetEnemy()->Classify() == CLASS_BULLSEYE ) ) + { + pHurt = GetEnemy(); + } + else + { + pHurt = CheckTraceHullAttack( 108, Vector(-32,-32,-32), Vector(32,32,32), 0, DMG_CLUB ); + } + + //Hit something + if ( pHurt != NULL ) + { + CTakeDamageInfo info( this, this, sk_ichthyosaur_melee_dmg.GetInt(), DMG_CLUB ); + + if ( pHurt->IsPlayer() ) + { + CBasePlayer *pPlayer = ToBasePlayer( pHurt ); + + if ( pPlayer ) + { + if ( ( ( m_flHoldTime < gpGlobals->curtime ) && ( pPlayer->m_iHealth < (pPlayer->m_iMaxHealth*0.5f)) ) || ( pPlayer->GetWaterLevel() < 1 ) ) + { + //EnsnareVictim( pHurt ); + } + else + { + info.SetDamage( sk_ichthyosaur_melee_dmg.GetInt() * 3 ); + } + CalculateMeleeDamageForce( &info, GetAbsVelocity(), pHurt->GetAbsOrigin() ); + pHurt->TakeDamage( info ); + + color32 red = {64, 0, 0, 255}; + UTIL_ScreenFade( pPlayer, red, 0.5, 0, FFADE_IN ); + + //Disorient the player + QAngle angles = pPlayer->GetLocalAngles(); + + angles.x += random->RandomInt( 60, 25 ); + angles.y += random->RandomInt( 60, 25 ); + angles.z = 0.0f; + + pPlayer->SetLocalAngles( angles ); + + pPlayer->SnapEyeAngles( angles ); + } + } + else + { + CalculateMeleeDamageForce( &info, GetAbsVelocity(), pHurt->GetAbsOrigin() ); + pHurt->TakeDamage( info ); + } + + m_flNextBiteTime = gpGlobals->curtime + random->RandomFloat( 2.0f, 4.0f ); + + //Bubbles! + UTIL_Bubbles( pHurt->GetAbsOrigin()+Vector(-32.0f,-32.0f,-32.0f), pHurt->GetAbsOrigin()+Vector(32.0f,32.0f,0.0f), random->RandomInt( 16, 32 ) ); + + // Play a random attack hit sound + EmitSound( "NPC_Ichthyosaur.Bite" ); + + if ( GetActivity() == ACT_MELEE_ATTACK1 ) + { + SetActivity( (Activity) ACT_ICH_BITE_HIT ); + } + + return; + } + + //Play the miss animation and sound + if ( GetActivity() == ACT_MELEE_ATTACK1 ) + { + SetActivity( (Activity) ACT_ICH_BITE_MISS ); + } + + //Miss sound + EmitSound( "NPC_Ichthyosaur.BiteMiss" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CNPC_Ichthyosaur::Beached( void ) +{ + trace_t tr; + Vector testPos; + + testPos = GetAbsOrigin() - Vector( 0, 0, ICH_DEPTH_PREFERENCE ); + + AI_TraceHull( GetAbsOrigin(), testPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); + + return ( tr.fraction < 1.0f ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::PrescheduleThink( void ) +{ + BaseClass::PrescheduleThink(); + + //Ambient sounds + /* + if ( random->RandomInt( 0, 20 ) == 10 ) + { + if ( random->RandomInt( 0, 1 ) ) + { + ENVELOPE_CONTROLLER.SoundChangeVolume( m_pSwimSound, random->RandomFloat( 0.0f, 0.5f ), 1.0f ); + } + else + { + ENVELOPE_CONTROLLER.SoundChangeVolume( m_pVoiceSound, random->RandomFloat( 0.0f, 0.5f ), 1.0f ); + } + } + */ + + //Pings + if ( m_flNextPingTime < gpGlobals->curtime ) + { + m_flNextPingTime = gpGlobals->curtime + random->RandomFloat( 3.0f, 8.0f ); + } + + //Growls + if ( ( m_NPCState == NPC_STATE_COMBAT || m_NPCState == NPC_STATE_ALERT ) && ( m_flNextGrowlTime < gpGlobals->curtime ) ) + { + m_flNextGrowlTime = gpGlobals->curtime + random->RandomFloat( 2.0f, 6.0f ); + } + + //Randomly emit bubbles + if ( random->RandomInt( 0, 10 ) == 0 ) + { + UTIL_Bubbles( GetAbsOrigin()+(GetHullMins()*0.5f), GetAbsOrigin()+(GetHullMaxs()*0.5f), 1 ); + } + + //Check our water level + if ( GetWaterLevel() != 3 ) + { + if ( GetWaterLevel() < 2 ) + { + DevMsg( 2, "Came out of water\n" ); + + if ( Beached() ) + { + SetSchedule( SCHED_ICH_THRASH ); + + Vector vecNewVelocity = GetAbsVelocity(); + vecNewVelocity[2] = 8.0f; + SetAbsVelocity( vecNewVelocity ); + } + } + else + { + //TODO: Wake effects + } + } + + //If we have a victim, update them + if ( m_pVictim != NULL ) + { + //See if it's time to release the victim + if ( m_flHoldTime < gpGlobals->curtime ) + { + ReleaseVictim(); + return; + } + + Bite(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pevInflictor - +// *pAttacker - +// flDamage - +// bitsDamageType - +//----------------------------------------------------------------------------- +int CNPC_Ichthyosaur::OnTakeDamage_Alive( const CTakeDamageInfo &info ) +{ + //Release the player if he's struck us while being held + if ( m_flHoldTime > gpGlobals->curtime ) + { + ReleaseVictim(); + + //Don't give them as much time to flee + m_flNextBiteTime = gpGlobals->curtime + 2.0f; + + SetSchedule( SCHED_ICH_THRASH ); + } + + return BaseClass::OnTakeDamage_Alive( info ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::EnsnareVictim( CBaseEntity *pVictim ) +{ + CBaseCombatCharacter* pBCC = (CBaseCombatCharacter *) pVictim; + + if ( pBCC && pBCC->DispatchInteraction( g_interactionBarnacleVictimGrab, NULL, this ) ) + { + if ( pVictim->IsPlayer() ) + { + CBasePlayer *pPlayer = dynamic_cast< CBasePlayer * >((CBaseEntity *) pVictim); + + if ( pPlayer ) + { + m_flHoldTime = MAX( gpGlobals->curtime+3.0f, pPlayer->PlayerDrownTime() - 2.0f ); + } + } + else + { + m_flHoldTime = gpGlobals->curtime + 4.0f; + } + + m_pVictim = pVictim; + m_pVictim->AddSolidFlags( FSOLID_NOT_SOLID ); + + SetSchedule( SCHED_ICH_DROWN_VICTIM ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::ReleaseVictim( void ) +{ + CBaseCombatCharacter *pBCC = (CBaseCombatCharacter *) m_pVictim; + + pBCC->DispatchInteraction( g_interactionBarnacleVictimReleased, NULL, this ); + + m_pVictim->RemoveSolidFlags( FSOLID_NOT_SOLID ); + + m_pVictim = NULL; + m_flNextBiteTime = gpGlobals->curtime + 8.0f; + m_flHoldTime = gpGlobals->curtime - 0.1f; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : speed to move at +//----------------------------------------------------------------------------- +float CNPC_Ichthyosaur::GetGroundSpeed( void ) +{ + if ( m_flHoldTime > gpGlobals->curtime ) + return ICH_SWIM_SPEED_WALK/2.0f; + + if ( GetIdealActivity() == ACT_WALK ) + return ICH_SWIM_SPEED_WALK; + + if ( GetIdealActivity() == ACT_ICH_THRASH ) + return ICH_SWIM_SPEED_WALK; + + return ICH_SWIM_SPEED_RUN; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// Output : int +//----------------------------------------------------------------------------- +int CNPC_Ichthyosaur::TranslateSchedule( int type ) +{ + if ( type == SCHED_CHASE_ENEMY ) return SCHED_ICH_CHASE_ENEMY; + //if ( type == SCHED_IDLE_STAND ) return SCHED_PATROL_WALK; + if ( type == SCHED_PATROL_RUN ) return SCHED_ICH_PATROL_RUN; + if ( type == SCHED_PATROL_WALK ) return SCHED_ICH_PATROL_WALK; + if ( type == SCHED_MELEE_ATTACK1 ) return SCHED_ICH_MELEE_ATTACK1; + + return BaseClass::TranslateSchedule( type ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pTask - +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::StartTask( const Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_ICH_THRASH_PATH: + GetNavigator()->SetMovementActivity( (Activity) ACT_ICH_THRASH ); + TaskComplete(); + break; + + case TASK_ICH_GET_PATH_TO_RANDOM_NODE: + { + if ( GetEnemy() == NULL || !GetNavigator()->SetRandomGoal( GetEnemy()->GetLocalOrigin(), pTask->flTaskData ) ) + { + if (!GetNavigator()->SetRandomGoal( pTask->flTaskData ) ) + { + TaskFail(FAIL_NO_REACHABLE_NODE); + return; + } + } + + TaskComplete(); + } + break; + + case TASK_ICH_GET_PATH_TO_DROWN_NODE: + { + Vector drownPos = GetLocalOrigin() - Vector( 0, 0, pTask->flTaskData ); + + if ( GetNavigator()->SetGoal( drownPos, AIN_CLEAR_TARGET ) == false ) + { + TaskFail( FAIL_NO_ROUTE ); + return; + } + + TaskComplete(); + } + break; + + case TASK_MELEE_ATTACK1: + m_flPlaybackRate = 1.0f; + BaseClass::StartTask(pTask); + break; + + default: + BaseClass::StartTask(pTask); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pTask - +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::RunTask( const Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_ICH_GET_PATH_TO_RANDOM_NODE: + return; + break; + + case TASK_ICH_GET_PATH_TO_DROWN_NODE: + return; + break; + + default: + BaseClass::RunTask(pTask); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : desired yaw speed +//----------------------------------------------------------------------------- +float CNPC_Ichthyosaur::MaxYawSpeed( void ) +{ + if ( GetIdealActivity() == ACT_MELEE_ATTACK1 ) + return 16.0f; + + if ( GetIdealActivity() == ACT_ICH_THRASH ) + return 16.0f; + + //Ramp up the yaw speed as we increase our speed + return ICH_MIN_TURN_SPEED + ( (ICH_MAX_TURN_SPEED-ICH_MIN_TURN_SPEED) * ( fabs(GetAbsVelocity().Length()) / ICH_SWIM_SPEED_RUN ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEnemy - +// &chasePosition - +// &tolerance - +//----------------------------------------------------------------------------- +void CNPC_Ichthyosaur::TranslateNavGoal( CBaseEntity *pEnemy, Vector &chasePosition ) +{ + Vector offset = pEnemy->EyePosition() - pEnemy->GetAbsOrigin(); + chasePosition += offset; +} + +float CNPC_Ichthyosaur::GetDefaultNavGoalTolerance() +{ + return GetHullWidth()*2.0f; +} + + +//----------------------------------------------------------------------------- +// +// Schedules +// +//----------------------------------------------------------------------------- + +//================================================== +// SCHED_ICH_CHASE_ENEMY +//================================================== + +AI_DEFINE_SCHEDULE +( + SCHED_ICH_CHASE_ENEMY, + + " Tasks" + " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ICH_PATROL_WALK" + " TASK_SET_TOLERANCE_DISTANCE 64" + " TASK_SET_GOAL GOAL:ENEMY" + " TASK_GET_PATH_TO_GOAL PATH:TRAVEL" + " TASK_RUN_PATH 0" + " TASK_WAIT_FOR_MOVEMENT 0" + "" + " Interrupts" + " COND_NEW_ENEMY" + " COND_ENEMY_DEAD" + " COND_ENEMY_UNREACHABLE" + " COND_CAN_MELEE_ATTACK1" + " COND_TOO_CLOSE_TO_ATTACK" + " COND_LOST_ENEMY" + " COND_TASK_FAILED" +); + +//================================================== +// SCHED_ICH_PATROL_RUN +//================================================== + +AI_DEFINE_SCHEDULE +( + SCHED_ICH_PATROL_RUN, + + " Tasks" + " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE" + " TASK_SET_TOLERANCE_DISTANCE 64" + " TASK_SET_ROUTE_SEARCH_TIME 4" + " TASK_ICH_GET_PATH_TO_RANDOM_NODE 200" + " TASK_RUN_PATH 0" + " TASK_WAIT_FOR_MOVEMENT 0" + "" + " Interrupts" + " COND_CAN_MELEE_ATTACK1" + " COND_GIVE_WAY" + " COND_NEW_ENEMY" + " COND_LIGHT_DAMAGE" + " COND_HEAVY_DAMAGE" +); + +//================================================== +// SCHED_ICH_PATROL_WALK +//================================================== + +AI_DEFINE_SCHEDULE +( + SCHED_ICH_PATROL_WALK, + + " Tasks" + " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE" + " TASK_SET_TOLERANCE_DISTANCE 64" + " TASK_SET_ROUTE_SEARCH_TIME 4" + " TASK_ICH_GET_PATH_TO_RANDOM_NODE 200" + " TASK_WALK_PATH 0" + " TASK_WAIT_FOR_MOVEMENT 0" + "" + " Interrupts" + " COND_CAN_MELEE_ATTACK1" + " COND_GIVE_WAY" + " COND_NEW_ENEMY" + " COND_LIGHT_DAMAGE" + " COND_HEAVY_DAMAGE" +); + +//================================================== +// SCHED_ICH_DROWN_VICTIM +//================================================== + +AI_DEFINE_SCHEDULE +( + SCHED_ICH_DROWN_VICTIM, + + " Tasks" + " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE" + " TASK_SET_TOLERANCE_DISTANCE 64" + " TASK_SET_ROUTE_SEARCH_TIME 4" + " TASK_ICH_GET_PATH_TO_DROWN_NODE 256" + " TASK_WALK_PATH 0" + " TASK_WAIT_FOR_MOVEMENT 0" + "" + " Interrupts" + " COND_NEW_ENEMY" + " COND_LIGHT_DAMAGE" + " COND_HEAVY_DAMAGE" +); + +//========================================================= +// SCHED_ICH_MELEE_ATTACK1 +//========================================================= + +AI_DEFINE_SCHEDULE +( + SCHED_ICH_MELEE_ATTACK1, + + " Tasks" + " TASK_ANNOUNCE_ATTACK 1" + " TASK_MELEE_ATTACK1 0" + "" + " Interrupts" + " COND_NEW_ENEMY" + " COND_ENEMY_DEAD" + " COND_ENEMY_OCCLUDED" +); + +//================================================== +// SCHED_ICH_THRASH +//================================================== + +AI_DEFINE_SCHEDULE +( + SCHED_ICH_THRASH, + + " Tasks" + " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE" + " TASK_SET_TOLERANCE_DISTANCE 64" + " TASK_SET_ROUTE_SEARCH_TIME 4" + " TASK_ICH_GET_PATH_TO_RANDOM_NODE 64" + " TASK_ICH_THRASH_PATH 0" + " TASK_WAIT_FOR_MOVEMENT 0" + "" + " Interrupts" +); |