|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "cbase.h"
#include "ai_default.h"
#include "ai_task.h"
#include "ai_schedule.h"
#include "ai_node.h"
#include "ai_hull.h"
#include "ai_hint.h"
#include "ai_memory.h"
#include "ai_route.h"
#include "ai_motor.h"
#include "soundent.h"
#include "game.h"
#include "npcevent.h"
#include "entitylist.h"
#include "activitylist.h"
#include "animation.h"
#include "basecombatweapon.h"
#include "IEffects.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "ammodef.h"
#include "util.h"
#include "hl1_ai_basenpc.h"
#include "hl1_basegrenade.h"
#include "movevars_shared.h"
#include "ai_basenpc.h"
ConVar sk_hassassin_health( "sk_hassassin_health", "50" );
//=========================================================
// monster-specific schedule types
//=========================================================
enum { SCHED_ASSASSIN_EXPOSED = LAST_SHARED_SCHEDULE,// cover was blown.
SCHED_ASSASSIN_JUMP, // fly through the air
SCHED_ASSASSIN_JUMP_ATTACK, // fly through the air and shoot
SCHED_ASSASSIN_JUMP_LAND, // hit and run away
SCHED_ASSASSIN_FAIL, SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY1, SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2, SCHED_ASSASSIN_TAKE_COVER_FROM_BEST_SOUND, SCHED_ASSASSIN_HIDE, SCHED_ASSASSIN_HUNT, };
Activity ACT_ASSASSIN_FLY_UP; Activity ACT_ASSASSIN_FLY_ATTACK; Activity ACT_ASSASSIN_FLY_DOWN;
//=========================================================
// monster-specific tasks
//=========================================================
enum { TASK_ASSASSIN_FALL_TO_GROUND = LAST_SHARED_TASK + 1, // falling and waiting to hit ground
};
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define ASSASSIN_AE_SHOOT1 1
#define ASSASSIN_AE_TOSS1 2
#define ASSASSIN_AE_JUMP 3
#define MEMORY_BADJUMP bits_MEMORY_CUSTOM1
class CNPC_HAssassin : public CHL1BaseNPC { DECLARE_CLASS( CNPC_HAssassin, CHL1BaseNPC );
public: void Spawn( void ); void Precache( void );
int TranslateSchedule( int scheduleType );
void HandleAnimEvent( animevent_t *pEvent ); float MaxYawSpeed() { return 360.0f; }
void Shoot ( void );
int MeleeAttack1Conditions ( float flDot, float flDist ); int RangeAttack1Conditions ( float flDot, float flDist ); int RangeAttack2Conditions ( float flDot, float flDist ); int SelectSchedule ( void );
void RunTask ( const Task_t *pTask ); void StartTask ( const Task_t *pTask );
Class_T Classify ( void );
int GetSoundInterests( void );
void RunAI( void );
float m_flLastShot; float m_flDiviation;
float m_flNextJump; Vector m_vecJumpVelocity;
float m_flNextGrenadeCheck; Vector m_vecTossVelocity; bool m_fThrowGrenade;
int m_iTargetRanderamt;
int m_iFrustration;
int m_iAmmoType;
public: DECLARE_DATADESC(); DEFINE_CUSTOM_AI;
};
LINK_ENTITY_TO_CLASS( monster_human_assassin, CNPC_HAssassin );
BEGIN_DATADESC( CNPC_HAssassin ) DEFINE_FIELD( m_flLastShot, FIELD_TIME ), DEFINE_FIELD( m_flDiviation, FIELD_FLOAT ),
DEFINE_FIELD( m_flNextJump, FIELD_TIME ), DEFINE_FIELD( m_vecJumpVelocity, FIELD_VECTOR ),
DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ), DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ), DEFINE_FIELD( m_fThrowGrenade, FIELD_BOOLEAN ),
DEFINE_FIELD( m_iTargetRanderamt, FIELD_INTEGER ), DEFINE_FIELD( m_iFrustration, FIELD_INTEGER ),
//DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ),
END_DATADESC()
//=========================================================
// Spawn
//=========================================================
void CNPC_HAssassin::Spawn() { Precache( );
SetModel( "models/hassassin.mdl"); SetHullType(HULL_HUMAN); SetHullSizeNormal();
SetNavType ( NAV_GROUND ); SetSolid( SOLID_BBOX ); AddSolidFlags( FSOLID_NOT_STANDABLE ); SetMoveType( MOVETYPE_STEP ); m_bloodColor = BLOOD_COLOR_RED; ClearEffects(); m_iHealth = sk_hassassin_health.GetFloat(); m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result )
m_NPCState = NPC_STATE_NONE; m_HackedGunPos = Vector( 0, 24, 48 );
m_iTargetRanderamt = 20; SetRenderColor( 255, 255, 255, 20 ); m_nRenderMode = kRenderTransTexture;
CapabilitiesClear(); CapabilitiesAdd( bits_CAP_MOVE_GROUND ); CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_RANGE_ATTACK2 | bits_CAP_INNATE_MELEE_ATTACK1 );
NPCInit(); }
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CNPC_HAssassin::Precache() { m_iAmmoType = GetAmmoDef()->Index("9mmRound");
PrecacheModel("models/hassassin.mdl");
UTIL_PrecacheOther( "npc_handgrenade" );
PrecacheScriptSound( "HAssassin.Shot" ); PrecacheScriptSound( "HAssassin.Beamsound" ); PrecacheScriptSound( "HAssassin.Footstep" ); }
int CNPC_HAssassin::GetSoundInterests( void ) { return SOUND_WORLD | SOUND_COMBAT | SOUND_PLAYER | SOUND_DANGER; }
Class_T CNPC_HAssassin::Classify ( void ) { return CLASS_HUMAN_MILITARY; }
//=========================================================
// CheckMeleeAttack1 - jump like crazy if the enemy gets too close.
//=========================================================
int CNPC_HAssassin::MeleeAttack1Conditions ( float flDot, float flDist ) { if ( m_flNextJump < gpGlobals->curtime && ( flDist <= 128 || HasMemory( MEMORY_BADJUMP )) && GetEnemy() != NULL ) { trace_t tr;
Vector vecMin = Vector( random->RandomFloat( 0, -64), random->RandomFloat( 0, -64 ), 0 ); Vector vecMax = Vector( random->RandomFloat( 0, 64), random->RandomFloat( 0, 64 ), 160 );
Vector vecDest = GetAbsOrigin() + Vector( random->RandomFloat( -64, 64), random->RandomFloat( -64, 64 ), 160 );
UTIL_TraceHull( GetAbsOrigin() + Vector( 0, 0, 36 ), GetAbsOrigin() + Vector( 0, 0, 36 ), vecMin, vecMax, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
//NDebugOverlay::Box( GetAbsOrigin() + Vector( 0, 0, 36 ), vecMin, vecMax, 0,0, 255, 0, 2.0 );
if ( tr.startsolid || tr.fraction < 1.0) { return COND_TOO_CLOSE_TO_ATTACK; }
float flGravity = GetCurrentGravity();
float time = sqrt( 160 / (0.5 * flGravity)); float speed = flGravity * time / 160; m_vecJumpVelocity = ( vecDest - GetAbsOrigin() ) * speed;
return COND_CAN_MELEE_ATTACK1; }
if ( flDist > 128 ) return COND_TOO_FAR_TO_ATTACK; return COND_NONE; }
//=========================================================
// CheckRangeAttack1 - drop a cap in their ass
//
//=========================================================
int CNPC_HAssassin::RangeAttack1Conditions ( float flDot, float flDist ) { if ( !HasCondition( COND_ENEMY_OCCLUDED ) && flDist > 64 && flDist <= 2048 ) { trace_t tr;
Vector vecSrc = GetAbsOrigin() + m_HackedGunPos;
// verify that a bullet fired from the gun will hit the enemy before the world.
UTIL_TraceLine( vecSrc, GetEnemy()->BodyTarget(vecSrc), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
if ( tr.fraction == 1.0 || tr.m_pEnt == GetEnemy() ) { return COND_CAN_RANGE_ATTACK1; } }
return COND_NONE; }
//=========================================================
// CheckRangeAttack2 - toss grenade is enemy gets in the way and is too close.
//=========================================================
int CNPC_HAssassin::RangeAttack2Conditions ( float flDot, float flDist ) { m_fThrowGrenade = false; if ( !FBitSet ( GetEnemy()->GetFlags(), FL_ONGROUND ) ) { // don't throw grenades at anything that isn't on the ground!
return COND_NONE; }
// don't get grenade happy unless the player starts to piss you off
if ( m_iFrustration <= 2) return COND_NONE;
if ( m_flNextGrenadeCheck < gpGlobals->curtime && !HasCondition( COND_ENEMY_OCCLUDED ) && flDist <= 512 ) { Vector vTossPos; QAngle vAngles;
GetAttachment( "grenadehand", vTossPos, vAngles );
Vector vecToss = VecCheckThrow( this, vTossPos, GetEnemy()->WorldSpaceCenter(), flDist, 0.5 ); // use dist as speed to get there in 1 second
if ( vecToss != vec3_origin ) { m_vecTossVelocity = vecToss;
// throw a hand grenade
m_fThrowGrenade = TRUE;
return COND_CAN_RANGE_ATTACK2; } }
return COND_NONE; }
//=========================================================
// StartTask
//=========================================================
void CNPC_HAssassin::StartTask ( const Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_RANGE_ATTACK2: if (!m_fThrowGrenade) { TaskComplete( ); } else { BaseClass::StartTask ( pTask ); } break; case TASK_ASSASSIN_FALL_TO_GROUND: m_flWaitFinished = gpGlobals->curtime + 2.0f; break; default: BaseClass::StartTask ( pTask ); break; } }
//=========================================================
// RunTask
//=========================================================
void CNPC_HAssassin::RunTask ( const Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_ASSASSIN_FALL_TO_GROUND: GetMotor()->SetIdealYawAndUpdate( GetEnemyLKP() );
if ( IsSequenceFinished() ) { if ( GetAbsVelocity().z > 0) { SetActivity( ACT_ASSASSIN_FLY_UP ); } else if ( HasCondition ( COND_SEE_ENEMY )) { SetActivity( ACT_ASSASSIN_FLY_ATTACK ); SetCycle( 0 ); } else { SetActivity( ACT_ASSASSIN_FLY_DOWN ); SetCycle( 0 ); } ResetSequenceInfo( ); }
if ( GetFlags() & FL_ONGROUND) { TaskComplete( ); } else if( gpGlobals->curtime > m_flWaitFinished || GetAbsVelocity().z == 0.0 ) { // I've waited two seconds and haven't hit the ground. Try to force it.
trace_t trace; UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 1 ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &trace );
if( trace.DidHitWorld() ) { SetGroundEntity( trace.m_pEnt ); } else { // Try again in a couple of seconds.
m_flWaitFinished = gpGlobals->curtime + 2.0f; } } break; default: BaseClass::RunTask ( pTask ); break; } }
//=========================================================
// GetSchedule - Decides which type of schedule best suits
// the monster's current state and conditions. Then calls
// monster's member function to get a pointer to a schedule
// of the proper type.
//=========================================================
int CNPC_HAssassin::SelectSchedule ( void ) { switch ( m_NPCState ) { case NPC_STATE_IDLE: case NPC_STATE_ALERT: { if ( HasCondition ( COND_HEAR_DANGER ) || HasCondition ( COND_HEAR_COMBAT ) ) { if ( HasCondition ( COND_HEAR_DANGER ) ) return SCHED_TAKE_COVER_FROM_BEST_SOUND; else return SCHED_INVESTIGATE_SOUND; } } 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(); }
// flying?
if ( GetMoveType() == MOVETYPE_FLYGRAVITY ) { if ( GetFlags() & FL_ONGROUND ) { //Msg( "landed\n" );
// just landed
SetMoveType( MOVETYPE_STEP ); return SCHED_ASSASSIN_JUMP_LAND; } else { //Msg("jump\n");
// jump or jump/shoot
if ( m_NPCState == NPC_STATE_COMBAT ) return SCHED_ASSASSIN_JUMP; else return SCHED_ASSASSIN_JUMP_ATTACK; } }
if ( HasCondition ( COND_HEAR_DANGER ) ) { return SCHED_TAKE_COVER_FROM_BEST_SOUND; }
if ( HasCondition ( COND_LIGHT_DAMAGE ) ) { m_iFrustration++; } if ( HasCondition ( COND_HEAVY_DAMAGE ) ) { m_iFrustration++; }
// jump player!
if ( HasCondition ( COND_CAN_MELEE_ATTACK1 ) ) { //Msg( "melee attack 1\n");
return SCHED_MELEE_ATTACK1; }
// throw grenade
if ( HasCondition ( COND_CAN_RANGE_ATTACK2 ) ) { //Msg( "range attack 2\n");
return SCHED_RANGE_ATTACK2; }
// spotted
if ( HasCondition ( COND_SEE_ENEMY ) && HasCondition ( COND_ENEMY_FACING_ME ) ) { //Msg("exposed\n");
m_iFrustration++; return SCHED_ASSASSIN_EXPOSED; }
// can attack
if ( HasCondition ( COND_CAN_RANGE_ATTACK1 ) ) { //Msg( "range attack 1\n" );
m_iFrustration = 0; return SCHED_RANGE_ATTACK1; }
if ( HasCondition ( COND_SEE_ENEMY ) ) { //Msg( "face\n");
return SCHED_COMBAT_FACE; }
// new enemy
if ( HasCondition ( COND_NEW_ENEMY ) ) { //Msg( "take cover\n");
return SCHED_TAKE_COVER_FROM_ENEMY; }
// ALERT( at_console, "stand\n");
return SCHED_ALERT_STAND; } break; }
return BaseClass::SelectSchedule(); }
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//
// Returns number of events handled, 0 if none.
//=========================================================
void CNPC_HAssassin::HandleAnimEvent( animevent_t *pEvent ) { switch( pEvent->event ) { case ASSASSIN_AE_SHOOT1: Shoot( ); break; case ASSASSIN_AE_TOSS1: { Vector vTossPos; QAngle vAngles;
GetAttachment( "grenadehand", vTossPos, vAngles );
CHandGrenade *pGrenade = (CHandGrenade*)Create( "grenade_hand", vTossPos, vec3_angle ); if ( pGrenade ) { pGrenade->ShootTimed( this, m_vecTossVelocity, 2.0 ); }
m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown.
m_fThrowGrenade = FALSE; // !!!LATER - when in a group, only try to throw grenade if ordered.
} break; case ASSASSIN_AE_JUMP: { SetMoveType( MOVETYPE_FLYGRAVITY ); SetGroundEntity( NULL ); SetAbsVelocity( m_vecJumpVelocity ); m_flNextJump = gpGlobals->curtime + 3.0; } return; default: BaseClass::HandleAnimEvent( pEvent ); break; } }
//=========================================================
// Shoot
//=========================================================
void CNPC_HAssassin::Shoot ( void ) { Vector vForward, vRight, vUp; Vector vecShootOrigin; QAngle vAngles;
if ( GetEnemy() == NULL) { return; }
GetAttachment( "guntip", vecShootOrigin, vAngles ); Vector vecShootDir = GetShootEnemyDir( vecShootOrigin );
if (m_flLastShot + 2 < gpGlobals->curtime) { m_flDiviation = 0.10; } else { m_flDiviation -= 0.01; if (m_flDiviation < 0.02) m_flDiviation = 0.02; } m_flLastShot = gpGlobals->curtime;
AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
Vector vecShellVelocity = vRight * random->RandomFloat(40,90) + vUp * random->RandomFloat(75,200) + vForward * random->RandomFloat(-40, 40); EjectShell( GetAbsOrigin() + vUp * 32 + vForward * 12, vecShellVelocity, GetAbsAngles().y, 0 ); FireBullets( 1, vecShootOrigin, vecShootDir, Vector( m_flDiviation, m_flDiviation, m_flDiviation ), 2048, m_iAmmoType ); // shoot +-8 degrees
//NDebugOverlay::Line( vecShootOrigin, vecShootOrigin + vecShootDir * 2048, 255, 0, 0, true, 2.0 );
CPASAttenuationFilter filter( this ); EmitSound( filter, entindex(), "HAssassin.Shot" );
DoMuzzleFlash();
VectorAngles( vecShootDir, vAngles ); SetPoseParameter( "shoot", vecShootDir.x );
m_cAmmoLoaded--; }
//=========================================================
//=========================================================
int CNPC_HAssassin::TranslateSchedule ( int scheduleType ) { // Msg( "%d\n", m_iFrustration );
switch ( scheduleType ) { case SCHED_TAKE_COVER_FROM_ENEMY:
if ( m_iHealth > 30 ) return SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY1; else return SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2;
case SCHED_TAKE_COVER_FROM_BEST_SOUND: return SCHED_ASSASSIN_TAKE_COVER_FROM_BEST_SOUND; case SCHED_FAIL:
if ( m_NPCState == NPC_STATE_COMBAT ) return SCHED_ASSASSIN_FAIL;
break; case SCHED_ALERT_STAND:
if ( m_NPCState == NPC_STATE_COMBAT ) return SCHED_ASSASSIN_HIDE;
break; //case SCHED_CHASE_ENEMY:
// return SCHED_ASSASSIN_HUNT;
case SCHED_MELEE_ATTACK1:
if ( GetFlags() & FL_ONGROUND) { if (m_flNextJump > gpGlobals->curtime) { // can't jump yet, go ahead and fail
return SCHED_ASSASSIN_FAIL; } else { return SCHED_ASSASSIN_JUMP; } } else { return SCHED_ASSASSIN_JUMP_ATTACK; } }
return BaseClass::TranslateSchedule( scheduleType ); }
//=========================================================
// RunAI
//=========================================================
void CNPC_HAssassin::RunAI( void ) { BaseClass::RunAI();
// always visible if moving
// always visible is not on hard
if (g_iSkillLevel != SKILL_HARD || GetEnemy() == NULL || m_lifeState == LIFE_DEAD || GetActivity() == ACT_RUN || GetActivity() == ACT_WALK || !(GetFlags() & FL_ONGROUND)) m_iTargetRanderamt = 255; else m_iTargetRanderamt = 20;
CPASAttenuationFilter filter( this );
if ( GetRenderColor().a > m_iTargetRanderamt) { if ( GetRenderColor().a == 255) { EmitSound( filter, entindex(), "HAssassin.Beamsound" ); }
SetRenderColorA( MAX( GetRenderColor().a - 50, m_iTargetRanderamt ) ); m_nRenderMode = kRenderTransTexture; } else if ( GetRenderColor().a < m_iTargetRanderamt) { SetRenderColorA ( MIN( GetRenderColor().a + 50, m_iTargetRanderamt ) ); if (GetRenderColor().a == 255) m_nRenderMode = kRenderNormal; }
if ( GetActivity() == ACT_RUN || GetActivity() == ACT_WALK) { static int iStep = 0; iStep = ! iStep; if (iStep) { EmitSound( filter, entindex(), "HAssassin.Footstep" ); } } }
AI_BEGIN_CUSTOM_NPC( monster_human_assassin, CNPC_HAssassin )
DECLARE_TASK( TASK_ASSASSIN_FALL_TO_GROUND )
DECLARE_ACTIVITY( ACT_ASSASSIN_FLY_UP ) DECLARE_ACTIVITY( ACT_ASSASSIN_FLY_ATTACK ) DECLARE_ACTIVITY( ACT_ASSASSIN_FLY_DOWN )
//=========================================================
// AI Schedules Specific to this monster
//=========================================================
//=========================================================
// Enemy exposed assasin's cover
//=========================================================
//=========================================================
// > SCHED_ASSASSIN_EXPOSED
//=========================================================
DEFINE_SCHEDULE ( SCHED_ASSASSIN_EXPOSED, " Tasks" " TASK_STOP_MOVING 0" " TASK_RANGE_ATTACK1 0" " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASSASSIN_JUMP" " TASK_SET_SCHEDULE SCHEDULE:SCHED_TAKE_COVER_FROM_ENEMY" " " " Interrupts" " COND_CAN_MELEE_ATTACK1" )
//=========================================================
// > SCHED_ASSASSIN_JUMP
//=========================================================
DEFINE_SCHEDULE ( SCHED_ASSASSIN_JUMP, " Tasks" " TASK_STOP_MOVING 0" " TASK_PLAY_SEQUENCE ACTIVITY:ACT_HOP" " TASK_SET_SCHEDULE SCHEDULE:SCHED_ASSASSIN_JUMP_ATTACK" " " " Interrupts" )
//=========================================================
// > SCHED_ASSASSIN_JUMP_ATTACK
//=========================================================
DEFINE_SCHEDULE ( SCHED_ASSASSIN_JUMP_ATTACK, " Tasks" " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASSASSIN_JUMP_LAND" " TASK_ASSASSIN_FALL_TO_GROUND 0" " " " Interrupts" )
//=========================================================
// > SCHED_ASSASSIN_JUMP_LAND
//=========================================================
DEFINE_SCHEDULE ( SCHED_ASSASSIN_JUMP_LAND, " Tasks" " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASSASSIN_EXPOSED" " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" " TASK_REMEMBER MEMORY:CUSTOM1" " TASK_FIND_NODE_COVER_FROM_ENEMY 0" " TASK_RUN_PATH 0" " TASK_FORGET MEMORY:CUSTOM1" " TASK_WAIT_FOR_MOVEMENT 0" " TASK_REMEMBER MEMORY:INCOVER" " TASK_FACE_ENEMY 0" " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RANGE_ATTACK1"
" " " Interrupts" )
//=========================================================
// Fail Schedule
//=========================================================
//=========================================================
// > SCHED_ASSASSIN_FAIL
//=========================================================
DEFINE_SCHEDULE ( SCHED_ASSASSIN_FAIL, " Tasks" " TASK_STOP_MOVING 0" " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" " TASK_WAIT_FACE_ENEMY 2" " TASK_SET_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY" " " " Interrupts" " COND_LIGHT_DAMAGE" " COND_HEAVY_DAMAGE" " COND_CAN_RANGE_ATTACK1" " COND_CAN_RANGE_ATTACK2" " COND_CAN_MELEE_ATTACK1" " COND_HEAR_DANGER" " COND_HEAR_PLAYER" )
//=========================================================
// > SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY1
//=========================================================
DEFINE_SCHEDULE ( SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY1, " Tasks" " TASK_STOP_MOVING 0" " TASK_WAIT 0.2" " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RANGE_ATTACK1" " TASK_FIND_COVER_FROM_ENEMY 0" " TASK_RUN_PATH 0" " TASK_WAIT_FOR_MOVEMENT 0" " TASK_REMEMBER MEMORY:INCOVER" " TASK_FACE_ENEMY 0" " " " Interrupts" " COND_CAN_MELEE_ATTACK1" " COND_NEW_ENEMY" " COND_HEAR_DANGER" )
//=========================================================
// > SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2
//=========================================================
DEFINE_SCHEDULE ( SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2, " Tasks" " TASK_STOP_MOVING 0" " TASK_WAIT 0.2" " TASK_FACE_ENEMY 0" " TASK_RANGE_ATTACK1 0" " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RANGE_ATTACK1" " TASK_FIND_COVER_FROM_ENEMY 0" " TASK_RUN_PATH 0" " TASK_WAIT_FOR_MOVEMENT 0" " TASK_REMEMBER MEMORY:INCOVER" " TASK_FACE_ENEMY 0" " " " Interrupts" " COND_CAN_MELEE_ATTACK1" " COND_NEW_ENEMY" " COND_HEAR_DANGER" )
//=========================================================
// hide from the loudest sound source
//=========================================================
//=========================================================
// > SCHED_ASSASSIN_TAKE_COVER_FROM_BEST_SOUND
//=========================================================
DEFINE_SCHEDULE ( SCHED_ASSASSIN_TAKE_COVER_FROM_BEST_SOUND, " Tasks" " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_MELEE_ATTACK1" " TASK_STOP_MOVING 0" " TASK_FIND_COVER_FROM_BEST_SOUND 0" " TASK_RUN_PATH 0" " TASK_WAIT_FOR_MOVEMENT 0" " TASK_REMEMBER MEMORY:INCOVER" " TASK_TURN_LEFT 179" " " " Interrupts" " COND_NEW_ENEMY" )
//=========================================================
// > SCHED_ASSASSIN_HIDE
//=========================================================
DEFINE_SCHEDULE ( SCHED_ASSASSIN_HIDE, " Tasks" " TASK_STOP_MOVING 0" " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" " TASK_WAIT 2.0" " TASK_SET_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY" " Interrupts" " COND_NEW_ENEMY" " COND_SEE_ENEMY" " COND_SEE_FEAR" " COND_LIGHT_DAMAGE" " COND_HEAVY_DAMAGE" " COND_PROVOKED" " COND_HEAR_DANGER" )
//=========================================================
// > SCHED_ASSASSIN_HUNT
//=========================================================
DEFINE_SCHEDULE ( SCHED_ASSASSIN_HUNT, " Tasks" " TASK_STOP_MOVING 0" " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2" " TASK_GET_PATH_TO_ENEMY 0" " TASK_RUN_PATH 0" " TASK_WAIT_FOR_MOVEMENT 0" " Interrupts" " COND_NEW_ENEMY" " COND_CAN_RANGE_ATTACK1" " COND_HEAR_DANGER" )
AI_END_CUSTOM_NPC()
|