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.
1346 lines
36 KiB
1346 lines
36 KiB
4 years ago
|
//========= 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;
|
||
|
}
|
||
|
|