From 4966b79f84c35abf79bb19fa8f0d141f47dcff4b Mon Sep 17 00:00:00 2001 From: Blixibon Date: Sat, 30 May 2020 10:48:00 -0500 Subject: [PATCH] Added VScript material proxy, exposed more stuff to clientside VScript --- sp/src/game/client/c_baseentity.cpp | 58 +++++ sp/src/game/client/c_baseentity.h | 20 ++ sp/src/game/client/c_baseplayer.cpp | 17 ++ sp/src/game/client/vscript_client.cpp | 284 ++++++++++++++++++++++ sp/src/game/shared/SoundEmitterSystem.cpp | 4 +- 5 files changed, 381 insertions(+), 2 deletions(-) diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index a6e2072c..eceec4d8 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -431,6 +431,35 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC_NAMED( ScriptGetLeft, "GetLeftVector", "Get the left vector of the entity" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetUp, "GetUpVector", "Get the up vector of the entity" ) DEFINE_SCRIPTFUNC( GetTeamNumber, "Gets this entity's team" ) + +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( GetHealth, "" ) + DEFINE_SCRIPTFUNC( GetMaxHealth, "" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelName, "GetModelName", "Returns the name of the model" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptEmitSound, "EmitSound", "Plays a sound from this entity." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptPrecacheScriptSound, "PrecacheSoundScript", "Precache a sound for later playing." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSoundDuration, "GetSoundDuration", "Returns float duration of the sound. Takes soundname and optional actormodelname." ) + + DEFINE_SCRIPTFUNC( GetClassname, "" ) + DEFINE_SCRIPTFUNC_NAMED( GetEntityName, "GetName", "" ) + + 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( 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( 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( GetEffects, "Get effects" ) + DEFINE_SCRIPTFUNC( IsEffectActive, "Check if an effect is active" ) +#endif END_SCRIPTDESC(); #ifndef NO_ENTITY_PREDICTION @@ -6482,6 +6511,35 @@ HSCRIPT C_BaseEntity::GetScriptInstance() return m_hScriptInstance; } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptGetMoveParent( void ) +{ + return ToHScript( GetMoveParent() ); +} +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptGetRootMoveParent() +{ + return ToHScript( GetRootMoveParent() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptFirstMoveChild( void ) +{ + return ToHScript( FirstMoveChild() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptNextMovePeer( void ) +{ + return ToHScript( NextMovePeer() ); +} +#endif + //------------------------------------------------------------------------------ void CC_CL_Find_Ent( const CCommand& args ) { diff --git a/sp/src/game/client/c_baseentity.h b/sp/src/game/client/c_baseentity.h index ff3f1e6a..cb8eae7a 100644 --- a/sp/src/game/client/c_baseentity.h +++ b/sp/src/game/client/c_baseentity.h @@ -1133,6 +1133,26 @@ public: const Vector& ScriptGetLeft(void) { static Vector vecLeft; GetVectors(NULL, &vecLeft, NULL); return vecLeft; } const Vector& ScriptGetUp(void) { static Vector vecUp; GetVectors(NULL, NULL, &vecUp); return vecUp; } +#ifdef MAPBASE_VSCRIPT + const char* ScriptGetModelName( void ) const { return STRING(GetModelName()); } + + void ScriptEmitSound(const char* soundname); + float ScriptSoundDuration(const char* soundname, const char* actormodel); + + 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 Vector& ScriptGetBoundingMins( void ) { return m_Collision.OBBMins(); } + const Vector& ScriptGetBoundingMaxs( void ) { return m_Collision.OBBMaxs(); } + + HSCRIPT ScriptGetMoveParent( void ); + HSCRIPT ScriptGetRootMoveParent(); + HSCRIPT ScriptFirstMoveChild( void ); + HSCRIPT ScriptNextMovePeer( void ); +#endif + // Stubs on client void NetworkStateManualMode( bool activate ) { } void NetworkStateChanged() { } diff --git a/sp/src/game/client/c_baseplayer.cpp b/sp/src/game/client/c_baseplayer.cpp index acbd1ade..fee1fdc3 100644 --- a/sp/src/game/client/c_baseplayer.cpp +++ b/sp/src/game/client/c_baseplayer.cpp @@ -480,6 +480,13 @@ C_BasePlayer::~C_BasePlayer() s_pLocalPlayer = NULL; } +#ifdef MAPBASE_VSCRIPT + if ( IsLocalPlayer() && g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", SCRIPT_VARIANT_NULL ); + } +#endif + delete m_pFlashlight; } @@ -974,6 +981,16 @@ void C_BasePlayer::OnRestore() input->ClearInputButton( IN_ATTACK | IN_ATTACK2 ); // GetButtonBits() has to be called for the above to take effect input->GetButtonBits( 0 ); + +#ifdef MAPBASE_VSCRIPT + // HACK: (03/25/09) Then the player goes across a transition it doesn't spawn and register + // it's instance. We're hacking around this for now, but this will go away when we get around to + // having entities cross transitions and keep their script state. + if ( g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } +#endif } // For ammo history icons to current value so they don't flash on level transtions diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index 3713eb13..6b386d7f 100644 --- a/sp/src/game/client/vscript_client.cpp +++ b/sp/src/game/client/vscript_client.cpp @@ -18,6 +18,9 @@ #endif #ifdef MAPBASE_VSCRIPT #include "c_world.h" +#include "proxyentity.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imaterialvar.h" #endif extern IScriptManager *scriptmanager; @@ -37,6 +40,280 @@ extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); #endif // VMPROFILE +#ifdef MAPBASE_VSCRIPT + +#define SCRIPT_MAT_PROXY_MAX_VARS 8 + +//----------------------------------------------------------------------------- +// Purpose: A material proxy which runs a VScript and allows it to read/write +// to material variables. +//----------------------------------------------------------------------------- +class CScriptMaterialProxy : public IMaterialProxy +{ +public: + CScriptMaterialProxy(); + virtual ~CScriptMaterialProxy(); + + virtual void Release( void ); + virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); + virtual void OnBind( void *pRenderable ); + virtual IMaterial *GetMaterial() { return NULL; } + + // It would be more preferable to init the script stuff in Init(), but + // the VM isn't usually active by that time, so we have to init it when + // it's first called in OnBind(). + bool InitScript(); + + bool ValidateIndex(int i) + { + if (i > SCRIPT_MAT_PROXY_MAX_VARS || i < 0) + { + Warning("VScriptProxy: %i out of range", i); + return false; + } + + return true; + } + + const char *GetVarString( int i ); + int GetVarInt( int i ); + float GetVarFloat( int i ); + const Vector& GetVarVector( int i ); + + void SetVarString( int i, const char *value ); + void SetVarInt( int i, int value ); + void SetVarFloat( int i, float value ); + void SetVarVector( int i, const Vector &value ); + +private: + IMaterialVar *m_MaterialVars[SCRIPT_MAT_PROXY_MAX_VARS]; + + // Save the keyvalue string for InitScript() + char m_szFilePath[MAX_PATH]; + + CScriptScope m_ScriptScope; + HSCRIPT m_hScriptInstance; + HSCRIPT m_hFuncOnBind; +}; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptMaterialProxy, "CScriptMaterialProxy", "Material proxy for VScript" ) + DEFINE_SCRIPTFUNC( GetVarString, "Gets a material var's string value" ) + DEFINE_SCRIPTFUNC( GetVarInt, "Gets a material var's int value" ) + DEFINE_SCRIPTFUNC( GetVarFloat, "Gets a material var's float value" ) + DEFINE_SCRIPTFUNC( GetVarVector, "Gets a material var's vector value" ) + DEFINE_SCRIPTFUNC( SetVarString, "Sets a material var's string value" ) + 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" ) +END_SCRIPTDESC(); + +CScriptMaterialProxy::CScriptMaterialProxy() +{ + m_hScriptInstance = NULL; + m_hFuncOnBind = NULL; +} + +CScriptMaterialProxy::~CScriptMaterialProxy() +{ +} + + +//----------------------------------------------------------------------------- +// Cleanup +//----------------------------------------------------------------------------- +void CScriptMaterialProxy::Release( void ) +{ + if ( m_hScriptInstance && g_pScriptVM ) + { + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + m_hScriptInstance = NULL; + } + + delete this; +} + +bool CScriptMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) +{ + for (KeyValues *pKey = pKeyValues->GetFirstSubKey(); pKey != NULL; pKey = pKey->GetNextKey()) + { + // Get each variable we're looking for + if (Q_strnicmp( pKey->GetName(), "var", 3 ) == 0) + { + int index = atoi(pKey->GetName() + 3); + if (index > SCRIPT_MAT_PROXY_MAX_VARS) + { + Warning("VScript material proxy only supports 8 vars (not %i)\n", index); + continue; + } + + bool foundVar; + m_MaterialVars[index] = pMaterial->FindVar( pKey->GetString(), &foundVar ); + + // Don't init if we didn't find the var + if (!foundVar) + return false; + } + else if (FStrEq( pKey->GetName(), "scriptfile" )) + { + Q_strncpy( m_szFilePath, pKey->GetString(), sizeof( m_szFilePath ) ); + } + } + + return true; +} + +bool CScriptMaterialProxy::InitScript() +{ + if (!m_ScriptScope.IsInitialized()) + { + if (scriptmanager == NULL) + { + ExecuteOnce(DevMsg("Cannot execute script because scripting is disabled (-scripting)\n")); + return false; + } + + if (g_pScriptVM == NULL) + { + ExecuteOnce(DevMsg(" Cannot execute script because there is no available VM\n")); + return false; + } + + char* iszScriptId = (char*)stackalloc( 1024 ); + g_pScriptVM->GenerateUniqueKey("VScriptProxy", iszScriptId, 1024); + + m_hScriptInstance = g_pScriptVM->RegisterInstance( GetScriptDescForClass( CScriptMaterialProxy ), this ); + g_pScriptVM->SetInstanceUniqeId( m_hScriptInstance, iszScriptId ); + + bool bResult = m_ScriptScope.Init( iszScriptId ); + + if (!bResult) + { + DevMsg("VScriptProxy couldn't create ScriptScope!\n"); + return false; + } + + g_pScriptVM->SetValue( m_ScriptScope, "self", m_hScriptInstance ); + } + + // Don't init if we can't run the script + if (!VScriptRunScript( m_szFilePath, m_ScriptScope, true )) + return false; + + m_hFuncOnBind = m_ScriptScope.LookupFunction( "OnBind" ); + + if (!m_hFuncOnBind) + { + // Don't init if we can't find our func + Warning("VScript material proxy can't find OnBind function\n"); + return false; + } + + return true; +} + +void CScriptMaterialProxy::OnBind( void *pRenderable ) +{ + if( !pRenderable ) + return; + + if (m_hFuncOnBind != NULL) + { + IClientRenderable *pRend = ( IClientRenderable* )pRenderable; + C_BaseEntity *pEnt = pRend->GetIClientUnknown()->GetBaseEntity(); + if ( pEnt ) + { + g_pScriptVM->SetValue( m_ScriptScope, "entity", pEnt->GetScriptInstance() ); + } + else + { + // 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 ); + } + + m_ScriptScope.Call( m_hFuncOnBind, NULL ); + + g_pScriptVM->ClearValue( m_ScriptScope, "entity" ); + } + else + { + // The VM might not exist if we do it from Init(), so we have to do it here. + // TODO: We have no handling for if this fails, how do you cancel a proxy? + if (InitScript()) + OnBind( pRenderable ); + } +} + +const char *CScriptMaterialProxy::GetVarString( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return NULL; + + return m_MaterialVars[i]->GetStringValue(); +} + +int CScriptMaterialProxy::GetVarInt( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return 0; + + return m_MaterialVars[i]->GetIntValue(); +} + +float CScriptMaterialProxy::GetVarFloat( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return 0.0f; + + return m_MaterialVars[i]->GetFloatValue(); +} + +const Vector& CScriptMaterialProxy::GetVarVector( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return vec3_origin; + + if (m_MaterialVars[i]->GetType() != MATERIAL_VAR_TYPE_VECTOR) + return vec3_origin; + + // This is really bad. Too bad! + return *(reinterpret_cast(m_MaterialVars[i]->GetVecValue())); +} + +void CScriptMaterialProxy::SetVarString( int i, const char *value ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return; + + return m_MaterialVars[i]->SetStringValue( value ); +} + +void CScriptMaterialProxy::SetVarInt( int i, int value ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return; + + return m_MaterialVars[i]->SetIntValue( value ); +} + +void CScriptMaterialProxy::SetVarFloat( int i, float value ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return; + + return m_MaterialVars[i]->SetFloatValue( value ); +} + +void CScriptMaterialProxy::SetVarVector( int i, const Vector &value ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return; + + return m_MaterialVars[i]->SetVecValue( value.Base(), 3 ); +} + +EXPOSE_INTERFACE( CScriptMaterialProxy, IMaterialProxy, "VScriptProxy" IMATERIAL_PROXY_INTERFACE_VERSION ); +#endif + //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- @@ -181,6 +458,13 @@ public: virtual void LevelInitPostEntity( void ) { m_bAllowEntityCreationInScripts = false; +#ifdef MAPBASE_VSCRIPT + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if (pPlayer) + { + g_pScriptVM->SetValue( "player", pPlayer->GetScriptInstance() ); + } +#endif } virtual void LevelShutdownPostEntity( void ) diff --git a/sp/src/game/shared/SoundEmitterSystem.cpp b/sp/src/game/shared/SoundEmitterSystem.cpp index 57e823d1..19b37f11 100644 --- a/sp/src/game/shared/SoundEmitterSystem.cpp +++ b/sp/src/game/shared/SoundEmitterSystem.cpp @@ -1217,7 +1217,7 @@ void CBaseEntity::EmitSound( const char *soundname, HSOUNDSCRIPTHANDLE& handle, EmitSound( filter, entindex(), params, handle ); } -#if !defined ( CLIENT_DLL ) +#if !defined ( CLIENT_DLL ) || defined( MAPBASE_VSCRIPT ) void CBaseEntity::ScriptEmitSound( const char *soundname ) { EmitSound( soundname ); @@ -1499,7 +1499,7 @@ HSOUNDSCRIPTHANDLE CBaseEntity::PrecacheScriptSound( const char *soundname ) #endif } -#if !defined ( CLIENT_DLL ) +#if !defined ( CLIENT_DLL ) || defined( MAPBASE_VSCRIPT ) // Same as server version of above, but signiture changed so it can be deduced by the macros void CBaseEntity::VScriptPrecacheScriptSound(const char* soundname) {