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