aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/hl2/npc_crow.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/hl2/npc_crow.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/hl2/npc_crow.cpp')
-rw-r--r--mp/src/game/server/hl2/npc_crow.cpp3198
1 files changed, 1599 insertions, 1599 deletions
diff --git a/mp/src/game/server/hl2/npc_crow.cpp b/mp/src/game/server/hl2/npc_crow.cpp
index 37b08f24..0b897b9e 100644
--- a/mp/src/game/server/hl2/npc_crow.cpp
+++ b/mp/src/game/server/hl2/npc_crow.cpp
@@ -1,1599 +1,1599 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Crows. Simple ambient birds that fly away when they hear gunfire or
-// when anything gets too close to them.
-//
-// TODO: landing
-// TODO: death
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "game.h"
-#include "ai_basenpc.h"
-#include "ai_schedule.h"
-#include "ai_hull.h"
-#include "ai_hint.h"
-#include "ai_motor.h"
-#include "ai_navigator.h"
-#include "hl2_shareddefs.h"
-#include "ai_route.h"
-#include "npcevent.h"
-#include "gib.h"
-#include "ai_interactions.h"
-#include "ndebugoverlay.h"
-#include "soundent.h"
-#include "vstdlib/random.h"
-#include "engine/IEngineSound.h"
-#include "movevars_shared.h"
-#include "npc_crow.h"
-#include "ai_moveprobe.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-//
-// Custom activities.
-//
-static int ACT_CROW_TAKEOFF;
-static int ACT_CROW_SOAR;
-static int ACT_CROW_LAND;
-
-//
-// Animation events.
-//
-static int AE_CROW_TAKEOFF;
-static int AE_CROW_FLY;
-static int AE_CROW_HOP;
-
-//
-// Skill settings.
-//
-ConVar sk_crow_health( "sk_crow_health","1");
-ConVar sk_crow_melee_dmg( "sk_crow_melee_dmg","0");
-
-LINK_ENTITY_TO_CLASS( npc_crow, CNPC_Crow );
-LINK_ENTITY_TO_CLASS( npc_seagull, CNPC_Seagull );
-LINK_ENTITY_TO_CLASS( npc_pigeon, CNPC_Pigeon );
-
-BEGIN_DATADESC( CNPC_Crow )
-
- DEFINE_FIELD( m_flGroundIdleMoveTime, FIELD_TIME ),
- DEFINE_FIELD( m_bOnJeep, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_flEnemyDist, FIELD_FLOAT ),
- DEFINE_FIELD( m_nMorale, FIELD_INTEGER ),
- DEFINE_FIELD( m_bReachedMoveGoal, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_flHopStartZ, FIELD_FLOAT ),
- DEFINE_FIELD( m_vDesiredTarget, FIELD_VECTOR ),
- DEFINE_FIELD( m_vCurrentTarget, FIELD_VECTOR ),
- DEFINE_FIELD( m_flSoarTime, FIELD_TIME ),
- DEFINE_FIELD( m_bSoar, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bPlayedLoopingSound, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_iBirdType, FIELD_INTEGER ),
- DEFINE_FIELD( m_vLastStoredOrigin, FIELD_POSITION_VECTOR ),
- DEFINE_FIELD( m_flLastStuckCheck, FIELD_TIME ),
- DEFINE_FIELD( m_flDangerSoundTime, FIELD_TIME ),
- DEFINE_KEYFIELD( m_bIsDeaf, FIELD_BOOLEAN, "deaf" ),
-
- // Inputs
- DEFINE_INPUTFUNC( FIELD_STRING, "FlyAway", InputFlyAway ),
-
-END_DATADESC()
-
-static ConVar birds_debug( "birds_debug", "0" );
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_Crow::Spawn( void )
-{
- BaseClass::Spawn();
-
-#ifdef _XBOX
- // Always fade the corpse
- AddSpawnFlags( SF_NPC_FADE_CORPSE );
-#endif // _XBOX
-
- char *szModel = (char *)STRING( GetModelName() );
- if (!szModel || !*szModel)
- {
- szModel = "models/crow.mdl";
- SetModelName( AllocPooledString(szModel) );
- }
-
- Precache();
- SetModel( szModel );
-
- m_iHealth = sk_crow_health.GetFloat();
-
- SetHullType(HULL_TINY);
- SetHullSizeNormal();
-
- SetSolid( SOLID_BBOX );
- SetMoveType( MOVETYPE_STEP );
-
- m_flFieldOfView = VIEW_FIELD_FULL;
- SetViewOffset( Vector(6, 0, 11) ); // Position of the eyes relative to NPC's origin.
-
- m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 0.0f, 5.0f );
-
- SetBloodColor( BLOOD_COLOR_RED );
- m_NPCState = NPC_STATE_NONE;
-
- m_nMorale = random->RandomInt( 0, 12 );
-
- SetCollisionGroup( HL2COLLISION_GROUP_CROW );
-
- CapabilitiesClear();
-
- bool bFlying = ( ( m_spawnflags & SF_CROW_FLYING ) != 0 );
- SetFlyingState( bFlying ? FlyState_Flying : FlyState_Walking );
-
- // We don't mind zombies so much. They smell good!
- AddClassRelationship( CLASS_ZOMBIE, D_NU, 0 );
-
- m_bSoar = false;
- m_bOnJeep = false;
- m_flSoarTime = gpGlobals->curtime;
-
- NPCInit();
-
- m_iBirdType = BIRDTYPE_CROW;
-
- m_vLastStoredOrigin = vec3_origin;
- m_flLastStuckCheck = gpGlobals->curtime;
-
- m_flDangerSoundTime = gpGlobals->curtime;
-
- SetGoalEnt( NULL );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns this monster's classification in the relationship table.
-//-----------------------------------------------------------------------------
-Class_T CNPC_Crow::Classify( void )
-{
- return( CLASS_EARTH_FAUNA );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pEnemy -
-//-----------------------------------------------------------------------------
-void CNPC_Crow::GatherEnemyConditions( CBaseEntity *pEnemy )
-{
- m_flEnemyDist = (GetLocalOrigin() - pEnemy->GetLocalOrigin()).Length();
-
- if ( m_flEnemyDist < 512 )
- {
- SetCondition( COND_CROW_ENEMY_WAY_TOO_CLOSE );
- }
-
- if ( m_flEnemyDist < 1024 )
- {
- SetCondition( COND_CROW_ENEMY_TOO_CLOSE );
- }
-
- BaseClass::GatherEnemyConditions(pEnemy);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : posSrc -
-// Output : Vector
-//-----------------------------------------------------------------------------
-Vector CNPC_Crow::BodyTarget( const Vector &posSrc, bool bNoisy )
-{
- Vector vecResult;
- vecResult = GetAbsOrigin();
- vecResult.z += 6;
- return vecResult;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_Crow::StopLoopingSounds( void )
-{
- //
- // Stop whatever flap sound might be playing.
- //
- if ( m_bPlayedLoopingSound )
- {
- StopSound( "NPC_Crow.Flap" );
- }
- BaseClass::StopLoopingSounds();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Catches the monster-specific messages that occur when tagged
-// animation frames are played.
-// Input : pEvent -
-//-----------------------------------------------------------------------------
-void CNPC_Crow::HandleAnimEvent( animevent_t *pEvent )
-{
- if ( pEvent->event == AE_CROW_TAKEOFF )
- {
- if ( GetNavigator()->GetPath()->GetCurWaypoint() )
- {
- Takeoff( GetNavigator()->GetCurWaypointPos() );
- }
- return;
- }
-
- if( pEvent->event == AE_CROW_HOP )
- {
- SetGroundEntity( NULL );
-
- //
- // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
- //
- UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
-
- //
- // How fast does the crow need to travel to reach the hop goal given gravity?
- //
- float flHopDistance = ( m_vSavePosition - GetLocalOrigin() ).Length();
- float gravity = GetCurrentGravity();
- if ( gravity <= 1 )
- {
- gravity = 1;
- }
-
- float height = 0.25 * flHopDistance;
- float speed = sqrt( 2 * gravity * height );
- float time = speed / gravity;
-
- //
- // Scale the sideways velocity to get there at the right time
- //
- Vector vecJumpDir = m_vSavePosition - GetLocalOrigin();
- vecJumpDir = vecJumpDir / time;
-
- //
- // Speed to offset gravity at the desired height.
- //
- vecJumpDir.z = speed;
-
- //
- // Don't jump too far/fast.
- //
- float distance = vecJumpDir.Length();
- if ( distance > 650 )
- {
- vecJumpDir = vecJumpDir * ( 650.0 / distance );
- }
-
- m_nMorale -= random->RandomInt( 1, 6 );
- if ( m_nMorale <= 0 )
- {
- m_nMorale = 0;
- }
-
- // Play a hop flap sound.
- EmitSound( "NPC_Crow.Hop" );
-
- SetAbsVelocity( vecJumpDir );
- return;
- }
-
- if( pEvent->event == AE_CROW_FLY )
- {
- //
- // Start flying.
- //
- SetActivity( ACT_FLY );
-
- m_bSoar = false;
- m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 3, 5 );
-
- return;
- }
-
- CAI_BaseNPC::HandleAnimEvent( pEvent );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : eNewActivity -
-//-----------------------------------------------------------------------------
-void CNPC_Crow::OnChangeActivity( Activity eNewActivity )
-{
-// if ( eNewActivity == ACT_FLY )
-// {
-// m_flGroundSpeed = CROW_AIRSPEED;
-// }
-//
- bool fRandomize = false;
- if ( eNewActivity == ACT_FLY )
- {
- fRandomize = true;
- }
-
- BaseClass::OnChangeActivity( eNewActivity );
- if ( fRandomize )
- {
- SetCycle( random->RandomFloat( 0.0, 0.75 ) );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler that makes the crow fly away.
-//-----------------------------------------------------------------------------
-void CNPC_Crow::InputFlyAway( inputdata_t &inputdata )
-{
- string_t sTarget = MAKE_STRING( inputdata.value.String() );
-
- if ( sTarget != NULL_STRING )// this npc has a target
- {
- CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, sTarget );
-
- if ( pEnt )
- {
- trace_t tr;
- AI_TraceLine ( EyePosition(), pEnt->GetAbsOrigin(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
-
- if ( tr.fraction != 1.0f )
- return;
-
- // Find the npc's initial target entity, stash it
- SetGoalEnt( pEnt );
- }
- }
- else
- SetGoalEnt( NULL );
-
- SetCondition( COND_CROW_FORCED_FLY );
- SetCondition( COND_PROVOKED );
-
-}
-
-void CNPC_Crow::UpdateEfficiency( bool bInPVS )
-{
- if ( IsFlying() )
- {
- SetEfficiency( ( GetSleepState() != AISS_AWAKE ) ? AIE_DORMANT : AIE_NORMAL );
- SetMoveEfficiency( AIME_NORMAL );
- return;
- }
-
- BaseClass::UpdateEfficiency( bInPVS );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Implements "deafness"
-//-----------------------------------------------------------------------------
-bool CNPC_Crow::QueryHearSound( CSound *pSound )
-{
- if( IsDeaf() )
- return false;
-
- return BaseClass::QueryHearSound( pSound );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Handles all flight movement because we don't ever build paths when
-// when we are flying.
-// Input : flInterval - Seconds to simulate.
-//-----------------------------------------------------------------------------
-bool CNPC_Crow::OverrideMove( float flInterval )
-{
- if ( GetNavigator()->GetPath()->CurWaypointNavType() == NAV_FLY && GetNavigator()->GetNavType() != NAV_FLY )
- {
- SetNavType( NAV_FLY );
- }
-
- if ( IsFlying() )
- {
- if ( GetNavigator()->GetPath()->GetCurWaypoint() )
- {
- if ( m_flLastStuckCheck <= gpGlobals->curtime )
- {
- if ( m_vLastStoredOrigin == GetAbsOrigin() )
- {
- if ( GetAbsVelocity() == vec3_origin )
- {
- float flDamage = m_iHealth;
-
- CTakeDamageInfo dmgInfo( this, this, flDamage, DMG_GENERIC );
- GuessDamageForce( &dmgInfo, vec3_origin - Vector( 0, 0, 0.1 ), GetAbsOrigin() );
- TakeDamage( dmgInfo );
-
- return false;
- }
- else
- {
- m_vLastStoredOrigin = GetAbsOrigin();
- }
- }
- else
- {
- m_vLastStoredOrigin = GetAbsOrigin();
- }
-
- m_flLastStuckCheck = gpGlobals->curtime + 1.0f;
- }
-
- if (m_bReachedMoveGoal )
- {
- SetIdealActivity( (Activity)ACT_CROW_LAND );
- SetFlyingState( FlyState_Landing );
- TaskMovementComplete();
- }
- else
- {
- SetIdealActivity ( ACT_FLY );
- MoveCrowFly( flInterval );
- }
-
- }
- else if ( !GetTask() || GetTask()->iTask == TASK_WAIT_FOR_MOVEMENT )
- {
- SetSchedule( SCHED_CROW_IDLE_FLY );
- SetFlyingState( FlyState_Flying );
- SetIdealActivity ( ACT_FLY );
- }
- return true;
- }
-
- return false;
-}
-
-Activity CNPC_Crow::NPC_TranslateActivity( Activity eNewActivity )
-{
- if ( IsFlying() && eNewActivity == ACT_IDLE )
- {
- return ACT_FLY;
- }
-
- if ( eNewActivity == ACT_FLY )
- {
- if ( m_flSoarTime < gpGlobals->curtime )
- {
- //Adrian: This should be revisited.
- if ( random->RandomInt( 0, 100 ) <= 50 && m_bSoar == false && GetAbsVelocity().z < 0 )
- {
- m_bSoar = true;
- m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 1, 4 );
- }
- else
- {
- m_bSoar = false;
- m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 3, 5 );
- }
- }
-
- if ( m_bSoar == true )
- {
- return (Activity)ACT_CROW_SOAR;
- }
- else
- return ACT_FLY;
- }
-
- return BaseClass::NPC_TranslateActivity( eNewActivity );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Handles all flight movement.
-// Input : flInterval - Seconds to simulate.
-//-----------------------------------------------------------------------------
-void CNPC_Crow::MoveCrowFly( float flInterval )
-{
- //
- // Bound interval so we don't get ludicrous motion when debugging
- // or when framerate drops catastrophically.
- //
- if (flInterval > 1.0)
- {
- flInterval = 1.0;
- }
-
- m_flDangerSoundTime = gpGlobals->curtime + 5.0f;
-
- //
- // Determine the goal of our movement.
- //
- Vector vecMoveGoal = GetAbsOrigin();
-
- if ( GetNavigator()->IsGoalActive() )
- {
- vecMoveGoal = GetNavigator()->GetCurWaypointPos();
-
- if ( GetNavigator()->CurWaypointIsGoal() == false )
- {
- AI_ProgressFlyPathParams_t params( MASK_NPCSOLID );
- params.bTrySimplify = false;
-
- GetNavigator()->ProgressFlyPath( params ); // ignore result, crow handles completion directly
-
- // Fly towards the hint.
- if ( GetNavigator()->GetPath()->GetCurWaypoint() )
- {
- vecMoveGoal = GetNavigator()->GetCurWaypointPos();
- }
- }
- }
- else
- {
- // No movement goal.
- vecMoveGoal = GetAbsOrigin();
- SetAbsVelocity( vec3_origin );
- return;
- }
-
- Vector vecMoveDir = ( vecMoveGoal - GetAbsOrigin() );
- Vector vForward;
- AngleVectors( GetAbsAngles(), &vForward );
-
- //
- // Fly towards the movement goal.
- //
- float flDistance = ( vecMoveGoal - GetAbsOrigin() ).Length();
-
- if ( vecMoveGoal != m_vDesiredTarget )
- {
- m_vDesiredTarget = vecMoveGoal;
- }
- else
- {
- m_vCurrentTarget = ( m_vDesiredTarget - GetAbsOrigin() );
- VectorNormalize( m_vCurrentTarget );
- }
-
- float flLerpMod = 0.25f;
-
- if ( flDistance <= 256.0f )
- {
- flLerpMod = 1.0f - ( flDistance / 256.0f );
- }
-
-
- VectorLerp( vForward, m_vCurrentTarget, flLerpMod, vForward );
-
-
- if ( flDistance < CROW_AIRSPEED * flInterval )
- {
- if ( GetNavigator()->IsGoalActive() )
- {
- if ( GetNavigator()->CurWaypointIsGoal() )
- {
- m_bReachedMoveGoal = true;
- }
- else
- {
- GetNavigator()->AdvancePath();
- }
- }
- else
- m_bReachedMoveGoal = true;
- }
-
- if ( GetHintNode() )
- {
- AIMoveTrace_t moveTrace;
- GetMoveProbe()->MoveLimit( NAV_FLY, GetAbsOrigin(), GetNavigator()->GetCurWaypointPos(), MASK_NPCSOLID, GetNavTargetEntity(), &moveTrace );
-
- //See if it succeeded
- if ( IsMoveBlocked( moveTrace.fStatus ) )
- {
- Vector vNodePos = vecMoveGoal;
- GetHintNode()->GetPosition(this, &vNodePos);
-
- GetNavigator()->SetGoal( vNodePos );
- }
- }
-
- //
- // Look to see if we are going to hit anything.
- //
- VectorNormalize( vForward );
- Vector vecDeflect;
- if ( Probe( vForward, CROW_AIRSPEED * flInterval, vecDeflect ) )
- {
- vForward = vecDeflect;
- VectorNormalize( vForward );
- }
-
- SetAbsVelocity( vForward * CROW_AIRSPEED );
-
- if ( GetAbsVelocity().Length() > 0 && GetNavigator()->CurWaypointIsGoal() && flDistance < CROW_AIRSPEED )
- {
- SetIdealActivity( (Activity)ACT_CROW_LAND );
- }
-
-
- //Bank and set angles.
- Vector vRight;
- QAngle vRollAngle;
-
- VectorAngles( vForward, vRollAngle );
- vRollAngle.z = 0;
-
- AngleVectors( vRollAngle, NULL, &vRight, NULL );
-
- float flRoll = DotProduct( vRight, vecMoveDir ) * 45;
- flRoll = clamp( flRoll, -45, 45 );
-
- vRollAngle[ROLL] = flRoll;
- SetAbsAngles( vRollAngle );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Looks ahead to see if we are going to hit something. If we are, a
-// recommended avoidance path is returned.
-// Input : vecMoveDir -
-// flSpeed -
-// vecDeflect -
-// Output : Returns true if we hit something and need to deflect our course,
-// false if all is well.
-//-----------------------------------------------------------------------------
-bool CNPC_Crow::Probe( const Vector &vecMoveDir, float flSpeed, Vector &vecDeflect )
-{
- //
- // Look 1/2 second ahead.
- //
- trace_t tr;
- AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + vecMoveDir * flSpeed, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, HL2COLLISION_GROUP_CROW, &tr );
- if ( tr.fraction < 1.0f )
- {
- //
- // If we hit something, deflect flight path parallel to surface hit.
- //
- Vector vecUp;
- CrossProduct( vecMoveDir, tr.plane.normal, vecUp );
- CrossProduct( tr.plane.normal, vecUp, vecDeflect );
- VectorNormalize( vecDeflect );
- return true;
- }
-
- vecDeflect = vec3_origin;
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Switches between flying mode and ground mode.
-//-----------------------------------------------------------------------------
-void CNPC_Crow::SetFlyingState( FlyState_t eState )
-{
- if ( eState == FlyState_Flying )
- {
- // Flying
- SetGroundEntity( NULL );
- AddFlag( FL_FLY );
- SetNavType( NAV_FLY );
- CapabilitiesRemove( bits_CAP_MOVE_GROUND );
- CapabilitiesAdd( bits_CAP_MOVE_FLY );
- SetMoveType( MOVETYPE_STEP );
- m_vLastStoredOrigin = GetAbsOrigin();
- m_flLastStuckCheck = gpGlobals->curtime + 3.0f;
- m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 5.0f, 10.0f );
- }
- else if ( eState == FlyState_Walking )
- {
- // Walking
- QAngle angles = GetAbsAngles();
- angles[PITCH] = 0.0f;
- angles[ROLL] = 0.0f;
- SetAbsAngles( angles );
-
- RemoveFlag( FL_FLY );
- SetNavType( NAV_GROUND );
- CapabilitiesRemove( bits_CAP_MOVE_FLY );
- CapabilitiesAdd( bits_CAP_MOVE_GROUND );
- SetMoveType( MOVETYPE_STEP );
- m_vLastStoredOrigin = vec3_origin;
- m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 5.0f, 10.0f );
- }
- else
- {
- // Falling
- RemoveFlag( FL_FLY );
- SetNavType( NAV_GROUND );
- CapabilitiesRemove( bits_CAP_MOVE_FLY );
- CapabilitiesAdd( bits_CAP_MOVE_GROUND );
- SetMoveType( MOVETYPE_STEP );
- m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 5.0f, 10.0f );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Performs a takeoff. Called via an animation event at the moment
-// our feet leave the ground.
-// Input : pGoalEnt - The entity that we are going to fly toward.
-//-----------------------------------------------------------------------------
-void CNPC_Crow::Takeoff( const Vector &vGoal )
-{
- if ( vGoal != vec3_origin )
- {
- //
- // Lift us off ground so engine doesn't instantly reset FL_ONGROUND.
- //
- UTIL_SetOrigin( this, GetAbsOrigin() + Vector( 0 , 0 , 1 ));
-
- //
- // Fly straight at the goal entity at our maximum airspeed.
- //
- Vector vecMoveDir = vGoal - GetAbsOrigin();
- VectorNormalize( vecMoveDir );
-
- // FIXME: pitch over time
-
- SetFlyingState( FlyState_Flying );
-
- QAngle angles;
- VectorAngles( vecMoveDir, angles );
- SetAbsAngles( angles );
-
- SetAbsVelocity( vecMoveDir * CROW_TAKEOFF_SPEED );
- }
-}
-
-void CNPC_Crow::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
-{
- CTakeDamageInfo newInfo = info;
-
- if ( info.GetDamageType() & DMG_PHYSGUN )
- {
- Vector puntDir = ( info.GetDamageForce() * 5000.0f );
-
- newInfo.SetDamage( m_iMaxHealth );
-
- PainSound( newInfo );
- newInfo.SetDamageForce( puntDir );
- }
-
- BaseClass::TraceAttack( newInfo, vecDir, ptr, pAccumulator );
-}
-
-
-void CNPC_Crow::StartTargetHandling( CBaseEntity *pTargetEnt )
-{
- AI_NavGoal_t goal( GOALTYPE_PATHCORNER, pTargetEnt->GetAbsOrigin(),
- ACT_FLY,
- AIN_DEF_TOLERANCE, AIN_YAW_TO_DEST);
-
- if ( !GetNavigator()->SetGoal( goal ) )
- {
- DevWarning( 2, "Can't Create Route!\n" );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pTask -
-//-----------------------------------------------------------------------------
-void CNPC_Crow::StartTask( const Task_t *pTask )
-{
- switch ( pTask->iTask )
- {
- //
- // This task enables us to build a path that requires flight.
- //
-// case TASK_CROW_PREPARE_TO_FLY:
-// {
-// SetFlyingState( FlyState_Flying );
-// TaskComplete();
-// break;
-// }
-
- case TASK_CROW_TAKEOFF:
- {
- if ( random->RandomInt( 1, 4 ) == 1 )
- {
- AlertSound();
- }
-
- FlapSound();
-
- SetIdealActivity( ( Activity )ACT_CROW_TAKEOFF );
- break;
- }
-
- case TASK_CROW_PICK_EVADE_GOAL:
- {
- if ( GetEnemy() != NULL )
- {
- //
- // Get our enemy's position in x/y.
- //
- Vector vecEnemyOrigin = GetEnemy()->GetAbsOrigin();
- vecEnemyOrigin.z = GetAbsOrigin().z;
-
- //
- // Pick a hop goal a random distance along a vector away from our enemy.
- //
- m_vSavePosition = GetAbsOrigin() - vecEnemyOrigin;
- VectorNormalize( m_vSavePosition );
- m_vSavePosition = GetAbsOrigin() + m_vSavePosition * ( 32 + random->RandomInt( 0, 32 ) );
-
- GetMotor()->SetIdealYawToTarget( m_vSavePosition );
- TaskComplete();
- }
- else
- {
- TaskFail( "No enemy" );
- }
- break;
- }
-
- case TASK_CROW_FALL_TO_GROUND:
- {
- SetFlyingState( FlyState_Falling );
- break;
- }
-
- case TASK_FIND_HINTNODE:
- {
- if ( GetGoalEnt() )
- {
- TaskComplete();
- return;
- }
- // Overloaded because we search over a greater distance.
- if ( !GetHintNode() )
- {
- SetHintNode(CAI_HintManager::FindHint( this, HINT_CROW_FLYTO_POINT, bits_HINT_NODE_NEAREST | bits_HINT_NODE_USE_GROUP, 10000 ));
- }
-
- if ( GetHintNode() )
- {
- TaskComplete();
- }
- else
- {
- TaskFail( FAIL_NO_HINT_NODE );
- }
- break;
- }
-
- case TASK_GET_PATH_TO_HINTNODE:
- {
- //How did this happen?!
- if ( GetGoalEnt() == this )
- {
- SetGoalEnt( NULL );
- }
-
- if ( GetGoalEnt() )
- {
- SetFlyingState( FlyState_Flying );
- StartTargetHandling( GetGoalEnt() );
-
- m_bReachedMoveGoal = false;
- TaskComplete();
- SetHintNode( NULL );
- return;
- }
-
- if ( GetHintNode() )
- {
- Vector vHintPos;
- GetHintNode()->GetPosition(this, &vHintPos);
-
- SetNavType( NAV_FLY );
- CapabilitiesAdd( bits_CAP_MOVE_FLY );
- // @HACKHACK: Force allow triangulation. Too many HL2 maps were relying on this feature WRT fly nodes (toml 8/1/2007)
- NPC_STATE state = GetState();
- m_NPCState = NPC_STATE_SCRIPT;
- bool bFoundPath = GetNavigator()->SetGoal( vHintPos );
- m_NPCState = state;
- if ( !bFoundPath )
- {
- GetHintNode()->DisableForSeconds( .3 );
- SetHintNode(NULL);
- }
- CapabilitiesRemove( bits_CAP_MOVE_FLY );
- }
-
- if ( GetHintNode() )
- {
- m_bReachedMoveGoal = false;
- TaskComplete();
- }
- else
- {
- TaskFail( FAIL_NO_ROUTE );
- }
- break;
- }
-
- //
- // We have failed to fly normally. Pick a random "up" direction and fly that way.
- //
- case TASK_CROW_FLY:
- {
- float flYaw = UTIL_AngleMod( random->RandomInt( -180, 180 ) );
-
- Vector vecNewVelocity( cos( DEG2RAD( flYaw ) ), sin( DEG2RAD( flYaw ) ), random->RandomFloat( 0.1f, 0.5f ) );
- vecNewVelocity *= CROW_AIRSPEED;
- SetAbsVelocity( vecNewVelocity );
-
- SetIdealActivity( ACT_FLY );
-
- m_bSoar = false;
- m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 2, 5 );
-
- break;
- }
-
- case TASK_CROW_PICK_RANDOM_GOAL:
- {
- m_vSavePosition = GetLocalOrigin() + Vector( random->RandomFloat( -48.0f, 48.0f ), random->RandomFloat( -48.0f, 48.0f ), 0 );
- TaskComplete();
- break;
- }
-
- case TASK_CROW_HOP:
- {
- SetIdealActivity( ACT_HOP );
- m_flHopStartZ = GetLocalOrigin().z;
- break;
- }
-
- case TASK_CROW_WAIT_FOR_BARNACLE_KILL:
- {
- break;
- }
-
- default:
- {
- BaseClass::StartTask( pTask );
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pTask -
-//-----------------------------------------------------------------------------
-void CNPC_Crow::RunTask( const Task_t *pTask )
-{
- switch ( pTask->iTask )
- {
- case TASK_CROW_TAKEOFF:
- {
- if ( GetNavigator()->IsGoalActive() )
- {
- GetMotor()->SetIdealYawToTargetAndUpdate( GetAbsOrigin() + GetNavigator()->GetCurWaypointPos(), AI_KEEP_YAW_SPEED );
- }
- else
- TaskFail( FAIL_NO_ROUTE );
-
- if ( IsActivityFinished() )
- {
- TaskComplete();
- SetIdealActivity( ACT_FLY );
-
- m_bSoar = false;
- m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 2, 5 );
- }
-
- break;
- }
-
- case TASK_CROW_HOP:
- {
- if ( IsActivityFinished() )
- {
- TaskComplete();
- SetIdealActivity( ACT_IDLE );
- }
-
- if ( ( GetAbsOrigin().z < m_flHopStartZ ) && ( !( GetFlags() & FL_ONGROUND ) ) )
- {
- //
- // We've hopped off of something! See if we're going to fall very far.
- //
- trace_t tr;
- AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, -32 ), MASK_SOLID, this, HL2COLLISION_GROUP_CROW, &tr );
- if ( tr.fraction == 1.0f )
- {
- //
- // We're falling! Better fly away. SelectSchedule will check ONGROUND and do the right thing.
- //
- TaskComplete();
- }
- else
- {
- //
- // We'll be okay. Don't check again unless what we're hopping onto moves
- // out from under us.
- //
- m_flHopStartZ = GetAbsOrigin().z - ( 32 * tr.fraction );
- }
- }
-
- break;
- }
-
- //
- // Face the direction we are flying.
- //
- case TASK_CROW_FLY:
- {
- GetMotor()->SetIdealYawToTargetAndUpdate( GetAbsOrigin() + GetAbsVelocity(), AI_KEEP_YAW_SPEED );
-
- break;
- }
-
- case TASK_CROW_FALL_TO_GROUND:
- {
- if ( GetFlags() & FL_ONGROUND )
- {
- SetFlyingState( FlyState_Walking );
- TaskComplete();
- }
- break;
- }
-
- case TASK_CROW_WAIT_FOR_BARNACLE_KILL:
- {
- if ( m_flNextFlinchTime < gpGlobals->curtime )
- {
- m_flNextFlinchTime = gpGlobals->curtime + random->RandomFloat( 0.5f, 2.0f );
- // dvs: TODO: squirm
- // dvs: TODO: spawn feathers
- EmitSound( "NPC_Crow.Squawk" );
- }
- break;
- }
-
- default:
- {
- CAI_BaseNPC::RunTask( pTask );
- }
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose: Override to do crow specific gibs.
-// Output : Returns true to gib, false to not gib.
-//-----------------------------------------------------------------------------
-bool CNPC_Crow::CorpseGib( const CTakeDamageInfo &info )
-{
- EmitSound( "NPC_Crow.Gib" );
-
- // TODO: crow gibs?
- //CGib::SpawnSpecificGibs( this, CROW_GIB_COUNT, 300, 400, "models/gibs/crow_gibs.mdl");
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Don't allow ridiculous forces to be applied to the crow. It only weighs
-// 1.5kg, so extreme forces will give it ridiculous velocity.
-//-----------------------------------------------------------------------------
-#define CROW_RAGDOLL_SPEED_LIMIT 1000.0f // Crow ragdoll speed limit in inches per second.
-bool CNPC_Crow::BecomeRagdollOnClient( const Vector &force )
-{
- Vector newForce = force;
-
- if( VPhysicsGetObject() )
- {
- float flMass = VPhysicsGetObject()->GetMass();
- float speed = VectorNormalize( newForce );
- speed = MIN( speed, (CROW_RAGDOLL_SPEED_LIMIT * flMass) );
- newForce *= speed;
- }
-
- return BaseClass::BecomeRagdollOnClient( newForce );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CNPC_Crow::FValidateHintType( CAI_Hint *pHint )
-{
- return( pHint->HintType() == HINT_CROW_FLYTO_POINT );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the activity for the given hint type.
-// Input : sHintType -
-//-----------------------------------------------------------------------------
-Activity CNPC_Crow::GetHintActivity( short sHintType, Activity HintsActivity )
-{
- if ( sHintType == HINT_CROW_FLYTO_POINT )
- {
- return ACT_FLY;
- }
-
- return BaseClass::GetHintActivity( sHintType, HintsActivity );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pevInflictor -
-// pevAttacker -
-// flDamage -
-// bitsDamageType -
-//-----------------------------------------------------------------------------
-int CNPC_Crow::OnTakeDamage_Alive( const CTakeDamageInfo &info )
-{
- // TODO: spew a feather or two
- return BaseClass::OnTakeDamage_Alive( info );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the best new schedule for this NPC based on current conditions.
-//-----------------------------------------------------------------------------
-int CNPC_Crow::SelectSchedule( void )
-{
- if ( HasCondition( COND_CROW_BARNACLED ) )
- {
- // Caught by a barnacle!
- return SCHED_CROW_BARNACLED;
- }
-
- //
- // If we're flying, just find somewhere to fly to.
- //
- if ( IsFlying() )
- {
- return SCHED_CROW_IDLE_FLY;
- }
-
- //
- // If we were told to fly away via our FlyAway input, do so ASAP.
- //
- if ( HasCondition( COND_CROW_FORCED_FLY ) )
- {
- ClearCondition( COND_CROW_FORCED_FLY );
- return SCHED_CROW_FLY_AWAY;
- }
-
- //
- // If we're not flying but we're not on the ground, start flying.
- // Maybe we hopped off of something? Don't do this immediately upon
- // because we may be falling to the ground on spawn.
- //
- if ( !( GetFlags() & FL_ONGROUND ) && ( gpGlobals->curtime > 2.0 ) && m_bOnJeep == false )
- {
- return SCHED_CROW_FLY_AWAY;
- }
-
- //
- // If we heard a gunshot or have taken damage, fly away.
- //
- if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
- {
- return SCHED_CROW_FLY_AWAY;
- }
-
- if ( m_flDangerSoundTime <= gpGlobals->curtime )
- {
- if ( HasCondition( COND_HEAR_DANGER ) || HasCondition( COND_HEAR_COMBAT ) )
- {
- m_flDangerSoundTime = gpGlobals->curtime + 10.0f;
- return SCHED_CROW_FLY_AWAY;
- }
- }
-
- //
- // If someone we hate is getting WAY too close for comfort, fly away.
- //
- if ( HasCondition( COND_CROW_ENEMY_WAY_TOO_CLOSE ) )
- {
- ClearCondition( COND_CROW_ENEMY_WAY_TOO_CLOSE );
-
- m_nMorale = 0;
- return SCHED_CROW_FLY_AWAY;
- }
-
- //
- // If someone we hate is getting a little too close for comfort, avoid them.
- //
- if ( HasCondition( COND_CROW_ENEMY_TOO_CLOSE ) && m_flDangerSoundTime <= gpGlobals->curtime )
- {
- ClearCondition( COND_CROW_ENEMY_TOO_CLOSE );
-
- if ( m_bOnJeep == true )
- {
- m_nMorale = 0;
- return SCHED_CROW_FLY_AWAY;
- }
-
- if ( m_flEnemyDist > 400 )
- {
- return SCHED_CROW_WALK_AWAY;
- }
- else if ( m_flEnemyDist > 300 )
- {
- m_nMorale -= 1;
- return SCHED_CROW_RUN_AWAY;
- }
- }
-
- switch ( m_NPCState )
- {
- case NPC_STATE_IDLE:
- case NPC_STATE_ALERT:
- case NPC_STATE_COMBAT:
- {
- if ( !IsFlying() )
- {
- if ( m_bOnJeep == true )
- return SCHED_IDLE_STAND;
-
- //
- // If we are hanging out on the ground, see if it is time to pick a new place to walk to.
- //
- if ( gpGlobals->curtime > m_flGroundIdleMoveTime )
- {
- m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 10.0f, 20.0f );
- return SCHED_CROW_IDLE_WALK;
- }
-
- return SCHED_IDLE_STAND;
- }
-
- // TODO: need idle flying behaviors!
- }
- }
-
- return BaseClass::SelectSchedule();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_Crow::Precache( void )
-{
- BaseClass::Precache();
-
- PrecacheModel( "models/crow.mdl" );
- PrecacheModel( "models/pigeon.mdl" );
- PrecacheModel( "models/seagull.mdl" );
-
- //Crow
- PrecacheScriptSound( "NPC_Crow.Hop" );
- PrecacheScriptSound( "NPC_Crow.Squawk" );
- PrecacheScriptSound( "NPC_Crow.Gib" );
- PrecacheScriptSound( "NPC_Crow.Idle" );
- PrecacheScriptSound( "NPC_Crow.Alert" );
- PrecacheScriptSound( "NPC_Crow.Die" );
- PrecacheScriptSound( "NPC_Crow.Pain" );
- PrecacheScriptSound( "NPC_Crow.Flap" );
-
- //Seagull
- PrecacheScriptSound( "NPC_Seagull.Pain" );
- PrecacheScriptSound( "NPC_Seagull.Idle" );
-
- //Pigeon
- PrecacheScriptSound( "NPC_Pigeon.Idle");
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Sounds.
-//-----------------------------------------------------------------------------
-void CNPC_Crow::IdleSound( void )
-{
- if ( m_iBirdType != BIRDTYPE_CROW )
- return;
-
- EmitSound( "NPC_Crow.Idle" );
-}
-
-
-void CNPC_Crow::AlertSound( void )
-{
- if ( m_iBirdType != BIRDTYPE_CROW )
- return;
-
- EmitSound( "NPC_Crow.Alert" );
-}
-
-
-void CNPC_Crow::PainSound( const CTakeDamageInfo &info )
-{
- if ( m_iBirdType != BIRDTYPE_CROW )
- return;
-
- EmitSound( "NPC_Crow.Pain" );
-}
-
-
-void CNPC_Crow::DeathSound( const CTakeDamageInfo &info )
-{
- if ( m_iBirdType != BIRDTYPE_CROW )
- return;
-
- EmitSound( "NPC_Crow.Die" );
-}
-
-void CNPC_Crow::FlapSound( void )
-{
- EmitSound( "NPC_Crow.Flap" );
- m_bPlayedLoopingSound = true;
-}
-
-
-//-----------------------------------------------------------------------------
-// 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_Crow::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sourceEnt )
-{
- if ( interactionType == g_interactionBarnacleVictimDangle )
- {
- // Die instantly
- return false;
- }
- else if ( interactionType == g_interactionBarnacleVictimGrab )
- {
- if ( GetFlags() & FL_ONGROUND )
- {
- SetGroundEntity( NULL );
- }
-
- // return ideal grab position
- if (data)
- {
- // FIXME: need a good way to ensure this contract
- *((Vector *)data) = GetAbsOrigin() + Vector( 0, 0, 5 );
- }
-
- StopLoopingSounds();
-
- SetThink( NULL );
- return true;
- }
-
- return BaseClass::HandleInteraction( interactionType, data, sourceEnt );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CNPC_Crow::DrawDebugTextOverlays( void )
-{
- int nOffset = BaseClass::DrawDebugTextOverlays();
-
- if (m_debugOverlays & OVERLAY_TEXT_BIT)
- {
- char tempstr[512];
- Q_snprintf( tempstr, sizeof( tempstr ), "morale: %d", m_nMorale );
- EntityText( nOffset, tempstr, 0 );
- nOffset++;
-
- if ( GetEnemy() != NULL )
- {
- Q_snprintf( tempstr, sizeof( tempstr ), "enemy (dist): %s (%g)", GetEnemy()->GetClassname(), ( double )m_flEnemyDist );
- EntityText( nOffset, tempstr, 0 );
- nOffset++;
- }
- }
-
- return nOffset;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Determines which sounds the crow cares about.
-//-----------------------------------------------------------------------------
-int CNPC_Crow::GetSoundInterests( void )
-{
- return SOUND_WORLD | SOUND_COMBAT | SOUND_PLAYER | SOUND_DANGER;
-}
-
-
-//-----------------------------------------------------------------------------
-//
-// Schedules
-//
-//-----------------------------------------------------------------------------
-
-AI_BEGIN_CUSTOM_NPC( npc_crow, CNPC_Crow )
-
- DECLARE_TASK( TASK_CROW_FIND_FLYTO_NODE )
- //DECLARE_TASK( TASK_CROW_PREPARE_TO_FLY )
- DECLARE_TASK( TASK_CROW_TAKEOFF )
- DECLARE_TASK( TASK_CROW_FLY )
- DECLARE_TASK( TASK_CROW_PICK_RANDOM_GOAL )
- DECLARE_TASK( TASK_CROW_HOP )
- DECLARE_TASK( TASK_CROW_PICK_EVADE_GOAL )
- DECLARE_TASK( TASK_CROW_WAIT_FOR_BARNACLE_KILL )
-
- // experiment
- DECLARE_TASK( TASK_CROW_FALL_TO_GROUND )
- DECLARE_TASK( TASK_CROW_PREPARE_TO_FLY_RANDOM )
-
- DECLARE_ACTIVITY( ACT_CROW_TAKEOFF )
- DECLARE_ACTIVITY( ACT_CROW_SOAR )
- DECLARE_ACTIVITY( ACT_CROW_LAND )
-
- DECLARE_ANIMEVENT( AE_CROW_HOP )
- DECLARE_ANIMEVENT( AE_CROW_FLY )
- DECLARE_ANIMEVENT( AE_CROW_TAKEOFF )
-
-
- DECLARE_CONDITION( COND_CROW_ENEMY_TOO_CLOSE )
- DECLARE_CONDITION( COND_CROW_ENEMY_WAY_TOO_CLOSE )
- DECLARE_CONDITION( COND_CROW_FORCED_FLY )
- DECLARE_CONDITION( COND_CROW_BARNACLED )
-
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CROW_IDLE_WALK,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_IDLE_STAND"
- " TASK_CROW_PICK_RANDOM_GOAL 0"
- " TASK_GET_PATH_TO_SAVEPOSITION 0"
- " TASK_WALK_PATH 0"
- " TASK_WAIT_FOR_MOVEMENT 0"
- " TASK_WAIT_PVS 0"
- " "
- " Interrupts"
- " COND_CROW_FORCED_FLY"
- " COND_PROVOKED"
- " COND_CROW_ENEMY_TOO_CLOSE"
- " COND_NEW_ENEMY"
- " COND_HEAVY_DAMAGE"
- " COND_LIGHT_DAMAGE"
- " COND_HEAVY_DAMAGE"
- " COND_HEAR_DANGER"
- " COND_HEAR_COMBAT"
- )
-
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CROW_WALK_AWAY,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_AWAY"
- " TASK_CROW_PICK_EVADE_GOAL 0"
- " TASK_GET_PATH_TO_SAVEPOSITION 0"
- " TASK_WALK_PATH 0"
- " TASK_WAIT_FOR_MOVEMENT 0"
- " "
- " Interrupts"
- " COND_CROW_FORCED_FLY"
- " COND_CROW_ENEMY_WAY_TOO_CLOSE"
- " COND_NEW_ENEMY"
- " COND_HEAVY_DAMAGE"
- " COND_LIGHT_DAMAGE"
- " COND_HEAVY_DAMAGE"
- " COND_HEAR_DANGER"
- " COND_HEAR_COMBAT"
- )
-
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CROW_RUN_AWAY,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_AWAY"
- " TASK_CROW_PICK_EVADE_GOAL 0"
- " TASK_GET_PATH_TO_SAVEPOSITION 0"
- " TASK_RUN_PATH 0"
- " TASK_WAIT_FOR_MOVEMENT 0"
- " "
- " Interrupts"
- " COND_CROW_FORCED_FLY"
- " COND_CROW_ENEMY_WAY_TOO_CLOSE"
- " COND_NEW_ENEMY"
- " COND_HEAVY_DAMAGE"
- " COND_LIGHT_DAMAGE"
- " COND_HEAVY_DAMAGE"
- " COND_HEAR_DANGER"
- " COND_HEAR_COMBAT"
- )
-
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CROW_HOP_AWAY,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_AWAY"
- " TASK_STOP_MOVING 0"
- " TASK_CROW_PICK_EVADE_GOAL 0"
- " TASK_FACE_IDEAL 0"
- " TASK_CROW_HOP 0"
- " "
- " Interrupts"
- " COND_CROW_FORCED_FLY"
- " COND_HEAVY_DAMAGE"
- " COND_LIGHT_DAMAGE"
- " COND_HEAVY_DAMAGE"
- " COND_HEAR_DANGER"
- " COND_HEAR_COMBAT"
- )
-
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CROW_IDLE_FLY,
-
- " Tasks"
- " TASK_FIND_HINTNODE 0"
- " TASK_GET_PATH_TO_HINTNODE 0"
- " TASK_WAIT_FOR_MOVEMENT 0"
- " "
- " Interrupts"
- )
-
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CROW_FLY_AWAY,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_FAIL"
- " TASK_STOP_MOVING 0"
- " TASK_FIND_HINTNODE 0"
- " TASK_GET_PATH_TO_HINTNODE 0"
- " TASK_CROW_TAKEOFF 0"
- " TASK_WAIT_FOR_MOVEMENT 0"
- " "
- " Interrupts"
- )
-
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CROW_FLY,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_FAIL"
- " TASK_STOP_MOVING 0"
- " TASK_CROW_TAKEOFF 0"
- " TASK_CROW_FLY 0"
- " "
- " Interrupts"
- )
-
- //=========================================================
- DEFINE_SCHEDULE
- (
- SCHED_CROW_FLY_FAIL,
-
- " Tasks"
- " TASK_CROW_FALL_TO_GROUND 0"
- " TASK_SET_SCHEDULE SCHEDULE:SCHED_CROW_IDLE_WALK"
- " "
- " Interrupts"
- )
-
- //=========================================================
- // Crow is in the clutches of a barnacle
- DEFINE_SCHEDULE
- (
- SCHED_CROW_BARNACLED,
-
- " Tasks"
- " TASK_STOP_MOVING 0"
- " TASK_SET_ACTIVITY ACTIVITY:ACT_HOP"
- " TASK_CROW_WAIT_FOR_BARNACLE_KILL 0"
-
- " Interrupts"
- )
-
-
-AI_END_CUSTOM_NPC()
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Crows. Simple ambient birds that fly away when they hear gunfire or
+// when anything gets too close to them.
+//
+// TODO: landing
+// TODO: death
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "game.h"
+#include "ai_basenpc.h"
+#include "ai_schedule.h"
+#include "ai_hull.h"
+#include "ai_hint.h"
+#include "ai_motor.h"
+#include "ai_navigator.h"
+#include "hl2_shareddefs.h"
+#include "ai_route.h"
+#include "npcevent.h"
+#include "gib.h"
+#include "ai_interactions.h"
+#include "ndebugoverlay.h"
+#include "soundent.h"
+#include "vstdlib/random.h"
+#include "engine/IEngineSound.h"
+#include "movevars_shared.h"
+#include "npc_crow.h"
+#include "ai_moveprobe.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//
+// Custom activities.
+//
+static int ACT_CROW_TAKEOFF;
+static int ACT_CROW_SOAR;
+static int ACT_CROW_LAND;
+
+//
+// Animation events.
+//
+static int AE_CROW_TAKEOFF;
+static int AE_CROW_FLY;
+static int AE_CROW_HOP;
+
+//
+// Skill settings.
+//
+ConVar sk_crow_health( "sk_crow_health","1");
+ConVar sk_crow_melee_dmg( "sk_crow_melee_dmg","0");
+
+LINK_ENTITY_TO_CLASS( npc_crow, CNPC_Crow );
+LINK_ENTITY_TO_CLASS( npc_seagull, CNPC_Seagull );
+LINK_ENTITY_TO_CLASS( npc_pigeon, CNPC_Pigeon );
+
+BEGIN_DATADESC( CNPC_Crow )
+
+ DEFINE_FIELD( m_flGroundIdleMoveTime, FIELD_TIME ),
+ DEFINE_FIELD( m_bOnJeep, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flEnemyDist, FIELD_FLOAT ),
+ DEFINE_FIELD( m_nMorale, FIELD_INTEGER ),
+ DEFINE_FIELD( m_bReachedMoveGoal, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flHopStartZ, FIELD_FLOAT ),
+ DEFINE_FIELD( m_vDesiredTarget, FIELD_VECTOR ),
+ DEFINE_FIELD( m_vCurrentTarget, FIELD_VECTOR ),
+ DEFINE_FIELD( m_flSoarTime, FIELD_TIME ),
+ DEFINE_FIELD( m_bSoar, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bPlayedLoopingSound, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_iBirdType, FIELD_INTEGER ),
+ DEFINE_FIELD( m_vLastStoredOrigin, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_flLastStuckCheck, FIELD_TIME ),
+ DEFINE_FIELD( m_flDangerSoundTime, FIELD_TIME ),
+ DEFINE_KEYFIELD( m_bIsDeaf, FIELD_BOOLEAN, "deaf" ),
+
+ // Inputs
+ DEFINE_INPUTFUNC( FIELD_STRING, "FlyAway", InputFlyAway ),
+
+END_DATADESC()
+
+static ConVar birds_debug( "birds_debug", "0" );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_Crow::Spawn( void )
+{
+ BaseClass::Spawn();
+
+#ifdef _XBOX
+ // Always fade the corpse
+ AddSpawnFlags( SF_NPC_FADE_CORPSE );
+#endif // _XBOX
+
+ char *szModel = (char *)STRING( GetModelName() );
+ if (!szModel || !*szModel)
+ {
+ szModel = "models/crow.mdl";
+ SetModelName( AllocPooledString(szModel) );
+ }
+
+ Precache();
+ SetModel( szModel );
+
+ m_iHealth = sk_crow_health.GetFloat();
+
+ SetHullType(HULL_TINY);
+ SetHullSizeNormal();
+
+ SetSolid( SOLID_BBOX );
+ SetMoveType( MOVETYPE_STEP );
+
+ m_flFieldOfView = VIEW_FIELD_FULL;
+ SetViewOffset( Vector(6, 0, 11) ); // Position of the eyes relative to NPC's origin.
+
+ m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 0.0f, 5.0f );
+
+ SetBloodColor( BLOOD_COLOR_RED );
+ m_NPCState = NPC_STATE_NONE;
+
+ m_nMorale = random->RandomInt( 0, 12 );
+
+ SetCollisionGroup( HL2COLLISION_GROUP_CROW );
+
+ CapabilitiesClear();
+
+ bool bFlying = ( ( m_spawnflags & SF_CROW_FLYING ) != 0 );
+ SetFlyingState( bFlying ? FlyState_Flying : FlyState_Walking );
+
+ // We don't mind zombies so much. They smell good!
+ AddClassRelationship( CLASS_ZOMBIE, D_NU, 0 );
+
+ m_bSoar = false;
+ m_bOnJeep = false;
+ m_flSoarTime = gpGlobals->curtime;
+
+ NPCInit();
+
+ m_iBirdType = BIRDTYPE_CROW;
+
+ m_vLastStoredOrigin = vec3_origin;
+ m_flLastStuckCheck = gpGlobals->curtime;
+
+ m_flDangerSoundTime = gpGlobals->curtime;
+
+ SetGoalEnt( NULL );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns this monster's classification in the relationship table.
+//-----------------------------------------------------------------------------
+Class_T CNPC_Crow::Classify( void )
+{
+ return( CLASS_EARTH_FAUNA );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pEnemy -
+//-----------------------------------------------------------------------------
+void CNPC_Crow::GatherEnemyConditions( CBaseEntity *pEnemy )
+{
+ m_flEnemyDist = (GetLocalOrigin() - pEnemy->GetLocalOrigin()).Length();
+
+ if ( m_flEnemyDist < 512 )
+ {
+ SetCondition( COND_CROW_ENEMY_WAY_TOO_CLOSE );
+ }
+
+ if ( m_flEnemyDist < 1024 )
+ {
+ SetCondition( COND_CROW_ENEMY_TOO_CLOSE );
+ }
+
+ BaseClass::GatherEnemyConditions(pEnemy);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : posSrc -
+// Output : Vector
+//-----------------------------------------------------------------------------
+Vector CNPC_Crow::BodyTarget( const Vector &posSrc, bool bNoisy )
+{
+ Vector vecResult;
+ vecResult = GetAbsOrigin();
+ vecResult.z += 6;
+ return vecResult;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_Crow::StopLoopingSounds( void )
+{
+ //
+ // Stop whatever flap sound might be playing.
+ //
+ if ( m_bPlayedLoopingSound )
+ {
+ StopSound( "NPC_Crow.Flap" );
+ }
+ BaseClass::StopLoopingSounds();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Catches the monster-specific messages that occur when tagged
+// animation frames are played.
+// Input : pEvent -
+//-----------------------------------------------------------------------------
+void CNPC_Crow::HandleAnimEvent( animevent_t *pEvent )
+{
+ if ( pEvent->event == AE_CROW_TAKEOFF )
+ {
+ if ( GetNavigator()->GetPath()->GetCurWaypoint() )
+ {
+ Takeoff( GetNavigator()->GetCurWaypointPos() );
+ }
+ return;
+ }
+
+ if( pEvent->event == AE_CROW_HOP )
+ {
+ SetGroundEntity( NULL );
+
+ //
+ // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
+ //
+ UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
+
+ //
+ // How fast does the crow need to travel to reach the hop goal given gravity?
+ //
+ float flHopDistance = ( m_vSavePosition - GetLocalOrigin() ).Length();
+ float gravity = GetCurrentGravity();
+ if ( gravity <= 1 )
+ {
+ gravity = 1;
+ }
+
+ float height = 0.25 * flHopDistance;
+ float speed = sqrt( 2 * gravity * height );
+ float time = speed / gravity;
+
+ //
+ // Scale the sideways velocity to get there at the right time
+ //
+ Vector vecJumpDir = m_vSavePosition - GetLocalOrigin();
+ vecJumpDir = vecJumpDir / time;
+
+ //
+ // Speed to offset gravity at the desired height.
+ //
+ vecJumpDir.z = speed;
+
+ //
+ // Don't jump too far/fast.
+ //
+ float distance = vecJumpDir.Length();
+ if ( distance > 650 )
+ {
+ vecJumpDir = vecJumpDir * ( 650.0 / distance );
+ }
+
+ m_nMorale -= random->RandomInt( 1, 6 );
+ if ( m_nMorale <= 0 )
+ {
+ m_nMorale = 0;
+ }
+
+ // Play a hop flap sound.
+ EmitSound( "NPC_Crow.Hop" );
+
+ SetAbsVelocity( vecJumpDir );
+ return;
+ }
+
+ if( pEvent->event == AE_CROW_FLY )
+ {
+ //
+ // Start flying.
+ //
+ SetActivity( ACT_FLY );
+
+ m_bSoar = false;
+ m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 3, 5 );
+
+ return;
+ }
+
+ CAI_BaseNPC::HandleAnimEvent( pEvent );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : eNewActivity -
+//-----------------------------------------------------------------------------
+void CNPC_Crow::OnChangeActivity( Activity eNewActivity )
+{
+// if ( eNewActivity == ACT_FLY )
+// {
+// m_flGroundSpeed = CROW_AIRSPEED;
+// }
+//
+ bool fRandomize = false;
+ if ( eNewActivity == ACT_FLY )
+ {
+ fRandomize = true;
+ }
+
+ BaseClass::OnChangeActivity( eNewActivity );
+ if ( fRandomize )
+ {
+ SetCycle( random->RandomFloat( 0.0, 0.75 ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handler that makes the crow fly away.
+//-----------------------------------------------------------------------------
+void CNPC_Crow::InputFlyAway( inputdata_t &inputdata )
+{
+ string_t sTarget = MAKE_STRING( inputdata.value.String() );
+
+ if ( sTarget != NULL_STRING )// this npc has a target
+ {
+ CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, sTarget );
+
+ if ( pEnt )
+ {
+ trace_t tr;
+ AI_TraceLine ( EyePosition(), pEnt->GetAbsOrigin(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
+
+ if ( tr.fraction != 1.0f )
+ return;
+
+ // Find the npc's initial target entity, stash it
+ SetGoalEnt( pEnt );
+ }
+ }
+ else
+ SetGoalEnt( NULL );
+
+ SetCondition( COND_CROW_FORCED_FLY );
+ SetCondition( COND_PROVOKED );
+
+}
+
+void CNPC_Crow::UpdateEfficiency( bool bInPVS )
+{
+ if ( IsFlying() )
+ {
+ SetEfficiency( ( GetSleepState() != AISS_AWAKE ) ? AIE_DORMANT : AIE_NORMAL );
+ SetMoveEfficiency( AIME_NORMAL );
+ return;
+ }
+
+ BaseClass::UpdateEfficiency( bInPVS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Implements "deafness"
+//-----------------------------------------------------------------------------
+bool CNPC_Crow::QueryHearSound( CSound *pSound )
+{
+ if( IsDeaf() )
+ return false;
+
+ return BaseClass::QueryHearSound( pSound );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles all flight movement because we don't ever build paths when
+// when we are flying.
+// Input : flInterval - Seconds to simulate.
+//-----------------------------------------------------------------------------
+bool CNPC_Crow::OverrideMove( float flInterval )
+{
+ if ( GetNavigator()->GetPath()->CurWaypointNavType() == NAV_FLY && GetNavigator()->GetNavType() != NAV_FLY )
+ {
+ SetNavType( NAV_FLY );
+ }
+
+ if ( IsFlying() )
+ {
+ if ( GetNavigator()->GetPath()->GetCurWaypoint() )
+ {
+ if ( m_flLastStuckCheck <= gpGlobals->curtime )
+ {
+ if ( m_vLastStoredOrigin == GetAbsOrigin() )
+ {
+ if ( GetAbsVelocity() == vec3_origin )
+ {
+ float flDamage = m_iHealth;
+
+ CTakeDamageInfo dmgInfo( this, this, flDamage, DMG_GENERIC );
+ GuessDamageForce( &dmgInfo, vec3_origin - Vector( 0, 0, 0.1 ), GetAbsOrigin() );
+ TakeDamage( dmgInfo );
+
+ return false;
+ }
+ else
+ {
+ m_vLastStoredOrigin = GetAbsOrigin();
+ }
+ }
+ else
+ {
+ m_vLastStoredOrigin = GetAbsOrigin();
+ }
+
+ m_flLastStuckCheck = gpGlobals->curtime + 1.0f;
+ }
+
+ if (m_bReachedMoveGoal )
+ {
+ SetIdealActivity( (Activity)ACT_CROW_LAND );
+ SetFlyingState( FlyState_Landing );
+ TaskMovementComplete();
+ }
+ else
+ {
+ SetIdealActivity ( ACT_FLY );
+ MoveCrowFly( flInterval );
+ }
+
+ }
+ else if ( !GetTask() || GetTask()->iTask == TASK_WAIT_FOR_MOVEMENT )
+ {
+ SetSchedule( SCHED_CROW_IDLE_FLY );
+ SetFlyingState( FlyState_Flying );
+ SetIdealActivity ( ACT_FLY );
+ }
+ return true;
+ }
+
+ return false;
+}
+
+Activity CNPC_Crow::NPC_TranslateActivity( Activity eNewActivity )
+{
+ if ( IsFlying() && eNewActivity == ACT_IDLE )
+ {
+ return ACT_FLY;
+ }
+
+ if ( eNewActivity == ACT_FLY )
+ {
+ if ( m_flSoarTime < gpGlobals->curtime )
+ {
+ //Adrian: This should be revisited.
+ if ( random->RandomInt( 0, 100 ) <= 50 && m_bSoar == false && GetAbsVelocity().z < 0 )
+ {
+ m_bSoar = true;
+ m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 1, 4 );
+ }
+ else
+ {
+ m_bSoar = false;
+ m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 3, 5 );
+ }
+ }
+
+ if ( m_bSoar == true )
+ {
+ return (Activity)ACT_CROW_SOAR;
+ }
+ else
+ return ACT_FLY;
+ }
+
+ return BaseClass::NPC_TranslateActivity( eNewActivity );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles all flight movement.
+// Input : flInterval - Seconds to simulate.
+//-----------------------------------------------------------------------------
+void CNPC_Crow::MoveCrowFly( float flInterval )
+{
+ //
+ // Bound interval so we don't get ludicrous motion when debugging
+ // or when framerate drops catastrophically.
+ //
+ if (flInterval > 1.0)
+ {
+ flInterval = 1.0;
+ }
+
+ m_flDangerSoundTime = gpGlobals->curtime + 5.0f;
+
+ //
+ // Determine the goal of our movement.
+ //
+ Vector vecMoveGoal = GetAbsOrigin();
+
+ if ( GetNavigator()->IsGoalActive() )
+ {
+ vecMoveGoal = GetNavigator()->GetCurWaypointPos();
+
+ if ( GetNavigator()->CurWaypointIsGoal() == false )
+ {
+ AI_ProgressFlyPathParams_t params( MASK_NPCSOLID );
+ params.bTrySimplify = false;
+
+ GetNavigator()->ProgressFlyPath( params ); // ignore result, crow handles completion directly
+
+ // Fly towards the hint.
+ if ( GetNavigator()->GetPath()->GetCurWaypoint() )
+ {
+ vecMoveGoal = GetNavigator()->GetCurWaypointPos();
+ }
+ }
+ }
+ else
+ {
+ // No movement goal.
+ vecMoveGoal = GetAbsOrigin();
+ SetAbsVelocity( vec3_origin );
+ return;
+ }
+
+ Vector vecMoveDir = ( vecMoveGoal - GetAbsOrigin() );
+ Vector vForward;
+ AngleVectors( GetAbsAngles(), &vForward );
+
+ //
+ // Fly towards the movement goal.
+ //
+ float flDistance = ( vecMoveGoal - GetAbsOrigin() ).Length();
+
+ if ( vecMoveGoal != m_vDesiredTarget )
+ {
+ m_vDesiredTarget = vecMoveGoal;
+ }
+ else
+ {
+ m_vCurrentTarget = ( m_vDesiredTarget - GetAbsOrigin() );
+ VectorNormalize( m_vCurrentTarget );
+ }
+
+ float flLerpMod = 0.25f;
+
+ if ( flDistance <= 256.0f )
+ {
+ flLerpMod = 1.0f - ( flDistance / 256.0f );
+ }
+
+
+ VectorLerp( vForward, m_vCurrentTarget, flLerpMod, vForward );
+
+
+ if ( flDistance < CROW_AIRSPEED * flInterval )
+ {
+ if ( GetNavigator()->IsGoalActive() )
+ {
+ if ( GetNavigator()->CurWaypointIsGoal() )
+ {
+ m_bReachedMoveGoal = true;
+ }
+ else
+ {
+ GetNavigator()->AdvancePath();
+ }
+ }
+ else
+ m_bReachedMoveGoal = true;
+ }
+
+ if ( GetHintNode() )
+ {
+ AIMoveTrace_t moveTrace;
+ GetMoveProbe()->MoveLimit( NAV_FLY, GetAbsOrigin(), GetNavigator()->GetCurWaypointPos(), MASK_NPCSOLID, GetNavTargetEntity(), &moveTrace );
+
+ //See if it succeeded
+ if ( IsMoveBlocked( moveTrace.fStatus ) )
+ {
+ Vector vNodePos = vecMoveGoal;
+ GetHintNode()->GetPosition(this, &vNodePos);
+
+ GetNavigator()->SetGoal( vNodePos );
+ }
+ }
+
+ //
+ // Look to see if we are going to hit anything.
+ //
+ VectorNormalize( vForward );
+ Vector vecDeflect;
+ if ( Probe( vForward, CROW_AIRSPEED * flInterval, vecDeflect ) )
+ {
+ vForward = vecDeflect;
+ VectorNormalize( vForward );
+ }
+
+ SetAbsVelocity( vForward * CROW_AIRSPEED );
+
+ if ( GetAbsVelocity().Length() > 0 && GetNavigator()->CurWaypointIsGoal() && flDistance < CROW_AIRSPEED )
+ {
+ SetIdealActivity( (Activity)ACT_CROW_LAND );
+ }
+
+
+ //Bank and set angles.
+ Vector vRight;
+ QAngle vRollAngle;
+
+ VectorAngles( vForward, vRollAngle );
+ vRollAngle.z = 0;
+
+ AngleVectors( vRollAngle, NULL, &vRight, NULL );
+
+ float flRoll = DotProduct( vRight, vecMoveDir ) * 45;
+ flRoll = clamp( flRoll, -45, 45 );
+
+ vRollAngle[ROLL] = flRoll;
+ SetAbsAngles( vRollAngle );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Looks ahead to see if we are going to hit something. If we are, a
+// recommended avoidance path is returned.
+// Input : vecMoveDir -
+// flSpeed -
+// vecDeflect -
+// Output : Returns true if we hit something and need to deflect our course,
+// false if all is well.
+//-----------------------------------------------------------------------------
+bool CNPC_Crow::Probe( const Vector &vecMoveDir, float flSpeed, Vector &vecDeflect )
+{
+ //
+ // Look 1/2 second ahead.
+ //
+ trace_t tr;
+ AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + vecMoveDir * flSpeed, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, HL2COLLISION_GROUP_CROW, &tr );
+ if ( tr.fraction < 1.0f )
+ {
+ //
+ // If we hit something, deflect flight path parallel to surface hit.
+ //
+ Vector vecUp;
+ CrossProduct( vecMoveDir, tr.plane.normal, vecUp );
+ CrossProduct( tr.plane.normal, vecUp, vecDeflect );
+ VectorNormalize( vecDeflect );
+ return true;
+ }
+
+ vecDeflect = vec3_origin;
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Switches between flying mode and ground mode.
+//-----------------------------------------------------------------------------
+void CNPC_Crow::SetFlyingState( FlyState_t eState )
+{
+ if ( eState == FlyState_Flying )
+ {
+ // Flying
+ SetGroundEntity( NULL );
+ AddFlag( FL_FLY );
+ SetNavType( NAV_FLY );
+ CapabilitiesRemove( bits_CAP_MOVE_GROUND );
+ CapabilitiesAdd( bits_CAP_MOVE_FLY );
+ SetMoveType( MOVETYPE_STEP );
+ m_vLastStoredOrigin = GetAbsOrigin();
+ m_flLastStuckCheck = gpGlobals->curtime + 3.0f;
+ m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 5.0f, 10.0f );
+ }
+ else if ( eState == FlyState_Walking )
+ {
+ // Walking
+ QAngle angles = GetAbsAngles();
+ angles[PITCH] = 0.0f;
+ angles[ROLL] = 0.0f;
+ SetAbsAngles( angles );
+
+ RemoveFlag( FL_FLY );
+ SetNavType( NAV_GROUND );
+ CapabilitiesRemove( bits_CAP_MOVE_FLY );
+ CapabilitiesAdd( bits_CAP_MOVE_GROUND );
+ SetMoveType( MOVETYPE_STEP );
+ m_vLastStoredOrigin = vec3_origin;
+ m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 5.0f, 10.0f );
+ }
+ else
+ {
+ // Falling
+ RemoveFlag( FL_FLY );
+ SetNavType( NAV_GROUND );
+ CapabilitiesRemove( bits_CAP_MOVE_FLY );
+ CapabilitiesAdd( bits_CAP_MOVE_GROUND );
+ SetMoveType( MOVETYPE_STEP );
+ m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 5.0f, 10.0f );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Performs a takeoff. Called via an animation event at the moment
+// our feet leave the ground.
+// Input : pGoalEnt - The entity that we are going to fly toward.
+//-----------------------------------------------------------------------------
+void CNPC_Crow::Takeoff( const Vector &vGoal )
+{
+ if ( vGoal != vec3_origin )
+ {
+ //
+ // Lift us off ground so engine doesn't instantly reset FL_ONGROUND.
+ //
+ UTIL_SetOrigin( this, GetAbsOrigin() + Vector( 0 , 0 , 1 ));
+
+ //
+ // Fly straight at the goal entity at our maximum airspeed.
+ //
+ Vector vecMoveDir = vGoal - GetAbsOrigin();
+ VectorNormalize( vecMoveDir );
+
+ // FIXME: pitch over time
+
+ SetFlyingState( FlyState_Flying );
+
+ QAngle angles;
+ VectorAngles( vecMoveDir, angles );
+ SetAbsAngles( angles );
+
+ SetAbsVelocity( vecMoveDir * CROW_TAKEOFF_SPEED );
+ }
+}
+
+void CNPC_Crow::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+ CTakeDamageInfo newInfo = info;
+
+ if ( info.GetDamageType() & DMG_PHYSGUN )
+ {
+ Vector puntDir = ( info.GetDamageForce() * 5000.0f );
+
+ newInfo.SetDamage( m_iMaxHealth );
+
+ PainSound( newInfo );
+ newInfo.SetDamageForce( puntDir );
+ }
+
+ BaseClass::TraceAttack( newInfo, vecDir, ptr, pAccumulator );
+}
+
+
+void CNPC_Crow::StartTargetHandling( CBaseEntity *pTargetEnt )
+{
+ AI_NavGoal_t goal( GOALTYPE_PATHCORNER, pTargetEnt->GetAbsOrigin(),
+ ACT_FLY,
+ AIN_DEF_TOLERANCE, AIN_YAW_TO_DEST);
+
+ if ( !GetNavigator()->SetGoal( goal ) )
+ {
+ DevWarning( 2, "Can't Create Route!\n" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pTask -
+//-----------------------------------------------------------------------------
+void CNPC_Crow::StartTask( const Task_t *pTask )
+{
+ switch ( pTask->iTask )
+ {
+ //
+ // This task enables us to build a path that requires flight.
+ //
+// case TASK_CROW_PREPARE_TO_FLY:
+// {
+// SetFlyingState( FlyState_Flying );
+// TaskComplete();
+// break;
+// }
+
+ case TASK_CROW_TAKEOFF:
+ {
+ if ( random->RandomInt( 1, 4 ) == 1 )
+ {
+ AlertSound();
+ }
+
+ FlapSound();
+
+ SetIdealActivity( ( Activity )ACT_CROW_TAKEOFF );
+ break;
+ }
+
+ case TASK_CROW_PICK_EVADE_GOAL:
+ {
+ if ( GetEnemy() != NULL )
+ {
+ //
+ // Get our enemy's position in x/y.
+ //
+ Vector vecEnemyOrigin = GetEnemy()->GetAbsOrigin();
+ vecEnemyOrigin.z = GetAbsOrigin().z;
+
+ //
+ // Pick a hop goal a random distance along a vector away from our enemy.
+ //
+ m_vSavePosition = GetAbsOrigin() - vecEnemyOrigin;
+ VectorNormalize( m_vSavePosition );
+ m_vSavePosition = GetAbsOrigin() + m_vSavePosition * ( 32 + random->RandomInt( 0, 32 ) );
+
+ GetMotor()->SetIdealYawToTarget( m_vSavePosition );
+ TaskComplete();
+ }
+ else
+ {
+ TaskFail( "No enemy" );
+ }
+ break;
+ }
+
+ case TASK_CROW_FALL_TO_GROUND:
+ {
+ SetFlyingState( FlyState_Falling );
+ break;
+ }
+
+ case TASK_FIND_HINTNODE:
+ {
+ if ( GetGoalEnt() )
+ {
+ TaskComplete();
+ return;
+ }
+ // Overloaded because we search over a greater distance.
+ if ( !GetHintNode() )
+ {
+ SetHintNode(CAI_HintManager::FindHint( this, HINT_CROW_FLYTO_POINT, bits_HINT_NODE_NEAREST | bits_HINT_NODE_USE_GROUP, 10000 ));
+ }
+
+ if ( GetHintNode() )
+ {
+ TaskComplete();
+ }
+ else
+ {
+ TaskFail( FAIL_NO_HINT_NODE );
+ }
+ break;
+ }
+
+ case TASK_GET_PATH_TO_HINTNODE:
+ {
+ //How did this happen?!
+ if ( GetGoalEnt() == this )
+ {
+ SetGoalEnt( NULL );
+ }
+
+ if ( GetGoalEnt() )
+ {
+ SetFlyingState( FlyState_Flying );
+ StartTargetHandling( GetGoalEnt() );
+
+ m_bReachedMoveGoal = false;
+ TaskComplete();
+ SetHintNode( NULL );
+ return;
+ }
+
+ if ( GetHintNode() )
+ {
+ Vector vHintPos;
+ GetHintNode()->GetPosition(this, &vHintPos);
+
+ SetNavType( NAV_FLY );
+ CapabilitiesAdd( bits_CAP_MOVE_FLY );
+ // @HACKHACK: Force allow triangulation. Too many HL2 maps were relying on this feature WRT fly nodes (toml 8/1/2007)
+ NPC_STATE state = GetState();
+ m_NPCState = NPC_STATE_SCRIPT;
+ bool bFoundPath = GetNavigator()->SetGoal( vHintPos );
+ m_NPCState = state;
+ if ( !bFoundPath )
+ {
+ GetHintNode()->DisableForSeconds( .3 );
+ SetHintNode(NULL);
+ }
+ CapabilitiesRemove( bits_CAP_MOVE_FLY );
+ }
+
+ if ( GetHintNode() )
+ {
+ m_bReachedMoveGoal = false;
+ TaskComplete();
+ }
+ else
+ {
+ TaskFail( FAIL_NO_ROUTE );
+ }
+ break;
+ }
+
+ //
+ // We have failed to fly normally. Pick a random "up" direction and fly that way.
+ //
+ case TASK_CROW_FLY:
+ {
+ float flYaw = UTIL_AngleMod( random->RandomInt( -180, 180 ) );
+
+ Vector vecNewVelocity( cos( DEG2RAD( flYaw ) ), sin( DEG2RAD( flYaw ) ), random->RandomFloat( 0.1f, 0.5f ) );
+ vecNewVelocity *= CROW_AIRSPEED;
+ SetAbsVelocity( vecNewVelocity );
+
+ SetIdealActivity( ACT_FLY );
+
+ m_bSoar = false;
+ m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 2, 5 );
+
+ break;
+ }
+
+ case TASK_CROW_PICK_RANDOM_GOAL:
+ {
+ m_vSavePosition = GetLocalOrigin() + Vector( random->RandomFloat( -48.0f, 48.0f ), random->RandomFloat( -48.0f, 48.0f ), 0 );
+ TaskComplete();
+ break;
+ }
+
+ case TASK_CROW_HOP:
+ {
+ SetIdealActivity( ACT_HOP );
+ m_flHopStartZ = GetLocalOrigin().z;
+ break;
+ }
+
+ case TASK_CROW_WAIT_FOR_BARNACLE_KILL:
+ {
+ break;
+ }
+
+ default:
+ {
+ BaseClass::StartTask( pTask );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pTask -
+//-----------------------------------------------------------------------------
+void CNPC_Crow::RunTask( const Task_t *pTask )
+{
+ switch ( pTask->iTask )
+ {
+ case TASK_CROW_TAKEOFF:
+ {
+ if ( GetNavigator()->IsGoalActive() )
+ {
+ GetMotor()->SetIdealYawToTargetAndUpdate( GetAbsOrigin() + GetNavigator()->GetCurWaypointPos(), AI_KEEP_YAW_SPEED );
+ }
+ else
+ TaskFail( FAIL_NO_ROUTE );
+
+ if ( IsActivityFinished() )
+ {
+ TaskComplete();
+ SetIdealActivity( ACT_FLY );
+
+ m_bSoar = false;
+ m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 2, 5 );
+ }
+
+ break;
+ }
+
+ case TASK_CROW_HOP:
+ {
+ if ( IsActivityFinished() )
+ {
+ TaskComplete();
+ SetIdealActivity( ACT_IDLE );
+ }
+
+ if ( ( GetAbsOrigin().z < m_flHopStartZ ) && ( !( GetFlags() & FL_ONGROUND ) ) )
+ {
+ //
+ // We've hopped off of something! See if we're going to fall very far.
+ //
+ trace_t tr;
+ AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, -32 ), MASK_SOLID, this, HL2COLLISION_GROUP_CROW, &tr );
+ if ( tr.fraction == 1.0f )
+ {
+ //
+ // We're falling! Better fly away. SelectSchedule will check ONGROUND and do the right thing.
+ //
+ TaskComplete();
+ }
+ else
+ {
+ //
+ // We'll be okay. Don't check again unless what we're hopping onto moves
+ // out from under us.
+ //
+ m_flHopStartZ = GetAbsOrigin().z - ( 32 * tr.fraction );
+ }
+ }
+
+ break;
+ }
+
+ //
+ // Face the direction we are flying.
+ //
+ case TASK_CROW_FLY:
+ {
+ GetMotor()->SetIdealYawToTargetAndUpdate( GetAbsOrigin() + GetAbsVelocity(), AI_KEEP_YAW_SPEED );
+
+ break;
+ }
+
+ case TASK_CROW_FALL_TO_GROUND:
+ {
+ if ( GetFlags() & FL_ONGROUND )
+ {
+ SetFlyingState( FlyState_Walking );
+ TaskComplete();
+ }
+ break;
+ }
+
+ case TASK_CROW_WAIT_FOR_BARNACLE_KILL:
+ {
+ if ( m_flNextFlinchTime < gpGlobals->curtime )
+ {
+ m_flNextFlinchTime = gpGlobals->curtime + random->RandomFloat( 0.5f, 2.0f );
+ // dvs: TODO: squirm
+ // dvs: TODO: spawn feathers
+ EmitSound( "NPC_Crow.Squawk" );
+ }
+ break;
+ }
+
+ default:
+ {
+ CAI_BaseNPC::RunTask( pTask );
+ }
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose: Override to do crow specific gibs.
+// Output : Returns true to gib, false to not gib.
+//-----------------------------------------------------------------------------
+bool CNPC_Crow::CorpseGib( const CTakeDamageInfo &info )
+{
+ EmitSound( "NPC_Crow.Gib" );
+
+ // TODO: crow gibs?
+ //CGib::SpawnSpecificGibs( this, CROW_GIB_COUNT, 300, 400, "models/gibs/crow_gibs.mdl");
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Don't allow ridiculous forces to be applied to the crow. It only weighs
+// 1.5kg, so extreme forces will give it ridiculous velocity.
+//-----------------------------------------------------------------------------
+#define CROW_RAGDOLL_SPEED_LIMIT 1000.0f // Crow ragdoll speed limit in inches per second.
+bool CNPC_Crow::BecomeRagdollOnClient( const Vector &force )
+{
+ Vector newForce = force;
+
+ if( VPhysicsGetObject() )
+ {
+ float flMass = VPhysicsGetObject()->GetMass();
+ float speed = VectorNormalize( newForce );
+ speed = MIN( speed, (CROW_RAGDOLL_SPEED_LIMIT * flMass) );
+ newForce *= speed;
+ }
+
+ return BaseClass::BecomeRagdollOnClient( newForce );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CNPC_Crow::FValidateHintType( CAI_Hint *pHint )
+{
+ return( pHint->HintType() == HINT_CROW_FLYTO_POINT );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the activity for the given hint type.
+// Input : sHintType -
+//-----------------------------------------------------------------------------
+Activity CNPC_Crow::GetHintActivity( short sHintType, Activity HintsActivity )
+{
+ if ( sHintType == HINT_CROW_FLYTO_POINT )
+ {
+ return ACT_FLY;
+ }
+
+ return BaseClass::GetHintActivity( sHintType, HintsActivity );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pevInflictor -
+// pevAttacker -
+// flDamage -
+// bitsDamageType -
+//-----------------------------------------------------------------------------
+int CNPC_Crow::OnTakeDamage_Alive( const CTakeDamageInfo &info )
+{
+ // TODO: spew a feather or two
+ return BaseClass::OnTakeDamage_Alive( info );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the best new schedule for this NPC based on current conditions.
+//-----------------------------------------------------------------------------
+int CNPC_Crow::SelectSchedule( void )
+{
+ if ( HasCondition( COND_CROW_BARNACLED ) )
+ {
+ // Caught by a barnacle!
+ return SCHED_CROW_BARNACLED;
+ }
+
+ //
+ // If we're flying, just find somewhere to fly to.
+ //
+ if ( IsFlying() )
+ {
+ return SCHED_CROW_IDLE_FLY;
+ }
+
+ //
+ // If we were told to fly away via our FlyAway input, do so ASAP.
+ //
+ if ( HasCondition( COND_CROW_FORCED_FLY ) )
+ {
+ ClearCondition( COND_CROW_FORCED_FLY );
+ return SCHED_CROW_FLY_AWAY;
+ }
+
+ //
+ // If we're not flying but we're not on the ground, start flying.
+ // Maybe we hopped off of something? Don't do this immediately upon
+ // because we may be falling to the ground on spawn.
+ //
+ if ( !( GetFlags() & FL_ONGROUND ) && ( gpGlobals->curtime > 2.0 ) && m_bOnJeep == false )
+ {
+ return SCHED_CROW_FLY_AWAY;
+ }
+
+ //
+ // If we heard a gunshot or have taken damage, fly away.
+ //
+ if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
+ {
+ return SCHED_CROW_FLY_AWAY;
+ }
+
+ if ( m_flDangerSoundTime <= gpGlobals->curtime )
+ {
+ if ( HasCondition( COND_HEAR_DANGER ) || HasCondition( COND_HEAR_COMBAT ) )
+ {
+ m_flDangerSoundTime = gpGlobals->curtime + 10.0f;
+ return SCHED_CROW_FLY_AWAY;
+ }
+ }
+
+ //
+ // If someone we hate is getting WAY too close for comfort, fly away.
+ //
+ if ( HasCondition( COND_CROW_ENEMY_WAY_TOO_CLOSE ) )
+ {
+ ClearCondition( COND_CROW_ENEMY_WAY_TOO_CLOSE );
+
+ m_nMorale = 0;
+ return SCHED_CROW_FLY_AWAY;
+ }
+
+ //
+ // If someone we hate is getting a little too close for comfort, avoid them.
+ //
+ if ( HasCondition( COND_CROW_ENEMY_TOO_CLOSE ) && m_flDangerSoundTime <= gpGlobals->curtime )
+ {
+ ClearCondition( COND_CROW_ENEMY_TOO_CLOSE );
+
+ if ( m_bOnJeep == true )
+ {
+ m_nMorale = 0;
+ return SCHED_CROW_FLY_AWAY;
+ }
+
+ if ( m_flEnemyDist > 400 )
+ {
+ return SCHED_CROW_WALK_AWAY;
+ }
+ else if ( m_flEnemyDist > 300 )
+ {
+ m_nMorale -= 1;
+ return SCHED_CROW_RUN_AWAY;
+ }
+ }
+
+ switch ( m_NPCState )
+ {
+ case NPC_STATE_IDLE:
+ case NPC_STATE_ALERT:
+ case NPC_STATE_COMBAT:
+ {
+ if ( !IsFlying() )
+ {
+ if ( m_bOnJeep == true )
+ return SCHED_IDLE_STAND;
+
+ //
+ // If we are hanging out on the ground, see if it is time to pick a new place to walk to.
+ //
+ if ( gpGlobals->curtime > m_flGroundIdleMoveTime )
+ {
+ m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 10.0f, 20.0f );
+ return SCHED_CROW_IDLE_WALK;
+ }
+
+ return SCHED_IDLE_STAND;
+ }
+
+ // TODO: need idle flying behaviors!
+ }
+ }
+
+ return BaseClass::SelectSchedule();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_Crow::Precache( void )
+{
+ BaseClass::Precache();
+
+ PrecacheModel( "models/crow.mdl" );
+ PrecacheModel( "models/pigeon.mdl" );
+ PrecacheModel( "models/seagull.mdl" );
+
+ //Crow
+ PrecacheScriptSound( "NPC_Crow.Hop" );
+ PrecacheScriptSound( "NPC_Crow.Squawk" );
+ PrecacheScriptSound( "NPC_Crow.Gib" );
+ PrecacheScriptSound( "NPC_Crow.Idle" );
+ PrecacheScriptSound( "NPC_Crow.Alert" );
+ PrecacheScriptSound( "NPC_Crow.Die" );
+ PrecacheScriptSound( "NPC_Crow.Pain" );
+ PrecacheScriptSound( "NPC_Crow.Flap" );
+
+ //Seagull
+ PrecacheScriptSound( "NPC_Seagull.Pain" );
+ PrecacheScriptSound( "NPC_Seagull.Idle" );
+
+ //Pigeon
+ PrecacheScriptSound( "NPC_Pigeon.Idle");
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sounds.
+//-----------------------------------------------------------------------------
+void CNPC_Crow::IdleSound( void )
+{
+ if ( m_iBirdType != BIRDTYPE_CROW )
+ return;
+
+ EmitSound( "NPC_Crow.Idle" );
+}
+
+
+void CNPC_Crow::AlertSound( void )
+{
+ if ( m_iBirdType != BIRDTYPE_CROW )
+ return;
+
+ EmitSound( "NPC_Crow.Alert" );
+}
+
+
+void CNPC_Crow::PainSound( const CTakeDamageInfo &info )
+{
+ if ( m_iBirdType != BIRDTYPE_CROW )
+ return;
+
+ EmitSound( "NPC_Crow.Pain" );
+}
+
+
+void CNPC_Crow::DeathSound( const CTakeDamageInfo &info )
+{
+ if ( m_iBirdType != BIRDTYPE_CROW )
+ return;
+
+ EmitSound( "NPC_Crow.Die" );
+}
+
+void CNPC_Crow::FlapSound( void )
+{
+ EmitSound( "NPC_Crow.Flap" );
+ m_bPlayedLoopingSound = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// 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_Crow::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sourceEnt )
+{
+ if ( interactionType == g_interactionBarnacleVictimDangle )
+ {
+ // Die instantly
+ return false;
+ }
+ else if ( interactionType == g_interactionBarnacleVictimGrab )
+ {
+ if ( GetFlags() & FL_ONGROUND )
+ {
+ SetGroundEntity( NULL );
+ }
+
+ // return ideal grab position
+ if (data)
+ {
+ // FIXME: need a good way to ensure this contract
+ *((Vector *)data) = GetAbsOrigin() + Vector( 0, 0, 5 );
+ }
+
+ StopLoopingSounds();
+
+ SetThink( NULL );
+ return true;
+ }
+
+ return BaseClass::HandleInteraction( interactionType, data, sourceEnt );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CNPC_Crow::DrawDebugTextOverlays( void )
+{
+ int nOffset = BaseClass::DrawDebugTextOverlays();
+
+ if (m_debugOverlays & OVERLAY_TEXT_BIT)
+ {
+ char tempstr[512];
+ Q_snprintf( tempstr, sizeof( tempstr ), "morale: %d", m_nMorale );
+ EntityText( nOffset, tempstr, 0 );
+ nOffset++;
+
+ if ( GetEnemy() != NULL )
+ {
+ Q_snprintf( tempstr, sizeof( tempstr ), "enemy (dist): %s (%g)", GetEnemy()->GetClassname(), ( double )m_flEnemyDist );
+ EntityText( nOffset, tempstr, 0 );
+ nOffset++;
+ }
+ }
+
+ return nOffset;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Determines which sounds the crow cares about.
+//-----------------------------------------------------------------------------
+int CNPC_Crow::GetSoundInterests( void )
+{
+ return SOUND_WORLD | SOUND_COMBAT | SOUND_PLAYER | SOUND_DANGER;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Schedules
+//
+//-----------------------------------------------------------------------------
+
+AI_BEGIN_CUSTOM_NPC( npc_crow, CNPC_Crow )
+
+ DECLARE_TASK( TASK_CROW_FIND_FLYTO_NODE )
+ //DECLARE_TASK( TASK_CROW_PREPARE_TO_FLY )
+ DECLARE_TASK( TASK_CROW_TAKEOFF )
+ DECLARE_TASK( TASK_CROW_FLY )
+ DECLARE_TASK( TASK_CROW_PICK_RANDOM_GOAL )
+ DECLARE_TASK( TASK_CROW_HOP )
+ DECLARE_TASK( TASK_CROW_PICK_EVADE_GOAL )
+ DECLARE_TASK( TASK_CROW_WAIT_FOR_BARNACLE_KILL )
+
+ // experiment
+ DECLARE_TASK( TASK_CROW_FALL_TO_GROUND )
+ DECLARE_TASK( TASK_CROW_PREPARE_TO_FLY_RANDOM )
+
+ DECLARE_ACTIVITY( ACT_CROW_TAKEOFF )
+ DECLARE_ACTIVITY( ACT_CROW_SOAR )
+ DECLARE_ACTIVITY( ACT_CROW_LAND )
+
+ DECLARE_ANIMEVENT( AE_CROW_HOP )
+ DECLARE_ANIMEVENT( AE_CROW_FLY )
+ DECLARE_ANIMEVENT( AE_CROW_TAKEOFF )
+
+
+ DECLARE_CONDITION( COND_CROW_ENEMY_TOO_CLOSE )
+ DECLARE_CONDITION( COND_CROW_ENEMY_WAY_TOO_CLOSE )
+ DECLARE_CONDITION( COND_CROW_FORCED_FLY )
+ DECLARE_CONDITION( COND_CROW_BARNACLED )
+
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_CROW_IDLE_WALK,
+
+ " Tasks"
+ " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_IDLE_STAND"
+ " TASK_CROW_PICK_RANDOM_GOAL 0"
+ " TASK_GET_PATH_TO_SAVEPOSITION 0"
+ " TASK_WALK_PATH 0"
+ " TASK_WAIT_FOR_MOVEMENT 0"
+ " TASK_WAIT_PVS 0"
+ " "
+ " Interrupts"
+ " COND_CROW_FORCED_FLY"
+ " COND_PROVOKED"
+ " COND_CROW_ENEMY_TOO_CLOSE"
+ " COND_NEW_ENEMY"
+ " COND_HEAVY_DAMAGE"
+ " COND_LIGHT_DAMAGE"
+ " COND_HEAVY_DAMAGE"
+ " COND_HEAR_DANGER"
+ " COND_HEAR_COMBAT"
+ )
+
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_CROW_WALK_AWAY,
+
+ " Tasks"
+ " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_AWAY"
+ " TASK_CROW_PICK_EVADE_GOAL 0"
+ " TASK_GET_PATH_TO_SAVEPOSITION 0"
+ " TASK_WALK_PATH 0"
+ " TASK_WAIT_FOR_MOVEMENT 0"
+ " "
+ " Interrupts"
+ " COND_CROW_FORCED_FLY"
+ " COND_CROW_ENEMY_WAY_TOO_CLOSE"
+ " COND_NEW_ENEMY"
+ " COND_HEAVY_DAMAGE"
+ " COND_LIGHT_DAMAGE"
+ " COND_HEAVY_DAMAGE"
+ " COND_HEAR_DANGER"
+ " COND_HEAR_COMBAT"
+ )
+
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_CROW_RUN_AWAY,
+
+ " Tasks"
+ " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_AWAY"
+ " TASK_CROW_PICK_EVADE_GOAL 0"
+ " TASK_GET_PATH_TO_SAVEPOSITION 0"
+ " TASK_RUN_PATH 0"
+ " TASK_WAIT_FOR_MOVEMENT 0"
+ " "
+ " Interrupts"
+ " COND_CROW_FORCED_FLY"
+ " COND_CROW_ENEMY_WAY_TOO_CLOSE"
+ " COND_NEW_ENEMY"
+ " COND_HEAVY_DAMAGE"
+ " COND_LIGHT_DAMAGE"
+ " COND_HEAVY_DAMAGE"
+ " COND_HEAR_DANGER"
+ " COND_HEAR_COMBAT"
+ )
+
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_CROW_HOP_AWAY,
+
+ " Tasks"
+ " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_AWAY"
+ " TASK_STOP_MOVING 0"
+ " TASK_CROW_PICK_EVADE_GOAL 0"
+ " TASK_FACE_IDEAL 0"
+ " TASK_CROW_HOP 0"
+ " "
+ " Interrupts"
+ " COND_CROW_FORCED_FLY"
+ " COND_HEAVY_DAMAGE"
+ " COND_LIGHT_DAMAGE"
+ " COND_HEAVY_DAMAGE"
+ " COND_HEAR_DANGER"
+ " COND_HEAR_COMBAT"
+ )
+
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_CROW_IDLE_FLY,
+
+ " Tasks"
+ " TASK_FIND_HINTNODE 0"
+ " TASK_GET_PATH_TO_HINTNODE 0"
+ " TASK_WAIT_FOR_MOVEMENT 0"
+ " "
+ " Interrupts"
+ )
+
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_CROW_FLY_AWAY,
+
+ " Tasks"
+ " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_FAIL"
+ " TASK_STOP_MOVING 0"
+ " TASK_FIND_HINTNODE 0"
+ " TASK_GET_PATH_TO_HINTNODE 0"
+ " TASK_CROW_TAKEOFF 0"
+ " TASK_WAIT_FOR_MOVEMENT 0"
+ " "
+ " Interrupts"
+ )
+
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_CROW_FLY,
+
+ " Tasks"
+ " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_FAIL"
+ " TASK_STOP_MOVING 0"
+ " TASK_CROW_TAKEOFF 0"
+ " TASK_CROW_FLY 0"
+ " "
+ " Interrupts"
+ )
+
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_CROW_FLY_FAIL,
+
+ " Tasks"
+ " TASK_CROW_FALL_TO_GROUND 0"
+ " TASK_SET_SCHEDULE SCHEDULE:SCHED_CROW_IDLE_WALK"
+ " "
+ " Interrupts"
+ )
+
+ //=========================================================
+ // Crow is in the clutches of a barnacle
+ DEFINE_SCHEDULE
+ (
+ SCHED_CROW_BARNACLED,
+
+ " Tasks"
+ " TASK_STOP_MOVING 0"
+ " TASK_SET_ACTIVITY ACTIVITY:ACT_HOP"
+ " TASK_CROW_WAIT_FOR_BARNACLE_KILL 0"
+
+ " Interrupts"
+ )
+
+
+AI_END_CUSTOM_NPC()