summaryrefslogtreecommitdiff
path: root/game/server/cstrike/hostage
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/cstrike/hostage
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/cstrike/hostage')
-rw-r--r--game/server/cstrike/hostage/cs_simple_hostage.cpp1345
-rw-r--r--game/server/cstrike/hostage/cs_simple_hostage.h256
2 files changed, 1601 insertions, 0 deletions
diff --git a/game/server/cstrike/hostage/cs_simple_hostage.cpp b/game/server/cstrike/hostage/cs_simple_hostage.cpp
new file mode 100644
index 0000000..aacab39
--- /dev/null
+++ b/game/server/cstrike/hostage/cs_simple_hostage.cpp
@@ -0,0 +1,1345 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+// cs_simple_hostage.cpp
+// Simple CS1.6 level hostage
+// Author: Michael S. Booth and Matt Boone, July 2004
+
+#include "cbase.h"
+#include "cs_simple_hostage.h"
+#include "cs_player.h"
+#include "cs_gamerules.h"
+#include "game.h"
+#include "bot.h"
+#include <KeyValues.h>
+#include "obstacle_pushaway.h"
+#include "props_shared.h"
+
+//=============================================================================
+// HPE_BEGIN
+//=============================================================================
+// [dwenger] Necessary for stats tracking
+#include "cs_gamestats.h"
+
+//[tj] Necessary for fast rescue achievement
+#include "cs_achievement_constants.h"
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define HOSTAGE_THINK_INTERVAL 0.1f
+
+#define DrawLine( from, to, duration, red, green, blue ) NDebugOverlay::Line( from, to, red, green, blue, true, 0.1f )
+#define HOSTAGE_PUSHAWAY_THINK_CONTEXT "HostagePushawayThink"
+
+#define HOSTAGE_BBOX_VEC_MIN Vector( -13, -13, 0 )
+#define HOSTAGE_BBOX_VEC_MAX Vector( 13, 13, 72 )
+
+
+ConVar mp_hostagepenalty( "mp_hostagepenalty", "13", FCVAR_NOTIFY, "Terrorist are kicked for killing too much hostages" );
+ConVar hostage_debug( "hostage_debug", "0", FCVAR_CHEAT, "Show hostage AI debug information" );
+
+extern ConVar sv_pushaway_force;
+extern ConVar sv_pushaway_min_player_speed;
+extern ConVar sv_pushaway_max_force;
+
+// We need hostage-specific pushaway cvars because the hostage doesn't have the same friction etc as players
+ConVar sv_pushaway_hostage_force( "sv_pushaway_hostage_force", "20000", FCVAR_REPLICATED | FCVAR_CHEAT, "How hard the hostage is pushed away from physics objects (falls off with inverse square of distance)." );
+ConVar sv_pushaway_max_hostage_force( "sv_pushaway_max_hostage_force", "1000", FCVAR_REPLICATED | FCVAR_CHEAT, "Maximum of how hard the hostage is pushed away from physics objects." );
+
+const int NumHostageModels = 4;
+static const char *HostageModel[NumHostageModels] =
+{
+ "models/Characters/Hostage_01.mdl",
+ "models/Characters/Hostage_02.mdl",
+ "models/Characters/hostage_03.mdl",
+ "models/Characters/hostage_04.mdl",
+};
+
+Vector DropToGround( CBaseEntity *pMainEnt, const Vector &vPos, const Vector &vMins, const Vector &vMaxs );
+
+//-----------------------------------------------------------------------------------------------------
+LINK_ENTITY_TO_CLASS( hostage_entity, CHostage );
+
+
+//-----------------------------------------------------------------------------------------------------
+BEGIN_DATADESC( CHostage )
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "OnRescueZoneTouch", HostageRescueZoneTouch ),
+
+ DEFINE_USEFUNC( HostageUse ),
+ DEFINE_THINKFUNC( HostageThink ),
+
+END_DATADESC()
+
+
+//-----------------------------------------------------------------------------------------------------
+IMPLEMENT_SERVERCLASS_ST( CHostage, DT_CHostage )
+ SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
+ SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
+ SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
+ SendPropExclude( "DT_BaseAnimating", "m_nNewSequenceParity" ),
+ SendPropExclude( "DT_BaseAnimating", "m_nResetEventsParity" ),
+ SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
+
+ // cs_playeranimstate and clientside animation takes care of these on the client
+ SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
+ SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
+
+ SendPropBool( SENDINFO(m_isRescued) ),
+ SendPropInt( SENDINFO(m_iHealth), 10 ),
+ SendPropInt( SENDINFO(m_iMaxHealth), 10 ),
+ SendPropInt( SENDINFO(m_lifeState), 3, SPROP_UNSIGNED ),
+
+ SendPropEHandle( SENDINFO(m_leader) ),
+
+END_SEND_TABLE()
+
+
+//-----------------------------------------------------------------------------------------------------
+CUtlVector< CHostage * > g_Hostages;
+static CountdownTimer announceTimer; // used to stop "hostage rescued" announcements from stepping on each other
+
+
+//-----------------------------------------------------------------------------------------------------
+CHostage::CHostage()
+{
+ g_Hostages.AddToTail( this );
+ m_PlayerAnimState = CreateHostageAnimState( this, this, LEGANIM_8WAY, false );
+ UseClientSideAnimation();
+ SetBloodColor( BLOOD_COLOR_RED );
+}
+
+//-----------------------------------------------------------------------------------------------------
+CHostage::~CHostage()
+{
+ g_Hostages.FindAndRemove( this );
+ m_PlayerAnimState->Release();
+}
+
+CWeaponCSBase* CHostage::CSAnim_GetActiveWeapon()
+{
+ return NULL;
+}
+
+bool CHostage::CSAnim_CanMove()
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------------------------------
+void CHostage::Spawn( void )
+{
+ Precache();
+
+ // round-robin through the hostage models
+ static int index = 0;
+ int whichModel = index % NumHostageModels;
+ ++index;
+
+ SetModel( HostageModel[ whichModel ] );
+
+
+ SetHullType( HULL_HUMAN );
+
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_NOT_STANDABLE );
+ SetMoveType( MOVETYPE_STEP );
+ SetCollisionGroup( COLLISION_GROUP_PLAYER );
+
+ SetGravity( 1.0 );
+
+ m_iHealth = 100;
+ m_iMaxHealth = m_iHealth;
+ m_takedamage = DAMAGE_YES;
+
+ InitBoneControllers( );
+
+ // we must set this, because its zero by default thus putting their eyes in their feet
+ SetViewOffset( Vector( 0, 0, 60 ) );
+
+
+ // set up think callback
+ SetNextThink( gpGlobals->curtime + HOSTAGE_THINK_INTERVAL );
+ SetThink( &CHostage::HostageThink );
+
+ SetContextThink( &CHostage::PushawayThink, gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL, HOSTAGE_PUSHAWAY_THINK_CONTEXT );
+
+ SetUse( &CHostage::HostageUse );
+
+ m_leader = NULL;
+ m_reuseTimer.Invalidate();
+ m_hasBeenUsed = false;
+
+ m_isRescued = false;
+
+ m_vel = Vector( 0, 0, 0 );
+ m_accel = Vector( 0, 0, 0 );
+
+ m_path.Invalidate();
+ m_repathTimer.Invalidate();
+
+ m_pathFollower.Reset();
+ m_pathFollower.SetPath( &m_path );
+ m_pathFollower.SetImprov( this );
+
+ m_lastKnownArea = NULL;
+
+ // Need to make sure the hostages are on the ground when they spawn
+ Vector GroundPos = DropToGround( this, GetAbsOrigin(), HOSTAGE_BBOX_VEC_MIN, HOSTAGE_BBOX_VEC_MAX );
+ SetAbsOrigin( GroundPos );
+
+ if (TheNavMesh)
+ {
+ Vector pos = GetAbsOrigin();
+ m_lastKnownArea = TheNavMesh->GetNearestNavArea( pos );
+ }
+
+ m_isCrouching = false;
+ m_isRunning = true;
+ m_jumpTimer.Invalidate();
+ m_inhibitObstacleAvoidanceTimer.Invalidate();
+
+ m_isWaitingForLeader = false;
+
+ m_isAdjusted = false;
+
+ m_lastLeaderID = 0;
+
+ announceTimer.Invalidate();
+ m_disappearTime = 0.0f;
+}
+
+//-----------------------------------------------------------------------------------------------------
+void CHostage::Precache()
+{
+ for ( int i=0; i<NumHostageModels; ++i )
+ {
+ PrecacheModel( HostageModel[i] );
+ }
+
+ PrecacheScriptSound( "Hostage.StartFollowCT" );
+ PrecacheScriptSound( "Hostage.StopFollowCT" );
+ PrecacheScriptSound( "Hostage.Pain" );
+
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------------------------------
+int CHostage::OnTakeDamage_Alive( const CTakeDamageInfo &info )
+{
+ float actualDamage = info.GetDamage();
+
+ // say something
+ EmitSound( "Hostage.Pain" );
+
+ CCSPlayer *player = ToCSPlayer( info.GetAttacker() );
+
+ if (player)
+ {
+ //=============================================================================
+ // HPE_BEGIN
+ // [dwenger] Track which player injured the hostage
+ //=============================================================================
+
+ player->SetInjuredAHostage(true);
+ CSGameRules()->HostageInjured();
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+ if ( !( player->m_iDisplayHistoryBits & DHF_HOSTAGE_INJURED ) )
+ {
+ player->HintMessage( "#Hint_careful_around_hostages", FALSE );
+ player->m_iDisplayHistoryBits |= DHF_HOSTAGE_INJURED;
+ }
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "hostage_hurt" );
+ if ( event )
+ {
+ event->SetInt( "userid", player->GetUserID() );
+ event->SetInt( "hostage", entindex() );
+ event->SetInt( "priority", 5 );
+
+ gameeventmanager->FireEvent( event );
+ }
+
+ player->AddAccount( -((int)actualDamage * 20) );
+ }
+
+ return BaseClass::OnTakeDamage_Alive( info );
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Modify damage the hostage takes by hitgroup
+ */
+float CHostage::GetModifiedDamage( float flDamage, int nHitGroup )
+{
+ switch ( nHitGroup )
+ {
+ case HITGROUP_GENERIC: flDamage *= 1.75; break;
+ case HITGROUP_HEAD: flDamage *= 2.5; break;
+ case HITGROUP_CHEST: flDamage *= 1.5; break;
+ case HITGROUP_STOMACH: flDamage *= 1.75; break;
+ case HITGROUP_LEFTARM: flDamage *= 0.75; break;
+ case HITGROUP_RIGHTARM: flDamage *= 0.75; break;
+ case HITGROUP_LEFTLEG: flDamage *= 0.6; break;
+ case HITGROUP_RIGHTLEG: flDamage *= 0.6; break;
+ default: flDamage *= 1.5; break;
+ }
+
+ return flDamage;
+}
+
+//-----------------------------------------------------------------------------------------------------
+void CHostage::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+ CTakeDamageInfo scaledInfo = info;
+ scaledInfo.SetDamage( GetModifiedDamage( info.GetDamage(), ptr->hitgroup ) );
+ BaseClass::TraceAttack( scaledInfo, vecDir, ptr, pAccumulator );
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Check for hostage-killer abuse
+ */
+void CHostage::CheckForHostageAbuse( CCSPlayer *player )
+{
+ int hostageKillLimit = mp_hostagepenalty.GetInt();
+
+ if (hostageKillLimit > 0)
+ {
+ player->m_iHostagesKilled++;
+
+ if ( player->m_iHostagesKilled == hostageKillLimit - 1 )
+ {
+ player->HintMessage( "#Hint_removed_for_next_hostage_killed", TRUE );
+ }
+ else if ( player->m_iHostagesKilled >= hostageKillLimit )
+ {
+ Msg( "Kicking client \"%s\" for killing too many hostages\n", player->GetPlayerName() );
+ engine->ServerCommand( UTIL_VarArgs( "kickid %d \"For killing too many hostages\"\n", player->GetUserID() ) );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Hostage was killed
+ */
+void CHostage::Event_Killed( const CTakeDamageInfo &info )
+{
+ // tell the game logic that we've died
+ CSGameRules()->CheckWinConditions();
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Let the game know that a hostage has been killed
+ //=============================================================================
+ CSGameRules()->HostageKilled();
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ CCSPlayer *attacker = ToCSPlayer( info.GetAttacker() );
+
+ if (attacker)
+ {
+ if ( !( attacker->m_iDisplayHistoryBits & DHF_HOSTAGE_KILLED ) )
+ {
+ attacker->HintMessage( "#Hint_lost_money", FALSE );
+ attacker->m_iDisplayHistoryBits |= DHF_HOSTAGE_KILLED;
+ }
+
+ // monetary penalty for killing the hostage
+ attacker->AddAccount( -( 500 + ((int)info.GetDamage() * 20) ) );
+
+ // check for hostage-killer abuse
+ if (attacker->GetTeamNumber() == TEAM_TERRORIST)
+ {
+ CheckForHostageAbuse( attacker );
+ }
+ }
+
+ m_lastLeaderID = 0;
+
+ SetUse( NULL );
+ BaseClass::Event_Killed( info );
+
+ IGameEvent *event = gameeventmanager->CreateEvent("hostage_killed");
+ if ( event )
+ {
+ event->SetInt( "userid", (attacker)?attacker->GetUserID():0 );
+ event->SetInt( "hostage", entindex() );
+ event->SetInt( "priority", 6 );
+ gameeventmanager->FireEvent( event );
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Invoked when a Hostage touches a Rescue Zone
+ */
+void CHostage::HostageRescueZoneTouch( inputdata_t &inputdata )
+{
+ if (!m_isRescued)
+ {
+ m_isRescued = true;
+ m_lastLeaderID = 0;
+
+ SetSolid( SOLID_NONE );
+ SetSolidFlags( 0 );
+
+ // start fading out
+ m_disappearTime = gpGlobals->curtime + 3.0f;
+
+ SetUse( NULL );
+ m_takedamage = DAMAGE_NO;
+
+ // give rescuer a cash bonus
+ CCSPlayer *player = GetLeader();
+ if (player)
+ {
+ const int rescuerCashBonus = 1000;
+ player->AddAccount( rescuerCashBonus );
+ }
+
+ Idle();
+
+ // tell the bots someone has rescued a hostage
+ IGameEvent *event = gameeventmanager->CreateEvent( "hostage_rescued" );
+ if ( event )
+ {
+ event->SetInt( "userid", player ? player->GetUserID() : (-1) );
+ event->SetInt( "hostage", entindex() );
+ event->SetInt( "site", 0 ); // TODO add site index
+ event->SetInt( "priority", 9 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ // update game rules
+ CSGameRules()->m_iHostagesRescued++;
+
+ //=============================================================================
+ // HPE_BEGIN
+ // [dwenger] Hostage rescue achievement processing
+ //=============================================================================
+
+ // Track last rescuer
+ if ( CSGameRules()->m_pLastRescuer == NULL )
+ {
+ // No rescuer yet, so assign one & set rescuer count to 1
+ CSGameRules()->m_pLastRescuer = player;
+ CSGameRules()->m_iNumRescuers = 1;
+ }
+ else
+ {
+ if ( CSGameRules()->m_pLastRescuer != player )
+ {
+ // Rescuer changed
+ CSGameRules()->m_pLastRescuer = player;
+ CSGameRules()->m_iNumRescuers++;
+ }
+ }
+
+ bool roundIsAlreadyOver = (CSGameRules()->m_iRoundWinStatus != WINNER_NONE);
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ if (CSGameRules()->CheckWinConditions() == false)
+ {
+ // this hostage didn't win the round, so announce its rescue to everyone
+ if (announceTimer.IsElapsed())
+ {
+ CSGameRules()->BroadcastSound( "Event.HostageRescued" );
+ }
+
+ // avoid having the announcer talk over himself
+ announceTimer.Start( 2.0f );
+ }
+ //=============================================================================
+ // HPE_BEGIN
+ // [dwenger] Awarding of hostage rescue achievement
+ //=============================================================================
+
+ else
+ {
+ //Check hostage rescue achievements
+ if ( CSGameRules()->m_iNumRescuers == 1 && !CSGameRules()->WasHostageKilled())
+ {
+ //check for unrescued hostages
+ bool allHostagesRescued = true;
+ CHostage* hostage = NULL;
+ int iNumHostages = g_Hostages.Count();
+
+ for ( int i = 0 ; i < iNumHostages; i++ )
+ {
+ hostage = g_Hostages[i];
+
+ if ( hostage->m_iHealth > 0 && !hostage->IsRescued() )
+ {
+ allHostagesRescued = false;
+ break;
+ }
+ }
+
+ if (allHostagesRescued)
+ {
+ player->AwardAchievement(CSRescueAllHostagesInARound);
+
+ //[tj] fast version
+ if (gpGlobals->curtime - CSGameRules()->GetRoundStartTime() < AchievementConsts::FastHostageRescue_Time)
+ {
+ if ( player )
+ {
+ player->AwardAchievement(CSFastHostageRescue);
+ }
+ }
+ }
+ }
+ //=============================================================================
+ // HPE_BEGIN:
+ // [menglish] If this rescue ended the round give an mvp to the rescuer
+ //=============================================================================
+
+ if ( player && !roundIsAlreadyOver)
+ {
+ player->IncrementNumMVPs( CSMVP_HOSTAGERESCUE );
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ }
+
+ if ( player )
+ {
+ CCS_GameStats.Event_HostageRescued( player );
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * In contact with "other"
+ */
+void CHostage::Touch( CBaseEntity *other )
+{
+ BaseClass::Touch( other );
+
+ // allow players and other hostages to push me around
+ if ( ( other->IsPlayer() && other->GetTeamNumber() == TEAM_CT ) || FClassnameIs( other, "hostage_entity" ) )
+ {
+ // only push in 2D
+ Vector to = GetAbsOrigin() - other->GetAbsOrigin();
+ to.z = 0.0f;
+ to.NormalizeInPlace();
+
+ const float pushForce = 500.0f;
+ ApplyForce( pushForce * to );
+ }
+ else if ( m_inhibitDoorTimer.IsElapsed() &&
+ ( other->ClassMatches( "func_door*" ) || other->ClassMatches( "prop_door*" ) ) )
+ {
+ m_inhibitDoorTimer.Start( 3.0f );
+ other->Use( this, this, USE_TOGGLE, 0.0f );
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Hostage is stuck - attempt to wiggle out
+ */
+void CHostage::Wiggle( void )
+{
+ if (m_wiggleTimer.IsElapsed())
+ {
+ m_wiggleDirection = (NavRelativeDirType)RandomInt( 0, 3 );
+ m_wiggleTimer.Start( RandomFloat( 0.3f, 0.5f ) );
+ }
+
+ Vector dir, lat;
+ AngleVectors( GetAbsAngles(), &dir, &lat, NULL );
+
+ const float speed = 500.0f;
+
+ switch( m_wiggleDirection )
+ {
+ case LEFT:
+ ApplyForce( speed * lat );
+ break;
+
+ case RIGHT:
+ ApplyForce( -speed * lat );
+ break;
+
+ case FORWARD:
+ ApplyForce( speed * dir );
+ break;
+
+ case BACKWARD:
+ ApplyForce( -speed * dir );
+ break;
+ }
+
+ const float minStuckJumpTime = 0.25f;
+ if (m_pathFollower.GetStuckDuration() > minStuckJumpTime)
+ {
+ Jump();
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Do following behavior
+ */
+void CHostage::UpdateFollowing( float deltaT )
+{
+ if ( !IsFollowingSomeone() && m_lastLeaderID != 0 )
+ {
+ // emit hostage_stops_following event
+ IGameEvent *event = gameeventmanager->CreateEvent( "hostage_stops_following" );
+ if ( event )
+ {
+ event->SetInt( "userid", m_lastLeaderID );
+ event->SetInt( "hostage", entindex() );
+ event->SetInt( "priority", 6 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ m_lastLeaderID = 0;
+ }
+
+ // if we have a leader, follow him
+ CCSPlayer *leader = GetLeader();
+ if (leader)
+ {
+ // if leader is dead, stop following him
+ if (!leader->IsAlive())
+ {
+ Idle();
+ return;
+ }
+
+ // if leader has moved, repath
+ if (m_path.IsValid())
+ {
+ Vector pathError = leader->GetAbsOrigin() - m_path.GetEndpoint();
+
+ const float repathRange = 100.0f;
+ if (pathError.IsLengthGreaterThan( repathRange ))
+ {
+ m_path.Invalidate();
+ }
+ }
+
+
+ // build a path to our leader
+ if (!m_path.IsValid() && m_repathTimer.IsElapsed())
+ {
+ const float repathInterval = 0.5f;
+ m_repathTimer.Start( repathInterval );
+
+ Vector from = GetAbsOrigin();
+ Vector to = leader->GetAbsOrigin();
+ HostagePathCost pathCost;
+
+ m_path.Compute( from, to, pathCost );
+ m_pathFollower.Reset();
+ }
+
+
+ // if our rescuer is too far away, give up
+ const float giveUpRange = 2000.0f;
+ const float maxPathLength = 4000.0f;
+ Vector toLeader = leader->GetAbsOrigin() - GetAbsOrigin();
+ if (toLeader.IsLengthGreaterThan( giveUpRange ) || (m_path.IsValid() && m_path.GetLength() > maxPathLength))
+ {
+ if ( hostage_debug.GetInt() < 2 )
+ {
+ Idle();
+ }
+ return;
+ }
+
+
+ // don't crowd the leader
+ if (m_isWaitingForLeader)
+ {
+ // we are close to our leader and waiting for him to move
+ const float waitRange = 150.0f;
+ if (toLeader.IsLengthGreaterThan( waitRange ))
+ {
+ // leader has moved away - follow him
+ m_isWaitingForLeader = false;
+ }
+
+ // face the leader
+ //FaceTowards( leader->GetAbsOrigin(), deltaT );
+ }
+ else
+ {
+ // we are far from our leader, and need to check if we're close enough to wait
+ const float nearRange = 125.0f;
+
+ if (toLeader.IsLengthLessThan( nearRange ))
+ {
+ // we are close to the leader - wait for him to move
+ m_isWaitingForLeader = true;
+ }
+ }
+
+ if (!m_isWaitingForLeader)
+ {
+ // move along path towards the leader
+ m_pathFollower.Update( deltaT, m_inhibitObstacleAvoidanceTimer.IsElapsed() );
+
+ if (hostage_debug.GetBool())
+ {
+ m_pathFollower.Debug( true );
+ }
+
+ if (m_pathFollower.IsStuck())
+ {
+ Wiggle();
+ }
+
+ if (hostage_debug.GetBool())
+ {
+ m_path.Draw();
+ }
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------------------------------
+void CHostage::AvoidPhysicsProps( void )
+{
+ if ( m_lifeState == LIFE_DEAD )
+ return;
+
+ CBaseEntity *props[512];
+ int nEnts = GetPushawayEnts( this, props, ARRAYSIZE( props ), 0.0f, PARTITION_ENGINE_SOLID_EDICTS );
+
+ for ( int i=0; i < nEnts; i++ )
+ {
+ // Don't respond to this entity on the client unless it has PHYSICS_MULTIPLAYER_FULL set.
+ IMultiplayerPhysics *pInterface = dynamic_cast<IMultiplayerPhysics*>( props[i] );
+ if ( pInterface && pInterface->GetMultiplayerPhysicsMode() != PHYSICS_MULTIPLAYER_SOLID )
+ continue;
+
+ const float minMass = 10.0f; // minimum mass that can push a player back
+ const float maxMass = 30.0f; // cap at a decently large value
+ float mass = maxMass;
+ if ( pInterface )
+ {
+ mass = pInterface->GetMass();
+ }
+ mass = MIN( mass, maxMass );
+ mass -= minMass;
+ mass = MAX( mass, 0 );
+ mass /= (maxMass-minMass); // bring into a 0..1 range
+
+ // Push away from the collision point. The closer our center is to the collision point,
+ // the harder we push away.
+ Vector vPushAway = (WorldSpaceCenter() - props[i]->WorldSpaceCenter());
+ float flDist = VectorNormalize( vPushAway );
+ flDist = MAX( flDist, 1 );
+
+ float flForce = sv_pushaway_hostage_force.GetFloat() / flDist * mass;
+ flForce = MIN( flForce, sv_pushaway_max_hostage_force.GetFloat() );
+ vPushAway *= flForce;
+
+ ApplyForce( vPushAway );
+ }
+
+ //
+ // Handle step and ledge "step-up" movement here, before m_accel is zero'd
+ //
+ if ( !m_accel.IsZero() )
+ {
+ trace_t trace;
+ Vector start = GetAbsOrigin();
+ Vector forward = m_accel;
+ forward.NormalizeInPlace();
+ UTIL_TraceEntity( this, start, start + forward, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER, &trace );
+ if ( !trace.startsolid && trace.fraction < 1.0f && trace.plane.normal.z < 0.7f )
+ {
+ float groundFraction = trace.fraction;
+ start.z += StepHeight;
+ UTIL_TraceEntity( this, start, start + forward, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER, &trace );
+ if ( !trace.startsolid && trace.fraction > groundFraction )
+ {
+ SetAbsOrigin( start );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Push physics objects away from the hostage
+ */
+void CHostage::PushawayThink( void )
+{
+ PerformObstaclePushaway( this );
+ SetNextThink( gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL, HOSTAGE_PUSHAWAY_THINK_CONTEXT );
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * @TODO imitate player movement:
+ * MoveHelperServer()->SetHost( this );
+ * this->PlayerRunCommand( &cmd, MoveHelperServer() );
+ */
+void CHostage::PhysicsSimulate( void )
+{
+ BaseClass::PhysicsSimulate();
+
+ SetAbsVelocity( m_vel );
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Update Hostage behaviors
+ */
+void CHostage::HostageThink( void )
+{
+ if (!m_isAdjusted)
+ {
+ m_isAdjusted = true;
+
+ // HACK - figure out why the default bbox is 6 units too low
+ SetCollisionBounds( HOSTAGE_BBOX_VEC_MIN, HOSTAGE_BBOX_VEC_MAX );
+ }
+
+ const float deltaT = HOSTAGE_THINK_INTERVAL;
+ SetNextThink( gpGlobals->curtime + deltaT );
+
+ // keep track of which Navigation Area we are in (or were in, if we're "off the mesh" right now)
+ CNavArea *area = TheNavMesh->GetNavArea( GetAbsOrigin() );
+ if (area != NULL && area != m_lastKnownArea)
+ {
+ // entered a new nav area
+ m_lastKnownArea = area;
+ }
+
+ // do leader-following behavior, if necessary
+ UpdateFollowing( deltaT );
+
+ AvoidPhysicsProps();
+
+ // update hostage velocity in the XY plane
+ const float damping = 2.0f;
+ m_vel += deltaT * (m_accel - damping * m_vel);
+
+ // leave Z component untouched
+ m_vel.z = GetAbsVelocity().z;
+
+ if ( m_accel.IsZero() && m_vel.AsVector2D().IsZero( 1.0f ) )
+ {
+ m_vel.x = 0.0f;
+ m_vel.y = 0.0f;
+ }
+
+ m_accel = Vector( 0, 0, 0 );
+
+ // set animation to idle for now
+ StudioFrameAdvance();
+
+ int sequence = SelectWeightedSequence( ACT_IDLE );
+ if (GetSequence() != sequence)
+ {
+ SetSequence( sequence );
+ }
+
+ m_PlayerAnimState->Update( GetAbsAngles()[YAW], GetAbsAngles()[PITCH] );
+
+
+ if ( m_disappearTime && m_disappearTime < gpGlobals->curtime )
+ {
+ // finished fading - remove us completely
+ AddEffects( EF_NODRAW );
+
+ SetSolid( SOLID_NONE );
+ SetSolidFlags( 0 );
+ m_disappearTime = 0.0f;
+ }
+}
+
+//-----------------------------------------------------------------------------------------------------
+bool CHostage::IsFollowingSomeone( void )
+{
+ return (m_leader.m_Value != NULL);
+}
+
+//-----------------------------------------------------------------------------------------------------
+bool CHostage::IsFollowing( const CBaseEntity *entity )
+{
+ return (m_leader.m_Value == entity);
+}
+
+//-----------------------------------------------------------------------------------------------------
+bool CHostage::IsValid( void ) const
+{
+ return (m_iHealth > 0 && !IsRescued());
+}
+
+//-----------------------------------------------------------------------------------------------------
+bool CHostage::IsRescuable( void ) const
+{
+ return (m_iHealth > 0 && !IsRescued());
+}
+
+//-----------------------------------------------------------------------------------------------------
+bool CHostage::IsRescued( void ) const
+{
+ return m_isRescued;
+}
+
+//-----------------------------------------------------------------------------------------------------
+bool CHostage::IsOnGround( void ) const
+{
+ return (GetFlags() & FL_ONGROUND);
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Return true if hostage can see position
+ */
+bool CHostage::IsVisible( const Vector &pos, bool testFOV ) const
+{
+ trace_t result;
+ UTIL_TraceLine( EyePosition(), pos, CONTENTS_SOLID, this, COLLISION_GROUP_NONE, &result );
+ return (result.fraction >= 1.0f);
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Give bonus to CT's for talking to a hostage
+ */
+void CHostage::GiveCTUseBonus( CCSPlayer *rescuer )
+{
+ // money to team
+ const int teamBonus = 100;
+ CSGameRules()->m_iAccountCT += teamBonus;
+
+ // money to rescuer
+ const int rescuerBonus = 150;
+ rescuer->AddAccount( rescuerBonus );
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Stand idle
+ */
+void CHostage::Idle( void )
+{
+ m_leader = NULL;
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Begin following "leader"
+ */
+void CHostage::Follow( CCSPlayer *leader )
+{
+ //=============================================================================
+ // HPE_BEGIN
+ // [dwenger] Set variable to track whether player is currently rescuing hostages
+ //=============================================================================
+
+ if ( leader )
+ {
+ leader->IncrementNumFollowers();
+ leader->SetIsRescuing(true);
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ m_leader = leader;
+ m_isWaitingForLeader = false;
+ m_lastLeaderID = (leader) ? leader->GetUserID() : 0;
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Return our leader, or NULL
+ */
+CCSPlayer *CHostage::GetLeader( void ) const
+{
+ return ToCSPlayer( m_leader.m_Value );
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Invoked when a Hostage is "used" by a player
+ */
+void CHostage::HostageUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ Vector to = pActivator->GetAbsOrigin() - GetAbsOrigin();
+
+ // limit use range
+ float useRange = 1000.0f;
+ if (to.IsLengthGreaterThan( useRange ))
+ {
+ return;
+ }
+
+ // TODO: check line of sight to hostage
+
+
+ CCSPlayer *user = ToCSPlayer( pActivator );
+ if (user == NULL)
+ {
+ return;
+ }
+
+ // only members of the CT team can use hostages (no T's or spectators)
+ if (!hostage_debug.GetBool() && user->GetTeamNumber() != TEAM_CT)
+ {
+ if ( user->GetTeamNumber() == TEAM_TERRORIST )
+ {
+ if ( !(user->m_iDisplayHistoryBits & DHF_HOSTAGE_CTMOVE) )
+ {
+ user->m_iDisplayHistoryBits |= DHF_HOSTAGE_CTMOVE;
+ user->HintMessage( "#Only_CT_Can_Move_Hostages", false, true );
+ }
+ }
+
+ return;
+ }
+
+ CCSPlayer *leader = GetLeader();
+ if( leader && !leader->IsAlive() )
+ {
+ Idle();
+ leader = NULL;
+ }
+
+ // throttle how often leader can change
+ if (!m_reuseTimer.IsElapsed())
+ {
+ return;
+ }
+
+ // give money bonus to first CT touching this hostage
+ if (!m_hasBeenUsed)
+ {
+ m_hasBeenUsed = true;
+
+ GiveCTUseBonus( user );
+
+ CSGameRules()->HostageTouched();
+ }
+
+ // if we are already following the player who used us, stop following
+ if (IsFollowing( user ))
+ {
+ Idle();
+
+ // say something
+ EmitSound( "Hostage.StopFollowCT" );
+ }
+ else
+ {
+ // if we're already following a CT, ignore new uses
+ if (IsFollowingSomeone())
+ {
+ return;
+ }
+
+ // start following
+ Follow( user );
+
+ // say something
+ EmitSound( "Hostage.StartFollowCT" );
+
+ // emit hostage_follows event
+ IGameEvent *event = gameeventmanager->CreateEvent( "hostage_follows" );
+ if ( event )
+ {
+ event->SetInt( "userid", user->GetUserID() );
+ event->SetInt( "hostage", entindex() );
+ event->SetInt( "priority", 6 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ if ( !(user->m_iDisplayHistoryBits & DHF_HOSTAGE_USED) )
+ {
+ user->m_iDisplayHistoryBits |= DHF_HOSTAGE_USED;
+ user->HintMessage( "#Hint_lead_hostage_to_rescue_point", false );
+ }
+ }
+
+ m_reuseTimer.Start( 1.0f );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Rotate body to face towards "target"
+ */
+void CHostage::FaceTowards( const Vector &target, float deltaT )
+{
+ Vector to = target - GetFeet();
+ to.z = 0.0f;
+
+ QAngle desiredAngles;
+ VectorAngles( to, desiredAngles );
+
+ QAngle angles = GetAbsAngles();
+
+ const float turnSpeed = 250.0f;
+ angles.y = ApproachAngle( desiredAngles.y, angles.y, turnSpeed * deltaT );
+
+ SetAbsAngles( angles );
+}
+
+//-----------------------------------------------------------------------------------------------------
+//-----------------------------------------------------------------------------------------------------
+
+const Vector &CHostage::GetCentroid( void ) const
+{
+ static Vector centroid;
+
+ centroid = GetFeet();
+ centroid.z += HalfHumanHeight;
+
+ return centroid;
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Return position of "feet" - point below centroid of improv at feet level
+ */
+const Vector &CHostage::GetFeet( void ) const
+{
+ static Vector feet;
+
+ feet = GetAbsOrigin();
+
+ return feet;
+}
+
+//-----------------------------------------------------------------------------------------------------
+const Vector &CHostage::GetEyes( void ) const
+{
+ static Vector eyes;
+
+ eyes = EyePosition();
+
+ return eyes;
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Return direction of movement
+ */
+float CHostage::GetMoveAngle( void ) const
+{
+ return GetAbsAngles().y;
+}
+
+//-----------------------------------------------------------------------------------------------------
+CNavArea *CHostage::GetLastKnownArea( void ) const
+{
+ return m_lastKnownArea;
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Find "simple" ground height, treating current nav area as part of the floo
+ */
+bool CHostage::GetSimpleGroundHeightWithFloor( const Vector &pos, float *height, Vector *normal )
+{
+ if (TheNavMesh->GetSimpleGroundHeight( pos, height, normal ))
+ {
+ // our current nav area also serves as a ground polygon
+ if (m_lastKnownArea && m_lastKnownArea->IsOverlapping( pos ))
+ {
+ *height = MAX( (*height), m_lastKnownArea->GetZ( pos ) );
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------------------------------
+void CHostage::Crouch( void )
+{
+ m_isCrouching = true;
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * un-crouch
+ */
+void CHostage::StandUp( void )
+{
+ m_isCrouching = false;
+}
+
+//-----------------------------------------------------------------------------------------------------
+bool CHostage::IsCrouching( void ) const
+{
+ return m_isCrouching;
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Initiate a jump
+ */
+void CHostage::Jump( void )
+{
+ if (m_jumpTimer.IsElapsed() && IsOnGround())
+ {
+ const float minJumpInterval = 0.5f;
+ m_jumpTimer.Start( minJumpInterval );
+
+ Vector vel = GetAbsVelocity();
+ vel.z += 200.0f;
+ SetAbsVelocity( vel );
+ }
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+bool CHostage::IsJumping( void ) const
+{
+ return !m_jumpTimer.IsElapsed();
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Set movement speed to running
+ */
+void CHostage::Run( void )
+{
+ m_isRunning = true;
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Set movement speed to walking
+ */
+void CHostage::Walk( void )
+{
+ m_isRunning = false;
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+bool CHostage::IsRunning( void ) const
+{
+ return m_isRunning;
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Invoked when a ladder is encountered while following a path
+ */
+void CHostage::StartLadder( const CNavLadder *ladder, NavTraverseType how, const Vector &approachPos, const Vector &departPos )
+{
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Traverse given ladder
+ */
+bool CHostage::TraverseLadder( const CNavLadder *ladder, NavTraverseType how, const Vector &approachPos, const Vector &departPos, float deltaT )
+{
+ return false;
+}
+
+//-----------------------------------------------------------------------------------------------------
+bool CHostage::IsUsingLadder( void ) const
+{
+ return false;
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Move Hostage directly toward "pathGoal", causing Hostage to track the current path.
+ */
+void CHostage::TrackPath( const Vector &pathGoal, float deltaT )
+{
+ // face in the direction of our motion
+ FaceTowards( GetAbsOrigin() + 10.0f * m_vel, deltaT );
+
+ if (GetFlags() & FL_ONGROUND)
+ {
+ // on the ground - move towards pathGoal
+ Vector to = pathGoal - GetFeet();
+ to.z = 0.0f;
+ to.NormalizeInPlace();
+
+ const float speed = 1000.0f;
+ ApplyForce( speed * to );
+ }
+ else
+ {
+ // in the air - continue forward motion
+ Vector to;
+ QAngle angles = GetAbsAngles();
+ AngleVectors( angles, &to );
+
+ const float airSpeed = 350.0f;
+ ApplyForce( airSpeed * to );
+ }
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Invoked when an improv reaches its MoveTo goal
+ */
+void CHostage::OnMoveToSuccess( const Vector &goal )
+{
+}
+
+//-----------------------------------------------------------------------------------------------------
+/**
+ * Invoked when an improv fails to reach a MoveTo goal
+ */
+void CHostage::OnMoveToFailure( const Vector &goal, MoveToFailureType reason )
+{
+}
+
+
+unsigned int CHostage::PhysicsSolidMaskForEntity() const
+{
+ return MASK_PLAYERSOLID;
+}
+
diff --git a/game/server/cstrike/hostage/cs_simple_hostage.h b/game/server/cstrike/hostage/cs_simple_hostage.h
new file mode 100644
index 0000000..cdc6544
--- /dev/null
+++ b/game/server/cstrike/hostage/cs_simple_hostage.h
@@ -0,0 +1,256 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// cs_simple_hostage.h
+// Simple CS1.6 level hostage
+// Author: Michael S. Booth, July 2004
+
+#ifndef _CS_SIMPLE_HOSTAGE_H_
+#define _CS_SIMPLE_HOSTAGE_H_
+
+#include "nav_mesh.h"
+#include "cs_nav_path.h"
+#include "cs_nav_pathfind.h"
+#include "improv_locomotor.h"
+#include "cs_playeranimstate.h"
+
+class CCSPlayer;
+
+
+//----------------------------------------------------------------------------------------------------------------
+/**
+ * A Counter-Strike Hostage
+ */
+class CHostage : public CBaseCombatCharacter, public CImprovLocomotor, public ICSPlayerAnimStateHelpers
+{
+public:
+ DECLARE_CLASS( CHostage, CBaseCombatCharacter );
+ DECLARE_SERVERCLASS();
+ DECLARE_DATADESC();
+
+ CHostage( void );
+ virtual ~CHostage();
+
+
+public:
+ virtual void Spawn( void );
+ virtual void Precache();
+ int ObjectCaps( void ) { return (BaseClass::ObjectCaps() | FCAP_IMPULSE_USE); } // make hostage "useable"
+
+ virtual void PhysicsSimulate( void );
+
+ virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info );
+ virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
+
+ virtual void Event_Killed( const CTakeDamageInfo &info );
+ virtual void Touch( CBaseEntity *other ); // in contact with "other"
+
+ void HostageRescueZoneTouch( inputdata_t &inputdata ); // invoked when hostage touches a rescue zone
+
+ void HostageThink( void ); // periodic update to initiate behaviors
+
+ void GiveCTUseBonus( CCSPlayer *rescuer ); // give bonus to CT's for talking to a hostage
+ void CheckForHostageAbuse( CCSPlayer *player ); // check for hostage-killer abuse
+
+ // queries
+ bool IsFollowingSomeone( void );
+ bool IsFollowing( const CBaseEntity *entity );
+ bool IsValid( void ) const;
+ bool IsRescuable( void ) const;
+ bool IsRescued( void ) const;
+ bool IsOnGround( void ) const;
+
+ bool IsVisible( const Vector &pos, bool testFOV = false ) const; // return true if hostage can see position
+
+ // hostage states
+ void Idle( void ); // stand idle
+ void Follow( CCSPlayer *leader ); // begin following "leader"
+ CCSPlayer *GetLeader( void ) const; // return our leader, or NULL
+
+ void FaceTowards( const Vector &target, float deltaT ); // rotate body to face towards "target"
+ void ApplyForce( const Vector &force ) { m_accel += force; } // apply a force to the hostage
+
+ unsigned int PhysicsSolidMaskForEntity() const;
+ Class_T Classify ( void ) { return CLASS_PLAYER_ALLY; }
+
+public:
+ // begin CImprovLocomotor -----------------------------------------------------------------------------------------------------------------
+ virtual const Vector &GetCentroid( void ) const;
+ virtual const Vector &GetFeet( void ) const; // return position of "feet" - point below centroid of improv at feet level
+ virtual const Vector &GetEyes( void ) const;
+ virtual float GetMoveAngle( void ) const; // return direction of movement
+
+ virtual CNavArea *GetLastKnownArea( void ) const;
+ virtual bool GetSimpleGroundHeightWithFloor( const Vector &pos, float *height, Vector *normal = NULL ); // find "simple" ground height, treating current nav area as part of the floor
+
+ virtual void Crouch( void );
+ virtual void StandUp( void ); // "un-crouch"
+ virtual bool IsCrouching( void ) const;
+
+ virtual void Jump( void ); // initiate a jump
+ virtual bool IsJumping( void ) const;
+
+ virtual void Run( void ); // set movement speed to running
+ virtual void Walk( void ); // set movement speed to walking
+ virtual bool IsRunning( void ) const;
+
+ virtual void StartLadder( const CNavLadder *ladder, NavTraverseType how, const Vector &approachPos, const Vector &departPos ); // invoked when a ladder is encountered while following a path
+ virtual bool TraverseLadder( const CNavLadder *ladder, NavTraverseType how, const Vector &approachPos, const Vector &departPos, float deltaT ); // traverse given ladder
+ virtual bool IsUsingLadder( void ) const;
+
+ virtual void TrackPath( const Vector &pathGoal, float deltaT ); // move along path by following "pathGoal"
+ virtual void OnMoveToSuccess( const Vector &goal ); // invoked when an improv reaches its MoveTo goal
+ virtual void OnMoveToFailure( const Vector &goal, MoveToFailureType reason ); // invoked when an improv fails to reach a MoveTo goal
+ // end CImprovLocomotor -------------------------------------------------------------------------------------------------------------------
+
+// ICSPlayerAnimState overrides.
+public:
+ virtual CWeaponCSBase* CSAnim_GetActiveWeapon();
+ virtual bool CSAnim_CanMove();
+
+
+
+protected:
+ virtual void HostageUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+
+private:
+ float GetModifiedDamage( float flDamage, int nHitGroup );
+
+ ICSPlayerAnimState *m_PlayerAnimState;
+
+ IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_iMaxHealth );
+ IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_iHealth );
+ IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_lifeState );
+ CNetworkVar( bool, m_isRescued ); // true if the hostage has been rescued
+
+ CNetworkVar( EHANDLE, m_leader ); // the player we are following
+ void UpdateFollowing( float deltaT ); // do following behavior
+
+ int m_lastLeaderID;
+
+ CountdownTimer m_reuseTimer; // to throttle how often hostage can be used
+ bool m_hasBeenUsed; // flag to give first rescuer bonus money
+
+ Vector m_vel;
+ Vector m_accel;
+
+ bool m_isRunning; // true if hostage move speed is to run (walk if false)
+ bool m_isCrouching; // true if hostage is crouching
+ CountdownTimer m_jumpTimer; // if zero, we can jump
+
+ bool m_isWaitingForLeader; // true if we are waiting for our rescuer to move
+
+ CCSNavPath m_path; // current path to follow
+ CountdownTimer m_repathTimer; // throttle pathfinder
+
+ CountdownTimer m_inhibitDoorTimer;
+
+ CNavPathFollower m_pathFollower; // path tracking mechanism
+ CountdownTimer m_inhibitObstacleAvoidanceTimer; // when active, turn off path following feelers
+
+ CNavArea *m_lastKnownArea; // last area we were in
+
+ void Wiggle( void ); // attempt to wiggle-out of begin stuck
+ CountdownTimer m_wiggleTimer; // for wiggling
+ NavRelativeDirType m_wiggleDirection;
+
+ bool m_isAdjusted; // hack for adjusting bounding box
+ float m_disappearTime; // has finished fading, remove me
+
+ void PushawayThink( void ); // pushes physics objects away from the hostage
+ void AvoidPhysicsProps( void ); // guides the hostage away from physics props
+};
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Functor used with NavAreaBuildPath() for building Hostage paths.
+ * Once we hook up crouching and ladders, this can be removed and ShortestPathCost() can be used instead.
+ */
+class HostagePathCost
+{
+public:
+
+ // HPE_TODO[pmf]: check that these new parameters are okay to be ignored
+ float operator() ( CNavArea *area, CNavArea *fromArea, const CNavLadder *ladder, const CFuncElevator *elevator, float length )
+ {
+ if (fromArea == NULL)
+ {
+ // first area in path, no cost
+ return 0.0f;
+ }
+ else
+ {
+ // if this area isn't available to hostages, skip it
+ if ( area->GetAttributes() & NAV_MESH_NO_HOSTAGES )
+ {
+ return -1.0f;
+ }
+
+ // compute distance travelled along path so far
+ float dist;
+
+ if (ladder)
+ {
+ // can't traverse ladders
+ return -1.0f;
+ }
+ else
+ {
+ dist = (area->GetCenter() - fromArea->GetCenter()).Length();
+ }
+
+ float cost = dist + fromArea->GetCostSoFar();
+
+ if (area->GetAttributes() & NAV_MESH_CROUCH) // && !(area->GetAttributes() & NAV_MESH_JUMP))
+ {
+ // can't traverse areas that require crouching
+ return -1.0f;
+ }
+
+ // if this is a "jump" area, add penalty
+ if (area->GetAttributes() & NAV_MESH_JUMP)
+ {
+ const float jumpPenalty = 5.0f;
+ cost += jumpPenalty * dist;
+ }
+
+ return cost;
+ }
+ }
+};
+
+
+//--------------------------------------------------------------------------------------------------------------
+// All the hostage entities.
+extern CUtlVector< CHostage * > g_Hostages;
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Iterate over all active hostages in the game, invoking functor on each.
+ * If functor returns false, stop iteration and return false.
+ */
+template < typename Functor >
+bool ForEachHostage( Functor &func )
+{
+ for( int i=0; i<g_Hostages.Count(); ++i )
+ {
+ CHostage *hostage = g_Hostages[i];
+
+ if ( hostage == NULL || !hostage->IsValid() )
+ continue;
+
+ if ( func( hostage ) == false )
+ return false;
+ }
+
+ return true;
+}
+
+
+#endif // _CS_SIMPLE_HOSTAGE_H_