You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1313 lines
31 KiB
1313 lines
31 KiB
//========= 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 "hl1_ai_basenpc.h" |
|
#include "ai_navigator.h" |
|
#include "decals.h" |
|
#include "effect_dispatch_data.h" |
|
#include "te_effect_dispatch.h" |
|
#include "Sprite.h" |
|
|
|
ConVar sk_bigmomma_health_factor( "sk_bigmomma_health_factor", "1" ); |
|
ConVar sk_bigmomma_dmg_slash( "sk_bigmomma_dmg_slash", "50" ); |
|
ConVar sk_bigmomma_dmg_blast( "sk_bigmomma_dmg_blast", "100" ); |
|
ConVar sk_bigmomma_radius_blast( "sk_bigmomma_radius_blast", "250" ); |
|
|
|
float GetCurrentGravity( void ); |
|
|
|
|
|
//========================================================= |
|
// Monster's Anim Events Go Here |
|
//========================================================= |
|
#define BIG_AE_STEP1 1 // Footstep left |
|
#define BIG_AE_STEP2 2 // Footstep right |
|
#define BIG_AE_STEP3 3 // Footstep back left |
|
#define BIG_AE_STEP4 4 // Footstep back right |
|
#define BIG_AE_SACK 5 // Sack slosh |
|
#define BIG_AE_DEATHSOUND 6 // Death sound |
|
|
|
#define BIG_AE_MELEE_ATTACKBR 8 // Leg attack |
|
#define BIG_AE_MELEE_ATTACKBL 9 // Leg attack |
|
#define BIG_AE_MELEE_ATTACK1 10 // Leg attack |
|
#define BIG_AE_MORTAR_ATTACK1 11 // Launch a mortar |
|
#define BIG_AE_LAY_CRAB 12 // Lay a headcrab |
|
#define BIG_AE_JUMP_FORWARD 13 // Jump up and forward |
|
#define BIG_AE_SCREAM 14 // alert sound |
|
#define BIG_AE_PAIN_SOUND 15 // pain sound |
|
#define BIG_AE_ATTACK_SOUND 16 // attack sound |
|
#define BIG_AE_BIRTH_SOUND 17 // birth sound |
|
#define BIG_AE_EARLY_TARGET 50 // Fire target early |
|
|
|
|
|
enum |
|
{ |
|
SCHED_NODE_FAIL = LAST_SHARED_SCHEDULE, |
|
SCHED_BIG_NODE, |
|
}; |
|
|
|
enum |
|
{ |
|
TASK_MOVE_TO_NODE_RANGE = LAST_SHARED_TASK, // Move within node range |
|
TASK_FIND_NODE, // Find my next node |
|
TASK_PLAY_NODE_PRESEQUENCE, // Play node pre-script |
|
TASK_PLAY_NODE_SEQUENCE, // Play node script |
|
TASK_PROCESS_NODE, // Fire targets, etc. |
|
TASK_WAIT_NODE, // Wait at the node |
|
TASK_NODE_DELAY, // Delay walking toward node for a bit. You've failed to get there |
|
TASK_NODE_YAW, // Get the best facing direction for this node |
|
TASK_CHECK_NODE_PROXIMITY, |
|
}; |
|
|
|
// User defined conditions |
|
//#define bits_COND_NODE_SEQUENCE ( COND_SPECIAL1 ) // pev->netname contains the name of a sequence to play |
|
|
|
// Attack distance constants |
|
#define BIG_ATTACKDIST 170 |
|
#define BIG_MORTARDIST 800 |
|
#define BIG_MAXCHILDREN 6 // Max # of live headcrab children |
|
|
|
|
|
#define bits_MEMORY_CHILDPAIR (bits_MEMORY_CUSTOM1) |
|
#define bits_MEMORY_ADVANCE_NODE (bits_MEMORY_CUSTOM2) |
|
#define bits_MEMORY_COMPLETED_NODE (bits_MEMORY_CUSTOM3) |
|
#define bits_MEMORY_FIRED_NODE (bits_MEMORY_CUSTOM4) |
|
|
|
int gSpitSprite, gSpitDebrisSprite; |
|
|
|
|
|
Vector VecCheckSplatToss( CBaseEntity *pEntity, const Vector &vecSpot1, Vector vecSpot2, float maxHeight ); |
|
void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count ); |
|
|
|
#define SF_INFOBM_RUN 0x0001 |
|
#define SF_INFOBM_WAIT 0x0002 |
|
|
|
//========================================================= |
|
// Mortar shot entity |
|
//========================================================= |
|
class CBMortar : public CBaseAnimating |
|
{ |
|
DECLARE_CLASS( CBMortar, CBaseAnimating ); |
|
public: |
|
void Spawn( void ); |
|
|
|
virtual void Precache(); |
|
|
|
static CBMortar *Shoot( CBaseEntity *pOwner, Vector vecStart, Vector vecVelocity ); |
|
void Touch( CBaseEntity *pOther ); |
|
void Animate( void ); |
|
|
|
float m_flDmgTime; |
|
|
|
DECLARE_DATADESC(); |
|
|
|
int m_maxFrame; |
|
int m_iFrame; |
|
|
|
CSprite* pSprite; |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( bmortar, CBMortar ); |
|
|
|
BEGIN_DATADESC( CBMortar ) |
|
DEFINE_FIELD( m_maxFrame, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_flDmgTime, FIELD_FLOAT ), |
|
DEFINE_FUNCTION( Animate ), |
|
DEFINE_FIELD( m_iFrame, FIELD_INTEGER ), |
|
DEFINE_FIELD( pSprite, FIELD_CLASSPTR ), |
|
END_DATADESC() |
|
|
|
|
|
// AI Nodes for Big Momma |
|
class CInfoBM : public CPointEntity |
|
{ |
|
DECLARE_CLASS( CInfoBM, CPointEntity ); |
|
public: |
|
void Spawn( void ); |
|
bool KeyValue( const char *szKeyName, const char *szValue ); |
|
|
|
// name in pev->targetname |
|
// next in pev->target |
|
// radius in pev->scale |
|
// health in pev->health |
|
// Reach target in pev->message |
|
// Reach delay in pev->speed |
|
// Reach sequence in pev->netname |
|
|
|
DECLARE_DATADESC(); |
|
|
|
float m_flRadius; |
|
float m_flDelay; |
|
string_t m_iszReachTarget; |
|
string_t m_iszReachSequence; |
|
string_t m_iszPreSequence; |
|
|
|
COutputEvent m_OnAnimationEvent; |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( info_bigmomma, CInfoBM ); |
|
|
|
BEGIN_DATADESC( CInfoBM ) |
|
DEFINE_FIELD( m_flRadius, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flDelay, FIELD_FLOAT ), |
|
DEFINE_KEYFIELD( m_iszReachTarget, FIELD_STRING, "reachtarget" ), |
|
DEFINE_KEYFIELD( m_iszReachSequence, FIELD_STRING, "reachsequence" ), |
|
DEFINE_KEYFIELD( m_iszPreSequence, FIELD_STRING, "presequence" ), |
|
DEFINE_OUTPUT( m_OnAnimationEvent, "OnAnimationEvent" ), |
|
END_DATADESC() |
|
|
|
|
|
void CInfoBM::Spawn( void ) |
|
{ |
|
BaseClass::Spawn(); |
|
|
|
// Msg( "Name %s\n", STRING( GetEntityName() ) ); |
|
} |
|
|
|
bool CInfoBM::KeyValue( const char *szKeyName, const char *szValue ) |
|
{ |
|
if (FStrEq( szKeyName, "radius")) |
|
{ |
|
m_flRadius = atof( szValue ); |
|
return true; |
|
} |
|
else if (FStrEq( szKeyName, "reachdelay" )) |
|
{ |
|
m_flDelay = atof( szValue); |
|
return true; |
|
} |
|
else if (FStrEq( szKeyName, "health" )) |
|
{ |
|
m_iHealth = atoi( szValue ); |
|
return true; |
|
} |
|
|
|
return BaseClass::KeyValue(szKeyName, szValue ); |
|
} |
|
|
|
// UNDONE: |
|
// |
|
#define BIG_CHILDCLASS "monster_babycrab" |
|
|
|
|
|
class CNPC_BigMomma : public CHL1BaseNPC |
|
{ |
|
DECLARE_CLASS( CNPC_BigMomma, CHL1BaseNPC ); |
|
public: |
|
|
|
void Spawn( void ); |
|
void Precache( void ); |
|
|
|
Class_T Classify( void ) { return CLASS_ALIEN_MONSTER; }; |
|
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ); |
|
int OnTakeDamage( const CTakeDamageInfo &info ); |
|
void HandleAnimEvent( animevent_t *pEvent ); |
|
void LayHeadcrab( void ); |
|
void LaunchMortar( void ); |
|
void DeathNotice( CBaseEntity *pevChild ); |
|
|
|
int MeleeAttack1Conditions( float flDot, float flDist ); // Slash |
|
int MeleeAttack2Conditions( float flDot, float flDist ); // Lay a crab |
|
int RangeAttack1Conditions( float flDot, float flDist ); // Mortar launch |
|
|
|
|
|
BOOL CanLayCrab( void ) |
|
{ |
|
if ( m_crabTime < gpGlobals->curtime && m_crabCount < BIG_MAXCHILDREN ) |
|
{ |
|
// Don't spawn crabs inside each other |
|
Vector mins = GetAbsOrigin() - Vector( 32, 32, 0 ); |
|
Vector maxs = GetAbsOrigin() + Vector( 32, 32, 0 ); |
|
|
|
CBaseEntity *pList[2]; |
|
int count = UTIL_EntitiesInBox( pList, 2, mins, maxs, FL_NPC ); |
|
for ( int i = 0; i < count; i++ ) |
|
{ |
|
if ( pList[i] != this ) // Don't hurt yourself! |
|
return COND_NONE; |
|
} |
|
return COND_CAN_MELEE_ATTACK2; |
|
} |
|
|
|
return COND_NONE; |
|
} |
|
|
|
void Activate ( void ); |
|
|
|
void NodeReach( void ); |
|
void NodeStart( string_t iszNextNode ); |
|
bool ShouldGoToNode( void ); |
|
|
|
const char *GetNodeSequence( void ) |
|
{ |
|
CInfoBM *pTarget = (CInfoBM*)GetTarget(); |
|
|
|
if ( pTarget && FClassnameIs( pTarget, "info_bigmomma" ) ) |
|
{ |
|
return STRING( pTarget->m_iszReachSequence ); // netname holds node sequence |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
const char *GetNodePresequence( void ) |
|
{ |
|
CInfoBM *pTarget = (CInfoBM *)GetTarget(); |
|
|
|
if ( pTarget && FClassnameIs( pTarget, "info_bigmomma" ) ) |
|
{ |
|
return STRING( pTarget->m_iszPreSequence ); |
|
} |
|
return NULL; |
|
} |
|
|
|
float GetNodeDelay( void ) |
|
{ |
|
CInfoBM *pTarget = (CInfoBM *)GetTarget(); |
|
|
|
if ( pTarget && FClassnameIs( pTarget, "info_bigmomma" ) ) |
|
{ |
|
return pTarget->m_flDelay; // Speed holds node delay |
|
} |
|
return 0; |
|
} |
|
|
|
float GetNodeRange( void ) |
|
{ |
|
CInfoBM *pTarget = (CInfoBM *)GetTarget(); |
|
|
|
if ( pTarget && FClassnameIs( pTarget, "info_bigmomma" ) ) |
|
{ |
|
return pTarget->m_flRadius; // Scale holds node delay |
|
} |
|
|
|
return 1e6; |
|
} |
|
|
|
float GetNodeYaw( void ) |
|
{ |
|
CBaseEntity *pTarget = GetTarget(); |
|
|
|
if ( pTarget ) |
|
{ |
|
if ( pTarget->GetAbsAngles().y != 0 ) |
|
return pTarget->GetAbsAngles().y; |
|
} |
|
|
|
return GetAbsAngles().y; |
|
} |
|
|
|
// Restart the crab count on each new level |
|
void OnRestore( void ) |
|
{ |
|
BaseClass::OnRestore(); |
|
m_crabCount = 0; |
|
} |
|
|
|
int SelectSchedule( void ); |
|
void StartTask( const Task_t *pTask ); |
|
void RunTask( const Task_t *pTask ); |
|
|
|
float MaxYawSpeed( void ); |
|
|
|
DECLARE_DATADESC(); |
|
|
|
DEFINE_CUSTOM_AI; |
|
|
|
/* |
|
void RunTask( Task_t *pTask ); |
|
void StartTask( Task_t *pTask ); |
|
Schedule_t *GetSchedule( void ); |
|
Schedule_t *GetScheduleOfType( int Type ); |
|
void SetYawSpeed( void ); |
|
|
|
CUSTOM_SCHEDULES; |
|
*/ |
|
|
|
private: |
|
float m_nodeTime; |
|
float m_crabTime; |
|
float m_mortarTime; |
|
float m_painSoundTime; |
|
int m_crabCount; |
|
float m_flDmgTime; |
|
|
|
bool m_bDoneWithPath; |
|
|
|
string_t m_iszTarget; |
|
string_t m_iszNetName; |
|
float m_flWait; |
|
|
|
Vector m_vTossDir; |
|
|
|
|
|
}; |
|
|
|
|
|
BEGIN_DATADESC( CNPC_BigMomma ) |
|
DEFINE_FIELD( m_nodeTime, FIELD_TIME ), |
|
DEFINE_FIELD( m_crabTime, FIELD_TIME ), |
|
DEFINE_FIELD( m_mortarTime, FIELD_TIME ), |
|
DEFINE_FIELD( m_painSoundTime, FIELD_TIME ), |
|
DEFINE_KEYFIELD( m_iszNetName, FIELD_STRING, "netname" ), |
|
DEFINE_FIELD( m_flWait, FIELD_TIME ), |
|
DEFINE_FIELD( m_iszTarget, FIELD_STRING ), |
|
|
|
DEFINE_FIELD( m_crabCount, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_flDmgTime, FIELD_TIME ), |
|
DEFINE_FIELD( m_bDoneWithPath, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_vTossDir, FIELD_VECTOR ), |
|
|
|
END_DATADESC() |
|
|
|
|
|
LINK_ENTITY_TO_CLASS ( monster_bigmomma, CNPC_BigMomma ); |
|
|
|
//========================================================= |
|
// Spawn |
|
//========================================================= |
|
void CNPC_BigMomma::Spawn() |
|
{ |
|
Precache( ); |
|
|
|
SetModel( "models/big_mom.mdl" ); |
|
UTIL_SetSize( this, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); |
|
|
|
Vector vecSurroundingMins( -95, -95, 0 ); |
|
Vector vecSurroundingMaxs( 95, 95, 190 ); |
|
CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &vecSurroundingMins, &vecSurroundingMaxs ); |
|
|
|
SetNavType( NAV_GROUND ); |
|
SetSolid( SOLID_BBOX ); |
|
AddSolidFlags( FSOLID_NOT_STANDABLE ); |
|
SetMoveType( MOVETYPE_STEP ); |
|
|
|
m_bloodColor = BLOOD_COLOR_GREEN; |
|
m_iHealth = 150 * sk_bigmomma_health_factor.GetFloat(); |
|
|
|
SetHullType( HULL_WIDE_HUMAN ); |
|
SetHullSizeNormal(); |
|
|
|
CapabilitiesAdd( bits_CAP_MOVE_GROUND ); |
|
CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK2 ); |
|
|
|
// pev->view_ofs = Vector ( 0, 0, 128 );// position of the eyes relative to monster's origin. |
|
m_flFieldOfView = 0.3;// indicates the width of this monster's forward view cone ( as a dotproduct result ) |
|
m_NPCState = NPC_STATE_NONE; |
|
|
|
SetRenderColor( 255, 255, 255, 255 ); |
|
|
|
m_bDoneWithPath = false; |
|
|
|
m_nodeTime = 0.0f; |
|
|
|
m_iszTarget = m_iszNetName; |
|
|
|
NPCInit(); |
|
|
|
BaseClass::Spawn(); |
|
} |
|
|
|
//========================================================= |
|
// Precache - precaches all resources this monster needs |
|
//========================================================= |
|
void CNPC_BigMomma::Precache() |
|
{ |
|
PrecacheModel("models/big_mom.mdl"); |
|
|
|
UTIL_PrecacheOther( BIG_CHILDCLASS ); |
|
|
|
// TEMP: Squid |
|
PrecacheModel("sprites/mommaspit.vmt");// spit projectile. |
|
gSpitSprite = PrecacheModel("sprites/mommaspout.vmt");// client side spittle. |
|
gSpitDebrisSprite = PrecacheModel("sprites/mommablob.vmt" ); |
|
|
|
PrecacheScriptSound( "BigMomma.Pain" ); |
|
PrecacheScriptSound( "BigMomma.Attack" ); |
|
PrecacheScriptSound( "BigMomma.AttackHit" ); |
|
PrecacheScriptSound( "BigMomma.Alert" ); |
|
PrecacheScriptSound( "BigMomma.Birth" ); |
|
PrecacheScriptSound( "BigMomma.Sack" ); |
|
PrecacheScriptSound( "BigMomma.Die" ); |
|
PrecacheScriptSound( "BigMomma.FootstepLeft" ); |
|
PrecacheScriptSound( "BigMomma.FootstepRight" ); |
|
PrecacheScriptSound( "BigMomma.LayHeadcrab" ); |
|
PrecacheScriptSound( "BigMomma.ChildDie" ); |
|
PrecacheScriptSound( "BigMomma.LaunchMortar" ); |
|
} |
|
|
|
//========================================================= |
|
// SetYawSpeed - allows each sequence to have a different |
|
// turn rate associated with it. |
|
//========================================================= |
|
float CNPC_BigMomma::MaxYawSpeed ( void ) |
|
{ |
|
float ys = 90.0f; |
|
|
|
switch ( GetActivity() ) |
|
{ |
|
case ACT_IDLE: |
|
ys = 100.0f; |
|
break; |
|
default: |
|
ys = 90.0f; |
|
} |
|
|
|
return ys; |
|
} |
|
|
|
void CNPC_BigMomma::Activate( void ) |
|
{ |
|
if ( GetTarget() == NULL ) |
|
Remember( bits_MEMORY_ADVANCE_NODE ); // Start 'er up |
|
|
|
BaseClass::Activate(); |
|
} |
|
|
|
void CNPC_BigMomma::NodeStart( string_t iszNextNode ) |
|
{ |
|
m_iszTarget = iszNextNode; |
|
|
|
const char *pTargetName = STRING( m_iszTarget ); |
|
|
|
CBaseEntity *pTarget = NULL; |
|
|
|
if ( pTargetName ) |
|
pTarget = gEntList.FindEntityByName( NULL, pTargetName ); |
|
|
|
if ( pTarget == NULL ) |
|
{ |
|
//Msg( "BM: Finished the path!!\n" ); |
|
m_bDoneWithPath = true; |
|
return; |
|
} |
|
|
|
SetTarget( pTarget ); |
|
} |
|
|
|
|
|
void CNPC_BigMomma::NodeReach( void ) |
|
{ |
|
CInfoBM *pTarget = (CInfoBM*)GetTarget(); |
|
|
|
Forget( bits_MEMORY_ADVANCE_NODE ); |
|
|
|
if ( !pTarget ) |
|
return; |
|
|
|
if ( pTarget->m_iHealth >= 1 ) |
|
m_iMaxHealth = m_iHealth = pTarget->m_iHealth * sk_bigmomma_health_factor.GetFloat(); |
|
|
|
if ( !HasMemory( bits_MEMORY_FIRED_NODE ) ) |
|
{ |
|
if ( pTarget ) |
|
{ |
|
pTarget->m_OnAnimationEvent.FireOutput( this, this ); |
|
} |
|
} |
|
|
|
Forget( bits_MEMORY_FIRED_NODE ); |
|
|
|
m_iszTarget = pTarget->m_target; |
|
|
|
if ( pTarget->m_iHealth == 0 ) |
|
Remember( bits_MEMORY_ADVANCE_NODE ); // Move on if no health at this node |
|
else |
|
{ |
|
GetNavigator()->ClearGoal(); |
|
} |
|
} |
|
|
|
|
|
void CNPC_BigMomma::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) |
|
{ |
|
CTakeDamageInfo dmgInfo = info; |
|
|
|
if ( ptr->hitbox <= 9 ) |
|
{ |
|
// didn't hit the sack? |
|
if ( m_flDmgTime != gpGlobals->curtime || (random->RandomInt( 0, 10 ) < 1) ) |
|
{ |
|
g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal ); |
|
m_flDmgTime = gpGlobals->curtime; |
|
} |
|
|
|
// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated |
|
dmgInfo.SetDamage( 0.1 ); |
|
} |
|
else |
|
{ |
|
SpawnBlood( ptr->endpos + ptr->plane.normal * 15, vecDir, m_bloodColor, 100 ); |
|
|
|
if ( gpGlobals->curtime > m_painSoundTime ) |
|
{ |
|
m_painSoundTime = gpGlobals->curtime + random->RandomInt(1, 3); |
|
EmitSound( "BigMomma.Pain" ); |
|
} |
|
} |
|
|
|
BaseClass::TraceAttack( dmgInfo, vecDir, ptr, pAccumulator ); |
|
} |
|
|
|
|
|
int CNPC_BigMomma::OnTakeDamage( const CTakeDamageInfo &info ) |
|
{ |
|
CTakeDamageInfo newInfo = info; |
|
|
|
// Don't take any acid damage -- BigMomma's mortar is acid |
|
if ( newInfo.GetDamageType() & DMG_ACID ) |
|
{ |
|
newInfo.SetDamage( 0 ); |
|
} |
|
|
|
// never die from damage, just advance to the next node |
|
if ( ( GetHealth() - newInfo.GetDamage() ) < 1 ) |
|
{ |
|
newInfo.SetDamage( 0 ); |
|
Remember( bits_MEMORY_ADVANCE_NODE ); |
|
DevMsg( 2, "BM: Finished node health!!!\n" ); |
|
} |
|
|
|
DevMsg( 2, "BM Health: %f\n", GetHealth() - newInfo.GetDamage() ); |
|
|
|
return BaseClass::OnTakeDamage( newInfo ); |
|
} |
|
|
|
bool CNPC_BigMomma::ShouldGoToNode( void ) |
|
{ |
|
if ( HasMemory( bits_MEMORY_ADVANCE_NODE ) ) |
|
{ |
|
if ( m_nodeTime < gpGlobals->curtime ) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
|
|
int CNPC_BigMomma::SelectSchedule( void ) |
|
{ |
|
if ( ShouldGoToNode() ) |
|
{ |
|
return SCHED_BIG_NODE; |
|
} |
|
|
|
return BaseClass::SelectSchedule(); |
|
} |
|
|
|
|
|
void CNPC_BigMomma::StartTask( const Task_t *pTask ) |
|
{ |
|
switch ( pTask->iTask ) |
|
{ |
|
case TASK_CHECK_NODE_PROXIMITY: |
|
{ |
|
|
|
} |
|
|
|
break; |
|
case TASK_FIND_NODE: |
|
{ |
|
CBaseEntity *pTarget = GetTarget(); |
|
|
|
if ( !HasMemory( bits_MEMORY_ADVANCE_NODE ) ) |
|
{ |
|
if ( pTarget ) |
|
m_iszTarget = pTarget->m_target; |
|
} |
|
|
|
NodeStart( m_iszTarget ); |
|
TaskComplete(); |
|
//Msg( "BM: Found node %s\n", STRING( m_iszTarget ) ); |
|
} |
|
break; |
|
|
|
case TASK_NODE_DELAY: |
|
m_nodeTime = gpGlobals->curtime + pTask->flTaskData; |
|
TaskComplete(); |
|
//Msg( "BM: FAIL! Delay %.2f\n", pTask->flTaskData ); |
|
break; |
|
|
|
case TASK_PROCESS_NODE: |
|
//Msg( "BM: Reached node %s\n", STRING( m_iszTarget ) ); |
|
NodeReach(); |
|
TaskComplete(); |
|
break; |
|
|
|
case TASK_PLAY_NODE_PRESEQUENCE: |
|
case TASK_PLAY_NODE_SEQUENCE: |
|
{ |
|
const char *pSequence = NULL; |
|
int iSequence; |
|
|
|
if ( pTask->iTask == TASK_PLAY_NODE_SEQUENCE ) |
|
pSequence = GetNodeSequence(); |
|
else |
|
pSequence = GetNodePresequence(); |
|
|
|
//Msg( "BM: Playing node sequence %s\n", pSequence ); |
|
|
|
if ( pSequence ) //ugh |
|
{ |
|
iSequence = LookupSequence( pSequence ); |
|
|
|
if ( iSequence != -1 ) |
|
{ |
|
SetIdealActivity( ACT_DO_NOT_DISTURB ); |
|
SetSequence( iSequence ); |
|
SetCycle( 0.0f ); |
|
|
|
ResetSequenceInfo(); |
|
//Msg( "BM: Sequence %s %f\n", GetNodeSequence(), gpGlobals->curtime ); |
|
return; |
|
} |
|
} |
|
TaskComplete(); |
|
} |
|
break; |
|
|
|
case TASK_NODE_YAW: |
|
GetMotor()->SetIdealYaw( GetNodeYaw() ); |
|
TaskComplete(); |
|
break; |
|
|
|
case TASK_WAIT_NODE: |
|
m_flWait = gpGlobals->curtime + GetNodeDelay(); |
|
|
|
/*if ( GetTarget() && GetTarget()->GetSpawnFlags() & SF_INFOBM_WAIT ) |
|
Msg( "BM: Wait at node %s forever\n", STRING( m_iszTarget) ); |
|
else |
|
Msg( "BM: Wait at node %s for %.2f\n", STRING( m_iszTarget ), GetNodeDelay() );*/ |
|
break; |
|
|
|
|
|
case TASK_MOVE_TO_NODE_RANGE: |
|
{ |
|
CBaseEntity *pTarget = GetTarget(); |
|
|
|
if ( !pTarget ) |
|
TaskFail( FAIL_NO_TARGET ); |
|
else |
|
{ |
|
if ( ( pTarget->GetAbsOrigin() - GetAbsOrigin() ).Length() < GetNodeRange() ) |
|
TaskComplete(); |
|
else |
|
{ |
|
Activity act = ACT_WALK; |
|
if ( pTarget->GetSpawnFlags() & SF_INFOBM_RUN ) |
|
act = ACT_RUN; |
|
|
|
AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin, act ); |
|
|
|
if ( !GetNavigator()->SetGoal( goal ) ) |
|
{ |
|
TaskFail( NO_TASK_FAILURE ); |
|
} |
|
} |
|
} |
|
} |
|
//Msg( "BM: Moving to node %s\n", STRING( m_iszTarget ) ); |
|
|
|
break; |
|
|
|
case TASK_MELEE_ATTACK1: |
|
{ |
|
|
|
// Play an attack sound here |
|
|
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "BigMomma.Attack" ); |
|
|
|
BaseClass::StartTask( pTask ); |
|
} |
|
|
|
break; |
|
|
|
default: |
|
BaseClass::StartTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
//========================================================= |
|
// RunTask |
|
//========================================================= |
|
void CNPC_BigMomma::RunTask( const Task_t *pTask ) |
|
{ |
|
switch ( pTask->iTask ) |
|
{ |
|
case TASK_CHECK_NODE_PROXIMITY: |
|
{ |
|
float distance; |
|
|
|
if ( GetTarget() == NULL ) |
|
TaskFail( FAIL_NO_TARGET ); |
|
else |
|
{ |
|
if ( GetNavigator()->IsGoalActive() ) |
|
{ |
|
distance = ( GetTarget()->GetAbsOrigin() - GetAbsOrigin() ).Length2D(); |
|
// Set the appropriate activity based on an overlapping range |
|
// overlap the range to prevent oscillation |
|
if ( distance < GetNodeRange() ) |
|
{ |
|
//Msg( "BM: Reached node PROXIMITY!!\n" ); |
|
TaskComplete(); |
|
GetNavigator()->ClearGoal(); // Stop moving |
|
} |
|
} |
|
else |
|
TaskComplete(); |
|
} |
|
} |
|
|
|
break; |
|
|
|
case TASK_WAIT_NODE: |
|
if ( GetTarget() != NULL && (GetTarget()->GetSpawnFlags() & SF_INFOBM_WAIT) ) |
|
return; |
|
|
|
if ( gpGlobals->curtime > m_flWaitFinished ) |
|
TaskComplete(); |
|
//Msg( "BM: The WAIT is over!\n" ); |
|
break; |
|
|
|
case TASK_PLAY_NODE_PRESEQUENCE: |
|
case TASK_PLAY_NODE_SEQUENCE: |
|
|
|
if ( IsSequenceFinished() ) |
|
{ |
|
CBaseEntity *pTarget = NULL; |
|
|
|
if ( GetTarget() ) |
|
pTarget = gEntList.FindEntityByName( NULL, STRING( GetTarget()->m_target ) ); |
|
|
|
if ( pTarget ) |
|
{ |
|
SetActivity( ACT_IDLE ); |
|
TaskComplete(); |
|
} |
|
} |
|
|
|
break; |
|
|
|
default: |
|
BaseClass::RunTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
//========================================================= |
|
// HandleAnimEvent - catches the monster-specific messages |
|
// that occur when tagged animation frames are played. |
|
// |
|
// Returns number of events handled, 0 if none. |
|
//========================================================= |
|
void CNPC_BigMomma::HandleAnimEvent( animevent_t *pEvent ) |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
|
|
Vector vecFwd, vecRight, vecUp; |
|
QAngle angles; |
|
angles = GetAbsAngles(); |
|
AngleVectors( angles, &vecFwd, &vecRight, &vecUp ); |
|
|
|
switch( pEvent->event ) |
|
{ |
|
case BIG_AE_MELEE_ATTACKBR: |
|
case BIG_AE_MELEE_ATTACKBL: |
|
case BIG_AE_MELEE_ATTACK1: |
|
{ |
|
Vector center = GetAbsOrigin() + vecFwd * 128; |
|
Vector mins = center - Vector( 64, 64, 0 ); |
|
Vector maxs = center + Vector( 64, 64, 64 ); |
|
|
|
CBaseEntity *pList[8]; |
|
int count = UTIL_EntitiesInBox( pList, 8, mins, maxs, FL_NPC | FL_CLIENT ); |
|
CBaseEntity *pHurt = NULL; |
|
|
|
for ( int i = 0; i < count && !pHurt; i++ ) |
|
{ |
|
if ( pList[i] != this ) |
|
{ |
|
if ( pList[i]->GetOwnerEntity() != this ) |
|
{ |
|
pHurt = pList[i]; |
|
} |
|
} |
|
} |
|
|
|
if ( pHurt ) |
|
{ |
|
CTakeDamageInfo info( this, this, 15, DMG_CLUB | DMG_SLASH ); |
|
CalculateMeleeDamageForce( &info, (pHurt->GetAbsOrigin() - GetAbsOrigin()), pHurt->GetAbsOrigin() ); |
|
pHurt->TakeDamage( info ); |
|
QAngle newAngles = angles; |
|
newAngles.x = 15; |
|
if ( pHurt->IsPlayer() ) |
|
{ |
|
((CBasePlayer *)pHurt)->SetPunchAngle( newAngles ); |
|
} |
|
switch( pEvent->event ) |
|
{ |
|
case BIG_AE_MELEE_ATTACKBR: |
|
// pHurt->pev->velocity = pHurt->pev->velocity + (vecFwd * 150) + Vector(0,0,250) - (vecRight * 200); |
|
pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + (vecFwd * 150) + Vector(0,0,250) - (vecRight * 200) ); |
|
break; |
|
|
|
case BIG_AE_MELEE_ATTACKBL: |
|
pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + (vecFwd * 150) + Vector(0,0,250) + (vecRight * 200) ); |
|
break; |
|
|
|
case BIG_AE_MELEE_ATTACK1: |
|
pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + (vecFwd * 220) + Vector(0,0,200) ); |
|
break; |
|
} |
|
|
|
pHurt->SetGroundEntity( NULL ); |
|
EmitSound( filter, entindex(), "BigMomma.AttackHit" ); |
|
} |
|
} |
|
break; |
|
|
|
case BIG_AE_SCREAM: |
|
EmitSound( filter, entindex(), "BigMomma.Alert" ); |
|
break; |
|
|
|
case BIG_AE_PAIN_SOUND: |
|
EmitSound( filter, entindex(), "BigMomma.Pain" ); |
|
break; |
|
|
|
case BIG_AE_ATTACK_SOUND: |
|
EmitSound( filter, entindex(), "BigMomma.Attack" ); |
|
break; |
|
|
|
case BIG_AE_BIRTH_SOUND: |
|
EmitSound( filter, entindex(), "BigMomma.Birth" ); |
|
break; |
|
|
|
case BIG_AE_SACK: |
|
if ( RandomInt(0,100) < 30 ) |
|
{ |
|
EmitSound( filter, entindex(), "BigMomma.Sack" ); |
|
} |
|
break; |
|
|
|
case BIG_AE_DEATHSOUND: |
|
EmitSound( filter, entindex(), "BigMomma.Die" ); |
|
break; |
|
|
|
case BIG_AE_STEP1: // Footstep left |
|
case BIG_AE_STEP3: // Footstep back left |
|
EmitSound( filter, entindex(), "BigMomma.FootstepLeft" ); |
|
break; |
|
|
|
case BIG_AE_STEP4: // Footstep back right |
|
case BIG_AE_STEP2: // Footstep right |
|
EmitSound( filter, entindex(), "BigMomma.FootstepRight" ); |
|
break; |
|
|
|
case BIG_AE_MORTAR_ATTACK1: |
|
LaunchMortar(); |
|
break; |
|
|
|
case BIG_AE_LAY_CRAB: |
|
LayHeadcrab(); |
|
break; |
|
|
|
case BIG_AE_JUMP_FORWARD: |
|
SetGroundEntity( NULL ); |
|
SetAbsOrigin(GetAbsOrigin() + Vector ( 0 , 0 , 1) );// take him off ground so engine doesn't instantly reset onground |
|
SetAbsVelocity(vecFwd * 200 + vecUp * 500 ); |
|
break; |
|
|
|
case BIG_AE_EARLY_TARGET: |
|
{ |
|
CInfoBM *pTarget = (CInfoBM*) GetTarget(); |
|
|
|
if ( pTarget ) |
|
{ |
|
pTarget->m_OnAnimationEvent.FireOutput( this, this ); |
|
} |
|
|
|
Remember( bits_MEMORY_FIRED_NODE ); |
|
} |
|
break; |
|
|
|
default: |
|
BaseClass::HandleAnimEvent( pEvent ); |
|
break; |
|
} |
|
} |
|
|
|
|
|
void CNPC_BigMomma::LayHeadcrab( void ) |
|
{ |
|
CBaseEntity *pChild = CBaseEntity::Create( BIG_CHILDCLASS, GetAbsOrigin(), GetAbsAngles(), this ); |
|
|
|
pChild->AddSpawnFlags( SF_NPC_FALL_TO_GROUND ); |
|
|
|
pChild->SetOwnerEntity( this ); |
|
|
|
// Is this the second crab in a pair? |
|
if ( HasMemory( bits_MEMORY_CHILDPAIR ) ) |
|
{ |
|
m_crabTime = gpGlobals->curtime + RandomFloat( 5, 10 ); |
|
Forget( bits_MEMORY_CHILDPAIR ); |
|
} |
|
else |
|
{ |
|
m_crabTime = gpGlobals->curtime + RandomFloat( 0.5, 2.5 ); |
|
Remember( bits_MEMORY_CHILDPAIR ); |
|
} |
|
|
|
trace_t tr; |
|
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,100), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); |
|
UTIL_DecalTrace( &tr, "MommaBlob" ); |
|
|
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "BigMomma.LayHeadcrab" ); |
|
|
|
m_crabCount++; |
|
} |
|
|
|
void CNPC_BigMomma::DeathNotice( CBaseEntity *pevChild ) |
|
{ |
|
if ( m_crabCount > 0 ) // Some babies may cross a transition, but we reset the count then |
|
{ |
|
m_crabCount--; |
|
} |
|
if ( IsAlive() ) |
|
{ |
|
// Make the "my baby's dead" noise! |
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "BigMomma.ChildDie" ); |
|
} |
|
} |
|
|
|
|
|
void CNPC_BigMomma::LaunchMortar( void ) |
|
{ |
|
m_mortarTime = gpGlobals->curtime + RandomFloat( 2, 15 ); |
|
|
|
Vector startPos = GetAbsOrigin(); |
|
startPos.z += 180; |
|
|
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "BigMomma.LaunchMortar" ); |
|
|
|
CBMortar *pBomb = CBMortar::Shoot( this, startPos, m_vTossDir ); |
|
pBomb->SetGravity( 1.0 ); |
|
MortarSpray( startPos, Vector(0,0,10), gSpitSprite, 24 ); |
|
} |
|
|
|
int CNPC_BigMomma::MeleeAttack1Conditions( float flDot, float flDist ) |
|
{ |
|
if (flDot >= 0.7) |
|
{ |
|
if ( flDist > BIG_ATTACKDIST ) |
|
return COND_TOO_FAR_TO_ATTACK; |
|
else |
|
return COND_CAN_MELEE_ATTACK1; |
|
} |
|
else |
|
{ |
|
return COND_NOT_FACING_ATTACK; |
|
} |
|
|
|
return COND_NONE; |
|
} |
|
|
|
|
|
// Lay a crab |
|
int CNPC_BigMomma::MeleeAttack2Conditions( float flDot, float flDist ) |
|
{ |
|
return CanLayCrab(); |
|
} |
|
|
|
|
|
Vector VecCheckSplatToss( CBaseEntity *pEnt, const Vector &vecSpot1, Vector vecSpot2, float maxHeight ) |
|
{ |
|
trace_t tr; |
|
Vector vecMidPoint;// halfway point between Spot1 and Spot2 |
|
Vector vecApex;// highest point |
|
Vector vecScale; |
|
Vector vecGrenadeVel; |
|
Vector vecTemp; |
|
float flGravity = GetCurrentGravity(); |
|
|
|
// calculate the midpoint and apex of the 'triangle' |
|
vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; |
|
UTIL_TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,maxHeight), MASK_SOLID_BRUSHONLY, pEnt, COLLISION_GROUP_NONE, &tr ); |
|
vecApex = tr.endpos; |
|
|
|
UTIL_TraceLine(vecSpot1, vecApex, MASK_SOLID, pEnt, COLLISION_GROUP_NONE, &tr ); |
|
if (tr.fraction != 1.0) |
|
{ |
|
// fail! |
|
return vec3_origin; |
|
} |
|
|
|
// Don't worry about actually hitting the target, this won't hurt us! |
|
|
|
// How high should the grenade travel (subtract 15 so the grenade doesn't hit the ceiling)? |
|
float height = (vecApex.z - vecSpot1.z) - 15; |
|
|
|
//HACK HACK |
|
if ( height < 0 ) |
|
height *= -1; |
|
|
|
// How fast does the grenade need to travel to reach that height given gravity? |
|
float speed = sqrt( 2 * flGravity * height ); |
|
|
|
// How much time does it take to get there? |
|
float time = speed / flGravity; |
|
vecGrenadeVel = (vecSpot2 - vecSpot1); |
|
vecGrenadeVel.z = 0; |
|
|
|
// Travel half the distance to the target in that time (apex is at the midpoint) |
|
vecGrenadeVel = vecGrenadeVel * ( 0.5 / time ); |
|
// Speed to offset gravity at the desired height |
|
vecGrenadeVel.z = speed; |
|
|
|
return vecGrenadeVel; |
|
} |
|
|
|
// Mortar launch |
|
int CNPC_BigMomma::RangeAttack1Conditions( float flDot, float flDist ) |
|
{ |
|
if ( flDist > BIG_MORTARDIST ) |
|
return COND_TOO_FAR_TO_ATTACK; |
|
|
|
if ( flDist <= BIG_MORTARDIST && m_mortarTime < gpGlobals->curtime ) |
|
{ |
|
CBaseEntity *pEnemy = GetEnemy(); |
|
|
|
if ( pEnemy ) |
|
{ |
|
Vector startPos = GetAbsOrigin(); |
|
startPos.z += 180; |
|
|
|
m_vTossDir = VecCheckSplatToss( this, startPos, pEnemy->BodyTarget( GetAbsOrigin() ), random->RandomFloat( 150, 500 ) ); |
|
|
|
if ( m_vTossDir != vec3_origin ) |
|
return COND_CAN_RANGE_ATTACK1; |
|
} |
|
} |
|
|
|
return COND_NONE; |
|
} |
|
|
|
// --------------------------------- |
|
// |
|
// Mortar |
|
// |
|
// --------------------------------- |
|
void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count ) |
|
{ |
|
CPVSFilter filter( position ); |
|
|
|
te->SpriteSpray( filter, 0.0, &position, &direction, spriteModel, 200, 80, count ); |
|
} |
|
|
|
|
|
// UNDONE: right now this is pretty much a copy of the squid spit with minor changes to the way it does damage |
|
void CBMortar:: Spawn( void ) |
|
{ |
|
SetMoveType( MOVETYPE_FLYGRAVITY ); |
|
SetClassname( "bmortar" ); |
|
|
|
SetSolid( SOLID_BBOX ); |
|
|
|
pSprite = CSprite::SpriteCreate( "sprites/mommaspit.vmt", GetAbsOrigin(), true ); |
|
|
|
if ( pSprite ) |
|
{ |
|
pSprite->SetAttachment( this, 0 ); |
|
pSprite->m_flSpriteFramerate = 5; |
|
|
|
pSprite->m_nRenderMode = kRenderTransAlpha; |
|
pSprite->SetBrightness( 255 ); |
|
|
|
m_iFrame = 0; |
|
|
|
pSprite->SetScale( 2.5f ); |
|
} |
|
|
|
UTIL_SetSize( this, Vector( 0, 0, 0), Vector(0, 0, 0) ); |
|
|
|
m_maxFrame = (float)modelinfo->GetModelFrameCount( GetModel() ) - 1; |
|
m_flDmgTime = gpGlobals->curtime + 0.4; |
|
} |
|
|
|
void CBMortar::Animate( void ) |
|
{ |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
|
|
Vector vVelocity = GetAbsVelocity(); |
|
|
|
VectorNormalize( vVelocity ); |
|
|
|
if ( gpGlobals->curtime > m_flDmgTime ) |
|
{ |
|
m_flDmgTime = gpGlobals->curtime + 0.2; |
|
MortarSpray( GetAbsOrigin() + Vector( 0, 0, 15 ), -vVelocity, gSpitSprite, 3 ); |
|
} |
|
if ( m_iFrame++ ) |
|
{ |
|
if ( m_iFrame > m_maxFrame ) |
|
{ |
|
m_iFrame = 0; |
|
} |
|
} |
|
} |
|
|
|
CBMortar *CBMortar::Shoot( CBaseEntity *pOwner, Vector vecStart, Vector vecVelocity ) |
|
{ |
|
CBMortar *pSpit = CREATE_ENTITY( CBMortar, "bmortar" ); |
|
pSpit->Spawn(); |
|
|
|
UTIL_SetOrigin( pSpit, vecStart ); |
|
pSpit->SetAbsVelocity( vecVelocity ); |
|
pSpit->SetOwnerEntity( pOwner ); |
|
pSpit->SetThink ( &CBMortar::Animate ); |
|
pSpit->SetNextThink( gpGlobals->curtime + 0.1 ); |
|
|
|
return pSpit; |
|
} |
|
|
|
void CBMortar::Precache() |
|
{ |
|
BaseClass::Precache(); |
|
|
|
PrecacheScriptSound( "NPC_BigMomma.SpitTouch1" ); |
|
PrecacheScriptSound( "NPC_BigMomma.SpitHit1" ); |
|
PrecacheScriptSound( "NPC_BigMomma.SpitHit2" ); |
|
} |
|
|
|
void CBMortar::Touch( CBaseEntity *pOther ) |
|
{ |
|
trace_t tr; |
|
int iPitch; |
|
|
|
// splat sound |
|
iPitch = random->RandomFloat( 90, 110 ); |
|
|
|
EmitSound( "NPC_BigMomma.SpitTouch1" ); |
|
|
|
switch ( random->RandomInt( 0, 1 ) ) |
|
{ |
|
case 0: |
|
EmitSound( "NPC_BigMomma.SpitHit1" ); |
|
break; |
|
case 1: |
|
EmitSound( "NPC_BigMomma.SpitHit2" ); |
|
break; |
|
} |
|
|
|
if ( pOther->IsBSPModel() ) |
|
{ |
|
// make a splat on the wall |
|
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity() * 10, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); |
|
UTIL_DecalTrace( &tr, "MommaBlob" ); |
|
} |
|
else |
|
{ |
|
tr.endpos = GetAbsOrigin(); |
|
|
|
Vector vVelocity = GetAbsVelocity(); |
|
VectorNormalize( vVelocity ); |
|
|
|
tr.plane.normal = -1 * vVelocity; |
|
} |
|
// make some flecks |
|
MortarSpray( tr.endpos + Vector( 0, 0, 15 ), tr.plane.normal, gSpitSprite, 24 ); |
|
|
|
CBaseEntity *pOwner = GetOwnerEntity(); |
|
|
|
RadiusDamage( CTakeDamageInfo( this, pOwner, sk_bigmomma_dmg_blast.GetFloat(), DMG_ACID ), GetAbsOrigin(), sk_bigmomma_radius_blast.GetFloat(), CLASS_NONE, NULL ); |
|
|
|
UTIL_Remove( pSprite ); |
|
UTIL_Remove( this ); |
|
} |
|
|
|
|
|
AI_BEGIN_CUSTOM_NPC( monster_bigmomma, CNPC_BigMomma ) |
|
|
|
DECLARE_TASK( TASK_MOVE_TO_NODE_RANGE ) |
|
DECLARE_TASK( TASK_FIND_NODE ) |
|
DECLARE_TASK( TASK_PLAY_NODE_PRESEQUENCE ) |
|
DECLARE_TASK( TASK_PLAY_NODE_SEQUENCE ) |
|
DECLARE_TASK( TASK_PROCESS_NODE ) |
|
DECLARE_TASK( TASK_WAIT_NODE ) |
|
DECLARE_TASK( TASK_NODE_DELAY ) |
|
DECLARE_TASK( TASK_NODE_YAW ) |
|
DECLARE_TASK( TASK_CHECK_NODE_PROXIMITY ) |
|
|
|
|
|
//========================================================= |
|
// > SCHED_BIG_NODE |
|
//========================================================= |
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_NODE_FAIL, |
|
|
|
" Tasks" |
|
" TASK_NODE_DELAY 3" |
|
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" |
|
" " |
|
" Interrupts" |
|
) |
|
|
|
//========================================================= |
|
// > SCHED_BIG_NODE |
|
//========================================================= |
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_BIG_NODE, |
|
|
|
" Tasks" |
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_NODE_FAIL" |
|
" TASK_STOP_MOVING 0" |
|
" TASK_FIND_NODE 0" |
|
" TASK_PLAY_NODE_PRESEQUENCE 0" |
|
" TASK_MOVE_TO_NODE_RANGE 0" |
|
" TASK_CHECK_NODE_PROXIMITY 0" |
|
" TASK_STOP_MOVING 0" |
|
" TASK_NODE_YAW 0" |
|
" TASK_FACE_IDEAL 0" |
|
" TASK_WAIT_NODE 0" |
|
" TASK_PLAY_NODE_SEQUENCE 0" |
|
" TASK_PROCESS_NODE 0" |
|
|
|
" " |
|
" Interrupts" |
|
) |
|
|
|
AI_END_CUSTOM_NPC() |
|
|
|
|
|
|