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