mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-23 14:46:53 +00:00
1396 lines
36 KiB
C++
1396 lines
36 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Houndeye - a spooky sonic dog.
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "npc_houndeye.h"
|
|
#include "ai_default.h"
|
|
#include "ai_node.h"
|
|
#include "ai_route.h"
|
|
#include "AI_Navigator.h"
|
|
#include "AI_Motor.h"
|
|
#include "ai_squad.h"
|
|
#include "AI_TacticalServices.h"
|
|
#include "soundent.h"
|
|
#include "EntityList.h"
|
|
#include "game.h"
|
|
#include "activitylist.h"
|
|
#include "hl2_shareddefs.h"
|
|
#include "grenade_energy.h"
|
|
#include "energy_wave.h"
|
|
#include "ai_interactions.h"
|
|
#include "ndebugoverlay.h"
|
|
#include "npcevent.h"
|
|
#include "player.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"
|
|
|
|
#define HOUNDEYE_MAX_ATTACK_RADIUS 500
|
|
#define HOUNDEYE_MIN_ATTACK_RADIUS 100
|
|
|
|
#define HOUNDEYE_EYE_FRAMES 4 // how many different switchable maps for the eye
|
|
|
|
ConVar sk_Houndeye_health( "sk_Houndeye_health","0");
|
|
ConVar sk_Houndeye_dmg_blast( "sk_Houndeye_dmg_blast","0");
|
|
|
|
//=========================================================
|
|
// Interactions
|
|
//=========================================================
|
|
int g_interactionHoundeyeGroupAttack = 0;
|
|
int g_interactionHoundeyeGroupRetreat = 0;
|
|
int g_interactionHoundeyeGroupRalley = 0;
|
|
|
|
//=========================================================
|
|
// Specialized Tasks
|
|
//=========================================================
|
|
enum
|
|
{
|
|
TASK_HOUND_CLOSE_EYE = LAST_SHARED_TASK,
|
|
TASK_HOUND_OPEN_EYE,
|
|
TASK_HOUND_THREAT_DISPLAY,
|
|
TASK_HOUND_FALL_ASLEEP,
|
|
TASK_HOUND_WAKE_UP,
|
|
TASK_HOUND_HOP_BACK,
|
|
|
|
TASK_HOUND_GET_PATH_TO_CIRCLE,
|
|
TASK_HOUND_REVERSE_STRAFE_DIR,
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Custom Conditions
|
|
//-----------------------------------------------------------------------------
|
|
enum Houndeye_Conds
|
|
{
|
|
COND_HOUND_GROUP_ATTACK = LAST_SHARED_CONDITION,
|
|
COND_HOUND_GROUP_RETREAT,
|
|
COND_HOUND_GROUP_RALLEY,
|
|
};
|
|
|
|
//=========================================================
|
|
// Specialized Shedules
|
|
//=========================================================
|
|
enum
|
|
{
|
|
SCHED_HOUND_AGITATED = LAST_SHARED_SCHEDULE,
|
|
SCHED_HOUND_HOP_RETREAT,
|
|
SCHED_HOUND_RANGE_ATTACK1,
|
|
|
|
SCHED_HOUND_ATTACK_STRAFE,
|
|
SCHED_HOUND_ATTACK_STRAFE_REVERSE,
|
|
SCHED_HOUND_GROUP_ATTACK,
|
|
SCHED_HOUND_GROUP_RETREAT,
|
|
SCHED_HOUND_CHASE_ENEMY,
|
|
SCHED_HOUND_COVER_WAIT,
|
|
SCHED_HOUND_GROUP_RALLEY,
|
|
};
|
|
|
|
//=========================================================
|
|
// Specialized activities
|
|
//=========================================================
|
|
int ACT_HOUND_GUARD;
|
|
|
|
//=========================================================
|
|
// Monster's Anim Events Go Here
|
|
//=========================================================
|
|
#define HOUND_AE_WARN 1
|
|
#define HOUND_AE_STARTATTACK 2
|
|
#define HOUND_AE_THUMP 3
|
|
#define HOUND_AE_ANGERSOUND1 4
|
|
#define HOUND_AE_ANGERSOUND2 5
|
|
#define HOUND_AE_HOPBACK 6
|
|
#define HOUND_AE_CLOSE_EYE 7
|
|
#define HOUND_AE_LEAP_HIT 8
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Initialize the custom schedules
|
|
// Input :
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
void CNPC_Houndeye::InitCustomSchedules(void)
|
|
{
|
|
INIT_CUSTOM_AI(CNPC_Houndeye);
|
|
|
|
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_CLOSE_EYE);
|
|
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_OPEN_EYE);
|
|
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_THREAT_DISPLAY);
|
|
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_FALL_ASLEEP);
|
|
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_WAKE_UP);
|
|
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_HOP_BACK);
|
|
|
|
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_GET_PATH_TO_CIRCLE);
|
|
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_REVERSE_STRAFE_DIR);
|
|
|
|
ADD_CUSTOM_CONDITION(CNPC_Houndeye, COND_HOUND_GROUP_ATTACK);
|
|
ADD_CUSTOM_CONDITION(CNPC_Houndeye, COND_HOUND_GROUP_RETREAT);
|
|
|
|
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_HOP_RETREAT);
|
|
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_RANGE_ATTACK1);
|
|
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_ATTACK_STRAFE);
|
|
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_ATTACK_STRAFE_REVERSE);
|
|
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_ATTACK);
|
|
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_RETREAT);
|
|
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_CHASE_ENEMY);
|
|
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_COVER_WAIT);
|
|
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_RALLEY);
|
|
|
|
ADD_CUSTOM_ACTIVITY(CNPC_Houndeye, ACT_HOUND_GUARD);
|
|
|
|
g_interactionHoundeyeGroupAttack = CBaseCombatCharacter::GetInteractionID();
|
|
g_interactionHoundeyeGroupRetreat = CBaseCombatCharacter::GetInteractionID();
|
|
g_interactionHoundeyeGroupRalley = CBaseCombatCharacter::GetInteractionID();
|
|
|
|
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_HOP_RETREAT);
|
|
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_RANGE_ATTACK1);
|
|
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_ATTACK_STRAFE);
|
|
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_ATTACK_STRAFE_REVERSE);
|
|
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_ATTACK);
|
|
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_RETREAT);
|
|
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_CHASE_ENEMY);
|
|
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_COVER_WAIT);
|
|
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_RALLEY);
|
|
}
|
|
|
|
LINK_ENTITY_TO_CLASS( npc_houndeye, CNPC_Houndeye );
|
|
IMPLEMENT_CUSTOM_AI( npc_houndeye, CNPC_Houndeye );
|
|
|
|
BEGIN_DATADESC( CNPC_Houndeye )
|
|
|
|
DEFINE_FIELD( m_fAsleep, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_fDontBlink, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_flNextSecondaryAttack, FIELD_TIME ),
|
|
DEFINE_FIELD( m_bLoopClockwise, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_pEnergyWave, FIELD_CLASSPTR ),
|
|
DEFINE_FIELD( m_flEndEnergyWaveTime, FIELD_TIME ),
|
|
|
|
END_DATADESC()
|
|
|
|
//=========================================================
|
|
// Classify - indicates this monster's place in the
|
|
// relationship table.
|
|
//=========================================================
|
|
Class_T CNPC_Houndeye::Classify ( void )
|
|
{
|
|
return CLASS_HOUNDEYE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input :
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
int CNPC_Houndeye::RangeAttack1Conditions ( float flDot, float flDist )
|
|
{
|
|
// I'm not allowed to attack if standing in another hound eye
|
|
// (note houndeyes allowed to interpenetrate)
|
|
trace_t tr;
|
|
AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,0.1),
|
|
GetHullMins(), GetHullMaxs(),
|
|
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
|
|
if (tr.startsolid)
|
|
{
|
|
CBaseEntity *pEntity = tr.m_pEnt;
|
|
if (pEntity->Classify() == CLASS_HOUNDEYE)
|
|
{
|
|
return( COND_NONE );
|
|
}
|
|
}
|
|
|
|
// If I'm really close to my enemy allow me to attack if
|
|
// I'm facing regardless of next attack time
|
|
if (flDist < 100 && flDot >= 0.3)
|
|
{
|
|
return COND_CAN_RANGE_ATTACK1;
|
|
}
|
|
if ( gpGlobals->curtime < m_flNextAttack )
|
|
{
|
|
return( COND_NONE );
|
|
}
|
|
if (flDist > ( HOUNDEYE_MAX_ATTACK_RADIUS * 0.5 ))
|
|
{
|
|
return COND_TOO_FAR_TO_ATTACK;
|
|
}
|
|
if (flDot < 0.3)
|
|
{
|
|
return COND_NOT_FACING_ATTACK;
|
|
}
|
|
return COND_CAN_RANGE_ATTACK1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Overidden for human grunts because they hear the DANGER sound
|
|
// Input :
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
int CNPC_Houndeye::GetSoundInterests( void )
|
|
{
|
|
return SOUND_WORLD |
|
|
SOUND_COMBAT |
|
|
SOUND_PLAYER |
|
|
SOUND_DANGER;
|
|
}
|
|
|
|
//=========================================================
|
|
// MaxYawSpeed - allows each sequence to have a different
|
|
// turn rate associated with it.
|
|
//=========================================================
|
|
float CNPC_Houndeye::MaxYawSpeed ( void )
|
|
{
|
|
int ys = 90;
|
|
|
|
switch ( GetActivity() )
|
|
{
|
|
case ACT_CROUCHIDLE://sleeping!
|
|
ys = 0;
|
|
break;
|
|
case ACT_IDLE:
|
|
ys = 60;
|
|
break;
|
|
case ACT_WALK:
|
|
ys = 90;
|
|
break;
|
|
case ACT_RUN:
|
|
ys = 90;
|
|
break;
|
|
case ACT_TURN_LEFT:
|
|
case ACT_TURN_RIGHT:
|
|
ys = 90;
|
|
break;
|
|
}
|
|
return ys;
|
|
}
|
|
|
|
//=========================================================
|
|
// HandleAnimEvent - catches the monster-specific messages
|
|
// that occur when tagged animation frames are played.
|
|
//=========================================================
|
|
void CNPC_Houndeye::HandleAnimEvent( animevent_t *pEvent )
|
|
{
|
|
switch ( pEvent->event )
|
|
{
|
|
case HOUND_AE_WARN:
|
|
// do stuff for this event.
|
|
WarnSound();
|
|
break;
|
|
|
|
case HOUND_AE_STARTATTACK:
|
|
WarmUpSound();
|
|
break;
|
|
|
|
case HOUND_AE_HOPBACK:
|
|
{
|
|
float flGravity = GetCurrentGravity();
|
|
|
|
SetGroundEntity( NULL );
|
|
|
|
Vector forward;
|
|
AngleVectors( GetLocalAngles(), &forward );
|
|
Vector vecNewVelocity = forward * -200;
|
|
//jump up 36 inches
|
|
vecNewVelocity.z += sqrt( 2 * flGravity * 36 );
|
|
SetAbsVelocity( vecNewVelocity );
|
|
break;
|
|
}
|
|
|
|
case HOUND_AE_THUMP:
|
|
// emit the shockwaves
|
|
SonicAttack();
|
|
m_flNextAttack = gpGlobals->curtime + random->RandomFloat( 5.0, 8.0 );
|
|
break;
|
|
|
|
case HOUND_AE_ANGERSOUND1:
|
|
{
|
|
EmitSound( "NPC_Houndeye.Anger1" );
|
|
}
|
|
break;
|
|
|
|
case HOUND_AE_ANGERSOUND2:
|
|
{
|
|
EmitSound( "NPC_Houndeye.Anger2" );
|
|
}
|
|
break;
|
|
|
|
case HOUND_AE_CLOSE_EYE:
|
|
if ( !m_fDontBlink )
|
|
{
|
|
//<<TEMP>> pev->skin = HOUNDEYE_EYE_FRAMES - 1;
|
|
}
|
|
break;
|
|
|
|
case HOUND_AE_LEAP_HIT:
|
|
{
|
|
//<<TEMP>>return;//<<TEMP>>
|
|
SetGroundEntity( NULL );
|
|
|
|
//
|
|
// Take him off ground so engine doesn't instantly reset FL_ONGROUND.
|
|
//
|
|
UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
|
|
Vector vecJumpDir;
|
|
if ( GetEnemy() != NULL )
|
|
{
|
|
Vector vecEnemyEyePos = GetEnemy()->EyePosition();
|
|
|
|
float gravity = GetCurrentGravity();
|
|
if ( gravity <= 1 )
|
|
{
|
|
gravity = 1;
|
|
}
|
|
|
|
//
|
|
// How fast does the houndeye need to travel to reach my enemy's eyes given gravity?
|
|
//
|
|
float height = ( vecEnemyEyePos.z - GetAbsOrigin().z );
|
|
if ( height < 16 )
|
|
{
|
|
height = 16;
|
|
}
|
|
else if ( height > 120 )
|
|
{
|
|
height = 120;
|
|
}
|
|
float speed = sqrt( 2 * gravity * height );
|
|
float time = speed / gravity;
|
|
|
|
//
|
|
// Scale the sideways velocity to get there at the right time
|
|
//
|
|
vecJumpDir = vecEnemyEyePos - GetAbsOrigin();
|
|
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 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Vector forward, up;
|
|
AngleVectors( GetLocalAngles(), &forward, NULL, &up );
|
|
//
|
|
// Jump hop, don't care where.
|
|
//
|
|
vecJumpDir = Vector( forward.x, forward.y, up.z ) * 350;
|
|
}
|
|
|
|
SetAbsVelocity( vecJumpDir );
|
|
m_flNextAttack = gpGlobals->curtime + 2;
|
|
break;
|
|
}
|
|
default:
|
|
BaseClass::HandleAnimEvent( pEvent );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//=========================================================
|
|
// Spawn
|
|
//=========================================================
|
|
void CNPC_Houndeye::Spawn()
|
|
{
|
|
Precache( );
|
|
|
|
SetModel("models/houndeye.mdl");
|
|
SetHullType(HULL_WIDE_SHORT);
|
|
SetHullSizeNormal();
|
|
|
|
SetSolid( SOLID_BBOX );
|
|
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
|
SetMoveType( MOVETYPE_STEP );
|
|
SetBloodColor( BLOOD_COLOR_YELLOW );
|
|
m_iHealth = sk_Houndeye_health.GetFloat();
|
|
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
|
m_NPCState = NPC_STATE_NONE;
|
|
m_fAsleep = false; // everyone spawns awake
|
|
m_fDontBlink = false;
|
|
CapabilitiesAdd( bits_CAP_SQUAD );
|
|
CapabilitiesAdd( bits_CAP_MOVE_GROUND );
|
|
CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 );
|
|
CapabilitiesAdd( bits_CAP_TURN_HEAD );
|
|
|
|
m_flNextSecondaryAttack = 0;
|
|
m_bLoopClockwise = random->RandomInt(0,1) ? true : false;
|
|
|
|
m_pEnergyWave = NULL;
|
|
m_flEndEnergyWaveTime = 0;
|
|
|
|
SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE );
|
|
|
|
NPCInit();
|
|
}
|
|
|
|
//=========================================================
|
|
// Precache - precaches all resources this monster needs
|
|
//=========================================================
|
|
void CNPC_Houndeye::Precache()
|
|
{
|
|
PrecacheModel("models/houndeye.mdl");
|
|
|
|
PrecacheScriptSound( "NPC_Houndeye.Anger1" );
|
|
PrecacheScriptSound( "NPC_Houndeye.Anger2" );
|
|
PrecacheScriptSound( "NPC_Houndeye.SpeakSentence" );
|
|
PrecacheScriptSound( "NPC_Houndeye.Idle" );
|
|
PrecacheScriptSound( "NPC_Houndeye.WarmUp" );
|
|
PrecacheScriptSound( "NPC_Houndeye.Warn" );
|
|
PrecacheScriptSound( "NPC_Houndeye.Alert" );
|
|
PrecacheScriptSound( "NPC_Houndeye.Die" );
|
|
PrecacheScriptSound( "NPC_Houndeye.Pain" );
|
|
PrecacheScriptSound( "NPC_Houndeye.Retreat" );
|
|
PrecacheScriptSound( "NPC_Houndeye.SonicAttack" );
|
|
|
|
PrecacheScriptSound( "NPC_Houndeye.GroupAttack" );
|
|
PrecacheScriptSound( "NPC_Houndeye.GroupFollow" );
|
|
|
|
|
|
UTIL_PrecacheOther("grenade_energy");
|
|
BaseClass::Precache();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Purpose :
|
|
// Input :
|
|
// Output :
|
|
//------------------------------------------------------------------------------
|
|
void CNPC_Houndeye::SpeakSentence( int sentenceType )
|
|
{
|
|
if (gpGlobals->curtime > m_flSoundWaitTime)
|
|
{
|
|
EmitSound( "NPC_Houndeye.SpeakSentence" );
|
|
m_flSoundWaitTime = gpGlobals->curtime + 1.0;
|
|
}
|
|
}
|
|
|
|
//=========================================================
|
|
// IdleSound
|
|
//=========================================================
|
|
void CNPC_Houndeye::IdleSound ( void )
|
|
{
|
|
if (!FOkToMakeSound())
|
|
{
|
|
return;
|
|
}
|
|
CPASAttenuationFilter filter( this,"NPC_Houndeye.Idle" );
|
|
EmitSound( filter, entindex(),"NPC_Houndeye.Idle" );
|
|
}
|
|
|
|
//=========================================================
|
|
// IdleSound
|
|
//=========================================================
|
|
void CNPC_Houndeye::WarmUpSound ( void )
|
|
{
|
|
EmitSound( "NPC_Houndeye.WarmUp" );
|
|
}
|
|
|
|
//=========================================================
|
|
// WarnSound
|
|
//=========================================================
|
|
void CNPC_Houndeye::WarnSound ( void )
|
|
{
|
|
EmitSound( "NPC_Houndeye.Warn" );
|
|
}
|
|
|
|
//=========================================================
|
|
// AlertSound
|
|
//=========================================================
|
|
void CNPC_Houndeye::AlertSound ( void )
|
|
{
|
|
// only first squad member makes ALERT sound.
|
|
if ( m_pSquad && !m_pSquad->IsLeader( this ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
EmitSound( "NPC_Houndeye.Alert" );
|
|
}
|
|
|
|
//=========================================================
|
|
// DeathSound
|
|
//=========================================================
|
|
void CNPC_Houndeye::DeathSound ( void )
|
|
{
|
|
EmitSound( "NPC_Houndeye.Die" );
|
|
}
|
|
|
|
//=========================================================
|
|
// PainSound
|
|
//=========================================================
|
|
void CNPC_Houndeye::PainSound ( const CTakeDamageInfo &info )
|
|
{
|
|
EmitSound( "NPC_Houndeye.Pain" );
|
|
}
|
|
|
|
//=========================================================
|
|
// WriteBeamColor - writes a color vector to the network
|
|
// based on the size of the group.
|
|
//=========================================================
|
|
void CNPC_Houndeye::WriteBeamColor ( void )
|
|
{
|
|
BYTE bRed, bGreen, bBlue;
|
|
|
|
if ( m_pSquad )
|
|
{
|
|
switch ( m_pSquad->NumMembers() )
|
|
{
|
|
case 2:
|
|
// no case for 0 or 1, cause those are impossible for monsters in Squads.
|
|
bRed = 101;
|
|
bGreen = 133;
|
|
bBlue = 221;
|
|
break;
|
|
case 3:
|
|
bRed = 67;
|
|
bGreen = 85;
|
|
bBlue = 255;
|
|
break;
|
|
case 4:
|
|
bRed = 62;
|
|
bGreen = 33;
|
|
bBlue = 211;
|
|
break;
|
|
default:
|
|
DevWarning( 2, "Unsupported Houndeye SquadSize!\n" );
|
|
bRed = 188;
|
|
bGreen = 220;
|
|
bBlue = 255;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// solo houndeye - weakest beam
|
|
bRed = 188;
|
|
bGreen = 220;
|
|
bBlue = 255;
|
|
}
|
|
|
|
WRITE_BYTE( bRed );
|
|
WRITE_BYTE( bGreen );
|
|
WRITE_BYTE( bBlue );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Plays the engine sound.
|
|
//-----------------------------------------------------------------------------
|
|
void CNPC_Houndeye::NPCThink(void)
|
|
{
|
|
if (m_pEnergyWave)
|
|
{
|
|
if (gpGlobals->curtime > m_flEndEnergyWaveTime)
|
|
{
|
|
UTIL_Remove(m_pEnergyWave);
|
|
m_pEnergyWave = NULL;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------
|
|
// Update collision group
|
|
// While I'm running I'm allowed to penetrate
|
|
// other houndeyes
|
|
// -----------------------------------------------------
|
|
Vector vVelocity;
|
|
GetVelocity( &vVelocity, NULL );
|
|
if (vVelocity.Length() > 10)
|
|
{
|
|
SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE );
|
|
}
|
|
else
|
|
{
|
|
// Don't go solid if resting in another houndeye
|
|
trace_t tr;
|
|
AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,1),
|
|
GetHullMins(), GetHullMaxs(),
|
|
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
|
|
if (!tr.startsolid)
|
|
{
|
|
SetCollisionGroup( COLLISION_GROUP_NONE );
|
|
}
|
|
else
|
|
{
|
|
SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE );
|
|
}
|
|
}
|
|
/*
|
|
if (GetCollisionGroup() == HL2COLLISION_GROUP_HOUNDEYE)
|
|
{
|
|
NDebugOverlay::Box(GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
NDebugOverlay::Box(GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 255, 0, 0, 0, 0);
|
|
}
|
|
*/
|
|
BaseClass::NPCThink();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Purpose : Broadcast retreat occasionally when hurt
|
|
// Input :
|
|
// Output :
|
|
//------------------------------------------------------------------------------
|
|
int CNPC_Houndeye::OnTakeDamage_Alive( const CTakeDamageInfo &info )
|
|
{
|
|
if (m_pSquad && random->RandomInt(0,10) == 10)
|
|
{
|
|
EmitSound( "NPC_Houndeye.Retreat" );
|
|
m_flSoundWaitTime = gpGlobals->curtime + 1.0;
|
|
|
|
m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRetreat, NULL, this );
|
|
}
|
|
|
|
return BaseClass::OnTakeDamage_Alive( info );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Purpose : Broadcast retreat when member of squad killed
|
|
// Input :
|
|
// Output :
|
|
//------------------------------------------------------------------------------
|
|
void CNPC_Houndeye::Event_Killed( const CTakeDamageInfo &info )
|
|
{
|
|
EmitSound( "NPC_Houndeye.Retreat" );
|
|
m_flSoundWaitTime = gpGlobals->curtime + 1.0;
|
|
|
|
if (m_pSquad)
|
|
{
|
|
m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRetreat, NULL, this );
|
|
}
|
|
|
|
BaseClass::Event_Killed( info );
|
|
}
|
|
|
|
//=========================================================
|
|
// SonicAttack
|
|
//=========================================================
|
|
void CNPC_Houndeye::SonicAttack ( void )
|
|
{
|
|
EmitSound( "NPC_Houndeye.SonicAttack" );
|
|
|
|
if (m_pEnergyWave)
|
|
{
|
|
UTIL_Remove(m_pEnergyWave);
|
|
}
|
|
Vector vFacingDir = EyeDirection3D( );
|
|
m_pEnergyWave = (CEnergyWave*)Create( "energy_wave", EyePosition(), GetLocalAngles() );
|
|
m_flEndEnergyWaveTime = gpGlobals->curtime + 1; //<<TEMP>> magic
|
|
m_pEnergyWave->SetAbsVelocity( 100*vFacingDir );
|
|
|
|
CBaseEntity *pEntity = NULL;
|
|
// iterate on all entities in the vicinity.
|
|
for ( CEntitySphereQuery sphere( GetAbsOrigin(), HOUNDEYE_MAX_ATTACK_RADIUS ); pEntity = sphere.GetCurrentEntity(); sphere.NextEntity() )
|
|
{
|
|
if (pEntity->Classify() == CLASS_HOUNDEYE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pEntity->GetFlags() & FL_NOTARGET)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
|
|
|
|
if ( pEntity->m_takedamage != DAMAGE_NO || pPhysicsObject)
|
|
{
|
|
// --------------------------
|
|
// Adjust damage by distance
|
|
// --------------------------
|
|
float flDist = (pEntity->WorldSpaceCenter() - GetAbsOrigin()).Length();
|
|
float flDamageAdjuster = 1-( flDist / HOUNDEYE_MAX_ATTACK_RADIUS );
|
|
|
|
// --------------------------
|
|
// Adjust damage by direction
|
|
// --------------------------
|
|
Vector forward;
|
|
AngleVectors( GetAbsAngles(), &forward );
|
|
Vector vEntDir = (pEntity->GetAbsOrigin() - GetAbsOrigin());
|
|
VectorNormalize(vEntDir);
|
|
float flDotPr = DotProduct(forward,vEntDir);
|
|
flDamageAdjuster *= flDotPr;
|
|
|
|
if (flDamageAdjuster < 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// --------------------------
|
|
// Adjust damage by visibility
|
|
// --------------------------
|
|
if ( !FVisible( pEntity ) )
|
|
{
|
|
if ( pEntity->IsPlayer() )
|
|
{
|
|
// if this entity is a client, and is not in full view, inflict half damage. We do this so that players still
|
|
// take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients
|
|
// so that monsters in other parts of the level don't take the damage and get pissed.
|
|
flDamageAdjuster *= 0.5;
|
|
}
|
|
else if ( !FClassnameIs( pEntity, "func_breakable" ) && !FClassnameIs( pEntity, "func_pushable" ) )
|
|
{
|
|
// do not hurt nonclients through walls, but allow damage to be done to breakables
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// ------------------------------
|
|
// Apply the damage
|
|
// ------------------------------
|
|
if (pEntity->m_takedamage != DAMAGE_NO)
|
|
{
|
|
CTakeDamageInfo info( this, this, flDamageAdjuster * sk_Houndeye_dmg_blast.GetFloat(), DMG_SONIC | DMG_ALWAYSGIB );
|
|
CalculateExplosiveDamageForce( &info, (pEntity->GetAbsOrigin() - GetAbsOrigin()), pEntity->GetAbsOrigin() );
|
|
|
|
pEntity->TakeDamage( info );
|
|
|
|
// Throw the player
|
|
if ( pEntity->IsPlayer() )
|
|
{
|
|
Vector forward;
|
|
AngleVectors( GetLocalAngles(), &forward );
|
|
|
|
Vector vecVelocity = pEntity->GetAbsVelocity();
|
|
vecVelocity += forward * 250 * flDamageAdjuster;
|
|
vecVelocity.z = 300 * flDamageAdjuster;
|
|
pEntity->SetAbsVelocity( vecVelocity );
|
|
pEntity->ViewPunch( QAngle(random->RandomInt(-20,20), 0, random->RandomInt(-20,20)) );
|
|
}
|
|
}
|
|
// ------------------------------
|
|
// Apply physics foces
|
|
// ------------------------------
|
|
IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
|
|
if (pPhysicsObject)
|
|
{
|
|
float flForce = flDamageAdjuster * 8000;
|
|
pPhysicsObject->ApplyForceCenter( (vEntDir+Vector(0,0,0.2)) * flForce );
|
|
pPhysicsObject->ApplyTorqueCenter( vEntDir * flForce );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=========================================================
|
|
// start task
|
|
//=========================================================
|
|
void CNPC_Houndeye::StartTask( const Task_t *pTask )
|
|
{
|
|
switch ( pTask->iTask )
|
|
{
|
|
case TASK_HOUND_GET_PATH_TO_CIRCLE:
|
|
{
|
|
if (GetEnemy() == NULL)
|
|
{
|
|
TaskFail(FAIL_NO_ENEMY);
|
|
}
|
|
else
|
|
{
|
|
Vector vTargetPos = GetEnemyLKP();
|
|
vTargetPos.z = GetFloorZ(vTargetPos);
|
|
|
|
if (GetNavigator()->SetRadialGoal(vTargetPos, random->RandomInt(50,500), 90, 175, m_bLoopClockwise))
|
|
{
|
|
TaskComplete();
|
|
return;
|
|
}
|
|
TaskFail(FAIL_NO_ROUTE);
|
|
}
|
|
break;
|
|
}
|
|
case TASK_HOUND_REVERSE_STRAFE_DIR:
|
|
{
|
|
// Try the other direction
|
|
m_bLoopClockwise = (m_bLoopClockwise) ? false : true;
|
|
TaskComplete();
|
|
break;
|
|
}
|
|
|
|
// Override to set appropriate distances
|
|
case TASK_GET_PATH_TO_ENEMY_LOS:
|
|
{
|
|
float flMaxRange = HOUNDEYE_MAX_ATTACK_RADIUS * 0.9;
|
|
float flMinRange = HOUNDEYE_MIN_ATTACK_RADIUS;
|
|
Vector posLos;
|
|
bool foundLos = false;
|
|
|
|
if (GetEnemy() != NULL)
|
|
{
|
|
foundLos = GetTacticalServices()->FindLos(GetEnemyLKP(),GetEnemy()->EyePosition(), flMinRange, flMaxRange, 0.0, &posLos);
|
|
}
|
|
else
|
|
{
|
|
TaskFail(FAIL_NO_TARGET);
|
|
return;
|
|
}
|
|
|
|
if (foundLos)
|
|
{
|
|
GetNavigator()->SetGoal( AI_NavGoal_t( posLos, ACT_RUN, AIN_HULL_TOLERANCE ) );
|
|
}
|
|
else
|
|
{
|
|
TaskFail(FAIL_NO_SHOOT);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TASK_HOUND_FALL_ASLEEP:
|
|
{
|
|
m_fAsleep = true; // signal that hound is lying down (must stand again before doing anything else!)
|
|
TaskComplete( true );
|
|
break;
|
|
}
|
|
case TASK_HOUND_WAKE_UP:
|
|
{
|
|
m_fAsleep = false; // signal that hound is standing again
|
|
TaskComplete( true );
|
|
break;
|
|
}
|
|
case TASK_HOUND_OPEN_EYE:
|
|
{
|
|
m_fDontBlink = false; // turn blinking back on and that code will automatically open the eye
|
|
TaskComplete( true );
|
|
break;
|
|
}
|
|
case TASK_HOUND_CLOSE_EYE:
|
|
{
|
|
//<<TEMP>> pev->skin = 0;
|
|
m_fDontBlink = true; // tell blink code to leave the eye alone.
|
|
break;
|
|
}
|
|
case TASK_HOUND_THREAT_DISPLAY:
|
|
{
|
|
SetIdealActivity( ACT_IDLE_ANGRY );
|
|
break;
|
|
}
|
|
case TASK_HOUND_HOP_BACK:
|
|
{
|
|
SetIdealActivity( ACT_LEAP );
|
|
break;
|
|
}
|
|
case TASK_RANGE_ATTACK1:
|
|
{
|
|
SetIdealActivity( ACT_RANGE_ATTACK1 );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
BaseClass::StartTask(pTask);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=========================================================
|
|
// RunTask
|
|
//=========================================================
|
|
void CNPC_Houndeye::RunTask( const Task_t *pTask )
|
|
{
|
|
switch ( pTask->iTask )
|
|
{
|
|
case TASK_HOUND_THREAT_DISPLAY:
|
|
{
|
|
GetMotor()->SetIdealYawToTargetAndUpdate( GetEnemyLKP(), AI_KEEP_YAW_SPEED );
|
|
|
|
if ( IsActivityFinished() )
|
|
{
|
|
TaskComplete();
|
|
}
|
|
|
|
break;
|
|
}
|
|
case TASK_HOUND_CLOSE_EYE:
|
|
{
|
|
/*//<<TEMP>>
|
|
if ( pev->skin < HOUNDEYE_EYE_FRAMES - 1 )
|
|
{
|
|
pev->skin++;
|
|
}
|
|
*/
|
|
break;
|
|
}
|
|
case TASK_HOUND_HOP_BACK:
|
|
{
|
|
if ( IsActivityFinished() )
|
|
{
|
|
TaskComplete();
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
BaseClass::RunTask(pTask);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Purpose :
|
|
// Input :
|
|
// Output :
|
|
//------------------------------------------------------------------------------
|
|
void CNPC_Houndeye::PrescheduleThink ( void )
|
|
{
|
|
BaseClass::PrescheduleThink();
|
|
|
|
// if the hound is mad and is running, make hunt noises.
|
|
if ( m_NPCState == NPC_STATE_COMBAT && ( GetActivity() == ACT_RUN ) && random->RandomFloat( 0, 1 ) < 0.2 )
|
|
{
|
|
WarnSound();
|
|
}
|
|
|
|
// at random, initiate a blink if not already blinking or sleeping
|
|
if ( !m_fDontBlink )
|
|
{
|
|
/*//<<TEMP>>//<<TEMP>>
|
|
if ( ( pev->skin == 0 ) && random->RandomInt(0,0x7F) == 0 )
|
|
{// start blinking!
|
|
pev->skin = HOUNDEYE_EYE_FRAMES - 1;
|
|
}
|
|
else if ( pev->skin != 0 )
|
|
{// already blinking
|
|
pev->skin--;
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Override base class activiites
|
|
// Input :
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
Activity CNPC_Houndeye::NPC_TranslateActivity( Activity eNewActivity )
|
|
{
|
|
if ( eNewActivity == ACT_IDLE && m_NPCState == NPC_STATE_COMBAT )
|
|
{
|
|
return ACT_IDLE_ANGRY;
|
|
}
|
|
return eNewActivity;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Purpose :
|
|
// Input :
|
|
// Output :
|
|
//------------------------------------------------------------------------------
|
|
int CNPC_Houndeye::TranslateSchedule( int scheduleType )
|
|
{
|
|
switch( scheduleType )
|
|
{
|
|
case SCHED_IDLE_STAND:
|
|
{
|
|
return BaseClass::TranslateSchedule( scheduleType );
|
|
}
|
|
case SCHED_RANGE_ATTACK1:
|
|
{
|
|
return SCHED_HOUND_RANGE_ATTACK1;
|
|
}
|
|
case SCHED_CHASE_ENEMY_FAILED:
|
|
{
|
|
return SCHED_COMBAT_FACE;
|
|
}
|
|
default:
|
|
{
|
|
return BaseClass::TranslateSchedule ( scheduleType );
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Purpose : Is anyone in the squad currently attacking
|
|
// Input :
|
|
// Output :
|
|
//------------------------------------------------------------------------------
|
|
bool CNPC_Houndeye::IsAnyoneInSquadAttacking( void )
|
|
{
|
|
if (!m_pSquad)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
AISquadIter_t iter;
|
|
for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
|
|
{
|
|
if (pSquadMember->IsCurSchedule(SCHED_HOUND_RANGE_ATTACK1))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//=========================================================
|
|
// SelectSchedule
|
|
//=========================================================
|
|
int CNPC_Houndeye::SelectSchedule( void )
|
|
{
|
|
switch ( m_NPCState )
|
|
{
|
|
case NPC_STATE_IDLE:
|
|
case NPC_STATE_ALERT:
|
|
{
|
|
if ( HasCondition(COND_LIGHT_DAMAGE) ||
|
|
HasCondition(COND_HEAVY_DAMAGE) )
|
|
{
|
|
return SCHED_TAKE_COVER_FROM_ORIGIN;
|
|
}
|
|
break;
|
|
}
|
|
case NPC_STATE_COMBAT:
|
|
{
|
|
// dead enemy
|
|
|
|
if ( HasCondition( COND_ENEMY_DEAD ) )
|
|
{
|
|
// call base class, all code to handle dead enemies is centralized there.
|
|
return BaseClass::SelectSchedule();
|
|
}
|
|
|
|
// If a group attack was requested attack even if attack conditions not met
|
|
if ( HasCondition( COND_HOUND_GROUP_ATTACK ))
|
|
{
|
|
// Check that I'm not standing in another hound eye
|
|
// before attacking
|
|
trace_t tr;
|
|
AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,1),
|
|
GetHullMins(), GetHullMaxs(),
|
|
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
|
|
if (!tr.startsolid)
|
|
{
|
|
return SCHED_HOUND_GROUP_ATTACK;
|
|
}
|
|
|
|
// Otherwise attack as soon as I can
|
|
else
|
|
{
|
|
m_flNextAttack = gpGlobals->curtime;
|
|
SCHED_HOUND_ATTACK_STRAFE;
|
|
}
|
|
}
|
|
|
|
// If a group retread was requested
|
|
if ( HasCondition( COND_HOUND_GROUP_RETREAT ))
|
|
{
|
|
return SCHED_HOUND_GROUP_RETREAT;
|
|
}
|
|
|
|
if ( HasCondition( COND_LIGHT_DAMAGE ) |
|
|
HasCondition( COND_HEAVY_DAMAGE ) )
|
|
{
|
|
if ( random->RandomFloat( 0 , 1 ) <= 0.4 )
|
|
{
|
|
trace_t tr;
|
|
Vector forward;
|
|
AngleVectors( GetAbsAngles(), &forward );
|
|
AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + forward * -128,
|
|
GetHullMins(), GetHullMaxs(),
|
|
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
|
|
|
|
if ( tr.fraction == 1.0 )
|
|
{
|
|
// it's clear behind, so the hound will jump
|
|
return SCHED_HOUND_HOP_RETREAT;
|
|
}
|
|
}
|
|
|
|
return SCHED_TAKE_COVER_FROM_ENEMY;
|
|
}
|
|
|
|
// If a group rally was requested
|
|
if ( HasCondition( COND_HOUND_GROUP_RALLEY ))
|
|
{
|
|
return SCHED_HOUND_GROUP_RALLEY;
|
|
}
|
|
|
|
if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
|
|
{
|
|
if (m_pSquad && random->RandomInt(0,4) == 0)
|
|
{
|
|
if (!IsAnyoneInSquadAttacking())
|
|
{
|
|
EmitSound( "NPC_Houndeye.GroupAttack" );
|
|
|
|
m_flSoundWaitTime = gpGlobals->curtime + 1.0;
|
|
|
|
m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupAttack, NULL, this );
|
|
return SCHED_HOUND_GROUP_ATTACK;
|
|
}
|
|
}
|
|
//<<TEMP>>comment
|
|
SetCollisionGroup( COLLISION_GROUP_NONE );
|
|
return SCHED_RANGE_ATTACK1;
|
|
}
|
|
else
|
|
{
|
|
if (m_pSquad && random->RandomInt(0,5) == 0)
|
|
{
|
|
if (!IsAnyoneInSquadAttacking())
|
|
{
|
|
EmitSound( "NPC_Houndeye.GroupFollow" );
|
|
|
|
m_flSoundWaitTime = gpGlobals->curtime + 1.0;
|
|
|
|
m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRalley, NULL, this );
|
|
return SCHED_HOUND_ATTACK_STRAFE;
|
|
}
|
|
}
|
|
return SCHED_HOUND_ATTACK_STRAFE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return BaseClass::SelectSchedule();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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_Houndeye::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
|
|
{
|
|
if (interactionType == g_interactionHoundeyeGroupAttack)
|
|
{
|
|
SetCondition(COND_HOUND_GROUP_ATTACK);
|
|
return true;
|
|
}
|
|
else if (interactionType == g_interactionHoundeyeGroupRetreat)
|
|
{
|
|
SetCondition(COND_HOUND_GROUP_RETREAT);
|
|
return true;
|
|
}
|
|
else if (interactionType == g_interactionHoundeyeGroupRalley)
|
|
{
|
|
SetCondition(COND_HOUND_GROUP_RALLEY);
|
|
SetTarget(sourceEnt);
|
|
m_bLoopClockwise = false;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Schedules
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//=========================================================
|
|
// SCHED_HOUND_ATTACK_STRAFE
|
|
//
|
|
// Run a cirle around my enemy
|
|
//=========================================================
|
|
AI_DEFINE_SCHEDULE
|
|
(
|
|
SCHED_HOUND_ATTACK_STRAFE ,
|
|
|
|
" Tasks "
|
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HOUND_ATTACK_STRAFE_REVERSE"
|
|
" TASK_HOUND_GET_PATH_TO_CIRCLE 0"
|
|
" TASK_RUN_PATH 0"
|
|
" TASK_WAIT_FOR_MOVEMENT 0"
|
|
""
|
|
" Interrupts "
|
|
" COND_WEAPON_SIGHT_OCCLUDED"
|
|
" COND_NEW_ENEMY"
|
|
" COND_ENEMY_DEAD"
|
|
" COND_HEAVY_DAMAGE"
|
|
" COND_CAN_RANGE_ATTACK1"
|
|
" COND_HOUND_GROUP_ATTACK"
|
|
" COND_HOUND_GROUP_RETREAT"
|
|
);
|
|
|
|
//=========================================================
|
|
// SCHED_HOUND_ATTACK_STRAFE_REVERSE
|
|
//
|
|
// Run a cirle around my enemy
|
|
//=========================================================
|
|
AI_DEFINE_SCHEDULE
|
|
(
|
|
SCHED_HOUND_ATTACK_STRAFE_REVERSE ,
|
|
|
|
" Tasks "
|
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HOUND_CHASE_ENEMY"
|
|
" TASK_HOUND_REVERSE_STRAFE_DIR 0"
|
|
" TASK_HOUND_GET_PATH_TO_CIRCLE 0"
|
|
" TASK_RUN_PATH 0"
|
|
" TASK_WAIT_FOR_MOVEMENT 0"
|
|
""
|
|
" Interrupts "
|
|
" COND_WEAPON_SIGHT_OCCLUDED"
|
|
" COND_NEW_ENEMY"
|
|
" COND_ENEMY_DEAD"
|
|
" COND_HEAVY_DAMAGE"
|
|
" COND_CAN_RANGE_ATTACK1"
|
|
" COND_HOUND_GROUP_ATTACK"
|
|
" COND_HOUND_GROUP_RETREAT"
|
|
);
|
|
|
|
//========================================================
|
|
// SCHED_HOUND_CHASE_ENEMY
|
|
//=========================================================
|
|
AI_DEFINE_SCHEDULE
|
|
(
|
|
SCHED_HOUND_CHASE_ENEMY,
|
|
|
|
" Tasks"
|
|
" TASK_SET_TOLERANCE_DISTANCE 30 "
|
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
|
|
" TASK_GET_PATH_TO_ENEMY 0"
|
|
" TASK_RUN_PATH 0"
|
|
" TASK_WAIT_FOR_MOVEMENT 0"
|
|
""
|
|
" Interrupts"
|
|
" COND_NEW_ENEMY"
|
|
" COND_ENEMY_DEAD"
|
|
" COND_CAN_RANGE_ATTACK1"
|
|
" COND_HOUND_GROUP_ATTACK"
|
|
" COND_HOUND_GROUP_RETREAT"
|
|
);
|
|
|
|
//=========================================================
|
|
// SCHED_HOUND_GROUP_ATTACK
|
|
//
|
|
// Face enemy, pause, then attack
|
|
//=========================================================
|
|
AI_DEFINE_SCHEDULE
|
|
(
|
|
SCHED_HOUND_GROUP_ATTACK ,
|
|
|
|
" Tasks "
|
|
" TASK_STOP_MOVING 0"
|
|
" TASK_FACE_ENEMY 0"
|
|
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE_ANGRY"
|
|
" TASK_SPEAK_SENTENCE 0"
|
|
" TASK_WAIT 1"
|
|
" TASK_SET_SCHEDULE SCHEDULE:SCHED_HOUND_RANGE_ATTACK1"
|
|
""
|
|
" Interrupts "
|
|
" COND_NEW_ENEMY"
|
|
" COND_ENEMY_DEAD"
|
|
" COND_HEAVY_DAMAGE"
|
|
);
|
|
|
|
//=========================================================
|
|
// > SCHED_HOUND_GROUP_RETREAT
|
|
//
|
|
// Take cover from enemy!
|
|
//=========================================================
|
|
AI_DEFINE_SCHEDULE
|
|
(
|
|
SCHED_HOUND_GROUP_RETREAT,
|
|
|
|
" Tasks"
|
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HOUND_ATTACK_STRAFE"
|
|
" TASK_STOP_MOVING 0"
|
|
" TASK_WAIT 0.2"
|
|
" TASK_SET_TOLERANCE_DISTANCE 24"
|
|
" TASK_FIND_COVER_FROM_ENEMY 0"
|
|
" TASK_RUN_PATH 0"
|
|
" TASK_WAIT_FOR_MOVEMENT 0"
|
|
" TASK_REMEMBER MEMORY:INCOVER"
|
|
" TASK_FACE_ENEMY 0"
|
|
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" // Translated to cover
|
|
" TASK_SET_SCHEDULE SCHEDULE:SCHED_HOUND_COVER_WAIT"
|
|
""
|
|
" Interrupts"
|
|
" COND_NEW_ENEMY"
|
|
);
|
|
|
|
//=========================================================
|
|
// > SCHED_HOUND_GROUP_RALLEY
|
|
//
|
|
// Run to rally hound!
|
|
//=========================================================
|
|
AI_DEFINE_SCHEDULE
|
|
(
|
|
SCHED_HOUND_GROUP_RALLEY,
|
|
|
|
" Tasks"
|
|
" TASK_SET_TOLERANCE_DISTANCE 30"
|
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HOUND_ATTACK_STRAFE"
|
|
" TASK_GET_PATH_TO_TARGET 0"
|
|
" TASK_RUN_PATH 0"
|
|
" TASK_WAIT_FOR_MOVEMENT 0"
|
|
""
|
|
" Interrupts"
|
|
" COND_NEW_ENEMY"
|
|
" COND_ENEMY_DEAD"
|
|
" COND_HEAVY_DAMAGE"
|
|
" COND_HOUND_GROUP_ATTACK"
|
|
" COND_HOUND_GROUP_RETREAT"
|
|
);
|
|
|
|
//=========================================================
|
|
// > SCHED_HOUND_COVER_WAIT
|
|
//
|
|
// Wait in cover till enemy see's me or I take damage
|
|
//=========================================================
|
|
AI_DEFINE_SCHEDULE
|
|
(
|
|
SCHED_HOUND_COVER_WAIT,
|
|
|
|
" Tasks"
|
|
" TASK_WAIT 2"
|
|
""
|
|
" Interrupts"
|
|
" COND_SEE_ENEMY"
|
|
" COND_LIGHT_DAMAGE"
|
|
);
|
|
|
|
//=========================================================
|
|
// > SCHED_HOUND_RANGE_ATTACK1
|
|
//=========================================================
|
|
AI_DEFINE_SCHEDULE
|
|
(
|
|
SCHED_HOUND_RANGE_ATTACK1,
|
|
|
|
" Tasks"
|
|
" TASK_STOP_MOVING 0"
|
|
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE_ANGRY"
|
|
" TASK_FACE_IDEAL 0"
|
|
" TASK_RANGE_ATTACK1 0"
|
|
""
|
|
" Interrupts"
|
|
//" COND_LIGHT_DAMAGE" // don't interupt on small damage
|
|
" COND_HEAVY_DAMAGE"
|
|
);
|
|
|
|
//=========================================================
|
|
// > SCHED_HOUND_HOP_RETREAT
|
|
//=========================================================
|
|
AI_DEFINE_SCHEDULE
|
|
(
|
|
SCHED_HOUND_HOP_RETREAT,
|
|
|
|
" Tasks"
|
|
" TASK_STOP_MOVING 0"
|
|
" TASK_HOUND_HOP_BACK 0"
|
|
" TASK_SET_SCHEDULE SCHEDULE:SCHED_TAKE_COVER_FROM_ENEMY"
|
|
""
|
|
" Interrupts"
|
|
);
|