From 6f0781b5b8281ad99b42882be726ea347a48baf6 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Thu, 24 Dec 2020 23:55:19 -0600 Subject: [PATCH 1/9] Added logic_script_client entity and a user message for calling a client-side function --- sp/src/game/shared/mapbase/mapbase_shared.cpp | 47 +++++ sp/src/game/shared/usermessages.cpp | 9 + sp/src/game/shared/vscript_shared.cpp | 180 ++++++++++++++++++ 3 files changed, 236 insertions(+) diff --git a/sp/src/game/shared/mapbase/mapbase_shared.cpp b/sp/src/game/shared/mapbase/mapbase_shared.cpp index 2763b457..3406d9c8 100644 --- a/sp/src/game/shared/mapbase/mapbase_shared.cpp +++ b/sp/src/game/shared/mapbase/mapbase_shared.cpp @@ -16,10 +16,12 @@ #include "saverestore_utlvector.h" #include "props_shared.h" #include "utlbuffer.h" +#include "usermessages.h" #ifdef CLIENT_DLL #include "hud_closecaption.h" #include "panelmetaclassmgr.h" #include "c_soundscape.h" +#include "hud_macros.h" #else #include "soundscape_system.h" #include "AI_ResponseSystem.h" @@ -616,3 +618,48 @@ BEGIN_DATADESC( CMapbaseManifestEntity ) END_DATADESC() #endif + +#ifdef CLIENT_DLL +void __MsgFunc_CallClientScriptFunction( bf_read &msg ) +{ + char szFunction[64]; + if (!msg.ReadString( szFunction, sizeof( szFunction ) )) + { + CGMsg( 0, CON_GROUP_VSCRIPT, "Unable to read function string\n" ); + } + + int idx = msg.ReadByte(); + C_BaseEntity *pEntity = CBaseEntity::Instance( idx ); + + if (pEntity) + { + if (pEntity->m_ScriptScope.IsInitialized()) + { + //CGMsg( 0, CON_GROUP_VSCRIPT, "%s calling function \"%s\"\n", pEntity->GetDebugName(), szFunction ); + pEntity->CallScriptFunction( szFunction, NULL ); + } + else + { + CGMsg( 0, CON_GROUP_VSCRIPT, "%s scope not initialized\n", pEntity->GetDebugName() ); + } + } + else + { + CGMsg( 0, CON_GROUP_VSCRIPT, "Clientside entity not found for script function (index %i)\n", idx ); + } +} + +void HookMapbaseUserMessages( void ) +{ + HOOK_MESSAGE( CallClientScriptFunction ); +} +#endif + +void RegisterMapbaseUserMessages( void ) +{ + usermessages->Register( "CallClientScriptFunction", -1 ); + +#ifdef CLIENT_DLL + HookMapbaseUserMessages(); +#endif +} diff --git a/sp/src/game/shared/usermessages.cpp b/sp/src/game/shared/usermessages.cpp index cf1beae3..6b3ad681 100644 --- a/sp/src/game/shared/usermessages.cpp +++ b/sp/src/game/shared/usermessages.cpp @@ -13,6 +13,10 @@ void RegisterUserMessages( void ); +#ifdef MAPBASE +void RegisterMapbaseUserMessages( void ); +#endif + //----------------------------------------------------------------------------- // Purpose: Force registration on .dll load // FIXME: Should this be a client/server system? @@ -21,6 +25,11 @@ CUserMessages::CUserMessages() { // Game specific registration function; RegisterUserMessages(); + +#ifdef MAPBASE + // Mapbase registration function; + RegisterMapbaseUserMessages(); +#endif } CUserMessages::~CUserMessages() diff --git a/sp/src/game/shared/vscript_shared.cpp b/sp/src/game/shared/vscript_shared.cpp index ebe997a8..d844c323 100644 --- a/sp/src/game/shared/vscript_shared.cpp +++ b/sp/src/game/shared/vscript_shared.cpp @@ -274,3 +274,183 @@ CON_COMMAND_SHARED( script_dump_all, "Dump the state of the VM to the console" ) } g_pScriptVM->DumpState(); } + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: An entity that acts as a container for game scripts. +//----------------------------------------------------------------------------- + +#define MAX_SCRIPT_GROUP_CLIENT 8 + +class CLogicScriptClient : public CBaseEntity +{ +public: + DECLARE_CLASS( CLogicScriptClient, CBaseEntity ); + DECLARE_DATADESC(); + DECLARE_NETWORKCLASS(); + +#ifdef CLIENT_DLL + void OnDataChanged( DataUpdateType_t type ) + { + BaseClass::OnDataChanged( type ); + + if ( !m_ScriptScope.IsInitialized() ) + { + RunVScripts(); + } + } +#else + int UpdateTransmitState() { return SetTransmitState( FL_EDICT_ALWAYS ); } +#endif + + bool KeyValue( const char *szKeyName, const char *szValue ) + { + if ( FStrEq( szKeyName, "vscripts" ) ) + { + Q_strcpy( m_iszClientScripts.GetForModify(), szValue ); + } + + return BaseClass::KeyValue( szKeyName, szValue ); + } + + void RunVScripts() + { +#ifdef CLIENT_DLL + if (m_iszClientScripts == NULL_STRING) + { + CGMsg( 0, CON_GROUP_VSCRIPT, "%s has no client scripts", GetDebugName() ); + return; + } + + if (g_pScriptVM == NULL) + { + return; + } + + ValidateScriptScope(); + + // All functions we want to have call chained instead of overwritten + // by other scripts in this entities list. + static const char* sCallChainFunctions[] = + { + "OnPostSpawn", + "Precache" + }; + + ScriptLanguage_t language = g_pScriptVM->GetLanguage(); + + // Make a call chainer for each in this entities scope + for (int j = 0; j < ARRAYSIZE( sCallChainFunctions ); ++j) + { + + if (language == SL_PYTHON) + { + // UNDONE - handle call chaining in python + ; + } + else if (language == SL_SQUIRREL) + { + //TODO: For perf, this should be precompiled and the %s should be passed as a parameter + HSCRIPT hCreateChainScript = g_pScriptVM->CompileScript( CFmtStr( "%sCallChain <- CSimpleCallChainer(\"%s\", self.GetScriptScope(), true)", sCallChainFunctions[j], sCallChainFunctions[j] ) ); + g_pScriptVM->Run( hCreateChainScript, (HSCRIPT)m_ScriptScope ); + } + } + + char szScriptsList[255]; + Q_strcpy( szScriptsList, m_iszClientScripts.Get() ); + CUtlStringList szScripts; + + V_SplitString( szScriptsList, " ", szScripts ); + + for (int i = 0; i < szScripts.Count(); i++) + { + CGMsg( 0, CON_GROUP_VSCRIPT, "%s executing script: %s\n", GetDebugName(), szScripts[i] ); + + RunScriptFile( szScripts[i], IsWorld() ); + + for (int j = 0; j < ARRAYSIZE( sCallChainFunctions ); ++j) + { + if (language == SL_PYTHON) + { + // UNDONE - handle call chaining in python + ; + } + else if (language == SL_SQUIRREL) + { + //TODO: For perf, this should be precompiled and the %s should be passed as a parameter. + HSCRIPT hRunPostScriptExecute = g_pScriptVM->CompileScript( CFmtStr( "%sCallChain.PostScriptExecute()", sCallChainFunctions[j] ) ); + g_pScriptVM->Run( hRunPostScriptExecute, (HSCRIPT)m_ScriptScope ); + } + } + } +#else + // Avoids issues from having m_iszVScripts set without actually having a script scope + ValidateScriptScope(); + + if (m_bRunOnServer) + { + BaseClass::RunVScripts(); + } +#endif + } + +#ifndef CLIENT_DLL + void InputCallScriptFunctionClient( inputdata_t &inputdata ) + { + // TODO: Support for specific players? + CBroadcastRecipientFilter filter; + filter.MakeReliable(); + + const char *pszFunction = inputdata.value.String(); + if (strlen( pszFunction ) > 64) + { + Msg("%s CallScriptFunctionClient: \"%s\" is too long at %i characters, must be 64 or less\n", GetDebugName(), pszFunction, strlen(pszFunction)); + return; + } + + UserMessageBegin( filter, "CallClientScriptFunction" ); + WRITE_STRING( pszFunction ); // function + WRITE_SHORT( entindex() ); // entity + MessageEnd(); + } +#endif + + //CNetworkArray( string_t, m_iszGroupMembers, MAX_SCRIPT_GROUP_CLIENT ); + CNetworkString( m_iszClientScripts, 128 ); + + bool m_bRunOnServer; +}; + +LINK_ENTITY_TO_CLASS( logic_script_client, CLogicScriptClient ); + +BEGIN_DATADESC( CLogicScriptClient ) + + //DEFINE_KEYFIELD( m_iszGroupMembers[0], FIELD_STRING, "Group00"), + //DEFINE_KEYFIELD( m_iszGroupMembers[1], FIELD_STRING, "Group01"), + //DEFINE_KEYFIELD( m_iszGroupMembers[2], FIELD_STRING, "Group02"), + //DEFINE_KEYFIELD( m_iszGroupMembers[3], FIELD_STRING, "Group03"), + //DEFINE_KEYFIELD( m_iszGroupMembers[4], FIELD_STRING, "Group04"), + //DEFINE_KEYFIELD( m_iszGroupMembers[5], FIELD_STRING, "Group05"), + //DEFINE_KEYFIELD( m_iszGroupMembers[6], FIELD_STRING, "Group06"), + //DEFINE_KEYFIELD( m_iszGroupMembers[7], FIELD_STRING, "Group07"), + + DEFINE_KEYFIELD( m_bRunOnServer, FIELD_BOOLEAN, "RunOnServer" ), + +#ifndef CLIENT_DLL + DEFINE_INPUTFUNC( FIELD_STRING, "CallScriptFunctionClient", InputCallScriptFunctionClient ), +#endif + +END_DATADESC() + +IMPLEMENT_NETWORKCLASS_DT( CLogicScriptClient, DT_LogicScriptClient ) + +#ifdef CLIENT_DLL + //RecvPropArray( RecvPropString( RECVINFO( m_iszGroupMembers[0] ) ), m_iszGroupMembers ), + RecvPropString( RECVINFO( m_iszClientScripts ) ), +#else + //SendPropArray( SendPropStringT( SENDINFO_ARRAY( m_iszGroupMembers ) ), m_iszGroupMembers ), + SendPropString( SENDINFO( m_iszClientScripts ) ), +#endif + +END_NETWORK_TABLE() +#endif From f70c066a8e8b931c146fb3809c9aef29633602c3 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Fri, 25 Dec 2020 09:09:33 -0600 Subject: [PATCH 2/9] Added a bunch of client-side VScript functions to C_BaseEntity --- sp/src/game/client/c_baseentity.cpp | 66 +++++++++++++++++++++++- sp/src/game/client/c_baseentity.h | 30 ++++++++++- sp/src/game/server/baseentity.cpp | 54 +------------------ sp/src/game/shared/baseentity_shared.cpp | 54 +++++++++++++++++++ 4 files changed, 150 insertions(+), 54 deletions(-) diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index 41a32a94..0c9060b9 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -456,21 +456,85 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC( GetClassname, "" ) DEFINE_SCRIPTFUNC_NAMED( GetEntityName, "GetName", "" ) + DEFINE_SCRIPTFUNC_NAMED( SetAbsOrigin, "SetOrigin", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetForward, "SetForwardVector", "Set the orientation of the entity to have this forward vector" ) + + DEFINE_SCRIPTFUNC( GetLocalOrigin, "GetLocalOrigin" ) + DEFINE_SCRIPTFUNC( SetLocalOrigin, "SetLocalOrigin" ) + DEFINE_SCRIPTFUNC( GetLocalAngles, "GetLocalAngles" ) + DEFINE_SCRIPTFUNC( SetLocalAngles, "SetLocalAngles" ) + DEFINE_SCRIPTFUNC_NAMED( WorldSpaceCenter, "GetCenter", "Get vector to center of object - absolute coords" ) DEFINE_SCRIPTFUNC_NAMED( ScriptEyePosition, "EyePosition", "Get vector to eye position - absolute coords" ) - DEFINE_SCRIPTFUNC_NAMED( ScriptGetAngles, "GetAngles", "Get entity pitch, yaw, roll as a vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEyeAngles, "EyeAngles", "Get eye pitch, yaw, roll as a vector" ) + DEFINE_SCRIPTFUNC_NAMED( GetAbsAngles, "GetAngles", "Get entity pitch, yaw, roll as a vector" ) + DEFINE_SCRIPTFUNC_NAMED( SetAbsAngles, "SetAngles", "Set entity pitch, yaw, roll" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoundingMins, "GetBoundingMins", "Get a vector containing min bounds, centered on object" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoundingMaxs, "GetBoundingMaxs", "Get a vector containing max bounds, centered on object" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEntityToWorldTransform, "EntityToWorldTransform", "Get the entity's transform" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPhysicsObject, "GetPhysicsObject", "Get the entity's physics object if it has one" ) + + DEFINE_SCRIPTFUNC( GetWaterLevel, "Get current level of water submergence" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMoveParent, "GetMoveParent", "If in hierarchy, retrieves the entity's parent" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetRootMoveParent, "GetRootMoveParent", "If in hierarchy, walks up the hierarchy to find the root parent" ) DEFINE_SCRIPTFUNC_NAMED( ScriptFirstMoveChild, "FirstMoveChild", "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptNextMovePeer, "NextMovePeer", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFollowEntity, "FollowEntity", "Begin following the specified entity. This makes this entity non-solid, parents it to the target entity, and teleports it to the specified entity's origin. The second parameter is whether or not to use bonemerging while following." ) + DEFINE_SCRIPTFUNC( StopFollowingEntity, "Stops following an entity if we're following one." ) + DEFINE_SCRIPTFUNC( IsFollowingEntity, "Returns true if this entity is following another entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFollowedEntity, "GetFollowedEntity", "Get the entity we're following." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorVector, "GetRenderColorVector", "Get the render color as a vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorR, "GetRenderColorR", "Get the render color's R value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorG, "GetRenderColorG", "Get the render color's G value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorB, "GetRenderColorB", "Get the render color's B value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAlpha, "GetRenderAlpha", "Get the render color's alpha value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorVector, "SetRenderColorVector", "Set the render color as a vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColor, "SetRenderColor", "Set the render color" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorR, "SetRenderColorR", "Set the render color's R value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorG, "SetRenderColorG", "Set the render color's G value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorB, "SetRenderColorB", "Set the render color's B value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAlpha, "SetRenderAlpha", "Set the render color's alpha value" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRenderMode, "GetRenderMode", "Get render mode" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetRenderMode, "SetRenderMode", "Set render mode" ) + DEFINE_SCRIPTFUNC( GetEffects, "Get effects" ) + DEFINE_SCRIPTFUNC( AddEffects, "Add effect(s)" ) + DEFINE_SCRIPTFUNC( RemoveEffects, "Remove effect(s)" ) + DEFINE_SCRIPTFUNC( ClearEffects, "Clear effect(s)" ) + DEFINE_SCRIPTFUNC( SetEffects, "Set effect(s)" ) DEFINE_SCRIPTFUNC( IsEffectActive, "Check if an effect is active" ) + DEFINE_SCRIPTFUNC( GetFlags, "Get flags" ) + DEFINE_SCRIPTFUNC( AddFlag, "Add flag" ) + DEFINE_SCRIPTFUNC( RemoveFlag, "Remove flag" ) + + DEFINE_SCRIPTFUNC( GetEFlags, "Get Eflags" ) + DEFINE_SCRIPTFUNC( AddEFlags, "Add Eflags" ) + DEFINE_SCRIPTFUNC( RemoveEFlags, "Remove Eflags" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMoveType, "GetMoveType", "Get the move type" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetMoveType, "SetMoveType", "Set the move type" ) + + DEFINE_SCRIPTFUNC( GetCollisionGroup, "Get the collision group" ) + DEFINE_SCRIPTFUNC( SetCollisionGroup, "Set the collision group" ) + + DEFINE_SCRIPTFUNC( GetSolidFlags, "Get solid flags" ) + DEFINE_SCRIPTFUNC( AddSolidFlags, "Add solid flags" ) + DEFINE_SCRIPTFUNC( RemoveSolidFlags, "Remove solid flags" ) + + DEFINE_SCRIPTFUNC( IsPlayer, "Returns true if this entity is a player." ) + DEFINE_SCRIPTFUNC( IsNPC, "Returns true if this entity is a NPC." ) + //DEFINE_SCRIPTFUNC( IsCombatCharacter, "Returns true if this entity is a combat character (player or NPC)." ) + DEFINE_SCRIPTFUNC_NAMED( IsBaseCombatWeapon, "IsWeapon", "Returns true if this entity is a weapon." ) + DEFINE_SCRIPTFUNC( IsWorld, "Returns true if this entity is the world." ) + DEFINE_SCRIPTFUNC_NAMED( GetEntityIndex, "entindex", "" ) #endif END_SCRIPTDESC(); diff --git a/sp/src/game/client/c_baseentity.h b/sp/src/game/client/c_baseentity.h index 8cd7fec7..de40c28e 100644 --- a/sp/src/game/client/c_baseentity.h +++ b/sp/src/game/client/c_baseentity.h @@ -1149,6 +1149,11 @@ public: bool IsFollowingEntity(); CBaseEntity *GetFollowedEntity(); +#ifdef MAPBASE_VSCRIPT + void ScriptFollowEntity( HSCRIPT hBaseEntity, bool bBoneMerge ); + HSCRIPT ScriptGetFollowedEntity(); +#endif + // For shadows rendering the correct body + sequence... virtual int GetBody() { return 0; } virtual int GetSkin() { return 0; } @@ -1170,15 +1175,38 @@ public: void VScriptPrecacheScriptSound(const char* soundname); const Vector& ScriptEyePosition(void) { static Vector vec; vec = EyePosition(); return vec; } - const Vector& ScriptGetAngles(void) { static Vector vec; QAngle qa = GetAbsAngles(); vec.x = qa.x; vec.y = qa.y; vec.z = qa.z; return vec; } + const QAngle& ScriptEyeAngles(void) { static QAngle ang; ang = EyeAngles(); return ang; } + void ScriptSetForward( const Vector& v ) { QAngle angles; VectorAngles( v, angles ); SetAbsAngles( angles ); } const Vector& ScriptGetBoundingMins( void ) { return m_Collision.OBBMins(); } const Vector& ScriptGetBoundingMaxs( void ) { return m_Collision.OBBMaxs(); } + HSCRIPT ScriptEntityToWorldTransform( void ); + + HSCRIPT ScriptGetPhysicsObject( void ); + HSCRIPT ScriptGetMoveParent( void ); HSCRIPT ScriptGetRootMoveParent(); HSCRIPT ScriptFirstMoveChild( void ); HSCRIPT ScriptNextMovePeer( void ); + + const Vector& ScriptGetColorVector(); + int ScriptGetColorR() { return m_clrRender.GetR(); } + int ScriptGetColorG() { return m_clrRender.GetG(); } + int ScriptGetColorB() { return m_clrRender.GetB(); } + int ScriptGetAlpha() { return m_clrRender.GetA(); } + void ScriptSetColorVector( const Vector& vecColor ); + void ScriptSetColor( int r, int g, int b ); + void ScriptSetColorR( int iVal ) { SetRenderColorR( iVal ); } + void ScriptSetColorG( int iVal ) { SetRenderColorG( iVal ); } + void ScriptSetColorB( int iVal ) { SetRenderColorB( iVal ); } + void ScriptSetAlpha( int iVal ) { SetRenderColorA( iVal ); } + + int ScriptGetRenderMode() { return GetRenderMode(); } + void ScriptSetRenderMode( int nRenderMode ) { SetRenderMode( (RenderMode_t)nRenderMode ); } + + int ScriptGetMoveType() { return GetMoveType(); } + void ScriptSetMoveType( int iMoveType ) { SetMoveType( (MoveType_t)iMoveType ); } #endif // Stubs on client diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index 3cca68f5..6d36d1de 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -623,18 +623,6 @@ CBaseEntity *CBaseEntity::GetFollowedEntity() return GetMoveParent(); } -#ifdef MAPBASE_VSCRIPT -void CBaseEntity::ScriptFollowEntity( HSCRIPT hBaseEntity, bool bBoneMerge ) -{ - FollowEntity( ToEnt( hBaseEntity ), bBoneMerge ); -} - -HSCRIPT CBaseEntity::ScriptGetFollowedEntity() -{ - return ToHScript( GetFollowedEntity() ); -} -#endif - void CBaseEntity::SetClassname( const char *className ) { m_iClassname = AllocPooledString( className ); @@ -2360,6 +2348,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorB, "SetRenderColorB", "Set the render color's B value" ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetAlpha, "SetRenderAlpha", "Set the render color's alpha value" ) + // LEGACY DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorVector, "GetColorVector", SCRIPT_HIDE ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorR, "GetColorR", SCRIPT_HIDE ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorG, "GetColorG", SCRIPT_HIDE ) @@ -2370,6 +2359,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorG, "SetColorG", SCRIPT_HIDE ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetColorB, "SetColorB", SCRIPT_HIDE ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetAlpha, "SetAlpha", SCRIPT_HIDE ) + // END LEGACY DEFINE_SCRIPTFUNC_NAMED( ScriptGetRenderMode, "GetRenderMode", "Get render mode" ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetRenderMode, "SetRenderMode", "Set render mode" ) @@ -10204,46 +10194,6 @@ const char *CBaseEntity::ScriptGetKeyValue( const char *pszKeyName ) return szValue; } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -const Vector& CBaseEntity::ScriptGetColorVector() -{ - static Vector vecColor; - vecColor.Init( m_clrRender.GetR(), m_clrRender.GetG(), m_clrRender.GetB() ); - return vecColor; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CBaseEntity::ScriptSetColorVector( const Vector& vecColor ) -{ - SetRenderColor( vecColor.x, vecColor.y, vecColor.z ); -} - -void CBaseEntity::ScriptSetColor( int r, int g, int b ) -{ - SetRenderColor( r, g, b ); -} - -//----------------------------------------------------------------------------- -// Vscript: Gets the entity matrix transform -//----------------------------------------------------------------------------- -HSCRIPT CBaseEntity::ScriptEntityToWorldTransform( void ) -{ - return g_pScriptVM->RegisterInstance( &EntityToWorldTransform() ); -} - -//----------------------------------------------------------------------------- -// Vscript: Gets the entity's physics object if it has one -//----------------------------------------------------------------------------- -HSCRIPT CBaseEntity::ScriptGetPhysicsObject( void ) -{ - if (VPhysicsGetObject()) - return g_pScriptVM->RegisterInstance( VPhysicsGetObject() ); - else - return NULL; -} - //----------------------------------------------------------------------------- // Vscript: Dispatch an interaction to the entity //----------------------------------------------------------------------------- diff --git a/sp/src/game/shared/baseentity_shared.cpp b/sp/src/game/shared/baseentity_shared.cpp index 3b656b7a..886d12e0 100644 --- a/sp/src/game/shared/baseentity_shared.cpp +++ b/sp/src/game/shared/baseentity_shared.cpp @@ -2418,6 +2418,18 @@ void CBaseEntity::FollowEntity( CBaseEntity *pBaseEntity, bool bBoneMerge ) } } +#ifdef MAPBASE_VSCRIPT +void CBaseEntity::ScriptFollowEntity( HSCRIPT hBaseEntity, bool bBoneMerge ) +{ + FollowEntity( ToEnt( hBaseEntity ), bBoneMerge ); +} + +HSCRIPT CBaseEntity::ScriptGetFollowedEntity() +{ + return ToHScript( GetFollowedEntity() ); +} +#endif + void CBaseEntity::SetEffectEntity( CBaseEntity *pEffectEnt ) { if ( m_hEffectEntity.Get() != pEffectEnt ) @@ -2607,3 +2619,45 @@ bool CBaseEntity::IsToolRecording() const #endif } #endif + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const Vector& CBaseEntity::ScriptGetColorVector() +{ + static Vector vecColor; + vecColor.Init( m_clrRender.GetR(), m_clrRender.GetG(), m_clrRender.GetB() ); + return vecColor; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptSetColorVector( const Vector& vecColor ) +{ + SetRenderColor( vecColor.x, vecColor.y, vecColor.z ); +} + +void CBaseEntity::ScriptSetColor( int r, int g, int b ) +{ + SetRenderColor( r, g, b ); +} + +//----------------------------------------------------------------------------- +// Vscript: Gets the entity matrix transform +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptEntityToWorldTransform( void ) +{ + return g_pScriptVM->RegisterInstance( &EntityToWorldTransform() ); +} + +//----------------------------------------------------------------------------- +// Vscript: Gets the entity's physics object if it has one +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptGetPhysicsObject( void ) +{ + if (VPhysicsGetObject()) + return g_pScriptVM->RegisterInstance( VPhysicsGetObject() ); + else + return NULL; +} +#endif From 5e254d19c064a02176148938404a1721e25a29a3 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Fri, 25 Dec 2020 09:11:25 -0600 Subject: [PATCH 3/9] Added more client-side model/animating VScript functions + a hook for handling client-side ragdolls --- sp/src/game/client/c_baseanimating.cpp | 84 ++++++++++++++++++++++++++ sp/src/game/client/c_baseanimating.h | 19 ++++-- 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/sp/src/game/client/c_baseanimating.cpp b/sp/src/game/client/c_baseanimating.cpp index 051eeb2c..715ef12a 100644 --- a/sp/src/game/client/c_baseanimating.cpp +++ b/sp/src/game/client/c_baseanimating.cpp @@ -281,6 +281,10 @@ BEGIN_DATADESC( C_ClientRagdoll ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +ScriptHook_t C_BaseAnimating::g_Hook_OnClientRagdoll; +#endif + BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-side" ) #ifdef MAPBASE_VSCRIPT DEFINE_SCRIPTFUNC_NAMED( ScriptGetPoseParameter, "GetPoseParameter", "Get the specified pose parameter's value" ) @@ -288,6 +292,14 @@ BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-si DEFINE_SCRIPTFUNC_NAMED( ScriptSetPoseParameter, "SetPoseParameter", "Set the specified pose parameter to the specified value" ) DEFINE_SCRIPTFUNC( IsSequenceFinished, "Ask whether the main sequence is done playing" ) #ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( LookupAttachment, "Get the named attachement id" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentOrigin, "GetAttachmentOrigin", "Get the attachement id's origin vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentAngles, "GetAttachmentAngles", "Get the attachement id's angles as a p,y,r vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentMatrix, "GetAttachmentMatrix", "Get the attachement id's matrix transform" ) + + DEFINE_SCRIPTFUNC( LookupBone, "Get the named bone id" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoneTransform, "GetBoneTransform", "Get the transform for the specified bone" ) + DEFINE_SCRIPTFUNC( SetBodygroup, "Sets a bodygroup") DEFINE_SCRIPTFUNC( GetBodygroup, "Gets a bodygroup" ) DEFINE_SCRIPTFUNC( GetBodygroupName, "Gets a bodygroup name" ) @@ -302,7 +314,22 @@ BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-si DEFINE_SCRIPTFUNC( LookupActivity, "Gets the ID of the specified activity name" ) DEFINE_SCRIPTFUNC( GetSequenceName, "Gets the name of the specified sequence index" ) DEFINE_SCRIPTFUNC( GetSequenceActivityName, "Gets the activity name of the specified sequence index" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSequenceMoveDist, "GetSequenceMoveDist", "Gets the move distance of the specified sequence" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetSequenceActivity, "GetSequenceActivity", "Gets the activity ID of the specified sequence index" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSelectWeightedSequence, "SelectWeightedSequence", "Selects a sequence for the specified activity ID" ) + + DEFINE_SCRIPTFUNC( GetPlaybackRate, "" ) + DEFINE_SCRIPTFUNC( SetPlaybackRate, "" ) + DEFINE_SCRIPTFUNC( GetCycle, "" ) + DEFINE_SCRIPTFUNC( SetCycle, "" ) + DEFINE_SCRIPTFUNC( GetSkin, "Gets the model's skin" ) + DEFINE_SCRIPTFUNC( SetSkin, "Sets the model's skin" ) + + DEFINE_SCRIPTFUNC( IsRagdoll, "" ) + + BEGIN_SCRIPTHOOK( C_BaseAnimating::g_Hook_OnClientRagdoll, "OnClientRagdoll", FIELD_VOID, "Called when this entity turns into a client-side ragdoll." ) + DEFINE_SCRIPTHOOK_PARAM( "ragdoll", FIELD_HSCRIPT ) + END_SCRIPTHOOK() #endif END_SCRIPTDESC(); @@ -1429,6 +1456,52 @@ float C_BaseAnimating::ClampCycle( float flCycle, bool isLooping ) } #ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: Returns the world location and world angles of an attachment to vscript caller +// Input : attachment name +// Output : location and angles +//----------------------------------------------------------------------------- +const Vector& C_BaseAnimating::ScriptGetAttachmentOrigin( int iAttachment ) +{ + + static Vector absOrigin; + static QAngle qa; + + C_BaseAnimating::GetAttachment( iAttachment, absOrigin, qa ); + + return absOrigin; +} + +const Vector& C_BaseAnimating::ScriptGetAttachmentAngles( int iAttachment ) +{ + + static Vector absOrigin; + static Vector absAngles; + static QAngle qa; + + C_BaseAnimating::GetAttachment( iAttachment, absOrigin, qa ); + absAngles.x = qa.x; + absAngles.y = qa.y; + absAngles.z = qa.z; + return absAngles; +} + +HSCRIPT C_BaseAnimating::ScriptGetAttachmentMatrix( int iAttachment ) +{ + static matrix3x4_t matrix; + + C_BaseAnimating::GetAttachment( iAttachment, matrix ); + return g_pScriptVM->RegisterInstance( &matrix ); +} + +void C_BaseAnimating::ScriptGetBoneTransform( int iBone, HSCRIPT hTransform ) +{ + if (hTransform == NULL) + return; + + GetBoneTransform( iBone, *HScriptToClass( hTransform ) ); +} + float C_BaseAnimating::ScriptGetPoseParameter( const char* szName ) { CStudioHdr* pHdr = GetModelPtr(); @@ -4736,6 +4809,17 @@ C_BaseAnimating *C_BaseAnimating::BecomeRagdollOnClient() const float boneDt = 0.1f; GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt ); pRagdoll->InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt ); + +#ifdef MAPBASE_VSCRIPT + // Hook for ragdolling + if (m_ScriptScope.IsInitialized() && g_Hook_OnClientRagdoll.CanRunInScope( m_ScriptScope )) + { + // ragdoll + ScriptVariant_t args[] = { ScriptVariant_t( pRagdoll->GetScriptInstance() ) }; + g_Hook_OnClientRagdoll.Call( m_ScriptScope, NULL, args ); + } +#endif + return pRagdoll; } diff --git a/sp/src/game/client/c_baseanimating.h b/sp/src/game/client/c_baseanimating.h index e1426b71..fe48cfa0 100644 --- a/sp/src/game/client/c_baseanimating.h +++ b/sp/src/game/client/c_baseanimating.h @@ -454,6 +454,21 @@ public: virtual bool IsViewModel() const; #ifdef MAPBASE_VSCRIPT + const Vector& ScriptGetAttachmentOrigin(int iAttachment); + const Vector& ScriptGetAttachmentAngles(int iAttachment); + HSCRIPT ScriptGetAttachmentMatrix(int iAttachment); + + void ScriptGetBoneTransform( int iBone, HSCRIPT hTransform ); + + int ScriptGetSequenceActivity( int iSequence ) { return GetSequenceActivity( iSequence ); } + float ScriptGetSequenceMoveDist( int iSequence ) { return GetSequenceMoveDist( GetModelPtr(), iSequence ); } + int ScriptSelectWeightedSequence( int activity ) { return SelectWeightedSequence( (Activity)activity ); } + + // For VScript + void SetSkin( int iSkin ) { m_nSkin = iSkin; } + + static ScriptHook_t g_Hook_OnClientRagdoll; + float ScriptGetPoseParameter(const char* szName); #endif void ScriptSetPoseParameter(const char* szName, float fValue); @@ -475,10 +490,6 @@ protected: virtual bool CalcAttachments(); -#ifdef MAPBASE_VSCRIPT - int ScriptGetSequenceActivity( int iSequence ) { return GetSequenceActivity( iSequence ); } -#endif - private: // This method should return true if the bones have changed + SetupBones needs to be called virtual float LastBoneChangedTime() { return FLT_MAX; } From cb7cee6283e5aae976ed16e6620083be71861eb9 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Fri, 25 Dec 2020 23:53:59 -0600 Subject: [PATCH 4/9] Added a few more VScript base entity functions to the client --- sp/src/game/client/c_baseentity.cpp | 6 +++ sp/src/game/client/c_baseentity.h | 5 +++ sp/src/game/server/baseentity.cpp | 47 +-------------------- sp/src/game/shared/baseentity_shared.cpp | 52 ++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 45 deletions(-) diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index 0c9060b9..a4a29ea1 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -442,6 +442,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities #ifdef MAPBASE_VSCRIPT DEFINE_SCRIPTFUNC( ValidateScriptScope, "Ensure that an entity's script scope has been created" ) + DEFINE_SCRIPTFUNC( GetOrCreatePrivateScriptScope, "Create and retrieve the script-side data associated with an entity" ) DEFINE_SCRIPTFUNC( GetScriptScope, "Retrieve the script-side data associated with an entity" ) DEFINE_SCRIPTFUNC( GetHealth, "" ) @@ -479,6 +480,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC( GetWaterLevel, "Get current level of water submergence" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetParent, "SetParent", "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetMoveParent, "GetMoveParent", "If in hierarchy, retrieves the entity's parent" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetRootMoveParent, "GetRootMoveParent", "If in hierarchy, walks up the hierarchy to find the root parent" ) DEFINE_SCRIPTFUNC_NAMED( ScriptFirstMoveChild, "FirstMoveChild", "" ) @@ -489,6 +491,9 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC( IsFollowingEntity, "Returns true if this entity is following another entity." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetFollowedEntity, "GetFollowedEntity", "Get the entity we're following." ) + DEFINE_SCRIPTFUNC_NAMED( GetScriptOwnerEntity, "GetOwner", "Gets this entity's owner" ) + DEFINE_SCRIPTFUNC_NAMED( SetScriptOwnerEntity, "SetOwner", "Sets this entity's owner" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorVector, "GetRenderColorVector", "Get the render color as a vector" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorR, "GetRenderColorR", "Get the render color's R value" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorG, "GetRenderColorG", "Get the render color's G value" ) @@ -537,6 +542,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC_NAMED( GetEntityIndex, "entindex", "" ) #endif + END_SCRIPTDESC(); #ifndef NO_ENTITY_PREDICTION diff --git a/sp/src/game/client/c_baseentity.h b/sp/src/game/client/c_baseentity.h index de40c28e..598b88c7 100644 --- a/sp/src/game/client/c_baseentity.h +++ b/sp/src/game/client/c_baseentity.h @@ -266,6 +266,7 @@ public: bool ValidateScriptScope(); bool CallScriptFunction( const char* pFunctionName, ScriptVariant_t* pFunctionReturn ); + HSCRIPT GetOrCreatePrivateScriptScope(); HSCRIPT GetScriptScope() { return m_ScriptScope; } HSCRIPT LookupScriptFunction(const char* pFunctionName); @@ -275,6 +276,9 @@ public: bool RunScript( const char* pScriptText, const char* pDebugFilename = "C_BaseEntity::RunScript" ); #endif + HSCRIPT GetScriptOwnerEntity(); + virtual void SetScriptOwnerEntity(HSCRIPT pOwner); + HSCRIPT GetScriptInstance(); HSCRIPT m_hScriptInstance; @@ -1185,6 +1189,7 @@ public: HSCRIPT ScriptGetPhysicsObject( void ); + void ScriptSetParent( HSCRIPT hParent, const char *szAttachment ); HSCRIPT ScriptGetMoveParent( void ); HSCRIPT ScriptGetRootMoveParent(); HSCRIPT ScriptFirstMoveChild( void ); diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index 6d36d1de..05c81eae 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -8725,51 +8725,6 @@ HSCRIPT CBaseEntity::GetScriptScope() return m_ScriptScope; } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -#ifdef MAPBASE_VSCRIPT -HSCRIPT CBaseEntity::GetOrCreatePrivateScriptScope() -{ - ValidateScriptScope(); - return m_ScriptScope; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CBaseEntity::ScriptSetParent(HSCRIPT hParent, const char *szAttachment) -{ - CBaseEntity *pParent = ToEnt(hParent); - if ( !pParent ) - { - SetParent(NULL); - return; - } - - // if an attachment is specified, the parent needs to be CBaseAnimating - if ( szAttachment && szAttachment[0] != '\0' ) - { - CBaseAnimating *pAnimating = pParent->GetBaseAnimating(); - if ( !pAnimating ) - { - Warning("ERROR: Tried to set parent for entity %s (%s), but its parent has no model.\n", GetClassname(), GetDebugName()); - return; - } - - int iAttachment = pAnimating->LookupAttachment(szAttachment); - if ( iAttachment <= 0 ) - { - Warning("ERROR: Tried to set parent for entity %s (%s), but it has no attachment named %s.\n", GetClassname(), GetDebugName(), szAttachment); - return; - } - - SetParent(pParent, iAttachment); - SetMoveType(MOVETYPE_NONE); - return; - } - - SetParent(pParent); -} -#endif //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- HSCRIPT CBaseEntity::ScriptGetMoveParent(void) @@ -10019,6 +9974,7 @@ void CBaseEntity::RunOnPostSpawnScripts(void) } } +#ifndef MAPBASE_VSCRIPT // This is shared now HSCRIPT CBaseEntity::GetScriptOwnerEntity() { return ToHScript(GetOwnerEntity()); @@ -10028,6 +9984,7 @@ void CBaseEntity::SetScriptOwnerEntity(HSCRIPT pOwner) { SetOwnerEntity(ToEnt(pOwner)); } +#endif //----------------------------------------------------------------------------- // VScript access to model's key values diff --git a/sp/src/game/shared/baseentity_shared.cpp b/sp/src/game/shared/baseentity_shared.cpp index 886d12e0..f07f3775 100644 --- a/sp/src/game/shared/baseentity_shared.cpp +++ b/sp/src/game/shared/baseentity_shared.cpp @@ -2621,6 +2621,58 @@ bool CBaseEntity::IsToolRecording() const #endif #ifdef MAPBASE_VSCRIPT +HSCRIPT CBaseEntity::GetOrCreatePrivateScriptScope() +{ + ValidateScriptScope(); + return m_ScriptScope; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptSetParent(HSCRIPT hParent, const char *szAttachment) +{ + CBaseEntity *pParent = ToEnt(hParent); + if ( !pParent ) + { + SetParent(NULL); + return; + } + + // if an attachment is specified, the parent needs to be CBaseAnimating + if ( szAttachment && szAttachment[0] != '\0' ) + { + CBaseAnimating *pAnimating = pParent->GetBaseAnimating(); + if ( !pAnimating ) + { + Warning("ERROR: Tried to set parent for entity %s (%s), but its parent has no model.\n", GetClassname(), GetDebugName()); + return; + } + + int iAttachment = pAnimating->LookupAttachment(szAttachment); + if ( iAttachment <= 0 ) + { + Warning("ERROR: Tried to set parent for entity %s (%s), but it has no attachment named %s.\n", GetClassname(), GetDebugName(), szAttachment); + return; + } + + SetParent(pParent, iAttachment); + SetMoveType(MOVETYPE_NONE); + return; + } + + SetParent(pParent); +} + +HSCRIPT CBaseEntity::GetScriptOwnerEntity() +{ + return ToHScript(GetOwnerEntity()); +} + +void CBaseEntity::SetScriptOwnerEntity(HSCRIPT pOwner) +{ + SetOwnerEntity(ToEnt(pOwner)); +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- const Vector& CBaseEntity::ScriptGetColorVector() From 4861893235d3d9bda2a6e1b4fc633730329ea5e5 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Fri, 25 Dec 2020 23:57:02 -0600 Subject: [PATCH 5/9] Exposed client-side ragdoll class and functions to VScript --- sp/src/game/client/c_baseanimating.cpp | 28 +++++++++++++++++++++ sp/src/game/client/c_baseanimating.h | 8 ++++++ sp/src/game/server/physics_prop_ragdoll.cpp | 1 + 3 files changed, 37 insertions(+) diff --git a/sp/src/game/client/c_baseanimating.cpp b/sp/src/game/client/c_baseanimating.cpp index 715ef12a..6b47d0b6 100644 --- a/sp/src/game/client/c_baseanimating.cpp +++ b/sp/src/game/client/c_baseanimating.cpp @@ -282,6 +282,16 @@ BEGIN_DATADESC( C_ClientRagdoll ) END_DATADESC() #ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( C_ClientRagdoll, C_BaseAnimating, "Client-side ragdolls" ) + + DEFINE_SCRIPTFUNC_NAMED( SUB_Remove, "FadeOut", "Fades out the ragdoll and removes it from the client." ) + + // TODO: Proper shared ragdoll funcs? + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRagdollObject, "GetRagdollObject", "Gets the ragdoll object of the specified index." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRagdollObjectCount, "GetRagdollObjectCount", "Gets the number of ragdoll objects on this ragdoll." ) + +END_SCRIPTDESC(); + ScriptHook_t C_BaseAnimating::g_Hook_OnClientRagdoll; #endif @@ -693,6 +703,24 @@ void C_ClientRagdoll::Release( void ) BaseClass::Release(); } +#ifdef MAPBASE_VSCRIPT +HSCRIPT C_ClientRagdoll::ScriptGetRagdollObject( int iIndex ) +{ + if (iIndex < 0 || iIndex > m_pRagdoll->RagdollBoneCount()) + { + Warning("%s GetRagdollObject: Index %i not valid (%i objects)\n", GetDebugName(), iIndex, m_pRagdoll->RagdollBoneCount()); + return NULL; + } + + return g_pScriptVM->RegisterInstance( m_pRagdoll->GetElement(iIndex) ); +} + +int C_ClientRagdoll::ScriptGetRagdollObjectCount() +{ + return m_pRagdoll->RagdollBoneCount(); +} +#endif + //----------------------------------------------------------------------------- // Incremented each frame in InvalidateModelBones. Models compare this value to what it // was last time they setup their bones to determine if they need to re-setup their bones. diff --git a/sp/src/game/client/c_baseanimating.h b/sp/src/game/client/c_baseanimating.h index fe48cfa0..91a50f94 100644 --- a/sp/src/game/client/c_baseanimating.h +++ b/sp/src/game/client/c_baseanimating.h @@ -678,6 +678,9 @@ public: C_ClientRagdoll( bool bRestoring = true ); DECLARE_CLASS( C_ClientRagdoll, C_BaseAnimating ); DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif // inherited from IPVSNotify virtual void OnPVSStatusChanged( bool bInPVS ); @@ -699,6 +702,11 @@ public: void FadeOut( void ); virtual float LastBoneChangedTime(); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetRagdollObject( int iIndex ); + int ScriptGetRagdollObjectCount(); +#endif + bool m_bFadeOut; bool m_bImportant; float m_flEffectTime; diff --git a/sp/src/game/server/physics_prop_ragdoll.cpp b/sp/src/game/server/physics_prop_ragdoll.cpp index 0844f7b7..64a5a648 100644 --- a/sp/src/game/server/physics_prop_ragdoll.cpp +++ b/sp/src/game/server/physics_prop_ragdoll.cpp @@ -166,6 +166,7 @@ BEGIN_ENT_SCRIPTDESC( CRagdollProp, CBaseAnimating, "Ragdoll physics prop." ) DEFINE_SCRIPTFUNC( SetSourceClassName, "Sets the ragdoll's source classname." ) DEFINE_SCRIPTFUNC( HasPhysgunInteraction, "Checks if the ragdoll has the specified interaction." ) + // TODO: Proper shared ragdoll funcs? DEFINE_SCRIPTFUNC_NAMED( ScriptGetRagdollObject, "GetRagdollObject", "Gets the ragdoll object of the specified index." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetRagdollObjectCount, "GetRagdollObjectCount", "Gets the number of ragdoll objects on this ragdoll." ) From 55cbdf49aa53051fa6139fcaed88be5c666aa33b Mon Sep 17 00:00:00 2001 From: Blixibon Date: Sat, 26 Dec 2020 00:00:25 -0600 Subject: [PATCH 6/9] Added CreateProp() function for VScript, allowing the creation of exclusively client-side dynamic props --- sp/src/game/client/c_props.cpp | 5 +++++ sp/src/game/client/vscript_client.cpp | 32 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/sp/src/game/client/c_props.cpp b/sp/src/game/client/c_props.cpp index 07f5710d..7981fe2c 100644 --- a/sp/src/game/client/c_props.cpp +++ b/sp/src/game/client/c_props.cpp @@ -24,6 +24,11 @@ BEGIN_NETWORK_TABLE( CDynamicProp, DT_DynamicProp ) RecvPropBool(RECVINFO(m_bUseHitboxesForRenderBox)), END_NETWORK_TABLE() +#ifdef MAPBASE_VSCRIPT +// Allows client-side VScript to create dynamic props via CreateProp() +LINK_ENTITY_TO_CLASS( prop_dynamic, C_DynamicProp ); +#endif + C_DynamicProp::C_DynamicProp( void ) { m_iCachedFrameCount = -1; diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index 3f7e7cea..f0e90936 100644 --- a/sp/src/game/client/vscript_client.cpp +++ b/sp/src/game/client/vscript_client.cpp @@ -431,6 +431,35 @@ bool DoIncludeScript( const char *pszScript, HSCRIPT hScope ) return true; } +#ifdef MAPBASE_VSCRIPT +// Creates a client-side prop +HSCRIPT CreateProp( const char *pszEntityName, const Vector &vOrigin, const char *pszModelName, int iAnim ) +{ + C_BaseAnimating *pBaseEntity = (C_BaseAnimating *)CreateEntityByName( pszEntityName ); + if (!pBaseEntity) + return NULL; + + pBaseEntity->SetAbsOrigin( vOrigin ); + pBaseEntity->SetModelName( pszModelName ); + if (!pBaseEntity->InitializeAsClientEntity( pszModelName, RENDER_GROUP_OPAQUE_ENTITY )) + { + Warning("Can't initialize %s as client entity\n", pszEntityName); + return NULL; + } + + pBaseEntity->SetPlaybackRate( 1.0f ); + + int iSequence = pBaseEntity->SelectWeightedSequence( (Activity)iAnim ); + + if ( iSequence != -1 ) + { + pBaseEntity->SetSequence( iSequence ); + } + + return ToHScript( pBaseEntity ); +} +#endif + bool VScriptClientInit() { VMPROF_START @@ -494,6 +523,9 @@ bool VScriptClientInit() ScriptRegisterFunction( g_pScriptVM, GetMapName, "Get the name of the map."); ScriptRegisterFunction( g_pScriptVM, Time, "Get the current server time" ); ScriptRegisterFunction( g_pScriptVM, DoIncludeScript, "Execute a script (internal)" ); +#ifdef MAPBASE_VSCRIPT + ScriptRegisterFunction( g_pScriptVM, CreateProp, "Create an animating prop" ); +#endif if ( GameRules() ) { From 5529127d681ea1a20cdefdc88cc494a4f5f02ef5 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Sat, 26 Dec 2020 00:02:43 -0600 Subject: [PATCH 7/9] Split logic_script_client and Mapbase user messages into their own files --- sp/src/game/client/client_mapbase.vpc | 2 + sp/src/game/server/server_mapbase.vpc | 2 + .../shared/mapbase/logic_script_client.cpp | 188 ++++++++++++++++++ sp/src/game/shared/mapbase/mapbase_shared.cpp | 45 ----- .../shared/mapbase/mapbase_usermessages.cpp | 64 ++++++ sp/src/game/shared/vscript_shared.cpp | 180 ----------------- 6 files changed, 256 insertions(+), 225 deletions(-) create mode 100644 sp/src/game/shared/mapbase/logic_script_client.cpp create mode 100644 sp/src/game/shared/mapbase/mapbase_usermessages.cpp diff --git a/sp/src/game/client/client_mapbase.vpc b/sp/src/game/client/client_mapbase.vpc index 3c2eb00f..243324f1 100644 --- a/sp/src/game/client/client_mapbase.vpc +++ b/sp/src/game/client/client_mapbase.vpc @@ -35,6 +35,7 @@ $Project $Folder "Mapbase" { $File "$SRCDIR\game\shared\mapbase\mapbase_shared.cpp" + $File "$SRCDIR\game\shared\mapbase\mapbase_usermessages.cpp" $File "$SRCDIR\game\shared\mapbase\mapbase_rpc.cpp" $File "$SRCDIR\game\shared\mapbase\mapbase_game_log.cpp" $File "$SRCDIR\game\shared\mapbase\MapEdit.cpp" @@ -50,6 +51,7 @@ $Project $File "$SRCDIR\game\shared\mapbase\vscript_consts_weapons.cpp" [$MAPBASE_VSCRIPT] $File "$SRCDIR\game\shared\mapbase\weapon_custom_scripted.cpp" [$MAPBASE_VSCRIPT] $File "$SRCDIR\game\shared\mapbase\weapon_custom_scripted.h" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\logic_script_client.cpp" [$MAPBASE_VSCRIPT] $File "mapbase\c_func_clientclip.cpp" $File "mapbase\c_func_fake_worldportal.cpp" diff --git a/sp/src/game/server/server_mapbase.vpc b/sp/src/game/server/server_mapbase.vpc index 40581e0f..37146238 100644 --- a/sp/src/game/server/server_mapbase.vpc +++ b/sp/src/game/server/server_mapbase.vpc @@ -31,6 +31,7 @@ $Project $Folder "Mapbase" { $File "$SRCDIR\game\shared\mapbase\mapbase_shared.cpp" + $File "$SRCDIR\game\shared\mapbase\mapbase_usermessages.cpp" $File "$SRCDIR\game\shared\mapbase\mapbase_rpc.cpp" $File "$SRCDIR\game\shared\mapbase\mapbase_game_log.cpp" $File "$SRCDIR\game\shared\mapbase\MapEdit.cpp" @@ -46,6 +47,7 @@ $Project $File "$SRCDIR\game\shared\mapbase\vscript_consts_weapons.cpp" [$MAPBASE_VSCRIPT] $File "$SRCDIR\game\shared\mapbase\weapon_custom_scripted.cpp" [$MAPBASE_VSCRIPT] $File "$SRCDIR\game\shared\mapbase\weapon_custom_scripted.h" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\logic_script_client.cpp" [$MAPBASE_VSCRIPT] $File "mapbase\ai_grenade.cpp" $File "mapbase\ai_grenade.h" diff --git a/sp/src/game/shared/mapbase/logic_script_client.cpp b/sp/src/game/shared/mapbase/logic_script_client.cpp new file mode 100644 index 00000000..60c59351 --- /dev/null +++ b/sp/src/game/shared/mapbase/logic_script_client.cpp @@ -0,0 +1,188 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Custom client-side equivalent of logic_script. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "vscript_shared.h" +#include "tier1/fmtstr.h" + +//----------------------------------------------------------------------------- +// Purpose: An entity that acts as a container for client-side game scripts. +//----------------------------------------------------------------------------- + +#define MAX_SCRIPT_GROUP_CLIENT 8 + +class CLogicScriptClient : public CBaseEntity +{ +public: + DECLARE_CLASS( CLogicScriptClient, CBaseEntity ); + DECLARE_DATADESC(); + DECLARE_NETWORKCLASS(); + +#ifdef CLIENT_DLL + void OnDataChanged( DataUpdateType_t type ) + { + BaseClass::OnDataChanged( type ); + + if ( !m_ScriptScope.IsInitialized() ) + { + RunVScripts(); + } + } +#else + int UpdateTransmitState() { return SetTransmitState( FL_EDICT_ALWAYS ); } +#endif + + bool KeyValue( const char *szKeyName, const char *szValue ) + { + if ( FStrEq( szKeyName, "vscripts" ) ) + { + Q_strcpy( m_iszClientScripts.GetForModify(), szValue ); + } + + return BaseClass::KeyValue( szKeyName, szValue ); + } + + void RunVScripts() + { +#ifdef CLIENT_DLL + if (m_iszClientScripts == NULL_STRING) + { + CGMsg( 0, CON_GROUP_VSCRIPT, "%s has no client scripts", GetDebugName() ); + return; + } + + if (g_pScriptVM == NULL) + { + return; + } + + ValidateScriptScope(); + + // All functions we want to have call chained instead of overwritten + // by other scripts in this entities list. + static const char* sCallChainFunctions[] = + { + "OnPostSpawn", + "Precache" + }; + + ScriptLanguage_t language = g_pScriptVM->GetLanguage(); + + // Make a call chainer for each in this entities scope + for (int j = 0; j < ARRAYSIZE( sCallChainFunctions ); ++j) + { + + if (language == SL_PYTHON) + { + // UNDONE - handle call chaining in python + ; + } + else if (language == SL_SQUIRREL) + { + //TODO: For perf, this should be precompiled and the %s should be passed as a parameter + HSCRIPT hCreateChainScript = g_pScriptVM->CompileScript( CFmtStr( "%sCallChain <- CSimpleCallChainer(\"%s\", self.GetScriptScope(), true)", sCallChainFunctions[j], sCallChainFunctions[j] ) ); + g_pScriptVM->Run( hCreateChainScript, (HSCRIPT)m_ScriptScope ); + } + } + + char szScriptsList[255]; + Q_strcpy( szScriptsList, m_iszClientScripts.Get() ); + CUtlStringList szScripts; + + V_SplitString( szScriptsList, " ", szScripts ); + + for (int i = 0; i < szScripts.Count(); i++) + { + CGMsg( 0, CON_GROUP_VSCRIPT, "%s executing script: %s\n", GetDebugName(), szScripts[i] ); + + RunScriptFile( szScripts[i], IsWorld() ); + + for (int j = 0; j < ARRAYSIZE( sCallChainFunctions ); ++j) + { + if (language == SL_PYTHON) + { + // UNDONE - handle call chaining in python + ; + } + else if (language == SL_SQUIRREL) + { + //TODO: For perf, this should be precompiled and the %s should be passed as a parameter. + HSCRIPT hRunPostScriptExecute = g_pScriptVM->CompileScript( CFmtStr( "%sCallChain.PostScriptExecute()", sCallChainFunctions[j] ) ); + g_pScriptVM->Run( hRunPostScriptExecute, (HSCRIPT)m_ScriptScope ); + } + } + } +#else + // Avoids issues from having m_iszVScripts set without actually having a script scope + ValidateScriptScope(); + + if (m_bRunOnServer) + { + BaseClass::RunVScripts(); + } +#endif + } + +#ifndef CLIENT_DLL + void InputCallScriptFunctionClient( inputdata_t &inputdata ) + { + // TODO: Support for specific players? + CBroadcastRecipientFilter filter; + filter.MakeReliable(); + + const char *pszFunction = inputdata.value.String(); + if (strlen( pszFunction ) > 64) + { + Msg("%s CallScriptFunctionClient: \"%s\" is too long at %i characters, must be 64 or less\n", GetDebugName(), pszFunction, strlen(pszFunction)); + return; + } + + UserMessageBegin( filter, "CallClientScriptFunction" ); + WRITE_STRING( pszFunction ); // function + WRITE_SHORT( entindex() ); // entity + MessageEnd(); + } +#endif + + //CNetworkArray( string_t, m_iszGroupMembers, MAX_SCRIPT_GROUP_CLIENT ); + CNetworkString( m_iszClientScripts, 128 ); + + bool m_bRunOnServer; +}; + +LINK_ENTITY_TO_CLASS( logic_script_client, CLogicScriptClient ); + +BEGIN_DATADESC( CLogicScriptClient ) + + //DEFINE_KEYFIELD( m_iszGroupMembers[0], FIELD_STRING, "Group00"), + //DEFINE_KEYFIELD( m_iszGroupMembers[1], FIELD_STRING, "Group01"), + //DEFINE_KEYFIELD( m_iszGroupMembers[2], FIELD_STRING, "Group02"), + //DEFINE_KEYFIELD( m_iszGroupMembers[3], FIELD_STRING, "Group03"), + //DEFINE_KEYFIELD( m_iszGroupMembers[4], FIELD_STRING, "Group04"), + //DEFINE_KEYFIELD( m_iszGroupMembers[5], FIELD_STRING, "Group05"), + //DEFINE_KEYFIELD( m_iszGroupMembers[6], FIELD_STRING, "Group06"), + //DEFINE_KEYFIELD( m_iszGroupMembers[7], FIELD_STRING, "Group07"), + + DEFINE_KEYFIELD( m_bRunOnServer, FIELD_BOOLEAN, "RunOnServer" ), + +#ifndef CLIENT_DLL + DEFINE_INPUTFUNC( FIELD_STRING, "CallScriptFunctionClient", InputCallScriptFunctionClient ), +#endif + +END_DATADESC() + +IMPLEMENT_NETWORKCLASS_DT( CLogicScriptClient, DT_LogicScriptClient ) + +#ifdef CLIENT_DLL + //RecvPropArray( RecvPropString( RECVINFO( m_iszGroupMembers[0] ) ), m_iszGroupMembers ), + RecvPropString( RECVINFO( m_iszClientScripts ) ), +#else + //SendPropArray( SendPropStringT( SENDINFO_ARRAY( m_iszGroupMembers ) ), m_iszGroupMembers ), + SendPropString( SENDINFO( m_iszClientScripts ) ), +#endif + +END_NETWORK_TABLE() diff --git a/sp/src/game/shared/mapbase/mapbase_shared.cpp b/sp/src/game/shared/mapbase/mapbase_shared.cpp index 3406d9c8..880e851b 100644 --- a/sp/src/game/shared/mapbase/mapbase_shared.cpp +++ b/sp/src/game/shared/mapbase/mapbase_shared.cpp @@ -618,48 +618,3 @@ BEGIN_DATADESC( CMapbaseManifestEntity ) END_DATADESC() #endif - -#ifdef CLIENT_DLL -void __MsgFunc_CallClientScriptFunction( bf_read &msg ) -{ - char szFunction[64]; - if (!msg.ReadString( szFunction, sizeof( szFunction ) )) - { - CGMsg( 0, CON_GROUP_VSCRIPT, "Unable to read function string\n" ); - } - - int idx = msg.ReadByte(); - C_BaseEntity *pEntity = CBaseEntity::Instance( idx ); - - if (pEntity) - { - if (pEntity->m_ScriptScope.IsInitialized()) - { - //CGMsg( 0, CON_GROUP_VSCRIPT, "%s calling function \"%s\"\n", pEntity->GetDebugName(), szFunction ); - pEntity->CallScriptFunction( szFunction, NULL ); - } - else - { - CGMsg( 0, CON_GROUP_VSCRIPT, "%s scope not initialized\n", pEntity->GetDebugName() ); - } - } - else - { - CGMsg( 0, CON_GROUP_VSCRIPT, "Clientside entity not found for script function (index %i)\n", idx ); - } -} - -void HookMapbaseUserMessages( void ) -{ - HOOK_MESSAGE( CallClientScriptFunction ); -} -#endif - -void RegisterMapbaseUserMessages( void ) -{ - usermessages->Register( "CallClientScriptFunction", -1 ); - -#ifdef CLIENT_DLL - HookMapbaseUserMessages(); -#endif -} diff --git a/sp/src/game/shared/mapbase/mapbase_usermessages.cpp b/sp/src/game/shared/mapbase/mapbase_usermessages.cpp new file mode 100644 index 00000000..6cbe9949 --- /dev/null +++ b/sp/src/game/shared/mapbase/mapbase_usermessages.cpp @@ -0,0 +1,64 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Mapbase-specific user messages. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "usermessages.h" +#ifdef CLIENT_DLL +#include "hud_macros.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef CLIENT_DLL +void __MsgFunc_CallClientScriptFunction( bf_read &msg ) +{ + char szFunction[64]; + if (!msg.ReadString( szFunction, sizeof( szFunction ) )) + { + CGMsg( 0, CON_GROUP_VSCRIPT, "Unable to read function string\n" ); + } + + int idx = msg.ReadByte(); + C_BaseEntity *pEntity = CBaseEntity::Instance( idx ); + + if (pEntity) + { + if (pEntity->m_ScriptScope.IsInitialized()) + { + //CGMsg( 0, CON_GROUP_VSCRIPT, "%s calling function \"%s\"\n", pEntity->GetDebugName(), szFunction ); + pEntity->CallScriptFunction( szFunction, NULL ); + } + else + { + CGMsg( 0, CON_GROUP_VSCRIPT, "%s scope not initialized\n", pEntity->GetDebugName() ); + } + } + else + { + CGMsg( 0, CON_GROUP_VSCRIPT, "Clientside entity not found for script function (index %i)\n", idx ); + } +} + +void HookMapbaseUserMessages( void ) +{ + // VScript + HOOK_MESSAGE( CallClientScriptFunction ); +} +#endif + +void RegisterMapbaseUserMessages( void ) +{ + // VScript + usermessages->Register( "CallClientScriptFunction", -1 ); + +#ifdef CLIENT_DLL + // TODO: Better placement? + HookMapbaseUserMessages(); +#endif +} diff --git a/sp/src/game/shared/vscript_shared.cpp b/sp/src/game/shared/vscript_shared.cpp index d844c323..ebe997a8 100644 --- a/sp/src/game/shared/vscript_shared.cpp +++ b/sp/src/game/shared/vscript_shared.cpp @@ -274,183 +274,3 @@ CON_COMMAND_SHARED( script_dump_all, "Dump the state of the VM to the console" ) } g_pScriptVM->DumpState(); } - -#ifdef MAPBASE_VSCRIPT -//----------------------------------------------------------------------------- -// Purpose: An entity that acts as a container for game scripts. -//----------------------------------------------------------------------------- - -#define MAX_SCRIPT_GROUP_CLIENT 8 - -class CLogicScriptClient : public CBaseEntity -{ -public: - DECLARE_CLASS( CLogicScriptClient, CBaseEntity ); - DECLARE_DATADESC(); - DECLARE_NETWORKCLASS(); - -#ifdef CLIENT_DLL - void OnDataChanged( DataUpdateType_t type ) - { - BaseClass::OnDataChanged( type ); - - if ( !m_ScriptScope.IsInitialized() ) - { - RunVScripts(); - } - } -#else - int UpdateTransmitState() { return SetTransmitState( FL_EDICT_ALWAYS ); } -#endif - - bool KeyValue( const char *szKeyName, const char *szValue ) - { - if ( FStrEq( szKeyName, "vscripts" ) ) - { - Q_strcpy( m_iszClientScripts.GetForModify(), szValue ); - } - - return BaseClass::KeyValue( szKeyName, szValue ); - } - - void RunVScripts() - { -#ifdef CLIENT_DLL - if (m_iszClientScripts == NULL_STRING) - { - CGMsg( 0, CON_GROUP_VSCRIPT, "%s has no client scripts", GetDebugName() ); - return; - } - - if (g_pScriptVM == NULL) - { - return; - } - - ValidateScriptScope(); - - // All functions we want to have call chained instead of overwritten - // by other scripts in this entities list. - static const char* sCallChainFunctions[] = - { - "OnPostSpawn", - "Precache" - }; - - ScriptLanguage_t language = g_pScriptVM->GetLanguage(); - - // Make a call chainer for each in this entities scope - for (int j = 0; j < ARRAYSIZE( sCallChainFunctions ); ++j) - { - - if (language == SL_PYTHON) - { - // UNDONE - handle call chaining in python - ; - } - else if (language == SL_SQUIRREL) - { - //TODO: For perf, this should be precompiled and the %s should be passed as a parameter - HSCRIPT hCreateChainScript = g_pScriptVM->CompileScript( CFmtStr( "%sCallChain <- CSimpleCallChainer(\"%s\", self.GetScriptScope(), true)", sCallChainFunctions[j], sCallChainFunctions[j] ) ); - g_pScriptVM->Run( hCreateChainScript, (HSCRIPT)m_ScriptScope ); - } - } - - char szScriptsList[255]; - Q_strcpy( szScriptsList, m_iszClientScripts.Get() ); - CUtlStringList szScripts; - - V_SplitString( szScriptsList, " ", szScripts ); - - for (int i = 0; i < szScripts.Count(); i++) - { - CGMsg( 0, CON_GROUP_VSCRIPT, "%s executing script: %s\n", GetDebugName(), szScripts[i] ); - - RunScriptFile( szScripts[i], IsWorld() ); - - for (int j = 0; j < ARRAYSIZE( sCallChainFunctions ); ++j) - { - if (language == SL_PYTHON) - { - // UNDONE - handle call chaining in python - ; - } - else if (language == SL_SQUIRREL) - { - //TODO: For perf, this should be precompiled and the %s should be passed as a parameter. - HSCRIPT hRunPostScriptExecute = g_pScriptVM->CompileScript( CFmtStr( "%sCallChain.PostScriptExecute()", sCallChainFunctions[j] ) ); - g_pScriptVM->Run( hRunPostScriptExecute, (HSCRIPT)m_ScriptScope ); - } - } - } -#else - // Avoids issues from having m_iszVScripts set without actually having a script scope - ValidateScriptScope(); - - if (m_bRunOnServer) - { - BaseClass::RunVScripts(); - } -#endif - } - -#ifndef CLIENT_DLL - void InputCallScriptFunctionClient( inputdata_t &inputdata ) - { - // TODO: Support for specific players? - CBroadcastRecipientFilter filter; - filter.MakeReliable(); - - const char *pszFunction = inputdata.value.String(); - if (strlen( pszFunction ) > 64) - { - Msg("%s CallScriptFunctionClient: \"%s\" is too long at %i characters, must be 64 or less\n", GetDebugName(), pszFunction, strlen(pszFunction)); - return; - } - - UserMessageBegin( filter, "CallClientScriptFunction" ); - WRITE_STRING( pszFunction ); // function - WRITE_SHORT( entindex() ); // entity - MessageEnd(); - } -#endif - - //CNetworkArray( string_t, m_iszGroupMembers, MAX_SCRIPT_GROUP_CLIENT ); - CNetworkString( m_iszClientScripts, 128 ); - - bool m_bRunOnServer; -}; - -LINK_ENTITY_TO_CLASS( logic_script_client, CLogicScriptClient ); - -BEGIN_DATADESC( CLogicScriptClient ) - - //DEFINE_KEYFIELD( m_iszGroupMembers[0], FIELD_STRING, "Group00"), - //DEFINE_KEYFIELD( m_iszGroupMembers[1], FIELD_STRING, "Group01"), - //DEFINE_KEYFIELD( m_iszGroupMembers[2], FIELD_STRING, "Group02"), - //DEFINE_KEYFIELD( m_iszGroupMembers[3], FIELD_STRING, "Group03"), - //DEFINE_KEYFIELD( m_iszGroupMembers[4], FIELD_STRING, "Group04"), - //DEFINE_KEYFIELD( m_iszGroupMembers[5], FIELD_STRING, "Group05"), - //DEFINE_KEYFIELD( m_iszGroupMembers[6], FIELD_STRING, "Group06"), - //DEFINE_KEYFIELD( m_iszGroupMembers[7], FIELD_STRING, "Group07"), - - DEFINE_KEYFIELD( m_bRunOnServer, FIELD_BOOLEAN, "RunOnServer" ), - -#ifndef CLIENT_DLL - DEFINE_INPUTFUNC( FIELD_STRING, "CallScriptFunctionClient", InputCallScriptFunctionClient ), -#endif - -END_DATADESC() - -IMPLEMENT_NETWORKCLASS_DT( CLogicScriptClient, DT_LogicScriptClient ) - -#ifdef CLIENT_DLL - //RecvPropArray( RecvPropString( RECVINFO( m_iszGroupMembers[0] ) ), m_iszGroupMembers ), - RecvPropString( RECVINFO( m_iszClientScripts ) ), -#else - //SendPropArray( SendPropStringT( SENDINFO_ARRAY( m_iszGroupMembers ) ), m_iszGroupMembers ), - SendPropString( SENDINFO( m_iszClientScripts ) ), -#endif - -END_NETWORK_TABLE() -#endif From 8af9c0dc61e98d383ef5ea158502a84555257407 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Sun, 27 Dec 2020 15:54:56 -0600 Subject: [PATCH 8/9] Added basic think hook to logic_script_client --- .../shared/mapbase/logic_script_client.cpp | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/sp/src/game/shared/mapbase/logic_script_client.cpp b/sp/src/game/shared/mapbase/logic_script_client.cpp index 60c59351..f7220eb8 100644 --- a/sp/src/game/shared/mapbase/logic_script_client.cpp +++ b/sp/src/game/shared/mapbase/logic_script_client.cpp @@ -9,6 +9,10 @@ #include "vscript_shared.h" #include "tier1/fmtstr.h" +#ifdef CLIENT_DLL +ConVar cl_script_think_interval( "cl_script_think_interval", "0.1" ); +#endif + //----------------------------------------------------------------------------- // Purpose: An entity that acts as a container for client-side game scripts. //----------------------------------------------------------------------------- @@ -116,6 +120,11 @@ public: } } } + + if (m_bClientThink) + { + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } #else // Avoids issues from having m_iszVScripts set without actually having a script scope ValidateScriptScope(); @@ -127,7 +136,32 @@ public: #endif } -#ifndef CLIENT_DLL +#ifdef CLIENT_DLL + void ClientThink() + { + ScriptVariant_t varThinkRetVal; + if (CallScriptFunction("ClientThink", &varThinkRetVal)) + { + float flThinkFrequency = 0.0f; + if (!varThinkRetVal.AssignTo(&flThinkFrequency)) + { + // use default think interval if script think function doesn't provide one + flThinkFrequency = cl_script_think_interval.GetFloat(); + } + + if (flThinkFrequency == CLIENT_THINK_ALWAYS) + SetNextClientThink( CLIENT_THINK_ALWAYS ); + else + SetNextClientThink( gpGlobals->curtime + flThinkFrequency ); + } + else + { + DevWarning("%s FAILED to call client script think function!\n", GetDebugName()); + } + + BaseClass::ClientThink(); + } +#else void InputCallScriptFunctionClient( inputdata_t &inputdata ) { // TODO: Support for specific players? @@ -150,14 +184,20 @@ public: //CNetworkArray( string_t, m_iszGroupMembers, MAX_SCRIPT_GROUP_CLIENT ); CNetworkString( m_iszClientScripts, 128 ); + CNetworkVar( bool, m_bClientThink ); +#ifndef CLIENT_DLL bool m_bRunOnServer; +#endif }; LINK_ENTITY_TO_CLASS( logic_script_client, CLogicScriptClient ); BEGIN_DATADESC( CLogicScriptClient ) + // TODO: Does this need to be saved? + //DEFINE_AUTO_ARRAY( m_iszClientScripts, FIELD_CHARACTER ), + //DEFINE_KEYFIELD( m_iszGroupMembers[0], FIELD_STRING, "Group00"), //DEFINE_KEYFIELD( m_iszGroupMembers[1], FIELD_STRING, "Group01"), //DEFINE_KEYFIELD( m_iszGroupMembers[2], FIELD_STRING, "Group02"), @@ -167,9 +207,11 @@ BEGIN_DATADESC( CLogicScriptClient ) //DEFINE_KEYFIELD( m_iszGroupMembers[6], FIELD_STRING, "Group06"), //DEFINE_KEYFIELD( m_iszGroupMembers[7], FIELD_STRING, "Group07"), - DEFINE_KEYFIELD( m_bRunOnServer, FIELD_BOOLEAN, "RunOnServer" ), + DEFINE_KEYFIELD( m_bClientThink, FIELD_BOOLEAN, "ClientThink" ), #ifndef CLIENT_DLL + DEFINE_KEYFIELD( m_bRunOnServer, FIELD_BOOLEAN, "RunOnServer" ), + DEFINE_INPUTFUNC( FIELD_STRING, "CallScriptFunctionClient", InputCallScriptFunctionClient ), #endif @@ -180,9 +222,11 @@ IMPLEMENT_NETWORKCLASS_DT( CLogicScriptClient, DT_LogicScriptClient ) #ifdef CLIENT_DLL //RecvPropArray( RecvPropString( RECVINFO( m_iszGroupMembers[0] ) ), m_iszGroupMembers ), RecvPropString( RECVINFO( m_iszClientScripts ) ), + RecvPropBool( RECVINFO( m_bClientThink ) ), #else //SendPropArray( SendPropStringT( SENDINFO_ARRAY( m_iszGroupMembers ) ), m_iszGroupMembers ), SendPropString( SENDINFO( m_iszClientScripts ) ), + SendPropBool( SENDINFO( m_bClientThink ) ), #endif END_NETWORK_TABLE() From e3550438b9eff9294d090a9c4f66b24eef172410 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Sat, 23 Jan 2021 13:20:07 -0600 Subject: [PATCH 9/9] Added functions to create and modify force behavior of client-side "death ragdolls" --- sp/src/game/client/c_baseanimating.cpp | 15 +++++++++++++++ sp/src/game/client/c_baseanimating.h | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/sp/src/game/client/c_baseanimating.cpp b/sp/src/game/client/c_baseanimating.cpp index 6b47d0b6..660c18b4 100644 --- a/sp/src/game/client/c_baseanimating.cpp +++ b/sp/src/game/client/c_baseanimating.cpp @@ -335,6 +335,12 @@ BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-si DEFINE_SCRIPTFUNC( GetSkin, "Gets the model's skin" ) DEFINE_SCRIPTFUNC( SetSkin, "Sets the model's skin" ) + DEFINE_SCRIPTFUNC( GetForceBone, "Gets the entity's force bone, which is used to determine which bone a ragdoll should apply its force to." ) + DEFINE_SCRIPTFUNC( SetForceBone, "Sets the entity's force bone, which is used to determine which bone a ragdoll should apply its force to." ) + DEFINE_SCRIPTFUNC( GetRagdollForce, "Gets the entity's ragdoll force, which is used to apply velocity to a ragdoll." ) + DEFINE_SCRIPTFUNC( SetRagdollForce, "Sets the entity's ragdoll force, which is used to apply velocity to a ragdoll." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptBecomeRagdollOnClient, "BecomeRagdollOnClient", "" ) DEFINE_SCRIPTFUNC( IsRagdoll, "" ) BEGIN_SCRIPTHOOK( C_BaseAnimating::g_Hook_OnClientRagdoll, "OnClientRagdoll", FIELD_VOID, "Called when this entity turns into a client-side ragdoll." ) @@ -1530,6 +1536,15 @@ void C_BaseAnimating::ScriptGetBoneTransform( int iBone, HSCRIPT hTransform ) GetBoneTransform( iBone, *HScriptToClass( hTransform ) ); } +HSCRIPT C_BaseAnimating::ScriptBecomeRagdollOnClient() +{ + C_BaseAnimating *pRagdoll = BecomeRagdollOnClient(); + if (!pRagdoll) + return NULL; + + return pRagdoll->GetScriptInstance(); +} + float C_BaseAnimating::ScriptGetPoseParameter( const char* szName ) { CStudioHdr* pHdr = GetModelPtr(); diff --git a/sp/src/game/client/c_baseanimating.h b/sp/src/game/client/c_baseanimating.h index 91a50f94..2c240a06 100644 --- a/sp/src/game/client/c_baseanimating.h +++ b/sp/src/game/client/c_baseanimating.h @@ -467,6 +467,13 @@ public: // For VScript void SetSkin( int iSkin ) { m_nSkin = iSkin; } + int GetForceBone() { return m_nForceBone; } + void SetForceBone( int iBone ) { m_nForceBone = iBone; } + const Vector& GetRagdollForce() { return m_vecForce; } + void SetRagdollForce( const Vector &vecForce ) { m_vecForce = vecForce; } + + HSCRIPT ScriptBecomeRagdollOnClient(); + static ScriptHook_t g_Hook_OnClientRagdoll; float ScriptGetPoseParameter(const char* szName);