First version of the SOurce SDK 2013

This commit is contained in:
Joe Ludwig
2013-06-26 15:22:04 -07:00
commit 39ed87570b
7469 changed files with 3312656 additions and 0 deletions

View File

@@ -0,0 +1,516 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "cbase.h"
#include "AI_Criteria.h"
#include "ai_speech.h"
#include <KeyValues.h>
#include "engine/IEngineSound.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
AI_CriteriaSet::AI_CriteriaSet() : m_Lookup( 0, 0, CritEntry_t::LessFunc )
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : src -
//-----------------------------------------------------------------------------
AI_CriteriaSet::AI_CriteriaSet( const AI_CriteriaSet& src ) : m_Lookup( 0, 0, CritEntry_t::LessFunc )
{
m_Lookup.Purge();
for ( short i = src.m_Lookup.FirstInorder();
i != src.m_Lookup.InvalidIndex();
i = src.m_Lookup.NextInorder( i ) )
{
m_Lookup.Insert( src.m_Lookup[ i ] );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
AI_CriteriaSet::~AI_CriteriaSet()
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *criteria -
// "" -
// 1.0f -
//-----------------------------------------------------------------------------
void AI_CriteriaSet::AppendCriteria( const char *criteria, const char *value /*= ""*/, float weight /*= 1.0f*/ )
{
// Note: value pointer may come from an entry inside m_Lookup!
// that value string must be copied out before any modification
// to the m_Lookup struct which could make the pointer invalid
int idx = FindCriterionIndex( criteria );
if ( idx == -1 )
{
CritEntry_t entry;
entry.criterianame = criteria;
MEM_ALLOC_CREDIT();
entry.SetValue(value);
entry.weight = weight;
m_Lookup.Insert( entry );
}
else
{
CritEntry_t *entry = &m_Lookup[ idx ];
entry->SetValue( value );
entry->weight = weight;
}
}
//-----------------------------------------------------------------------------
// Removes criteria in a set
//-----------------------------------------------------------------------------
void AI_CriteriaSet::RemoveCriteria( const char *criteria )
{
int idx = FindCriterionIndex( criteria );
if ( idx == -1 )
return;
m_Lookup.RemoveAt( idx );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int AI_CriteriaSet::GetCount() const
{
return m_Lookup.Count();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *name -
// Output : int
//-----------------------------------------------------------------------------
int AI_CriteriaSet::FindCriterionIndex( const char *name ) const
{
CritEntry_t search;
search.criterianame = name;
int idx = m_Lookup.Find( search );
if ( idx == m_Lookup.InvalidIndex() )
return -1;
return idx;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : index -
// Output : char const
//-----------------------------------------------------------------------------
const char *AI_CriteriaSet::GetName( int index ) const
{
static char namebuf[ 128 ];
if ( index < 0 || index >= (int)m_Lookup.Count() )
return "";
const CritEntry_t *entry = &m_Lookup[ index ];
Q_strncpy( namebuf, entry->criterianame.String(), sizeof( namebuf ) );
return namebuf;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : index -
// Output : char const
//-----------------------------------------------------------------------------
const char *AI_CriteriaSet::GetValue( int index ) const
{
if ( index < 0 || index >= (int)m_Lookup.Count() )
return "";
const CritEntry_t *entry = &m_Lookup[ index ];
return entry->value ? entry->value : "";
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : index -
// Output : float
//-----------------------------------------------------------------------------
float AI_CriteriaSet::GetWeight( int index ) const
{
if ( index < 0 || index >= (int)m_Lookup.Count() )
return 1.0f;
const CritEntry_t *entry = &m_Lookup[ index ];
return entry->weight;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void AI_CriteriaSet::Describe()
{
for ( short i = m_Lookup.FirstInorder(); i != m_Lookup.InvalidIndex(); i = m_Lookup.NextInorder( i ) )
{
CritEntry_t *entry = &m_Lookup[ i ];
if ( entry->weight != 1.0f )
{
DevMsg( " %20s = '%s' (weight %f)\n", entry->criterianame.String(), entry->value ? entry->value : "", entry->weight );
}
else
{
DevMsg( " %20s = '%s'\n", entry->criterianame.String(), entry->value ? entry->value : "" );
}
}
}
BEGIN_SIMPLE_DATADESC( AI_ResponseParams )
DEFINE_FIELD( flags, FIELD_SHORT ),
DEFINE_FIELD( odds, FIELD_SHORT ),
DEFINE_FIELD( soundlevel, FIELD_CHARACTER ),
DEFINE_FIELD( delay, FIELD_INTEGER ), // These are compressed down to two float16s, so treat as an INT for saverestore
DEFINE_FIELD( respeakdelay, FIELD_INTEGER ), //
END_DATADESC()
BEGIN_SIMPLE_DATADESC( AI_Response )
DEFINE_FIELD( m_Type, FIELD_CHARACTER ),
DEFINE_ARRAY( m_szResponseName, FIELD_CHARACTER, AI_Response::MAX_RESPONSE_NAME ),
DEFINE_ARRAY( m_szMatchingRule, FIELD_CHARACTER, AI_Response::MAX_RULE_NAME ),
// DEFINE_FIELD( m_pCriteria, FIELD_??? ), // Don't need to save this probably
DEFINE_EMBEDDED( m_Params ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
AI_Response::AI_Response()
{
m_Type = RESPONSE_NONE;
m_szResponseName[0] = 0;
m_pCriteria = NULL;
m_szMatchingRule[0]=0;
m_szContext = NULL;
m_bApplyContextToWorld = false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
AI_Response::AI_Response( const AI_Response &from )
{
Assert( (void*)(&m_Type) == (void*)this );
m_pCriteria = NULL;
memcpy( this, &from, sizeof(*this) );
m_pCriteria = NULL;
m_szContext = NULL;
SetContext( from.m_szContext );
m_bApplyContextToWorld = from.m_bApplyContextToWorld;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
AI_Response::~AI_Response()
{
delete m_pCriteria;
delete[] m_szContext;
}
//-----------------------------------------------------------------------------
AI_Response &AI_Response::operator=( const AI_Response &from )
{
Assert( (void*)(&m_Type) == (void*)this );
delete m_pCriteria;
m_pCriteria = NULL;
memcpy( this, &from, sizeof(*this) );
m_pCriteria = NULL;
m_szContext = NULL;
SetContext( from.m_szContext );
m_bApplyContextToWorld = from.m_bApplyContextToWorld;
return *this;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *response -
// *criteria -
//-----------------------------------------------------------------------------
void AI_Response::Init( ResponseType_t type, const char *responseName, const AI_CriteriaSet& criteria, const AI_ResponseParams& responseparams, const char *ruleName, const char *applyContext, bool bApplyContextToWorld )
{
m_Type = type;
Q_strncpy( m_szResponseName, responseName, sizeof( m_szResponseName ) );
// Copy underlying criteria
m_pCriteria = new AI_CriteriaSet( criteria );
Q_strncpy( m_szMatchingRule, ruleName ? ruleName : "NULL", sizeof( m_szMatchingRule ) );
m_Params = responseparams;
SetContext( applyContext );
m_bApplyContextToWorld = bApplyContextToWorld;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void AI_Response::Describe()
{
if ( m_pCriteria )
{
DevMsg( "Search criteria:\n" );
m_pCriteria->Describe();
}
if ( m_szMatchingRule[ 0 ] )
{
DevMsg( "Matched rule '%s', ", m_szMatchingRule );
}
if ( m_szContext )
{
DevMsg( "Contexts to set '%s' on %s, ", m_szContext, m_bApplyContextToWorld ? "world" : "speaker" );
}
DevMsg( "response %s = '%s'\n", DescribeResponse( (ResponseType_t)m_Type ), m_szResponseName );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : char const
//-----------------------------------------------------------------------------
void AI_Response::GetName( char *buf, size_t buflen ) const
{
Q_strncpy( buf, m_szResponseName, buflen );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : char const
//-----------------------------------------------------------------------------
void AI_Response::GetResponse( char *buf, size_t buflen ) const
{
GetName( buf, buflen );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : type -
// Output : char const
//-----------------------------------------------------------------------------
const char *AI_Response::DescribeResponse( ResponseType_t type )
{
if ( (int)type < 0 || (int)type >= NUM_RESPONSES )
{
Assert( 0 );
return "???AI_Response bogus index";
}
switch( type )
{
default:
{
Assert( 0 );
}
// Fall through
case RESPONSE_NONE:
return "RESPONSE_NONE";
case RESPONSE_SPEAK:
return "RESPONSE_SPEAK";
case RESPONSE_SENTENCE:
return "RESPONSE_SENTENCE";
case RESPONSE_SCENE:
return "RESPONSE_SCENE";
case RESPONSE_RESPONSE:
return "RESPONSE_RESPONSE";
case RESPONSE_PRINT:
return "RESPONSE_PRINT";
}
return "RESPONSE_NONE";
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : const AI_CriteriaSet
//-----------------------------------------------------------------------------
const AI_CriteriaSet *AI_Response::GetCriteria()
{
return m_pCriteria;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void AI_Response::Release()
{
delete this;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : soundlevel_t
//-----------------------------------------------------------------------------
soundlevel_t AI_Response::GetSoundLevel() const
{
if ( m_Params.flags & AI_ResponseParams::RG_SOUNDLEVEL )
{
return (soundlevel_t)m_Params.soundlevel;
}
return SNDLVL_TALKING;
}
float AI_Response::GetRespeakDelay( void ) const
{
if ( m_Params.flags & AI_ResponseParams::RG_RESPEAKDELAY )
{
interval_t temp;
m_Params.respeakdelay.ToInterval( temp );
return RandomInterval( temp );
}
return 0.0f;
}
float AI_Response::GetWeaponDelay( void ) const
{
if ( m_Params.flags & AI_ResponseParams::RG_WEAPONDELAY )
{
interval_t temp;
m_Params.weapondelay.ToInterval( temp );
return RandomInterval( temp );
}
return 0.0f;
}
bool AI_Response::GetSpeakOnce( void ) const
{
if ( m_Params.flags & AI_ResponseParams::RG_SPEAKONCE )
{
return true;
}
return false;
}
bool AI_Response::ShouldntUseScene( void ) const
{
return ( m_Params.flags & AI_ResponseParams::RG_DONT_USE_SCENE ) != 0;
}
bool AI_Response::ShouldBreakOnNonIdle( void ) const
{
return ( m_Params.flags & AI_ResponseParams::RG_STOP_ON_NONIDLE ) != 0;
}
int AI_Response::GetOdds( void ) const
{
if ( m_Params.flags & AI_ResponseParams::RG_ODDS )
{
return m_Params.odds;
}
return 100;
}
float AI_Response::GetDelay() const
{
if ( m_Params.flags & AI_ResponseParams::RG_DELAYAFTERSPEAK )
{
interval_t temp;
m_Params.delay.ToInterval( temp );
return RandomInterval( temp );
}
return 0.0f;
}
float AI_Response::GetPreDelay() const
{
if ( m_Params.flags & AI_ResponseParams::RG_DELAYBEFORESPEAK )
{
interval_t temp;
m_Params.predelay.ToInterval( temp );
return RandomInterval( temp );
}
return 0.0f;
}
//-----------------------------------------------------------------------------
// Purpose: Sets context string
// Output : void
//-----------------------------------------------------------------------------
void AI_Response::SetContext( const char *context )
{
delete[] m_szContext;
m_szContext = NULL;
if ( context )
{
int len = Q_strlen( context );
m_szContext = new char[ len + 1 ];
Q_memcpy( m_szContext, context, len );
m_szContext[ len ] = 0;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *raw -
// *key -
// keylen -
// *value -
// valuelen -
// *duration -
// Output : static bool
//-----------------------------------------------------------------------------
const char *SplitContext( const char *raw, char *key, int keylen, char *value, int valuelen, float *duration )
{
char *colon1 = Q_strstr( raw, ":" );
if ( !colon1 )
{
DevMsg( "SplitContext: warning, ignoring context '%s', missing colon separator!\n", raw );
*key = *value = 0;
return NULL;
}
int len = colon1 - raw;
Q_strncpy( key, raw, MIN( len + 1, keylen ) );
key[ MIN( len, keylen - 1 ) ] = 0;
bool last = false;
char *end = Q_strstr( colon1 + 1, "," );
if ( !end )
{
int remaining = Q_strlen( colon1 + 1 );
end = colon1 + 1 + remaining;
last = true;
}
char *colon2 = Q_strstr( colon1 + 1, ":" );
if ( colon2 && ( colon2 < end ) )
{
if ( duration )
*duration = atof( colon2 + 1 );
len = MIN( colon2 - ( colon1 + 1 ), valuelen - 1 );
Q_strncpy( value, colon1 + 1, len + 1 );
value[ len ] = 0;
}
else
{
if ( duration )
*duration = 0.0;
len = MIN( end - ( colon1 + 1 ), valuelen - 1 );
Q_strncpy( value, colon1 + 1, len + 1 );
value[ len ] = 0;
}
return last ? NULL : end + 1;
}

View File

@@ -0,0 +1,237 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef AI_CRITERIA_H
#define AI_CRITERIA_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlrbtree.h"
#include "tier1/utlsymbol.h"
#include "interval.h"
#include "mathlib/compressed_vector.h"
extern const char *SplitContext( const char *raw, char *key, int keylen, char *value, int valuelen, float *duration );
class AI_CriteriaSet
{
public:
AI_CriteriaSet();
AI_CriteriaSet( const AI_CriteriaSet& src );
~AI_CriteriaSet();
void AppendCriteria( const char *criteria, const char *value = "", float weight = 1.0f );
void RemoveCriteria( const char *criteria );
void Describe();
int GetCount() const;
int FindCriterionIndex( const char *name ) const;
const char *GetName( int index ) const;
const char *GetValue( int index ) const;
float GetWeight( int index ) const;
private:
struct CritEntry_t
{
CritEntry_t() :
criterianame( UTL_INVAL_SYMBOL ),
weight( 0.0f )
{
value[ 0 ] = 0;
}
CritEntry_t( const CritEntry_t& src )
{
criterianame = src.criterianame;
value[ 0 ] = 0;
weight = src.weight;
SetValue( src.value );
}
CritEntry_t& operator=( const CritEntry_t& src )
{
if ( this == &src )
return *this;
criterianame = src.criterianame;
weight = src.weight;
SetValue( src.value );
return *this;
}
static bool LessFunc( const CritEntry_t& lhs, const CritEntry_t& rhs )
{
return Q_stricmp( lhs.criterianame.String(), rhs.criterianame.String() ) < 0 ? true : false;
}
void SetValue( char const *str )
{
if ( !str )
{
value[ 0 ] = 0;
}
else
{
Q_strncpy( value, str, sizeof( value ) );
}
}
CUtlSymbol criterianame;
char value[ 64 ];
float weight;
};
CUtlRBTree< CritEntry_t, short > m_Lookup;
};
#pragma pack(1)
template<typename T>
struct response_interval_t
{
T start;
T range;
interval_t &ToInterval( interval_t &dest ) const { dest.start = start; dest.range = range; return dest; }
void FromInterval( const interval_t &from ) { start = from.start; range = from.range; }
float Random() const { interval_t temp = { start, range }; return RandomInterval( temp ); }
};
typedef response_interval_t<float16_with_assign> responseparams_interval_t;
struct AI_ResponseParams
{
DECLARE_SIMPLE_DATADESC();
enum
{
RG_DELAYAFTERSPEAK = (1<<0),
RG_SPEAKONCE = (1<<1),
RG_ODDS = (1<<2),
RG_RESPEAKDELAY = (1<<3),
RG_SOUNDLEVEL = (1<<4),
RG_DONT_USE_SCENE = (1<<5),
RG_STOP_ON_NONIDLE = (1<<6),
RG_WEAPONDELAY = (1<<7),
RG_DELAYBEFORESPEAK = (1<<8),
};
AI_ResponseParams()
{
flags = 0;
odds = 100;
delay.start = 0;
delay.range = 0;
respeakdelay.start = 0;
respeakdelay.range = 0;
weapondelay.start = 0;
weapondelay.range = 0;
soundlevel = 0;
predelay.start = 0;
predelay.range = 0;
}
responseparams_interval_t delay; //4
responseparams_interval_t respeakdelay; //8
responseparams_interval_t weapondelay; //12
short odds; //14
short flags; //16
byte soundlevel; //17
responseparams_interval_t predelay; //21
};
#pragma pack()
//-----------------------------------------------------------------------------
// Purpose: Generic container for a response to a match to a criteria set
// This is what searching for a response returns
//-----------------------------------------------------------------------------
enum ResponseType_t
{
RESPONSE_NONE = 0,
RESPONSE_SPEAK,
RESPONSE_SENTENCE,
RESPONSE_SCENE,
RESPONSE_RESPONSE, // A reference to another response by name
RESPONSE_PRINT,
NUM_RESPONSES,
};
class AI_Response
{
public:
DECLARE_SIMPLE_DATADESC();
AI_Response();
AI_Response( const AI_Response &from );
~AI_Response();
AI_Response &operator=( const AI_Response &from );
void Release();
void GetName( char *buf, size_t buflen ) const;
void GetResponse( char *buf, size_t buflen ) const;
const AI_ResponseParams *GetParams() const { return &m_Params; }
ResponseType_t GetType() const { return (ResponseType_t)m_Type; }
soundlevel_t GetSoundLevel() const;
float GetRespeakDelay() const;
float GetWeaponDelay() const;
bool GetSpeakOnce() const;
bool ShouldntUseScene( ) const;
bool ShouldBreakOnNonIdle( void ) const;
int GetOdds() const;
float GetDelay() const;
float GetPreDelay() const;
void SetContext( const char *context );
const char * GetContext( void ) const { return m_szContext; }
bool IsApplyContextToWorld( void ) { return m_bApplyContextToWorld; }
void Describe();
const AI_CriteriaSet* GetCriteria();
void Init( ResponseType_t type,
const char *responseName,
const AI_CriteriaSet& criteria,
const AI_ResponseParams& responseparams,
const char *matchingRule,
const char *applyContext,
bool bApplyContextToWorld );
static const char *DescribeResponse( ResponseType_t type );
enum
{
MAX_RESPONSE_NAME = 64,
MAX_RULE_NAME = 64
};
private:
byte m_Type;
char m_szResponseName[ MAX_RESPONSE_NAME ];
char m_szMatchingRule[ MAX_RULE_NAME ];
// The initial criteria to which we are responsive
AI_CriteriaSet *m_pCriteria;
AI_ResponseParams m_Params;
char * m_szContext;
bool m_bApplyContextToWorld;
};
#endif // AI_CRITERIA_H

View File

@@ -0,0 +1,139 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Hooks and classes for the support of humanoid NPCs with
// groovy facial animation capabilities, aka, "Actors"
//
//=============================================================================//
#include "cbase.h"
#include "AI_Interest_Target.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
bool CAI_InterestTarget_t::IsThis( CBaseEntity *pThis )
{
return (pThis == m_hTarget);
};
const Vector &CAI_InterestTarget_t::GetPosition( void )
{
if (m_eType == LOOKAT_ENTITY && m_hTarget != NULL)
{
m_vecPosition = m_hTarget->EyePosition();
}
return m_vecPosition;
};
bool CAI_InterestTarget_t::IsActive( void )
{
if (m_flEndTime < gpGlobals->curtime) return false;
if (m_eType == LOOKAT_ENTITY && m_hTarget == NULL) return false;
return true;
};
float CAI_InterestTarget_t::Interest( void )
{
float t = (gpGlobals->curtime - m_flStartTime) / (m_flEndTime - m_flStartTime);
if (t < 0.0f || t > 1.0f)
return 0.0f;
if (m_flRamp && t < 1 - m_flRamp)
{
//t = t / m_flRamp;
t = 1.0 - ExponentialDecay( 0.2, m_flRamp, t );
//t = 1.0 - ExponentialDecay( 0.01, 1 - m_flRamp, t );
}
else if (t > 1.0f - m_flRamp)
{
t = (1.0 - t) / m_flRamp;
t = 3.0f * t * t - 2.0f * t * t * t;
}
else
{
t = 1.0f;
}
// ramp
t *= m_flInterest;
return t;
}
void CAI_InterestTarget::Add( CBaseEntity *pTarget, float flImportance, float flDuration, float flRamp )
{
int i;
for (i = 0; i < Count(); i++)
{
CAI_InterestTarget_t &target = Element( i );
if (target.m_hTarget == pTarget && target.m_flRamp == 0)
{
if (target.m_flStartTime == gpGlobals->curtime)
{
flImportance = MAX( flImportance, target.m_flInterest );
}
Remove( i );
break;
}
}
Add( CAI_InterestTarget_t::LOOKAT_ENTITY, pTarget, Vector( 0, 0, 0 ), flImportance, flDuration, flRamp );
}
void CAI_InterestTarget::Add( const Vector &vecPosition, float flImportance, float flDuration, float flRamp )
{
int i;
for (i = 0; i < Count(); i++)
{
CAI_InterestTarget_t &target = Element( i );
if (target.m_vecPosition == vecPosition)
{
Remove( i );
break;
}
}
Add( CAI_InterestTarget_t::LOOKAT_POSITION, NULL, vecPosition, flImportance, flDuration, flRamp );
}
void CAI_InterestTarget::Add( CBaseEntity *pTarget, const Vector &vecPosition, float flImportance, float flDuration, float flRamp )
{
int i;
for (i = 0; i < Count(); i++)
{
CAI_InterestTarget_t &target = Element( i );
if (target.m_hTarget == pTarget)
{
if (target.m_flStartTime == gpGlobals->curtime)
{
flImportance = MAX( flImportance, target.m_flInterest );
}
Remove( i );
break;
}
}
Add( CAI_InterestTarget_t::LOOKAT_BOTH, pTarget, vecPosition, flImportance, flDuration, flRamp );
}
void CAI_InterestTarget::Add( CAI_InterestTarget_t::CAI_InterestTarget_e type, CBaseEntity *pTarget, const Vector &vecPosition, float flImportance, float flDuration, float flRamp )
{
int i = AddToTail();
CAI_InterestTarget_t &target = Element( i );
target.m_eType = type;
target.m_hTarget = pTarget;
target.m_vecPosition = vecPosition;
target.m_flInterest = flImportance;
target.m_flStartTime = gpGlobals->curtime;
target.m_flEndTime = gpGlobals->curtime + flDuration;
target.m_flRamp = flRamp / flDuration;
}

View File

@@ -0,0 +1,88 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Hooks and classes for the support of humanoid NPCs with
// groovy facial animation capabilities, aka, "Actors"
//
//=============================================================================//
#ifndef AI_INTEREST_TARGET_H
#define AI_INTEREST_TARGET_H
#if defined( _WIN32 )
#pragma once
#endif
//-----------------------------------------------------------------------------
// CAI_BaseActor
//
// Purpose: The base class for all facially expressive NPCS.
//
//-----------------------------------------------------------------------------
class CAI_InterestTarget_t
{
public:
enum CAI_InterestTarget_e
{
LOOKAT_ENTITY = 0,
LOOKAT_POSITION,
LOOKAT_BOTH
};
public:
bool IsThis( CBaseEntity *pThis );
const Vector &GetPosition( void );
bool IsActive( void );
float Interest( void );
public:
CAI_InterestTarget_e m_eType; // ????
EHANDLE m_hTarget;
Vector m_vecPosition;
float m_flStartTime;
float m_flEndTime;
float m_flRamp;
float m_flInterest;
DECLARE_SIMPLE_DATADESC();
};
class CAI_InterestTarget : public CUtlVector<CAI_InterestTarget_t>
{
public:
void Add( CBaseEntity *pTarget, float flImportance, float flDuration, float flRamp );
void Add( const Vector &vecPosition, float flImportance, float flDuration, float flRamp );
void Add( CBaseEntity *pTarget, const Vector &vecPosition, float flImportance, float flDuration, float flRamp );
int Find( CBaseEntity *pTarget )
{
int i;
for ( i = 0; i < Count(); i++)
{
if (pTarget == (*this)[i].m_hTarget)
return i;
}
return InvalidIndex();
}
void Cleanup( void )
{
int i;
for (i = Count() - 1; i >= 0; i--)
{
if (!Element(i).IsActive())
{
Remove( i );
}
}
};
private:
void Add( CAI_InterestTarget_t::CAI_InterestTarget_e type, CBaseEntity *pTarget, const Vector &vecPosition, float flImportance, float flDuration, float flRamp );
};
//-----------------------------------------------------------------------------
#endif // AI_INTEREST_TARGET_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef AI_RESPONSESYSTEM_H
#define AI_RESPONSESYSTEM_H
#include "utlvector.h"
#ifdef _WIN32
#pragma once
#endif
#include "AI_Criteria.h"
abstract_class IResponseFilter
{
public:
virtual bool IsValidResponse( ResponseType_t type, const char *pszValue ) = 0;
};
abstract_class IResponseSystem
{
public:
virtual ~IResponseSystem() {}
virtual bool FindBestResponse( const AI_CriteriaSet& set, AI_Response& response, IResponseFilter *pFilter = NULL ) = 0;
virtual void GetAllResponses( CUtlVector<AI_Response *> *pResponses ) = 0;
virtual void PrecacheResponses( bool bEnable ) = 0;
};
IResponseSystem *PrecacheCustomResponseSystem( const char *scriptfile );
IResponseSystem *BuildCustomResponseSystemGivenCriteria( const char *pszBaseFile, const char *pszCustomName, AI_CriteriaSet &criteriaSet, float flCriteriaScore );
void DestroyCustomResponseSystems();
class ISaveRestoreBlockHandler *GetDefaultResponseSystemSaveRestoreBlockHandler();
class ISaveRestoreOps *GetResponseSystemSaveRestoreOps();
#endif // AI_RESPONSESYSTEM_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,231 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// #include "BaseAnimating.h"
#ifndef BASE_ANIMATING_OVERLAY_H
#define BASE_ANIMATING_OVERLAY_H
#ifdef _WIN32
#pragma once
#endif
class CBaseAnimatingOverlay;
class CAnimationLayer
{
public:
DECLARE_CLASS_NOBASE( CAnimationLayer );
CAnimationLayer( void );
void Init( CBaseAnimatingOverlay *pOverlay );
// float SetBlending( int iBlender, float flValue, CBaseAnimating *pOwner );
void StudioFrameAdvance( float flInterval, CBaseAnimating *pOwner );
void DispatchAnimEvents( CBaseAnimating *eventHandler, CBaseAnimating *pOwner );
void SetOrder( int nOrder );
float GetFadeout( float flCurTime );
// For CNetworkVars.
void NetworkStateChanged();
void NetworkStateChanged( void *pVar );
public:
#define ANIM_LAYER_ACTIVE 0x0001
#define ANIM_LAYER_AUTOKILL 0x0002
#define ANIM_LAYER_KILLME 0x0004
#define ANIM_LAYER_DONTRESTORE 0x0008
#define ANIM_LAYER_CHECKACCESS 0x0010
#define ANIM_LAYER_DYING 0x0020
int m_fFlags;
bool m_bSequenceFinished;
bool m_bLooping;
CNetworkVar( int, m_nSequence );
CNetworkVar( float, m_flCycle );
CNetworkVar( float, m_flPrevCycle );
CNetworkVar( float, m_flWeight );
float m_flPlaybackRate;
float m_flBlendIn; // start and end blend frac (0.0 for now blend)
float m_flBlendOut;
float m_flKillRate;
float m_flKillDelay;
float m_flLayerAnimtime;
float m_flLayerFadeOuttime;
// For checking for duplicates
Activity m_nActivity;
// order of layering on client
int m_nPriority;
CNetworkVar( int, m_nOrder );
bool IsActive( void ) { return ((m_fFlags & ANIM_LAYER_ACTIVE) != 0); }
bool IsAutokill( void ) { return ((m_fFlags & ANIM_LAYER_AUTOKILL) != 0); }
bool IsKillMe( void ) { return ((m_fFlags & ANIM_LAYER_KILLME) != 0); }
bool IsAutoramp( void ) { return (m_flBlendIn != 0.0 || m_flBlendOut != 0.0); }
void KillMe( void ) { m_fFlags |= ANIM_LAYER_KILLME; }
void Dying( void ) { m_fFlags |= ANIM_LAYER_DYING; }
bool IsDying( void ) { return ((m_fFlags & ANIM_LAYER_DYING) != 0); }
void Dead( void ) { m_fFlags &= ~ANIM_LAYER_DYING; }
bool IsAbandoned( void );
void MarkActive( void );
float m_flLastEventCheck;
float m_flLastAccess;
// Network state changes get forwarded here.
CBaseAnimatingOverlay *m_pOwnerEntity;
DECLARE_SIMPLE_DATADESC();
};
inline float CAnimationLayer::GetFadeout( float flCurTime )
{
float s;
if (m_flLayerFadeOuttime <= 0.0f)
{
s = 0;
}
else
{
// blend in over 0.2 seconds
s = 1.0 - (flCurTime - m_flLayerAnimtime) / m_flLayerFadeOuttime;
if (s > 0 && s <= 1.0)
{
// do a nice spline curve
s = 3 * s * s - 2 * s * s * s;
}
else if ( s > 1.0f )
{
// Shouldn't happen, but maybe curtime is behind animtime?
s = 1.0f;
}
}
return s;
}
class CBaseAnimatingOverlay : public CBaseAnimating
{
DECLARE_CLASS( CBaseAnimatingOverlay, CBaseAnimating );
public:
enum
{
MAX_OVERLAYS = 15,
};
private:
CUtlVector< CAnimationLayer > m_AnimOverlay;
//int m_nActiveLayers;
//int m_nActiveBaseLayers;
public:
virtual void OnRestore();
virtual void StudioFrameAdvance();
virtual void DispatchAnimEvents ( CBaseAnimating *eventHandler );
virtual void GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaternion q[], int boneMask );
int AddGestureSequence( int sequence, bool autokill = true );
int AddGestureSequence( int sequence, float flDuration, bool autokill = true );
int AddGesture( Activity activity, bool autokill = true );
int AddGesture( Activity activity, float flDuration, bool autokill = true );
bool IsPlayingGesture( Activity activity );
void RestartGesture( Activity activity, bool addifmissing = true, bool autokill = true );
void RemoveGesture( Activity activity );
void RemoveAllGestures( void );
int AddLayeredSequence( int sequence, int iPriority );
void SetLayerPriority( int iLayer, int iPriority );
bool IsValidLayer( int iLayer );
void SetLayerDuration( int iLayer, float flDuration );
float GetLayerDuration( int iLayer );
void SetLayerCycle( int iLayer, float flCycle );
void SetLayerCycle( int iLayer, float flCycle, float flPrevCycle );
float GetLayerCycle( int iLayer );
void SetLayerPlaybackRate( int iLayer, float flPlaybackRate );
void SetLayerWeight( int iLayer, float flWeight );
float GetLayerWeight( int iLayer );
void SetLayerBlendIn( int iLayer, float flBlendIn );
void SetLayerBlendOut( int iLayer, float flBlendOut );
void SetLayerAutokill( int iLayer, bool bAutokill );
void SetLayerLooping( int iLayer, bool bLooping );
void SetLayerNoRestore( int iLayer, bool bNoRestore );
Activity GetLayerActivity( int iLayer );
int GetLayerSequence( int iLayer );
int FindGestureLayer( Activity activity );
void RemoveLayer( int iLayer, float flKillRate = 0.2, float flKillDelay = 0.0 );
void FastRemoveLayer( int iLayer );
CAnimationLayer *GetAnimOverlay( int iIndex );
int GetNumAnimOverlays() const;
void SetNumAnimOverlays( int num );
void VerifyOrder( void );
bool HasActiveLayer( void );
private:
int AllocateLayer( int iPriority = 0 ); // lower priorities are processed first
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
DECLARE_PREDICTABLE();
};
EXTERN_SEND_TABLE(DT_BaseAnimatingOverlay);
inline int CBaseAnimatingOverlay::GetNumAnimOverlays() const
{
return m_AnimOverlay.Count();
}
// ------------------------------------------------------------------------------------------ //
// CAnimationLayer inlines.
// ------------------------------------------------------------------------------------------ //
inline void CAnimationLayer::SetOrder( int nOrder )
{
m_nOrder = nOrder;
}
inline void CAnimationLayer::NetworkStateChanged()
{
if ( m_pOwnerEntity )
m_pOwnerEntity->NetworkStateChanged();
}
inline void CAnimationLayer::NetworkStateChanged( void *pVar )
{
if ( m_pOwnerEntity )
m_pOwnerEntity->NetworkStateChanged();
}
#endif // BASE_ANIMATING_OVERLAY_H

View File

@@ -0,0 +1,275 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A base class for model-based doors. The exact movement required to
// open or close the door is not dictated by this class, only that
// the door has open, closed, opening, and closing states.
//
// Doors must satisfy these requirements:
//
// - Derived classes must support being opened by NPCs.
// - Never autoclose in the face of a player.
// - Never close into an NPC.
//
//=============================================================================//
#ifndef BASEPROPDOOR_H
#define BASEPROPDOOR_H
#ifdef _WIN32
#pragma once
#endif
#include "props.h"
#include "locksounds.h"
#include "entityoutput.h"
extern ConVar g_debug_doors;
struct opendata_t
{
Vector vecStandPos; // Where the NPC should stand.
Vector vecFaceDir; // What direction the NPC should face.
Activity eActivity; // What activity the NPC should play.
};
abstract_class CBasePropDoor : public CDynamicProp
{
public:
DECLARE_CLASS( CBasePropDoor, CDynamicProp );
DECLARE_SERVERCLASS();
CBasePropDoor( void );
void Spawn();
void Precache();
void Activate();
int ObjectCaps();
void HandleAnimEvent( animevent_t *pEvent );
// Base class services.
// Do not make the functions in this block virtual!!
// {
inline bool IsDoorOpen();
inline bool IsDoorAjar();
inline bool IsDoorOpening();
inline bool IsDoorClosed();
inline bool IsDoorClosing();
inline bool IsDoorLocked();
inline bool IsDoorBlocked() const;
inline bool IsNPCOpening(CAI_BaseNPC *pNPC);
inline bool IsPlayerOpening();
inline bool IsOpener(CBaseEntity *pEnt);
bool NPCOpenDoor(CAI_BaseNPC *pNPC);
bool TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace );
// }
// Implement these in your leaf class.
// {
virtual bool DoorCanClose( bool bAutoClose ) { return true; }
virtual bool DoorCanOpen( void ) { return true; }
virtual void GetNPCOpenData(CAI_BaseNPC *pNPC, opendata_t &opendata) = 0;
virtual float GetOpenInterval(void) = 0;
// }
protected:
enum DoorState_t
{
DOOR_STATE_CLOSED = 0,
DOOR_STATE_OPENING,
DOOR_STATE_OPEN,
DOOR_STATE_CLOSING,
DOOR_STATE_AJAR,
};
// dvs: FIXME: make these private
void DoorClose();
CBasePropDoor *GetMaster( void ) { return m_hMaster; }
bool HasSlaves( void ) { return ( m_hDoorList.Count() > 0 ); }
inline void SetDoorState( DoorState_t eDoorState );
float m_flAutoReturnDelay; // How many seconds to wait before automatically closing, -1 never closes automatically.
CUtlVector< CHandle< CBasePropDoor > > m_hDoorList; // List of doors linked to us
inline CBaseEntity *GetActivator();
private:
// Implement these in your leaf class.
// {
// Called when the door becomes fully open.
virtual void OnDoorOpened() {}
// Called when the door becomes fully closed.
virtual void OnDoorClosed() {}
// Called to tell the door to start opening.
virtual void BeginOpening(CBaseEntity *pOpenAwayFrom) = 0;
// Called to tell the door to start closing.
virtual void BeginClosing( void ) = 0;
// Called when blocked to tell the door to stop moving.
virtual void DoorStop( void ) = 0;
// Called when blocked to tell the door to continue moving.
virtual void DoorResume( void ) = 0;
// Called to send the door instantly to its spawn positions.
virtual void DoorTeleportToSpawnPosition() = 0;
// }
private:
// Main entry points for the door base behaviors.
// Do not make the functions in this block virtual!!
// {
bool DoorActivate();
void DoorOpen( CBaseEntity *pOpenAwayFrom );
void OpenIfUnlocked(CBaseEntity *pActivator, CBaseEntity *pOpenAwayFrom);
void DoorOpenMoveDone();
void DoorCloseMoveDone();
void DoorAutoCloseThink();
void Lock();
void Unlock();
void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
void OnUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
inline bool WillAutoReturn() { return m_flAutoReturnDelay != -1; }
void StartBlocked(CBaseEntity *pOther);
void OnStartBlocked( CBaseEntity *pOther );
void MasterStartBlocked( CBaseEntity *pOther );
void Blocked(CBaseEntity *pOther);
void EndBlocked(void);
void OnEndBlocked( void );
void UpdateAreaPortals(bool bOpen);
// Input handlers
void InputClose(inputdata_t &inputdata);
void InputLock(inputdata_t &inputdata);
void InputOpen(inputdata_t &inputdata);
void InputOpenAwayFrom(inputdata_t &inputdata);
void InputToggle(inputdata_t &inputdata);
void InputUnlock(inputdata_t &inputdata);
void SetDoorBlocker( CBaseEntity *pBlocker );
void SetMaster( CBasePropDoor *pMaster ) { m_hMaster = pMaster; }
void CalcDoorSounds();
// }
int m_nHardwareType;
DoorState_t m_eDoorState; // Holds whether the door is open, closed, opening, or closing.
locksound_t m_ls; // The sounds the door plays when being locked, unlocked, etc.
EHANDLE m_hActivator;
bool m_bLocked; // True if the door is locked.
EHANDLE m_hBlocker; // Entity blocking the door currently
bool m_bFirstBlocked; // Marker for being the first door (in a group) to be blocked (needed for motion control)
bool m_bForceClosed; // True if this door must close no matter what.
string_t m_SoundMoving;
string_t m_SoundOpen;
string_t m_SoundClose;
// dvs: FIXME: can we remove m_flSpeed from CBaseEntity?
//float m_flSpeed; // Rotation speed when opening or closing in degrees per second.
DECLARE_DATADESC();
string_t m_SlaveName;
CHandle< CBasePropDoor > m_hMaster;
static void RegisterPrivateActivities();
// Outputs
COutputEvent m_OnBlockedClosing; // Triggered when the door becomes blocked while closing.
COutputEvent m_OnBlockedOpening; // Triggered when the door becomes blocked while opening.
COutputEvent m_OnUnblockedClosing; // Triggered when the door becomes unblocked while closing.
COutputEvent m_OnUnblockedOpening; // Triggered when the door becomes unblocked while opening.
COutputEvent m_OnFullyClosed; // Triggered when the door reaches the fully closed position.
COutputEvent m_OnFullyOpen; // Triggered when the door reaches the fully open position.
COutputEvent m_OnClose; // Triggered when the door is told to close.
COutputEvent m_OnOpen; // Triggered when the door is told to open.
COutputEvent m_OnLockedUse; // Triggered when the user tries to open a locked door.
};
void CBasePropDoor::SetDoorState( DoorState_t eDoorState )
{
m_eDoorState = eDoorState;
}
bool CBasePropDoor::IsDoorOpen()
{
return m_eDoorState == DOOR_STATE_OPEN;
}
bool CBasePropDoor::IsDoorAjar()
{
return ( m_eDoorState == DOOR_STATE_AJAR );
}
bool CBasePropDoor::IsDoorOpening()
{
return m_eDoorState == DOOR_STATE_OPENING;
}
bool CBasePropDoor::IsDoorClosed()
{
return m_eDoorState == DOOR_STATE_CLOSED;
}
bool CBasePropDoor::IsDoorClosing()
{
return m_eDoorState == DOOR_STATE_CLOSING;
}
bool CBasePropDoor::IsDoorLocked()
{
return m_bLocked;
}
CBaseEntity *CBasePropDoor::GetActivator()
{
return m_hActivator;
}
bool CBasePropDoor::IsDoorBlocked() const
{
return ( m_hBlocker != NULL );
}
bool CBasePropDoor::IsNPCOpening( CAI_BaseNPC *pNPC )
{
return ( pNPC == ( CAI_BaseNPC * )GetActivator() );
}
inline bool CBasePropDoor::IsPlayerOpening()
{
return ( GetActivator() && GetActivator()->IsPlayer() );
}
inline bool CBasePropDoor::IsOpener(CBaseEntity *pEnt)
{
return ( GetActivator() == pEnt );
}
#endif // BASEPROPDOOR_H

View File

@@ -0,0 +1,222 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "game.h"
#include "CRagdollMagnet.h"
#include "cplane.h"
ConVar ai_debug_ragdoll_magnets( "ai_debug_ragdoll_magnets", "0");
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
LINK_ENTITY_TO_CLASS( phys_ragdollmagnet, CRagdollMagnet );
BEGIN_DATADESC( CRagdollMagnet )
DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "radius" ),
DEFINE_KEYFIELD( m_force, FIELD_FLOAT, "force" ),
DEFINE_KEYFIELD( m_axis, FIELD_VECTOR, "axis" ),
DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CRagdollMagnet::InputEnable( inputdata_t &inputdata )
{
Enable( true );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CRagdollMagnet::InputDisable( inputdata_t &inputdata )
{
Enable( false );
}
//-----------------------------------------------------------------------------
// Purpose: Find the ragdoll magnet entity that should pull this entity's ragdoll
// Input : *pNPC - the npc that's dying
// Output : CRagdollMagnet - the magnet that's best to use.
//
// NOTES:
//
// The nearest ragdoll magnet pulls me in IF:
// - Present
// - I'm within the magnet's RADIUS
// - LATER: There is clear line of sight from my center to the magnet
// - LATER: I'm not flagged to ignore ragdoll magnets
// - LATER: The magnet is not turned OFF
//-----------------------------------------------------------------------------
CRagdollMagnet *CRagdollMagnet::FindBestMagnet( CBaseEntity *pNPC )
{
CRagdollMagnet *pMagnet = NULL;
CRagdollMagnet *pBestMagnet;
float flClosestDist;
// Assume we won't find one.
pBestMagnet = NULL;
flClosestDist = FLT_MAX;
do
{
pMagnet = (CRagdollMagnet *)gEntList.FindEntityByClassname( pMagnet, "phys_ragdollmagnet" );
if( pMagnet && pMagnet->IsEnabled() )
{
if( pMagnet->m_target != NULL_STRING )
{
// if this magnet has a target, only affect that target!
if( pNPC->GetEntityName() == pMagnet->m_target )
{
return pMagnet;
}
else
{
continue;
}
}
float flDist;
flDist = pMagnet->DistToPoint( pNPC->WorldSpaceCenter() );
if( flDist < flClosestDist && flDist <= pMagnet->GetRadius() )
{
// This is the closest magnet that can pull this npc.
flClosestDist = flDist;
pBestMagnet = pMagnet;
}
}
} while( pMagnet );
return pBestMagnet;
}
//-----------------------------------------------------------------------------
// Purpose: Get the force that we should add to this NPC's ragdoll.
// Input : *pNPC -
// Output : Vector
//
// NOTE: This function assumes pNPC is within this magnet's radius.
//-----------------------------------------------------------------------------
Vector CRagdollMagnet::GetForceVector( CBaseEntity *pNPC )
{
Vector vecForceToApply;
if( IsBarMagnet() )
{
CPlane axis;
Vector vecForceDir;
Vector vecClosest;
CalcClosestPointOnLineSegment( pNPC->WorldSpaceCenter(), GetAbsOrigin(), m_axis, vecClosest, NULL );
vecForceDir = (vecClosest - pNPC->WorldSpaceCenter() );
VectorNormalize( vecForceDir );
vecForceToApply = vecForceDir * m_force;
}
else
{
Vector vecForce;
vecForce = GetAbsOrigin() - pNPC->WorldSpaceCenter();
VectorNormalize( vecForce );
vecForceToApply = vecForce * m_force;
}
if( ai_debug_ragdoll_magnets.GetBool() )
{
IPhysicsObject *pPhysObject;
pPhysObject = pNPC->VPhysicsGetObject();
if( pPhysObject )
{
Msg("Ragdoll magnet adding %f inches/sec to %s\n", m_force/pPhysObject->GetMass(), pNPC->GetClassname() );
}
}
return vecForceToApply;
}
//-----------------------------------------------------------------------------
// Purpose: How far away is this point? This is different for point and bar magnets
// Input : &vecPoint - the point
// Output : float - the dist
//-----------------------------------------------------------------------------
float CRagdollMagnet::DistToPoint( const Vector &vecPoint )
{
if( IsBarMagnet() )
{
// I'm a bar magnet, so the point's distance is really the plane constant.
// A bar magnet is a cylinder who's length is AbsOrigin() to m_axis, and whose
// diameter is m_radius.
// first we build two planes. The TOP and BOTTOM planes.
// the idea is that vecPoint must be on the right side of both
// planes to be affected by this particular magnet.
// TOP and BOTTOM planes can be visualized as the 'caps' of the cylinder
// that describes the bar magnet, and they point towards each other.
// We're making sure vecPoint is between the caps.
Vector vecAxis;
vecAxis = GetAxisVector();
VectorNormalize( vecAxis );
CPlane top, bottom;
bottom.InitializePlane( -vecAxis, m_axis );
top.InitializePlane( vecAxis, GetAbsOrigin() );
if( top.PointInFront( vecPoint ) && bottom.PointInFront( vecPoint ) )
{
// This point is between the two caps, so calculate the distance
// of vecPoint from the axis of the bar magnet
CPlane axis;
Vector vecUp;
Vector vecRight;
// Horizontal and Vertical distances.
float hDist, vDist;
// Need to get a vector that's right-hand to m_axis
VectorVectors( vecAxis, vecRight, vecUp );
//CrossProduct( vecAxis, vecUp, vecRight );
//VectorNormalize( vecRight );
//VectorNormalize( vecUp );
// Set up the plane to measure horizontal dist.
axis.InitializePlane( vecRight, GetAbsOrigin() );
hDist = fabs( axis.PointDist( vecPoint ) );
axis.InitializePlane( vecUp, GetAbsOrigin() );
vDist = fabs( axis.PointDist( vecPoint ) );
return MAX( hDist, vDist );
}
else
{
return FLT_MAX;
}
}
else
{
// I'm a point magnet. Just return dist
return ( GetAbsOrigin() - vecPoint ).Length();
}
}

View File

@@ -0,0 +1,45 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Used to influence the initial force for a dying NPC's ragdoll.
// Passive entity. Just represents position in the world, radius, force
//
// $NoKeywords: $
//=============================================================================//
#pragma once
#ifndef CRAGDOLLMAGNET_H
#define CRAGDOLLMAGNET_H
#define SF_RAGDOLLMAGNET_BAR 0x00000002 // this is a bar magnet.
class CRagdollMagnet : public CPointEntity
{
public:
DECLARE_CLASS( CRagdollMagnet, CPointEntity );
DECLARE_DATADESC();
Vector GetForceVector( CBaseEntity *pNPC );
float GetRadius( void ) { return m_radius; }
Vector GetAxisVector( void ) { return m_axis - GetAbsOrigin(); }
float DistToPoint( const Vector &vecPoint );
bool IsEnabled( void ) { return !m_bDisabled; }
int IsBarMagnet( void ) { return (m_spawnflags & SF_RAGDOLLMAGNET_BAR); }
static CRagdollMagnet *FindBestMagnet( CBaseEntity *pNPC );
void Enable( bool bEnable ) { m_bDisabled = !bEnable; }
// Inputs
void InputEnable( inputdata_t &inputdata );
void InputDisable( inputdata_t &inputdata );
private:
bool m_bDisabled;
float m_radius;
float m_force;
Vector m_axis;
};
#endif //CRAGDOLLMAGNET_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,198 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Utility code.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "te.h"
#include "shake.h"
#include "decals.h"
#include "IEffects.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud
extern short g_sModelIndexBloodDrop; // (in combatweapon.cpp) holds the sprite index for the initial blood
extern short g_sModelIndexBloodSpray; // (in combatweapon.cpp) holds the sprite index for splattered blood
//-----------------------------------------------------------------------------
// Client-server neutral effects interface
//-----------------------------------------------------------------------------
class CEffectsServer : public IEffects
{
public:
CEffectsServer();
virtual ~CEffectsServer();
// Members of the IEffect interface
virtual void Beam( const Vector &Start, const Vector &End, int nModelIndex,
int nHaloIndex, unsigned char frameStart, unsigned char frameRate,
float flLife, unsigned char width, unsigned char endWidth, unsigned char fadeLength,
unsigned char noise, unsigned char red, unsigned char green,
unsigned char blue, unsigned char brightness, unsigned char speed);
virtual void Smoke( const Vector &origin, int mModel, float flScale, float flFramerate );
virtual void Sparks( const Vector &position, int nMagnitude = 1, int nTrailLength = 1, const Vector *pvecDir = NULL );
virtual void Dust( const Vector &pos, const Vector &dir, float size, float speed );
virtual void MuzzleFlash( const Vector &origin, const QAngle &angles, float scale, int type );
virtual void MetalSparks( const Vector &position, const Vector &direction );
virtual void EnergySplash( const Vector &position, const Vector &direction, bool bExplosive = false );
virtual void Ricochet( const Vector &position, const Vector &direction );
// FIXME: Should these methods remain in this interface? Or go in some
// other client-server neutral interface?
virtual float Time();
virtual bool IsServer();
virtual void SuppressEffectsSounds( bool bSuppress ) { Assert(0); }
private:
//-----------------------------------------------------------------------------
// Purpose: Returning true means don't even call TE func
// Input : filter -
// *suppress_host -
// Output : static bool
//-----------------------------------------------------------------------------
bool SuppressTE( CRecipientFilter& filter )
{
if ( GetSuppressHost() )
{
if ( !filter.IgnorePredictionCull() )
{
filter.RemoveRecipient( (CBasePlayer *)GetSuppressHost() );
}
if ( !filter.GetRecipientCount() )
{
// Suppress it
return true;
}
}
// There's at least one recipient
return false;
}
};
//-----------------------------------------------------------------------------
// Client-server neutral effects interface accessor
//-----------------------------------------------------------------------------
static CEffectsServer s_EffectServer;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CEffectsServer, IEffects, IEFFECTS_INTERFACE_VERSION, s_EffectServer);
IEffects *g_pEffects = &s_EffectServer;
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CEffectsServer::CEffectsServer()
{
}
CEffectsServer::~CEffectsServer()
{
}
//-----------------------------------------------------------------------------
// Generates a beam
//-----------------------------------------------------------------------------
void CEffectsServer::Beam( const Vector &vecStart, const Vector &vecEnd, int nModelIndex,
int nHaloIndex, unsigned char frameStart, unsigned char frameRate,
float flLife, unsigned char width, unsigned char endWidth, unsigned char fadeLength,
unsigned char noise, unsigned char red, unsigned char green,
unsigned char blue, unsigned char brightness, unsigned char speed)
{
CBroadcastRecipientFilter filter;
if ( !SuppressTE( filter ) )
{
te->BeamPoints( filter, 0.0,
&vecStart, &vecEnd, nModelIndex, nHaloIndex, frameStart, frameRate, flLife,
width, endWidth, fadeLength, noise, red, green, blue, brightness, speed );
}
}
//-----------------------------------------------------------------------------
// Generates various tempent effects
//-----------------------------------------------------------------------------
void CEffectsServer::Smoke( const Vector &origin, int mModel, float flScale, float flFramerate )
{
CPVSFilter filter( origin );
if ( !SuppressTE( filter ) )
{
te->Smoke( filter, 0.0, &origin, mModel, flScale * 0.1f, flFramerate );
}
}
void CEffectsServer::Sparks( const Vector &position, int nMagnitude, int nTrailLength, const Vector *pvecDir )
{
CPVSFilter filter( position );
if ( !SuppressTE( filter ) )
{
te->Sparks( filter, 0.0, &position, nMagnitude, nTrailLength, pvecDir );
}
}
void CEffectsServer::Dust( const Vector &pos, const Vector &dir, float size, float speed )
{
CPVSFilter filter( pos );
if ( !SuppressTE( filter ) )
{
te->Dust( filter, 0.0, pos, dir, size, speed );
}
}
void CEffectsServer::MuzzleFlash( const Vector &origin, const QAngle &angles, float scale, int type )
{
CPVSFilter filter( origin );
if ( !SuppressTE( filter ) )
{
te->MuzzleFlash( filter, 0.0f, origin, angles, scale, type );
}
}
void CEffectsServer::MetalSparks( const Vector &position, const Vector &direction )
{
CPVSFilter filter( position );
if ( !SuppressTE( filter ) )
{
te->MetalSparks( filter, 0.0, &position, &direction );
}
}
void CEffectsServer::EnergySplash( const Vector &position, const Vector &direction, bool bExplosive )
{
CPVSFilter filter( position );
if ( !SuppressTE( filter ) )
{
te->EnergySplash( filter, 0.0, &position, &direction, bExplosive );
}
}
void CEffectsServer::Ricochet( const Vector &position, const Vector &direction )
{
CPVSFilter filter( position );
if ( !SuppressTE( filter ) )
{
te->ArmorRicochet( filter, 0.0, &position, &direction );
}
}
//-----------------------------------------------------------------------------
// FIXME: Should these methods remain in this interface? Or go in some
// other client-server neutral interface?
//-----------------------------------------------------------------------------
float CEffectsServer::Time()
{
return gpGlobals->curtime;
}
bool CEffectsServer::IsServer()
{
return true;
}

View File

@@ -0,0 +1,388 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Dissolve entity to be attached to target entity. Serves two purposes:
//
// 1) An entity that can be placed by a level designer and triggered
// to ignite a target entity.
//
// 2) An entity that can be created at runtime to ignite a target entity.
//
//=============================================================================//
#include "cbase.h"
#include "EntityDissolve.h"
#include "baseanimating.h"
#include "physics_prop_ragdoll.h"
#include "ai_basenpc.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static const char *s_pElectroThinkContext = "ElectroThinkContext";
//-----------------------------------------------------------------------------
// Lifetime
//-----------------------------------------------------------------------------
#define DISSOLVE_FADE_IN_START_TIME 0.0f
#define DISSOLVE_FADE_IN_END_TIME 1.0f
#define DISSOLVE_FADE_OUT_MODEL_START_TIME 1.9f
#define DISSOLVE_FADE_OUT_MODEL_END_TIME 2.0f
#define DISSOLVE_FADE_OUT_START_TIME 2.0f
#define DISSOLVE_FADE_OUT_END_TIME 2.0f
//-----------------------------------------------------------------------------
// Model
//-----------------------------------------------------------------------------
#define DISSOLVE_SPRITE_NAME "sprites/blueglow1.vmt"
//-----------------------------------------------------------------------------
// Save/load
//-----------------------------------------------------------------------------
BEGIN_DATADESC( CEntityDissolve )
DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
DEFINE_FIELD( m_flFadeInStart, FIELD_FLOAT ),
DEFINE_FIELD( m_flFadeInLength, FIELD_FLOAT ),
DEFINE_FIELD( m_flFadeOutModelStart, FIELD_FLOAT ),
DEFINE_FIELD( m_flFadeOutModelLength, FIELD_FLOAT ),
DEFINE_FIELD( m_flFadeOutStart, FIELD_FLOAT ),
DEFINE_FIELD( m_flFadeOutLength, FIELD_FLOAT ),
DEFINE_KEYFIELD( m_nDissolveType, FIELD_INTEGER, "dissolvetype" ),
DEFINE_FIELD( m_vDissolverOrigin, FIELD_VECTOR ),
DEFINE_KEYFIELD( m_nMagnitude, FIELD_INTEGER, "magnitude" ),
DEFINE_FUNCTION( DissolveThink ),
DEFINE_FUNCTION( ElectrocuteThink ),
DEFINE_INPUTFUNC( FIELD_STRING, "Dissolve", InputDissolve ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Networking
//-----------------------------------------------------------------------------
IMPLEMENT_SERVERCLASS_ST( CEntityDissolve, DT_EntityDissolve )
SendPropTime( SENDINFO( m_flStartTime ) ),
SendPropFloat( SENDINFO( m_flFadeInStart ), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO( m_flFadeInLength ), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO( m_flFadeOutModelStart ), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO( m_flFadeOutModelLength ), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO( m_flFadeOutStart ), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO( m_flFadeOutLength ), 0, SPROP_NOSCALE ),
SendPropInt( SENDINFO( m_nDissolveType ), ENTITY_DISSOLVE_BITS, SPROP_UNSIGNED ),
SendPropVector (SENDINFO(m_vDissolverOrigin), 0, SPROP_NOSCALE ),
SendPropInt( SENDINFO( m_nMagnitude ), 8, SPROP_UNSIGNED ),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( env_entity_dissolver, CEntityDissolve );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEntityDissolve::CEntityDissolve( void )
{
m_flStartTime = 0.0f;
m_nMagnitude = 250;
}
CEntityDissolve::~CEntityDissolve( void )
{
}
//-----------------------------------------------------------------------------
// Precache
//-----------------------------------------------------------------------------
void CEntityDissolve::Precache()
{
if ( NULL_STRING == GetModelName() )
{
PrecacheModel( DISSOLVE_SPRITE_NAME );
}
else
{
PrecacheModel( STRING( GetModelName() ) );
}
}
//-----------------------------------------------------------------------------
// Spawn
//-----------------------------------------------------------------------------
void CEntityDissolve::Spawn()
{
BaseClass::Spawn();
Precache();
UTIL_SetModel( this, STRING( GetModelName() ) );
if ( (m_nDissolveType == ENTITY_DISSOLVE_ELECTRICAL) || (m_nDissolveType == ENTITY_DISSOLVE_ELECTRICAL_LIGHT) )
{
if ( dynamic_cast< CRagdollProp* >( GetMoveParent() ) )
{
SetContextThink( &CEntityDissolve::ElectrocuteThink, gpGlobals->curtime + 0.01f, s_pElectroThinkContext );
}
}
// Setup our times
m_flFadeInStart = DISSOLVE_FADE_IN_START_TIME;
m_flFadeInLength = DISSOLVE_FADE_IN_END_TIME - DISSOLVE_FADE_IN_START_TIME;
m_flFadeOutModelStart = DISSOLVE_FADE_OUT_MODEL_START_TIME;
m_flFadeOutModelLength = DISSOLVE_FADE_OUT_MODEL_END_TIME - DISSOLVE_FADE_OUT_MODEL_START_TIME;
m_flFadeOutStart = DISSOLVE_FADE_OUT_START_TIME;
m_flFadeOutLength = DISSOLVE_FADE_OUT_END_TIME - DISSOLVE_FADE_OUT_START_TIME;
if ( m_nDissolveType == ENTITY_DISSOLVE_CORE )
{
m_flFadeInStart = 0.0f;
m_flFadeOutStart = CORE_DISSOLVE_FADE_START;
m_flFadeOutModelStart = CORE_DISSOLVE_MODEL_FADE_START;
m_flFadeOutModelLength = CORE_DISSOLVE_MODEL_FADE_LENGTH;
m_flFadeInLength = CORE_DISSOLVE_FADEIN_LENGTH;
}
m_nRenderMode = kRenderTransColor;
SetRenderColor( 255, 255, 255, 255 );
m_nRenderFX = kRenderFxNone;
SetThink( &CEntityDissolve::DissolveThink );
if ( gpGlobals->curtime > m_flStartTime )
{
// Necessary for server-side ragdolls
DissolveThink();
}
else
{
SetNextThink( gpGlobals->curtime + 0.01f );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : inputdata -
//-----------------------------------------------------------------------------
void CEntityDissolve::InputDissolve( inputdata_t &inputdata )
{
string_t strTarget = inputdata.value.StringID();
if (strTarget == NULL_STRING)
{
strTarget = m_target;
}
CBaseEntity *pTarget = NULL;
while ((pTarget = gEntList.FindEntityGeneric(pTarget, STRING(strTarget), this, inputdata.pActivator)) != NULL)
{
CBaseAnimating *pBaseAnim = pTarget->GetBaseAnimating();
if (pBaseAnim)
{
pBaseAnim->Dissolve( NULL, gpGlobals->curtime, false, m_nDissolveType, GetAbsOrigin(), m_nMagnitude );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Creates a flame and attaches it to a target entity.
// Input : pTarget -
//-----------------------------------------------------------------------------
CEntityDissolve *CEntityDissolve::Create( CBaseEntity *pTarget, const char *pMaterialName,
float flStartTime, int nDissolveType, bool *pRagdollCreated )
{
if ( pRagdollCreated )
{
*pRagdollCreated = false;
}
if ( !pMaterialName )
{
pMaterialName = DISSOLVE_SPRITE_NAME;
}
if ( pTarget->IsPlayer() )
{
// Simply immediately kill the player.
CBasePlayer *pPlayer = assert_cast< CBasePlayer* >( pTarget );
pPlayer->SetArmorValue( 0 );
CTakeDamageInfo info( pPlayer, pPlayer, pPlayer->GetHealth(), DMG_GENERIC | DMG_REMOVENORAGDOLL | DMG_PREVENT_PHYSICS_FORCE );
pPlayer->TakeDamage( info );
return NULL;
}
CEntityDissolve *pDissolve = (CEntityDissolve *) CreateEntityByName( "env_entity_dissolver" );
if ( pDissolve == NULL )
return NULL;
pDissolve->m_nDissolveType = nDissolveType;
if ( (nDissolveType == ENTITY_DISSOLVE_ELECTRICAL) || (nDissolveType == ENTITY_DISSOLVE_ELECTRICAL_LIGHT) )
{
if ( pTarget->IsNPC() && pTarget->MyNPCPointer()->CanBecomeRagdoll() )
{
CTakeDamageInfo info;
CBaseEntity *pRagdoll = CreateServerRagdoll( pTarget->MyNPCPointer(), 0, info, COLLISION_GROUP_DEBRIS, true );
pRagdoll->SetCollisionBounds( pTarget->CollisionProp()->OBBMins(), pTarget->CollisionProp()->OBBMaxs() );
// Necessary to cause it to do the appropriate death cleanup
if ( pTarget->m_lifeState == LIFE_ALIVE )
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
CTakeDamageInfo ragdollInfo( pPlayer, pPlayer, 10000.0, DMG_SHOCK | DMG_REMOVENORAGDOLL | DMG_PREVENT_PHYSICS_FORCE );
pTarget->TakeDamage( ragdollInfo );
}
if ( pRagdollCreated )
{
*pRagdollCreated = true;
}
UTIL_Remove( pTarget );
pTarget = pRagdoll;
}
}
pDissolve->SetModelName( AllocPooledString(pMaterialName) );
pDissolve->AttachToEntity( pTarget );
pDissolve->SetStartTime( flStartTime );
pDissolve->Spawn();
// Send to the client even though we don't have a model
pDissolve->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
// Play any appropriate noises when we start to dissolve
if ( (nDissolveType == ENTITY_DISSOLVE_ELECTRICAL) || (nDissolveType == ENTITY_DISSOLVE_ELECTRICAL_LIGHT) )
{
pTarget->DispatchResponse( "TLK_ELECTROCUTESCREAM" );
}
else
{
pTarget->DispatchResponse( "TLK_DISSOLVESCREAM" );
}
return pDissolve;
}
//-----------------------------------------------------------------------------
// What type of dissolve?
//-----------------------------------------------------------------------------
CEntityDissolve *CEntityDissolve::Create( CBaseEntity *pTarget, CBaseEntity *pSource )
{
// Look for other boogies on the ragdoll + kill them
for ( CBaseEntity *pChild = pSource->FirstMoveChild(); pChild; pChild = pChild->NextMovePeer() )
{
CEntityDissolve *pDissolve = dynamic_cast<CEntityDissolve*>(pChild);
if ( !pDissolve )
continue;
return Create( pTarget, STRING( pDissolve->GetModelName() ), pDissolve->m_flStartTime, pDissolve->m_nDissolveType );
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Attaches the flame to an entity and moves with it
// Input : pTarget - target entity to attach to
//-----------------------------------------------------------------------------
void CEntityDissolve::AttachToEntity( CBaseEntity *pTarget )
{
// So our dissolver follows the entity around on the server.
SetParent( pTarget );
SetLocalOrigin( vec3_origin );
SetLocalAngles( vec3_angle );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : lifetime -
//-----------------------------------------------------------------------------
void CEntityDissolve::SetStartTime( float flStartTime )
{
m_flStartTime = flStartTime;
}
//-----------------------------------------------------------------------------
// Purpose: Burn targets around us
//-----------------------------------------------------------------------------
void CEntityDissolve::DissolveThink( void )
{
CBaseAnimating *pTarget = ( GetMoveParent() ) ? GetMoveParent()->GetBaseAnimating() : NULL;
if ( GetModelName() == NULL_STRING && pTarget == NULL )
return;
if ( pTarget == NULL )
{
UTIL_Remove( this );
return;
}
// Turn them into debris
pTarget->SetCollisionGroup( COLLISION_GROUP_DISSOLVING );
if ( pTarget && pTarget->GetFlags() & FL_TRANSRAGDOLL )
{
SetRenderColorA( 0 );
}
float dt = gpGlobals->curtime - m_flStartTime;
if ( dt < m_flFadeInStart )
{
SetNextThink( m_flStartTime + m_flFadeInStart );
return;
}
// If we're done fading, then kill our target entity and us
if ( dt >= m_flFadeOutStart + m_flFadeOutLength )
{
// Necessary to cause it to do the appropriate death cleanup
// Yeah, the player may have nothing to do with it, but
// passing NULL to TakeDamage causes bad things to happen
CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
int iNoPhysicsDamage = g_pGameRules->Damage_GetNoPhysicsForce();
CTakeDamageInfo info( pPlayer, pPlayer, 10000.0, DMG_GENERIC | DMG_REMOVENORAGDOLL | iNoPhysicsDamage );
pTarget->TakeDamage( info );
if ( pTarget != pPlayer )
{
UTIL_Remove( pTarget );
}
UTIL_Remove( this );
return;
}
SetNextThink( gpGlobals->curtime + TICK_INTERVAL );
}
//-----------------------------------------------------------------------------
// Purpose: Burn targets around us
//-----------------------------------------------------------------------------
void CEntityDissolve::ElectrocuteThink( void )
{
CRagdollProp *pRagdoll = dynamic_cast< CRagdollProp* >( GetMoveParent() );
if ( !pRagdoll )
return;
ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll( );
for ( int j = 0; j < pRagdollPhys->listCount; ++j )
{
Vector vecForce;
vecForce = RandomVector( -2400.0f, 2400.0f );
pRagdollPhys->list[j].pObject->ApplyForceCenter( vecForce );
}
SetContextThink( &CEntityDissolve::ElectrocuteThink, gpGlobals->curtime + random->RandomFloat( 0.1, 0.2f ),
s_pElectroThinkContext );
}

View File

@@ -0,0 +1,63 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef ENTITYDISSOLVE_H
#define ENTITYDISSOLVE_H
#ifdef _WIN32
#pragma once
#endif
class CEntityDissolve : public CBaseEntity
{
public:
DECLARE_SERVERCLASS();
DECLARE_CLASS( CEntityDissolve, CBaseEntity );
CEntityDissolve( void );
~CEntityDissolve( void );
static CEntityDissolve *Create( CBaseEntity *pTarget, const char *pMaterialName,
float flStartTime, int nDissolveType = 0, bool *pRagdollCreated = NULL );
static CEntityDissolve *Create( CBaseEntity *pTarget, CBaseEntity *pSource );
void Precache();
void Spawn();
void AttachToEntity( CBaseEntity *pTarget );
void SetStartTime( float flStartTime );
void SetDissolverOrigin( Vector vOrigin ) { m_vDissolverOrigin = vOrigin; }
void SetMagnitude( int iMagnitude ){ m_nMagnitude = iMagnitude; }
void SetDissolveType( int iType ) { m_nDissolveType = iType; }
Vector GetDissolverOrigin( void )
{
Vector vReturn = m_vDissolverOrigin;
return vReturn;
}
int GetMagnitude( void ) { return m_nMagnitude; }
int GetDissolveType( void ) { return m_nDissolveType; }
DECLARE_DATADESC();
CNetworkVar( float, m_flStartTime );
CNetworkVar( float, m_flFadeInStart );
CNetworkVar( float, m_flFadeInLength );
CNetworkVar( float, m_flFadeOutModelStart );
CNetworkVar( float, m_flFadeOutModelLength );
CNetworkVar( float, m_flFadeOutStart );
CNetworkVar( float, m_flFadeOutLength );
protected:
void InputDissolve( inputdata_t &inputdata );
void DissolveThink( void );
void ElectrocuteThink( void );
CNetworkVar( int, m_nDissolveType );
CNetworkVector( m_vDissolverOrigin );
CNetworkVar( int, m_nMagnitude );
};
#endif // ENTITYDISSOLVE_H

View File

@@ -0,0 +1,335 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Flame entity to be attached to target entity. Serves two purposes:
//
// 1) An entity that can be placed by a level designer and triggered
// to ignite a target entity.
//
// 2) An entity that can be created at runtime to ignite a target entity.
//
//=============================================================================//
#include "cbase.h"
#include "EntityFlame.h"
#include "ai_basenpc.h"
#include "fire.h"
#include "shareddefs.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
BEGIN_DATADESC( CEntityFlame )
DEFINE_KEYFIELD( m_flLifetime, FIELD_FLOAT, "lifetime" ),
DEFINE_FIELD( m_flSize, FIELD_FLOAT ),
DEFINE_FIELD( m_hEntAttached, FIELD_EHANDLE ),
DEFINE_FIELD( m_bUseHitboxes, FIELD_BOOLEAN ),
DEFINE_FIELD( m_iNumHitboxFires, FIELD_INTEGER ),
DEFINE_FIELD( m_flHitboxFireScale, FIELD_FLOAT ),
// DEFINE_FIELD( m_bPlayingSound, FIELD_BOOLEAN ),
DEFINE_FUNCTION( FlameThink ),
DEFINE_INPUTFUNC( FIELD_VOID, "Ignite", InputIgnite ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CEntityFlame, DT_EntityFlame )
SendPropEHandle( SENDINFO( m_hEntAttached ) ),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( entityflame, CEntityFlame );
LINK_ENTITY_TO_CLASS( env_entity_igniter, CEntityFlame );
PRECACHE_REGISTER(entityflame);
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEntityFlame::CEntityFlame( void )
{
m_flSize = 0.0f;
m_iNumHitboxFires = 10;
m_flHitboxFireScale = 1.0f;
m_flLifetime = 0.0f;
m_bPlayingSound = false;
}
void CEntityFlame::UpdateOnRemove()
{
// Sometimes the entity I'm burning gets destroyed by other means,
// which kills me. Make sure to stop the burning sound.
if ( m_bPlayingSound )
{
EmitSound( "General.StopBurning" );
m_bPlayingSound = false;
}
BaseClass::UpdateOnRemove();
}
void CEntityFlame::Precache()
{
BaseClass::Precache();
PrecacheScriptSound( "General.StopBurning" );
PrecacheScriptSound( "General.BurningFlesh" );
PrecacheScriptSound( "General.BurningObject" );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : inputdata -
//-----------------------------------------------------------------------------
void CEntityFlame::InputIgnite( inputdata_t &inputdata )
{
if (m_target != NULL_STRING)
{
CBaseEntity *pTarget = NULL;
while ((pTarget = gEntList.FindEntityGeneric(pTarget, STRING(m_target), this, inputdata.pActivator)) != NULL)
{
// Combat characters know how to catch themselves on fire.
CBaseCombatCharacter *pBCC = pTarget->MyCombatCharacterPointer();
if (pBCC)
{
// DVS TODO: consider promoting Ignite to CBaseEntity and doing everything here
pBCC->Ignite(m_flLifetime);
}
// Everything else, we handle here.
else
{
CEntityFlame *pFlame = CEntityFlame::Create(pTarget);
if (pFlame)
{
pFlame->SetLifetime(m_flLifetime);
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Creates a flame and attaches it to a target entity.
// Input : pTarget -
//-----------------------------------------------------------------------------
CEntityFlame *CEntityFlame::Create( CBaseEntity *pTarget, bool useHitboxes )
{
CEntityFlame *pFlame = (CEntityFlame *) CreateEntityByName( "entityflame" );
if ( pFlame == NULL )
return NULL;
float xSize = pTarget->CollisionProp()->OBBMaxs().x - pTarget->CollisionProp()->OBBMins().x;
float ySize = pTarget->CollisionProp()->OBBMaxs().y - pTarget->CollisionProp()->OBBMins().y;
float size = ( xSize + ySize ) * 0.5f;
if ( size < 16.0f )
{
size = 16.0f;
}
UTIL_SetOrigin( pFlame, pTarget->GetAbsOrigin() );
pFlame->m_flSize = size;
pFlame->SetThink( &CEntityFlame::FlameThink );
pFlame->SetNextThink( gpGlobals->curtime + 0.1f );
pFlame->AttachToEntity( pTarget );
pFlame->SetLifetime( 2.0f );
//Send to the client even though we don't have a model
pFlame->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
pFlame->SetUseHitboxes( useHitboxes );
return pFlame;
}
//-----------------------------------------------------------------------------
// Purpose: Attaches the flame to an entity and moves with it
// Input : pTarget - target entity to attach to
//-----------------------------------------------------------------------------
void CEntityFlame::AttachToEntity( CBaseEntity *pTarget )
{
// For networking to the client.
m_hEntAttached = pTarget;
if( pTarget->IsNPC() )
{
EmitSound( "General.BurningFlesh" );
}
else
{
EmitSound( "General.BurningObject" );
}
m_bPlayingSound = true;
// So our heat emitter follows the entity around on the server.
SetParent( pTarget );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : lifetime -
//-----------------------------------------------------------------------------
void CEntityFlame::SetLifetime( float lifetime )
{
m_flLifetime = gpGlobals->curtime + lifetime;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : use -
//-----------------------------------------------------------------------------
void CEntityFlame::SetUseHitboxes( bool use )
{
m_bUseHitboxes = use;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : iNumHitBoxFires -
//-----------------------------------------------------------------------------
void CEntityFlame::SetNumHitboxFires( int iNumHitboxFires )
{
m_iNumHitboxFires = iNumHitboxFires;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : flHitboxFireScale -
//-----------------------------------------------------------------------------
void CEntityFlame::SetHitboxFireScale( float flHitboxFireScale )
{
m_flHitboxFireScale = flHitboxFireScale;
}
float CEntityFlame::GetRemainingLife( void )
{
return m_flLifetime - gpGlobals->curtime;
}
int CEntityFlame::GetNumHitboxFires( void )
{
return m_iNumHitboxFires;
}
float CEntityFlame::GetHitboxFireScale( void )
{
return m_flHitboxFireScale;
}
//-----------------------------------------------------------------------------
// Purpose: Burn targets around us
//-----------------------------------------------------------------------------
void CEntityFlame::FlameThink( void )
{
// Assure that this function will be ticked again even if we early-out in the if below.
SetNextThink( gpGlobals->curtime + FLAME_DAMAGE_INTERVAL );
if ( m_hEntAttached )
{
if ( m_hEntAttached->GetFlags() & FL_TRANSRAGDOLL )
{
SetRenderColorA( 0 );
return;
}
CAI_BaseNPC *pNPC = m_hEntAttached->MyNPCPointer();
if ( pNPC && !pNPC->IsAlive() )
{
UTIL_Remove( this );
// Notify the NPC that it's no longer burning!
pNPC->Extinguish();
return;
}
if( m_hEntAttached->GetWaterLevel() > 0 )
{
Vector mins, maxs;
mins = m_hEntAttached->WorldSpaceCenter();
maxs = mins;
maxs.z = m_hEntAttached->WorldSpaceCenter().z;
maxs.x += 32;
maxs.y += 32;
mins.z -= 32;
mins.x -= 32;
mins.y -= 32;
UTIL_Bubbles( mins, maxs, 12 );
}
}
else
{
UTIL_Remove( this );
return;
}
// See if we're done burning, or our attached ent has vanished
if ( m_flLifetime < gpGlobals->curtime || m_hEntAttached == NULL )
{
EmitSound( "General.StopBurning" );
m_bPlayingSound = false;
SetThink( &CEntityFlame::SUB_Remove );
SetNextThink( gpGlobals->curtime + 0.5f );
// Notify anything we're attached to
if ( m_hEntAttached )
{
CBaseCombatCharacter *pAttachedCC = m_hEntAttached->MyCombatCharacterPointer();
if( pAttachedCC )
{
// Notify the NPC that it's no longer burning!
pAttachedCC->Extinguish();
}
}
return;
}
if ( m_hEntAttached )
{
// Do radius damage ignoring the entity I'm attached to. This will harm things around me.
RadiusDamage( CTakeDamageInfo( this, this, 4.0f, DMG_BURN ), GetAbsOrigin(), m_flSize/2, CLASS_NONE, m_hEntAttached );
// Directly harm the entity I'm attached to. This is so we can precisely control how much damage the entity
// that is on fire takes without worrying about the flame's position relative to the bodytarget (which is the
// distance that the radius damage code uses to determine how much damage to inflict)
m_hEntAttached->TakeDamage( CTakeDamageInfo( this, this, FLAME_DIRECT_DAMAGE, DMG_BURN | DMG_DIRECT ) );
if( !m_hEntAttached->IsNPC() && hl2_episodic.GetBool() )
{
const float ENTITYFLAME_MOVE_AWAY_DIST = 24.0f;
// Make a sound near my origin, and up a little higher (in case I'm on the ground, so NPC's still hear it)
CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin(), ENTITYFLAME_MOVE_AWAY_DIST, 0.1f, this, SOUNDENT_CHANNEL_REPEATED_DANGER );
CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin() + Vector( 0, 0, 48.0f ), ENTITYFLAME_MOVE_AWAY_DIST, 0.1f, this, SOUNDENT_CHANNEL_REPEATING );
}
}
else
{
RadiusDamage( CTakeDamageInfo( this, this, FLAME_RADIUS_DAMAGE, DMG_BURN ), GetAbsOrigin(), m_flSize/2, CLASS_NONE, NULL );
}
FireSystem_AddHeatInRadius( GetAbsOrigin(), m_flSize/2, 2.0f );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pEnt -
//-----------------------------------------------------------------------------
void CreateEntityFlame(CBaseEntity *pEnt)
{
CEntityFlame::Create( pEnt );
}

View File

@@ -0,0 +1,66 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef ENTITYFLAME_H
#define ENTITYFLAME_H
#ifdef _WIN32
#pragma once
#endif
#define FLAME_DAMAGE_INTERVAL 0.2f // How often to deal damage.
#define FLAME_DIRECT_DAMAGE_PER_SEC 5.0f
#define FLAME_RADIUS_DAMAGE_PER_SEC 4.0f
#define FLAME_DIRECT_DAMAGE ( FLAME_DIRECT_DAMAGE_PER_SEC * FLAME_DAMAGE_INTERVAL )
#define FLAME_RADIUS_DAMAGE ( FLAME_RADIUS_DAMAGE_PER_SEC * FLAME_DAMAGE_INTERVAL )
#define FLAME_MAX_LIFETIME_ON_DEAD_NPCS 10.0f
class CEntityFlame : public CBaseEntity
{
public:
DECLARE_SERVERCLASS();
DECLARE_CLASS( CEntityFlame, CBaseEntity );
CEntityFlame( void );
static CEntityFlame *Create( CBaseEntity *pTarget, bool useHitboxes = true );
void AttachToEntity( CBaseEntity *pTarget );
void SetLifetime( float lifetime );
void SetUseHitboxes( bool use );
void SetNumHitboxFires( int iNumHitBoxFires );
void SetHitboxFireScale( float flHitboxFireScale );
float GetRemainingLife( void );
int GetNumHitboxFires( void );
float GetHitboxFireScale( void );
virtual void Precache();
virtual void UpdateOnRemove();
void SetSize( float size ) { m_flSize = size; }
DECLARE_DATADESC();
protected:
void InputIgnite( inputdata_t &inputdata );
void FlameThink( void );
CNetworkHandle( CBaseEntity, m_hEntAttached ); // The entity that we are burning (attached to).
CNetworkVar( float, m_flSize );
CNetworkVar( bool, m_bUseHitboxes );
CNetworkVar( int, m_iNumHitboxFires );
CNetworkVar( float, m_flHitboxFireScale );
CNetworkVar( float, m_flLifetime );
bool m_bPlayingSound;
};
#endif // ENTITYFLAME_H

View File

@@ -0,0 +1,207 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Drops particles where the entity was.
//
//=============================================================================//
#include "cbase.h"
#include "EntityParticleTrail.h"
#include "networkstringtable_gamedll.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Used to retire the entity
//-----------------------------------------------------------------------------
static const char *s_pRetireContext = "RetireContext";
//-----------------------------------------------------------------------------
// Save/load
//-----------------------------------------------------------------------------
BEGIN_DATADESC( CEntityParticleTrail )
DEFINE_FIELD( m_iMaterialName, FIELD_MATERIALINDEX ),
DEFINE_EMBEDDED( m_Info ),
DEFINE_FIELD( m_hConstraintEntity, FIELD_EHANDLE ),
// Think this should be handled by StartTouch/etc.
// DEFINE_FIELD( m_nRefCount, FIELD_INTEGER ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Networking
//-----------------------------------------------------------------------------
IMPLEMENT_SERVERCLASS_ST( CEntityParticleTrail, DT_EntityParticleTrail )
SendPropInt(SENDINFO(m_iMaterialName), MAX_MATERIAL_STRING_BITS, SPROP_UNSIGNED ),
SendPropDataTable( SENDINFO_DT( m_Info ), &REFERENCE_SEND_TABLE( DT_EntityParticleTrailInfo ) ),
SendPropEHandle(SENDINFO(m_hConstraintEntity)),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( env_particle_trail, CEntityParticleTrail );
//-----------------------------------------------------------------------------
// Purpose: Creates a flame and attaches it to a target entity.
// Input : pTarget -
//-----------------------------------------------------------------------------
CEntityParticleTrail *CEntityParticleTrail::Create( CBaseEntity *pTarget, const EntityParticleTrailInfo_t &info, CBaseEntity *pConstraintEntity )
{
int iMaterialName = GetMaterialIndex( STRING(info.m_strMaterialName) );
// Look for other particle trails on the entity + copy state to the new entity
CEntityParticleTrail *pTrail;
CBaseEntity *pNext;
for ( CBaseEntity *pChild = pTarget->FirstMoveChild(); pChild; pChild = pNext )
{
pNext = pChild->NextMovePeer();
pTrail = dynamic_cast<CEntityParticleTrail*>(pChild);
if ( pTrail && (pTrail->m_iMaterialName == iMaterialName) )
{
// Prevent destruction if it re-enters the field
pTrail->IncrementRefCount();
return pTrail;
}
}
pTrail = (CEntityParticleTrail *)CreateEntityByName( "env_particle_trail" );
if ( pTrail == NULL )
return NULL;
pTrail->m_hConstraintEntity = pConstraintEntity;
pTrail->m_iMaterialName = iMaterialName;
pTrail->m_Info.CopyFrom(info);
pTrail->m_nRefCount = 1;
pTrail->AttachToEntity( pTarget );
pTrail->Spawn();
return pTrail;
}
//-----------------------------------------------------------------------------
// Spawn
//-----------------------------------------------------------------------------
void CEntityParticleTrail::Spawn()
{
BaseClass::Spawn();
/*
SetThink( &CEntityParticleTrail::BoogieThink );
SetNextThink( gpGlobals->curtime + 0.01f );
if ( HasSpawnFlags( SF_RAGDOLL_BOOGIE_ELECTRICAL ) )
{
SetContextThink( ZapThink, gpGlobals->curtime + random->RandomFloat( 0.1f, 0.3f ), s_pZapContext );
}
*/
}
//-----------------------------------------------------------------------------
// Spawn
//-----------------------------------------------------------------------------
void CEntityParticleTrail::UpdateOnRemove()
{
g_pNotify->ClearEntity( this );
BaseClass::UpdateOnRemove();
}
//-----------------------------------------------------------------------------
// Force our constraint entity to be trasmitted
//-----------------------------------------------------------------------------
void CEntityParticleTrail::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
{
// Are we already marked for transmission?
if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
return;
BaseClass::SetTransmit( pInfo, bAlways );
// Force our constraint entity to be sent too.
if ( m_hConstraintEntity )
{
m_hConstraintEntity->SetTransmit( pInfo, bAlways );
}
}
//-----------------------------------------------------------------------------
// Retire
//-----------------------------------------------------------------------------
void CEntityParticleTrail::IncrementRefCount()
{
if ( m_nRefCount == 0 )
{
SetContextThink( NULL, gpGlobals->curtime, s_pRetireContext );
}
++m_nRefCount;
}
void CEntityParticleTrail::DecrementRefCount()
{
--m_nRefCount;
Assert( m_nRefCount >= 0 );
if ( m_nRefCount == 0 )
{
FollowEntity( NULL );
g_pNotify->ClearEntity( this );
SetContextThink( &CEntityParticleTrail::SUB_Remove, gpGlobals->curtime + m_Info.m_flLifetime, s_pRetireContext );
}
}
//-----------------------------------------------------------------------------
// Clean up when the entity goes away.
//-----------------------------------------------------------------------------
void CEntityParticleTrail::NotifySystemEvent( CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params )
{
BaseClass::NotifySystemEvent( pNotify, eventType, params );
Assert( pNotify == GetMoveParent() );
if ( eventType == NOTIFY_EVENT_DESTROY )
{
FollowEntity( NULL );
g_pNotify->ClearEntity( this );
if ( m_nRefCount != 0 )
{
m_nRefCount = 0;
SetContextThink( &CEntityParticleTrail::SUB_Remove, gpGlobals->curtime + m_Info.m_flLifetime, s_pRetireContext );
}
}
}
//-----------------------------------------------------------------------------
// Suppression count
//-----------------------------------------------------------------------------
void CEntityParticleTrail::Destroy( CBaseEntity *pTarget, const EntityParticleTrailInfo_t &info )
{
int iMaterialName = GetMaterialIndex( STRING(info.m_strMaterialName) );
// Look for the particle trail attached to this entity + decrease refcount
CBaseEntity *pNext;
for ( CBaseEntity *pChild = pTarget->FirstMoveChild(); pChild; pChild = pNext )
{
pNext = pChild->NextMovePeer();
CEntityParticleTrail *pTrail = dynamic_cast<CEntityParticleTrail*>(pChild);
if ( !pTrail || (pTrail->m_iMaterialName != iMaterialName) )
continue;
pTrail->DecrementRefCount();
}
}
//-----------------------------------------------------------------------------
// Attach to an entity
//-----------------------------------------------------------------------------
void CEntityParticleTrail::AttachToEntity( CBaseEntity *pTarget )
{
FollowEntity( pTarget );
g_pNotify->AddEntity( this, pTarget );
}

View File

@@ -0,0 +1,52 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef ENTITYPARTICLETRAIL_H
#define ENTITYPARTICLETRAIL_H
#ifdef _WIN32
#pragma once
#endif
#include "baseparticleentity.h"
#include "entityparticletrail_shared.h"
//-----------------------------------------------------------------------------
// Spawns particles after the entity
//-----------------------------------------------------------------------------
class CEntityParticleTrail : public CBaseParticleEntity
{
DECLARE_DATADESC();
DECLARE_CLASS( CEntityParticleTrail, CBaseParticleEntity );
DECLARE_SERVERCLASS();
public:
static CEntityParticleTrail *Create( CBaseEntity *pTarget, const EntityParticleTrailInfo_t &info, CBaseEntity *pConstraint );
static void Destroy( CBaseEntity *pTarget, const EntityParticleTrailInfo_t &info );
void Spawn();
virtual void UpdateOnRemove();
// Force our constraint entity to be trasmitted
virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways );
// Clean up when the entity goes away.
virtual void NotifySystemEvent( CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params );
private:
void AttachToEntity( CBaseEntity *pTarget );
void IncrementRefCount();
void DecrementRefCount();
CNetworkVar( int, m_iMaterialName );
CNetworkVarEmbedded( EntityParticleTrailInfo_t, m_Info );
CNetworkHandle( CBaseEntity, m_hConstraintEntity );
int m_nRefCount;
};
#endif // ENTITYPARTICLETRAIL_H

View File

@@ -0,0 +1,762 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements visual effects entities: sprites, beams, bubbles, etc.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "beam_shared.h"
#include "ndebugoverlay.h"
#include "filters.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// Keeps us from doing strcmps in the tracefilter.
string_t g_iszPhysicsPropClassname;
enum Touch_t
{
touch_none = 0,
touch_player_only,
touch_npc_only,
touch_player_or_npc,
touch_player_or_npc_or_physicsprop,
};
class CEnvBeam : public CBeam
{
public:
DECLARE_CLASS( CEnvBeam, CBeam );
void Spawn( void );
void Precache( void );
void Activate( void );
void StrikeThink( void );
void UpdateThink( void );
void RandomArea( void );
void RandomPoint( const Vector &vecSrc );
void Zap( const Vector &vecSrc, const Vector &vecDest );
void Strike( void );
bool PassesTouchFilters(CBaseEntity *pOther);
void InputTurnOn( inputdata_t &inputdata );
void InputTurnOff( inputdata_t &inputdata );
void InputToggle( inputdata_t &inputdata );
void InputStrikeOnce( inputdata_t &inputdata );
void TurnOn( void );
void TurnOff( void );
void Toggle( void );
const char *GetDecalName( void ){ return STRING( m_iszDecal );}
inline bool ServerSide( void )
{
if ( m_life == 0 && !HasSpawnFlags(SF_BEAM_RING) )
return true;
return false;
}
DECLARE_DATADESC();
void BeamUpdateVars( void );
int m_active;
int m_spriteTexture;
string_t m_iszStartEntity;
string_t m_iszEndEntity;
float m_life;
float m_boltWidth;
float m_noiseAmplitude;
int m_speed;
float m_restrike;
string_t m_iszSpriteName;
int m_frameStart;
float m_radius;
Touch_t m_TouchType;
string_t m_iFilterName;
EHANDLE m_hFilter;
string_t m_iszDecal;
COutputEvent m_OnTouchedByEntity;
};
LINK_ENTITY_TO_CLASS( env_beam, CEnvBeam );
BEGIN_DATADESC( CEnvBeam )
DEFINE_FIELD( m_active, FIELD_INTEGER ),
DEFINE_FIELD( m_spriteTexture, FIELD_INTEGER ),
DEFINE_KEYFIELD( m_iszStartEntity, FIELD_STRING, "LightningStart" ),
DEFINE_KEYFIELD( m_iszEndEntity, FIELD_STRING, "LightningEnd" ),
DEFINE_KEYFIELD( m_life, FIELD_FLOAT, "life" ),
DEFINE_KEYFIELD( m_boltWidth, FIELD_FLOAT, "BoltWidth" ),
DEFINE_KEYFIELD( m_noiseAmplitude, FIELD_FLOAT, "NoiseAmplitude" ),
DEFINE_KEYFIELD( m_speed, FIELD_INTEGER, "TextureScroll" ),
DEFINE_KEYFIELD( m_restrike, FIELD_FLOAT, "StrikeTime" ),
DEFINE_KEYFIELD( m_iszSpriteName, FIELD_STRING, "texture" ),
DEFINE_KEYFIELD( m_frameStart, FIELD_INTEGER, "framestart" ),
DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "Radius" ),
DEFINE_KEYFIELD( m_TouchType, FIELD_INTEGER, "TouchType" ),
DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ),
DEFINE_KEYFIELD( m_iszDecal, FIELD_STRING, "decalname" ),
DEFINE_FIELD( m_hFilter, FIELD_EHANDLE ),
// Function Pointers
DEFINE_FUNCTION( StrikeThink ),
DEFINE_FUNCTION( UpdateThink ),
// Input functions
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
DEFINE_INPUTFUNC( FIELD_VOID, "StrikeOnce", InputStrikeOnce ),
DEFINE_OUTPUT( m_OnTouchedByEntity, "OnTouchedByEntity" ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvBeam::Spawn( void )
{
if ( !m_iszSpriteName )
{
SetThink( &CEnvBeam::SUB_Remove );
return;
}
BaseClass::Spawn();
m_noiseAmplitude = MIN(MAX_BEAM_NOISEAMPLITUDE, m_noiseAmplitude);
// Check for tapering
if ( HasSpawnFlags( SF_BEAM_TAPEROUT ) )
{
SetWidth( m_boltWidth );
SetEndWidth( 0 );
}
else
{
SetWidth( m_boltWidth );
SetEndWidth( GetWidth() ); // Note: EndWidth is not scaled
}
if ( ServerSide() )
{
SetThink( &CEnvBeam::UpdateThink );
SetNextThink( gpGlobals->curtime );
SetFireTime( gpGlobals->curtime );
if ( GetEntityName() != NULL_STRING )
{
if ( !(m_spawnflags & SF_BEAM_STARTON) )
{
AddEffects( EF_NODRAW );
m_active = 0;
SetNextThink( TICK_NEVER_THINK );
}
else
{
m_active = 1;
}
}
}
else
{
m_active = 0;
if ( !GetEntityName() || FBitSet(m_spawnflags, SF_BEAM_STARTON) )
{
SetThink( &CEnvBeam::StrikeThink );
SetNextThink( gpGlobals->curtime + 1.0f );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvBeam::Precache( void )
{
if ( !Q_stristr( STRING(m_iszSpriteName), ".vmt" ) )
{
// HACK/YWB: This was almost always the laserbeam.spr, so alloc'ing the name a second time with the proper extension isn't going to
// kill us on memrory.
//Warning( "Level Design Error: %s (%i:%s) Sprite name (%s) missing .vmt extension!\n",
// STRING( m_iClassname ), entindex(), GetEntityName(), STRING(m_iszSpriteName) );
char fixedname[ 512 ];
Q_strncpy( fixedname, STRING( m_iszSpriteName ), sizeof( fixedname ) );
Q_SetExtension( fixedname, ".vmt", sizeof( fixedname ) );
m_iszSpriteName = AllocPooledString( fixedname );
}
g_iszPhysicsPropClassname = AllocPooledString( "prop_physics" );
m_spriteTexture = PrecacheModel( STRING(m_iszSpriteName) );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvBeam::Activate( void )
{
// Get a handle to my filter entity if there is one
if (m_iFilterName != NULL_STRING)
{
m_hFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iFilterName ));
}
BaseClass::Activate();
if ( ServerSide() )
BeamUpdateVars();
}
//-----------------------------------------------------------------------------
// Purpose: Input handler to turn the lightning on either continually or for
// interval refiring.
//-----------------------------------------------------------------------------
void CEnvBeam::InputTurnOn( inputdata_t &inputdata )
{
if ( !m_active )
{
TurnOn();
}
}
//-----------------------------------------------------------------------------
// Purpose: Input handler to turn the lightning off.
//-----------------------------------------------------------------------------
void CEnvBeam::InputTurnOff( inputdata_t &inputdata )
{
if ( m_active )
{
TurnOff();
}
}
//-----------------------------------------------------------------------------
// Purpose: Input handler to toggle the lightning on/off.
//-----------------------------------------------------------------------------
void CEnvBeam::InputToggle( inputdata_t &inputdata )
{
if ( m_active )
{
TurnOff();
}
else
{
TurnOn();
}
}
//-----------------------------------------------------------------------------
// Purpose: Input handler for making the beam strike once. This will not affect
// any interval refiring that might be going on. If the lifetime is set
// to zero (infinite) it will turn on and stay on.
//-----------------------------------------------------------------------------
void CEnvBeam::InputStrikeOnce( inputdata_t &inputdata )
{
Strike();
}
//-----------------------------------------------------------------------------
// Purpose: Turns the lightning on. If it is set for interval refiring, it will
// begin doing so. If it is set to be continually on, it will do so.
//-----------------------------------------------------------------------------
void CEnvBeam::TurnOn( void )
{
m_active = 1;
if ( ServerSide() )
{
RemoveEffects( EF_NODRAW );
DoSparks( GetAbsStartPos(), GetAbsEndPos() );
SetThink( &CEnvBeam::UpdateThink );
SetNextThink( gpGlobals->curtime );
SetFireTime( gpGlobals->curtime );
}
else
{
SetThink( &CEnvBeam::StrikeThink );
SetNextThink( gpGlobals->curtime );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvBeam::TurnOff( void )
{
m_active = 0;
if ( ServerSide() )
{
AddEffects( EF_NODRAW );
}
SetNextThink( TICK_NEVER_THINK );
SetThink( NULL );
}
//-----------------------------------------------------------------------------
// Purpose: Think function for striking at intervals.
//-----------------------------------------------------------------------------
void CEnvBeam::StrikeThink( void )
{
if ( m_life != 0 )
{
if ( m_spawnflags & SF_BEAM_RANDOM )
SetNextThink( gpGlobals->curtime + m_life + random->RandomFloat( 0, m_restrike ) );
else
SetNextThink( gpGlobals->curtime + m_life + m_restrike );
}
m_active = 1;
if (!m_iszEndEntity)
{
if (!m_iszStartEntity)
{
RandomArea( );
}
else
{
CBaseEntity *pStart = RandomTargetname( STRING(m_iszStartEntity) );
if (pStart != NULL)
{
RandomPoint( pStart->GetAbsOrigin() );
}
else
{
Msg( "env_beam: unknown entity \"%s\"\n", STRING(m_iszStartEntity) );
}
}
return;
}
Strike();
}
//-----------------------------------------------------------------------------
// Purpose: Strikes once for its configured lifetime.
//-----------------------------------------------------------------------------
void CEnvBeam::Strike( void )
{
CBroadcastRecipientFilter filter;
CBaseEntity *pStart = RandomTargetname( STRING(m_iszStartEntity) );
CBaseEntity *pEnd = RandomTargetname( STRING(m_iszEndEntity) );
if ( pStart == NULL || pEnd == NULL )
return;
m_speed = clamp( (int) m_speed, 0, (int) MAX_BEAM_SCROLLSPEED );
int pointStart = IsStaticPointEntity( pStart );
int pointEnd = IsStaticPointEntity( pEnd );
if ( pointStart || pointEnd )
{
if ( m_spawnflags & SF_BEAM_RING )
{
// don't work
return;
}
te->BeamEntPoint( filter, 0.0,
pointStart ? 0 : pStart->entindex(),
pointStart ? &pStart->GetAbsOrigin() : NULL,
pointEnd ? 0 : pEnd->entindex(),
pointEnd ? &pEnd->GetAbsOrigin() : NULL,
m_spriteTexture,
0, // No halo
m_frameStart,
(int)m_flFrameRate,
m_life,
m_boltWidth,
m_boltWidth, // End width
0, // No fade
m_noiseAmplitude,
m_clrRender->r, m_clrRender->g, m_clrRender->b, m_clrRender->a,
m_speed );
}
else
{
if ( m_spawnflags & SF_BEAM_RING)
{
te->BeamRing( filter, 0.0,
pStart->entindex(),
pEnd->entindex(),
m_spriteTexture,
0, // No halo
m_frameStart,
(int)m_flFrameRate,
m_life,
m_boltWidth,
0, // No spread
m_noiseAmplitude,
m_clrRender->r,
m_clrRender->g,
m_clrRender->b,
m_clrRender->a,
m_speed );
}
else
{
te->BeamEnts( filter, 0.0,
pStart->entindex(),
pEnd->entindex(),
m_spriteTexture,
0, // No halo
m_frameStart,
(int)m_flFrameRate,
m_life,
m_boltWidth,
m_boltWidth, // End width
0, // No fade
m_noiseAmplitude,
m_clrRender->r,
m_clrRender->g,
m_clrRender->b,
m_clrRender->a,
m_speed );
}
}
DoSparks( pStart->GetAbsOrigin(), pEnd->GetAbsOrigin() );
if ( m_flDamage > 0 )
{
trace_t tr;
UTIL_TraceLine( pStart->GetAbsOrigin(), pEnd->GetAbsOrigin(), MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
BeamDamageInstant( &tr, m_flDamage );
}
}
class CTraceFilterPlayersNPCs : public ITraceFilter
{
public:
bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
{
CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
if ( pEntity )
{
if ( pEntity->IsPlayer() || pEntity->MyNPCPointer() )
return true;
}
return false;
}
virtual TraceType_t GetTraceType() const
{
return TRACE_ENTITIES_ONLY;
}
};
class CTraceFilterPlayersNPCsPhysicsProps : public ITraceFilter
{
public:
bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
{
CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
if ( pEntity )
{
if ( pEntity->IsPlayer() || pEntity->MyNPCPointer() || pEntity->m_iClassname == g_iszPhysicsPropClassname )
return true;
}
return false;
}
virtual TraceType_t GetTraceType() const
{
return TRACE_ENTITIES_ONLY;
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CEnvBeam::PassesTouchFilters(CBaseEntity *pOther)
{
bool fPassedSoFar = false;
// Touched some player or NPC!
if( m_TouchType != touch_npc_only )
{
if( pOther->IsPlayer() )
{
fPassedSoFar = true;
}
}
if( m_TouchType != touch_player_only )
{
if( pOther->IsNPC() )
{
fPassedSoFar = true;
}
}
if( m_TouchType == touch_player_or_npc_or_physicsprop )
{
if( pOther->m_iClassname == g_iszPhysicsPropClassname )
{
fPassedSoFar = true;
}
}
if( fPassedSoFar )
{
CBaseFilter* pFilter = (CBaseFilter*)(m_hFilter.Get());
return (!pFilter) ? true : pFilter->PassesFilter( this, pOther );
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvBeam::UpdateThink( void )
{
// Apply damage every 1/10th of a second.
if ( ( m_flDamage > 0 ) && ( gpGlobals->curtime >= m_flFireTime + 0.1 ) )
{
trace_t tr;
UTIL_TraceLine( GetAbsStartPos(), GetAbsEndPos(), MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
BeamDamage( &tr );
// BeamDamage calls RelinkBeam, so no need to call it again.
}
else
{
RelinkBeam();
}
if( m_TouchType != touch_none )
{
trace_t tr;
Ray_t ray;
ray.Init( GetAbsStartPos(), GetAbsEndPos() );
if( m_TouchType == touch_player_or_npc_or_physicsprop )
{
CTraceFilterPlayersNPCsPhysicsProps traceFilter;
enginetrace->TraceRay( ray, MASK_SHOT, &traceFilter, &tr );
}
else
{
CTraceFilterPlayersNPCs traceFilter;
enginetrace->TraceRay( ray, MASK_SHOT, &traceFilter, &tr );
}
if( tr.fraction != 1.0 && PassesTouchFilters( tr.m_pEnt ) )
{
m_OnTouchedByEntity.FireOutput( tr.m_pEnt, this, 0 );
return;
}
}
SetNextThink( gpGlobals->curtime );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &vecSrc -
// &vecDest -
//-----------------------------------------------------------------------------
void CEnvBeam::Zap( const Vector &vecSrc, const Vector &vecDest )
{
CBroadcastRecipientFilter filter;
te->BeamPoints( filter, 0.0,
&vecSrc,
&vecDest,
m_spriteTexture,
0, // No halo
m_frameStart,
(int)m_flFrameRate,
m_life,
m_boltWidth,
m_boltWidth, // End width
0, // No fade
m_noiseAmplitude,
m_clrRender->r,
m_clrRender->g,
m_clrRender->b,
m_clrRender->a,
m_speed );
DoSparks( vecSrc, vecDest );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvBeam::RandomArea( void )
{
int iLoops = 0;
for (iLoops = 0; iLoops < 10; iLoops++)
{
Vector vecSrc = GetAbsOrigin();
Vector vecDir1 = Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ),random->RandomFloat( -1.0, 1.0 ) );
VectorNormalize( vecDir1 );
trace_t tr1;
UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr1 );
if (tr1.fraction == 1.0)
continue;
Vector vecDir2;
do {
vecDir2 = Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ),random->RandomFloat( -1.0, 1.0 ) );
} while (DotProduct(vecDir1, vecDir2 ) > 0);
VectorNormalize( vecDir2 );
trace_t tr2;
UTIL_TraceLine( vecSrc, vecSrc + vecDir2 * m_radius, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr2 );
if (tr2.fraction == 1.0)
continue;
if ((tr1.endpos - tr2.endpos).Length() < m_radius * 0.1)
continue;
UTIL_TraceLine( tr1.endpos, tr2.endpos, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr2 );
if (tr2.fraction != 1.0)
continue;
Zap( tr1.endpos, tr2.endpos );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : vecSrc -
//-----------------------------------------------------------------------------
void CEnvBeam::RandomPoint( const Vector &vecSrc )
{
int iLoops = 0;
for (iLoops = 0; iLoops < 10; iLoops++)
{
Vector vecDir1 = Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ),random->RandomFloat( -1.0, 1.0 ) );
VectorNormalize( vecDir1 );
trace_t tr1;
UTIL_TraceLine( vecSrc, vecSrc + vecDir1 * m_radius, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr1 );
if ((tr1.endpos - vecSrc).Length() < m_radius * 0.1)
continue;
if (tr1.fraction == 1.0)
continue;
Zap( vecSrc, tr1.endpos );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvBeam::BeamUpdateVars( void )
{
CBaseEntity *pStart = gEntList.FindEntityByName( NULL, m_iszStartEntity );
CBaseEntity *pEnd = gEntList.FindEntityByName( NULL, m_iszEndEntity );
if (( pStart == NULL ) || ( pEnd == NULL ))
{
return;
}
m_nNumBeamEnts = 2;
m_speed = clamp( (int) m_speed, 0, (int) MAX_BEAM_SCROLLSPEED );
// NOTE: If the end entity is the beam itself (and the start entity
// isn't *also* the beam itself, we've got problems. This is a problem
// because SetAbsStartPos actually sets the entity's origin.
if ( ( pEnd == this ) && ( pStart != this ) )
{
DevMsg("env_beams cannot have the end entity be the beam itself\n"
"unless the start entity is also the beam itself!\n" );
Assert(0);
}
SetModelName( m_iszSpriteName );
SetTexture( m_spriteTexture );
SetType( BEAM_ENTPOINT );
if ( IsStaticPointEntity( pStart ) )
{
SetAbsStartPos( pStart->GetAbsOrigin() );
}
else
{
SetStartEntity( pStart );
}
if ( IsStaticPointEntity( pEnd ) )
{
SetAbsEndPos( pEnd->GetAbsOrigin() );
}
else
{
SetEndEntity( pEnd );
}
RelinkBeam();
SetWidth( MIN(MAX_BEAM_WIDTH, m_boltWidth) );
SetNoise( MIN(MAX_BEAM_NOISEAMPLITUDE, m_noiseAmplitude) );
SetFrame( m_frameStart );
SetScrollRate( m_speed );
if ( m_spawnflags & SF_BEAM_SHADEIN )
{
SetBeamFlags( FBEAM_SHADEIN );
}
else if ( m_spawnflags & SF_BEAM_SHADEOUT )
{
SetBeamFlags( FBEAM_SHADEOUT );
}
}

View File

@@ -0,0 +1,200 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements visual effects entities: sprites, beams, bubbles, etc.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "shake.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
class CEnvFade : public CLogicalEntity
{
private:
float m_Duration;
float m_HoldTime;
COutputEvent m_OnBeginFade;
DECLARE_DATADESC();
public:
DECLARE_CLASS( CEnvFade, CLogicalEntity );
virtual void Spawn( void );
inline float Duration( void ) { return m_Duration; }
inline float HoldTime( void ) { return m_HoldTime; }
inline void SetDuration( float duration ) { m_Duration = duration; }
inline void SetHoldTime( float hold ) { m_HoldTime = hold; }
int DrawDebugTextOverlays(void);
// Inputs
void InputFade( inputdata_t &inputdata );
};
LINK_ENTITY_TO_CLASS( env_fade, CEnvFade );
BEGIN_DATADESC( CEnvFade )
DEFINE_KEYFIELD( m_Duration, FIELD_FLOAT, "duration" ),
DEFINE_KEYFIELD( m_HoldTime, FIELD_FLOAT, "holdtime" ),
DEFINE_INPUTFUNC( FIELD_VOID, "Fade", InputFade ),
DEFINE_OUTPUT( m_OnBeginFade, "OnBeginFade"),
END_DATADESC()
#define SF_FADE_IN 0x0001 // Fade in, not out
#define SF_FADE_MODULATE 0x0002 // Modulate, don't blend
#define SF_FADE_ONLYONE 0x0004
#define SF_FADE_STAYOUT 0x0008
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvFade::Spawn( void )
{
}
//-----------------------------------------------------------------------------
// Purpose: Input handler that does the screen fade.
//-----------------------------------------------------------------------------
void CEnvFade::InputFade( inputdata_t &inputdata )
{
int fadeFlags = 0;
if ( m_spawnflags & SF_FADE_IN )
{
fadeFlags |= FFADE_IN;
}
else
{
fadeFlags |= FFADE_OUT;
}
if ( m_spawnflags & SF_FADE_MODULATE )
{
fadeFlags |= FFADE_MODULATE;
}
if ( m_spawnflags & SF_FADE_STAYOUT )
{
fadeFlags |= FFADE_STAYOUT;
}
if ( m_spawnflags & SF_FADE_ONLYONE )
{
if ( inputdata.pActivator->IsNetClient() )
{
UTIL_ScreenFade( inputdata.pActivator, m_clrRender, Duration(), HoldTime(), fadeFlags );
}
}
else
{
UTIL_ScreenFadeAll( m_clrRender, Duration(), HoldTime(), fadeFlags|FFADE_PURGE );
}
m_OnBeginFade.FireOutput( inputdata.pActivator, this );
}
//-----------------------------------------------------------------------------
// Purpose: Fetches the arguments from the command line for the fadein and fadeout
// console commands.
// Input : flTime - Returns the fade time in seconds (the time to fade in or out)
// clrFade - Returns the color to fade to or from.
//-----------------------------------------------------------------------------
static void GetFadeParms( const CCommand &args, float &flTime, color32 &clrFade)
{
flTime = 2.0f;
if ( args.ArgC() > 1 )
{
flTime = atof( args[1] );
}
clrFade.r = 0;
clrFade.g = 0;
clrFade.b = 0;
clrFade.a = 255;
if ( args.ArgC() > 4 )
{
clrFade.r = atoi( args[2] );
clrFade.g = atoi( args[3] );
clrFade.b = atoi( args[4] );
if ( args.ArgC() == 5 )
{
clrFade.a = atoi( args[5] );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Console command to fade out to a given color.
//-----------------------------------------------------------------------------
static void CC_FadeOut( const CCommand &args )
{
float flTime;
color32 clrFade;
GetFadeParms( args, flTime, clrFade );
CBasePlayer *pPlayer = UTIL_GetCommandClient();
UTIL_ScreenFade( pPlayer, clrFade, flTime, 0, FFADE_OUT | FFADE_PURGE | FFADE_STAYOUT );
}
static ConCommand fadeout("fadeout", CC_FadeOut, "fadeout {time r g b}: Fades the screen to black or to the specified color over the given number of seconds.", FCVAR_CHEAT );
//-----------------------------------------------------------------------------
// Purpose: Console command to fade in from a given color.
//-----------------------------------------------------------------------------
static void CC_FadeIn( const CCommand &args )
{
float flTime;
color32 clrFade;
GetFadeParms( args, flTime, clrFade );
CBasePlayer *pPlayer = UTIL_GetCommandClient();
UTIL_ScreenFade( pPlayer, clrFade, flTime, 0, FFADE_IN | FFADE_PURGE );
}
static ConCommand fadein("fadein", CC_FadeIn, "fadein {time r g b}: Fades the screen in from black or from the specified color over the given number of seconds.", FCVAR_CHEAT );
//-----------------------------------------------------------------------------
// Purpose: Draw any debug text overlays
// Output : Current text offset from the top
//-----------------------------------------------------------------------------
int CEnvFade::DrawDebugTextOverlays( void )
{
int text_offset = BaseClass::DrawDebugTextOverlays();
if (m_debugOverlays & OVERLAY_TEXT_BIT)
{
char tempstr[512];
// print duration
Q_snprintf(tempstr,sizeof(tempstr)," duration: %f", m_Duration);
EntityText(text_offset,tempstr,0);
text_offset++;
// print hold time
Q_snprintf(tempstr,sizeof(tempstr)," hold time: %f", m_HoldTime);
EntityText(text_offset,tempstr,0);
text_offset++;
}
return text_offset;
}

View File

@@ -0,0 +1,142 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements visual effects entities: sprites, beams, bubbles, etc.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "engine/IEngineSound.h"
#include "baseentity.h"
#include "entityoutput.h"
#include "recipientfilter.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define SF_HUDHINT_ALLPLAYERS 0x0001
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CEnvHudHint : public CPointEntity
{
public:
DECLARE_CLASS( CEnvHudHint, CPointEntity );
void Spawn( void );
void Precache( void );
private:
inline bool AllPlayers( void ) { return (m_spawnflags & SF_HUDHINT_ALLPLAYERS) != 0; }
void InputShowHudHint( inputdata_t &inputdata );
void InputHideHudHint( inputdata_t &inputdata );
string_t m_iszMessage;
DECLARE_DATADESC();
};
LINK_ENTITY_TO_CLASS( env_hudhint, CEnvHudHint );
BEGIN_DATADESC( CEnvHudHint )
DEFINE_KEYFIELD( m_iszMessage, FIELD_STRING, "message" ),
DEFINE_INPUTFUNC( FIELD_VOID, "ShowHudHint", InputShowHudHint ),
DEFINE_INPUTFUNC( FIELD_VOID, "HideHudHint", InputHideHudHint ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvHudHint::Spawn( void )
{
Precache();
SetSolid( SOLID_NONE );
SetMoveType( MOVETYPE_NONE );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvHudHint::Precache( void )
{
}
//-----------------------------------------------------------------------------
// Purpose: Input handler for showing the message and/or playing the sound.
//-----------------------------------------------------------------------------
void CEnvHudHint::InputShowHudHint( inputdata_t &inputdata )
{
if ( AllPlayers() )
{
CReliableBroadcastRecipientFilter user;
UserMessageBegin( user, "KeyHintText" );
WRITE_BYTE( 1 ); // one message
WRITE_STRING( STRING(m_iszMessage) );
MessageEnd();
}
else
{
CBaseEntity *pPlayer = NULL;
if ( inputdata.pActivator && inputdata.pActivator->IsPlayer() )
{
pPlayer = inputdata.pActivator;
}
else
{
pPlayer = UTIL_GetLocalPlayer();
}
if ( !pPlayer || !pPlayer->IsNetClient() )
return;
CSingleUserRecipientFilter user( (CBasePlayer *)pPlayer );
user.MakeReliable();
UserMessageBegin( user, "KeyHintText" );
WRITE_BYTE( 1 ); // one message
WRITE_STRING( STRING(m_iszMessage) );
MessageEnd();
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CEnvHudHint::InputHideHudHint( inputdata_t &inputdata )
{
if ( AllPlayers() )
{
CReliableBroadcastRecipientFilter user;
UserMessageBegin( user, "KeyHintText" );
WRITE_BYTE( 1 ); // one message
WRITE_STRING( STRING(NULL_STRING) );
MessageEnd();
}
else
{
CBaseEntity *pPlayer = NULL;
if ( inputdata.pActivator && inputdata.pActivator->IsPlayer() )
{
pPlayer = inputdata.pActivator;
}
else
{
pPlayer = UTIL_GetLocalPlayer();
}
if ( !pPlayer || !pPlayer->IsNetClient() )
return;
CSingleUserRecipientFilter user( (CBasePlayer *)pPlayer );
user.MakeReliable();
UserMessageBegin( user, "KeyHintText" );
WRITE_BYTE( 1 ); // one message
WRITE_STRING( STRING(NULL_STRING) );
MessageEnd();
}
}

View File

@@ -0,0 +1,250 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A special kind of beam effect that traces from its start position to
// its end position and stops if it hits anything.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EnvLaser.h"
#include "Sprite.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
LINK_ENTITY_TO_CLASS( env_laser, CEnvLaser );
BEGIN_DATADESC( CEnvLaser )
DEFINE_KEYFIELD( m_iszLaserTarget, FIELD_STRING, "LaserTarget" ),
DEFINE_FIELD( m_pSprite, FIELD_CLASSPTR ),
DEFINE_KEYFIELD( m_iszSpriteName, FIELD_STRING, "EndSprite" ),
DEFINE_FIELD( m_firePosition, FIELD_VECTOR ),
DEFINE_KEYFIELD( m_flStartFrame, FIELD_FLOAT, "framestart" ),
// Function Pointers
DEFINE_FUNCTION( StrikeThink ),
// Input functions
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvLaser::Spawn( void )
{
if ( !GetModelName() )
{
SetThink( &CEnvLaser::SUB_Remove );
return;
}
SetSolid( SOLID_NONE ); // Remove model & collisions
SetThink( &CEnvLaser::StrikeThink );
SetEndWidth( GetWidth() ); // Note: EndWidth is not scaled
PointsInit( GetLocalOrigin(), GetLocalOrigin() );
Precache( );
if ( !m_pSprite && m_iszSpriteName != NULL_STRING )
{
m_pSprite = CSprite::SpriteCreate( STRING(m_iszSpriteName), GetAbsOrigin(), TRUE );
}
else
{
m_pSprite = NULL;
}
if ( m_pSprite )
{
m_pSprite->SetParent( GetMoveParent() );
m_pSprite->SetTransparency( kRenderGlow, m_clrRender->r, m_clrRender->g, m_clrRender->b, m_clrRender->a, m_nRenderFX );
}
if ( GetEntityName() != NULL_STRING && !(m_spawnflags & SF_BEAM_STARTON) )
{
TurnOff();
}
else
{
TurnOn();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvLaser::Precache( void )
{
SetModelIndex( PrecacheModel( STRING( GetModelName() ) ) );
if ( m_iszSpriteName != NULL_STRING )
PrecacheModel( STRING(m_iszSpriteName) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEnvLaser::KeyValue( const char *szKeyName, const char *szValue )
{
if (FStrEq(szKeyName, "width"))
{
SetWidth( atof(szValue) );
}
else if (FStrEq(szKeyName, "NoiseAmplitude"))
{
SetNoise( atoi(szValue) );
}
else if (FStrEq(szKeyName, "TextureScroll"))
{
SetScrollRate( atoi(szValue) );
}
else if (FStrEq(szKeyName, "texture"))
{
SetModelName( AllocPooledString(szValue) );
}
else
{
BaseClass::KeyValue( szKeyName, szValue );
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Returns whether the laser is currently active.
//-----------------------------------------------------------------------------
int CEnvLaser::IsOn( void )
{
if ( IsEffectActive( EF_NODRAW ) )
return 0;
return 1;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvLaser::InputTurnOn( inputdata_t &inputdata )
{
if (!IsOn())
{
TurnOn();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvLaser::InputTurnOff( inputdata_t &inputdata )
{
if (IsOn())
{
TurnOff();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvLaser::InputToggle( inputdata_t &inputdata )
{
if ( IsOn() )
{
TurnOff();
}
else
{
TurnOn();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvLaser::TurnOff( void )
{
AddEffects( EF_NODRAW );
if ( m_pSprite )
m_pSprite->TurnOff();
SetNextThink( TICK_NEVER_THINK );
SetThink( NULL );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvLaser::TurnOn( void )
{
RemoveEffects( EF_NODRAW );
if ( m_pSprite )
m_pSprite->TurnOn();
m_flFireTime = gpGlobals->curtime;
SetThink( &CEnvLaser::StrikeThink );
//
// Call StrikeThink here to update the end position, otherwise we will see
// the beam in the wrong place for one frame since we cleared the nodraw flag.
//
StrikeThink();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvLaser::FireAtPoint( trace_t &tr )
{
SetAbsEndPos( tr.endpos );
if ( m_pSprite )
{
UTIL_SetOrigin( m_pSprite, tr.endpos );
}
// Apply damage and do sparks every 1/10th of a second.
if ( gpGlobals->curtime >= m_flFireTime + 0.1 )
{
BeamDamage( &tr );
DoSparks( GetAbsStartPos(), tr.endpos );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvLaser::StrikeThink( void )
{
CBaseEntity *pEnd = RandomTargetname( STRING( m_iszLaserTarget ) );
Vector vecFireAt = GetAbsEndPos();
if ( pEnd )
{
vecFireAt = pEnd->GetAbsOrigin();
}
trace_t tr;
UTIL_TraceLine( GetAbsOrigin(), vecFireAt, MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
FireAtPoint( tr );
SetNextThink( gpGlobals->curtime );
}

View File

@@ -0,0 +1,51 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ENVLASER_H
#define ENVLASER_H
#ifdef _WIN32
#pragma once
#endif
#include "baseentity.h"
#include "beam_shared.h"
#include "entityoutput.h"
class CSprite;
class CEnvLaser : public CBeam
{
DECLARE_CLASS( CEnvLaser, CBeam );
public:
void Spawn( void );
void Precache( void );
bool KeyValue( const char *szKeyName, const char *szValue );
void TurnOn( void );
void TurnOff( void );
int IsOn( void );
void FireAtPoint( trace_t &point );
void StrikeThink( void );
void InputTurnOn( inputdata_t &inputdata );
void InputTurnOff( inputdata_t &inputdata );
void InputToggle( inputdata_t &inputdata );
DECLARE_DATADESC();
string_t m_iszLaserTarget; // Name of entity or entities to strike at, randomly picked if more than one match.
CSprite *m_pSprite;
string_t m_iszSpriteName;
Vector m_firePosition;
float m_flStartFrame;
};
#endif // ENVLASER_H

View File

@@ -0,0 +1,278 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements visual effects entities: sprites, beams, bubbles, etc.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EnvMessage.h"
#include "engine/IEngineSound.h"
#include "KeyValues.h"
#include "filesystem.h"
#include "Color.h"
#include "gamestats.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
LINK_ENTITY_TO_CLASS( env_message, CMessage );
BEGIN_DATADESC( CMessage )
DEFINE_KEYFIELD( m_iszMessage, FIELD_STRING, "message" ),
DEFINE_KEYFIELD( m_sNoise, FIELD_SOUNDNAME, "messagesound" ),
DEFINE_KEYFIELD( m_MessageAttenuation, FIELD_INTEGER, "messageattenuation" ),
DEFINE_KEYFIELD( m_MessageVolume, FIELD_FLOAT, "messagevolume" ),
DEFINE_FIELD( m_Radius, FIELD_FLOAT ),
DEFINE_INPUTFUNC( FIELD_VOID, "ShowMessage", InputShowMessage ),
DEFINE_OUTPUT(m_OnShowMessage, "OnShowMessage"),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMessage::Spawn( void )
{
Precache();
SetSolid( SOLID_NONE );
SetMoveType( MOVETYPE_NONE );
switch( m_MessageAttenuation )
{
case 1: // Medium radius
m_Radius = ATTN_STATIC;
break;
case 2: // Large radius
m_Radius = ATTN_NORM;
break;
case 3: //EVERYWHERE
m_Radius = ATTN_NONE;
break;
default:
case 0: // Small radius
m_Radius = SNDLVL_IDLE;
break;
}
m_MessageAttenuation = 0;
// Remap volume from [0,10] to [0,1].
m_MessageVolume *= 0.1;
// No volume, use normal
if ( m_MessageVolume <= 0 )
{
m_MessageVolume = 1.0;
}
}
void CMessage::Precache( void )
{
if ( m_sNoise != NULL_STRING )
{
PrecacheScriptSound( STRING(m_sNoise) );
}
}
//-----------------------------------------------------------------------------
// Purpose: Input handler for showing the message and/or playing the sound.
//-----------------------------------------------------------------------------
void CMessage::InputShowMessage( inputdata_t &inputdata )
{
CBaseEntity *pPlayer = NULL;
if ( m_spawnflags & SF_MESSAGE_ALL )
{
UTIL_ShowMessageAll( STRING( m_iszMessage ) );
}
else
{
if ( inputdata.pActivator && inputdata.pActivator->IsPlayer() )
{
pPlayer = inputdata.pActivator;
}
else
{
pPlayer = (gpGlobals->maxClients > 1) ? NULL : UTIL_GetLocalPlayer();
}
if ( pPlayer && pPlayer->IsPlayer() )
{
UTIL_ShowMessage( STRING( m_iszMessage ), ToBasePlayer( pPlayer ) );
}
}
if ( m_sNoise != NULL_STRING )
{
CPASAttenuationFilter filter( this );
EmitSound_t ep;
ep.m_nChannel = CHAN_BODY;
ep.m_pSoundName = (char*)STRING(m_sNoise);
ep.m_flVolume = m_MessageVolume;
ep.m_SoundLevel = ATTN_TO_SNDLVL( m_Radius );
EmitSound( filter, entindex(), ep );
}
if ( m_spawnflags & SF_MESSAGE_ONCE )
{
UTIL_Remove( this );
}
m_OnShowMessage.FireOutput( inputdata.pActivator, this );
}
void CMessage::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
inputdata_t inputdata;
inputdata.pActivator = NULL;
inputdata.pCaller = NULL;
InputShowMessage( inputdata );
}
class CCredits : public CPointEntity
{
public:
DECLARE_CLASS( CMessage, CPointEntity );
DECLARE_DATADESC();
void Spawn( void );
void InputRollCredits( inputdata_t &inputdata );
void InputRollOutroCredits( inputdata_t &inputdata );
void InputShowLogo( inputdata_t &inputdata );
void InputSetLogoLength( inputdata_t &inputdata );
COutputEvent m_OnCreditsDone;
virtual void OnRestore();
private:
void RollOutroCredits();
bool m_bRolledOutroCredits;
float m_flLogoLength;
};
LINK_ENTITY_TO_CLASS( env_credits, CCredits );
BEGIN_DATADESC( CCredits )
DEFINE_INPUTFUNC( FIELD_VOID, "RollCredits", InputRollCredits ),
DEFINE_INPUTFUNC( FIELD_VOID, "RollOutroCredits", InputRollOutroCredits ),
DEFINE_INPUTFUNC( FIELD_VOID, "ShowLogo", InputShowLogo ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetLogoLength", InputSetLogoLength ),
DEFINE_OUTPUT( m_OnCreditsDone, "OnCreditsDone"),
DEFINE_FIELD( m_bRolledOutroCredits, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flLogoLength, FIELD_FLOAT )
END_DATADESC()
void CCredits::Spawn( void )
{
SetSolid( SOLID_NONE );
SetMoveType( MOVETYPE_NONE );
}
static void CreditsDone_f( void )
{
CCredits *pCredits = (CCredits*)gEntList.FindEntityByClassname( NULL, "env_credits" );
if ( pCredits )
{
pCredits->m_OnCreditsDone.FireOutput( pCredits, pCredits );
}
}
static ConCommand creditsdone("creditsdone", CreditsDone_f );
extern ConVar sv_unlockedchapters;
void CCredits::OnRestore()
{
BaseClass::OnRestore();
if ( m_bRolledOutroCredits )
{
// Roll them again so that the client .dll will send the "creditsdone" message and we'll
// actually get back to the main menu
RollOutroCredits();
}
}
void CCredits::RollOutroCredits()
{
sv_unlockedchapters.SetValue( "15" );
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
CSingleUserRecipientFilter user( pPlayer );
user.MakeReliable();
UserMessageBegin( user, "CreditsMsg" );
WRITE_BYTE( 3 );
MessageEnd();
}
void CCredits::InputRollOutroCredits( inputdata_t &inputdata )
{
RollOutroCredits();
// In case we save restore
m_bRolledOutroCredits = true;
gamestats->Event_Credits();
}
void CCredits::InputShowLogo( inputdata_t &inputdata )
{
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
CSingleUserRecipientFilter user( pPlayer );
user.MakeReliable();
if ( m_flLogoLength )
{
UserMessageBegin( user, "LogoTimeMsg" );
WRITE_FLOAT( m_flLogoLength );
MessageEnd();
}
else
{
UserMessageBegin( user, "CreditsMsg" );
WRITE_BYTE( 1 );
MessageEnd();
}
}
void CCredits::InputSetLogoLength( inputdata_t &inputdata )
{
m_flLogoLength = inputdata.value.Float();
}
void CCredits::InputRollCredits( inputdata_t &inputdata )
{
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
CSingleUserRecipientFilter user( pPlayer );
user.MakeReliable();
UserMessageBegin( user, "CreditsMsg" );
WRITE_BYTE( 2 );
MessageEnd();
}

View File

@@ -0,0 +1,48 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ENVMESSAGE_H
#define ENVMESSAGE_H
#ifdef _WIN32
#pragma once
#endif
#include "baseentity.h"
#include "entityoutput.h"
#define SF_MESSAGE_ONCE 0x0001 // Fade in, not out
#define SF_MESSAGE_ALL 0x0002 // Send to all clients
class CMessage : public CPointEntity
{
public:
DECLARE_CLASS( CMessage, CPointEntity );
void Spawn( void );
void Precache( void );
inline void SetMessage( string_t iszMessage ) { m_iszMessage = iszMessage; }
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
private:
void InputShowMessage( inputdata_t &inputdata );
string_t m_iszMessage; // Message to display.
float m_MessageVolume;
int m_MessageAttenuation;
float m_Radius;
DECLARE_DATADESC();
string_t m_sNoise;
COutputEvent m_OnShowMessage;
};
#endif // ENVMESSAGE_H

View File

@@ -0,0 +1,413 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements a screen shake effect that can also shake physics objects.
//
// NOTE: UTIL_ScreenShake() will only shake players who are on the ground
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "shake.h"
#include "physics_saverestore.h"
#include "rope.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
class CPhysicsShake : public IMotionEvent
{
DECLARE_SIMPLE_DATADESC();
public:
virtual simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
{
Vector contact;
if ( !pObject->GetContactPoint( &contact, NULL ) )
return SIM_NOTHING;
// fudge the force a bit to make it more dramatic
pObject->CalculateForceOffset( m_force * (1.0f + pObject->GetMass()*0.4f), contact, &linear, &angular );
return SIM_LOCAL_FORCE;
}
Vector m_force;
};
BEGIN_SIMPLE_DATADESC( CPhysicsShake )
DEFINE_FIELD( m_force, FIELD_VECTOR ),
END_DATADESC()
class CEnvShake : public CPointEntity
{
private:
float m_Amplitude;
float m_Frequency;
float m_Duration;
float m_Radius; // radius of 0 means all players
float m_stopTime;
float m_nextShake;
float m_currentAmp;
Vector m_maxForce;
IPhysicsMotionController *m_pShakeController;
CPhysicsShake m_shakeCallback;
DECLARE_DATADESC();
public:
DECLARE_CLASS( CEnvShake, CPointEntity );
~CEnvShake( void );
virtual void Spawn( void );
virtual void OnRestore( void );
inline float Amplitude( void ) { return m_Amplitude; }
inline float Frequency( void ) { return m_Frequency; }
inline float Duration( void ) { return m_Duration; }
float Radius( bool bPlayers = true );
inline void SetAmplitude( float amplitude ) { m_Amplitude = amplitude; }
inline void SetFrequency( float frequency ) { m_Frequency = frequency; }
inline void SetDuration( float duration ) { m_Duration = duration; }
inline void SetRadius( float radius ) { m_Radius = radius; }
int DrawDebugTextOverlays(void);
// Input handlers
void InputStartShake( inputdata_t &inputdata );
void InputStopShake( inputdata_t &inputdata );
void InputAmplitude( inputdata_t &inputdata );
void InputFrequency( inputdata_t &inputdata );
// Causes the camera/physics shakes to happen:
void ApplyShake( ShakeCommand_t command );
void Think( void );
};
LINK_ENTITY_TO_CLASS( env_shake, CEnvShake );
BEGIN_DATADESC( CEnvShake )
DEFINE_KEYFIELD( m_Amplitude, FIELD_FLOAT, "amplitude" ),
DEFINE_KEYFIELD( m_Frequency, FIELD_FLOAT, "frequency" ),
DEFINE_KEYFIELD( m_Duration, FIELD_FLOAT, "duration" ),
DEFINE_KEYFIELD( m_Radius, FIELD_FLOAT, "radius" ),
DEFINE_FIELD( m_stopTime, FIELD_TIME ),
DEFINE_FIELD( m_nextShake, FIELD_TIME ),
DEFINE_FIELD( m_currentAmp, FIELD_FLOAT ),
DEFINE_FIELD( m_maxForce, FIELD_VECTOR ),
DEFINE_PHYSPTR( m_pShakeController ),
DEFINE_EMBEDDED( m_shakeCallback ),
DEFINE_INPUTFUNC( FIELD_VOID, "StartShake", InputStartShake ),
DEFINE_INPUTFUNC( FIELD_VOID, "StopShake", InputStopShake ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "Amplitude", InputAmplitude ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "Frequency", InputFrequency ),
END_DATADESC()
#define SF_SHAKE_EVERYONE 0x0001 // Don't check radius
#define SF_SHAKE_INAIR 0x0004 // Shake players in air
#define SF_SHAKE_PHYSICS 0x0008 // Shake physically (not just camera)
#define SF_SHAKE_ROPES 0x0010 // Shake ropes too.
#define SF_SHAKE_NO_VIEW 0x0020 // DON'T shake the view (only ropes and/or physics objects)
#define SF_SHAKE_NO_RUMBLE 0x0040 // DON'T Rumble the XBox Controller
//-----------------------------------------------------------------------------
// Purpose: Destructor.
//-----------------------------------------------------------------------------
CEnvShake::~CEnvShake( void )
{
if ( m_pShakeController )
{
physenv->DestroyMotionController( m_pShakeController );
}
}
float CEnvShake::Radius(bool bPlayers)
{
// The radius for players is zero if SF_SHAKE_EVERYONE is set
if ( bPlayers && HasSpawnFlags(SF_SHAKE_EVERYONE))
return 0;
return m_Radius;
}
//-----------------------------------------------------------------------------
// Purpose: Sets default member values when spawning.
//-----------------------------------------------------------------------------
void CEnvShake::Spawn( void )
{
SetSolid( SOLID_NONE );
SetMoveType( MOVETYPE_NONE );
if ( GetSpawnFlags() & SF_SHAKE_EVERYONE )
{
m_Radius = 0;
}
if ( HasSpawnFlags( SF_SHAKE_NO_VIEW ) && !HasSpawnFlags( SF_SHAKE_PHYSICS ) && !HasSpawnFlags( SF_SHAKE_ROPES ) )
{
DevWarning( "env_shake %s with \"Don't shake view\" spawnflag set without \"Shake physics\" or \"Shake ropes\" spawnflags set.", GetDebugName() );
}
}
//-----------------------------------------------------------------------------
// Purpose: Restore the motion controller
//-----------------------------------------------------------------------------
void CEnvShake::OnRestore( void )
{
BaseClass::OnRestore();
if ( m_pShakeController )
{
m_pShakeController->SetEventHandler( &m_shakeCallback );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvShake::ApplyShake( ShakeCommand_t command )
{
if ( !HasSpawnFlags( SF_SHAKE_NO_VIEW ) || !HasSpawnFlags( SF_SHAKE_NO_RUMBLE ) )
{
bool air = (GetSpawnFlags() & SF_SHAKE_INAIR) ? true : false;
UTIL_ScreenShake( GetAbsOrigin(), Amplitude(), Frequency(), Duration(), Radius(), command, air );
}
if ( GetSpawnFlags() & SF_SHAKE_ROPES )
{
CRopeKeyframe::ShakeRopes( GetAbsOrigin(), Radius(false), Frequency() );
}
if ( GetSpawnFlags() & SF_SHAKE_PHYSICS )
{
if ( !m_pShakeController )
{
m_pShakeController = physenv->CreateMotionController( &m_shakeCallback );
}
// do physics shake
switch( command )
{
case SHAKE_START:
case SHAKE_START_NORUMBLE:
case SHAKE_START_RUMBLEONLY:
{
m_stopTime = gpGlobals->curtime + Duration();
m_nextShake = 0;
m_pShakeController->ClearObjects();
SetNextThink( gpGlobals->curtime );
m_currentAmp = Amplitude();
CBaseEntity *list[1024];
float radius = Radius(false);
// probably checked "Shake Everywhere" do a big radius
if ( !radius )
{
radius = 512;
}
Vector extents = Vector(radius, radius, radius);
extents.z = MAX(extents.z, 100);
Vector mins = GetAbsOrigin() - extents;
Vector maxs = GetAbsOrigin() + extents;
int count = UTIL_EntitiesInBox( list, 1024, mins, maxs, 0 );
for ( int i = 0; i < count; i++ )
{
//
// Only shake physics entities that players can see. This is one frame out of date
// so it's possible that we could miss objects if a player changed PVS this frame.
//
if ( ( list[i]->GetMoveType() == MOVETYPE_VPHYSICS ) )
{
IPhysicsObject *pPhys = list[i]->VPhysicsGetObject();
if ( pPhys && pPhys->IsMoveable() )
{
m_pShakeController->AttachObject( pPhys, false );
pPhys->Wake();
}
}
}
}
break;
case SHAKE_STOP:
m_pShakeController->ClearObjects();
break;
case SHAKE_AMPLITUDE:
m_currentAmp = Amplitude();
case SHAKE_FREQUENCY:
m_pShakeController->WakeObjects();
break;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Input handler that starts the screen shake.
//-----------------------------------------------------------------------------
void CEnvShake::InputStartShake( inputdata_t &inputdata )
{
if ( HasSpawnFlags( SF_SHAKE_NO_RUMBLE ) )
{
ApplyShake( SHAKE_START_NORUMBLE );
}
else if ( HasSpawnFlags( SF_SHAKE_NO_VIEW ) )
{
ApplyShake( SHAKE_START_RUMBLEONLY );
}
else
{
ApplyShake( SHAKE_START );
}
}
//-----------------------------------------------------------------------------
// Purpose: Input handler that stops the screen shake.
//-----------------------------------------------------------------------------
void CEnvShake::InputStopShake( inputdata_t &inputdata )
{
ApplyShake( SHAKE_STOP );
}
//-----------------------------------------------------------------------------
// Purpose: Handles changes to the shake amplitude from an external source.
//-----------------------------------------------------------------------------
void CEnvShake::InputAmplitude( inputdata_t &inputdata )
{
SetAmplitude( inputdata.value.Float() );
ApplyShake( SHAKE_AMPLITUDE );
}
//-----------------------------------------------------------------------------
// Purpose: Handles changes to the shake frequency from an external source.
//-----------------------------------------------------------------------------
void CEnvShake::InputFrequency( inputdata_t &inputdata )
{
SetFrequency( inputdata.value.Float() );
ApplyShake( SHAKE_FREQUENCY );
}
//-----------------------------------------------------------------------------
// Purpose: Calculates the physics shake values
//-----------------------------------------------------------------------------
void CEnvShake::Think( void )
{
int i;
if ( gpGlobals->curtime > m_nextShake )
{
// Higher frequency means we recalc the extents more often and perturb the display again
m_nextShake = gpGlobals->curtime + (1.0f / Frequency());
// Compute random shake extents (the shake will settle down from this)
for (i = 0; i < 2; i++ )
{
m_maxForce[i] = random->RandomFloat( -1, 1 );
}
// make the force it point mostly up
m_maxForce.z = 4;
VectorNormalize( m_maxForce );
m_maxForce *= m_currentAmp * 400; // amplitude is the acceleration of a 100kg object
}
float fraction = ( m_stopTime - gpGlobals->curtime ) / Duration();
if ( fraction < 0 )
{
m_pShakeController->ClearObjects();
return;
}
float freq = 0;
// Ramp up frequency over duration
if ( fraction )
{
freq = (Frequency() / fraction);
}
// square fraction to approach zero more quickly
fraction *= fraction;
// Sine wave that slowly settles to zero
fraction = fraction * sin( gpGlobals->curtime * freq );
// Add to view origin
for ( i = 0; i < 3; i++ )
{
// store the force in the controller callback
m_shakeCallback.m_force[i] = m_maxForce[i] * fraction;
}
// Drop amplitude a bit, less for higher frequency shakes
m_currentAmp -= m_currentAmp * ( gpGlobals->frametime / (Duration() * Frequency()) );
SetNextThink( gpGlobals->curtime );
}
//------------------------------------------------------------------------------
// Purpose: Console command to cause a screen shake.
//------------------------------------------------------------------------------
void CC_Shake( void )
{
CBasePlayer *pPlayer = UTIL_GetCommandClient();
if (pPlayer)
{
UTIL_ScreenShake( pPlayer->WorldSpaceCenter(), 25.0, 150.0, 1.0, 750, SHAKE_START );
}
}
//-----------------------------------------------------------------------------
// Purpose: Draw any debug text overlays
// Returns current text offset from the top
//-----------------------------------------------------------------------------
int CEnvShake::DrawDebugTextOverlays( void )
{
int text_offset = BaseClass::DrawDebugTextOverlays();
if (m_debugOverlays & OVERLAY_TEXT_BIT)
{
char tempstr[512];
// print amplitude
Q_snprintf(tempstr,sizeof(tempstr)," magnitude: %f", m_Amplitude);
EntityText(text_offset,tempstr,0);
text_offset++;
// print frequency
Q_snprintf(tempstr,sizeof(tempstr)," frequency: %f", m_Frequency);
EntityText(text_offset,tempstr,0);
text_offset++;
// print duration
Q_snprintf(tempstr,sizeof(tempstr)," duration: %f", m_Duration);
EntityText(text_offset,tempstr,0);
text_offset++;
// print radius
Q_snprintf(tempstr,sizeof(tempstr)," radius: %f", m_Radius);
EntityText(text_offset,tempstr,0);
text_offset++;
}
return text_offset;
}
static ConCommand shake("shake", CC_Shake, "Shake the screen.", FCVAR_CHEAT );

View File

@@ -0,0 +1,195 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A point entity that periodically emits sparks and "bzzt" sounds.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "IEffects.h"
#include "engine/IEngineSound.h"
#include "envspark.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Purpose: Emits sparks from the given location and plays a random spark sound.
// Input : pev -
// location -
//-----------------------------------------------------------------------------
void DoSpark( CBaseEntity *ent, const Vector &location, int nMagnitude, int nTrailLength, bool bPlaySound, const Vector &vecDir )
{
g_pEffects->Sparks( location, nMagnitude, nTrailLength, &vecDir );
if ( bPlaySound )
{
ent->EmitSound( "DoSpark" );
}
}
const int SF_SPARK_START_ON = 64;
const int SF_SPARK_GLOW = 128;
const int SF_SPARK_SILENT = 256;
const int SF_SPARK_DIRECTIONAL = 512;
BEGIN_DATADESC( CEnvSpark )
DEFINE_KEYFIELD( m_flDelay, FIELD_FLOAT, "MaxDelay" ),
DEFINE_FIELD( m_nGlowSpriteIndex, FIELD_INTEGER ),
DEFINE_KEYFIELD( m_nMagnitude, FIELD_INTEGER, "Magnitude" ),
DEFINE_KEYFIELD( m_nTrailLength, FIELD_INTEGER, "TrailLength" ),
// Function Pointers
DEFINE_FUNCTION( SparkThink ),
DEFINE_INPUTFUNC( FIELD_VOID, "StartSpark", InputStartSpark ),
DEFINE_INPUTFUNC( FIELD_VOID, "StopSpark", InputStopSpark ),
DEFINE_INPUTFUNC( FIELD_VOID, "ToggleSpark", InputToggleSpark ),
DEFINE_INPUTFUNC( FIELD_VOID, "SparkOnce", InputSparkOnce ),
DEFINE_OUTPUT( m_OnSpark, "OnSpark" ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( env_spark, CEnvSpark );
//-----------------------------------------------------------------------------
// Purpose: Constructor! Exciting, isn't it?
//-----------------------------------------------------------------------------
CEnvSpark::CEnvSpark( void )
{
m_nMagnitude = 1;
m_nTrailLength = 1;
}
//-----------------------------------------------------------------------------
// Purpose: Called when spawning, after keyvalues have been handled.
//-----------------------------------------------------------------------------
void CEnvSpark::Spawn(void)
{
SetThink( NULL );
SetUse( NULL );
if ( FBitSet(m_spawnflags, SF_SPARK_START_ON ) )
{
SetThink( &CEnvSpark::SparkThink ); // start sparking
}
SetNextThink( gpGlobals->curtime + 0.1 + random->RandomFloat( 0, 1.5 ) );
// Negative delays are not allowed
if ( m_flDelay < 0 )
{
m_flDelay = 0;
}
#ifdef HL1_DLL
// Don't allow 0 delays in HL1 Port. Enforce a default
if( m_flDelay == 0 )
{
m_flDelay = 1.0f;
}
#endif//HL1_DLL
Precache( );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvSpark::Precache(void)
{
m_nGlowSpriteIndex = PrecacheModel( "sprites/glow01.vmt" );
PrecacheScriptSound( "DoSpark" );
}
extern ConVar phys_pushscale;
//-----------------------------------------------------------------------------
// Purpose: Emits sparks at random intervals.
//-----------------------------------------------------------------------------
void CEnvSpark::SparkThink(void)
{
SetNextThink( gpGlobals->curtime + 0.1 + random->RandomFloat(0, m_flDelay) );
Vector vecDir = vec3_origin;
if ( FBitSet( m_spawnflags, SF_SPARK_DIRECTIONAL ) )
{
AngleVectors( GetAbsAngles(), &vecDir );
}
DoSpark( this, WorldSpaceCenter(), m_nMagnitude, m_nTrailLength, !( m_spawnflags & SF_SPARK_SILENT ), vecDir );
m_OnSpark.FireOutput( this, this );
if (FBitSet(m_spawnflags, SF_SPARK_GLOW))
{
CPVSFilter filter( GetAbsOrigin() );
te->GlowSprite( filter, 0.0, &GetAbsOrigin(), m_nGlowSpriteIndex, 0.2, 1.5, 25 );
}
}
//-----------------------------------------------------------------------------
// Purpose: Input handler for starting the sparks.
//-----------------------------------------------------------------------------
void CEnvSpark::InputStartSpark( inputdata_t &inputdata )
{
StartSpark();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvSpark::StartSpark( void )
{
SetThink( &CEnvSpark::SparkThink );
SetNextThink( gpGlobals->curtime );
}
//-----------------------------------------------------------------------------
// Purpose: Shoot one spark.
//-----------------------------------------------------------------------------
void CEnvSpark::InputSparkOnce( inputdata_t &inputdata )
{
SparkThink();
SetNextThink( TICK_NEVER_THINK );
}
//-----------------------------------------------------------------------------
// Purpose: Input handler for starting the sparks.
//-----------------------------------------------------------------------------
void CEnvSpark::InputStopSpark( inputdata_t &inputdata )
{
StopSpark();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnvSpark::StopSpark( void )
{
SetThink( NULL );
}
//-----------------------------------------------------------------------------
// Purpose: Input handler for toggling the on/off state of the sparks.
//-----------------------------------------------------------------------------
void CEnvSpark::InputToggleSpark( inputdata_t &inputdata )
{
if ( GetNextThink() == TICK_NEVER_THINK )
{
InputStartSpark( inputdata );
}
else
{
InputStopSpark( inputdata );
}
}

View File

@@ -0,0 +1,257 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "cbase.h"
#include "EventLog.h"
#include "team.h"
#include "KeyValues.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
CEventLog::CEventLog()
{
}
CEventLog::~CEventLog()
{
}
void CEventLog::FireGameEvent( IGameEvent *event )
{
PrintEvent ( event );
}
bool CEventLog::PrintEvent( IGameEvent *event )
{
const char * name = event->GetName();
if ( Q_strncmp(name, "server_", strlen("server_")) == 0 )
{
return true; // we don't care about server events (engine does)
}
else if ( Q_strncmp(name, "player_", strlen("player_")) == 0 )
{
return PrintPlayerEvent( event );
}
else if ( Q_strncmp(name, "team_", strlen("team_")) == 0 )
{
return PrintTeamEvent( event );
}
else if ( Q_strncmp(name, "game_", strlen("game_")) == 0 )
{
return PrintGameEvent( event );
}
else
{
return PrintOtherEvent( event ); // bomb_, round_, et al
}
}
bool CEventLog::PrintGameEvent( IGameEvent *event )
{
// const char * name = event->GetName() + Q_strlen("game_"); // remove prefix
return false;
}
bool CEventLog::PrintPlayerEvent( IGameEvent *event )
{
const char * eventName = event->GetName();
const int userid = event->GetInt( "userid" );
if ( !Q_strncmp( eventName, "player_connect", Q_strlen("player_connect") ) ) // player connect is before the CBasePlayer pointer is setup
{
const char *name = event->GetString( "name" );
const char *address = event->GetString( "address" );
const char *networkid = event->GetString("networkid" );
UTIL_LogPrintf( "\"%s<%i><%s><>\" connected, address \"%s\"\n", name, userid, networkid, address);
return true;
}
else if ( !Q_strncmp( eventName, "player_disconnect", Q_strlen("player_disconnect") ) )
{
const char *reason = event->GetString("reason" );
const char *name = event->GetString("name" );
const char *networkid = event->GetString("networkid" );
CTeam *team = NULL;
CBasePlayer *pPlayer = UTIL_PlayerByUserId( userid );
if ( pPlayer )
{
team = pPlayer->GetTeam();
}
UTIL_LogPrintf( "\"%s<%i><%s><%s>\" disconnected (reason \"%s\")\n", name, userid, networkid, team ? team->GetName() : "", reason );
return true;
}
CBasePlayer *pPlayer = UTIL_PlayerByUserId( userid );
if ( !pPlayer)
{
DevMsg( "CEventLog::PrintPlayerEvent: Failed to find player (userid: %i, event: %s)\n", userid, eventName );
return false;
}
if ( !Q_strncmp( eventName, "player_team", Q_strlen("player_team") ) )
{
const bool bDisconnecting = event->GetBool( "disconnect" );
if ( !bDisconnecting )
{
const int newTeam = event->GetInt( "team" );
const int oldTeam = event->GetInt( "oldteam" );
CTeam *team = GetGlobalTeam( newTeam );
CTeam *oldteam = GetGlobalTeam( oldTeam );
UTIL_LogPrintf( "\"%s<%i><%s><%s>\" joined team \"%s\"\n",
pPlayer->GetPlayerName(),
pPlayer->GetUserID(),
pPlayer->GetNetworkIDString(),
oldteam->GetName(),
team->GetName() );
}
return true;
}
else if ( !Q_strncmp( eventName, "player_death", Q_strlen("player_death") ) )
{
const int attackerid = event->GetInt("attacker" );
#ifdef HL2MP
const char *weapon = event->GetString( "weapon" );
#endif
CBasePlayer *pAttacker = UTIL_PlayerByUserId( attackerid );
CTeam *team = pPlayer->GetTeam();
CTeam *attackerTeam = NULL;
if ( pAttacker )
{
attackerTeam = pAttacker->GetTeam();
}
if ( pPlayer == pAttacker && pPlayer )
{
#ifdef HL2MP
UTIL_LogPrintf( "\"%s<%i><%s><%s>\" committed suicide with \"%s\"\n",
pPlayer->GetPlayerName(),
userid,
pPlayer->GetNetworkIDString(),
team ? team->GetName() : "",
weapon
);
#else
UTIL_LogPrintf( "\"%s<%i><%s><%s>\" committed suicide with \"%s\"\n",
pPlayer->GetPlayerName(),
userid,
pPlayer->GetNetworkIDString(),
team ? team->GetName() : "",
pAttacker->GetClassname()
);
#endif
}
else if ( pAttacker )
{
CTeam *attackerTeam = pAttacker->GetTeam();
#ifdef HL2MP
UTIL_LogPrintf( "\"%s<%i><%s><%s>\" killed \"%s<%i><%s><%s>\" with \"%s\"\n",
pAttacker->GetPlayerName(),
attackerid,
pAttacker->GetNetworkIDString(),
attackerTeam ? attackerTeam->GetName() : "",
pPlayer->GetPlayerName(),
userid,
pPlayer->GetNetworkIDString(),
team ? team->GetName() : "",
weapon
);
#else
UTIL_LogPrintf( "\"%s<%i><%s><%s>\" killed \"%s<%i><%s><%s>\"\n",
pAttacker->GetPlayerName(),
attackerid,
pAttacker->GetNetworkIDString(),
attackerTeam ? attackerTeam->GetName() : "",
pPlayer->GetPlayerName(),
userid,
pPlayer->GetNetworkIDString(),
team ? team->GetName() : ""
);
#endif
}
else
{
// killed by the world
UTIL_LogPrintf( "\"%s<%i><%s><%s>\" committed suicide with \"world\"\n",
pPlayer->GetPlayerName(),
userid,
pPlayer->GetNetworkIDString(),
team ? team->GetName() : ""
);
}
return true;
}
else if ( !Q_strncmp( eventName, "player_activate", Q_strlen("player_activate") ) )
{
UTIL_LogPrintf( "\"%s<%i><%s><>\" entered the game\n",
pPlayer->GetPlayerName(),
userid,
pPlayer->GetNetworkIDString()
);
return true;
}
else if ( !Q_strncmp( eventName, "player_changename", Q_strlen("player_changename") ) )
{
const char *newName = event->GetString( "newname" );
const char *oldName = event->GetString( "oldname" );
CTeam *team = pPlayer->GetTeam();
UTIL_LogPrintf( "\"%s<%i><%s><%s>\" changed name to \"%s\"\n",
oldName,
userid,
pPlayer->GetNetworkIDString(),
team ? team->GetName() : "",
newName
);
return true;
}
// ignored events
//player_hurt
return false;
}
bool CEventLog::PrintTeamEvent( IGameEvent *event )
{
// const char * name = event->GetName() + Q_strlen("team_"); // remove prefix
return false;
}
bool CEventLog::PrintOtherEvent( IGameEvent *event )
{
return false;
}
bool CEventLog::Init()
{
ListenForGameEvent( "player_changename" );
ListenForGameEvent( "player_activate" );
ListenForGameEvent( "player_death" );
ListenForGameEvent( "player_team" );
ListenForGameEvent( "player_disconnect" );
ListenForGameEvent( "player_connect" );
return true;
}
void CEventLog::Shutdown()
{
StopListeningForAllEvents();
}

View File

@@ -0,0 +1,46 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#if !defined EVENTLOG_H
#define EVENTLOG_H
#ifdef _WIN32
#pragma once
#endif
#include "GameEventListener.h"
#include <igamesystem.h>
class CEventLog : public CGameEventListener, public CBaseGameSystem
{
public:
CEventLog();
virtual ~CEventLog();
public: // IGameEventListener Interface
virtual void FireGameEvent( IGameEvent * event );
public: // CBaseGameSystem overrides
virtual bool Init();
virtual void Shutdown();
protected:
virtual bool PrintEvent( IGameEvent * event );
virtual bool PrintGameEvent( IGameEvent * event );
virtual bool PrintPlayerEvent( IGameEvent * event );
virtual bool PrintTeamEvent( IGameEvent * event );
virtual bool PrintOtherEvent( IGameEvent * event );
};
extern IGameSystem* GameLogSystem();
#endif // EVENTLOG_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,183 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "cbase.h"
#include "gamestats.h"
void BasicGameStatsRecord_t::Clear()
{
m_nCount = 0;
m_nSeconds = 0;
m_nCommentary = 0;
m_nHDR = 0;
m_nCaptions = 0;
m_bSteam = true;
m_bCyberCafe = false;
Q_memset( m_nSkill, 0, sizeof( m_nSkill ) );
m_nDeaths = 0;
}
void BasicGameStatsRecord_t::SaveToBuffer( CUtlBuffer &buf )
{
buf.PutInt( m_nCount );
buf.PutInt( m_nSeconds );
buf.PutInt( m_nCommentary );
buf.PutInt( m_nHDR );
buf.PutInt( m_nCaptions );
for ( int i = 0; i < 3; ++i )
{
buf.PutInt( m_nSkill[ i ] );
}
buf.PutChar( m_bSteam ? 1 : 0 );
buf.PutChar( m_bCyberCafe ? 1 : 0 );
buf.PutInt( m_nDeaths );
}
bool BasicGameStatsRecord_t::ParseFromBuffer( CUtlBuffer &buf, int iBufferStatsVersion )
{
bool bret = true;
m_nCount = buf.GetInt();
if ( m_nCount > 100000 || m_nCount < 0 )
{
bret = false;
}
m_nSeconds = buf.GetInt();
// Note, don't put the buf.GetInt() in the macro since it'll get evaluated twice!!!
m_nSeconds = MAX( m_nSeconds, 0 );
m_nCommentary = buf.GetInt();
if ( m_nCommentary < 0 || m_nCommentary > 100000 )
{
bret = false;
}
m_nHDR = buf.GetInt();
if ( m_nHDR < 0 || m_nHDR > 100000 )
{
bret = false;
}
m_nCaptions = buf.GetInt();
if ( m_nCaptions < 0 || m_nCaptions > 100000 )
{
bret = false;
}
for ( int i = 0; i < 3; ++i )
{
m_nSkill[ i ] = buf.GetInt();
if ( m_nSkill[ i ] < 0 || m_nSkill[ i ] > 100000 )
{
bret = false;
}
}
if ( iBufferStatsVersion > GAMESTATS_FILE_VERSION_OLD )
{
m_bSteam = buf.GetChar() ? true : false;
}
if ( iBufferStatsVersion > GAMESTATS_FILE_VERSION_OLD2 )
{
m_bCyberCafe = buf.GetChar() ? true : false;
}
if ( iBufferStatsVersion > GAMESTATS_FILE_VERSION_OLD5 )
{
m_nDeaths = buf.GetInt();
}
return bret;
}
void BasicGameStats_t::Clear()
{
m_nSecondsToCompleteGame = 0;
m_Summary.Clear();
m_MapTotals.Purge();
}
void BasicGameStats_t::SaveToBuffer( CUtlBuffer& buf )
{
buf.PutInt( m_nSecondsToCompleteGame );
m_Summary.SaveToBuffer( buf );
int c = m_MapTotals.Count();
buf.PutInt( c );
for ( int i = m_MapTotals.First(); i != m_MapTotals.InvalidIndex(); i = m_MapTotals.Next( i ) )
{
char const *name = m_MapTotals.GetElementName( i );
BasicGameStatsRecord_t &rec = m_MapTotals[ i ];
buf.PutString( name );
rec.SaveToBuffer( buf );
}
buf.PutChar( (char)m_nHL2ChaptureUnlocked );
buf.PutChar( m_bSteam ? 1 : 0 );
buf.PutChar( m_bCyberCafe ? 1 : 0 );
buf.PutShort( (short)m_nDXLevel );
}
BasicGameStatsRecord_t *BasicGameStats_t::FindOrAddRecordForMap( char const *mapname )
{
int idx = m_MapTotals.Find( mapname );
if ( idx == m_MapTotals.InvalidIndex() )
{
idx = m_MapTotals.Insert( mapname );
}
return &m_MapTotals[ idx ];
}
bool BasicGameStats_t::ParseFromBuffer( CUtlBuffer& buf, int iBufferStatsVersion )
{
bool bret = true;
m_nSecondsToCompleteGame = buf.GetInt();
if ( m_nSecondsToCompleteGame < 0 || m_nSecondsToCompleteGame > 10000000 )
{
bret = false;
}
m_Summary.ParseFromBuffer( buf, iBufferStatsVersion );
int c = buf.GetInt();
if ( c > 1024 || c < 0 )
{
bret = false;
}
for ( int i = 0; i < c; ++i )
{
char mapname[ 256 ];
buf.GetString( mapname, sizeof( mapname ) );
BasicGameStatsRecord_t *rec = FindOrAddRecordForMap( mapname );
bool valid= rec->ParseFromBuffer( buf, iBufferStatsVersion );
if ( !valid )
{
bret = false;
}
}
if ( iBufferStatsVersion >= GAMESTATS_FILE_VERSION_OLD2 )
{
m_nHL2ChaptureUnlocked = (int)buf.GetChar();
m_bSteam = buf.GetChar() ? true : false;
}
if ( iBufferStatsVersion > GAMESTATS_FILE_VERSION_OLD2 )
{
m_bCyberCafe = buf.GetChar() ? true : false;
}
if ( iBufferStatsVersion > GAMESTATS_FILE_VERSION_OLD3 )
{
m_nDXLevel = (int)buf.GetShort();
}
return bret;
}

View File

@@ -0,0 +1,289 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Material modify control entity.
//
//=============================================================================//
#include "cbase.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//------------------------------------------------------------------------------
// FIXME: This really should inherit from something more lightweight.
//------------------------------------------------------------------------------
#define MATERIAL_MODIFY_STRING_SIZE 255
#define MATERIAL_MODIFY_ANIMATION_UNSET -1
// Must match C_MaterialModifyControl.cpp
enum MaterialModifyMode_t
{
MATERIAL_MODIFY_MODE_NONE = 0,
MATERIAL_MODIFY_MODE_SETVAR = 1,
MATERIAL_MODIFY_MODE_ANIM_SEQUENCE = 2,
MATERIAL_MODIFY_MODE_FLOAT_LERP = 3,
};
ConVar debug_materialmodifycontrol( "debug_materialmodifycontrol", "0" );
class CMaterialModifyControl : public CBaseEntity
{
public:
DECLARE_CLASS( CMaterialModifyControl, CBaseEntity );
CMaterialModifyControl();
void Spawn( void );
bool KeyValue( const char *szKeyName, const char *szValue );
int UpdateTransmitState();
int ShouldTransmit( const CCheckTransmitInfo *pInfo );
void SetMaterialVar( inputdata_t &inputdata );
void SetMaterialVarToCurrentTime( inputdata_t &inputdata );
void InputStartAnimSequence( inputdata_t &inputdata );
void InputStartFloatLerp( inputdata_t &inputdata );
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
private:
CNetworkString( m_szMaterialName, MATERIAL_MODIFY_STRING_SIZE );
CNetworkString( m_szMaterialVar, MATERIAL_MODIFY_STRING_SIZE );
CNetworkString( m_szMaterialVarValue, MATERIAL_MODIFY_STRING_SIZE );
CNetworkVar( int, m_iFrameStart );
CNetworkVar( int, m_iFrameEnd );
CNetworkVar( bool, m_bWrap );
CNetworkVar( float, m_flFramerate );
CNetworkVar( bool, m_bNewAnimCommandsSemaphore );
CNetworkVar( float, m_flFloatLerpStartValue );
CNetworkVar( float, m_flFloatLerpEndValue );
CNetworkVar( float, m_flFloatLerpTransitionTime );
CNetworkVar( int, m_nModifyMode );
};
LINK_ENTITY_TO_CLASS(material_modify_control, CMaterialModifyControl);
BEGIN_DATADESC( CMaterialModifyControl )
// Variables.
DEFINE_AUTO_ARRAY( m_szMaterialName, FIELD_CHARACTER ),
DEFINE_AUTO_ARRAY( m_szMaterialVar, FIELD_CHARACTER ),
DEFINE_AUTO_ARRAY( m_szMaterialVarValue, FIELD_CHARACTER ),
DEFINE_FIELD( m_iFrameStart, FIELD_INTEGER ),
DEFINE_FIELD( m_iFrameEnd, FIELD_INTEGER ),
DEFINE_FIELD( m_bWrap, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flFramerate, FIELD_FLOAT ),
DEFINE_FIELD( m_bNewAnimCommandsSemaphore, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flFloatLerpStartValue, FIELD_FLOAT ),
DEFINE_FIELD( m_flFloatLerpEndValue, FIELD_FLOAT ),
DEFINE_FIELD( m_flFloatLerpTransitionTime, FIELD_FLOAT ),
DEFINE_FIELD( m_nModifyMode, FIELD_INTEGER ),
// Inputs.
DEFINE_INPUTFUNC( FIELD_STRING, "SetMaterialVar", SetMaterialVar ),
DEFINE_INPUTFUNC( FIELD_VOID, "SetMaterialVarToCurrentTime", SetMaterialVarToCurrentTime ),
DEFINE_INPUTFUNC( FIELD_STRING, "StartAnimSequence", InputStartAnimSequence ),
DEFINE_INPUTFUNC( FIELD_STRING, "StartFloatLerp", InputStartFloatLerp ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST(CMaterialModifyControl, DT_MaterialModifyControl)
SendPropString( SENDINFO( m_szMaterialName ) ),
SendPropString( SENDINFO( m_szMaterialVar ) ),
SendPropString( SENDINFO( m_szMaterialVarValue ) ),
SendPropInt( SENDINFO(m_iFrameStart), 8 ),
SendPropInt( SENDINFO(m_iFrameEnd), 8 ),
SendPropInt( SENDINFO(m_bWrap), 1, SPROP_UNSIGNED ),
SendPropFloat( SENDINFO(m_flFramerate), 0, SPROP_NOSCALE ),
SendPropInt( SENDINFO(m_bNewAnimCommandsSemaphore), 1, SPROP_UNSIGNED ),
SendPropFloat( SENDINFO(m_flFloatLerpStartValue), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO(m_flFloatLerpEndValue), 0, SPROP_NOSCALE ),
SendPropFloat( SENDINFO(m_flFloatLerpTransitionTime), 0, SPROP_NOSCALE ),
SendPropInt( SENDINFO(m_nModifyMode), 2, SPROP_UNSIGNED ),
END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CMaterialModifyControl::CMaterialModifyControl()
{
m_iFrameStart = MATERIAL_MODIFY_ANIMATION_UNSET;
m_iFrameEnd = MATERIAL_MODIFY_ANIMATION_UNSET;
m_nModifyMode = MATERIAL_MODIFY_MODE_NONE;
}
//------------------------------------------------------------------------------
// Purpose :
//------------------------------------------------------------------------------
void CMaterialModifyControl::Spawn( void )
{
Precache();
SetSolid( SOLID_NONE );
}
//------------------------------------------------------------------------------
// Purpose :
//------------------------------------------------------------------------------
bool CMaterialModifyControl::KeyValue( const char *szKeyName, const char *szValue )
{
if ( FStrEq( szKeyName, "materialName" ) )
{
Q_strncpy( m_szMaterialName.GetForModify(), szValue, MATERIAL_MODIFY_STRING_SIZE );
return true;
}
if ( FStrEq( szKeyName, "materialVar" ) )
{
Q_strncpy( m_szMaterialVar.GetForModify(), szValue, MATERIAL_MODIFY_STRING_SIZE );
return true;
}
return BaseClass::KeyValue( szKeyName, szValue );
}
//------------------------------------------------------------------------------
// Purpose : Send even though we don't have a model.
//------------------------------------------------------------------------------
int CMaterialModifyControl::UpdateTransmitState()
{
// ALWAYS transmit to all clients.
return SetTransmitState( FL_EDICT_FULLCHECK );
}
//-----------------------------------------------------------------------------
// Send if the parent is being sent:
//-----------------------------------------------------------------------------
int CMaterialModifyControl::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
CBaseEntity *pEnt = GetMoveParent();
if ( pEnt )
{
return pEnt->ShouldTransmit( pInfo );
}
return FL_EDICT_DONTSEND;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialModifyControl::SetMaterialVar( inputdata_t &inputdata )
{
//if( debug_materialmodifycontrol.GetBool() && Q_stristr( GetDebugName(), "alyx" ) )
//{
//DevMsg( 1, "CMaterialModifyControl::SetMaterialVar %s %s %s=\"%s\"\n",
//GetDebugName(), m_szMaterialName.Get(), m_szMaterialVar.Get(), inputdata.value.String() );
//}
Q_strncpy( m_szMaterialVarValue.GetForModify(), inputdata.value.String(), MATERIAL_MODIFY_STRING_SIZE );
m_nModifyMode = MATERIAL_MODIFY_MODE_SETVAR;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialModifyControl::SetMaterialVarToCurrentTime( inputdata_t &inputdata )
{
char temp[32];
Q_snprintf( temp, 32, "%f", gpGlobals->curtime );
Q_strncpy( m_szMaterialVarValue.GetForModify(), temp, MATERIAL_MODIFY_STRING_SIZE );
m_nModifyMode = MATERIAL_MODIFY_MODE_SETVAR;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialModifyControl::InputStartAnimSequence( inputdata_t &inputdata )
{
char parseString[255];
Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString));
// Get the start & end frames
char *pszParam = strtok(parseString," ");
if ( pszParam && pszParam[0] )
{
int iFrameStart = atoi(pszParam);
pszParam = strtok(NULL," ");
if ( pszParam && pszParam[0] )
{
int iFrameEnd = atoi(pszParam);
pszParam = strtok(NULL," ");
if ( pszParam && pszParam[0] )
{
float flFramerate = atof(pszParam);
pszParam = strtok(NULL," ");
if ( pszParam && pszParam[0] )
{
bool bWrap = atoi(pszParam) != 0;
// Got all the parameters. Save 'em and return;
m_iFrameStart = iFrameStart;
m_iFrameEnd = iFrameEnd;
m_flFramerate = flFramerate;
m_bWrap = bWrap;
m_nModifyMode = MATERIAL_MODIFY_MODE_ANIM_SEQUENCE;
m_bNewAnimCommandsSemaphore = !m_bNewAnimCommandsSemaphore;
return;
}
}
}
}
Warning("%s (%s) received StartAnimSequence input without correct parameters. Syntax: <Frame Start> <Frame End> <Frame Rate> <Loop>\nSetting <Frame End> to -1 uses the last frame of the texture. <Loop> should be 1 or 0.\n", GetClassname(), GetDebugName() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialModifyControl::InputStartFloatLerp( inputdata_t &inputdata )
{
char parseString[255];
Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString));
// if( debug_materialmodifycontrol.GetBool() )//&& Q_stristr( GetDebugName(), "alyx" ) )
// {
// DevMsg( 1, "CMaterialModifyControl::InputStartFloatLerp %s %s %s \"%s\"\n",
// GetDebugName(), m_szMaterialName.Get(), m_szMaterialVar.Get(), inputdata.value.String() );
// }
// Get the start & end values
char *pszParam = strtok(parseString," ");
if ( pszParam && pszParam[0] )
{
float flStartValue = atof(pszParam);
pszParam = strtok(NULL," ");
if ( pszParam && pszParam[0] )
{
float flEndValue = atof(pszParam);
pszParam = strtok(NULL," ");
if ( pszParam && pszParam[0] )
{
float flTransitionTime = atof(pszParam);
pszParam = strtok(NULL," ");
if ( pszParam && pszParam[0] )
{
bool bWrap = atoi(pszParam) != 0;
// We don't implement wrap currently.
bWrap = bWrap;
// Got all the parameters. Save 'em and return;
m_flFloatLerpStartValue = flStartValue;
m_flFloatLerpEndValue = flEndValue;
m_flFloatLerpTransitionTime = flTransitionTime;
m_nModifyMode = MATERIAL_MODIFY_MODE_FLOAT_LERP;
m_bNewAnimCommandsSemaphore = !m_bNewAnimCommandsSemaphore;
return;
}
}
}
}
Warning("%s (%s) received StartFloatLerp input without correct parameters. Syntax: <Start Value> <End Value> <Transition Time> <Loop>\n<Loop> should be 1 or 0.\n", GetClassname(), GetDebugName() );
}

View File

@@ -0,0 +1,529 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Used to fire events based on the orientation of a given entity.
//
// Looks at its target's anglular velocity every frame and fires outputs
// as the angular velocity passes a given threshold value.
//
//=============================================================================//
#include "cbase.h"
#include "entityinput.h"
#include "entityoutput.h"
#include "eventqueue.h"
#include "mathlib/mathlib.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
enum
{
AVELOCITY_SENSOR_NO_LAST_RESULT = -2
};
ConVar g_debug_angularsensor( "g_debug_angularsensor", "0", FCVAR_CHEAT );
class CPointAngularVelocitySensor : public CPointEntity
{
DECLARE_CLASS( CPointAngularVelocitySensor, CPointEntity );
public:
CPointAngularVelocitySensor();
void Activate(void);
void Spawn(void);
void Think(void);
private:
float SampleAngularVelocity(CBaseEntity *pEntity);
int CompareToThreshold(CBaseEntity *pEntity, float flThreshold, bool bFireVelocityOutput);
void FireCompareOutput(int nCompareResult, CBaseEntity *pActivator);
void DrawDebugLines( void );
// Input handlers
void InputTest( inputdata_t &inputdata );
void InputTestWithInterval( inputdata_t &inputdata );
EHANDLE m_hTargetEntity; // Entity whose angles are being monitored.
float m_flThreshold; // The threshold angular velocity that we are looking for.
int m_nLastCompareResult; // The comparison result from our last measurement, expressed as -1, 0, or 1
int m_nLastFireResult; // The last result for which we fire the output.
float m_flFireTime;
float m_flFireInterval;
float m_flLastAngVelocity;
QAngle m_lastOrientation;
Vector m_vecAxis;
bool m_bUseHelper;
// Outputs
COutputFloat m_AngularVelocity;
// Compare the target's angular velocity to the threshold velocity and fire the appropriate output.
// These outputs are filtered by m_flFireInterval to ignore excessive oscillations.
COutputEvent m_OnLessThan;
COutputEvent m_OnLessThanOrEqualTo;
COutputEvent m_OnGreaterThan;
COutputEvent m_OnGreaterThanOrEqualTo;
COutputEvent m_OnEqualTo;
DECLARE_DATADESC();
};
LINK_ENTITY_TO_CLASS(point_angularvelocitysensor, CPointAngularVelocitySensor);
BEGIN_DATADESC( CPointAngularVelocitySensor )
// Fields
DEFINE_FIELD( m_hTargetEntity, FIELD_EHANDLE ),
DEFINE_KEYFIELD(m_flThreshold, FIELD_FLOAT, "threshold"),
DEFINE_FIELD(m_nLastCompareResult, FIELD_INTEGER),
DEFINE_FIELD( m_nLastFireResult, FIELD_INTEGER ),
DEFINE_FIELD( m_flFireTime, FIELD_TIME ),
DEFINE_KEYFIELD( m_flFireInterval, FIELD_FLOAT, "fireinterval" ),
DEFINE_FIELD( m_flLastAngVelocity, FIELD_FLOAT ),
DEFINE_FIELD( m_lastOrientation, FIELD_VECTOR ),
// Inputs
DEFINE_INPUTFUNC(FIELD_VOID, "Test", InputTest),
DEFINE_INPUTFUNC(FIELD_VOID, "TestWithInterval", InputTestWithInterval),
// Outputs
DEFINE_OUTPUT(m_OnLessThan, "OnLessThan"),
DEFINE_OUTPUT(m_OnLessThanOrEqualTo, "OnLessThanOrEqualTo"),
DEFINE_OUTPUT(m_OnGreaterThan, "OnGreaterThan"),
DEFINE_OUTPUT(m_OnGreaterThanOrEqualTo, "OnGreaterThanOrEqualTo"),
DEFINE_OUTPUT(m_OnEqualTo, "OnEqualTo"),
DEFINE_OUTPUT(m_AngularVelocity, "AngularVelocity"),
DEFINE_KEYFIELD( m_vecAxis, FIELD_VECTOR, "axis" ),
DEFINE_KEYFIELD( m_bUseHelper, FIELD_BOOLEAN, "usehelper" ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose: constructor provides default values
//-----------------------------------------------------------------------------
CPointAngularVelocitySensor::CPointAngularVelocitySensor()
{
m_flFireInterval = 0.2f;
}
//-----------------------------------------------------------------------------
// Purpose: Called when spawning after parsing keyvalues.
//-----------------------------------------------------------------------------
void CPointAngularVelocitySensor::Spawn(void)
{
m_flThreshold = fabs(m_flThreshold);
m_nLastFireResult = AVELOCITY_SENSOR_NO_LAST_RESULT;
m_nLastCompareResult = AVELOCITY_SENSOR_NO_LAST_RESULT;
// m_flFireInterval = 0.2;
m_lastOrientation = vec3_angle;
}
//-----------------------------------------------------------------------------
// Purpose: Called after all entities in the map have spawned.
//-----------------------------------------------------------------------------
void CPointAngularVelocitySensor::Activate(void)
{
BaseClass::Activate();
m_hTargetEntity = gEntList.FindEntityByName( NULL, m_target );
if (m_hTargetEntity)
{
SetNextThink( gpGlobals->curtime );
}
}
//-----------------------------------------------------------------------------
// Purpose: Draws magic lines...
//-----------------------------------------------------------------------------
void CPointAngularVelocitySensor::DrawDebugLines( void )
{
if ( m_hTargetEntity )
{
Vector vForward, vRight, vUp;
AngleVectors( m_hTargetEntity->GetAbsAngles(), &vForward, &vRight, &vUp );
NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + vForward * 64, 255, 0, 0, false, 0 );
NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + vRight * 64, 0, 255, 0, false, 0 );
NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + vUp * 64, 0, 0, 255, false, 0 );
}
if ( m_bUseHelper == true )
{
QAngle Angles;
Vector vAxisForward, vAxisRight, vAxisUp;
Vector vLine = m_vecAxis - GetAbsOrigin();
VectorNormalize( vLine );
VectorAngles( vLine, Angles );
AngleVectors( Angles, &vAxisForward, &vAxisRight, &vAxisUp );
NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + vAxisForward * 64, 255, 0, 0, false, 0 );
NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + vAxisRight * 64, 0, 255, 0, false, 0 );
NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + vAxisUp * 64, 0, 0, 255, false, 0 );
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns the magnitude of the entity's angular velocity.
//-----------------------------------------------------------------------------
float CPointAngularVelocitySensor::SampleAngularVelocity(CBaseEntity *pEntity)
{
if (pEntity->GetMoveType() == MOVETYPE_VPHYSICS)
{
IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
if (pPhys != NULL)
{
Vector vecVelocity;
AngularImpulse vecAngVelocity;
pPhys->GetVelocity(&vecVelocity, &vecAngVelocity);
QAngle angles;
pPhys->GetPosition( NULL, &angles );
float dt = gpGlobals->curtime - GetLastThink();
if ( dt == 0 )
dt = 0.1;
// HACKHACK: We don't expect a real 'delta' orientation here, just enough of an error estimate to tell if this thing
// is trying to move, but failing.
QAngle delta = angles - m_lastOrientation;
if ( ( delta.Length() / dt ) < ( vecAngVelocity.Length() * 0.01 ) )
{
return 0.0f;
}
m_lastOrientation = angles;
if ( m_bUseHelper == false )
{
return vecAngVelocity.Length();
}
else
{
Vector vLine = m_vecAxis - GetAbsOrigin();
VectorNormalize( vLine );
Vector vecWorldAngVelocity;
pPhys->LocalToWorldVector( &vecWorldAngVelocity, vecAngVelocity );
float flDot = DotProduct( vecWorldAngVelocity, vLine );
return flDot;
}
}
}
else
{
QAngle vecAngVel = pEntity->GetLocalAngularVelocity();
float flMax = MAX(fabs(vecAngVel[PITCH]), fabs(vecAngVel[YAW]));
return MAX(flMax, fabs(vecAngVel[ROLL]));
}
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Compares the given entity's angular velocity to the threshold velocity.
// Input : pEntity - Entity whose angular velocity is being measured.
// flThreshold -
// Output : Returns -1 if less than, 0 if equal to, or 1 if greater than the threshold.
//-----------------------------------------------------------------------------
int CPointAngularVelocitySensor::CompareToThreshold(CBaseEntity *pEntity, float flThreshold, bool bFireVelocityOutput)
{
if (pEntity == NULL)
{
return 0;
}
float flAngVelocity = SampleAngularVelocity(pEntity);
if ( g_debug_angularsensor.GetBool() )
{
DrawDebugLines();
}
if (bFireVelocityOutput && (flAngVelocity != m_flLastAngVelocity))
{
m_AngularVelocity.Set(flAngVelocity, pEntity, this);
m_flLastAngVelocity = flAngVelocity;
}
if (flAngVelocity > flThreshold)
{
return 1;
}
if (flAngVelocity == flThreshold)
{
return 0;
}
return -1;
}
//-----------------------------------------------------------------------------
// Called every frame to sense the angular velocity of the target entity.
// Output is filtered by m_flFireInterval to ignore excessive oscillations.
//-----------------------------------------------------------------------------
void CPointAngularVelocitySensor::Think(void)
{
if (m_hTargetEntity != NULL)
{
//
// Check to see if the measure entity's angular velocity has been within
// tolerance of the threshold for the given period of time.
//
int nCompare = CompareToThreshold(m_hTargetEntity, m_flThreshold, true);
if (nCompare != m_nLastCompareResult)
{
// If we've oscillated back to where we last fired the output, don't
// fire the same output again.
if (nCompare == m_nLastFireResult)
{
m_flFireTime = 0;
}
else if (m_nLastCompareResult != AVELOCITY_SENSOR_NO_LAST_RESULT)
{
//
// The value has changed -- reset the timer. We'll fire the output if
// it stays at this value until the interval expires.
//
m_flFireTime = gpGlobals->curtime + m_flFireInterval;
}
m_nLastCompareResult = nCompare;
}
else if ((m_flFireTime != 0) && (gpGlobals->curtime >= m_flFireTime))
{
//
// The compare result has held steady long enough -- time to
// fire the output.
//
FireCompareOutput(nCompare, this);
m_nLastFireResult = nCompare;
m_flFireTime = 0;
}
SetNextThink( gpGlobals->curtime );
}
}
//-----------------------------------------------------------------------------
// Fires the output after the fire interval if the velocity is stable.
//-----------------------------------------------------------------------------
void CPointAngularVelocitySensor::InputTestWithInterval( inputdata_t &inputdata )
{
if (m_hTargetEntity != NULL)
{
m_flFireTime = gpGlobals->curtime + m_flFireInterval;
m_nLastFireResult = AVELOCITY_SENSOR_NO_LAST_RESULT;
m_nLastCompareResult = CompareToThreshold(m_hTargetEntity, m_flThreshold, true);
SetNextThink( gpGlobals->curtime );
}
}
//-----------------------------------------------------------------------------
// Purpose: Input handler for forcing an instantaneous test of the condition.
//-----------------------------------------------------------------------------
void CPointAngularVelocitySensor::InputTest( inputdata_t &inputdata )
{
int nCompareResult = CompareToThreshold(m_hTargetEntity, m_flThreshold, false);
FireCompareOutput(nCompareResult, inputdata.pActivator);
}
//-----------------------------------------------------------------------------
// Purpose: Fires the appropriate output based on the given comparison result.
// Input : nCompareResult -
// pActivator -
//-----------------------------------------------------------------------------
void CPointAngularVelocitySensor::FireCompareOutput( int nCompareResult, CBaseEntity *pActivator )
{
if (nCompareResult == -1)
{
m_OnLessThan.FireOutput(pActivator, this);
m_OnLessThanOrEqualTo.FireOutput(pActivator, this);
}
else if (nCompareResult == 1)
{
m_OnGreaterThan.FireOutput(pActivator, this);
m_OnGreaterThanOrEqualTo.FireOutput(pActivator, this);
}
else
{
m_OnEqualTo.FireOutput(pActivator, this);
m_OnLessThanOrEqualTo.FireOutput(pActivator, this);
m_OnGreaterThanOrEqualTo.FireOutput(pActivator, this);
}
}
// ============================================================================
//
// Simple velocity sensor
//
// ============================================================================
class CPointVelocitySensor : public CPointEntity
{
DECLARE_CLASS( CPointVelocitySensor, CPointEntity );
public:
void Spawn();
void Activate( void );
void Think( void );
private:
void SampleVelocity( void );
EHANDLE m_hTargetEntity; // Entity whose angles are being monitored.
Vector m_vecAxis; // Axis along which to measure the speed.
bool m_bEnabled; // Whether we're measuring or not
// Outputs
float m_fPrevVelocity; // stores velocity from last frame, so we only write the output if it has changed
COutputFloat m_Velocity;
void InputEnable( inputdata_t &inputdata );
void InputDisable( inputdata_t &inputdata );
DECLARE_DATADESC();
};
LINK_ENTITY_TO_CLASS( point_velocitysensor, CPointVelocitySensor );
BEGIN_DATADESC( CPointVelocitySensor )
// Fields
DEFINE_FIELD( m_hTargetEntity, FIELD_EHANDLE ),
DEFINE_KEYFIELD( m_vecAxis, FIELD_VECTOR, "axis" ),
DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "enabled" ),
DEFINE_FIELD( m_fPrevVelocity, FIELD_FLOAT ),
// Outputs
DEFINE_OUTPUT( m_Velocity, "Velocity" ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
END_DATADESC()
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CPointVelocitySensor::Spawn()
{
Vector vLine = m_vecAxis - GetAbsOrigin();
VectorNormalize( vLine );
m_vecAxis = vLine;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPointVelocitySensor::Activate( void )
{
BaseClass::Activate();
m_hTargetEntity = gEntList.FindEntityByName( NULL, m_target );
if ( m_bEnabled && m_hTargetEntity )
{
SetNextThink( gpGlobals->curtime );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPointVelocitySensor::InputEnable( inputdata_t &inputdata )
{
// Don't interrupt us if we're already enabled
if ( m_bEnabled )
return;
m_bEnabled = true;
if ( m_hTargetEntity )
{
SetNextThink( gpGlobals->curtime );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPointVelocitySensor::InputDisable( inputdata_t &inputdata )
{
m_bEnabled = false;
}
//-----------------------------------------------------------------------------
// Purpose: Called every frame
//-----------------------------------------------------------------------------
void CPointVelocitySensor::Think( void )
{
if ( m_hTargetEntity != NULL && m_bEnabled )
{
SampleVelocity();
SetNextThink( gpGlobals->curtime );
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns the magnitude of the entity's angular velocity.
//-----------------------------------------------------------------------------
void CPointVelocitySensor::SampleVelocity( void )
{
if ( m_hTargetEntity == NULL )
return;
Vector vecVelocity;
if ( m_hTargetEntity->GetMoveType() == MOVETYPE_VPHYSICS )
{
IPhysicsObject *pPhys = m_hTargetEntity->VPhysicsGetObject();
if ( pPhys != NULL )
{
pPhys->GetVelocity( &vecVelocity, NULL );
}
}
else
{
vecVelocity = m_hTargetEntity->GetAbsVelocity();
}
/*
float flSpeed = VectorNormalize( vecVelocity );
float flDot = ( m_vecAxis != vec3_origin ) ? DotProduct( vecVelocity, m_vecAxis ) : 1.0f;
*/
// We want the component of the velocity vector in the direction of the axis, which since the
// axis is normalized is simply their dot product (eg V . A = |V|*|A|*cos(theta) )
m_fPrevVelocity = ( m_vecAxis != vec3_origin ) ? DotProduct( vecVelocity, m_vecAxis ) : 1.0f;
// if it's changed since the last frame, poke the output
if ( m_fPrevVelocity != m_Velocity.Get() )
{
m_Velocity.Set( m_fPrevVelocity, NULL, NULL );
}
}

View File

@@ -0,0 +1,268 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Dissolve entity to be attached to target entity. Serves two purposes:
//
// 1) An entity that can be placed by a level designer and triggered
// to ignite a target entity.
//
// 2) An entity that can be created at runtime to ignite a target entity.
//
//=============================================================================//
#include "cbase.h"
#include "RagdollBoogie.h"
#include "physics_prop_ragdoll.h"
#include "effect_dispatch_data.h"
#include "te_effect_dispatch.h"
#include "IEffects.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Make electriciy every so often
//-----------------------------------------------------------------------------
static const char *s_pZapContext = "ZapContext";
//-----------------------------------------------------------------------------
// Save/load
//-----------------------------------------------------------------------------
BEGIN_DATADESC( CRagdollBoogie )
DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
DEFINE_FIELD( m_flBoogieLength, FIELD_FLOAT ),
DEFINE_FIELD( m_flMagnitude, FIELD_FLOAT ),
// Think this should be handled by StartTouch/etc.
// DEFINE_FIELD( m_nSuppressionCount, FIELD_INTEGER ),
DEFINE_FUNCTION( BoogieThink ),
DEFINE_FUNCTION( ZapThink ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( env_ragdoll_boogie, CRagdollBoogie );
//-----------------------------------------------------------------------------
// Purpose: Creates a flame and attaches it to a target entity.
// Input : pTarget -
//-----------------------------------------------------------------------------
CRagdollBoogie *CRagdollBoogie::Create( CBaseEntity *pTarget, float flMagnitude,
float flStartTime, float flLengthTime, int nSpawnFlags )
{
CRagdollProp *pRagdoll = dynamic_cast< CRagdollProp* >( pTarget );
if ( !pRagdoll )
return NULL;
CRagdollBoogie *pBoogie = (CRagdollBoogie *)CreateEntityByName( "env_ragdoll_boogie" );
if ( pBoogie == NULL )
return NULL;
pBoogie->AddSpawnFlags( nSpawnFlags );
pBoogie->AttachToEntity( pTarget );
pBoogie->SetBoogieTime( flStartTime, flLengthTime );
pBoogie->SetMagnitude( flMagnitude );
pBoogie->Spawn();
return pBoogie;
}
//-----------------------------------------------------------------------------
// Spawn
//-----------------------------------------------------------------------------
void CRagdollBoogie::Spawn()
{
BaseClass::Spawn();
SetThink( &CRagdollBoogie::BoogieThink );
SetNextThink( gpGlobals->curtime + 0.01f );
if ( HasSpawnFlags( SF_RAGDOLL_BOOGIE_ELECTRICAL ) )
{
SetContextThink( &CRagdollBoogie::ZapThink, gpGlobals->curtime + random->RandomFloat( 0.1f, 0.3f ), s_pZapContext );
}
}
//-----------------------------------------------------------------------------
// Zap!
//-----------------------------------------------------------------------------
void CRagdollBoogie::ZapThink()
{
if ( !GetMoveParent() )
return;
CBaseAnimating *pRagdoll = GetMoveParent()->GetBaseAnimating();
if ( !pRagdoll )
return;
// Make electricity on the client
CStudioHdr *pStudioHdr = pRagdoll->GetModelPtr( );
if (!pStudioHdr)
return;
mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pRagdoll->GetHitboxSet() );
if ( set->numhitboxes == 0 )
return;
if ( m_nSuppressionCount == 0 )
{
CEffectData data;
data.m_nEntIndex = GetMoveParent()->entindex();
data.m_flMagnitude = 4;
data.m_flScale = HasSpawnFlags(SF_RAGDOLL_BOOGIE_ELECTRICAL_NARROW_BEAM) ? 1.0f : 2.0f;
DispatchEffect( "TeslaHitboxes", data );
}
#ifdef HL2_EPISODIC
EmitSound( "RagdollBoogie.Zap" );
#endif
SetContextThink( &CRagdollBoogie::ZapThink, gpGlobals->curtime + random->RandomFloat( 0.1f, 0.3f ), s_pZapContext );
}
//-----------------------------------------------------------------------------
// Suppression count
//-----------------------------------------------------------------------------
void CRagdollBoogie::IncrementSuppressionCount( CBaseEntity *pTarget )
{
// Look for other boogies on the ragdoll + kill them
for ( CBaseEntity *pChild = pTarget->FirstMoveChild(); pChild; pChild = pChild->NextMovePeer() )
{
CRagdollBoogie *pBoogie = dynamic_cast<CRagdollBoogie*>(pChild);
if ( !pBoogie )
continue;
++pBoogie->m_nSuppressionCount;
}
}
void CRagdollBoogie::DecrementSuppressionCount( CBaseEntity *pTarget )
{
// Look for other boogies on the ragdoll + kill them
CBaseEntity *pNext;
for ( CBaseEntity *pChild = pTarget->FirstMoveChild(); pChild; pChild = pNext )
{
pNext = pChild->NextMovePeer();
CRagdollBoogie *pBoogie = dynamic_cast<CRagdollBoogie*>(pChild);
if ( !pBoogie )
continue;
if ( --pBoogie->m_nSuppressionCount <= 0 )
{
pBoogie->m_nSuppressionCount = 0;
float dt = gpGlobals->curtime - pBoogie->m_flStartTime;
if ( dt >= pBoogie->m_flBoogieLength )
{
PhysCallbackRemove( pBoogie->NetworkProp() );
}
}
}
}
//-----------------------------------------------------------------------------
// Attach to an entity
//-----------------------------------------------------------------------------
void CRagdollBoogie::AttachToEntity( CBaseEntity *pTarget )
{
m_nSuppressionCount = 0;
// Look for other boogies on the ragdoll + kill them
CBaseEntity *pNext;
for ( CBaseEntity *pChild = pTarget->FirstMoveChild(); pChild; pChild = pNext )
{
pNext = pChild->NextMovePeer();
CRagdollBoogie *pBoogie = dynamic_cast<CRagdollBoogie*>(pChild);
if ( !pBoogie )
continue;
m_nSuppressionCount = pBoogie->m_nSuppressionCount;
UTIL_Remove( pChild );
}
FollowEntity( pTarget );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : lifetime -
//-----------------------------------------------------------------------------
void CRagdollBoogie::SetBoogieTime( float flStartTime, float flLengthTime )
{
m_flStartTime = flStartTime;
m_flBoogieLength = flLengthTime;
}
//-----------------------------------------------------------------------------
// Purpose: Burn targets around us
//-----------------------------------------------------------------------------
void CRagdollBoogie::SetMagnitude( float flMagnitude )
{
m_flMagnitude = flMagnitude;
}
//-----------------------------------------------------------------------------
// Purpose: Burn targets around us
//-----------------------------------------------------------------------------
void CRagdollBoogie::BoogieThink( void )
{
CRagdollProp *pRagdoll = dynamic_cast< CRagdollProp* >( GetMoveParent() );
if ( !pRagdoll )
{
UTIL_Remove( this );
return;
}
float flMagnitude = m_flMagnitude;
if ( m_flBoogieLength != 0 )
{
float dt = gpGlobals->curtime - m_flStartTime;
if ( dt >= m_flBoogieLength )
{
// Don't remove while suppressed... this helps if we try to start another boogie
if ( m_nSuppressionCount == 0 )
{
UTIL_Remove( this );
}
SetThink( NULL );
return;
}
if ( dt < 0 )
{
SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1, 0.2f ) );
return;
}
flMagnitude = SimpleSplineRemapVal( dt, 0.0f, m_flBoogieLength, m_flMagnitude, 0.0f );
}
#ifndef _XBOX
if ( m_nSuppressionCount == 0 )
{
ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll( );
for ( int j = 0; j < pRagdollPhys->listCount; ++j )
{
float flMass = pRagdollPhys->list[j].pObject->GetMass();
float flForce = m_flMagnitude * flMass;
Vector vecForce;
vecForce = RandomVector( -flForce, flForce );
pRagdollPhys->list[j].pObject->ApplyForceCenter( vecForce );
}
}
#endif // !_XBOX
SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1, 0.2f ) );
}

View File

@@ -0,0 +1,50 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef RAGDOLLBOOGIE_H
#define RAGDOLLBOOGIE_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
// Set this spawnflag before calling Spawn to get electrical effects
//-----------------------------------------------------------------------------
#define SF_RAGDOLL_BOOGIE_ELECTRICAL 0x10000
#define SF_RAGDOLL_BOOGIE_ELECTRICAL_NARROW_BEAM 0x20000
//-----------------------------------------------------------------------------
// Makes ragdolls DANCE!
//-----------------------------------------------------------------------------
class CRagdollBoogie : public CBaseEntity
{
DECLARE_DATADESC();
DECLARE_CLASS( CRagdollBoogie, CBaseEntity );
public:
static CRagdollBoogie *Create( CBaseEntity *pTarget, float flMagnitude, float flStartTime, float flLengthTime = 0.0f, int nSpawnFlags = 0 );
static void IncrementSuppressionCount( CBaseEntity *pTarget );
static void DecrementSuppressionCount( CBaseEntity *pTarget );
void Spawn();
private:
void AttachToEntity( CBaseEntity *pTarget );
void SetBoogieTime( float flStartTime, float flLengthTime );
void SetMagnitude( float flMagnitude );
void BoogieThink( void );
void ZapThink();
float m_flStartTime;
float m_flBoogieLength;
float m_flMagnitude;
int m_nSuppressionCount;
};
#endif // RAGDOLLBOOGIE_H

View File

@@ -0,0 +1,305 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include "ServerNetworkProperty.h"
#include "tier0/dbg.h"
#include "gameinterface.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern CTimedEventMgr g_NetworkPropertyEventMgr;
//-----------------------------------------------------------------------------
// Save/load
//-----------------------------------------------------------------------------
BEGIN_DATADESC_NO_BASE( CServerNetworkProperty )
// DEFINE_FIELD( m_pOuter, FIELD_CLASSPTR ),
// DEFINE_FIELD( m_pPev, FIELD_CLASSPTR ),
// DEFINE_FIELD( m_PVSInfo, PVSInfo_t ),
// DEFINE_FIELD( m_pServerClass, FIELD_CLASSPTR ),
DEFINE_GLOBAL_FIELD( m_hParent, FIELD_EHANDLE ),
// DEFINE_FIELD( m_TimerEvent, CEventRegister ),
// DEFINE_FIELD( m_bPendingStateChange, FIELD_BOOLEAN ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CServerNetworkProperty::CServerNetworkProperty()
{
Init( NULL );
}
CServerNetworkProperty::~CServerNetworkProperty()
{
/* Free our transmit proxy.
if ( m_pTransmitProxy )
{
m_pTransmitProxy->Release();
}*/
engine->CleanUpEntityClusterList( &m_PVSInfo );
// remove the attached edict if it exists
DetachEdict();
}
//-----------------------------------------------------------------------------
// Initialization
//-----------------------------------------------------------------------------
void CServerNetworkProperty::Init( CBaseEntity *pEntity )
{
m_pPev = NULL;
m_pOuter = pEntity;
m_pServerClass = NULL;
// m_pTransmitProxy = NULL;
m_bPendingStateChange = false;
m_PVSInfo.m_nClusterCount = 0;
m_TimerEvent.Init( &g_NetworkPropertyEventMgr, this );
}
//-----------------------------------------------------------------------------
// Connects, disconnects edicts
//-----------------------------------------------------------------------------
void CServerNetworkProperty::AttachEdict( edict_t *pRequiredEdict )
{
Assert ( !m_pPev );
// see if there is an edict allocated for it, otherwise get one from the engine
if ( !pRequiredEdict )
{
pRequiredEdict = engine->CreateEdict();
}
m_pPev = pRequiredEdict;
m_pPev->SetEdict( GetBaseEntity(), true );
}
void CServerNetworkProperty::DetachEdict()
{
if ( m_pPev )
{
m_pPev->SetEdict( NULL, false );
engine->RemoveEdict( m_pPev );
m_pPev = NULL;
}
}
//-----------------------------------------------------------------------------
// Entity handles
//-----------------------------------------------------------------------------
IHandleEntity *CServerNetworkProperty::GetEntityHandle( )
{
return m_pOuter;
}
void CServerNetworkProperty::Release()
{
delete m_pOuter;
}
//-----------------------------------------------------------------------------
// Returns the network parent
//-----------------------------------------------------------------------------
CServerNetworkProperty* CServerNetworkProperty::GetNetworkParent()
{
CBaseEntity *pParent = m_hParent.Get();
return pParent ? pParent->NetworkProp() : NULL;
}
//-----------------------------------------------------------------------------
// Marks for deletion
//-----------------------------------------------------------------------------
void CServerNetworkProperty::MarkForDeletion()
{
m_pOuter->AddEFlags( EFL_KILLME );
}
bool CServerNetworkProperty::IsMarkedForDeletion() const
{
return ( m_pOuter->GetEFlags() & EFL_KILLME ) != 0;
}
//-----------------------------------------------------------------------------
// PVS information
//-----------------------------------------------------------------------------
void CServerNetworkProperty::RecomputePVSInformation()
{
if ( m_pPev && ( ( m_pPev->m_fStateFlags & FL_EDICT_DIRTY_PVS_INFORMATION ) != 0 ) )
{
m_pPev->m_fStateFlags &= ~FL_EDICT_DIRTY_PVS_INFORMATION;
engine->BuildEntityClusterList( edict(), &m_PVSInfo );
}
}
//-----------------------------------------------------------------------------
// Serverclass
//-----------------------------------------------------------------------------
ServerClass* CServerNetworkProperty::GetServerClass()
{
if ( !m_pServerClass )
m_pServerClass = m_pOuter->GetServerClass();
return m_pServerClass;
}
const char* CServerNetworkProperty::GetClassName() const
{
return STRING(m_pOuter->m_iClassname);
}
//-----------------------------------------------------------------------------
// Transmit proxies
/*-----------------------------------------------------------------------------
void CServerNetworkProperty::SetTransmitProxy( CBaseTransmitProxy *pProxy )
{
if ( m_pTransmitProxy )
{
m_pTransmitProxy->Release();
}
m_pTransmitProxy = pProxy;
if ( m_pTransmitProxy )
{
m_pTransmitProxy->AddRef();
}
}*/
//-----------------------------------------------------------------------------
// PVS rules
//-----------------------------------------------------------------------------
bool CServerNetworkProperty::IsInPVS( const edict_t *pRecipient, const void *pvs, int pvssize )
{
RecomputePVSInformation();
// ignore if not touching a PV leaf
// negative leaf count is a node number
// If no pvs, add any entity
Assert( pvs && ( edict() != pRecipient ) );
unsigned char *pPVS = ( unsigned char * )pvs;
if ( m_PVSInfo.m_nClusterCount < 0 ) // too many clusters, use headnode
{
return ( engine->CheckHeadnodeVisible( m_PVSInfo.m_nHeadNode, pPVS, pvssize ) != 0);
}
for ( int i = m_PVSInfo.m_nClusterCount; --i >= 0; )
{
if (pPVS[m_PVSInfo.m_pClusters[i] >> 3] & (1 << (m_PVSInfo.m_pClusters[i] & 7) ))
return true;
}
return false; // not visible
}
//-----------------------------------------------------------------------------
// PVS: this function is called a lot, so it avoids function calls
//-----------------------------------------------------------------------------
bool CServerNetworkProperty::IsInPVS( const CCheckTransmitInfo *pInfo )
{
// PVS data must be up to date
Assert( !m_pPev || ( ( m_pPev->m_fStateFlags & FL_EDICT_DIRTY_PVS_INFORMATION ) == 0 ) );
int i;
// Early out if the areas are connected
if ( !m_PVSInfo.m_nAreaNum2 )
{
for ( i=0; i< pInfo->m_AreasNetworked; i++ )
{
int clientArea = pInfo->m_Areas[i];
if ( clientArea == m_PVSInfo.m_nAreaNum || engine->CheckAreasConnected( clientArea, m_PVSInfo.m_nAreaNum ) )
break;
}
}
else
{
// doors can legally straddle two areas, so
// we may need to check another one
for ( i=0; i< pInfo->m_AreasNetworked; i++ )
{
int clientArea = pInfo->m_Areas[i];
if ( clientArea == m_PVSInfo.m_nAreaNum || clientArea == m_PVSInfo.m_nAreaNum2 )
break;
if ( engine->CheckAreasConnected( clientArea, m_PVSInfo.m_nAreaNum ) )
break;
if ( engine->CheckAreasConnected( clientArea, m_PVSInfo.m_nAreaNum2 ) )
break;
}
}
if ( i == pInfo->m_AreasNetworked )
{
// areas not connected
return false;
}
// ignore if not touching a PV leaf
// negative leaf count is a node number
// If no pvs, add any entity
Assert( edict() != pInfo->m_pClientEnt );
unsigned char *pPVS = ( unsigned char * )pInfo->m_PVS;
if ( m_PVSInfo.m_nClusterCount < 0 ) // too many clusters, use headnode
{
return (engine->CheckHeadnodeVisible( m_PVSInfo.m_nHeadNode, pPVS, pInfo->m_nPVSSize ) != 0);
}
for ( i = m_PVSInfo.m_nClusterCount; --i >= 0; )
{
int nCluster = m_PVSInfo.m_pClusters[i];
if ( ((int)(pPVS[nCluster >> 3])) & BitVec_BitInByte( nCluster ) )
return true;
}
return false; // not visible
}
void CServerNetworkProperty::SetUpdateInterval( float val )
{
if ( val == 0 )
m_TimerEvent.StopUpdates();
else
m_TimerEvent.SetUpdateInterval( val );
}
void CServerNetworkProperty::FireEvent()
{
// Our timer went off. If our state has changed in the background, then
// trigger a state change in the edict.
if ( m_bPendingStateChange )
{
m_pPev->StateChanged();
m_bPendingStateChange = false;
}
}

View File

@@ -0,0 +1,258 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#ifndef SERVERNETWORKPROPERTY_H
#define SERVERNETWORKPROPERTY_H
#ifdef _WIN32
#pragma once
#endif
#include "iservernetworkable.h"
#include "server_class.h"
#include "edict.h"
#include "timedeventmgr.h"
//
// Lightweight base class for networkable data on the server.
//
class CServerNetworkProperty : public IServerNetworkable, public IEventRegisterCallback
{
public:
DECLARE_CLASS_NOBASE( CServerNetworkProperty );
DECLARE_DATADESC();
public:
CServerNetworkProperty();
virtual ~CServerNetworkProperty();
public:
// IServerNetworkable implementation.
virtual IHandleEntity *GetEntityHandle( );
virtual edict_t *GetEdict() const;
virtual CBaseNetworkable* GetBaseNetworkable();
virtual CBaseEntity* GetBaseEntity();
virtual ServerClass* GetServerClass();
virtual const char* GetClassName() const;
virtual void Release();
virtual int AreaNum() const;
virtual PVSInfo_t* GetPVSInfo();
public:
// Other public methods
void Init( CBaseEntity *pEntity );
void AttachEdict( edict_t *pRequiredEdict = NULL );
// Methods to get the entindex + edict
int entindex() const;
edict_t *edict();
const edict_t *edict() const;
// Sets the edict pointer (for swapping edicts)
void SetEdict( edict_t *pEdict );
// All these functions call through to CNetStateMgr.
// See CNetStateMgr for details about these functions.
void NetworkStateForceUpdate();
void NetworkStateChanged();
void NetworkStateChanged( unsigned short offset );
// Marks the PVS information dirty
void MarkPVSInformationDirty();
// Marks for deletion
void MarkForDeletion();
bool IsMarkedForDeletion() const;
// Sets the network parent
void SetNetworkParent( EHANDLE hParent );
CServerNetworkProperty* GetNetworkParent();
// This is useful for entities that don't change frequently or that the client
// doesn't need updates on very often. If you use this mode, the server will only try to
// detect state changes every N seconds, so it will save CPU cycles and bandwidth.
//
// Note: N must be less than AUTOUPDATE_MAX_TIME_LENGTH.
//
// Set back to zero to disable the feature.
//
// This feature works on top of manual mode.
// - If you turn it on and manual mode is off, it will autodetect changes every N seconds.
// - If you turn it on and manual mode is on, then every N seconds it will only say there
// is a change if you've called NetworkStateChanged.
void SetUpdateInterval( float N );
// You can use this to override any entity's ShouldTransmit behavior.
// void SetTransmitProxy( CBaseTransmitProxy *pProxy );
// This version does a PVS check which also checks for connected areas
bool IsInPVS( const CCheckTransmitInfo *pInfo );
// This version doesn't do the area check
bool IsInPVS( const edict_t *pRecipient, const void *pvs, int pvssize );
// Called by the timed event manager when it's time to detect a state change.
virtual void FireEvent();
// Recomputes PVS information
void RecomputePVSInformation();
private:
// Detaches the edict.. should only be called by CBaseNetworkable's destructor.
void DetachEdict();
CBaseEntity *GetOuter();
// Marks the networkable that it will should transmit
void SetTransmit( CCheckTransmitInfo *pInfo );
private:
CBaseEntity *m_pOuter;
// CBaseTransmitProxy *m_pTransmitProxy;
edict_t *m_pPev;
PVSInfo_t m_PVSInfo;
ServerClass *m_pServerClass;
// NOTE: This state is 'owned' by the entity. It's only copied here
// also to help improve cache performance in networking code.
EHANDLE m_hParent;
// Counters for SetUpdateInterval.
CEventRegister m_TimerEvent;
bool m_bPendingStateChange : 1;
// friend class CBaseTransmitProxy;
};
//-----------------------------------------------------------------------------
// inline methods // TODOMO does inline work on virtual functions ?
//-----------------------------------------------------------------------------
inline CBaseNetworkable* CServerNetworkProperty::GetBaseNetworkable()
{
return NULL;
}
inline CBaseEntity* CServerNetworkProperty::GetBaseEntity()
{
return m_pOuter;
}
inline CBaseEntity *CServerNetworkProperty::GetOuter()
{
return m_pOuter;
}
inline PVSInfo_t *CServerNetworkProperty::GetPVSInfo()
{
return &m_PVSInfo;
}
//-----------------------------------------------------------------------------
// Marks the PVS information dirty
//-----------------------------------------------------------------------------
inline void CServerNetworkProperty::MarkPVSInformationDirty()
{
if ( m_pPev )
{
m_pPev->m_fStateFlags |= FL_EDICT_DIRTY_PVS_INFORMATION;
}
}
//-----------------------------------------------------------------------------
// Sets/gets the network parent
//-----------------------------------------------------------------------------
inline void CServerNetworkProperty::SetNetworkParent( EHANDLE hParent )
{
m_hParent = hParent;
}
//-----------------------------------------------------------------------------
// Methods related to the net state mgr
//-----------------------------------------------------------------------------
inline void CServerNetworkProperty::NetworkStateForceUpdate()
{
if ( m_pPev )
m_pPev->StateChanged();
}
inline void CServerNetworkProperty::NetworkStateChanged()
{
// If we're using the timer, then ignore this call.
if ( m_TimerEvent.IsRegistered() )
{
// If we're waiting for a timer event, then queue the change so it happens
// when the timer goes off.
m_bPendingStateChange = true;
}
else
{
if ( m_pPev )
m_pPev->StateChanged();
}
}
inline void CServerNetworkProperty::NetworkStateChanged( unsigned short varOffset )
{
// If we're using the timer, then ignore this call.
if ( m_TimerEvent.IsRegistered() )
{
// If we're waiting for a timer event, then queue the change so it happens
// when the timer goes off.
m_bPendingStateChange = true;
}
else
{
if ( m_pPev )
m_pPev->StateChanged( varOffset );
}
}
//-----------------------------------------------------------------------------
// Methods to get the entindex + edict
//-----------------------------------------------------------------------------
inline int CServerNetworkProperty::entindex() const
{
return ENTINDEX( m_pPev );
}
inline edict_t* CServerNetworkProperty::GetEdict() const
{
// This one's virtual, that's why we have to two other versions
return m_pPev;
}
inline edict_t *CServerNetworkProperty::edict()
{
return m_pPev;
}
inline const edict_t *CServerNetworkProperty::edict() const
{
return m_pPev;
}
//-----------------------------------------------------------------------------
// Sets the edict pointer (for swapping edicts)
//-----------------------------------------------------------------------------
inline void CServerNetworkProperty::SetEdict( edict_t *pEdict )
{
m_pPev = pEdict;
}
inline int CServerNetworkProperty::AreaNum() const
{
const_cast<CServerNetworkProperty*>(this)->RecomputePVSInformation();
return m_PVSInfo.m_nAreaNum;
}
#endif // SERVERNETWORKPROPERTY_H

View File

@@ -0,0 +1,147 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "igamesystem.h"
#include "entitylist.h"
#include "SkyCamera.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// automatically hooks in the system's callbacks
CEntityClassList<CSkyCamera> g_SkyList;
template <> CSkyCamera *CEntityClassList<CSkyCamera>::m_pClassList = NULL;
//-----------------------------------------------------------------------------
// Retrives the current skycamera
//-----------------------------------------------------------------------------
CSkyCamera* GetCurrentSkyCamera()
{
return g_SkyList.m_pClassList;
}
CSkyCamera* GetSkyCameraList()
{
return g_SkyList.m_pClassList;
}
//=============================================================================
LINK_ENTITY_TO_CLASS( sky_camera, CSkyCamera );
BEGIN_DATADESC( CSkyCamera )
DEFINE_KEYFIELD( m_skyboxData.scale, FIELD_INTEGER, "scale" ),
DEFINE_FIELD( m_skyboxData.origin, FIELD_VECTOR ),
DEFINE_FIELD( m_skyboxData.area, FIELD_INTEGER ),
// Quiet down classcheck
// DEFINE_FIELD( m_skyboxData, sky3dparams_t ),
// This is re-set up in the constructor
// DEFINE_FIELD( m_pNext, CSkyCamera ),
// fog data for 3d skybox
DEFINE_KEYFIELD( m_bUseAngles, FIELD_BOOLEAN, "use_angles" ),
DEFINE_KEYFIELD( m_skyboxData.fog.enable, FIELD_BOOLEAN, "fogenable" ),
DEFINE_KEYFIELD( m_skyboxData.fog.blend, FIELD_BOOLEAN, "fogblend" ),
DEFINE_KEYFIELD( m_skyboxData.fog.dirPrimary, FIELD_VECTOR, "fogdir" ),
DEFINE_KEYFIELD( m_skyboxData.fog.colorPrimary, FIELD_COLOR32, "fogcolor" ),
DEFINE_KEYFIELD( m_skyboxData.fog.colorSecondary, FIELD_COLOR32, "fogcolor2" ),
DEFINE_KEYFIELD( m_skyboxData.fog.start, FIELD_FLOAT, "fogstart" ),
DEFINE_KEYFIELD( m_skyboxData.fog.end, FIELD_FLOAT, "fogend" ),
DEFINE_KEYFIELD( m_skyboxData.fog.maxdensity, FIELD_FLOAT, "fogmaxdensity" ),
END_DATADESC()
//-----------------------------------------------------------------------------
// List of maps in HL2 that we must apply our skybox fog fixup hack to
//-----------------------------------------------------------------------------
static const char *s_pBogusFogMaps[] =
{
"d1_canals_01",
"d1_canals_01a",
"d1_canals_02",
"d1_canals_03",
"d1_canals_09",
"d1_canals_10",
"d1_canals_11",
"d1_canals_12",
"d1_canals_13",
"d1_eli_01",
"d1_trainstation_01",
"d1_trainstation_03",
"d1_trainstation_04",
"d1_trainstation_05",
"d1_trainstation_06",
"d3_c17_04",
"d3_c17_11",
"d3_c17_12",
"d3_citadel_01",
NULL
};
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CSkyCamera::CSkyCamera()
{
g_SkyList.Insert( this );
m_skyboxData.fog.maxdensity = 1.0f;
}
CSkyCamera::~CSkyCamera()
{
g_SkyList.Remove( this );
}
void CSkyCamera::Spawn( void )
{
m_skyboxData.origin = GetLocalOrigin();
m_skyboxData.area = engine->GetArea( m_skyboxData.origin );
Precache();
}
//-----------------------------------------------------------------------------
// Activate!
//-----------------------------------------------------------------------------
void CSkyCamera::Activate( )
{
BaseClass::Activate();
if ( m_bUseAngles )
{
AngleVectors( GetAbsAngles(), &m_skyboxData.fog.dirPrimary.GetForModify() );
m_skyboxData.fog.dirPrimary.GetForModify() *= -1.0f;
}
#ifdef HL2_DLL
// NOTE! This is a hack. There was a bug in the skybox fog computation
// on the client DLL that caused it to use the average of the primary and
// secondary fog color when blending was enabled. The bug is fixed, but to make
// the maps look the same as before the bug fix without having to download new maps,
// I have to cheat here and slam the primary and secondary colors to be the average of
// the primary and secondary colors.
if ( m_skyboxData.fog.blend )
{
for ( int i = 0; s_pBogusFogMaps[i]; ++i )
{
if ( !Q_stricmp( s_pBogusFogMaps[i], STRING(gpGlobals->mapname) ) )
{
m_skyboxData.fog.colorPrimary.SetR( ( m_skyboxData.fog.colorPrimary.GetR() + m_skyboxData.fog.colorSecondary.GetR() ) * 0.5f );
m_skyboxData.fog.colorPrimary.SetG( ( m_skyboxData.fog.colorPrimary.GetG() + m_skyboxData.fog.colorSecondary.GetG() ) * 0.5f );
m_skyboxData.fog.colorPrimary.SetB( ( m_skyboxData.fog.colorPrimary.GetB() + m_skyboxData.fog.colorSecondary.GetB() ) * 0.5f );
m_skyboxData.fog.colorPrimary.SetA( ( m_skyboxData.fog.colorPrimary.GetA() + m_skyboxData.fog.colorSecondary.GetA() ) * 0.5f );
m_skyboxData.fog.colorSecondary = m_skyboxData.fog.colorPrimary;
}
}
}
#endif
}

View File

@@ -0,0 +1,47 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Resource collection entity
//
// $NoKeywords: $
//=============================================================================//
#ifndef SKYCAMERA_H
#define SKYCAMERA_H
#ifdef _WIN32
#pragma once
#endif
class CSkyCamera;
//=============================================================================
//
// Sky Camera Class
//
class CSkyCamera : public CLogicalEntity
{
DECLARE_CLASS( CSkyCamera, CLogicalEntity );
public:
DECLARE_DATADESC();
CSkyCamera();
~CSkyCamera();
virtual void Spawn( void );
virtual void Activate();
public:
sky3dparams_t m_skyboxData;
bool m_bUseAngles;
CSkyCamera *m_pNext;
};
//-----------------------------------------------------------------------------
// Retrives the current skycamera
//-----------------------------------------------------------------------------
CSkyCamera* GetCurrentSkyCamera();
CSkyCamera* GetSkyCameraList();
#endif // SKYCAMERA_H

View File

@@ -0,0 +1,510 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Template entities are used by spawners to create copies of entities
// that were configured by the level designer. This allows us to spawn
// entities with arbitrary sets of key/value data and entity I/O
// connections.
//
// Template entities are marked with a special spawnflag which causes
// them not to spawn, but to be saved as a string containing all the
// map data (keyvalues and I/O connections) from the BSP. Template
// entities are looked up by name by the spawner, which copies the
// map data into a local string (that's how the template data is saved
// and restored). Once all the entities in the map have been activated,
// the template database is freed.
//
//=============================================================================//
#include "cbase.h"
#include "igamesystem.h"
#include "mapentities_shared.h"
#include "point_template.h"
#include "eventqueue.h"
#include "TemplateEntities.h"
#include "utldict.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
ConVar template_debug( "template_debug", "0" );
// This is appended to key's values that will need to be unique in template instances
const char *ENTITYIO_FIXUP_STRING = "&0000";
int MapEntity_GetNumKeysInEntity( const char *pEntData );
struct TemplateEntityData_t
{
const char *pszName;
char *pszMapData;
string_t iszMapData;
int iMapDataLength;
bool bNeedsEntityIOFixup; // If true, this template has entity I/O in its mapdata that needs fixup before spawning.
char *pszFixedMapData; // A single copy of this template that we used to fix up the Entity I/O whenever someone wants a fixed version of this template
DECLARE_SIMPLE_DATADESC();
};
BEGIN_SIMPLE_DATADESC( TemplateEntityData_t )
//DEFINE_FIELD( pszName, FIELD_STRING ), // Saved custom, see below
//DEFINE_FIELD( pszMapData, FIELD_STRING ), // Saved custom, see below
DEFINE_FIELD( iszMapData, FIELD_STRING ),
DEFINE_FIELD( iMapDataLength, FIELD_INTEGER ),
DEFINE_FIELD( bNeedsEntityIOFixup, FIELD_BOOLEAN ),
//DEFINE_FIELD( pszFixedMapData, FIELD_STRING ), // Not saved at all
END_DATADESC()
struct grouptemplate_t
{
CEntityMapData *pMapDataParser;
char pszName[MAPKEY_MAXLENGTH];
int iIndex;
bool bChangeTargetname;
};
static CUtlVector<TemplateEntityData_t *> g_Templates;
int g_iCurrentTemplateInstance;
//-----------------------------------------------------------------------------
// Purpose: Saves the given entity's keyvalue data for later use by a spawner.
// Returns the index into the templates.
//-----------------------------------------------------------------------------
int Templates_Add(CBaseEntity *pEntity, const char *pszMapData, int nLen)
{
const char *pszName = STRING(pEntity->GetEntityName());
if ((!pszName) || (!strlen(pszName)))
{
DevWarning(1, "RegisterTemplateEntity: template entity with no name, class %s\n", pEntity->GetClassname());
return -1;
}
TemplateEntityData_t *pEntData = (TemplateEntityData_t *)malloc(sizeof(TemplateEntityData_t));
pEntData->pszName = strdup( pszName );
// We may modify the values of the keys in this mapdata chunk later on to fix Entity I/O
// connections. For this reason, we need to ensure we have enough memory to do that.
int iKeys = MapEntity_GetNumKeysInEntity( pszMapData );
int iExtraSpace = (strlen(ENTITYIO_FIXUP_STRING)+1) * iKeys;
// Extra 1 because the mapdata passed in isn't null terminated
pEntData->iMapDataLength = nLen + iExtraSpace + 1;
pEntData->pszMapData = (char *)malloc( pEntData->iMapDataLength );
memcpy(pEntData->pszMapData, pszMapData, nLen + 1);
pEntData->pszMapData[nLen] = '\0';
// We don't alloc these suckers right now because that gives us no time to
// tweak them for Entity I/O purposes.
pEntData->iszMapData = NULL_STRING;
pEntData->bNeedsEntityIOFixup = false;
pEntData->pszFixedMapData = NULL;
return g_Templates.AddToTail(pEntData);
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the specified index needs to be fixed up to be unique
// when the template is spawned.
//-----------------------------------------------------------------------------
bool Templates_IndexRequiresEntityIOFixup( int iIndex )
{
Assert( iIndex < g_Templates.Count() );
return g_Templates[iIndex]->bNeedsEntityIOFixup;
}
//-----------------------------------------------------------------------------
// Purpose: Looks up a template entity by its index in the templates
// Used by point_templates because they often have multiple templates with the same name
//-----------------------------------------------------------------------------
string_t Templates_FindByIndex( int iIndex )
{
Assert( iIndex < g_Templates.Count() );
// First time through we alloc the mapdata onto the pool.
// It's safe to do it now because this isn't called until post Entity I/O cleanup.
if ( g_Templates[iIndex]->iszMapData == NULL_STRING )
{
g_Templates[iIndex]->iszMapData = AllocPooledString( g_Templates[iIndex]->pszMapData );
}
return g_Templates[iIndex]->iszMapData;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int Templates_GetStringSize( int iIndex )
{
Assert( iIndex < g_Templates.Count() );
return g_Templates[iIndex]->iMapDataLength;
}
//-----------------------------------------------------------------------------
// Purpose: Looks up a template entity by name, returning the map data blob as
// a null-terminated string containing key/value pairs.
// NOTE: This can't handle multiple templates with the same targetname.
//-----------------------------------------------------------------------------
string_t Templates_FindByTargetName(const char *pszName)
{
int nCount = g_Templates.Count();
for (int i = 0; i < nCount; i++)
{
TemplateEntityData_t *pTemplate = g_Templates.Element(i);
if ( !stricmp(pTemplate->pszName, pszName) )
return Templates_FindByIndex( i );
}
return NULL_STRING;
}
//-----------------------------------------------------------------------------
// Purpose: A CPointTemplate has asked us to reconnect all the entity I/O links
// inside it's templates. Go through the keys and add look for values
// that match a name within the group's entity names. Append %d to any
// found values, which will later be filled out by a unique identifier
// whenever the template is instanced.
//-----------------------------------------------------------------------------
void Templates_ReconnectIOForGroup( CPointTemplate *pGroup )
{
int iCount = pGroup->GetNumTemplates();
if ( !iCount )
return;
// First assemble a list of the targetnames of all the templates in the group.
// We need to store off the original names here, because we're going to change
// them as we go along.
CUtlVector< grouptemplate_t > GroupTemplates;
int i;
for ( i = 0; i < iCount; i++ )
{
grouptemplate_t newGroupTemplate;
newGroupTemplate.iIndex = pGroup->GetTemplateIndexForTemplate(i);
newGroupTemplate.pMapDataParser = new CEntityMapData( g_Templates[ newGroupTemplate.iIndex ]->pszMapData, g_Templates[ newGroupTemplate.iIndex ]->iMapDataLength );
Assert( newGroupTemplate.pMapDataParser );
newGroupTemplate.pMapDataParser->ExtractValue( "targetname", newGroupTemplate.pszName );
newGroupTemplate.bChangeTargetname = false;
GroupTemplates.AddToTail( newGroupTemplate );
}
if (pGroup->AllowNameFixup())
{
char keyName[MAPKEY_MAXLENGTH];
char value[MAPKEY_MAXLENGTH];
char valueclipped[MAPKEY_MAXLENGTH];
// Now go through all the entities in the group and parse their mapdata keyvalues.
// We're looking for any values that match targetnames of any of the group entities.
for ( i = 0; i < iCount; i++ )
{
// We need to know what instance of each key we're changing.
// Store a table of the count of the keys we've run into.
CUtlDict< int, int > KeyInstanceCount;
CEntityMapData *mapData = GroupTemplates[i].pMapDataParser;
// Loop through our keys
if ( !mapData->GetFirstKey(keyName, value) )
continue;
do
{
// Ignore targetnames
if ( !stricmp( keyName, "targetname" ) )
continue;
// Add to the count for this
int idx = KeyInstanceCount.Find( keyName );
if ( idx == KeyInstanceCount.InvalidIndex() )
{
idx = KeyInstanceCount.Insert( keyName, 0 );
}
KeyInstanceCount[idx]++;
// Entity I/O values are stored as "Targetname,<data>", so we need to see if there's a ',' in the string
char *sValue = value;
// FIXME: This is very brittle. Any key with a , will not be found.
char *s = strchr( value, ',' );
if ( s )
{
// Grab just the targetname of the receiver
Q_strncpy( valueclipped, value, (s - value+1) );
sValue = valueclipped;
}
// Loop through our group templates
for ( int iTName = 0; iTName < iCount; iTName++ )
{
char *pName = GroupTemplates[iTName].pszName;
if ( stricmp( pName, sValue ) )
continue;
if ( template_debug.GetInt() )
{
Msg("Template Connection Found: Key %s (\"%s\") in entity named \"%s\"(%d) matches entity %d's targetname\n", keyName, sValue, GroupTemplates[i].pszName, i, iTName );
}
char newvalue[MAPKEY_MAXLENGTH];
// Get the current key instance. (-1 because it's this one we're changing)
int nKeyInstance = KeyInstanceCount[idx] - 1;
// Add our IO value to the targetname
// We need to append it if this isn't an Entity I/O value, or prepend it to the ',' if it is
if ( s )
{
Q_strncpy( newvalue, valueclipped, MAPKEY_MAXLENGTH );
Q_strncat( newvalue, ENTITYIO_FIXUP_STRING, sizeof(newvalue), COPY_ALL_CHARACTERS );
Q_strncat( newvalue, s, sizeof(newvalue), COPY_ALL_CHARACTERS );
mapData->SetValue( keyName, newvalue, nKeyInstance );
}
else
{
Q_strncpy( newvalue, sValue, MAPKEY_MAXLENGTH );
Q_strncat( newvalue, ENTITYIO_FIXUP_STRING, sizeof(newvalue), COPY_ALL_CHARACTERS );
mapData->SetValue( keyName, newvalue, nKeyInstance );
}
// Remember we changed this targetname
GroupTemplates[iTName].bChangeTargetname = true;
// Set both entity's flags telling them their template needs fixup when it's spawned
g_Templates[ GroupTemplates[i].iIndex ]->bNeedsEntityIOFixup = true;
g_Templates[ GroupTemplates[iTName].iIndex ]->bNeedsEntityIOFixup = true;
}
}
while ( mapData->GetNextKey(keyName, value) );
}
// Now change targetnames for all entities that need them changed
for ( i = 0; i < iCount; i++ )
{
char value[MAPKEY_MAXLENGTH];
if ( GroupTemplates[i].bChangeTargetname )
{
CEntityMapData *mapData = GroupTemplates[i].pMapDataParser;
mapData->ExtractValue( "targetname", value );
Q_strncat( value, ENTITYIO_FIXUP_STRING, sizeof(value), COPY_ALL_CHARACTERS );
mapData->SetValue( "targetname", value );
}
}
}
// Delete our group parsers
for ( i = 0; i < iCount; i++ )
{
delete GroupTemplates[i].pMapDataParser;
}
GroupTemplates.Purge();
}
//-----------------------------------------------------------------------------
// Purpose: Someone's about to start instancing a new group of entities.
// Generate a unique identifier for this group.
//-----------------------------------------------------------------------------
void Templates_StartUniqueInstance( void )
{
g_iCurrentTemplateInstance++;
// Make sure there's enough room to fit it into the string
int iMax = pow(10.0f, (int)((strlen(ENTITYIO_FIXUP_STRING) - 1))); // -1 for the &
if ( g_iCurrentTemplateInstance >= iMax )
{
// We won't hit this.
Assert(0);
// Hopefully there were still be instance number 0 around.
g_iCurrentTemplateInstance = 0;
}
}
//-----------------------------------------------------------------------------
// Purpose: Someone wants to spawn an instance of a template that requires
// entity IO fixup. Fill out the pMapData with a copy of the template
// with unique key/values where the template requires them.
//-----------------------------------------------------------------------------
char *Templates_GetEntityIOFixedMapData( int iIndex )
{
Assert( Templates_IndexRequiresEntityIOFixup( iIndex ) );
// First time through?
if ( !g_Templates[iIndex]->pszFixedMapData )
{
g_Templates[iIndex]->pszFixedMapData = new char[g_Templates[iIndex]->iMapDataLength];
Q_strncpy( g_Templates[iIndex]->pszFixedMapData, g_Templates[iIndex]->pszMapData, g_Templates[iIndex]->iMapDataLength );
}
int iFixupSize = strlen(ENTITYIO_FIXUP_STRING); // don't include \0 when copying in the fixup
char *sOurFixup = new char[iFixupSize+1]; // do alloc room here for the null terminator
Q_snprintf( sOurFixup, iFixupSize+1, "%c%.4d", ENTITYIO_FIXUP_STRING[0], g_iCurrentTemplateInstance );
// Now rip through the map data string and replace any instances of the fixup string with our unique identifier
char *c = g_Templates[iIndex]->pszFixedMapData;
do
{
if ( *c == ENTITYIO_FIXUP_STRING[0] )
{
// Make sure it's our fixup string
bool bValid = true;
for ( int i = 1; i < iFixupSize; i++ )
{
// Look for any number, because we've already used this string
if ( !(*(c+i) >= '0' && *(c+i) <= '9') )
{
// Some other string
bValid = false;
break;
}
}
// Stomp it with our unique string
if ( bValid )
{
memcpy( c, sOurFixup, iFixupSize );
c += iFixupSize;
}
}
c++;
} while (*c);
return g_Templates[iIndex]->pszFixedMapData;
}
//-----------------------------------------------------------------------------
// Purpose: Frees all the template data. Called on level shutdown.
//-----------------------------------------------------------------------------
void Templates_RemoveAll(void)
{
int nCount = g_Templates.Count();
for (int i = 0; i < nCount; i++)
{
TemplateEntityData_t *pTemplate = g_Templates.Element(i);
free((void *)pTemplate->pszName);
free(pTemplate->pszMapData);
if ( pTemplate->pszFixedMapData )
{
free(pTemplate->pszFixedMapData);
}
free(pTemplate);
}
g_Templates.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: Hooks in the template manager's callbacks.
//-----------------------------------------------------------------------------
class CTemplatesHook : public CAutoGameSystem
{
public:
CTemplatesHook( char const *name ) : CAutoGameSystem( name )
{
}
virtual void LevelShutdownPostEntity( void )
{
Templates_RemoveAll();
}
};
CTemplatesHook g_TemplateEntityHook( "CTemplatesHook" );
//-----------------------------------------------------------------------------
// TEMPLATE SAVE / RESTORE
//-----------------------------------------------------------------------------
static short TEMPLATE_SAVE_RESTORE_VERSION = 1;
class CTemplate_SaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler
{
public:
const char *GetBlockName()
{
return "Templates";
}
//---------------------------------
void Save( ISave *pSave )
{
pSave->WriteInt( &g_iCurrentTemplateInstance );
short nCount = g_Templates.Count();
pSave->WriteShort( &nCount );
for ( int i = 0; i < nCount; i++ )
{
TemplateEntityData_t *pTemplate = g_Templates[i];
pSave->WriteAll( pTemplate );
pSave->WriteString( pTemplate->pszName );
pSave->WriteString( pTemplate->pszMapData );
}
}
//---------------------------------
void WriteSaveHeaders( ISave *pSave )
{
pSave->WriteShort( &TEMPLATE_SAVE_RESTORE_VERSION );
}
//---------------------------------
void ReadRestoreHeaders( IRestore *pRestore )
{
// No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so.
short version;
pRestore->ReadShort( &version );
m_fDoLoad = ( version == TEMPLATE_SAVE_RESTORE_VERSION );
}
//---------------------------------
void Restore( IRestore *pRestore, bool createPlayers )
{
if ( m_fDoLoad )
{
Templates_RemoveAll();
g_Templates.Purge();
g_iCurrentTemplateInstance = pRestore->ReadInt();
int iTemplates = pRestore->ReadShort();
while ( iTemplates-- )
{
TemplateEntityData_t *pNewTemplate = (TemplateEntityData_t *)malloc(sizeof(TemplateEntityData_t));
pRestore->ReadAll( pNewTemplate );
int sizeData = 0;//pRestore->SkipHeader();
char szName[MAPKEY_MAXLENGTH];
pRestore->ReadString( szName, MAPKEY_MAXLENGTH, sizeData );
pNewTemplate->pszName = strdup( szName );
//sizeData = pRestore->SkipHeader();
pNewTemplate->pszMapData = (char *)malloc( pNewTemplate->iMapDataLength );
pRestore->ReadString( pNewTemplate->pszMapData, pNewTemplate->iMapDataLength, sizeData );
// Set this to NULL so it'll be created the first time it gets used
pNewTemplate->pszFixedMapData = NULL;
g_Templates.AddToTail( pNewTemplate );
}
}
}
private:
bool m_fDoLoad;
};
//-----------------------------------------------------------------------------
CTemplate_SaveRestoreBlockHandler g_Template_SaveRestoreBlockHandler;
//-------------------------------------
ISaveRestoreBlockHandler *GetTemplateSaveRestoreBlockHandler()
{
return &g_Template_SaveRestoreBlockHandler;
}

View File

@@ -0,0 +1,36 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Template entities are used by spawners to create copies of entities
// that were configured by the level designer. This allows us to spawn
// entities with arbitrary sets of key/value data and entity I/O
// connections.
//
//=============================================================================//
#ifndef TEMPLATEENTITIES_H
#define TEMPLATEENTITIES_H
#ifdef _WIN32
#pragma once
#endif
#include "isaverestore.h"
class CBaseEntity;
class CPointTemplate;
int Templates_Add(CBaseEntity *pEntity, const char *pszMapData, int nLen);
string_t Templates_FindByIndex( int iIndex );
int Templates_GetStringSize( int iIndex );
string_t Templates_FindByTargetName(const char *pszName);
void Templates_ReconnectIOForGroup( CPointTemplate *pGroup );
// Some templates have Entity I/O connecting the entities within the template.
// Unique versions of these templates need to be created whenever they're instanced.
void Templates_StartUniqueInstance( void );
bool Templates_IndexRequiresEntityIOFixup( int iIndex );
char *Templates_GetEntityIOFixedMapData( int iIndex );
// Save / Restore
ISaveRestoreBlockHandler *GetTemplateSaveRestoreBlockHandler( void );
#endif // TEMPLATEENTITIES_H

View File

@@ -0,0 +1,118 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Shadow control entity.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//------------------------------------------------------------------------------
// FIXME: This really should inherit from something more lightweight
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Purpose : Water LOD control entity
//------------------------------------------------------------------------------
class CWaterLODControl : public CBaseEntity
{
public:
DECLARE_CLASS( CWaterLODControl, CBaseEntity );
CWaterLODControl();
void Spawn( void );
bool KeyValue( const char *szKeyName, const char *szValue );
int UpdateTransmitState();
void SetCheapWaterStartDistance( inputdata_t &inputdata );
void SetCheapWaterEndDistance( inputdata_t &inputdata );
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
private:
CNetworkVar( float, m_flCheapWaterStartDistance );
CNetworkVar( float, m_flCheapWaterEndDistance );
};
LINK_ENTITY_TO_CLASS(water_lod_control, CWaterLODControl);
BEGIN_DATADESC( CWaterLODControl )
DEFINE_KEYFIELD( m_flCheapWaterStartDistance, FIELD_FLOAT, "cheapwaterstartdistance" ),
DEFINE_KEYFIELD( m_flCheapWaterEndDistance, FIELD_FLOAT, "cheapwaterenddistance" ),
// Inputs
DEFINE_INPUT( m_flCheapWaterStartDistance, FIELD_FLOAT, "SetCheapWaterStartDistance" ),
DEFINE_INPUT( m_flCheapWaterEndDistance, FIELD_FLOAT, "SetCheapWaterEndDistance" ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST_NOBASE(CWaterLODControl, DT_WaterLODControl)
SendPropFloat(SENDINFO(m_flCheapWaterStartDistance), 0, SPROP_NOSCALE ),
SendPropFloat(SENDINFO(m_flCheapWaterEndDistance), 0, SPROP_NOSCALE ),
END_SEND_TABLE()
CWaterLODControl::CWaterLODControl()
{
m_flCheapWaterStartDistance = 1000.0f;
m_flCheapWaterEndDistance = 2000.0f;
}
//------------------------------------------------------------------------------
// Purpose : Send even though we don't have a model
//------------------------------------------------------------------------------
int CWaterLODControl::UpdateTransmitState()
{
// ALWAYS transmit to all clients.
return SetTransmitState( FL_EDICT_ALWAYS );
}
bool CWaterLODControl::KeyValue( const char *szKeyName, const char *szValue )
{
if ( FStrEq( szKeyName, "cheapwaterstartdistance" ) )
{
m_flCheapWaterStartDistance = atof( szValue );
return true;
}
if ( FStrEq( szKeyName, "cheapwaterenddistance" ) )
{
m_flCheapWaterEndDistance = atof( szValue );
return true;
}
return BaseClass::KeyValue( szKeyName, szValue );
}
//------------------------------------------------------------------------------
// Purpose :
//------------------------------------------------------------------------------
void CWaterLODControl::Spawn( void )
{
Precache();
SetSolid( SOLID_NONE );
}
//------------------------------------------------------------------------------
// Input values
//------------------------------------------------------------------------------
void CWaterLODControl::SetCheapWaterStartDistance( inputdata_t &inputdata )
{
m_flCheapWaterStartDistance = atof( inputdata.value.String() );
}
void CWaterLODControl::SetCheapWaterEndDistance( inputdata_t &inputdata )
{
m_flCheapWaterEndDistance = atof( inputdata.value.String() );
}

View File

@@ -0,0 +1,31 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "cbase.h"
#include "actanimating.h"
#include "animation.h"
#include "activitylist.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
BEGIN_DATADESC( CActAnimating )
DEFINE_CUSTOM_FIELD( m_Activity, ActivityDataOps() ),
END_DATADESC()
void CActAnimating::SetActivity( Activity act )
{
int sequence = SelectWeightedSequence( act );
if ( sequence != ACTIVITY_NOT_AVAILABLE )
{
ResetSequence( sequence );
m_Activity = act;
SetCycle( 0 );
}
}

View File

@@ -0,0 +1,35 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ACTANIMATING_H
#define ACTANIMATING_H
#ifdef _WIN32
#pragma once
#endif
#include "baseanimating.h"
class CActAnimating : public CBaseAnimating
{
public:
DECLARE_CLASS( CActAnimating, CBaseAnimating );
void SetActivity( Activity act );
inline Activity GetActivity( void ) { return m_Activity; }
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
DECLARE_DATADESC();
private:
Activity m_Activity;
};
#endif // ACTANIMATING_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,304 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Hooks and classes for the support of humanoid NPCs with
// groovy facial animation capabilities, aka, "Actors"
//
//=============================================================================//
#ifndef AI_BASEACTOR_H
#define AI_BASEACTOR_H
#include "ai_basehumanoid.h"
#include "ai_speech.h"
#include "AI_Interest_Target.h"
#include <limits.h>
#if defined( _WIN32 )
#pragma once
#endif
//-----------------------------------------------------------------------------
// CAI_BaseActor
//
// Purpose: The base class for all head/body/eye expressive NPCS.
//
//-----------------------------------------------------------------------------
enum PoseParameter_t { POSE_END=INT_MAX };
enum FlexWeight_t { FLEX_END=INT_MAX };
struct AILookTargetArgs_t
{
EHANDLE hTarget;
Vector vTarget;
float flDuration;
float flInfluence;
float flRamp;
bool bExcludePlayers;
CAI_InterestTarget *pQueue;
};
class CAI_BaseActor : public CAI_ExpresserHost<CAI_BaseHumanoid>
{
DECLARE_CLASS( CAI_BaseActor, CAI_ExpresserHost<CAI_BaseHumanoid> );
//friend CPoseParameter;
//friend CFlexWeight;
public:
// FIXME: this method is lame, isn't there some sort of template thing that would get rid of the Outer pointer?
void Init( PoseParameter_t &index, const char *szName ) { index = (PoseParameter_t)LookupPoseParameter( szName ); };
void Set( PoseParameter_t index, float flValue ) { SetPoseParameter( (int)index, flValue ); }
float Get( PoseParameter_t index ) { return GetPoseParameter( (int)index ); }
float ClampWithBias( PoseParameter_t index, float value, float base );
// Note, you must add all names to this static function in order for Init to work
static bool IsServerSideFlexController( char const *szName );
void Init( FlexWeight_t &index, const char *szName )
{
// Make this fatal!!!
if ( !IsServerSideFlexController( szName ) )
{
Error( "You forgot to add flex controller %s to list in CAI_BaseActor::IsServerSideFlexController().", szName );
}
index = (FlexWeight_t)FindFlexController( szName );
}
void Set( FlexWeight_t index, float flValue ) { SetFlexWeight( (LocalFlexController_t)index, flValue ); }
float Get( FlexWeight_t index ) { return GetFlexWeight( (LocalFlexController_t)index ); }
public:
CAI_BaseActor()
: m_fLatchedPositions( 0 ),
m_latchedEyeOrigin( vec3_origin ),
m_latchedEyeDirection( vec3_origin ),
m_latchedHeadDirection( vec3_origin ),
m_flBlinktime( 0 ),
m_hLookTarget( NULL ),
m_iszExpressionScene( NULL_STRING ),
m_iszIdleExpression( NULL_STRING ),
m_iszAlertExpression( NULL_STRING ),
m_iszCombatExpression( NULL_STRING ),
m_iszDeathExpression( NULL_STRING ),
m_iszExpressionOverride( NULL_STRING )
{
memset( m_flextarget, 0, 64 * sizeof( m_flextarget[0] ) );
}
~CAI_BaseActor()
{
delete m_pExpresser;
}
virtual void StudioFrameAdvance();
virtual void Precache();
virtual void SetModel( const char *szModelName );
virtual bool StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget );
virtual bool ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event );
virtual bool ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled );
virtual bool CheckSceneEventCompletion( CSceneEventInfo *info, float currenttime, CChoreoScene *scene, CChoreoEvent *event );
Vector EyePosition( );
virtual Vector HeadDirection2D( void );
virtual Vector HeadDirection3D( void );
virtual Vector EyeDirection2D( void );
virtual Vector EyeDirection3D( void );
CBaseEntity *GetLooktarget() { return m_hLookTarget.Get(); }
virtual void OnNewLookTarget() {};
// CBaseFlex
virtual void SetViewtarget( const Vector &viewtarget );
// CAI_BaseNPC
virtual float PickLookTarget( bool bExcludePlayers = false, float minTime = 1.5, float maxTime = 2.5 );
virtual float PickLookTarget( CAI_InterestTarget &queue, bool bExcludePlayers = false, float minTime = 1.5, float maxTime = 2.5 );
virtual bool PickTacticalLookTarget( AILookTargetArgs_t *pArgs );
virtual bool PickRandomLookTarget( AILookTargetArgs_t *pArgs );
virtual void MakeRandomLookTarget( AILookTargetArgs_t *pArgs, float minTime, float maxTime );
virtual bool HasActiveLookTargets( void );
virtual void OnSelectedLookTarget( AILookTargetArgs_t *pArgs ) { return; }
virtual void ClearLookTarget( CBaseEntity *pTarget );
virtual void ExpireCurrentRandomLookTarget() { m_flNextRandomLookTime = gpGlobals->curtime - 0.1f; }
virtual void StartTaskRangeAttack1( const Task_t *pTask );
virtual void AddLookTarget( CBaseEntity *pTarget, float flImportance, float flDuration, float flRamp = 0.0 );
virtual void AddLookTarget( const Vector &vecPosition, float flImportance, float flDuration, float flRamp = 0.0 );
virtual void SetHeadDirection( const Vector &vTargetPos, float flInterval );
void UpdateBodyControl( void );
void UpdateHeadControl( const Vector &vHeadTarget, float flHeadInfluence );
virtual float GetHeadDebounce( void ) { return 0.3; } // how much of previous head turn to use
virtual void MaintainLookTargets( float flInterval );
virtual bool ValidEyeTarget(const Vector &lookTargetPos);
virtual bool ValidHeadTarget(const Vector &lookTargetPos);
virtual float HeadTargetValidity(const Vector &lookTargetPos);
virtual bool ShouldBruteForceFailedNav() { return true; }
void AccumulateIdealYaw( float flYaw, float flIntensity );
bool SetAccumulatedYawAndUpdate( void );
float m_flAccumYawDelta;
float m_flAccumYawScale;
//---------------------------------
virtual void OnStateChange( NPC_STATE OldState, NPC_STATE NewState );
//---------------------------------
virtual void PlayExpressionForState( NPC_STATE state );
virtual const char *SelectRandomExpressionForState( NPC_STATE state );
float SetExpression( const char * );
void ClearExpression();
const char * GetExpression();
enum
{
SCENE_AI_BLINK = 1,
SCENE_AI_HOLSTER,
SCENE_AI_UNHOLSTER,
SCENE_AI_AIM,
SCENE_AI_RANDOMLOOK,
SCENE_AI_RANDOMFACEFLEX,
SCENE_AI_RANDOMHEADFLEX,
SCENE_AI_IGNORECOLLISION,
SCENE_AI_DISABLEAI
};
DECLARE_DATADESC();
private:
enum
{
HUMANOID_LATCHED_EYE = 0x0001,
HUMANOID_LATCHED_HEAD = 0x0002,
HUMANOID_LATCHED_ALL = 0x0003,
};
//---------------------------------
void UpdateLatchedValues( void );
// Input handlers.
void InputSetExpressionOverride( inputdata_t &inputdata );
//---------------------------------
int m_fLatchedPositions;
Vector m_latchedEyeOrigin;
Vector m_latchedEyeDirection; // direction eyes are looking
Vector m_latchedHeadDirection; // direction head is aiming
void ClearHeadAdjustment( void );
Vector m_goalHeadDirection;
float m_goalHeadInfluence;
//---------------------------------
float m_goalSpineYaw;
float m_goalBodyYaw;
Vector m_goalHeadCorrection;
//---------------------------------
float m_flBlinktime;
EHANDLE m_hLookTarget;
CAI_InterestTarget m_lookQueue;
CAI_InterestTarget m_syntheticLookQueue;
CAI_InterestTarget m_randomLookQueue;
float m_flNextRandomLookTime; // FIXME: move to scene
//---------------------------------
string_t m_iszExpressionScene;
EHANDLE m_hExpressionSceneEnt;
float m_flNextRandomExpressionTime;
string_t m_iszExpressionOverride;
protected:
string_t m_iszIdleExpression;
string_t m_iszAlertExpression;
string_t m_iszCombatExpression;
string_t m_iszDeathExpression;
private:
//---------------------------------
//PoseParameter_t m_ParameterBodyTransY; // "body_trans_Y"
//PoseParameter_t m_ParameterBodyTransX; // "body_trans_X"
//PoseParameter_t m_ParameterBodyLift; // "body_lift"
PoseParameter_t m_ParameterBodyYaw; // "body_yaw"
//PoseParameter_t m_ParameterBodyPitch; // "body_pitch"
//PoseParameter_t m_ParameterBodyRoll; // "body_roll"
PoseParameter_t m_ParameterSpineYaw; // "spine_yaw"
//PoseParameter_t m_ParameterSpinePitch; // "spine_pitch"
//PoseParameter_t m_ParameterSpineRoll; // "spine_roll"
PoseParameter_t m_ParameterNeckTrans; // "neck_trans"
PoseParameter_t m_ParameterHeadYaw; // "head_yaw"
PoseParameter_t m_ParameterHeadPitch; // "head_pitch"
PoseParameter_t m_ParameterHeadRoll; // "head_roll"
//FlexWeight_t m_FlexweightMoveRightLeft; // "move_rightleft"
//FlexWeight_t m_FlexweightMoveForwardBack;// "move_forwardback"
//FlexWeight_t m_FlexweightMoveUpDown; // "move_updown"
FlexWeight_t m_FlexweightBodyRightLeft; // "body_rightleft"
//FlexWeight_t m_FlexweightBodyUpDown; // "body_updown"
//FlexWeight_t m_FlexweightBodyTilt; // "body_tilt"
FlexWeight_t m_FlexweightChestRightLeft; // "chest_rightleft"
//FlexWeight_t m_FlexweightChestUpDown; // "chest_updown"
//FlexWeight_t m_FlexweightChestTilt; // "chest_tilt"
FlexWeight_t m_FlexweightHeadForwardBack;// "head_forwardback"
FlexWeight_t m_FlexweightHeadRightLeft; // "head_rightleft"
FlexWeight_t m_FlexweightHeadUpDown; // "head_updown"
FlexWeight_t m_FlexweightHeadTilt; // "head_tilt"
PoseParameter_t m_ParameterGestureHeight; // "gesture_height"
PoseParameter_t m_ParameterGestureWidth; // "gesture_width"
FlexWeight_t m_FlexweightGestureUpDown; // "gesture_updown"
FlexWeight_t m_FlexweightGestureRightLeft; // "gesture_rightleft"
private:
//---------------------------------
bool RandomFaceFlex( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event );
bool RandomHeadFlex( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event );
float m_flextarget[64];
public:
virtual bool UseSemaphore( void );
protected:
bool m_bDontUseSemaphore;
public:
//---------------------------------
//
// Speech support
//
virtual CAI_Expresser *GetExpresser();
protected:
bool CreateComponents();
virtual CAI_Expresser *CreateExpresser();
private:
//---------------------------------
CAI_Expresser *m_pExpresser;
};
//-----------------------------------------------------------------------------
#endif // AI_BASEACTOR_H

View File

@@ -0,0 +1,335 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "BasePropDoor.h"
#include "ai_basehumanoid.h"
#include "ai_blended_movement.h"
#include "ai_navigator.h"
#include "ai_memory.h"
#ifdef HL2_DLL
#include "ai_interactions.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Purpose: This is a generic function (to be implemented by sub-classes) to
// handle specific interactions between different types of characters
// (For example the barnacle grabbing an NPC)
// Input : Constant for the type of interaction
// Output : true - if sub-class has a response for the interaction
// false - if sub-class has no response
//-----------------------------------------------------------------------------
bool CAI_BaseHumanoid::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
{
#ifdef HL2_DLL
// Annoying to ifdef this out. Copy it into all the HL2 specific humanoid NPC's instead?
if ( interactionType == g_interactionBarnacleVictimDangle )
{
// Force choosing of a new schedule
ClearSchedule( "Grabbed by a barnacle" );
return true;
}
else if ( interactionType == g_interactionBarnacleVictimReleased )
{
// Destroy the entity, the barnacle is going to use the ragdoll that it is releasing
// as the corpse.
UTIL_Remove( this );
return true;
}
#endif
return BaseClass::HandleInteraction( interactionType, data, sourceEnt);
}
//-----------------------------------------------------------------------------
// Purpose: check ammo
//-----------------------------------------------------------------------------
void CAI_BaseHumanoid::CheckAmmo( void )
{
BaseClass::CheckAmmo();
// FIXME: put into GatherConditions()?
// FIXME: why isn't this a baseclass function?
if (!GetActiveWeapon())
return;
// Don't do this while holstering / unholstering
if ( IsWeaponStateChanging() )
return;
if (GetActiveWeapon()->UsesPrimaryAmmo())
{
if (!GetActiveWeapon()->HasPrimaryAmmo() )
{
SetCondition(COND_NO_PRIMARY_AMMO);
}
else if (GetActiveWeapon()->UsesClipsForAmmo1() && GetActiveWeapon()->Clip1() < (GetActiveWeapon()->GetMaxClip1() / 4 + 1))
{
// don't check for low ammo if you're near the max range of the weapon
SetCondition(COND_LOW_PRIMARY_AMMO);
}
}
if (!GetActiveWeapon()->HasSecondaryAmmo() )
{
if ( GetActiveWeapon()->UsesClipsForAmmo2() )
{
SetCondition(COND_NO_SECONDARY_AMMO);
}
}
}
//-----------------------------------------------------------------------------
// TASK_RANGE_ATTACK1
//-----------------------------------------------------------------------------
void CAI_BaseHumanoid::BuildScheduleTestBits( )
{
BaseClass::BuildScheduleTestBits();
if ( CapabilitiesGet() & bits_CAP_USE_SHOT_REGULATOR )
{
if ( GetShotRegulator()->IsInRestInterval() )
{
ClearCustomInterruptCondition( COND_CAN_RANGE_ATTACK1 );
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static bool IsSmall( CBaseEntity *pBlocker )
{
CCollisionProperty *pCollisionProp = pBlocker->CollisionProp();
int nSmaller = 0;
Vector vecSize = pCollisionProp->OBBMaxs() - pCollisionProp->OBBMins();
for ( int i = 0; i < 3; i++ )
{
if ( vecSize[i] >= 42 )
return false;
if ( vecSize[i] <= 30 )
{
nSmaller++;
}
}
return ( nSmaller >= 2 );
}
bool CAI_BaseHumanoid::OnMoveBlocked( AIMoveResult_t *pResult )
{
if ( *pResult != AIMR_BLOCKED_NPC && GetNavigator()->GetBlockingEntity() && !GetNavigator()->GetBlockingEntity()->IsNPC() )
{
CBaseEntity *pBlocker = GetNavigator()->GetBlockingEntity();
float massBonus = ( IsNavigationUrgent() ) ? 40.0 : 0;
if ( pBlocker->GetMoveType() == MOVETYPE_VPHYSICS &&
pBlocker != GetGroundEntity() &&
!pBlocker->IsNavIgnored() &&
!dynamic_cast<CBasePropDoor *>(pBlocker) &&
pBlocker->VPhysicsGetObject() &&
pBlocker->VPhysicsGetObject()->IsMoveable() &&
( pBlocker->VPhysicsGetObject()->GetMass() <= 35.0 + massBonus + 0.1 ||
( pBlocker->VPhysicsGetObject()->GetMass() <= 50.0 + massBonus + 0.1 && IsSmall( pBlocker ) ) ) )
{
DbgNavMsg1( this, "Setting ignore on object %s", pBlocker->GetDebugName() );
pBlocker->SetNavIgnore( 2.5 );
}
#if 0
else
{
CPhysicsProp *pProp = dynamic_cast<CPhysicsProp*>( pBlocker );
if ( pProp && pProp->GetHealth() && pProp->GetExplosiveDamage() == 0.0 && GetActiveWeapon() && !GetActiveWeapon()->ClassMatches( "weapon_rpg" ) )
{
Msg( "!\n" );
// Destroy!
}
}
#endif
}
return BaseClass::OnMoveBlocked( pResult );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#define SNEAK_ATTACK_DIST 360.0f // 30 feet
void CAI_BaseHumanoid::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
{
bool bSneakAttacked = false;
if( ptr->hitgroup == HITGROUP_HEAD )
{
if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() && info.GetAttacker() != GetEnemy() && !IsInAScript() )
{
// Shot in the head by a player I've never seen. In this case the player
// has gotten the drop on this enemy and such an attack is always lethal (at close range)
bSneakAttacked = true;
AIEnemiesIter_t iter;
for( AI_EnemyInfo_t *pMemory = GetEnemies()->GetFirst(&iter); pMemory != NULL; pMemory = GetEnemies()->GetNext(&iter) )
{
if ( pMemory->hEnemy == info.GetAttacker() )
{
bSneakAttacked = false;
break;
}
}
float flDist;
flDist = (info.GetAttacker()->GetAbsOrigin() - GetAbsOrigin()).Length();
if( flDist > SNEAK_ATTACK_DIST )
{
bSneakAttacked = false;
}
}
}
if( bSneakAttacked )
{
CTakeDamageInfo newInfo = info;
newInfo.SetDamage( GetHealth() );
BaseClass::TraceAttack( newInfo, vecDir, ptr, pAccumulator );
return;
}
BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
}
//-----------------------------------------------------------------------------
// TASK_RANGE_ATTACK1
//-----------------------------------------------------------------------------
void CAI_BaseHumanoid::StartTaskRangeAttack1( const Task_t *pTask )
{
if ( ( CapabilitiesGet() & bits_CAP_USE_SHOT_REGULATOR ) == 0 )
{
BaseClass::StartTask( pTask );
return;
}
// Can't shoot if we're in the rest interval; fail the schedule
if ( GetShotRegulator()->IsInRestInterval() )
{
TaskFail( "Shot regulator in rest interval" );
return;
}
if ( GetShotRegulator()->ShouldShoot() )
{
OnRangeAttack1();
ResetIdealActivity( ACT_RANGE_ATTACK1 );
}
else
{
// This can happen if we start while in the middle of a burst
// which shouldn't happen, but given the chaotic nature of our AI system,
// does occasionally happen.
ResetIdealActivity( ACT_IDLE_ANGRY );
}
}
//-----------------------------------------------------------------------------
// Starting Tasks
//-----------------------------------------------------------------------------
void CAI_BaseHumanoid::StartTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_RANGE_ATTACK1:
StartTaskRangeAttack1( pTask );
break;
default:
BaseClass::StartTask( pTask );
}
}
//-----------------------------------------------------------------------------
// TASK_RANGE_ATTACK1 / TASK_RANGE_ATTACK2 / etc.
//-----------------------------------------------------------------------------
void CAI_BaseHumanoid::RunTaskRangeAttack1( const Task_t *pTask )
{
if ( ( CapabilitiesGet() & bits_CAP_USE_SHOT_REGULATOR ) == 0 )
{
BaseClass::RunTask( pTask );
return;
}
AutoMovement( );
Vector vecEnemyLKP = GetEnemyLKP();
// If our enemy was killed, but I'm not done animating, the last known position comes
// back as the origin and makes the me face the world origin if my attack schedule
// doesn't break when my enemy dies. (sjb)
if( vecEnemyLKP != vec3_origin )
{
if ( ( pTask->iTask == TASK_RANGE_ATTACK1 || pTask->iTask == TASK_RELOAD ) &&
( CapabilitiesGet() & bits_CAP_AIM_GUN ) &&
FInAimCone( vecEnemyLKP ) )
{
// Arms will aim, so leave body yaw as is
GetMotor()->SetIdealYawAndUpdate( GetMotor()->GetIdealYaw(), AI_KEEP_YAW_SPEED );
}
else
{
GetMotor()->SetIdealYawToTargetAndUpdate( vecEnemyLKP, AI_KEEP_YAW_SPEED );
}
}
if ( IsActivityFinished() )
{
if ( !GetEnemy() || !GetEnemy()->IsAlive() )
{
TaskComplete();
return;
}
if ( !GetShotRegulator()->IsInRestInterval() )
{
if ( GetShotRegulator()->ShouldShoot() )
{
OnRangeAttack1();
ResetIdealActivity( ACT_RANGE_ATTACK1 );
}
return;
}
TaskComplete();
}
}
//-----------------------------------------------------------------------------
// Running Tasks
//-----------------------------------------------------------------------------
void CAI_BaseHumanoid::RunTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_RANGE_ATTACK1:
RunTaskRangeAttack1( pTask );
break;
default:
BaseClass::RunTask( pTask );
}
}

View File

@@ -0,0 +1,50 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef AI_BASEHUMANOID_H
#define AI_BASEHUMANOID_H
#include "ai_behavior.h"
#include "ai_blended_movement.h"
//-----------------------------------------------------------------------------
// CLASS: CAI_BaseHumanoid
//-----------------------------------------------------------------------------
typedef CAI_BlendingHost< CAI_BehaviorHost<CAI_BaseNPC> > CAI_BaseHumanoidBase;
class CAI_BaseHumanoid : public CAI_BaseHumanoidBase
{
DECLARE_CLASS( CAI_BaseHumanoid, CAI_BaseHumanoidBase );
public:
bool HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt);
// Tasks
virtual void StartTask( const Task_t *pTask );
virtual void RunTask( const Task_t *pTask );
virtual void BuildScheduleTestBits( );
// Navigation
bool OnMoveBlocked( AIMoveResult_t *pResult );
// Damage
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
// Various start tasks
virtual void StartTaskRangeAttack1( const Task_t *pTask );
// Various run tasks
virtual void RunTaskRangeAttack1( const Task_t *pTask );
// Purpose: check ammo
virtual void CheckAmmo( void );
};
//-----------------------------------------------------------------------------
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,263 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Base class for many flying NPCs
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "ai_basenpc_flyer.h"
#include "ai_route.h"
#include "ai_navigator.h"
#include "ai_motor.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
BEGIN_DATADESC( CAI_BaseFlyingBot )
DEFINE_FIELD( m_vCurrentVelocity, FIELD_VECTOR),
DEFINE_FIELD( m_vCurrentAngularVelocity, FIELD_VECTOR ),
DEFINE_FIELD( m_vCurrentBanking, FIELD_VECTOR),
DEFINE_FIELD( m_vNoiseMod, FIELD_VECTOR),
DEFINE_FIELD( m_fHeadYaw, FIELD_FLOAT),
DEFINE_FIELD( m_vLastPatrolDir, FIELD_VECTOR),
END_DATADESC()
//------------------------------------------------------------------------------
// Purpose : Override to return correct velocity
// Input :
// Output :
//------------------------------------------------------------------------------
void CAI_BaseFlyingBot::GetVelocity(Vector *vVelocity, AngularImpulse *vAngVelocity)
{
if (vVelocity != NULL)
{
VectorCopy(m_vCurrentVelocity,*vVelocity);
}
if (vAngVelocity != NULL)
{
QAngle tmp = GetLocalAngularVelocity();
QAngleToAngularImpulse( tmp, *vAngVelocity );
}
}
//-----------------------------------------------------------------------------
// Purpose: Turn head yaw into facing direction
// Input :
// Output :
//-----------------------------------------------------------------------------
QAngle CAI_BaseFlyingBot::BodyAngles()
{
return QAngle(0,m_fHeadYaw,0);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CAI_BaseFlyingBot::TurnHeadToTarget(float flInterval, const Vector &MoveTarget )
{
float flDestYaw = VecToYaw( MoveTarget - GetLocalOrigin() );
float newYaw = AI_ClampYaw( GetHeadTurnRate() * 10.0f, m_fHeadYaw, flDestYaw, gpGlobals->curtime - GetLastThink() );
if ( newYaw != m_fHeadYaw )
{
m_fHeadYaw = newYaw;
}
// Set us to face that way
SetBoneController( 0, m_fHeadYaw );
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
float CAI_BaseFlyingBot::MinGroundDist(void)
{
return 0;
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
Vector CAI_BaseFlyingBot::VelocityToAvoidObstacles(float flInterval)
{
// --------------------------------
// Avoid banging into stuff
// --------------------------------
trace_t tr;
Vector vTravelDir = m_vCurrentVelocity*flInterval;
Vector endPos = GetAbsOrigin() + vTravelDir;
AI_TraceEntity( this, GetAbsOrigin(), endPos, MASK_NPCSOLID|CONTENTS_WATER, &tr );
if (tr.fraction != 1.0)
{
// Bounce off in normal
Vector vBounce = tr.plane.normal * 0.5 * m_vCurrentVelocity.Length();
return (vBounce);
}
// --------------------------------
// Try to remain above the ground.
// --------------------------------
float flMinGroundDist = MinGroundDist();
AI_TraceLine(GetAbsOrigin(), GetAbsOrigin() + Vector(0, 0, -flMinGroundDist),
MASK_NPCSOLID_BRUSHONLY|CONTENTS_WATER, this, COLLISION_GROUP_NONE, &tr);
if (tr.fraction < 1)
{
// Clamp veloctiy
if (tr.fraction < 0.1)
{
tr.fraction = 0.1;
}
return Vector(0, 0, 50/tr.fraction);
}
return vec3_origin;
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CAI_BaseFlyingBot::StartTask( const Task_t *pTask )
{
switch (pTask->iTask)
{
// Skip as done via bone controller
case TASK_FACE_ENEMY:
{
TaskComplete();
break;
}
// Activity is just idle (have no run)
case TASK_RUN_PATH:
{
GetNavigator()->SetMovementActivity(ACT_IDLE);
TaskComplete();
break;
}
// Don't check for run/walk activity
case TASK_SCRIPT_RUN_TO_TARGET:
case TASK_SCRIPT_WALK_TO_TARGET:
{
if (GetTarget() == NULL)
{
TaskFail(FAIL_NO_TARGET);
}
else
{
if (!GetNavigator()->SetGoal( GOALTYPE_TARGETENT ) )
{
TaskFail(FAIL_NO_ROUTE);
GetNavigator()->ClearGoal();
}
}
TaskComplete();
break;
}
// Override to get more to get a directional path
case TASK_GET_PATH_TO_RANDOM_NODE:
{
if ( GetNavigator()->SetRandomGoal( pTask->flTaskData, m_vLastPatrolDir ) )
TaskComplete();
else
TaskFail(FAIL_NO_REACHABLE_NODE);
break;
}
default:
{
BaseClass::StartTask(pTask);
}
}
}
//------------------------------------------------------------------------------
void CAI_BaseFlyingBot::MoveToTarget(float flInterval, const Vector &MoveTarget)
{
Assert(0); // This must be overridden in the leaf classes
}
//------------------------------------------------------------------------------
AI_NavPathProgress_t CAI_BaseFlyingBot::ProgressFlyPath(
float flInterval,
const CBaseEntity *pNewTarget,
unsigned collisionMask,
bool bNewTrySimplify,
float strictPointTolerance)
{
AI_ProgressFlyPathParams_t params( collisionMask, strictPointTolerance );
params.SetCurrent( pNewTarget, bNewTrySimplify );
AI_NavPathProgress_t progress = GetNavigator()->ProgressFlyPath( params );
switch ( progress )
{
case AINPP_NO_CHANGE:
case AINPP_ADVANCED:
{
MoveToTarget(flInterval, GetNavigator()->GetCurWaypointPos());
break;
}
case AINPP_COMPLETE:
{
TaskMovementComplete();
break;
}
case AINPP_BLOCKED: // function is not supposed to test blocking, just simple path progression
default:
{
AssertMsg( 0, ( "Unexpected result" ) );
break;
}
}
return progress;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTarget -
// &chasePosition -
//-----------------------------------------------------------------------------
void CAI_BaseFlyingBot::TranslateNavGoal( CBaseEntity *pTarget, Vector &chasePosition )
{
Assert( pTarget != NULL );
if ( pTarget == NULL )
{
chasePosition = vec3_origin;
return;
}
// Chase their eyes
chasePosition = pTarget->GetAbsOrigin() + pTarget->GetViewOffset();
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
CAI_BaseFlyingBot::CAI_BaseFlyingBot()
{
#ifdef _DEBUG
m_vCurrentVelocity.Init();
m_vCurrentBanking.Init();
m_vLastPatrolDir.Init();
#endif
}

View File

@@ -0,0 +1,132 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef AI_BASENPC_FLYER_H
#define AI_BASENPC_FLYER_H
#ifdef _WIN32
#pragma once
#endif
#include "ai_basenpc.h"
#include "ai_navigator.h"
//-----------------------------------------------------------------------------
// The combot.
//-----------------------------------------------------------------------------
abstract_class CAI_BaseFlyingBot : public CAI_BaseNPC
{
DECLARE_CLASS( CAI_BaseFlyingBot, CAI_BaseNPC );
public:
DECLARE_DATADESC();
void StartTask( const Task_t *pTask );
void GetVelocity(Vector *vVelocity, AngularImpulse *vAngVelocity);
virtual QAngle BodyAngles();
protected:
CAI_BaseFlyingBot();
Vector VelocityToAvoidObstacles(float flInterval);
virtual float MinGroundDist(void);
void TurnHeadToTarget( float flInterval, const Vector &moveTarget );
void MoveInDirection( float flInterval, const Vector &targetDir,
float accelXY, float accelZ, float decay)
{
decay = ExponentialDecay( decay, 1.0, flInterval );
accelXY *= flInterval;
accelZ *= flInterval;
m_vCurrentVelocity.x = ( decay * m_vCurrentVelocity.x + accelXY * targetDir.x );
m_vCurrentVelocity.y = ( decay * m_vCurrentVelocity.y + accelXY * targetDir.y );
m_vCurrentVelocity.z = ( decay * m_vCurrentVelocity.z + accelZ * targetDir.z );
}
void MoveToLocation( float flInterval, const Vector &target,
float accelXY, float accelZ, float decay)
{
Vector targetDir = target - GetLocalOrigin();
VectorNormalize(targetDir);
MoveInDirection(flInterval, targetDir, accelXY, accelZ, decay);
}
void Decelerate( float flInterval, float decay )
{
decay *= flInterval;
m_vCurrentVelocity.x = (decay * m_vCurrentVelocity.x);
m_vCurrentVelocity.y = (decay * m_vCurrentVelocity.y);
m_vCurrentVelocity.z = (decay * m_vCurrentVelocity.z);
}
void AddNoiseToVelocity( float noiseScale = 1.0 )
{
if( m_vNoiseMod.x )
{
m_vCurrentVelocity.x += noiseScale*sin(m_vNoiseMod.x * gpGlobals->curtime + m_vNoiseMod.x);
}
if( m_vNoiseMod.y )
{
m_vCurrentVelocity.y += noiseScale*cos(m_vNoiseMod.y * gpGlobals->curtime + m_vNoiseMod.y);
}
if( m_vNoiseMod.z )
{
m_vCurrentVelocity.z -= noiseScale*cos(m_vNoiseMod.z * gpGlobals->curtime + m_vNoiseMod.z);
}
}
void LimitSpeed( float zLimit, float maxSpeed = -1 )
{
if ( maxSpeed == -1 )
maxSpeed = m_flSpeed;
if (m_vCurrentVelocity.Length() > maxSpeed)
{
VectorNormalize(m_vCurrentVelocity);
m_vCurrentVelocity *= maxSpeed;
}
// Limit fall speed
if (zLimit > 0 && m_vCurrentVelocity.z < -zLimit)
{
m_vCurrentVelocity.z = -zLimit;
}
}
AI_NavPathProgress_t ProgressFlyPath( float flInterval,
const CBaseEntity *pNewTarget,
unsigned collisionMask,
bool bNewTrySimplify = true,
float strictPointTolerance = 32.0 );
virtual float GetHeadTurnRate( void ) { return 15.0f; } // Degrees per second
const Vector &GetCurrentVelocity() const { return m_vCurrentVelocity; }
void SetCurrentVelocity(const Vector &vNewVel) { m_vCurrentVelocity = vNewVel; }
const Vector &GetNoiseMod() const { return m_vNoiseMod; }
void SetNoiseMod( float x, float y, float z ) { m_vNoiseMod.Init( x, y, z ); }
void SetNoiseMod( const Vector &noise ) { m_vNoiseMod = noise; }
virtual void MoveToTarget(float flInterval, const Vector &MoveTarget) = 0;
void TranslateNavGoal( CBaseEntity *pTarget, Vector &chasePosition );
// -------------------------------
// Movement vars
// -------------------------------
Vector m_vCurrentVelocity;
Vector m_vCurrentAngularVelocity;
Vector m_vCurrentBanking;
Vector m_vNoiseMod;
float m_fHeadYaw;
Vector m_vLastPatrolDir;
};
#endif // AI_BASENPC_FLYER_H

View File

@@ -0,0 +1,140 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Base class for many flying NPCs
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "ai_basenpc_flyer_new.h"
#include "ai_route.h"
#include "ai_navigator.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define FLYER_ROUTE_REBUILD_TIME 3.0 // Time between route rebuilds
// NOTE: Never instantiate ai_base_npc_flyer_new directly!!
//IMPLEMENT_CUSTOM_AI( ai_base_npc_flyer_new, CAI_BaseNPCFlyerNew);
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
CAI_BaseNPCFlyerNew::CAI_BaseNPCFlyerNew()
{
}
//------------------------------------------------------------------------------
// Used to set up a flyer
//------------------------------------------------------------------------------
void CAI_BaseNPCFlyerNew::SpawnFlyer()
{
SetNavType( NAV_FLY );
AddFlag( FL_FLY );
SetMoveType( MOVETYPE_STEP );
CapabilitiesAdd( bits_CAP_MOVE_FLY );
}
/*
void CAI_BaseNPCFlyerNew::InitCustomSchedules(void)
{
INIT_CUSTOM_AI(CAI_BaseNPCFlyerNew);
ADD_CUSTOM_CONDITION(CAI_BaseNPCFlyerNew, COND_FLYER_MOVE_BLOCKED);
ADD_CUSTOM_CONDITION(CAI_BaseNPCFlyerNew, COND_FLYER_MOVE_IMPOSSIBLE);
}
*/
//------------------------------------------------------------------------------
// Should be called during Select Schedule (BLEAH!)
//------------------------------------------------------------------------------
void CAI_BaseNPCFlyerNew::ClearFlyerConditions(void)
{
// ClearCondition( COND_FLYER_MOVE_BLOCKED );
// ClearCondition( COND_FLYER_MOVE_IMPOSSIBLE );
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
float CAI_BaseNPCFlyerNew::MinGroundDist(void)
{
return 0;
}
//-----------------------------------------------------------------------------
// Sets the ground speed appropriately:
//-----------------------------------------------------------------------------
float CAI_BaseNPCFlyerNew::GetIdealSpeed( ) const
{
return m_flSpeed;
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CAI_BaseNPCFlyerNew::StartTask( const Task_t *pTask )
{
switch (pTask->iTask)
{
// Activity is just idle (have no run)
case TASK_RUN_PATH:
{
GetNavigator()->SetMovementActivity(ACT_IDLE);
TaskComplete();
break;
}
// Don't check for run/walk activity
case TASK_SCRIPT_RUN_TO_TARGET:
case TASK_SCRIPT_WALK_TO_TARGET:
{
if (GetTarget() == NULL)
{
TaskFail(FAIL_NO_TARGET);
}
else
{
if (!GetNavigator()->SetGoal( GOALTYPE_TARGETENT ) )
{
TaskFail(FAIL_NO_ROUTE);
GetNavigator()->ClearGoal();
}
}
TaskComplete();
break;
}
default:
{
BaseClass::StartTask(pTask);
}
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAI_BaseNPCFlyerNew::RunTask( const Task_t *pTask )
{
BaseClass::RunTask(pTask);
}

View File

@@ -0,0 +1,58 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef AI_BASENPC_FLYER_NEW_H
#define AI_BASENPC_FLYER_NEW_H
#ifdef _WIN32
#pragma once
#endif
#include "ai_basenpc.h"
#include "ai_condition.h"
enum BaseNPCFlyerConditions_t
{
COND_FLYER_MOVE_BLOCKED = LAST_SHARED_CONDITION,
COND_FLYER_MOVE_IMPOSSIBLE,
// ======================================
// IMPORTANT: This must be the last enum
// ======================================
LAST_FLYER_SHARED_CONDITION
};
//-----------------------------------------------------------------------------
// The combot.
//-----------------------------------------------------------------------------
class CAI_BaseNPCFlyerNew : public CAI_BaseNPC
{
DECLARE_CLASS( CAI_BaseNPCFlyerNew, CAI_BaseNPC );
public:
// DEFINE_CUSTOM_AI;
virtual void StartTask( const Task_t *pTask );
virtual void RunTask( const Task_t *pTask );
virtual float GetIdealSpeed( ) const;
virtual float MinGroundDist(void);
CAI_BaseNPCFlyerNew();
protected:
// Call this to set up a flyer
void SpawnFlyer();
// Yarg! Must be chained down from leaf classes...
void ClearFlyerConditions(void);
// Override this when we had to abort movement
virtual void AbortedMovement( void ) {}
};
#endif // AI_BASENPC_FLYER_NEW_H

View File

@@ -0,0 +1,499 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "game.h"
#include "ndebugoverlay.h"
#include "ai_basenpc.h"
#include "ai_hull.h"
#include "ai_node.h"
#include "ai_motor.h"
#include "ai_navigator.h"
#include "ai_hint.h"
#include "scripted.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//=============================================================================
// PATHING & HIGHER LEVEL MOVEMENT
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Static debug function to force all selected npcs to go to the
// given node
// Input :
// Output :
//-----------------------------------------------------------------------------
void CAI_BaseNPC::ForceSelectedGo(CBaseEntity *pPlayer, const Vector &targetPos, const Vector &traceDir, bool bRun)
{
CAI_BaseNPC *npc = gEntList.NextEntByClass( (CAI_BaseNPC *)NULL );
while (npc)
{
if (npc->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)
{
Vector chasePosition = targetPos;
npc->TranslateNavGoal( pPlayer, chasePosition );
// It it legal to drop me here
Vector vUpBit = chasePosition;
vUpBit.z += 1;
trace_t tr;
AI_TraceHull( chasePosition, vUpBit, npc->GetHullMins(),
npc->GetHullMaxs(), MASK_NPCSOLID, npc, COLLISION_GROUP_NONE, &tr );
if (tr.startsolid || tr.fraction != 1.0 )
{
NDebugOverlay::BoxAngles(chasePosition, npc->GetHullMins(),
npc->GetHullMaxs(), npc->GetAbsAngles(), 255,0,0,20,0.5);
}
npc->m_vecLastPosition = chasePosition;
if (npc->m_hCine != NULL)
{
npc->ExitScriptedSequence();
}
if ( bRun )
npc->SetSchedule( SCHED_FORCED_GO_RUN );
else
npc->SetSchedule( SCHED_FORCED_GO );
npc->m_flMoveWaitFinished = gpGlobals->curtime;
}
npc = gEntList.NextEntByClass(npc);
}
}
//-----------------------------------------------------------------------------
// Purpose: Static debug function to make all selected npcs run around
// Input :
// Output :
//-----------------------------------------------------------------------------
void CAI_BaseNPC::ForceSelectedGoRandom(void)
{
CAI_BaseNPC *npc = gEntList.NextEntByClass( (CAI_BaseNPC *)NULL );
while (npc)
{
if (npc->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)
{
npc->SetSchedule( SCHED_RUN_RANDOM );
npc->GetNavigator()->SetMovementActivity(ACT_RUN);
}
npc = gEntList.NextEntByClass(npc);
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::ScheduledMoveToGoalEntity( int scheduleType, CBaseEntity *pGoalEntity, Activity movementActivity )
{
if ( m_NPCState == NPC_STATE_NONE )
{
// More than likely being grabbed before first think. Set ideal state to prevent schedule stomp
m_NPCState = m_IdealNPCState;
}
SetSchedule( scheduleType );
SetGoalEnt( pGoalEntity );
// HACKHACK: Call through TranslateNavGoal to fixup this goal position
// UNDONE: Remove this and have NPCs that need this functionality fix up paths in the
// movement system instead of when they are specified.
AI_NavGoal_t goal(pGoalEntity->GetAbsOrigin(), movementActivity, AIN_DEF_TOLERANCE, AIN_YAW_TO_DEST);
TranslateNavGoal( pGoalEntity, goal.dest );
return GetNavigator()->SetGoal( goal );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::ScheduledFollowPath( int scheduleType, CBaseEntity *pPathStart, Activity movementActivity )
{
if ( m_NPCState == NPC_STATE_NONE )
{
// More than likely being grabbed before first think. Set ideal state to prevent schedule stomp
m_NPCState = m_IdealNPCState;
}
SetSchedule( scheduleType );
SetGoalEnt( pPathStart );
// HACKHACK: Call through TranslateNavGoal to fixup this goal position
AI_NavGoal_t goal(GOALTYPE_PATHCORNER, pPathStart->GetLocalOrigin(), movementActivity, AIN_DEF_TOLERANCE, AIN_YAW_TO_DEST);
TranslateNavGoal( pPathStart, goal.dest );
return GetNavigator()->SetGoal( goal );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::IsMoving( void )
{
return GetNavigator()->IsGoalSet();
}
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::IsCurTaskContinuousMove()
{
const Task_t* pTask = GetTask();
// This bit of logic strikes me funny, but the case does exist. (sjb)
if( !pTask )
return true;
switch( pTask->iTask )
{
case TASK_WAIT_FOR_MOVEMENT:
case TASK_MOVE_TO_TARGET_RANGE:
case TASK_MOVE_TO_GOAL_RANGE:
case TASK_WEAPON_RUN_PATH:
case TASK_PLAY_SCENE:
case TASK_RUN_PATH_TIMED:
case TASK_WALK_PATH_TIMED:
case TASK_RUN_PATH_FOR_UNITS:
case TASK_WALK_PATH_FOR_UNITS:
case TASK_RUN_PATH_FLEE:
case TASK_WALK_PATH_WITHIN_DIST:
case TASK_RUN_PATH_WITHIN_DIST:
return true;
break;
default:
return false;
break;
}
}
//-----------------------------------------------------------------------------
// Purpose: Used to specify that the NPC has a reason not to use the a navigation node
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::IsUnusableNode(int iNodeID, CAI_Hint *pHint)
{
if ( m_bHintGroupNavLimiting && m_strHintGroup != NULL_STRING && STRING(m_strHintGroup)[0] != 0 )
{
if (!pHint || pHint->GetGroup() != GetHintGroup())
{
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Checks the validity of the given route's goaltype
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::ValidateNavGoal()
{
if (GetNavigator()->GetGoalType() == GOALTYPE_COVER)
{
// Check if this location will block my enemy's line of sight to me
if (GetEnemy())
{
Activity nCoverActivity = GetCoverActivity( GetHintNode() );
Vector vCoverLocation = GetNavigator()->GetGoalPos();
// For now we have to drop the node to the floor so we can
// get an accurate postion of the NPC. Should change once Ken checks in
float floorZ = GetFloorZ(vCoverLocation);
vCoverLocation.z = floorZ;
Vector vEyePos = vCoverLocation + EyeOffset(nCoverActivity);
if (!IsCoverPosition( GetEnemy()->EyePosition(), vEyePos ) )
{
TaskFail(FAIL_BAD_PATH_GOAL);
return false;
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
float CAI_BaseNPC::OpenDoorAndWait( CBaseEntity *pDoor )
{
float flTravelTime = 0;
//DevMsg( 2, "A door. ");
if (pDoor && !pDoor->IsLockedByMaster())
{
pDoor->Use(this, this, USE_ON, 0.0);
flTravelTime = pDoor->GetMoveDoneTime();
if ( pDoor->GetEntityName() != NULL_STRING )
{
CBaseEntity *pTarget = NULL;
for (;;)
{
pTarget = gEntList.FindEntityByName( pTarget, pDoor->GetEntityName() );
if ( pTarget != pDoor )
{
if ( !pTarget )
break;
if ( FClassnameIs( pTarget, pDoor->GetClassname() ) )
{
pTarget->Use(this, this, USE_ON, 0.0);
}
}
}
}
}
return gpGlobals->curtime + flTravelTime;
}
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::CanStandOn( CBaseEntity *pSurface ) const
{
if ( !pSurface->IsAIWalkable() )
{
return false;
}
CAI_Navigator *pNavigator = const_cast<CAI_Navigator *>(GetNavigator());
if ( pNavigator->IsGoalActive() &&
pSurface == pNavigator->GetGoalTarget() )
return false;
return BaseClass::CanStandOn( pSurface );
}
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos,
float maxUp, float maxDown, float maxDist ) const
{
if ((endPos.z - startPos.z) > maxUp + 0.1)
return false;
if ((startPos.z - endPos.z) > maxDown + 0.1)
return false;
if ((apex.z - startPos.z) > maxUp * 1.25 )
return false;
float dist = (startPos - endPos).Length();
if ( dist > maxDist + 0.1)
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if a reasonable jumping distance
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos ) const
{
const float MAX_JUMP_RISE = 80.0f;
const float MAX_JUMP_DISTANCE = 250.0f;
const float MAX_JUMP_DROP = 192.0f;
return IsJumpLegal( startPos, apex, endPos, MAX_JUMP_RISE, MAX_JUMP_DROP, MAX_JUMP_DISTANCE );
}
//-----------------------------------------------------------------------------
// Purpose: Returns a throw velocity from start to end position
// Input :
// Output :
//-----------------------------------------------------------------------------
Vector CAI_BaseNPC::CalcThrowVelocity(const Vector &startPos, const Vector &endPos, float fGravity, float fArcSize)
{
// Get the height I have to throw to get to the target
float stepHeight = endPos.z - startPos.z;
float throwHeight = 0;
// -----------------------------------------------------------------
// Now calcluate the distance to a point halfway between our current
// and target position. (the apex of our throwing arc)
// -----------------------------------------------------------------
Vector targetDir2D = endPos - startPos;
targetDir2D.z = 0;
float distance = VectorNormalize(targetDir2D);
// If jumping up we want to throw a bit higher than the height diff
if (stepHeight > 0)
{
throwHeight = stepHeight + fArcSize;
}
else
{
throwHeight = fArcSize;
}
// Make sure that I at least catch some air
if (throwHeight < fArcSize)
{
throwHeight = fArcSize;
}
// -------------------------------------------------------------
// calculate the vertical and horizontal launch velocities
// -------------------------------------------------------------
float velVert = (float)sqrt(2.0f*fGravity*throwHeight);
float divisor = velVert;
divisor += (float)sqrt((2.0f*(-fGravity)*(stepHeight-throwHeight)));
float velHorz = (distance * fGravity)/divisor;
// -----------------------------------------------------------
// Make the horizontal throw vector and add vertical component
// -----------------------------------------------------------
Vector throwVel = targetDir2D * velHorz;
throwVel.z = velVert;
return throwVel;
}
bool CAI_BaseNPC::ShouldMoveWait()
{
return (m_flMoveWaitFinished > gpGlobals->curtime);
}
float CAI_BaseNPC::GetStepDownMultiplier() const
{
return m_pNavigator->GetStepDownMultiplier();
}
//-----------------------------------------------------------------------------
// Purpose: execute any movement this sequence may have
// Output :
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::AutoMovement( CBaseEntity *pTarget, AIMoveTrace_t *pTraceResult )
{
return AutoMovement( GetAnimTimeInterval(), pTarget, pTraceResult );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : flInterval -
// -
// *pTraceResult -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::AutoMovement( float flInterval, CBaseEntity *pTarget, AIMoveTrace_t *pTraceResult )
{
bool ignored;
Vector newPos;
QAngle newAngles;
if (flInterval <= 0.0)
return true;
m_ScheduleState.bTaskRanAutomovement = true;
if (GetIntervalMovement( flInterval, ignored, newPos, newAngles ))
{
// DevMsg( "%.2f : (%.1f) %.1f %.1f %.1f\n", gpGlobals->curtime, (newPos - GetLocalOrigin()).Length(), newPos.x, newPos.y, newAngles.y );
if ( m_hCine )
{
m_hCine->ModifyScriptedAutoMovement( &newPos );
}
if (GetMoveType() == MOVETYPE_STEP)
{
if (!(GetFlags() & FL_FLY))
{
if ( !pTarget )
{
pTarget = GetNavTargetEntity();
}
return ( GetMotor()->MoveGroundStep( newPos, pTarget, newAngles.y, false, true, pTraceResult ) == AIM_SUCCESS );
}
else
{
// FIXME: here's no direct interface to a fly motor, plus this needs to support a state where going through the world is okay.
// FIXME: add callbacks into the script system for validation
// FIXME: add function on scripts to force only legal movements
// FIXME: GetIntervalMovement deals in Local space, nor global. Currently now way to communicate that through these interfaces.
SetLocalOrigin( newPos );
SetLocalAngles( newAngles );
return true;
}
}
else if (GetMoveType() == MOVETYPE_FLY)
{
Vector dist = newPos - GetLocalOrigin();
VectorScale( dist, 1.0 / flInterval, dist );
SetLocalVelocity( dist );
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: return max 1/10 second rate of turning
// Input :
// Output :
//-----------------------------------------------------------------------------
float CAI_BaseNPC::MaxYawSpeed( void )
{
return 45;
}
//-----------------------------------------------------------------------------
// Returns the estimate in seconds before we reach our nav goal.
// -1 means we don't know / haven't calculated it yet.
//-----------------------------------------------------------------------------
float CAI_BaseNPC::GetTimeToNavGoal()
{
float flDist = GetNavigator()->BuildAndGetPathDistToGoal();
if ( flDist < 0 )
{
return -1.0f;
}
float flSpeed = GetIdealSpeed();
// FIXME: needs to consider stopping time!
if (flSpeed > 0 && flDist > 0)
{
return flDist / flSpeed;
}
return 0.0;
}
//=============================================================================

View File

@@ -0,0 +1,382 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Base class for many flying NPCs
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "ai_basenpc_physicsflyer.h"
#include "ai_route.h"
#include "ai_navigator.h"
#include "ai_motor.h"
#include "physics_saverestore.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
BEGIN_DATADESC( CAI_BasePhysicsFlyingBot )
DEFINE_FIELD( m_vCurrentVelocity, FIELD_VECTOR),
DEFINE_FIELD( m_vCurrentBanking, FIELD_VECTOR),
DEFINE_FIELD( m_vNoiseMod, FIELD_VECTOR),
DEFINE_FIELD( m_fHeadYaw, FIELD_FLOAT),
DEFINE_FIELD( m_vLastPatrolDir, FIELD_VECTOR),
DEFINE_PHYSPTR( m_pMotionController ),
END_DATADESC()
//------------------------------------------------------------------------------
// Purpose : Override to return correct velocity
// Input :
// Output :
//------------------------------------------------------------------------------
void CAI_BasePhysicsFlyingBot::GetVelocity(Vector *vVelocity, AngularImpulse *vAngVelocity)
{
Assert( GetMoveType() == MOVETYPE_VPHYSICS );
if ( VPhysicsGetObject() )
{
VPhysicsGetObject()->GetVelocity( vVelocity, vAngVelocity );
}
else
{
if ( vVelocity )
{
vVelocity->Init();
}
if ( vAngVelocity )
{
vAngVelocity->Init();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Turn head yaw into facing direction
// Input :
// Output :
//-----------------------------------------------------------------------------
QAngle CAI_BasePhysicsFlyingBot::BodyAngles()
{
return QAngle(0,m_fHeadYaw,0);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CAI_BasePhysicsFlyingBot::TurnHeadToTarget(float flInterval, const Vector &MoveTarget )
{
float desYaw = UTIL_AngleDiff(VecToYaw(MoveTarget - GetLocalOrigin()), 0 );
m_fHeadYaw = desYaw;
return;
// If I've flipped completely around, reverse angles
float fYawDiff = m_fHeadYaw - desYaw;
if (fYawDiff > 180)
{
m_fHeadYaw -= 360;
}
else if (fYawDiff < -180)
{
m_fHeadYaw += 360;
}
// RIGHT NOW, this affects every flying bot. This rate should be member data that individuals
// can manipulate. THIS change for MANHACKS E3 2003 (sjb)
float iRate = 0.8;
// Make frame rate independent
float timeToUse = flInterval;
while (timeToUse > 0)
{
m_fHeadYaw = (iRate * m_fHeadYaw) + (1-iRate)*desYaw;
timeToUse -= 0.1;
}
while( m_fHeadYaw > 360 )
{
m_fHeadYaw -= 360.0f;
}
while( m_fHeadYaw < 0 )
{
m_fHeadYaw += 360.f;
}
// SetBoneController( 0, m_fHeadYaw );
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
float CAI_BasePhysicsFlyingBot::MinGroundDist(void)
{
return 0;
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
Vector CAI_BasePhysicsFlyingBot::VelocityToAvoidObstacles(float flInterval)
{
// --------------------------------
// Avoid banging into stuff
// --------------------------------
trace_t tr;
Vector vTravelDir = m_vCurrentVelocity*flInterval;
Vector endPos = GetAbsOrigin() + vTravelDir;
AI_TraceEntity( this, GetAbsOrigin(), endPos, MASK_NPCSOLID|CONTENTS_WATER, &tr);
if (tr.fraction != 1.0)
{
// Bounce off in normal
Vector vBounce = tr.plane.normal * 0.5 * m_vCurrentVelocity.Length();
return (vBounce);
}
// --------------------------------
// Try to remain above the ground.
// --------------------------------
float flMinGroundDist = MinGroundDist();
AI_TraceLine(GetAbsOrigin(), GetAbsOrigin() + Vector(0, 0, -flMinGroundDist),
MASK_NPCSOLID_BRUSHONLY|CONTENTS_WATER, this, COLLISION_GROUP_NONE, &tr);
if (tr.fraction < 1)
{
// Clamp veloctiy
if (tr.fraction < 0.1)
{
tr.fraction = 0.1;
}
return Vector(0, 0, 50/tr.fraction);
}
return vec3_origin;
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CAI_BasePhysicsFlyingBot::StartTask( const Task_t *pTask )
{
switch (pTask->iTask)
{
// Skip as done via bone controller
case TASK_FACE_ENEMY:
{
TaskComplete();
break;
}
// Activity is just idle (have no run)
case TASK_RUN_PATH:
{
GetNavigator()->SetMovementActivity(ACT_IDLE);
TaskComplete();
break;
}
// Don't check for run/walk activity
case TASK_SCRIPT_RUN_TO_TARGET:
case TASK_SCRIPT_WALK_TO_TARGET:
{
if (GetTarget() == NULL)
{
TaskFail(FAIL_NO_TARGET);
}
else
{
if (!GetNavigator()->SetGoal( GOALTYPE_TARGETENT ) )
{
TaskFail(FAIL_NO_ROUTE);
GetNavigator()->ClearGoal();
}
}
TaskComplete();
break;
}
// Override to get more to get a directional path
case TASK_GET_PATH_TO_RANDOM_NODE:
{
if ( GetNavigator()->SetRandomGoal( pTask->flTaskData, m_vLastPatrolDir ) )
TaskComplete();
else
TaskFail(FAIL_NO_REACHABLE_NODE);
break;
}
default:
{
BaseClass::StartTask(pTask);
}
}
}
//------------------------------------------------------------------------------
void CAI_BasePhysicsFlyingBot::MoveToTarget(float flInterval, const Vector &MoveTarget)
{
Assert(0); // This must be overridden in the leaf classes
}
//------------------------------------------------------------------------------
AI_NavPathProgress_t CAI_BasePhysicsFlyingBot::ProgressFlyPath(
float flInterval,
const CBaseEntity *pNewTarget,
unsigned collisionMask,
bool bNewTrySimplify,
float strictPointTolerance)
{
AI_ProgressFlyPathParams_t params( collisionMask );
params.strictPointTolerance = strictPointTolerance;
params.SetCurrent( pNewTarget, bNewTrySimplify );
AI_NavPathProgress_t progress = GetNavigator()->ProgressFlyPath( params );
switch ( progress )
{
case AINPP_NO_CHANGE:
case AINPP_ADVANCED:
{
MoveToTarget(flInterval, GetNavigator()->GetCurWaypointPos());
break;
}
case AINPP_COMPLETE:
{
TaskMovementComplete();
break;
}
case AINPP_BLOCKED: // function is not supposed to test blocking, just simple path progression
default:
{
AssertMsg( 0, ( "Unexpected result" ) );
break;
}
}
return progress;
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
CAI_BasePhysicsFlyingBot::CAI_BasePhysicsFlyingBot()
{
#ifdef _DEBUG
m_vCurrentVelocity.Init();
m_vCurrentBanking.Init();
m_vLastPatrolDir.Init();
#endif
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CAI_BasePhysicsFlyingBot::~CAI_BasePhysicsFlyingBot( void )
{
physenv->DestroyMotionController( m_pMotionController );
}
//-----------------------------------------------------------------------------
// Purpose:
//
//
//-----------------------------------------------------------------------------
bool CAI_BasePhysicsFlyingBot::CreateVPhysics( void )
{
// Create the object in the physics system
IPhysicsObject *pPhysicsObject = VPhysicsInitNormal( SOLID_BBOX, FSOLID_NOT_STANDABLE, false );
m_pMotionController = physenv->CreateMotionController( this );
m_pMotionController->AttachObject( pPhysicsObject, true );
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTarget -
// &chasePosition -
//-----------------------------------------------------------------------------
void CAI_BasePhysicsFlyingBot::TranslateNavGoal( CBaseEntity *pTarget, Vector &chasePosition )
{
Assert( pTarget != NULL );
if ( pTarget == NULL )
{
chasePosition = vec3_origin;
return;
}
// Chase their eyes
chasePosition = pTarget->GetAbsOrigin() + pTarget->GetViewOffset();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pController -
// *pObject -
// deltaTime -
// &linear -
// &angular -
// Output : IMotionEvent::simresult_e
//-----------------------------------------------------------------------------
IMotionEvent::simresult_e CAI_BasePhysicsFlyingBot::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
{
static int count;
IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
// Assert( pPhysicsObject );
if (!pPhysicsObject)
return SIM_NOTHING;
// move
Vector actualVelocity;
AngularImpulse actualAngularVelocity;
pPhysicsObject->GetVelocity( &actualVelocity, &actualAngularVelocity );
linear = (m_vCurrentVelocity - actualVelocity) * (0.1 / deltaTime) * 10.0;
/*
DevMsg("Sim %d : %5.1f %5.1f %5.1f\n", count++,
m_vCurrentVelocity.x - actualVelocity.x,
m_vCurrentVelocity.y - actualVelocity.y,
m_vCurrentVelocity.z - actualVelocity.z );
*/
// do angles.
Vector actualPosition;
QAngle actualAngles;
pPhysicsObject->GetPosition( &actualPosition, &actualAngles );
// FIXME: banking currently disabled, forces simple upright posture
angular.x = (UTIL_AngleDiff( m_vCurrentBanking.z, actualAngles.z ) - actualAngularVelocity.x) * (1 / deltaTime);
angular.y = (UTIL_AngleDiff( m_vCurrentBanking.x, actualAngles.x ) - actualAngularVelocity.y) * (1 / deltaTime);
// turn toward target
angular.z = UTIL_AngleDiff( m_fHeadYaw, actualAngles.y + actualAngularVelocity.z * 0.1 ) * (1 / deltaTime);
// angular = m_vCurrentAngularVelocity - actualAngularVelocity;
// DevMsg("Sim %d : %.1f %.1f %.1f (%.1f)\n", count++, actualAngles.x, actualAngles.y, actualAngles.z, m_fHeadYaw );
// FIXME: remove the stuff from MoveExecute();
// FIXME: check MOVE?
ClampMotorForces( linear, angular );
return SIM_GLOBAL_ACCELERATION; // on my local axis. SIM_GLOBAL_ACCELERATION
}

View File

@@ -0,0 +1,149 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef AI_BASENPC_PHYSICSFLYER_H
#define AI_BASENPC_PHYSICSFLYER_H
#ifdef _WIN32
#pragma once
#endif
#include "ai_basenpc.h"
#include "ai_navigator.h"
//-----------------------------------------------------------------------------
// The combot.
//-----------------------------------------------------------------------------
abstract_class CAI_BasePhysicsFlyingBot : public CAI_BaseNPC, public IMotionEvent
{
DECLARE_CLASS( CAI_BasePhysicsFlyingBot, CAI_BaseNPC );
public:
DECLARE_DATADESC();
void StartTask( const Task_t *pTask );
void GetVelocity(Vector *vVelocity, AngularImpulse *vAngVelocity);
virtual QAngle BodyAngles();
virtual bool ShouldSavePhysics() { return true; }
protected:
CAI_BasePhysicsFlyingBot();
~CAI_BasePhysicsFlyingBot();
Vector VelocityToAvoidObstacles(float flInterval);
virtual float MinGroundDist(void);
virtual void TurnHeadToTarget( float flInterval, const Vector &moveTarget );
void MoveInDirection( float flInterval, const Vector &targetDir,
float accelXY, float accelZ, float decay)
{
decay = ExponentialDecay( decay, 1.0, flInterval );
accelXY *= flInterval;
accelZ *= flInterval;
m_vCurrentVelocity.x = ( decay * m_vCurrentVelocity.x + accelXY * targetDir.x );
m_vCurrentVelocity.y = ( decay * m_vCurrentVelocity.y + accelXY * targetDir.y );
m_vCurrentVelocity.z = ( decay * m_vCurrentVelocity.z + accelZ * targetDir.z );
}
void MoveToLocation( float flInterval, const Vector &target,
float accelXY, float accelZ, float decay)
{
Vector targetDir = target - GetLocalOrigin();
VectorNormalize(targetDir);
MoveInDirection(flInterval, targetDir, accelXY, accelZ, decay);
}
void Decelerate( float flInterval, float decay )
{
decay *= flInterval;
m_vCurrentVelocity.x = (decay * m_vCurrentVelocity.x);
m_vCurrentVelocity.y = (decay * m_vCurrentVelocity.y);
m_vCurrentVelocity.z = (decay * m_vCurrentVelocity.z);
}
void AddNoiseToVelocity( float noiseScale = 1.0 )
{
if( m_vNoiseMod.x )
{
m_vCurrentVelocity.x += noiseScale*sin(m_vNoiseMod.x * gpGlobals->curtime + m_vNoiseMod.x);
}
if( m_vNoiseMod.y )
{
m_vCurrentVelocity.y += noiseScale*cos(m_vNoiseMod.y * gpGlobals->curtime + m_vNoiseMod.y);
}
if( m_vNoiseMod.z )
{
m_vCurrentVelocity.z -= noiseScale*cos(m_vNoiseMod.z * gpGlobals->curtime + m_vNoiseMod.z);
}
}
void LimitSpeed( float zLimit, float maxSpeed = -1 )
{
if ( maxSpeed == -1 )
maxSpeed = m_flSpeed;
if (m_vCurrentVelocity.Length() > maxSpeed)
{
VectorNormalize(m_vCurrentVelocity);
m_vCurrentVelocity *= maxSpeed;
}
// Limit fall speed
if (zLimit > 0 && m_vCurrentVelocity.z < -zLimit)
{
m_vCurrentVelocity.z = -zLimit;
}
}
AI_NavPathProgress_t ProgressFlyPath( float flInterval,
const CBaseEntity *pNewTarget,
unsigned collisionMask,
bool bNewTrySimplify = true,
float strictPointTolerance = 32.0 );
const Vector &GetCurrentVelocity() const { return m_vCurrentVelocity; }
void SetCurrentVelocity(const Vector &vNewVel) { m_vCurrentVelocity = vNewVel; }
const Vector &GetNoiseMod() const { return m_vNoiseMod; }
void SetNoiseMod( float x, float y, float z ) { m_vNoiseMod.Init( x, y, z ); }
void SetNoiseMod( const Vector &noise ) { m_vNoiseMod = noise; }
void TranslateNavGoal( CBaseEntity *pTarget, Vector &chasePosition );
virtual void MoveToTarget(float flInterval, const Vector &MoveTarget) = 0;
virtual float GetHeadTurnRate( void ) { return 15.0f; } // Degrees per second
bool CreateVPhysics( void );
IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
virtual void ClampMotorForces( Vector &linear, AngularImpulse &angular )
{
// limit reaction forces
linear.x = clamp( linear.x, -3000.f, 3000.f );
linear.y = clamp( linear.y, -3000.f, 3000.f );
linear.z = clamp( linear.z, -3000.f, 3000.f );
// add in weightlessness
linear.z += 800.f;
}
// -------------------------------
// Movement vars
// -------------------------------
Vector m_vCurrentVelocity;
Vector m_vCurrentBanking;
Vector m_vNoiseMod;
float m_fHeadYaw;
Vector m_vLastPatrolDir;
IPhysicsMotionController *m_pMotionController;
};
#endif // AI_BASENPC_PHYSICSFLYER_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,292 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "ai_basenpc.h"
#include "ai_default.h"
#include "ai_hull.h"
#include "ai_squadslot.h"
#include "ai_squad.h"
#include "bitstring.h"
#include "entitylist.h"
#include "ai_hint.h"
#include "IEffects.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Purpose: If requested slot is available return true and take the slot
// Otherwise return false
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::OccupyStrategySlot( int squadSlotID )
{
return OccupyStrategySlotRange( squadSlotID, squadSlotID );
}
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::OccupyStrategySlotRange( int slotIDStart, int slotIDEnd )
{
// If I'm not in a squad a I don't fill slots
return ( !m_pSquad || m_pSquad->OccupyStrategySlotRange( GetEnemy(), slotIDStart, slotIDEnd, &m_iMySquadSlot ) );
}
//-----------------------------------------------------------------------------
// Returns true if all in the range are full
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::IsStrategySlotRangeOccupied( int slotIDStart, int slotIDEnd )
{
return m_pSquad && m_pSquad->IsStrategySlotRangeOccupied( GetEnemy(), slotIDStart, slotIDEnd );
}
//=========================================================
// HasStrategySlot
//=========================================================
bool CAI_BaseNPC::HasStrategySlot( int squadSlotID )
{
// If I wasn't taking up a squad slot I'm done
return (m_iMySquadSlot == squadSlotID);
}
bool CAI_BaseNPC::HasStrategySlotRange( int slotIDStart, int slotIDEnd )
{
// If I wasn't taking up a squad slot I'm done
if (m_iMySquadSlot < slotIDStart || m_iMySquadSlot > slotIDEnd)
{
return false;
}
return true;
}
//=========================================================
// VacateSlot
//=========================================================
void CAI_BaseNPC::VacateStrategySlot(void)
{
if (m_pSquad)
{
m_pSquad->VacateStrategySlot(GetEnemy(), m_iMySquadSlot);
m_iMySquadSlot = SQUAD_SLOT_NONE;
}
}
//------------------------------------------------------------------------------
// Purpose : Is cover node valid
// Input :
// Output :
//------------------------------------------------------------------------------
bool CAI_BaseNPC::IsValidCover( const Vector &vecCoverLocation, CAI_Hint const *pHint )
{
// firstly, limit choices to hint groups
string_t iszHint = GetHintGroup();
char *pszHint = (char *)STRING(iszHint);
if ((iszHint != NULL_STRING) && (pszHint[0] != '\0'))
{
if (!pHint || pHint->GetGroup() != GetHintGroup())
{
return false;
}
}
/*
// If I'm in a squad don't pick cover node it other squad member
// is already nearby
if (m_pSquad)
{
return m_pSquad->IsValidCover( vecCoverLocation, pHint );
}
*/
// UNDONE: Do we really need this test?
// ----------------------------------------------------------------
// Make sure my hull can fit at this node before accepting it.
// Could be another NPC there or it could be blocked
// ----------------------------------------------------------------
// FIXME: shouldn't this see that if I crouch behind it it'll be safe?
Vector startPos = vecCoverLocation;
startPos.z -= GetHullMins().z; // Move hull bottom up to node
Vector endPos = startPos;
endPos.z += 0.01;
trace_t tr;
AI_TraceEntity( this, vecCoverLocation, endPos, MASK_NPCSOLID, &tr );
if (tr.startsolid)
{
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Is squad member in my way from shooting here
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::IsValidShootPosition( const Vector &vecShootLocation, CAI_Node *pNode, CAI_Hint const *pHint )
{
// limit choices to hint groups
if (GetHintGroup() != NULL_STRING)
{
if (!pHint || pHint->GetGroup() != GetHintGroup())
{
if ( ( vecShootLocation - GetAbsOrigin() ).Length2DSqr() > 1 )
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------
bool CAI_BaseNPC::IsSquadmateInSpread( const Vector &sourcePos, const Vector &targetPos, float flSpread, float maxDistOffCenter )
{
if( !m_pSquad )
return false;
AISquadIter_t iter;
CAI_BaseNPC *pSquadmate = m_pSquad->GetFirstMember( &iter );
while ( pSquadmate )
{
// Ignore squadmates that can't take damage. This is primarily to ignore npc_enemyfinders.
if ( pSquadmate->m_takedamage != DAMAGE_NO )
{
if ( pSquadmate != this )
{
if ( PointInSpread( pSquadmate, sourcePos, targetPos, pSquadmate->GetAbsOrigin(), flSpread, maxDistOffCenter ) )
return true;
}
}
pSquadmate = m_pSquad->GetNextMember( &iter );
}
return false;
}
//-----------------------------------------------------------------------------
void CAI_BaseNPC::AddToSquad( string_t name )
{
g_AI_SquadManager.FindCreateSquad( this, name );
}
//-----------------------------------------------------------------------------
void CAI_BaseNPC::SetSquad( CAI_Squad *pSquad )
{
if ( m_pSquad == pSquad )
{
return;
}
if ( m_pSquad && m_iMySquadSlot != SQUAD_SLOT_NONE)
{
VacateStrategySlot();
}
m_pSquad = pSquad;
}
//-----------------------------------------------------------------------------
void CAI_BaseNPC::RemoveFromSquad()
{
if ( m_pSquad )
{
m_pSquad->RemoveFromSquad( this, false );
m_pSquad = NULL;
}
}
//-----------------------------------------------------------------------------
void CAI_BaseNPC::CheckSquad()
{
if( !IsInSquad() )
return;
if( !GetSquad()->IsLeader(this) )
return;
if( VPhysicsGetObject() != NULL && (VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) )
{
// I AM the leader, and I'm currently being held. This will screw up all of my relationship checks
// if I'm a manhack or a rollermine, so just bomb out and try next time.
return;
}
AISquadIter_t iter;
CAI_BaseNPC *pSquadmate = m_pSquad->GetFirstMember( &iter );
while ( pSquadmate )
{
if( IRelationType(pSquadmate) < D_LI )
{
bool bWarn = true;
// Rollermines and manhacks set their Class to NONE when held by the player, which makes all of
// their squadmates complain that an enemy is in the squad. Suppress this.
if( pSquadmate->VPhysicsGetObject() != NULL )
{
if (pSquadmate->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD)
{
bWarn = false;
}
}
if( bWarn )
{
Warning( "ERROR: Squad '%s' has enemies in it!\n", GetSquad()->GetName() );
Warning( "%s doesn't like %s\n\n", GetDebugName(), pSquadmate->GetDebugName() );
}
}
pSquadmate = m_pSquad->GetNextMember( &iter );
}
}
//-----------------------------------------------------------------------------
// Returns the number of weapons of this type currently owned by squad members.
//-----------------------------------------------------------------------------
int CAI_BaseNPC::NumWeaponsInSquad( const char *pszWeaponClassname )
{
string_t iszWeaponClassname = FindPooledString( pszWeaponClassname );
if( !GetSquad() )
{
if( GetActiveWeapon() && GetActiveWeapon()->m_iClassname == iszWeaponClassname )
{
// I'm alone in my squad, but I do have this weapon.
return 1;
}
return 0;
}
int count = 0;
AISquadIter_t iter;
CAI_BaseNPC *pSquadmate = m_pSquad->GetFirstMember( &iter );
while ( pSquadmate )
{
if( pSquadmate->GetActiveWeapon() && pSquadmate->GetActiveWeapon()->m_iClassname == iszWeaponClassname )
{
count++;
}
pSquadmate = m_pSquad->GetNextMember( &iter );
}
return count;
}

View File

@@ -0,0 +1,533 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "isaverestore.h"
#include "ai_behavior.h"
#include "scripted.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
bool g_bBehaviorHost_PreventBaseClassGatherConditions;
//-----------------------------------------------------------------------------
// CAI_BehaviorBase
//-----------------------------------------------------------------------------
BEGIN_DATADESC_NO_BASE( CAI_BehaviorBase )
END_DATADESC()
//-------------------------------------
CAI_ClassScheduleIdSpace *CAI_BehaviorBase::GetClassScheduleIdSpace()
{
return GetOuter()->GetClassScheduleIdSpace();
}
//-----------------------------------------------------------------------------
// Purpose: Draw any text overlays (override in subclass to add additional text)
// Input : Previous text offset from the top
// Output : Current text offset from the top
//-----------------------------------------------------------------------------
int CAI_BehaviorBase::DrawDebugTextOverlays( int text_offset )
{
char tempstr[ 512 ];
int offset = text_offset;
if ( GetOuter()->m_debugOverlays & OVERLAY_TEXT_BIT )
{
Q_snprintf( tempstr, sizeof( tempstr ), "Behv: %s, ", GetName() );
GetOuter()->EntityText( offset, tempstr, 0 );
offset++;
}
return offset;
}
//-------------------------------------
void CAI_BehaviorBase::GatherConditions()
{
Assert( m_pBackBridge != NULL );
m_pBackBridge->BackBridge_GatherConditions();
}
//-------------------------------------
void CAI_BehaviorBase::PrescheduleThink()
{
}
//-------------------------------------
void CAI_BehaviorBase::OnScheduleChange()
{
}
//-------------------------------------
void CAI_BehaviorBase::OnStartSchedule( int scheduleType )
{
}
//-------------------------------------
int CAI_BehaviorBase::SelectSchedule()
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_SelectSchedule();
}
//-------------------------------------
int CAI_BehaviorBase::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
{
m_fOverrode = false;
return SCHED_NONE;
}
//-------------------------------------
void CAI_BehaviorBase::StartTask( const Task_t *pTask )
{
m_fOverrode = false;
}
//-------------------------------------
void CAI_BehaviorBase::RunTask( const Task_t *pTask )
{
m_fOverrode = false;
}
//-------------------------------------
void CAI_BehaviorBase::AimGun( void )
{
m_fOverrode = false;
}
//-------------------------------------
int CAI_BehaviorBase::TranslateSchedule( int scheduleType )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_TranslateSchedule( scheduleType );
}
//-------------------------------------
CAI_Schedule *CAI_BehaviorBase::GetSchedule(int schedule)
{
if (!GetClassScheduleIdSpace()->IsGlobalBaseSet())
{
Warning("ERROR: %s missing schedule!\n", GetSchedulingErrorName());
return g_AI_SchedulesManager.GetScheduleFromID(SCHED_IDLE_STAND);
}
if ( AI_IdIsLocal( schedule ) )
{
schedule = GetClassScheduleIdSpace()->ScheduleLocalToGlobal(schedule);
}
if ( schedule == -1 )
return NULL;
return g_AI_SchedulesManager.GetScheduleFromID( schedule );
}
//-------------------------------------
bool CAI_BehaviorBase::IsCurSchedule( int schedule, bool fIdeal )
{
if ( AI_IdIsLocal( schedule ) )
schedule = GetClassScheduleIdSpace()->ScheduleLocalToGlobal(schedule);
return GetOuter()->IsCurSchedule( schedule, fIdeal );
}
//-------------------------------------
const char *CAI_BehaviorBase::GetSchedulingErrorName()
{
return "CAI_Behavior";
}
//-------------------------------------
Activity CAI_BehaviorBase::NPC_TranslateActivity( Activity activity )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_NPC_TranslateActivity( activity );
}
//-------------------------------------
bool CAI_BehaviorBase::IsCurTaskContinuousMove()
{
m_fOverrode = false;
return false;
}
//-------------------------------------
float CAI_BehaviorBase::GetDefaultNavGoalTolerance()
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_GetDefaultNavGoalTolerance();
}
//-------------------------------------
bool CAI_BehaviorBase::FValidateHintType( CAI_Hint *pHint )
{
m_fOverrode = false;
return false;
}
//-------------------------------------
bool CAI_BehaviorBase::IsValidEnemy( CBaseEntity *pEnemy )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_IsValidEnemy( pEnemy );
}
//-------------------------------------
CBaseEntity *CAI_BehaviorBase::BestEnemy( void )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_BestEnemy();
}
//-------------------------------------
bool CAI_BehaviorBase::IsValidCover( const Vector &vLocation, CAI_Hint const *pHint )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_IsValidCover( vLocation, pHint );
}
//-------------------------------------
bool CAI_BehaviorBase::IsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_IsValidShootPosition( vLocation, pNode, pHint );
}
//-------------------------------------
float CAI_BehaviorBase::GetMaxTacticalLateralMovement( void )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_GetMaxTacticalLateralMovement();
}
//-------------------------------------
bool CAI_BehaviorBase::ShouldIgnoreSound( CSound *pSound )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_ShouldIgnoreSound( pSound );
}
//-------------------------------------
void CAI_BehaviorBase::OnSeeEntity( CBaseEntity *pEntity )
{
Assert( m_pBackBridge != NULL );
m_pBackBridge->BackBridge_OnSeeEntity( pEntity );
}
//-------------------------------------
void CAI_BehaviorBase::OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker )
{
Assert( m_pBackBridge != NULL );
m_pBackBridge->BackBridge_OnFriendDamaged( pSquadmate, pAttacker );
}
//-------------------------------------
bool CAI_BehaviorBase::IsInterruptable( void )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_IsInterruptable();
}
//-------------------------------------
bool CAI_BehaviorBase::IsNavigationUrgent( void )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_IsNavigationUrgent();
}
//-------------------------------------
bool CAI_BehaviorBase::CanFlinch( void )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_CanFlinch();
}
//-------------------------------------
bool CAI_BehaviorBase::IsCrouching( void )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_IsCrouching();
}
//-------------------------------------
bool CAI_BehaviorBase::IsCrouchedActivity( Activity activity )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_IsCrouchedActivity( activity );
}
//-------------------------------------
bool CAI_BehaviorBase::QueryHearSound( CSound *pSound )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_QueryHearSound( pSound );
}
//-------------------------------------
bool CAI_BehaviorBase::CanRunAScriptedNPCInteraction( bool bForced )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_CanRunAScriptedNPCInteraction( bForced );
}
//-------------------------------------
bool CAI_BehaviorBase::ShouldPlayerAvoid( void )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_ShouldPlayerAvoid();
}
//-------------------------------------
int CAI_BehaviorBase::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_OnTakeDamage_Alive( info );
}
//-------------------------------------
float CAI_BehaviorBase::GetReasonableFacingDist( void )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_GetReasonableFacingDist();
}
//-------------------------------------
bool CAI_BehaviorBase::ShouldAlwaysThink()
{
m_fOverrode = false;
return false;
}
//-------------------------------------
Activity CAI_BehaviorBase::GetFlinchActivity( bool bHeavyDamage, bool bGesture )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_GetFlinchActivity( bHeavyDamage, bGesture );
}
//-------------------------------------
bool CAI_BehaviorBase::OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_OnCalcBaseMove( pMoveGoal, distClear, pResult );
}
//-------------------------------------
void CAI_BehaviorBase::ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_ModifyOrAppendCriteria( criteriaSet );
}
//-------------------------------------
void CAI_BehaviorBase::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
{
Assert( m_pBackBridge != NULL );
return m_pBackBridge->BackBridge_Teleport( newPosition, newAngles, newVelocity );
}
//-------------------------------------
void CAI_BehaviorBase::HandleAnimEvent( animevent_t *pEvent )
{
Assert( m_pBackBridge != NULL );
m_pBackBridge->BackBridge_HandleAnimEvent( pEvent );
}
//-------------------------------------
bool CAI_BehaviorBase::NotifyChangeBehaviorStatus( bool fCanFinishSchedule )
{
bool fInterrupt = GetOuter()->OnBehaviorChangeStatus( this, fCanFinishSchedule );
if ( !GetOuter()->IsInterruptable())
return false;
if ( fInterrupt )
{
if ( GetOuter()->m_hCine )
{
if( GetOuter()->m_hCine->PlayedSequence() )
{
DevWarning( "NPC: %s canceled running script %s due to behavior change\n", GetOuter()->GetDebugName(), GetOuter()->m_hCine->GetDebugName() );
}
else
{
DevWarning( "NPC: %s canceled script %s without playing, due to behavior change\n", GetOuter()->GetDebugName(), GetOuter()->m_hCine->GetDebugName() );
}
GetOuter()->m_hCine->CancelScript();
}
//!!!HACKHACK
// this is dirty, but it forces NPC to pick a new schedule next time through.
GetOuter()->ClearSchedule( "Changed behavior status" );
}
return fInterrupt;
}
//-------------------------------------
int CAI_BehaviorBase::Save( ISave &save )
{
return save.WriteAll( this, GetDataDescMap() );
}
//-------------------------------------
int CAI_BehaviorBase::Restore( IRestore &restore )
{
return restore.ReadAll( this, GetDataDescMap() );
}
//-------------------------------------
#define BEHAVIOR_SAVE_BLOCKNAME "AI_Behaviors"
#define BEHAVIOR_SAVE_VERSION 2
void CAI_BehaviorBase::SaveBehaviors(ISave &save, CAI_BehaviorBase *pCurrentBehavior, CAI_BehaviorBase **ppBehavior, int nBehaviors )
{
save.StartBlock( BEHAVIOR_SAVE_BLOCKNAME );
short temp = BEHAVIOR_SAVE_VERSION;
save.WriteShort( &temp );
temp = (short)nBehaviors;
save.WriteShort( &temp );
for ( int i = 0; i < nBehaviors; i++ )
{
if ( strcmp( ppBehavior[i]->GetDataDescMap()->dataClassName, CAI_BehaviorBase::m_DataMap.dataClassName ) != 0 )
{
save.StartBlock();
save.WriteString( ppBehavior[i]->GetDataDescMap()->dataClassName );
bool bIsCurrent = ( pCurrentBehavior == ppBehavior[i] );
save.WriteBool( &bIsCurrent );
ppBehavior[i]->Save( save );
save.EndBlock();
}
}
save.EndBlock();
}
//-------------------------------------
int CAI_BehaviorBase::RestoreBehaviors(IRestore &restore, CAI_BehaviorBase **ppBehavior, int nBehaviors )
{
int iCurrent = -1;
char szBlockName[SIZE_BLOCK_NAME_BUF];
restore.StartBlock( szBlockName );
if ( strcmp( szBlockName, BEHAVIOR_SAVE_BLOCKNAME ) == 0 )
{
short version;
restore.ReadShort( &version );
if ( version == BEHAVIOR_SAVE_VERSION )
{
short nToRestore;
char szClassNameCurrent[256];
restore.ReadShort( &nToRestore );
for ( int i = 0; i < nToRestore; i++ )
{
restore.StartBlock();
restore.ReadString( szClassNameCurrent, sizeof( szClassNameCurrent ), 0 );
bool bIsCurrent;
restore.ReadBool( &bIsCurrent );
for ( int j = 0; j < nBehaviors; j++ )
{
if ( strcmp( ppBehavior[j]->GetDataDescMap()->dataClassName, szClassNameCurrent ) == 0 )
{
if ( bIsCurrent )
iCurrent = j;
ppBehavior[j]->Restore( restore );
}
}
restore.EndBlock();
}
}
}
restore.EndBlock();
return iCurrent;
}
//-----------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,299 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef AI_BEHAVIOR_ASSAULT_H
#define AI_BEHAVIOR_ASSAULT_H
#ifdef _WIN32
#pragma once
#endif
#include "simtimer.h"
#include "ai_behavior.h"
#include "ai_goalentity.h"
#include "ai_moveshoot.h"
#include "ai_utils.h"
#define CUE_POINT_TOLERANCE (3.0*12.0)
enum RallySelectMethod_t
{
RALLY_POINT_SELECT_DEFAULT = 0,
RALLY_POINT_SELECT_RANDOM,
};
enum AssaultCue_t
{
CUE_NO_ASSAULT = 0, // used to indicate that no assault is being conducted presently
CUE_ENTITY_INPUT = 1,
CUE_PLAYER_GUNFIRE,
CUE_DONT_WAIT,
CUE_COMMANDER,
CUE_NONE,
};
enum
{
ASSAULT_SENTENCE_HIT_RALLY_POINT = SENTENCE_BASE_BEHAVIOR_INDEX,
ASSAULT_SENTENCE_HIT_ASSAULT_POINT,
ASSAULT_SENTENCE_SQUAD_ADVANCE_TO_RALLY,
ASSAULT_SENTENCE_SQUAD_ADVANCE_TO_ASSAULT,
ASSAULT_SENTENCE_COVER_NO_AMMO,
ASSAULT_SENTENCE_UNDER_ATTACK,
};
// Allow diversion from the assault up to this amount of time after last having an enemy
#define ASSAULT_DIVERSION_TIME 4
#define SF_ASSAULTPOINT_CLEARONARRIVAL 0x00000001
//=============================================================================
//=============================================================================
class CRallyPoint : public CPointEntity
{
DECLARE_CLASS( CRallyPoint, CPointEntity );
public:
CRallyPoint()
{
m_hLockedBy.Set(NULL);
m_sExclusivity = RALLY_EXCLUSIVE_NOT_EVALUATED;
}
bool Lock( CBaseEntity *pLocker )
{
if( IsLocked() )
{
// Already locked.
return false;
}
m_hLockedBy.Set( pLocker );
return true;
}
bool Unlock( CBaseEntity *pUnlocker )
{
if( IsLocked() )
{
if( m_hLockedBy.Get() != pUnlocker )
{
// Refuse! Only the locker may unlock.
return false;
}
}
m_hLockedBy.Set( NULL );
return true;
}
bool IsLocked( void ) { return (m_hLockedBy.Get() != NULL); }
int DrawDebugTextOverlays();
bool IsExclusive();
enum
{
RALLY_EXCLUSIVE_NOT_EVALUATED = -1,
RALLY_EXCLUSIVE_NO,
RALLY_EXCLUSIVE_YES,
};
string_t m_AssaultPointName;
string_t m_RallySequenceName;
float m_flAssaultDelay;
int m_iPriority;
int m_iStrictness;
bool m_bForceCrouch;
bool m_bIsUrgent;
short m_sExclusivity;
COutputEvent m_OnArrival;
DECLARE_DATADESC();
private:
EHANDLE m_hLockedBy;
};
//=============================================================================
//=============================================================================
class CAssaultPoint : public CPointEntity
{
DECLARE_CLASS( CAssaultPoint, CPointEntity );
public:
CAssaultPoint()
{
// This used to be a constant in code. Now it's a keyfield in hammer.
// So in the constructor, we set this value to the former constant
// default value, for legacy maps. (sjb)
m_flAssaultPointTolerance = CUE_POINT_TOLERANCE;
}
void InputSetClearOnContact( inputdata_t &inputdata )
{
m_bClearOnContact = inputdata.value.Bool();
}
void InputSetAllowDiversion( inputdata_t &inputdata )
{
m_bAllowDiversion = inputdata.value.Bool();
}
void InputSetForceClear( inputdata_t &inputdata )
{
m_bInputForcedClear = inputdata.value.Bool();
}
public:
string_t m_AssaultHintGroup;
string_t m_NextAssaultPointName;
COutputEvent m_OnAssaultClear;
float m_flAssaultTimeout;
bool m_bClearOnContact;
bool m_bAllowDiversion;
float m_flAllowDiversionRadius;
bool m_bNeverTimeout;
int m_iStrictness;
bool m_bForceCrouch;
bool m_bIsUrgent;
bool m_bInputForcedClear;
float m_flAssaultPointTolerance;
float m_flTimeLastUsed;
COutputEvent m_OnArrival;
DECLARE_DATADESC();
};
//=============================================================================
//=============================================================================
class CAI_AssaultBehavior : public CAI_SimpleBehavior
{
DECLARE_CLASS( CAI_AssaultBehavior, CAI_SimpleBehavior );
public:
CAI_AssaultBehavior();
virtual const char *GetName() { return "Assault"; }
virtual int DrawDebugTextOverlays( int text_offset );
virtual void OnRestore();
bool CanRunAScriptedNPCInteraction( bool bForced );
virtual bool CanSelectSchedule();
virtual void BeginScheduleSelection();
virtual void EndScheduleSelection();
bool HasHitRallyPoint() { return m_bHitRallyPoint; }
bool HasHitAssaultPoint() { return m_bHitAssaultPoint; }
void ClearAssaultPoint( void );
void OnHitAssaultPoint( void );
bool PollAssaultCue( void );
void ReceiveAssaultCue( AssaultCue_t cue );
bool HasAssaultCue( void ) { return m_AssaultCue != CUE_NO_ASSAULT; }
bool AssaultHasBegun();
CAssaultPoint *FindAssaultPoint( string_t iszAssaultPointName );
void SetAssaultPoint( CAssaultPoint *pAssaultPoint );
void GatherConditions( void );
void StartTask( const Task_t *pTask );
void RunTask( const Task_t *pTask );
void BuildScheduleTestBits();
int TranslateSchedule( int scheduleType );
void OnStartSchedule( int scheduleType );
void ClearSchedule( const char *szReason );
void InitializeBehavior();
void SetParameters( string_t rallypointname, AssaultCue_t assaultcue, int rallySelectMethod );
void SetParameters( CBaseEntity *pRallyEnt, AssaultCue_t assaultcue );
bool IsAllowedToDivert( void );
bool IsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint );
float GetMaxTacticalLateralMovement( void );
void UpdateOnRemove();
bool OnStrictAssault( void );
bool UpdateForceCrouch( void );
bool IsForcingCrouch( void );
bool IsUrgent( void );
CRallyPoint *FindBestRallyPointInRadius( const Vector &vecCenter, float flRadius );;
void Disable( void ) { m_AssaultCue = CUE_NO_ASSAULT; m_bHitRallyPoint = false; m_bHitAssaultPoint = false; }
enum
{
SCHED_MOVE_TO_RALLY_POINT = BaseClass::NEXT_SCHEDULE, // Try to get out of the player's way
SCHED_ASSAULT_FAILED_TO_MOVE,
SCHED_FAIL_MOVE_TO_RALLY_POINT,
SCHED_MOVE_TO_ASSAULT_POINT,
SCHED_AT_ASSAULT_POINT,
SCHED_HOLD_RALLY_POINT,
SCHED_HOLD_ASSAULT_POINT,
SCHED_WAIT_AND_CLEAR,
SCHED_ASSAULT_MOVE_AWAY,
SCHED_CLEAR_ASSAULT_POINT,
NEXT_SCHEDULE,
TASK_GET_PATH_TO_RALLY_POINT = BaseClass::NEXT_TASK,
TASK_FACE_RALLY_POINT,
TASK_GET_PATH_TO_ASSAULT_POINT,
TASK_FACE_ASSAULT_POINT,
TASK_HIT_ASSAULT_POINT,
TASK_HIT_RALLY_POINT,
TASK_AWAIT_CUE,
TASK_AWAIT_ASSAULT_TIMEOUT,
TASK_ANNOUNCE_CLEAR,
TASK_WAIT_ASSAULT_DELAY,
TASK_ASSAULT_MOVE_AWAY_PATH,
TASK_ASSAULT_DEFER_SCHEDULE_SELECTION,
NEXT_TASK,
/*
COND_PUT_CONDITIONS_HERE = BaseClass::NEXT_CONDITION,
NEXT_CONDITION,
*/
};
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
public:
CHandle<CAssaultPoint> m_hAssaultPoint;
CHandle<CRallyPoint> m_hRallyPoint;
public:
void UnlockRallyPoint( void );
private:
void OnScheduleChange();
virtual int SelectSchedule();
AssaultCue_t m_AssaultCue; // the cue we're waiting for to begin the assault
AssaultCue_t m_ReceivedAssaultCue; // the last assault cue we received from someone/thing external.
bool m_bHitRallyPoint;
bool m_bHitAssaultPoint;
// Diversion
bool m_bDiverting;
float m_flLastSawAnEnemyAt;
float m_flTimeDeferScheduleSelection;
string_t m_AssaultPointName;
//---------------------------------
DECLARE_DATADESC();
};
#endif // AI_BEHAVIOR_ASSAULT_H

View File

@@ -0,0 +1,552 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "ai_motor.h"
#include "ai_behavior_fear.h"
#include "ai_hint.h"
#include "ai_navigator.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
BEGIN_DATADESC( CAI_FearBehavior )
DEFINE_FIELD( m_flTimeToSafety, FIELD_TIME ),
DEFINE_FIELD( m_flTimePlayerLastVisible, FIELD_TIME ),
DEFINE_FIELD( m_hSafePlaceHint, FIELD_EHANDLE ),
DEFINE_FIELD( m_hMovingToHint, FIELD_EHANDLE ),
DEFINE_EMBEDDED( m_SafePlaceMoveMonitor ),
DEFINE_FIELD( m_flDeferUntil, FIELD_TIME ),
END_DATADESC();
#define BEHAVIOR_FEAR_SAFETY_TIME 5
#define FEAR_SAFE_PLACE_TOLERANCE 36.0f
#define FEAR_ENEMY_TOLERANCE_CLOSE_DIST_SQR Square(300.0f) // (25 feet)
#define FEAR_ENEMY_TOLERANCE_TOO_CLOSE_DIST_SQR Square( 60.0f ) // (5 Feet)
ConVar ai_enable_fear_behavior( "ai_enable_fear_behavior", "1" );
ConVar ai_fear_player_dist("ai_fear_player_dist", "720" );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CAI_FearBehavior::CAI_FearBehavior()
{
ReleaseAllHints();
m_SafePlaceMoveMonitor.ClearMark();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAI_FearBehavior::Precache( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTask -
//-----------------------------------------------------------------------------
void CAI_FearBehavior::StartTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_FEAR_IN_SAFE_PLACE:
// We've arrived! Lock the hint and set the marker. we're safe for now.
m_hSafePlaceHint = m_hMovingToHint;
m_hSafePlaceHint->Lock( GetOuter() );
m_SafePlaceMoveMonitor.SetMark( GetOuter(), FEAR_SAFE_PLACE_TOLERANCE );
TaskComplete();
break;
case TASK_FEAR_GET_PATH_TO_SAFETY_HINT:
// Using TaskInterrupt() optimizations. See RunTask().
break;
case TASK_FEAR_WAIT_FOR_SAFETY:
m_flTimeToSafety = gpGlobals->curtime + BEHAVIOR_FEAR_SAFETY_TIME;
break;
default:
BaseClass::StartTask( pTask );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTask -
//-----------------------------------------------------------------------------
void CAI_FearBehavior::RunTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_FEAR_WAIT_FOR_SAFETY:
if( HasCondition(COND_SEE_ENEMY) )
{
m_flTimeToSafety = gpGlobals->curtime + BEHAVIOR_FEAR_SAFETY_TIME;
}
else
{
if( gpGlobals->curtime > m_flTimeToSafety )
{
TaskComplete();
}
}
break;
case TASK_FEAR_GET_PATH_TO_SAFETY_HINT:
{
switch( GetOuter()->GetTaskInterrupt() )
{
case 0:// Find the hint node
{
ReleaseAllHints();
CAI_Hint *pHint = FindFearWithdrawalDest();
if( pHint == NULL )
{
TaskFail("Fear: Couldn't find hint node\n");
m_flDeferUntil = gpGlobals->curtime + 3.0f;// Don't bang the hell out of this behavior. If we don't find a node, take a short break and run regular AI.
}
else
{
m_hMovingToHint.Set( pHint );
GetOuter()->TaskInterrupt();
}
}
break;
case 1:// Do the pathfinding.
{
Assert( m_hMovingToHint != NULL );
AI_NavGoal_t goal(m_hMovingToHint->GetAbsOrigin());
goal.pTarget = NULL;
if( GetNavigator()->SetGoal( goal ) == false )
{
m_hMovingToHint.Set( NULL );
// Do whatever we'd want to do if we can't find a path
/*
Msg("Can't path to the Fear Hint!\n");
AI_NavGoal_t nearGoal( GOALTYPE_LOCATION_NEAREST_NODE, m_hRallyPoint->GetAbsOrigin(), AIN_DEF_ACTIVITY, 256 );
if ( GetNavigator()->SetGoal( nearGoal, AIN_CLEAR_PREVIOUS_STATE ) )
{
//FIXME: HACK! The internal pathfinding is setting this without our consent, so override it!
ClearCondition( COND_TASK_FAILED );
GetNavigator()->SetArrivalDirection( m_hRallyPoint->GetAbsAngles() );
TaskComplete();
return;
}
*/
}
else
{
GetNavigator()->SetArrivalDirection( m_hMovingToHint->GetAbsAngles() );
}
}
break;
}
}
break;
default:
BaseClass::RunTask( pTask );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : TRUE if I have an enemy and that enemy would attack me if it could
// Notes : Returns FALSE if the enemy is neutral or likes me.
//-----------------------------------------------------------------------------
bool CAI_FearBehavior::EnemyDislikesMe()
{
CBaseEntity *pEnemy = GetEnemy();
if( pEnemy == NULL )
return false;
if( pEnemy->MyNPCPointer() == NULL )
return false;
Disposition_t disposition = pEnemy->MyNPCPointer()->IRelationType(GetOuter());
Assert(disposition != D_ER);
if( disposition >= D_LI )
return false;
return true;
}
//-----------------------------------------------------------------------------
// This place is definitely no longer safe. Stop picking it for a while.
//-----------------------------------------------------------------------------
void CAI_FearBehavior::MarkAsUnsafe()
{
Assert( m_hSafePlaceHint );
// Disable the node to stop anyone from picking it for a while.
m_hSafePlaceHint->DisableForSeconds( 5.0f );
}
//-----------------------------------------------------------------------------
// Am I in safe place from my enemy?
//-----------------------------------------------------------------------------
bool CAI_FearBehavior::IsInASafePlace()
{
// No safe place in mind.
if( !m_SafePlaceMoveMonitor.IsMarkSet() )
return false;
// I have a safe place, but I'm not there.
if( m_SafePlaceMoveMonitor.TargetMoved(GetOuter()) )
return false;
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_FearBehavior::SpoilSafePlace()
{
m_SafePlaceMoveMonitor.ClearMark();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_FearBehavior::ReleaseAllHints()
{
if( m_hSafePlaceHint )
{
// If I have a safe place, unlock it for others.
m_hSafePlaceHint->Unlock();
// Don't make it available right away. I probably left for a good reason.
// We also don't want to oscillate
m_hSafePlaceHint->DisableForSeconds( 4.0f );
m_hSafePlaceHint = NULL;
}
if( m_hMovingToHint )
{
m_hMovingToHint->Unlock();
m_hMovingToHint = NULL;
}
m_SafePlaceMoveMonitor.ClearMark();
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
// Notes : This behavior runs when I have an enemy that I fear, but who
// does NOT hate or fear me (meaning they aren't going to fight me)
//-----------------------------------------------------------------------------
bool CAI_FearBehavior::CanSelectSchedule()
{
if( !GetOuter()->IsInterruptable() )
return false;
if( m_flDeferUntil > gpGlobals->curtime )
return false;
CBaseEntity *pEnemy = GetEnemy();
if( pEnemy == NULL )
return false;
//if( !HasCondition(COND_SEE_PLAYER) )
// return false;
if( !ai_enable_fear_behavior.GetBool() )
return false;
if( GetOuter()->IRelationType(pEnemy) != D_FR )
return false;
if( !pEnemy->ClassMatches("npc_hunter") )
return false;
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_FearBehavior::GatherConditions()
{
BaseClass::GatherConditions();
ClearCondition( COND_FEAR_ENEMY_CLOSE );
ClearCondition( COND_FEAR_ENEMY_TOO_CLOSE );
if( GetEnemy() )
{
float flEnemyDistSqr = GetAbsOrigin().DistToSqr(GetEnemy()->GetAbsOrigin());
if( flEnemyDistSqr < FEAR_ENEMY_TOLERANCE_TOO_CLOSE_DIST_SQR )
{
SetCondition( COND_FEAR_ENEMY_TOO_CLOSE );
if( IsInASafePlace() )
{
SpoilSafePlace();
}
}
else if( flEnemyDistSqr < FEAR_ENEMY_TOLERANCE_CLOSE_DIST_SQR && GetEnemy()->GetEnemy() == GetOuter() )
{
// Only become scared of an enemy at this range if they're my enemy, too
SetCondition( COND_FEAR_ENEMY_CLOSE );
if( IsInASafePlace() )
{
SpoilSafePlace();
}
}
}
ClearCondition(COND_FEAR_SEPARATED_FROM_PLAYER);
// Check for separation from the player
// -The player is farther away than 60 feet
// -I haven't seen the player in 2 seconds
//
// Here's the distance check:
CBasePlayer *pPlayer = AI_GetSinglePlayer();
if( pPlayer != NULL && GetAbsOrigin().DistToSqr(pPlayer->GetAbsOrigin()) >= Square( ai_fear_player_dist.GetFloat() * 1.5f ) )
{
SetCondition(COND_FEAR_SEPARATED_FROM_PLAYER);
}
// Here's the visibility check. We can't skip this because it's time-sensitive
if( GetOuter()->FVisible(pPlayer) )
{
m_flTimePlayerLastVisible = gpGlobals->curtime;
}
else
{
if( gpGlobals->curtime - m_flTimePlayerLastVisible >= 2.0f )
{
SetCondition(COND_FEAR_SEPARATED_FROM_PLAYER);
}
}
if( HasCondition(COND_FEAR_SEPARATED_FROM_PLAYER) )
{
//Msg("I am separated from player\n");
if( IsInASafePlace() )
{
SpoilSafePlace();
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_FearBehavior::BeginScheduleSelection()
{
if( m_hSafePlaceHint )
{
// We think we're safe. Is it true?
if( !IsInASafePlace() )
{
// no! So mark it so.
ReleaseAllHints();
}
}
m_flTimePlayerLastVisible = gpGlobals->curtime;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_FearBehavior::EndScheduleSelection()
{
// We don't have to release our hints or markers or anything here.
// Just because we ran other AI for a while doesn't mean we aren't still in a safe place.
//ReleaseAllHints();
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
// Notes : If fear behavior is running at all, we know we're afraid of our enemy
//-----------------------------------------------------------------------------
int CAI_FearBehavior::SelectSchedule()
{
bool bInSafePlace = IsInASafePlace();
if( !HasCondition(COND_HEAR_DANGER) )
{
if( !bInSafePlace )
{
// Always move to a safe place if we're not running from a danger sound
return SCHED_FEAR_MOVE_TO_SAFE_PLACE;
}
else
{
// We ARE in a safe place
if( HasCondition(COND_CAN_RANGE_ATTACK1) )
return SCHED_RANGE_ATTACK1;
return SCHED_FEAR_STAY_IN_SAFE_PLACE;
}
}
return BaseClass::SelectSchedule();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_FearBehavior::BuildScheduleTestBits()
{
BaseClass::BuildScheduleTestBits();
if( GetOuter()->GetState() != NPC_STATE_SCRIPT )
{
// Stop doing ANYTHING if we get scared.
//GetOuter()->SetCustomInterruptCondition( COND_HEAR_DANGER );
if( !IsCurSchedule(SCHED_FEAR_MOVE_TO_SAFE_PLACE_RETRY, false) && !IsCurSchedule(SCHED_FEAR_MOVE_TO_SAFE_PLACE, false) )
{
GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal(COND_FEAR_SEPARATED_FROM_PLAYER) );
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CAI_FearBehavior::TranslateSchedule( int scheduleType )
{
switch( scheduleType )
{
case SCHED_FEAR_MOVE_TO_SAFE_PLACE:
if( HasCondition(COND_FEAR_ENEMY_TOO_CLOSE) )
{
// If I'm moving to a safe place AND have an enemy too close to me,
// make the move to safety while ignoring the condition.
// this stops an oscillation
// IS THIS CODE EVER EVEN BEING CALLED? (sjb)
return SCHED_FEAR_MOVE_TO_SAFE_PLACE_RETRY;
}
break;
}
return BaseClass::TranslateSchedule( scheduleType );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CAI_Hint *CAI_FearBehavior::FindFearWithdrawalDest()
{
CAI_Hint *pHint;
CHintCriteria hintCriteria;
CAI_BaseNPC *pOuter = GetOuter();
Assert(pOuter != NULL);
hintCriteria.AddHintType( HINT_PLAYER_ALLY_FEAR_DEST );
hintCriteria.SetFlag( bits_HINT_NODE_VISIBLE_TO_PLAYER | bits_HINT_NOT_CLOSE_TO_ENEMY /*| bits_HINT_NODE_IN_VIEWCONE | bits_HINT_NPC_IN_NODE_FOV*/ );
hintCriteria.AddIncludePosition( AI_GetSinglePlayer()->GetAbsOrigin(), ( ai_fear_player_dist.GetFloat() ) );
pHint = CAI_HintManager::FindHint( pOuter, hintCriteria );
if( pHint )
{
// Reserve this node while I try to get to it. When I get there I will lock it.
// Otherwise, if I fail to get there, the node will come available again soon.
pHint->DisableForSeconds( 4.0f );
}
#if 0
else
{
Msg("DID NOT FIND HINT\n");
NDebugOverlay::Cross3D( GetOuter()->WorldSpaceCenter(), 32, 255, 255, 0, false, 10.0f );
}
#endif
return pHint;
}
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_FearBehavior )
DECLARE_TASK( TASK_FEAR_GET_PATH_TO_SAFETY_HINT )
DECLARE_TASK( TASK_FEAR_WAIT_FOR_SAFETY )
DECLARE_TASK( TASK_FEAR_IN_SAFE_PLACE )
DECLARE_CONDITION( COND_FEAR_ENEMY_CLOSE )
DECLARE_CONDITION( COND_FEAR_ENEMY_TOO_CLOSE )
DECLARE_CONDITION( COND_FEAR_SEPARATED_FROM_PLAYER )
//===============================================
//===============================================
DEFINE_SCHEDULE
(
SCHED_FEAR_MOVE_TO_SAFE_PLACE,
" Tasks"
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RUN_FROM_ENEMY"
" TASK_FEAR_GET_PATH_TO_SAFETY_HINT 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
" TASK_FEAR_IN_SAFE_PLACE 0"
" TASK_SET_SCHEDULE SCHEDULE:SCHED_FEAR_STAY_IN_SAFE_PLACE"
""
" Interrupts"
""
" COND_HEAR_DANGER"
" COND_NEW_ENEMY"
" COND_FEAR_ENEMY_TOO_CLOSE"
);
DEFINE_SCHEDULE
(
SCHED_FEAR_MOVE_TO_SAFE_PLACE_RETRY,
" Tasks"
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RUN_FROM_ENEMY"
" TASK_FEAR_GET_PATH_TO_SAFETY_HINT 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
" TASK_FEAR_IN_SAFE_PLACE 0"
" TASK_SET_SCHEDULE SCHEDULE:SCHED_FEAR_STAY_IN_SAFE_PLACE"
""
" Interrupts"
""
" COND_HEAR_DANGER"
" COND_NEW_ENEMY"
);
//===============================================
//===============================================
DEFINE_SCHEDULE
(
SCHED_FEAR_STAY_IN_SAFE_PLACE,
" Tasks"
" TASK_FEAR_WAIT_FOR_SAFETY 0"
""
" Interrupts"
""
" COND_NEW_ENEMY"
" COND_HEAR_DANGER"
" COND_FEAR_ENEMY_CLOSE"
" COND_FEAR_ENEMY_TOO_CLOSE"
" COND_CAN_RANGE_ATTACK1"
" COND_FEAR_SEPARATED_FROM_PLAYER"
);
AI_END_CUSTOM_SCHEDULE_PROVIDER()

View File

@@ -0,0 +1,98 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Deal intelligently with an enemy that we're afraid of
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#ifndef AI_BEHAVIOR_FEAR_H
#define AI_BEHAVIOR_FEAR_H
#ifdef _WIN32
#pragma once
#endif
#include "ai_behavior.h"
class CAI_FearBehavior : public CAI_SimpleBehavior
{
DECLARE_CLASS( CAI_FearBehavior, CAI_SimpleBehavior );
public:
CAI_FearBehavior();
void Precache( void );
virtual const char *GetName() { return "Fear"; }
virtual bool CanSelectSchedule();
void GatherConditions();
virtual void BeginScheduleSelection();
virtual void EndScheduleSelection();
void StartTask( const Task_t *pTask );
void RunTask( const Task_t *pTask );
//void BuildScheduleTestBits();
//int TranslateSchedule( int scheduleType );
//void OnStartSchedule( int scheduleType );
//void InitializeBehavior();
bool EnemyDislikesMe();
void MarkAsUnsafe();
bool IsInASafePlace();
void SpoilSafePlace();
void ReleaseAllHints();
CAI_Hint *FindFearWithdrawalDest();
void BuildScheduleTestBits();
int TranslateSchedule( int scheduleType );
enum
{
SCHED_FEAR_MOVE_TO_SAFE_PLACE = BaseClass::NEXT_SCHEDULE,
SCHED_FEAR_MOVE_TO_SAFE_PLACE_RETRY,
SCHED_FEAR_STAY_IN_SAFE_PLACE,
NEXT_SCHEDULE,
TASK_FEAR_GET_PATH_TO_SAFETY_HINT = BaseClass::NEXT_TASK,
TASK_FEAR_WAIT_FOR_SAFETY,
TASK_FEAR_IN_SAFE_PLACE,
NEXT_TASK,
COND_FEAR_ENEMY_CLOSE = BaseClass::NEXT_CONDITION, // within 30 feet
COND_FEAR_ENEMY_TOO_CLOSE, // within 5 feet
COND_FEAR_SEPARATED_FROM_PLAYER,
NEXT_CONDITION,
};
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
public:
private:
virtual int SelectSchedule();
float m_flTimeToSafety;
float m_flTimePlayerLastVisible;
float m_flDeferUntil;
CAI_MoveMonitor m_SafePlaceMoveMonitor;
CHandle<CAI_Hint> m_hSafePlaceHint;
CHandle<CAI_Hint> m_hMovingToHint;
DECLARE_DATADESC();
};
#endif // AI_BEHAVIOR_FEAR_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,383 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef AI_BEHAVIOR_FOLLOW_H
#define AI_BEHAVIOR_FOLLOW_H
#include "simtimer.h"
#include "ai_behavior.h"
#include "ai_goalentity.h"
#include "ai_utils.h"
#include "ai_moveshoot.h"
#ifdef HL2_EPISODIC
#include "hl2_gamerules.h"
#endif
#if defined( _WIN32 )
#pragma once
#endif
//-----------------------------------------------------------------------------
// NOTE: these must correspond with the AI_FollowFormation_t array in AI_Behavior_Follow.cpp!!
//-----------------------------------------------------------------------------
enum AI_Formations_t
{
AIF_SIMPLE,
AIF_WIDE,
AIF_ANTLION,
AIF_COMMANDER,
AIF_TIGHT,
AIF_MEDIUM,
AIF_SIDEKICK,
AIF_HUNTER,
AIF_VORTIGAUNT,
};
enum AI_FollowFormationFlags_t
{
AIFF_DEFAULT = 0,
AIFF_USE_FOLLOW_POINTS = 0x01,
AIFF_REQUIRE_LOS_OUTSIDE_COMBAT = 0x02,
};
//-----------------------------------------------------------------------------
//
// CAI_FollowGoal
//
// Purpose: A level tool to control the follow behavior. Use is not required
// in order to use behavior.
//
//-----------------------------------------------------------------------------
class CAI_FollowGoal : public CAI_GoalEntity
{
DECLARE_CLASS( CAI_FollowGoal, CAI_GoalEntity );
public:
virtual void EnableGoal( CAI_BaseNPC *pAI );
virtual void DisableGoal( CAI_BaseNPC *pAI );
#ifdef HL2_EPISODIC
virtual void InputOutsideTransition( inputdata_t &inputdata );
#endif
int m_iFormation;
DECLARE_DATADESC();
};
//-----------------------------------------------------------------------------
int AIGetNumFollowers( CBaseEntity *pEntity, string_t iszClassname = NULL_STRING );
//-----------------------------------------------------------------------------
struct AI_FollowNavInfo_t
{
int flags;
Vector position;
float range;
float Zrange;
float tolerance;
float followPointTolerance;
float targetMoveTolerance;
float repathOnRouteTolerance;
float walkTolerance;
float coverTolerance;
float enemyLOSTolerance;
float chaseEnemyTolerance;
DECLARE_SIMPLE_DATADESC();
};
struct AI_FollowGroup_t;
struct AI_FollowManagerInfoHandle_t
{
AI_FollowGroup_t *m_pGroup;
int m_hFollower;
};
//-------------------------------------
struct AI_FollowParams_t
{
AI_FollowParams_t( AI_Formations_t formation = AIF_SIMPLE, bool bNormalMemoryDiscard = false )
: formation(formation),
bNormalMemoryDiscard( bNormalMemoryDiscard )
{
}
AI_Formations_t formation;
bool bNormalMemoryDiscard;
DECLARE_SIMPLE_DATADESC();
};
//-------------------------------------
class CAI_FollowBehavior : public CAI_SimpleBehavior
{
DECLARE_CLASS( CAI_FollowBehavior, CAI_SimpleBehavior );
public:
CAI_FollowBehavior( const AI_FollowParams_t &params = AIF_SIMPLE );
~CAI_FollowBehavior();
virtual int DrawDebugTextOverlays( int text_offset );
virtual void DrawDebugGeometryOverlays();
// Returns true if the NPC is actively following a target.
bool IsActive( void );
void SetParameters( const AI_FollowParams_t &params );
virtual const char *GetName() { return "Follow"; }
AI_Formations_t GetFormation() const { return m_params.formation; }
virtual bool CanSelectSchedule();
const AI_FollowNavInfo_t &GetFollowGoalInfo();
CBaseEntity * GetFollowTarget();
void SetFollowTarget( CBaseEntity *pLeader, bool fFinishCurSchedule = false );
CAI_FollowGoal *GetFollowGoal() { return m_hFollowGoalEnt; } // if any
bool SetFollowGoal( CAI_FollowGoal *pGoal, bool fFinishCurSchedule = false );
void ClearFollowGoal( CAI_FollowGoal *pGoal );
void SetFollowGoalDirect( CAI_FollowGoal *pGoal );
virtual bool FarFromFollowTarget() { return ( m_hFollowTarget && (GetAbsOrigin() - m_hFollowTarget->GetAbsOrigin()).LengthSqr() > (75*12)*(75*12) ); }
virtual bool TargetIsUnreachable() { return m_bTargetUnreachable; }
int GetNumFailedFollowAttempts() { return m_nFailedFollowAttempts; }
float GetTimeFailFollowStarted() { return m_flTimeFailFollowStarted; }
bool FollowTargetVisible() { return HasCondition( COND_FOLLOW_TARGET_VISIBLE ); };
bool IsMovingToFollowTarget();
float GetGoalRange();
float GetGoalZRange();
virtual Activity NPC_TranslateActivity( Activity activity );
virtual int TranslateSchedule( int scheduleType );
virtual void StartTask( const Task_t *pTask );
virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode );
virtual void TaskComplete( bool fIgnoreSetFailedCondition = false );
virtual void GatherConditions();
protected:
const Vector &GetGoalPosition();
virtual bool ShouldFollow();
friend class CAI_FollowManager;
virtual void BeginScheduleSelection();
virtual void EndScheduleSelection();
virtual void CleanupOnDeath( CBaseEntity *pCulprit, bool bFireDeathOutput );
virtual void Precache();
virtual int SelectSchedule();
virtual int FollowCallBaseSelectSchedule() { return BaseClass::SelectSchedule(); }
virtual void OnStartSchedule( int scheduleType );
virtual void RunTask( const Task_t *pTask );
void BuildScheduleTestBits();
bool IsCurScheduleFollowSchedule();
virtual bool IsCurTaskContinuousMove();
virtual void OnMovementFailed();
virtual void OnMovementComplete();
virtual bool FValidateHintType( CAI_Hint *pHint );
bool IsValidCover( const Vector &vLocation, CAI_Hint const *pHint );
bool IsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint );
bool FindCoverFromEnemyAtFollowTarget( float coverRadius, Vector *pResult );
bool ShouldAlwaysThink();
bool ShouldMoveToFollowTarget();
int SelectScheduleManagePosition();
int SelectScheduleFollowPoints();
int SelectScheduleMoveToFormation();
void GetFollowTargetViewLoc( Vector *pResult);
bool ValidateFaceTarget( Vector *pFaceTarget );
//----------------------------
bool ShouldUseFollowPoints();
bool HasFollowPoint();
void SetFollowPoint( CAI_Hint *pHintNode );
void ClearFollowPoint();
const Vector & GetFollowPoint();
CAI_Hint * FindFollowPoint();
bool IsFollowPointInRange();
bool ShouldIgnoreFollowPointFacing();
//----------------------------
bool UpdateFollowPosition();
const int GetGoalFlags();
float GetGoalTolerance();
bool PlayerIsPushing();
bool IsFollowTargetInRange( float rangeMultiplier = 1.0 );
bool IsFollowGoalInRange( float tolerance, float zTolerance, int flags );
virtual bool IsChaseGoalInRange();
void NoteFailedFollow();
void NoteSuccessfulFollow();
//----------------------------
protected:
enum
{
SCHED_FOLLOWER_MOVE_AWAY_FAIL = BaseClass::NEXT_SCHEDULE, // Turn back toward player
SCHED_FOLLOWER_MOVE_AWAY_END,
SCHED_FOLLOW,
SCHED_FOLLOWER_IDLE_STAND,
SCHED_MOVE_TO_FACE_FOLLOW_TARGET,
SCHED_FACE_FOLLOW_TARGET,
SCHED_FOLLOWER_GO_TO_WAIT_POINT,
SCHED_FOLLOWER_GO_TO_WAIT_POINT_FAIL,
SCHED_FOLLOWER_STAND_AT_WAIT_POINT,
SCHED_FOLLOWER_COMBAT_FACE,
NEXT_SCHEDULE,
TASK_CANT_FOLLOW = BaseClass::NEXT_TASK,
TASK_FACE_FOLLOW_TARGET,
TASK_MOVE_TO_FOLLOW_POSITION,
TASK_GET_PATH_TO_FOLLOW_POSITION,
TASK_SET_FOLLOW_TARGET_MARK,
TASK_FOLLOWER_FACE_TACTICAL,
TASK_SET_FOLLOW_DELAY,
TASK_GET_PATH_TO_FOLLOW_POINT,
TASK_ARRIVE_AT_FOLLOW_POINT,
TASK_SET_FOLLOW_POINT_STAND_SCHEDULE,
TASK_BEGIN_STAND_AT_WAIT_POINT,
NEXT_TASK,
COND_TARGET_MOVED_FROM_MARK = BaseClass::NEXT_CONDITION,
COND_FOUND_WAIT_POINT,
COND_FOLLOW_DELAY_EXPIRED,
COND_FOLLOW_TARGET_VISIBLE,
COND_FOLLOW_TARGET_NOT_VISIBLE,
COND_FOLLOW_WAIT_POINT_INVALID,
COND_FOLLOW_PLAYER_IS_LIT,
COND_FOLLOW_PLAYER_IS_NOT_LIT,
NEXT_CONDITION,
};
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
protected:
//----------------------------
EHANDLE m_hFollowTarget;
AI_FollowNavInfo_t m_FollowNavGoal;
float m_flTimeUpdatedFollowPosition;
bool m_bFirstFacing;
float m_flTimeFollowTargetVisible;
CAI_MoveMonitor m_TargetMonitor;
bool m_bTargetUnreachable;
bool m_bFollowNavFailed; // Set when pathfinding fails to limit impact of m_FollowDelay on ShouldFollow
int m_nFailedFollowAttempts;
float m_flTimeFailFollowStarted;
Vector m_vFollowMoveAnchor;
bool m_bMovingToCover;
float m_flOriginalEnemyDiscardTime;
float m_SavedDistTooFar;
CRandStopwatch m_FollowDelay;
CSimpleSimTimer m_RepathOnFollowTimer;
//---------------------------------
Activity m_CurrentFollowActivity;
//---------------------------------
CRandSimTimer m_TimeBlockUseWaitPoint;
CSimTimer m_TimeCheckForWaitPoint;
CAI_Hint * m_pInterruptWaitPoint;
//---------------------------------
CRandSimTimer m_TimeBeforeSpreadFacing;
CRandSimTimer m_TimeNextSpreadFacing;
//---------------------------------
AI_FollowManagerInfoHandle_t m_hFollowManagerInfo;
AI_FollowParams_t m_params;
//---------------------------------
CHandle<CAI_FollowGoal> m_hFollowGoalEnt;
//---------------------------------
DECLARE_DATADESC();
};
//-------------------------------------
inline const AI_FollowNavInfo_t &CAI_FollowBehavior::GetFollowGoalInfo()
{
return m_FollowNavGoal;
}
//-------------------------------------
inline const int CAI_FollowBehavior::GetGoalFlags()
{
return m_FollowNavGoal.flags;
}
//-------------------------------------
inline const Vector &CAI_FollowBehavior::GetGoalPosition()
{
return m_FollowNavGoal.position;
}
//-------------------------------------
inline float CAI_FollowBehavior::GetGoalTolerance()
{
return m_FollowNavGoal.tolerance;
}
//-------------------------------------
inline float CAI_FollowBehavior::GetGoalRange()
{
return m_FollowNavGoal.range;
}
//-------------------------------------
inline float CAI_FollowBehavior::GetGoalZRange()
{
return m_FollowNavGoal.Zrange;
}
//-----------------------------------------------------------------------------
#endif // AI_BEHAVIOR_FOLLOW_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,236 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef AI_BEHAVIOR_LEAD_H
#define AI_BEHAVIOR_LEAD_H
#include "simtimer.h"
#include "ai_behavior.h"
#if defined( _WIN32 )
#pragma once
#endif
typedef const char *AIConcept_t;
// Speak concepts
#define TLK_LEAD_START "TLK_LEAD_START"
#define TLK_LEAD_ARRIVAL "TLK_LEAD_ARRIVAL"
#define TLK_LEAD_SUCCESS "TLK_LEAD_SUCCESS"
#define TLK_LEAD_FAILURE "lead_fail"
#define TLK_LEAD_COMINGBACK "TLK_LEAD_COMINGBACK"
#define TLK_LEAD_CATCHUP "TLK_LEAD_CATCHUP"
#define TLK_LEAD_RETRIEVE "TLK_LEAD_RETRIEVE"
#define TLK_LEAD_ATTRACTPLAYER "TLK_LEAD_ATTRACTPLAYER"
#define TLK_LEAD_WAITOVER "TLK_LEAD_WAITOVER"
#define TLK_LEAD_MISSINGWEAPON "TLK_LEAD_MISSING_WEAPON"
#define TLK_LEAD_IDLE "TLK_LEAD_IDLE"
//-----------------------------------------------------------------------------
// class CAI_LeadBehavior
//
// Purpose:
//
//-----------------------------------------------------------------------------
enum LeadBehaviorEvents_t
{
LBE_ARRIVAL,
LBE_ARRIVAL_DONE,
LBE_SUCCESS,
LBE_FAILURE,
LBE_DONE,
};
//-------------------------------------
//
// Handler class interface to listen to and modify actions of the lead behavior.
// Could be an NPC, or another entity (like a goal entity)
//
class CAI_LeadBehaviorHandler
{
public:
virtual void OnEvent( int event ) {}
virtual const char *GetConceptModifiers( const char *pszConcept ) { return NULL; }
};
//-------------------------------------
enum AI_LeadFlags_t
{
AILF_NO_DEF_SUCCESS = 0x01,
AILF_NO_DEF_FAILURE = 0x02,
AILF_USE_GOAL_FACING = 0x04,
};
struct AI_LeadArgs_t
{
const char *pszGoal;
const char *pszWaitPoint;
unsigned flags;
float flWaitDistance;
float flLeadDistance;
float flRetrieveDistance;
float flSuccessDistance;
bool bRun;
int iRetrievePlayer;
int iRetrieveWaitForSpeak;
int iComingBackWaitForSpeak;
bool bStopScenesWhenPlayerLost;
bool bDontSpeakStart;
bool bLeadDuringCombat;
bool bGagLeader;
DECLARE_SIMPLE_DATADESC();
};
class CAI_LeadBehavior : public CAI_SimpleBehavior
{
DECLARE_CLASS( CAI_LeadBehavior, CAI_SimpleBehavior );
public:
CAI_LeadBehavior()
: m_pSink(NULL),
m_LostTimer( 3.0, 4.0 ),
m_LostLOSTimer( 2.0, 3.0 )
{
memset( &m_args, 0, sizeof(m_args) );
ClearGoal();
}
virtual void OnRestore();
virtual const char *GetName() { return "Lead"; }
virtual int DrawDebugTextOverlays( int text_offset );
virtual bool IsNavigationUrgent();
void LeadPlayer( const AI_LeadArgs_t &leadArgs, CAI_LeadBehaviorHandler *pSink = NULL );
void StopLeading( void );
virtual bool CanSelectSchedule();
void BeginScheduleSelection();
virtual bool IsCurTaskContinuousMove();
bool SetGoal( const AI_LeadArgs_t &args );
void ClearGoal() { m_goal = vec3_origin; m_waitpoint = vec3_origin; m_pSink = NULL; m_weaponname = NULL_STRING; }
bool HasGoal() const { return (m_goal != vec3_origin); }
bool HasWaitPoint() const { return (m_waitpoint != vec3_origin); }
bool Connect( CAI_LeadBehaviorHandler *);
bool Disconnect( CAI_LeadBehaviorHandler *);
void SetWaitForWeapon( string_t iszWeaponName ) { m_weaponname = iszWeaponName; m_flWeaponSafetyTimeOut = gpGlobals->curtime + 60; }
enum
{
// Schedules
SCHED_LEAD_PLAYER = BaseClass::NEXT_SCHEDULE,
SCHED_LEAD_PAUSE,
SCHED_LEAD_PAUSE_COMBAT,
SCHED_LEAD_RETRIEVE,
SCHED_LEAD_RETRIEVE_WAIT,
SCHED_LEAD_SUCCEED,
SCHED_LEAD_AWAIT_SUCCESS,
SCHED_LEAD_WAITFORPLAYER,
SCHED_LEAD_WAITFORPLAYERIDLE,
SCHED_LEAD_PLAYERNEEDSWEAPON,
SCHED_LEAD_SPEAK_START,
SCHED_LEAD_SPEAK_THEN_RETRIEVE_PLAYER,
SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER,
NEXT_SCHEDULE,
// Tasks
TASK_GET_PATH_TO_LEAD_GOAL = BaseClass::NEXT_TASK,
TASK_STOP_LEADING,
TASK_LEAD_FACE_GOAL,
TASK_LEAD_ARRIVE,
TASK_LEAD_SUCCEED,
TASK_LEAD_GET_PATH_TO_WAITPOINT,
TASK_LEAD_WAVE_TO_PLAYER,
TASK_LEAD_PLAYER_NEEDS_WEAPON,
TASK_LEAD_SPEAK_START,
TASK_LEAD_MOVE_TO_RANGE,
TASK_LEAD_RETRIEVE_WAIT,
TASK_LEAD_WALK_PATH,
NEXT_TASK,
// Conditions
COND_LEAD_FOLLOWER_LOST = BaseClass::NEXT_CONDITION,
COND_LEAD_FOLLOWER_LAGGING,
COND_LEAD_FOLLOWER_NOT_LAGGING,
COND_LEAD_FOLLOWER_VERY_CLOSE,
COND_LEAD_SUCCESS,
COND_LEAD_HAVE_FOLLOWER_LOS,
COND_LEAD_FOLLOWER_MOVED_FROM_MARK,
COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME,
NEXT_CONDITION
};
private:
void GatherConditions();
virtual int SelectSchedule();
virtual int TranslateSchedule( int scheduleType );
virtual void StartTask( const Task_t *pTask );
virtual void RunTask( const Task_t *pTask );
bool GetClosestPointOnRoute( const Vector &targetPos, Vector *pVecClosestPoint );
bool PlayerIsAheadOfMe( bool bForce = false );
bool Speak( AIConcept_t concept );
bool IsSpeaking();
// --------------------------------
//
// Sink notifiers. Isolated to limit exposure to actual sink storage,
// provide debugging pinch pount, and allow for class-local logic
// in addition to sink logic
//
void NotifyEvent( int event ) { if ( m_pSink ) m_pSink->OnEvent( event ) ; }
const char * GetConceptModifiers( const char *pszConcept ) { return ( m_pSink ) ? m_pSink->GetConceptModifiers( pszConcept ) : NULL; }
// --------------------------------
AI_LeadArgs_t m_args;
CAI_LeadBehaviorHandler *m_pSink;
EHANDLE m_hSinkImplementor;
// --------------------------------
Vector m_goal;
float m_goalyaw;
Vector m_waitpoint;
float m_waitdistance;
float m_leaddistance;
float m_retrievedistance;
float m_successdistance;
string_t m_weaponname;
bool m_run;
bool m_gagleader;
bool m_hasspokenstart;
bool m_hasspokenarrival;
bool m_hasPausedScenes;
float m_flSpeakNextNagTime;
float m_flWeaponSafetyTimeOut;
float m_flNextLeadIdle;
bool m_bInitialAheadTest;
CAI_MoveMonitor m_MoveMonitor;
CRandStopwatch m_LostTimer;
CRandStopwatch m_LostLOSTimer;
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
DECLARE_DATADESC();
};
//-----------------------------------------------------------------------------
#endif // AI_BEHAVIOR_LEAD_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,245 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef AI_BEHAVIOR_PASSENGER_H
#define AI_BEHAVIOR_PASSENGER_H
#ifdef _WIN32
#pragma once
#endif
#include "ai_speech.h"
#include "ai_behavior.h"
#include "ai_utils.h"
#include "vehicle_jeep_episodic.h"
#define STOPPED_VELOCITY_THRESHOLD 32.0f
#define STOPPED_VELOCITY_THRESHOLD_SQR (STOPPED_VELOCITY_THRESHOLD*STOPPED_VELOCITY_THRESHOLD)
#define STARTED_VELOCITY_THRESHOLD 64.0f
#define STARTED_VELOCITY_THRESHOLD_SQR (STARTED_VELOCITY_THRESHOLD*STARTED_VELOCITY_THRESHOLD)
// Custom activities
extern int ACT_PASSENGER_IDLE;
extern int ACT_PASSENGER_RANGE_ATTACK1;
// ---------------------------------------------
// Vehicle state
// ---------------------------------------------
struct passengerVehicleState_t
{
Vector m_vecLastLocalVelocity;
Vector m_vecDeltaVelocity;
QAngle m_vecLastAngles;
float m_flNextWarningTime;
float m_flLastSpeedSqr;
bool m_bPlayerInVehicle;
bool m_bWasBoosting;
bool m_bWasOverturned;
DECLARE_SIMPLE_DATADESC();
};
// ---------------------------------------------
// Passenger intent
// ---------------------------------------------
enum passesngerVehicleIntent_e
{
PASSENGER_INTENT_NONE,
PASSENGER_INTENT_ENTER, // We want to be in the vehicle
PASSENGER_INTENT_EXIT, // We want to be outside the vehicle
};
// ---------------------------------------------
// Passenger state functions
// ---------------------------------------------
enum PassengerState_e
{
PASSENGER_STATE_OUTSIDE = 0, // Not in the vehicle
PASSENGER_STATE_ENTERING,
PASSENGER_STATE_INSIDE,
PASSENGER_STATE_EXITING,
};
class CAI_PassengerBehavior : public CAI_SimpleBehavior
{
DECLARE_CLASS( CAI_PassengerBehavior, CAI_SimpleBehavior );
DECLARE_DATADESC()
public:
CAI_PassengerBehavior( void );
enum
{
// Schedules
SCHED_PASSENGER_IDLE = BaseClass::NEXT_SCHEDULE,
SCHED_PASSENGER_ENTER_VEHICLE,
SCHED_PASSENGER_EXIT_VEHICLE,
SCHED_PASSENGER_RUN_TO_ENTER_VEHICLE,
SCHED_PASSENGER_ENTER_VEHICLE_PAUSE,
SCHED_PASSENGER_RUN_TO_ENTER_VEHICLE_FAILED,
SCHED_PASSENGER_PLAY_SCRIPTED_ANIM,
NEXT_SCHEDULE,
// Tasks
TASK_PASSENGER_ENTER_VEHICLE = BaseClass::NEXT_TASK,
TASK_PASSENGER_EXIT_VEHICLE,
TASK_PASSENGER_ATTACH_TO_VEHICLE,
TASK_PASSENGER_DETACH_FROM_VEHICLE,
TASK_PASSENGER_SET_IDEAL_ENTRY_YAW,
NEXT_TASK,
// Conditions
COND_PASSENGER_HARD_IMPACT = BaseClass::NEXT_CONDITION,
COND_PASSENGER_ENTERING,
COND_PASSENGER_EXITING,
COND_PASSENGER_VEHICLE_STARTED,
COND_PASSENGER_VEHICLE_STOPPED,
COND_PASSENGER_OVERTURNED,
COND_PASSENGER_CANCEL_ENTER,
COND_PASSENGER_ERRATIC_DRIVING,
COND_PASSENGER_PLAYER_ENTERED_VEHICLE,
COND_PASSENGER_PLAYER_EXITED_VEHICLE,
COND_PASSENGER_JOSTLE_SMALL,
NEXT_CONDITION
};
bool ForceVehicleInteraction( const char *lpszInteractionName, CBaseCombatCharacter *pOther );
virtual bool CanSelectSchedule( void );
virtual int SelectSchedule( void );
virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode );
virtual void RunTask( const Task_t *pTask );
virtual void StartTask( const Task_t *pTask );
virtual void BuildScheduleTestBits( void );
virtual int TranslateSchedule( int scheduleType );
virtual void GetEntryTarget( Vector *vecOrigin, QAngle *vecAngles );
virtual void GatherConditions( void );
virtual void ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet );
virtual void Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity );
virtual void ClearSchedule( const char *szReason );
virtual bool IsInterruptable( void );
virtual void PrescheduleThink( void );
virtual void CancelEnterVehicle( void );
virtual const char *GetName( void ) { return "Passenger"; }
virtual string_t GetRoleName( void ) { return MAKE_STRING( "passenger" ); }
// Enable/disable code
void Enable( CPropJeepEpisodic *pVehicle, bool bImmediateEntrance = false );
void Disable( void );
bool IsEnabled( void ) const { return m_bEnabled; }
virtual void EnterVehicle( void );
virtual void ExitVehicle( void );
void AddPhysicsPush( float force );
CPropVehicleDriveable *GetTargetVehicle( void ) const { return m_hVehicle; }
PassengerState_e GetPassengerState( void ) const { return m_PassengerState; }
virtual void OnRestore();
protected:
virtual int SelectTransitionSchedule( void );
bool SpeakIfAllowed( AIConcept_t concept, const char *modifiers = NULL, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 );
bool CanExitVehicle( void );
void SetTransitionSequence( int nSequence );
void AttachToVehicle( void );
virtual void OnExitVehicleFailed( void ) { } // NPC attempted to leave vehicle, but was unable to
virtual void GatherVehicleStateConditions( void );
// ------------------------------------------
// Entry/exit transition code
// ------------------------------------------
virtual void FinishEnterVehicle( void );
virtual void FinishExitVehicle( void );
void DetachFromVehicle( void );
void DrawDebugTransitionInfo( const Vector &vecIdealPos, const QAngle &vecIdealAngles, const Vector &vecAnimPos, const QAngle &vecAnimAngles );
bool GetEntryPoint( int nSequence, Vector *vecEntryPoint, QAngle *vecEntryAngles = NULL );
bool GetExitPoint( int nSequence, Vector *vecExitPoint, QAngle *vecExitAngles = NULL );
bool PointIsNavigable( const Vector &vecTargetPos );
bool ReserveEntryPoint( VehicleSeatQuery_e eSeatSearchType );
bool ReserveExitPoint( void );
bool FindGroundAtPosition( const Vector &in, float flUpDelta, float flDownDelta, Vector *out );
bool DoTransitionMovement( void );
bool GetSequenceBlendAmount( float flCycle, float *posBlend, float *angBlend );
bool LocalIntervalMovement( float flInterval, bool &bMoveSeqFinished, Vector &newPosition, QAngle &newAngles );
void GetTransitionAnimationIdeal( float flCycle, const Vector &vecTargetPos, const QAngle &vecTargetAngles, Vector *idealOrigin, QAngle *idealAngles );
float GetNextCycleForInterval( int nSequence, float flInterval );
void GetLocalVehicleVelocity( Vector *pOut );
void CacheBlendTargets( void );
void InitVehicleState( void );
int FindEntrySequence( bool bNearest = false );
int FindExitSequence( void );
bool IsValidTransitionPoint( const Vector &vecStartPos, const Vector &vecEndPos );
void SetPassengerState( PassengerState_e state ) { m_PassengerState = state; }
PassengerState_e m_PassengerState; // State we're in, for the vehicle
// ---------------------------------------------
bool IsPassengerHostile( void );
passengerVehicleState_t m_vehicleState; // Internal vehicle state
CHandle <CPropVehicleDriveable> m_hVehicle; // The vehicle we're bound to
CHandle <CEntityBlocker> m_hBlocker; // Blocking entity for space reservation
Vector m_vecTargetPosition; // Target destination for exiting the vehicle
QAngle m_vecTargetAngles; // Target angles for exiting the vehicle
bool m_bEnabled; // If the behavior is running
passesngerVehicleIntent_e m_PassengerIntent; // Gives us information about whether we're meant to get in/out, etc.
int m_nTransitionSequence; // Animation we're using to transition with
float m_flOriginStartFrame;
float m_flOriginEndFrame;
float m_flAnglesStartFrame;
float m_flAnglesEndFrame;
protected:
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
};
class CTraceFilterVehicleTransition : public CTraceFilterSkipTwoEntities
{
public:
DECLARE_CLASS( CTraceFilterVehicleTransition, CTraceFilterSkipTwoEntities );
CTraceFilterVehicleTransition( const IHandleEntity *passentity, const IHandleEntity *passentity2, int collisionGroup ) :
CTraceFilterSkipTwoEntities( passentity, passentity2, collisionGroup ) {}
bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
{
bool bRet = BaseClass::ShouldHitEntity( pServerEntity, contentsMask );
CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
if ( pEntity )
{
IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
if ( pPhys )
{
// Ignore physics objects
// TODO: This will have to be fleshed out more as cases arise
if ( pPhys->IsMoveable() && pPhys->GetMass() < 80.0f )
return false;
}
}
return bRet;
}
};
#endif // AI_BEHAVIOR_PASSENGER_H

View File

@@ -0,0 +1,459 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "ai_motor.h"
#include "ai_behavior_rappel.h"
#include "beam_shared.h"
#include "rope.h"
#include "eventqueue.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
BEGIN_DATADESC( CAI_RappelBehavior )
DEFINE_FIELD( m_bWaitingToRappel, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bOnGround, FIELD_BOOLEAN ),
DEFINE_FIELD( m_hLine, FIELD_EHANDLE ),
DEFINE_FIELD( m_vecRopeAnchor, FIELD_POSITION_VECTOR ),
END_DATADESC();
//=========================================================
//=========================================================
class CRopeAnchor : public CPointEntity
{
DECLARE_CLASS( CRopeAnchor, CPointEntity );
public:
void Spawn( void );
void FallThink( void );
void RemoveThink( void );
EHANDLE m_hRope;
DECLARE_DATADESC();
};
BEGIN_DATADESC( CRopeAnchor )
DEFINE_FIELD( m_hRope, FIELD_EHANDLE ),
DEFINE_THINKFUNC( FallThink ),
DEFINE_THINKFUNC( RemoveThink ),
END_DATADESC();
LINK_ENTITY_TO_CLASS( rope_anchor, CRopeAnchor );
//---------------------------------------------------------
//---------------------------------------------------------
#define RAPPEL_ROPE_WIDTH 1
void CRopeAnchor::Spawn()
{
BaseClass::Spawn();
// Decent enough default in case something happens to our owner!
float flDist = 384;
if( GetOwnerEntity() )
{
flDist = fabs( GetOwnerEntity()->GetAbsOrigin().z - GetAbsOrigin().z );
}
m_hRope = CRopeKeyframe::CreateWithSecondPointDetached( this, -1, flDist, RAPPEL_ROPE_WIDTH, "cable/cable.vmt", 5, true );
ASSERT( m_hRope != NULL );
SetThink( &CRopeAnchor::FallThink );
SetNextThink( gpGlobals->curtime + 0.2 );
}
//---------------------------------------------------------
//---------------------------------------------------------
void CRopeAnchor::FallThink()
{
SetMoveType( MOVETYPE_FLYGRAVITY );
Vector vecVelocity = GetAbsVelocity();
vecVelocity.x = random->RandomFloat( -30.0f, 30.0f );
vecVelocity.y = random->RandomFloat( -30.0f, 30.0f );
SetAbsVelocity( vecVelocity );
SetThink( &CRopeAnchor::RemoveThink );
SetNextThink( gpGlobals->curtime + 3.0 );
}
//---------------------------------------------------------
//---------------------------------------------------------
void CRopeAnchor::RemoveThink()
{
UTIL_Remove( m_hRope );
SetThink( &CRopeAnchor::SUB_Remove );
SetNextThink( gpGlobals->curtime );
}
//=========================================================
//=========================================================
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CAI_RappelBehavior::CAI_RappelBehavior()
{
m_hLine = NULL;
m_bWaitingToRappel = false;
m_bOnGround = true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CAI_RappelBehavior::KeyValue( const char *szKeyName, const char *szValue )
{
if( FStrEq( szKeyName, "waitingtorappel" ) )
{
m_bWaitingToRappel = ( atoi(szValue) != 0);
m_bOnGround = !m_bWaitingToRappel;
return true;
}
return BaseClass::KeyValue( szKeyName, szValue );
}
void CAI_RappelBehavior::Precache()
{
CBaseEntity::PrecacheModel( "cable/cable.vmt" );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#define RAPPEL_MAX_SPEED 600 // Go this fast if you're really high.
#define RAPPEL_MIN_SPEED 60 // Go no slower than this.
#define RAPPEL_DECEL_DIST (20.0f * 12.0f) // Start slowing down when you're this close to the ground.
void CAI_RappelBehavior::SetDescentSpeed()
{
// Trace to the floor and see how close we're getting. Slow down if we're close.
// STOP if there's an NPC under us.
trace_t tr;
AI_TraceLine( GetOuter()->GetAbsOrigin(), GetOuter()->GetAbsOrigin() - Vector( 0, 0, 8192 ), MASK_SHOT, GetOuter(), COLLISION_GROUP_NONE, &tr );
float flDist = fabs( GetOuter()->GetAbsOrigin().z - tr.endpos.z );
float speed = RAPPEL_MAX_SPEED;
if( flDist <= RAPPEL_DECEL_DIST )
{
float factor;
factor = flDist / RAPPEL_DECEL_DIST;
speed = MAX( RAPPEL_MIN_SPEED, speed * factor );
}
Vector vecNewVelocity = vec3_origin;
vecNewVelocity.z = -speed;
GetOuter()->SetAbsVelocity( vecNewVelocity );
}
void CAI_RappelBehavior::CleanupOnDeath( CBaseEntity *pCulprit, bool bFireDeathOutput )
{
BaseClass::CleanupOnDeath( pCulprit, bFireDeathOutput );
//This will remove the beam and create a rope if the NPC dies while rappeling down.
if ( m_hLine )
{
CAI_BaseNPC *pNPC = GetOuter();
if ( pNPC )
{
CutZipline();
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTask -
//-----------------------------------------------------------------------------
void CAI_RappelBehavior::StartTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_MOVE_AWAY_PATH:
GetOuter()->GetMotor()->SetIdealYaw( UTIL_AngleMod( GetOuter()->GetLocalAngles().y - 180.0f ) );
BaseClass::StartTask( pTask );
break;
case TASK_RANGE_ATTACK1:
BaseClass::StartTask( pTask );
break;
case TASK_RAPPEL:
{
CreateZipline();
SetDescentSpeed();
}
break;
case TASK_HIT_GROUND:
m_bOnGround = true;
if( GetOuter()->GetGroundEntity() != NULL && GetOuter()->GetGroundEntity()->IsNPC() && GetOuter()->GetGroundEntity()->m_iClassname == GetOuter()->m_iClassname )
{
// Although I tried to get NPC's out from under me, I landed on one. Kill it, so long as it's the same type of character as me.
variant_t val;
val.SetFloat( 0 );
g_EventQueue.AddEvent( GetOuter()->GetGroundEntity(), "sethealth", val, 0, GetOuter(), GetOuter() );
}
TaskComplete();
break;
default:
BaseClass::StartTask( pTask );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTask -
//-----------------------------------------------------------------------------
void CAI_RappelBehavior::RunTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_RAPPEL:
{
// If we don't do this, the beam won't show up sometimes. Ideally, all beams would update their
// bboxes correctly, but we're close to shipping and we can't change that now.
if ( m_hLine )
{
m_hLine->RelinkBeam();
}
if( GetEnemy() )
{
// Face the enemy if there's one.
Vector vecEnemyLKP = GetEnemyLKP();
GetOuter()->GetMotor()->SetIdealYawToTargetAndUpdate( vecEnemyLKP );
}
SetDescentSpeed();
if( GetOuter()->GetFlags() & FL_ONGROUND )
{
CBaseEntity *pGroundEnt = GetOuter()->GetGroundEntity();
if( pGroundEnt && pGroundEnt->IsPlayer() )
{
// try to shove the player in the opposite direction as they are facing (so they'll see me)
Vector vecForward;
pGroundEnt->GetVectors( &vecForward, NULL, NULL );
pGroundEnt->SetAbsVelocity( vecForward * -500 );
break;
}
GetOuter()->m_OnRappelTouchdown.FireOutput( GetOuter(), GetOuter(), 0 );
GetOuter()->RemoveFlag( FL_FLY );
CutZipline();
TaskComplete();
}
}
break;
default:
BaseClass::RunTask( pTask );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAI_RappelBehavior::CanSelectSchedule()
{
if ( !GetOuter()->IsInterruptable() )
return false;
if ( m_bWaitingToRappel )
return true;
if ( m_bOnGround )
return false;
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_RappelBehavior::GatherConditions()
{
BaseClass::GatherConditions();
if( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
{
// Shoot at the enemy so long as I'm six feet or more above them.
if( (GetAbsOrigin().z - GetEnemy()->GetAbsOrigin().z >= 36.0f) && GetOuter()->GetShotRegulator()->ShouldShoot() )
{
Activity activity = GetOuter()->TranslateActivity( ACT_GESTURE_RANGE_ATTACK1 );
Assert( activity != ACT_INVALID );
GetOuter()->AddGesture( activity );
// FIXME: this seems a bit wacked
GetOuter()->Weapon_SetActivity( GetOuter()->Weapon_TranslateActivity( ACT_RANGE_ATTACK1 ), 0 );
GetOuter()->OnRangeAttack1();
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int CAI_RappelBehavior::SelectSchedule()
{
if ( HasCondition( COND_BEGIN_RAPPEL ) )
{
m_bWaitingToRappel = false;
return SCHED_RAPPEL;
}
if ( m_bWaitingToRappel )
{
return SCHED_RAPPEL_WAIT;
}
else
{
return SCHED_RAPPEL;
}
return BaseClass::SelectSchedule();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_RappelBehavior::BeginRappel()
{
// Send the message to begin rappeling!
SetCondition( COND_BEGIN_RAPPEL );
m_vecRopeAnchor = GetOuter()->GetAbsOrigin();
trace_t tr;
UTIL_TraceEntity( GetOuter(), GetAbsOrigin(), GetAbsOrigin()-Vector(0,0,4096), MASK_SHOT, GetOuter(), COLLISION_GROUP_NONE, &tr );
if( tr.m_pEnt != NULL && tr.m_pEnt->IsNPC() )
{
Vector forward;
GetOuter()->GetVectors( &forward, NULL, NULL );
CSoundEnt::InsertSound( SOUND_DANGER, tr.m_pEnt->EarPosition() - forward * 12.0f, 32.0f, 0.2f, GetOuter() );
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_RappelBehavior::CutZipline()
{
if( m_hLine )
{
UTIL_Remove( m_hLine );
}
CBaseEntity *pAnchor = CreateEntityByName( "rope_anchor" );
pAnchor->SetOwnerEntity( GetOuter() ); // Boy, this is a hack!!
pAnchor->SetAbsOrigin( m_vecRopeAnchor );
pAnchor->Spawn();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_RappelBehavior::CreateZipline()
{
#if 1
if( !m_hLine )
{
int attachment = GetOuter()->LookupAttachment( "zipline" );
if( attachment > 0 )
{
CBeam *pBeam;
pBeam = CBeam::BeamCreate( "cable/cable.vmt", 1 );
pBeam->SetColor( 150, 150, 150 );
pBeam->SetWidth( 0.3 );
pBeam->SetEndWidth( 0.3 );
CAI_BaseNPC *pNPC = GetOuter();
pBeam->PointEntInit( pNPC->GetAbsOrigin() + Vector( 0, 0, 80 ), pNPC );
pBeam->SetEndAttachment( attachment );
m_hLine.Set( pBeam );
}
}
#endif
}
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_RappelBehavior )
DECLARE_TASK( TASK_RAPPEL )
DECLARE_TASK( TASK_HIT_GROUND )
DECLARE_CONDITION( COND_BEGIN_RAPPEL )
//===============================================
//===============================================
DEFINE_SCHEDULE
(
SCHED_RAPPEL_WAIT,
" Tasks"
" TASK_SET_ACTIVITY ACTIVITY:ACT_RAPPEL_LOOP"
" TASK_WAIT_INDEFINITE 0"
""
" Interrupts"
" COND_BEGIN_RAPPEL"
);
//===============================================
//===============================================
DEFINE_SCHEDULE
(
SCHED_RAPPEL,
" Tasks"
" TASK_SET_ACTIVITY ACTIVITY:ACT_RAPPEL_LOOP"
" TASK_RAPPEL 0"
" TASK_SET_SCHEDULE SCHEDULE:SCHED_CLEAR_RAPPEL_POINT"
""
" Interrupts"
""
" COND_NEW_ENEMY" // Only so the enemy selection code will pick an enemy!
);
//===============================================
//===============================================
DEFINE_SCHEDULE
(
SCHED_CLEAR_RAPPEL_POINT,
" Tasks"
" TASK_HIT_GROUND 0"
" TASK_MOVE_AWAY_PATH 128" // Clear this spot for other rappellers
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
""
" Interrupts"
""
);
AI_END_CUSTOM_SCHEDULE_PROVIDER()

View File

@@ -0,0 +1,93 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Deal with weapon being out
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#ifndef AI_BEHAVIOR_RAPPEL_H
#define AI_BEHAVIOR_RAPPEL_H
#ifdef _WIN32
#pragma once
#endif
#include "ai_behavior.h"
class CBeam;
class CAI_RappelBehavior : public CAI_SimpleBehavior
{
DECLARE_CLASS( CAI_RappelBehavior, CAI_SimpleBehavior );
public:
CAI_RappelBehavior();
void Precache( void );
virtual const char *GetName() { return "Rappel"; }
virtual bool KeyValue( const char *szKeyName, const char *szValue );
virtual bool CanSelectSchedule();
void GatherConditions();
void CleanupOnDeath( CBaseEntity *pCulprit = NULL, bool bFireDeathOutput = true );
//virtual void BeginScheduleSelection();
//virtual void EndScheduleSelection();
void StartTask( const Task_t *pTask );
void RunTask( const Task_t *pTask );
bool IsWaitingToRappel() { return m_bWaitingToRappel; }
void BeginRappel();
void SetDescentSpeed();
void CreateZipline();
void CutZipline();
//void BuildScheduleTestBits();
//int TranslateSchedule( int scheduleType );
//void OnStartSchedule( int scheduleType );
//void InitializeBehavior();
enum
{
SCHED_RAPPEL_WAIT = BaseClass::NEXT_SCHEDULE,
SCHED_RAPPEL,
SCHED_CLEAR_RAPPEL_POINT, // Get out of the way for the next guy
NEXT_SCHEDULE,
TASK_RAPPEL = BaseClass::NEXT_TASK,
TASK_HIT_GROUND,
NEXT_TASK,
COND_BEGIN_RAPPEL = BaseClass::NEXT_CONDITION,
NEXT_CONDITION,
};
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
public:
private:
virtual int SelectSchedule();
//---------------------------------
bool m_bWaitingToRappel;
bool m_bOnGround;
CHandle<CBeam> m_hLine;
Vector m_vecRopeAnchor;
DECLARE_DATADESC();
};
#endif // AI_BEHAVIOR_RAPPEL_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,232 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Combat behaviors for AIs in a relatively self-preservationist mode.
// Lots of cover taking and attempted shots out of cover.
//
//=============================================================================//
#ifndef AI_BEHAVIOR_STANDOFF_H
#define AI_BEHAVIOR_STANDOFF_H
#include "utlvector.h"
#include "utlmap.h"
#include "ai_behavior.h"
#include "ai_utils.h"
#include "ai_hint.h"
#if defined( _WIN32 )
#pragma once
#endif
//-----------------------------------------------------------------------------
enum AI_HintChangeReaction_t
{
AIHCR_DEFAULT_AI,
AIHCR_MOVE_ON_COVER,
AIHCR_MOVE_IMMEDIATE,
};
struct AI_StandoffParams_t
{
AI_HintChangeReaction_t hintChangeReaction;
bool fCoverOnReload;
bool fPlayerIsBattleline;
float minTimeShots;
float maxTimeShots;
int minShots;
int maxShots;
int oddsCover;
bool fStayAtCover;
float flAbandonTimeLimit;
DECLARE_SIMPLE_DATADESC();
};
//-------------------------------------
enum AI_Posture_t
{
AIP_INDIFFERENT,
AIP_STANDING,
AIP_CROUCHING,
AIP_PEEKING,
};
enum
{
STANDOFF_SENTENCE_BEGIN_STANDOFF = SENTENCE_BASE_BEHAVIOR_INDEX,
STANDOFF_SENTENCE_END_STANDOFF,
STANDOFF_SENTENCE_OUT_OF_AMMO,
STANDOFF_SENTENCE_FORCED_TAKE_COVER,
STANDOFF_SENTENCE_STAND_CHECK_TARGET,
};
class CAI_MappedActivityBehavior_Temporary : public CAI_SimpleBehavior
{
DECLARE_CLASS( CAI_MappedActivityBehavior_Temporary, CAI_SimpleBehavior );
public:
CAI_MappedActivityBehavior_Temporary( CAI_BaseNPC *pOuter = NULL )
: CAI_SimpleBehavior(pOuter)
{
SetDefLessFunc( m_ActivityMap );
}
protected:
Activity GetMappedActivity( AI_Posture_t posture, Activity activity );
void OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon );
virtual void UpdateTranslateActivityMap();
private:
CUtlMap<unsigned, Activity> m_ActivityMap;
};
class CAI_StandoffBehavior : public CAI_MappedActivityBehavior_Temporary
{
DECLARE_CLASS( CAI_StandoffBehavior, CAI_MappedActivityBehavior_Temporary );
public:
CAI_StandoffBehavior( CAI_BaseNPC *pOuter = NULL );
virtual const char *GetName() { return "Standoff"; }
void SetActive( bool fActive );
void SetParameters( const AI_StandoffParams_t &params, CAI_GoalEntity *pGoalEntity = NULL );
Vector GetStandoffGoalPosition();
void SetStandoffGoalPosition( const Vector &vecPos );
void ClearStandoffGoalPosition();
AI_Posture_t GetPosture();
bool IsActive( void ) { return m_fActive; }
void OnChangeTacticalConstraints();
bool CanSelectSchedule();
bool IsBehindBattleLines( const Vector &point );
protected:
void Spawn();
void BeginScheduleSelection();
void EndScheduleSelection();
void PrescheduleThink();
void GatherConditions();
int SelectSchedule();
int TranslateSchedule( int scheduleType );
void StartTask( const Task_t *pTask );
void BuildScheduleTestBits();
virtual void OnUpdateShotRegulator();
Activity NPC_TranslateActivity( Activity eNewActivity );
bool IsValidCover( const Vector &vecCoverLocation, CAI_Hint const *pHint );
bool IsValidShootPosition( const Vector &vecCoverLocation, CAI_Node *pNode, CAI_Hint const *pHint );
void SetPosture( AI_Posture_t posture );
void OnChangeHintGroup( string_t oldGroup, string_t newGroup );
virtual int SelectScheduleUpdateWeapon();
virtual int SelectScheduleCheckCover();
virtual int SelectScheduleEstablishAim();
virtual int SelectScheduleAttack();
bool PlayerIsLeading();
CBaseEntity *GetPlayerLeader();
bool GetDirectionOfStandoff( Vector *pDir );
void UpdateBattleLines();
Hint_e GetHintType();
void SetReuseCurrentCover();
void UnlockHintNode();
Activity GetCoverActivity();
void OnRestore();
void UpdateTranslateActivityMap();
// Standoff overrides base AI crouch handling
bool IsCrouching( void ) { return false; }
private:
//----------------------------
enum
{
NEXT_SCHEDULE = BaseClass::NEXT_SCHEDULE,
NEXT_TASK = BaseClass::NEXT_TASK,
COND_ABANDON_TIME_EXPIRED = BaseClass::NEXT_CONDITION,
NEXT_CONDITION
};
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
//---------------------------------
// @TODO (toml 07-30-03): replace all these booleans with a singe 32 bit unsigned & bit flags
bool m_fActive;
bool m_fTestNoDamage;
Vector m_vecStandoffGoalPosition;
AI_Posture_t m_posture;
AI_StandoffParams_t m_params;
EHANDLE m_hStandoffGoal;
bool m_fTakeCover;
float m_SavedDistTooFar;
bool m_fForceNewEnemy;
CAI_MoveMonitor m_PlayerMoveMonitor;
CSimTimer m_TimeForceCoverHint;
CSimTimer m_TimePreventForceNewEnemy;
CRandSimTimer m_RandomCoverChangeTimer;
// FIXME: TEMPORARY! REMOVE
int m_nSavedMinShots, m_nSavedMaxShots;
float m_flSavedMinRest, m_flSavedMaxRest;
//---------------------------------
struct BattleLine_t
{
Vector point;
Vector normal;
};
CThinkOnceSemaphore m_UpdateBattleLinesSemaphore;
CUtlVector<BattleLine_t> m_BattleLines;
bool m_fIgnoreFronts;
//---------------------------------
bool m_bHasLowCoverActivity;
//---------------------------------
DECLARE_DATADESC();
};
//-------------------------------------
inline void CAI_StandoffBehavior::SetPosture( AI_Posture_t posture )
{
m_posture = posture;
}
//-------------------------------------
inline AI_Posture_t CAI_StandoffBehavior::GetPosture()
{
return m_posture;
}
//-----------------------------------------------------------------------------
#endif // AI_BEHAVIOR_STANDOFF_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,260 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef AI_BLENDED_MOVEMENT_H
#define AI_BLENDED_MOVEMENT_H
#include "ai_basenpc.h"
#include "ai_motor.h"
#include "ai_navigator.h"
struct AI_Waypoint_t;
//-----------------------------------------------------------------------------
// CLASS: CAI_BlendedMotor
//
// Purpose: Home of fancy human animation transition code
//
//-----------------------------------------------------------------------------
class CAI_BlendedMotor : public CAI_Motor
{
typedef CAI_Motor BaseClass;
public:
CAI_BlendedMotor( CAI_BaseNPC *pOuter )
: BaseClass( pOuter )
{
m_iPrimaryLayer = -1;
m_nPrimarySequence = ACT_INVALID;
m_iSecondaryLayer = -1;
m_nSecondarySequence = ACT_INVALID;
m_flSecondaryWeight = 0.0f;
m_nSavedGoalActivity = ACT_INVALID;
m_nSavedTranslatedGoalActivity = ACT_INVALID;
m_nGoalSequence = ACT_INVALID;
m_nPrevMovementSequence = ACT_INVALID;
m_nInteriorSequence = ACT_INVALID;
m_bDeceleratingToGoal = false;
m_flStartCycle = 0.0f;
m_flPredictiveSpeedAdjust = 1.0f;
m_flReactiveSpeedAdjust = 1.0f;
m_vecPrevOrigin1.Init();
m_vecPrevOrigin2.Init();
m_prevYaw = 0.0f;
m_doTurn = 0.0f;
m_doLeft = 0.0f;
m_doRight = 0.0f;
m_flNextTurnAct = 0.0f;
}
void MoveClimbStart( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw );
void MoveJumpStart( const Vector &velocity );
void ResetMoveCalculations();
void MoveStart();
void ResetGoalSequence();
void MoveStop();
void MovePaused();
void MoveContinue();
float OverrideMaxYawSpeed( Activity activity );
void UpdateYaw( int speed );
void RecalculateYawSpeed();
bool IsDeceleratingToGoal() const { return m_bDeceleratingToGoal; }
float GetMoveScriptTotalTime();
void MaintainTurnActivity( void );
bool AddTurnGesture( float flYD );
private:
AIMotorMoveResult_t MoveGroundExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult );
AIMotorMoveResult_t MoveFlyExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult );
// --------------------------------
void BuildMoveScript( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult );
void BuildVelocityScript( const AILocalMoveGoal_t &move );
void InsertSlowdown( float distToObstruction, float idealAccel, bool bAlwaysSlowdown );
int BuildTurnScript( int i, int j );
void BuildTurnScript( const AILocalMoveGoal_t &move );
int BuildInsertNode( int i, float flTime );
Activity GetTransitionActivity( void );
// --------------------------------
// helpers to simplify code
float GetCycle() { return GetOuter()->GetCycle(); }
int AddLayeredSequence( int sequence, int iPriority ) { return GetOuter()->AddLayeredSequence( sequence, iPriority ); }
void SetLayerWeight( int iLayer, float flWeight ) { GetOuter()->SetLayerWeight( iLayer, flWeight ); }
void SetLayerPlaybackRate( int iLayer, float flPlaybackRate ) { GetOuter()->SetLayerPlaybackRate( iLayer, flPlaybackRate ); }
void SetLayerNoRestore( int iLayer, bool bNoRestore ) { GetOuter()->SetLayerNoRestore( iLayer, bNoRestore ); }
void SetLayerCycle( int iLayer, float flCycle ) { GetOuter()->SetLayerCycle( iLayer, flCycle ); }
void SetLayerCycle( int iLayer, float flCycle, float flPrevCycle ) { GetOuter()->SetLayerCycle( iLayer, flCycle, flPrevCycle ); }
void RemoveLayer( int iLayer, float flKillRate, float flKillDelay ) { GetOuter()->RemoveLayer( iLayer, flKillRate, flKillDelay ); }
// --------------------------------
struct AI_Movementscript_t
{
public:
AI_Movementscript_t( )
{
Init( );
};
void Init( void )
{
memset( this, 0, sizeof(*this) );
};
float flTime; // time till next entry
float flElapsedTime; // time since first entry
float flDist; // distance to next entry
float flMaxVelocity;
// float flVelocity;
float flYaw;
float flAngularVelocity;
bool bLooping;
int nFlags;
AI_Waypoint_t *pWaypoint;
public:
AI_Movementscript_t *pNext;
AI_Movementscript_t *pPrev;
Vector vecLocation;
};
//---------------------------------
CUtlVector<AI_Movementscript_t> m_scriptMove;
CUtlVector<AI_Movementscript_t> m_scriptTurn;
//---------------------------------
bool m_bDeceleratingToGoal;
int m_iPrimaryLayer;
int m_iSecondaryLayer;
int m_nPrimarySequence;
int m_nSecondarySequence;
float m_flSecondaryWeight;
Activity m_nSavedGoalActivity;
Activity m_nSavedTranslatedGoalActivity;
int m_nGoalSequence;
int m_nPrevMovementSequence;
int m_nInteriorSequence;
float m_flStartCycle;
float m_flCurrRate;
float m_flPredictiveSpeedAdjust; // predictive speed adjust from probing slope
float m_flReactiveSpeedAdjust; // reactive speed adjust when slope movement detected
Vector m_vecPrevOrigin1;
Vector m_vecPrevOrigin2;
//---------------------------------
float m_flNextTurnGesture; // next time for large turn gesture
//---------------------------------
float m_prevYaw;
float m_doTurn;
float m_doLeft;
float m_doRight;
float m_flNextTurnAct; // next time for small turn gesture
float GetMoveScriptDist( float &flNewSpeed );
float GetMoveScriptYaw( void );
void SetMoveScriptAnim( float flNewSpeed );
int GetInteriorSequence( int fromSequence );
DECLARE_SIMPLE_DATADESC();
};
//-----------------------------------------------------------------------------
// CLASS: CAI_BlendingHost
//
// Purpose: Bridge to the home of fancy human animation transition code
//
//-----------------------------------------------------------------------------
template <class BASE_NPC>
class CAI_BlendingHost : public BASE_NPC
{
DECLARE_CLASS_NOFRIEND( CAI_BlendingHost, BASE_NPC );
public:
const CAI_BlendedMotor *GetBlendedMotor() const { return assert_cast<const CAI_BlendedMotor *>(this->GetMotor()); }
CAI_BlendedMotor * GetBlendedMotor() { return assert_cast<CAI_BlendedMotor *>(this->GetMotor()); }
CAI_Motor *CreateMotor()
{
MEM_ALLOC_CREDIT();
return new CAI_BlendedMotor( this );
}
CAI_Navigator *CreateNavigator()
{
CAI_Navigator *pNavigator = BaseClass::CreateNavigator();
pNavigator->SetValidateActivitySpeed( false );
return pNavigator;
}
float MaxYawSpeed( void )
{
float override = GetBlendedMotor()->OverrideMaxYawSpeed( this->GetActivity() );
if ( override != -1 )
return override;
return BaseClass::MaxYawSpeed();
}
float GetTimeToNavGoal()
{
float result = GetBlendedMotor()->GetMoveScriptTotalTime();
if ( result != -1 )
return result;
return BaseClass::GetTimeToNavGoal();
}
};
//-------------------------------------
// to simplify basic usage:
class CAI_BlendedNPC : public CAI_BlendingHost<CAI_BaseNPC>
{
DECLARE_CLASS( CAI_BlendedNPC, CAI_BlendingHost<CAI_BaseNPC> );
};
//-----------------------------------------------------------------------------
#endif

View File

@@ -0,0 +1,171 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef AI_COMPONENT_H
#define AI_COMPONENT_H
#if defined( _WIN32 )
#pragma once
#endif
class CAI_BaseNPC;
class CAI_Enemies;
typedef int AI_TaskFailureCode_t;
struct Task_t;
//-----------------------------------------------------------------------------
// CAI_Component
//
// Purpose: Shared functionality of all classes that assume some of the
// responsibilities of an owner AI.
//-----------------------------------------------------------------------------
class CAI_Component
{
DECLARE_CLASS_NOBASE( CAI_Component );
protected:
CAI_Component( CAI_BaseNPC *pOuter = NULL )
: m_pOuter(pOuter)
{
}
virtual ~CAI_Component() {}
public:
virtual void SetOuter( CAI_BaseNPC *pOuter ) { m_pOuter = pOuter; }
CAI_BaseNPC * GetOuter() { return m_pOuter; }
const CAI_BaseNPC * GetOuter() const { return m_pOuter; }
Hull_t GetHullType() const;
float GetHullWidth() const;
float GetHullHeight() const;
const Vector & GetHullMins() const;
const Vector & GetHullMaxs() const;
protected:
//
// Common services provided by CAI_BaseNPC, Convenience methods to simplify derived code
//
edict_t * GetEdict();
const Vector & GetLocalOrigin() const;
void SetLocalOrigin( const Vector &origin );
const Vector & GetAbsOrigin() const;
const QAngle& GetAbsAngles() const;
void SetLocalAngles( const QAngle& angles );
const QAngle & GetLocalAngles( void ) const;
const Vector& WorldAlignMins() const;
const Vector& WorldAlignMaxs() const;
Vector WorldSpaceCenter() const;
int GetCollisionGroup() const;
void SetSolid( SolidType_t val );
SolidType_t GetSolid() const;
float GetGravity() const;
void SetGravity( float );
CBaseEntity* GetEnemy();
const Vector & GetEnemyLKP() const;
void TranslateNavGoal( CBaseEntity *pEnemy, Vector &chasePosition);
CBaseEntity* GetTarget();
void SetTarget( CBaseEntity *pTarget );
const Task_t* GetCurTask( void );
virtual void TaskFail( AI_TaskFailureCode_t );
void TaskFail( const char *pszGeneralFailText );
virtual void TaskComplete( bool fIgnoreSetFailedCondition = false );
int TaskIsRunning();
inline int TaskIsComplete();
Activity GetActivity();
void SetActivity( Activity NewActivity );
float GetIdealSpeed() const;
float GetIdealAccel() const;
int GetSequence();
int GetEntFlags() const;
void AddEntFlag( int flags );
void RemoveEntFlag( int flagsToRemove );
void ToggleEntFlag( int flagToToggle );
void SetGroundEntity( CBaseEntity *ground );
CBaseEntity* GetGoalEnt();
void SetGoalEnt( CBaseEntity *pGoalEnt );
void Remember( int iMemory );
void Forget( int iMemory );
bool HasMemory( int iMemory );
CAI_Enemies * GetEnemies();
const char * GetEntClassname();
int CapabilitiesGet();
float GetLastThink( const char *szContext = NULL );
public:
#if defined(new)
#error
#endif
void *operator new( size_t nBytes )
{
MEM_ALLOC_CREDIT();
void *pResult = MemAlloc_Alloc( nBytes );
memset( pResult, 0, nBytes );
return pResult;
};
void *operator new( size_t nBytes, int nBlockUse, const char *pFileName, int nLine )
{
MEM_ALLOC_CREDIT();
void *pResult = MemAlloc_Alloc( nBytes, pFileName, nLine );
memset( pResult, 0, nBytes );
return pResult;
}
private:
CAI_BaseNPC *m_pOuter;
};
//-----------------------------------------------------------------------------
template <class NPC_CLASS, class BASE_COMPONENT = CAI_Component>
class CAI_ComponentWithOuter : public BASE_COMPONENT
{
protected:
CAI_ComponentWithOuter(NPC_CLASS *pOuter = NULL)
: BASE_COMPONENT(pOuter)
{
}
public:
// Hides base version
void SetOuter( NPC_CLASS *pOuter ) { BASE_COMPONENT::SetOuter((CAI_BaseNPC *)pOuter); }
NPC_CLASS * GetOuter() { return (NPC_CLASS *)(BASE_COMPONENT::GetOuter()); }
const NPC_CLASS * GetOuter() const { return (NPC_CLASS *)(BASE_COMPONENT::GetOuter()); }
};
//-----------------------------------------------------------------------------
#define DEFINE_AI_COMPONENT_OUTER( NPC_CLASS ) \
void SetOuter( NPC_CLASS *pOuter ) { CAI_Component::SetOuter((CAI_BaseNPC *)pOuter); } \
NPC_CLASS * GetOuter() { return (NPC_CLASS *)(CAI_Component::GetOuter()); } \
const NPC_CLASS * GetOuter() const { return (NPC_CLASS *)(CAI_Component::GetOuter()); }
//-----------------------------------------------------------------------------
#endif // AI_COMPONENT_H

View File

@@ -0,0 +1,890 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Console commands for debugging and manipulating NPCs.
//
//===========================================================================//
#include "cbase.h"
#include "ai_basenpc.h"
#include "player.h"
#include "entitylist.h"
#include "ai_network.h"
#include "ai_node.h"
#include "ai_link.h"
#include "ai_networkmanager.h"
#include "ndebugoverlay.h"
#include "datacache/imdlcache.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern CAI_Node* FindPickerAINode( CBasePlayer* pPlayer, NodeType_e nNodeType );
extern void SetDebugBits( CBasePlayer* pPlayer, const char *name, int bit );
extern CBaseEntity *FindPickerEntity( CBasePlayer *pPlayer );
extern bool g_bAIDisabledByUser;
//------------------------------------------------------------------------------
// Purpose: Disables all NPCs
//------------------------------------------------------------------------------
void CC_AI_Disable( void )
{
if (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI)
{
CAI_BaseNPC::m_nDebugBits &= ~bits_debugDisableAI;
DevMsg("AI Enabled.\n");
}
else
{
CAI_BaseNPC::m_nDebugBits |= bits_debugDisableAI;
DevMsg("AI Disabled.\n");
g_bAIDisabledByUser = true;
}
CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
}
static ConCommand ai_disable("ai_disable", CC_AI_Disable, "Bi-passes all AI logic routines and puts all NPCs into their idle animations. Can be used to get NPCs out of your way and to test effect of AI logic routines on frame rate", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show hint nodes
//------------------------------------------------------------------------------
void CC_AI_ShowHints( void )
{
// Eventually this will be done by name when mulitple
// networks are used, but for now have one big AINet
//static char entName[256];
//Q_strncpy( entName, args[1],sizeof(entName) );
if ( !g_pAINetworkManager )
return;
g_pAINetworkManager->GetEditOps()->SetDebugBits("BigNet",bits_debugOverlayHints);
CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
}
static ConCommand ai_show_hints("ai_show_hints", CC_AI_ShowHints, "Displays all hints as small boxes\n\tBlue - hint is available for use\n\tRed - hint is currently being used by an NPC\n\tOrange - hint not being used by timed out\n\tGrey - hint has been disabled", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show node connections with hulls
//------------------------------------------------------------------------------
void CC_AI_ShowHull( const CCommand &args )
{
// Eventually this will be done by name when mulitple
// networks are used, but for now have one big AINet
//static char entName[256];
//Q_strncpy( entName, args[1),sizeof(entName) );
g_pAINetworkManager->GetEditOps()->SetDebugBits("BigNet",bits_debugOverlayHulls);
CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
}
static ConCommand ai_show_hull("ai_show_hull", CC_AI_ShowHull, "Displays the allowed hulls between each node for the currently selected hull type. Hulls are color code as follows:\n\tGreen - ground movement \n\tBlue - jumping movement\n\tCyan - flying movement\n\tMagenta - climbing movement\n\tArguments: -none-", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show node connections with lines
//------------------------------------------------------------------------------
void CC_AI_ShowConnect( const CCommand &args )
{
// Eventually this will be done by name when mulitple
// networks are used, but for now have one big AINet
//static char entName[256];
//Q_strncpy( entName, args[1],sizeof(entName) );
if ( !g_pAINetworkManager )
return;
g_pAINetworkManager->GetEditOps()->SetDebugBits("BigNet",bits_debugOverlayConnections);
CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
}
static ConCommand ai_show_connect("ai_show_connect", CC_AI_ShowConnect, "Displays the allowed connections between each node for the currently selected hull type. Hulls are color code as follows:\n\tGreen - ground movement \n\tBlue - jumping movement\n\tCyan - flying movement\n\tMagenta - climbing movement\n\tRed - connection disabled", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show node connections with lines
//------------------------------------------------------------------------------
void CC_AI_ShowJumpConnect( const CCommand &args )
{
// Eventually this will be done by name when mulitple
// networks are used, but for now have one big AINet
//static char entName[256];
//Q_strncpy( entName, args[1],sizeof(entName) );
if ( !g_pAINetworkManager )
return;
g_pAINetworkManager->GetEditOps()->SetDebugBits("BigNet",bits_debugOverlayConnections);
g_pAINetworkManager->GetEditOps()->SetDebugBits("BigNet",bits_debugOverlayJumpConnections);
CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
}
static ConCommand ai_show_connect_jump("ai_show_connect_jump", CC_AI_ShowJumpConnect, "Displays the allowed connections between each node for the currently selected hull type. Hulls are color code as follows:\n\tGreen - ground movement \n\tBlue - jumping movement\n\tCyan - flying movement\n\tMagenta - climbing movement\n\tRed - connection disabled", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show node connections with lines
//------------------------------------------------------------------------------
void CC_AI_ShowFlyConnect( const CCommand &args )
{
// Eventually this will be done by name when mulitple
// networks are used, but for now have one big AINet
//static char entName[256];
//Q_strncpy( entName, args[1],sizeof(entName) );
if ( !g_pAINetworkManager )
return;
g_pAINetworkManager->GetEditOps()->SetDebugBits("BigNet",bits_debugOverlayConnections);
g_pAINetworkManager->GetEditOps()->SetDebugBits("BigNet",bits_debugOverlayFlyConnections);
CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
}
static ConCommand ai_show_connect_fly("ai_show_connect_fly", CC_AI_ShowFlyConnect, "Displays the allowed connections between each node for the currently selected hull type. Hulls are color code as follows:\n\tGreen - ground movement \n\tBlue - jumping movement\n\tCyan - flying movement\n\tMagenta - climbing movement\n\tRed - connection disabled", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Draw a grid on the screen (good for laying down nodes)
//------------------------------------------------------------------------------
void CC_AI_ShowGrid( const CCommand &args )
{
// Eventually this will be done by name when mulitple
// networks are used, but for now have one big AINet
//static char entName[256];
//Q_strncpy( entName, args[1],sizeof(entName) );
if ( !g_pAINetworkManager )
return;
g_pAINetworkManager->GetEditOps()->SetDebugBits("BigNet",bits_debugOverlayGrid);
CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
}
static ConCommand ai_show_grid("ai_show_grid", CC_AI_ShowGrid, "Draw a grid on the floor where looking.", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: NPC step trough AI
//------------------------------------------------------------------------------
void CC_AI_Step( void )
{
DevMsg("AI Stepping...\n");
// Start NPC's stepping through tasks
CAI_BaseNPC::m_nDebugBits |= bits_debugStepAI;
CAI_BaseNPC::m_nDebugPauseIndex++;
}
static ConCommand ai_step("ai_step", CC_AI_Step, "NPCs will freeze after completing their current task. To complete the next task, use 'ai_step' again. To resume processing normally use 'ai_resume'", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Resume normal AI processing after stepping
//------------------------------------------------------------------------------
void CC_AI_Resume( void )
{
DevMsg("AI Resume...\n");
// End NPC's stepping through tasks
CAI_BaseNPC::m_nDebugBits &= ~bits_debugStepAI;
}
static ConCommand ai_resume("ai_resume", CC_AI_Resume, "If NPC is stepping through tasks (see ai_step ) will resume normal processing.", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Switch to display of next hull type
//------------------------------------------------------------------------------
void CC_AI_NextHull( const CCommand &args )
{
// Eventually this will be done by name when mulitple
// networks are used, but for now have one big AINet
//static char entName[256];
//Q_strncpy( entName, args[1],sizeof(entName) );
if ( !g_pAINetworkManager )
return;
g_pAINetworkManager->GetEditOps()->DrawNextHull("BigNet");
CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
}
static ConCommand ai_next_hull("ai_next_hull", CC_AI_NextHull, "Cycles through the various hull sizes. Currently selected hull size is written to the screen. Controls which connections are shown when ai_show_hull or ai_show_connect commands are used\n\tArguments: -none-", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Display the hull type of the specified NPC.
//------------------------------------------------------------------------------
void CC_AI_Hull( const CCommand &args )
{
if ( !g_pAINetworkManager )
return;
bool bSpawned = false;
CBaseEntity *pEnt = NULL;
if ( !args[1] || !args[1][0] )
{
// No arg means the entity under the crosshair.
pEnt = FindPickerEntity( UTIL_GetCommandClient() );
if ( !pEnt )
{
DevMsg( "No entity under the crosshair.\n" );
return;
}
}
else
{
// Find the entity specified on the command line.
pEnt = gEntList.FindEntityGeneric( NULL, args[1] );
if ( !pEnt )
{
// Not found, try to create one.
pEnt = (CAI_BaseNPC *)CreateEntityByName( args[1] );
if ( !pEnt )
{
DevMsg( "Entity %s not found, and couldn't create!\n", args[1] );
return;
}
bSpawned = true;
DispatchSpawn( pEnt );
}
}
CAI_BaseNPC *pNPC = dynamic_cast<CAI_BaseNPC *>( pEnt );
if ( !pNPC )
{
DevMsg( "Entity %s is not an NPC.\n", pEnt->GetDebugName() );
return;
}
Hull_t eHull = pNPC->GetHullType();
if ( bSpawned )
{
UTIL_Remove( pEnt );
}
g_pAINetworkManager->GetEditOps()->DrawHull( eHull );
CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
}
static ConCommand ai_hull("ai_hull", CC_AI_Hull, "Controls which connections are shown when ai_show_hull or ai_show_connect commands are used\n\tArguments: NPC name or classname, <none>=NPC under crosshair", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show AI nodes
//------------------------------------------------------------------------------
void CC_AI_Nodes( const CCommand &args )
{
// Eventually this will be done by name when mulitple
// networks are used, but for now have one big AINet
// static char entName[256];
//Q_strncpy( entName, args[1],sizeof(entName) );
if ( !g_pAINetworkManager )
return;
g_pAINetworkManager->GetEditOps()->SetDebugBits("BigNet",bits_debugOverlayNodes);
CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
}
static ConCommand ai_nodes("ai_nodes", CC_AI_Nodes, "Toggles node display. First call displays the nodes for the given network as green objects. Second call displays the nodes and their IDs. Nodes are color coded as follows:\n\tGreen - ground node\n\tCyan - air node\n\tMagenta - climb node\n\tGrey - node not available for selected hull size\n\tOrange - node currently locked", FCVAR_CHEAT);
CON_COMMAND(ai_show_node, "Highlight the specified node")
{
if ( args.ArgC() > 1 )
{
int node = atoi(args[1]);
CAI_Node* pAINode = g_pBigAINet->GetNode( node, false );
if ( pAINode )
{
NDebugOverlay::Cross3D(pAINode->GetOrigin(), 1024, 255, 255, 255, true, 5.0 );
}
}
}
//------------------------------------------------------------------------------
// Purpose: Show visibility from selected node to all other nodes
//------------------------------------------------------------------------------
void CC_AI_ShowVisibility( const CCommand &args )
{
// Eventually this will be done by name when mulitple
// networks are used, but for now have one big AINet
// static char entName[256];
//Q_strncpy( entName, args[1],sizeof(entName) );
if ( !g_pAINetworkManager )
return;
g_pAINetworkManager->GetEditOps()->SetDebugBits("BigNet",bits_debugOverlayVisibility);
CAI_Node* pAINode = FindPickerAINode(UTIL_GetCommandClient(), NODE_ANY);
if (pAINode != NULL)
{
g_pAINetworkManager->GetEditOps()->m_iVisibilityNode = pAINode->GetId();
}
else
{
g_pAINetworkManager->GetEditOps()->m_iVisibilityNode = NO_NODE;
}
CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
}
static ConCommand ai_show_visibility("ai_show_visibility", CC_AI_ShowVisibility, "Toggles visibility display for the node that the player is looking at. Nodes that are visible from the selected node will be drawn in red with yellow lines connecting to the selected node. Nodes that are not visible from the selected node will be drawn in blue.", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show what nodes the selected node is connected to using the
// netowrk graph
//------------------------------------------------------------------------------
void CC_AI_GraphConnect( const CCommand &args )
{
if ( !g_pAINetworkManager )
return;
// Eventually this will be done by name when mulitple
// networks are used, but for now have one big AINet
//static char entName[256];
//Q_strncpy( entName, args[1],sizeof(entName) );
g_pAINetworkManager->GetEditOps()->SetDebugBits("BigNet",bits_debugOverlayGraphConnect);
CAI_Node* pAINode = FindPickerAINode(UTIL_GetCommandClient(), NODE_ANY);
if (pAINode != NULL)
{
g_pAINetworkManager->GetEditOps()->m_iGConnectivityNode = pAINode->GetId();
}
else
{
g_pAINetworkManager->GetEditOps()->m_iGConnectivityNode = NO_NODE;
}
CBaseEntity::m_nDebugPlayer = UTIL_GetCommandClientIndex();
}
static ConCommand ai_show_graph_connect("ai_show_graph_connect", CC_AI_GraphConnect, "Toggles graph connection display for the node that the player is looking at. Nodes that are connected to the selected node by the net graph will be drawn in red with magenta lines connecting to the selected node. Nodes that are not connected via the net graph from the selected node will be drawn in blue.", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show route triangulation attempts
//------------------------------------------------------------------------------
void CC_NPC_Bipass( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(),args[1],OVERLAY_NPC_TRIANGULATE_BIT);
}
static ConCommand npc_bipass("npc_bipass", CC_NPC_Bipass, "Displays the local movement attempts by the given NPC(s) (triangulation detours). Failed bypass routes are displayed in red, successful bypasses are shown in green.\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at.", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Destroy selected NPC
//------------------------------------------------------------------------------
void CC_NPC_Destroy( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(),args[1],OVERLAY_NPC_ZAP_BIT);
}
static ConCommand npc_destroy("npc_destroy", CC_NPC_Destroy, "Removes the given NPC(s) from the universe\nArguments: {npc_name} / {npc_class_name} / no argument picks what player is looking at", FCVAR_CHEAT);
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CC_NPC_Kill( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(),args[1],OVERLAY_NPC_KILL_BIT);
}
static ConCommand npc_kill("npc_kill", CC_NPC_Kill, "Kills the given NPC(s)\nArguments: {npc_name} / {npc_class_name} / no argument picks what player is looking at", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show selected NPC's enemies
//------------------------------------------------------------------------------
void CC_NPC_Enemies( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(),args[1],OVERLAY_NPC_ENEMIES_BIT);
}
static ConCommand npc_enemies("npc_enemies", CC_NPC_Enemies, "Shows memory of NPC. Draws an X on top of each memory.\n\tEluded entities drawn in blue (don't know where it went)\n\tUnreachable entities drawn in green (can't get to it)\n\tCurrent enemy drawn in red\n\tCurrent target entity drawn in magenta\n\tAll other entities drawn in pink\n\tArguments: {npc_name} / {npc class_name} / no argument picks what player is looking at", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show seletected NPC's current enemy and target entity
//------------------------------------------------------------------------------
void CC_NPC_Focus( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(),args[1],OVERLAY_NPC_FOCUS_BIT);
}
static ConCommand npc_focus("npc_focus", CC_NPC_Focus, "Displays red line to NPC's enemy (if has one) and blue line to NPC's target entity (if has one)\n\tArguments: {npc_name} / {npc class_name} / no argument picks what player is looking at", FCVAR_CHEAT);
ConVar npc_create_equipment("npc_create_equipment", "");
//------------------------------------------------------------------------------
// Purpose: Create an NPC of the given type
//------------------------------------------------------------------------------
void CC_NPC_Create( const CCommand &args )
{
MDLCACHE_CRITICAL_SECTION();
bool allowPrecache = CBaseEntity::IsPrecacheAllowed();
CBaseEntity::SetAllowPrecache( true );
// Try to create entity
CAI_BaseNPC *baseNPC = dynamic_cast< CAI_BaseNPC * >( CreateEntityByName(args[1]) );
if (baseNPC)
{
baseNPC->KeyValue( "additionalequipment", npc_create_equipment.GetString() );
baseNPC->Precache();
if ( args.ArgC() == 3 )
{
baseNPC->SetName( AllocPooledString( args[2] ) );
}
DispatchSpawn(baseNPC);
// Now attempt to drop into the world
CBasePlayer* pPlayer = UTIL_GetCommandClient();
trace_t tr;
Vector forward;
pPlayer->EyeVectors( &forward );
AI_TraceLine(pPlayer->EyePosition(),
pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_NPCSOLID,
pPlayer, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction != 1.0)
{
if (baseNPC->CapabilitiesGet() & bits_CAP_MOVE_FLY)
{
Vector pos = tr.endpos - forward * 36;
baseNPC->Teleport( &pos, NULL, NULL );
}
else
{
// Raise the end position a little up off the floor, place the npc and drop him down
tr.endpos.z += 12;
baseNPC->Teleport( &tr.endpos, NULL, NULL );
UTIL_DropToFloor( baseNPC, MASK_NPCSOLID );
}
// Now check that this is a valid location for the new npc to be
Vector vUpBit = baseNPC->GetAbsOrigin();
vUpBit.z += 1;
AI_TraceHull( baseNPC->GetAbsOrigin(), vUpBit, baseNPC->GetHullMins(), baseNPC->GetHullMaxs(),
MASK_NPCSOLID, baseNPC, COLLISION_GROUP_NONE, &tr );
if ( tr.startsolid || (tr.fraction < 1.0) )
{
baseNPC->SUB_Remove();
DevMsg("Can't create %s. Bad Position!\n",args[1]);
NDebugOverlay::Box(baseNPC->GetAbsOrigin(), baseNPC->GetHullMins(), baseNPC->GetHullMaxs(), 255, 0, 0, 0, 0);
}
}
baseNPC->Activate();
}
CBaseEntity::SetAllowPrecache( allowPrecache );
}
static ConCommand npc_create("npc_create", CC_NPC_Create, "Creates an NPC of the given type where the player is looking (if the given NPC can actually stand at that location). Note that this only works for npc classes that are already in the world. You can not create an entity that doesn't have an instance in the level.\n\tArguments: {npc_class_name}", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Create an NPC of the given type
//------------------------------------------------------------------------------
void CC_NPC_Create_Aimed( const CCommand &args )
{
MDLCACHE_CRITICAL_SECTION();
bool allowPrecache = CBaseEntity::IsPrecacheAllowed();
CBaseEntity::SetAllowPrecache( true );
// Try to create entity
CAI_BaseNPC *baseNPC = dynamic_cast< CAI_BaseNPC * >( CreateEntityByName(args[1]) );
if (baseNPC)
{
baseNPC->KeyValue( "additionalequipment", npc_create_equipment.GetString() );
baseNPC->Precache();
DispatchSpawn( baseNPC );
// Now attempt to drop into the world
QAngle angles;
CBasePlayer* pPlayer = UTIL_GetCommandClient();
trace_t tr;
Vector forward;
pPlayer->EyeVectors( &forward );
VectorAngles( forward, angles );
angles.x = 0;
angles.z = 0;
AI_TraceLine( pPlayer->EyePosition(),
pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_NPCSOLID,
pPlayer, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction != 1.0)
{
if (baseNPC->CapabilitiesGet() & bits_CAP_MOVE_FLY)
{
Vector pos = tr.endpos - forward * 36;
baseNPC->Teleport( &pos, &angles, NULL );
}
else
{
// Raise the end position a little up off the floor, place the npc and drop him down
tr.endpos.z += 12;
baseNPC->Teleport( &tr.endpos, &angles, NULL );
UTIL_DropToFloor( baseNPC, MASK_NPCSOLID );
}
// Now check that this is a valid location for the new npc to be
Vector vUpBit = baseNPC->GetAbsOrigin();
vUpBit.z += 1;
AI_TraceHull( baseNPC->GetAbsOrigin(), vUpBit, baseNPC->GetHullMins(), baseNPC->GetHullMaxs(),
MASK_NPCSOLID, baseNPC, COLLISION_GROUP_NONE, &tr );
if ( tr.startsolid || (tr.fraction < 1.0) )
{
baseNPC->SUB_Remove();
DevMsg("Can't create %s. Bad Position!\n",args[1]);
NDebugOverlay::Box(baseNPC->GetAbsOrigin(), baseNPC->GetHullMins(), baseNPC->GetHullMaxs(), 255, 0, 0, 0, 0);
}
}
else
{
baseNPC->Teleport( NULL, &angles, NULL );
}
baseNPC->Activate();
}
CBaseEntity::SetAllowPrecache( allowPrecache );
}
static ConCommand npc_create_aimed("npc_create_aimed", CC_NPC_Create_Aimed, "Creates an NPC aimed away from the player of the given type where the player is looking (if the given NPC can actually stand at that location). Note that this only works for npc classes that are already in the world. You can not create an entity that doesn't have an instance in the level.\n\tArguments: {npc_class_name}", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Destroy unselected NPCs
//------------------------------------------------------------------------------
void CC_NPC_DestroyUnselected( void )
{
CAI_BaseNPC *pNPC = gEntList.NextEntByClass( (CAI_BaseNPC *)NULL );
while (pNPC)
{
if (!(pNPC->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) && !pNPC->ClassMatches("npc_bullseye"))
{
pNPC->m_debugOverlays |= OVERLAY_NPC_ZAP_BIT;
}
pNPC = gEntList.NextEntByClass(pNPC);
}
}
static ConCommand npc_destroy_unselected("npc_destroy_unselected", CC_NPC_DestroyUnselected, "Removes all NPCs from the universe that aren't currently selected", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Freeze or unfreeze the selected NPCs. If no NPCs are selected, the
// NPC under the crosshair is frozen/unfrozen.
//------------------------------------------------------------------------------
void CC_NPC_Freeze( const CCommand &args )
{
if (FStrEq(args[1], ""))
{
//
// No NPC was specified, try to freeze selected NPCs.
//
bool bFound = false;
CAI_BaseNPC *npc = gEntList.NextEntByClass( (CAI_BaseNPC *)NULL );
while (npc)
{
if (npc->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)
{
bFound = true;
npc->ToggleFreeze();
}
npc = gEntList.NextEntByClass(npc);
}
if (!bFound)
{
//
// No selected NPCs, look for the NPC under the crosshair.
//
CBaseEntity *pEntity = FindPickerEntity( UTIL_GetCommandClient() );
if ( pEntity )
{
CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
if (pNPC)
{
pNPC->ToggleFreeze();
}
}
}
}
else
{
// TODO: look for NPCs by name, classname.
}
}
static ConCommand npc_freeze("npc_freeze", CC_NPC_Freeze, "Selected NPC(s) will freeze in place (or unfreeze). If there are no selected NPCs, uses the NPC under the crosshair.\n\tArguments: -none-", FCVAR_CHEAT);
CON_COMMAND( npc_freeze_unselected, "Freeze all NPCs not selected" )
{
CAI_BaseNPC *pNPC = gEntList.NextEntByClass( (CAI_BaseNPC *)NULL );
while (pNPC)
{
if (!(pNPC->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT))
{
pNPC->ToggleFreeze();
}
pNPC = gEntList.NextEntByClass(pNPC);
}
}
//------------------------------------------------------------------------------
CON_COMMAND(npc_thinknow, "Trigger NPC to think")
{
if ( !UTIL_IsCommandIssuedByServerAdmin() )
return;
CBaseEntity *pEntity = FindPickerEntity( UTIL_GetCommandClient() );
if ( pEntity )
{
CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
if (pNPC)
{
pNPC->SetThink( &CAI_BaseNPC::CallNPCThink );
pNPC->SetNextThink( gpGlobals->curtime );
}
}
}
//------------------------------------------------------------------------------
// Purpose: Tell selected NPC to go to a where player is looking
//------------------------------------------------------------------------------
void CC_NPC_Teleport( void )
{
CBasePlayer* pPlayer = UTIL_GetCommandClient();
trace_t tr;
Vector forward;
pPlayer->EyeVectors( &forward );
AI_TraceLine(pPlayer->EyePosition(),
pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_NPCSOLID,
pPlayer, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction != 1.0)
{
CAI_BaseNPC *npc = gEntList.NextEntByClass( (CAI_BaseNPC *)NULL );
while (npc)
{
//Only Teleport one NPC if more than one is selected.
if (npc->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)
{
npc->Teleport( &tr.endpos, NULL, NULL );
break;
}
npc = gEntList.NextEntByClass(npc);
}
}
}
static ConCommand npc_teleport("npc_teleport", CC_NPC_Teleport, "Selected NPC will teleport to the location that the player is looking (shown with a purple box)\n\tArguments: -none-", FCVAR_CHEAT);
static ConVar npc_go_do_run( "npc_go_do_run", "1", 0, "Set whether should run on NPC go" );
void CC_NPC_Go( void )
{
CBasePlayer* pPlayer = UTIL_GetCommandClient();
trace_t tr;
Vector forward;
pPlayer->EyeVectors( &forward );
AI_TraceLine(pPlayer->EyePosition(),
pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_NPCSOLID,
pPlayer, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction != 1.0)
{
CAI_BaseNPC::ForceSelectedGo(pPlayer, tr.endpos, forward, npc_go_do_run.GetBool());
}
}
static ConCommand npc_go("npc_go", CC_NPC_Go, "Selected NPC(s) will go to the location that the player is looking (shown with a purple box)\n\tArguments: -none-", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Tell seclected NPC to go to a random node location
//------------------------------------------------------------------------------
void CC_NPC_GoRandom( void )
{
CAI_BaseNPC::ForceSelectedGoRandom();
}
static ConCommand npc_go_random("npc_go_random", CC_NPC_GoRandom, "Sends all selected NPC(s) to a random node.\n\tArguments: -none-", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: ?Does this work?
//------------------------------------------------------------------------------
void CC_NPC_Reset( void )
{
CAI_BaseNPC::ClearAllSchedules();
g_AI_SchedulesManager.LoadAllSchedules();
}
static ConCommand npc_reset("npc_reset", CC_NPC_Reset, "Reloads schedules for all NPC's from their script files\n\tArguments: -none-", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show the selected NPC's nearest node
//------------------------------------------------------------------------------
void CC_NPC_Nearest( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(),args[1],OVERLAY_NPC_NEAREST_BIT);
}
static ConCommand npc_nearest("npc_nearest", CC_NPC_Nearest, "Draw's a while box around the NPC(s) nearest node\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show the selected NPC's route
//------------------------------------------------------------------------------
void CC_NPC_Route( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(),args[1],OVERLAY_NPC_ROUTE_BIT);
}
static ConCommand npc_route("npc_route", CC_NPC_Route, "Displays the current route of the given NPC as a line on the screen. Waypoints along the route are drawn as small cyan rectangles. Line is color coded in the following manner:\n\tBlue - path to a node\n\tCyan - detour around an object (triangulation)\n\tRed - jump\n\tMaroon - path to final target position\n\tArguments: {npc_name} / {npc_class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Select an NPC
//------------------------------------------------------------------------------
void CC_NPC_Select( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(),args[1],OVERLAY_NPC_SELECTED_BIT);
}
static ConCommand npc_select("npc_select", CC_NPC_Select, "Select or deselects the given NPC(s) for later manipulation. Selected NPC's are shown surrounded by a red translucent box\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show combat related data for an NPC
//------------------------------------------------------------------------------
void CC_NPC_Combat( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(),args[1],OVERLAY_NPC_SQUAD_BIT);
}
static ConCommand npc_combat("npc_combat", CC_NPC_Combat, "Displays text debugging information about the squad and enemy of the selected NPC (See Overlay Text)\n\tArguments: {npc_name} / {npc class_name} / no argument picks what player is looking at", FCVAR_CHEAT);
// For backwards compatibility
static ConCommand npc_squads("npc_squads", CC_NPC_Combat, "Obsolete. Replaced by npc_combat", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show tasks for an NPC
//------------------------------------------------------------------------------
void CC_NPC_Tasks( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(),args[1],OVERLAY_NPC_TASK_BIT);
}
static ConCommand npc_tasks("npc_tasks", CC_NPC_Tasks, "Displays detailed text debugging information about the all the tasks of the selected NPC current schedule (See Overlay Text)\n\tArguments: {npc_name} / {npc class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show tasks (on the console) for an NPC
//------------------------------------------------------------------------------
void CC_NPC_Task_Text( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(), args[1], OVERLAY_TASK_TEXT_BIT);
}
static ConCommand npc_task_text("npc_task_text", CC_NPC_Task_Text, "Outputs text debugging information to the console about the all the tasks + break conditions of the selected NPC current schedule\n\tArguments: {npc_name} / {npc class_name} / no argument picks what player is looking at ", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Shows all current conditions for an NPC.
//------------------------------------------------------------------------------
void CC_NPC_Conditions( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(), args[1], OVERLAY_NPC_CONDITIONS_BIT);
}
static ConCommand npc_conditions("npc_conditions", CC_NPC_Conditions, "Displays all the current AI conditions that an NPC has in the overlay text.\n\tArguments: {npc_name} / {npc class_name} / no argument picks what player is looking at", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show an NPC's viewcone
//------------------------------------------------------------------------------
void CC_NPC_Viewcone( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(),args[1],OVERLAY_NPC_VIEWCONE_BIT);
}
static ConCommand npc_viewcone("npc_viewcone", CC_NPC_Viewcone, "Displays the viewcone of the NPC (where they are currently looking and what the extents of there vision is)\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at", FCVAR_CHEAT);
//------------------------------------------------------------------------------
// Purpose: Show an NPC's relationships to other NPCs
//------------------------------------------------------------------------------
void CC_NPC_Relationships( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(),args[1], OVERLAY_NPC_RELATION_BIT );
}
static ConCommand npc_relationships("npc_relationships", CC_NPC_Relationships, "Displays the relationships between this NPC and all others.\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at", FCVAR_CHEAT );
//------------------------------------------------------------------------------
// Purpose: Show an NPC's steering regulations
//------------------------------------------------------------------------------
void CC_NPC_ViewSteeringRegulations( const CCommand &args )
{
SetDebugBits( UTIL_GetCommandClient(), args[1], OVERLAY_NPC_STEERING_REGULATIONS);
}
static ConCommand npc_steering("npc_steering", CC_NPC_ViewSteeringRegulations, "Displays the steering obstructions of the NPC (used to perform local avoidance)\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at", FCVAR_CHEAT);
void CC_NPC_ViewSteeringRegulationsAll( void )
{
CAI_BaseNPC *pNPC = gEntList.NextEntByClass( (CAI_BaseNPC *)NULL );
while (pNPC)
{
if (!(pNPC->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT))
{
pNPC->m_debugOverlays |= OVERLAY_NPC_STEERING_REGULATIONS;
}
else
{
pNPC->m_debugOverlays &= ~OVERLAY_NPC_STEERING_REGULATIONS;
}
pNPC = gEntList.NextEntByClass(pNPC);
}
}
static ConCommand npc_steering_all("npc_steering_all", CC_NPC_ViewSteeringRegulationsAll, "Displays the steering obstructions of all NPCs (used to perform local avoidance)\n", FCVAR_CHEAT);
//------------------------------------------------------------------------------
CON_COMMAND( npc_heal, "Heals the target back to full health" )
{
if ( !UTIL_IsCommandIssuedByServerAdmin() )
return;
CBaseEntity *pEntity = FindPickerEntity( UTIL_GetCommandClient() );
if ( pEntity )
{
CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
if (pNPC)
{
pNPC->SetHealth( pNPC->GetMaxHealth() );
}
}
}
CON_COMMAND( npc_ammo_deplete, "Subtracts half of the target's ammo" )
{
if ( !UTIL_IsCommandIssuedByServerAdmin() )
return;
CBaseEntity *pEntity = FindPickerEntity( UTIL_GetCommandClient() );
if ( pEntity )
{
CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
if (pNPC && pNPC->GetActiveWeapon())
{
pNPC->GetActiveWeapon()->m_iClip1 *= 0.5;
}
}
}
CON_COMMAND( ai_clear_bad_links, "Clears bits set on nav links indicating link is unusable " )
{
if ( !UTIL_IsCommandIssuedByServerAdmin() )
return;
CAI_Node *pNode;
for ( int i = 0; i < g_pBigAINet->NumNodes(); i++ )
{
pNode = g_pBigAINet->GetNode( i );
for ( int j = 0; j < pNode->NumLinks(); j++ )
{
pNode->GetLinkByIndex( j )->m_LinkInfo &= ~bits_LINK_STALE_SUGGESTED;
}
}
}
CON_COMMAND( ai_test_los, "Test AI LOS from the player's POV" )
{
if ( !UTIL_IsCommandIssuedByServerAdmin() )
return;
trace_t tr;
// Use the custom LOS trace filter
CTraceFilterLOS traceFilter( UTIL_GetLocalPlayer(), COLLISION_GROUP_NONE );
UTIL_TraceLine( UTIL_GetLocalPlayer()->EyePosition(), UTIL_GetLocalPlayer()->EyePosition() + UTIL_GetLocalPlayer()->EyeDirection3D() * MAX_COORD_RANGE, MASK_BLOCKLOS_AND_NPCS, &traceFilter, &tr );
NDebugOverlay::Line( UTIL_GetLocalPlayer()->EyePosition(), tr.endpos, 127, 127, 127, true, 5 );
NDebugOverlay::Cross3D( tr.endpos, 24, 255, 255, 255, true, 5 );
}
#ifdef VPROF_ENABLED
CON_COMMAND(ainet_generate_report, "Generate a report to the console.")
{
g_VProfCurrentProfile.OutputReport( VPRT_FULL, "AINet" );
}
CON_COMMAND(ainet_generate_report_only, "Generate a report to the console.")
{
g_VProfCurrentProfile.OutputReport( VPRT_FULL, "AINet", g_VProfCurrentProfile.BudgetGroupNameToBudgetGroupID( "AINet" ) );
}
#endif

View File

@@ -0,0 +1,105 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "stringregistry.h"
#include "ai_basenpc.h"
#include "ai_condition.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Purpose: Given and condition name, return the condition ID
//-----------------------------------------------------------------------------
int CAI_BaseNPC::GetConditionID(const char* condName)
{
return GetSchedulingSymbols()->ConditionSymbolToId(condName);
}
//-----------------------------------------------------------------------------
// Purpose: Register the default conditions
// Input :
// Output :
//-----------------------------------------------------------------------------
#define ADD_CONDITION_TO_SR( _n ) idSpace.AddCondition( #_n, _n, "CAI_BaseNPC" )
void CAI_BaseNPC::InitDefaultConditionSR(void)
{
CAI_ClassScheduleIdSpace &idSpace = CAI_BaseNPC::AccessClassScheduleIdSpaceDirect();
ADD_CONDITION_TO_SR( COND_NONE );
ADD_CONDITION_TO_SR( COND_IN_PVS );
ADD_CONDITION_TO_SR( COND_IDLE_INTERRUPT );
ADD_CONDITION_TO_SR( COND_LOW_PRIMARY_AMMO );
ADD_CONDITION_TO_SR( COND_NO_PRIMARY_AMMO );
ADD_CONDITION_TO_SR( COND_NO_SECONDARY_AMMO );
ADD_CONDITION_TO_SR( COND_NO_WEAPON );
ADD_CONDITION_TO_SR( COND_SEE_HATE );
ADD_CONDITION_TO_SR( COND_SEE_FEAR );
ADD_CONDITION_TO_SR( COND_SEE_DISLIKE );
ADD_CONDITION_TO_SR( COND_SEE_ENEMY );
ADD_CONDITION_TO_SR( COND_LOST_ENEMY );
ADD_CONDITION_TO_SR( COND_ENEMY_WENT_NULL );
ADD_CONDITION_TO_SR( COND_HAVE_ENEMY_LOS );
ADD_CONDITION_TO_SR( COND_HAVE_TARGET_LOS );
ADD_CONDITION_TO_SR( COND_ENEMY_OCCLUDED );
ADD_CONDITION_TO_SR( COND_TARGET_OCCLUDED );
ADD_CONDITION_TO_SR( COND_ENEMY_TOO_FAR );
ADD_CONDITION_TO_SR( COND_LIGHT_DAMAGE );
ADD_CONDITION_TO_SR( COND_HEAVY_DAMAGE );
ADD_CONDITION_TO_SR( COND_PHYSICS_DAMAGE );
ADD_CONDITION_TO_SR( COND_REPEATED_DAMAGE );
ADD_CONDITION_TO_SR( COND_CAN_RANGE_ATTACK1 );
ADD_CONDITION_TO_SR( COND_CAN_RANGE_ATTACK2 );
ADD_CONDITION_TO_SR( COND_CAN_MELEE_ATTACK1 );
ADD_CONDITION_TO_SR( COND_CAN_MELEE_ATTACK2 );
ADD_CONDITION_TO_SR( COND_PROVOKED );
ADD_CONDITION_TO_SR( COND_NEW_ENEMY );
ADD_CONDITION_TO_SR( COND_ENEMY_FACING_ME );
ADD_CONDITION_TO_SR( COND_BEHIND_ENEMY );
ADD_CONDITION_TO_SR( COND_ENEMY_DEAD );
ADD_CONDITION_TO_SR( COND_ENEMY_UNREACHABLE );
ADD_CONDITION_TO_SR( COND_SEE_PLAYER );
ADD_CONDITION_TO_SR( COND_LOST_PLAYER );
ADD_CONDITION_TO_SR( COND_SEE_NEMESIS );
ADD_CONDITION_TO_SR( COND_TASK_FAILED );
ADD_CONDITION_TO_SR( COND_SCHEDULE_DONE );
ADD_CONDITION_TO_SR( COND_SMELL );
ADD_CONDITION_TO_SR( COND_TOO_CLOSE_TO_ATTACK );
ADD_CONDITION_TO_SR( COND_TOO_FAR_TO_ATTACK );
ADD_CONDITION_TO_SR( COND_NOT_FACING_ATTACK );
ADD_CONDITION_TO_SR( COND_WEAPON_HAS_LOS );
ADD_CONDITION_TO_SR( COND_WEAPON_BLOCKED_BY_FRIEND ); // Friend between gun and target
ADD_CONDITION_TO_SR( COND_WEAPON_PLAYER_IN_SPREAD ); // Player in shooting direction
ADD_CONDITION_TO_SR( COND_WEAPON_PLAYER_NEAR_TARGET ); // Player near shooting position
ADD_CONDITION_TO_SR( COND_WEAPON_SIGHT_OCCLUDED );
ADD_CONDITION_TO_SR( COND_BETTER_WEAPON_AVAILABLE );
ADD_CONDITION_TO_SR( COND_HEALTH_ITEM_AVAILABLE );
ADD_CONDITION_TO_SR( COND_FLOATING_OFF_GROUND );
ADD_CONDITION_TO_SR( COND_MOBBED_BY_ENEMIES );
ADD_CONDITION_TO_SR( COND_GIVE_WAY );
ADD_CONDITION_TO_SR( COND_WAY_CLEAR );
ADD_CONDITION_TO_SR( COND_HEAR_DANGER );
ADD_CONDITION_TO_SR( COND_HEAR_THUMPER );
ADD_CONDITION_TO_SR( COND_HEAR_COMBAT );
ADD_CONDITION_TO_SR( COND_HEAR_WORLD );
ADD_CONDITION_TO_SR( COND_HEAR_PLAYER );
ADD_CONDITION_TO_SR( COND_HEAR_BULLET_IMPACT );
ADD_CONDITION_TO_SR( COND_HEAR_BUGBAIT );
ADD_CONDITION_TO_SR( COND_HEAR_PHYSICS_DANGER );
ADD_CONDITION_TO_SR( COND_HEAR_MOVE_AWAY );
ADD_CONDITION_TO_SR( COND_NO_HEAR_DANGER );
ADD_CONDITION_TO_SR( COND_PLAYER_PUSHING );
ADD_CONDITION_TO_SR( COND_RECEIVED_ORDERS );
ADD_CONDITION_TO_SR( COND_PLAYER_ADDED_TO_SQUAD );
ADD_CONDITION_TO_SR( COND_PLAYER_REMOVED_FROM_SQUAD );
ADD_CONDITION_TO_SR( COND_NPC_FREEZE );
ADD_CONDITION_TO_SR( COND_NPC_UNFREEZE );
ADD_CONDITION_TO_SR( COND_TALKER_RESPOND_TO_QUESTION );
ADD_CONDITION_TO_SR( COND_NO_CUSTOM_INTERRUPTS );
}

View File

@@ -0,0 +1,121 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The default shared conditions
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#ifndef CONDITION_H
#define CONDITION_H
// NOTE: Changing this constant will break save files!!! (changes type of CAI_ScheduleBits)
#ifndef MAX_CONDITIONS
#define MAX_CONDITIONS 32*8
#endif
//=========================================================
// These are the default shared conditions
//=========================================================
enum SCOND_t
{
COND_NONE, // A way for a function to return no condition to get
COND_IN_PVS,
COND_IDLE_INTERRUPT, // The schedule in question is a low priority idle, and therefore a candidate for translation into something else
COND_LOW_PRIMARY_AMMO,
COND_NO_PRIMARY_AMMO,
COND_NO_SECONDARY_AMMO,
COND_NO_WEAPON,
COND_SEE_HATE,
COND_SEE_FEAR,
COND_SEE_DISLIKE,
COND_SEE_ENEMY,
COND_LOST_ENEMY,
COND_ENEMY_WENT_NULL, // What most people think COND_LOST_ENEMY is: This condition is set in the edge case where you had an enemy last think, but don't have one this think.
COND_ENEMY_OCCLUDED, // Can't see m_hEnemy
COND_TARGET_OCCLUDED, // Can't see m_hTargetEnt
COND_HAVE_ENEMY_LOS,
COND_HAVE_TARGET_LOS,
COND_LIGHT_DAMAGE,
COND_HEAVY_DAMAGE,
COND_PHYSICS_DAMAGE,
COND_REPEATED_DAMAGE, // Damaged several times in a row
COND_CAN_RANGE_ATTACK1, // Hitscan weapon only
COND_CAN_RANGE_ATTACK2, // Grenade weapon only
COND_CAN_MELEE_ATTACK1,
COND_CAN_MELEE_ATTACK2,
COND_PROVOKED,
COND_NEW_ENEMY,
COND_ENEMY_TOO_FAR, // Can we get rid of this one!?!?
COND_ENEMY_FACING_ME,
COND_BEHIND_ENEMY,
COND_ENEMY_DEAD,
COND_ENEMY_UNREACHABLE, // Not connected to me via node graph
COND_SEE_PLAYER,
COND_LOST_PLAYER,
COND_SEE_NEMESIS,
COND_TASK_FAILED,
COND_SCHEDULE_DONE,
COND_SMELL,
COND_TOO_CLOSE_TO_ATTACK, // FIXME: most of this next group are meaningless since they're shared between all attack checks!
COND_TOO_FAR_TO_ATTACK,
COND_NOT_FACING_ATTACK,
COND_WEAPON_HAS_LOS,
COND_WEAPON_BLOCKED_BY_FRIEND, // Friend between weapon and target
COND_WEAPON_PLAYER_IN_SPREAD, // Player in shooting direction
COND_WEAPON_PLAYER_NEAR_TARGET, // Player near shooting position
COND_WEAPON_SIGHT_OCCLUDED,
COND_BETTER_WEAPON_AVAILABLE,
COND_HEALTH_ITEM_AVAILABLE, // There's a healthkit available.
COND_GIVE_WAY, // Another npc requested that I give way
COND_WAY_CLEAR, // I no longer have to give way
COND_HEAR_DANGER,
COND_HEAR_THUMPER,
COND_HEAR_BUGBAIT,
COND_HEAR_COMBAT,
COND_HEAR_WORLD,
COND_HEAR_PLAYER,
COND_HEAR_BULLET_IMPACT,
COND_HEAR_PHYSICS_DANGER,
COND_HEAR_MOVE_AWAY,
COND_HEAR_SPOOKY, // Zombies make this when Alyx is in darkness mode
COND_NO_HEAR_DANGER, // Since we can't use ~CONDITION. Mutually exclusive with COND_HEAR_DANGER
COND_FLOATING_OFF_GROUND,
COND_MOBBED_BY_ENEMIES, // Surrounded by a large number of enemies melee attacking me. (Zombies or Antlions, usually).
// Commander stuff
COND_RECEIVED_ORDERS,
COND_PLAYER_ADDED_TO_SQUAD,
COND_PLAYER_REMOVED_FROM_SQUAD,
COND_PLAYER_PUSHING,
COND_NPC_FREEZE, // We received an npc_freeze command while we were unfrozen
COND_NPC_UNFREEZE, // We received an npc_freeze command while we were frozen
// This is a talker condition, but done here because we need to handle it in base AI
// due to it's interaction with behaviors.
COND_TALKER_RESPOND_TO_QUESTION,
COND_NO_CUSTOM_INTERRUPTS, // Don't call BuildScheduleTestBits for this schedule. Used for schedules that must strictly control their interruptibility.
// ======================================
// IMPORTANT: This must be the last enum
// ======================================
LAST_SHARED_CONDITION
};
#endif //CONDITION_H

View File

@@ -0,0 +1,88 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef AI_DEBUG_H
#define AI_DEBUG_H
#include "fmtstr.h"
#include "ai_debug_shared.h"
#if defined( _WIN32 )
#pragma once
#endif
// This dumps a summary result on exit
//#define PROFILE_AI 1
#define AI_PROFILE_SCOPE_BEGIN( tag ) if (0) ; else { AI_PROFILE_SCOPE( tag )
#define AI_PROFILE_SCOPE_BEGIN_( pszName ) if (0) ; else { AI_PROFILE_SCOPE_( pszName )
#define AI_PROFILE_SCOPE_END() } do {} while (0)
#if defined(VPROF_AI)
#define VProfAI() true
#else
#define VProfAI() false
#endif
#if defined(VPROF_AI)
#include "tier0/vprof.h"
#define AI_PROFILE_SCOPE( tag ) VPROF( #tag )
#define AI_PROFILE_SCOPE_( pszName ) VPROF( pszName )
#define AI_PROFILE_MEASURE_SCOPE( tag ) VPROF( #tag )
#elif defined(PROFILE_AI)
#include "tier0/fasttimer.h"
#define AI_PROFILE_SCOPE( tag ) PROFILE_SCOPE( tag )
#define AI_PROFILE_MEASURE_SCOPE( tag ) PROFILE_SCOPE( tag )
#else
#define AI_PROFILE_MEASURE_SCOPE( tag ) ((void)0)
#define AI_PROFILE_SCOPE( tag ) ((void)0)
#endif
#ifndef AI_PROFILE_SCOPE_
#define AI_PROFILE_SCOPE_( pszName ) ((void)0)
#endif
enum AIMsgFlags
{
AIMF_IGNORE_SELECTED = 0x01
};
void DevMsg( CAI_BaseNPC *pAI, unsigned flags, PRINTF_FORMAT_STRING const char *pszFormat, ... );
void DevMsg( CAI_BaseNPC *pAI, PRINTF_FORMAT_STRING const char *pszFormat, ... );
//-----------------------------------------------------------------------------
// Purpose: Use this to perform AI tracelines that are trying to determine LOS between points.
// LOS checks between entities should use FVisible.
//-----------------------------------------------------------------------------
void AI_TraceLOS( const Vector& vecAbsStart, const Vector& vecAbsEnd, CBaseEntity *pLooker, trace_t *ptr, ITraceFilter *pFilter = NULL );
//-----------------------------------------------------------------------------
#ifdef DEBUG
extern bool g_fTestSteering;
#define TestingSteering() g_fTestSteering
#else
#define TestingSteering() false
#endif
//-----------------------------------------------------------------------------
#ifdef _DEBUG
extern ConVar ai_debug_doors;
#define AIIsDebuggingDoors( pNPC ) ( ai_debug_doors.GetBool() && pNPC->m_bSelected )
#define AIDoorDebugMsg( pNPC, msg ) if ( !AIIsDebuggingDoors( pNPC ) ) ; else Msg( msg )
#else
#define AIIsDebuggingDoors( pNPC ) (false)
#define AIDoorDebugMsg( pNPC, msg ) ((void)(0))
#endif
//-----------------------------------------------------------------------------
#endif // AI_DEBUG_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Default schedules.
//
//=============================================================================//
#ifndef AI_DEFAULT_H
#define AI_DEFAULT_H
#ifdef _WIN32
#pragma once
#endif
//=========================================================
// These are the schedule types
//=========================================================
enum
{
SCHED_NONE = 0,
SCHED_IDLE_STAND,
SCHED_IDLE_WALK,
SCHED_IDLE_WANDER,
SCHED_WAKE_ANGRY,
SCHED_ALERT_FACE,
SCHED_ALERT_FACE_BESTSOUND,
SCHED_ALERT_REACT_TO_COMBAT_SOUND,
SCHED_ALERT_SCAN,
SCHED_ALERT_STAND,
SCHED_ALERT_WALK,
SCHED_INVESTIGATE_SOUND,
SCHED_COMBAT_FACE,
SCHED_COMBAT_SWEEP,
SCHED_FEAR_FACE,
SCHED_COMBAT_STAND,
SCHED_COMBAT_WALK,
SCHED_CHASE_ENEMY,
SCHED_CHASE_ENEMY_FAILED,
SCHED_VICTORY_DANCE,
SCHED_TARGET_FACE,
SCHED_TARGET_CHASE,
SCHED_SMALL_FLINCH,
SCHED_BIG_FLINCH,
SCHED_BACK_AWAY_FROM_ENEMY,
SCHED_MOVE_AWAY_FROM_ENEMY,
SCHED_BACK_AWAY_FROM_SAVE_POSITION,
SCHED_TAKE_COVER_FROM_ENEMY,
SCHED_TAKE_COVER_FROM_BEST_SOUND,
SCHED_FLEE_FROM_BEST_SOUND,
SCHED_TAKE_COVER_FROM_ORIGIN,
SCHED_FAIL_TAKE_COVER,
SCHED_RUN_FROM_ENEMY,
SCHED_RUN_FROM_ENEMY_FALLBACK,
SCHED_MOVE_TO_WEAPON_RANGE,
SCHED_ESTABLISH_LINE_OF_FIRE,
SCHED_ESTABLISH_LINE_OF_FIRE_FALLBACK,
SCHED_PRE_FAIL_ESTABLISH_LINE_OF_FIRE,
SCHED_FAIL_ESTABLISH_LINE_OF_FIRE,
SCHED_SHOOT_ENEMY_COVER,
SCHED_COWER, // usually a last resort!
SCHED_MELEE_ATTACK1,
SCHED_MELEE_ATTACK2,
SCHED_RANGE_ATTACK1,
SCHED_RANGE_ATTACK2,
SCHED_SPECIAL_ATTACK1,
SCHED_SPECIAL_ATTACK2,
SCHED_STANDOFF,
SCHED_ARM_WEAPON,
SCHED_DISARM_WEAPON,
SCHED_HIDE_AND_RELOAD,
SCHED_RELOAD,
SCHED_AMBUSH,
SCHED_DIE,
SCHED_DIE_RAGDOLL,
SCHED_WAIT_FOR_SCRIPT,
SCHED_AISCRIPT,
SCHED_SCRIPTED_WALK,
SCHED_SCRIPTED_RUN,
SCHED_SCRIPTED_CUSTOM_MOVE,
SCHED_SCRIPTED_WAIT,
SCHED_SCRIPTED_FACE,
SCHED_SCENE_GENERIC,
SCHED_NEW_WEAPON,
SCHED_NEW_WEAPON_CHEAT,
SCHED_SWITCH_TO_PENDING_WEAPON,
SCHED_GET_HEALTHKIT,
SCHED_WAIT_FOR_SPEAK_FINISH,
SCHED_MOVE_AWAY,
SCHED_MOVE_AWAY_FAIL,
SCHED_MOVE_AWAY_END,
SCHED_FORCED_GO,
SCHED_FORCED_GO_RUN,
SCHED_NPC_FREEZE,
SCHED_PATROL_WALK,
SCHED_COMBAT_PATROL,
SCHED_PATROL_RUN,
SCHED_RUN_RANDOM,
SCHED_FALL_TO_GROUND,
SCHED_DROPSHIP_DUSTOFF,
SCHED_FLINCH_PHYSICS,
SCHED_FAIL,
SCHED_FAIL_NOSTOP,
SCHED_RUN_FROM_ENEMY_MOB,
SCHED_DUCK_DODGE,
SCHED_INTERACTION_MOVE_TO_PARTNER,
SCHED_INTERACTION_WAIT_FOR_PARTNER,
SCHED_SLEEP,
// ======================================
// IMPORTANT: This must be the last enum
// ======================================
LAST_SHARED_SCHEDULE
};
#endif // AI_DEFAULT_H

View File

@@ -0,0 +1,730 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A link that can be turned on and off. Unlike normal links
// dyanimc links must be entities so they can receive messages.
// They update the state of the actual links. Allows us to save
// a lot of memory by not making all links into entities
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "collisionutils.h"
#include "ai_dynamiclink.h"
#include "ai_node.h"
#include "ai_link.h"
#include "ai_network.h"
#include "ai_networkmanager.h"
#include "saverestore_utlvector.h"
#include "editor_sendcommand.h"
#include "bitstring.h"
#include "tier0/vprof.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
LINK_ENTITY_TO_CLASS(info_node_link_controller, CAI_DynamicLinkController);
BEGIN_DATADESC( CAI_DynamicLinkController )
DEFINE_KEYFIELD( m_nLinkState, FIELD_INTEGER, "initialstate" ),
DEFINE_KEYFIELD( m_strAllowUse, FIELD_STRING, "AllowUse" ),
DEFINE_KEYFIELD( m_bInvertAllow, FIELD_BOOLEAN, "InvertAllow" ),
DEFINE_KEYFIELD( m_bUseAirLinkRadius, FIELD_BOOLEAN, "useairlinkradius" ),
// m_ControlledLinks (rebuilt)
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetAllowed", InputSetAllowed ),
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetInvert", InputSetInvert ),
END_DATADESC()
void CAI_DynamicLinkController::GenerateLinksFromVolume()
{
Assert( m_ControlledLinks.Count() == 0 );
int nNodes = g_pBigAINet->NumNodes();
CAI_Node **ppNodes = g_pBigAINet->AccessNodes();
float MinDistCareSq = 0;
if (m_bUseAirLinkRadius)
{
MinDistCareSq = Square(MAX_AIR_NODE_LINK_DIST + 0.1);
}
else
{
MinDistCareSq = Square(MAX_NODE_LINK_DIST + 0.1);
}
const Vector &origin = WorldSpaceCenter();
Vector vAbsMins, vAbsMaxs;
CollisionProp()->WorldSpaceAABB( &vAbsMins, &vAbsMaxs );
vAbsMins -= Vector( 1, 1, 1 );
vAbsMaxs += Vector( 1, 1, 1 );
for ( int i = 0; i < nNodes; i++ )
{
CAI_Node *pNode = ppNodes[i];
const Vector &nodeOrigin = pNode->GetOrigin();
if ( origin.DistToSqr(nodeOrigin) < MinDistCareSq )
{
int nLinks = pNode->NumLinks();
for ( int j = 0; j < nLinks; j++ )
{
CAI_Link *pLink = pNode->GetLinkByIndex( j );
int iLinkDest = pLink->DestNodeID( i );
if ( iLinkDest > i )
{
const Vector &originOther = ppNodes[iLinkDest]->GetOrigin();
if ( origin.DistToSqr(originOther) < MinDistCareSq )
{
if ( IsBoxIntersectingRay( vAbsMins, vAbsMaxs, nodeOrigin, originOther - nodeOrigin ) )
{
Assert( IsBoxIntersectingRay( vAbsMins, vAbsMaxs, originOther, nodeOrigin - originOther ) );
CAI_DynamicLink *pLink = (CAI_DynamicLink *)CreateEntityByName( "info_node_link" );
pLink->m_nSrcID = i;
pLink->m_nDestID = iLinkDest;
pLink->m_nSrcEditID = g_pAINetworkManager->GetEditOps()->GetWCIdFromNodeId( pLink->m_nSrcID );
pLink->m_nDestEditID = g_pAINetworkManager->GetEditOps()->GetWCIdFromNodeId( pLink->m_nDestID );
pLink->m_nLinkState = m_nLinkState;
pLink->m_strAllowUse = m_strAllowUse;
pLink->m_bInvertAllow = m_bInvertAllow;
pLink->m_bFixedUpIds = true;
pLink->m_bNotSaved = true;
pLink->Spawn();
m_ControlledLinks.AddToTail( pLink );
}
}
}
}
}
}
}
void CAI_DynamicLinkController::InputTurnOn( inputdata_t &inputdata )
{
for ( int i = 0; i < m_ControlledLinks.Count(); i++ )
{
if ( m_ControlledLinks[i] == NULL )
{
m_ControlledLinks.FastRemove(i);
if ( i >= m_ControlledLinks.Count() )
break;
}
m_ControlledLinks[i]->InputTurnOn( inputdata );
}
m_nLinkState = LINK_ON;
}
void CAI_DynamicLinkController::InputTurnOff( inputdata_t &inputdata )
{
for ( int i = 0; i < m_ControlledLinks.Count(); i++ )
{
if ( m_ControlledLinks[i] == NULL )
{
m_ControlledLinks.FastRemove(i);
if ( i >= m_ControlledLinks.Count() )
break;
}
m_ControlledLinks[i]->InputTurnOff( inputdata );
}
m_nLinkState = LINK_OFF;
}
void CAI_DynamicLinkController::InputSetAllowed( inputdata_t &inputdata )
{
m_strAllowUse = inputdata.value.StringID();
for ( int i = 0; i < m_ControlledLinks.Count(); i++ )
{
if ( m_ControlledLinks[i] == NULL )
{
m_ControlledLinks.FastRemove(i);
if ( i >= m_ControlledLinks.Count() )
break;
}
m_ControlledLinks[i]->m_strAllowUse = m_strAllowUse;
}
}
void CAI_DynamicLinkController::InputSetInvert( inputdata_t &inputdata )
{
m_bInvertAllow = inputdata.value.Bool();
for ( int i = 0; i < m_ControlledLinks.Count(); i++ )
{
if ( m_ControlledLinks[i] == NULL )
{
m_ControlledLinks.FastRemove(i);
if ( i >= m_ControlledLinks.Count() )
break;
}
m_ControlledLinks[i]->m_bInvertAllow = m_bInvertAllow;
}
}
//-----------------------------------------------------------------------------
LINK_ENTITY_TO_CLASS(info_node_link, CAI_DynamicLink);
BEGIN_DATADESC( CAI_DynamicLink )
// m_pNextDynamicLink
DEFINE_KEYFIELD( m_nLinkState, FIELD_INTEGER, "initialstate" ),
DEFINE_KEYFIELD( m_nSrcEditID, FIELD_INTEGER, "startnode" ),
DEFINE_KEYFIELD( m_nDestEditID, FIELD_INTEGER, "endnode" ),
DEFINE_KEYFIELD( m_nLinkType, FIELD_INTEGER, "linktype" ),
DEFINE_FIELD( m_bInvertAllow, FIELD_BOOLEAN ),
// m_nSrcID (rebuilt)
// m_nDestID (rebuilt)
DEFINE_KEYFIELD( m_strAllowUse, FIELD_STRING, "AllowUse" ),
// m_bFixedUpIds (part of rebuild)
// m_bNotSaved (rebuilt)
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Init static variables
//-----------------------------------------------------------------------------
CAI_DynamicLink *CAI_DynamicLink::m_pAllDynamicLinks = NULL;
bool CAI_DynamicLink::gm_bInitialized;
//------------------------------------------------------------------------------
void CAI_DynamicLink::GenerateControllerLinks()
{
CAI_DynamicLinkController *pController = NULL;
while ( ( pController = gEntList.NextEntByClass( pController ) ) != NULL )
{
pController->GenerateLinksFromVolume();
}
}
//------------------------------------------------------------------------------
// Purpose : Initializes src and dest IDs for all dynamic links
//
// Input :
// Output :
//------------------------------------------------------------------------------
void CAI_DynamicLink::InitDynamicLinks(void)
{
if (!g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable)
{
Warning("ERROR: Trying initialize links with no WC ID table!\n");
return;
}
if ( gm_bInitialized )
return;
gm_bInitialized = true;
bool bUpdateZones = false;
GenerateControllerLinks();
CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
while (pDynamicLink)
{
// -------------------------------------------------------------
// First convert this links WC IDs to engine IDs
// -------------------------------------------------------------
if ( !pDynamicLink->m_bFixedUpIds )
{
int nSrcID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( pDynamicLink->m_nSrcEditID );
if (nSrcID == -1)
{
DevMsg( "ERROR: Dynamic link source WC node %d not found\n", pDynamicLink->m_nSrcEditID );
nSrcID = NO_NODE;
}
int nDestID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( pDynamicLink->m_nDestEditID );
if (nDestID == -1)
{
DevMsg( "ERROR: Dynamic link dest WC node %d not found\n", pDynamicLink->m_nDestEditID );
nDestID = NO_NODE;
}
pDynamicLink->m_nSrcID = nSrcID;
pDynamicLink->m_nDestID = nDestID;
pDynamicLink->m_bFixedUpIds = true;
}
if ( pDynamicLink->m_nSrcID != NO_NODE && pDynamicLink->m_nDestID != NO_NODE )
{
if ( ( pDynamicLink->GetSpawnFlags() & bits_HULL_BITS_MASK ) != 0 )
{
CAI_Link *pLink = pDynamicLink->FindLink();
if ( !pLink )
{
CAI_Node *pNode1, *pNode2;
pNode1 = g_pBigAINet->GetNode( pDynamicLink->m_nSrcID );
pNode2 = g_pBigAINet->GetNode( pDynamicLink->m_nDestID );
if ( pNode1 && pNode2 )
{
pLink = g_pBigAINet->CreateLink( pDynamicLink->m_nSrcID, pDynamicLink->m_nDestID );
if ( !pLink )
DevMsg( "Failed to create dynamic link (%d <--> %d)\n", pDynamicLink->m_nSrcEditID, pDynamicLink->m_nDestEditID );
}
}
if ( pLink )
{
bUpdateZones = true;
int hullBits = ( pDynamicLink->GetSpawnFlags() & bits_HULL_BITS_MASK );
for ( int i = 0; i < NUM_HULLS; i++ )
{
if ( hullBits & ( 1 << i ) )
{
pLink->m_iAcceptedMoveTypes[i] = pDynamicLink->m_nLinkType;
}
}
}
}
// Now set the link's state
pDynamicLink->SetLinkState();
// Go on to the next dynamic link
pDynamicLink = pDynamicLink->m_pNextDynamicLink;
}
else
{
CAI_DynamicLink *pBadDynamicLink = pDynamicLink;
// Go on to the next dynamic link
pDynamicLink = pDynamicLink->m_pNextDynamicLink;
UTIL_RemoveImmediate( pBadDynamicLink );
}
}
if ( bUpdateZones )
{
g_AINetworkBuilder.InitZones( g_pBigAINet );
}
}
//------------------------------------------------------------------------------
// Purpose : Goes through each dynamic link and updates the state of all
// AINetwork links
// Input :
// Output :
//------------------------------------------------------------------------------
void CAI_DynamicLink::ResetDynamicLinks(void)
{
CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
while (pDynamicLink)
{
// Now set the link's state
pDynamicLink->SetLinkState();
// Go on to the next dynamic link
pDynamicLink = pDynamicLink->m_pNextDynamicLink;
}
}
//------------------------------------------------------------------------------
// Purpose : Goes through each dynamic link and checks to make sure that
// there is still a corresponding node link, if not removes it
// Input :
// Output :
//------------------------------------------------------------------------------
void CAI_DynamicLink::PurgeDynamicLinks(void)
{
CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
while (pDynamicLink)
{
if (!pDynamicLink->IsLinkValid())
{
// Didn't find the link, so remove it
#ifdef _WIN32
int nWCSrcID = g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable[pDynamicLink->m_nSrcID];
int nWCDstID = g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable[pDynamicLink->m_nDestID];
int status = Editor_DeleteNodeLink(nWCSrcID, nWCDstID, false);
if (status == Editor_BadCommand)
{
DevMsg( "Worldcraft failed in PurgeDynamicLinks...\n" );
}
#endif
// Safe to remove it here as this happens only after I leave this function
UTIL_Remove(pDynamicLink);
}
// Go on to the next dynamic link
pDynamicLink = pDynamicLink->m_pNextDynamicLink;
}
}
//------------------------------------------------------------------------------
// Purpose : Returns false if the dynamic link doesn't have a corresponding
// node link
// Input :
// Output :
//------------------------------------------------------------------------------
bool CAI_DynamicLink::IsLinkValid( void )
{
CAI_Node *pNode = g_pBigAINet->GetNode(m_nSrcID);
return ( pNode->GetLink( m_nDestID ) != NULL );
}
//------------------------------------------------------------------------------
// Purpose :
//------------------------------------------------------------------------------
void CAI_DynamicLink::InputTurnOn( inputdata_t &inputdata )
{
if (m_nLinkState == LINK_OFF)
{
m_nLinkState = LINK_ON;
CAI_DynamicLink::SetLinkState();
}
}
//------------------------------------------------------------------------------
// Purpose :
//------------------------------------------------------------------------------
void CAI_DynamicLink::InputTurnOff( inputdata_t &inputdata )
{
if (m_nLinkState == LINK_ON)
{
m_nLinkState = LINK_OFF;
CAI_DynamicLink::SetLinkState();
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
CAI_Link *CAI_DynamicLink::FindLink()
{
CAI_Node * pSrcNode = g_pBigAINet->GetNode(m_nSrcID, false);
if ( pSrcNode )
{
int numLinks = pSrcNode->NumLinks();
for (int i=0;i<numLinks;i++)
{
CAI_Link* pLink = pSrcNode->GetLinkByIndex(i);
if (((pLink->m_iSrcID == m_nSrcID )&&
(pLink->m_iDestID == m_nDestID)) ||
((pLink->m_iSrcID == m_nDestID)&&
(pLink->m_iDestID == m_nSrcID )) )
{
return pLink;
}
}
}
return NULL;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
int CAI_DynamicLink::ObjectCaps()
{
int caps = BaseClass::ObjectCaps();
if ( m_bNotSaved )
caps |= FCAP_DONT_SAVE;
return caps;
}
//------------------------------------------------------------------------------
// Purpose : Updates network link state if dynamic link state has changed
// Input :
// Output :
//------------------------------------------------------------------------------
void CAI_DynamicLink::SetLinkState(void)
{
if ( !gm_bInitialized )
{
// Safe to quietly return. Consistency will be enforced when InitDynamicLinks() is called
return;
}
if (m_nSrcID == NO_NODE || m_nDestID == NO_NODE)
{
Vector pos = GetAbsOrigin();
DevWarning("ERROR: Dynamic link at %f %f %f pointing to invalid node ID!!\n", pos.x, pos.y, pos.z);
return;
}
// ------------------------------------------------------------------
// Now update the node links...
// Nodes share links so we only have to find the node from the src
// For now just using one big AINetwork so find src node on that network
// ------------------------------------------------------------------
CAI_Node * pSrcNode = g_pBigAINet->GetNode(m_nSrcID, false);
if ( pSrcNode )
{
CAI_Link* pLink = FindLink();
if ( pLink )
{
pLink->m_pDynamicLink = this;
if (m_nLinkState == LINK_OFF)
{
pLink->m_LinkInfo |= bits_LINK_OFF;
}
else
{
pLink->m_LinkInfo &= ~bits_LINK_OFF;
}
}
else
{
DevMsg("Dynamic Link Error: (%s) unable to form between nodes %d and %d\n", GetDebugName(), m_nSrcID, m_nDestID );
}
}
}
//------------------------------------------------------------------------------
// Purpose : Given two node ID's return the related dynamic link if any or NULL
//
// Input :
// Output :
//------------------------------------------------------------------------------
CAI_DynamicLink* CAI_DynamicLink::GetDynamicLink(int nSrcID, int nDstID)
{
CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
while (pDynamicLink)
{
if ((nSrcID == pDynamicLink->m_nSrcID && nDstID == pDynamicLink->m_nDestID) ||
(nSrcID == pDynamicLink->m_nDestID && nDstID == pDynamicLink->m_nSrcID ) )
{
return pDynamicLink;
}
// Go on to the next dynamic link
pDynamicLink = pDynamicLink->m_pNextDynamicLink;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
// Input :
// Output :
//-----------------------------------------------------------------------------
CAI_DynamicLink::CAI_DynamicLink(void)
{
m_bFixedUpIds = false;
m_bNotSaved = false;
m_nSrcID = NO_NODE;
m_nDestID = NO_NODE;
m_nLinkState = LINK_OFF;
m_nLinkType = bits_CAP_MOVE_GROUND;
m_bInvertAllow = false;
// -------------------------------------
// Add to linked list of dynamic links
// -------------------------------------
m_pNextDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
CAI_DynamicLink::m_pAllDynamicLinks = this;
};
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
CAI_DynamicLink::~CAI_DynamicLink(void) {
// ----------------------------------------------
// Remove from linked list of all dynamic links
// ----------------------------------------------
CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
if (pDynamicLink == this)
{
m_pAllDynamicLinks = pDynamicLink->m_pNextDynamicLink;
}
else
{
while (pDynamicLink)
{
if (pDynamicLink->m_pNextDynamicLink == this)
{
pDynamicLink->m_pNextDynamicLink = pDynamicLink->m_pNextDynamicLink->m_pNextDynamicLink;
break;
}
pDynamicLink = pDynamicLink->m_pNextDynamicLink;
}
}
}
LINK_ENTITY_TO_CLASS(info_radial_link_controller, CAI_RadialLinkController);
BEGIN_DATADESC( CAI_RadialLinkController )
DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
DEFINE_FIELD( m_vecAtRestOrigin, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( m_bAtRest, FIELD_BOOLEAN ),
DEFINE_THINKFUNC( PollMotionThink ),
END_DATADESC()
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_RadialLinkController::Spawn()
{
SetSolid( SOLID_NONE );
AddEffects( EF_NODRAW );
}
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_RadialLinkController::Activate()
{
BaseClass::Activate();
m_bAtRest = false;
m_vecAtRestOrigin = vec3_invalid;
// Force re-evaluation
SetThink( &CAI_RadialLinkController::PollMotionThink );
// Spread think times out.
SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.0f, 1.0f) );
if( GetParent() != NULL )
{
float flDist = GetAbsOrigin().DistTo( GetParent()->GetAbsOrigin() );
if( flDist > 200.0f )
{
// Warn at the console if a link controller is far away from its parent. This
// most likely means that a level designer has copied an entity without researching its hierarchy.
DevMsg("RadialLinkController (%s) is far from its parent!\n", GetDebugName() );
}
}
}
//---------------------------------------------------------
//---------------------------------------------------------
void CAI_RadialLinkController::PollMotionThink()
{
SetNextThink( gpGlobals->curtime + 0.5f );
CBaseEntity *pParent = GetParent();
if( pParent )
{
if( pParent->VPhysicsGetObject()->IsAsleep() )
{
if( !m_bAtRest )
{
m_vecAtRestOrigin = GetAbsOrigin();
ModifyNodeLinks( true );
m_bAtRest = true;
//Msg("At Rest!\n");
}
}
else
{
if( m_bAtRest )
{
float flDist;
flDist = GetAbsOrigin().DistTo(m_vecAtRestOrigin);
if( flDist < 18.0f )
{
// Ignore movement If moved less than 18 inches from the place where we came to rest.
//Msg("Reject.\n");
return;
}
}
//Msg("Polling!\n");
if( m_vecAtRestOrigin != vec3_invalid )
{
ModifyNodeLinks( false );
m_bAtRest = false;
m_vecAtRestOrigin = vec3_invalid;
}
}
}
}
//---------------------------------------------------------
//---------------------------------------------------------
ConVar ai_radial_max_link_dist( "ai_radial_max_link_dist", "512" );
void CAI_RadialLinkController::ModifyNodeLinks( bool bMakeStale )
{
int nNodes = g_pBigAINet->NumNodes();
CAI_Node **ppNodes = g_pBigAINet->AccessNodes();
VPROF_BUDGET("ModifyLinks", "ModifyLinks");
const float MinDistCareSq = Square( ai_radial_max_link_dist.GetFloat() + 0.1 );
for ( int i = 0; i < nNodes; i++ )
{
CAI_Node *pNode = ppNodes[i];
const Vector &nodeOrigin = pNode->GetOrigin();
if ( m_vecAtRestOrigin.DistToSqr(nodeOrigin) < MinDistCareSq )
{
int nLinks = pNode->NumLinks();
for ( int j = 0; j < nLinks; j++ )
{
CAI_Link *pLink = pNode->GetLinkByIndex( j );
int iLinkDest = pLink->DestNodeID( i );
if ( iLinkDest > i )
{
bool bQualify = true;
if( ( (pLink->m_iAcceptedMoveTypes[HULL_HUMAN]||pLink->m_iAcceptedMoveTypes[HULL_WIDE_HUMAN]) & bits_CAP_MOVE_GROUND) == 0 )
{
// Micro-optimization: Ignore any connection that's not a walking connection for humans.(sjb)
bQualify = false;
}
const Vector &originOther = ppNodes[iLinkDest]->GetOrigin();
if ( bQualify && m_vecAtRestOrigin.DistToSqr(originOther) < MinDistCareSq )
{
if ( IsRayIntersectingSphere(nodeOrigin, originOther - nodeOrigin, m_vecAtRestOrigin, m_flRadius) )
{
if( bMakeStale )
{
pLink->m_LinkInfo |= bits_LINK_STALE_SUGGESTED;
pLink->m_timeStaleExpires = FLT_MAX;
}
else
{
pLink->m_LinkInfo &= ~bits_LINK_STALE_SUGGESTED;
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,125 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A link that can be turned on and off. Unlike normal links
// dyanimc links must be entities so they and receive messages.
// They update the state of the actual links. Allows us to save
// a lot of memory by not making all links into entities
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#ifndef AI_DYNAMICLINK_H
#define AI_DYNAMICLINK_H
#pragma once
enum DynamicLinkState_t
{
LINK_OFF = 0,
LINK_ON = 1,
};
class CAI_Link;
//=============================================================================
// >> CAI_DynanicLink
//=============================================================================
class CAI_DynamicLink : public CServerOnlyEntity
{
DECLARE_CLASS( CAI_DynamicLink, CServerOnlyEntity );
public:
static void InitDynamicLinks(void);
static void ResetDynamicLinks(void);
static void PurgeDynamicLinks(void);
static void GenerateControllerLinks();
static bool gm_bInitialized;
static CAI_DynamicLink* GetDynamicLink(int nSrcID, int nDstID);
static CAI_DynamicLink* m_pAllDynamicLinks; // A linked list of all dynamic link
CAI_DynamicLink* m_pNextDynamicLink; // The next dynamic link in the list of dynamic links
int m_nSrcEditID; // the node that 'owns' this link
int m_nDestEditID; // the node on the other end of the link.
int m_nSrcID; // the node that 'owns' this link
int m_nDestID; // the node on the other end of the link.
DynamicLinkState_t m_nLinkState; //
string_t m_strAllowUse; // Only this entity name or classname may use the link
bool m_bInvertAllow; // Instead of only allowing the m_strAllowUse entity, exclude only it
bool m_bFixedUpIds;
bool m_bNotSaved;
int m_nLinkType;
void SetLinkState( void );
bool IsLinkValid( void );
CAI_Link * FindLink();
int ObjectCaps();
// ----------------
// Inputs
// ----------------
void InputTurnOn( inputdata_t &inputdata );
void InputTurnOff( inputdata_t &inputdata );
DECLARE_DATADESC();
CAI_DynamicLink();
~CAI_DynamicLink();
};
//=============================================================================
// >> CAI_DynanicLinkVolume
//=============================================================================
class CAI_DynamicLinkController : public CServerOnlyEntity
{
DECLARE_CLASS( CAI_DynamicLinkController, CServerOnlyEntity );
public:
void GenerateLinksFromVolume();
// ----------------
// Inputs
// ----------------
void InputTurnOn( inputdata_t &inputdata );
void InputTurnOff( inputdata_t &inputdata );
void InputSetAllowed( inputdata_t &inputdata );
void InputSetInvert( inputdata_t &inputdata );
CUtlVector< CHandle<CAI_DynamicLink> > m_ControlledLinks;
DynamicLinkState_t m_nLinkState;
string_t m_strAllowUse; // Only this entity name or classname may use the link
bool m_bInvertAllow; // Instead of only allowing the m_strAllowUse entity, exclude only it
bool m_bUseAirLinkRadius;
DECLARE_DATADESC();
};
//=============================================================================
//=============================================================================
class CAI_RadialLinkController : public CBaseEntity
{
DECLARE_CLASS( CAI_RadialLinkController, CBaseEntity );
public:
void Spawn();
void Activate();
void PollMotionThink();
void ModifyNodeLinks( bool bMakeStale );
public:
float m_flRadius;
Vector m_vecAtRestOrigin;
bool m_bAtRest;
DECLARE_DATADESC();
};
#endif // AI_DYNAMICLINK_H

View File

@@ -0,0 +1,50 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Events that are available to all NPCs.
//
//=============================================================================//
#include "cbase.h"
#include "ai_basenpc.h"
#include "eventlist.h"
#include "stringregistry.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//=============================================================================
// Init static variables
//=============================================================================
CStringRegistry* CAI_BaseNPC::m_pEventSR = NULL;
int CAI_BaseNPC::m_iNumEvents = 0;
//-----------------------------------------------------------------------------
// Purpose: Add an activity to the activity string registry and increment
// the acitivty counter
//-----------------------------------------------------------------------------
void CAI_BaseNPC::AddEventToSR(const char *eventName, int eventID)
{
MEM_ALLOC_CREDIT();
Assert( m_pEventSR );
m_pEventSR->AddString( eventName, eventID );
m_iNumEvents++;
}
//-----------------------------------------------------------------------------
// Purpose: Given and activity ID, return the activity name
//-----------------------------------------------------------------------------
const char *CAI_BaseNPC::GetEventName(int eventID)
{
const char *name = m_pEventSR->GetStringText( eventID );
return name;
}
//-----------------------------------------------------------------------------
// Purpose: Given and activity name, return the activity ID
//-----------------------------------------------------------------------------
int CAI_BaseNPC::GetEventID(const char* eventName)
{
return m_pEventSR->GetStringID( eventName );
}

View File

@@ -0,0 +1,215 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: AI system that makes NPCs verbally respond to game events
//
//=============================================================================
#include "cbase.h"
#include "ai_eventresponse.h"
#include "ai_basenpc.h"
ConVar ai_debug_eventresponses( "ai_debug_eventresponses", "0", FCVAR_NONE, "Set to 1 to see all NPC response events trigger, and which NPCs choose to respond to them." );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CNPCEventResponseSystem g_NPCEventResponseSystem( "CNPCEventResponseSystem" );
CNPCEventResponseSystem *NPCEventResponse()
{
return &g_NPCEventResponseSystem;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPCEventResponseSystem::LevelInitPreEntity( void )
{
m_ActiveEvents.Purge();
m_flNextEventPoll = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPCEventResponseSystem::TriggerEvent( const char *pResponse, bool bForce, bool bCancelScript )
{
m_flNextEventPoll = gpGlobals->curtime;
// Find the event by name
int iIndex = m_ActiveEvents.Find( pResponse );
if ( iIndex == m_ActiveEvents.InvalidIndex() )
{
storedevent_t newEvent;
newEvent.flEventTime = gpGlobals->curtime;
newEvent.flNextResponseTime = 0;
newEvent.bForce = bForce;
newEvent.bCancelScript = bCancelScript;
newEvent.bPreventExpiration = false;
m_ActiveEvents.Insert( pResponse, newEvent );
if ( ai_debug_eventresponses.GetBool() )
{
Msg( "NPCEVENTRESPONSE: (%.2f) Trigger fired for event named: %s\n", gpGlobals->curtime, pResponse );
}
}
else
{
// Update the trigger time
m_ActiveEvents[iIndex].flEventTime = gpGlobals->curtime;
m_ActiveEvents[iIndex].bForce = bForce;
m_ActiveEvents[iIndex].bCancelScript = bCancelScript;
if ( ai_debug_eventresponses.GetBool() )
{
Msg( "NPCEVENTRESPONSE: (%.2f) Trigger resetting already-active event firing named: %s\n", gpGlobals->curtime, pResponse );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPCEventResponseSystem::FrameUpdatePreEntityThink()
{
if ( !m_ActiveEvents.Count() || !AI_IsSinglePlayer() || !UTIL_GetLocalPlayer() )
return;
if ( m_flNextEventPoll > gpGlobals->curtime )
return;
m_flNextEventPoll = gpGlobals->curtime + 0.2;
// Move through all events, removing expired ones and finding NPCs for active ones.
for ( int i = m_ActiveEvents.First(); i != m_ActiveEvents.InvalidIndex(); )
{
float flTime = m_ActiveEvents[i].flEventTime;
const char *pResponse = m_ActiveEvents.GetElementName(i);
// Save off the next index so we can safely remove this one
int iNext = m_ActiveEvents.Next(i);
// Should it have expired by now?
if ( !m_ActiveEvents[i].bPreventExpiration && (flTime + NPCEVENTRESPONSE_GIVEUP_TIME) < gpGlobals->curtime )
{
if ( ai_debug_eventresponses.GetBool() )
{
Msg( "NPCEVENTRESPONSE: (%.2f) Removing expired event named: %s\n", gpGlobals->curtime, pResponse );
}
m_ActiveEvents.RemoveAt(i);
}
else if ( m_ActiveEvents[i].flNextResponseTime < gpGlobals->curtime )
{
// If we've fired once, and our current event should expire now, then expire.
if ( m_ActiveEvents[i].bPreventExpiration && (flTime + NPCEVENTRESPONSE_GIVEUP_TIME) < gpGlobals->curtime )
{
if ( ai_debug_eventresponses.GetBool() )
{
Msg( "NPCEVENTRESPONSE: (%.2f) Removing expired fired event named: %s\n", gpGlobals->curtime, pResponse );
}
m_ActiveEvents.RemoveAt(i);
}
else
{
float flNearestDist = NPCEVENTRESPONSE_DISTANCE_SQR;
CAI_BaseNPC *pNearestNPC = NULL;
Vector vecPlayerCenter = UTIL_GetLocalPlayer()->WorldSpaceCenter();
// Try and find the nearest NPC to the player
CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
for ( int j = 0; j < g_AI_Manager.NumAIs(); j++ )
{
if ( ppAIs[j]->CanRespondToEvent( pResponse ))
{
float flDistToPlayer = ( vecPlayerCenter - ppAIs[j]->WorldSpaceCenter()).LengthSqr();
if ( flDistToPlayer < flNearestDist )
{
flNearestDist = flDistToPlayer;
pNearestNPC = ppAIs[j];
}
}
}
// Found one?
if ( pNearestNPC )
{
if ( pNearestNPC->RespondedTo( pResponse, m_ActiveEvents[i].bForce, m_ActiveEvents[i].bCancelScript ) )
{
// Don't remove the response yet. Leave it around until the refire time has expired.
// This stops repeated firings of the same concept from spamming the NPCs.
m_ActiveEvents[i].bPreventExpiration = true;
m_ActiveEvents[i].flNextResponseTime = gpGlobals->curtime + NPCEVENTRESPONSE_REFIRE_TIME;
if ( ai_debug_eventresponses.GetBool() )
{
Msg( "NPCEVENTRESPONSE: (%.2f) Event '%s' responded to by NPC '%s'. Refire available at: %.2f\n", gpGlobals->curtime, pResponse, pNearestNPC->GetDebugName(), m_ActiveEvents[i].flNextResponseTime );
}
// Don't issue multiple responses at once
return;
}
}
}
}
i = iNext;
}
}
//---------------------------------------------------------------------------------------------
// Entity version for mapmaker to hook into the system
//---------------------------------------------------------------------------------------------
class CNPCEventResponseSystemEntity : public CBaseEntity
{
DECLARE_CLASS( CNPCEventResponseSystemEntity, CBaseEntity );
public:
DECLARE_DATADESC();
void Spawn();
void InputTriggerResponseEvent( inputdata_t &inputdata );
void InputForceTriggerResponseEvent( inputdata_t &inputdata );
void InputForceTriggerResponseEventNoCancel( inputdata_t &inputdata );
};
LINK_ENTITY_TO_CLASS( ai_npc_eventresponsesystem, CNPCEventResponseSystemEntity );
BEGIN_DATADESC( CNPCEventResponseSystemEntity )
DEFINE_INPUTFUNC( FIELD_STRING, "TriggerResponseEvent", InputTriggerResponseEvent ),
DEFINE_INPUTFUNC( FIELD_STRING, "ForceTriggerResponseEvent", InputForceTriggerResponseEvent ),
DEFINE_INPUTFUNC( FIELD_STRING, "ForceTriggerResponseEventNoCancel", InputForceTriggerResponseEventNoCancel ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPCEventResponseSystemEntity::Spawn( void )
{
// Invisible, non solid.
AddSolidFlags( FSOLID_NOT_SOLID );
AddEffects( EF_NODRAW );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPCEventResponseSystemEntity::InputTriggerResponseEvent( inputdata_t &inputdata )
{
NPCEventResponse()->TriggerEvent( inputdata.value.String(), false, false );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPCEventResponseSystemEntity::InputForceTriggerResponseEvent( inputdata_t &inputdata )
{
NPCEventResponse()->TriggerEvent( inputdata.value.String(), true, true );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPCEventResponseSystemEntity::InputForceTriggerResponseEventNoCancel( inputdata_t &inputdata )
{
NPCEventResponse()->TriggerEvent( inputdata.value.String(), true, false );
}

View File

@@ -0,0 +1,48 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: AI system that makes NPCs verbally respond to game events
//
//=============================================================================//
#ifndef AI_EVENTRESPONSE_H
#define AI_EVENTRESPONSE_H
#include "utldict.h"
#define NPCEVENTRESPONSE_DISTANCE_SQR (768 * 768) // Maximum distance for responding to NPCs
#define NPCEVENTRESPONSE_REFIRE_TIME 15.0 // Time after giving a response before giving any more
#define NPCEVENTRESPONSE_GIVEUP_TIME 4.0 // Time after a response trigger was fired before discarding it without responding
//-----------------------------------------------------------------------------
// Purpose: AI system that makes NPCs verbally respond to game events
//-----------------------------------------------------------------------------
class CNPCEventResponseSystem : public CAutoGameSystemPerFrame
{
public:
CNPCEventResponseSystem( char const *name ) : CAutoGameSystemPerFrame( name )
{
}
void LevelInitPreEntity();
void FrameUpdatePreEntityThink();
void TriggerEvent( const char *pResponse, bool bForce, bool bCancelScript );
private:
float m_flNextEventPoll;
struct storedevent_t
{
float flEventTime;
float flNextResponseTime;
bool bForce;
bool bCancelScript;
bool bPreventExpiration;
};
typedef CUtlDict< storedevent_t, int > EventMap;
EventMap m_ActiveEvents;
};
CNPCEventResponseSystem *NPCEventResponse();
#endif // AI_EVENTRESPONSE_H

View File

@@ -0,0 +1,278 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "utlrbtree.h"
#include "saverestore_utlvector.h"
#include "ai_goalentity.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
//
// CAI_GoalEntity implementation
//
BEGIN_DATADESC( CAI_GoalEntity )
DEFINE_KEYFIELD( m_iszActor, FIELD_STRING, "Actor" ),
DEFINE_KEYFIELD( m_iszGoal, FIELD_STRING, "Goal" ),
DEFINE_KEYFIELD( m_fStartActive, FIELD_BOOLEAN, "StartActive" ),
DEFINE_KEYFIELD( m_iszConceptModifiers, FIELD_STRING, "BaseConceptModifiers" ),
DEFINE_KEYFIELD( m_SearchType, FIELD_INTEGER, "SearchType" ),
DEFINE_UTLVECTOR( m_actors, FIELD_EHANDLE ),
DEFINE_FIELD( m_hGoalEntity, FIELD_EHANDLE ),
DEFINE_FIELD( m_flags, FIELD_INTEGER ),
DEFINE_THINKFUNC( DelayedRefresh ),
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
DEFINE_INPUTFUNC( FIELD_VOID, "UpdateActors", InputUpdateActors ),
DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ),
END_DATADESC()
//-------------------------------------
void CAI_GoalEntity::Spawn()
{
SetThink( &CAI_GoalEntity::DelayedRefresh );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-------------------------------------
void CAI_GoalEntity::OnRestore()
{
BaseClass::OnRestore();
ExitDormant();
if ( ( m_flags & ACTIVE ) )
gEntList.AddListenerEntity( this );
}
//-------------------------------------
void CAI_GoalEntity::DelayedRefresh()
{
inputdata_t ignored;
if ( m_fStartActive )
{
Assert( !(m_flags & ACTIVE) );
InputActivate( ignored );
m_fStartActive = false;
}
else
InputUpdateActors( ignored );
SetThink( NULL );
}
//-------------------------------------
void CAI_GoalEntity::PruneActors()
{
for ( int i = m_actors.Count() - 1; i >= 0; i-- )
{
if ( m_actors[i] == NULL || m_actors[i]->IsMarkedForDeletion() || m_actors[i]->GetState() == NPC_STATE_DEAD )
m_actors.FastRemove( i );
}
}
//-------------------------------------
void CAI_GoalEntity::ResolveNames()
{
m_actors.SetCount( 0 );
CBaseEntity *pEntity = NULL;
for (;;)
{
switch ( m_SearchType )
{
case ST_ENTNAME:
{
pEntity = gEntList.FindEntityByName( pEntity, m_iszActor );
break;
}
case ST_CLASSNAME:
{
pEntity = gEntList.FindEntityByClassname( pEntity, STRING( m_iszActor ) );
break;
}
}
if ( !pEntity )
break;
CAI_BaseNPC *pActor = pEntity->MyNPCPointer();
if ( pActor && pActor->GetState() != NPC_STATE_DEAD )
{
AIHANDLE temp;
temp = pActor;
m_actors.AddToTail( temp );
}
}
m_hGoalEntity = gEntList.FindEntityByName( NULL, m_iszGoal );
}
//-------------------------------------
void CAI_GoalEntity::InputActivate( inputdata_t &inputdata )
{
if ( !( m_flags & ACTIVE ) )
{
gEntList.AddListenerEntity( this );
UpdateActors();
m_flags |= ACTIVE;
for ( int i = 0; i < m_actors.Count(); i++ )
{
EnableGoal( m_actors[i] );
}
}
}
//-------------------------------------
void CAI_GoalEntity::InputUpdateActors( inputdata_t &inputdata )
{
int i;
CUtlRBTree<CAI_BaseNPC *> prevActors;
CUtlRBTree<CAI_BaseNPC *>::IndexType_t index;
SetDefLessFunc( prevActors );
PruneActors();
for ( i = 0; i < m_actors.Count(); i++ )
{
prevActors.Insert( m_actors[i] );
}
ResolveNames();
for ( i = 0; i < m_actors.Count(); i++ )
{
index = prevActors.Find( m_actors[i] );
if ( index == prevActors.InvalidIndex() )
{
if ( m_flags & ACTIVE )
EnableGoal( m_actors[i] );
}
else
prevActors.Remove( m_actors[i] );
}
for ( index = prevActors.FirstInorder(); index != prevActors.InvalidIndex(); index = prevActors.NextInorder( index ) )
{
if ( m_flags & ACTIVE )
DisableGoal( prevActors[ index ] );
}
}
//-------------------------------------
void CAI_GoalEntity::InputDeactivate( inputdata_t &inputdata )
{
if ( m_flags & ACTIVE )
{
gEntList.RemoveListenerEntity( this );
UpdateActors();
m_flags &= ~ACTIVE;
for ( int i = 0; i < m_actors.Count(); i++ )
{
DisableGoal( m_actors[i] );
}
}
}
//-------------------------------------
void CAI_GoalEntity::EnterDormant( void )
{
if ( m_flags & ACTIVE )
{
m_flags |= DORMANT;
for ( int i = 0; i < m_actors.Count(); i++ )
{
DisableGoal( m_actors[i] );
}
}
}
//-------------------------------------
void CAI_GoalEntity::ExitDormant( void )
{
if ( m_flags & DORMANT )
{
m_flags &= ~DORMANT;
inputdata_t ignored;
InputUpdateActors( ignored );
}
}
//-------------------------------------
void CAI_GoalEntity::UpdateOnRemove()
{
if ( m_flags & ACTIVE )
{
inputdata_t inputdata;
InputDeactivate( inputdata );
}
BaseClass::UpdateOnRemove();
}
//-------------------------------------
void CAI_GoalEntity::OnEntityCreated( CBaseEntity *pEntity )
{
Assert( m_flags & ACTIVE );
if ( pEntity->MyNPCPointer() )
{
SetThink( &CAI_GoalEntity::DelayedRefresh );
SetNextThink( gpGlobals->curtime + 0.1f );
}
}
//-------------------------------------
void CAI_GoalEntity::OnEntityDeleted( CBaseEntity *pEntity )
{
Assert( pEntity != this );
}
//-----------------------------------------------------------------------------
int CAI_GoalEntity::DrawDebugTextOverlays()
{
char tempstr[512];
int offset = BaseClass::DrawDebugTextOverlays();
Q_snprintf( tempstr, sizeof(tempstr), "Active: %s", IsActive() ? "yes" : "no" );
EntityText( offset, tempstr, 0 );
offset++;
return offset;
}

View File

@@ -0,0 +1,194 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef AI_GOALENTITY_H
#define AI_GOALENTITY_H
#include "ai_basenpc.h"
#include "utlvector.h"
#if defined( _WIN32 )
#pragma once
#endif
//-----------------------------------------------------------------------------
//
// CAI_GoalEntity
//
// Purpose: Serves as the base class for all entities the designer may place
// that establish an NPC goal. Provides standard input, output &
// fields common to all goals.
//
class CAI_GoalEntity : public CBaseEntity,
public IEntityListener
{
DECLARE_CLASS( CAI_GoalEntity, CBaseEntity );
public:
CAI_GoalEntity()
: m_iszActor(NULL_STRING),
m_iszGoal(NULL_STRING),
m_fStartActive(false),
m_SearchType(ST_ENTNAME),
m_iszConceptModifiers(NULL_STRING),
m_hGoalEntity(NULL),
m_flags( 0 )
{
}
virtual int ObjectCaps() { return ((BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_NOTIFY_ON_TRANSITION); }
virtual void Spawn();
virtual void OnRestore();
virtual int DrawDebugTextOverlays();
virtual void InputActivate( inputdata_t &inputdata );
virtual void InputUpdateActors( inputdata_t &inputdata );
virtual void InputDeactivate( inputdata_t &inputdata );
// Goal entities can become Dormant if they're left behind on previous maps.
// Transitioning back to the map with cause a dormant goal entity to reactivate itself.
void EnterDormant( void );
void ExitDormant( void );
bool IsActive();
int NumActors();
CAI_BaseNPC * GetActor( int iActor = 0 );
void SetGoalEntity( CBaseEntity *pGoalEntity );
CBaseEntity * GetGoalEntity();
const char * GetGoalEntityName();
const char * GetConceptModifiers();
protected:
virtual void UpdateOnRemove();
virtual void OnEntityCreated( CBaseEntity *pEntity );
virtual void OnEntityDeleted( CBaseEntity *pEntity );
virtual void EnableGoal( CAI_BaseNPC *pAI ) {}
virtual void DisableGoal( CAI_BaseNPC *pAI ) {}
void UpdateActors();
const CUtlVector<AIHANDLE> &AccessActors()
{
return m_actors;
}
private:
enum Flags_t
{
ACTIVE = 0x01,
RESOLVED_NAME = 0x02,
DORMANT = 0x04,
};
enum SearchType_t
{
ST_ENTNAME,
ST_CLASSNAME,
};
void DelayedRefresh();
void PruneActors();
void ResolveNames();
// From Worldcraft
string_t m_iszActor;
string_t m_iszGoal;
bool m_fStartActive;
SearchType_t m_SearchType;
string_t m_iszConceptModifiers;
CUtlVector<AIHANDLE> m_actors;
EHANDLE m_hGoalEntity;
unsigned m_flags;
protected:
DECLARE_DATADESC();
};
//-------------------------------------
// @TODO (toml 03-18-03): Efficiency wart -- make this an explicit duty of the client?
inline void CAI_GoalEntity::UpdateActors()
{
if ( !( m_flags & ACTIVE ) || !( m_flags & RESOLVED_NAME ) )
{
ResolveNames();
m_flags |= RESOLVED_NAME;
}
else
PruneActors();
}
//-------------------------------------
inline bool CAI_GoalEntity::IsActive()
{
if ( m_flags & ACTIVE )
{
UpdateActors();
return ( m_actors.Count() != 0 );
}
return false;
}
//-------------------------------------
inline int CAI_GoalEntity::NumActors()
{
UpdateActors();
return m_actors.Count();
}
//-------------------------------------
inline CAI_BaseNPC *CAI_GoalEntity::GetActor( int iActor )
{
UpdateActors();
if ( m_actors.Count() > iActor )
return m_actors[iActor];
return NULL;
}
//-------------------------------------
inline void CAI_GoalEntity::SetGoalEntity( CBaseEntity *pGoalEntity )
{
m_iszGoal = pGoalEntity->GetEntityName();
m_hGoalEntity = pGoalEntity;
}
//-------------------------------------
inline CBaseEntity *CAI_GoalEntity::GetGoalEntity()
{
UpdateActors();
return m_hGoalEntity;
}
//-------------------------------------
inline const char *CAI_GoalEntity::GetGoalEntityName()
{
return STRING( m_iszGoal );
}
//-------------------------------------
inline const char *CAI_GoalEntity::GetConceptModifiers()
{
return STRING( m_iszConceptModifiers );
}
//-----------------------------------------------------------------------------
#endif // AI_GOALENTITY_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,341 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Hint node utilities and functions.
//
// $NoKeywords: $
//=============================================================================//
#ifndef AI_HINT_H
#define AI_HINT_H
#pragma once
#include "ai_initutils.h"
#include "tier1/utlmap.h"
//Flags for FindHintNode
#define bits_HINT_NODE_NONE 0x00000000
#define bits_HINT_NODE_VISIBLE 0x00000001
#define bits_HINT_NODE_NEAREST 0x00000002 // Choose the node nearest me
#define bits_HINT_NODE_RANDOM 0x00000004 // Find a random hintnode meeting other criteria
#define bits_HINT_NODE_CLEAR 0x00000008 // Only choose nodes that have clear room for my bounding box (requires NPC)
#define bits_HINT_NODE_USE_GROUP 0x00000010 // Use the NPC's hintgroup when searching for a node (requires NPC)
#define bits_HINT_NODE_VISIBLE_TO_PLAYER 0x00000020
#define bits_HINT_NODE_NOT_VISIBLE_TO_PLAYER 0x00000040
#define bits_HINT_NODE_REPORT_FAILURES 0x00000080
#define bits_HINT_NODE_IN_VIEWCONE 0x00000100
#define bits_HINT_NODE_IN_AIMCONE 0x00000200
#define bits_HINT_NPC_IN_NODE_FOV 0x00000400 // Is the searcher inside the hint node's FOV?
#define bits_HINT_NOT_CLOSE_TO_ENEMY 0x00000800 // Hint must not be within 30 feet of my enemy
#define bits_HINT_HAS_LOS_TO_PLAYER 0x00001000 // Like VISIBLE_TO_PLAYER but doesn't care about player's facing
#define bits_HAS_EYEPOSITION_LOS_TO_PLAYER 0x00002000 // Like HAS LOS TO PLAYER, but checks NPC's eye position at the node, not node origin.
//-----------------------------------------------------------------------------
//
// hints - these MUST coincide with the HINTS listed under
// info_node in the FGD file!
//
// For debugging, they must also coincide with g_pszHintDescriptions.
//
//-----------------------------------------------------------------------------
enum Hint_e
{
HINT_ANY = -1,
HINT_NONE = 0,
HINT_NOT_USED_WORLD_DOOR,
HINT_WORLD_WINDOW,
HINT_NOT_USED_WORLD_BUTTON,
HINT_NOT_USED_WORLD_MACHINERY,
HINT_NOT_USED_WORLD_LEDGE,
HINT_NOT_USED_WORLD_LIGHT_SOURCE,
HINT_NOT_USED_WORLD_HEAT_SOURCE,
HINT_NOT_USED_WORLD_BLINKING_LIGHT,
HINT_NOT_USED_WORLD_BRIGHT_COLORS,
HINT_NOT_USED_WORLD_HUMAN_BLOOD,
HINT_NOT_USED_WORLD_ALIEN_BLOOD,
HINT_WORLD_WORK_POSITION,
HINT_WORLD_VISUALLY_INTERESTING,
HINT_WORLD_VISUALLY_INTERESTING_DONT_AIM,
HINT_WORLD_INHIBIT_COMBINE_MINES,
HINT_WORLD_VISUALLY_INTERESTING_STEALTH,
HINT_TACTICAL_COVER_MED = 100,
HINT_TACTICAL_COVER_LOW,
HINT_TACTICAL_SPAWN,
HINT_TACTICAL_PINCH, // Exit / entrance to an arena
HINT_NOT_USED_TACTICAL_GUARD,
HINT_TACTICAL_ENEMY_DISADVANTAGED, //Disadvantageous position for the enemy
HINT_NOT_USED_HEALTH_KIT,
HINT_NOT_USED_URBAN_STREETCORNER = 200,
HINT_NOT_USED_URBAN_STREETLAMP,
HINT_NOT_USED_URBAN_DARK_SPOT,
HINT_NOT_USED_URBAN_POSTER,
HINT_NOT_USED_URBAN_SHELTER,
HINT_NOT_USED_ASSASSIN_SECLUDED = 300,
HINT_NOT_USED_ASSASSIN_RAFTERS,
HINT_NOT_USED_ASSASSIN_GROUND,
HINT_NOT_USED_ASSASSIN_MONKEYBARS,
HINT_ANTLION_BURROW_POINT = 400,
HINT_ANTLION_THUMPER_FLEE_POINT,
HINT_HEADCRAB_BURROW_POINT = 450,
HINT_HEADCRAB_EXIT_POD_POINT,
HINT_NOT_USED_ROLLER_PATROL_POINT = 500,
HINT_NOT_USED_ROLLER_CLEANUP_POINT,
HINT_NOT_USED_PSTORM_ROCK_SPAWN = 600,
HINT_CROW_FLYTO_POINT = 700,
// TF2 Hints
HINT_BUG_PATROL_POINT = 800,
// HL2 Hints
HINT_FOLLOW_WAIT_POINT = 900,
HINT_JUMP_OVERRIDE = 901,
HINT_PLAYER_SQUAD_TRANSITON_POINT = 902,
HINT_NPC_EXIT_POINT = 903,
HINT_STRIDER_NODE = 904,
HINT_PLAYER_ALLY_MOVE_AWAY_DEST = 950,
HINT_PLAYER_ALLY_FEAR_DEST,
// HL1 port hints
HINT_HL1_WORLD_MACHINERY = 1000,
HINT_HL1_WORLD_BLINKING_LIGHT,
HINT_HL1_WORLD_HUMAN_BLOOD,
HINT_HL1_WORLD_ALIEN_BLOOD,
// CS port hints
HINT_CSTRIKE_HOSTAGE_ESCAPE = 1100,
};
const char *GetHintTypeDescription( Hint_e iHintType );
const char *GetHintTypeDescription( CAI_Hint *pHint );
//-----------------------------------------------------------------------------
// CHintCriteria
//-----------------------------------------------------------------------------
class CHintCriteria
{
public:
CHintCriteria();
~CHintCriteria();
bool HasFlag( int bitmask ) const { return ( m_iFlags & bitmask ) != 0; }
void SetFlag( int bitmask );
void ClearFlag( int bitmask );
void SetGroup( string_t group );
string_t GetGroup( void ) const { return m_strGroup; }
int GetFirstHintType( void ) const { return m_iFirstHintType; }
int GetLastHintType( void ) const { return m_iLastHintType; }
bool MatchesHintType( int hintType ) const;
bool MatchesSingleHintType() const;
bool HasIncludeZones( void ) const { return ( m_zoneInclude.Count() != 0 ); }
bool HasExcludeZones( void ) const { return ( m_zoneExclude.Count() != 0 ); }
void AddIncludePosition( const Vector &position, float radius );
void AddExcludePosition( const Vector &position, float radius );
void SetHintType( int hintType );
void SetHintTypeRange( int firstType, int lastType );
void AddHintType( int hintType );
bool InIncludedZone( const Vector &testPosition ) const;
bool InExcludedZone( const Vector &testPosition ) const;
int NumHintTypes() const;
int GetHintType( int idx ) const;
private:
struct hintZone_t
{
Vector position;
float radiussqr;
};
typedef CUtlVector < hintZone_t > zoneList_t;
void AddZone( zoneList_t &list, const Vector &position, float radius );
bool InZone( const zoneList_t &zone, const Vector &testPosition ) const;
CUtlVector<int> m_HintTypes;
int m_iFlags;
int m_iFirstHintType;
int m_iLastHintType;
string_t m_strGroup;
zoneList_t m_zoneInclude;
zoneList_t m_zoneExclude;
};
class CAI_Node;
//-----------------------------------------------------------------------------
// CAI_HintManager
//-----------------------------------------------------------------------------
DECLARE_POINTER_HANDLE(AIHintIter_t);
class CAIHintVector : public CUtlVector< CAI_Hint * >
{
public:
CAIHintVector() : CUtlVector< CAI_Hint * >( 1, 0 )
{
}
CAIHintVector( const CAIHintVector& src )
{
CopyArray( src.Base(), src.Count() );
}
CAIHintVector &operator=( const CAIHintVector &src )
{
CopyArray( src.Base(), src.Count() );
return *this;
}
};
class CAI_HintManager
{
friend class CAI_Hint;
public:
// Hint node creation
static CAI_Hint *CreateHint( HintNodeData *pNodeData, const char *pMapData = NULL );
static void DrawHintOverlays(float flDrawDuration);
static void AddHint( CAI_Hint *pTestHint );
static void RemoveHint( CAI_Hint *pTestHint );
static void AddHintByType( CAI_Hint *pHint );
static void RemoveHintByType( CAI_Hint *pHintToRemove );
// Interface for searching the hint node list
static CAI_Hint *FindHint( CAI_BaseNPC *pNPC, const Vector &position, const CHintCriteria &hintCriteria );
static CAI_Hint *FindHint( CAI_BaseNPC *pNPC, const CHintCriteria &hintCriteria );
static CAI_Hint *FindHint( const Vector &position, const CHintCriteria &hintCriteria );
static CAI_Hint *FindHint( CAI_BaseNPC *pNPC, Hint_e nHintType, int nFlags, float flMaxDist, const Vector *pMaxDistFrom = NULL );
// Purpose: Finds a random suitable hint within the requested radious of the npc
static CAI_Hint *FindHintRandom( CAI_BaseNPC *pNPC, const Vector &position, const CHintCriteria &hintCriteria );
static int FindAllHints( CAI_BaseNPC *pNPC, const Vector &position, const CHintCriteria &hintCriteria, CUtlVector<CAI_Hint *> *pResult );
static int FindAllHints( const Vector &position, const CHintCriteria &hintCriteria, CUtlVector<CAI_Hint *> *pResult ) { return FindAllHints( NULL, position, hintCriteria, pResult ); }
static int FindAllHints( CAI_BaseNPC *pNPC, const CHintCriteria &hintCriteria, CUtlVector<CAI_Hint *> *pResult ) { return FindAllHints( pNPC, pNPC->GetAbsOrigin(), hintCriteria, pResult ); }
static int GetFlags( const char *token );
static CAI_Hint *GetFirstHint( AIHintIter_t *pIter );
static CAI_Hint *GetNextHint( AIHintIter_t *pIter );
static void DumpHints();
static void ValidateHints();
private:
enum
{
// MUST BE POWER OF 2
HINT_HISTORY = (1<<3),
HINT_HISTORY_MASK = (HINT_HISTORY-1)
};
static CAI_Hint *AddFoundHint( CAI_Hint *hint );
static int GetFoundHintCount();
static CAI_Hint *GetFoundHint( int index );
static CAI_Hint *GetLastFoundHint();
static void ResetFoundHints();
static bool IsInFoundHintList( CAI_Hint *hint );
static int gm_nFoundHintIndex;
static CAI_Hint *gm_pLastFoundHints[ HINT_HISTORY ]; // Last used hint
static CAIHintVector gm_AllHints; // A linked list of all hints
static CUtlMap< int, CAIHintVector > gm_TypedHints;
};
//-----------------------------------------------------------------------------
// CAI_Hint
//-----------------------------------------------------------------------------
class CAI_Hint : public CServerOnlyEntity
{
DECLARE_CLASS( CAI_Hint, CServerOnlyEntity );
public:
CAI_Hint( void );
~CAI_Hint( void );
// Interface for specific nodes
bool Lock( CBaseEntity *pNPC ); // Makes unavailable for hints
void Unlock( float delay = 0.0 ); // Makes available for hints after delay
bool IsLocked(void); // Whether this node is available for use.
bool IsLockedBy( CBaseEntity *pNPC ); // Whether this node is available for use.
void GetPosition(CBaseCombatCharacter *pBCC, Vector *vPosition);
void GetPosition( Hull_t hull, Vector *vPosition );
Vector GetDirection( void );
float Yaw( void );
CAI_Node *GetNode( void );
string_t GetGroup( void ) const { return m_NodeData.strGroup; }
CBaseEntity *User( void ) const { return m_hHintOwner; };
Hint_e HintType( void ) const { return (Hint_e)m_NodeData.nHintType; };
void SetHintType( int hintType, bool force = false );
string_t HintActivityName( void ) const { return m_NodeData.iszActivityName; }
int GetTargetNode( void ) const { return m_nTargetNodeID; }
bool IsDisabled( void ) const { return (m_NodeData.iDisabled != 0); }
void SetDisabled( bool bDisabled ) { m_NodeData.iDisabled = bDisabled; }
void DisableForSeconds( float flSeconds );
void EnableThink();
void FixupTargetNode();
void NPCStartedUsing( CAI_BaseNPC *pNPC );
void NPCStoppedUsing( CAI_BaseNPC *pNPC );
HintIgnoreFacing_t GetIgnoreFacing() const { return m_NodeData.fIgnoreFacing; }
NPC_STATE GetMinState() const { return m_NodeData.minState; }
NPC_STATE GetMaxState() const { return m_NodeData.maxState; }
int GetNodeId() { return m_NodeData.nNodeID; }
int GetWCId() { return m_NodeData.nWCNodeID; }
bool HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hintCriteria, const Vector &position, float *flNearestDistance, bool bIgnoreLock = false, bool bIgnoreHintType = false );
bool IsInNodeFOV( CBaseEntity *pOther );
private:
void Spawn( void );
virtual void Activate();
virtual void UpdateOnRemove( void );
int DrawDebugTextOverlays(void);
virtual int ObjectCaps( void ) { return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }
virtual void OnRestore();
bool IsViewable( void );
// Input handlers
void InputEnableHint( inputdata_t &inputdata );
void InputDisableHint( inputdata_t &inputdata );
private:
HintNodeData m_NodeData;
int m_nTargetNodeID;
EHANDLE m_hHintOwner; // Is hint locked (being used by NPC / NPC en-route to use it)
float m_flNextUseTime; // When can I be used again?
COutputEHANDLE m_OnNPCStartedUsing; // Triggered when an NPC has actively begun to use the node.
COutputEHANDLE m_OnNPCStoppedUsing; // Triggered when an NPC has finished using this node.
float m_nodeFOV;
Vector m_vecForward;
// The next hint in list of all hints
friend class CAI_HintManager;
DECLARE_DATADESC();
};
#define SF_ALLOW_JUMP_UP 65536
#endif //AI_HINT_H

View File

@@ -0,0 +1,185 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=============================================================================//
#include "cbase.h"
#include "ai_hull.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
struct ai_hull_t
{
ai_hull_t( int bit, const char *pName, const Vector &_mins, const Vector &_maxs, const Vector &_smallMins, const Vector &_smallMaxs )
: hullBit( bit ), mins( _mins ), maxs( _maxs ), smallMins( _smallMins ), smallMaxs( _smallMaxs ), name( pName ) {}
int hullBit;
const char* name;
Vector mins;
Vector maxs;
Vector smallMins;
Vector smallMaxs;
};
//=================================================================================
// Create the hull types here.
//=================================================================================
#ifdef HL1_DLL
ai_hull_t Human_Hull (bits_HUMAN_HULL, "HUMAN_HULL", Vector(-13,-13, 0), Vector(13, 13, 72), Vector(-8,-8, 0), Vector( 8, 8, 72) );
ai_hull_t Small_Centered_Hull (bits_SMALL_CENTERED_HULL, "SMALL_CENTERED_HULL", Vector(-20,-20, -20), Vector(20, 20, 20), Vector(-12,-12,-12), Vector(12, 12, 12) );
ai_hull_t Wide_Human_Hull (bits_WIDE_HUMAN_HULL, "WIDE_HUMAN_HULL", Vector(-32,-32, 0), Vector(32, 32, 72), Vector(-10,-10, 0), Vector(10, 10, 72) );
ai_hull_t Tiny_Hull (bits_TINY_HULL, "TINY_HULL", Vector(-12,-12, 0), Vector(12, 12, 24), Vector(-12,-12, 0), Vector(12, 12, 24) );
ai_hull_t Wide_Short_Hull (bits_WIDE_SHORT_HULL, "WIDE_SHORT_HULL", Vector(-35,-35, 0), Vector(35, 35, 32), Vector(-20,-20, 0), Vector(20, 20, 32) );
ai_hull_t Medium_Hull (bits_MEDIUM_HULL, "MEDIUM_HULL", Vector(-16,-16, 0), Vector(16, 16, 64), Vector(-8,-8, 0), Vector(8, 8, 64) );
ai_hull_t Tiny_Centered_Hull (bits_TINY_CENTERED_HULL, "TINY_CENTERED_HULL", Vector(-8, -8, -4), Vector(8, 8, 4), Vector(-8,-8, -4), Vector( 8, 8, 4) );
ai_hull_t Large_Hull (bits_LARGE_HULL, "LARGE_HULL", Vector(-40,-40, 0), Vector(40, 40, 100), Vector(-40,-40, 0), Vector(40, 40, 100) );
ai_hull_t Large_Centered_Hull (bits_LARGE_CENTERED_HULL, "LARGE_CENTERED_HULL", Vector(-38,-38, -38), Vector(38, 38, 38), Vector(-30,-30,-30), Vector(30, 30, 30) );
ai_hull_t Medium_Tall_Hull (bits_MEDIUM_TALL_HULL, "MEDIUM_TALL_HULL", Vector(-18,-18, 0), Vector(18, 18, 100), Vector(-12,-12, 0), Vector(12, 12, 100) );
#else
ai_hull_t Human_Hull (bits_HUMAN_HULL, "HUMAN_HULL", Vector(-13,-13, 0), Vector(13, 13, 72), Vector(-8,-8, 0), Vector( 8, 8, 72) );
ai_hull_t Small_Centered_Hull (bits_SMALL_CENTERED_HULL, "SMALL_CENTERED_HULL", Vector(-20,-20, -20), Vector(20, 20, 20), Vector(-12,-12,-12), Vector(12, 12, 12) );
ai_hull_t Wide_Human_Hull (bits_WIDE_HUMAN_HULL, "WIDE_HUMAN_HULL", Vector(-15,-15, 0), Vector(15, 15, 72), Vector(-10,-10, 0), Vector(10, 10, 72) );
ai_hull_t Tiny_Hull (bits_TINY_HULL, "TINY_HULL", Vector(-12,-12, 0), Vector(12, 12, 24), Vector(-12,-12, 0), Vector(12, 12, 24) );
ai_hull_t Wide_Short_Hull (bits_WIDE_SHORT_HULL, "WIDE_SHORT_HULL", Vector(-35,-35, 0), Vector(35, 35, 32), Vector(-20,-20, 0), Vector(20, 20, 32) );
ai_hull_t Medium_Hull (bits_MEDIUM_HULL, "MEDIUM_HULL", Vector(-16,-16, 0), Vector(16, 16, 64), Vector(-8,-8, 0), Vector(8, 8, 64) );
ai_hull_t Tiny_Centered_Hull (bits_TINY_CENTERED_HULL, "TINY_CENTERED_HULL", Vector(-8, -8, -4), Vector(8, 8, 4), Vector(-8,-8, -4), Vector( 8, 8, 4) );
ai_hull_t Large_Hull (bits_LARGE_HULL, "LARGE_HULL", Vector(-40,-40, 0), Vector(40, 40, 100), Vector(-40,-40, 0), Vector(40, 40, 100) );
ai_hull_t Large_Centered_Hull (bits_LARGE_CENTERED_HULL, "LARGE_CENTERED_HULL", Vector(-38,-38, -38), Vector(38, 38, 38), Vector(-30,-30,-30), Vector(30, 30, 30) );
ai_hull_t Medium_Tall_Hull (bits_MEDIUM_TALL_HULL, "MEDIUM_TALL_HULL", Vector(-18,-18, 0), Vector(18, 18, 100), Vector(-12,-12, 0), Vector(12, 12, 100) );
#endif//HL1_DLL
//
// Array of hulls. These hulls must correspond with the enumerations in AI_Hull.h!
//
ai_hull_t* hull[NUM_HULLS] =
{
&Human_Hull,
&Small_Centered_Hull,
&Wide_Human_Hull,
&Tiny_Hull,
&Wide_Short_Hull,
&Medium_Hull,
&Tiny_Centered_Hull,
&Large_Hull,
&Large_Centered_Hull,
&Medium_Tall_Hull,
};
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
const Vector &NAI_Hull::Mins(int id)
{
return hull[id]->mins;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
const Vector &NAI_Hull::Maxs(int id)
{
return hull[id]->maxs;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
const Vector &NAI_Hull::SmallMins(int id)
{
return hull[id]->smallMins;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
const Vector &NAI_Hull::SmallMaxs(int id)
{
return hull[id]->smallMaxs;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
float NAI_Hull::Length(int id)
{
return (hull[id]->maxs.x - hull[id]->mins.x);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
float NAI_Hull::Width(int id)
{
return (hull[id]->maxs.y - hull[id]->mins.y);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
float NAI_Hull::Height(int id)
{
return (hull[id]->maxs.z - hull[id]->mins.z);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
int NAI_Hull::Bits(int id)
{
return hull[id]->hullBit;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
const char *NAI_Hull::Name(int id)
{
return hull[id]->name;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
Hull_t NAI_Hull::LookupId(const char *szName)
{
int i;
if (!szName)
{
return HULL_HUMAN;
}
for (i = 0; i < NUM_HULLS; i++)
{
if (stricmp( szName, NAI_Hull::Name( i )) == 0)
{
return (Hull_t)i;
}
}
return HULL_HUMAN;
}

View File

@@ -0,0 +1,75 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=============================================================================//
#ifndef AI_HULL_H
#define AI_HULL_H
#pragma once
class Vector;
//=========================================================
// Link Properties. These hulls must correspond to the hulls
// in AI_Hull.cpp!
//=========================================================
enum Hull_t
{
HULL_HUMAN, // Combine, Stalker, Zombie...
HULL_SMALL_CENTERED, // Scanner
HULL_WIDE_HUMAN, // Vortigaunt
HULL_TINY, // Headcrab
HULL_WIDE_SHORT, // Bullsquid
HULL_MEDIUM, // Cremator
HULL_TINY_CENTERED, // Manhack
HULL_LARGE, // Antlion Guard
HULL_LARGE_CENTERED, // Mortar Synth
HULL_MEDIUM_TALL, // Hunter
//--------------------------------------------
NUM_HULLS,
HULL_NONE // No Hull (appears after num hulls as we don't want to count it)
};
enum Hull_Bits_t
{
bits_HUMAN_HULL = 0x00000001,
bits_SMALL_CENTERED_HULL = 0x00000002,
bits_WIDE_HUMAN_HULL = 0x00000004,
bits_TINY_HULL = 0x00000008,
bits_WIDE_SHORT_HULL = 0x00000010,
bits_MEDIUM_HULL = 0x00000020,
bits_TINY_CENTERED_HULL = 0x00000040,
bits_LARGE_HULL = 0x00000080,
bits_LARGE_CENTERED_HULL = 0x00000100,
bits_MEDIUM_TALL_HULL = 0x00000200,
bits_HULL_BITS_MASK = 0x000002ff,
};
inline int HullToBit( Hull_t hull )
{
return ( 1 << hull );
}
//=============================================================================
// >> CAI_Hull
//=============================================================================
namespace NAI_Hull
{
const Vector &Mins(int id);
const Vector &Maxs(int id);
const Vector &SmallMins(int id);
const Vector &SmallMaxs(int id);
float Length(int id);
float Width(int id);
float Height(int id);
int Bits(int id);
const char* Name(int id);
Hull_t LookupId(const char *szName);
};
#endif // AI_HULL_H

View File

@@ -0,0 +1,342 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: AI Utility classes for building the initial AI Networks
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "ai_node.h"
#include "ai_hull.h"
#include "ai_hint.h"
#include "ai_initutils.h"
#include "ai_networkmanager.h"
// to help eliminate node clutter by level designers, this is used to cap how many other nodes
// any given node is allowed to 'see' in the first stage of graph creation "LinkVisibleNodes()".
#include "ai_network.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
LINK_ENTITY_TO_CLASS( info_hint, CNodeEnt );
LINK_ENTITY_TO_CLASS( info_node, CNodeEnt );
LINK_ENTITY_TO_CLASS( info_node_hint, CNodeEnt );
LINK_ENTITY_TO_CLASS( info_node_air, CNodeEnt );
LINK_ENTITY_TO_CLASS( info_node_air_hint, CNodeEnt );
LINK_ENTITY_TO_CLASS( info_node_climb, CNodeEnt );
LINK_ENTITY_TO_CLASS( aitesthull, CAI_TestHull );
//-----------------------------------------------------------------------------
// Init static variables
//-----------------------------------------------------------------------------
CAI_TestHull* CAI_TestHull::pTestHull = NULL;
#ifdef CSTRIKE_DLL
#define PLAYER_MODEL "models/player/ct_urban.mdl"
#else
#define PLAYER_MODEL "models/player.mdl"
#endif
//-----------------------------------------------------------------------------
// Purpose: Make sure we have a "player.mdl" hull to test with
//-----------------------------------------------------------------------------
void CAI_TestHull::Precache()
{
BaseClass::Precache();
PrecacheModel( PLAYER_MODEL );
}
//=========================================================
// CAI_TestHull::Spawn
//=========================================================
void CAI_TestHull::Spawn(void)
{
Precache();
SetModel( PLAYER_MODEL );
// Set an initial hull size (this will change later)
SetHullType(HULL_HUMAN);
SetHullSizeNormal();
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_SOLID );
SetMoveType( MOVETYPE_STEP );
m_iHealth = 50;
bInUse = false;
// Make this invisible
AddEffects( EF_NODRAW );
}
//-----------------------------------------------------------------------------
// Purpose: Get the test hull (create if none)
// Input :
// Output :
//-----------------------------------------------------------------------------
CAI_TestHull* CAI_TestHull::GetTestHull(void)
{
if (!CAI_TestHull::pTestHull)
{
CAI_TestHull::pTestHull = CREATE_ENTITY( CAI_TestHull, "aitesthull" );
CAI_TestHull::pTestHull->Spawn();
CAI_TestHull::pTestHull->AddFlag( FL_NPC );
}
if (CAI_TestHull::pTestHull->bInUse == true)
{
DevMsg("WARNING: TestHull used and never returned!\n");
Assert( 0 );
}
CAI_TestHull::pTestHull->RemoveSolidFlags( FSOLID_NOT_SOLID );
CAI_TestHull::pTestHull->bInUse = true;
return CAI_TestHull::pTestHull;
}
//-----------------------------------------------------------------------------
// Purpose: Get the test hull (create if none)
// Input :
// Output :
//-----------------------------------------------------------------------------
void CAI_TestHull::ReturnTestHull(void)
{
CAI_TestHull::pTestHull->bInUse = false;
CAI_TestHull::pTestHull->AddSolidFlags( FSOLID_NOT_SOLID );
UTIL_SetSize(CAI_TestHull::pTestHull, vec3_origin, vec3_origin);
UTIL_RemoveImmediate( pTestHull );
pTestHull = NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &startPos -
// &endPos -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAI_TestHull::IsJumpLegal(const Vector &startPos, const Vector &apex, const Vector &endPos) const
{
const float MAX_JUMP_RISE = 1024.0f;
const float MAX_JUMP_DISTANCE = 1024.0f;
const float MAX_JUMP_DROP = 1024.0f;
return BaseClass::IsJumpLegal( startPos, apex, endPos, MAX_JUMP_RISE, MAX_JUMP_DISTANCE, MAX_JUMP_DROP );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
CAI_TestHull::~CAI_TestHull(void)
{
CAI_TestHull::pTestHull = NULL;
}
//###########################################################
// > CNodeEnt
//
// nodes start out as ents in the world. As they are spawned,
// the node info is recorded then the ents are discarded.
//###########################################################
//----------------------------------------------------
// Static vars
//----------------------------------------------------
int CNodeEnt::m_nNodeCount = 0;
// -------------
// Data table
// -------------
BEGIN_SIMPLE_DATADESC( HintNodeData )
DEFINE_FIELD( strEntityName, FIELD_STRING ),
// DEFINE_FIELD( vecPosition, FIELD_VECTOR ), // Don't save
DEFINE_KEYFIELD( nHintType, FIELD_SHORT, "hinttype" ),
DEFINE_KEYFIELD( strGroup, FIELD_STRING, "Group" ),
DEFINE_KEYFIELD( iDisabled, FIELD_INTEGER, "StartHintDisabled" ),
DEFINE_FIELD( nNodeID, FIELD_INTEGER ),
DEFINE_KEYFIELD( iszActivityName, FIELD_STRING, "hintactivity" ),
DEFINE_KEYFIELD( nTargetWCNodeID, FIELD_INTEGER, "TargetNode" ),
DEFINE_KEYFIELD( nWCNodeID, FIELD_INTEGER, "nodeid" ),
DEFINE_KEYFIELD( fIgnoreFacing, FIELD_INTEGER, "IgnoreFacing" ),
DEFINE_KEYFIELD( minState, FIELD_INTEGER, "MinimumState" ),
DEFINE_KEYFIELD( maxState, FIELD_INTEGER, "MaximumState" ),
END_DATADESC()
// -------------
// Data table
// -------------
BEGIN_DATADESC( CNodeEnt )
DEFINE_EMBEDDED( m_NodeData ),
END_DATADESC()
//=========================================================
//=========================================================
void CNodeEnt::Spawn( void )
{
Spawn( NULL );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pMapData -
//-----------------------------------------------------------------------------
int CNodeEnt::Spawn( const char *pMapData )
{
m_NodeData.strEntityName = GetEntityName();
m_NodeData.vecPosition = GetAbsOrigin();
m_NodeData.nNodeID = NO_NODE;
if ( m_NodeData.minState == NPC_STATE_NONE )
m_NodeData.minState = NPC_STATE_IDLE;
if ( m_NodeData.maxState == NPC_STATE_NONE )
m_NodeData.maxState = NPC_STATE_COMBAT;
// ---------------------------------------------------------------------------------
// If just a hint node (not used for navigation) just create a hint and bail
// ---------------------------------------------------------------------------------
if (FClassnameIs( this, "info_hint" ))
{
if (m_NodeData.nHintType)
{
CAI_HintManager::CreateHint( &m_NodeData, pMapData );
}
else
{
Warning("info_hint (HammerID: %d, position (%.2f, %.2f, %.2f)) with no hint type.\n", m_NodeData.nWCNodeID, m_NodeData.vecPosition.x, m_NodeData.vecPosition.y, m_NodeData.vecPosition.z );
}
UTIL_RemoveImmediate( this );
return -1;
}
// ---------------------------------------------------------------------------------
// First check if this node has a hint. If so create a hint entity
// ---------------------------------------------------------------------------------
CAI_Hint *pHint = NULL;
if ( ClassMatches( "info_node_hint" ) || ClassMatches( "info_node_air_hint" ) )
{
if ( m_NodeData.nHintType || m_NodeData.strGroup != NULL_STRING || m_NodeData.strEntityName != NULL_STRING )
{
m_NodeData.nNodeID = m_nNodeCount;
pHint = CAI_HintManager::CreateHint( &m_NodeData, pMapData );
pHint->AddSpawnFlags( GetSpawnFlags() );
}
}
// ---------------------------------------------------------------------------------
// If we loaded from disk, we can discard all these node ents as soon as they spawn
// unless we are in WC edited mode
// ---------------------------------------------------------------------------------
if ( g_pAINetworkManager->NetworksLoaded() && !engine->IsInEditMode())
{
// If hint exists for this node, set it
if (pHint)
{
CAI_Node *pNode = g_pBigAINet->GetNode(m_nNodeCount);
if (pNode)
pNode->SetHint( pHint );
else
{
DevMsg("AI node graph corrupt\n");
}
}
m_nNodeCount++;
UTIL_RemoveImmediate( this );
return -1;
}
else
{
m_nNodeCount++;
}
// ---------------------------------------------------------------------------------
// Add a new node to the network
// ---------------------------------------------------------------------------------
// For now just using one big AI network
CAI_Node *new_node = g_pBigAINet->AddNode( GetAbsOrigin(), GetAbsAngles().y );
new_node->SetHint( pHint );
// -------------------------------------------------------------------------
// Update table of how each WC id relates to each engine ID
// -------------------------------------------------------------------------
if (g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable)
{
g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable[new_node->GetId()] = m_NodeData.nWCNodeID;
}
// Keep track of largest index used by WC
if (g_pAINetworkManager->GetEditOps()->m_nNextWCIndex <= m_NodeData.nWCNodeID)
{
g_pAINetworkManager->GetEditOps()->m_nNextWCIndex = m_NodeData.nWCNodeID+1;
}
// -------------------------------------------------------------------------
// If in WC edit mode:
// Remember the original positions of the nodes before
// they drop so we can send the undropped positions to wc.
// -------------------------------------------------------------------------
if (engine->IsInEditMode())
{
if (g_pAINetworkManager->GetEditOps()->m_pWCPosition)
{
g_pAINetworkManager->GetEditOps()->m_pWCPosition[new_node->GetId()] = new_node->GetOrigin();
}
}
if (FClassnameIs( this, "info_node_air" ) || FClassnameIs( this, "info_node_air_hint" ))
{
new_node->SetType( NODE_AIR );
}
else if (FClassnameIs( this, "info_node_climb" ))
{
new_node->SetType( NODE_CLIMB );
}
else
{
new_node->SetType( NODE_GROUND );
}
new_node->m_eNodeInfo = ( m_spawnflags << NODE_ENT_FLAGS_SHIFT );
// If changed as part of WC editing process note that network must be rebuilt
if (m_debugOverlays & OVERLAY_WC_CHANGE_ENTITY)
{
g_pAINetworkManager->GetEditOps()->SetRebuildFlags();
new_node->m_eNodeInfo |= bits_NODE_WC_CHANGED;
// Initialize the new nodes position. The graph may not be rebuild
// right away but the node should at least be positioned correctly
g_AINetworkBuilder.InitNodePosition( g_pBigAINet, new_node );
}
UTIL_RemoveImmediate( this );
return -1;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
CNodeEnt::CNodeEnt( void )
{
m_debugOverlays = 0;
}

View File

@@ -0,0 +1,105 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: AI Utility classes for building the initial AI Networks
//
// $NoKeywords: $
//=============================================================================//
#ifndef AI_INITUTILS_H
#define AI_INITUTILS_H
#ifdef _WIN32
#pragma once
#endif
#include "ai_basenpc.h"
#include "ai_node.h"
//###########################################################
// >> HintNodeData
//
// This is a chunk of data that's passed to a hint node entity
// when it's created from a CNodeEnt.
//###########################################################
enum HintIgnoreFacing_t
{
HIF_NO,
HIF_YES,
HIF_DEFAULT,
};
struct HintNodeData
{
string_t strEntityName;
Vector vecPosition;
short nHintType;
int nNodeID;
string_t strGroup;
int iDisabled;
string_t iszActivityName;
int nTargetWCNodeID;
HintIgnoreFacing_t fIgnoreFacing;
NPC_STATE minState;
NPC_STATE maxState;
int nWCNodeID; // Node ID assigned by worldcraft (not same as engine!)
DECLARE_SIMPLE_DATADESC();
};
//###########################################################
// >> CNodeEnt
//
// This is the entity that is loaded in from worldcraft.
// It is only used to build the network and is deleted
// immediately
//###########################################################
class CNodeEnt : public CServerOnlyPointEntity
{
DECLARE_CLASS( CNodeEnt, CServerOnlyPointEntity );
public:
virtual void SetOwnerEntity( CBaseEntity* pOwner ) { BaseClass::SetOwnerEntity( NULL ); }
static int m_nNodeCount;
void Spawn( void );
int Spawn( const char *pMapData );
DECLARE_DATADESC();
CNodeEnt(void);
public:
HintNodeData m_NodeData;
};
//###########################################################
// >> CAI_TestHull
//
// a modelless clip hull that verifies reachable nodes by
// walking from every node to each of it's connections//
//###########################################################
class CAI_TestHull : public CAI_BaseNPC
{
DECLARE_CLASS( CAI_TestHull, CAI_BaseNPC );
private:
static CAI_TestHull* pTestHull; // Hull for testing connectivity
public:
static CAI_TestHull* GetTestHull(void); // Get the test hull
static void ReturnTestHull(void); // Return the test hull
bool bInUse;
virtual void Precache();
void Spawn(void);
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~(FCAP_ACROSS_TRANSITION|FCAP_DONT_SAVE); }
virtual bool IsJumpLegal(const Vector &startPos, const Vector &apex, const Vector &endPos) const;
~CAI_TestHull(void);
};
#endif // AI_INITUTILS_H

View File

@@ -0,0 +1,57 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "ai_link.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
ASSERT_INVARIANT( ( bits_LINK_STALE_SUGGESTED | bits_LINK_OFF ) <= 255 && ( AI_MOVE_TYPE_BITS <= 255 ) );
//-----------------------------------------------------------------------------
// Purpose: Given the source node ID, returns the destination ID
// Input :
// Output :
//-----------------------------------------------------------------------------
int CAI_Link::DestNodeID(int srcID)
{
if (srcID == m_iSrcID)
{
return m_iDestID;
}
else
{
return m_iSrcID;
}
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
// Input :
// Output :
//-----------------------------------------------------------------------------
CAI_Link::CAI_Link(void)
{
m_iSrcID = -1;
m_iDestID = -1;
m_LinkInfo = 0;
m_timeStaleExpires = 0;
m_pDynamicLink = NULL;
for (int hull=0;hull<NUM_HULLS;hull++)
{
m_iAcceptedMoveTypes[hull] = 0;
}
};

Some files were not shown because too many files have changed in this diff Show More