Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

331 lines
9.1 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
// ghost.cpp
// A spooky halloween ghost bot
// Michael Booth, October 2011
#include "cbase.h"
#include "tf_player.h"
#include "tf_gamerules.h"
#include "tf_team.h"
#include "tf_projectile_arrow.h"
#include "tf_weapon_grenade_pipebomb.h"
#include "nav_mesh/tf_nav_area.h"
#include "ghost.h"
#include "NextBot/Path/NextBotChasePath.h"
#include "econ_wearable.h"
#include "team_control_point_master.h"
#include "particle_parse.h"
#include "CRagdollMagnet.h"
#include "NextBot/Behavior/BehaviorMoveTo.h"
void CC_GhostSpawn( const CCommand& args )
{
MDLCACHE_CRITICAL_SECTION();
CBaseEntity *entity = CreateEntityByName( "ghost" );
if ( entity )
{
entity->Precache();
DispatchSpawn( entity );
// Now attempt to drop into the world
CBasePlayer* pPlayer = UTIL_GetCommandClient();
trace_t tr;
Vector forward;
pPlayer->EyeVectors( &forward );
UTIL_TraceLine(pPlayer->EyePosition(),
pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_SOLID,
pPlayer, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction != 1.0 )
{
// Raise the end position a little up off the floor, place the npc and drop him down
tr.endpos.z += 12;
entity->Teleport( &tr.endpos, NULL, NULL );
}
}
}
static ConCommand ghost_spawn( "ghost_spawn", CC_GhostSpawn, "Spawns a Ghost where the player is looking.", FCVAR_GAMEDLL | FCVAR_CHEAT );
//-----------------------------------------------------------------------------------------------------
CGhost *SpawnGhost( const Vector &spot, const QAngle &angles, float lifetime )
{
CGhost *ghost = (CGhost *)CreateEntityByName( "ghost" );
if ( ghost )
{
DispatchSpawn( ghost );
ghost->SetAbsOrigin( spot );
ghost->SetLocalAngles( angles );
ghost->SetLifetime( lifetime );
return ghost;
}
return NULL;
}
//-----------------------------------------------------------------------------------------------------
// The Ghost
//-----------------------------------------------------------------------------------------------------
LINK_ENTITY_TO_CLASS( ghost, CGhost );
//-----------------------------------------------------------------------------------------------------
CGhost::CGhost()
{
ALLOCATE_INTENTION_INTERFACE( CGhost );
m_locomotor = new CGhostLocomotion( this );
m_eyeOffset = vec3_origin;
m_lifetime = 10.0f;
}
//-----------------------------------------------------------------------------------------------------
CGhost::~CGhost()
{
DEALLOCATE_INTENTION_INTERFACE;
if ( m_locomotor )
delete m_locomotor;
}
//-----------------------------------------------------------------------------------------------------
void CGhost::PrecacheGhost()
{
PrecacheModel( "models/props_halloween/ghost_no_hat.mdl" );
PrecacheParticleSystem( "ghost_appearation" );
PrecacheScriptSound( "Halloween.GhostMoan" );
PrecacheScriptSound( "Halloween.GhostBoo" );
PrecacheScriptSound( "Halloween.Haunted" );
}
//-----------------------------------------------------------------------------------------------------
void CGhost::Precache()
{
BaseClass::Precache();
// always allow late precaching, so we don't pay the cost of the
// Halloween Ghost for the entire year
bool bAllowPrecache = CBaseEntity::IsPrecacheAllowed();
CBaseEntity::SetAllowPrecache( true );
PrecacheGhost();
CBaseEntity::SetAllowPrecache( bAllowPrecache );
}
//-----------------------------------------------------------------------------------------------------
void CGhost::Spawn( void )
{
Precache();
BaseClass::Spawn();
SetCollisionGroup( COLLISION_GROUP_NONE );
SetSolid( SOLID_NONE );
AddSolidFlags( FSOLID_NOT_SOLID );
SetModel( "models/props_halloween/ghost_no_hat.mdl" );
}
//---------------------------------------------------------------------------------------------
bool CGhost::ShouldCollide( int collisionGroup, int contentsMask ) const
{
return false;
//return BaseClass::ShouldCollide( collisionGroup, contentsMask );
}
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
class CGhostBehavior : public Action< CGhost >
{
public:
//---------------------------------------------------------------------------------------------
virtual ActionResult< CGhost > OnStart( CGhost *me, Action< CGhost > *priorAction )
{
m_lifeTimer.Start();
m_stuckAnchor = me->GetAbsOrigin();
m_stuckTimer.Start( 1.0f );
me->GetVectors( &m_forward, NULL, NULL );
DispatchParticleEffect( "ghost_appearation", me->WorldSpaceCenter(), me->GetAbsAngles() );
return Continue();
}
//---------------------------------------------------------------------------------------------
virtual ActionResult< CGhost > Update( CGhost *me, float interval )
{
if ( m_lifeTimer.IsGreaterThen( me->GetLifetime() ) || m_stuckTimer.IsElapsed() )
{
DispatchParticleEffect( "ghost_appearation", me->WorldSpaceCenter(), me->GetAbsAngles() );
me->EmitSound( "Halloween.Haunted" );
UTIL_Remove( me );
return Done();
}
if ( m_moanTimer.IsElapsed() )
{
me->EmitSound( "Halloween.GhostMoan" );
m_moanTimer.Start( RandomFloat( 5.0f, 7.0f ) );
}
DriftAroundAndAvoidObstacles( me );
ScareNearbyPlayers( me );
return Continue();
}
//---------------------------------------------------------------------------------------------
void DriftAroundAndAvoidObstacles( CGhost *me )
{
const float feelerRange = 150.0f;
Vector left( -m_forward.y, m_forward.x, 0.0f );
Vector right( m_forward.y, -m_forward.x, 0.0f );
CTraceFilterNoNPCsOrPlayer traceFilter( me, COLLISION_GROUP_NONE );
trace_t resultLeft;
UTIL_TraceLine( me->WorldSpaceCenter(), me->WorldSpaceCenter() + feelerRange * ( m_forward + left ), MASK_PLAYERSOLID, &traceFilter, &resultLeft );
//NDebugOverlay::Line( me->WorldSpaceCenter(), me->WorldSpaceCenter() + feelerRange * ( m_forward + left ), 0, 0, 255, true, interval );
trace_t resultRight;
UTIL_TraceLine( me->WorldSpaceCenter(), me->WorldSpaceCenter() + feelerRange * ( m_forward + right ), MASK_PLAYERSOLID, &traceFilter, &resultRight );
//NDebugOverlay::Line( me->WorldSpaceCenter(), me->WorldSpaceCenter() + feelerRange * ( m_forward + right ), 255, 0, 0, true, interval );
const float turnRate = 0.2f;
if ( resultLeft.DidHit() )
{
if ( resultRight.DidHit() )
{
// both sides hit
if ( resultLeft.fraction < resultRight.fraction )
{
// left hit closer - turn right
m_forward += turnRate * right;
}
else
{
// right hit closer - turn left
m_forward += turnRate * left;
}
}
else
{
// left hit - turn right
m_forward += turnRate * right;
}
}
else if ( resultRight.DidHit() )
{
// right hit - turn left
m_forward += turnRate * left;
}
m_forward.NormalizeInPlace();
Vector goal = 100.0f * m_forward + me->GetAbsOrigin();
me->GetLocomotionInterface()->Approach( goal );
me->GetLocomotionInterface()->FaceTowards( goal );
me->GetLocomotionInterface()->Run();
if ( me->IsRangeGreaterThan( m_stuckAnchor, 50.0f ) )
{
m_stuckAnchor = me->GetAbsOrigin();
m_stuckTimer.Reset();
}
}
//---------------------------------------------------------------------------------------------
void ScareNearbyPlayers( CGhost *me )
{
if ( m_scareTimer.IsElapsed() )
{
m_scareTimer.Start( 1.0f );
CUtlVector< CTFPlayer * > playerVector;
CollectPlayers( &playerVector, TF_TEAM_RED, COLLECT_ONLY_LIVING_PLAYERS );
CollectPlayers( &playerVector, TF_TEAM_BLUE, COLLECT_ONLY_LIVING_PLAYERS, APPEND_PLAYERS );
for( int i=0; i<playerVector.Count(); ++i )
{
CTFPlayer *victim = playerVector[i];
if ( victim && !victim->HasPurgatoryBuff() )
{
if ( me->IsRangeLessThan( victim, GHOST_SCARE_RADIUS ) )
{
if ( me->IsLineOfSightClear( victim ) )
{
// scare them!
const float scareTime = 2.0f;
const float speedReduction = 0.0f;
// "stun by trigger" results in the Halloween "yikes" effects
int stunFlags = TF_STUN_LOSER_STATE | TF_STUN_BY_TRIGGER;
victim->m_Shared.StunPlayer( scareTime, speedReduction, stunFlags, NULL );
}
}
}
}
}
}
virtual const char *GetName( void ) const { return "Behavior"; } // return name of this action
private:
IntervalTimer m_lifeTimer;
CountdownTimer m_moanTimer;
CountdownTimer m_scareTimer;
Vector m_forward;
Vector m_stuckAnchor;
CountdownTimer m_stuckTimer;
};
IMPLEMENT_INTENTION_INTERFACE( CGhost, CGhostBehavior );
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
// Get maximum running speed
float CGhostLocomotion::GetRunSpeed( void ) const
{
return 90.0f;
}
//---------------------------------------------------------------------------------------------
// Return maximum acceleration of locomotor
float CGhostLocomotion::GetMaxAcceleration( void ) const
{
return 500.0f;
}
//---------------------------------------------------------------------------------------------
// Return maximum deceleration of locomotor
float CGhostLocomotion::GetMaxDeceleration( void ) const
{
return 500.0f;
}