mirror of
https://github.com/mapbase-source/source-sdk-2013.git
synced 2025-01-26 21:57:57 +03:00
Merge branch 'develop' into feature/vscript/hook-handler-prototype-1
# Conflicts: # sp/src/vscript/vscript_squirrel.nut
This commit is contained in:
commit
0d9a5349b0
7
.github/CONTRIBUTING.md
vendored
7
.github/CONTRIBUTING.md
vendored
@ -34,6 +34,13 @@ All contributions must follow the following rules:
|
||||
use the custom "Mapbase - Source 2013" header used in other Mapbase files as of Mapbase v5.0.
|
||||
You are encouraged to append an "Author(s)" part to that header in your file in order to clarify who wrote it.
|
||||
|
||||
Contributions which do not follow these guidelines cannot be accepted into Mapbase.
|
||||
|
||||
Attempting to contribute content which seriously violates the rules above can lead to being blocked from contributing,
|
||||
especially if done repeatedly.
|
||||
|
||||
---
|
||||
|
||||
If your contribution is accepted, you may be listed in Mapbase's credits and the README's external content list:
|
||||
https://github.com/mapbase-source/source-sdk-2013/wiki/Mapbase-Credits#Contributors
|
||||
https://github.com/mapbase-source/source-sdk-2013/blob/master/README
|
||||
|
17
README
17
README
@ -20,6 +20,9 @@ Mapbase's main content in this repository may include:
|
||||
- View rendering changes for drawing 3D skyboxes and RT-based entities
|
||||
- Countless other fixes and improvements
|
||||
|
||||
For more information, view this page:
|
||||
https://github.com/mapbase-source/source-sdk-2013/wiki/Introduction-to-Mapbase
|
||||
|
||||
//===================================================================================================================================================
|
||||
|
||||
Mapbase is an open-source project and its contents can be distributed and used at the discretion of its users. However, this project represents many parts of
|
||||
@ -37,7 +40,7 @@ and repositories (especially ones which are specifically published as free sourc
|
||||
or complicated code changes accessible and easy to use for level designers and other kinds of Source modders who would otherwise have no idea how to implement them.
|
||||
|
||||
*** DISCLAIMER: Mapbase has a strict no-leak-content policy and only allows content created directly by contributors or content originating from open-source repositories.
|
||||
If you believe any content in Mapbase originates from any leak or unauthorized source (from Valve or otherwise), please contact Blixibon immediately.
|
||||
If you believe any content in Mapbase originates from any leak or unauthorized source (Valve or otherwise), please contact Blixibon immediately.
|
||||
Mapbase is intended to be usable by everyone, including licensed Source projects and Steam mods. ***
|
||||
|
||||
-- The Alien Swarm SDK was used to backport features and code from newer branches of Source into a Source 2013/Half-Life 2 environment.
|
||||
@ -91,9 +94,9 @@ Direct contributions:
|
||||
|
||||
- https://github.com/mapbase-source/source-sdk-2013/pull/5 (Custom VScript implementation by ReDucTor; was placed into feature branch before being merged in a subsequent PR)
|
||||
- https://github.com/mapbase-source/source-sdk-2013/pull/3 ("playvideo" command playback fix from Avantate)
|
||||
- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux fixes from z33ky)
|
||||
- https://github.com/mapbase-source/source-sdk-2013/pull/60 (Adjustment by RoyaleNoir to one of Saul's VDC changes)
|
||||
- https://github.com/mapbase-source/source-sdk-2013/pull/84 (CS:S viewmodel chirality from 1upD)
|
||||
- https://github.com/mapbase-source/source-sdk-2013/pull/116 (vgui_movie_display mute keyvalue from Alivebyte/rzkid)
|
||||
- Demo autorecord code provided by Klems
|
||||
- cc_emit crash fix provided by 1upD
|
||||
- Custom HL2 ammo crate models created by Rara (Textures created by Blixibon; This is asset-based and, aside from the SLAM crate, not reflected in the code)
|
||||
@ -107,6 +110,12 @@ Direct contributions:
|
||||
=-- https://github.com/mapbase-source/source-sdk-2013/pull/59 (New VScript functions and singletons based on API documentation in later Source/Source 2 games)
|
||||
=-- https://github.com/mapbase-source/source-sdk-2013/pull/80 (More VScript changes, including support for extremely flexible client/server messaging)
|
||||
=-- https://github.com/mapbase-source/source-sdk-2013/pull/105 (VScript fixes and optimizations, Vector class extensions, custom convars/commands)
|
||||
=-- https://github.com/mapbase-source/source-sdk-2013/pull/114 (VScript fixes and extensions)
|
||||
|
||||
== Contributions from z33ky:
|
||||
=-- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux compilation fixes)
|
||||
=-- https://github.com/mapbase-source/source-sdk-2013/pull/95 (Additional GCC/Linux compilation fixes)
|
||||
=-- https://github.com/mapbase-source/source-sdk-2013/pull/117 (Additional GCC/Linux compilation fixes)
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -135,6 +144,10 @@ If there is anything missing from this list, please contact Blixibon.
|
||||
Aside from the content list above, Mapbase has more descriptive and up-to-date credits on this wiki article:
|
||||
https://github.com/mapbase-source/source-sdk-2013/wiki/Mapbase-Credits
|
||||
|
||||
Other relevant articles:
|
||||
* https://github.com/mapbase-source/source-sdk-2013/wiki/Mapbase-Disclaimers
|
||||
* https://github.com/mapbase-source/source-sdk-2013/wiki/Frequently-Asked-Questions-(FAQ)
|
||||
|
||||
//===================================================================================================================================================
|
||||
|
||||
Please see the Source SDK 2013 license below:
|
||||
|
@ -6,10 +6,11 @@ MAKEFILE_LINK:=$(THISFILE).link
|
||||
|
||||
-include $(MAKEFILE_LINK)
|
||||
|
||||
$(MAKEFILE_LINK): $(shell which $(CC)) $(THISFILE)
|
||||
if [ "$(shell printf "$(shell $(CC) -dumpversion)\n8" | sort -Vr | head -1)" = 8 ]; then \
|
||||
$(COMPILE.cpp) -o gcc9+support.o gcc9+support.c ;\
|
||||
# depend on CXX so the correct makefile can be selected when the system is updated
|
||||
$(MAKEFILE_LINK): $(shell which $(CXX)) $(THISFILE) $(SRCROOT)/devtools/gcc9+support.cpp
|
||||
@ if [ "$(shell printf "$(shell $(CXX) -dumpversion)\n8" | sort -Vr | head -1)" = 8 ]; then \
|
||||
ln -sf $(MAKEFILE_BASE).default $@ ;\
|
||||
else \
|
||||
$(COMPILE.cpp) -o $(SRCROOT)/devtools/gcc9+support.o $(SRCROOT)/devtools/gcc9+support.cpp &&\
|
||||
ln -sf $(MAKEFILE_BASE).gcc8 $@ ;\
|
||||
fi
|
||||
|
@ -294,6 +294,7 @@ END_SCRIPTDESC();
|
||||
|
||||
ScriptHook_t C_BaseAnimating::g_Hook_OnClientRagdoll;
|
||||
ScriptHook_t C_BaseAnimating::g_Hook_FireEvent;
|
||||
ScriptHook_t C_BaseAnimating::g_Hook_BuildTransformations;
|
||||
#endif
|
||||
|
||||
BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-side" )
|
||||
@ -310,6 +311,14 @@ BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-si
|
||||
|
||||
DEFINE_SCRIPTFUNC( LookupBone, "Get the named bone id" )
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoneTransform, "GetBoneTransform", "Get the transform for the specified bone" )
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptSetBoneTransform, "SetBoneTransform", "Set the transform for the specified bone" )
|
||||
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptAttachEntityToBone, "AttachEntityToBone", "Attaches this entity to the specified target and bone. Also allows for optional local position offset" )
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptRemoveBoneAttachment, "RemoveBoneAttachment", "Removes the specified bone attachment" )
|
||||
//DEFINE_SCRIPTFUNC( RemoveBoneAttachments, "Removes all bone attachments" )
|
||||
DEFINE_SCRIPTFUNC( DestroyBoneAttachments, "Destroys all bone attachments" )
|
||||
DEFINE_SCRIPTFUNC( GetNumBoneAttachments, "Gets the number of bone attachments" )
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoneAttachment, "GetBoneAttachment", "Gets the specified bone attachment" )
|
||||
|
||||
DEFINE_SCRIPTFUNC( SetBodygroup, "Sets a bodygroup")
|
||||
DEFINE_SCRIPTFUNC( GetBodygroup, "Gets a bodygroup" )
|
||||
@ -354,6 +363,9 @@ BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-si
|
||||
DEFINE_SCRIPTHOOK_PARAM( "event", FIELD_INTEGER )
|
||||
DEFINE_SCRIPTHOOK_PARAM( "options", FIELD_CSTRING )
|
||||
END_SCRIPTHOOK()
|
||||
|
||||
BEGIN_SCRIPTHOOK( C_BaseAnimating::g_Hook_BuildTransformations, "BuildTransformations", FIELD_VOID, "Called when building bone transformations. Allows VScript to read/write any bone with Get/SetBoneTransform." )
|
||||
END_SCRIPTHOOK()
|
||||
#endif
|
||||
END_SCRIPTDESC();
|
||||
|
||||
@ -1538,10 +1550,43 @@ HSCRIPT C_BaseAnimating::ScriptGetAttachmentMatrix( int iAttachment )
|
||||
|
||||
void C_BaseAnimating::ScriptGetBoneTransform( int iBone, HSCRIPT hTransform )
|
||||
{
|
||||
if (hTransform == NULL)
|
||||
matrix3x4_t *matTransform = HScriptToClass<matrix3x4_t>( hTransform );
|
||||
if (matTransform == NULL)
|
||||
return;
|
||||
|
||||
GetBoneTransform( iBone, *HScriptToClass<matrix3x4_t>( hTransform ) );
|
||||
GetBoneTransform( iBone, *matTransform );
|
||||
}
|
||||
|
||||
void C_BaseAnimating::ScriptSetBoneTransform( int iBone, HSCRIPT hTransform )
|
||||
{
|
||||
matrix3x4_t *matTransform = HScriptToClass<matrix3x4_t>( hTransform );
|
||||
if (matTransform == NULL)
|
||||
return;
|
||||
|
||||
MatrixCopy( *matTransform, GetBoneForWrite( iBone ) );
|
||||
}
|
||||
|
||||
void C_BaseAnimating::ScriptAttachEntityToBone( HSCRIPT attachTarget, int boneIndexAttached, const Vector &bonePosition, const QAngle &boneAngles )
|
||||
{
|
||||
C_BaseEntity *pTarget = ToEnt( attachTarget );
|
||||
if (pTarget == NULL)
|
||||
return;
|
||||
|
||||
AttachEntityToBone( pTarget->GetBaseAnimating(), boneIndexAttached, bonePosition, boneAngles );
|
||||
}
|
||||
|
||||
void C_BaseAnimating::ScriptRemoveBoneAttachment( HSCRIPT boneAttachment )
|
||||
{
|
||||
C_BaseEntity *pTarget = ToEnt( boneAttachment );
|
||||
if (pTarget == NULL)
|
||||
return;
|
||||
|
||||
RemoveBoneAttachment( pTarget->GetBaseAnimating() );
|
||||
}
|
||||
|
||||
HSCRIPT C_BaseAnimating::ScriptGetBoneAttachment( int i )
|
||||
{
|
||||
return ToHScript( GetBoneAttachment( i ) );
|
||||
}
|
||||
|
||||
HSCRIPT C_BaseAnimating::ScriptBecomeRagdollOnClient()
|
||||
@ -1719,7 +1764,23 @@ void C_BaseAnimating::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quater
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
if (m_ScriptScope.IsInitialized() && g_Hook_BuildTransformations.CanRunInScope(m_ScriptScope))
|
||||
{
|
||||
int oldWritableBones = m_BoneAccessor.GetWritableBones();
|
||||
int oldReadableBones = m_BoneAccessor.GetReadableBones();
|
||||
m_BoneAccessor.SetWritableBones( BONE_USED_BY_ANYTHING );
|
||||
m_BoneAccessor.SetReadableBones( BONE_USED_BY_ANYTHING );
|
||||
|
||||
// No parameters
|
||||
//ScriptVariant_t args[] = {};
|
||||
//ScriptVariant_t returnValue;
|
||||
g_Hook_BuildTransformations.Call( m_ScriptScope, NULL, NULL /*&returnValue, args*/ );
|
||||
|
||||
m_BoneAccessor.SetWritableBones( oldWritableBones );
|
||||
m_BoneAccessor.SetReadableBones( oldReadableBones );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -4808,12 +4869,18 @@ void C_BaseAnimating::GetRagdollInitBoneArrays( matrix3x4_t *pDeltaBones0, matri
|
||||
}
|
||||
}
|
||||
|
||||
C_ClientRagdoll *C_BaseAnimating::CreateClientRagdoll( bool bRestoring )
|
||||
{
|
||||
//DevMsg( "Creating ragdoll at tick %d\n", gpGlobals->tickcount );
|
||||
return new C_ClientRagdoll( bRestoring );
|
||||
}
|
||||
|
||||
C_BaseAnimating *C_BaseAnimating::CreateRagdollCopy()
|
||||
{
|
||||
//Adrian: We now create a separate entity that becomes this entity's ragdoll.
|
||||
//That way the server side version of this entity can go away.
|
||||
//Plus we can hook save/restore code to these ragdolls so they don't fall on restore anymore.
|
||||
C_ClientRagdoll *pRagdoll = new C_ClientRagdoll( false );
|
||||
C_ClientRagdoll *pRagdoll = CreateClientRagdoll( false );
|
||||
if ( pRagdoll == NULL )
|
||||
return NULL;
|
||||
|
||||
@ -5366,6 +5433,11 @@ void C_BaseAnimating::StudioFrameAdvance()
|
||||
|
||||
if ( flNewCycle < 0.0f || flNewCycle >= 1.0f )
|
||||
{
|
||||
if (flNewCycle >= 1.0f)
|
||||
{
|
||||
ReachedEndOfSequence();
|
||||
}
|
||||
|
||||
if ( IsSequenceLooping( hdr, GetSequence() ) )
|
||||
{
|
||||
flNewCycle -= (int)(flNewCycle);
|
||||
|
@ -38,6 +38,7 @@ class C_BaseClientShader
|
||||
*/
|
||||
|
||||
class IRagdoll;
|
||||
class C_ClientRagdoll;
|
||||
class CIKContext;
|
||||
class CIKState;
|
||||
class ConVar;
|
||||
@ -301,6 +302,7 @@ public:
|
||||
bool IsRagdoll() const;
|
||||
bool IsAboutToRagdoll() const;
|
||||
virtual C_BaseAnimating *BecomeRagdollOnClient();
|
||||
virtual C_ClientRagdoll *CreateClientRagdoll( bool bRestoring = false );
|
||||
C_BaseAnimating *CreateRagdollCopy();
|
||||
bool InitAsClientRagdoll( const matrix3x4_t *pDeltaBones0, const matrix3x4_t *pDeltaBones1, const matrix3x4_t *pCurrentBonePosition, float boneDt, bool bFixedConstraints=false );
|
||||
void IgniteRagdoll( C_BaseAnimating *pSource );
|
||||
@ -354,6 +356,8 @@ public:
|
||||
void ClientSideAnimationChanged();
|
||||
virtual unsigned int ComputeClientSideAnimationFlags();
|
||||
|
||||
virtual void ReachedEndOfSequence() { return; }
|
||||
|
||||
virtual void ResetClientsideFrame( void ) { SetCycle( 0 ); }
|
||||
|
||||
void SetCycle( float flCycle );
|
||||
@ -464,6 +468,11 @@ public:
|
||||
HSCRIPT ScriptGetAttachmentMatrix(int iAttachment);
|
||||
|
||||
void ScriptGetBoneTransform( int iBone, HSCRIPT hTransform );
|
||||
void ScriptSetBoneTransform( int iBone, HSCRIPT hTransform );
|
||||
|
||||
void ScriptAttachEntityToBone( HSCRIPT attachTarget, int boneIndexAttached, const Vector &bonePosition, const QAngle &boneAngles );
|
||||
void ScriptRemoveBoneAttachment( HSCRIPT boneAttachment );
|
||||
HSCRIPT ScriptGetBoneAttachment( int i );
|
||||
|
||||
int ScriptGetSequenceActivity( int iSequence ) { return GetSequenceActivity( iSequence ); }
|
||||
float ScriptGetSequenceMoveDist( int iSequence ) { return GetSequenceMoveDist( GetModelPtr(), iSequence ); }
|
||||
@ -482,6 +491,7 @@ public:
|
||||
|
||||
static ScriptHook_t g_Hook_OnClientRagdoll;
|
||||
static ScriptHook_t g_Hook_FireEvent;
|
||||
static ScriptHook_t g_Hook_BuildTransformations;
|
||||
|
||||
float ScriptGetPoseParameter(const char* szName);
|
||||
#endif
|
||||
|
@ -428,6 +428,10 @@ BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_AnimTimeMustBeFirst )
|
||||
RecvPropInt( RECVINFO(m_flAnimTime), 0, RecvProxy_AnimTime ),
|
||||
END_RECV_TABLE()
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
ScriptHook_t CBaseEntity::g_Hook_UpdateOnRemove;
|
||||
#endif
|
||||
|
||||
BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities" )
|
||||
DEFINE_SCRIPT_INSTANCE_HELPER( &g_BaseEntityScriptInstanceHelper )
|
||||
DEFINE_SCRIPTFUNC_NAMED( GetAbsOrigin, "GetOrigin", "" )
|
||||
@ -550,7 +554,10 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities
|
||||
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptSetContextThink, "SetContextThink", "Set a think function on this entity." )
|
||||
|
||||
#endif
|
||||
|
||||
DEFINE_SIMPLE_SCRIPTHOOK( CBaseEntity::g_Hook_UpdateOnRemove, "UpdateOnRemove", FIELD_VOID, "Called when the entity is being removed." )
|
||||
|
||||
#endif // MAPBASE_VSCRIPT
|
||||
|
||||
END_SCRIPTDESC();
|
||||
|
||||
@ -1340,6 +1347,12 @@ void C_BaseEntity::Term()
|
||||
|
||||
if ( m_hScriptInstance )
|
||||
{
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
if ( m_ScriptScope.IsInitialized() )
|
||||
{
|
||||
g_Hook_UpdateOnRemove.Call( m_ScriptScope, NULL, NULL );
|
||||
}
|
||||
#endif
|
||||
g_pScriptVM->RemoveInstance( m_hScriptInstance );
|
||||
m_hScriptInstance = NULL;
|
||||
|
||||
|
@ -164,10 +164,9 @@ struct thinkfunc_t
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
struct scriptthinkfunc_t
|
||||
{
|
||||
int m_nNextThinkTick;
|
||||
HSCRIPT m_hfnThink;
|
||||
unsigned short m_iContextHash;
|
||||
bool m_bNoParam;
|
||||
float m_flNextThink;
|
||||
HSCRIPT m_hfnThink;
|
||||
unsigned m_iContextHash;
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -295,6 +294,8 @@ public:
|
||||
string_t m_iszScriptId;
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
CScriptScope m_ScriptScope;
|
||||
|
||||
static ScriptHook_t g_Hook_UpdateOnRemove;
|
||||
#endif
|
||||
|
||||
// IClientUnknown overrides.
|
||||
@ -400,7 +401,7 @@ public:
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
// "I don't know why but wrapping entindex() works, while calling it directly crashes."
|
||||
inline int C_BaseEntity::GetEntityIndex() const { return entindex(); }
|
||||
inline int GetEntityIndex() const { return entindex(); }
|
||||
#endif
|
||||
|
||||
// This works for client-only entities and returns the GetEntryIndex() of the entity's handle,
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "ammodef.h"
|
||||
#include "vprof.h"
|
||||
#include "view.h"
|
||||
#include "vstdlib/ikeyvaluessystem.h"
|
||||
#include "vstdlib/IKeyValuesSystem.h"
|
||||
#ifdef MAPBASE
|
||||
#include "usermessages.h"
|
||||
#endif
|
||||
@ -666,7 +666,8 @@ void CIconLesson::UpdateInactive()
|
||||
CUtlBuffer msg_data;
|
||||
msg_data.PutChar( 1 );
|
||||
msg_data.PutString( m_szHudHint.String() );
|
||||
usermessages->DispatchUserMessage( usermessages->LookupUserMessage( "KeyHintText" ), bf_read( msg_data.Base(), msg_data.TellPut() ) );
|
||||
bf_read msg( msg_data.Base(), msg_data.TellPut() );
|
||||
usermessages->DispatchUserMessage( usermessages->LookupUserMessage( "KeyHintText" ), msg );
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1039,40 +1040,40 @@ Vector CIconLesson::GetIconTargetPosition( C_BaseEntity *pIconTarget )
|
||||
|
||||
#define LESSON_VARIABLE_INIT_SYMBOL( _varEnum, _varName, _varType ) g_n##_varEnum##Symbol = KeyValuesSystem()->GetSymbolForString( #_varEnum );
|
||||
|
||||
#define LESSON_SCRIPT_STRING_ADD_TO_MAP( _varEnum, _varName, _varType ) g_NameToTypeMap.Insert( #_varEnum, LESSON_VARIABLE_##_varEnum## );
|
||||
#define LESSON_SCRIPT_STRING_ADD_TO_MAP( _varEnum, _varName, _varType ) g_NameToTypeMap.Insert( #_varEnum, LESSON_VARIABLE_##_varEnum );
|
||||
|
||||
// Create enum value
|
||||
#define LESSON_VARIABLE_ENUM( _varEnum, _varName, _varType ) LESSON_VARIABLE_##_varEnum##,
|
||||
#define LESSON_VARIABLE_ENUM( _varEnum, _varName, _varType ) LESSON_VARIABLE_##_varEnum,
|
||||
|
||||
// Init info call
|
||||
#define LESSON_VARIABLE_INIT_INFO_CALL( _varEnum, _varName, _varType ) g_pLessonVariableInfo[ LESSON_VARIABLE_##_varEnum## ].Init_##_varEnum##();
|
||||
#define LESSON_VARIABLE_INIT_INFO_CALL( _varEnum, _varName, _varType ) g_pLessonVariableInfo[ LESSON_VARIABLE_##_varEnum ].Init_##_varEnum();
|
||||
|
||||
// Init info
|
||||
#define LESSON_VARIABLE_INIT_INFO( _varEnum, _varName, _varType ) \
|
||||
void Init_##_varEnum##() \
|
||||
void Init_##_varEnum() \
|
||||
{ \
|
||||
iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \
|
||||
iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::_varName ); \
|
||||
varType = LessonParamTypeFromString( #_varType ); \
|
||||
}
|
||||
|
||||
#define LESSON_VARIABLE_INIT_INFO_BOOL( _varEnum, _varName, _varType ) \
|
||||
void Init_##_varEnum##() \
|
||||
void Init_##_varEnum() \
|
||||
{ \
|
||||
iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \
|
||||
iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::_varName ); \
|
||||
varType = FIELD_BOOLEAN; \
|
||||
}
|
||||
|
||||
#define LESSON_VARIABLE_INIT_INFO_EHANDLE( _varEnum, _varName, _varType ) \
|
||||
void Init_##_varEnum##() \
|
||||
void Init_##_varEnum() \
|
||||
{ \
|
||||
iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \
|
||||
iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::_varName ); \
|
||||
varType = FIELD_EHANDLE; \
|
||||
}
|
||||
|
||||
#define LESSON_VARIABLE_INIT_INFO_STRING( _varEnum, _varName, _varType ) \
|
||||
void Init_##_varEnum##() \
|
||||
void Init_##_varEnum() \
|
||||
{ \
|
||||
iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::##_varName## ); \
|
||||
iOffset = offsetof( CScriptedIconLesson, CScriptedIconLesson::_varName ); \
|
||||
varType = FIELD_STRING; \
|
||||
}
|
||||
|
||||
@ -1094,15 +1095,15 @@ Vector CIconLesson::GetIconTargetPosition( C_BaseEntity *pIconTarget )
|
||||
|
||||
// Process the element action on this variable
|
||||
#define PROCESS_LESSON_ACTION( _varEnum, _varName, _varType ) \
|
||||
case LESSON_VARIABLE_##_varEnum##:\
|
||||
case LESSON_VARIABLE_##_varEnum:\
|
||||
return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, _varName, &pLessonElement->szParam, eventParam_float );
|
||||
|
||||
#define PROCESS_LESSON_ACTION_EHANDLE( _varEnum, _varName, _varType ) \
|
||||
case LESSON_VARIABLE_##_varEnum##:\
|
||||
case LESSON_VARIABLE_##_varEnum:\
|
||||
return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, _varName, &pLessonElement->szParam, eventParam_float, eventParam_BaseEntity, eventParam_string );
|
||||
|
||||
#define PROCESS_LESSON_ACTION_STRING( _varEnum, _varName, _varType ) \
|
||||
case LESSON_VARIABLE_##_varEnum##:\
|
||||
case LESSON_VARIABLE_##_varEnum:\
|
||||
return ProcessElementAction( pLessonElement->iAction, pLessonElement->bNot, #_varName, &_varName, &pLessonElement->szParam, eventParam_string );
|
||||
|
||||
// Init the variable from the script (or a convar)
|
||||
@ -2957,7 +2958,7 @@ bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const ch
|
||||
{
|
||||
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
|
||||
{
|
||||
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName, pchVarName );
|
||||
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName );
|
||||
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "... " );
|
||||
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() );
|
||||
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam );
|
||||
@ -2969,7 +2970,7 @@ bool CScriptedIconLesson::ProcessElementAction( int iAction, bool bNot, const ch
|
||||
|
||||
if ( gameinstructor_verbose.GetInt() > 0 && ShouldShowSpew() )
|
||||
{
|
||||
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName, pchVarName );
|
||||
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, "\t[%s]->HealthFraction() ", pchVarName );
|
||||
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f ", pVar->HealthFraction() );
|
||||
ConColorMsg( CBaseLesson::m_rgbaVerbosePlain, ( bNot ) ? ( ">= [%s] " ) : ( "< [%s] " ), pchParamName->String() );
|
||||
ConColorMsg( CBaseLesson::m_rgbaVerboseName, "%f\n", fParam );
|
||||
|
@ -426,7 +426,7 @@ private:
|
||||
LessonEvent_t * AddUpdateEvent( void );
|
||||
|
||||
private:
|
||||
static CUtlDict< int, int > CScriptedIconLesson::LessonActionMap;
|
||||
static CUtlDict< int, int > LessonActionMap;
|
||||
|
||||
EHANDLE m_hLocalPlayer;
|
||||
float m_fOutput;
|
||||
|
@ -25,6 +25,11 @@ ConVar cl_globallight_freeze( "cl_globallight_freeze", "0" );
|
||||
// You can set these as KV anyway.
|
||||
ConVar cl_globallight_xoffset( "cl_globallight_xoffset", "0" );
|
||||
ConVar cl_globallight_yoffset( "cl_globallight_yoffset", "0" );
|
||||
|
||||
static ConVar cl_globallight_slopescaledepthbias_shadowmap( "cl_globallight_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT );
|
||||
static ConVar cl_globallight_shadowfiltersize( "cl_globallight_shadowfiltersize", "0.1", FCVAR_CHEAT );
|
||||
static ConVar cl_globallight_depthbias_shadowmap( "cl_globallight_depthbias_shadowmap", "0.00001", FCVAR_CHEAT );
|
||||
static ConVar cl_globallight_depthres( "cl_globallight_depthres", "8192", FCVAR_CHEAT );
|
||||
#else
|
||||
ConVar cl_globallight_xoffset( "cl_globallight_xoffset", "-800" );
|
||||
ConVar cl_globallight_yoffset( "cl_globallight_yoffset", "1600" );
|
||||
@ -286,16 +291,21 @@ void C_GlobalLight::ClientThink()
|
||||
state.m_bOrtho = false;
|
||||
}
|
||||
|
||||
#ifndef MAPBASE // Don't draw that huge debug thing
|
||||
#ifdef MAPBASE
|
||||
//state.m_bDrawShadowFrustum = true; // Don't draw that huge debug thing
|
||||
state.m_flShadowMapResolution = cl_globallight_depthres.GetFloat();
|
||||
state.m_flShadowFilterSize = cl_globallight_shadowfiltersize.GetFloat();
|
||||
state.m_flShadowSlopeScaleDepthBias = cl_globallight_slopescaledepthbias_shadowmap.GetFloat();
|
||||
state.m_flShadowDepthBias = cl_globallight_depthbias_shadowmap.GetFloat();
|
||||
state.m_bEnableShadows = m_bEnableShadows;
|
||||
state.m_pSpotlightTexture = m_SpotlightTexture;
|
||||
state.m_nSpotlightTextureFrame = m_nSpotlightTextureFrame;
|
||||
#else
|
||||
state.m_bDrawShadowFrustum = true;
|
||||
#endif
|
||||
/*state.m_flShadowSlopeScaleDepthBias = g_pMaterialSystemHardwareConfig->GetShadowSlopeScaleDepthBias();;
|
||||
state.m_flShadowDepthBias = g_pMaterialSystemHardwareConfig->GetShadowDepthBias();*/
|
||||
state.m_bEnableShadows = m_bEnableShadows;
|
||||
state.m_pSpotlightTexture = m_SpotlightTexture;
|
||||
#ifdef MAPBASE
|
||||
state.m_nSpotlightTextureFrame = m_nSpotlightTextureFrame;
|
||||
#else
|
||||
state.m_nSpotlightTextureFrame = 0;
|
||||
#endif
|
||||
|
||||
|
@ -25,8 +25,8 @@
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#ifdef ASW_PROJECTED_TEXTURES
|
||||
static ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT );
|
||||
static ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "0.00001", FCVAR_CHEAT );
|
||||
extern ConVarRef mat_slopescaledepthbias_shadowmap;
|
||||
extern ConVarRef mat_depthbias_shadowmap;
|
||||
|
||||
float C_EnvProjectedTexture::m_flVisibleBBoxMinHeight = -FLT_MAX;
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
|
||||
#include "GameEventListener.h"
|
||||
#include "vgui_controls/phandle.h"
|
||||
#include "vgui_controls/PHandle.h"
|
||||
|
||||
class CBaseLesson;
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
IMPLEMENT_CLIENTCLASS_DT( C_MovieDisplay, DT_MovieDisplay, CMovieDisplay )
|
||||
RecvPropBool( RECVINFO( m_bEnabled ) ),
|
||||
RecvPropBool( RECVINFO( m_bLooping ) ),
|
||||
RecvPropBool( RECVINFO( m_bMuted ) ),
|
||||
RecvPropString( RECVINFO( m_szMovieFilename ) ),
|
||||
RecvPropString( RECVINFO( m_szGroupName ) ),
|
||||
END_RECV_TABLE()
|
||||
|
@ -20,6 +20,7 @@ public:
|
||||
|
||||
bool IsEnabled( void ) const { return m_bEnabled; }
|
||||
bool IsLooping( void ) const { return m_bLooping; }
|
||||
bool IsMuted(void) const { return m_bMuted; }
|
||||
|
||||
const char *GetMovieFilename( void ) const { return m_szMovieFilename; }
|
||||
const char *GetGroupName( void ) const { return m_szGroupName; }
|
||||
@ -27,6 +28,7 @@ public:
|
||||
private:
|
||||
bool m_bEnabled;
|
||||
bool m_bLooping;
|
||||
bool m_bMuted;
|
||||
char m_szMovieFilename[128];
|
||||
char m_szGroupName[128];
|
||||
};
|
||||
|
@ -73,6 +73,27 @@ IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_RopeKeyframe, DT_RopeKeyframe, CRopeKeyframe
|
||||
RecvPropInt( RECVINFO( m_iParentAttachment ) ),
|
||||
END_RECV_TABLE()
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
BEGIN_ENT_SCRIPTDESC( C_RopeKeyframe, C_BaseEntity, "The clientside class of move_rope and keyframe_rope" )
|
||||
DEFINE_SCRIPTFUNC( GetNodePosition, "Gets the position of the specified node index" )
|
||||
DEFINE_SCRIPTFUNC( GetNumNodes, "Gets the number of nodes available" )
|
||||
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptGetStartEntity, "GetStartEntity", "Gets the rope's start entity" )
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptGetEndEntity, "GetEndEntity", "Gets the rope's end entity" )
|
||||
|
||||
DEFINE_SCRIPTFUNC( SetupHangDistance, "Sets the rope's hang distance" )
|
||||
DEFINE_SCRIPTFUNC( SetSlack, "Sets the rope's slack value (extra length)" )
|
||||
DEFINE_SCRIPTFUNC( GetRopeFlags, "Gets the rope's flags" )
|
||||
DEFINE_SCRIPTFUNC( SetRopeFlags, "Sets the rope's flags" )
|
||||
|
||||
DEFINE_SCRIPTFUNC( SetColorMod, "Sets the rope's color mod value" )
|
||||
|
||||
DEFINE_SCRIPTFUNC( ShakeRope, "Shakes the rope with the specified center, radius, and magnitude" )
|
||||
|
||||
DEFINE_SCRIPTFUNC( AnyPointsMoved, "Returns true if any points have moved recently" )
|
||||
END_SCRIPTDESC();
|
||||
#endif
|
||||
|
||||
#define ROPE_IMPULSE_SCALE 20
|
||||
#define ROPE_IMPULSE_DECAY 0.95
|
||||
|
||||
@ -2022,6 +2043,25 @@ bool C_RopeKeyframe::GetAttachment( int number, Vector &origin, QAngle &angles )
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef MAPBASE
|
||||
const Vector &C_RopeKeyframe::GetNodePosition( int index )
|
||||
{
|
||||
int nNodes = m_RopePhysics.NumNodes();
|
||||
if ( index >= nNodes || nNodes < 2 )
|
||||
{
|
||||
Warning( "C_RopeKeyframe::GetNodePosition(): Invalid node index %i (number of nodes is %i)\n", index, nNodes );
|
||||
return vec3_origin;
|
||||
}
|
||||
|
||||
return m_RopePhysics.GetNode( index )->m_vPredicted;
|
||||
}
|
||||
|
||||
int C_RopeKeyframe::GetNumNodes()
|
||||
{
|
||||
return m_RopePhysics.NumNodes();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool C_RopeKeyframe::AnyPointsMoved()
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
|
@ -33,6 +33,9 @@ public:
|
||||
DECLARE_CLASS( C_RopeKeyframe, C_BaseEntity );
|
||||
DECLARE_CLIENTCLASS();
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
DECLARE_ENT_SCRIPTDESC();
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
@ -142,6 +145,11 @@ public:
|
||||
virtual bool GetAttachment( int number, Vector &origin );
|
||||
virtual bool GetAttachmentVelocity( int number, Vector &originVel, Quaternion &angleVel );
|
||||
|
||||
#ifdef MAPBASE
|
||||
const Vector &GetNodePosition( int index );
|
||||
int GetNumNodes();
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
void FinishInit( const char *pMaterialName );
|
||||
@ -166,6 +174,11 @@ private:
|
||||
void ReceiveMessage( int classID, bf_read &msg );
|
||||
bool CalculateEndPointAttachment( C_BaseEntity *pEnt, int iAttachment, Vector &vPos, QAngle *pAngles );
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
HSCRIPT ScriptGetStartEntity() { return ToHScript( GetStartEntity() ); }
|
||||
HSCRIPT ScriptGetEndEntity() { return ToHScript( GetEndEntity() ); }
|
||||
#endif
|
||||
|
||||
|
||||
private:
|
||||
// Track which links touched something last frame. Used to prevent wind from gusting on them.
|
||||
|
@ -166,7 +166,6 @@ inline bool FStrEq(const char *sz1, const char *sz2)
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
// V_stricmp() already checks if the pointers are equal, so having a comparison here is pointless.
|
||||
// I had few reasons to do this, but maybe you'll thank me later.
|
||||
return ( V_stricmp(sz1, sz2) == 0 );
|
||||
#else
|
||||
return ( sz1 == sz2 || V_stricmp(sz1, sz2) == 0 );
|
||||
|
@ -536,7 +536,6 @@ $Project
|
||||
"$SRCDIR\public\dt_utlvector_recv.cpp" \
|
||||
"$SRCDIR\public\filesystem_helpers.cpp" \
|
||||
"$SRCDIR\public\interpolatortypes.cpp" \
|
||||
"$SRCDIR\game\shared\interval.cpp" \
|
||||
"$SRCDIR\common\language.cpp" \
|
||||
"$SRCDIR\public\networkvar.cpp" \
|
||||
"$SRCDIR\common\randoverride.cpp" \
|
||||
@ -1107,6 +1106,7 @@ $Project
|
||||
$File "$SRCDIR\public\vgui_controls\WizardSubPanel.h"
|
||||
$File "$SRCDIR\public\worldsize.h"
|
||||
$File "$SRCDIR\public\zip_uncompressed.h"
|
||||
$File "$SRCDIR\public\tier1\interval.h"
|
||||
//Haptics
|
||||
$File "$SRCDIR\public\haptics\ihaptics.h" [$WIN32]
|
||||
$File "$SRCDIR\public\haptics\haptic_utils.h" [$WIN32]
|
||||
@ -1163,7 +1163,6 @@ $Project
|
||||
$File "$SRCDIR\game\shared\igamesystem.h"
|
||||
$File "$SRCDIR\game\shared\imovehelper.h"
|
||||
$File "$SRCDIR\game\shared\in_buttons.h"
|
||||
$File "$SRCDIR\game\shared\interval.h"
|
||||
$File "$SRCDIR\game\shared\iplayeranimstate.h"
|
||||
$File "$SRCDIR\game\shared\ipredictionsystem.h"
|
||||
$File "$SRCDIR\game\shared\itempents.h"
|
||||
|
@ -12,6 +12,7 @@ $Configuration
|
||||
|
||||
$PreprocessorDefinitions "$BASE;MAPBASE_RPC;DISCORD_RPC;STEAM_RPC" [$MAPBASE_RPC]
|
||||
$PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT]
|
||||
$PreprocessorDefinitions "$BASE;NEW_RESPONSE_SYSTEM" [$NEW_RESPONSE_SYSTEM]
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,6 +35,7 @@ $Project
|
||||
$File "c_movie_display.cpp"
|
||||
$File "c_movie_display.h"
|
||||
$File "vgui_movie_display.cpp"
|
||||
$File "convarproxy.cpp"
|
||||
|
||||
$Folder "Mapbase"
|
||||
{
|
||||
|
@ -1259,7 +1259,7 @@ void ClientModeShared::FireGameEvent( IGameEvent *event )
|
||||
}
|
||||
}
|
||||
|
||||
if ( team == 0 && GetLocalTeam() > 0 )
|
||||
if ( team == 0 && GetLocalTeam() )
|
||||
{
|
||||
bValidTeam = false;
|
||||
}
|
||||
|
@ -125,6 +125,11 @@ ConVar r_threaded_client_shadow_manager( "r_threaded_client_shadow_manager", "1"
|
||||
ConVar r_threaded_client_shadow_manager( "r_threaded_client_shadow_manager", "0" );
|
||||
#endif
|
||||
|
||||
#ifdef MAPBASE
|
||||
ConVarRef mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap" );
|
||||
ConVarRef mat_depthbias_shadowmap( "mat_depthbias_shadowmap" );
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning( disable: 4701 )
|
||||
#endif
|
||||
@ -1424,6 +1429,15 @@ bool CClientShadowMgr::Init()
|
||||
|
||||
materials->AddRestoreFunc( ShadowRestoreFunc );
|
||||
|
||||
#ifdef MAPBASE
|
||||
// These need to be referenced here since the cvars don't exist in the initial declaration
|
||||
mat_slopescaledepthbias_shadowmap = ConVarRef( "mat_slopescaledepthbias_shadowmap" );
|
||||
mat_depthbias_shadowmap = ConVarRef( "mat_depthbias_shadowmap" );
|
||||
|
||||
mat_slopescaledepthbias_shadowmap.SetValue( "16" ); // Would do something like 2 here, but it causes citizens to look weird under flashlights
|
||||
mat_depthbias_shadowmap.SetValue( "0.00005" );
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
113
sp/src/game/client/convarproxy.cpp
Normal file
113
sp/src/game/client/convarproxy.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Material proxy to stuff a convar into a material var.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
// identifier was truncated to '255' characters in the debug information
|
||||
//#pragma warning(disable: 4786)
|
||||
|
||||
#include "convar.h"
|
||||
#include "MaterialSystem/imaterialproxy.h"
|
||||
#include "materialsystem/IMaterialVar.h"
|
||||
//#include "imaterialproxydict.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
class CConVarMaterialProxy: public IMaterialProxy
|
||||
{
|
||||
public:
|
||||
CConVarMaterialProxy()
|
||||
: m_pResult( NULL ),
|
||||
m_conVarRef( "", true )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~CConVarMaterialProxy()
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues )
|
||||
{
|
||||
const char *pResult = pKeyValues->GetString( "resultVar" );
|
||||
if ( !pResult )
|
||||
return false;
|
||||
|
||||
bool found;
|
||||
m_pResult = pMaterial->FindVar( pResult, &found );
|
||||
if ( !found )
|
||||
{
|
||||
m_pResult = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
if ( !Q_stricmp( pResult, "$alpha" ) )
|
||||
{
|
||||
pMaterial->SetMaterialVarFlag( MATERIAL_VAR_ALPHA_MODIFIED_BY_PROXY, true );
|
||||
}
|
||||
*/
|
||||
|
||||
pResult = pKeyValues->GetString( "convar" );
|
||||
if( !pResult )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_conVarRef.Init( pResult, false );
|
||||
if ( !m_conVarRef.IsValid() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void OnBind( void* )
|
||||
{
|
||||
switch( m_pResult->GetType() )
|
||||
{
|
||||
case MATERIAL_VAR_TYPE_VECTOR:
|
||||
{
|
||||
float f = m_conVarRef.GetFloat();
|
||||
Vector4D vec( f, f, f, f );
|
||||
m_pResult->SetVecValue( vec.Base(), m_pResult->VectorSize() );
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef MAPBASE
|
||||
case MATERIAL_VAR_TYPE_STRING:
|
||||
m_pResult->SetStringValue( m_conVarRef.GetString() );
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MATERIAL_VAR_TYPE_INT:
|
||||
m_pResult->SetIntValue( m_conVarRef.GetInt() );
|
||||
break;
|
||||
|
||||
case MATERIAL_VAR_TYPE_FLOAT:
|
||||
default:
|
||||
m_pResult->SetFloatValue( m_conVarRef.GetFloat() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual IMaterial *GetMaterial()
|
||||
{
|
||||
return m_pResult->GetOwningMaterial();
|
||||
}
|
||||
|
||||
virtual void Release()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
IMaterialVar *m_pResult;
|
||||
ConVarRef m_conVarRef;
|
||||
};
|
||||
|
||||
EXPOSE_INTERFACE( CConVarMaterialProxy, IMaterialProxy, "ConVar" IMATERIAL_PROXY_INTERFACE_VERSION );
|
@ -5,6 +5,10 @@
|
||||
//=============================================================================
|
||||
|
||||
#include "cbase.h"
|
||||
#ifdef MAPBASE
|
||||
#include "proxyentity.h"
|
||||
#include "materialsystem/imaterialvar.h"
|
||||
#endif
|
||||
|
||||
class C_PropScalable : public C_BaseAnimating
|
||||
{
|
||||
@ -194,3 +198,56 @@ void C_PropScalable::GetRenderBounds( Vector &theMins, Vector &theMaxs )
|
||||
Assert( theMins.IsValid() && theMaxs.IsValid() );
|
||||
|
||||
}
|
||||
|
||||
#ifdef MAPBASE
|
||||
ConVar r_coreball_update_sphere_center( "r_coreball_update_sphere_center", "1", FCVAR_NONE, "Allows prop_coreball to update its center to the entity's origin" );
|
||||
|
||||
class CCoreBallUpdateMaterialProxy : public CEntityMaterialProxy
|
||||
{
|
||||
public:
|
||||
CCoreBallUpdateMaterialProxy()
|
||||
{
|
||||
m_pMaterial = NULL;
|
||||
m_pSphereCenter = NULL;
|
||||
}
|
||||
virtual ~CCoreBallUpdateMaterialProxy()
|
||||
{
|
||||
}
|
||||
virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues )
|
||||
{
|
||||
m_pMaterial = pMaterial;
|
||||
bool found;
|
||||
m_pSphereCenter = m_pMaterial->FindVar( "$spherecenter", &found );
|
||||
if( !found )
|
||||
{
|
||||
m_pSphereCenter = NULL;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
virtual void OnBind( C_BaseEntity *pC_BaseEntity )
|
||||
{
|
||||
if (r_coreball_update_sphere_center.GetBool())
|
||||
{
|
||||
const Vector &origin = pC_BaseEntity->GetAbsOrigin();
|
||||
m_pSphereCenter->SetVecValue( origin.x, origin.y, origin.z );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just continuously bind the old hacked value (TODO: Optimize so it's not just assigning the same value constantly?)
|
||||
m_pSphereCenter->SetVecValue( 2688.0, 12139.0, 5170.0 );
|
||||
}
|
||||
}
|
||||
|
||||
virtual IMaterial *GetMaterial()
|
||||
{
|
||||
return m_pMaterial;
|
||||
}
|
||||
|
||||
protected:
|
||||
IMaterial *m_pMaterial;
|
||||
IMaterialVar *m_pSphereCenter;
|
||||
};
|
||||
|
||||
EXPOSE_INTERFACE( CCoreBallUpdateMaterialProxy, IMaterialProxy, "CoreBallUpdate" IMATERIAL_PROXY_INTERFACE_VERSION );
|
||||
#endif
|
||||
|
@ -52,8 +52,8 @@ static ConVar r_flashlightladderdist( "r_flashlightladderdist", "40.0", FCVAR_CH
|
||||
static ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT );
|
||||
static ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "0.0005", FCVAR_CHEAT );
|
||||
#else
|
||||
static ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "4", FCVAR_CHEAT );
|
||||
static ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "0.00001", FCVAR_CHEAT );
|
||||
extern ConVarRef mat_slopescaledepthbias_shadowmap;
|
||||
extern ConVarRef mat_depthbias_shadowmap;
|
||||
#endif
|
||||
#ifdef MAPBASE
|
||||
static ConVar r_flashlighttextureoverride( "r_flashlighttextureoverride", "", FCVAR_CHEAT );
|
||||
|
@ -150,6 +150,10 @@ private:
|
||||
static const int ENTRY_IN_USE = -2;
|
||||
};
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
// For unregistration boundary check
|
||||
public:
|
||||
#endif
|
||||
CUtlVector< GlowObjectDefinition_t > m_GlowObjectDefinitions;
|
||||
int m_nFirstFreeSlot;
|
||||
};
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "iclientmode.h"
|
||||
#include <vgui/ILocalize.h>
|
||||
#include <vgui/ISurface.h>
|
||||
#include <vgui/IVGUI.h>
|
||||
#include <vgui/IVGui.h>
|
||||
#include <vgui_controls/EditablePanel.h>
|
||||
#include <vgui_controls/Controls.h>
|
||||
#include <vgui_controls/Label.h>
|
||||
|
@ -34,7 +34,7 @@
|
||||
#define LOCATOR_ICON_FX_FADE_OUT 0x00000800 // Set when deactivated so it can smoothly vanish
|
||||
#define LOCATOR_ICON_FX_FADE_IN 0x00001000 // Set when activated so it can smoothly appear
|
||||
|
||||
#include "tier1/UtlSymbol.h"
|
||||
#include "tier1/utlsymbol.h"
|
||||
|
||||
// See comments in UtlSymbol on why this is useful
|
||||
DECLARE_PRIVATE_SYMBOLTYPE( CGameInstructorSymbol );
|
||||
|
@ -69,6 +69,16 @@ BEGIN_SIMPLE_DATADESC( CRagdoll )
|
||||
DEFINE_RAGDOLL_ELEMENT( 21 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 22 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 23 ),
|
||||
#ifdef MAPBASE
|
||||
DEFINE_RAGDOLL_ELEMENT( 24 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 25 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 26 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 27 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 28 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 29 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 30 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 31 ),
|
||||
#endif
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
@ -7,16 +7,16 @@
|
||||
#include "cbase.h"
|
||||
#include "c_vguiscreen.h"
|
||||
#include "vgui_controls/Label.h"
|
||||
#include "vgui_BitmapPanel.h"
|
||||
#include <vgui/IVGUI.h>
|
||||
#include "vgui_bitmappanel.h"
|
||||
#include <vgui/IVGui.h>
|
||||
#include "c_slideshow_display.h"
|
||||
#include "ienginevgui.h"
|
||||
#include "fmtstr.h"
|
||||
#include "vgui_controls/ImagePanel.h"
|
||||
#include <vgui/ISurface.h>
|
||||
#include "video/ivideoservices.h"
|
||||
#include "engine/ienginesound.h"
|
||||
#include "VGUIMatSurface/IMatSystemSurface.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "VGuiMatSurface/IMatSystemSurface.h"
|
||||
#include "c_movie_display.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
@ -81,7 +81,6 @@ private:
|
||||
bool m_bBlackBackground;
|
||||
bool m_bSlaved;
|
||||
bool m_bInitialized;
|
||||
|
||||
bool m_bLastActiveState; // HACK: I'd rather get a real callback...
|
||||
|
||||
// VGUI specifics
|
||||
@ -110,10 +109,9 @@ CMovieDisplayScreen::CMovieDisplayScreen( vgui::Panel *parent, const char *panel
|
||||
m_bBlackBackground = true;
|
||||
m_bSlaved = false;
|
||||
m_bInitialized = false;
|
||||
|
||||
// Add ourselves to the global list of movie displays
|
||||
g_MovieDisplays.AddToTail( this );
|
||||
|
||||
//m_VideoMaterial->SetMuted(true);
|
||||
m_bLastActiveState = IsActive();
|
||||
}
|
||||
|
||||
@ -295,6 +293,11 @@ void CMovieDisplayScreen::UpdateMovie( void )
|
||||
// OnVideoOver();
|
||||
// StopPlayback();
|
||||
}
|
||||
|
||||
if (!m_hScreenEntity->IsMuted())
|
||||
{
|
||||
m_VideoMaterial->SetMuted(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,7 +371,7 @@ bool CMovieDisplayScreen::BeginPlayback( const char *pFilename )
|
||||
Q_strncpy( szMaterialName, pFilename, sizeof(szMaterialName) );
|
||||
}
|
||||
|
||||
const char *pszMaterialName = CFmtStrN<128>( "VideoMaterial_", m_hScreenEntity->GetEntityName() );
|
||||
const char *pszMaterialName = CFmtStrN<128>( "VideoMaterial_%s", m_hScreenEntity->GetEntityName() );
|
||||
m_VideoMaterial = g_pVideo->CreateVideoMaterial( pszMaterialName, pFilename, "GAME",
|
||||
VideoPlaybackFlags::DEFAULT_MATERIAL_OPTIONS,
|
||||
VideoSystem::DETERMINE_FROM_FILE_EXTENSION/*, m_bAllowAlternateMedia*/ );
|
||||
@ -376,14 +379,17 @@ bool CMovieDisplayScreen::BeginPlayback( const char *pFilename )
|
||||
if ( m_VideoMaterial == NULL )
|
||||
return false;
|
||||
|
||||
m_VideoMaterial->SetMuted( true ); // FIXME: Allow?
|
||||
|
||||
|
||||
m_VideoMaterial->SetMuted(true); // FIXME: Allow?
|
||||
|
||||
|
||||
if ( m_hScreenEntity->IsLooping() )
|
||||
{
|
||||
m_VideoMaterial->SetLooping( true );
|
||||
}
|
||||
|
||||
if ( m_VideoMaterial->HasAudio() )
|
||||
if ( m_VideoMaterial->HasAudio())
|
||||
{
|
||||
// We want to be the sole audio source
|
||||
enginesound->NotifyBeginMoviePlayback();
|
||||
|
@ -165,6 +165,8 @@ public:
|
||||
void SetVarFloat( int i, float value );
|
||||
void SetVarVector( int i, const Vector &value );
|
||||
|
||||
const char *GetVarName( int i );
|
||||
|
||||
private:
|
||||
IMaterialVar *m_MaterialVars[SCRIPT_MAT_PROXY_MAX_VARS];
|
||||
|
||||
@ -194,12 +196,19 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptMaterialProxy, "CScriptMaterialProxy", "Mate
|
||||
DEFINE_SCRIPTFUNC( SetVarInt, "Sets a material var's int value" )
|
||||
DEFINE_SCRIPTFUNC( SetVarFloat, "Sets a material var's float value" )
|
||||
DEFINE_SCRIPTFUNC( SetVarVector, "Sets a material var's vector value" )
|
||||
|
||||
DEFINE_SCRIPTFUNC( GetVarName, "Gets a material var's name" )
|
||||
END_SCRIPTDESC();
|
||||
|
||||
CScriptMaterialProxy::CScriptMaterialProxy()
|
||||
{
|
||||
m_hScriptInstance = NULL;
|
||||
m_hFuncOnBind = NULL;
|
||||
|
||||
for (int i = 0; i < SCRIPT_MAT_PROXY_MAX_VARS; i++)
|
||||
{
|
||||
m_MaterialVars[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CScriptMaterialProxy::~CScriptMaterialProxy()
|
||||
@ -316,18 +325,20 @@ void CScriptMaterialProxy::TermScript()
|
||||
|
||||
void CScriptMaterialProxy::OnBind( void *pRenderable )
|
||||
{
|
||||
if( !pRenderable )
|
||||
return;
|
||||
|
||||
if (m_hFuncOnBind != NULL)
|
||||
{
|
||||
IClientRenderable *pRend = ( IClientRenderable* )pRenderable;
|
||||
C_BaseEntity *pEnt = pRend->GetIClientUnknown()->GetBaseEntity();
|
||||
if ( pEnt )
|
||||
C_BaseEntity *pEnt = NULL;
|
||||
if (pRenderable)
|
||||
{
|
||||
g_pScriptVM->SetValue( m_ScriptScope, "entity", pEnt->GetScriptInstance() );
|
||||
IClientRenderable *pRend = (IClientRenderable*)pRenderable;
|
||||
pEnt = pRend->GetIClientUnknown()->GetBaseEntity();
|
||||
if ( pEnt )
|
||||
{
|
||||
g_pScriptVM->SetValue( m_ScriptScope, "entity", pEnt->GetScriptInstance() );
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (!pEnt)
|
||||
{
|
||||
// Needs to register as a null value so the script doesn't break if it looks for an entity
|
||||
g_pScriptVM->SetValue( m_ScriptScope, "entity", SCRIPT_VARIANT_NULL );
|
||||
@ -414,6 +425,14 @@ void CScriptMaterialProxy::SetVarVector( int i, const Vector &value )
|
||||
return m_MaterialVars[i]->SetVecValue( value.Base(), 3 );
|
||||
}
|
||||
|
||||
const char *CScriptMaterialProxy::GetVarName( int i )
|
||||
{
|
||||
if (!ValidateIndex( i ) || !m_MaterialVars[i])
|
||||
return NULL;
|
||||
|
||||
return m_MaterialVars[i]->GetName();
|
||||
}
|
||||
|
||||
EXPOSE_INTERFACE( CScriptMaterialProxy, IMaterialProxy, "VScriptProxy" IMATERIAL_PROXY_INTERFACE_VERSION );
|
||||
|
||||
bool CMaterialProxyScriptInstanceHelper::ToString( void *p, char *pBuf, int bufSize )
|
||||
|
@ -24,7 +24,13 @@ function IncludeScript( name, scope = null )
|
||||
|
||||
function DispatchParticleEffect( particleName, origin, angles, entity = null )
|
||||
{
|
||||
DoDispatchParticleEffect( particleName, origin, angles, entity );
|
||||
return DoDispatchParticleEffect( particleName, origin, angles, entity );
|
||||
}
|
||||
|
||||
function ImpulseScale( flTargetMass, flDesiredSpeed )
|
||||
{
|
||||
return flTargetMass * flDesiredSpeed;
|
||||
}
|
||||
__Documentation.RegisterHelp( "ImpulseScale", "float ImpulseScale(float, float)", "Returns an impulse scale required to push an object." );
|
||||
|
||||
)vscript";
|
@ -27,6 +27,10 @@
|
||||
|
||||
static IVEngineServer *g_pEngineServer = NULL;
|
||||
|
||||
#ifdef MAPBASE
|
||||
ConVar cl_worldlight_use_new_method("cl_worldlight_use_new_method", "1", FCVAR_NONE, "Uses the new world light iteration method which splits lights into multiple lists for each cluster.");
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Singleton exposure
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -192,6 +196,83 @@ void CWorldLights::LevelInitPreEntity()
|
||||
g_pFullFileSystem->Close(hFile);
|
||||
|
||||
DevMsg("CWorldLights: load successful (%d lights at 0x%p)\n", m_nWorldLights, m_pWorldLights);
|
||||
|
||||
#ifdef MAPBASE
|
||||
// Now that the lights have been gathered, begin separating them into lists for each PVS cluster.
|
||||
// This code is adapted from the soundscape cluster list code (see soundscape_system.cpp) and is intended to
|
||||
// reduce frame drops in large maps which use dynamic RTT shadow angles.
|
||||
CUtlVector<bbox_t> clusterbounds;
|
||||
int clusterCount = g_pEngineServer->GetClusterCount();
|
||||
clusterbounds.SetCount( clusterCount );
|
||||
g_pEngineServer->GetAllClusterBounds( clusterbounds.Base(), clusterCount );
|
||||
m_WorldLightsInCluster.SetCount(clusterCount);
|
||||
for ( int i = 0; i < clusterCount; i++ )
|
||||
{
|
||||
m_WorldLightsInCluster[i].lightCount = 0;
|
||||
m_WorldLightsInCluster[i].firstLight = 0;
|
||||
}
|
||||
unsigned char myPVS[16 * 1024];
|
||||
CUtlVector<short> clusterIndexList;
|
||||
CUtlVector<short> lightIndexList;
|
||||
|
||||
// Find the clusters visible from each light, then add it to those clusters' light lists
|
||||
// (Also try to clip for radius if possible)
|
||||
for (int i = 0; i < m_nWorldLights; ++i)
|
||||
{
|
||||
dworldlight_t *light = &m_pWorldLights[i];
|
||||
|
||||
// Assign the sun to its own pointer
|
||||
if (light->type == emit_skylight)
|
||||
{
|
||||
m_iSunIndex = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
float radiusSq = light->radius * light->radius;
|
||||
if (radiusSq == 0.0f)
|
||||
{
|
||||
// TODO: Use intensity instead?
|
||||
radiusSq = FLT_MAX;
|
||||
}
|
||||
|
||||
g_pEngineServer->GetPVSForCluster( light->cluster, sizeof( myPVS ), myPVS );
|
||||
for ( int j = 0; j < clusterCount; j++ )
|
||||
{
|
||||
if ( myPVS[ j >> 3 ] & (1<<(j&7)) )
|
||||
{
|
||||
float distSq = CalcSqrDistanceToAABB( clusterbounds[j].mins, clusterbounds[j].maxs, light->origin );
|
||||
if ( distSq < radiusSq )
|
||||
{
|
||||
m_WorldLightsInCluster[j].lightCount++;
|
||||
clusterIndexList.AddToTail(j);
|
||||
lightIndexList.AddToTail(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_WorldLightsIndexList.SetCount(lightIndexList.Count());
|
||||
|
||||
// Compute the starting index of each cluster
|
||||
int firstLight = 0;
|
||||
for ( int i = 0; i < clusterCount; i++ )
|
||||
{
|
||||
m_WorldLightsInCluster[i].firstLight = firstLight;
|
||||
firstLight += m_WorldLightsInCluster[i].lightCount;
|
||||
m_WorldLightsInCluster[i].lightCount = 0;
|
||||
}
|
||||
|
||||
// Now add each light index to the appropriate cluster's list
|
||||
for ( int i = 0; i < lightIndexList.Count(); i++ )
|
||||
{
|
||||
int cluster = clusterIndexList[i];
|
||||
int outIndex = m_WorldLightsInCluster[cluster].lightCount + m_WorldLightsInCluster[cluster].firstLight;
|
||||
m_WorldLightsInCluster[cluster].lightCount++;
|
||||
m_WorldLightsIndexList[outIndex] = lightIndexList[i];
|
||||
}
|
||||
|
||||
//DevMsg( "CWorldLights: Light clusters list has %i elements; Light index list has %i\n", m_WorldLightsInCluster.Count(), m_WorldLightsIndexList.Count() );
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -208,6 +289,25 @@ bool CWorldLights::GetBrightestLightSource(const Vector &vecPosition, Vector &ve
|
||||
|
||||
// Find the size of the PVS for our current position
|
||||
int nCluster = g_pEngineServer->GetClusterForOrigin(vecPosition);
|
||||
|
||||
#ifdef MAPBASE
|
||||
if (cl_worldlight_use_new_method.GetBool())
|
||||
{
|
||||
FindBrightestLightSourceNew( vecPosition, vecLightPos, vecLightBrightness, nCluster );
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
FindBrightestLightSourceOld( vecPosition, vecLightPos, vecLightBrightness, nCluster );
|
||||
}
|
||||
|
||||
//engine->Con_NPrintf(m_nWorldLights, "result: %d", !vecLightBrightness.IsZero());
|
||||
return !vecLightBrightness.IsZero();
|
||||
}
|
||||
|
||||
void CWorldLights::FindBrightestLightSourceOld( const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness, int nCluster )
|
||||
{
|
||||
// Find the size of the PVS for our current position
|
||||
int nPVSSize = g_pEngineServer->GetPVSForCluster(nCluster, 0, NULL);
|
||||
|
||||
// Get the PVS at our position
|
||||
@ -257,7 +357,7 @@ bool CWorldLights::GetBrightestLightSource(const Vector &vecPosition, Vector &ve
|
||||
|
||||
delete[] pvs;
|
||||
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate square distance to this worldlight
|
||||
@ -308,7 +408,87 @@ bool CWorldLights::GetBrightestLightSource(const Vector &vecPosition, Vector &ve
|
||||
}
|
||||
|
||||
delete[] pvs;
|
||||
}
|
||||
|
||||
//engine->Con_NPrintf(m_nWorldLights, "result: %d", !vecLightBrightness.IsZero());
|
||||
return !vecLightBrightness.IsZero();
|
||||
}
|
||||
#ifdef MAPBASE
|
||||
void CWorldLights::FindBrightestLightSourceNew( const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness, int nCluster )
|
||||
{
|
||||
// Handle sun
|
||||
if (m_iSunIndex != -1)
|
||||
{
|
||||
dworldlight_t *light = &m_pWorldLights[m_iSunIndex];
|
||||
|
||||
// Calculate sun position
|
||||
Vector vecAbsStart = vecPosition + Vector(0,0,30);
|
||||
Vector vecAbsEnd = vecAbsStart - (light->normal * MAX_TRACE_LENGTH);
|
||||
|
||||
trace_t tr;
|
||||
UTIL_TraceLine(vecPosition, vecAbsEnd, MASK_OPAQUE, NULL, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
// If we didn't hit anything then we have a problem
|
||||
if(tr.DidHit())
|
||||
{
|
||||
// If we did hit something, and it wasn't the skybox, then skip
|
||||
// this worldlight
|
||||
if((tr.surface.flags & SURF_SKY) && (tr.surface.flags & SURF_SKY2D))
|
||||
{
|
||||
// Act like we didn't find any valid worldlights, so the shadow
|
||||
// manager uses the default shadow direction instead (should be the
|
||||
// sun direction)
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through all the worldlights
|
||||
if ( nCluster >= 0 && nCluster < m_WorldLightsInCluster.Count() )
|
||||
{
|
||||
// find all soundscapes that could possibly attach to this player and update them
|
||||
for ( int j = 0; j < m_WorldLightsInCluster[nCluster].lightCount; j++ )
|
||||
{
|
||||
int ssIndex = m_WorldLightsIndexList[m_WorldLightsInCluster[nCluster].firstLight + j];
|
||||
dworldlight_t *light = &m_pWorldLights[ssIndex];
|
||||
|
||||
// Calculate square distance to this worldlight
|
||||
Vector vecDelta = light->origin - vecPosition;
|
||||
float flDistSqr = vecDelta.LengthSqr();
|
||||
float flRadiusSqr = light->radius * light->radius;
|
||||
|
||||
// Skip lights that are out of our radius
|
||||
if(flRadiusSqr > 0 && flDistSqr >= flRadiusSqr)
|
||||
{
|
||||
//engine->Con_NPrintf(i, "%d: out-of-radius (dist: %d, radius: %d)", i, sqrt(flDistSqr), light->radius);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate intensity at our position
|
||||
float flRatio = Engine_WorldLightDistanceFalloff(light, vecDelta);
|
||||
Vector vecIntensity = light->intensity * flRatio;
|
||||
|
||||
// Is this light more intense than the one we already found?
|
||||
if(vecIntensity.LengthSqr() <= vecLightBrightness.LengthSqr())
|
||||
{
|
||||
//engine->Con_NPrintf(i, "%d: too dim", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Can we see the light?
|
||||
trace_t tr;
|
||||
Vector vecAbsStart = vecPosition + Vector(0,0,30);
|
||||
UTIL_TraceLine(vecAbsStart, light->origin, MASK_OPAQUE, NULL, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
if(tr.DidHit())
|
||||
{
|
||||
//engine->Con_NPrintf(i, "%d: trace failed", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
vecLightPos = light->origin;
|
||||
vecLightBrightness = vecIntensity;
|
||||
|
||||
//engine->Con_NPrintf(i, "%d: set (%.2f)", i, vecIntensity.Length());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -27,7 +27,9 @@ public:
|
||||
// Find the brightest light source at a point
|
||||
//-------------------------------------------------------------------------
|
||||
bool GetBrightestLightSource(const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness);
|
||||
void FindBrightestLightSourceOld( const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness, int nCluster );
|
||||
#ifdef MAPBASE
|
||||
void FindBrightestLightSourceNew(const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness, int nCluster);
|
||||
bool GetCumulativeLightSource(const Vector &vecPosition, Vector &vecLightPos, float flMinBrightnessSqr);
|
||||
#endif
|
||||
|
||||
@ -42,6 +44,19 @@ private:
|
||||
|
||||
int m_nWorldLights;
|
||||
dworldlight_t *m_pWorldLights;
|
||||
|
||||
#ifdef MAPBASE
|
||||
int m_iSunIndex = -1; // The sun's personal index
|
||||
|
||||
struct clusterLightList_t
|
||||
{
|
||||
unsigned short lightCount;
|
||||
unsigned short firstLight;
|
||||
};
|
||||
|
||||
CUtlVector<clusterLightList_t> m_WorldLightsInCluster;
|
||||
CUtlVector<unsigned short> m_WorldLightsIndexList;
|
||||
#endif
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -4,6 +4,9 @@
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
#include "ai_criteria_new.h"
|
||||
#else
|
||||
#ifndef AI_CRITERIA_H
|
||||
#define AI_CRITERIA_H
|
||||
#ifdef _WIN32
|
||||
@ -276,3 +279,4 @@ private:
|
||||
};
|
||||
|
||||
#endif // AI_CRITERIA_H
|
||||
#endif
|
||||
|
@ -4,6 +4,9 @@
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
#include "ai_responsesystem_new.h"
|
||||
#else
|
||||
#ifndef AI_RESPONSESYSTEM_H
|
||||
#define AI_RESPONSESYSTEM_H
|
||||
|
||||
@ -39,3 +42,4 @@ class ISaveRestoreBlockHandler *GetDefaultResponseSystemSaveRestoreBlockHandler(
|
||||
class ISaveRestoreOps *GetResponseSystemSaveRestoreOps();
|
||||
|
||||
#endif // AI_RESPONSESYSTEM_H
|
||||
#endif
|
||||
|
@ -242,7 +242,12 @@ void CEntityFlame::FlameThink( void )
|
||||
}
|
||||
|
||||
CAI_BaseNPC *pNPC = m_hEntAttached->MyNPCPointer();
|
||||
#ifdef MAPBASE
|
||||
// Don't extingish if the NPC is still dying
|
||||
if ( pNPC && !pNPC->IsAlive() && pNPC->m_lifeState != LIFE_DYING )
|
||||
#else
|
||||
if ( pNPC && !pNPC->IsAlive() )
|
||||
#endif
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
// Notify the NPC that it's no longer burning!
|
||||
|
@ -2033,7 +2033,11 @@ bool CAI_BaseActor::UseSemaphore( void )
|
||||
|
||||
CAI_Expresser *CAI_BaseActor::CreateExpresser()
|
||||
{
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
m_pExpresser = new CAI_ExpresserWithFollowup(this);
|
||||
#else
|
||||
m_pExpresser = new CAI_Expresser(this);
|
||||
#endif
|
||||
return m_pExpresser;
|
||||
}
|
||||
|
||||
|
@ -97,6 +97,7 @@
|
||||
|
||||
#ifdef MAPBASE
|
||||
#include "mapbase/matchers.h"
|
||||
#include "items.h"
|
||||
#endif
|
||||
|
||||
#include "env_debughistory.h"
|
||||
@ -11509,17 +11510,9 @@ void CAI_BaseNPC::PickupItem( CBaseEntity *pItem )
|
||||
m_OnItemPickup.Set( pItem, pItem, this );
|
||||
|
||||
Assert( pItem != NULL );
|
||||
if( FClassnameIs( pItem, "item_healthkit" ) )
|
||||
if( FClassnameIs( pItem, "item_health*" ) ) // item_healthkit, item_healthvial, item_healthkit_custom, etc.
|
||||
{
|
||||
if ( TakeHealth( sk_healthkit.GetFloat(), DMG_GENERIC ) )
|
||||
{
|
||||
RemoveAllDecals();
|
||||
UTIL_Remove( pItem );
|
||||
}
|
||||
}
|
||||
else if( FClassnameIs( pItem, "item_healthvial" ) )
|
||||
{
|
||||
if ( TakeHealth( sk_healthvial.GetFloat(), DMG_GENERIC ) )
|
||||
if ( TakeHealth( static_cast<CItem*>(pItem)->GetItemAmount(), DMG_GENERIC ) )
|
||||
{
|
||||
RemoveAllDecals();
|
||||
UTIL_Remove( pItem );
|
||||
@ -15251,7 +15244,11 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void )
|
||||
const char *p = STRING(pInteraction->MiscCriteria);
|
||||
while ( p )
|
||||
{
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL, STRING(pInteraction->MiscCriteria) );
|
||||
#else
|
||||
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL );
|
||||
#endif
|
||||
|
||||
index = set.FindCriterionIndex(key);
|
||||
if (index != -1)
|
||||
|
@ -64,7 +64,9 @@ class CBaseGrenade;
|
||||
class CBaseDoor;
|
||||
class CBasePropDoor;
|
||||
struct AI_Waypoint_t;
|
||||
#ifndef NEW_RESPONSE_SYSTEM
|
||||
class AI_Response;
|
||||
#endif
|
||||
class CBaseFilter;
|
||||
|
||||
typedef CBitVec<MAX_CONDITIONS> CAI_ScheduleBits;
|
||||
@ -98,11 +100,6 @@ extern bool AIStrongOpt( void );
|
||||
#ifdef MAPBASE
|
||||
// Defines Mapbase's extended NPC response system usage.
|
||||
#define EXPANDED_RESPONSE_SYSTEM_USAGE
|
||||
|
||||
// Use the model keyvalue if it is defined
|
||||
#define DefaultOrCustomModel(defaultModel) GetModelName() != NULL_STRING ? STRING(GetModelName()) : defaultModel
|
||||
#else
|
||||
#define DefaultOrCustomModel() defaultModel
|
||||
#endif
|
||||
|
||||
#ifdef EXPANDED_RESPONSE_SYSTEM_USAGE
|
||||
@ -670,6 +667,7 @@ public:
|
||||
|
||||
virtual bool ShouldAlwaysThink();
|
||||
void ForceGatherConditions() { m_bForceConditionsGather = true; SetEfficiency( AIE_NORMAL ); } // Force an NPC out of PVS to call GatherConditions on next think
|
||||
bool IsForceGatherConditionsSet() { return m_bForceConditionsGather; }
|
||||
|
||||
virtual float LineOfSightDist( const Vector &vecDir = vec3_invalid, float zEye = FLT_MAX );
|
||||
|
||||
@ -963,7 +961,7 @@ public:
|
||||
void RemoveSleepFlags( int flags ) { m_SleepFlags &= ~flags; }
|
||||
bool HasSleepFlags( int flags ) { return (m_SleepFlags & flags) == flags; }
|
||||
|
||||
void UpdateSleepState( bool bInPVS );
|
||||
virtual void UpdateSleepState( bool bInPVS );
|
||||
virtual void Wake( bool bFireOutput = true );
|
||||
#ifdef MAPBASE
|
||||
// A version of Wake() that takes an activator
|
||||
|
@ -3979,10 +3979,16 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask )
|
||||
// If we have an entry, we have to play it first
|
||||
if ( m_hCine->m_iszEntry != NULL_STRING )
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
m_hCine->OnEntrySequence( this );
|
||||
#endif
|
||||
m_hCine->StartSequence( (CAI_BaseNPC *)this, m_hCine->m_iszEntry, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
m_hCine->OnActionSequence( this );
|
||||
#endif
|
||||
m_hCine->StartSequence( (CAI_BaseNPC *)this, m_hCine->m_iszPlay, true );
|
||||
}
|
||||
|
||||
|
@ -9,12 +9,19 @@
|
||||
|
||||
#include "simtimer.h"
|
||||
#include "ai_behavior.h"
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
#include "ai_speechconcept.h"
|
||||
#endif
|
||||
|
||||
#if defined( _WIN32 )
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
typedef CAI_Concept AIConcept_t;
|
||||
#else
|
||||
typedef const char *AIConcept_t;
|
||||
#endif
|
||||
|
||||
// Speak concepts
|
||||
#define TLK_LEAD_START "TLK_LEAD_START"
|
||||
|
509
sp/src/game/server/ai_expresserfollowup.cpp
Normal file
509
sp/src/game/server/ai_expresserfollowup.cpp
Normal file
@ -0,0 +1,509 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "ai_speech.h"
|
||||
|
||||
#include "game.h"
|
||||
#include "eventqueue.h"
|
||||
#include "ai_basenpc.h"
|
||||
#include "basemultiplayerplayer.h"
|
||||
#include "ai_baseactor.h"
|
||||
#include "sceneentity.h"
|
||||
//#include "flex_expresser.h"
|
||||
/*
|
||||
#include "engine/ienginesound.h"
|
||||
#include "keyvalues.h"
|
||||
#include "ai_criteria.h"
|
||||
#include "isaverestore.h"
|
||||
#include "sceneentity.h"
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include <tier0/memdbgon.h>
|
||||
|
||||
static const char *GetResponseName( CBaseEntity *pEnt )
|
||||
{
|
||||
Assert( pEnt );
|
||||
if ( pEnt == NULL )
|
||||
return "";
|
||||
return STRING( pEnt->GetEntityName() );
|
||||
}
|
||||
|
||||
// This is a tiny helper function for below -- what I'd use a lambda for, usually
|
||||
static void DispatchComeback( CAI_ExpresserWithFollowup *pExpress, CBaseEntity *pSpeaker, CBaseEntity *pRespondent, AI_ResponseFollowup &followup )
|
||||
{
|
||||
AssertMsg(pSpeaker != NULL, "Response expressor somehow got called with a NULL Outer.\n");
|
||||
if ( !pRespondent )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float delay = followup.followup_delay;
|
||||
if (pSpeaker == pRespondent && delay < 0)
|
||||
{
|
||||
Warning("Response rule with a 'self' target specified negative delay, which isn't legal because that would make someone talk over himself.");
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
// Msg( "%s: Dispatch comeback about %s to %s\n", pSpeaker->GetBotString(), g_pConceptManager->GetTopicName( handle ), pRespondent->GetBotString() );
|
||||
|
||||
// build an input event that we will use to force the bot to talk through the IO system
|
||||
variant_t value;
|
||||
// Don't send along null contexts
|
||||
if (followup.followup_contexts && followup.followup_contexts[0] != '\0')
|
||||
{
|
||||
value.SetString( MAKE_STRING( followup.followup_contexts ) );
|
||||
g_EventQueue.AddEvent( pRespondent, "AddContext", value, delay - 0.01, pSpeaker, pSpeaker );
|
||||
}
|
||||
|
||||
/*
|
||||
value.SetString(MAKE_STRING(followup.followup_concept));
|
||||
g_EventQueue.AddEvent( pRespondent, "SpeakResponseConcept", value, delay , pSpeaker, pSpeaker );
|
||||
*/
|
||||
|
||||
AI_CriteriaSet criteria;
|
||||
|
||||
// add in the FROM context so dispatchee knows was from me
|
||||
const char * RESTRICT pszSpeakerName = GetResponseName( pSpeaker );
|
||||
criteria.AppendCriteria( "From", pszSpeakerName );
|
||||
#ifdef MAPBASE
|
||||
// See DispatchFollowupThroughQueue()
|
||||
criteria.AppendCriteria( "From_idx", CNumStr( pSpeaker->entindex() ) );
|
||||
criteria.AppendCriteria( "From_class", pSpeaker->GetClassname() );
|
||||
#endif
|
||||
// if a SUBJECT criteria is missing, put it back in.
|
||||
if ( criteria.FindCriterionIndex( "Subject" ) == -1 )
|
||||
{
|
||||
criteria.AppendCriteria( "Subject", pszSpeakerName );
|
||||
}
|
||||
|
||||
// add in any provided contexts from the parameters onto the ones stored in the followup
|
||||
criteria.Merge( followup.followup_contexts );
|
||||
|
||||
// This is kludgy and needs to be fixed in class hierarchy, but for now, try to guess at the most likely
|
||||
// kinds of targets and dispatch to them.
|
||||
if (CBaseMultiplayerPlayer *pPlayer = dynamic_cast<CBaseMultiplayerPlayer *>(pRespondent))
|
||||
{
|
||||
pPlayer->Speak( followup.followup_concept, &criteria );
|
||||
}
|
||||
|
||||
else if (CAI_BaseActor *pActor = dynamic_cast<CAI_BaseActor *>(pRespondent))
|
||||
{
|
||||
pActor->Speak( followup.followup_concept, &criteria );
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Placeholder for rules based response system
|
||||
// Input : concept -
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_ExpresserWithFollowup::Speak( AIConcept_t &concept, const char *modifiers /*= NULL*/, char *pszOutResponseChosen /* = NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ )
|
||||
{
|
||||
AI_Response *result = SpeakFindResponse( concept, modifiers );
|
||||
if ( !result )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CNPC_CompanionBot *pBot = dynamic_cast<CNPC_CompanionBot *>(GetOuter());
|
||||
if ( pBot )
|
||||
{
|
||||
pBot->SetConversationTopic( g_pConceptManager->GetTopic( handle ) );
|
||||
pBot->SetLastSpeaker( g_pConceptManager->GetSpeaker( handle ) );
|
||||
// Msg( "%s: Conversing about %s\n", pBot->GetBotString(), g_pConceptManager->GetTopicName( handle ) );
|
||||
}
|
||||
|
||||
SpeechMsg( GetOuter(), "%s (%x) spoke %s (%f)\n", STRING(GetOuter()->GetEntityName()), GetOuter(), g_pConceptManager->GetConcept( handle ), gpGlobals->curtime );
|
||||
|
||||
bool spoke = SpeakDispatchResponse( handle, result, filter );
|
||||
if ( pszOutResponseChosen )
|
||||
{
|
||||
result->GetResponse( pszOutResponseChosen, bufsize );
|
||||
}
|
||||
|
||||
return spoke;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Work out the character from the "subject" context.
|
||||
// Right now, this is a simple find by entity name search.
|
||||
// But you can define arbitrary subject names, like L4D does
|
||||
// for "biker", "manager", etc.
|
||||
static CBaseEntity *AscertainSpeechSubjectFromContext( AI_Response *response, AI_CriteriaSet &criteria, const char *pContextName )
|
||||
{
|
||||
const char *subject = criteria.GetValue( criteria.FindCriterionIndex( pContextName ) );
|
||||
if (subject)
|
||||
{
|
||||
CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, subject );
|
||||
|
||||
#ifdef MAPBASE
|
||||
// Allow entity indices to be used (see DispatchFollowupThroughQueue() for one particular use case)
|
||||
if (!pEnt && atoi(subject))
|
||||
{
|
||||
pEnt = CBaseEntity::Instance( atoi( subject ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
return pEnt;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Currently uses awful stricmp. Use symbols! Once I know which ones we want, that is.
|
||||
static CResponseQueue::CFollowupTargetSpec_t ResolveFollowupTargetToEntity( AIConcept_t &concept, AI_CriteriaSet &criteria, const char * RESTRICT szTarget, AI_Response * RESTRICT response = NULL )
|
||||
{
|
||||
|
||||
|
||||
|
||||
if ( Q_stricmp(szTarget, "self") == 0 )
|
||||
{
|
||||
return CResponseQueue::CFollowupTargetSpec_t( kDRT_SPECIFIC, concept.GetSpeaker() );
|
||||
}
|
||||
else if ( Q_stricmp(szTarget, "subject") == 0 )
|
||||
{
|
||||
return CResponseQueue::CFollowupTargetSpec_t( AscertainSpeechSubjectFromContext( response, criteria, "Subject" ) );
|
||||
}
|
||||
else if ( Q_stricmp(szTarget, "from") == 0 )
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
// See DispatchFollowupThroughQueue()
|
||||
return CResponseQueue::CFollowupTargetSpec_t( AscertainSpeechSubjectFromContext( response, criteria, "From_idx" ) );
|
||||
#else
|
||||
return CResponseQueue::CFollowupTargetSpec_t( AscertainSpeechSubjectFromContext( response, criteria, "From" ) );
|
||||
#endif
|
||||
}
|
||||
else if ( Q_stricmp(szTarget, "any") == 0 )
|
||||
{
|
||||
return CResponseQueue::CFollowupTargetSpec_t( kDRT_ANY, concept.GetSpeaker() );
|
||||
}
|
||||
else if ( Q_stricmp(szTarget, "all") == 0 )
|
||||
{
|
||||
return CResponseQueue::CFollowupTargetSpec_t( kDRT_ALL );
|
||||
}
|
||||
|
||||
// last resort, try a named lookup
|
||||
#ifdef MAPBASE
|
||||
else if ( CBaseEntity *pSpecific = gEntList.FindEntityByName(NULL, szTarget, concept.GetSpeaker()) ) // it could be anything
|
||||
#else
|
||||
else if ( CBaseEntity *pSpecific = gEntList.FindEntityByName(NULL, szTarget) ) // it could be anything
|
||||
#endif
|
||||
{
|
||||
return CResponseQueue::CFollowupTargetSpec_t( pSpecific );
|
||||
}
|
||||
|
||||
Warning("Couldn't resolve response target %s\n", szTarget );
|
||||
return CResponseQueue::CFollowupTargetSpec_t(); // couldn't resolve.
|
||||
}
|
||||
|
||||
|
||||
// TODO: Currently uses awful stricmp. Use symbols! Once I know which ones we want, that is.
|
||||
static CResponseQueue::CFollowupTargetSpec_t ResolveFollowupTargetToEntity( AIConcept_t &concept, AI_CriteriaSet &criteria, AI_Response * RESTRICT response, AI_ResponseFollowup * RESTRICT followup )
|
||||
{
|
||||
const char * RESTRICT szTarget = followup->followup_target;
|
||||
const CResponseQueue::CFollowupTargetSpec_t INVALID; // default: invalid result
|
||||
if ( szTarget == NULL )
|
||||
return INVALID;
|
||||
else
|
||||
return ResolveFollowupTargetToEntity( concept, criteria, szTarget, response );
|
||||
}
|
||||
|
||||
|
||||
ConVar chet_debug_idle( "chet_debug_idle", "0", FCVAR_ARCHIVE, "If set one, many debug prints to help track down the TLK_IDLE issue. Set two for super verbose info" );
|
||||
// extern ConVar chet_debug_idle;
|
||||
bool CAI_ExpresserWithFollowup::Speak( AIConcept_t concept, const char *modifiers /*= NULL*/, char *pszOutResponseChosen /* = NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ )
|
||||
{
|
||||
VPROF("CAI_Expresser::Speak");
|
||||
if ( IsSpeechGloballySuppressed() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
concept.SetSpeaker(GetOuter());
|
||||
AI_CriteriaSet criteria;
|
||||
GatherCriteria(&criteria, concept, modifiers);
|
||||
GetOuter()->ModifyOrAppendDerivedCriteria(criteria);
|
||||
AI_Response result;
|
||||
if ( !FindResponse( result, concept, &criteria ) )
|
||||
{
|
||||
if (chet_debug_idle.GetBool())
|
||||
{
|
||||
|
||||
const char *name = GetOuter()->GetDebugName();
|
||||
|
||||
Msg( "TLK_IDLE: %s did not FindResponse\n", name );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (chet_debug_idle.GetBool())
|
||||
{
|
||||
|
||||
|
||||
const char *name = GetOuter()->GetDebugName();
|
||||
|
||||
Msg( "TLK_IDLE: %s SUCCESSFUL FindResponse\n", name );
|
||||
}
|
||||
}
|
||||
|
||||
SpeechMsg( GetOuter(), "%s (%p) spoke %s (%f)", STRING(GetOuter()->GetEntityName()), GetOuter(), (const char*)concept, gpGlobals->curtime );
|
||||
// Msg( "%s:%s to %s:%s\n", GetOuter()->GetDebugName(), concept.GetStringConcept(), criteria.GetValue(criteria.FindCriterionIndex("Subject")), pTarget ? pTarget->GetDebugName() : "none" );
|
||||
|
||||
bool spoke = SpeakDispatchResponse( concept, &result, &criteria, filter );
|
||||
if ( pszOutResponseChosen )
|
||||
{
|
||||
result.GetResponse( pszOutResponseChosen, bufsize );
|
||||
}
|
||||
|
||||
return spoke;
|
||||
}
|
||||
|
||||
extern ISoundEmitterSystemBase* soundemitterbase;
|
||||
|
||||
static float GetSpeechDurationForResponse( const AI_Response * RESTRICT response, const char *szActorModel)
|
||||
{
|
||||
switch (response->GetType())
|
||||
{
|
||||
case ResponseRules::RESPONSE_SCENE:
|
||||
{
|
||||
char szScene[MAX_PATH];
|
||||
soundemitterbase->GenderExpandString(szActorModel, response->GetResponsePtr(), szScene, MAX_PATH);
|
||||
return GetSceneSpeechDuration(szScene);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Dispatches the result
|
||||
// Input : *response -
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_ExpresserWithFollowup::SpeakDispatchResponse( AIConcept_t concept, AI_Response *response, AI_CriteriaSet *criteria, IRecipientFilter *filter )
|
||||
{
|
||||
// This gives the chance for the other bot to respond.
|
||||
if ( !concept.GetSpeaker().IsValid() )
|
||||
{
|
||||
concept.SetSpeaker(GetOuter());
|
||||
}
|
||||
|
||||
bool bInterrupted = IsSpeaking();
|
||||
bool bSuc = CAI_Expresser::SpeakDispatchResponse( concept, response, criteria, filter );
|
||||
if (!bSuc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( bInterrupted )
|
||||
{
|
||||
g_ResponseQueueManager.GetQueue()->RemoveSpeechQueuedFor( GetOuter() );
|
||||
}
|
||||
|
||||
// Record my followup details so that I may defer its use til end of the speech
|
||||
AI_ResponseFollowup * RESTRICT followup = response->GetParams()->m_pFollowup;
|
||||
if ( followup )
|
||||
{
|
||||
if ( followup->followup_entityiotarget && followup->followup_entityioinput )
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
CBaseEntity * RESTRICT pTarget = ResolveFollowupTargetToEntity( concept, *criteria, followup->followup_entityiotarget, response ).m_hHandle;
|
||||
#else
|
||||
CBaseEntity * RESTRICT pTarget = gEntList.FindEntityByName( NULL, followup->followup_entityiotarget );
|
||||
#endif
|
||||
if ( pTarget )
|
||||
{
|
||||
g_EventQueue.AddEvent( pTarget, followup->followup_entityioinput, variant_t(), followup->followup_entityiodelay, GetOuter(), GetOuter() );
|
||||
}
|
||||
}
|
||||
if ( followup->IsValid() )
|
||||
{
|
||||
// 11th hour change: rather than trigger followups from the end of a VCD,
|
||||
// instead fire it from the end of the last speech event in the VCD, because
|
||||
// there's a multisecond facial relax delay built into the scene.
|
||||
// The speech length is stored in the cache, so we can post the followup now.
|
||||
if ( response->GetType() == ResponseRules::RESPONSE_SCENE &&
|
||||
followup->followup_delay >= 0 )
|
||||
{
|
||||
float fTimeToLastSpeech = GetSpeechDurationForResponse( response, STRING(GetOuter()->GetModelName()) );
|
||||
// failsafe
|
||||
if ( fTimeToLastSpeech > 0 )
|
||||
{
|
||||
DispatchFollowupThroughQueue( followup->followup_concept, followup->followup_contexts,
|
||||
ResolveFollowupTargetToEntity( concept, *criteria, response, followup ),
|
||||
fTimeToLastSpeech + followup->followup_delay, GetOuter() );
|
||||
}
|
||||
else // error
|
||||
{
|
||||
// old way, copied from "else" below
|
||||
m_pPostponedFollowup = followup;
|
||||
if ( criteria )
|
||||
m_followupTarget = ResolveFollowupTargetToEntity( concept, *criteria, response, m_pPostponedFollowup );
|
||||
else
|
||||
{
|
||||
AI_CriteriaSet tmpCriteria;
|
||||
m_followupTarget = ResolveFollowupTargetToEntity( concept, tmpCriteria, response, m_pPostponedFollowup );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( followup->followup_delay < 0 )
|
||||
{
|
||||
// a negative delay has a special meaning. Usually the comeback dispatches after
|
||||
// the currently said line is finished; the delay is added to that, to provide a
|
||||
// pause between when character A finishes speaking and B begins.
|
||||
// A negative delay (-n) actually means "dispatch the comeback n seconds
|
||||
// after I start talking".
|
||||
// In this case we do not need to postpone the followup; we just throw it directly
|
||||
// into the queue.
|
||||
DispatchFollowupThroughQueue( followup->followup_concept, followup->followup_contexts,
|
||||
ResolveFollowupTargetToEntity( concept, *criteria, response, followup ),
|
||||
-followup->followup_delay, GetOuter() );
|
||||
}
|
||||
#ifndef MAPBASE // RESPONSE_PRINT now notes speaking time
|
||||
else if ( response->GetType() == ResponseRules::RESPONSE_PRINT )
|
||||
{ // zero-duration responses dispatch immediately via the queue (must be the queue bec.
|
||||
// the m_pPostponedFollowup will never trigger)
|
||||
DispatchFollowupThroughQueue( followup->followup_concept, followup->followup_contexts,
|
||||
ResolveFollowupTargetToEntity( concept, *criteria, response, followup ),
|
||||
followup->followup_delay, GetOuter() );
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
// this is kind of a quick patch to immediately deal with the issue of null criteria
|
||||
// (arose while branching to main) without replumbing a bunch of stuff -- to be fixed
|
||||
// 5.13.08 egr
|
||||
m_pPostponedFollowup = followup;
|
||||
if ( criteria )
|
||||
m_followupTarget = ResolveFollowupTargetToEntity( concept, *criteria, response, m_pPostponedFollowup );
|
||||
else
|
||||
{
|
||||
AI_CriteriaSet tmpCriteria;
|
||||
m_followupTarget = ResolveFollowupTargetToEntity( concept, tmpCriteria, response, m_pPostponedFollowup );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return bSuc;
|
||||
}
|
||||
|
||||
// This is a gimmick used when a negative delay is specified in a followup, which is a shorthand
|
||||
// for "this many seconds after the beginning of the line" rather than "this may seconds after the end
|
||||
// of the line", eg to create a THEN rule when two characters talk over each other.
|
||||
// It's static to avoid accidental use of the postponed followup/target members.
|
||||
void CAI_ExpresserWithFollowup::DispatchFollowupThroughQueue( const AIConcept_t &concept,
|
||||
const char * RESTRICT criteriaStr,
|
||||
const CResponseQueue::CFollowupTargetSpec_t &target,
|
||||
float delay,
|
||||
CBaseEntity * RESTRICT pOuter
|
||||
)
|
||||
{
|
||||
AI_CriteriaSet criteria;
|
||||
// Don't add my own criteria! GatherCriteria( &criteria, followup.followup_concept, followup.followup_contexts );
|
||||
|
||||
criteria.AppendCriteria( "From", STRING( pOuter->GetEntityName() ) );
|
||||
#ifdef MAPBASE
|
||||
// The index of the "From" entity.
|
||||
// In HL2 mods, many followup users would be generic NPCs (e.g. citizens) who might not have any particular significance.
|
||||
// Those generic NPCs are quite likely to have no name or have a name in common with other entities. As a result, Mapbase
|
||||
// changes internal operations of the "From" context to search for an entity index. This won't be 100% reliable if the source
|
||||
// talker dies and another entity is created immediately afterwards, but it's a lot more reliable than a simple entity name search.
|
||||
criteria.AppendCriteria( "From_idx", CNumStr( pOuter->entindex() ) );
|
||||
|
||||
// Generic NPCs should also be attributable by classname
|
||||
criteria.AppendCriteria( "From_class", pOuter->GetClassname() );
|
||||
#endif
|
||||
|
||||
criteria.Merge( criteriaStr );
|
||||
g_ResponseQueueManager.GetQueue()->Add( concept, &criteria, gpGlobals->curtime + delay, target, pOuter );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Handles the new concept objects
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_ExpresserWithFollowup::SpeakDispatchFollowup( AI_ResponseFollowup &followup )
|
||||
{
|
||||
if ( !m_followupTarget.IsValid() )
|
||||
return;
|
||||
|
||||
// If a specific entity target is given, use the old pathway for now
|
||||
if ( m_followupTarget.m_iTargetType == kDRT_SPECIFIC && followup.followup_delay == 0 )
|
||||
{
|
||||
CBaseEntity *pTarget = m_followupTarget.m_hHandle.Get();
|
||||
if (!pTarget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DispatchComeback( this, GetOuter(), pTarget, followup );
|
||||
}
|
||||
else
|
||||
{
|
||||
DispatchFollowupThroughQueue( followup.followup_concept, followup.followup_contexts, m_followupTarget, followup.followup_delay, GetOuter() );
|
||||
}
|
||||
// clear out the followup member just in case.
|
||||
m_pPostponedFollowup = NULL;
|
||||
m_followupTarget.m_iTargetType = kDRT_MAX;
|
||||
}
|
||||
|
||||
void CAI_ExpresserWithFollowup::OnSpeechFinished()
|
||||
{
|
||||
if (m_pPostponedFollowup && m_pPostponedFollowup->IsValid())
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
// HACKHACK: Non-scene speech (e.g. noscene speak/sentence) fire OnSpeechFinished() immediately,
|
||||
// so add the actual speech time to the followup delay
|
||||
if (GetTimeSpeechCompleteWithoutDelay() > gpGlobals->curtime)
|
||||
m_pPostponedFollowup->followup_delay += GetTimeSpeechCompleteWithoutDelay() - gpGlobals->curtime;
|
||||
#endif
|
||||
return SpeakDispatchFollowup(*m_pPostponedFollowup);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void CC_RR_ForceConcept_f( const CCommand &args )
|
||||
{
|
||||
if ( args.ArgC() < 3 )
|
||||
{
|
||||
Msg("USAGE: rr_forceconcept <target> <concept> \"criteria1:value1,criteria2:value2,...\"\n");
|
||||
return;
|
||||
}
|
||||
|
||||
AI_CriteriaSet criteria;
|
||||
if ( args.ArgC() >= 3 )
|
||||
{
|
||||
const char *criteriastring = args[3];
|
||||
criteria.Merge( criteriastring );
|
||||
}
|
||||
|
||||
AIConcept_t concept( args[2] );
|
||||
QueueSpeak( concept, ResolveFollowupTargetToEntity( concept, criteria, args[1] ), criteria );
|
||||
}
|
||||
|
||||
|
||||
static ConCommand rr_forceconcept( "rr_forceconcept", CC_RR_ForceConcept_f,
|
||||
"fire a response concept directly at a given character.\n"
|
||||
"USAGE: rr_forceconcept <target> <concept> \"criteria1:value1,criteria2:value2,...\"\n"
|
||||
"criteria values are optional.\n"
|
||||
|
||||
, FCVAR_CHEAT );
|
@ -289,6 +289,11 @@ public:
|
||||
void SetHintType( int hintType, bool force = false );
|
||||
string_t HintActivityName( void ) const { return m_NodeData.iszActivityName; }
|
||||
int GetTargetNode( void ) const { return m_nTargetNodeID; }
|
||||
#ifdef MAPBASE
|
||||
// HACKHACK: This is for when target nodes need to be accessed before being sorted into engine IDs
|
||||
int GetTargetWCNodeID( void ) const { return m_NodeData.nTargetWCNodeID; }
|
||||
int GetWCNodeID( void ) const { return m_NodeData.nWCNodeID; }
|
||||
#endif
|
||||
bool IsDisabled( void ) const { return (m_NodeData.iDisabled != 0); }
|
||||
void SetDisabled( bool bDisabled ) { m_NodeData.iDisabled = bDisabled; }
|
||||
void DisableForSeconds( float flSeconds );
|
||||
@ -319,7 +324,9 @@ public:
|
||||
const char* ScriptGetHintActivity() { return STRING( HintActivityName() ); }
|
||||
#endif
|
||||
|
||||
#ifndef MAPBASE
|
||||
private:
|
||||
#endif
|
||||
void Spawn( void );
|
||||
virtual void Activate();
|
||||
virtual void UpdateOnRemove( void );
|
||||
|
@ -227,7 +227,11 @@ int CNodeEnt::Spawn( const char *pMapData )
|
||||
// ---------------------------------------------------------------------------------
|
||||
CAI_Hint *pHint = NULL;
|
||||
|
||||
if ( ClassMatches( "info_node_hint" ) || ClassMatches( "info_node_air_hint" ) )
|
||||
if ( ClassMatches( "info_node_hint" ) || ClassMatches( "info_node_air_hint" )
|
||||
#ifdef MAPBASE
|
||||
|| ClassMatches( "info_node_climb" ) // Climb nodes contain hint data in the FGD
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if ( m_NodeData.nHintType || m_NodeData.strGroup != NULL_STRING || m_NodeData.strEntityName != NULL_STRING )
|
||||
{
|
||||
|
@ -3059,6 +3059,16 @@ int CAI_NetworkBuilder::ComputeConnection( CAI_Node *pSrcNode, CAI_Node *pDestNo
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
// This is kind of a hack since target node IDs are designed to be used *after* the nodegraph is generated.
|
||||
// However, for the purposes of forcing a climb connection outside of regular lineup bounds, it seems to be a reasonable solution.
|
||||
if (pSrcNode->GetHint() && pDestNode->GetHint() &&
|
||||
(pSrcNode->GetHint()->GetTargetWCNodeID() == pDestNode->GetHint()->GetWCId() || pDestNode->GetHint()->GetTargetWCNodeID() == pSrcNode->GetHint()->GetWCId()))
|
||||
{
|
||||
DebugConnectMsg( srcId, destId, " Ignoring climbing lineup due to manual target ID linkage\n" );
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if ( !IsInLineForClimb(srcPos, UTIL_YawToVector( pSrcNode->m_flYaw ), destPos, UTIL_YawToVector( pDestNode->m_flYaw ) ) )
|
||||
{
|
||||
Assert( !IsInLineForClimb(destPos, UTIL_YawToVector( pDestNode->m_flYaw ), srcPos, UTIL_YawToVector( pSrcNode->m_flYaw ) ) );
|
||||
|
@ -137,12 +137,23 @@ bool ConceptStringLessFunc( const string_t &lhs, const string_t &rhs )
|
||||
return CaselessStringLessThan( STRING(lhs), STRING(rhs) );
|
||||
}
|
||||
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
bool ConceptInfoStringLessFunc( const AIConcept_t& lhs, const AIConcept_t& rhs )
|
||||
{
|
||||
return CaselessStringLessThan( lhs.GetStringConcept(), rhs.GetStringConcept() );
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CConceptInfoMap : public CUtlMap<AIConcept_t, ConceptInfo_t *> {
|
||||
public:
|
||||
CConceptInfoMap() :
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
CUtlMap<AIConcept_t, ConceptInfo_t *>( ConceptInfoStringLessFunc )
|
||||
#else
|
||||
CUtlMap<AIConcept_t, ConceptInfo_t *>( CaselessStringLessThan )
|
||||
#endif
|
||||
{
|
||||
for ( int i = 0; i < ARRAYSIZE(g_ConceptInfos); i++ )
|
||||
{
|
||||
@ -557,7 +568,11 @@ void CAI_PlayerAlly::PrescheduleThink( void )
|
||||
if ( SelectNonCombatSpeech( &selection ) )
|
||||
{
|
||||
SetSpeechTarget( selection.hSpeechTarget );
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
SpeakDispatchResponse( selection.concept.c_str(), &selection.Response );
|
||||
#else
|
||||
SpeakDispatchResponse( selection.concept.c_str(), selection.pResponse );
|
||||
#endif
|
||||
m_flNextIdleSpeechTime = gpGlobals->curtime + RandomFloat( 20,30 );
|
||||
}
|
||||
else
|
||||
@ -599,12 +614,22 @@ bool CAI_PlayerAlly::SelectSpeechResponse( AIConcept_t concept, const char *pszM
|
||||
{
|
||||
if ( IsAllowedToSpeak( concept ) )
|
||||
{
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
bool result = SpeakFindResponse( pSelection->Response, concept, pszModifiers );
|
||||
if ( result )
|
||||
{
|
||||
pSelection->concept = concept;
|
||||
pSelection->hSpeechTarget = pTarget;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
AI_Response *pResponse = SpeakFindResponse( concept, pszModifiers );
|
||||
if ( pResponse )
|
||||
{
|
||||
pSelection->Set( concept, pResponse, pTarget );
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -614,7 +639,9 @@ bool CAI_PlayerAlly::SelectSpeechResponse( AIConcept_t concept, const char *pszM
|
||||
void CAI_PlayerAlly::SetPendingSpeech( AIConcept_t concept, AI_Response *pResponse )
|
||||
{
|
||||
m_PendingResponse = *pResponse;
|
||||
#ifndef NEW_RESPONSE_SYSTEM
|
||||
pResponse->Release();
|
||||
#endif
|
||||
m_PendingConcept = concept;
|
||||
m_TimePendingSet = gpGlobals->curtime;
|
||||
}
|
||||
@ -696,7 +723,11 @@ bool CAI_PlayerAlly::SelectInterjection()
|
||||
if ( SelectIdleSpeech( &selection ) )
|
||||
{
|
||||
SetSpeechTarget( selection.hSpeechTarget );
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
SpeakDispatchResponse( selection.concept.c_str(), &selection.Response );
|
||||
#else
|
||||
SpeakDispatchResponse( selection.concept.c_str(), selection.pResponse );
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -761,11 +792,11 @@ void CAI_PlayerAlly::PostSpeakDispatchResponse( AIConcept_t concept, AI_Response
|
||||
{
|
||||
if ( bSaidHelloToNPC )
|
||||
{
|
||||
Warning("Q&A: '%s' said Hello to '%s' (concept %s)\n", GetDebugName(), GetSpeechTarget()->GetDebugName(), concept );
|
||||
Warning("Q&A: '%s' said Hello to '%s' (concept %s)\n", GetDebugName(), GetSpeechTarget()->GetDebugName(), (const char*)concept );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning("Q&A: '%s' questioned '%s' (concept %s)\n", GetDebugName(), GetSpeechTarget()->GetDebugName(), concept );
|
||||
Warning("Q&A: '%s' questioned '%s' (concept %s)\n", GetDebugName(), GetSpeechTarget()->GetDebugName(), (const char*)concept );
|
||||
}
|
||||
NDebugOverlay::HorzArrow( GetAbsOrigin(), GetSpeechTarget()->GetAbsOrigin(), 8, 0, 255, 0, 64, true, duration );
|
||||
}
|
||||
@ -881,6 +912,18 @@ bool CAI_PlayerAlly::AskQuestionNow( CBaseEntity *pSpeechTarget, int iQARandomNu
|
||||
m_iQARandomNumber = RandomInt(0, 100);
|
||||
|
||||
AISpeechSelection_t selection;
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
if (SelectSpeechResponse( concept, NULL, m_hPotentialSpeechTarget.Get(), &selection ))
|
||||
{
|
||||
SetSpeechTarget( selection.hSpeechTarget );
|
||||
ClearPendingSpeech();
|
||||
|
||||
// Speak immediately
|
||||
return SpeakDispatchResponse( selection.concept.c_str(), &selection.Response );
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
SelectSpeechResponse( concept, NULL, m_hPotentialSpeechTarget.Get(), &selection );
|
||||
|
||||
SetSpeechTarget( selection.hSpeechTarget );
|
||||
@ -891,6 +934,7 @@ bool CAI_PlayerAlly::AskQuestionNow( CBaseEntity *pSpeechTarget, int iQARandomNu
|
||||
|
||||
// Speak immediately
|
||||
return SpeakDispatchResponse( selection.concept.c_str(), selection.pResponse );
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -978,9 +1022,13 @@ void CAI_PlayerAlly::AnswerQuestion( CAI_PlayerAlly *pQuestioner, int iQARandomN
|
||||
}
|
||||
}
|
||||
|
||||
Assert( selection.pResponse );
|
||||
SetSpeechTarget( selection.hSpeechTarget );
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
SpeakDispatchResponse( selection.concept.c_str(), &selection.Response );
|
||||
#else
|
||||
Assert( selection.pResponse );
|
||||
SpeakDispatchResponse( selection.concept.c_str(), selection.pResponse );
|
||||
#endif
|
||||
|
||||
// Prevent idle speech for a while
|
||||
DeferAllIdleSpeech( random->RandomFloat( TALKER_DEFER_IDLE_SPEAK_MIN, TALKER_DEFER_IDLE_SPEAK_MAX ), GetSpeechTarget()->MyNPCPointer() );
|
||||
@ -1030,9 +1078,13 @@ int CAI_PlayerAlly::SelectNonCombatSpeechSchedule()
|
||||
AISpeechSelection_t selection;
|
||||
if ( SelectNonCombatSpeech( &selection ) )
|
||||
{
|
||||
Assert( selection.pResponse );
|
||||
SetSpeechTarget( selection.hSpeechTarget );
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
SetPendingSpeech( selection.concept.c_str(), &selection.Response );
|
||||
#else
|
||||
Assert( selection.pResponse );
|
||||
SetPendingSpeech( selection.concept.c_str(), selection.pResponse );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -1107,9 +1159,13 @@ void CAI_PlayerAlly::StartTask( const Task_t *pTask )
|
||||
case TASK_TALKER_SPEAK_PENDING:
|
||||
if ( !m_PendingConcept.empty() )
|
||||
{
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
SpeakDispatchResponse( m_PendingConcept.c_str(), &m_PendingResponse );
|
||||
#else
|
||||
AI_Response *pResponse = new AI_Response;
|
||||
*pResponse = m_PendingResponse;
|
||||
SpeakDispatchResponse( m_PendingConcept.c_str(), pResponse );
|
||||
#endif
|
||||
m_PendingConcept.erase();
|
||||
TaskComplete();
|
||||
}
|
||||
@ -1844,6 +1900,18 @@ bool CAI_PlayerAlly::RespondedTo( const char *ResponseConcept, bool bForce, bool
|
||||
{
|
||||
// We're being forced to respond to the event, probably because it's the
|
||||
// player dying or something equally important.
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
AI_Response response;
|
||||
bool result = SpeakFindResponse( response, ResponseConcept, NULL );
|
||||
if ( result )
|
||||
{
|
||||
// We've got something to say. Stop any scenes we're in, and speak the response.
|
||||
if ( bCancelScene )
|
||||
RemoveActorFromScriptedScenes( this, false );
|
||||
|
||||
return SpeakDispatchResponse( ResponseConcept, &response );
|
||||
}
|
||||
#else
|
||||
AI_Response *result = SpeakFindResponse( ResponseConcept, NULL );
|
||||
if ( result )
|
||||
{
|
||||
@ -1854,6 +1922,7 @@ bool CAI_PlayerAlly::RespondedTo( const char *ResponseConcept, bool bForce, bool
|
||||
bool spoke = SpeakDispatchResponse( ResponseConcept, result );
|
||||
return spoke;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -252,6 +252,11 @@ enum AISpeechTargetSearchFlags_t
|
||||
|
||||
struct AISpeechSelection_t
|
||||
{
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
std::string concept;
|
||||
AI_Response Response;
|
||||
EHANDLE hSpeechTarget;
|
||||
#else
|
||||
AISpeechSelection_t()
|
||||
: pResponse(NULL)
|
||||
{
|
||||
@ -267,6 +272,7 @@ struct AISpeechSelection_t
|
||||
std::string concept;
|
||||
AI_Response * pResponse;
|
||||
EHANDLE hSpeechTarget;
|
||||
#endif
|
||||
};
|
||||
|
||||
//-------------------------------------
|
||||
|
@ -1240,7 +1240,7 @@ char *CAI_Expresser::ParseApplyContext( const char *szContext )
|
||||
{
|
||||
// If it's really 0, then this is a waste of time
|
||||
Warning("\"%s\" was detected by applyContext operators as an operable number, but it's not.\n", szValue);
|
||||
return strdup(szContext);
|
||||
return szContext;
|
||||
}
|
||||
|
||||
// This is the existing value; will be operated upon and become the final assignment
|
||||
|
@ -5,6 +5,9 @@
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
#include "ai_speech_new.h"
|
||||
#else
|
||||
#ifndef AI_SPEECH_H
|
||||
#define AI_SPEECH_H
|
||||
|
||||
@ -452,3 +455,4 @@ inline void CAI_ExpresserHost<BASE_NPC>::DispatchResponse( const char *conceptNa
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#endif // AI_SPEECH_H
|
||||
#endif
|
||||
|
1739
sp/src/game/server/ai_speech_new.cpp
Normal file
1739
sp/src/game/server/ai_speech_new.cpp
Normal file
File diff suppressed because it is too large
Load Diff
705
sp/src/game/server/ai_speech_new.h
Normal file
705
sp/src/game/server/ai_speech_new.h
Normal file
@ -0,0 +1,705 @@
|
||||
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef AI_SPEECH_H
|
||||
#define AI_SPEECH_H
|
||||
|
||||
#include "utlmap.h"
|
||||
|
||||
#include "soundflags.h"
|
||||
#include "AI_Criteria.h"
|
||||
#include "AI_ResponseSystem.h"
|
||||
#include "utldict.h"
|
||||
#include "ai_speechconcept.h"
|
||||
|
||||
#if defined( _WIN32 )
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
class KeyValues;
|
||||
|
||||
using ResponseRules::ResponseType_t;
|
||||
using ResponseRules::AI_ResponseFollowup;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Used to share a global resource or prevent a system stepping on
|
||||
// own toes.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CAI_TimedSemaphore
|
||||
{
|
||||
public:
|
||||
CAI_TimedSemaphore()
|
||||
: m_ReleaseTime( 0 )
|
||||
{
|
||||
m_hCurrentTalker = NULL;
|
||||
}
|
||||
|
||||
void Acquire( float time, CBaseEntity *pTalker ) { m_ReleaseTime = gpGlobals->curtime + time; m_hCurrentTalker = pTalker; }
|
||||
void Release() { m_ReleaseTime = 0; m_hCurrentTalker = NULL; }
|
||||
|
||||
// Current owner of the semaphore is always allowed to talk
|
||||
bool IsAvailable( CBaseEntity *pTalker ) const { return ((gpGlobals->curtime > m_ReleaseTime) || (m_hCurrentTalker == pTalker)); }
|
||||
float GetReleaseTime() const { return m_ReleaseTime; }
|
||||
|
||||
CBaseEntity *GetOwner() { return m_hCurrentTalker; }
|
||||
|
||||
private:
|
||||
float m_ReleaseTime;
|
||||
EHANDLE m_hCurrentTalker;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
extern CAI_TimedSemaphore g_AIFriendliesTalkSemaphore;
|
||||
extern CAI_TimedSemaphore g_AIFoesTalkSemaphore;
|
||||
|
||||
#define GetSpeechSemaphore( pNpc ) (((pNpc)->IsPlayerAlly()) ? &g_AIFriendliesTalkSemaphore : &g_AIFoesTalkSemaphore )
|
||||
//-----------------------------------------------------------------------------
|
||||
// Basic speech system types
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------
|
||||
// Constants
|
||||
|
||||
|
||||
const float AIS_NO_DELAY = 0;
|
||||
const soundlevel_t AIS_DEF_SNDLVL = SNDLVL_TALKING;
|
||||
#define AI_NULL_CONCEPT NULL
|
||||
|
||||
#define AI_NULL_SENTENCE NULL
|
||||
|
||||
// Sentence prefix constants
|
||||
#define AI_SP_SPECIFIC_SENTENCE '!'
|
||||
#define AI_SP_WAVFILE '^'
|
||||
#define AI_SP_SCENE_GROUP '='
|
||||
#define AI_SP_SPECIFIC_SCENE '?'
|
||||
|
||||
#define AI_SPECIFIC_SENTENCE(str_constant) "!" str_constant
|
||||
#define AI_WAVFILE(str_constant) "^" str_constant
|
||||
// @Note (toml 09-12-02): as scene groups are not currently implemented, the string is a semi-colon delimited list
|
||||
#define AI_SCENE_GROUP(str_constant) "=" str_constant
|
||||
#define AI_SPECIFIC_SCENE(str_constant) "?" str_constant
|
||||
|
||||
// Designer overriding modifiers
|
||||
#define AI_SPECIFIC_SCENE_MODIFIER "scene:"
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
//-------------------------------------
|
||||
// An id that represents the core meaning of a spoken phrase,
|
||||
// eventually to be mapped to a sentence group or scene
|
||||
|
||||
#if AI_CONCEPTS_ARE_STRINGS
|
||||
typedef const char *AIConcept_t;
|
||||
inline bool CompareConcepts( AIConcept_t c1, AIConcept_t c2 )
|
||||
{
|
||||
return ( (void *)c1 == (void *)c2 || ( c1 && c2 && Q_stricmp( c1, c2 ) == 0 ) );
|
||||
}
|
||||
#else
|
||||
typedef CAI_Concept AIConcept_t;
|
||||
inline bool CompareConcepts( AIConcept_t c1, AIConcept_t c2 )
|
||||
{
|
||||
return c1.m_iConcept == c2.m_iConcept;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CAI_Expresser
|
||||
//
|
||||
// Purpose: Provides the functionality of going from abstract concept ("hello")
|
||||
// to specific sentence/scene/wave
|
||||
//
|
||||
|
||||
//-------------------------------------
|
||||
// Sink supports behavior control and receives notifications of internal events
|
||||
|
||||
class CAI_ExpresserSink
|
||||
{
|
||||
public:
|
||||
virtual void OnSpokeConcept( AIConcept_t concept, AI_Response *response ) {};
|
||||
virtual void OnStartSpeaking() {}
|
||||
virtual bool UseSemaphore() { return true; }
|
||||
};
|
||||
|
||||
struct ConceptHistory_t
|
||||
{
|
||||
DECLARE_SIMPLE_DATADESC();
|
||||
|
||||
ConceptHistory_t(float timeSpoken = -1 )
|
||||
: timeSpoken( timeSpoken ), m_response( )
|
||||
{
|
||||
}
|
||||
|
||||
ConceptHistory_t( const ConceptHistory_t& src );
|
||||
ConceptHistory_t& operator = ( const ConceptHistory_t& src );
|
||||
|
||||
~ConceptHistory_t();
|
||||
|
||||
float timeSpoken;
|
||||
AI_Response m_response;
|
||||
};
|
||||
//-------------------------------------
|
||||
|
||||
class CAI_Expresser : public ResponseRules::IResponseFilter
|
||||
{
|
||||
public:
|
||||
CAI_Expresser( CBaseFlex *pOuter = NULL );
|
||||
~CAI_Expresser();
|
||||
|
||||
// --------------------------------
|
||||
|
||||
bool Connect( CAI_ExpresserSink *pSink ) { m_pSink = pSink; return true; }
|
||||
bool Disconnect( CAI_ExpresserSink *pSink ) { m_pSink = NULL; return true;}
|
||||
|
||||
void TestAllResponses();
|
||||
|
||||
// --------------------------------
|
||||
|
||||
bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );
|
||||
bool Speak( const AIConcept_t &concept, AI_CriteriaSet *criteria, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );
|
||||
|
||||
// Given modifiers (which are colon-delimited strings), fill out a criteria set including this
|
||||
// character's contexts and the ones in the modifier. This lets us hang on to them after a call
|
||||
// to SpeakFindResponse.
|
||||
void GatherCriteria( AI_CriteriaSet *outputCritera, const AIConcept_t &concept, const char *modifiers );
|
||||
// These two methods allow looking up a response and dispatching it to be two different steps
|
||||
// AI_Response *SpeakFindResponse( AIConcept_t concept, const char *modifiers = NULL );
|
||||
// AI_Response *SpeakFindResponse( AIConcept_t &concept, AI_CriteriaSet *criteria );
|
||||
// Find the appropriate response for the given concept. Return false if none found.
|
||||
// Fills out the response object that you provide.
|
||||
bool FindResponse( AI_Response &outResponse, const AIConcept_t &concept, AI_CriteriaSet *modifiers = NULL );
|
||||
virtual bool SpeakDispatchResponse( AIConcept_t concept, AI_Response *response, AI_CriteriaSet *criteria, IRecipientFilter *filter = NULL );
|
||||
float GetResponseDuration( AI_Response *response );
|
||||
|
||||
#ifdef MAPBASE
|
||||
void SetUsingProspectiveResponses( bool bToggle );
|
||||
void MarkResponseAsUsed( AI_Response *response );
|
||||
#endif
|
||||
|
||||
virtual int SpeakRawSentence( const char *pszSentence, float delay, float volume = VOL_NORM, soundlevel_t soundlevel = SNDLVL_TALKING, CBaseEntity *pListener = NULL );
|
||||
|
||||
bool SemaphoreIsAvailable( CBaseEntity *pTalker );
|
||||
float GetSemaphoreAvailableTime( CBaseEntity *pTalker );
|
||||
|
||||
virtual void OnSpeechFinished() {};
|
||||
|
||||
// This function can be overriden by games to suppress speech altogether during glue screens, etc
|
||||
static bool IsSpeechGloballySuppressed();
|
||||
|
||||
// --------------------------------
|
||||
|
||||
virtual bool IsSpeaking();
|
||||
bool CanSpeak();
|
||||
bool CanSpeakAfterMyself();
|
||||
float GetTimeSpeechComplete() const { return m_flStopTalkTime; }
|
||||
#ifdef MAPBASE
|
||||
float GetTimeSpeechCompleteWithoutDelay() const { return m_flStopTalkTimeWithoutDelay; }
|
||||
#endif
|
||||
void BlockSpeechUntil( float time );
|
||||
|
||||
// --------------------------------
|
||||
|
||||
bool CanSpeakConcept( const AIConcept_t &concept );
|
||||
bool SpokeConcept( const AIConcept_t &concept );
|
||||
float GetTimeSpokeConcept( const AIConcept_t &concept ); // returns -1 if never
|
||||
void SetSpokeConcept( const AIConcept_t &concept, AI_Response *response, bool bCallback = true );
|
||||
void ClearSpokeConcept( const AIConcept_t &concept );
|
||||
|
||||
// --------------------------------
|
||||
|
||||
void SetVoicePitch( int voicePitch ) { m_voicePitch = voicePitch; }
|
||||
int GetVoicePitch() const;
|
||||
|
||||
void NoteSpeaking( float duration, float delay = 0 );
|
||||
|
||||
// Force the NPC to release the semaphore & clear next speech time
|
||||
void ForceNotSpeaking( void );
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
bool ScriptSpeakRawScene( char const *soundname, float delay ) { return SpeakRawScene( soundname, delay, NULL ); }
|
||||
bool ScriptSpeakAutoGeneratedScene( char const *soundname, float delay ) { return SpeakAutoGeneratedScene( soundname, delay ); }
|
||||
int ScriptSpeakRawSentence( char const *pszSentence, float delay ) { return SpeakRawSentence( pszSentence, delay ); }
|
||||
bool ScriptSpeak( char const *concept, const char *modifiers ) { return Speak( concept, modifiers[0] != '\0' ? modifiers : NULL ); }
|
||||
#endif
|
||||
|
||||
// helper used in dealing with RESPONSE_ENTITYIO
|
||||
// response is the output of AI_Response::GetName
|
||||
// note: the response string will get stomped on (by strtok)
|
||||
// returns false on failure (eg, couldn't match parse contents)
|
||||
static bool FireEntIOFromResponse( char *response, CBaseEntity *pInitiator );
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
// Used for RESPONSE_VSCRIPT(_FILE)
|
||||
static bool RunScriptResponse( CBaseEntity *pTarget, const char *response, AI_CriteriaSet *criteria, bool file );
|
||||
#endif
|
||||
|
||||
protected:
|
||||
CAI_TimedSemaphore *GetMySpeechSemaphore( CBaseEntity *pNpc );
|
||||
|
||||
bool SpeakRawScene( const char *pszScene, float delay, AI_Response *response, IRecipientFilter *filter = NULL );
|
||||
// This will create a fake .vcd/CChoreoScene to wrap the sound to be played
|
||||
bool SpeakAutoGeneratedScene( char const *soundname, float delay );
|
||||
|
||||
void DumpHistories();
|
||||
|
||||
void SpeechMsg( CBaseEntity *pFlex, PRINTF_FORMAT_STRING const char *pszFormat, ... ) FMTFUNCTION(3, 4);
|
||||
|
||||
// --------------------------------
|
||||
|
||||
CAI_ExpresserSink *GetSink() { return m_pSink; }
|
||||
|
||||
private:
|
||||
// --------------------------------
|
||||
|
||||
virtual bool IsValidResponse( ResponseType_t type, const char *pszValue );
|
||||
|
||||
// --------------------------------
|
||||
|
||||
CAI_ExpresserSink *m_pSink;
|
||||
|
||||
// --------------------------------
|
||||
//
|
||||
// Speech concept data structures
|
||||
//
|
||||
|
||||
CUtlDict< ConceptHistory_t, int > m_ConceptHistories;
|
||||
|
||||
// --------------------------------
|
||||
//
|
||||
// Speaking states
|
||||
//
|
||||
|
||||
float m_flStopTalkTime; // when in the future that I'll be done saying this sentence.
|
||||
float m_flStopTalkTimeWithoutDelay; // same as the above, but minus the delay before other people can speak
|
||||
float m_flBlockedTalkTime;
|
||||
int m_voicePitch; // pitch of voice for this head
|
||||
float m_flLastTimeAcceptedSpeak; // because speech may not be blocked until NoteSpeaking called by scene ent, this handles in-think blocking
|
||||
|
||||
DECLARE_SIMPLE_DATADESC();
|
||||
|
||||
// --------------------------------
|
||||
//
|
||||
public:
|
||||
void SetOuter( CBaseFlex *pOuter );
|
||||
|
||||
CBaseFlex * GetOuter() { return m_pOuter; }
|
||||
const CBaseFlex * GetOuter() const { return m_pOuter; }
|
||||
|
||||
private:
|
||||
CHandle<CBaseFlex> m_pOuter;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// An NPC base class to assist a branch of the inheritance graph
|
||||
// in utilizing CAI_Expresser
|
||||
//
|
||||
|
||||
template <class BASE_NPC>
|
||||
class CAI_ExpresserHost : public BASE_NPC, protected CAI_ExpresserSink
|
||||
{
|
||||
DECLARE_CLASS_NOFRIEND( CAI_ExpresserHost, BASE_NPC );
|
||||
|
||||
public:
|
||||
virtual void NoteSpeaking( float duration, float delay );
|
||||
|
||||
virtual bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );
|
||||
virtual bool Speak( AIConcept_t concept, AI_CriteriaSet *pCriteria, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );
|
||||
#ifdef MAPBASE
|
||||
virtual bool Speak( AIConcept_t concept, AI_CriteriaSet& modifiers, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ) { return Speak( concept, &modifiers, pszOutResponseChosen, bufsize, filter ); }
|
||||
#endif
|
||||
|
||||
|
||||
void GatherCriteria( AI_CriteriaSet *outputCritera, const AIConcept_t &concept, const char *modifiers );
|
||||
// These two methods allow looking up a response and dispatching it to be two different steps
|
||||
#ifdef MAPBASE
|
||||
//AI_Response *SpeakFindResponse( AIConcept_t concept, const AI_CriteriaSet& modifiers );
|
||||
inline bool SpeakDispatchResponse( const AIConcept_t &concept, AI_Response &response, AI_CriteriaSet *criteria = NULL ) { return SpeakDispatchResponse( concept, &response, criteria ); }
|
||||
#endif
|
||||
bool SpeakFindResponse( AI_Response& outResponse, const AIConcept_t &concept, const char *modifiers = NULL );
|
||||
// AI_Response * SpeakFindResponse( AIConcept_t concept, const char *modifiers = NULL );
|
||||
// AI_Response *SpeakFindResponse( AIConcept_t concept, AI_CriteriaSet *criteria );
|
||||
// AI_Response *SpeakFindResponse( AIConcept_t concept );
|
||||
// Find the appropriate response for the given concept. Return false if none found.
|
||||
// Fills out the response object that you provide.
|
||||
bool FindResponse( AI_Response &outResponse, const AIConcept_t &concept, AI_CriteriaSet *criteria = NULL );
|
||||
|
||||
bool SpeakDispatchResponse( AIConcept_t concept, AI_Response *response, AI_CriteriaSet *criteria = NULL );
|
||||
virtual void PostSpeakDispatchResponse( AIConcept_t concept, AI_Response *response ) { return; }
|
||||
float GetResponseDuration( AI_Response *response );
|
||||
|
||||
float GetTimeSpeechComplete() const { return this->GetExpresser()->GetTimeSpeechComplete(); }
|
||||
|
||||
bool IsSpeaking() { return this->GetExpresser()->IsSpeaking(); }
|
||||
bool CanSpeak() { return this->GetExpresser()->CanSpeak(); }
|
||||
bool CanSpeakAfterMyself() { return this->GetExpresser()->CanSpeakAfterMyself(); }
|
||||
|
||||
void SetSpokeConcept( AIConcept_t concept, AI_Response *response, bool bCallback = true ) { this->GetExpresser()->SetSpokeConcept( concept, response, bCallback ); }
|
||||
float GetTimeSpokeConcept( AIConcept_t concept ) { return this->GetExpresser()->GetTimeSpokeConcept( concept ); }
|
||||
bool SpokeConcept( AIConcept_t concept ) { return this->GetExpresser()->SpokeConcept( concept ); }
|
||||
|
||||
protected:
|
||||
int PlaySentence( const char *pszSentence, float delay, float volume = VOL_NORM, soundlevel_t soundlevel = SNDLVL_TALKING, CBaseEntity *pListener = NULL );
|
||||
virtual void ModifyOrAppendCriteria( AI_CriteriaSet& set );
|
||||
|
||||
virtual IResponseSystem *GetResponseSystem();
|
||||
// Override of base entity response input handler
|
||||
virtual void DispatchResponse( const char *conceptName );
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline void CAI_ExpresserHost<BASE_NPC>::NoteSpeaking( float duration, float delay )
|
||||
{
|
||||
this->GetExpresser()->NoteSpeaking( duration, delay );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline bool CAI_ExpresserHost<BASE_NPC>::Speak( AIConcept_t concept, const char *modifiers /*= NULL*/, char *pszOutResponseChosen /*=NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ )
|
||||
{
|
||||
AssertOnce( this->GetExpresser()->GetOuter() == this );
|
||||
return this->GetExpresser()->Speak( concept, modifiers, pszOutResponseChosen, bufsize, filter );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline bool CAI_ExpresserHost<BASE_NPC>::Speak( AIConcept_t concept, AI_CriteriaSet *pCriteria, char *pszOutResponseChosen /*=NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ )
|
||||
{
|
||||
AssertOnce( this->GetExpresser()->GetOuter() == this );
|
||||
CAI_Expresser * const RESTRICT pExpresser = this->GetExpresser();
|
||||
concept.SetSpeaker(this);
|
||||
// add in any local criteria to the one passed on the command line.
|
||||
pExpresser->GatherCriteria( pCriteria, concept, NULL );
|
||||
// call the "I have aleady gathered criteria" version of Expresser::Speak
|
||||
return pExpresser->Speak( concept, pCriteria, pszOutResponseChosen, bufsize, filter );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline int CAI_ExpresserHost<BASE_NPC>::PlaySentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, CBaseEntity *pListener )
|
||||
{
|
||||
return this->GetExpresser()->SpeakRawSentence( pszSentence, delay, volume, soundlevel, pListener );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
extern void CAI_ExpresserHost_NPC_DoModifyOrAppendCriteria( CAI_BaseNPC *pSpeaker, AI_CriteriaSet& criteriaSet );
|
||||
|
||||
template <class BASE_NPC>
|
||||
inline void CAI_ExpresserHost<BASE_NPC>::ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet )
|
||||
{
|
||||
BaseClass::ModifyOrAppendCriteria( criteriaSet );
|
||||
|
||||
|
||||
if ( this->MyNPCPointer() )
|
||||
{
|
||||
CAI_ExpresserHost_NPC_DoModifyOrAppendCriteria( this->MyNPCPointer(), criteriaSet );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline IResponseSystem *CAI_ExpresserHost<BASE_NPC>::GetResponseSystem()
|
||||
{
|
||||
extern IResponseSystem *g_pResponseSystem;
|
||||
// Expressive NPC's use the general response system
|
||||
return g_pResponseSystem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline void CAI_ExpresserHost<BASE_NPC>::GatherCriteria( AI_CriteriaSet *outputCriteria, const AIConcept_t &concept, const char *modifiers )
|
||||
{
|
||||
return this->GetExpresser()->GatherCriteria( outputCriteria, concept, modifiers );
|
||||
}
|
||||
|
||||
|
||||
#if 1
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline bool CAI_ExpresserHost<BASE_NPC>::SpeakFindResponse(AI_Response& outResponse, const AIConcept_t &concept, const char *modifiers /*= NULL*/ )
|
||||
{
|
||||
AI_CriteriaSet criteria;
|
||||
GatherCriteria(&criteria, concept, modifiers);
|
||||
return FindResponse( outResponse, concept, &criteria );
|
||||
}
|
||||
#else
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline AI_Response *CAI_ExpresserHost<BASE_NPC>::SpeakFindResponse( const AIConcept_t &concept, const char *modifiers /*= NULL*/ )
|
||||
{
|
||||
return this->GetExpresser()->SpeakFindResponse( concept, modifiers );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline AI_Response *CAI_ExpresserHost<BASE_NPC>::SpeakFindResponse( const AIConcept_t &concept, AI_CriteriaSet *criteria /*= NULL*/ )
|
||||
{
|
||||
return this->GetExpresser()->SpeakFindResponse( concept, criteria );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// In this case we clearly don't care to hang on to the criteria, so make a convenience
|
||||
// class that generates a one off.
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline AI_Response * CAI_ExpresserHost<BASE_NPC>::SpeakFindResponse( const AIConcept_t &concept )
|
||||
{
|
||||
AI_CriteriaSet criteria;
|
||||
GatherCriteria( &criteria, concept, NULL );
|
||||
return this->GetExpresser()->SpeakFindResponse( concept, &criteria );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline bool CAI_ExpresserHost<BASE_NPC>::FindResponse( AI_Response &outResponse, const AIConcept_t &concept, AI_CriteriaSet *criteria )
|
||||
{
|
||||
return this->GetExpresser()->FindResponse( outResponse, concept, criteria );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline bool CAI_ExpresserHost<BASE_NPC>::SpeakDispatchResponse( AIConcept_t concept, AI_Response *response, AI_CriteriaSet *criteria )
|
||||
{
|
||||
if ( this->GetExpresser()->SpeakDispatchResponse( concept, response, criteria ) )
|
||||
{
|
||||
PostSpeakDispatchResponse( concept, response );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline float CAI_ExpresserHost<BASE_NPC>::GetResponseDuration( AI_Response *response )
|
||||
{
|
||||
return this->GetExpresser()->GetResponseDuration( response );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Override of base entity response input handler
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class BASE_NPC>
|
||||
inline void CAI_ExpresserHost<BASE_NPC>::DispatchResponse( const char *conceptName )
|
||||
{
|
||||
Speak( AIConcept_t( conceptName ) );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// A shim under CAI_ExpresserHost you can use when deriving a new expresser
|
||||
/// host type under CAI_BaseNPC. This does the extra step of declaring an m_pExpresser
|
||||
/// member and initializing it from CreateComponents(). If your BASE_NPC class isn't
|
||||
/// actually an NPC, then CreateComponents() never gets called and you won't have
|
||||
/// an expresser created.
|
||||
/// Note: you still need to add m_pExpresser to the Datadesc for your derived type.
|
||||
/// This is because I couldn't figure out how to make a templatized datadesc declaration
|
||||
/// that works generically on the template type.
|
||||
template <class BASE_NPC, class EXPRESSER_TYPE>
|
||||
class CAI_ExpresserHostWithData : public CAI_ExpresserHost<BASE_NPC>
|
||||
{
|
||||
DECLARE_CLASS_NOFRIEND( CAI_ExpresserHostWithData, CAI_ExpresserHost<BASE_NPC> );
|
||||
|
||||
public:
|
||||
CAI_ExpresserHostWithData( ) : m_pExpresser(NULL) {};
|
||||
|
||||
virtual CAI_Expresser *GetExpresser() { return m_pExpresser; }
|
||||
const CAI_Expresser *GetExpresser() const { return m_pExpresser; }
|
||||
|
||||
virtual bool CreateComponents()
|
||||
{
|
||||
return BaseClass::CreateComponents() && ( CreateExpresser() != NULL );
|
||||
}
|
||||
|
||||
protected:
|
||||
EXPRESSER_TYPE *CreateExpresser( void )
|
||||
{
|
||||
AssertMsg1( m_pExpresser == NULL, "Tried to double-initialize expresser in %s\n", this->GetDebugName() );
|
||||
m_pExpresser = new EXPRESSER_TYPE(this);
|
||||
if ( !m_pExpresser)
|
||||
{
|
||||
AssertMsg1( false, "Creating an expresser failed in %s\n", this->GetDebugName() );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_pExpresser->Connect(this);
|
||||
return m_pExpresser;
|
||||
}
|
||||
|
||||
virtual ~CAI_ExpresserHostWithData( void )
|
||||
{
|
||||
delete m_pExpresser;
|
||||
m_pExpresser = NULL;
|
||||
}
|
||||
|
||||
EXPRESSER_TYPE *m_pExpresser;
|
||||
};
|
||||
|
||||
/// response rules
|
||||
namespace RR
|
||||
{
|
||||
/// some applycontext clauses have operators preceding them,
|
||||
/// like ++1 which means "take the current value and increment it
|
||||
/// by one". These classes detect these cases and do the appropriate
|
||||
/// thing.
|
||||
class CApplyContextOperator
|
||||
{
|
||||
public:
|
||||
inline CApplyContextOperator( int nSkipChars ) : m_nSkipChars(nSkipChars) {};
|
||||
|
||||
/// perform whatever this operator does upon the given context value.
|
||||
/// Default op is simply to copy old to new.
|
||||
/// pOldValue should be the currently set value of the context. May be NULL meaning no prior value.
|
||||
/// pOperator the value that applycontext says to set
|
||||
/// pNewValue a pointer to a buffer where the real new value will be writ.
|
||||
/// returns true on success; false on failure (eg, tried to increment a
|
||||
/// non-numeric value).
|
||||
virtual bool Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize );
|
||||
|
||||
/// This is the function that should be called from outside,
|
||||
/// fed the input string, it'll select the right operator
|
||||
/// to apply.
|
||||
static CApplyContextOperator *FindOperator( const char *pContextString );
|
||||
|
||||
protected:
|
||||
int m_nSkipChars; // how many chars to "skip" in the value string to get past the op specifier to the actual value
|
||||
// eg, "++3" has a m_nSkipChars of 2, because the op string "++" is two characters.
|
||||
};
|
||||
|
||||
class CIncrementOperator : public CApplyContextOperator
|
||||
{
|
||||
public:
|
||||
inline CIncrementOperator( int nSkipChars ) : CApplyContextOperator(nSkipChars) {};
|
||||
virtual bool Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize );
|
||||
};
|
||||
|
||||
class CDecrementOperator : public CApplyContextOperator
|
||||
{
|
||||
public:
|
||||
inline CDecrementOperator( int nSkipChars ) : CApplyContextOperator(nSkipChars) {};
|
||||
virtual bool Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize );
|
||||
};
|
||||
|
||||
#ifdef MAPBASE
|
||||
class CMultiplyOperator : public CApplyContextOperator
|
||||
{
|
||||
public:
|
||||
inline CMultiplyOperator( int nSkipChars ) : CApplyContextOperator(nSkipChars) {};
|
||||
virtual bool Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize );
|
||||
};
|
||||
|
||||
class CDivideOperator : public CApplyContextOperator
|
||||
{
|
||||
public:
|
||||
inline CDivideOperator( int nSkipChars ) : CApplyContextOperator(nSkipChars) {};
|
||||
virtual bool Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize );
|
||||
};
|
||||
#endif
|
||||
|
||||
class CToggleOperator : public CApplyContextOperator
|
||||
{
|
||||
public:
|
||||
inline CToggleOperator( int nSkipChars ) : CApplyContextOperator(nSkipChars) {};
|
||||
virtual bool Apply( const char *pOldValue, const char *pOperator, char *pNewValue, int pNewValBufSize );
|
||||
};
|
||||
|
||||
// the singleton operators
|
||||
extern CApplyContextOperator sm_OpCopy;
|
||||
extern CIncrementOperator sm_OpIncrement;
|
||||
extern CDecrementOperator sm_OpDecrement;
|
||||
#ifdef MAPBASE
|
||||
extern CMultiplyOperator sm_OpMultiply;
|
||||
extern CDivideOperator sm_OpDivide;
|
||||
#endif
|
||||
extern CToggleOperator sm_OpToggle;
|
||||
|
||||
#ifdef MAPBASE
|
||||
// LEGACY - See CApplyContextOperator::FindOperator()
|
||||
extern CIncrementOperator sm_OpLegacyIncrement;
|
||||
extern CDecrementOperator sm_OpLegacyDecrement;
|
||||
extern CMultiplyOperator sm_OpLegacyMultiply;
|
||||
extern CDivideOperator sm_OpLegacyDivide;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "ai_speechqueue.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A kind of AI Expresser that can dispatch a follow-up speech event when it
|
||||
// finishes speaking.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAI_ExpresserWithFollowup : public CAI_Expresser
|
||||
{
|
||||
public:
|
||||
CAI_ExpresserWithFollowup( CBaseFlex *pOuter = NULL ) : CAI_Expresser(pOuter),
|
||||
m_pPostponedFollowup(NULL)
|
||||
{};
|
||||
virtual bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );
|
||||
virtual bool SpeakDispatchResponse( AIConcept_t concept, AI_Response *response, AI_CriteriaSet *criteria, IRecipientFilter *filter = NULL );
|
||||
virtual void SpeakDispatchFollowup( AI_ResponseFollowup &followup );
|
||||
|
||||
virtual void OnSpeechFinished();
|
||||
|
||||
typedef CAI_Expresser BaseClass;
|
||||
protected:
|
||||
static void DispatchFollowupThroughQueue( const AIConcept_t &concept,
|
||||
const char *criteriaStr,
|
||||
const CResponseQueue::CFollowupTargetSpec_t &target,
|
||||
float delay,
|
||||
CBaseEntity * RESTRICT pOuter );
|
||||
|
||||
AI_ResponseFollowup *m_pPostponedFollowup; // TODO: save/restore
|
||||
CResponseQueue::CFollowupTargetSpec_t m_followupTarget;
|
||||
};
|
||||
|
||||
class CMultiplayer_Expresser : public CAI_ExpresserWithFollowup
|
||||
{
|
||||
public:
|
||||
CMultiplayer_Expresser( CBaseFlex *pOuter = NULL );
|
||||
//~CMultiplayer_Expresser();
|
||||
|
||||
virtual bool IsSpeaking();
|
||||
|
||||
void AllowMultipleScenes();
|
||||
void DisallowMultipleScenes();
|
||||
|
||||
private:
|
||||
bool m_bAllowMultipleScenes;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // AI_SPEECH_H
|
495
sp/src/game/server/ai_speechqueue.cpp
Normal file
495
sp/src/game/server/ai_speechqueue.cpp
Normal file
@ -0,0 +1,495 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "basemultiplayerplayer.h"
|
||||
#include "ai_baseactor.h"
|
||||
#include "ai_speech.h"
|
||||
//#include "flex_expresser.h"
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include <tier0/memdbgon.h>
|
||||
|
||||
extern ConVar ai_debug_speech;
|
||||
#define DebuggingSpeech() ai_debug_speech.GetBool()
|
||||
extern ConVar rr_debugresponses;
|
||||
|
||||
ConVar rr_followup_maxdist( "rr_followup_maxdist", "1800", FCVAR_CHEAT, "'then ANY' or 'then ALL' response followups will be dispatched only to characters within this distance." );
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RESPONSE QUEUE DATA STRUCTURE
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CResponseQueue::CResponseQueue( int queueSize ) : m_Queue(queueSize), m_ExpresserTargets(8,8)
|
||||
{};
|
||||
|
||||
/// Add a deferred response.
|
||||
void CResponseQueue::Add( const AIConcept_t &concept, ///< concept to dispatch
|
||||
const AI_CriteriaSet * RESTRICT contexts,
|
||||
float time, ///< when to dispatch it. You can specify a time of zero to mean "immediately."
|
||||
const CFollowupTargetSpec_t &targetspec,
|
||||
CBaseEntity *pIssuer
|
||||
)
|
||||
{
|
||||
// Add a response.
|
||||
AssertMsg( m_Queue.Count() < AI_RESPONSE_QUEUE_SIZE, "AI Response queue overfilled." );
|
||||
QueueType_t::IndexLocalType_t idx = m_Queue.AddToTail();
|
||||
m_Queue[idx].Init( concept, contexts, time, targetspec, pIssuer );
|
||||
}
|
||||
|
||||
|
||||
/// Remove a deferred response matching the concept and issuer.
|
||||
void CResponseQueue::Remove( const AIConcept_t &concept, ///< concept to dispatch
|
||||
CBaseEntity * const RESTRICT pIssuer ///< the entity issuing the response, if one exists.
|
||||
) RESTRICT
|
||||
{
|
||||
// walk through the queue until we find a response matching the concept and issuer, then strike it.
|
||||
QueueType_t::IndexLocalType_t idx = m_Queue.Head();
|
||||
while (idx != m_Queue.InvalidIndex())
|
||||
{
|
||||
CDeferredResponse &response = m_Queue[idx];
|
||||
QueueType_t::IndexLocalType_t previdx = idx; // advance the index immediately because we may be deleting the "current" element
|
||||
idx = m_Queue.Next(idx); // is now the next index
|
||||
if ( CompareConcepts( response.m_concept, concept ) && // if concepts match and
|
||||
( !pIssuer || ( response.m_hIssuer.Get() == pIssuer ) ) // issuer is null, or matches the one in the response
|
||||
)
|
||||
{
|
||||
m_Queue.Remove(previdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CResponseQueue::RemoveSpeechQueuedFor( const CBaseEntity *pSpeaker )
|
||||
{
|
||||
// walk through the queue until we find a response matching the speaker, then strike it.
|
||||
// because responses are dispatched from inside a loop that is already walking through the
|
||||
// queue, it's not safe to actually remove the elements. Instead, quash it by replacing it
|
||||
// with a null event.
|
||||
|
||||
for ( QueueType_t::IndexLocalType_t idx = m_Queue.Head() ;
|
||||
idx != m_Queue.InvalidIndex() ;
|
||||
idx = m_Queue.Next(idx) ) // is now the next index
|
||||
{
|
||||
CDeferredResponse &response = m_Queue[idx];
|
||||
if ( response.m_Target.m_hHandle.Get() == pSpeaker )
|
||||
{
|
||||
response.Quash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: use a more compact representation.
|
||||
void CResponseQueue::DeferContextsFromCriteriaSet( DeferredContexts_t &contextsOut, const AI_CriteriaSet * RESTRICT criteriaIn )
|
||||
{
|
||||
contextsOut.Reset();
|
||||
if (criteriaIn)
|
||||
{
|
||||
contextsOut.Merge(criteriaIn);
|
||||
}
|
||||
}
|
||||
|
||||
void CResponseQueue::PerFrameDispatch()
|
||||
{
|
||||
failsafe:
|
||||
// Walk through the list, find any messages whose time has come, and dispatch them. Then remove them.
|
||||
QueueType_t::IndexLocalType_t idx = m_Queue.Head();
|
||||
while (idx != m_Queue.InvalidIndex())
|
||||
{
|
||||
// do we need to dispatch this concept?
|
||||
CDeferredResponse &response = m_Queue[idx];
|
||||
QueueType_t::IndexLocalType_t previdx = idx; // advance the index immediately because we may be deleting the "current" element
|
||||
idx = m_Queue.Next(idx); // is now the next index
|
||||
|
||||
if ( response.IsQuashed() )
|
||||
{
|
||||
// we can delete this entry now
|
||||
m_Queue.Remove(previdx);
|
||||
}
|
||||
else if ( response.m_fDispatchTime <= gpGlobals->curtime )
|
||||
{
|
||||
// dispatch. we've had bugs where dispatches removed things from inside the queue;
|
||||
// so, as a failsafe, if the queue length changes as a result, start over.
|
||||
int oldLength = m_Queue.Count();
|
||||
DispatchOneResponse(response);
|
||||
if ( m_Queue.Count() < oldLength )
|
||||
{
|
||||
AssertMsg( false, "Response queue length changed in non-reentrant way! FAILSAFE TRIGGERED" );
|
||||
goto failsafe; // ick
|
||||
}
|
||||
|
||||
// we can delete this entry now
|
||||
m_Queue.Remove(previdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Add an expressor owner to this queue.
|
||||
void CResponseQueue::AddExpresserHost(CBaseEntity *host)
|
||||
{
|
||||
EHANDLE ehost(host);
|
||||
// see if it's in there already
|
||||
if (m_ExpresserTargets.HasElement(ehost))
|
||||
{
|
||||
AssertMsg1(false, "Tried to add %s to response queue when it was already in there.", host->GetDebugName());
|
||||
}
|
||||
else
|
||||
{
|
||||
// zip through the queue front to back, first see if there's any invalid handles to replace
|
||||
int count = m_ExpresserTargets.Count();
|
||||
for (int i = 0 ; i < count ; ++i )
|
||||
{
|
||||
if ( !m_ExpresserTargets[i].Get() )
|
||||
{
|
||||
m_ExpresserTargets[i] = ehost;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we're down here we didn't find one to replace, so append the host to the end.
|
||||
m_ExpresserTargets.AddToTail(ehost);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an expresser host from this queue.
|
||||
void CResponseQueue::RemoveExpresserHost(CBaseEntity *host)
|
||||
{
|
||||
int idx = m_ExpresserTargets.Find(host);
|
||||
if (idx == -1)
|
||||
{
|
||||
// AssertMsg1(false, "Tried to remove %s from response queue, but it's not in there to begin with!", host->GetDebugName() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ExpresserTargets.FastRemove(idx);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the expresser for a base entity.
|
||||
/// TODO: Kind of an ugly hack until I get the class hierarchy straightened out.
|
||||
static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt)
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
if ( CBasePlayer *pPlayer = ToBasePlayer(pEnt) )
|
||||
#else
|
||||
if ( CBaseMultiplayerPlayer *pPlayer = dynamic_cast<CBaseMultiplayerPlayer *>(pEnt) )
|
||||
#endif
|
||||
{
|
||||
return pPlayer->GetExpresser();
|
||||
}
|
||||
else if ( CAI_BaseActor *pActor = dynamic_cast<CAI_BaseActor *>(pEnt) )
|
||||
{
|
||||
return pActor->GetExpresser();
|
||||
}
|
||||
/*
|
||||
else if ( CFlexExpresser *pFlex = dynamic_cast<CFlexExpresser *>(pEnt) )
|
||||
{
|
||||
return pFlex->GetExpresser();
|
||||
}
|
||||
*/
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CResponseQueue::CDeferredResponse::Quash()
|
||||
{
|
||||
m_Target = CFollowupTargetSpec_t();
|
||||
m_fDispatchTime = 0;
|
||||
}
|
||||
|
||||
bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
|
||||
{
|
||||
// find the target.
|
||||
CBaseEntity * RESTRICT pTarget = NULL;
|
||||
AI_CriteriaSet &deferredCriteria = response.m_contexts;
|
||||
CAI_Expresser * RESTRICT pEx = NULL;
|
||||
CBaseEntity * RESTRICT pIssuer = response.m_hIssuer.Get(); // MAY BE NULL
|
||||
float followupMaxDistSq;
|
||||
{
|
||||
/*
|
||||
CFlexExpresser * RESTRICT pOrator = CFlexExpresser::AsFlexExpresser( pIssuer );
|
||||
if ( pOrator )
|
||||
{
|
||||
// max dist is overridden. "0" means infinite distance (for orators only),
|
||||
// anything else is a finite distance.
|
||||
if ( pOrator->m_flThenAnyMaxDist > 0 )
|
||||
{
|
||||
followupMaxDistSq = pOrator->m_flThenAnyMaxDist * pOrator->m_flThenAnyMaxDist;
|
||||
}
|
||||
else
|
||||
{
|
||||
followupMaxDistSq = FLT_MAX;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
*/
|
||||
{
|
||||
followupMaxDistSq = rr_followup_maxdist.GetFloat(); // square of max audibility distance
|
||||
followupMaxDistSq *= followupMaxDistSq;
|
||||
}
|
||||
}
|
||||
|
||||
switch (response.m_Target.m_iTargetType)
|
||||
{
|
||||
case kDRT_SPECIFIC:
|
||||
{
|
||||
pTarget = response.m_Target.m_hHandle.Get();
|
||||
}
|
||||
break;
|
||||
case kDRT_ANY:
|
||||
{
|
||||
return DispatchOneResponse_ThenANY( response, &deferredCriteria, pIssuer, followupMaxDistSq );
|
||||
}
|
||||
break;
|
||||
case kDRT_ALL:
|
||||
{
|
||||
bool bSaidAnything = false;
|
||||
Vector issuerLocation;
|
||||
if ( pIssuer )
|
||||
{
|
||||
issuerLocation = pIssuer->GetAbsOrigin();
|
||||
}
|
||||
|
||||
// find all characters
|
||||
int numExprs = GetNumExpresserTargets();
|
||||
for ( int i = 0 ; i < numExprs; ++i )
|
||||
{
|
||||
pTarget = GetExpresserHost(i);
|
||||
float distIssuerToTargetSq = 0.0f;
|
||||
if ( pIssuer )
|
||||
{
|
||||
distIssuerToTargetSq = (pTarget->GetAbsOrigin() - issuerLocation).LengthSqr();
|
||||
if ( distIssuerToTargetSq > followupMaxDistSq )
|
||||
continue; // too far
|
||||
}
|
||||
|
||||
pEx = InferExpresserFromBaseEntity(pTarget);
|
||||
if ( !pEx || pTarget == pIssuer )
|
||||
continue;
|
||||
AI_CriteriaSet characterCriteria;
|
||||
pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
|
||||
characterCriteria.Merge(&deferredCriteria);
|
||||
if ( pIssuer )
|
||||
{
|
||||
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) );
|
||||
}
|
||||
AI_Response prospectiveResponse;
|
||||
if ( pEx->FindResponse( prospectiveResponse, response.m_concept, &characterCriteria ) )
|
||||
{
|
||||
// dispatch it
|
||||
bSaidAnything = pEx->SpeakDispatchResponse(response.m_concept, &prospectiveResponse, &deferredCriteria) || bSaidAnything ;
|
||||
}
|
||||
}
|
||||
|
||||
return bSaidAnything;
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// WTF?
|
||||
AssertMsg1( false, "Unknown deferred response type %d\n", response.m_Target.m_iTargetType );
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pTarget)
|
||||
return false; // we're done right here.
|
||||
|
||||
// Get the expresser for the target.
|
||||
pEx = InferExpresserFromBaseEntity(pTarget);
|
||||
if (!pEx)
|
||||
return false;
|
||||
|
||||
|
||||
AI_CriteriaSet characterCriteria;
|
||||
pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
|
||||
characterCriteria.Merge(&deferredCriteria);
|
||||
pEx->Speak( response.m_concept, &characterCriteria );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
ConVar rr_thenany_score_slop( "rr_thenany_score_slop", "0.0", FCVAR_CHEAT, "When computing respondents for a 'THEN ANY' rule, all rule-matching scores within this much of the best score will be considered." );
|
||||
#define EXARRAYMAX 32 // maximum number of prospective expressers in the array (hardcoded for simplicity)
|
||||
bool CResponseQueue::DispatchOneResponse_ThenANY( CDeferredResponse &response, AI_CriteriaSet * RESTRICT pDeferredCriteria, CBaseEntity * const RESTRICT pIssuer, float followupMaxDistSq )
|
||||
{
|
||||
CBaseEntity * RESTRICT pTarget = NULL;
|
||||
CAI_Expresser * RESTRICT pEx = NULL;
|
||||
float bestScore = 0;
|
||||
float slop = rr_thenany_score_slop.GetFloat();
|
||||
Vector issuerLocation;
|
||||
if ( pIssuer )
|
||||
{
|
||||
issuerLocation = pIssuer->GetAbsOrigin();
|
||||
}
|
||||
|
||||
// this is an array of prospective respondents.
|
||||
CAI_Expresser * RESTRICT pBestEx[EXARRAYMAX];
|
||||
AI_Response responseToSay[EXARRAYMAX];
|
||||
int numExFound = 0; // and this is the high water mark for the array.
|
||||
|
||||
// Here's the algorithm: we're going to walk through all the characters, finding the
|
||||
// highest scoring ones for this rule. Let the highest score be called k.
|
||||
// Because there may be (n) many characters all scoring k, we store an array of
|
||||
// all characters with score k, then choose randomly from that array at return.
|
||||
// We also define an allowable error for k in the global cvar
|
||||
// rr_thenany_score_slop , which may be zero.
|
||||
|
||||
// find all characters (except the issuer)
|
||||
int numExprs = GetNumExpresserTargets();
|
||||
AssertMsg1( numExprs <= EXARRAYMAX, "Response queue has %d possible expresser targets, please increase EXARRAYMAX ", numExprs );
|
||||
for ( int i = 0 ; i < numExprs; ++i )
|
||||
{
|
||||
pTarget = GetExpresserHost(i);
|
||||
if ( pTarget == pIssuer )
|
||||
continue; // don't dispatch to myself
|
||||
|
||||
if ( !pTarget->IsAlive() )
|
||||
continue; // dead men tell no tales
|
||||
|
||||
float distIssuerToTargetSq = 0.0f;
|
||||
if ( pIssuer )
|
||||
{
|
||||
distIssuerToTargetSq = (pTarget->GetAbsOrigin() - issuerLocation).LengthSqr();
|
||||
if ( distIssuerToTargetSq > followupMaxDistSq )
|
||||
continue; // too far
|
||||
}
|
||||
|
||||
pEx = InferExpresserFromBaseEntity(pTarget);
|
||||
if ( !pEx )
|
||||
continue;
|
||||
|
||||
AI_CriteriaSet characterCriteria;
|
||||
pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
|
||||
characterCriteria.Merge( pDeferredCriteria );
|
||||
pTarget->ModifyOrAppendDerivedCriteria( characterCriteria );
|
||||
if ( pIssuer )
|
||||
{
|
||||
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) );
|
||||
}
|
||||
AI_Response prospectiveResponse;
|
||||
|
||||
#ifdef MAPBASE
|
||||
pEx->SetUsingProspectiveResponses( true );
|
||||
#endif
|
||||
|
||||
if ( pEx->FindResponse( prospectiveResponse, response.m_concept, &characterCriteria ) )
|
||||
{
|
||||
float score = prospectiveResponse.GetMatchScore();
|
||||
if ( score > 0 && !prospectiveResponse.IsEmpty() ) // ignore scores that are zero, regardless of slop
|
||||
{
|
||||
// if this score is better than all we've seen (outside the slop), then replace the array with
|
||||
// an entry just to this expresser
|
||||
if ( score > bestScore + slop )
|
||||
{
|
||||
responseToSay[0] = prospectiveResponse;
|
||||
pBestEx[0] = pEx;
|
||||
bestScore = score;
|
||||
numExFound = 1;
|
||||
}
|
||||
else if ( score >= bestScore - slop ) // if this score is at least as good as the best we've seen, but not better than all
|
||||
{
|
||||
if ( numExFound >= EXARRAYMAX )
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
pEx->SetUsingProspectiveResponses( false );
|
||||
#endif
|
||||
continue; // SAFETY: don't overflow the array
|
||||
}
|
||||
|
||||
responseToSay[numExFound] = prospectiveResponse;
|
||||
pBestEx[numExFound] = pEx;
|
||||
bestScore = fpmax( score, bestScore );
|
||||
numExFound += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MAPBASE
|
||||
pEx->SetUsingProspectiveResponses( false );
|
||||
#endif
|
||||
}
|
||||
|
||||
// if I have a response, dispatch it.
|
||||
if ( numExFound > 0 )
|
||||
{
|
||||
// get a random number between 0 and the responses found
|
||||
int iSelect = numExFound > 1 ? RandomInt( 0, numExFound - 1 ) : 0;
|
||||
|
||||
if ( pBestEx[iSelect] != NULL )
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
pBestEx[iSelect]->MarkResponseAsUsed( responseToSay + iSelect );
|
||||
#endif
|
||||
return pBestEx[iSelect]->SpeakDispatchResponse( response.m_concept, responseToSay + iSelect, pDeferredCriteria );
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertMsg( false, "Response queue somehow found a response, but no expresser for it.\n" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // I did not find a response.
|
||||
return false;
|
||||
}
|
||||
|
||||
return false; // just in case
|
||||
}
|
||||
|
||||
void CResponseQueue::Evacuate()
|
||||
{
|
||||
m_Queue.RemoveAll();
|
||||
}
|
||||
|
||||
#undef EXARRAYMAX
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RESPONSE QUEUE MANAGER
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void CResponseQueueManager::LevelInitPreEntity( void )
|
||||
{
|
||||
if (m_pQueue == NULL)
|
||||
{
|
||||
m_pQueue = new CResponseQueue(AI_RESPONSE_QUEUE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
CResponseQueueManager::~CResponseQueueManager()
|
||||
{
|
||||
if (m_pQueue != NULL)
|
||||
{
|
||||
delete m_pQueue;
|
||||
m_pQueue = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CResponseQueueManager::Shutdown()
|
||||
{
|
||||
if (m_pQueue != NULL)
|
||||
{
|
||||
delete m_pQueue;
|
||||
m_pQueue = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CResponseQueueManager::FrameUpdatePostEntityThink()
|
||||
{
|
||||
Assert(m_pQueue);
|
||||
m_pQueue->PerFrameDispatch();
|
||||
}
|
||||
|
||||
CResponseQueueManager g_ResponseQueueManager( "CResponseQueueManager" );
|
||||
|
239
sp/src/game/server/ai_speechqueue.h
Normal file
239
sp/src/game/server/ai_speechqueue.h
Normal file
@ -0,0 +1,239 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: An event queue of AI concepts that dispatches them to appropriate characters.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef AI_SPEECHQUEUE_H
|
||||
#define AI_SPEECHQUEUE_H
|
||||
|
||||
#if defined( _WIN32 )
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "ai_speech.h"
|
||||
|
||||
#define AI_RESPONSE_QUEUE_SIZE 64
|
||||
|
||||
enum DeferredResponseTarget_t // possible targets for a deferred response
|
||||
{
|
||||
kDRT_ANY, // best matching respondent within range -- except for the one in the m_hTarget handle
|
||||
kDRT_ALL, // send to everyone in range -- except for the one in the m_hTarget handle
|
||||
kDRT_SPECIFIC, // a specific entity is targeted
|
||||
|
||||
kDRT_MAX, // high water mark
|
||||
};
|
||||
|
||||
// Allows you to postpone AI speech concepts to a later time, or to direct them to
|
||||
// a specific character, or all of them.
|
||||
class CResponseQueue
|
||||
{
|
||||
//////////////////// Local types ////////////////////
|
||||
public:
|
||||
|
||||
// We pack up contexts to send along with the concept.
|
||||
// For now I'll just copy criteria sets, but it will be better to do something
|
||||
// more efficient in the future.
|
||||
typedef AI_CriteriaSet DeferredContexts_t;
|
||||
|
||||
struct CFollowupTargetSpec_t ///< to whom a followup is directed. Can be a specific entity or something more exotic.
|
||||
{
|
||||
DeferredResponseTarget_t m_iTargetType; ///< ANY, ALL, or SPECIFIC. If specific, pass through a handle to:
|
||||
EHANDLE m_hHandle; ///< a specific target for the message, or a specific character to OMIT.
|
||||
inline bool IsValid( void ) const;
|
||||
|
||||
// constructors/destructors
|
||||
explicit CFollowupTargetSpec_t(const DeferredResponseTarget_t &targetType, const EHANDLE &handle)
|
||||
: m_iTargetType(targetType), m_hHandle(handle)
|
||||
{};
|
||||
explicit CFollowupTargetSpec_t(const EHANDLE &handle)
|
||||
: m_iTargetType(kDRT_SPECIFIC), m_hHandle(handle)
|
||||
{};
|
||||
CFollowupTargetSpec_t(DeferredResponseTarget_t target) // eg, ANY, ALL, etc.
|
||||
: m_iTargetType(target)
|
||||
{
|
||||
AssertMsg(m_iTargetType != kDRT_SPECIFIC, "Response rule followup tried to specify an entity target, but didn't provide the target.\n" );
|
||||
}
|
||||
CFollowupTargetSpec_t(void) // default: invalid
|
||||
: m_iTargetType(kDRT_MAX)
|
||||
{};
|
||||
};
|
||||
|
||||
/// A single deferred response.
|
||||
struct CDeferredResponse
|
||||
{
|
||||
AIConcept_t m_concept;
|
||||
DeferredContexts_t m_contexts; ///< contexts to send along with the concept
|
||||
float m_fDispatchTime;
|
||||
EHANDLE m_hIssuer; ///< an entity, if issued by an entity
|
||||
/*
|
||||
DeferredResponseTarget_t m_iTargetType;
|
||||
EHANDLE m_hTarget; // May be invalid.
|
||||
*/
|
||||
CFollowupTargetSpec_t m_Target;
|
||||
|
||||
inline void Init( const AIConcept_t &concept, const AI_CriteriaSet * RESTRICT contexts, float dtime, const CFollowupTargetSpec_t &target, CBaseEntity *pIssuer );
|
||||
inline bool IsQuashed() { return !m_Target.IsValid(); }
|
||||
void Quash(); ///< make this response invalid.
|
||||
};
|
||||
/// write
|
||||
static void DeferContextsFromCriteriaSet( DeferredContexts_t &contextsOut, const AI_CriteriaSet *criteriaIn );
|
||||
|
||||
//////////////////// Methods ////////////////////
|
||||
public:
|
||||
CResponseQueue( int queueSize );
|
||||
|
||||
/// Add a deferred response.
|
||||
void Add( const AIConcept_t &concept, ///< concept to dispatch
|
||||
const AI_CriteriaSet * RESTRICT contexts, ///< the contexts that come with it (may be NULL)
|
||||
float time, ///< when to dispatch it. You can specify a time of zero to mean "immediately."
|
||||
const CFollowupTargetSpec_t &targetspec, /// All information necessary to target this response
|
||||
CBaseEntity *pIssuer = NULL ///< the entity who should not respond if this is a ANY or ALL rule. (eg, don't let people talk to themselves.)
|
||||
);
|
||||
|
||||
/// Remove all deferred responses matching the concept and issuer.
|
||||
void Remove( const AIConcept_t &concept, ///< concept to dispatch
|
||||
CBaseEntity * const pIssuer = NULL ///< the entity issuing the response, if one exists.
|
||||
);
|
||||
|
||||
/// Remove all deferred responses queued to be spoken by given character
|
||||
void RemoveSpeechQueuedFor( const CBaseEntity *pSpeaker );
|
||||
|
||||
/// Empty out all pending events
|
||||
void Evacuate();
|
||||
|
||||
/// Go through and dispatch any deferred responses.
|
||||
void PerFrameDispatch();
|
||||
|
||||
/// Add an expressor owner to this queue.
|
||||
void AddExpresserHost(CBaseEntity *host);
|
||||
|
||||
/// Remove an expresser host from this queue.
|
||||
void RemoveExpresserHost(CBaseEntity *host);
|
||||
|
||||
/// Iterate over potential expressers for this queue
|
||||
inline int GetNumExpresserTargets() const;
|
||||
inline CBaseEntity *GetExpresserHost(int which) const;
|
||||
|
||||
protected:
|
||||
/// Actually send off one response to a consumer
|
||||
/// Return true if dispatch succeeded
|
||||
bool DispatchOneResponse( CDeferredResponse &response );
|
||||
|
||||
private:
|
||||
/// Helper function for one case in DispatchOneResponse
|
||||
/// (for better organization)
|
||||
bool DispatchOneResponse_ThenANY( CDeferredResponse &response, AI_CriteriaSet * RESTRICT pDeferredCriteria, CBaseEntity * const RESTRICT pIssuer, float followupMaxDistSq );
|
||||
|
||||
//////////////////// Data ////////////////////
|
||||
protected:
|
||||
typedef CUtlFixedLinkedList< CDeferredResponse > QueueType_t;
|
||||
QueueType_t m_Queue; // the queue of deferred responses, will eventually be sorted
|
||||
/// Note about the queue type: if you move to replace it with a sorted priority queue,
|
||||
/// make sure it is a type such that an iterator is not invalidated by inserts and deletes.
|
||||
/// CResponseQueue::PerFrameDispatch() iterates over the queue calling DispatchOneResponse
|
||||
/// on each in turn, and those responses may very easily add new events to the queue.
|
||||
/// A crash will result if the iterator used in CResponseQueue::PerFrameDispatch()'s loop
|
||||
/// becomes invalid.
|
||||
|
||||
CUtlVector<EHANDLE> m_ExpresserTargets; // a list of legitimate expresser targets
|
||||
};
|
||||
|
||||
inline void CResponseQueue::CDeferredResponse::Init(const AIConcept_t &concept, const AI_CriteriaSet * RESTRICT contexts, float dtime, const CFollowupTargetSpec_t &target, CBaseEntity *pIssuer )
|
||||
{
|
||||
m_concept = concept;
|
||||
m_fDispatchTime = dtime;
|
||||
/*
|
||||
m_iTargetType = targetType;
|
||||
m_hTarget = handle ;
|
||||
*/
|
||||
m_Target = target;
|
||||
m_hIssuer = pIssuer;
|
||||
DeferContextsFromCriteriaSet(m_contexts, contexts);
|
||||
}
|
||||
|
||||
int CResponseQueue::GetNumExpresserTargets() const
|
||||
{
|
||||
return m_ExpresserTargets.Count();
|
||||
}
|
||||
|
||||
CBaseEntity *CResponseQueue::GetExpresserHost(int which) const
|
||||
{
|
||||
return m_ExpresserTargets[which];
|
||||
}
|
||||
|
||||
|
||||
// The wrapper game system that contains a response queue, and ticks it each frame.
|
||||
|
||||
class CResponseQueueManager : public CAutoGameSystemPerFrame
|
||||
{
|
||||
public:
|
||||
CResponseQueueManager(char const *name) : CAutoGameSystemPerFrame( name )
|
||||
{
|
||||
m_pQueue = NULL;
|
||||
}
|
||||
virtual ~CResponseQueueManager(void);
|
||||
virtual void Shutdown();
|
||||
virtual void FrameUpdatePostEntityThink( void );
|
||||
virtual void LevelInitPreEntity( void );
|
||||
|
||||
inline CResponseQueue *GetQueue(void) { Assert(m_pQueue); return m_pQueue; }
|
||||
|
||||
protected:
|
||||
CResponseQueue *m_pQueue;
|
||||
};
|
||||
|
||||
|
||||
// Valid if the target type enum is within bounds. Furthermore if it
|
||||
// specifies a specific entity, that handle must be valid.
|
||||
bool CResponseQueue::CFollowupTargetSpec_t::IsValid( void ) const
|
||||
{
|
||||
if (m_iTargetType >= kDRT_MAX)
|
||||
return false;
|
||||
if (m_iTargetType < 0)
|
||||
return false;
|
||||
if (m_iTargetType == kDRT_SPECIFIC && !m_hHandle.IsValid())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
extern CResponseQueueManager g_ResponseQueueManager;
|
||||
|
||||
|
||||
// Handy global helper funcs
|
||||
|
||||
/// Automatically queue up speech to happen immediately -- calls straight through to response rules add
|
||||
inline void QueueSpeak( const AIConcept_t &concept, ///< concept name to say
|
||||
const CResponseQueue::CFollowupTargetSpec_t& targetspec, ///< kDRT_ANY, kDRT_ALL, etc
|
||||
CBaseEntity *pIssuer = NULL ///< if specifying ANY or ALL, use this to specify the one you *don't* want to speak
|
||||
)
|
||||
{
|
||||
return g_ResponseQueueManager.GetQueue()->Add( concept, NULL, 0.0f, targetspec, pIssuer );
|
||||
}
|
||||
|
||||
/// Automatically queue up speech to happen immediately -- calls straight through to response rules add
|
||||
inline void QueueSpeak( const AIConcept_t &concept, ///< concept name to say
|
||||
const CResponseQueue::CFollowupTargetSpec_t& targetspec, ///< kDRT_ANY, kDRT_ALL, etc
|
||||
const AI_CriteriaSet &criteria, ///< criteria to pass in
|
||||
CBaseEntity *pIssuer = NULL ///< if specifying ANY or ALL, use this to specify the one you *don't* want to speak
|
||||
)
|
||||
{
|
||||
return g_ResponseQueueManager.GetQueue()->Add( concept, &criteria, 0.0f, targetspec, pIssuer );
|
||||
}
|
||||
|
||||
/// Automatically queue up speech to happen immediately -- calls straight through to response rules add
|
||||
inline void QueueSpeak( const AIConcept_t &concept, ///< concept name to say
|
||||
const EHANDLE &target, ///< which entity shall speak
|
||||
float delay, ///< how far in the future to speak
|
||||
const AI_CriteriaSet &criteria, ///< criteria to pass in
|
||||
CBaseEntity *pIssuer = NULL )
|
||||
{
|
||||
return g_ResponseQueueManager.GetQueue()->Add( concept, &criteria, gpGlobals->curtime + delay,
|
||||
CResponseQueue::CFollowupTargetSpec_t(target), pIssuer );
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // AI_SPEECHQUEUE_H
|
@ -506,6 +506,11 @@ void CBaseAnimating::StudioFrameAdvanceInternal( CStudioHdr *pStudioHdr, float f
|
||||
float flNewCycle = GetCycle() + flCycleDelta;
|
||||
if (flNewCycle < 0.0 || flNewCycle >= 1.0)
|
||||
{
|
||||
if (flNewCycle >= 1.0f)
|
||||
{
|
||||
ReachedEndOfSequence();
|
||||
}
|
||||
|
||||
if (m_bSequenceLoops)
|
||||
{
|
||||
flNewCycle -= (int)(flNewCycle);
|
||||
|
@ -84,6 +84,7 @@ public:
|
||||
virtual void StudioFrameAdvance(); // advance animation frame to some time in the future
|
||||
void StudioFrameAdvanceManual( float flInterval );
|
||||
bool IsValidSequence( int iSequence );
|
||||
virtual void ReachedEndOfSequence() { return; }
|
||||
|
||||
inline float GetPlaybackRate();
|
||||
inline void SetPlaybackRate( float rate );
|
||||
|
@ -1725,25 +1725,6 @@ Killed
|
||||
*/
|
||||
void CBaseCombatCharacter::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
if (m_ScriptScope.IsInitialized() && g_Hook_OnDeath.CanRunInScope( m_ScriptScope ))
|
||||
{
|
||||
HSCRIPT hInfo = g_pScriptVM->RegisterInstance( const_cast<CTakeDamageInfo*>(&info) );
|
||||
|
||||
// info
|
||||
ScriptVariant_t functionReturn;
|
||||
ScriptVariant_t args[] = { ScriptVariant_t( hInfo ) };
|
||||
if ( g_Hook_OnDeath.Call( m_ScriptScope, &functionReturn, args ) && (functionReturn.m_type == FIELD_BOOLEAN && functionReturn.m_bool == false) )
|
||||
{
|
||||
// Make this entity cheat death
|
||||
g_pScriptVM->RemoveInstance( hInfo );
|
||||
return;
|
||||
}
|
||||
|
||||
g_pScriptVM->RemoveInstance( hInfo );
|
||||
}
|
||||
#endif
|
||||
|
||||
extern ConVar npc_vphysics;
|
||||
|
||||
// Advance life state to dying
|
||||
@ -2893,6 +2874,12 @@ int CBaseCombatCharacter::OnTakeDamage( const CTakeDamageInfo &info )
|
||||
#endif
|
||||
if ( m_iHealth <= 0 )
|
||||
{
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
// False = Cheat death
|
||||
if (ScriptDeathHook( const_cast<CTakeDamageInfo*>(&info) ) == false)
|
||||
return retVal;
|
||||
#endif
|
||||
|
||||
IPhysicsObject *pPhysics = VPhysicsGetObject();
|
||||
if ( pPhysics )
|
||||
{
|
||||
|
@ -66,6 +66,9 @@
|
||||
#include "mapbase/matchers.h"
|
||||
#include "mapbase/datadesc_mod.h"
|
||||
#endif
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
#include "ai_speech.h"
|
||||
#endif
|
||||
|
||||
#if defined( TF_DLL )
|
||||
#include "tf_gamerules.h"
|
||||
@ -1716,22 +1719,9 @@ int CBaseEntity::VPhysicsTakeDamage( const CTakeDamageInfo &info )
|
||||
void CBaseEntity::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
if (m_ScriptScope.IsInitialized() && g_Hook_OnDeath.CanRunInScope( m_ScriptScope ))
|
||||
{
|
||||
HSCRIPT hInfo = g_pScriptVM->RegisterInstance( const_cast<CTakeDamageInfo*>(&info) );
|
||||
|
||||
// info
|
||||
ScriptVariant_t functionReturn;
|
||||
ScriptVariant_t args[] = { ScriptVariant_t( hInfo ) };
|
||||
if ( g_Hook_OnDeath.Call( m_ScriptScope, &functionReturn, args ) && (functionReturn.m_type == FIELD_BOOLEAN && functionReturn.m_bool == false) )
|
||||
{
|
||||
// Make this entity cheat death
|
||||
g_pScriptVM->RemoveInstance( hInfo );
|
||||
return;
|
||||
}
|
||||
|
||||
g_pScriptVM->RemoveInstance( hInfo );
|
||||
}
|
||||
// False = Cheat death
|
||||
if (ScriptDeathHook( const_cast<CTakeDamageInfo*>(&info) ) == false)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if( info.GetAttacker() )
|
||||
@ -2311,6 +2301,8 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities"
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelKeyValues, "GetModelKeyValues", "Get a KeyValue class instance on this entity's model")
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
DEFINE_SCRIPTFUNC( Activate, "" )
|
||||
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptIsVisible, "IsVisible", "Check if the specified position can be visible to this entity." )
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptIsEntVisible, "IsEntVisible", "Check if the specified entity can be visible to this entity." )
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptIsVisibleWithMask, "IsVisibleWithMask", "Check if the specified position can be visible to this entity with a specific trace mask." )
|
||||
@ -2408,7 +2400,10 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities"
|
||||
DEFINE_SCRIPTFUNC( SetFriction, "" )
|
||||
DEFINE_SCRIPTFUNC( GetMass, "" )
|
||||
DEFINE_SCRIPTFUNC( SetMass, "" )
|
||||
|
||||
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptGetSolid, "GetSolid", "" )
|
||||
DEFINE_SCRIPTFUNC_NAMED( ScriptSetSolid, "SetSolid", "" )
|
||||
|
||||
DEFINE_SCRIPTFUNC( GetSolidFlags, "Get solid flags" )
|
||||
DEFINE_SCRIPTFUNC( AddSolidFlags, "Add solid flags" )
|
||||
DEFINE_SCRIPTFUNC( RemoveSolidFlags, "Remove solid flags" )
|
||||
@ -4660,6 +4655,16 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator,
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
bool CBaseEntity::ScriptAcceptInput( const char *szInputName, const char *szValue, HSCRIPT hActivator, HSCRIPT hCaller )
|
||||
{
|
||||
variant_t value;
|
||||
value.SetString( MAKE_STRING( szValue ) );
|
||||
|
||||
return AcceptInput( szInputName, ToEnt( hActivator ), ToEnt( hCaller ), value, 0 );
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -4692,12 +4697,26 @@ bool CBaseEntity::ScriptInputHook( const char *szInputName, CBaseEntity *pActiva
|
||||
}
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
bool CBaseEntity::ScriptAcceptInput( const char *szInputName, const char *szValue, HSCRIPT hActivator, HSCRIPT hCaller )
|
||||
bool CBaseEntity::ScriptDeathHook( CTakeDamageInfo *info )
|
||||
{
|
||||
variant_t value;
|
||||
value.SetString( MAKE_STRING(szValue) );
|
||||
if (m_ScriptScope.IsInitialized() && g_Hook_OnDeath.CanRunInScope( m_ScriptScope ))
|
||||
{
|
||||
HSCRIPT hInfo = g_pScriptVM->RegisterInstance( info );
|
||||
|
||||
return AcceptInput( szInputName, ToEnt(hActivator), ToEnt(hCaller), value, 0 );
|
||||
// info
|
||||
ScriptVariant_t functionReturn;
|
||||
ScriptVariant_t args[] = { ScriptVariant_t( hInfo ) };
|
||||
if ( g_Hook_OnDeath.Call( m_ScriptScope, &functionReturn, args ) && (functionReturn.m_type == FIELD_BOOLEAN && functionReturn.m_bool == false) )
|
||||
{
|
||||
// Make this entity cheat death
|
||||
g_pScriptVM->RemoveInstance( hInfo );
|
||||
return false;
|
||||
}
|
||||
|
||||
g_pScriptVM->RemoveInstance( hInfo );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -7657,7 +7676,11 @@ bool CBaseEntity::HasContext( const char *nameandvalue ) const
|
||||
const char *p = nameandvalue;
|
||||
while ( p )
|
||||
{
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL, nameandvalue );
|
||||
#else
|
||||
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL );
|
||||
#endif
|
||||
|
||||
return HasContext( key, value );
|
||||
}
|
||||
@ -7704,7 +7727,11 @@ void CBaseEntity::RemoveContext( const char *contextName )
|
||||
while ( p )
|
||||
{
|
||||
duration = 0.0f;
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), &duration, contextName );
|
||||
#else
|
||||
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), &duration );
|
||||
#endif
|
||||
if ( duration )
|
||||
{
|
||||
duration += gpGlobals->curtime;
|
||||
@ -8775,56 +8802,76 @@ void CBaseEntity::AddContext( const char *contextName )
|
||||
{
|
||||
char key[ 128 ];
|
||||
char value[ 128 ];
|
||||
float duration;
|
||||
float duration = 0.0f;
|
||||
|
||||
const char *p = contextName;
|
||||
while ( p )
|
||||
{
|
||||
duration = 0.0f;
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), &duration, contextName );
|
||||
#else
|
||||
p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), &duration );
|
||||
#endif
|
||||
if ( duration )
|
||||
{
|
||||
duration += gpGlobals->curtime;
|
||||
}
|
||||
|
||||
int iIndex = FindContextByName( key );
|
||||
if ( iIndex != -1 )
|
||||
{
|
||||
// Set the existing context to the new value
|
||||
m_ResponseContexts[iIndex].m_iszValue = AllocPooledString( value );
|
||||
m_ResponseContexts[iIndex].m_fExpirationTime = duration;
|
||||
continue;
|
||||
}
|
||||
|
||||
ResponseContext_t newContext;
|
||||
newContext.m_iszName = AllocPooledString( key );
|
||||
newContext.m_iszValue = AllocPooledString( value );
|
||||
newContext.m_fExpirationTime = duration;
|
||||
|
||||
m_ResponseContexts.AddToTail( newContext );
|
||||
AddContext( key, value, duration );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MAPBASE
|
||||
void CBaseEntity::AddContext( const char *name, const char *value, float duration )
|
||||
{
|
||||
int iIndex = FindContextByName( name );
|
||||
if ( iIndex != -1 )
|
||||
{
|
||||
// Set the existing context to the new value
|
||||
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
char buf[64];
|
||||
if ( RR::CApplyContextOperator::FindOperator( value )->Apply(
|
||||
m_ResponseContexts[iIndex].m_iszValue.ToCStr(), value, buf, sizeof(buf) ) )
|
||||
{
|
||||
m_ResponseContexts[iIndex].m_iszValue = AllocPooledString( buf );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "RR: could not apply operator %s to prior value %s\n",
|
||||
value, m_ResponseContexts[iIndex].m_iszValue.ToCStr() );
|
||||
m_ResponseContexts[iIndex].m_iszValue = AllocPooledString( value );
|
||||
}
|
||||
#else
|
||||
m_ResponseContexts[iIndex].m_iszValue = AllocPooledString( value );
|
||||
m_ResponseContexts[iIndex].m_fExpirationTime = duration;
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseContext_t newContext;
|
||||
newContext.m_iszName = AllocPooledString( name );
|
||||
newContext.m_iszValue = AllocPooledString( value );
|
||||
newContext.m_fExpirationTime = duration;
|
||||
|
||||
m_ResponseContexts.AddToTail( newContext );
|
||||
}
|
||||
#endif
|
||||
m_ResponseContexts[iIndex].m_fExpirationTime = duration;
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseContext_t newContext;
|
||||
newContext.m_iszName = AllocPooledString( name );
|
||||
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
char buf[64];
|
||||
if ( RR::CApplyContextOperator::FindOperator( value )->Apply(
|
||||
NULL, value, buf, sizeof(buf) ) )
|
||||
{
|
||||
newContext.m_iszValue = AllocPooledString( buf );
|
||||
}
|
||||
else
|
||||
{
|
||||
newContext.m_iszValue = AllocPooledString( value );
|
||||
}
|
||||
#else
|
||||
newContext.m_iszValue = AllocPooledString( value );
|
||||
#endif
|
||||
|
||||
newContext.m_fExpirationTime = duration;
|
||||
|
||||
m_ResponseContexts.AddToTail( newContext );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
@ -8976,6 +9023,11 @@ void CBaseEntity::InputChangeVariable( inputdata_t &inputdata )
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseEntity::DispatchResponse( const char *conceptName )
|
||||
{
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
#undef IResponseSystem
|
||||
using namespace ResponseRules;
|
||||
#endif
|
||||
|
||||
IResponseSystem *rs = GetResponseSystem();
|
||||
if ( !rs )
|
||||
return;
|
||||
@ -9006,6 +9058,61 @@ void CBaseEntity::DispatchResponse( const char *conceptName )
|
||||
// Handle the response here...
|
||||
char response[ 256 ];
|
||||
result.GetResponse( response, sizeof( response ) );
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
switch (result.GetType())
|
||||
{
|
||||
case ResponseRules::RESPONSE_SPEAK:
|
||||
{
|
||||
EmitSound(response);
|
||||
}
|
||||
break;
|
||||
case ResponseRules::RESPONSE_SENTENCE:
|
||||
{
|
||||
int sentenceIndex = SENTENCEG_Lookup(response);
|
||||
if (sentenceIndex == -1)
|
||||
{
|
||||
// sentence not found
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME: Get pitch from npc?
|
||||
CPASAttenuationFilter filter(this);
|
||||
CBaseEntity::EmitSentenceByIndex(filter, entindex(), CHAN_VOICE, sentenceIndex, 1, result.GetSoundLevel(), 0, PITCH_NORM);
|
||||
}
|
||||
break;
|
||||
case ResponseRules::RESPONSE_SCENE:
|
||||
{
|
||||
// Try to fire scene w/o an actor
|
||||
InstancedScriptedScene(NULL, response);
|
||||
}
|
||||
break;
|
||||
case ResponseRules::RESPONSE_PRINT:
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
case ResponseRules::RESPONSE_ENTITYIO:
|
||||
{
|
||||
CAI_Expresser::FireEntIOFromResponse(response, this);
|
||||
break;
|
||||
}
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
case ResponseRules::RESPONSE_VSCRIPT:
|
||||
{
|
||||
CAI_Expresser::RunScriptResponse( this, response, &set, false );
|
||||
break;
|
||||
}
|
||||
case ResponseRules::RESPONSE_VSCRIPT_FILE:
|
||||
{
|
||||
CAI_Expresser::RunScriptResponse( this, response, &set, true );
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
// Don't know how to handle .vcds!!!
|
||||
break;
|
||||
}
|
||||
#else
|
||||
#ifdef MAPBASE
|
||||
if (response[0] == '$')
|
||||
{
|
||||
@ -9100,6 +9207,7 @@ void CBaseEntity::DispatchResponse( const char *conceptName )
|
||||
// Don't know how to handle .vcds!!!
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -9426,6 +9534,7 @@ void CBaseEntity::RemoveRecipientsIfNotCloseCaptioning( CRecipientFilter& filter
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MAPBASE // Moved to SoundEmitterSystem.cpp
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Wrapper to emit a sentence and also a close caption token for the sentence as appropriate.
|
||||
// Input : filter -
|
||||
@ -9448,6 +9557,7 @@ void CBaseEntity::EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex,
|
||||
enginesound->EmitSentenceByIndex( filter, iEntIndex, iChannel, iSentenceIndex,
|
||||
flVolume, iSoundlevel, iFlags, iPitch, 0, pOrigin, pDirection, &dummy, bUpdatePositions, soundtime );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void CBaseEntity::SetRefEHandle( const CBaseHandle &handle )
|
||||
|
@ -20,6 +20,10 @@
|
||||
#include "ServerNetworkProperty.h"
|
||||
#include "shareddefs.h"
|
||||
#include "engine/ivmodelinfo.h"
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
#include "AI_Criteria.h"
|
||||
#include "AI_ResponseSystem.h"
|
||||
#endif
|
||||
|
||||
#include "vscript/ivscript.h"
|
||||
#include "vscript_server.h"
|
||||
@ -29,8 +33,10 @@ class CDmgAccumulator;
|
||||
|
||||
struct CSoundParameters;
|
||||
|
||||
#ifndef NEW_RESPONSE_SYSTEM
|
||||
class AI_CriteriaSet;
|
||||
class IResponseSystem;
|
||||
#endif
|
||||
class IEntitySaveUtils;
|
||||
class CRecipientFilter;
|
||||
class CStudioHdr;
|
||||
@ -39,6 +45,11 @@ class CStudioHdr;
|
||||
// FIXME: Could do this in the script file by making it required and bumping up weighting there instead...
|
||||
#define CONCEPT_WEIGHT 5.0f
|
||||
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
// Relax the namespace standard a bit so that less code has to be changed
|
||||
#define IResponseSystem ResponseRules::IResponseSystem
|
||||
#endif
|
||||
|
||||
typedef CHandle<CBaseEntity> EHANDLE;
|
||||
|
||||
#define MANUALMODE_GETSET_PROP(type, accessorName, varName) \
|
||||
@ -341,10 +352,10 @@ struct thinkfunc_t
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
struct scriptthinkfunc_t
|
||||
{
|
||||
int m_nNextThinkTick;
|
||||
HSCRIPT m_hfnThink;
|
||||
unsigned short m_iContextHash;
|
||||
bool m_bNoParam;
|
||||
float m_flNextThink;
|
||||
HSCRIPT m_hfnThink;
|
||||
unsigned m_iContextHash;
|
||||
bool m_bNoParam;
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -682,6 +693,9 @@ public:
|
||||
#endif
|
||||
|
||||
bool ScriptInputHook( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, ScriptVariant_t &functionReturn );
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
bool ScriptDeathHook( CTakeDamageInfo *info );
|
||||
#endif
|
||||
|
||||
//
|
||||
// Input handlers.
|
||||
@ -1001,10 +1015,10 @@ public:
|
||||
const char *GetContextValue( const char *contextName ) const;
|
||||
float GetContextExpireTime( const char *name );
|
||||
void RemoveContext( const char *nameandvalue );
|
||||
void AddContext( const char *name, const char *value, float duration = 0.0f );
|
||||
#endif
|
||||
|
||||
void AddContext( const char *nameandvalue );
|
||||
void AddContext( const char *name, const char *value, float duration = 0.0f );
|
||||
|
||||
protected:
|
||||
CUtlVector< ResponseContext_t > m_ResponseContexts;
|
||||
@ -1294,6 +1308,12 @@ public:
|
||||
|
||||
#endif
|
||||
virtual void ModifyOrAppendCriteria( AI_CriteriaSet& set );
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
// this computes criteria that depend on the other criteria having been set.
|
||||
// needs to be done in a second pass because we may have multiple overrids for
|
||||
// a context before it all settles out.
|
||||
virtual void ModifyOrAppendDerivedCriteria( AI_CriteriaSet& set ) {};
|
||||
#endif
|
||||
void AppendContextToCriteria( AI_CriteriaSet& set, const char *prefix = "" );
|
||||
#ifdef MAPBASE
|
||||
void ReAppendContextCriteria( AI_CriteriaSet& set );
|
||||
@ -1546,7 +1566,11 @@ public:
|
||||
static void EmitCloseCaption( IRecipientFilter& filter, int entindex, char const *token, CUtlVector< Vector >& soundorigins, float duration, bool warnifmissing = false );
|
||||
static void EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex, int iChannel, int iSentenceIndex,
|
||||
float flVolume, soundlevel_t iSoundlevel, int iFlags = 0, int iPitch = PITCH_NORM,
|
||||
const Vector *pOrigin = NULL, const Vector *pDirection = NULL, bool bUpdatePositions = true, float soundtime = 0.0f );
|
||||
const Vector *pOrigin = NULL, const Vector *pDirection = NULL, bool bUpdatePositions = true, float soundtime = 0.0f
|
||||
#ifdef MAPBASE
|
||||
, int iSpecialDSP = 0, int iSpeakerIndex = 0 // Needed for env_microphone
|
||||
#endif
|
||||
);
|
||||
|
||||
static bool IsPrecacheAllowed();
|
||||
static void SetAllowPrecache( bool allow );
|
||||
@ -2115,6 +2139,9 @@ public:
|
||||
int ScriptGetMoveType() { return GetMoveType(); }
|
||||
void ScriptSetMoveType( int iMoveType ) { SetMoveType( (MoveType_t)iMoveType ); }
|
||||
|
||||
int ScriptGetSolid() { return GetSolid(); }
|
||||
void ScriptSetSolid( int i ) { SetSolid( (SolidType_t)i ); }
|
||||
|
||||
bool ScriptDispatchInteraction( int interactionType, HSCRIPT data, HSCRIPT sourceEnt );
|
||||
|
||||
int ScriptGetTakeDamage() { return m_takedamage; }
|
||||
|
@ -799,6 +799,15 @@ bool CBaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CCh
|
||||
textParams.fadeinTime = 0.5f;
|
||||
textParams.fadeoutTime = 0.5f;
|
||||
|
||||
textParams.channel = 3;
|
||||
textParams.x = -1;
|
||||
textParams.y = 0.6;
|
||||
textParams.effect = 0;
|
||||
|
||||
textParams.r1 = 255;
|
||||
textParams.g1 = 255;
|
||||
textParams.b1 = 255;
|
||||
|
||||
if ( GetGameTextSpeechParams( textParams ) )
|
||||
{
|
||||
CRecipientFilter filter;
|
||||
@ -2091,15 +2100,6 @@ float CBaseFlex::PlayAutoGeneratedSoundScene( const char *soundname )
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseFlex::GetGameTextSpeechParams( hudtextparms_t ¶ms )
|
||||
{
|
||||
params.channel = 3;
|
||||
params.x = -1;
|
||||
params.y = 0.6;
|
||||
params.effect = 0;
|
||||
|
||||
params.r1 = 255;
|
||||
params.g1 = 255;
|
||||
params.b1 = 255;
|
||||
|
||||
ScriptVariant_t varTable;
|
||||
if (g_pScriptVM->GetValue(m_ScriptScope, "m_GameTextSpeechParams", &varTable) && varTable.m_type == FIELD_HSCRIPT)
|
||||
{
|
||||
|
@ -19,7 +19,9 @@
|
||||
|
||||
struct flexsettinghdr_t;
|
||||
struct flexsetting_t;
|
||||
#ifndef NEW_RESPONSE_SYSTEM
|
||||
class AI_Response;
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: A .vfe referenced by a scene during .vcd playback
|
||||
|
@ -28,6 +28,7 @@ CBaseMultiplayerPlayer::CBaseMultiplayerPlayer()
|
||||
CBaseMultiplayerPlayer::~CBaseMultiplayerPlayer()
|
||||
{
|
||||
m_pAchievementKV->deleteThis();
|
||||
delete m_pExpresser;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -87,10 +88,19 @@ IResponseSystem *CBaseMultiplayerPlayer::GetResponseSystem()
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Doesn't actually speak the concept. Just finds a response in the system. You then have to play it yourself.
|
||||
//-----------------------------------------------------------------------------
|
||||
AI_Response *CBaseMultiplayerPlayer::SpeakConcept( int iConcept )
|
||||
bool CBaseMultiplayerPlayer::SpeakConcept( AI_Response &response, int iConcept )
|
||||
{
|
||||
m_iCurrentConcept = iConcept;
|
||||
return SpeakFindResponse( g_pszMPConcepts[iConcept] );
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
CAI_Concept concept(g_pszMPConcepts[iConcept]);
|
||||
concept.SetSpeaker(this);
|
||||
return FindResponse( response, concept );
|
||||
#else
|
||||
AI_Response *pResponse = SpeakFindResponse( g_pszMPConcepts[iConcept] );
|
||||
if (pResponse)
|
||||
response = *pResponse;
|
||||
return pResponse != NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -28,7 +28,7 @@ public:
|
||||
|
||||
virtual bool SpeakIfAllowed( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );
|
||||
virtual IResponseSystem *GetResponseSystem();
|
||||
AI_Response *SpeakConcept( int iConcept );
|
||||
bool SpeakConcept( AI_Response &response, int iConcept );
|
||||
virtual bool SpeakConceptIfAllowed( int iConcept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );
|
||||
|
||||
virtual bool CanHearAndReadChatFrom( CBasePlayer *pPlayer );
|
||||
|
@ -104,6 +104,13 @@ extern void FireTargets( const char *targetName, CBaseEntity *pActivator, CBaseE
|
||||
|
||||
#define MAX_OLD_ENEMIES 4 // how many old enemies to remember
|
||||
|
||||
#ifdef MAPBASE
|
||||
// Use the model keyvalue if it is defined
|
||||
#define DefaultOrCustomModel(defaultModel) GetModelName() != NULL_STRING ? STRING(GetModelName()) : defaultModel
|
||||
#else
|
||||
#define DefaultOrCustomModel() defaultModel
|
||||
#endif
|
||||
|
||||
// used by suit voice to indicate damage sustained and repaired type to player
|
||||
|
||||
enum
|
||||
|
@ -2511,7 +2511,7 @@ class CBreakableGibShooter : public CBaseEntity
|
||||
DECLARE_DATADESC();
|
||||
public:
|
||||
|
||||
const char *GetRandomTemplateModel( CPointTemplate *pTemplate );
|
||||
int GetRandomTemplateModelIndex( CPointTemplate *pTemplate );
|
||||
|
||||
void Precache( void );
|
||||
|
||||
@ -2560,19 +2560,16 @@ END_DATADESC()
|
||||
LINK_ENTITY_TO_CLASS( env_break_shooter, CBreakableGibShooter );
|
||||
|
||||
|
||||
const char *CBreakableGibShooter::GetRandomTemplateModel( CPointTemplate *pTemplate )
|
||||
int CBreakableGibShooter::GetRandomTemplateModelIndex( CPointTemplate *pTemplate )
|
||||
{
|
||||
int iIndex = RandomInt( 0, pTemplate->GetNumTemplates() );
|
||||
char *iszTemplate = (char*)(STRING(Templates_FindByIndex(pTemplate->GetTemplateIndexForTemplate(iIndex))));
|
||||
|
||||
CEntityMapData entData( iszTemplate );
|
||||
const char *szTemplate = STRING(Templates_FindByIndex(pTemplate->GetTemplateIndexForTemplate(iIndex)));
|
||||
|
||||
// This might seem a little messy, but I think it's cheaper than creating the entity.
|
||||
char szModel[MAPKEY_MAXLENGTH];
|
||||
if (!entData.ExtractValue("model", szModel))
|
||||
return NULL;
|
||||
bool modelExtracted = MapEntity_ExtractValue(szTemplate, "model", szModel);
|
||||
|
||||
return strdup(szModel);
|
||||
return modelinfo->GetModelIndex( modelExtracted ? szModel : NULL );
|
||||
}
|
||||
|
||||
void CBreakableGibShooter::Precache( void )
|
||||
@ -2604,7 +2601,7 @@ void CBreakableGibShooter::Shoot( void )
|
||||
if (m_iModelType == MODELTYPE_BREAKABLECHUNKS)
|
||||
iModelIndex = modelinfo->GetModelIndex( g_PropDataSystem.GetRandomChunkModel( STRING( GetModelName() ) ) );
|
||||
else if (m_iModelType == MODELTYPE_TEMPLATE)
|
||||
iModelIndex = modelinfo->GetModelIndex( GetRandomTemplateModel(pTemplate) );
|
||||
iModelIndex = GetRandomTemplateModelIndex( pTemplate );
|
||||
|
||||
// All objects except the first one in this run are marked as slaves...
|
||||
int slaveFlag = 0;
|
||||
|
@ -189,7 +189,7 @@ void CEnvTonemapController::InputBlendTonemapScale( inputdata_t &inputdata )
|
||||
void CEnvTonemapController::InputSetBloomScaleRange( inputdata_t &inputdata )
|
||||
{
|
||||
float bloom_max=1, bloom_min=1;
|
||||
int nargs=sscanf("%f %f",inputdata.value.String(), bloom_max, bloom_min );
|
||||
int nargs=sscanf( inputdata.value.String(), "%f %f", &bloom_max, &bloom_min );
|
||||
if (nargs != 2)
|
||||
{
|
||||
Warning("%s (%s) received SetBloomScaleRange input without 2 arguments. Syntax: <max bloom> <min bloom>\n", GetClassname(), GetDebugName() );
|
||||
@ -197,6 +197,9 @@ void CEnvTonemapController::InputSetBloomScaleRange( inputdata_t &inputdata )
|
||||
}
|
||||
m_flCustomBloomScale=bloom_max;
|
||||
m_flCustomBloomScaleMinimum=bloom_min;
|
||||
#ifdef MAPBASE
|
||||
m_bUseCustomBloomScale = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -19,6 +19,9 @@
|
||||
#include "soundflags.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "filters.h"
|
||||
#ifdef MAPBASE
|
||||
#include "fmtstr.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
@ -27,6 +30,10 @@
|
||||
|
||||
const float MICROPHONE_SETTLE_EPSILON = 0.005;
|
||||
|
||||
#ifdef MAPBASE
|
||||
static ConVar sv_microphones_always_pickup_sentences( "sv_microphones_always_pickup_sentences", "0", FCVAR_NONE, "Allows env_microphones to always detect and play back sentences, regardless of their keyvalues." );
|
||||
#endif
|
||||
|
||||
// List of env_microphones who want to be told whenever a sound is started
|
||||
static CUtlVector< CHandle<CEnvMicrophone> > s_Microphones;
|
||||
|
||||
@ -236,7 +243,11 @@ void CEnvMicrophone::InputDisable( inputdata_t &inputdata )
|
||||
m_bDisabled = true;
|
||||
if ( m_hSpeaker )
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
CBaseEntity::StopSound( m_hSpeaker->entindex(), m_nChannel, m_szLastSound );
|
||||
#else
|
||||
CBaseEntity::StopSound( m_hSpeaker->entindex(), CHAN_STATIC, m_szLastSound );
|
||||
#endif
|
||||
m_szLastSound[0] = 0;
|
||||
|
||||
// Remove ourselves from the list of active mics
|
||||
@ -554,31 +565,41 @@ MicrophoneResult_t CEnvMicrophone::SoundPlayed( int entindex, const char *soundn
|
||||
CPASAttenuationFilter filter( m_hSpeaker );
|
||||
|
||||
EmitSound_t ep;
|
||||
#ifdef MAPBASE
|
||||
ep.m_nChannel = m_nChannel;
|
||||
if (m_flVolumeScale != 1.0f)
|
||||
ep.m_flVolume = (flVolume * m_flVolumeScale);
|
||||
#else
|
||||
ep.m_nChannel = CHAN_STATIC;
|
||||
ep.m_flVolume = flVolume;
|
||||
#endif
|
||||
ep.m_pSoundName = soundname;
|
||||
ep.m_SoundLevel = soundlevel;
|
||||
ep.m_nFlags = iFlags;
|
||||
#ifdef MAPBASE
|
||||
if (m_flPitchScale != 1.0f)
|
||||
ep.m_nPitch = (int)((float)iPitch * m_flPitchScale);
|
||||
else
|
||||
ep.m_nPitch = iPitch;
|
||||
ep.m_pOrigin = &vecOrigin;
|
||||
#else
|
||||
ep.m_nPitch = iPitch;
|
||||
ep.m_pOrigin = &m_hSpeaker->GetAbsOrigin();
|
||||
#endif
|
||||
ep.m_flSoundTime = soundtime;
|
||||
ep.m_nSpeakerEntity = entindex;
|
||||
|
||||
CBaseEntity::EmitSound( filter, m_hSpeaker->entindex(), ep );
|
||||
#ifdef MAPBASE
|
||||
if (m_bHearingSentence)
|
||||
{
|
||||
CBaseEntity::EmitSentenceByIndex( filter, m_hSpeaker->entindex(), m_nChannel, atoi(soundname), flVolume, soundlevel, 0, iPitch, &vecOrigin, NULL, true, soundtime,
|
||||
m_iSpeakerDSPPreset, entindex );
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
ep.m_nChannel = m_nChannel;
|
||||
if (m_flVolumeScale != 1.0f)
|
||||
ep.m_flVolume = (flVolume * m_flVolumeScale);
|
||||
else
|
||||
ep.m_flVolume = flVolume;
|
||||
if (m_flPitchScale != 1.0f)
|
||||
ep.m_nPitch = (int)((float)iPitch * m_flPitchScale);
|
||||
else
|
||||
ep.m_nPitch = iPitch;
|
||||
ep.m_pOrigin = &vecOrigin;
|
||||
#else
|
||||
ep.m_nChannel = CHAN_STATIC;
|
||||
ep.m_flVolume = flVolume;
|
||||
ep.m_nPitch = iPitch;
|
||||
ep.m_pOrigin = &m_hSpeaker->GetAbsOrigin();
|
||||
#endif
|
||||
ep.m_pSoundName = soundname;
|
||||
ep.m_SoundLevel = soundlevel;
|
||||
ep.m_nFlags = iFlags;
|
||||
ep.m_flSoundTime = soundtime;
|
||||
ep.m_nSpeakerEntity = entindex;
|
||||
|
||||
CBaseEntity::EmitSound( filter, m_hSpeaker->entindex(), ep );
|
||||
}
|
||||
|
||||
Q_strncpy( m_szLastSound, soundname, sizeof(m_szLastSound) );
|
||||
m_OnRoutedSound.FireOutput( this, this, 0 );
|
||||
@ -646,3 +667,56 @@ bool CEnvMicrophone::OnSoundPlayed( int entindex, const char *soundname, soundle
|
||||
|
||||
return bSwallowed;
|
||||
}
|
||||
|
||||
#ifdef MAPBASE
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Called by the sound system whenever a sentence is played so that
|
||||
// active microphones can have a chance to pick up the sound.
|
||||
// Output : Returns whether or not the sentence was swallowed by the microphone.
|
||||
// Swallowed sentences should not be played by the sound system.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CEnvMicrophone::OnSentencePlayed( int entindex, int sentenceIndex, soundlevel_t soundlevel, float flVolume, int iFlags, int iPitch, const Vector *pOrigin, float soundtime, CUtlVector< Vector >& soundorigins )
|
||||
{
|
||||
bool bSwallowed = false;
|
||||
|
||||
// Loop through all registered microphones and tell them the sound was just played
|
||||
int iCount = s_Microphones.Count();
|
||||
if ( iCount > 0 )
|
||||
{
|
||||
CNumStr szSentenceStr( sentenceIndex );
|
||||
|
||||
// Iterate backwards because we might be deleting microphones.
|
||||
for ( int i = iCount - 1; i >= 0; i-- )
|
||||
{
|
||||
if ( s_Microphones[i] && (s_Microphones[i]->ShouldHearSentences() || sv_microphones_always_pickup_sentences.GetBool()) )
|
||||
{
|
||||
// HACKHACK: Don't want to duplicate all of the code, so just use the same function with a new member variable
|
||||
s_Microphones[i]->ToggleHearingSentence( true );
|
||||
MicrophoneResult_t eResult = s_Microphones[i]->SoundPlayed(
|
||||
entindex,
|
||||
szSentenceStr,
|
||||
soundlevel,
|
||||
flVolume,
|
||||
iFlags,
|
||||
iPitch,
|
||||
pOrigin,
|
||||
soundtime,
|
||||
soundorigins );
|
||||
s_Microphones[i]->ToggleHearingSentence( false );
|
||||
|
||||
if ( eResult == MicrophoneResult_Swallow )
|
||||
{
|
||||
// Microphone told us to swallow it
|
||||
bSwallowed = true;
|
||||
}
|
||||
else if ( eResult == MicrophoneResult_Remove )
|
||||
{
|
||||
s_Microphones.FastRemove( i );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bSwallowed;
|
||||
}
|
||||
#endif
|
||||
|
@ -20,6 +20,9 @@ const int SF_MICROPHONE_SOUND_BULLET_IMPACT = 0x08;
|
||||
const int SF_MICROPHONE_SWALLOW_ROUTED_SOUNDS = 0x10;
|
||||
const int SF_MICROPHONE_SOUND_EXPLOSION = 0x20;
|
||||
const int SF_MICROPHONE_IGNORE_NONATTENUATED = 0x40;
|
||||
#ifdef MAPBASE
|
||||
const int SF_MICROPHONE_SOUND_SENTENCE = 0x80;
|
||||
#endif
|
||||
|
||||
|
||||
// Return codes from SoundPlayed
|
||||
@ -50,6 +53,10 @@ public:
|
||||
|
||||
void SetSensitivity( float flSensitivity );
|
||||
void SetSpeakerName( string_t iszSpeakerName );
|
||||
#ifdef MAPBASE
|
||||
bool ShouldHearSentences() const { return HasSpawnFlags( SF_MICROPHONE_SOUND_SENTENCE ); }
|
||||
inline void ToggleHearingSentence( bool bToggle ) { m_bHearingSentence = bToggle; }
|
||||
#endif
|
||||
|
||||
void InputEnable( inputdata_t &inputdata );
|
||||
void InputDisable( inputdata_t &inputdata );
|
||||
@ -67,6 +74,12 @@ public:
|
||||
static bool OnSoundPlayed( int entindex, const char *soundname, soundlevel_t soundlevel,
|
||||
float flVolume, int iFlags, int iPitch, const Vector *pOrigin, float soundtime, CUtlVector< Vector >& soundorigins );
|
||||
|
||||
#ifdef MAPBASE
|
||||
// Same as above, except for sentences.
|
||||
static bool OnSentencePlayed( int entindex, int sentenceIndex, soundlevel_t soundlevel,
|
||||
float flVolume, int iFlags, int iPitch, const Vector *pOrigin, float soundtime, CUtlVector< Vector >& soundorigins );
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
// Per-microphone notification that a sound has played.
|
||||
@ -91,6 +104,8 @@ private:
|
||||
float m_flPitchScale = 1.0f;
|
||||
float m_flVolumeScale = 1.0f;
|
||||
int m_nChannel = CHAN_STATIC;
|
||||
|
||||
bool m_bHearingSentence; // HACKHACK: Allows SoundPlayed() to know when to play a sentence instead
|
||||
#endif
|
||||
|
||||
COutputFloat m_SoundLevel; // Fired when the sampled volume level changes.
|
||||
|
@ -37,6 +37,11 @@ public:
|
||||
Class_T Classify ( void );
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
int GetSoundInterests ( void );
|
||||
|
||||
#ifdef MAPBASE
|
||||
// Use Magnusson's default subtitle color (209,178,178)
|
||||
bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 209; params.g1 = 178; params.b1 = 178; return BaseClass::GetGameTextSpeechParams( params ); }
|
||||
#endif
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( npc_magnusson, CNPC_Magnusson );
|
||||
|
@ -277,7 +277,8 @@ bool CGenericActorCustom::KeyValue( const char *szKeyName, const char *szValue )
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGenericActorCustom::SpeakIfAllowed( const char *concept, AI_CriteriaSet *modifiers )
|
||||
{
|
||||
Speak( concept, modifiers ? *modifiers : AI_CriteriaSet() );
|
||||
AI_CriteriaSet empty;
|
||||
Speak( concept, modifiers ? *modifiers : empty );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -1755,7 +1755,11 @@ void CAI_ActBusyBehavior::PlaySoundForActBusy( busyanimparts_t AnimPart )
|
||||
CAI_Expresser *pExpresser = GetOuter()->GetExpresser();
|
||||
if ( pExpresser )
|
||||
{
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
CAI_Concept concept = STRING(pBusyAnim->iszSounds[AnimPart]);
|
||||
#else
|
||||
const char *concept = STRING(pBusyAnim->iszSounds[AnimPart]);
|
||||
#endif
|
||||
|
||||
// Must be able to speak the concept
|
||||
if ( !pExpresser->IsSpeaking() && pExpresser->CanSpeakConcept( concept ) )
|
||||
@ -2738,8 +2742,15 @@ void CAI_ActBusyGoal::InputForceThisNPCToStopBusy( inputdata_t &inputdata )
|
||||
if ( !pBehavior )
|
||||
return;
|
||||
|
||||
// Just stop busying
|
||||
pBehavior->StopBusying();
|
||||
if (!IsActive() && pBehavior->GetActBusyGoal() == this)
|
||||
{
|
||||
pBehavior->Disable();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just stop busying
|
||||
pBehavior->StopBusying();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -153,6 +153,10 @@ public:
|
||||
bool IsInSafeZone( CBaseEntity *pEntity );
|
||||
int CountEnemiesInSafeZone();
|
||||
|
||||
#ifdef MAPBASE
|
||||
CAI_ActBusyGoal *GetActBusyGoal() const { return m_hActBusyGoal; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
virtual int SelectSchedule( void );
|
||||
int SelectScheduleForLeaving( void );
|
||||
|
@ -143,7 +143,12 @@ void CAI_PolicingBehavior::HostSpeakSentence( const char *pSentence, SentencePri
|
||||
}
|
||||
else if ( GetOuter()->GetExpresser() )
|
||||
{
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
CAI_Concept concept = pSentence;
|
||||
GetOuter()->GetExpresser()->Speak( concept );
|
||||
#else
|
||||
GetOuter()->GetExpresser()->Speak( pSentence );
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -168,7 +173,12 @@ void CAI_PolicingBehavior::HostSpeakSentence( const char *pSentence, const char
|
||||
}
|
||||
else if ( GetOuter()->GetExpresser() )
|
||||
{
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
CAI_Concept concept( pSentence );
|
||||
GetOuter()->GetExpresser()->Speak( concept, modifiers );
|
||||
#else
|
||||
GetOuter()->GetExpresser()->Speak( pSentence, modifiers );
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -243,6 +243,65 @@ void CSpeaker::DispatchResponse( const char *conceptName )
|
||||
PrecacheScriptSound( response );
|
||||
}
|
||||
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
switch (result.GetType())
|
||||
{
|
||||
case ResponseRules::RESPONSE_SPEAK:
|
||||
{
|
||||
pTarget->EmitSound( response );
|
||||
}
|
||||
break;
|
||||
case ResponseRules::RESPONSE_SENTENCE:
|
||||
{
|
||||
int sentenceIndex = SENTENCEG_Lookup( response );
|
||||
if (sentenceIndex == -1)
|
||||
{
|
||||
// sentence not found
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME: Get pitch from npc?
|
||||
CPASAttenuationFilter filter( pTarget );
|
||||
CBaseEntity::EmitSentenceByIndex( filter, pTarget->entindex(), CHAN_VOICE, sentenceIndex, 1, result.GetSoundLevel(), 0, PITCH_NORM );
|
||||
}
|
||||
break;
|
||||
case ResponseRules::RESPONSE_SCENE:
|
||||
{
|
||||
CBaseFlex *pFlex = NULL;
|
||||
if (pTarget != this)
|
||||
{
|
||||
// Attempt to get flex on the target
|
||||
pFlex = dynamic_cast<CBaseFlex*>(pTarget);
|
||||
}
|
||||
InstancedScriptedScene(pFlex, response);
|
||||
}
|
||||
break;
|
||||
case ResponseRules::RESPONSE_PRINT:
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
case ResponseRules::RESPONSE_ENTITYIO:
|
||||
{
|
||||
CAI_Expresser::FireEntIOFromResponse( response, pTarget );
|
||||
break;
|
||||
}
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
case ResponseRules::RESPONSE_VSCRIPT:
|
||||
{
|
||||
CAI_Expresser::RunScriptResponse( pTarget, response, &set, false );
|
||||
break;
|
||||
}
|
||||
case ResponseRules::RESPONSE_VSCRIPT_FILE:
|
||||
{
|
||||
CAI_Expresser::RunScriptResponse( pTarget, response, &set, true );
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#else
|
||||
switch ( result.GetType() )
|
||||
{
|
||||
case RESPONSE_SPEAK:
|
||||
@ -283,6 +342,7 @@ void CSpeaker::DispatchResponse( const char *conceptName )
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// AllocPooledString?
|
||||
m_OnSpeak.Set(MAKE_STRING(response), pTarget, this);
|
||||
|
@ -4734,7 +4734,8 @@ bool CLogicPlayerProxy::AcceptInput( const char *szInputName, CBaseEntity *pActi
|
||||
{
|
||||
DevMsg("logic_playerproxy: Player not found!\n");
|
||||
|
||||
g_EventQueue.AddEvent("!player", szInputName, Value, 0.01f, pActivator, pCaller);
|
||||
// Need to allocate the string here in case szInputName is freed before the input fires
|
||||
g_EventQueue.AddEvent("!player", STRING( AllocPooledString(szInputName) ), Value, 0.01f, pActivator, pCaller);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,7 +303,7 @@ public:
|
||||
virtual bool IsHoldingEntity( CBaseEntity *pEnt );
|
||||
virtual void ForceDropOfCarriedPhysObjects( CBaseEntity *pOnlyIfHoldindThis );
|
||||
virtual float GetHeldObjectMass( IPhysicsObject *pHeldObject );
|
||||
virtual CBaseEntity *CHL2_Player::GetHeldObject( void );
|
||||
virtual CBaseEntity *GetHeldObject( void );
|
||||
|
||||
virtual bool IsFollowingPhysics( void ) { return (m_afPhysicsFlags & PFLAG_ONBARNACLE) > 0; }
|
||||
void InputForceDropPhysObjects( inputdata_t &data );
|
||||
|
@ -23,12 +23,12 @@ public:
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/battery.mdl" );
|
||||
SetModel( DefaultOrCustomModel( "models/items/battery.mdl" ) );
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/battery.mdl");
|
||||
PrecacheModel( DefaultOrCustomModel( "models/items/battery.mdl" ) );
|
||||
|
||||
PrecacheScriptSound( "ItemBattery.Touch" );
|
||||
|
||||
@ -36,10 +36,30 @@ public:
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
CHL2_Player *pHL2Player = dynamic_cast<CHL2_Player *>( pPlayer );
|
||||
#ifdef MAPBASE
|
||||
return ( pHL2Player && pHL2Player->ApplyBattery( m_flPowerMultiplier ) );
|
||||
#else
|
||||
return ( pHL2Player && pHL2Player->ApplyBattery() );
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MAPBASE
|
||||
void InputSetPowerMultiplier( inputdata_t &inputdata ) { m_flPowerMultiplier = inputdata.value.Float(); }
|
||||
float m_flPowerMultiplier = 1.0f;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
#endif
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_battery, CItemBattery);
|
||||
PRECACHE_REGISTER(item_battery);
|
||||
|
||||
#ifdef MAPBASE
|
||||
BEGIN_DATADESC( CItemBattery )
|
||||
|
||||
DEFINE_KEYFIELD( m_flPowerMultiplier, FIELD_FLOAT, "PowerMultiplier" ),
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPowerMultiplier", InputSetPowerMultiplier ),
|
||||
|
||||
END_DATADESC()
|
||||
#endif
|
||||
|
||||
|
@ -30,11 +30,29 @@ public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
bool MyTouch( CBasePlayer *pPlayer );
|
||||
|
||||
#ifdef MAPBASE
|
||||
float GetItemAmount() { return sk_healthkit.GetFloat() * m_flHealthMultiplier; }
|
||||
|
||||
void InputSetHealthMultiplier( inputdata_t &inputdata ) { m_flHealthMultiplier = inputdata.value.Float(); }
|
||||
float m_flHealthMultiplier = 1.0f;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
#endif
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_healthkit, CHealthKit );
|
||||
PRECACHE_REGISTER(item_healthkit);
|
||||
|
||||
#ifdef MAPBASE
|
||||
BEGIN_DATADESC( CHealthKit )
|
||||
|
||||
DEFINE_KEYFIELD( m_flHealthMultiplier, FIELD_FLOAT, "HealthMultiplier" ),
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetHealthMultiplier", InputSetHealthMultiplier ),
|
||||
|
||||
END_DATADESC()
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
@ -66,7 +84,11 @@ void CHealthKit::Precache( void )
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CHealthKit::MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
if ( pPlayer->TakeHealth( GetItemAmount(), DMG_GENERIC ) )
|
||||
#else
|
||||
if ( pPlayer->TakeHealth( sk_healthkit.GetFloat(), DMG_GENERIC ) )
|
||||
#endif
|
||||
{
|
||||
CSingleUserRecipientFilter user( pPlayer );
|
||||
user.MakeReliable();
|
||||
@ -119,7 +141,11 @@ public:
|
||||
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
if ( pPlayer->TakeHealth( GetItemAmount(), DMG_GENERIC ) )
|
||||
#else
|
||||
if ( pPlayer->TakeHealth( sk_healthvial.GetFloat(), DMG_GENERIC ) )
|
||||
#endif
|
||||
{
|
||||
CSingleUserRecipientFilter user( pPlayer );
|
||||
user.MakeReliable();
|
||||
@ -145,11 +171,132 @@ public:
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef MAPBASE
|
||||
float GetItemAmount() { return sk_healthvial.GetFloat() * m_flHealthMultiplier; }
|
||||
|
||||
void InputSetHealthMultiplier( inputdata_t &inputdata ) { m_flHealthMultiplier = inputdata.value.Float(); }
|
||||
float m_flHealthMultiplier = 1.0f;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
#endif
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_healthvial, CHealthVial );
|
||||
PRECACHE_REGISTER( item_healthvial );
|
||||
|
||||
#ifdef MAPBASE
|
||||
BEGIN_DATADESC( CHealthVial )
|
||||
|
||||
DEFINE_KEYFIELD( m_flHealthMultiplier, FIELD_FLOAT, "HealthMultiplier" ),
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetHealthMultiplier", InputSetHealthMultiplier ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Small health kit. Heals the player when picked up.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CHealthKitCustom : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHealthKitCustom, CItem );
|
||||
CHealthKitCustom();
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
bool MyTouch( CBasePlayer *pPlayer );
|
||||
|
||||
float GetItemAmount() { return m_flHealthAmount; }
|
||||
|
||||
void InputSetHealthAmount( inputdata_t &inputdata ) { m_flHealthAmount = inputdata.value.Float(); }
|
||||
|
||||
float m_flHealthAmount;
|
||||
string_t m_iszTouchSound;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_healthkit_custom, CHealthKitCustom );
|
||||
//PRECACHE_REGISTER(item_healthkit_custom);
|
||||
|
||||
#ifdef MAPBASE
|
||||
BEGIN_DATADESC( CHealthKitCustom )
|
||||
|
||||
DEFINE_KEYFIELD( m_flHealthAmount, FIELD_FLOAT, "HealthAmount" ),
|
||||
DEFINE_KEYFIELD( m_iszTouchSound, FIELD_STRING, "TouchSound" ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetHealthAmount", InputSetHealthAmount ),
|
||||
|
||||
END_DATADESC()
|
||||
#endif
|
||||
|
||||
|
||||
CHealthKitCustom::CHealthKitCustom()
|
||||
{
|
||||
SetModelName( AllocPooledString( "models/items/healthkit.mdl" ) );
|
||||
m_flHealthAmount = sk_healthkit.GetFloat();
|
||||
m_iszTouchSound = AllocPooledString( "HealthKit.Touch" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHealthKitCustom::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( STRING( GetModelName() ) );
|
||||
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHealthKitCustom::Precache( void )
|
||||
{
|
||||
PrecacheModel( STRING( GetModelName() ) );
|
||||
|
||||
PrecacheScriptSound( STRING( m_iszTouchSound ) );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pPlayer -
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CHealthKitCustom::MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( pPlayer->TakeHealth( GetItemAmount(), DMG_GENERIC ) )
|
||||
{
|
||||
CSingleUserRecipientFilter user( pPlayer );
|
||||
user.MakeReliable();
|
||||
|
||||
UserMessageBegin( user, "ItemPickup" );
|
||||
WRITE_STRING( GetClassname() );
|
||||
MessageEnd();
|
||||
|
||||
CPASAttenuationFilter filter( pPlayer, STRING( m_iszTouchSound ) );
|
||||
EmitSound( filter, pPlayer->entindex(), STRING( m_iszTouchSound ) );
|
||||
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_YES )
|
||||
{
|
||||
Respawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Wall mounted health kit. Heals the player when used.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -1210,6 +1210,10 @@ void CNPC_BaseZombie::DieChopped( const CTakeDamageInfo &info )
|
||||
if( pAnimating )
|
||||
{
|
||||
pAnimating->SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless );
|
||||
#ifdef MAPBASE
|
||||
// Inherit some animating properties
|
||||
pAnimating->m_nSkin = m_nSkin;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MAPBASE
|
||||
|
@ -36,6 +36,9 @@ public:
|
||||
// Now that all allies can holster/unholster, this is a precaution in case it breaks anything.
|
||||
// Try OnFoundEnemy > UnholsterWeapon if you want Alyx to automatically unholster in non-episodic HL2 maps.
|
||||
bool CanUnholsterWeapon() { return false; }
|
||||
|
||||
// Use Alyx's default subtitle color (255,212,255)
|
||||
bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 255; params.g1 = 212; params.b1 = 255; return BaseClass::GetGameTextSpeechParams( params ); }
|
||||
#endif
|
||||
|
||||
EHANDLE m_hEmpTool;
|
||||
|
@ -57,6 +57,9 @@ public:
|
||||
#ifdef MAPBASE
|
||||
// This skips CAI_PlayerAlly's CanFlinch() function since Episodic Alyx can flinch to begin with.
|
||||
virtual bool CanFlinch( void ) { return CAI_BaseActor::CanFlinch(); }
|
||||
|
||||
// Use Alyx's default subtitle color (255,212,255)
|
||||
bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 255; params.g1 = 212; params.b1 = 255; return BaseClass::GetGameTextSpeechParams( params ); }
|
||||
#endif
|
||||
|
||||
virtual float GetJumpGravity() const { return 1.8f; }
|
||||
|
@ -81,6 +81,11 @@ public:
|
||||
void GatherConditions();
|
||||
void UseFunc( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
#ifdef MAPBASE
|
||||
// Use Barney's default subtitle color (215,255,255)
|
||||
bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 215; params.g1 = 255; params.b1 = 255; return BaseClass::GetGameTextSpeechParams( params ); }
|
||||
#endif
|
||||
|
||||
CAI_FuncTankBehavior m_FuncTankBehavior;
|
||||
COutputEvent m_OnPlayerUse;
|
||||
|
||||
|
@ -34,6 +34,11 @@ public:
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
int GetSoundInterests ( void );
|
||||
bool UseSemaphore( void );
|
||||
|
||||
#ifdef MAPBASE
|
||||
// Use Breen's default subtitle color (188,188,188)
|
||||
bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 188; params.g1 = 188; params.b1 = 188; return BaseClass::GetGameTextSpeechParams( params ); }
|
||||
#endif
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( npc_breen, CNPC_Breen );
|
||||
|
@ -1557,6 +1557,13 @@ Activity CNPC_Combine::NPC_BackupActivity( Activity eNewActivity )
|
||||
else if (eNewActivity == ACT_RUN)
|
||||
return ACT_RUN_RIFLE;
|
||||
|
||||
// Some models might not contain ACT_COMBINE_BUGBAIT, which the soldier model uses instead of ACT_IDLE_ON_FIRE.
|
||||
// Contrariwise, soldiers may be called to use ACT_IDLE_ON_FIRE in other parts of the AI and need to translate to ACT_COMBINE_BUGBAIT.
|
||||
if (eNewActivity == ACT_COMBINE_BUGBAIT)
|
||||
return ACT_IDLE_ON_FIRE;
|
||||
else if (eNewActivity == ACT_IDLE_ON_FIRE)
|
||||
return ACT_COMBINE_BUGBAIT;
|
||||
|
||||
return BaseClass::NPC_BackupActivity( eNewActivity );
|
||||
}
|
||||
#endif
|
||||
@ -3004,7 +3011,11 @@ bool CNPC_Combine::SpeakIfAllowed( const char *concept, const char *modifiers, S
|
||||
AI_CriteriaSet set;
|
||||
if (modifiers)
|
||||
{
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
GatherCriteria( &set, concept, modifiers );
|
||||
#else
|
||||
GetExpresser()->MergeModifiers(set, modifiers);
|
||||
#endif
|
||||
}
|
||||
return SpeakIfAllowed( concept, set, sentencepriority, sentencecriteria );
|
||||
}
|
||||
|
@ -689,9 +689,15 @@ int CCombineDropshipContainer::OnTakeDamage( const CTakeDamageInfo &info )
|
||||
|
||||
if ( m_iHealth <= 0 )
|
||||
{
|
||||
m_iHealth = 0;
|
||||
Event_Killed( dmgInfo );
|
||||
return 0;
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
// False = Cheat death
|
||||
if (ScriptDeathHook( const_cast<CTakeDamageInfo*>(&info) ) != false)
|
||||
#endif
|
||||
{
|
||||
m_iHealth = 0;
|
||||
Event_Killed( dmgInfo );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn damage effects
|
||||
|
@ -37,6 +37,11 @@ public:
|
||||
int GetSoundInterests( void );
|
||||
void SetupWithoutParent( void );
|
||||
void PrescheduleThink( void );
|
||||
|
||||
#ifdef MAPBASE
|
||||
// Use Eli's default subtitle color (255,208,172)
|
||||
bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 255; params.g1 = 208; params.b1 = 172; return BaseClass::GetGameTextSpeechParams( params ); }
|
||||
#endif
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( npc_eli, CNPC_Eli );
|
||||
|
@ -35,6 +35,11 @@ public:
|
||||
Class_T Classify ( void );
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
int GetSoundInterests ( void );
|
||||
|
||||
#ifdef MAPBASE
|
||||
// Use Kleiner's default subtitle color (255,255,200)
|
||||
bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 255; params.g1 = 255; params.b1 = 200; return BaseClass::GetGameTextSpeechParams( params ); }
|
||||
#endif
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( npc_kleiner, CNPC_Kleiner );
|
||||
|
@ -1156,7 +1156,11 @@ bool CNPC_MetroPolice::SpeakIfAllowed( const char *concept, const char *modifier
|
||||
AI_CriteriaSet set;
|
||||
if (modifiers)
|
||||
{
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
GatherCriteria( &set, concept, modifiers );
|
||||
#else
|
||||
GetExpresser()->MergeModifiers(set, modifiers);
|
||||
#endif
|
||||
}
|
||||
return SpeakIfAllowed( concept, set, sentencepriority, sentencecriteria );
|
||||
}
|
||||
@ -1622,6 +1626,11 @@ bool CNPC_MetroPolice::ShouldAttemptToStitch()
|
||||
//-----------------------------------------------------------------------------
|
||||
Vector CNPC_MetroPolice::StitchAimTarget( const Vector &posSrc, bool bNoisy )
|
||||
{
|
||||
#ifdef MAPBASE
|
||||
if ( !GetEnemy() )
|
||||
return vec3_origin;
|
||||
#endif
|
||||
|
||||
// This will make us aim a stitch at the feet of the player so we can see it
|
||||
if ( !GetEnemy()->IsPlayer() )
|
||||
return GetShootTarget()->BodyTarget( posSrc, bNoisy );
|
||||
|
@ -41,6 +41,11 @@ public:
|
||||
bool CreateBehaviors( void );
|
||||
int SelectSchedule( void );
|
||||
|
||||
#ifdef MAPBASE
|
||||
// Use Mossman's default subtitle color (220,255,198)
|
||||
bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 220; params.g1 = 255; params.b1 = 198; return BaseClass::GetGameTextSpeechParams( params ); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
CAI_FollowBehavior m_FollowBehavior;
|
||||
};
|
||||
|
@ -4223,17 +4223,10 @@ void CNPC_PlayerCompanion::Event_KilledOther( CBaseEntity *pVictim, const CTakeD
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Handles stuff ported from Alyx.
|
||||
//
|
||||
// For some reason, I thought Alyx's mobbed AI was used to measure enemy count for criteria stuff, which I wanted citizens to use.
|
||||
// Now that I realize enemy counting for criteria is elsewhere and this is used for just mobbing in general, I deactivated it
|
||||
// since it would barely be used and I don't know what kind of an impact it has on performance.
|
||||
//
|
||||
// If you want to use it, feel free to re-activate.
|
||||
// Purpose: Handles custom combat speech stuff ported from Alyx.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_PlayerCompanion::DoCustomCombatAI( void )
|
||||
{
|
||||
/*
|
||||
#define COMPANION_MIN_MOB_DIST_SQR Square(120) // Any enemy closer than this adds to the 'mob'
|
||||
#define COMPANION_MIN_CONSIDER_DIST Square(1200) // Only enemies within this range are counted and considered to generate AI speech
|
||||
|
||||
@ -4296,7 +4289,6 @@ void CNPC_PlayerCompanion::DoCustomCombatAI( void )
|
||||
{
|
||||
SpeakIfAllowed( TLK_MANY_ENEMIES );
|
||||
}
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -138,6 +138,11 @@ public:
|
||||
// used so a grub can notify me that I stepped on it. Says a line.
|
||||
void OnSquishedGrub( const CBaseEntity *pGrub );
|
||||
|
||||
#ifdef MAPBASE
|
||||
// Use the vortigaunts' default subtitle color (188,241,174)
|
||||
bool GetGameTextSpeechParams( hudtextparms_t ¶ms ) { params.r1 = 188; params.g1 = 241; params.b1 = 174; return BaseClass::GetGameTextSpeechParams( params ); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
int NumAntlionsInRadius( float flRadius );
|
||||
|
@ -1210,16 +1210,28 @@ const char *CZombieCustom::GetMoanSound( int nSound )
|
||||
// We could probably do this through the response system alone now, but whatever.
|
||||
modifiers.AppendCriteria( "moansound", UTIL_VarArgs("%i", nSound & 4) );
|
||||
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
AI_Response response;
|
||||
CAI_Concept concept = "TLK_ZOMBIE_MOAN";
|
||||
concept.SetSpeaker( this );
|
||||
if (!FindResponse( response, concept, &modifiers ))
|
||||
return "NPC_BaseZombie.Moan1";
|
||||
#else
|
||||
AI_Response *response = SpeakFindResponse(TLK_ZOMBIE_MOAN, modifiers);
|
||||
|
||||
if ( !response )
|
||||
return "NPC_BaseZombie.Moan1";
|
||||
#endif
|
||||
|
||||
// Must be static so it could be returned
|
||||
static char szSound[128];
|
||||
#ifdef NEW_RESPONSE_SYSTEM
|
||||
response.GetName(szSound, sizeof(szSound));
|
||||
#else
|
||||
response->GetName(szSound, sizeof(szSound));
|
||||
|
||||
delete response;
|
||||
#endif
|
||||
|
||||
return szSound;
|
||||
}
|
||||
|
||||
@ -1251,7 +1263,8 @@ void CZombieCustom::AttackSound( void )
|
||||
//-----------------------------------------------------------------------------
|
||||
void CZombieCustom::SpeakIfAllowed(const char *concept, AI_CriteriaSet *modifiers)
|
||||
{
|
||||
Speak( concept, modifiers ? *modifiers : AI_CriteriaSet() );
|
||||
AI_CriteriaSet empty;
|
||||
Speak( concept, modifiers ? *modifiers : empty );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -561,9 +561,15 @@ int CPropAPC::OnTakeDamage( const CTakeDamageInfo &info )
|
||||
m_iHealth -= dmgInfo.GetDamage();
|
||||
if ( m_iHealth <= 0 )
|
||||
{
|
||||
m_iHealth = 0;
|
||||
Event_Killed( dmgInfo );
|
||||
return 0;
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
// False = Cheat death
|
||||
if (ScriptDeathHook( const_cast<CTakeDamageInfo*>(&info) ) != false)
|
||||
#endif
|
||||
{
|
||||
m_iHealth = 0;
|
||||
Event_Killed( dmgInfo );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Chain
|
||||
|
@ -90,9 +90,12 @@ public:
|
||||
|
||||
#ifdef MAPBASE
|
||||
// This is in CBaseEntity, but I can't find a use for it anywhere.
|
||||
// Must not have been fully implemented. Please remove this if it turns out to be something important.
|
||||
// It may have been originally intended for TF2 or some other game-specific item class. Please remove this if it turns out to be something important.
|
||||
virtual bool IsCombatItem() { return true; }
|
||||
|
||||
// Used to access item_healthkit values, etc. from outside of the class
|
||||
virtual float GetItemAmount() { return 1.0f; }
|
||||
|
||||
void InputEnablePlayerPickup( inputdata_t &inputdata );
|
||||
void InputDisablePlayerPickup( inputdata_t &inputdata );
|
||||
void InputEnableNPCPickup( inputdata_t &inputdata );
|
||||
|
@ -18,13 +18,8 @@
|
||||
// Valve uses global pooled strings in various parts of the code, particularly Episodic code,
|
||||
// so they could get away with integer/pointer comparisons instead of string comparisons.
|
||||
//
|
||||
// While developing Mapbase, I thought of what it would be like if that system was more uniform and more widely used.
|
||||
// My OCD ended up taking me over and that thought became real, even though on today's machines these are (for the most part) micro-optimizations
|
||||
// that just clutter the code.
|
||||
//
|
||||
// It was fun and satisfying to do this and I hope you are not judging me for my strange ways.
|
||||
// I wanted to toggle them via a preprocessor so you could turn them off at will, but that just made things messier.
|
||||
// I hope this doesn't cause problems for anyone.
|
||||
// This system was developed early in Mapbase's development as an attempt to make this technique more widely used.
|
||||
// For the most part, this mainly just serves to apply micro-optimize parts of the code.
|
||||
//
|
||||
// -------------------------------------------------------------
|
||||
|
||||
@ -79,7 +74,6 @@ inline bool EntIsClass( CBaseEntity *ent, string_t str2 )
|
||||
|
||||
// Since classnames are pooled, the global string and the entity's classname should point to the same string in memory.
|
||||
// As long as this rule is preserved, we only need a pointer comparison. A string comparison isn't necessary.
|
||||
// Feel free to correct me if I'm disastrously wrong.
|
||||
return ent->m_iClassname == str2;
|
||||
}
|
||||
|
||||
|
@ -672,6 +672,33 @@ int CAI_Monitor::TranslateScheduleString(const char *schedName)
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename Translator>
|
||||
static void SetForEachDelimited( CAI_Monitor &monitor, const char *szValue, const char *delimiters, void (CAI_Monitor::*setter)(int), Translator translator)
|
||||
{
|
||||
char *value = strdup(szValue);
|
||||
char *token = strtok(value, ":");
|
||||
while (token)
|
||||
{
|
||||
(monitor.*setter)(translator(token));
|
||||
|
||||
token = strtok(NULL, ":");
|
||||
}
|
||||
free(value);
|
||||
}
|
||||
|
||||
template<int (CAI_Monitor::*translator)(const char*)>
|
||||
struct CAI_MonitorTranslator
|
||||
{
|
||||
CAI_Monitor &monitor;
|
||||
|
||||
CAI_MonitorTranslator(CAI_Monitor &monitor) : monitor(monitor) {}
|
||||
|
||||
int operator()(const char *value)
|
||||
{
|
||||
return (monitor.*translator)(value);
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Cache user entity field values until spawn is called.
|
||||
// Input : szKeyName - Key to handle.
|
||||
@ -688,13 +715,7 @@ bool CAI_Monitor::KeyValue( const char *szKeyName, const char *szValue )
|
||||
}
|
||||
else if (FStrEq(szKeyName, "Conditions"))
|
||||
{
|
||||
char *token = strtok(strdup(szValue), ":");
|
||||
while (token)
|
||||
{
|
||||
SetCondition(TranslateConditionString(token));
|
||||
|
||||
token = strtok(NULL, ":");
|
||||
}
|
||||
SetForEachDelimited(*this, szValue, ":", &CAI_Monitor::SetCondition, CAI_MonitorTranslator<&CAI_Monitor::TranslateConditionString>(*this));
|
||||
}
|
||||
else if (FStrEq(szKeyName, "SchedulesSimple"))
|
||||
{
|
||||
@ -703,13 +724,7 @@ bool CAI_Monitor::KeyValue( const char *szKeyName, const char *szValue )
|
||||
}
|
||||
else if (FStrEq(szKeyName, "Schedules"))
|
||||
{
|
||||
char *token = strtok(strdup(szValue), ":");
|
||||
while (token)
|
||||
{
|
||||
SetSchedule(TranslateScheduleString(token));
|
||||
|
||||
token = strtok(NULL, ":");
|
||||
}
|
||||
SetForEachDelimited(*this, szValue, ":", &CAI_Monitor::SetSchedule, CAI_MonitorTranslator<&CAI_Monitor::TranslateScheduleString>(*this));
|
||||
}
|
||||
else if (FStrEq(szKeyName, "HintsSimple"))
|
||||
{
|
||||
@ -718,13 +733,7 @@ bool CAI_Monitor::KeyValue( const char *szKeyName, const char *szValue )
|
||||
}
|
||||
else if (FStrEq(szKeyName, "Hints"))
|
||||
{
|
||||
char *token = strtok(strdup(szValue), ":");
|
||||
while (token)
|
||||
{
|
||||
SetHint(atoi(szValue));
|
||||
|
||||
token = strtok(NULL, ":");
|
||||
}
|
||||
SetForEachDelimited(*this, szValue, ":", &CAI_Monitor::SetHint, atoi);
|
||||
}
|
||||
else
|
||||
return CBaseEntity::KeyValue( szKeyName, szValue );
|
||||
|
@ -199,7 +199,7 @@ void CLogicExternalData::InputWriteKeyValue( inputdata_t &inputdata )
|
||||
|
||||
// Separate key from value
|
||||
char *delimiter = Q_strstr(szValue, " ");
|
||||
if (delimiter && (delimiter + 1) != '\0')
|
||||
if (delimiter && delimiter[1] != '\0')
|
||||
{
|
||||
Q_strncpy(key, szValue, MIN((delimiter - szValue) + 1, sizeof(key)));
|
||||
Q_strncpy(value, delimiter + 1, sizeof(value));
|
||||
|
@ -21,6 +21,8 @@ public:
|
||||
DECLARE_DATADESC();
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
CMovieDisplay() { m_bMuted = true; }
|
||||
|
||||
virtual ~CMovieDisplay();
|
||||
|
||||
virtual bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
@ -53,6 +55,7 @@ private:
|
||||
private:
|
||||
CNetworkVar( bool, m_bEnabled );
|
||||
CNetworkVar( bool, m_bLooping );
|
||||
CNetworkVar( bool, m_bMuted);
|
||||
|
||||
CNetworkString( m_szDisplayText, 128 );
|
||||
|
||||
@ -93,6 +96,7 @@ BEGIN_DATADESC( CMovieDisplay )
|
||||
DEFINE_KEYFIELD( m_iScreenWidth, FIELD_INTEGER, "width" ),
|
||||
DEFINE_KEYFIELD( m_iScreenHeight, FIELD_INTEGER, "height" ),
|
||||
DEFINE_KEYFIELD( m_bLooping, FIELD_BOOLEAN, "looping" ),
|
||||
DEFINE_KEYFIELD( m_bMuted, FIELD_BOOLEAN, "muted"),
|
||||
|
||||
DEFINE_FIELD( m_bDoFullTransmit, FIELD_BOOLEAN ),
|
||||
|
||||
@ -108,6 +112,7 @@ END_DATADESC()
|
||||
IMPLEMENT_SERVERCLASS_ST( CMovieDisplay, DT_MovieDisplay )
|
||||
SendPropBool( SENDINFO( m_bEnabled ) ),
|
||||
SendPropBool( SENDINFO( m_bLooping ) ),
|
||||
SendPropBool( SENDINFO( m_bMuted ) ),
|
||||
SendPropString( SENDINFO( m_szMovieFilename ) ),
|
||||
SendPropString( SENDINFO( m_szGroupName ) ),
|
||||
END_SEND_TABLE()
|
||||
@ -120,6 +125,7 @@ CMovieDisplay::~CMovieDisplay()
|
||||
//-----------------------------------------------------------------------------
|
||||
// Read in Hammer data
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool CMovieDisplay::KeyValue( const char *szKeyName, const char *szValue )
|
||||
{
|
||||
// NOTE: Have to do these separate because they set two values instead of one
|
||||
|
@ -159,6 +159,16 @@ BEGIN_DATADESC(CRagdollProp)
|
||||
DEFINE_RAGDOLL_ELEMENT( 21 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 22 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 23 ),
|
||||
#ifdef MAPBASE
|
||||
DEFINE_RAGDOLL_ELEMENT( 24 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 25 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 26 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 27 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 28 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 29 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 30 ),
|
||||
DEFINE_RAGDOLL_ELEMENT( 31 ),
|
||||
#endif
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
@ -191,8 +201,10 @@ void CRagdollProp::Spawn( void )
|
||||
// Starts out as the default fade scale value
|
||||
m_flDefaultFadeScale = m_flFadeScale;
|
||||
|
||||
#ifndef MAPBASE
|
||||
// NOTE: If this fires, then the assert or the datadesc is wrong! (see DEFINE_RAGDOLL_ELEMENT above)
|
||||
Assert( RAGDOLL_MAX_ELEMENTS == 24 );
|
||||
#endif
|
||||
Precache();
|
||||
SetModel( STRING( GetModelName() ) );
|
||||
|
||||
@ -1363,7 +1375,7 @@ CBaseAnimating *CreateServerRagdollSubmodel( CBaseAnimating *pOwner, const char
|
||||
|
||||
#ifdef MAPBASE_VSCRIPT
|
||||
// Hook for pre-spawn ragdolling
|
||||
if (pOwner->m_ScriptScope.IsInitialized() && CBaseAnimating::g_Hook_OnServerRagdoll.CanRunInScope( pOwner->m_ScriptScope ))
|
||||
if (pOwner && pOwner->m_ScriptScope.IsInitialized() && CBaseAnimating::g_Hook_OnServerRagdoll.CanRunInScope( pOwner->m_ScriptScope ))
|
||||
{
|
||||
// ragdoll, submodel
|
||||
ScriptVariant_t args[] = { ScriptVariant_t( pRagdoll->GetScriptInstance() ), true };
|
||||
|
@ -7698,6 +7698,10 @@ void CBasePlayer::Weapon_DropSlot( int weaponSlot )
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MAPBASE
|
||||
ConVar player_autoswitch_on_first_pickup("player_autoswitch_on_pickup", "1", FCVAR_NONE, "Determines how the player should autoswitch when picking up a new weapon. 0 = no autoswitch, 1 = always (default), 2 = use unused weighting system");
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Override to add weapon to the hud
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -7706,24 +7710,25 @@ void CBasePlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon )
|
||||
BaseClass::Weapon_Equip( pWeapon );
|
||||
|
||||
#ifdef MAPBASE
|
||||
// So, I discovered that BumpWeapon seems to have some deprecated code.
|
||||
// It automatically switches the player to all new weapons. Sounds normal, right?
|
||||
// Except that's *also* handled here. Since the BumpWeapon code implied the player could pick up weapons while carrying the mega physcannon,
|
||||
// I assumed it was some old, deprecated code and, since I needed to remove a piece of (also deprecated) code in it, I decided to remove it entirely
|
||||
// and hand weapon switching to this alone. Seems straightforward, right?
|
||||
//
|
||||
// Well, it turns out, this code was more complicated than I thought and used various weights and stuff to determine if a weapon was worth automatically switching to.
|
||||
// It doesn't automatically switch to most of the weapons. Even though I seem to be right about that old code being deprecated,
|
||||
// I got irritated and...uh...replaced the correct Weapon_Equip code with the old deprecated code from BumpWeapon.
|
||||
//
|
||||
// Trust me. It was hard and pointless to get used to. You'll thank me later.
|
||||
|
||||
if ( !PlayerHasMegaPhysCannon() )
|
||||
// BumpWeapon's code appeared to be deprecated; The same operation is already handled here, but with much more code involved.
|
||||
// There's also an unused weighting system which was overridden by that deprecated code. The unused weighting code can be enabled
|
||||
// via player_autoswitch_on_first_pickup.
|
||||
bool bShouldSwitch = false;
|
||||
switch (player_autoswitch_on_first_pickup.GetInt())
|
||||
{
|
||||
Weapon_Switch( pWeapon );
|
||||
// Unused Weighting
|
||||
case 2:
|
||||
bShouldSwitch = g_pGameRules->FShouldSwitchWeapon( this, pWeapon );
|
||||
break;
|
||||
|
||||
// Always (old behavior)
|
||||
case 1:
|
||||
bShouldSwitch = true;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
bool bShouldSwitch = g_pGameRules->FShouldSwitchWeapon( this, pWeapon );
|
||||
#endif
|
||||
|
||||
#ifdef HL2_DLL
|
||||
if ( bShouldSwitch == false && PhysCannonGetHeldEntity( GetActiveWeapon() ) == pWeapon &&
|
||||
@ -7738,7 +7743,6 @@ void CBasePlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon )
|
||||
{
|
||||
Weapon_Switch( pWeapon );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MAPBASE
|
||||
|
@ -90,6 +90,10 @@ BEGIN_DATADESC( CRopeKeyframe )
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Break", InputBreak ),
|
||||
|
||||
#ifdef MAPBASE
|
||||
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSlack", InputSetSlack ),
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetWidth", InputSetWidth ),
|
||||
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSubdivision", InputSetSubdivision ),
|
||||
|
||||
// Outputs
|
||||
DEFINE_OUTPUT( m_OnBreak, "OnBreak" ),
|
||||
#endif
|
||||
@ -613,6 +617,51 @@ void CRopeKeyframe::InputBreak( inputdata_t &inputdata )
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MAPBASE
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Sets the slack
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CRopeKeyframe::InputSetSlack( inputdata_t &inputdata )
|
||||
{
|
||||
m_Slack = inputdata.value.Int();
|
||||
|
||||
// Must resize in order for changes to occur
|
||||
m_RopeFlags |= ROPE_RESIZE;
|
||||
|
||||
if (!(m_RopeFlags & ROPE_USE_WIND))
|
||||
{
|
||||
Warning( "WARNING: SetSlack on %s may need wind enabled in order to function\n", GetDebugName() );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Sets the width
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CRopeKeyframe::InputSetWidth( inputdata_t &inputdata )
|
||||
{
|
||||
m_Width = inputdata.value.Float();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Sets the subdivision
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CRopeKeyframe::InputSetSubdivision( inputdata_t &inputdata )
|
||||
{
|
||||
m_Subdiv = inputdata.value.Int();
|
||||
|
||||
// Must resize in order for changes to occur
|
||||
m_RopeFlags |= ROPE_RESIZE;
|
||||
|
||||
if (!(m_RopeFlags & ROPE_USE_WIND))
|
||||
{
|
||||
Warning( "WARNING: SetSubdivision on %s may need wind enabled in order to function\n", GetDebugName() );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Breaks the rope
|
||||
// Output : Returns true on success, false on failure.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user