summaryrefslogtreecommitdiff
path: root/game/server/hl1/hl1_npc_hassassin.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl1/hl1_npc_hassassin.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/hl1/hl1_npc_hassassin.cpp')
-rw-r--r--game/server/hl1/hl1_npc_hassassin.cpp951
1 files changed, 951 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_hassassin.cpp b/game/server/hl1/hl1_npc_hassassin.cpp
new file mode 100644
index 0000000..fa978fe
--- /dev/null
+++ b/game/server/hl1/hl1_npc_hassassin.cpp
@@ -0,0 +1,951 @@
+//========= 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()