diff options
Diffstat (limited to 'game/server/tf2/npc_bug_builder.cpp')
| -rw-r--r-- | game/server/tf2/npc_bug_builder.cpp | 577 |
1 files changed, 577 insertions, 0 deletions
diff --git a/game/server/tf2/npc_bug_builder.cpp b/game/server/tf2/npc_bug_builder.cpp new file mode 100644 index 0000000..e2bdfe2 --- /dev/null +++ b/game/server/tf2/npc_bug_builder.cpp @@ -0,0 +1,577 @@ +//========= 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" +); + |