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.
577 lines
15 KiB
577 lines
15 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: The builder bug |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#include "cbase.h" |
|
#include "AI_Task.h" |
|
#include "AI_Default.h" |
|
#include "AI_Schedule.h" |
|
#include "AI_Hull.h" |
|
#include "AI_Hint.h" |
|
#include "AI_Navigator.h" |
|
#include "activitylist.h" |
|
#include "soundent.h" |
|
#include "game.h" |
|
#include "NPCEvent.h" |
|
#include "tf_player.h" |
|
#include "EntityList.h" |
|
#include "ndebugoverlay.h" |
|
#include "shake.h" |
|
#include "monstermaker.h" |
|
#include "decals.h" |
|
#include "vstdlib/random.h" |
|
#include "tf_obj.h" |
|
#include "engine/IEngineSound.h" |
|
#include "IEffects.h" |
|
#include "npc_bug_builder.h" |
|
#include "npc_bug_hole.h" |
|
|
|
ConVar npc_bug_builder_health( "npc_bug_builder_health", "100" ); |
|
|
|
BEGIN_DATADESC( CNPC_Bug_Builder ) |
|
|
|
DEFINE_FIELD( m_flIdleDelay, FIELD_FLOAT ), |
|
|
|
END_DATADESC() |
|
|
|
LINK_ENTITY_TO_CLASS( npc_bug_builder, CNPC_Bug_Builder ); |
|
IMPLEMENT_CUSTOM_AI( npc_bug_builder, CNPC_Bug_Builder ); |
|
|
|
// Dawdling details |
|
// Max & Min distances for dawdle forward movement |
|
#define DAWDLE_MIN_DIST 64 |
|
#define DAWDLE_MAX_DIST 1024 |
|
|
|
//================================================== |
|
// Bug Conditions |
|
//================================================== |
|
enum BugConditions |
|
{ |
|
COND_BBUG_RETURN_TO_BUGHOLE = LAST_SHARED_CONDITION, |
|
}; |
|
|
|
//================================================== |
|
// Bug Schedules |
|
//================================================== |
|
|
|
enum BugSchedules |
|
{ |
|
SCHED_BBUG_FLEE_ENEMY = LAST_SHARED_SCHEDULE, |
|
SCHED_BBUG_RETURN_TO_BUGHOLE, |
|
SCHED_BBUG_RETURN_TO_BUGHOLE_AND_REMOVE, |
|
SCHED_BBUG_DAWDLE, |
|
}; |
|
|
|
//================================================== |
|
// Bug Tasks |
|
//================================================== |
|
|
|
enum BugTasks |
|
{ |
|
TASK_BBUG_GET_PATH_TO_FLEE = LAST_SHARED_TASK, |
|
TASK_BBUG_GET_PATH_TO_BUGHOLE, |
|
TASK_BBUG_HOLE_REMOVE, |
|
TASK_BBUG_GET_PATH_TO_DAWDLE, |
|
TASK_BBUG_FACE_DAWDLE, |
|
}; |
|
|
|
//================================================== |
|
// Bug Activities |
|
//================================================== |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CNPC_Bug_Builder::CNPC_Bug_Builder( void ) |
|
{ |
|
m_flFieldOfView = 0.5f; |
|
m_flIdleDelay = 0.0f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Setup our schedules and tasks, etc. |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bug_Builder::InitCustomSchedules( void ) |
|
{ |
|
INIT_CUSTOM_AI( CNPC_Bug_Builder ); |
|
|
|
// Schedules |
|
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_FLEE_ENEMY ); |
|
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_RETURN_TO_BUGHOLE ); |
|
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_RETURN_TO_BUGHOLE_AND_REMOVE ); |
|
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_DAWDLE ); |
|
|
|
// Conditions |
|
ADD_CUSTOM_CONDITION( CNPC_Bug_Builder, COND_BBUG_RETURN_TO_BUGHOLE ); |
|
|
|
// Tasks |
|
ADD_CUSTOM_TASK( CNPC_Bug_Builder, TASK_BBUG_GET_PATH_TO_FLEE ); |
|
ADD_CUSTOM_TASK( CNPC_Bug_Builder, TASK_BBUG_GET_PATH_TO_BUGHOLE ); |
|
ADD_CUSTOM_TASK( CNPC_Bug_Builder, TASK_BBUG_HOLE_REMOVE ); |
|
ADD_CUSTOM_TASK( CNPC_Bug_Builder, TASK_BBUG_GET_PATH_TO_DAWDLE ); |
|
ADD_CUSTOM_TASK( CNPC_Bug_Builder, TASK_BBUG_FACE_DAWDLE ); |
|
|
|
// Activities |
|
//ADD_CUSTOM_ACTIVITY( CNPC_Bug_Builder, ACT_BUG_WARRIOR_DISTRACT ); |
|
|
|
AI_LOAD_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_FLEE_ENEMY ); |
|
AI_LOAD_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_RETURN_TO_BUGHOLE ); |
|
AI_LOAD_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_RETURN_TO_BUGHOLE_AND_REMOVE ); |
|
AI_LOAD_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_DAWDLE ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bug_Builder::Spawn( void ) |
|
{ |
|
Precache(); |
|
|
|
SetModel( BUG_BUILDER_MODEL ); |
|
|
|
SetHullType(HULL_TINY); |
|
SetHullSizeNormal(); |
|
SetDefaultEyeOffset(); |
|
SetViewOffset( (WorldAlignMins() + WorldAlignMaxs()) * 0.5 ); // See from my center |
|
SetDistLook( 1024.0 ); |
|
m_flNextDawdle = 0; |
|
|
|
SetNavType(NAV_GROUND); |
|
m_NPCState = NPC_STATE_NONE; |
|
SetBloodColor( BLOOD_COLOR_YELLOW ); |
|
m_iHealth = npc_bug_builder_health.GetFloat(); |
|
|
|
SetSolid( SOLID_BBOX ); |
|
AddSolidFlags( FSOLID_NOT_STANDABLE ); |
|
SetMoveType( MOVETYPE_STEP ); |
|
|
|
CapabilitiesAdd( bits_CAP_MOVE_GROUND ); |
|
|
|
NPCInit(); |
|
|
|
BaseClass::Spawn(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bug_Builder::Precache( void ) |
|
{ |
|
PrecacheModel( BUG_BUILDER_MODEL ); |
|
|
|
PrecacheScriptSound( "NPC_Bug_Builder.Idle" ); |
|
PrecacheScriptSound( "NPC_Bug_Builder.Pain" ); |
|
|
|
BaseClass::Precache(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CNPC_Bug_Builder::SelectSchedule( void ) |
|
{ |
|
// If I'm not in idle anymore, don't idle |
|
if ( m_NPCState != NPC_STATE_IDLE ) |
|
{ |
|
m_flNextDawdle = 0; |
|
} |
|
|
|
switch ( m_NPCState ) |
|
{ |
|
case NPC_STATE_IDLE: |
|
{ |
|
// BugHole might be requesting help |
|
if ( HasCondition( COND_BBUG_RETURN_TO_BUGHOLE ) ) |
|
return SCHED_BBUG_RETURN_TO_BUGHOLE; |
|
|
|
// Setup to dawdle a bit from now |
|
if ( !m_flNextDawdle ) |
|
{ |
|
m_flNextDawdle = gpGlobals->curtime + random->RandomFloat( 3.0, 5.0 ); |
|
} |
|
else if ( m_flNextDawdle < gpGlobals->curtime ) |
|
{ |
|
m_flNextDawdle = 0; |
|
return SCHED_BBUG_DAWDLE; |
|
} |
|
|
|
// When I take damage, I flee |
|
if ( HasCondition( COND_LIGHT_DAMAGE | COND_HEAVY_DAMAGE ) ) |
|
return SCHED_BBUG_FLEE_ENEMY; |
|
|
|
// Return to my bughole |
|
//return SCHED_BBUG_RETURN_TO_BUGHOLE_AND_REMOVE; |
|
break; |
|
} |
|
case NPC_STATE_ALERT: |
|
{ |
|
// BugHole might be requesting help |
|
if ( HasCondition( COND_BBUG_RETURN_TO_BUGHOLE ) ) |
|
return SCHED_BBUG_RETURN_TO_BUGHOLE; |
|
|
|
// When I take damage, I flee |
|
if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) ) |
|
return SCHED_BBUG_FLEE_ENEMY; |
|
|
|
break; |
|
} |
|
case NPC_STATE_COMBAT: |
|
{ |
|
// Did I lose my enemy? |
|
if ( HasCondition ( COND_LOST_ENEMY ) || HasCondition ( COND_ENEMY_UNREACHABLE ) ) |
|
{ |
|
SetEnemy( NULL ); |
|
SetState(NPC_STATE_IDLE); |
|
return BaseClass::SelectSchedule(); |
|
} |
|
|
|
// When I take damage, I flee |
|
if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) ) |
|
return SCHED_BBUG_FLEE_ENEMY; |
|
} |
|
break; |
|
} |
|
|
|
return BaseClass::SelectSchedule(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pTask - |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bug_Builder::StartTask( const Task_t *pTask ) |
|
{ |
|
switch ( pTask->iTask ) |
|
{ |
|
case TASK_BBUG_GET_PATH_TO_FLEE: |
|
{ |
|
// Always tell our bughole that we're under attack |
|
if ( m_hMyBugHole ) |
|
{ |
|
m_hMyBugHole->IncomingFleeingBug( this ); |
|
} |
|
|
|
// If we have no squad, or we couldn't get a path to our squadmate, move to our bughole |
|
if ( m_hMyBugHole ) |
|
{ |
|
SetTarget( m_hMyBugHole ); |
|
AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin, ACT_RUN ); |
|
if ( GetNavigator()->SetGoal( goal ) ) |
|
{ |
|
TaskComplete(); |
|
return; |
|
} |
|
} |
|
|
|
TaskComplete(); |
|
} |
|
break; |
|
|
|
case TASK_BBUG_GET_PATH_TO_BUGHOLE: |
|
{ |
|
// Get a path back to my bughole |
|
// If we have no squad, or we couldn't get a path to our squadmate, look for a bughole |
|
if ( m_hMyBugHole ) |
|
{ |
|
SetTarget( m_hMyBugHole ); |
|
AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin, ACT_RUN ); |
|
if ( GetNavigator()->SetGoal( goal ) ) |
|
{ |
|
TaskComplete(); |
|
return; |
|
} |
|
} |
|
|
|
TaskFail( "Couldn't get to bughole." ); |
|
} |
|
break; |
|
|
|
case TASK_BBUG_HOLE_REMOVE: |
|
{ |
|
TaskComplete(); |
|
|
|
// Crawl inside the bughole and remove myself |
|
AddEffects( EF_NODRAW ); |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
Event_Killed( CTakeDamageInfo( this, this, 200, DMG_CRUSH ) ); |
|
|
|
// Tell the bughole |
|
if ( m_hMyBugHole ) |
|
{ |
|
m_hMyBugHole->BugReturned(); |
|
} |
|
} |
|
break; |
|
|
|
case TASK_BBUG_GET_PATH_TO_DAWDLE: |
|
{ |
|
// Get a dawdle point ahead of us |
|
Vector vecForward, vecTarget; |
|
AngleVectors( GetAbsAngles(), &vecForward ); |
|
VectorMA( GetAbsOrigin(), random->RandomFloat( DAWDLE_MIN_DIST, DAWDLE_MAX_DIST ), vecForward, vecTarget ); |
|
|
|
// See how far we could move ahead |
|
trace_t tr; |
|
UTIL_TraceEntity( this, GetAbsOrigin(), vecTarget, MASK_SOLID, &tr); |
|
float flDistance = tr.fraction * (vecTarget - GetAbsOrigin()).Length(); |
|
if ( flDistance >= DAWDLE_MIN_DIST ) |
|
{ |
|
AI_NavGoal_t goal( tr.endpos ); |
|
GetNavigator()->SetGoal( goal ); |
|
} |
|
|
|
TaskComplete(); |
|
} |
|
break; |
|
|
|
case TASK_BBUG_FACE_DAWDLE: |
|
{ |
|
// Turn a random amount to the right |
|
float flYaw = GetMotor()->GetIdealYaw(); |
|
flYaw = flYaw + random->RandomFloat( 45, 135 ); |
|
GetMotor()->SetIdealYaw( UTIL_AngleMod(flYaw) ); |
|
SetTurnActivity(); |
|
break; |
|
} |
|
break; |
|
|
|
default: |
|
BaseClass::StartTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pTask - |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bug_Builder::RunTask( const Task_t *pTask ) |
|
{ |
|
switch ( pTask->iTask ) |
|
{ |
|
case TASK_BBUG_FACE_DAWDLE: |
|
{ |
|
GetMotor()->UpdateYaw(); |
|
if ( FacingIdeal() ) |
|
{ |
|
TaskComplete(); |
|
} |
|
break; |
|
} |
|
|
|
default: |
|
BaseClass::RunTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CNPC_Bug_Builder::FValidateHintType(CAI_Hint *pHint) |
|
{ |
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pVictim - |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bug_Builder::Event_Killed( const CTakeDamageInfo &info ) |
|
{ |
|
BaseClass::Event_Killed( info ); |
|
|
|
// Remove myself in a minute |
|
if ( !ShouldFadeOnDeath() ) |
|
{ |
|
SetThink( SUB_Remove ); |
|
SetNextThink( gpGlobals->curtime + 20 ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pEvent - |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bug_Builder::HandleAnimEvent( animevent_t *pEvent ) |
|
{ |
|
BaseClass::HandleAnimEvent( pEvent ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
float CNPC_Bug_Builder::MaxYawSpeed( void ) |
|
{ |
|
return 2.0f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bug_Builder::IdleSound( void ) |
|
{ |
|
EmitSound( "NPC_Bug_Builder.Idle" ); |
|
m_flIdleDelay = gpGlobals->curtime + 4.0f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bug_Builder::PainSound( const CTakeDamageInfo &info ) |
|
{ |
|
EmitSound( "NPC_Bug_Builder.Pain" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CNPC_Bug_Builder::ShouldPlayIdleSound( void ) |
|
{ |
|
//Only do idles in the right states |
|
if ( ( m_NPCState != NPC_STATE_IDLE && m_NPCState != NPC_STATE_ALERT ) ) |
|
return false; |
|
|
|
//Gagged monsters don't talk |
|
if ( HasSpawnFlags( SF_NPC_GAG ) ) |
|
return false; |
|
|
|
//Don't cut off another sound or play again too soon |
|
if ( m_flIdleDelay > gpGlobals->curtime ) |
|
return false; |
|
|
|
//Randomize it a bit |
|
if ( random->RandomInt( 0, 20 ) ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bug_Builder::SetBugHole( CMaker_BugHole *pBugHole ) |
|
{ |
|
m_hMyBugHole = pBugHole; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: BugHole is calling me home to defend it |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bug_Builder::ReturnToBugHole( void ) |
|
{ |
|
SetCondition( COND_BBUG_RETURN_TO_BUGHOLE ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bug_Builder::AlertSound( void ) |
|
{ |
|
if ( GetEnemy() ) |
|
{ |
|
//FIXME: We need a better solution for inner-squad alerts!! |
|
//SOUND_DANGER is designed to frighten NPC's away. Need a different SOUND_ type. |
|
CSoundEnt::InsertSound( SOUND_DANGER, GetEnemy()->GetAbsOrigin(), 1024, 0.5f, this ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Overridden for team handling |
|
//----------------------------------------------------------------------------- |
|
Disposition_t CNPC_Bug_Builder::IRelationType( CBaseEntity *pTarget ) |
|
{ |
|
// Builders ignore everything |
|
return D_NU; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Schedules |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
//========================================================= |
|
// Dawdle around |
|
//========================================================= |
|
AI_DEFINE_SCHEDULE |
|
( |
|
SCHED_BBUG_DAWDLE, |
|
|
|
" Tasks" |
|
" TASK_SET_TOLERANCE_DISTANCE 32" |
|
" TASK_BBUG_GET_PATH_TO_DAWDLE 0" |
|
" TASK_RUN_PATH 0" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
" TASK_BBUG_FACE_DAWDLE 0" |
|
" " |
|
" Interrupts" |
|
" COND_LIGHT_DAMAGE" |
|
" COND_HEAVY_DAMAGE" |
|
); |
|
|
|
//========================================================= |
|
// Flee from our enemy |
|
//========================================================= |
|
AI_DEFINE_SCHEDULE |
|
( |
|
SCHED_BBUG_FLEE_ENEMY, |
|
|
|
" Tasks" |
|
" TASK_SET_TOLERANCE_DISTANCE 128" |
|
" TASK_BBUG_GET_PATH_TO_FLEE 0" |
|
" TASK_RUN_PATH 0" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
" TASK_TURN_RIGHT 180" |
|
" " |
|
" Interrupts" |
|
" COND_ENEMY_DEAD" |
|
" COND_LOST_ENEMY" |
|
); |
|
|
|
//========================================================= |
|
// Retreat to a bughole |
|
//========================================================= |
|
AI_DEFINE_SCHEDULE |
|
( |
|
SCHED_BBUG_RETURN_TO_BUGHOLE, |
|
|
|
" Tasks" |
|
" TASK_SET_TOLERANCE_DISTANCE 128" |
|
" TASK_BBUG_GET_PATH_TO_BUGHOLE 0" |
|
" TASK_RUN_PATH 0" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
" " |
|
" Interrupts" |
|
" COND_NEW_ENEMY" |
|
" COND_HEAR_COMBAT" |
|
" COND_HEAR_DANGER" |
|
); |
|
|
|
//========================================================= |
|
// Return to a bughole and remove myself |
|
//========================================================= |
|
AI_DEFINE_SCHEDULE |
|
( |
|
SCHED_BBUG_RETURN_TO_BUGHOLE_AND_REMOVE, |
|
|
|
" Tasks" |
|
" TASK_WAIT 5" // Wait for 5-10 seconds to see if anything happens |
|
" TASK_WAIT_RANDOM 5" |
|
" TASK_SET_TOLERANCE_DISTANCE 128" |
|
" TASK_BBUG_GET_PATH_TO_BUGHOLE 0" |
|
" TASK_RUN_PATH 0" |
|
" TASK_WAIT_FOR_MOVEMENT 0" |
|
" TASK_BBUG_HOLE_REMOVE 0" |
|
" " |
|
" Interrupts" |
|
" COND_NEW_ENEMY" |
|
" COND_HEAR_COMBAT" |
|
" COND_HEAR_DANGER" |
|
); |
|
|
|
|