diff --git a/sp/src/game/client/c_baseanimating.cpp b/sp/src/game/client/c_baseanimating.cpp index 22dde7ba..4f09a112 100644 --- a/sp/src/game/client/c_baseanimating.cpp +++ b/sp/src/game/client/c_baseanimating.cpp @@ -281,6 +281,11 @@ BEGIN_DATADESC( C_ClientRagdoll ) END_DATADESC() +BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-side" ) + 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" ) +END_SCRIPTDESC(); + C_ClientRagdoll::C_ClientRagdoll( bool bRestoring ) { m_iCurrentFriction = 0; @@ -1403,6 +1408,15 @@ float C_BaseAnimating::ClampCycle( float flCycle, bool isLooping ) return flCycle; } +void C_BaseAnimating::ScriptSetPoseParameter(const char* szName, float fValue) +{ + CStudioHdr* pHdr = GetModelPtr(); + if (pHdr == NULL) + return; + + int iPoseParam = LookupPoseParameter(pHdr, szName); + SetPoseParameter(pHdr, iPoseParam, fValue); +} void C_BaseAnimating::GetCachedBoneMatrix( int boneIndex, matrix3x4_t &out ) { diff --git a/sp/src/game/client/c_baseanimating.h b/sp/src/game/client/c_baseanimating.h index f91745ef..1a460b85 100644 --- a/sp/src/game/client/c_baseanimating.h +++ b/sp/src/game/client/c_baseanimating.h @@ -95,6 +95,7 @@ public: DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); DECLARE_INTERPOLATION(); + DECLARE_ENT_SCRIPTDESC(); enum { @@ -445,6 +446,7 @@ public: virtual bool IsViewModel() const; + void ScriptSetPoseParameter(const char* szName, float fValue); protected: // View models scale their attachment positions to account for FOV. To get the unmodified // attachment position (like if you're rendering something else during the view model's DrawModel call), diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index 5d1cfd73..10415ab5 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -44,6 +44,8 @@ #include "viewrender.h" #endif +#include "gamestringpool.h" + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -423,6 +425,13 @@ BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_AnimTimeMustBeFirst ) RecvPropInt( RECVINFO(m_flAnimTime), 0, RecvProxy_AnimTime ), END_RECV_TABLE() +BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities" ) + DEFINE_SCRIPTFUNC_NAMED( GetAbsOrigin, "GetOrigin", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetForward, "GetForwardVector", "Get the forward vector of the entity" ) + 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" ) +END_SCRIPTDESC(); #ifndef NO_ENTITY_PREDICTION BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_PredictableId ) @@ -466,6 +475,8 @@ BEGIN_RECV_TABLE_NOBASE(C_BaseEntity, DT_BaseEntity) RecvPropInt( RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent ), RecvPropInt( RECVINFO( m_iParentAttachment ) ), + RecvPropString(RECVINFO(m_iName)), + RecvPropInt( "movetype", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveType ), RecvPropInt( "movecollide", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveCollide ), RecvPropDataTable( RECVINFO_DT( m_Collision ), 0, &REFERENCE_RECV_TABLE(DT_CollisionProperty) ), @@ -1165,6 +1176,7 @@ void C_BaseEntity::Term() g_Predictables.RemoveFromPredictablesList( GetClientHandle() ); } + // If it's play simulated, remove from simulation list if the player still exists... if ( IsPlayerSimulated() && C_BasePlayer::GetLocalPlayer() ) { @@ -1201,6 +1213,12 @@ void C_BaseEntity::Term() RemoveFromLeafSystem(); RemoveFromAimEntsList(); + + if ( m_hScriptInstance ) + { + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + m_hScriptInstance = NULL; + } } @@ -6442,6 +6460,26 @@ int C_BaseEntity::GetCreationTick() const return m_nCreationTick; } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::GetScriptInstance() +{ + if (!m_hScriptInstance) + { + if (m_iszScriptId == NULL_STRING) + { + char* szName = (char*)stackalloc(1024); + g_pScriptVM->GenerateUniqueKey((m_iName != NULL_STRING) ? STRING(GetEntityName()) : GetClassname(), szName, 1024); + m_iszScriptId = AllocPooledString(szName); + } + + m_hScriptInstance = g_pScriptVM->RegisterInstance(GetScriptDesc(), this); + g_pScriptVM->SetInstanceUniqeId(m_hScriptInstance, STRING(m_iszScriptId)); + } + return m_hScriptInstance; +} + //------------------------------------------------------------------------------ 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 c68e3868..ff3f1e6a 100644 --- a/sp/src/game/client/c_baseentity.h +++ b/sp/src/game/client/c_baseentity.h @@ -36,6 +36,9 @@ #include "toolframework/itoolentity.h" #include "tier0/threadtools.h" +#include "vscript/ivscript.h" +#include "vscript_shared.h" + class C_Team; class IPhysicsObject; class IClientVehicle; @@ -183,6 +186,8 @@ public: DECLARE_DATADESC(); DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); + // script description + DECLARE_ENT_SCRIPTDESC(); C_BaseEntity(); virtual ~C_BaseEntity(); @@ -256,6 +261,11 @@ public: string_t m_iClassname; + HSCRIPT GetScriptInstance(); + + HSCRIPT m_hScriptInstance; + string_t m_iszScriptId; + // IClientUnknown overrides. public: @@ -1119,6 +1129,10 @@ public: virtual int GetBody() { return 0; } virtual int GetSkin() { return 0; } + const Vector& ScriptGetForward(void) { static Vector vecForward; GetVectors(&vecForward, NULL, NULL); return vecForward; } + 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; } + // Stubs on client void NetworkStateManualMode( bool activate ) { } void NetworkStateChanged() { } @@ -1266,6 +1280,7 @@ public: void SetRenderMode( RenderMode_t nRenderMode, bool bForceUpdate = false ); RenderMode_t GetRenderMode() const; + const char* GetEntityName(); public: // Determine what entity this corresponds to @@ -1648,6 +1663,8 @@ private: // The owner! EHANDLE m_hOwnerEntity; EHANDLE m_hEffectEntity; + + char m_iName[MAX_PATH]; // This is a random seed used by the networking code to allow client - side prediction code // randon number generators to spit out the same random numbers on both sides for a particular @@ -2203,6 +2220,12 @@ inline bool C_BaseEntity::ShouldRecordInTools() const #endif } +inline const char *C_BaseEntity::GetEntityName() +{ + return m_iName; +} + + C_BaseEntity *CreateEntityByName( const char *className ); #endif // C_BASEENTITY_H diff --git a/sp/src/game/client/c_baseflex.cpp b/sp/src/game/client/c_baseflex.cpp index 606ba009..6d0193cc 100644 --- a/sp/src/game/client/c_baseflex.cpp +++ b/sp/src/game/client/c_baseflex.cpp @@ -1480,7 +1480,7 @@ bool C_BaseFlex::ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool can // expression - // duration - //----------------------------------------------------------------------------- -void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget, bool bClientSide ) +void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget, bool bClientSide, C_SceneEntity* pSceneEntity) { if ( !scene || !event ) { @@ -1505,6 +1505,7 @@ void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseE info.m_hTarget = pTarget; info.m_bStarted = false; info.m_bClientSide = bClientSide; + info.m_hSceneEntity = pSceneEntity; if (StartSceneEvent( &info, scene, event, actor, pTarget )) { diff --git a/sp/src/game/client/c_baseflex.h b/sp/src/game/client/c_baseflex.h index 71cee3d8..6d9f1b54 100644 --- a/sp/src/game/client/c_baseflex.h +++ b/sp/src/game/client/c_baseflex.h @@ -214,7 +214,7 @@ public: virtual bool ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled ); // Add the event to the queue for this actor - void AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, C_BaseEntity *pTarget = NULL, bool bClientSide = false ); + void AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, C_BaseEntity *pTarget = NULL, bool bClientSide = false, C_SceneEntity* pSceneEntity = NULL); // Remove the event from the queue for this actor void RemoveSceneEvent( CChoreoScene *scene, CChoreoEvent *event, bool fastKill ); diff --git a/sp/src/game/client/c_sceneentity.cpp b/sp/src/game/client/c_sceneentity.cpp index 99a412d2..1b15faf4 100644 --- a/sp/src/game/client/c_sceneentity.cpp +++ b/sp/src/game/client/c_sceneentity.cpp @@ -674,7 +674,7 @@ void C_SceneEntity::DispatchStartSpeak( CChoreoScene *scene, C_BaseFlex *actor, es.m_pSoundName = event->GetParameters(); EmitSound( filter, actor->entindex(), es ); - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); // Close captioning only on master token no matter what... if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER ) @@ -964,7 +964,7 @@ void C_SceneEntity::UnloadScene( void ) //----------------------------------------------------------------------------- void C_SceneEntity::DispatchStartFlexAnimation( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) { - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -984,7 +984,7 @@ void C_SceneEntity::DispatchEndFlexAnimation( CChoreoScene *scene, C_BaseFlex *a //----------------------------------------------------------------------------- void C_SceneEntity::DispatchStartExpression( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) { - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -1008,7 +1008,7 @@ void C_SceneEntity::DispatchStartGesture( CChoreoScene *scene, C_BaseFlex *actor if ( !Q_stricmp( event->GetName(), "NULL" ) ) return; - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -1023,7 +1023,7 @@ void C_SceneEntity::DispatchProcessGesture( CChoreoScene *scene, C_BaseFlex *act return; actor->RemoveSceneEvent( scene, event, false ); - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -1046,7 +1046,7 @@ void C_SceneEntity::DispatchEndGesture( CChoreoScene *scene, C_BaseFlex *actor, //----------------------------------------------------------------------------- void C_SceneEntity::DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) { - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -1056,7 +1056,7 @@ void C_SceneEntity::DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor void C_SceneEntity::DispatchProcessSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) { actor->RemoveSceneEvent( scene, event, false ); - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- diff --git a/sp/src/game/client/cdll_client_int.cpp b/sp/src/game/client/cdll_client_int.cpp index c12cb2c7..64d5f24d 100644 --- a/sp/src/game/client/cdll_client_int.cpp +++ b/sp/src/game/client/cdll_client_int.cpp @@ -223,6 +223,8 @@ IReplaySystem *g_pReplay = NULL; IVEngineServer *serverengine = NULL; #endif +IScriptManager *scriptmanager = NULL; + IHaptics* haptics = NULL;// NVNT haptics system interface singleton //============================================================================= @@ -964,6 +966,11 @@ int CHLClient::Init( CreateInterfaceFn appSystemFactory, CreateInterfaceFn physi if (!g_pMatSystemSurface) return false; + if ( !CommandLine()->CheckParm( "-noscripting") ) + { + scriptmanager = (IScriptManager *)appSystemFactory( VSCRIPT_INTERFACE_VERSION, NULL ); + } + #ifdef WORKSHOP_IMPORT_ENABLED if ( !ConnectDataModel( appSystemFactory ) ) return false; diff --git a/sp/src/game/client/client_base.vpc b/sp/src/game/client/client_base.vpc index e15c2a66..29e5acc6 100644 --- a/sp/src/game/client/client_base.vpc +++ b/sp/src/game/client/client_base.vpc @@ -493,6 +493,11 @@ $Project $File "viewrender.cpp" $File "$SRCDIR\game\shared\voice_banmgr.cpp" $File "$SRCDIR\game\shared\voice_status.cpp" + $File "vscript_client.cpp" + $File "vscript_client.h" + $File "vscript_client.nut" + $File "$SRCDIR\game\shared\vscript_shared.cpp" + $File "$SRCDIR\game\shared\vscript_shared.h" $File "warp_overlay.cpp" $File "WaterLODMaterialProxy.cpp" $File "$SRCDIR\game\shared\weapon_parse.cpp" diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp new file mode 100644 index 00000000..de113970 --- /dev/null +++ b/sp/src/game/client/vscript_client.cpp @@ -0,0 +1,190 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "vscript_client.h" +#include "icommandline.h" +#include "tier1/utlbuffer.h" +#include "tier1/fmtstr.h" +#include "filesystem.h" +#include "characterset.h" +#include "isaverestore.h" +#include "gamerules.h" +#ifdef _WIN32 +//#include "vscript_client_nut.h" +#endif + +extern IScriptManager *scriptmanager; +extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); + +// #define VMPROFILE 1 + +#ifdef VMPROFILE + +#define VMPROF_START float debugStartTime = Plat_FloatTime(); +#define VMPROF_SHOW( funcname, funcdesc ) DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", (##funcname), (##funcdesc), (Plat_FloatTime() - debugStartTime)*1000.0 ); + +#else // !VMPROFILE + +#define VMPROF_START +#define VMPROF_SHOW + +#endif // VMPROFILE + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static float Time() +{ + return gpGlobals->curtime; +} + +static const char *GetMapName() +{ + return engine->GetLevelName(); +} + +static const char *DoUniqueString( const char *pszBase ) +{ + static char szBuf[512]; + g_pScriptVM->GenerateUniqueKey( pszBase, szBuf, ARRAYSIZE(szBuf) ); + return szBuf; +} + +bool DoIncludeScript( const char *pszScript, HSCRIPT hScope ) +{ + if ( !VScriptRunScript( pszScript, hScope, true ) ) + { + g_pScriptVM->RaiseException( CFmtStr( "Failed to include script \"%s\"", ( pszScript ) ? pszScript : "unknown" ) ); + return false; + } + return true; +} + +bool VScriptClientInit() +{ + VMPROF_START + + if( scriptmanager != NULL ) + { + ScriptLanguage_t scriptLanguage = SL_DEFAULT; + + char const *pszScriptLanguage; + if ( CommandLine()->CheckParm( "-scriptlang", &pszScriptLanguage ) ) + { + if( !Q_stricmp(pszScriptLanguage, "gamemonkey") ) + { + scriptLanguage = SL_GAMEMONKEY; + } + else if( !Q_stricmp(pszScriptLanguage, "squirrel") ) + { + scriptLanguage = SL_SQUIRREL; + } + else if( !Q_stricmp(pszScriptLanguage, "python") ) + { + scriptLanguage = SL_PYTHON; + } + else + { + DevWarning("-scriptlang does not recognize a language named '%s'. virtual machine did NOT start.\n", pszScriptLanguage ); + scriptLanguage = SL_NONE; + } + + } + if( scriptLanguage != SL_NONE ) + { + if ( g_pScriptVM == NULL ) + g_pScriptVM = scriptmanager->CreateVM( scriptLanguage ); + + if( g_pScriptVM ) + { + Log( "VSCRIPT: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); + 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)" ); + + if ( GameRules() ) + { + GameRules()->RegisterScriptFunctions(); + } + + //g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); + + if ( scriptLanguage == SL_SQUIRREL ) + { + //g_pScriptVM->Run( g_Script_vscript_client ); + } + + VScriptRunScript( "mapspawn", false ); + + VMPROF_SHOW( pszScriptLanguage, "virtual machine startup" ); + + return true; + } + else + { + DevWarning("VM Did not start!\n"); + } + } + } + else + { + Log( "\nVSCRIPT: Scripting is disabled.\n" ); + } + g_pScriptVM = NULL; + return false; +} + +void VScriptClientTerm() +{ + if( g_pScriptVM != NULL ) + { + if( g_pScriptVM ) + { + scriptmanager->DestroyVM( g_pScriptVM ); + g_pScriptVM = NULL; + } + } +} + + +class CVScriptGameSystem : public CAutoGameSystemPerFrame +{ +public: + // Inherited from IAutoServerSystem + virtual void LevelInitPreEntity( void ) + { + m_bAllowEntityCreationInScripts = true; + VScriptClientInit(); + } + + virtual void LevelInitPostEntity( void ) + { + m_bAllowEntityCreationInScripts = false; + } + + virtual void LevelShutdownPostEntity( void ) + { + VScriptClientTerm(); + } + + virtual void FrameUpdatePostEntityThink() + { + if ( g_pScriptVM ) + g_pScriptVM->Frame( gpGlobals->frametime ); + } + + bool m_bAllowEntityCreationInScripts; +}; + +CVScriptGameSystem g_VScriptGameSystem; + +bool IsEntityCreationAllowedInScripts( void ) +{ + return g_VScriptGameSystem.m_bAllowEntityCreationInScripts; +} + + diff --git a/sp/src/game/client/vscript_client.h b/sp/src/game/client/vscript_client.h new file mode 100644 index 00000000..b2aa3aae --- /dev/null +++ b/sp/src/game/client/vscript_client.h @@ -0,0 +1,22 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_SERVER_H +#define VSCRIPT_SERVER_H + +#include "vscript/ivscript.h" +#include "vscript_shared.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +extern IScriptVM * g_pScriptVM; + +// Only allow scripts to create entities during map initialization +bool IsEntityCreationAllowedInScripts( void ); + +#endif // VSCRIPT_SERVER_H diff --git a/sp/src/game/client/vscript_client.nut b/sp/src/game/client/vscript_client.nut new file mode 100644 index 00000000..2efd3417 --- /dev/null +++ b/sp/src/game/client/vscript_client.nut @@ -0,0 +1,19 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +function UniqueString( string = "" ) +{ + return DoUniqueString( string.tostring() ); +} + +function IncludeScript( name, scope = null ) +{ + if ( scope == null ) + { + scope = this; + } + return ::DoIncludeScript( name, scope ); +} diff --git a/sp/src/game/server/baseanimating.cpp b/sp/src/game/server/baseanimating.cpp index 5fb0ff2f..26743f80 100644 --- a/sp/src/game/server/baseanimating.cpp +++ b/sp/src/game/server/baseanimating.cpp @@ -282,6 +282,14 @@ IMPLEMENT_SERVERCLASS_ST(CBaseAnimating, DT_BaseAnimating) END_SEND_TABLE() +BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" ) + + 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( IsSequenceFinished, "Ask whether the main sequence is done playing" ) + DEFINE_SCRIPTFUNC( SetBodygroup, "Sets a bodygroup") +END_SCRIPTDESC(); CBaseAnimating::CBaseAnimating() { @@ -2117,6 +2125,35 @@ bool CBaseAnimating::GetAttachment( int iAttachment, Vector &absOrigin, Vector * return bRet; } +//----------------------------------------------------------------------------- +// Purpose: Returns the world location and world angles of an attachment to vscript caller +// Input : attachment name +// Output : location and angles +//----------------------------------------------------------------------------- +const Vector& CBaseAnimating::ScriptGetAttachmentOrigin( int iAttachment ) +{ + + static Vector absOrigin; + static QAngle qa; + + CBaseAnimating::GetAttachment( iAttachment, absOrigin, qa ); + + return absOrigin; +} + +const Vector& CBaseAnimating::ScriptGetAttachmentAngles( int iAttachment ) +{ + + static Vector absOrigin; + static Vector absAngles; + static QAngle qa; + + CBaseAnimating::GetAttachment( iAttachment, absOrigin, qa ); + absAngles.x = qa.x; + absAngles.y = qa.y; + absAngles.z = qa.z; + return absAngles; +} //----------------------------------------------------------------------------- // Returns the attachment in local space diff --git a/sp/src/game/server/baseanimating.h b/sp/src/game/server/baseanimating.h index 9ad963bc..61d0e5a6 100644 --- a/sp/src/game/server/baseanimating.h +++ b/sp/src/game/server/baseanimating.h @@ -44,6 +44,7 @@ public: DECLARE_DATADESC(); DECLARE_SERVERCLASS(); + DECLARE_ENT_SCRIPTDESC(); virtual void SetModel( const char *szModelName ); virtual void Activate(); @@ -186,6 +187,8 @@ public: bool GetAttachment( int iAttachment, Vector &absOrigin, QAngle &absAngles ); int GetAttachmentBone( int iAttachment ); virtual bool GetAttachment( int iAttachment, matrix3x4_t &attachmentToWorld ); + const Vector& ScriptGetAttachmentOrigin(int iAttachment); + const Vector& ScriptGetAttachmentAngles(int iAttachment); // These return the attachment in the space of the entity bool GetAttachmentLocal( const char *szName, Vector &origin, QAngle &angles ); diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index 6bc1b6ea..d194e706 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -101,6 +101,9 @@ bool CBaseEntity::s_bAbsQueriesValid = true; ConVar sv_netvisdist( "sv_netvisdist", "10000", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Test networking visibility distance" ); +ConVar sv_script_think_interval("sv_script_think_interval", "0.1"); + + // This table encodes edict data. void SendProxy_AnimTime( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID ) { @@ -291,6 +294,8 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CBaseEntity, DT_BaseEntity ) SendPropEHandle (SENDINFO_NAME(m_hMoveParent, moveparent)), SendPropInt (SENDINFO(m_iParentAttachment), NUM_PARENTATTACHMENT_BITS, SPROP_UNSIGNED), + SendPropStringT( SENDINFO( m_iName ) ), + SendPropInt (SENDINFO_NAME( m_MoveType, movetype ), MOVETYPE_MAX_BITS, SPROP_UNSIGNED ), SendPropInt (SENDINFO_NAME( m_MoveCollide, movecollide ), MOVECOLLIDE_MAX_BITS, SPROP_UNSIGNED ), #if PREDICTION_ERROR_CHECK_LEVEL > 1 @@ -1313,6 +1318,19 @@ void CBaseEntity::FireNamedOutput( const char *pszOutput, variant_t variant, CBa if ( pszOutput == NULL ) return; + CBaseEntityOutput *pOutput = FindNamedOutput( pszOutput ); + if ( pOutput ) + { + pOutput->FireOutput( variant, pActivator, pCaller, flDelay ); + return; + } +} + +CBaseEntityOutput *CBaseEntity::FindNamedOutput( const char *pszOutput ) +{ + if ( pszOutput == NULL ) + return NULL; + datamap_t *dmap = GetDataDescMap(); while ( dmap ) { @@ -1322,17 +1340,16 @@ void CBaseEntity::FireNamedOutput( const char *pszOutput, variant_t variant, CBa typedescription_t *dataDesc = &dmap->dataDesc[i]; if ( ( dataDesc->fieldType == FIELD_CUSTOM ) && ( dataDesc->flags & FTYPEDESC_OUTPUT ) ) { - CBaseEntityOutput *pOutput = ( CBaseEntityOutput * )( ( int )this + ( int )dataDesc->fieldOffset[0] ); + CBaseEntityOutput *pOutput = ( CBaseEntityOutput * )( ( int )this + ( int )dataDesc->fieldOffset ); if ( !Q_stricmp( dataDesc->externalName, pszOutput ) ) { - pOutput->FireOutput( variant, pActivator, pCaller, flDelay ); - return; + return pOutput; } } } - dmap = dmap->baseMap; } + return NULL; } void CBaseEntity::Activate( void ) @@ -1822,6 +1839,12 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_FIELD( m_flSimulationTime, FIELD_TIME ), DEFINE_FIELD( m_nLastThinkTick, FIELD_TICK ), + DEFINE_FIELD(m_iszScriptId, FIELD_STRING), + // m_ScriptScope; + // m_hScriptInstance; + + DEFINE_KEYFIELD(m_iszVScripts, FIELD_STRING, "vscripts"), + DEFINE_KEYFIELD(m_iszScriptThinkFunction, FIELD_STRING, "thinkfunction"), DEFINE_KEYFIELD( m_nNextThinkTick, FIELD_TICK, "nextthink" ), DEFINE_KEYFIELD( m_fEffects, FIELD_INTEGER, "effects" ), DEFINE_KEYFIELD( m_clrRender, FIELD_COLOR32, "rendercolor" ), @@ -2004,6 +2027,10 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_INPUTFUNC( FIELD_STRING, "FireUser4", InputFireUser4 ), #endif + DEFINE_INPUTFUNC(FIELD_STRING, "RunScriptFile", InputRunScriptFile), + DEFINE_INPUTFUNC(FIELD_STRING, "RunScriptCode", InputRunScript), + DEFINE_INPUTFUNC(FIELD_STRING, "CallScriptFunction", InputCallScriptFunction), + #ifdef MAPBASE DEFINE_OUTPUT( m_OutUser1, "OutUser1" ), DEFINE_OUTPUT( m_OutUser2, "OutUser2" ), @@ -2081,6 +2108,7 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_FUNCTION( SUB_Vanish ), DEFINE_FUNCTION( SUB_CallUseToggle ), DEFINE_THINKFUNC( ShadowCastDistThink ), + DEFINE_THINKFUNC( ScriptThink ), #ifdef MAPBASE DEFINE_FUNCTION( SUB_RemoveWhenNotVisible ), @@ -2097,6 +2125,81 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) END_DATADESC() + +BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" ) + DEFINE_SCRIPT_INSTANCE_HELPER( &g_BaseEntityScriptInstanceHelper ) + DEFINE_SCRIPTFUNC_NAMED( ConnectOutputToScript, "ConnectOutput", "Adds an I/O connection that will call the named function when the specified output fires" ) + DEFINE_SCRIPTFUNC_NAMED( DisconnectOutputFromScript, "DisconnectOutput", "Removes a connected script function from an I/O event." ) + + DEFINE_SCRIPTFUNC( GetHealth, "" ) + DEFINE_SCRIPTFUNC( SetHealth, "" ) + DEFINE_SCRIPTFUNC( GetMaxHealth, "" ) + DEFINE_SCRIPTFUNC( SetMaxHealth, "" ) + + DEFINE_SCRIPTFUNC( SetModel, "" ) + 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( GetEntityNameAsCStr, "GetName", "" ) + DEFINE_SCRIPTFUNC( GetPreTemplateName, "Get the entity name stripped of template unique decoration" ) + + DEFINE_SCRIPTFUNC_NAMED( GetAbsOrigin, "GetOrigin", "" ) + DEFINE_SCRIPTFUNC( SetAbsOrigin, "SetAbsOrigin" ) + + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetOrigin, "SetOrigin", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetForward, "GetForwardVector", "Get the forward vector of the entity" ) + 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_NAMED( ScriptSetForward, "SetForwardVector", "Set the orientation of the entity to have this forward vector" ) + DEFINE_SCRIPTFUNC_NAMED( GetAbsVelocity, "GetVelocity", "" ) + DEFINE_SCRIPTFUNC_NAMED( SetAbsVelocity, "SetVelocity", "" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetLocalAngularVelocity, "SetAngularVelocity", "Set the local angular velocity - takes float pitch,yaw,roll velocities" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLocalAngularVelocity, "GetAngularVelocity", "Get the local angular velocity - returns a vector of pitch,yaw,roll" ) + + + 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( ScriptSetAngles, "SetAngles", "Set entity pitch, yaw, roll") + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAngles, "GetAngles", "Get entity pitch, yaw, roll as a vector") + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetSize, "SetSize", "" ) + 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( ScriptUtilRemove, "Destroy", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetOwner, "SetOwner", "" ) + DEFINE_SCRIPTFUNC_NAMED( GetTeamNumber, "GetTeam", "" ) + DEFINE_SCRIPTFUNC_NAMED( ChangeTeam, "SetTeam", "" ) + + 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( KeyValueFromString, "__KeyValueFromString", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( KeyValueFromFloat, "__KeyValueFromFloat", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( KeyValueFromInt, "__KeyValueFromInt", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( KeyValueFromVector, "__KeyValueFromVector", SCRIPT_HIDE ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelKeyValues, "GetModelKeyValues", "Get a KeyValue class instance on this entity's model") + + DEFINE_SCRIPTFUNC( ValidateScriptScope, "Ensure that an entity's script scope has been created" ) + DEFINE_SCRIPTFUNC( GetScriptScope, "Retrieve the script-side data associated with an entity" ) + DEFINE_SCRIPTFUNC( GetScriptId, "Retrieve the unique identifier used to refer to the entity within the scripting system" ) + DEFINE_SCRIPTFUNC_NAMED( GetScriptOwnerEntity, "GetOwner", "Gets this entity's owner" ) + DEFINE_SCRIPTFUNC_NAMED( SetScriptOwnerEntity, "SetOwner", "Sets this entity's owner" ) + DEFINE_SCRIPTFUNC( entindex, "" ) +END_SCRIPTDESC(); + + // For code error checking extern bool g_bReceivedChainedUpdateOnRemove; @@ -2196,6 +2299,12 @@ void CBaseEntity::UpdateOnRemove( void ) modelinfo->ReleaseDynamicModel( m_nModelIndex ); // no-op if not dynamic m_nModelIndex = -1; } + + if ( m_hScriptInstance ) + { + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + m_hScriptInstance = NULL; + } } //----------------------------------------------------------------------------- @@ -3925,9 +4034,9 @@ const char *CBaseEntity::GetDebugName(void) if ( this == NULL ) return "<>"; - if ( m_iName != NULL_STRING ) + if ( m_iName.Get() != NULL_STRING ) { - return STRING(m_iName); + return STRING(m_iName.Get()); } else { @@ -4108,7 +4217,7 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, // mapper debug message if (pCaller != NULL) { - Q_snprintf( szBuffer, sizeof(szBuffer), "(%0.2f) input %s: %s.%s(%s)\n", gpGlobals->curtime, STRING(pCaller->m_iName), GetDebugName(), szInputName, Value.String() ); + Q_snprintf( szBuffer, sizeof(szBuffer), "(%0.2f) input %s: %s.%s(%s)\n", gpGlobals->curtime, STRING(pCaller->m_iName.Get()), GetDebugName(), szInputName, Value.String() ); } else { @@ -4155,7 +4264,7 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, Warning( "!! ERROR: bad input/output link:\n!! Unable to convert value \"%s\" from %s (%s) to field type %i\n!! Target Entity: %s (%s), Input: %s\n", Value.GetDebug(), ( pCaller != NULL ) ? STRING(pCaller->m_iClassname) : "", - ( pCaller != NULL ) ? STRING(pCaller->m_iName) : "", + ( pCaller != NULL ) ? STRING(pCaller->m_iName.Get()) : "", dmap->dataDesc[i].fieldType, STRING(m_iClassname), GetDebugName(), szInputName ); return false; @@ -4168,7 +4277,7 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, Warning( "!! ERROR: bad input/output link:\n!! %s(%s,%s) doesn't match type from %s(%s)\n", STRING(m_iClassname), GetDebugName(), szInputName, ( pCaller != NULL ) ? STRING(pCaller->m_iClassname) : "", - ( pCaller != NULL ) ? STRING(pCaller->m_iName) : "" ); + ( pCaller != NULL ) ? STRING(pCaller->m_iName.Get()) : "" ); return false; } #endif @@ -4187,7 +4296,37 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, data.value = Value; data.nOutputID = outputID; - (this->*pfnInput)( data ); + + // Now, see if there's a function named Input in this entity's script file. + // If so, execute it and let it decide whether to allow the default behavior to also execute. + bool bCallInputFunc = true; // Always assume default behavior (do call the input function) + ScriptVariant_t functionReturn; + + if ( m_ScriptScope.IsInitialized() ) + { + char szScriptFunctionName[255]; + Q_strcpy( szScriptFunctionName, "Input" ); + Q_strcat( szScriptFunctionName, szInputName, 255 ); + + g_pScriptVM->SetValue( "activator", ( pActivator ) ? ScriptVariant_t( pActivator->GetScriptInstance() ) : SCRIPT_VARIANT_NULL ); + g_pScriptVM->SetValue( "caller", ( pCaller ) ? ScriptVariant_t( pCaller->GetScriptInstance() ) : SCRIPT_VARIANT_NULL ); + + if( CallScriptFunction( szScriptFunctionName, &functionReturn ) ) + { + bCallInputFunc = functionReturn.m_bool; + } + } + + if( bCallInputFunc ) + { + (this->*pfnInput)( data ); + } + + if ( m_ScriptScope.IsInitialized() ) + { + g_pScriptVM->ClearValue( "activator" ); + g_pScriptVM->ClearValue( "caller" ); + } } else if ( dmap->dataDesc[i].flags & FTYPEDESC_KEY ) { @@ -4210,7 +4349,7 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, } } - DevMsg( 2, "unhandled input: (%s) -> (%s,%s)\n", szInputName, STRING(m_iClassname), GetDebugName()/*,", from (%s,%s)" STRING(pCaller->m_iClassname), STRING(pCaller->m_iName)*/ ); + DevMsg( 2, "unhandled input: (%s) -> (%s,%s)\n", szInputName, STRING(m_iClassname), GetDebugName()/*,", from (%s,%s)" STRING(pCaller->m_iClassname), STRING(pCaller->m_iName.Get())*/ ); return false; } @@ -5300,6 +5439,36 @@ void CC_Ent_Name( const CCommand& args ) } static ConCommand ent_name("ent_name", CC_Ent_Name, 0, FCVAR_CHEAT); + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void DumpScriptScope(CBasePlayer* pPlayer, const char* name) +{ + CBaseEntity* pEntity = NULL; + while ((pEntity = GetNextCommandEntity(pPlayer, name, pEntity)) != NULL) + { + if (pEntity->m_ScriptScope.IsInitialized()) + { + Msg("----Script Dump for entity %s\n", pEntity->GetDebugName()); + HSCRIPT hDumpScopeFunc = g_pScriptVM->LookupFunction("__DumpScope"); + g_pScriptVM->Call(hDumpScopeFunc, NULL, true, NULL, 1, (HSCRIPT)pEntity->m_ScriptScope); + Msg("----End Script Dump\n"); + } + else + { + DevWarning("ent_script_dump: Entity %s has no script scope!\n", pEntity->GetDebugName()); + } + } +} + +//------------------------------------------------------------------------------ +void CC_Ent_Script_Dump( const CCommand& args ) +{ + DumpScriptScope(UTIL_GetCommandClient(),args[1]); +} +static ConCommand ent_script_dump("ent_script_dump", CC_Ent_Script_Dump, "Dumps the names and values of this entity's script scope to the console\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT); + + //------------------------------------------------------------------------------ #ifdef MAPBASE class CEntTextAutoCompletionFunctor : public ICommandCallback, public ICommandCompletionCallback @@ -7223,7 +7392,6 @@ void CBaseEntity::InputPassRandomUser( inputdata_t& inputdata ) } #endif - #ifdef MAPBASE //----------------------------------------------------------------------------- // Purpose: Sets the entity's targetname. @@ -7820,6 +7988,256 @@ void CBaseEntity::InputSetThinkNull( inputdata_t& inputdata ) #endif +//--------------------------------------------------------- +// Use the string as the filename of a script file +// that should be loaded from disk, compiled, and run. +//--------------------------------------------------------- +void CBaseEntity::InputRunScriptFile(inputdata_t& inputdata) +{ + RunScriptFile(inputdata.value.String()); +} + +//--------------------------------------------------------- +// Send the string to the VM as source code and execute it +//--------------------------------------------------------- +void CBaseEntity::InputRunScript(inputdata_t& inputdata) +{ + RunScript(inputdata.value.String(), "InputRunScript"); +} + +//--------------------------------------------------------- +// Make an explicit function call. +//--------------------------------------------------------- +void CBaseEntity::InputCallScriptFunction(inputdata_t& inputdata) +{ + CallScriptFunction(inputdata.value.String(), NULL); +} + +// #define VMPROFILE // define to profile vscript calls + +#ifdef VMPROFILE +float g_debugCumulativeTime = 0.0; +float g_debugCounter = 0; + +#define START_VMPROFILE float debugStartTime = Plat_FloatTime(); +#define UPDATE_VMPROFILE \ + g_debugCumulativeTime += Plat_FloatTime() - debugStartTime; \ + g_debugCounter++; \ + if ( g_debugCounter >= 500 ) \ + { \ + DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", "500 vscript function calls", "", g_debugCumulativeTime*1000.0 ); \ + g_debugCounter = 0; \ + g_debugCumulativeTime = 0.0; \ + } \ + +#else + +#define START_VMPROFILE +#define UPDATE_VMPROFILE + +#endif // VMPROFILE + +//----------------------------------------------------------------------------- +// Returns true if the function was located and called. false otherwise. +// NOTE: Assumes the function takes no parameters at the moment. +//----------------------------------------------------------------------------- +bool CBaseEntity::CallScriptFunction(const char* pFunctionName, ScriptVariant_t* pFunctionReturn) +{ + START_VMPROFILE + + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + + HSCRIPT hFunc = m_ScriptScope.LookupFunction(pFunctionName); + + if (hFunc) + { + m_ScriptScope.Call(hFunc, pFunctionReturn); + m_ScriptScope.ReleaseFunction(hFunc); + + UPDATE_VMPROFILE + + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ConnectOutputToScript(const char* pszOutput, const char* pszScriptFunc) +{ + CBaseEntityOutput* pOutput = FindNamedOutput(pszOutput); + if (!pOutput) + { + DevMsg(2, "Script failed to find output \"%s\"\n", pszOutput); + return; + } + + string_t iszSelf = AllocPooledString("!self"); // @TODO: cache this [4/25/2008 tom] + CEventAction* pAction = pOutput->GetActionList(); + while (pAction) + { + if (pAction->m_iTarget == iszSelf && + pAction->m_flDelay == 0 && + pAction->m_nTimesToFire == EVENT_FIRE_ALWAYS && + V_strcmp(STRING(pAction->m_iTargetInput), "CallScriptFunction") == 0 && + V_strcmp(STRING(pAction->m_iParameter), pszScriptFunc) == 0) + { + return; + } + pAction = pAction->m_pNext; + } + + pAction = new CEventAction(NULL); + pAction->m_iTarget = iszSelf; + pAction->m_iTargetInput = AllocPooledString("CallScriptFunction"); + pAction->m_iParameter = AllocPooledString(pszScriptFunc); + pOutput->AddEventAction(pAction); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::DisconnectOutputFromScript(const char* pszOutput, const char* pszScriptFunc) +{ + CBaseEntityOutput* pOutput = FindNamedOutput(pszOutput); + if (!pOutput) + { + DevMsg(2, "Script failed to find output \"%s\"\n", pszOutput); + return; + } + + string_t iszSelf = AllocPooledString("!self"); // @TODO: cache this [4/25/2008 tom] + CEventAction* pAction = pOutput->GetActionList(); + while (pAction) + { + if (pAction->m_iTarget == iszSelf && + pAction->m_flDelay == 0 && + pAction->m_nTimesToFire == EVENT_FIRE_ALWAYS && + V_strcmp(STRING(pAction->m_iTargetInput), "CallScriptFunction") == 0 && + V_strcmp(STRING(pAction->m_iParameter), pszScriptFunc) == 0) + { + pOutput->RemoveEventAction(pAction); + delete pAction; + return; + } + pAction = pAction->m_pNext; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptThink(void) +{ + ScriptVariant_t varThinkRetVal; + if (CallScriptFunction(m_iszScriptThinkFunction.ToCStr(), &varThinkRetVal)) + { + float flThinkFrequency = 0.0f; + if (!varThinkRetVal.AssignTo(&flThinkFrequency)) + { + // use default think interval if script think function doesn't provide one + flThinkFrequency = sv_script_think_interval.GetFloat(); + } + SetContextThink(&CBaseEntity::ScriptThink, + gpGlobals->curtime + flThinkFrequency, "ScriptThink"); + } + else + { + DevWarning("%s FAILED to call script think function %s!\n", GetDebugName(), STRING(m_iszScriptThinkFunction)); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char* CBaseEntity::GetScriptId() +{ + return STRING(m_iszScriptThinkFunction); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::GetScriptScope() +{ + return m_ScriptScope; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptGetMoveParent(void) +{ + return ToHScript(GetMoveParent()); +} +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptGetRootMoveParent() +{ + return ToHScript(GetRootMoveParent()); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptFirstMoveChild(void) +{ + return ToHScript(FirstMoveChild()); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptNextMovePeer(void) +{ + return ToHScript(NextMovePeer()); +} + +//----------------------------------------------------------------------------- +// Purpose: Load, compile, and run a script file from disk. +// Input : *pScriptFile - The filename of the script file. +// bUseRootScope - If true, runs this script in the root scope, not +// in this entity's private scope. +//----------------------------------------------------------------------------- +bool CBaseEntity::RunScriptFile(const char* pScriptFile, bool bUseRootScope) +{ + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + if (bUseRootScope) + { + return VScriptRunScript(pScriptFile); + } + else + { + return VScriptRunScript(pScriptFile, m_ScriptScope, true); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Compile and execute a discrete string of script source code +// Input : *pScriptText - A string containing script code to compile and run +//----------------------------------------------------------------------------- +bool CBaseEntity::RunScript(const char* pScriptText, const char* pDebugFilename) +{ + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + if (m_ScriptScope.Run(pScriptText, pDebugFilename) == SCRIPT_ERROR) + { + DevWarning(" Entity %s encountered an error in RunScript()\n", GetDebugName()); + } + + return true; +} + //----------------------------------------------------------------------------- // Purpose: // Input : *contextName - @@ -8796,6 +9214,266 @@ void CBaseEntity::SetCollisionBoundsFromModel() } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::GetScriptInstance() +{ + if (!m_hScriptInstance) + { + if (m_iszScriptId == NULL_STRING) + { + char* szName = (char*)stackalloc(1024); + g_pScriptVM->GenerateUniqueKey((m_iName.Get() != NULL_STRING) ? STRING(GetEntityName()) : GetClassname(), szName, 1024); + m_iszScriptId = AllocPooledString(szName); + } + + m_hScriptInstance = g_pScriptVM->RegisterInstance(GetScriptDesc(), this); + g_pScriptVM->SetInstanceUniqeId(m_hScriptInstance, STRING(m_iszScriptId)); + } + return m_hScriptInstance; +} + +//----------------------------------------------------------------------------- +// Using my edict, cook up a unique VScript scope that's private to me, and +// persistent. +//----------------------------------------------------------------------------- +bool CBaseEntity::ValidateScriptScope() +{ + 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; + } + + // Force instance creation + GetScriptInstance(); + + EHANDLE hThis; + hThis.Set(this); + + bool bResult = m_ScriptScope.Init(STRING(m_iszScriptId)); + + if (!bResult) + { + DevMsg("%s couldn't create ScriptScope!\n", GetDebugName()); + return false; + } + g_pScriptVM->SetValue(m_ScriptScope, "self", GetScriptInstance()); + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Run all of the vscript files that are set in this entity's VSCRIPTS +// field in Hammer. The list is space-delimited. +//----------------------------------------------------------------------------- +void CBaseEntity::RunVScripts() +{ + if (m_iszVScripts == NULL_STRING) + { + 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, STRING(m_iszVScripts)); + CUtlStringList szScripts; + + V_SplitString(szScriptsList, " ", szScripts); + + for (int i = 0; i < szScripts.Count(); i++) + { + Log( "%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_iszScriptThinkFunction != NULL_STRING) + { + SetContextThink(&CBaseEntity::ScriptThink, gpGlobals->curtime + sv_script_think_interval.GetFloat(), "ScriptThink"); + } +} + + +//-------------------------------------------------------------------------------------------------- +// This is called during entity spawning and after restore to allow scripts to precache any +// resources they need. +//-------------------------------------------------------------------------------------------------- +void CBaseEntity::RunPrecacheScripts(void) +{ + if (m_iszVScripts == NULL_STRING) + { + return; + } + + HSCRIPT hScriptPrecache = m_ScriptScope.LookupFunction("DispatchPrecache"); + if (hScriptPrecache) + { + g_pScriptVM->Call(hScriptPrecache, m_ScriptScope); + m_ScriptScope.ReleaseFunction(hScriptPrecache); + } +} + +void CBaseEntity::RunOnPostSpawnScripts(void) +{ + if (m_iszVScripts == NULL_STRING) + { + return; + } + + HSCRIPT hFuncConnect = g_pScriptVM->LookupFunction("ConnectOutputs"); + if (hFuncConnect) + { + g_pScriptVM->Call(hFuncConnect, NULL, true, NULL, (HSCRIPT)m_ScriptScope); + g_pScriptVM->ReleaseFunction(hFuncConnect); + } + + HSCRIPT hFuncDisp = m_ScriptScope.LookupFunction("DispatchOnPostSpawn"); + if (hFuncDisp) + { + variant_t variant; + variant.SetString(MAKE_STRING("DispatchOnPostSpawn")); + g_EventQueue.AddEvent(this, "CallScriptFunction", variant, 0, this, this); + m_ScriptScope.ReleaseFunction(hFuncDisp); + + } +} + +HSCRIPT CBaseEntity::GetScriptOwnerEntity() +{ + return ToHScript(GetOwnerEntity()); +} + +void CBaseEntity::SetScriptOwnerEntity(HSCRIPT pOwner) +{ + SetOwnerEntity(ToEnt(pOwner)); +} + +//----------------------------------------------------------------------------- +// VScript access to model's key values +// for iteration and value access, use: +// ScriptFindKey, ScriptGetFirstSubKey, ScriptGetString, +// ScriptGetInt, ScriptGetFloat, ScriptGetNextKey +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptGetModelKeyValues( void ) +{ + KeyValues *pModelKeyValues = new KeyValues(""); + HSCRIPT hScript = NULL; + const char *pszModelName = modelinfo->GetModelName( GetModel() ); + const char *pBuffer = modelinfo->GetModelKeyValueText( GetModel() ) ; + + if ( pModelKeyValues->LoadFromBuffer( pszModelName, pBuffer ) ) + { + // UNDONE: how does destructor get called on this + m_pScriptModelKeyValues = new CScriptKeyValues( pModelKeyValues ); + + // UNDONE: who calls ReleaseInstance on this??? Does name need to be unique??? + + hScript = g_pScriptVM->RegisterInstance( m_pScriptModelKeyValues ); + + /* + KeyValues *pParticleEffects = pModelKeyValues->FindKey("Particles"); + if ( pParticleEffects ) + { + // Start grabbing the sounds and slotting them in + for ( KeyValues *pSingleEffect = pParticleEffects->GetFirstSubKey(); pSingleEffect; pSingleEffect = pSingleEffect->GetNextKey() ) + { + const char *pParticleEffectName = pSingleEffect->GetString( "name", "" ); + PrecacheParticleSystem( pParticleEffectName ); + } + } + */ + } + + return hScript; +} + +void CBaseEntity::ScriptSetLocalAngularVelocity(float pitchVel, float yawVel, float rollVel) +{ + QAngle qa; + qa.Init(pitchVel, yawVel, rollVel); + SetLocalAngularVelocity(qa); +} + +const Vector& CBaseEntity::ScriptGetLocalAngularVelocity(void) +{ + QAngle qa = GetLocalAngularVelocity(); + static Vector v; + v.x = qa.x; + v.y = qa.y; + v.z = qa.z; + return v; +} + +//----------------------------------------------------------------------------- +// Vscript: Gets the min collision bounds, centered on object +//----------------------------------------------------------------------------- +const Vector& CBaseEntity::ScriptGetBoundingMins(void) +{ + return m_Collision.OBBMins(); +} + +//----------------------------------------------------------------------------- +// Vscript: Gets the max collision bounds, centered on object +//----------------------------------------------------------------------------- +const Vector& CBaseEntity::ScriptGetBoundingMaxs(void) +{ + return m_Collision.OBBMaxs(); +} + + #ifdef MAPBASE extern int EntityFactory_AutoComplete( const char *cmdname, CUtlVector< CUtlString > &commands, CUtlRBTree< CUtlString > &symbols, char *substring, int checklen = 0 ); diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index 8bccccbc..fd045d12 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -21,6 +21,9 @@ #include "shareddefs.h" #include "engine/ivmodelinfo.h" +#include "vscript/ivscript.h" +#include "vscript_server.h" + class CDamageModifier; class CDmgAccumulator; @@ -311,11 +314,13 @@ a list of all CBaseEntitys is kept in gEntList CBaseEntity *CreateEntityByName( const char *className, int iForceEdictIndex = -1 ); CBaseNetworkable *CreateNetworkableByName( const char *className ); +CBaseEntity* ToEnt(HSCRIPT hScript); + // creates an entity and calls all the necessary spawn functions extern void SpawnEntityByName( const char *className, CEntityMapData *mapData = NULL ); // calls the spawn functions for an entity -extern int DispatchSpawn( CBaseEntity *pEntity ); +extern int DispatchSpawn( CBaseEntity *pEntity, bool bRunVScripts = true); inline CBaseEntity *GetContainingEntity( edict_t *pent ); @@ -379,6 +384,8 @@ public: DECLARE_SERVERCLASS(); // data description DECLARE_DATADESC(); + // script description + DECLARE_ENT_SCRIPTDESC(); // memory handling void *operator new( size_t stAllocateBlock ); @@ -493,6 +500,8 @@ public: virtual void SetOwnerEntity( CBaseEntity* pOwner ); void SetEffectEntity( CBaseEntity *pEffectEnt ); CBaseEntity *GetEffectEntity() const; + HSCRIPT GetScriptOwnerEntity(); + virtual void SetScriptOwnerEntity(HSCRIPT pOwner); // Only CBaseEntity implements these. CheckTransmit calls the virtual ShouldTransmit to see if the // entity wants to be sent. If so, it calls SetTransmit, which will mark any dependents for transmission too. @@ -563,9 +572,14 @@ public: virtual bool KeyValue( const char *szKeyName, float flValue ); virtual bool KeyValue( const char *szKeyName, const Vector &vecValue ); virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); + bool KeyValueFromString( const char *szKeyName, const char *szValue ) { return KeyValue( szKeyName, szValue ); } + bool KeyValueFromFloat( const char *szKeyName, float flValue ) { return KeyValue( szKeyName, flValue ); } + bool KeyValueFromInt( const char *szKeyName, int nValue ) { return KeyValue( szKeyName, nValue ); } + bool KeyValueFromVector( const char *szKeyName, const Vector &vecValue ) { return KeyValue( szKeyName, vecValue ); } void ValidateEntityConnections(); void FireNamedOutput( const char *pszOutput, variant_t variant, CBaseEntity *pActivator, CBaseEntity *pCaller, float flDelay = 0.0f ); + CBaseEntityOutput *FindNamedOutput( const char *pszOutput ); // Activate - called for each entity after each load game and level load virtual void Activate( void ); @@ -588,6 +602,8 @@ public: int GetParentAttachment(); string_t GetEntityName(); + const char* GetEntityNameAsCStr(); // This method is temporary for VSCRIPT functionality until we figure out what to do with string_t (sjb) + const char* GetPreTemplateName(); // Not threadsafe. Get the name stripped of template unique decoration bool NameMatches( const char *pszNameOrWildcard ); bool ClassMatches( const char *pszClassOrWildcard ); @@ -735,6 +751,14 @@ public: COutputEvent m_OnKilled; #endif + void InputRunScript(inputdata_t& inputdata); + void InputRunScriptFile(inputdata_t& inputdata); + void InputCallScriptFunction(inputdata_t& inputdata); + + bool RunScriptFile(const char* pScriptFile, bool bUseRootScope = false); + bool RunScript(const char* pScriptText, const char* pDebugFilename = "CBaseEntity::RunScript"); + + // Returns the origin at which to play an inputted dispatcheffect virtual void GetInputDispatchEffectPosition( const char *sInputString, Vector &pOrigin, QAngle &pAngles ); @@ -1715,7 +1739,7 @@ private: // was pev->flags CNetworkVarForDerived( int, m_fFlags ); - string_t m_iName; // name used to identify this entity + CNetworkVar( string_t, m_iName ); // name used to identify this entity // Damage modifiers friend class CDamageModifier; @@ -1920,6 +1944,59 @@ public: { return s_bAbsQueriesValid; } + + + // VSCRIPT + HSCRIPT GetScriptInstance(); + bool ValidateScriptScope(); + virtual void RunVScripts(); + bool CallScriptFunction(const char* pFunctionName, ScriptVariant_t* pFunctionReturn); + void ConnectOutputToScript(const char* pszOutput, const char* pszScriptFunc); + void DisconnectOutputFromScript(const char* pszOutput, const char* pszScriptFunc); + void ScriptThink(); + const char* GetScriptId(); + HSCRIPT GetScriptScope(); + void RunPrecacheScripts(void); + void RunOnPostSpawnScripts(void); + + HSCRIPT ScriptGetMoveParent(void); + HSCRIPT ScriptGetRootMoveParent(); + HSCRIPT ScriptFirstMoveChild(void); + HSCRIPT ScriptNextMovePeer(void); + + const Vector& ScriptEyePosition(void) { static Vector vec; vec = EyePosition(); return vec; } + void ScriptSetAngles(float fPitch, float fYaw, float fRoll) { QAngle angles(fPitch, fYaw, fRoll); Teleport(NULL, &angles, NULL); } + const Vector& ScriptGetAngles(void) { static Vector vec; QAngle qa = GetAbsAngles(); vec.x = qa.x; vec.y = qa.y; vec.z = qa.z; return vec; } + + void ScriptSetSize(const Vector& mins, const Vector& maxs) { UTIL_SetSize(this, mins, maxs); } + void ScriptUtilRemove(void) { UTIL_Remove(this); } + void ScriptSetOwner(HSCRIPT hEntity) { SetOwnerEntity(ToEnt(hEntity)); } + void ScriptSetOrigin(const Vector& v) { Teleport(&v, NULL, NULL); } + void ScriptSetForward(const Vector& v) { QAngle angles; VectorAngles(v, angles); Teleport(NULL, &angles, NULL); } + const Vector& ScriptGetForward(void) { static Vector vecForward; GetVectors(&vecForward, NULL, NULL); return vecForward; } + 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; } + + const char* ScriptGetModelName(void) const; + HSCRIPT ScriptGetModelKeyValues(void); + + void ScriptEmitSound(const char* soundname); + float ScriptSoundDuration(const char* soundname, const char* actormodel); + + void VScriptPrecacheScriptSound(const char* soundname); + + const Vector& ScriptGetLocalAngularVelocity( void ); + void ScriptSetLocalAngularVelocity( float pitchVel, float yawVel, float rollVel ); + + const Vector& ScriptGetBoundingMins(void); + const Vector& ScriptGetBoundingMaxs(void); + + string_t m_iszVScripts; + string_t m_iszScriptThinkFunction; + CScriptScope m_ScriptScope; + HSCRIPT m_hScriptInstance; + string_t m_iszScriptId; + CScriptKeyValues* m_pScriptModelKeyValues; }; // Send tables exposed in this module. @@ -2048,6 +2125,21 @@ inline string_t CBaseEntity::GetEntityName() return m_iName; } +inline const char *CBaseEntity::GetEntityNameAsCStr() +{ + return STRING(m_iName.Get()); +} + +inline const char *CBaseEntity::GetPreTemplateName() +{ + const char *pszDelimiter = V_strrchr( STRING(m_iName.Get()), '&' ); + if ( !pszDelimiter ) + return STRING( m_iName.Get() ); + static char szStrippedName[128]; + V_strncpy( szStrippedName, STRING( m_iName.Get() ), MIN( ARRAYSIZE(szStrippedName), pszDelimiter - STRING( m_iName.Get() ) + 1 ) ); + return szStrippedName; +} + inline void CBaseEntity::SetName( string_t newName ) { m_iName = newName; @@ -2726,6 +2818,14 @@ inline void CBaseEntity::FireBullets( int cShots, const Vector &vecSrc, FireBullets( info ); } +//----------------------------------------------------------------------------- +// VScript +//----------------------------------------------------------------------------- +inline const char* CBaseEntity::ScriptGetModelName(void) const +{ + return STRING(m_ModelName); +} + // Ugly technique to override base member functions // Normally it's illegal to cast a pointer to a member function of a derived class to a pointer to a // member function of a base class. static_cast is a sleezy way around that problem. diff --git a/sp/src/game/server/baseflex.cpp b/sp/src/game/server/baseflex.cpp index e76ce1dd..006b85e8 100644 --- a/sp/src/game/server/baseflex.cpp +++ b/sp/src/game/server/baseflex.cpp @@ -95,6 +95,10 @@ BEGIN_DATADESC( CBaseFlex ) END_DATADESC() +BEGIN_ENT_SCRIPTDESC( CBaseFlex, CBaseAnimating, "Animated characters who have vertex flex capability." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetOldestScene, "GetCurrentScene", "Returns the instance of the oldest active scene entity (if any)." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSceneByIndex, "GetSceneByIndex", "Returns the instance of the scene entity at the specified index." ) +END_SCRIPTDESC(); LINK_ENTITY_TO_CLASS( funCBaseFlex, CBaseFlex ); // meaningless independant class!! @@ -421,7 +425,8 @@ void CBaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEn info.m_pEvent = event; info.m_pScene = scene; info.m_hTarget = pTarget; - info.m_bStarted = false; + info.m_bStarted = false; + info.m_hSceneEntity = pSceneEnt; #ifdef MAPBASE if (StartSceneEvent( &info, scene, event, actor, pTarget, pSceneEnt )) @@ -2029,6 +2034,37 @@ float CBaseFlex::PlayAutoGeneratedSoundScene( const char *soundname ) #endif +//-------------------------------------------------------------------------------------------------- +// Returns the script instance of the scene entity associated with our oldest ("top level") scene event +//-------------------------------------------------------------------------------------------------- +HSCRIPT CBaseFlex::ScriptGetOldestScene( void ) +{ + if ( m_SceneEvents.Count() > 0 ) + { + CSceneEventInfo curScene = m_SceneEvents.Head(); + return ToHScript( (CBaseEntity*)(curScene.m_hSceneEntity.Get()) ); + } + else + { + return NULL; + } +} + +//-------------------------------------------------------------------------------------------------- +// Returns the script instance of the scene at the specified index, or null if index >= count +//-------------------------------------------------------------------------------------------------- +HSCRIPT CBaseFlex::ScriptGetSceneByIndex( int index ) +{ + if ( m_SceneEvents.IsValidIndex( index ) ) + { + CSceneEventInfo curScene = m_SceneEvents.Element( index ); + return ToHScript( (CBaseEntity*)(curScene.m_hSceneEntity.Get()) ); + } + else + { + return NULL; + } +} // FIXME: move to CBaseActor diff --git a/sp/src/game/server/baseflex.h b/sp/src/game/server/baseflex.h index aaa03c9c..838ba89e 100644 --- a/sp/src/game/server/baseflex.h +++ b/sp/src/game/server/baseflex.h @@ -46,6 +46,8 @@ public: DECLARE_SERVERCLASS(); DECLARE_DATADESC(); DECLARE_PREDICTABLE(); + // script description + DECLARE_ENT_SCRIPTDESC(); // Construction CBaseFlex( void ); @@ -130,6 +132,10 @@ public: virtual float PlayAutoGeneratedSoundScene( const char *soundname ); #endif + // Returns the script instance of the scene entity associated with our oldest ("top level") scene event + virtual HSCRIPT ScriptGetOldestScene( void ); + virtual HSCRIPT ScriptGetSceneByIndex( int index ); + virtual int GetSpecialDSP( void ) { return 0; } protected: diff --git a/sp/src/game/server/cbase.cpp b/sp/src/game/server/cbase.cpp index 57c3f2cd..d51de2db 100644 --- a/sp/src/game/server/cbase.cpp +++ b/sp/src/game/server/cbase.cpp @@ -396,6 +396,28 @@ void CBaseEntityOutput::AddEventAction( CEventAction *pEventAction ) m_ActionList = pEventAction; } +void CBaseEntityOutput::RemoveEventAction( CEventAction *pEventAction ) +{ + CEventAction *pAction = GetActionList(); + CEventAction *pPrevAction = NULL; + while ( pAction ) + { + if ( pAction == pEventAction ) + { + if ( !pPrevAction ) + { + m_ActionList = NULL; + } + else + { + pPrevAction->m_pNext = pAction->m_pNext; + } + return; + } + pAction = pAction->m_pNext; + } +} + // save data description for the event queue BEGIN_SIMPLE_DATADESC( CBaseEntityOutput ) diff --git a/sp/src/game/server/enginecallback.h b/sp/src/game/server/enginecallback.h index cce07866..45993be9 100644 --- a/sp/src/game/server/enginecallback.h +++ b/sp/src/game/server/enginecallback.h @@ -26,6 +26,7 @@ class IDataCache; class IMDLCache; class IServerEngineTools; class IXboxSystem; +class IScriptManager; class CSteamAPIContext; class CSteamGameServerAPIContext; @@ -43,6 +44,7 @@ extern IDataCache *datacache; extern IMDLCache *mdlcache; extern IServerEngineTools *serverenginetools; extern IXboxSystem *xboxsystem; // 360 only +extern IScriptManager *scriptmanager; extern CSteamAPIContext *steamapicontext; // available on game clients extern CSteamGameServerAPIContext *steamgameserverapicontext; //available on game servers diff --git a/sp/src/game/server/entitylist.cpp b/sp/src/game/server/entitylist.cpp index 74135391..cbe944ab 100644 --- a/sp/src/game/server/entitylist.cpp +++ b/sp/src/game/server/entitylist.cpp @@ -667,7 +667,7 @@ CBaseEntity *CGlobalEntityList::FindEntityByName( CBaseEntity *pStartEntity, con continue; } - if ( !ent->m_iName ) + if ( !ent->m_iName.Get() ) continue; if ( ent->NameMatches( szName ) ) diff --git a/sp/src/game/server/entityoutput.h b/sp/src/game/server/entityoutput.h index 792ac6da..424ae761 100644 --- a/sp/src/game/server/entityoutput.h +++ b/sp/src/game/server/entityoutput.h @@ -63,6 +63,7 @@ public: void ParseEventAction( const char *EventData ); void AddEventAction( CEventAction *pEventAction ); + void RemoveEventAction( CEventAction *pEventAction ); int Save( ISave &save ); int Restore( IRestore &restore, int elementCount ); diff --git a/sp/src/game/server/env_entity_maker.cpp b/sp/src/game/server/env_entity_maker.cpp index cbbf1b0a..19d8ddf6 100644 --- a/sp/src/game/server/env_entity_maker.cpp +++ b/sp/src/game/server/env_entity_maker.cpp @@ -30,6 +30,7 @@ class CEnvEntityMaker : public CPointEntity DECLARE_CLASS( CEnvEntityMaker, CPointEntity ); public: DECLARE_DATADESC(); + DECLARE_ENT_SCRIPTDESC(); virtual void Spawn( void ); virtual void Activate( void ); @@ -43,6 +44,10 @@ public: void InputForceSpawnAtPosition( inputdata_t &inputdata ); #endif + void SpawnEntityFromScript(); + void SpawnEntityAtEntityOriginFromScript(HSCRIPT hEntity); + void SpawnEntityAtNamedEntityOriginFromScript(const char* pszName); + void SpawnEntityAtLocationFromScript(const Vector& vecAlternateOrigin, const Vector& vecAlternateAngles); private: CPointTemplate *FindTemplate(); @@ -102,6 +107,13 @@ BEGIN_DATADESC( CEnvEntityMaker ) DEFINE_THINKFUNC( CheckSpawnThink ), END_DATADESC() +BEGIN_ENT_SCRIPTDESC( CEnvEntityMaker, CBaseEntity, "env_entity_maker" ) + DEFINE_SCRIPTFUNC_NAMED( SpawnEntityFromScript, "SpawnEntity", "Create an entity at the location of the maker" ) + DEFINE_SCRIPTFUNC_NAMED( SpawnEntityAtEntityOriginFromScript, "SpawnEntityAtEntityOrigin", "Create an entity at the location of a specified entity instance" ) + DEFINE_SCRIPTFUNC_NAMED( SpawnEntityAtNamedEntityOriginFromScript, "SpawnEntityAtNamedEntityOrigin", "Create an entity at the location of a named entity" ) + DEFINE_SCRIPTFUNC_NAMED( SpawnEntityAtLocationFromScript, "SpawnEntityAtLocation", "Create an entity at a specified location and orientaton, orientation is Euler angle in degrees (pitch, yaw, roll)" ) +END_SCRIPTDESC() + LINK_ENTITY_TO_CLASS( env_entity_maker, CEnvEntityMaker ); @@ -266,8 +278,50 @@ void CEnvEntityMaker::SpawnEntity( Vector vecAlternateOrigin, QAngle vecAlternat } } #endif + + pTemplate->CreationComplete( hNewEntities ); } +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntityFromScript() +{ + SpawnEntity(); +} + +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntityAtEntityOriginFromScript( HSCRIPT hEntity ) +{ + CBaseEntity *pTargetEntity = ToEnt( hEntity ); + if ( pTargetEntity ) + { + SpawnEntity( pTargetEntity->GetAbsOrigin(), pTargetEntity->GetAbsAngles() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntityAtNamedEntityOriginFromScript( const char *pszName ) +{ + CBaseEntity *pTargetEntity = gEntList.FindEntityByName( NULL, pszName, this, NULL, NULL ); + + if( pTargetEntity ) + { + SpawnEntity( pTargetEntity->GetAbsOrigin(), pTargetEntity->GetAbsAngles() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntityAtLocationFromScript( const Vector &vecAlternateOrigin, const Vector &vecAlternateAngles ) +{ + SpawnEntity( vecAlternateOrigin, *((QAngle *)&vecAlternateAngles) ); +} //----------------------------------------------------------------------------- // Purpose: Returns whether or not the template entities can fit if spawned. diff --git a/sp/src/game/server/gameinterface.cpp b/sp/src/game/server/gameinterface.cpp index 0a91d34e..7a08f4c0 100644 --- a/sp/src/game/server/gameinterface.cpp +++ b/sp/src/game/server/gameinterface.cpp @@ -93,6 +93,9 @@ #include "world.h" #endif +#include "vscript/ivscript.h" +#include "vscript_server.h" + #ifdef TF_DLL #include "gc_clientsystem.h" @@ -185,6 +188,7 @@ IServerEngineTools *serverenginetools = NULL; ISceneFileCache *scenefilecache = NULL; IXboxSystem *xboxsystem = NULL; // Xbox 360 only IMatchmaking *matchmaking = NULL; // Xbox 360 only +IScriptManager *scriptmanager = NULL; #if defined( REPLAY_ENABLED ) IReplaySystem *g_pReplay = NULL; IServerReplayContext *g_pReplayServerContext = NULL; @@ -627,6 +631,11 @@ bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory, if ( IsX360() && (matchmaking = (IMatchmaking *)appSystemFactory( VENGINE_MATCHMAKING_VERSION, NULL )) == NULL ) return false; + if (!CommandLine()->CheckParm("-noscripting")) + { + scriptmanager = (IScriptManager*)appSystemFactory(VSCRIPT_INTERFACE_VERSION, NULL); + } + // If not running dedicated, grab the engine vgui interface if ( !engine->IsDedicatedServer() ) { @@ -684,6 +693,7 @@ bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory, g_pGameSaveRestoreBlockSet->AddBlockHandler( GetCommentarySaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEventQueueSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->AddBlockHandler( GetAchievementSaveRestoreBlockHandler() ); + g_pGameSaveRestoreBlockSet->AddBlockHandler( GetVScriptSaveRestoreBlockHandler() ); // The string system must init first + shutdown last IGameSystem::Add( GameStringSystem() ); @@ -762,6 +772,7 @@ void CServerGameDLL::DLLShutdown( void ) // Due to dependencies, these are not autogamesystems ModelSoundsCacheShutdown(); + g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetVScriptSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetAchievementSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetCommentarySaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEventQueueSaveRestoreBlockHandler() ); diff --git a/sp/src/game/server/logicentities.cpp b/sp/src/game/server/logicentities.cpp index 0f524464..4df93c86 100644 --- a/sp/src/game/server/logicentities.cpp +++ b/sp/src/game/server/logicentities.cpp @@ -27,6 +27,126 @@ extern CServerGameDLL g_ServerGameDLL; +//----------------------------------------------------------------------------- +// Purpose: An entity that acts as a container for game scripts. +//----------------------------------------------------------------------------- + +#define MAX_SCRIPT_GROUP 16 + +class CLogicScript : public CPointEntity +{ +public: + DECLARE_CLASS( CLogicScript, CPointEntity ); + DECLARE_DATADESC(); + + void RunVScripts() + { + /* + EntityGroup <- []; + function __AppendToScriptGroup( name ) + { + if ( name.len() == 0 ) + { + EntityGroup.append( null ); + } + else + { + local ent = Entities.FindByName( null, name ); + EntityGroup.append( ent ); + if ( ent != null ) + { + ent.ValidateScriptScope(); + ent.GetScriptScope().EntityGroup <- EntityGroup; + } + } + } + */ + + static const char szAddCode[] = + { + 0x45,0x6e,0x74,0x69,0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x20,0x3c,0x2d,0x20,0x5b,0x5d,0x3b,0x0d,0x0a, + 0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x5f,0x5f,0x41,0x70,0x70,0x65,0x6e,0x64,0x54,0x6f,0x53, + 0x63,0x72,0x69,0x70,0x74,0x47,0x72,0x6f,0x75,0x70,0x28,0x20,0x6e,0x61,0x6d,0x65,0x20,0x29,0x20,0x0d, + 0x0a,0x7b,0x0d,0x0a,0x09,0x69,0x66,0x20,0x28,0x20,0x6e,0x61,0x6d,0x65,0x2e,0x6c,0x65,0x6e,0x28,0x29, + 0x20,0x3d,0x3d,0x20,0x30,0x20,0x29,0x20,0x0d,0x0a,0x09,0x7b,0x20,0x0d,0x0a,0x09,0x09,0x45,0x6e,0x74, + 0x69,0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x2e,0x61,0x70,0x70,0x65,0x6e,0x64,0x28,0x20,0x6e,0x75,0x6c, + 0x6c,0x20,0x29,0x3b,0x20,0x0d,0x0a,0x09,0x7d,0x20,0x0d,0x0a,0x09,0x65,0x6c,0x73,0x65,0x0d,0x0a,0x09, + 0x7b,0x20,0x0d,0x0a,0x09,0x09,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x65,0x6e,0x74,0x20,0x3d,0x20,0x45,0x6e, + 0x74,0x69,0x74,0x69,0x65,0x73,0x2e,0x46,0x69,0x6e,0x64,0x42,0x79,0x4e,0x61,0x6d,0x65,0x28,0x20,0x6e, + 0x75,0x6c,0x6c,0x2c,0x20,0x6e,0x61,0x6d,0x65,0x20,0x29,0x3b,0x0d,0x0a,0x09,0x09,0x45,0x6e,0x74,0x69, + 0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x2e,0x61,0x70,0x70,0x65,0x6e,0x64,0x28,0x20,0x65,0x6e,0x74,0x20, + 0x29,0x3b,0x0d,0x0a,0x09,0x09,0x69,0x66,0x20,0x28,0x20,0x65,0x6e,0x74,0x20,0x21,0x3d,0x20,0x6e,0x75, + 0x6c,0x6c,0x20,0x29,0x0d,0x0a,0x09,0x09,0x7b,0x0d,0x0a,0x09,0x09,0x09,0x65,0x6e,0x74,0x2e,0x56,0x61, + 0x6c,0x69,0x64,0x61,0x74,0x65,0x53,0x63,0x72,0x69,0x70,0x74,0x53,0x63,0x6f,0x70,0x65,0x28,0x29,0x3b, + 0x0d,0x0a,0x09,0x09,0x09,0x65,0x6e,0x74,0x2e,0x47,0x65,0x74,0x53,0x63,0x72,0x69,0x70,0x74,0x53,0x63, + 0x6f,0x70,0x65,0x28,0x29,0x2e,0x45,0x6e,0x74,0x69,0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x20,0x3c,0x2d, + 0x20,0x45,0x6e,0x74,0x69,0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x3b,0x0d,0x0a,0x09,0x09,0x7d,0x0d,0x0a, + 0x09,0x7d,0x0d,0x0a,0x7d,0x0d,0x0a,0x00 + }; + + int iLastMember; + for ( iLastMember = MAX_SCRIPT_GROUP - 1; iLastMember >= 0; iLastMember-- ) + { + if ( m_iszGroupMembers[iLastMember] != NULL_STRING ) + { + break; + } + } + + if ( iLastMember >= 0 ) + { + HSCRIPT hAddScript = g_pScriptVM->CompileScript( szAddCode ); + if ( hAddScript ) + { + ValidateScriptScope(); + m_ScriptScope.Run( hAddScript ); + HSCRIPT hAddFunc = m_ScriptScope.LookupFunction( "__AppendToScriptGroup" ); + if ( hAddFunc ) + { + for ( int i = 0; i <= iLastMember; i++ ) + { + m_ScriptScope.Call( hAddFunc, NULL, STRING(m_iszGroupMembers[i]) ); + } + g_pScriptVM->ReleaseFunction( hAddFunc ); + m_ScriptScope.ClearValue( "__AppendToScriptGroup" ); + } + + g_pScriptVM->ReleaseScript( hAddScript ); + } + } + BaseClass::RunVScripts(); + } + + string_t m_iszGroupMembers[MAX_SCRIPT_GROUP]; + +}; + +LINK_ENTITY_TO_CLASS( logic_script, CLogicScript ); + +BEGIN_DATADESC( CLogicScript ) + // Silence, Classcheck! + // DEFINE_ARRAY( m_iszGroupMembers, FIELD_STRING, MAX_NUM_TEMPLATES ), + + 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_iszGroupMembers[8], FIELD_STRING, "Group08"), + DEFINE_KEYFIELD( m_iszGroupMembers[9], FIELD_STRING, "Group09"), + DEFINE_KEYFIELD( m_iszGroupMembers[10], FIELD_STRING, "Group10"), + DEFINE_KEYFIELD( m_iszGroupMembers[11], FIELD_STRING, "Group11"), + DEFINE_KEYFIELD( m_iszGroupMembers[12], FIELD_STRING, "Group12"), + DEFINE_KEYFIELD( m_iszGroupMembers[13], FIELD_STRING, "Group13"), + DEFINE_KEYFIELD( m_iszGroupMembers[14], FIELD_STRING, "Group14"), + DEFINE_KEYFIELD( m_iszGroupMembers[15], FIELD_STRING, "Group16"), + +END_DATADESC() + + //----------------------------------------------------------------------------- // Purpose: Compares a set of integer inputs to the one main input diff --git a/sp/src/game/server/monstermaker.cpp b/sp/src/game/server/monstermaker.cpp index 98f1e02f..5d56eabf 100644 --- a/sp/src/game/server/monstermaker.cpp +++ b/sp/src/game/server/monstermaker.cpp @@ -18,6 +18,8 @@ #include "IEffects.h" #include "props.h" +#include "point_template.h" + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -114,6 +116,8 @@ END_DATADESC() //----------------------------------------------------------------------------- void CBaseNPCMaker::Spawn( void ) { + ScriptInstallPreSpawnHook(); + SetSolid( SOLID_NONE ); m_nLiveChildren = 0; Precache(); @@ -830,6 +834,12 @@ void CTemplateNPCMaker::MakeNPC( void ) pent->SetAbsAngles( angles ); } + if ( !ScriptPreInstanceSpawn( &m_ScriptScope, pEntity, m_iszTemplateData ) ) + { + UTIL_RemoveImmediate( pEntity ); + return; + } + m_OnSpawnNPC.Set( pEntity, pEntity, this ); if ( m_spawnflags & SF_NPCMAKER_FADE ) @@ -867,6 +877,8 @@ void CTemplateNPCMaker::MakeNPC( void ) SetUse( NULL ); } } + + ScriptPostSpawn( &m_ScriptScope, &pEntity, 1 ); } //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/player.cpp b/sp/src/game/server/player.cpp index af5ef0cb..52cfe3cd 100644 --- a/sp/src/game/server/player.cpp +++ b/sp/src/game/server/player.cpp @@ -478,6 +478,10 @@ BEGIN_DATADESC( CBasePlayer ) // DEFINE_UTLVECTOR( m_vecPlayerSimInfo ), END_DATADESC() +BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseAnimating, "The player entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsPlayerNoclipping, "IsNoclipping", "Returns true if the player is in noclip mode." ) +END_SCRIPTDESC(); + int giPrecacheGrunt = 0; edict_t *CBasePlayer::s_PlayerEdict = NULL; @@ -690,6 +694,11 @@ CBasePlayer::~CBasePlayer( ) //----------------------------------------------------------------------------- void CBasePlayer::UpdateOnRemove( void ) { + if ( !g_pGameRules->IsMultiplayer() && g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", SCRIPT_VARIANT_NULL ); + } + VPhysicsDestroyObject(); // Remove him from his current team @@ -5110,6 +5119,11 @@ void CBasePlayer::Spawn( void ) UpdateLastKnownArea(); m_weaponFiredTimer.Invalidate(); + + if ( !g_pGameRules->IsMultiplayer() && g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } } void CBasePlayer::Activate( void ) @@ -5300,6 +5314,14 @@ void CBasePlayer::OnRestore( void ) m_nVehicleViewSavedFrame = 0; m_nBodyPitchPoseParam = LookupPoseParameter( "body_pitch" ); + + // 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_pGameRules->IsMultiplayer() && g_pScriptVM && (gpGlobals->eLoadType == MapLoad_Transition) ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } } /* void CBasePlayer::SetTeamName( const char *pTeamName ) @@ -6864,6 +6886,14 @@ void CBasePlayer::ShowCrosshair( bool bShow ) } } +//----------------------------------------------------------------------------- +// Used by vscript to determine if the player is noclipping +//----------------------------------------------------------------------------- +bool CBasePlayer::ScriptIsPlayerNoclipping(void) +{ + return (GetMoveType() == MOVETYPE_NOCLIP); +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/player.h b/sp/src/game/server/player.h index 1e547fea..261d9a26 100644 --- a/sp/src/game/server/player.h +++ b/sp/src/game/server/player.h @@ -244,6 +244,8 @@ protected: public: DECLARE_DATADESC(); DECLARE_SERVERCLASS(); + // script description + DECLARE_ENT_SCRIPTDESC(); CBasePlayer(); ~CBasePlayer(); @@ -386,6 +388,8 @@ public: void ShowViewModel( bool bShow ); void ShowCrosshair( bool bShow ); + bool ScriptIsPlayerNoclipping(void); + // View model prediction setup void CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov ); diff --git a/sp/src/game/server/point_devshot_camera.cpp b/sp/src/game/server/point_devshot_camera.cpp index fdcefa25..4d476664 100644 --- a/sp/src/game/server/point_devshot_camera.cpp +++ b/sp/src/game/server/point_devshot_camera.cpp @@ -53,24 +53,6 @@ END_DATADESC() LINK_ENTITY_TO_CLASS( point_devshot_camera, CPointDevShotCamera ); -//----------------------------------------------------------------------------- -// Purpose: Convenience function so we don't have to make this check all over -//----------------------------------------------------------------------------- -static CBasePlayer * UTIL_GetLocalPlayerOrListenServerHost( void ) -{ - if ( gpGlobals->maxClients > 1 ) - { - if ( engine->IsDedicatedServer() ) - { - return NULL; - } - - return UTIL_GetListenServerHost(); - } - - return UTIL_GetLocalPlayer(); -} - //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/point_template.cpp b/sp/src/game/server/point_template.cpp index a1af651b..940b501c 100644 --- a/sp/src/game/server/point_template.cpp +++ b/sp/src/game/server/point_template.cpp @@ -133,6 +133,8 @@ void PrecachePointTemplates() void CPointTemplate::Spawn( void ) { Precache(); + ScriptInstallPreSpawnHook(); + ValidateScriptScope(); } void CPointTemplate::Precache() @@ -381,7 +383,15 @@ bool CPointTemplate::CreateInstance( const Vector &vecOrigin, const QAngle &vecA pEntity->SetAbsOrigin( vecNewOrigin ); pEntity->SetAbsAngles( vecNewAngles ); - pSpawnList[i].m_pEntity = pEntity; + if (ScriptPreInstanceSpawn(&m_ScriptScope, pEntity, Templates_FindByIndex(iTemplateIndex))) + { + pSpawnList[i].m_pEntity = pEntity; + } + else + { + pSpawnList[i].m_pEntity = NULL; + UTIL_RemoveImmediate(pEntity); + } pSpawnList[i].m_nDepth = 0; pSpawnList[i].m_pDeferredParent = NULL; } @@ -474,6 +484,17 @@ bool CPointTemplate::CreateSpecificInstance( int iTemplate, const Vector &vecOri } #endif +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CPointTemplate::CreationComplete( const CUtlVector &entities ) +{ + if ( !entities.Count() ) + return; + + ScriptPostSpawn( &m_ScriptScope, (CBaseEntity **)entities.Base(), entities.Count() ); +} + //----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - @@ -514,3 +535,72 @@ void CPointTemplate::InputForceSpawnRandomTemplate( inputdata_t &inputdata ) m_pOutputOutEntity.Set(pEntity, pEntity, this); } #endif + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void ScriptInstallPreSpawnHook() +{ +#ifdef IS_WINDOWS_PC + if ( !g_pScriptVM->ValueExists( "__ExecutePreSpawn " ) ) + { + //g_pScriptVM->Run( g_Script_spawn_helper ); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: This function is called after a spawner creates its child entity +// but before the keyvalues are injected. This gives us an +// opportunity to change any keyvalues before the entity is +// configured and spawned. In this case, we see if there is a VScript +// that wants to change anything about this entity. +//----------------------------------------------------------------------------- +bool ScriptPreInstanceSpawn( CScriptScope *pScriptScope, CBaseEntity *pChild, string_t iszKeyValueData ) +{ + if ( !pScriptScope->IsInitialized() ) + return true; + + ScriptVariant_t result; + if ( pScriptScope->Call( "__ExecutePreSpawn", &result, ToHScript( pChild ) ) != SCRIPT_DONE ) + return true; + + if ( ( result.m_type == FIELD_BOOLEAN && !result.m_bool ) || ( result.m_type == FIELD_INTEGER && !result.m_int ) ) + return false; + + return true; + +} + +void ScriptPostSpawn( CScriptScope *pScriptScope, CBaseEntity **ppEntities, int nEntities ) +{ + if ( !pScriptScope->IsInitialized() ) + return; + + HSCRIPT hPostSpawnFunc = pScriptScope->LookupFunction( "PostSpawn" ); + + if ( !hPostSpawnFunc ) + return; + + ScriptVariant_t varEntityMakerResultTable; + if ( !g_pScriptVM->GetValue( *pScriptScope, "__EntityMakerResult", &varEntityMakerResultTable ) ) + return; + + if ( varEntityMakerResultTable.m_type != FIELD_HSCRIPT ) + return; + + HSCRIPT hEntityMakerResultTable = varEntityMakerResultTable.m_hScript; + char szEntName[256]; + for ( int i = 0; i < nEntities; i++ ) + { + V_strncpy( szEntName, ppEntities[i]->GetEntityNameAsCStr(), ARRAYSIZE(szEntName) ); + char *pAmpersand = V_strrchr( szEntName, '&' ); + if ( pAmpersand ) + *pAmpersand = 0; + g_pScriptVM->SetValue( hEntityMakerResultTable, szEntName, ToHScript( ppEntities[i] ) ); + } + pScriptScope->Call( hPostSpawnFunc, NULL, hEntityMakerResultTable ); + pScriptScope->Call( "__FinishSpawn" ); + g_pScriptVM->ReleaseValue( varEntityMakerResultTable ); + g_pScriptVM->ReleaseFunction( hPostSpawnFunc ); +} diff --git a/sp/src/game/server/point_template.h b/sp/src/game/server/point_template.h index dc60d078..b5c34d47 100644 --- a/sp/src/game/server/point_template.h +++ b/sp/src/game/server/point_template.h @@ -20,6 +20,10 @@ struct template_t DECLARE_SIMPLE_DATADESC(); }; +void ScriptInstallPreSpawnHook(); +bool ScriptPreInstanceSpawn( CScriptScope *pScriptScope, CBaseEntity *pChild, string_t iszKeyValueData ); +void ScriptPostSpawn( CScriptScope *pScriptScope, CBaseEntity **ppEntities, int nEntities ); + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -52,6 +56,7 @@ public: #ifdef MAPBASE bool CreateSpecificInstance( int iTemplate, const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity **pOutEntity ); #endif + void CreationComplete(const CUtlVector& entities); // Inputs void InputForceSpawn( inputdata_t &inputdata ); diff --git a/sp/src/game/server/sceneentity.cpp b/sp/src/game/server/sceneentity.cpp index e43cdf33..eb926a42 100644 --- a/sp/src/game/server/sceneentity.cpp +++ b/sp/src/game/server/sceneentity.cpp @@ -33,6 +33,8 @@ #include "SceneCache.h" #include "scripted.h" #include "env_debughistory.h" +#include "team.h" +#include "triggers.h" #ifdef HL2_EPISODIC #include "npc_alyx_episodic.h" @@ -323,6 +325,8 @@ public: DECLARE_CLASS( CSceneEntity, CPointEntity ); DECLARE_SERVERCLASS(); + // script description + DECLARE_ENT_SCRIPTDESC(); CSceneEntity( void ); ~CSceneEntity( void ); @@ -470,6 +474,8 @@ public: void InputScriptPlayerDeath( inputdata_t &inputdata ); + void AddBroadcastTeamTarget( int nTeamIndex ); + void RemoveBroadcastTeamTarget( int nTeamIndex ); // Data public: string_t m_iszSceneFile; @@ -543,6 +549,9 @@ public: virtual CBaseEntity *FindNamedEntity( const char *name, CBaseEntity *pActor = NULL, bool bBaseFlexOnly = false, bool bUseClear = false ); CBaseEntity *FindNamedTarget( string_t iszTarget, bool bBaseFlexOnly = false ); virtual CBaseEntity *FindNamedEntityClosest( const char *name, CBaseEntity *pActor = NULL, bool bBaseFlexOnly = false, bool bUseClear = false, const char *pszSecondary = NULL ); + HSCRIPT ScriptFindNamedEntity( const char *name ); + bool ScriptLoadSceneFromString( const char * pszFilename, const char *pszData ); + private: @@ -764,6 +773,17 @@ BEGIN_DATADESC( CSceneEntity ) DEFINE_OUTPUT( m_OnTrigger16, "OnTrigger16"), END_DATADESC() + +BEGIN_ENT_SCRIPTDESC( CSceneEntity, CBaseEntity, "Choreographed scene which controls animation and/or dialog on one or more actors." ) + DEFINE_SCRIPTFUNC( EstimateLength, "Returns length of this scene in seconds." ) + DEFINE_SCRIPTFUNC( IsPlayingBack, "If this scene is currently playing." ) + DEFINE_SCRIPTFUNC( IsPaused, "If this scene is currently paused." ) + DEFINE_SCRIPTFUNC( AddBroadcastTeamTarget, "Adds a team (by index) to the broadcast list" ) + DEFINE_SCRIPTFUNC( RemoveBroadcastTeamTarget, "Removes a team (by index) from the broadcast list" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFindNamedEntity, "FindNamedEntity", "given an entity reference, such as !target, get actual entity from scene object" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptLoadSceneFromString, "LoadSceneFromString", "given a dummy scene name and a vcd string, load the scene" ) +END_SCRIPTDESC(); + const ConVar *CSceneEntity::m_pcvSndMixahead = NULL; //----------------------------------------------------------------------------- @@ -3066,7 +3086,7 @@ void CSceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEv CBaseFlex *pActor = NULL; CChoreoActor *actor = event->GetActor(); - if ( actor ) + if ( actor && (event->GetType() != CChoreoEvent::SCRIPT) && (event->GetType() != CChoreoEvent::CAMERA) ) { pActor = FindNamedActor( actor ); if (pActor == NULL) @@ -3229,6 +3249,80 @@ void CSceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEv } } break; + + case CChoreoEvent::CAMERA: + { + // begin the camera shot + const char *pszShotType = event->GetParameters(); + + CBaseEntity *pActor1 = FindNamedEntity( event->GetParameters2( ), pActor ); + CBaseEntity *pActor2 = FindNamedEntity( event->GetParameters3( ), pActor ); + float duration = event->GetDuration(); + + // grab any camera we find in the map + // TODO: find camera that is nearest this scene entity? + CTriggerCamera *pCamera = (CTriggerCamera *)gEntList.FindEntityByClassname( NULL, "point_viewcontrol" ); + + if ( !pCamera ) + { + Warning( "CSceneEntity %s unable to find a camera (point_viewcontrol) in this map!\n", STRING(GetEntityName()) ); + } + else + { + pCamera->StartCameraShot( pszShotType, this, pActor1, pActor2, duration ); + } + } + break; + + case CChoreoEvent::SCRIPT: + { + // NOTE: this is only used by auto-generated vcds to embed script commands to map entities. + + // vscript call - param1 is entity name, param2 is function name, param3 is function parameter string + // calls a vscript function defined on the scope of the named CBaseEntity object/actor. + // script call is of the format FunctionName(pActor, pThisSceneEntity, pszScriptParameters, duration) + const char *pszActorName = event->GetParameters(); + const char *pszFunctionName = event->GetParameters2(); + const char *pszScriptParameters = event->GetParameters3(); + + float duration = event->GetDuration(); + + // TODO: should be new method CBaseEntity::CallScriptFunctionParams() + CBaseEntity *pEntity = (CBaseEntity *)gEntList.FindEntityByName( NULL, pszActorName ); + + //START_VMPROFILE + if ( !pEntity ) + { + Warning( "CSceneEntity::SCRIPT event - unable to find entity named '%s' in this map!\n", pszActorName ); + } + else + { + + if( !pEntity->ValidateScriptScope() ) + { + DevMsg("\n***\nCChoreoEvent::SCRIPT - FAILED to create private ScriptScope. ABORTING script call\n***\n"); + break; + } + + HSCRIPT hFunc = pEntity->m_ScriptScope.LookupFunction( pszFunctionName ); + + if( hFunc ) + { + pEntity->m_ScriptScope.Call( hFunc, NULL, ToHScript(this), pszScriptParameters, duration ); + pEntity->m_ScriptScope.ReleaseFunction( hFunc ); + + //UPDATE_VMPROFILE + } + else + { + Warning("CSceneEntity::SCRIPT event - '%s' entity has no script function '%s' defined!\n", pszActorName,pszFunctionName); + } + } + + } + break; + + case CChoreoEvent::FIRETRIGGER: { if ( IsMultiplayer() ) @@ -3437,6 +3531,19 @@ void CSceneEntity::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEven } } break; + + case CChoreoEvent::CAMERA: + { + // call the end of camera or call a dispatch function + } + break; + + case CChoreoEvent::SCRIPT: + { + // call the end of script or call a dispatch function + } + break; + case CChoreoEvent::SEQUENCE: { if ( pActor ) @@ -4268,6 +4375,53 @@ CBaseEntity *CSceneEntity::FindNamedEntityClosest( const char *name, CBaseEntity } +HSCRIPT CSceneEntity::ScriptFindNamedEntity(const char* name) +{ + return ToHScript(FindNamedEntity(name, NULL, false, false)); +} + +//----------------------------------------------------------------------------- +// Purpose: vscript - create a scene directly from a buffer containing +// a vcd description, and load it into the scene entity. +//----------------------------------------------------------------------------- +bool CSceneEntity::ScriptLoadSceneFromString(const char* pszFilename, const char* pszData) +{ + CChoreoScene* pScene = new CChoreoScene(NULL); + + // CSceneTokenProcessor SceneTokenProcessor; + // SceneTokenProcessor.SetBuffer( pszData ); + g_TokenProcessor.SetBuffer((char*)pszData); + + if (!pScene->ParseFromBuffer(pszFilename, &g_TokenProcessor)) //&SceneTokenProcessor ) ) + { + Warning("CSceneEntity::LoadSceneFromString: Unable to parse scene data '%s'\n", pszFilename); + delete pScene; + pScene = NULL; + } + else + { + pScene->SetPrintFunc(LocalScene_Printf); + pScene->SetEventCallbackInterface(this); + + + // precache all sounds for the newly constructed scene + PrecacheScene(pScene); + } + + if (pScene != NULL) + { + // release prior scene if present + UnloadScene(); + m_pScene = pScene; + return true; + } + else + { + return false; + } +} + + //----------------------------------------------------------------------------- // Purpose: Remove all "scene" expressions from all actors in this scene //----------------------------------------------------------------------------- @@ -4691,6 +4845,44 @@ void CSceneEntity::SetRecipientFilter( IRecipientFilter *filter ) } } +//----------------------------------------------------------------------------- +// Purpose: Adds a player (by index) to the recipient filter +//----------------------------------------------------------------------------- +void CSceneEntity::AddBroadcastTeamTarget(int nTeamIndex) +{ + if (m_pRecipientFilter == NULL) + { + CRecipientFilter filter; + SetRecipientFilter(&filter); + } + + CTeam* pTeam = GetGlobalTeam(nTeamIndex); + Assert(pTeam); + if (pTeam == NULL) + return; + + m_pRecipientFilter->AddRecipientsByTeam(pTeam); +} + +//----------------------------------------------------------------------------- +// Purpose: Removes a player (by index) from the recipient filter +//----------------------------------------------------------------------------- +void CSceneEntity::RemoveBroadcastTeamTarget(int nTeamIndex) +{ + if (m_pRecipientFilter == NULL) + { + CRecipientFilter filter; + SetRecipientFilter(&filter); + } + + CTeam* pTeam = GetGlobalTeam(nTeamIndex); + Assert(pTeam); + if (pTeam == NULL) + return; + + m_pRecipientFilter->RemoveRecipientsByTeam(pTeam); +} + //----------------------------------------------------------------------------- // Purpose: @@ -4986,6 +5178,26 @@ int GetSceneSpeechCount( char const *pszScene ) return 0; } +HSCRIPT ScriptCreateSceneEntity( const char* pszScene ) +{ + if ( IsEntityCreationAllowedInScripts() == false ) + { + Warning( "VScript error: A script attempted to create a scene entity mid-game. Entity creation from scripts is only allowed during map init.\n" ); + return NULL; + } + + g_pScriptVM->RegisterClass( GetScriptDescForClass( CSceneEntity ) ); + CSceneEntity *pScene = (CSceneEntity *)CBaseEntity::CreateNoSpawn( "logic_choreographed_scene", vec3_origin, vec3_angle ); + + if ( pScene ) + { + pScene->m_iszSceneFile = AllocPooledString( pszScene ); + DispatchSpawn( pScene ); + } + + return ToHScript( pScene ); +} + #ifdef MAPBASE CON_COMMAND(mapbase_scene_precache, "Just work") { diff --git a/sp/src/game/server/sceneentity.h b/sp/src/game/server/sceneentity.h index 9bc5b4b0..6e005a60 100644 --- a/sp/src/game/server/sceneentity.h +++ b/sp/src/game/server/sceneentity.h @@ -48,6 +48,7 @@ int GetSceneSpeechCount( char const *pszScene ); bool IsInInterruptableScenes( CBaseFlex *pActor ); void PrecacheInstancedScene( char const *pszScene ); +HSCRIPT ScriptCreateSceneEntity( char const *pszScene ); char const *GetSceneFilename( CBaseEntity *ent ); void ReloadSceneFromDisk( CBaseEntity *ent ); diff --git a/sp/src/game/server/server_base.vpc b/sp/src/game/server/server_base.vpc index 6f3abc5e..df35da1e 100644 --- a/sp/src/game/server/server_base.vpc +++ b/sp/src/game/server/server_base.vpc @@ -648,6 +648,11 @@ $Project $File "$SRCDIR\game\shared\voice_common.h" $File "$SRCDIR\game\shared\voice_gamemgr.cpp" $File "$SRCDIR\game\shared\voice_gamemgr.h" + $File "vscript_server.cpp" + $File "vscript_server.h" + $File "vscript_server.nut" + $File "$SRCDIR\game\shared\vscript_shared.cpp" + $File "$SRCDIR\game\shared\vscript_shared.h" $File "waterbullet.cpp" $File "waterbullet.h" $File "WaterLODControl.cpp" diff --git a/sp/src/game/server/triggers.cpp b/sp/src/game/server/triggers.cpp index c6722384..1cc438a3 100644 --- a/sp/src/game/server/triggers.cpp +++ b/sp/src/game/server/triggers.cpp @@ -2978,94 +2978,6 @@ void CAI_ChangeHintGroup::InputActivate( inputdata_t &inputdata ) #endif -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -class CTriggerCamera : public CBaseEntity -{ -public: - DECLARE_CLASS( CTriggerCamera, CBaseEntity ); - -#ifdef MAPBASE - CTriggerCamera(); - - void UpdateOnRemove(); -#endif - - void Spawn( void ); - bool KeyValue( const char *szKeyName, const char *szValue ); - void Enable( void ); - void Disable( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void FollowTarget( void ); -#ifdef MAPBASE - void MoveThink( void ); -#endif - void Move(void); - - // Always transmit to clients so they know where to move the view to - virtual int UpdateTransmitState(); - - DECLARE_DATADESC(); - - // Input handlers - void InputEnable( inputdata_t &inputdata ); - void InputDisable( inputdata_t &inputdata ); - -#ifdef MAPBASE - void InputSetFOV( inputdata_t &inputdata ); - void InputSetFOVRate( inputdata_t &inputdata ); -#endif - -private: - EHANDLE m_hPlayer; - EHANDLE m_hTarget; - - // used for moving the camera along a path (rail rides) - CBaseEntity *m_pPath; - string_t m_sPath; - float m_flWait; - float m_flReturnTime; - float m_flStopTime; - float m_moveDistance; - float m_targetSpeed; - float m_initialSpeed; - float m_acceleration; - float m_deceleration; - int m_state; - Vector m_vecMoveDir; - -#ifdef MAPBASE - float m_fov; - float m_fovSpeed; - - bool m_bDontSetPlayerView; -#endif - - string_t m_iszTargetAttachment; - int m_iAttachmentIndex; - bool m_bSnapToGoal; - -#if HL2_EPISODIC - bool m_bInterpolatePosition; - - // these are interpolation vars used for interpolating the camera over time - Vector m_vStartPos, m_vEndPos; - float m_flInterpStartTime; - - const static float kflPosInterpTime; // seconds -#endif - - int m_nPlayerButtons; - int m_nOldTakeDamage; - -private: - COutputEvent m_OnEndFollow; -#ifdef MAPBASE - COutputEvent m_OnStartFollow; -#endif -}; - #if HL2_EPISODIC const float CTriggerCamera::kflPosInterpTime = 2.0f; #endif @@ -3127,6 +3039,12 @@ BEGIN_DATADESC( CTriggerCamera ) END_DATADESC() +// VScript: publish class and select members to script language +BEGIN_ENT_SCRIPTDESC( CTriggerCamera, CBaseEntity, "Server-side camera entity" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFov, "GetFov", "get camera's current fov setting as integer" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetFov, "SetFov", "set camera's current fov in integer degrees and fov change rate as float" ) +END_SCRIPTDESC(); + #ifdef MAPBASE //----------------------------------------------------------------------------- // Purpose: @@ -3641,6 +3559,59 @@ void CTriggerCamera::FollowTarget( ) Move(); } +void CTriggerCamera::StartCameraShot( const char *pszShotType, CBaseEntity *pSceneEntity, CBaseEntity *pActor1, CBaseEntity *pActor2, float duration ) +{ + // called from SceneEntity in response to a CChoreoEvent::CAMERA sent from a VCD. + // talk to vscript, start a camera move + + HSCRIPT hStartCameraShot = NULL; + + // switch to this camera + // Enable(); + + // get script module associated with this ent, lookup function in module + if( m_iszVScripts != NULL_STRING ) + { + hStartCameraShot = m_ScriptScope.LookupFunction( "ScriptStartCameraShot" ); + } + + // call the script function to begin the camera move + if ( hStartCameraShot ) + { + g_pScriptVM->Call( hStartCameraShot, m_ScriptScope, true, NULL, pszShotType, ToHScript(pSceneEntity), ToHScript(pActor1), ToHScript(pActor2), duration ); + g_pScriptVM->ReleaseFunction( hStartCameraShot ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: vscript callback to get the player's fov +//----------------------------------------------------------------------------- +int CTriggerCamera::ScriptGetFov(void) +{ + if (m_hPlayer) + { + CBasePlayer* pBasePlayer = (CBasePlayer*)m_hPlayer.Get(); + int iFOV = pBasePlayer->GetFOV(); + return iFOV; + } + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: vscript callback to slam the player's fov +//----------------------------------------------------------------------------- +void CTriggerCamera::ScriptSetFov(int iFOV, float fovSpeed) +{ + if (m_hPlayer) + { + m_fov = iFOV; + m_fovSpeed = fovSpeed; + + CBasePlayer* pBasePlayer = (CBasePlayer*)m_hPlayer.Get(); + pBasePlayer->SetFOV(this, iFOV, fovSpeed); + } +} + #ifdef MAPBASE void CTriggerCamera::MoveThink() { diff --git a/sp/src/game/server/triggers.h b/sp/src/game/server/triggers.h index 9d7c1829..a1a2cdb7 100644 --- a/sp/src/game/server/triggers.h +++ b/sp/src/game/server/triggers.h @@ -244,4 +244,98 @@ public: CUtlVector m_hurtEntities; }; +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTriggerCamera : public CBaseEntity +{ +public: + DECLARE_CLASS( CTriggerCamera, CBaseEntity ); + // script description + DECLARE_ENT_SCRIPTDESC(); + +#ifdef MAPBASE + CTriggerCamera(); + + void UpdateOnRemove(); +#endif + + void Spawn( void ); + bool KeyValue( const char *szKeyName, const char *szValue ); + void Enable( void ); + void Disable( void ); + + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void FollowTarget( void ); + void StartCameraShot( const char *pszShotType, CBaseEntity *pSceneEntity, CBaseEntity *pActor1, CBaseEntity *pActor2, float duration ); + int ScriptGetFov(void); + void ScriptSetFov(int iFOV, float rate); +#ifdef MAPBASE + void MoveThink( void ); +#endif + void Move(void); + + // Always transmit to clients so they know where to move the view to + virtual int UpdateTransmitState(); + + DECLARE_DATADESC(); + + // Input handlers + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + +#ifdef MAPBASE + void InputSetFOV( inputdata_t &inputdata ); + void InputSetFOVRate( inputdata_t &inputdata ); +#endif + +private: + EHANDLE m_hPlayer; + EHANDLE m_hTarget; + + // used for moving the camera along a path (rail rides) + CBaseEntity *m_pPath; + string_t m_sPath; + float m_flWait; + float m_flReturnTime; + float m_flStopTime; + float m_moveDistance; + float m_targetSpeed; + float m_initialSpeed; + float m_acceleration; + float m_deceleration; + int m_state; + Vector m_vecMoveDir; + +#ifdef MAPBASE + float m_fov; + float m_fovSpeed; + + bool m_bDontSetPlayerView; +#endif + + string_t m_iszTargetAttachment; + int m_iAttachmentIndex; + bool m_bSnapToGoal; + +#if HL2_EPISODIC + bool m_bInterpolatePosition; + + // these are interpolation vars used for interpolating the camera over time + Vector m_vStartPos, m_vEndPos; + float m_flInterpStartTime; + + const static float kflPosInterpTime; // seconds +#endif + + int m_nPlayerButtons; + int m_nOldTakeDamage; + +private: + COutputEvent m_OnEndFollow; +#ifdef MAPBASE + COutputEvent m_OnStartFollow; +#endif +}; + #endif // TRIGGERS_H diff --git a/sp/src/game/server/util.cpp b/sp/src/game/server/util.cpp index c0e8912d..0a146700 100644 --- a/sp/src/game/server/util.cpp +++ b/sp/src/game/server/util.cpp @@ -1959,7 +1959,7 @@ extern "C" void Sys_Error( char *error, ... ) // *mapData - pointer a block of entity map data // Output : -1 if the entity was not successfully created; 0 on success //----------------------------------------------------------------------------- -int DispatchSpawn( CBaseEntity *pEntity ) +int DispatchSpawn( CBaseEntity *pEntity, bool bRunVScripts ) { if ( pEntity ) { @@ -1974,6 +1974,12 @@ int DispatchSpawn( CBaseEntity *pEntity ) //pEntity->SetAbsMins( pEntity->GetOrigin() - Vector(1,1,1) ); //pEntity->SetAbsMaxs( pEntity->GetOrigin() + Vector(1,1,1) ); + if (bRunVScripts) + { + pEntity->RunVScripts(); + pEntity->RunPrecacheScripts(); + } + #if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG) const char *pszClassname = NULL; int iClassname = ((CEntityFactoryDictionary*)EntityFactoryDictionary())->m_Factories.Find( pEntity->GetClassname() ); @@ -2040,6 +2046,11 @@ int DispatchSpawn( CBaseEntity *pEntity ) } gEntList.NotifySpawn( pEntity ); + + if( bRunVScripts ) + { + pEntity->RunOnPostSpawnScripts(); + } } return 0; diff --git a/sp/src/game/server/util.h b/sp/src/game/server/util.h index d13cb3a7..9f356891 100644 --- a/sp/src/game/server/util.h +++ b/sp/src/game/server/util.h @@ -237,6 +237,24 @@ CBasePlayer* UTIL_GetLocalPlayer( void ); // get the local player on a listen server CBasePlayer *UTIL_GetListenServerHost( void ); +//----------------------------------------------------------------------------- +// Purpose: Convenience function so we don't have to make this check all over +//----------------------------------------------------------------------------- +static CBasePlayer * UTIL_GetLocalPlayerOrListenServerHost( void ) +{ + if ( gpGlobals->maxClients > 1 ) + { + if ( engine->IsDedicatedServer() ) + { + return NULL; + } + + return UTIL_GetListenServerHost(); + } + + return UTIL_GetLocalPlayer(); +} + CBasePlayer* UTIL_PlayerByUserId( int userID ); CBasePlayer* UTIL_PlayerByName( const char *name ); // not case sensitive diff --git a/sp/src/game/server/vscript_server.cpp b/sp/src/game/server/vscript_server.cpp new file mode 100644 index 00000000..259a3d52 --- /dev/null +++ b/sp/src/game/server/vscript_server.cpp @@ -0,0 +1,787 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "vscript_server.h" +#include "icommandline.h" +#include "tier1/utlbuffer.h" +#include "tier1/fmtstr.h" +#include "filesystem.h" +#include "eventqueue.h" +#include "characterset.h" +#include "sceneentity.h" // for exposing scene precache function +#include "isaverestore.h" +#include "gamerules.h" +#ifdef _WIN32 +//#include "vscript_server_nut.h" +#endif + +extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); + +// #define VMPROFILE 1 + +#ifdef VMPROFILE + +#define VMPROF_START float debugStartTime = Plat_FloatTime(); +#define VMPROF_SHOW( funcname, funcdesc ) DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", (##funcname), (##funcdesc), (Plat_FloatTime() - debugStartTime)*1000.0 ); + +#else // !VMPROFILE + +#define VMPROF_START +#define VMPROF_SHOW + +#endif // VMPROFILE + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +class CScriptEntityIterator +{ +public: + HSCRIPT First() { return Next(NULL); } + + HSCRIPT Next( HSCRIPT hStartEntity ) + { + return ToHScript( gEntList.NextEnt( ToEnt( hStartEntity ) ) ); + } + + HSCRIPT CreateByClassname( const char *className ) + { + return ToHScript( CreateEntityByName( className ) ); + } + + HSCRIPT FindByClassname( HSCRIPT hStartEntity, const char *szName ) + { + return ToHScript( gEntList.FindEntityByClassname( ToEnt( hStartEntity ), szName ) ); + } + + HSCRIPT FindByName( HSCRIPT hStartEntity, const char *szName ) + { + return ToHScript( gEntList.FindEntityByName( ToEnt( hStartEntity ), szName ) ); + } + + HSCRIPT FindInSphere( HSCRIPT hStartEntity, const Vector &vecCenter, float flRadius ) + { + return ToHScript( gEntList.FindEntityInSphere( ToEnt( hStartEntity ), vecCenter, flRadius ) ); + } + + HSCRIPT FindByTarget( HSCRIPT hStartEntity, const char *szName ) + { + return ToHScript( gEntList.FindEntityByTarget( ToEnt( hStartEntity ), szName ) ); + } + + HSCRIPT FindByModel( HSCRIPT hStartEntity, const char *szModelName ) + { + return ToHScript( gEntList.FindEntityByModel( ToEnt( hStartEntity ), szModelName ) ); + } + + HSCRIPT FindByNameNearest( const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByNameNearest( szName, vecSrc, flRadius ) ); + } + + HSCRIPT FindByNameWithin( HSCRIPT hStartEntity, const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByNameWithin( ToEnt( hStartEntity ), szName, vecSrc, flRadius ) ); + } + + HSCRIPT FindByClassnameNearest( const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByClassnameNearest( szName, vecSrc, flRadius ) ); + } + + HSCRIPT FindByClassnameWithin( HSCRIPT hStartEntity , const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByClassnameWithin( ToEnt( hStartEntity ), szName, vecSrc, flRadius ) ); + } + +private: +} g_ScriptEntityIterator; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptEntityIterator, "CEntities", SCRIPT_SINGLETON "The global list of entities" ) + DEFINE_SCRIPTFUNC( First, "Begin an iteration over the list of entities" ) + DEFINE_SCRIPTFUNC( Next, "Continue an iteration over the list of entities, providing reference to a previously found entity" ) + DEFINE_SCRIPTFUNC( CreateByClassname, "Creates an entity by classname" ) + DEFINE_SCRIPTFUNC( FindByClassname, "Find entities by class name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByName, "Find entities by name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindInSphere, "Find entities within a radius. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByTarget, "Find entities by targetname. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByModel, "Find entities by model name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByNameNearest, "Find entities by name nearest to a point." ) + DEFINE_SCRIPTFUNC( FindByNameWithin, "Find entities by name within a radius. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByClassnameNearest, "Find entities by class name nearest to a point." ) + DEFINE_SCRIPTFUNC( FindByClassnameWithin, "Find entities by class name within a radius. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) +END_SCRIPTDESC(); + +// ---------------------------------------------------------------------------- +// KeyValues access - CBaseEntity::ScriptGetKeyFromModel returns root KeyValues +// ---------------------------------------------------------------------------- + +BEGIN_SCRIPTDESC_ROOT( CScriptKeyValues, "Wrapper class over KeyValues instance" ) + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPTFUNC_NAMED( ScriptFindKey, "FindKey", "Given a KeyValues object and a key name, find a KeyValues object associated with the key name" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFirstSubKey, "GetFirstSubKey", "Given a KeyValues object, return the first sub key object" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNextKey, "GetNextKey", "Given a KeyValues object, return the next key object in a sub key group" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueInt, "GetKeyInt", "Given a KeyValues object and a key name, return associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueFloat, "GetKeyFloat", "Given a KeyValues object and a key name, return associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueBool, "GetKeyBool", "Given a KeyValues object and a key name, return associated bool value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueString, "GetKeyString", "Given a KeyValues object and a key name, return associated string value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptIsKeyValueEmpty, "IsKeyEmpty", "Given a KeyValues object and a key name, return true if key name has no value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptReleaseKeyValues, "ReleaseKeyValues", "Given a root KeyValues object, release its contents" ); +END_SCRIPTDESC(); + +HSCRIPT CScriptKeyValues::ScriptFindKey( const char *pszName ) +{ + KeyValues *pKeyValues = m_pKeyValues->FindKey(pszName); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +HSCRIPT CScriptKeyValues::ScriptGetFirstSubKey( void ) +{ + KeyValues *pKeyValues = m_pKeyValues->GetFirstSubKey(); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +HSCRIPT CScriptKeyValues::ScriptGetNextKey( void ) +{ + KeyValues *pKeyValues = m_pKeyValues->GetNextKey(); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +int CScriptKeyValues::ScriptGetKeyValueInt( const char *pszName ) +{ + int i = m_pKeyValues->GetInt( pszName ); + return i; +} + +float CScriptKeyValues::ScriptGetKeyValueFloat( const char *pszName ) +{ + float f = m_pKeyValues->GetFloat( pszName ); + return f; +} + +const char *CScriptKeyValues::ScriptGetKeyValueString( const char *pszName ) +{ + const char *psz = m_pKeyValues->GetString( pszName ); + return psz; +} + +bool CScriptKeyValues::ScriptIsKeyValueEmpty( const char *pszName ) +{ + bool b = m_pKeyValues->IsEmpty( pszName ); + return b; +} + +bool CScriptKeyValues::ScriptGetKeyValueBool( const char *pszName ) +{ + bool b = m_pKeyValues->GetBool( pszName ); + return b; +} + +void CScriptKeyValues::ScriptReleaseKeyValues( ) +{ + m_pKeyValues->deleteThis(); + m_pKeyValues = NULL; +} + + +// constructors +CScriptKeyValues::CScriptKeyValues( KeyValues *pKeyValues = NULL ) +{ + m_pKeyValues = pKeyValues; +} + +// destructor +CScriptKeyValues::~CScriptKeyValues( ) +{ + if (m_pKeyValues) + { + m_pKeyValues->deleteThis(); + } + m_pKeyValues = NULL; +} + + + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static float Time() +{ + return gpGlobals->curtime; +} + +static float FrameTime() +{ + return gpGlobals->frametime; +} + +static void SendToConsole( const char *pszCommand ) +{ + CBasePlayer *pPlayer = UTIL_GetLocalPlayerOrListenServerHost(); + if ( !pPlayer ) + { + DevMsg ("Cannot execute \"%s\", no player\n", pszCommand ); + return; + } + + engine->ClientCommand( pPlayer->edict(), pszCommand ); +} + +static const char *GetMapName() +{ + return STRING( gpGlobals->mapname ); +} + +static const char *DoUniqueString( const char *pszBase ) +{ + static char szBuf[512]; + g_pScriptVM->GenerateUniqueKey( pszBase, szBuf, ARRAYSIZE(szBuf) ); + return szBuf; +} + +static void DoEntFire( const char *pszTarget, const char *pszAction, const char *pszValue, float delay, HSCRIPT hActivator, HSCRIPT hCaller ) +{ + const char *target = "", *action = "Use"; + variant_t value; + + target = STRING( AllocPooledString( pszTarget ) ); + + // Don't allow them to run anything on a point_servercommand unless they're the host player. Otherwise they can ent_fire + // and run any command on the server. Admittedly, they can only do the ent_fire if sv_cheats is on, but + // people complained about users resetting the rcon password if the server briefly turned on cheats like this: + // give point_servercommand + // ent_fire point_servercommand command "rcon_password mynewpassword" + if ( gpGlobals->maxClients > 1 && V_stricmp( target, "point_servercommand" ) == 0 ) + { + return; + } + + if ( *pszAction ) + { + action = STRING( AllocPooledString( pszAction ) ); + } + if ( *pszValue ) + { + value.SetString( AllocPooledString( pszValue ) ); + } + if ( delay < 0 ) + { + delay = 0; + } + + g_EventQueue.AddEvent( target, action, value, delay, ToEnt(hActivator), ToEnt(hCaller) ); +} + + +bool DoIncludeScript( const char *pszScript, HSCRIPT hScope ) +{ + if ( !VScriptRunScript( pszScript, hScope, true ) ) + { + g_pScriptVM->RaiseException( CFmtStr( "Failed to include script \"%s\"", ( pszScript ) ? pszScript : "unknown" ) ); + return false; + } + return true; +} + +HSCRIPT CreateProp( const char *pszEntityName, const Vector &vOrigin, const char *pszModelName, int iAnim ) +{ + CBaseAnimating *pBaseEntity = (CBaseAnimating *)CreateEntityByName( pszEntityName ); + pBaseEntity->SetAbsOrigin( vOrigin ); + pBaseEntity->SetModel( pszModelName ); + pBaseEntity->SetPlaybackRate( 1.0f ); + + int iSequence = pBaseEntity->SelectWeightedSequence( (Activity)iAnim ); + + if ( iSequence != -1 ) + { + pBaseEntity->SetSequence( iSequence ); + } + + return ToHScript( pBaseEntity ); +} + +//-------------------------------------------------------------------------------------------------- +// Use an entity's script instance to add an entity IO event (used for firing events on unnamed entities from vscript) +//-------------------------------------------------------------------------------------------------- +static void DoEntFireByInstanceHandle( HSCRIPT hTarget, const char *pszAction, const char *pszValue, float delay, HSCRIPT hActivator, HSCRIPT hCaller ) +{ + const char *action = "Use"; + variant_t value; + + if ( *pszAction ) + { + action = STRING( AllocPooledString( pszAction ) ); + } + if ( *pszValue ) + { + value.SetString( AllocPooledString( pszValue ) ); + } + if ( delay < 0 ) + { + delay = 0; + } + + CBaseEntity* pTarget = ToEnt(hTarget); + + if ( !pTarget ) + { + Warning( "VScript error: DoEntFire was passed an invalid entity instance.\n" ); + return; + } + + g_EventQueue.AddEvent( pTarget, action, value, delay, ToEnt(hActivator), ToEnt(hCaller) ); +} + +static float ScriptTraceLine( const Vector &vecStart, const Vector &vecEnd, HSCRIPT entIgnore ) +{ + // UTIL_TraceLine( vecAbsStart, vecAbsEnd, MASK_BLOCKLOS, pLooker, COLLISION_GROUP_NONE, ptr ); + trace_t tr; + CBaseEntity *pLooker = ToEnt(entIgnore); + UTIL_TraceLine( vecStart, vecEnd, MASK_NPCWORLDSTATIC, pLooker, COLLISION_GROUP_NONE, &tr); + if (tr.fractionleftsolid && tr.startsolid) + { + return 1.0 - tr.fractionleftsolid; + } + else + { + return tr.fraction; + } +} + +bool VScriptServerInit() +{ + VMPROF_START + + if( scriptmanager != NULL ) + { + ScriptLanguage_t scriptLanguage = SL_DEFAULT; + + char const *pszScriptLanguage; + if ( CommandLine()->CheckParm( "-scriptlang", &pszScriptLanguage ) ) + { + if( !Q_stricmp(pszScriptLanguage, "gamemonkey") ) + { + scriptLanguage = SL_GAMEMONKEY; + } + else if( !Q_stricmp(pszScriptLanguage, "squirrel") ) + { + scriptLanguage = SL_SQUIRREL; + } + else if( !Q_stricmp(pszScriptLanguage, "python") ) + { + scriptLanguage = SL_PYTHON; + } + else + { + DevWarning("-server_script does not recognize a language named '%s'. virtual machine did NOT start.\n", pszScriptLanguage ); + scriptLanguage = SL_NONE; + } + + } + if( scriptLanguage != SL_NONE ) + { + if ( g_pScriptVM == NULL ) + g_pScriptVM = scriptmanager->CreateVM( scriptLanguage ); + + if( g_pScriptVM ) + { + Log( "VSCRIPT: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); + ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_ShowMessageAll, "ShowMessage", "Print a hud message on all clients" ); + + ScriptRegisterFunction( g_pScriptVM, SendToConsole, "Send a string to the console as a command" ); + ScriptRegisterFunction( g_pScriptVM, GetMapName, "Get the name of the map."); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptTraceLine, "TraceLine", "given 2 points & ent to ignore, return fraction along line that hits world or models" ); + + ScriptRegisterFunction( g_pScriptVM, Time, "Get the current server time" ); + ScriptRegisterFunction( g_pScriptVM, FrameTime, "Get the time spent on the server in the last frame" ); + ScriptRegisterFunction( g_pScriptVM, DoEntFire, SCRIPT_ALIAS( "EntFire", "Generate and entity i/o event" ) ); + ScriptRegisterFunctionNamed( g_pScriptVM, DoEntFireByInstanceHandle, "EntFireByHandle", "Generate and entity i/o event. First parameter is an entity instance." ); + ScriptRegisterFunction( g_pScriptVM, DoUniqueString, SCRIPT_ALIAS( "UniqueString", "Generate a string guaranteed to be unique across the life of the script VM, with an optional root string. Useful for adding data to tables when not sure what keys are already in use in that table." ) ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCreateSceneEntity, "CreateSceneEntity", "Create a scene entity to play the specified scene." ); + ScriptRegisterFunctionNamed( g_pScriptVM, NDebugOverlay::Box, "DebugDrawBox", "Draw a debug overlay box" ); + ScriptRegisterFunctionNamed( g_pScriptVM, NDebugOverlay::Line, "DebugDrawLine", "Draw a debug overlay box" ); + ScriptRegisterFunction( g_pScriptVM, DoIncludeScript, "Execute a script (internal)" ); + ScriptRegisterFunction( g_pScriptVM, CreateProp, "Create a physics prop" ); + + + if ( GameRules() ) + { + GameRules()->RegisterScriptFunctions(); + } + + g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); + + if ( scriptLanguage == SL_SQUIRREL ) + { + //g_pScriptVM->Run( g_Script_vscript_server ); + } + + VScriptRunScript( "mapspawn", false ); + + VMPROF_SHOW( pszScriptLanguage, "virtual machine startup" ); + + return true; + } + else + { + DevWarning("VM Did not start!\n"); + } + } + } + else + { + Log( "\nVSCRIPT: Scripting is disabled.\n" ); + } + g_pScriptVM = NULL; + return false; +} + +void VScriptServerTerm() +{ + if( g_pScriptVM != NULL ) + { + if( g_pScriptVM ) + { + scriptmanager->DestroyVM( g_pScriptVM ); + g_pScriptVM = NULL; + } + } +} + + +bool VScriptServerReplaceClosures( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return false; + } + + HSCRIPT hReplaceClosuresFunc = g_pScriptVM->LookupFunction( "__ReplaceClosures" ); + if ( !hReplaceClosuresFunc ) + { + return false; + } + HSCRIPT hNewScript = VScriptCompileScript( pszScriptName, bWarnMissing ); + if ( !hNewScript ) + { + return false; + } + + g_pScriptVM->Call( hReplaceClosuresFunc, NULL, true, NULL, hNewScript, hScope ); + return true; +} + +CON_COMMAND( script_reload_code, "Execute a vscript file, replacing existing functions with the functions in the run script" ) +{ + if ( !*args[1] ) + { + Warning( "No script specified\n" ); + return; + } + + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + + VScriptServerReplaceClosures( args[1], NULL, true ); +} + +CON_COMMAND( script_reload_entity_code, "Execute all of this entity's VScripts, replacing existing functions with the functions in the run scripts" ) +{ + extern CBaseEntity *GetNextCommandEntity( CBasePlayer *pPlayer, const char *name, CBaseEntity *ent ); + + const char *pszTarget = ""; + if ( *args[1] ) + { + pszTarget = args[1]; + } + + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + + CBasePlayer *pPlayer = UTIL_GetCommandClient(); + if ( !pPlayer ) + return; + + CBaseEntity *pEntity = NULL; + while ( (pEntity = GetNextCommandEntity( pPlayer, pszTarget, pEntity )) != NULL ) + { + if ( pEntity->m_ScriptScope.IsInitialized() && pEntity->m_iszVScripts != NULL_STRING ) + { + char szScriptsList[255]; + Q_strcpy( szScriptsList, STRING(pEntity->m_iszVScripts) ); + CUtlStringList szScripts; + V_SplitString( szScriptsList, " ", szScripts); + + for( int i = 0 ; i < szScripts.Count() ; i++ ) + { + VScriptServerReplaceClosures( szScripts[i], pEntity->m_ScriptScope, true ); + } + } + } +} + +CON_COMMAND( script_reload_think, "Execute an activation script, replacing existing functions with the functions in the run script" ) +{ + extern CBaseEntity *GetNextCommandEntity( CBasePlayer *pPlayer, const char *name, CBaseEntity *ent ); + + const char *pszTarget = ""; + if ( *args[1] ) + { + pszTarget = args[1]; + } + + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + + CBasePlayer *pPlayer = UTIL_GetCommandClient(); + if ( !pPlayer ) + return; + + CBaseEntity *pEntity = NULL; + while ( (pEntity = GetNextCommandEntity( pPlayer, pszTarget, pEntity )) != NULL ) + { + if ( pEntity->m_ScriptScope.IsInitialized() && pEntity->m_iszScriptThinkFunction != NULL_STRING ) + { + VScriptServerReplaceClosures( STRING(pEntity->m_iszScriptThinkFunction), pEntity->m_ScriptScope, true ); + } + } +} + +class CVScriptGameSystem : public CAutoGameSystemPerFrame +{ +public: + // Inherited from IAutoServerSystem + virtual void LevelInitPreEntity( void ) + { + m_bAllowEntityCreationInScripts = true; + VScriptServerInit(); + } + + virtual void LevelInitPostEntity( void ) + { + m_bAllowEntityCreationInScripts = false; + } + + virtual void LevelShutdownPostEntity( void ) + { + VScriptServerTerm(); + } + + virtual void FrameUpdatePostEntityThink() + { + if ( g_pScriptVM ) + g_pScriptVM->Frame( gpGlobals->frametime ); + } + + bool m_bAllowEntityCreationInScripts; +}; + +CVScriptGameSystem g_VScriptGameSystem; + +bool IsEntityCreationAllowedInScripts( void ) +{ + return g_VScriptGameSystem.m_bAllowEntityCreationInScripts; +} + +static short VSCRIPT_SERVER_SAVE_RESTORE_VERSION = 2; + + +//----------------------------------------------------------------------------- + +class CVScriptSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler +{ +public: + CVScriptSaveRestoreBlockHandler() : + m_InstanceMap( DefLessFunc(const char *) ) + { + } + const char *GetBlockName() + { + return "VScriptServer"; + } + + //--------------------------------- + + void Save( ISave *pSave ) + { + pSave->StartBlock(); + + int temp = g_pScriptVM != NULL; + pSave->WriteInt( &temp ); + if ( g_pScriptVM ) + { + temp = g_pScriptVM->GetLanguage(); + pSave->WriteInt( &temp ); + CUtlBuffer buffer; + g_pScriptVM->WriteState( &buffer ); + temp = buffer.TellPut(); + pSave->WriteInt( &temp ); + if ( temp > 0 ) + { + pSave->WriteData( (const char *)buffer.Base(), temp ); + } + } + + pSave->EndBlock(); + } + + //--------------------------------- + + void WriteSaveHeaders( ISave *pSave ) + { + pSave->WriteShort( &VSCRIPT_SERVER_SAVE_RESTORE_VERSION ); + } + + //--------------------------------- + + void ReadRestoreHeaders( IRestore *pRestore ) + { + // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so. + short version; + pRestore->ReadShort( &version ); + m_fDoLoad = ( version == VSCRIPT_SERVER_SAVE_RESTORE_VERSION ); + } + + //--------------------------------- + + void Restore( IRestore *pRestore, bool createPlayers ) + { + if ( !m_fDoLoad && g_pScriptVM ) + { + return; + } + CBaseEntity *pEnt = gEntList.FirstEnt(); + while ( pEnt ) + { + if ( pEnt->m_iszScriptId != NULL_STRING ) + { + g_pScriptVM->RegisterClass( pEnt->GetScriptDesc() ); + m_InstanceMap.Insert( STRING( pEnt->m_iszScriptId ), pEnt ); + } + pEnt = gEntList.NextEnt( pEnt ); + } + + pRestore->StartBlock(); + if ( pRestore->ReadInt() && pRestore->ReadInt() == g_pScriptVM->GetLanguage() ) + { + int nBytes = pRestore->ReadInt(); + if ( nBytes > 0 ) + { + CUtlBuffer buffer; + buffer.EnsureCapacity( nBytes ); + pRestore->ReadData( (char *)buffer.AccessForDirectRead( nBytes ), nBytes, 0 ); + g_pScriptVM->ReadState( &buffer ); + } + } + pRestore->EndBlock(); + } + + void PostRestore( void ) + { + for ( int i = m_InstanceMap.FirstInorder(); i != m_InstanceMap.InvalidIndex(); i = m_InstanceMap.NextInorder( i ) ) + { + CBaseEntity *pEnt = m_InstanceMap[i]; + if ( pEnt->m_hScriptInstance ) + { + ScriptVariant_t variant; + if ( g_pScriptVM->GetValue( STRING(pEnt->m_iszScriptId), &variant ) && variant.m_type == FIELD_HSCRIPT ) + { + pEnt->m_ScriptScope.Init( variant.m_hScript, false ); + pEnt->RunPrecacheScripts(); + } + } + else + { + // Script system probably has no internal references + pEnt->m_iszScriptId = NULL_STRING; + } + } + m_InstanceMap.Purge(); + } + + + CUtlMap m_InstanceMap; + +private: + bool m_fDoLoad; +}; + +//----------------------------------------------------------------------------- + +CVScriptSaveRestoreBlockHandler g_VScriptSaveRestoreBlockHandler; + +//------------------------------------- + +ISaveRestoreBlockHandler *GetVScriptSaveRestoreBlockHandler() +{ + return &g_VScriptSaveRestoreBlockHandler; +} + +//----------------------------------------------------------------------------- + +bool CBaseEntityScriptInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) +{ + CBaseEntity *pEntity = (CBaseEntity *)p; + if ( pEntity->GetEntityName() != NULL_STRING ) + { + V_snprintf( pBuf, bufSize, "([%d] %s: %s)", pEntity->entindex(), STRING(pEntity->m_iClassname), STRING( pEntity->GetEntityName() ) ); + } + else + { + V_snprintf( pBuf, bufSize, "([%d] %s)", pEntity->entindex(), STRING(pEntity->m_iClassname) ); + } + return true; +} + +void *CBaseEntityScriptInstanceHelper::BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) +{ + int iEntity = g_VScriptSaveRestoreBlockHandler.m_InstanceMap.Find( pszId ); + if ( iEntity != g_VScriptSaveRestoreBlockHandler.m_InstanceMap.InvalidIndex() ) + { + CBaseEntity *pEnt = g_VScriptSaveRestoreBlockHandler.m_InstanceMap[iEntity]; + pEnt->m_hScriptInstance = hInstance; + return pEnt; + } + return NULL; +} + + +CBaseEntityScriptInstanceHelper g_BaseEntityScriptInstanceHelper; + + diff --git a/sp/src/game/server/vscript_server.h b/sp/src/game/server/vscript_server.h new file mode 100644 index 00000000..83108c33 --- /dev/null +++ b/sp/src/game/server/vscript_server.h @@ -0,0 +1,57 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_SERVER_H +#define VSCRIPT_SERVER_H + +#include "vscript/ivscript.h" +#include "tier1/KeyValues.h" +#include "vscript_shared.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +class ISaveRestoreBlockHandler; + +bool VScriptServerReplaceClosures( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing = false ); +ISaveRestoreBlockHandler *GetVScriptSaveRestoreBlockHandler(); + + +class CBaseEntityScriptInstanceHelper : public IScriptInstanceHelper +{ + bool ToString( void *p, char *pBuf, int bufSize ); + void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ); +}; + +extern CBaseEntityScriptInstanceHelper g_BaseEntityScriptInstanceHelper; + +// Only allow scripts to create entities during map initialization +bool IsEntityCreationAllowedInScripts( void ); + +// ---------------------------------------------------------------------------- +// KeyValues access +// ---------------------------------------------------------------------------- +class CScriptKeyValues +{ +public: + CScriptKeyValues( KeyValues *pKeyValues ); + ~CScriptKeyValues( ); + + HSCRIPT ScriptFindKey( const char *pszName ); + HSCRIPT ScriptGetFirstSubKey( void ); + HSCRIPT ScriptGetNextKey( void ); + int ScriptGetKeyValueInt( const char *pszName ); + float ScriptGetKeyValueFloat( const char *pszName ); + const char *ScriptGetKeyValueString( const char *pszName ); + bool ScriptIsKeyValueEmpty( const char *pszName ); + bool ScriptGetKeyValueBool( const char *pszName ); + void ScriptReleaseKeyValues( ); + + KeyValues *m_pKeyValues; // actual KeyValue entity +}; + +#endif // VSCRIPT_SERVER_H diff --git a/sp/src/game/server/vscript_server.nut b/sp/src/game/server/vscript_server.nut new file mode 100644 index 00000000..5f6c285a --- /dev/null +++ b/sp/src/game/server/vscript_server.nut @@ -0,0 +1,127 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +function UniqueString( string = "" ) +{ + return DoUniqueString( string.tostring() ); +} + +function EntFire( target, action, value = null, delay = 0.0, activator = null ) +{ + if ( !value ) + { + value = ""; + } + + local caller = null; + if ( "self" in this ) + { + caller = self; + if ( !activator ) + { + activator = self; + } + } + + DoEntFire( target.tostring(), action.tostring(), value.tostring(), delay, activator, caller ); +} + +function __ReplaceClosures( script, scope ) +{ + if ( !scope ) + { + scope = getroottable(); + } + + local tempParent = { getroottable = function() { return null; } }; + local temp = { runscript = script }; + delegate tempParent : temp; + + temp.runscript() + foreach( key,val in temp ) + { + if ( typeof(val) == "function" && key != "runscript" ) + { + printl( " Replacing " + key ); + scope[key] <- val; + } + } +} + +__OutputsPattern <- regexp("^On.*Output$"); + +function ConnectOutputs( table ) +{ + const nCharsToStrip = 6; + foreach( key, val in table ) + { + if ( typeof( val ) == "function" && __OutputsPattern.match( key ) ) + { + //printl(key.slice( 0, nCharsToStrip ) ); + table.self.ConnectOutput( key.slice( 0, key.len() - nCharsToStrip ), key ); + } + } +} + +function IncludeScript( name, scope = null ) +{ + if ( scope == null ) + { + scope = this; + } + return ::DoIncludeScript( name, scope ); +} + +//--------------------------------------------------------- +// Text dump this scope's contents to the console. +//--------------------------------------------------------- +function __DumpScope( depth, table ) +{ + local indent=function( count ) + { + local i; + for( i = 0 ; i < count ; i++ ) + { + print(" "); + } + } + + foreach(key, value in table) + { + indent(depth); + print( key ); + switch (type(value)) + { + case "table": + print("(TABLE)\n"); + indent(depth); + print("{\n"); + __DumpScope( depth + 1, value); + indent(depth); + print("}"); + break; + case "array": + print("(ARRAY)\n"); + indent(depth); + print("[\n") + __DumpScope( depth + 1, value); + indent(depth); + print("]"); + break; + case "string": + print(" = \""); + print(value); + print("\""); + break; + default: + print(" = "); + print(value); + break; + } + print("\n"); + } +} + diff --git a/sp/src/game/shared/SoundEmitterSystem.cpp b/sp/src/game/shared/SoundEmitterSystem.cpp index 09a568f8..57e823d1 100644 --- a/sp/src/game/shared/SoundEmitterSystem.cpp +++ b/sp/src/game/shared/SoundEmitterSystem.cpp @@ -1217,6 +1217,19 @@ void CBaseEntity::EmitSound( const char *soundname, HSOUNDSCRIPTHANDLE& handle, EmitSound( filter, entindex(), params, handle ); } +#if !defined ( CLIENT_DLL ) +void CBaseEntity::ScriptEmitSound( const char *soundname ) +{ + EmitSound( soundname ); +} + +float CBaseEntity::ScriptSoundDuration( const char *soundname, const char *actormodel ) +{ + float duration = CBaseEntity::GetSoundDuration( soundname, actormodel ); + return duration; +} +#endif // !CLIENT + //----------------------------------------------------------------------------- // Purpose: // Input : filter - @@ -1486,6 +1499,14 @@ HSOUNDSCRIPTHANDLE CBaseEntity::PrecacheScriptSound( const char *soundname ) #endif } +#if !defined ( CLIENT_DLL ) +// Same as server version of above, but signiture changed so it can be deduced by the macros +void CBaseEntity::VScriptPrecacheScriptSound(const char* soundname) +{ + g_SoundEmitterSystem.PrecacheScriptSound(soundname); +} +#endif // !CLIENT_DLL + void CBaseEntity::PrefetchScriptSound( const char *soundname ) { g_SoundEmitterSystem.PrefetchScriptSound( soundname ); diff --git a/sp/src/game/shared/baseentity_shared.h b/sp/src/game/shared/baseentity_shared.h index b5c95ba6..85a0ffd8 100644 --- a/sp/src/game/shared/baseentity_shared.h +++ b/sp/src/game/shared/baseentity_shared.h @@ -68,6 +68,9 @@ enum InvalidatePhysicsBits_t #endif +#include "vscript/ivscript.h" +#include "vscript_shared.h" + #if !defined( NO_ENTITY_PREDICTION ) // CBaseEntity inlines inline bool CBaseEntity::IsPlayerSimulated( void ) const @@ -247,6 +250,17 @@ inline bool CBaseEntity::IsEffectActive( int nEffects ) const return (m_fEffects & nEffects) != 0; } +inline HSCRIPT ToHScript(CBaseEntity* pEnt) +{ + return (pEnt) ? pEnt->GetScriptInstance() : NULL; +} + +template <> ScriptClassDesc_t* GetScriptDesc(CBaseEntity*); +inline CBaseEntity* ToEnt(HSCRIPT hScript) +{ + return (hScript) ? (CBaseEntity*)g_pScriptVM->GetInstanceValue(hScript, GetScriptDescForClass(CBaseEntity)) : NULL; +} + // Shared EntityMessage between game and client .dlls #define BASEENTITY_MSG_REMOVE_DECALS 1 diff --git a/sp/src/game/shared/choreoevent.cpp b/sp/src/game/shared/choreoevent.cpp index 50755f04..1623a37d 100644 --- a/sp/src/game/shared/choreoevent.cpp +++ b/sp/src/game/shared/choreoevent.cpp @@ -2064,6 +2064,8 @@ static EventNameMap_t g_NameMap[] = { CChoreoEvent::STOPPOINT, "stoppoint" }, { CChoreoEvent::PERMIT_RESPONSES, "permitresponses" }, { CChoreoEvent::GENERIC, "generic" }, + { CChoreoEvent::CAMERA, "camera" }, + { CChoreoEvent::SCRIPT, "script" }, }; //----------------------------------------------------------------------------- diff --git a/sp/src/game/shared/choreoevent.h b/sp/src/game/shared/choreoevent.h index ad4828d3..64db52af 100644 --- a/sp/src/game/shared/choreoevent.h +++ b/sp/src/game/shared/choreoevent.h @@ -307,6 +307,12 @@ public: // A string passed to the game code for interpretation GENERIC, + // Camera control + CAMERA, + + // Script function call + SCRIPT, + // THIS MUST BE LAST!!! NUM_TYPES, } EVENTTYPE; diff --git a/sp/src/game/shared/gamerules.h b/sp/src/game/shared/gamerules.h index 58c29077..384d5b4e 100644 --- a/sp/src/game/shared/gamerules.h +++ b/sp/src/game/shared/gamerules.h @@ -173,6 +173,8 @@ public: virtual bool InRoundRestart( void ) { return false; } + virtual void RegisterScriptFunctions( void ){ }; + //Allow thirdperson camera. virtual bool AllowThirdPersonCamera( void ) { return false; } diff --git a/sp/src/game/shared/sceneentity_shared.h b/sp/src/game/shared/sceneentity_shared.h index 6293e23b..a190e53b 100644 --- a/sp/src/game/shared/sceneentity_shared.h +++ b/sp/src/game/shared/sceneentity_shared.h @@ -36,6 +36,7 @@ public: m_pEvent( 0 ), m_pScene( 0 ), m_pActor( 0 ), + m_hSceneEntity(0), m_bStarted( false ), m_iLayer( -1 ), m_iPriority( 0 ), @@ -63,6 +64,8 @@ public: // Current actor CChoreoActor *m_pActor; + CHandle< CSceneEntity > m_hSceneEntity; + // Set after the first time the event has been configured ( allows // bumping markov index only at start of event playback, not every frame ) bool m_bStarted; diff --git a/sp/src/game/shared/vscript_shared.cpp b/sp/src/game/shared/vscript_shared.cpp new file mode 100644 index 00000000..f4c64871 --- /dev/null +++ b/sp/src/game/shared/vscript_shared.cpp @@ -0,0 +1,266 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "vscript_shared.h" +#include "icommandline.h" +#include "tier1/utlbuffer.h" +#include "tier1/fmtstr.h" +#include "filesystem.h" +#include "characterset.h" +#include "isaverestore.h" +#include "gamerules.h" + +IScriptVM * g_pScriptVM; +extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); + +// #define VMPROFILE 1 + +#ifdef VMPROFILE + +#define VMPROF_START float debugStartTime = Plat_FloatTime(); +#define VMPROF_SHOW( funcname, funcdesc ) DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", (##funcname), (##funcdesc), (Plat_FloatTime() - debugStartTime)*1000.0 ); + +#else // !VMPROFILE + +#define VMPROF_START +#define VMPROF_SHOW + +#endif // VMPROFILE + + + +HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return NULL; + } + + static const char *pszExtensions[] = + { + "", // SL_NONE + ".gm", // SL_GAMEMONKEY + ".nut", // SL_SQUIRREL + ".lua", // SL_LUA + ".py", // SL_PYTHON + }; + + const char *pszVMExtension = pszExtensions[g_pScriptVM->GetLanguage()]; + const char *pszIncomingExtension = V_strrchr( pszScriptName , '.' ); + if ( pszIncomingExtension && V_strcmp( pszIncomingExtension, pszVMExtension ) != 0 ) + { + Warning( "Script file type does not match VM type\n" ); + return NULL; + } + + CFmtStr scriptPath; + if ( pszIncomingExtension ) + { + scriptPath.sprintf( "scripts/vscripts/%s", pszScriptName ); + } + else + { + scriptPath.sprintf( "scripts/vscripts/%s%s", pszScriptName, pszVMExtension ); + } + + const char *pBase; + CUtlBuffer bufferScript; + + if ( g_pScriptVM->GetLanguage() == SL_PYTHON ) + { + // python auto-loads raw or precompiled modules - don't load data here + pBase = NULL; + } + else + { + bool bResult = filesystem->ReadFile( scriptPath, "GAME", bufferScript ); + + if( !bResult ) + { + Warning( "Script not found (%s) \n", scriptPath.operator const char *() ); + Assert( "Error running script" ); + } + + pBase = (const char *) bufferScript.Base(); + + if ( !pBase || !*pBase ) + { + return NULL; + } + } + + + const char *pszFilename = V_strrchr( scriptPath, '/' ); + pszFilename++; + HSCRIPT hScript = g_pScriptVM->CompileScript( pBase, pszFilename ); + if ( !hScript ) + { + Warning( "FAILED to compile and execute script file named %s\n", scriptPath.operator const char *() ); + Assert( "Error running script" ); + } + return hScript; +} + +static int g_ScriptServerRunScriptDepth; + +bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return false; + } + + if ( !pszScriptName || !*pszScriptName ) + { + Warning( "Cannot run script: NULL script name\n" ); + return false; + } + + // Prevent infinite recursion in VM + if ( g_ScriptServerRunScriptDepth > 16 ) + { + Warning( "IncludeScript stack overflow\n" ); + return false; + } + + g_ScriptServerRunScriptDepth++; + HSCRIPT hScript = VScriptCompileScript( pszScriptName, bWarnMissing ); + bool bSuccess = false; + if ( hScript ) + { +#ifdef GAME_DLL + if ( gpGlobals->maxClients == 1 ) + { + CBaseEntity *pPlayer = UTIL_GetLocalPlayer(); + if ( pPlayer ) + { + g_pScriptVM->SetValue( "player", pPlayer->GetScriptInstance() ); + } + } +#endif + bSuccess = ( g_pScriptVM->Run( hScript, hScope ) != SCRIPT_ERROR ); + if ( !bSuccess ) + { + Warning( "Error running script named %s\n", pszScriptName ); + Assert( "Error running script" ); + } + } + g_ScriptServerRunScriptDepth--; + return bSuccess; +} + +#ifdef CLIENT_DLL +CON_COMMAND( script_client, "Run the text as a script" ) +#else +CON_COMMAND( script, "Run the text as a script" ) +#endif +{ + if ( !*args[1] ) + { + Warning( "No function name specified\n" ); + return; + } + + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + + const char *pszScript = args.GetCommandString(); + +#ifdef CLIENT_DLL + pszScript += 13; +#else + pszScript += 6; +#endif + + while ( *pszScript == ' ' ) + { + pszScript++; + } + + if ( !*pszScript ) + { + return; + } + + if ( *pszScript != '\"' ) + { + g_pScriptVM->Run( pszScript ); + } + else + { + pszScript++; + const char *pszEndQuote = pszScript; + while ( *pszEndQuote != '\"' ) + { + pszEndQuote++; + } + if ( !*pszEndQuote ) + { + return; + } + *((char *)pszEndQuote) = 0; + g_pScriptVM->Run( pszScript ); + *((char *)pszEndQuote) = '\"'; + } +} + + +CON_COMMAND_SHARED( script_execute, "Run a vscript file" ) +{ + if ( !*args[1] ) + { + Warning( "No script specified\n" ); + return; + } + + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + + VScriptRunScript( args[1], true ); +} + +CON_COMMAND_SHARED( script_debug, "Connect the vscript VM to the script debugger" ) +{ + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + g_pScriptVM->ConnectDebugger(); +} + +CON_COMMAND_SHARED( script_help, "Output help for script functions, optionally with a search string" ) +{ + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + const char *pszArg1 = "*"; + if ( *args[1] ) + { + pszArg1 = args[1]; + } + + g_pScriptVM->Run( CFmtStr( "PrintHelp( \"%s\" );", pszArg1 ) ); +} + +CON_COMMAND_SHARED( script_dump_all, "Dump the state of the VM to the console" ) +{ + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + g_pScriptVM->DumpState(); +} diff --git a/sp/src/game/shared/vscript_shared.h b/sp/src/game/shared/vscript_shared.h new file mode 100644 index 00000000..8c8ee86d --- /dev/null +++ b/sp/src/game/shared/vscript_shared.h @@ -0,0 +1,34 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_SHARED_H +#define VSCRIPT_SHARED_H + +#include "vscript/ivscript.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +extern IScriptVM * g_pScriptVM; + +HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing = false ); +bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing = false ); +inline bool VScriptRunScript( const char *pszScriptName, bool bWarnMissing = false ) { return VScriptRunScript( pszScriptName, NULL, bWarnMissing ); } + +#define DECLARE_ENT_SCRIPTDESC() ALLOW_SCRIPT_ACCESS(); virtual ScriptClassDesc_t *GetScriptDesc() + +#define BEGIN_ENT_SCRIPTDESC( className, baseClass, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC( className, baseClass, description ) +#define BEGIN_ENT_SCRIPTDESC_ROOT( className, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT( className, description ) +#define BEGIN_ENT_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) +#define BEGIN_ENT_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) + +#define _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ) template <> ScriptClassDesc_t * GetScriptDesc( className * ); ScriptClassDesc_t *className::GetScriptDesc() { return ::GetScriptDesc( this ); } + +// Only allow scripts to create entities during map initialization +bool IsEntityCreationAllowedInScripts( void ); + +#endif // VSCRIPT_SHARED_H diff --git a/sp/src/public/tier1/convar.h b/sp/src/public/tier1/convar.h index 4bff787e..e83c17c8 100644 --- a/sp/src/public/tier1/convar.h +++ b/sp/src/public/tier1/convar.h @@ -632,6 +632,18 @@ private: static ConCommand name##_command( #name, name, description ); \ static void name( const CCommand &args ) +#ifdef CLIENT_DLL + #define CON_COMMAND_SHARED( name, description ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command_client( #name "_client", name, description ); \ + static void name( const CCommand &args ) +#else + #define CON_COMMAND_SHARED( name, description ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command( #name, name, description ); \ + static void name( const CCommand &args ) +#endif + #define CON_COMMAND_F( name, description, flags ) \ static void name( const CCommand &args ); \ static ConCommand name##_command( #name, name, description, flags ); \ diff --git a/sp/src/public/tier1/utlbuffer.h b/sp/src/public/tier1/utlbuffer.h index 8ce115a7..033407b4 100644 --- a/sp/src/public/tier1/utlbuffer.h +++ b/sp/src/public/tier1/utlbuffer.h @@ -142,6 +142,9 @@ public: // Makes sure we've got at least this much memory void EnsureCapacity( int num ); + + // Access for direct read into buffer + void * AccessForDirectRead( int nBytes ); // Attaches the buffer to external memory.... void SetExternalBuffer( void* pMemory, int nSize, int nInitialPut, int nFlags = 0 ); @@ -1096,5 +1099,13 @@ inline void CUtlBuffer::CopyBuffer( const void *pubData, int cubData ) } } +inline void *CUtlBuffer::AccessForDirectRead( int nBytes ) +{ + Assert( m_Get == 0 && m_Put == 0 && m_nMaxPut == 0 ); + EnsureCapacity( nBytes ); + m_nMaxPut = nBytes; + return Base(); +} + #endif // UTLBUFFER_H diff --git a/sp/src/public/vscript/ivscript.h b/sp/src/public/vscript/ivscript.h new file mode 100644 index 00000000..cc6f9c8a --- /dev/null +++ b/sp/src/public/vscript/ivscript.h @@ -0,0 +1,1412 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: VScript +// +// Overview +// -------- +// VScript is an abstract binding layer that allows code to expose itself to +// multiple scripting languages in a uniform format. Code can expose +// functions, classes, and data to the scripting languages, and can also +// call functions that reside in scripts. +// +// Initializing +// ------------ +// +// To create a script virtual machine (VM), grab the global instance of +// IScriptManager, call CreateVM, then call Init on the returned VM. Right +// now you can have multiple VMs, but only VMs for a specific language. +// +// Exposing functions and classes +// ------------------------------ +// +// To expose a C++ function to the scripting system, you just need to fill out a +// description block. Using templates, the system will automatically deduce +// all of the binding requirements (parameters and return values). Functions +// are limited as to what the types of the parameters can be. See ScriptVariant_t. +// +// extern IScriptVM *pScriptVM; +// bool Foo( int ); +// void Bar(); +// float FooBar( int, const char * ); +// float OverlyTechnicalName( bool ); +// +// void RegisterFuncs() +// { +// ScriptRegisterFunction( pScriptVM, Foo ); +// ScriptRegisterFunction( pScriptVM, Bar ); +// ScriptRegisterFunction( pScriptVM, FooBar ); +// ScriptRegisterFunctionNamed( pScriptVM, OverlyTechnicalName, "SimpleName" ); +// } +// +// class CMyClass +// { +// public: +// bool Foo( int ); +// void Bar(); +// float FooBar( int, const char * ); +// float OverlyTechnicalName( bool ); +// }; +// +// BEGIN_SCRIPTDESC_ROOT( CMyClass ) +// DEFINE_SCRIPTFUNC( Foo ) +// DEFINE_SCRIPTFUNC( Bar ) +// DEFINE_SCRIPTFUNC( FooBar ) +// DEFINE_SCRIPTFUNC_NAMED( OverlyTechnicalName, "SimpleMemberName" ) +// END_SCRIPTDESC(); +// +// class CMyDerivedClass : public CMyClass +// { +// public: +// float DerivedFunc() const; +// }; +// +// BEGIN_SCRIPTDESC( CMyDerivedClass, CMyClass ) +// DEFINE_SCRIPTFUNC( DerivedFunc ) +// END_SCRIPTDESC(); +// +// CMyDerivedClass derivedInstance; +// +// void AnotherFunction() +// { +// // Manual class exposure +// pScriptVM->RegisterClass( GetScriptDescForClass( CMyClass ) ); +// +// // Auto registration by instance +// pScriptVM->RegisterInstance( &derivedInstance, "theInstance" ); +// } +// +// Classes with "DEFINE_SCRIPT_CONSTRUCTOR()" in their description can be instanced within scripts +// +// Scopes +// ------ +// Scripts can either be run at the global scope, or in a user defined scope. In the latter case, +// all "globals" within the script are actually in the scope. This can be used to bind private +// data spaces with C++ objects. +// +// Calling a function on a script +// ------------------------------ +// Generally, use the "Call" functions. This example is the equivalent of DoIt("Har", 6.0, 99). +// +// hFunction = pScriptVM->LookupFunction( "DoIt", hScope ); +// pScriptVM->Call( hFunction, hScope, true, NULL, "Har", 6.0, 99 ); +// +//============================================================================= + +#ifndef IVSCRIPT_H +#define IVSCRIPT_H + +#include "platform.h" +#include "datamap.h" +#include "appframework/IAppSystem.h" +#include "tier1/functors.h" +#include "tier0/memdbgon.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +#ifdef VSCRIPT_DLL_EXPORT +#define VSCRIPT_INTERFACE DLL_EXPORT +#define VSCRIPT_OVERLOAD DLL_GLOBAL_EXPORT +#define VSCRIPT_CLASS DLL_CLASS_EXPORT +#else +#define VSCRIPT_INTERFACE DLL_IMPORT +#define VSCRIPT_OVERLOAD DLL_GLOBAL_IMPORT +#define VSCRIPT_CLASS DLL_CLASS_IMPORT +#endif + +class CUtlBuffer; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define VSCRIPT_INTERFACE_VERSION "VScriptManager009" + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +class IScriptVM; + +enum ScriptLanguage_t +{ + SL_NONE, + SL_GAMEMONKEY, + SL_SQUIRREL, + SL_LUA, + SL_PYTHON, + + SL_DEFAULT = SL_SQUIRREL +}; + +class IScriptManager : public IAppSystem +{ +public: + virtual IScriptVM *CreateVM( ScriptLanguage_t language = SL_DEFAULT ) = 0; + virtual void DestroyVM( IScriptVM * ) = 0; +}; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +DECLARE_POINTER_HANDLE( HSCRIPT ); +#define INVALID_HSCRIPT ((HSCRIPT)-1) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum ExtendedFieldType +{ + FIELD_TYPEUNKNOWN = FIELD_TYPECOUNT, + FIELD_CSTRING, + FIELD_HSCRIPT, + FIELD_VARIANT, +}; + +typedef int ScriptDataType_t; +struct ScriptVariant_t; + +template struct ScriptDeducer { /*enum { FIELD_TYPE = FIELD_TYPEUNKNOWN };*/ }; +#define DECLARE_DEDUCE_FIELDTYPE( fieldType, type ) template<> struct ScriptDeducer { enum { FIELD_TYPE = fieldType }; }; + +DECLARE_DEDUCE_FIELDTYPE( FIELD_VOID, void ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_FLOAT, float ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CSTRING, const char * ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CSTRING, char * ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, Vector ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, const Vector &); +DECLARE_DEDUCE_FIELDTYPE( FIELD_INTEGER, int ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_BOOLEAN, bool ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CHARACTER, char ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_HSCRIPT, HSCRIPT ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VARIANT, ScriptVariant_t ); + +#define ScriptDeduceType( T ) ScriptDeducer::FIELD_TYPE + +template +inline const char * ScriptFieldTypeName() +{ + T::using_unknown_script_type(); +} + +#define DECLARE_NAMED_FIELDTYPE( fieldType, strName ) template <> inline const char * ScriptFieldTypeName() { return strName; } +DECLARE_NAMED_FIELDTYPE( void, "void" ); +DECLARE_NAMED_FIELDTYPE( float, "float" ); +DECLARE_NAMED_FIELDTYPE( const char *, "cstring" ); +DECLARE_NAMED_FIELDTYPE( char *, "cstring" ); +DECLARE_NAMED_FIELDTYPE( Vector, "vector" ); +DECLARE_NAMED_FIELDTYPE( const Vector&, "vector" ); +DECLARE_NAMED_FIELDTYPE( int, "integer" ); +DECLARE_NAMED_FIELDTYPE( bool, "boolean" ); +DECLARE_NAMED_FIELDTYPE( char, "character" ); +DECLARE_NAMED_FIELDTYPE( HSCRIPT, "hscript" ); +DECLARE_NAMED_FIELDTYPE( ScriptVariant_t, "variant" ); + +inline const char * ScriptFieldTypeName( int16 eType) +{ + switch( eType ) + { + case FIELD_VOID: return "void"; + case FIELD_FLOAT: return "float"; + case FIELD_CSTRING: return "cstring"; + case FIELD_VECTOR: return "vector"; + case FIELD_INTEGER: return "integer"; + case FIELD_BOOLEAN: return "boolean"; + case FIELD_CHARACTER: return "character"; + case FIELD_HSCRIPT: return "hscript"; + case FIELD_VARIANT: return "variant"; + default: return "unknown_script_type"; + } +} + +//--------------------------------------------------------- + +struct ScriptFuncDescriptor_t +{ + ScriptFuncDescriptor_t() + { + m_pszFunction = NULL; + m_ReturnType = FIELD_TYPEUNKNOWN; + m_pszDescription = NULL; + } + + const char *m_pszScriptName; + const char *m_pszFunction; + const char *m_pszDescription; + ScriptDataType_t m_ReturnType; + CUtlVector m_Parameters; +}; + + +//--------------------------------------------------------- + +// Prefix a script description with this in order to not show the function or class in help +#define SCRIPT_HIDE "@" + +// Prefix a script description of a class to indicate it is a singleton and the single instance should be in the help +#define SCRIPT_SINGLETON "!" + +// Prefix a script description with this to indicate it should be represented using an alternate name +#define SCRIPT_ALIAS( alias, description ) "#" alias ":" description + +//--------------------------------------------------------- + +enum ScriptFuncBindingFlags_t +{ + SF_MEMBER_FUNC = 0x01, +}; + +typedef bool (*ScriptBindingFunc_t)( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ); + +struct ScriptFunctionBinding_t +{ + ScriptFuncDescriptor_t m_desc; + ScriptBindingFunc_t m_pfnBinding; + void * m_pFunction; + unsigned m_flags; +}; + +//--------------------------------------------------------- +class IScriptInstanceHelper +{ +public: + virtual void *GetProxied( void *p ) { return p; } + virtual bool ToString( void *p, char *pBuf, int bufSize ) { return false; } + virtual void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) { return NULL; } +}; + +//--------------------------------------------------------- + +struct ScriptClassDesc_t +{ + ScriptClassDesc_t() : m_pszScriptName( 0 ), m_pszClassname( 0 ), m_pszDescription( 0 ), m_pBaseDesc( 0 ), m_pfnConstruct( 0 ), m_pfnDestruct( 0 ), pHelper(NULL) {} + + const char * m_pszScriptName; + const char * m_pszClassname; + const char * m_pszDescription; + ScriptClassDesc_t * m_pBaseDesc; + CUtlVector m_FunctionBindings; + + void *(*m_pfnConstruct)(); + void (*m_pfnDestruct)( void *); + IScriptInstanceHelper * pHelper; // optional helper +}; + +//--------------------------------------------------------- +// A simple variant type. Intentionally not full featured (no implicit conversion, no memory management) +//--------------------------------------------------------- + +enum SVFlags_t +{ + SV_FREE = 0x01, +}; + +#pragma warning(push) +#pragma warning(disable:4800) +struct ScriptVariant_t +{ + ScriptVariant_t() : m_flags( 0 ), m_type( FIELD_VOID ) { m_pVector = 0; } + ScriptVariant_t( int val ) : m_flags( 0 ), m_type( FIELD_INTEGER ) { m_int = val;} + ScriptVariant_t( float val ) : m_flags( 0 ), m_type( FIELD_FLOAT ) { m_float = val; } + ScriptVariant_t( double val ) : m_flags( 0 ), m_type( FIELD_FLOAT ) { m_float = (float)val; } + ScriptVariant_t( char val ) : m_flags( 0 ), m_type( FIELD_CHARACTER ) { m_char = val; } + ScriptVariant_t( bool val ) : m_flags( 0 ), m_type( FIELD_BOOLEAN ) { m_bool = val; } + ScriptVariant_t( HSCRIPT val ) : m_flags( 0 ), m_type( FIELD_HSCRIPT ) { m_hScript = val; } + + ScriptVariant_t( const Vector &val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pVector = &val; } else { m_pVector = new Vector( val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const Vector *val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pVector = val; } else { m_pVector = new Vector( *val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const char *val , bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_CSTRING ) { if ( !bCopy ) { m_pszString = val; } else { m_pszString = strdup( val ); m_flags |= SV_FREE; } } + + bool IsNull() const { return (m_type == FIELD_VOID ); } + + operator int() const { Assert( m_type == FIELD_INTEGER ); return m_int; } + operator float() const { Assert( m_type == FIELD_FLOAT ); return m_float; } + operator const char *() const { Assert( m_type == FIELD_CSTRING ); return ( m_pszString ) ? m_pszString : ""; } + operator const Vector &() const { Assert( m_type == FIELD_VECTOR ); static Vector vecNull(0, 0, 0); return (m_pVector) ? *m_pVector : vecNull; } + operator char() const { Assert( m_type == FIELD_CHARACTER ); return m_char; } + operator bool() const { Assert( m_type == FIELD_BOOLEAN ); return m_bool; } + operator HSCRIPT() const { Assert( m_type == FIELD_HSCRIPT ); return m_hScript; } + + void operator=( int i ) { m_type = FIELD_INTEGER; m_int = i; } + void operator=( float f ) { m_type = FIELD_FLOAT; m_float = f; } + void operator=( double f ) { m_type = FIELD_FLOAT; m_float = (float)f; } + void operator=( const Vector &vec ) { m_type = FIELD_VECTOR; m_pVector = &vec; } + void operator=( const Vector *vec ) { m_type = FIELD_VECTOR; m_pVector = vec; } + void operator=( const char *psz ) { m_type = FIELD_CSTRING; m_pszString = psz; } + void operator=( char c ) { m_type = FIELD_CHARACTER; m_char = c; } + void operator=( bool b ) { m_type = FIELD_BOOLEAN; m_bool = b; } + void operator=( HSCRIPT h ) { m_type = FIELD_HSCRIPT; m_hScript = h; } + + void Free() { if ( ( m_flags & SV_FREE ) && ( m_type == FIELD_HSCRIPT || m_type == FIELD_VECTOR || m_type == FIELD_CSTRING ) ) delete m_pszString; } // Generally only needed for return results + + template + T Get() + { + T value; + AssignTo( &value ); + return value; + } + + template + bool AssignTo( T *pDest ) + { + ScriptDataType_t destType = ScriptDeduceType( T ); + if ( destType == FIELD_TYPEUNKNOWN ) + { + DevWarning( "Unable to convert script variant to unknown type\n" ); + } + if ( destType == m_type ) + { + *pDest = *this; + return true; + } + + if ( m_type != FIELD_VECTOR && m_type != FIELD_CSTRING && destType != FIELD_VECTOR && destType != FIELD_CSTRING ) + { + switch ( m_type ) + { + case FIELD_VOID: *pDest = 0; break; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_CHARACTER: *pDest = m_char; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + case FIELD_HSCRIPT: *pDest = m_hScript; return true; + } + } + else + { + DevWarning( "No free conversion of %s script variant to %s right now\n", + ScriptFieldTypeName( m_type ), ScriptFieldTypeName() ); + if ( destType != FIELD_VECTOR ) + { + *pDest = 0; + } + } + return false; + } + + bool AssignTo( float *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to float now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( int *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to int now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( bool *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to bool now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( char **pDest ) + { + DevWarning( "No free conversion of string or vector script variant right now\n" ); + // If want to support this, probably need to malloc string and require free on other side [3/24/2008 tom] + *pDest = ""; + return false; + } + + bool AssignTo( ScriptVariant_t *pDest ) + { + pDest->m_type = m_type; + if ( m_type == FIELD_VECTOR ) + { + pDest->m_pVector = new Vector; + ((Vector *)(pDest->m_pVector))->Init( m_pVector->x, m_pVector->y, m_pVector->z ); + pDest->m_flags |= SV_FREE; + } + else if ( m_type == FIELD_CSTRING ) + { + pDest->m_pszString = strdup( m_pszString ); + pDest->m_flags |= SV_FREE; + } + else + { + pDest->m_int = m_int; + } + return false; + } + + union + { + int m_int; + float m_float; + const char * m_pszString; + const Vector * m_pVector; + char m_char; + bool m_bool; + HSCRIPT m_hScript; + }; + + int16 m_type; + int16 m_flags; + +private: +}; + +#define SCRIPT_VARIANT_NULL ScriptVariant_t() + +#pragma warning(pop) + + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#include "vscript_templates.h" + +// Lower level macro primitives +#define ScriptInitFunctionBinding( pScriptFunction, func ) ScriptInitFunctionBindingNamed( pScriptFunction, func, #func ) +#define ScriptInitFunctionBindingNamed( pScriptFunction, func, scriptName ) do { ScriptInitFuncDescriptorNamed( (&(pScriptFunction)->m_desc), func, scriptName ); (pScriptFunction)->m_pfnBinding = ScriptCreateBinding( &func ); (pScriptFunction)->m_pFunction = (void *)&func; } while (0) + +#define ScriptInitMemberFunctionBinding( pScriptFunction, class, func ) ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, #func ) +#define ScriptInitMemberFunctionBindingNamed( pScriptFunction, class, func, scriptName ) ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, scriptName ) +#define ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, scriptName ) do { ScriptInitMemberFuncDescriptor_( (&(pScriptFunction)->m_desc), class, func, scriptName ); (pScriptFunction)->m_pfnBinding = ScriptCreateBinding( ((class *)0), &class::func ); (pScriptFunction)->m_pFunction = ScriptConvertFuncPtrToVoid( &class::func ); (pScriptFunction)->m_flags = SF_MEMBER_FUNC; } while (0) + +#define ScriptInitClassDesc( pClassDesc, class, pBaseClassDesc ) ScriptInitClassDescNamed( pClassDesc, class, pBaseClassDesc, #class ) +#define ScriptInitClassDescNamed( pClassDesc, class, pBaseClassDesc, scriptName ) ScriptInitClassDescNamed_( pClassDesc, class, pBaseClassDesc, scriptName ) +#define ScriptInitClassDescNoBase( pClassDesc, class ) ScriptInitClassDescNoBaseNamed( pClassDesc, class, #class ) +#define ScriptInitClassDescNoBaseNamed( pClassDesc, class, scriptName ) ScriptInitClassDescNamed_( pClassDesc, class, NULL, scriptName ) +#define ScriptInitClassDescNamed_( pClassDesc, class, pBaseClassDesc, scriptName ) do { (pClassDesc)->m_pszScriptName = scriptName; (pClassDesc)->m_pszClassname = #class; (pClassDesc)->m_pBaseDesc = pBaseClassDesc; } while ( 0 ) + +#define ScriptAddFunctionToClassDesc( pClassDesc, class, func, description ) ScriptAddFunctionToClassDescNamed( pClassDesc, class, func, #func, description ) +#define ScriptAddFunctionToClassDescNamed( pClassDesc, class, func, scriptName, description ) do { ScriptFunctionBinding_t *pBinding = &((pClassDesc)->m_FunctionBindings[(pClassDesc)->m_FunctionBindings.AddToTail()]); pBinding->m_desc.m_pszDescription = description; ScriptInitMemberFunctionBindingNamed( pBinding, class, func, scriptName ); } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define ScriptRegisterFunction( pVM, func, description ) ScriptRegisterFunctionNamed( pVM, func, #func, description ) +#define ScriptRegisterFunctionNamed( pVM, func, scriptName, description ) do { static ScriptFunctionBinding_t binding; binding.m_desc.m_pszDescription = description; binding.m_desc.m_Parameters.RemoveAll(); ScriptInitFunctionBindingNamed( &binding, func, scriptName ); pVM->RegisterFunction( &binding ); } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define ALLOW_SCRIPT_ACCESS() template friend ScriptClassDesc_t *GetScriptDesc(T *); + +#define BEGIN_SCRIPTDESC( className, baseClass, description ) BEGIN_SCRIPTDESC_NAMED( className, baseClass, #className, description ) +#define BEGIN_SCRIPTDESC_ROOT( className, description ) BEGIN_SCRIPTDESC_ROOT_NAMED( className, #className, description ) + +#ifdef MSVC + #define DEFINE_SCRIPTDESC_FUNCTION( className, baseClass ) \ + ScriptClassDesc_t * GetScriptDesc( className * ) +#else + #define DEFINE_SCRIPTDESC_FUNCTION( className, baseClass ) \ + template <> ScriptClassDesc_t * GetScriptDesc( baseClass *); \ + template <> ScriptClassDesc_t * GetScriptDesc( className *) +#endif + +#define BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) \ + ScriptClassDesc_t g_##className##_ScriptDesc; \ + DEFINE_SCRIPTDESC_FUNCTION( className, baseClass ) \ + { \ + static bool bInitialized; \ + if ( bInitialized ) \ + { \ + return &g_##className##_ScriptDesc; \ + } \ + \ + bInitialized = true; \ + \ + typedef className _className; \ + ScriptClassDesc_t *pDesc = &g_##className##_ScriptDesc; \ + pDesc->m_pszDescription = description; \ + ScriptInitClassDescNamed( pDesc, className, GetScriptDescForClass( baseClass ), scriptName ); \ + ScriptClassDesc_t *pInstanceHelperBase = pDesc->m_pBaseDesc; \ + while ( pInstanceHelperBase ) \ + { \ + if ( pInstanceHelperBase->pHelper ) \ + { \ + pDesc->pHelper = pInstanceHelperBase->pHelper; \ + break; \ + } \ + pInstanceHelperBase = pInstanceHelperBase->m_pBaseDesc; \ + } + + +#define BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) \ + BEGIN_SCRIPTDESC_NAMED( className, ScriptNoBase_t, scriptName, description ) + +#define END_SCRIPTDESC() \ + return pDesc; \ + } + +#define DEFINE_SCRIPTFUNC( func, description ) DEFINE_SCRIPTFUNC_NAMED( func, #func, description ) +#define DEFINE_SCRIPTFUNC_NAMED( func, scriptName, description ) ScriptAddFunctionToClassDescNamed( pDesc, _className, func, scriptName, description ); +#define DEFINE_SCRIPT_CONSTRUCTOR() ScriptAddConstructorToClassDesc( pDesc, _className ); +#define DEFINE_SCRIPT_INSTANCE_HELPER( p ) pDesc->pHelper = (p); + +template ScriptClassDesc_t *GetScriptDesc(T *); + +struct ScriptNoBase_t; +template <> inline ScriptClassDesc_t *GetScriptDesc( ScriptNoBase_t *) { return NULL; } + +#define GetScriptDescForClass( className ) GetScriptDesc( ( className *)NULL ) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +template +class CScriptConstructor +{ +public: + static void *Construct() { return new T; } + static void Destruct( void *p ) { delete (T *)p; } +}; + +#define ScriptAddConstructorToClassDesc( pClassDesc, class ) do { (pClassDesc)->m_pfnConstruct = &CScriptConstructor::Construct; (pClassDesc)->m_pfnDestruct = &CScriptConstructor::Destruct; } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum ScriptErrorLevel_t +{ + SCRIPT_LEVEL_WARNING = 0, + SCRIPT_LEVEL_ERROR, +}; + +typedef void ( *ScriptOutputFunc_t )( const char *pszText ); +typedef bool ( *ScriptErrorFunc_t )( ScriptErrorLevel_t eLevel, const char *pszText ); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#ifdef RegisterClass +#undef RegisterClass +#endif + +enum ScriptStatus_t +{ + SCRIPT_ERROR = -1, + SCRIPT_DONE, + SCRIPT_RUNNING, +}; + +class IScriptVM +{ +public: + virtual bool Init() = 0; + virtual void Shutdown() = 0; + + virtual bool ConnectDebugger() = 0; + virtual void DisconnectDebugger() = 0; + + virtual ScriptLanguage_t GetLanguage() = 0; + virtual const char *GetLanguageName() = 0; + + virtual void AddSearchPath( const char *pszSearchPath ) = 0; + + //-------------------------------------------------------- + + virtual bool Frame( float simTime ) = 0; + + //-------------------------------------------------------- + // Simple script usage + //-------------------------------------------------------- + virtual ScriptStatus_t Run( const char *pszScript, bool bWait = true ) = 0; + inline ScriptStatus_t Run( const unsigned char *pszScript, bool bWait = true ) { return Run( (char *)pszScript, bWait ); } + + //-------------------------------------------------------- + // Compilation + //-------------------------------------------------------- + virtual HSCRIPT CompileScript( const char *pszScript, const char *pszId = NULL ) = 0; + inline HSCRIPT CompileScript( const unsigned char *pszScript, const char *pszId = NULL ) { return CompileScript( (char *)pszScript, pszId ); } + virtual void ReleaseScript( HSCRIPT ) = 0; + + //-------------------------------------------------------- + // Execution of compiled + //-------------------------------------------------------- + virtual ScriptStatus_t Run( HSCRIPT hScript, HSCRIPT hScope = NULL, bool bWait = true ) = 0; + virtual ScriptStatus_t Run( HSCRIPT hScript, bool bWait ) = 0; + + //-------------------------------------------------------- + // Scope + //-------------------------------------------------------- + virtual HSCRIPT CreateScope( const char *pszScope, HSCRIPT hParent = NULL ) = 0; + virtual void ReleaseScope( HSCRIPT hScript ) = 0; + + //-------------------------------------------------------- + // Script functions + //-------------------------------------------------------- + virtual HSCRIPT LookupFunction( const char *pszFunction, HSCRIPT hScope = NULL ) = 0; + virtual void ReleaseFunction( HSCRIPT hScript ) = 0; + + //-------------------------------------------------------- + // Script functions (raw, use Call()) + //-------------------------------------------------------- + virtual ScriptStatus_t ExecuteFunction( HSCRIPT hFunction, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) = 0; + + //-------------------------------------------------------- + // External functions + //-------------------------------------------------------- + virtual void RegisterFunction( ScriptFunctionBinding_t *pScriptFunction ) = 0; + + //-------------------------------------------------------- + // External classes + //-------------------------------------------------------- + virtual bool RegisterClass( ScriptClassDesc_t *pClassDesc ) = 0; + + //-------------------------------------------------------- + // External instances. Note class will be auto-registered. + //-------------------------------------------------------- + + virtual HSCRIPT RegisterInstance( ScriptClassDesc_t *pDesc, void *pInstance ) = 0; + virtual void SetInstanceUniqeId( HSCRIPT hInstance, const char *pszId ) = 0; + template HSCRIPT RegisterInstance( T *pInstance ) { return RegisterInstance( GetScriptDesc( pInstance ), pInstance ); } + template HSCRIPT RegisterInstance( T *pInstance, const char *pszInstance, HSCRIPT hScope = NULL) { HSCRIPT hInstance = RegisterInstance( GetScriptDesc( pInstance ), pInstance ); SetValue( hScope, pszInstance, hInstance ); return hInstance; } + virtual void RemoveInstance( HSCRIPT ) = 0; + void RemoveInstance( HSCRIPT hInstance, const char *pszInstance, HSCRIPT hScope = NULL ) { ClearValue( hScope, pszInstance ); RemoveInstance( hInstance ); } + void RemoveInstance( const char *pszInstance, HSCRIPT hScope = NULL ) { ScriptVariant_t val; if ( GetValue( hScope, pszInstance, &val ) ) { if ( val.m_type == FIELD_HSCRIPT ) { RemoveInstance( val, pszInstance, hScope ); } ReleaseValue( val ); } } + + virtual void *GetInstanceValue( HSCRIPT hInstance, ScriptClassDesc_t *pExpectedType = NULL ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool GenerateUniqueKey( const char *pszRoot, char *pBuf, int nBufSize ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool ValueExists( HSCRIPT hScope, const char *pszKey ) = 0; + bool ValueExists( const char *pszKey ) { return ValueExists( NULL, pszKey ); } + + virtual bool SetValue( HSCRIPT hScope, const char *pszKey, const char *pszValue ) = 0; + virtual bool SetValue( HSCRIPT hScope, const char *pszKey, const ScriptVariant_t &value ) = 0; + bool SetValue( const char *pszKey, const ScriptVariant_t &value ) { return SetValue(NULL, pszKey, value ); } + + virtual void CreateTable( ScriptVariant_t &Table ) = 0; + virtual int GetNumTableEntries( HSCRIPT hScope ) = 0; + virtual int GetKeyValue( HSCRIPT hScope, int nIterator, ScriptVariant_t *pKey, ScriptVariant_t *pValue ) = 0; + + virtual bool GetValue( HSCRIPT hScope, const char *pszKey, ScriptVariant_t *pValue ) = 0; + bool GetValue( const char *pszKey, ScriptVariant_t *pValue ) { return GetValue(NULL, pszKey, pValue ); } + virtual void ReleaseValue( ScriptVariant_t &value ) = 0; + + virtual bool ClearValue( HSCRIPT hScope, const char *pszKey ) = 0; + bool ClearValue( const char *pszKey) { return ClearValue( NULL, pszKey ); } + + //---------------------------------------------------------------------------- + + virtual void WriteState( CUtlBuffer *pBuffer ) = 0; + virtual void ReadState( CUtlBuffer *pBuffer ) = 0; + virtual void RemoveOrphanInstances() = 0; + + virtual void DumpState() = 0; + + virtual void SetOutputCallback( ScriptOutputFunc_t pFunc ) = 0; + virtual void SetErrorCallback( ScriptErrorFunc_t pFunc ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool RaiseException( const char *pszExceptionText ) = 0; + + //---------------------------------------------------------------------------- + // Call API + // + // Note for string and vector return types, the caller must delete the pointed to memory + //---------------------------------------------------------------------------- + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope = NULL, bool bWait = true, ScriptVariant_t *pReturn = NULL ) + { + return ExecuteFunction( hFunction, NULL, 0, pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + +}; + + +//----------------------------------------------------------------------------- +// Script scope helper class +//----------------------------------------------------------------------------- + +class CDefScriptScopeBase +{ +public: + static IScriptVM *GetVM() + { + extern IScriptVM *g_pScriptVM; + return g_pScriptVM; + } +}; + +template +class CScriptScopeT : public CDefScriptScopeBase +{ +public: + CScriptScopeT() : + m_hScope( INVALID_HSCRIPT ), + m_flags( 0 ) + { + } + + ~CScriptScopeT() + { + Term(); + } + + bool IsInitialized() + { + return m_hScope != INVALID_HSCRIPT; + } + + bool Init( const char *pszName ) + { + m_hScope = GetVM()->CreateScope( pszName ); + return ( m_hScope != NULL ); + } + + bool Init( HSCRIPT hScope, bool bExternal = true ) + { + if ( bExternal ) + { + m_flags |= EXTERNAL; + } + m_hScope = hScope; + return ( m_hScope != NULL ); + } + + bool InitGlobal() + { + Assert( 0 ); // todo [3/24/2008 tom] + m_hScope = GetVM()->CreateScope( "" ); + return ( m_hScope != NULL ); + } + + void Term() + { + if ( m_hScope != INVALID_HSCRIPT ) + { + IScriptVM *pVM = GetVM(); + if ( pVM ) + { + for ( int i = 0; i < m_FuncHandles.Count(); i++ ) + { + pVM->ReleaseFunction( *m_FuncHandles[i] ); + } + } + m_FuncHandles.Purge(); + if ( m_hScope && pVM && !(m_flags & EXTERNAL) ) + { + pVM->ReleaseScope( m_hScope ); + } + m_hScope = INVALID_HSCRIPT; + } + m_flags = 0; + } + + void InvalidateCachedValues() + { + IScriptVM *pVM = GetVM(); + for ( int i = 0; i < m_FuncHandles.Count(); i++ ) + { + if ( *m_FuncHandles[i] ) + pVM->ReleaseFunction( *m_FuncHandles[i] ); + *m_FuncHandles[i] = INVALID_HSCRIPT; + } + m_FuncHandles.RemoveAll(); + } + + operator HSCRIPT() + { + return ( m_hScope != INVALID_HSCRIPT ) ? m_hScope : NULL; + } + + bool ValueExists( const char *pszKey ) { return GetVM()->ValueExists( m_hScope, pszKey ); } + bool SetValue( const char *pszKey, const ScriptVariant_t &value ) { return GetVM()->SetValue(m_hScope, pszKey, value ); } + bool GetValue( const char *pszKey, ScriptVariant_t *pValue ) { return GetVM()->GetValue(m_hScope, pszKey, pValue ); } + void ReleaseValue( ScriptVariant_t &value ) { GetVM()->ReleaseValue( value ); } + bool ClearValue( const char *pszKey) { return GetVM()->ClearValue( m_hScope, pszKey ); } + + ScriptStatus_t Run( HSCRIPT hScript ) + { + InvalidateCachedValues(); + return GetVM()->Run( hScript, m_hScope ); + } + + ScriptStatus_t Run( const char *pszScriptText, const char *pszScriptName = NULL ) + { + InvalidateCachedValues(); + HSCRIPT hScript = GetVM()->CompileScript( pszScriptText, pszScriptName ); + if ( hScript ) + { + ScriptStatus_t result = GetVM()->Run( hScript, m_hScope ); + GetVM()->ReleaseScript( hScript ); + return result; + } + return SCRIPT_ERROR; + } + + ScriptStatus_t Run( const unsigned char *pszScriptText, const char *pszScriptName = NULL ) + { + return Run( (const char *)pszScriptText, pszScriptName); + } + + HSCRIPT LookupFunction( const char *pszFunction ) + { + return GetVM()->LookupFunction( pszFunction, m_hScope ); + } + + void ReleaseFunction( HSCRIPT hScript ) + { + GetVM()->ReleaseFunction( hScript ); + } + + bool FunctionExists( const char *pszFunction ) + { + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + GetVM()->ReleaseFunction( hFunction ); + return ( hFunction != NULL ) ; + } + + //----------------------------------------------------- + + enum Flags_t + { + EXTERNAL = 0x01, + }; + + //----------------------------------------------------- + + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn = NULL ) + { + return GetVM()->ExecuteFunction( hFunction, NULL, 0, pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn = NULL ) + { + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, NULL, 0, pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + +protected: + HSCRIPT m_hScope; + int m_flags; + CUtlVectorConservative m_FuncHandles; +}; + +typedef CScriptScopeT<> CScriptScope; + +#define VScriptAddEnumToScope_( scope, enumVal, scriptName ) (scope).SetValue( scriptName, (int)enumVal ) +#define VScriptAddEnumToScope( scope, enumVal ) VScriptAddEnumToScope_( scope, enumVal, #enumVal ) + +#define VScriptAddEnumToRoot( enumVal ) g_pScriptVM->SetValue( #enumVal, (int)enumVal ) + +//----------------------------------------------------------------------------- +// Script function proxy support +//----------------------------------------------------------------------------- + +class CScriptFuncHolder +{ +public: + CScriptFuncHolder() : hFunction( INVALID_HSCRIPT ) {} + bool IsValid() { return ( hFunction != INVALID_HSCRIPT ); } + bool IsNull() { return ( !hFunction ); } + HSCRIPT hFunction; +}; + +#define DEFINE_SCRIPT_PROXY_GUTS( FuncName, N ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + template < typename RET_TYPE FUNC_TEMPLATE_ARG_PARAMS_##N> \ + bool FuncName( RET_TYPE *pRetVal FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptVariant_t returnVal; \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, &returnVal, FUNC_CALL_ARGS_##N ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + returnVal.AssignTo( pRetVal ); \ + returnVal.Free(); \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, N ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + template < FUNC_SOLO_TEMPLATE_ARG_PARAMS_##N> \ + bool FuncName( FUNC_PROXY_ARG_FORMAL_PARAMS_##N ) \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, NULL, FUNC_CALL_ARGS_##N ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_0V( FuncName ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + bool FuncName() \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, NULL ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_0( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 0 ) +#define DEFINE_SCRIPT_PROXY_1( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 1 ) +#define DEFINE_SCRIPT_PROXY_2( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 2 ) +#define DEFINE_SCRIPT_PROXY_3( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 3 ) +#define DEFINE_SCRIPT_PROXY_4( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 4 ) +#define DEFINE_SCRIPT_PROXY_5( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 5 ) +#define DEFINE_SCRIPT_PROXY_6( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 6 ) +#define DEFINE_SCRIPT_PROXY_7( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 7 ) +#define DEFINE_SCRIPT_PROXY_8( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 8 ) +#define DEFINE_SCRIPT_PROXY_9( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 9 ) +#define DEFINE_SCRIPT_PROXY_10( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 10 ) +#define DEFINE_SCRIPT_PROXY_11( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 11 ) +#define DEFINE_SCRIPT_PROXY_12( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 12 ) +#define DEFINE_SCRIPT_PROXY_13( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 13 ) +#define DEFINE_SCRIPT_PROXY_14( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 14 ) + +#define DEFINE_SCRIPT_PROXY_1V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 1 ) +#define DEFINE_SCRIPT_PROXY_2V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 2 ) +#define DEFINE_SCRIPT_PROXY_3V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 3 ) +#define DEFINE_SCRIPT_PROXY_4V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 4 ) +#define DEFINE_SCRIPT_PROXY_5V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 5 ) +#define DEFINE_SCRIPT_PROXY_6V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 6 ) +#define DEFINE_SCRIPT_PROXY_7V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 7 ) +#define DEFINE_SCRIPT_PROXY_8V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 8 ) +#define DEFINE_SCRIPT_PROXY_9V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 9 ) +#define DEFINE_SCRIPT_PROXY_10V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 10 ) +#define DEFINE_SCRIPT_PROXY_11V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 11 ) +#define DEFINE_SCRIPT_PROXY_12V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 12 ) +#define DEFINE_SCRIPT_PROXY_13V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 13 ) +#define DEFINE_SCRIPT_PROXY_14V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 14 ) + +//----------------------------------------------------------------------------- + +#include "tier0/memdbgoff.h" + +#endif // IVSCRIPT_H diff --git a/sp/src/public/vscript/vscript_templates.h b/sp/src/public/vscript/vscript_templates.h new file mode 100644 index 00000000..e23a9fe9 --- /dev/null +++ b/sp/src/public/vscript/vscript_templates.h @@ -0,0 +1,414 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_TEMPLATES_H +#define VSCRIPT_TEMPLATES_H + +#include "tier0/basetypes.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +#define FUNC_APPEND_PARAMS_0 +#define FUNC_APPEND_PARAMS_1 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 1 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); +#define FUNC_APPEND_PARAMS_2 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 2 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); +#define FUNC_APPEND_PARAMS_3 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 3 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); +#define FUNC_APPEND_PARAMS_4 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 4 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); +#define FUNC_APPEND_PARAMS_5 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 5 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); +#define FUNC_APPEND_PARAMS_6 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 6 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); +#define FUNC_APPEND_PARAMS_7 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 7 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); +#define FUNC_APPEND_PARAMS_8 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 8 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); +#define FUNC_APPEND_PARAMS_9 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 9 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); +#define FUNC_APPEND_PARAMS_10 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 10 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); +#define FUNC_APPEND_PARAMS_11 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 11 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); +#define FUNC_APPEND_PARAMS_12 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 12 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); +#define FUNC_APPEND_PARAMS_13 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 13 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_13 ) ); +#define FUNC_APPEND_PARAMS_14 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 14 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_13 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_14 ) ); + +#define DEFINE_NONMEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_NONMEMBER_FUNC_TYPE_DEDUCER ); + +#define DEFINE_MEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNC_TYPE_DEDUCER ); + +//------------------------------------- + +#define DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER ); + +#define ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, (class *)(0), &class::func ); } + +#define ScriptInitFuncDescriptorNamed( pDesc, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, &func ); } +#define ScriptInitFuncDescriptor( pDesc, func ) ScriptInitFuncDescriptorNamed( pDesc, func, #func ) +#define ScriptInitMemberFuncDescriptorNamed( pDesc, class, func, scriptName ) ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) +#define ScriptInitMemberFuncDescriptor( pDesc, class, func ) ScriptInitMemberFuncDescriptorNamed( pDesc, class, func, #func ) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +template +inline void *ScriptConvertFuncPtrToVoid( FUNCPTR_TYPE pFunc ) +{ + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) ) ) + { + union FuncPtrConvert + { + void *p; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvert convert; + convert.pFunc = pFunc; + return convert.p; + } +#if defined( _MSC_VER ) + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.pFunc = pFunc; + if ( convert.mfp.m_delta == 0 ) + { + return convert.mfp.p; + } + AssertMsg( 0, "Function pointer must be from primary vtable" ); + } + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + ( sizeof( int ) * 3 ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + int m_vtordisp; + int m_vtable_index; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.pFunc = pFunc; + if ( convert.mfp.m_delta == 0 ) + { + return convert.mfp.p; + } + AssertMsg( 0, "Function pointer must be from primary vtable" ); + } +#elif defined( GNUC ) + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + AssertMsg( 0, "Note: This path has not been verified yet. See comments below in #else case." ); + + struct GnuMFP + { + union + { + void *funcadr; // If vtable_index_2 is even, then this is the function pointer. + int vtable_index_2; // If vtable_index_2 is odd, then this = vindex*2+1. + }; + int delta; + }; + + GnuMFP *p = (GnuMFP*)&pFunc; + if ( p->vtable_index_2 & 1 ) + { + char **delta = (char**)p->delta; + char *pCur = *delta + (p->vtable_index_2+1)/2; + return (void*)( pCur + 4 ); + } + else + { + return p->funcadr; + } + } +#else +#error "Need to implement code to crack non-offset member function pointer case" + // For gcc, see: http://www.codeproject.com/KB/cpp/FastDelegate.aspx + // + // Current versions of the GNU compiler use a strange and tricky + // optimization. It observes that, for virtual inheritance, you have to look + // up the vtable in order to get the voffset required to calculate the this + // pointer. While you're doing that, you might as well store the function + // pointer in the vtable. By doing this, they combine the m_func_address and + // m_vtable_index fields into one, and they distinguish between them by + // ensuring that function pointers always point to even addresses but vtable + // indices are always odd: + // + // // GNU g++ uses a tricky space optimisation, also adopted by IBM's VisualAge and XLC. + // struct GnuMFP { + // union { + // CODEPTR funcadr; // always even + // int vtable_index_2; // = vindex*2+1, always odd + // }; + // int delta; + // }; + // adjustedthis = this + delta + // if (funcadr & 1) CALL (* ( *delta + (vindex+1)/2) + 4) + // else CALL funcadr + // + // The G++ method is well documented, so it has been adopted by many other + // vendors, including IBM's VisualAge and XLC compilers, recent versions of + // Open64, Pathscale EKO, and Metrowerks' 64-bit compilers. A simpler scheme + // used by earlier versions of GCC is also very common. SGI's now + // discontinued MIPSPro and Pro64 compilers, and Apple's ancient MrCpp + // compiler used this method. (Note that the Pro64 compiler has become the + // open source Open64 compiler). + +#endif + else + AssertMsg( 0, "Member function pointer not supported. Why on earth are you using virtual inheritance!?" ); + return NULL; +} + +template +inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p ) +{ + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) ) ) + { + union FuncPtrConvert + { + void *p; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvert convert; + convert.p = p; + return convert.pFunc; + } + +#if defined( _MSC_VER ) + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.mfp.p = p; + convert.mfp.m_delta = 0; + return convert.pFunc; + } + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + ( sizeof( int ) * 3 ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + int m_vtordisp; + int m_vtable_index; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.mfp.p = p; + convert.mfp.m_delta = 0; + return convert.pFunc; + } +#elif defined( POSIX ) + AssertMsg( 0, "Note: This path has not been implemented yet." ); +#else +#error "Need to implement code to crack non-offset member function pointer case" +#endif + Assert( 0 ); + return NULL; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_0 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_1 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_1 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_2 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_2 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_3 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_3 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_4 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_4 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_5 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_5 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_6 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_6 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_7 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_7 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_8 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_8 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_9 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_9 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_10 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_10 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_11 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_11 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_12 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_12 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_13 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_13 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_14 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_14 + +#define SCRIPT_BINDING_ARGS_0 +#define SCRIPT_BINDING_ARGS_1 pArguments[0] +#define SCRIPT_BINDING_ARGS_2 pArguments[0], pArguments[1] +#define SCRIPT_BINDING_ARGS_3 pArguments[0], pArguments[1], pArguments[2] +#define SCRIPT_BINDING_ARGS_4 pArguments[0], pArguments[1], pArguments[2], pArguments[3] +#define SCRIPT_BINDING_ARGS_5 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4] +#define SCRIPT_BINDING_ARGS_6 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5] +#define SCRIPT_BINDING_ARGS_7 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6] +#define SCRIPT_BINDING_ARGS_8 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7] +#define SCRIPT_BINDING_ARGS_9 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8] +#define SCRIPT_BINDING_ARGS_10 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9] +#define SCRIPT_BINDING_ARGS_11 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10] +#define SCRIPT_BINDING_ARGS_12 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11] +#define SCRIPT_BINDING_ARGS_13 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11], pArguments[12] +#define SCRIPT_BINDING_ARGS_14 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11], pArguments[12], pArguments[13] + + +#define DEFINE_SCRIPT_BINDINGS(N) \ + template \ + class CNonMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( pReturn ); \ + Assert( !pContext ); \ + \ + if ( nArguments != N || !pReturn || pContext ) \ + { \ + return false; \ + } \ + *pReturn = ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ); \ + if ( pReturn->m_type == FIELD_VECTOR ) \ + pReturn->m_pVector = new Vector(*pReturn->m_pVector); \ + return true; \ + } \ + }; \ + \ + template \ + class CNonMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( !pReturn ); \ + Assert( !pContext ); \ + \ + if ( nArguments != N || pReturn || pContext ) \ + { \ + return false; \ + } \ + ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ); \ + return true; \ + } \ + }; \ + \ + template \ + class CMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( pReturn ); \ + Assert( pContext ); \ + \ + if ( nArguments != N || !pReturn || !pContext ) \ + { \ + return false; \ + } \ + *pReturn = (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid(pFunction))( SCRIPT_BINDING_ARGS_##N ); \ + if ( pReturn->m_type == FIELD_VECTOR ) \ + pReturn->m_pVector = new Vector(*pReturn->m_pVector); \ + return true; \ + } \ + }; \ + \ + template \ + class CMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( !pReturn ); \ + Assert( pContext ); \ + \ + if ( nArguments != N || pReturn || !pContext ) \ + { \ + return false; \ + } \ + (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid(pFunction))( SCRIPT_BINDING_ARGS_##N ); \ + return true; \ + } \ + }; \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + typedef FUNCTION_RETTYPE (*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CNonMemberScriptBinding##N::Call; \ + } \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE (FUNCTION_CLASS::*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + typedef FUNCTION_RETTYPE (FUNCTION_CLASS::*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CMemberScriptBinding##N::Call; \ + } \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE (FUNCTION_CLASS::*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const ) \ + { \ + typedef FUNCTION_RETTYPE (FUNCTION_CLASS::*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CMemberScriptBinding##N::Call; \ + } + +FUNC_GENERATE_ALL( DEFINE_SCRIPT_BINDINGS ); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#endif // VSCRIPT_TEMPLATES_H