source-sdk-2013-mapbase/sp/src/game/shared/mapbase/logic_script_client.cpp
2021-01-27 11:01:38 -06:00

259 lines
6.9 KiB
C++

//========= 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"
#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.
//-----------------------------------------------------------------------------
#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 );
}
}
}
if (m_bClientThink)
{
SetNextClientThink( CLIENT_THINK_ALWAYS );
}
#else
// Avoids issues from having m_iszVScripts set without actually having a script scope
ValidateScriptScope();
if (m_bRunOnServer)
{
BaseClass::RunVScripts();
}
#endif
}
#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();
}
void OnSave()
{
// HACKHACK: Save the next think in the VM since the VM is saved
if (m_bClientThink)
{
g_pScriptVM->SetValue( m_ScriptScope, "__c_think", GetNextThink() );
}
BaseClass::OnSave();
}
void OnRestore()
{
// HACKHACK: See OnSave()
if (m_bClientThink)
{
ScriptVariant_t flNextThink;
if (g_pScriptVM->GetValue( m_ScriptScope, "__c_think", &flNextThink ))
{
SetNextClientThink( flNextThink );
}
}
BaseClass::OnRestore();
}
#else
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 );
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"),
//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_bClientThink, FIELD_BOOLEAN, "ClientThink" ),
#ifndef CLIENT_DLL
DEFINE_KEYFIELD( m_bRunOnServer, FIELD_BOOLEAN, "RunOnServer" ),
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 ) ),
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()