diff --git a/sp/src/game/server/baseanimating.cpp b/sp/src/game/server/baseanimating.cpp index a2f5e39c..3daeeb73 100644 --- a/sp/src/game/server/baseanimating.cpp +++ b/sp/src/game/server/baseanimating.cpp @@ -301,6 +301,7 @@ BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" ) DEFINE_SCRIPTFUNC( GetSequence, "Gets the current sequence" ) DEFINE_SCRIPTFUNC( SetSequence, "Sets the current sequence" ) DEFINE_SCRIPTFUNC( SequenceLoops, "Loops the current sequence" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSequenceDuration, "SequenceDuration", "Get the specified sequence duration" ) DEFINE_SCRIPTFUNC( LookupSequence, "Gets the index of the specified sequence name" ) DEFINE_SCRIPTFUNC( LookupActivity, "Gets the ID of the specified activity name" ) DEFINE_SCRIPTFUNC_NAMED( HasMovement, "SequenceHasMovement", "Checks if the specified sequence has movement" ) @@ -312,6 +313,10 @@ BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" ) DEFINE_SCRIPTFUNC_NAMED( ScriptSelectWeightedSequence, "SelectWeightedSequence", "Selects a sequence for the specified activity ID" ) DEFINE_SCRIPTFUNC_NAMED( ScriptSelectHeaviestSequence, "SelectHeaviestSequence", "Selects the sequence with the heaviest weight for the specified activity ID" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetSequenceKeyValues, "GetSequenceKeyValues", "Get a KeyValue class instance on the specified sequence. WARNING: This uses the same KeyValue pointer as GetModelKeyValues!" ) + DEFINE_SCRIPTFUNC( GetPlaybackRate, "" ) + DEFINE_SCRIPTFUNC( SetPlaybackRate, "" ) + DEFINE_SCRIPTFUNC( GetCycle, "" ) + DEFINE_SCRIPTFUNC( SetCycle, "" ) DEFINE_SCRIPTFUNC( GetSkin, "Gets the model's skin" ) DEFINE_SCRIPTFUNC( SetSkin, "Sets the model's skin" ) #endif diff --git a/sp/src/game/server/baseanimating.h b/sp/src/game/server/baseanimating.h index ce9b77dc..8f640827 100644 --- a/sp/src/game/server/baseanimating.h +++ b/sp/src/game/server/baseanimating.h @@ -100,6 +100,9 @@ public: inline float SequenceDuration( void ) { return SequenceDuration( m_nSequence ); } float SequenceDuration( CStudioHdr *pStudioHdr, int iSequence ); inline float SequenceDuration( int iSequence ) { return SequenceDuration(GetModelPtr(), iSequence); } +#ifdef MAPBASE_VSCRIPT + inline float ScriptSequenceDuration( int iSequence ) { return SequenceDuration(GetModelPtr(), iSequence); } +#endif float GetSequenceCycleRate( CStudioHdr *pStudioHdr, int iSequence ); inline float GetSequenceCycleRate( int iSequence ) { return GetSequenceCycleRate(GetModelPtr(),iSequence); } float GetLastVisibleCycle( CStudioHdr *pStudioHdr, int iSequence ); diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index ca69fbda..ceeb625f 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -1329,6 +1329,31 @@ void CBaseEntity::FireNamedOutput( const char *pszOutput, variant_t variant, CBa } } +#ifdef MAPBASE_VSCRIPT +void CBaseEntity::ScriptFireOutput( const char *pszOutput, HSCRIPT hActivator, HSCRIPT hCaller, const char *szValue, float flDelay ) +{ + variant_t value; + value.SetString( MAKE_STRING(szValue) ); + + FireNamedOutput( pszOutput, value, ToEnt(hActivator), ToEnt(hCaller), flDelay ); +} + +float CBaseEntity::GetMaxOutputDelay( const char *pszOutput ) +{ + CBaseEntityOutput *pOutput = FindNamedOutput( pszOutput ); + if ( pOutput ) + { + return pOutput->GetMaxDelay(); + } + return 0; +} + +void CBaseEntity::CancelEventsByInput( const char *szInput ) +{ + g_EventQueue.CancelEventsByInput( this, szInput ); +} +#endif // MAPBASE_VSCRIPT + CBaseEntityOutput *CBaseEntity::FindNamedOutput( const char *pszOutput ) { if ( pszOutput == NULL ) @@ -2271,6 +2296,11 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC_NAMED( ScriptClassify, "Classify", "Get Class_T class ID" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAcceptInput, "AcceptInput", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFireOutput, "FireOutput", "Fire an entity output" ) + DEFINE_SCRIPTFUNC( GetMaxOutputDelay, "Get the longest delay for all events attached to an output" ) + DEFINE_SCRIPTFUNC( CancelEventsByInput, "Cancel all I/O events for this entity, match input" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddOutput, "AddOutput", "Add an output" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValue, "GetKeyValue", "Get a keyvalue" ) @@ -2321,6 +2351,9 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC( AddEFlags, "Add Eflags" ) DEFINE_SCRIPTFUNC( RemoveEFlags, "Remove Eflags" ) + DEFINE_SCRIPTFUNC( GetTransmitState, "" ) + DEFINE_SCRIPTFUNC( SetTransmitState, "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMoveType, "GetMoveType", "Get the move type" ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetMoveType, "SetMoveType", "Set the move type" ) @@ -2474,9 +2507,18 @@ void CBaseEntity::UpdateOnRemove( void ) { CallScriptFunctionHandle( hFunc, NULL ); } -#endif +#endif // MAPBASE_VSCRIPT + g_pScriptVM->RemoveInstance( m_hScriptInstance ); m_hScriptInstance = NULL; + +#ifdef MAPBASE_VSCRIPT + if ( m_hfnThink ) + { + g_pScriptVM->ReleaseScript( m_hfnThink ); + m_hfnThink = NULL; + } +#endif // MAPBASE_VSCRIPT } } @@ -4411,7 +4453,11 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, // found a match // mapper debug message +#ifdef MAPBASE + ConColorMsg( 2, Color(CON_COLOR_DEV_VERBOSE), "(%0.2f) input %s: %s.%s(%s)\n", gpGlobals->curtime, pCaller ? STRING(pCaller->m_iName.Get()) : "", GetDebugName(), szInputName, Value.String() ); +#else DevMsg( 2, "(%0.2f) input %s: %s.%s(%s)\n", gpGlobals->curtime, pCaller ? STRING(pCaller->m_iName.Get()) : "", GetDebugName(), szInputName, Value.String() ); +#endif ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer ); if (m_debugOverlays & OVERLAY_MESSAGE_BIT) @@ -4544,10 +4590,25 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, } } +#ifdef MAPBASE + ConColorMsg( 2, Color(CON_COLOR_DEV_VERBOSE), "unhandled input: (%s) -> (%s,%s)\n", szInputName, STRING(m_iClassname), GetDebugName() ); +#else 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())*/ ); +#endif return false; } +#ifdef MAPBASE_VSCRIPT +bool CBaseEntity::ScriptAcceptInput( const char *szInputName, const char *szValue, HSCRIPT hActivator, HSCRIPT hCaller ) +{ + variant_t value; + value.SetString( MAKE_STRING(szValue) ); + + return AcceptInput( szInputName, ToEnt(hActivator), ToEnt(hCaller), value, 0 ); +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Input handler for the entity alpha. // Input : nAlpha - Alpha value (0 - 255). @@ -4587,6 +4648,17 @@ void CBaseEntity::InputColor( inputdata_t &inputdata ) void CBaseEntity::InputUse( inputdata_t &inputdata ) { Use( inputdata.pActivator, inputdata.pCaller, (USE_TYPE)inputdata.nOutputID, 0 ); + +#ifdef MAPBASE + IGameEvent *event = gameeventmanager->CreateEvent( "player_use" ); + if ( event ) + { + event->SetInt( "userid", inputdata.pActivator && inputdata.pActivator->IsPlayer() ? + ((CBasePlayer*)inputdata.pActivator)->GetUserID() : 0 ); + event->SetInt( "entity", entindex() ); + gameeventmanager->FireEvent( event ); + } +#endif // MAPBASE } @@ -4702,7 +4774,19 @@ void CBaseEntity::InputKill( inputdata_t &inputdata ) m_OnKilled.FireOutput( inputdata.pActivator, this ); #endif +#ifdef MAPBASE + // Kick players + if ( IsPlayer() ) + { + engine->ServerCommand( UTIL_VarArgs( "kickid %d CBaseEntity::InputKill()\n", engine->GetPlayerUserId(edict()) ) ); + } + else + { + UTIL_Remove( this ); + } +#else UTIL_Remove( this ); +#endif } void CBaseEntity::InputKillHierarchy( inputdata_t &inputdata ) @@ -4724,6 +4808,9 @@ void CBaseEntity::InputKillHierarchy( inputdata_t &inputdata ) #ifdef MAPBASE m_OnKilled.FireOutput( inputdata.pActivator, this ); + + // Kicking players in InputKillHierarchy does not exist in future Valve games + // if ( IsPlayer() ) #endif UTIL_Remove( this ); @@ -8410,7 +8497,7 @@ void CBaseEntity::ScriptThink(void) #ifdef MAPBASE_VSCRIPT //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -void CBaseEntity::ScriptSetThinkFunction(const char *szFunc, float time) +void CBaseEntity::ScriptSetThinkFunction( const char *szFunc, float flTime ) { // Empty string stops thinking if (!szFunc || szFunc[0] == '\0') @@ -8420,7 +8507,8 @@ void CBaseEntity::ScriptSetThinkFunction(const char *szFunc, float time) else { m_iszScriptThinkFunction = AllocPooledString(szFunc); - SetContextThink( &CBaseEntity::ScriptThink, gpGlobals->curtime + time, "ScriptThink" ); + flTime = max( 0, flTime ); + SetContextThink( &CBaseEntity::ScriptThink, gpGlobals->curtime + flTime, "ScriptThink" ); } } @@ -8434,37 +8522,30 @@ void CBaseEntity::ScriptStopThinkFunction() //----------------------------------------------------------------------------- void CBaseEntity::ScriptThinkH() { - if (m_hfnThink) - { - ScriptVariant_t varThinkRetVal; - if (g_pScriptVM->ExecuteFunction(m_hfnThink, NULL, 0, &varThinkRetVal, NULL, true) == SCRIPT_ERROR) - { - DevWarning("%s FAILED to call script think function (invalid closure)!\n", GetDebugName()); - ScriptStopThink(); - return; - } - - float flThinkFrequency = 0.f; - if (!varThinkRetVal.AssignTo(&flThinkFrequency)) - { - // no return value stops thinking - ScriptStopThink(); - return; - } - - SetNextThink(gpGlobals->curtime + flThinkFrequency, "ScriptThinkH"); - } - else + ScriptVariant_t varThinkRetVal; + if ( g_pScriptVM->ExecuteFunction(m_hfnThink, NULL, 0, &varThinkRetVal, NULL, true) == SCRIPT_ERROR ) { DevWarning("%s FAILED to call script think function (invalid closure)!\n", GetDebugName()); + ScriptStopThink(); + return; } + + float flThinkFrequency = 0.f; + if ( !varThinkRetVal.AssignTo(&flThinkFrequency) ) + { + // no return value stops thinking + ScriptStopThink(); + return; + } + + SetNextThink( gpGlobals->curtime + flThinkFrequency, "ScriptThinkH" ); } -void CBaseEntity::ScriptSetThink(HSCRIPT hFunc, float time) +void CBaseEntity::ScriptSetThink( HSCRIPT hFunc, float flTime ) { - if (hFunc) + if ( hFunc ) { - if (m_hfnThink) + if ( m_hfnThink ) { // release old func ScriptStopThink(); @@ -8473,7 +8554,8 @@ void CBaseEntity::ScriptSetThink(HSCRIPT hFunc, float time) // no type check here, print error on call instead m_hfnThink = hFunc; - SetContextThink( &CBaseEntity::ScriptThinkH, gpGlobals->curtime + time, "ScriptThinkH" ); + flTime = max( 0, flTime ); + SetContextThink( &CBaseEntity::ScriptThinkH, gpGlobals->curtime + flTime, "ScriptThinkH" ); } else { diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index e5aca7e3..98fe9fe1 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -581,6 +581,12 @@ public: void ValidateEntityConnections(); void FireNamedOutput( const char *pszOutput, variant_t variant, CBaseEntity *pActivator, CBaseEntity *pCaller, float flDelay = 0.0f ); CBaseEntityOutput *FindNamedOutput( const char *pszOutput ); +#ifdef MAPBASE_VSCRIPT + void ScriptFireOutput( const char *pszOutput, HSCRIPT hActivator, HSCRIPT hCaller, const char *szValue, float flDelay ); + float GetMaxOutputDelay( const char *pszOutput ); + void CancelEventsByInput( const char *szInput ); +#endif + // Activate - called for each entity after each load game and level load virtual void Activate( void ); @@ -656,6 +662,9 @@ public: // handles an input (usually caused by outputs) // returns true if the the value in the pass in should be set, false if the input is to be ignored virtual bool AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID ); +#ifdef MAPBASE_VSCRIPT + bool ScriptAcceptInput(const char *szInputName, const char *szValue, HSCRIPT hActivator, HSCRIPT hCaller); +#endif // // Input handlers. diff --git a/sp/src/game/server/cbase.cpp b/sp/src/game/server/cbase.cpp index d51de2db..57e465f8 100644 --- a/sp/src/game/server/cbase.cpp +++ b/sp/src/game/server/cbase.cpp @@ -302,7 +302,11 @@ void CBaseEntityOutput::FireOutput(variant_t Value, CBaseEntity *pActivator, CBa ev->m_flDelay, STRING(ev->m_iParameter) ); +#ifdef MAPBASE + ConColorMsg( 2, Color(CON_COLOR_DEV_VERBOSE), "%s", szBuffer ); +#else DevMsg( 2, "%s", szBuffer ); +#endif ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer ); } else @@ -321,7 +325,11 @@ void CBaseEntityOutput::FireOutput(variant_t Value, CBaseEntity *pActivator, CBa STRING(ev->m_iTargetInput), STRING(ev->m_iParameter) ); +#ifdef MAPBASE + ConColorMsg( 2, Color(CON_COLOR_DEV_VERBOSE), "%s", szBuffer ); +#else DevMsg( 2, "%s", szBuffer ); +#endif ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer ); } @@ -342,7 +350,12 @@ void CBaseEntityOutput::FireOutput(variant_t Value, CBaseEntity *pActivator, CBa { char szBuffer[256]; Q_snprintf( szBuffer, sizeof(szBuffer), "Removing from action list: (%s,%s) -> (%s,%s)\n", pCaller ? STRING(pCaller->m_iClassname) : "NULL", pCaller ? STRING(pCaller->GetEntityName()) : "NULL", STRING(ev->m_iTarget), STRING(ev->m_iTargetInput)); + +#ifdef MAPBASE + ConColorMsg( 2, Color(CON_COLOR_DEV_VERBOSE), "%s", szBuffer ); +#else DevMsg( 2, "%s", szBuffer ); +#endif ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer ); bRemove = true; } @@ -836,7 +849,12 @@ void CEventQueue::Dump( void ) //----------------------------------------------------------------------------- // Purpose: adds the action into the correct spot in the priority queue, targeting entity via string name //----------------------------------------------------------------------------- -void CEventQueue::AddEvent( const char *target, const char *targetInput, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID ) +#ifdef MAPBASE_VSCRIPT +intptr_t +#else +void +#endif +CEventQueue::AddEvent( const char *target, const char *targetInput, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID ) { // build the new event EventQueuePrioritizedEvent_t *newEvent = new EventQueuePrioritizedEvent_t; @@ -854,12 +872,21 @@ void CEventQueue::AddEvent( const char *target, const char *targetInput, variant newEvent->m_iOutputID = outputID; AddEvent( newEvent ); + +#ifdef MAPBASE_VSCRIPT + return reinterpret_cast(newEvent); // POINTER_TO_INT +#endif } //----------------------------------------------------------------------------- // Purpose: adds the action into the correct spot in the priority queue, targeting entity via pointer //----------------------------------------------------------------------------- -void CEventQueue::AddEvent( CBaseEntity *target, const char *targetInput, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID ) +#ifdef MAPBASE_VSCRIPT +intptr_t +#else +void +#endif +CEventQueue::AddEvent( CBaseEntity *target, const char *targetInput, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID ) { // build the new event EventQueuePrioritizedEvent_t *newEvent = new EventQueuePrioritizedEvent_t; @@ -877,6 +904,10 @@ void CEventQueue::AddEvent( CBaseEntity *target, const char *targetInput, varian newEvent->m_iOutputID = outputID; AddEvent( newEvent ); + +#ifdef MAPBASE_VSCRIPT + return reinterpret_cast(newEvent); +#endif } void CEventQueue::AddEvent( CBaseEntity *target, const char *action, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID ) @@ -1072,7 +1103,11 @@ void CEventQueue::ServiceEvents( void ) char szBuffer[256]; Q_snprintf( szBuffer, sizeof(szBuffer), "unhandled input: (%s) -> (%s), from (%s,%s); target entity not found\n", STRING(pe->m_iTargetInput), STRING(pe->m_iTarget), pClass, pName ); +#ifdef MAPBASE + ConColorMsg( 2, Color(CON_COLOR_DEV_VERBOSE), "%s", szBuffer ); +#else DevMsg( 2, "%s", szBuffer ); +#endif ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer ); } @@ -1220,6 +1255,80 @@ void ServiceEventQueue( void ) } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Remove events on entity by input. +//----------------------------------------------------------------------------- +void CEventQueue::CancelEventsByInput( CBaseEntity *pTarget, const char *szInput ) +{ + if ( !pTarget ) + return; + + string_t iszDebugName = MAKE_STRING( pTarget->GetDebugName() ); + EventQueuePrioritizedEvent_t *pCur = m_Events.m_pNext; + + while ( pCur ) + { + bool bRemove = false; + + if ( pTarget == pCur->m_pEntTarget || pCur->m_iTarget == iszDebugName ) + { + if ( !V_strncmp( STRING(pCur->m_iTargetInput), szInput, strlen(szInput) ) ) + { + bRemove = true; + } + } + + EventQueuePrioritizedEvent_t *pPrev = pCur; + pCur = pCur->m_pNext; + + if ( bRemove ) + { + RemoveEvent(pPrev); + delete pPrev; + } + } +} + +bool CEventQueue::RemoveEvent( intptr_t event ) +{ + if ( !event ) + return false; + + EventQueuePrioritizedEvent_t *pe = reinterpret_cast(event); // INT_TO_POINTER + + for ( EventQueuePrioritizedEvent_t *pCur = m_Events.m_pNext; pCur; pCur = pCur->m_pNext ) + { + if ( pCur == pe ) + { + RemoveEvent(pCur); + delete pCur; + return true; + } + } + + return false; +} + +float CEventQueue::GetTimeLeft( intptr_t event ) +{ + if ( !event ) + return 0.f; + + EventQueuePrioritizedEvent_t *pe = reinterpret_cast(event); // INT_TO_POINTER + + for ( EventQueuePrioritizedEvent_t *pCur = m_Events.m_pNext; pCur; pCur = pCur->m_pNext ) + { + if ( pCur == pe ) + { + return (pCur->m_flFireTime - gpGlobals->curtime); + } + } + + return 0.f; +} +#endif // MAPBASE_VSCRIPT + // save data description for the event queue BEGIN_SIMPLE_DATADESC( CEventQueue ) diff --git a/sp/src/game/server/cbase.h b/sp/src/game/server/cbase.h index 0d56ec3b..e2a9788d 100644 --- a/sp/src/game/server/cbase.h +++ b/sp/src/game/server/cbase.h @@ -151,4 +151,8 @@ class CSound; #include "ndebugoverlay.h" #include "recipientfilter.h" +#ifdef MAPBASE +#define CON_COLOR_DEV_VERBOSE 192,128,192,255 +#endif + #endif // CBASE_H diff --git a/sp/src/game/server/eventqueue.h b/sp/src/game/server/eventqueue.h index 1c7f030e..61b0d252 100644 --- a/sp/src/game/server/eventqueue.h +++ b/sp/src/game/server/eventqueue.h @@ -40,9 +40,14 @@ class CEventQueue { public: // pushes an event into the queue, targeting a string name (m_iName), or directly by a pointer - void AddEvent( const char *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); +#ifdef MAPBASE_VSCRIPT + intptr_t AddEvent( const char *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); + intptr_t AddEvent( CBaseEntity *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); +#else + void AddEvent( const char *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); + void AddEvent( CBaseEntity *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); +#endif void AddEvent( CBaseEntity *target, const char *action, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); - void AddEvent( CBaseEntity *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); void CancelEvents( CBaseEntity *pCaller ); void CancelEventOn( CBaseEntity *pTarget, const char *sInputName ); @@ -66,6 +71,12 @@ public: void Dump( void ); +#ifdef MAPBASE_VSCRIPT + void CancelEventsByInput( CBaseEntity *pTarget, const char *szInput ); + bool RemoveEvent( intptr_t event ); + float GetTimeLeft( intptr_t event ); +#endif // MAPBASE_VSCRIPT + private: void AddEvent( EventQueuePrioritizedEvent_t *event ); diff --git a/sp/src/game/server/mapbase/logic_eventlistener.cpp b/sp/src/game/server/mapbase/logic_eventlistener.cpp index 8f0f0058..903703f3 100644 --- a/sp/src/game/server/mapbase/logic_eventlistener.cpp +++ b/sp/src/game/server/mapbase/logic_eventlistener.cpp @@ -110,8 +110,6 @@ bool CLogicEventListener::KeyValue(const char *szKeyName, const char *szValue) //----------------------------------------------------------------------------- void CLogicEventListener::FireGameEvent( IGameEvent *event ) { - DevMsg("Heard Event\n"); - if (m_bDisabled) return; diff --git a/sp/src/game/server/triggers.cpp b/sp/src/game/server/triggers.cpp index abc906a3..7f2f1f08 100644 --- a/sp/src/game/server/triggers.cpp +++ b/sp/src/game/server/triggers.cpp @@ -131,6 +131,16 @@ BEGIN_DATADESC( CBaseTrigger ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT + +BEGIN_ENT_SCRIPTDESC( CBaseTrigger, CBaseEntity, "Trigger entity" ) + DEFINE_SCRIPTFUNC( Enable, "" ) + DEFINE_SCRIPTFUNC( Disable, "" ) + DEFINE_SCRIPTFUNC( TouchTest, "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsTouching, "IsTouching", "Checks whether the passed entity is touching the trigger." ) +END_SCRIPTDESC(); + +#endif // MAPBASE_VSCRIPT LINK_ENTITY_TO_CLASS( trigger, CBaseTrigger ); @@ -560,6 +570,19 @@ bool CBaseTrigger::IsTouching( CBaseEntity *pOther ) return ( m_hTouchingEntities.Find( hOther ) != m_hTouchingEntities.InvalidIndex() ); } +#ifdef MAPBASE_VSCRIPT +bool CBaseTrigger::ScriptIsTouching( HSCRIPT hOther ) +{ + CBaseEntity *pOther = ToEnt(hOther); + if ( !pOther ) + return false; + + EHANDLE eOther; + eOther = pOther; + return ( m_hTouchingEntities.Find( eOther ) != m_hTouchingEntities.InvalidIndex() ); +} +#endif // MAPBASE_VSCRIPT + //----------------------------------------------------------------------------- // Purpose: Return a pointer to the first entity of the specified type being touched by this trigger //----------------------------------------------------------------------------- @@ -3605,10 +3628,18 @@ int CTriggerCamera::ScriptGetFov(void) //----------------------------------------------------------------------------- void CTriggerCamera::ScriptSetFov(int iFOV, float fovSpeed) { - if (m_hPlayer) +#ifdef MAPBASE + m_fov = iFOV; + m_fovSpeed = fovSpeed; + + if ( m_state == USE_ON && m_hPlayer ) + { +#else + if ( m_hPlayer ) { m_fov = iFOV; m_fovSpeed = fovSpeed; +#endif CBasePlayer* pBasePlayer = (CBasePlayer*)m_hPlayer.Get(); pBasePlayer->SetFOV(this, iFOV, fovSpeed); diff --git a/sp/src/game/server/triggers.h b/sp/src/game/server/triggers.h index a1a2cdb7..1dc87e46 100644 --- a/sp/src/game/server/triggers.h +++ b/sp/src/game/server/triggers.h @@ -82,6 +82,9 @@ public: virtual void StartTouch(CBaseEntity *pOther); virtual void EndTouch(CBaseEntity *pOther); bool IsTouching( CBaseEntity *pOther ); +#ifdef MAPBASE_VSCRIPT + bool ScriptIsTouching( HSCRIPT hOther ); +#endif CBaseEntity *GetTouchedEntityOfType( const char *sClassName ); @@ -123,6 +126,9 @@ protected: #endif DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif }; //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/vscript_server.cpp b/sp/src/game/server/vscript_server.cpp index f5e2805f..44234516 100644 --- a/sp/src/game/server/vscript_server.cpp +++ b/sp/src/game/server/vscript_server.cpp @@ -17,7 +17,6 @@ #include "gamerules.h" #include "vscript_server.nut" #ifdef MAPBASE_VSCRIPT -#include "particle_parse.h" #include "world.h" #endif @@ -106,7 +105,12 @@ public: { return ToHScript( gEntList.FindEntityByClassnameWithin( ToEnt( hStartEntity ), szName, vecSrc, flRadius ) ); } - +#ifdef MAPBASE_VSCRIPT + HSCRIPT FindByClassnameWithinBox( HSCRIPT hStartEntity , const char *szName, const Vector &vecMins, const Vector &vecMaxs ) + { + return ToHScript( gEntList.FindEntityByClassnameWithin( ToEnt( hStartEntity ), szName, vecMins, vecMaxs ) ); + } +#endif private: } g_ScriptEntityIterator; @@ -126,6 +130,9 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptEntityIterator, "CEntities", SCRIPT_SINGLETO 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" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( FindByClassnameWithinBox, "Find entities by class name within an AABB. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) +#endif END_SCRIPTDESC(); // ---------------------------------------------------------------------------- @@ -815,6 +822,12 @@ static void SendToConsole( const char *pszCommand ) engine->ClientCommand( pPlayer->edict(), pszCommand ); } +static void SendToConsoleServer( const char *pszCommand ) +{ + // TODO: whitelist for multiplayer + engine->ServerCommand( UTIL_VarArgs("%s\n", pszCommand) ); +} + static const char *GetMapName() { return STRING( gpGlobals->mapname ); @@ -827,7 +840,11 @@ static const char *DoUniqueString( const char *pszBase ) return szBuf; } +#ifdef MAPBASE_VSCRIPT +static int DoEntFire( const char *pszTarget, const char *pszAction, const char *pszValue, float delay, HSCRIPT hActivator, HSCRIPT hCaller ) +#else static void DoEntFire( const char *pszTarget, const char *pszAction, const char *pszValue, float delay, HSCRIPT hActivator, HSCRIPT hCaller ) +#endif { const char *target = "", *action = "Use"; variant_t value; @@ -841,7 +858,7 @@ static void DoEntFire( const char *pszTarget, const char *pszAction, const char // ent_fire point_servercommand command "rcon_password mynewpassword" if ( gpGlobals->maxClients > 1 && V_stricmp( target, "point_servercommand" ) == 0 ) { - return; + return 0; } if ( *pszAction ) @@ -857,6 +874,9 @@ static void DoEntFire( const char *pszTarget, const char *pszAction, const char delay = 0; } +#ifdef MAPBASE_VSCRIPT + return +#endif g_EventQueue.AddEvent( target, action, value, delay, ToEnt(hActivator), ToEnt(hCaller) ); } @@ -891,7 +911,11 @@ HSCRIPT CreateProp( const char *pszEntityName, const Vector &vOrigin, const char //-------------------------------------------------------------------------------------------------- // Use an entity's script instance to add an entity IO event (used for firing events on unnamed entities from vscript) //-------------------------------------------------------------------------------------------------- +#ifdef MAPBASE_VSCRIPT +static int DoEntFireByInstanceHandle( HSCRIPT hTarget, const char *pszAction, const char *pszValue, float delay, HSCRIPT hActivator, HSCRIPT hCaller ) +#else static void DoEntFireByInstanceHandle( HSCRIPT hTarget, const char *pszAction, const char *pszValue, float delay, HSCRIPT hActivator, HSCRIPT hCaller ) +#endif { const char *action = "Use"; variant_t value; @@ -914,9 +938,16 @@ static void DoEntFireByInstanceHandle( HSCRIPT hTarget, const char *pszAction, c if ( !pTarget ) { Warning( "VScript error: DoEntFire was passed an invalid entity instance.\n" ); +#ifdef MAPBASE_VSCRIPT + return 0; +#else return; +#endif } +#ifdef MAPBASE_VSCRIPT + return +#endif g_EventQueue.AddEvent( pTarget, action, value, delay, ToEnt(hActivator), ToEnt(hCaller) ); } @@ -937,14 +968,16 @@ static float ScriptTraceLine( const Vector &vecStart, const Vector &vecEnd, HSCR } #ifdef MAPBASE_VSCRIPT -//----------------------------------------------------------------------------- -// Simple particle effect dispatch -//----------------------------------------------------------------------------- -static void ScriptDispatchParticleEffect(const char *pszParticleName, const Vector &vecOrigin, const QAngle &vecAngles) +static bool CancelEntityIOEvent( int event ) { - DispatchParticleEffect(pszParticleName, vecOrigin, vecAngles); + return g_EventQueue.RemoveEvent(event); } -#endif + +static float GetEntityIOEventTimeLeft( int event ) +{ + return g_EventQueue.GetTimeLeft(event); +} +#endif // MAPBASE_VSCRIPT bool VScriptServerInit() { @@ -1007,7 +1040,17 @@ bool VScriptServerInit() Log( "VSCRIPT: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); #endif +#ifdef MAPBASE_VSCRIPT + // MULTIPLAYER + // ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByIndex, "GetPlayerByIndex", "PlayerInstanceFromIndex" ); + // ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByUserId, "GetPlayerByUserId", "GetPlayerFromUserID" ); + // ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByName, "GetPlayerByName", "" ); + // ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetPlayerByNetworkID, "GetPlayerByNetworkID", "" ); + ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_ShowMessageAll, "ShowMessage", "Print a hud message on all clients" ); +#else + ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_ShowMessageAll, "ShowMessage", "Print a hud message on all clients" ); +#endif ScriptRegisterFunction( g_pScriptVM, SendToConsole, "Send a string to the console as a command" ); ScriptRegisterFunction( g_pScriptVM, GetMapName, "Get the name of the map."); @@ -1016,11 +1059,15 @@ bool VScriptServerInit() ScriptRegisterFunction( g_pScriptVM, Time, "Get the current server time" ); ScriptRegisterFunction( g_pScriptVM, FrameTime, "Get the time spent on the server in the last frame" ); #ifdef MAPBASE_VSCRIPT + ScriptRegisterFunction( g_pScriptVM, SendToConsoleServer, "Send a string to the server console as a command" ); ScriptRegisterFunction( g_pScriptVM, MaxPlayers, "Get the maximum number of players allowed on this server" ); ScriptRegisterFunction( g_pScriptVM, IntervalPerTick, "Get the interval used between each tick" ); ScriptRegisterFunction( g_pScriptVM, DoEntFire, SCRIPT_ALIAS( "EntFire", "Generate an entity i/o event" ) ); ScriptRegisterFunction( g_pScriptVM, DoEntFireByInstanceHandle, SCRIPT_ALIAS( "EntFireByHandle", "Generate an entity i/o event. First parameter is an entity instance." ) ); // ScriptRegisterFunction( g_pScriptVM, IsValidEntity, "Returns true if the entity is valid." ); + + ScriptRegisterFunction( g_pScriptVM, CancelEntityIOEvent, "Remove entity I/O event." ); + ScriptRegisterFunction( g_pScriptVM, GetEntityIOEventTimeLeft, "Get time left on entity I/O event." ); #else 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." ); @@ -1033,10 +1080,7 @@ bool VScriptServerInit() #endif ScriptRegisterFunction( g_pScriptVM, DoIncludeScript, "Execute a script (internal)" ); ScriptRegisterFunction( g_pScriptVM, CreateProp, "Create a physics prop" ); -#ifdef MAPBASE_VSCRIPT - ScriptRegisterFunctionNamed( g_pScriptVM, ScriptDispatchParticleEffect, "DispatchParticleEffect", "Dispatches a one-off particle system" ); -#endif - + if ( GameRules() ) { GameRules()->RegisterScriptFunctions(); @@ -1045,7 +1089,7 @@ bool VScriptServerInit() g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); #ifdef MAPBASE_VSCRIPT g_pScriptVM->RegisterInstance( &g_ScriptDebugOverlay, "debugoverlay" ); -#endif +#endif // MAPBASE_VSCRIPT #ifdef MAPBASE_VSCRIPT g_pScriptVM->RegisterAllClasses(); diff --git a/sp/src/game/server/vscript_server.nut b/sp/src/game/server/vscript_server.nut index b4e5804b..5294ab4b 100644 --- a/sp/src/game/server/vscript_server.nut +++ b/sp/src/game/server/vscript_server.nut @@ -7,16 +7,19 @@ static char g_Script_vscript_server[] = R"vscript( function UniqueString( string = "" ) { - return DoUniqueString( string.tostring() ); + return ::DoUniqueString( string.tostring() ); } +local DoEntFire = ::DoEntFire +local DoEntFireByInstanceHandle = ::DoEntFireByInstanceHandle + function EntFire( target, action, value = null, delay = 0.0, activator = null, caller = null ) { if ( !value ) { value = ""; } - + if ( "self" in this ) { if ( !caller ) @@ -29,8 +32,8 @@ function EntFire( target, action, value = null, delay = 0.0, activator = null, c activator = self; } } - - DoEntFire( target.tostring(), action.tostring(), value.tostring(), delay, activator, caller ); + + return DoEntFire( target.tostring(), action.tostring(), value.tostring(), delay, activator, caller ); } function EntFireByHandle( target, action, value = null, delay = 0.0, activator = null, caller = null ) @@ -39,7 +42,7 @@ function EntFireByHandle( target, action, value = null, delay = 0.0, activator = { value = ""; } - + if ( "self" in this ) { if ( !caller ) @@ -52,8 +55,8 @@ function EntFireByHandle( target, action, value = null, delay = 0.0, activator = activator = self; } } - - DoEntFireByInstanceHandle( target, action.tostring(), value.tostring(), delay, activator, caller ); + + return DoEntFireByInstanceHandle( target, action.tostring(), value.tostring(), delay, activator, caller ); } function __ReplaceClosures( script, scope ) @@ -62,11 +65,11 @@ function __ReplaceClosures( script, scope ) { scope = getroottable(); } - + local tempParent = { getroottable = function() { return null; } }; local temp = { runscript = script }; temp.set_delegate(tempParent); - + temp.runscript() foreach( key,val in temp ) { @@ -78,11 +81,11 @@ function __ReplaceClosures( script, scope ) } } -__OutputsPattern <- regexp("^On.*Output$"); +local __OutputsPattern = regexp("^On.*Output$"); function ConnectOutputs( table ) { - const nCharsToStrip = 6; + local nCharsToStrip = 6; foreach( key, val in table ) { if ( typeof( val ) == "function" && __OutputsPattern.match( key ) ) @@ -95,7 +98,7 @@ function ConnectOutputs( table ) function IncludeScript( name, scope = null ) { - if ( scope == null ) + if ( !scope ) { scope = this; } diff --git a/sp/src/game/shared/gamestringpool.cpp b/sp/src/game/shared/gamestringpool.cpp index a0544cb7..ee529bf1 100644 --- a/sp/src/game/shared/gamestringpool.cpp +++ b/sp/src/game/shared/gamestringpool.cpp @@ -55,12 +55,23 @@ public: void Dump( void ) { - for (UtlHashHandle_t i = m_Strings.FirstHandle(); i != m_Strings.InvalidHandle(); i = m_Strings.NextHandle(i)) + CUtlVector strings( 0, m_Strings.Count() ); + for ( UtlHashHandle_t i = m_Strings.FirstHandle(); i != m_Strings.InvalidHandle(); i = m_Strings.NextHandle(i) ) { - DevMsg(" %d (0x%p) : %s\n", i, m_Strings[i], m_Strings[i]); + strings.AddToTail( m_Strings[i] ); } - DevMsg("\n"); - DevMsg("Size: %d items\n", m_Strings.Count()); + + struct _Local { + static int __cdecl F(const char * const *a, const char * const *b) { return strcmp(*a, *b); } + }; + strings.Sort( _Local::F ); + + for ( int i = 0; i < strings.Count(); ++i ) + { + DevMsg( " %d (0x%p) : %s\n", i, strings[i], strings[i] ); + } + DevMsg( "\n" ); + DevMsg( "Size: %d items\n", strings.Count() ); } const char *Find(const char *string) diff --git a/sp/src/game/shared/mapbase/vscript_funcs_math.cpp b/sp/src/game/shared/mapbase/vscript_funcs_math.cpp index e6162f2e..d214a301 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_math.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_math.cpp @@ -389,10 +389,10 @@ void RegisterMathScriptFunctions() { ScriptRegisterFunction( g_pScriptVM, RandomFloat, "Generate a random floating point number within a range, inclusive." ); ScriptRegisterFunction( g_pScriptVM, RandomInt, "Generate a random integer within a range, inclusive." ); - ScriptRegisterFunction( g_pScriptVM, Approach, "Returns a value which approaches the target value from the input value with the specified speed." ); + //ScriptRegisterFunction( g_pScriptVM, Approach, "Returns a value which approaches the target value from the input value with the specified speed." ); ScriptRegisterFunction( g_pScriptVM, ApproachAngle, "Returns an angle which approaches the target angle from the input angle with the specified speed." ); ScriptRegisterFunction( g_pScriptVM, AngleDiff, "Returns the degrees difference between two yaw angles." ); - ScriptRegisterFunction( g_pScriptVM, AngleDistance, "Returns the distance between two angles." ); + //ScriptRegisterFunction( g_pScriptVM, AngleDistance, "Returns the distance between two angles." ); ScriptRegisterFunction( g_pScriptVM, AngleNormalize, "Clamps an angle to be in between -360 and 360." ); ScriptRegisterFunction( g_pScriptVM, AngleNormalizePositive, "Clamps an angle to be in between 0 and 360." ); ScriptRegisterFunction( g_pScriptVM, AnglesAreEqual, "Checks if two angles are equal based on a given tolerance value." ); diff --git a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp index 4625ec68..3d322593 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp @@ -19,7 +19,15 @@ #ifndef CLIENT_DLL #include "globalstate.h" #include "vscript_server.h" -#endif + +#include "usermessages.h" +#endif // !CLIENT_DLL + +#include "filesystem.h" +#include "igameevents.h" +#include "icommandline.h" +#include "con_nprint.h" +#include "particle_parse.h" #include "vscript_funcs_shared.h" @@ -361,7 +369,7 @@ END_SCRIPTDESC(); void EmitSoundOn( const char *pszSound, HSCRIPT hEnt ) { CBaseEntity *pEnt = ToEnt( hEnt ); - if (!hEnt || !pEnt) + if (!pEnt) return; pEnt->EmitSound( pszSound ); @@ -371,7 +379,7 @@ void EmitSoundOnClient( const char *pszSound, HSCRIPT hEnt, HSCRIPT hPlayer ) { CBaseEntity *pEnt = ToEnt( hEnt ); CBasePlayer *pPlayer = ToBasePlayer( ToEnt( hPlayer ) ); - if (!hPlayer || !pEnt || !pPlayer) + if (!pEnt || !pPlayer) return; CSingleUserRecipientFilter filter( pPlayer ); @@ -391,7 +399,7 @@ void AddThinkToEnt( HSCRIPT entity, const char *pszFuncName ) if (!pEntity) return; - pEntity->ScriptSetThinkFunction(pszFuncName, 0.f); + pEntity->ScriptSetThinkFunction(pszFuncName, TICK_INTERVAL); } HSCRIPT EntIndexToHScript( int index ) @@ -471,6 +479,11 @@ HSCRIPT SpawnEntityFromTable( const char *pszClassname, HSCRIPT hKV ) // Mapbase-specific functions start here //----------------------------------------------------------------------------- +static void ScriptMsg( const char *msg ) +{ + Msg( "%s", msg ); +} + static void ScriptColorPrint( int r, int g, int b, const char *pszMsg ) { const Color clr(r, g, b, 255); @@ -525,7 +538,7 @@ void ScriptDispatchSpawn( HSCRIPT hEntity ) DispatchSpawn( pEntity ); } } -#endif +#endif // !CLIENT_DLL //----------------------------------------------------------------------------- // @@ -901,8 +914,7 @@ static void SetPhysVelocity( HSCRIPT hPhys, const Vector& vecVelocity, const Vec if (!pPhys) return; - if (pPhys) - pPhys->SetVelocity( &vecVelocity, &vecAngVelocity ); + pPhys->SetVelocity( &vecVelocity, &vecAngVelocity ); } static void AddPhysVelocity( HSCRIPT hPhys, const Vector& vecVelocity, const Vector& vecAngVelocity ) @@ -911,13 +923,454 @@ static void AddPhysVelocity( HSCRIPT hPhys, const Vector& vecVelocity, const Vec if (!pPhys) return; - if (pPhys) - pPhys->AddVelocity( &vecVelocity, &vecAngVelocity ); + pPhys->AddVelocity( &vecVelocity, &vecAngVelocity ); } //============================================================================= //============================================================================= +class CScriptGameEventListener : public IGameEventListener2, public CAutoGameSystem +{ +public: + CScriptGameEventListener() : m_bActive(false) {} + ~CScriptGameEventListener() + { + StopListeningForEvent(); + } + + intptr_t ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ); + void StopListeningForEvent(); + +public: + static bool StopListeningToGameEvent( intptr_t listener ); + static void StopListeningToAllGameEvents( const char* szContext ); + +public: + void FireGameEvent( IGameEvent *event ); + void LevelShutdownPreEntity(); + +private: + bool m_bActive; + const char *m_pszContext; + HSCRIPT m_hCallback; + + static const char *FindContext( const char *szContext, CScriptGameEventListener *pIgnore = NULL ); + //inline const char *GetContext( CScriptGameEventListener *p ); + //inline const char *GetContext(); + +public: + static void DumpEventListeners(); +#ifndef CLIENT_DLL + static void LoadAllEvents(); + static void LoadEventsFromFile( const char *filename, const char *pathID = NULL ); + static void WriteEventData( IGameEvent *event, HSCRIPT hTable ); +#endif // !CLIENT_DLL + +private: +#ifndef CLIENT_DLL + static CUtlVector< KeyValues* > s_GameEvents; +#endif // !CLIENT_DLL + static CUtlVectorAutoPurge< CScriptGameEventListener* > s_GameEventListeners; + +}; + +#ifndef CLIENT_DLL +CUtlVector< KeyValues* > CScriptGameEventListener::s_GameEvents; +#endif // !CLIENT_DLL +CUtlVectorAutoPurge< CScriptGameEventListener* > CScriptGameEventListener::s_GameEventListeners; + +#if 0 +#ifdef CLIENT_DLL +CON_COMMAND_F( cl_dump_script_game_event_listeners, "Dump all game event listeners created from script.", FCVAR_CHEAT ) +{ + CScriptGameEventListener::DumpEventListeners(); +} +#else // GAME_DLL +CON_COMMAND_F( dump_script_game_event_listeners, "Dump all game event listeners created from script.", FCVAR_CHEAT ) +{ + CScriptGameEventListener::DumpEventListeners(); +} +#endif // CLIENT_DLL +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CScriptGameEventListener::DumpEventListeners() +{ + Msg("--- Script game event listener dump start\n"); + FOR_EACH_VEC( s_GameEventListeners, i ) + { + Msg(" %d (0x%p) %d : %s\n", i,s_GameEventListeners[i], + s_GameEventListeners[i], + s_GameEventListeners[i]->m_pszContext ? s_GameEventListeners[i]->m_pszContext : ""); + } + Msg("--- Script game event listener dump end\n"); +} + +void CScriptGameEventListener::FireGameEvent( IGameEvent *event ) +{ + ScriptVariant_t hTable; + g_pScriptVM->CreateTable( hTable ); + // TODO: pass event data on client +#ifdef GAME_DLL + WriteEventData( event, hTable ); +#endif + g_pScriptVM->SetValue( hTable, "game_event_listener", reinterpret_cast(this) ); // POINTER_TO_INT + // g_pScriptVM->SetValue( hTable, "game_event_name", event->GetName() ); + g_pScriptVM->ExecuteFunction( m_hCallback, &hTable, 1, NULL, NULL, true ); + g_pScriptVM->ReleaseScript( hTable ); +} + +void CScriptGameEventListener::LevelShutdownPreEntity() +{ + s_GameEventListeners.FindAndFastRemove(this); + delete this; +} + +//----------------------------------------------------------------------------- +// Executed in LevelInitPreEntity +//----------------------------------------------------------------------------- +#ifndef CLIENT_DLL +void CScriptGameEventListener::LoadAllEvents() +{ + // Listed in the same order they are loaded in GameEventManager + const char *filenames[] = + { + "resource/serverevents.res", + "resource/gameevents.res", + "resource/mapbaseevents.res", + "resource/modevents.res" + }; + + const char *pathlist[] = + { + "GAME", + "MOD" + }; + + // Destroy old KeyValues + if ( s_GameEvents.Count() ) + { + for ( int i = 0; i < s_GameEvents.Count(); ++i ) + s_GameEvents[i]->deleteThis(); + s_GameEvents.Purge(); + } + + for ( int j = 0; j < ARRAYSIZE(pathlist); ++j ) + for ( int i = 0; i < ARRAYSIZE(filenames); ++i ) + { + LoadEventsFromFile( filenames[i], pathlist[j] ); + } +} + +//----------------------------------------------------------------------------- +// Load event files into a lookup array to be able to return the event data to the VM. +//----------------------------------------------------------------------------- +void CScriptGameEventListener::LoadEventsFromFile( const char *filename, const char *pathID ) +{ + KeyValues *pKV = new KeyValues("GameEvents"); + + if ( !pKV->LoadFromFile( filesystem, filename, pathID ) ) + { + // DevMsg( "CScriptGameEventListener::LoadEventsFromFile: Failed to load file %s, %s\n", filename, pathID ); + pKV->deleteThis(); + return; + } + + // Set the key value types to what they are from their string description values to read the correct data type in WriteEventData. + // There might be a better way of doing this, but this is okay since it's only done on file load. + for ( KeyValues *key = pKV->GetFirstSubKey(); key; key = key->GetNextKey() ) + for ( KeyValues *sub = key->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) + { + if ( sub->GetDataType() == KeyValues::TYPE_STRING ) + { + const char *szVal = sub->GetString(); + if ( !V_stricmp( szVal, "byte" ) || !V_stricmp( szVal, "short" ) || !V_stricmp( szVal, "long" ) || !V_stricmp( szVal, "bool" ) ) + { + sub->SetInt( NULL, 0 ); + } + else if ( !V_stricmp( szVal, "float" ) ) + { + sub->SetFloat( NULL, 0.0f ); + } + } + // none : value is not networked + // string : a zero terminated string + // bool : unsigned int, 1 bit + // byte : unsigned int, 8 bit + // short : signed int, 16 bit + // long : signed int, 32 bit + // float : float, 32 bit + } + + DevMsg( 2, "CScriptGameEventListener::LoadEventsFromFile: Loaded %s, %s\n", filename, pathID ); + + s_GameEvents.AddToTail(pKV); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CScriptGameEventListener::WriteEventData( IGameEvent *event, HSCRIPT hTable ) +{ + // TODO: Something more efficient than iterating through all the events that ever exist one by one + + const char *szEvent = event->GetName(); + for ( int i = 0; i < s_GameEvents.Count(); ++i ) + { + KeyValues *pKV = s_GameEvents[i]; + for ( KeyValues *key = pKV->GetFirstSubKey(); key; key = key->GetNextKey() ) + { + if ( !V_stricmp( key->GetName(), szEvent ) ) + { + for ( KeyValues *sub = key->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) + { + const char *szKey = sub->GetName(); + switch ( sub->GetDataType() ) + { + case KeyValues::TYPE_STRING: g_pScriptVM->SetValue( hTable, szKey, event->GetString( szKey ) ); break; + case KeyValues::TYPE_INT: g_pScriptVM->SetValue( hTable, szKey, event->GetInt ( szKey ) ); break; + case KeyValues::TYPE_FLOAT: g_pScriptVM->SetValue( hTable, szKey, event->GetFloat ( szKey ) ); break; + // default: DevWarning( 2, "CScriptGameEventListener::WriteEventData: unknown data type '%d' on key '%s' in event '%s'\n", sub->GetDataType(), szKey, szEvent ); + } + } + return; + } + } + } +} +#endif // !CLIENT_DLL +//----------------------------------------------------------------------------- +// Find if context is in use by others; used to alloc/dealloc only when required. +// Returns allocated pointer to string +// Expects non-NULL context input +//----------------------------------------------------------------------------- +const char *CScriptGameEventListener::FindContext( const char *szContext, CScriptGameEventListener *pIgnore ) +{ + for ( int i = s_GameEventListeners.Count(); i--; ) + { + CScriptGameEventListener *pCur = s_GameEventListeners[i]; + if ( pCur != pIgnore ) + { + if ( pCur->m_pszContext && !V_stricmp( szContext, pCur->m_pszContext ) ) + { + return pCur->m_pszContext; + } + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +intptr_t CScriptGameEventListener::ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ) +{ + m_bActive = true; + + char *psz; + + if ( szContext && *szContext ) + { + psz = const_cast(FindContext(szContext)); + if ( !psz ) + { + int len = V_strlen(szContext) + 1; + if ( len > 1 ) + { + int size = min( len, 256 ); // arbitrary clamp + psz = new char[size]; + V_strncpy( psz, szContext, size ); + } + } + } + else + { + psz = NULL; + } + + m_pszContext = psz; + m_hCallback = hFunc; + + if ( gameeventmanager ) +#ifdef CLIENT_DLL + gameeventmanager->AddListener( this, szEvent, false ); +#else + gameeventmanager->AddListener( this, szEvent, true ); +#endif + s_GameEventListeners.AddToTail( this ); + + return reinterpret_cast(this); // POINTER_TO_INT +} + +//----------------------------------------------------------------------------- +// Free stuff. Called from the destructor, does not remove itself from the listener list. +//----------------------------------------------------------------------------- +void CScriptGameEventListener::StopListeningForEvent() +{ + if ( !m_bActive ) + return; + + if ( g_pScriptVM ) + { + g_pScriptVM->ReleaseScript( m_hCallback ); + } + else if ( m_hCallback ) + { + AssertMsg( 0, "LEAK (0x%p)\n", (void*)m_hCallback ); + } + + if ( m_pszContext ) + { + if ( !FindContext( m_pszContext, this ) ) + { + delete[] m_pszContext; + } + + m_pszContext = NULL; + } + + m_hCallback = NULL; + + if ( gameeventmanager ) + gameeventmanager->RemoveListener( this ); + + m_bActive = false; +} + +//----------------------------------------------------------------------------- +// Stop the specified event listener. +//----------------------------------------------------------------------------- +bool CScriptGameEventListener::StopListeningToGameEvent( intptr_t listener ) +{ + CScriptGameEventListener *p = reinterpret_cast(listener); // INT_TO_POINTER + + bool bRemoved = s_GameEventListeners.FindAndFastRemove(p); + if ( bRemoved ) + { + delete p; + } + + return bRemoved; +} + +//----------------------------------------------------------------------------- +// Stops listening to all events within a context. +//----------------------------------------------------------------------------- +void CScriptGameEventListener::StopListeningToAllGameEvents( const char* szContext ) +{ + if ( szContext ) + { + if ( *szContext ) + { + // Iterate from the end so they can be safely removed as they are deleted + for ( int i = s_GameEventListeners.Count(); i--; ) + { + CScriptGameEventListener *pCur = s_GameEventListeners[i]; + if ( pCur->m_pszContext && !V_stricmp( szContext, pCur->m_pszContext ) ) + { + s_GameEventListeners.Remove(i); // keep list order + delete pCur; + } + } + } + else // empty (NULL) context + { + for ( int i = s_GameEventListeners.Count(); i--; ) + { + CScriptGameEventListener *pCur = s_GameEventListeners[i]; + if ( !pCur->m_pszContext ) + { + s_GameEventListeners.Remove(i); + delete pCur; + } + } + } + } +#if 0 + if ( !szContext ) + { + for ( int i = s_GameEventListeners.Count(); i--; ) + delete s_GameEventListeners[i]; + s_GameEventListeners.Purge(); + } +#endif +} + +//============================================================================= +//============================================================================= + +static int ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ) +{ + CScriptGameEventListener *p = new CScriptGameEventListener(); + return p->ListenToGameEvent( szEvent, hFunc, szContext ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static void FireGameEvent( const char* szEvent, HSCRIPT hTable ) +{ + IGameEvent *event = gameeventmanager->CreateEvent( szEvent ); + if ( event ) + { + ScriptVariant_t key, val; + int nIterator = -1; + while ( ( nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &key, &val ) ) != -1 ) + { + switch ( val.m_type ) + { + case FIELD_FLOAT: event->SetFloat ( key.m_pszString, val.m_float ); break; + case FIELD_INTEGER: event->SetInt ( key.m_pszString, val.m_int ); break; + case FIELD_BOOLEAN: event->SetBool ( key.m_pszString, val.m_bool ); break; + case FIELD_CSTRING: event->SetString( key.m_pszString, val.m_pszString ); break; + } + + g_pScriptVM->ReleaseValue(key); + g_pScriptVM->ReleaseValue(val); + } + +#ifdef CLIENT_DLL + gameeventmanager->FireEventClientSide(event); +#else + gameeventmanager->FireEvent(event); +#endif + } +} + +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Copy of FireGameEvent, server only with no broadcast to clients. +//----------------------------------------------------------------------------- +static void FireGameEventLocal( const char* szEvent, HSCRIPT hTable ) +{ + IGameEvent *event = gameeventmanager->CreateEvent( szEvent ); + if ( event ) + { + ScriptVariant_t key, val; + int nIterator = -1; + while ( ( nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &key, &val ) ) != -1 ) + { + switch ( val.m_type ) + { + case FIELD_FLOAT: event->SetFloat ( key.m_pszString, val.m_float ); break; + case FIELD_INTEGER: event->SetInt ( key.m_pszString, val.m_int ); break; + case FIELD_BOOLEAN: event->SetBool ( key.m_pszString, val.m_bool ); break; + case FIELD_CSTRING: event->SetString( key.m_pszString, val.m_pszString ); break; + } + + g_pScriptVM->ReleaseValue(key); + g_pScriptVM->ReleaseValue(val); + } + + gameeventmanager->FireEvent(event,true); + } +} +#endif // !CLIENT_DLL +//============================================================================= +//============================================================================= + static int ScriptPrecacheModel( const char *modelname ) { return CBaseEntity::PrecacheModel( modelname ); @@ -938,7 +1391,7 @@ static void ScriptEntitiesInBox( HSCRIPT hTable, int listMax, const Vector &hull for ( int i = 0; i < count; i++ ) { - g_pScriptVM->SetValue( hTable, STRING(list[i]->GetEntityName()), ToHScript( list[i] ) ); + g_pScriptVM->ArrayAppend( hTable, ToHScript(list[i]) ); } } @@ -949,7 +1402,7 @@ static void ScriptEntitiesAtPoint( HSCRIPT hTable, int listMax, const Vector &po for ( int i = 0; i < count; i++ ) { - g_pScriptVM->SetValue( hTable, STRING(list[i]->GetEntityName()), ToHScript( list[i] ) ); + g_pScriptVM->ArrayAppend( hTable, ToHScript(list[i]) ); } } @@ -960,7 +1413,7 @@ static void ScriptEntitiesInSphere( HSCRIPT hTable, int listMax, const Vector &c for ( int i = 0; i < count; i++ ) { - g_pScriptVM->SetValue( hTable, STRING(list[i]->GetEntityName()), ToHScript( list[i] ) ); + g_pScriptVM->ArrayAppend( hTable, ToHScript(list[i]) ); } } @@ -972,6 +1425,14 @@ static void ScriptDecalTrace( HSCRIPT hTrace, const char *decalName ) UTIL_DecalTrace( &traceInfo->GetTrace(), decalName ); } +//----------------------------------------------------------------------------- +// Simple particle effect dispatch +//----------------------------------------------------------------------------- +static void ScriptDispatchParticleEffect( const char *pszParticleName, const Vector &vecOrigin, const QAngle &vecAngles ) +{ + DispatchParticleEffect( pszParticleName, vecOrigin, vecAngles ); +} + //============================================================================= //============================================================================= @@ -1006,11 +1467,507 @@ bool ScriptIsClient() } // Notification printing on the right edge of the screen -void NPrint(int pos, const char* fmt) +void NPrint( int pos, const char* fmt ) { engine->Con_NPrintf(pos, fmt); } +void NXPrint( int pos, int r, int g, int b, bool fixed, float ftime, const char* fmt ) +{ + static con_nprint_t *info = new con_nprint_t; + + info->index = pos; + info->time_to_live = ftime; + info->color[0] = r / 255.f; + info->color[1] = g / 255.f; + info->color[2] = b / 255.f; + info->fixed_width_font = fixed; + + engine->Con_NXPrintf(info, fmt); + + // delete info; +} + +class CGlobalSys +{ +public: + const char* ScriptGetCommandLine() + { + return CommandLine()->GetCmdLine(); + } + + bool CommandLineCheck(const char* name) + { + return !!CommandLine()->FindParm(name); + } + + const char* CommandLineCheckStr(const char* name) + { + return CommandLine()->ParmValue(name); + } + + float CommandLineCheckFloat(const char* name) + { + return CommandLine()->ParmValue(name, 0); + } + + int CommandLineCheckInt(const char* name) + { + return CommandLine()->ParmValue(name, 0); + } +} g_ScriptGlobalSys; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CGlobalSys, "CGlobalSys", SCRIPT_SINGLETON "GlobalSys" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetCommandLine, "GetCommandLine", "returns the command line" ) + DEFINE_SCRIPTFUNC( CommandLineCheck, "returns true if the command line param was used, otherwise false." ) + DEFINE_SCRIPTFUNC( CommandLineCheckStr, "returns the command line param as a string." ) + DEFINE_SCRIPTFUNC( CommandLineCheckFloat, "returns the command line param as a float." ) + DEFINE_SCRIPTFUNC( CommandLineCheckInt, "returns the command line param as an int." ) +END_SCRIPTDESC(); + + +class CScriptSaveRestoreUtil : public CAutoGameSystem +{ +public: + static void SaveTable( const char *szId, HSCRIPT hTable ); + static void RestoreTable( const char *szId, HSCRIPT hTable ); + static void ClearSavedTable( const char *szId ); + +// IGameSystem interface +public: + void OnSave() + { + if ( g_pScriptVM ) + { + HSCRIPT hFunc = g_pScriptVM->LookupFunction( "OnSave" ); + if ( hFunc ) + { + g_pScriptVM->Call( hFunc ); + } + } + } + + void OnRestore() + { + if ( g_pScriptVM ) + { + HSCRIPT hFunc = g_pScriptVM->LookupFunction( "OnRestore" ); + if ( hFunc ) + { + g_pScriptVM->Call( hFunc ); + } + } + } + + void Shutdown() + { + FOR_EACH_VEC( m_aKeyValues, i ) + m_aKeyValues[i]->deleteThis(); + m_aKeyValues.Purge(); + m_aContext.PurgeAndDeleteElements(); + } + +private: + static int GetIndexForContext( const char *szId ); + + // indices must match, always remove keeping order + static CUtlStringList m_aContext; + static CUtlVector m_aKeyValues; + +} g_ScriptSaveRestoreUtil; + +CUtlStringList CScriptSaveRestoreUtil::m_aContext; +CUtlVector CScriptSaveRestoreUtil::m_aKeyValues; + +int CScriptSaveRestoreUtil::GetIndexForContext( const char *szId ) +{ + int idx = -1; + FOR_EACH_VEC( m_aContext, i ) + { + if ( !V_stricmp( szId, m_aContext[i] ) ) + { + idx = i; + break; + } + } + return idx; +} + +//----------------------------------------------------------------------------- +// Store a table with primitive values that will persist across level transitions and save loads. +//----------------------------------------------------------------------------- +void CScriptSaveRestoreUtil::SaveTable( const char *szId, HSCRIPT hTable ) +{ + int idx = GetIndexForContext(szId); + + KeyValues *pKV; + + if ( idx == -1 ) + { + pKV = new KeyValues("ScriptSavedTable"); + m_aKeyValues.AddToTail(pKV); + + if ( V_strlen(szId) > 255 ) // arbitrary clamp + { + char c[256]; + V_strncpy( c, szId, sizeof(c) ); + m_aContext.CopyAndAddToTail(c); + } + else + { + m_aContext.CopyAndAddToTail(szId); + } + } + else + { + pKV = m_aKeyValues[idx]; + pKV->Clear(); + } + + ScriptVariant_t key, val; + int nIterator = -1; + while ( ( nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &key, &val ) ) != -1 ) + { + switch ( val.m_type ) + { + case FIELD_FLOAT: pKV->SetFloat ( key.m_pszString, val.m_float ); break; + case FIELD_INTEGER: pKV->SetInt ( key.m_pszString, val.m_int ); break; + case FIELD_BOOLEAN: pKV->SetBool ( key.m_pszString, val.m_bool ); break; + case FIELD_CSTRING: pKV->SetString( key.m_pszString, val.m_pszString ); break; + } + + g_pScriptVM->ReleaseValue(key); + g_pScriptVM->ReleaseValue(val); + } +} + +//----------------------------------------------------------------------------- +// Retrieves a table from storage. Write into input table. +//----------------------------------------------------------------------------- +void CScriptSaveRestoreUtil::RestoreTable( const char *szId, HSCRIPT hTable ) +{ + int idx = GetIndexForContext(szId); + + KeyValues *pKV; + + if ( idx == -1 ) + { + // DevWarning( 2, "RestoreTable could not find saved table with context '%s'\n", szId ); + return; + } + else + { + pKV = m_aKeyValues[idx]; + } + + FOR_EACH_SUBKEY( pKV, key ) + { + switch ( key->GetDataType() ) + { + case KeyValues::TYPE_STRING: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetString() ); break; + case KeyValues::TYPE_INT: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetInt() ); break; + case KeyValues::TYPE_FLOAT: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetFloat() ); break; + } + } +} + +//----------------------------------------------------------------------------- +// Remove a saved table. +//----------------------------------------------------------------------------- +void CScriptSaveRestoreUtil::ClearSavedTable( const char *szId ) +{ + int idx = GetIndexForContext(szId); + + if ( idx == -1 ) + { + // DevWarning( 2, "ClearSavedTable could not find saved table with context '%s'\n", szId ); + return; + } + + m_aKeyValues[idx]->deleteThis(); + m_aKeyValues.Remove(idx); + + delete[] m_aContext[idx]; + m_aContext.Remove(idx); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +#define SCRIPT_MAX_FILE_READ_SIZE (16 * 1024) // 16KB +#define SCRIPT_MAX_FILE_WRITE_SIZE (64 * 1024 * 1024) // 64MB +#define SCRIPT_RW_PATH_ID "MOD" +#define SCRIPT_RW_FULL_PATH_FMT "export/%s" + +class CScriptReadWriteFile : public CAutoGameSystem +{ +public: + static bool ScriptFileWrite( const char *szFile, const char *szInput ); + static const char *ScriptFileRead( const char *szFile ); + //static const char *CRC32_Checksum( const char *szFilename ); + + void LevelShutdownPostEntity() + { + if ( m_pszReturnReadFile ) + { + delete[] m_pszReturnReadFile; + m_pszReturnReadFile = NULL; + } + + //if ( m_pszReturnCRC32 ) + //{ + // delete[] m_pszReturnCRC32; + // m_pszReturnCRC32 = NULL; + //} + } + +private: + static const char *m_pszReturnReadFile; + //static const char *m_pszReturnCRC32; + +} g_ScriptReadWrite; + +const char *CScriptReadWriteFile::m_pszReturnReadFile = NULL; +//const char *CScriptReadWriteFile::m_pszReturnCRC32 = NULL; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CScriptReadWriteFile::ScriptFileWrite( const char *szFile, const char *szInput ) +{ + size_t len = strlen(szInput); + if ( len > SCRIPT_MAX_FILE_WRITE_SIZE ) + { + DevWarning( 2, "Input is too large for a ScriptFileWrite ( %s / %d MB )\n", V_pretifymem(len,2,true), (SCRIPT_MAX_FILE_WRITE_SIZE >> 20) ); + return false; + } + + char pszFullName[MAX_PATH]; + V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); + + if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + { + DevWarning( 2, "Invalid file location : %s\n", szFile ); + return false; + } + + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + buf.PutString(szInput); + + int nSize = V_strlen(pszFullName) + 1; + char *pszDir = (char*)stackalloc(nSize); + V_memcpy( pszDir, pszFullName, nSize ); + V_StripFilename( pszDir ); + + g_pFullFileSystem->CreateDirHierarchy( pszDir, SCRIPT_RW_PATH_ID ); + bool res = g_pFullFileSystem->WriteFile( pszFullName, SCRIPT_RW_PATH_ID, buf ); + buf.Purge(); + return res; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +const char *CScriptReadWriteFile::ScriptFileRead( const char *szFile ) +{ + char pszFullName[MAX_PATH]; + V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); + + if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + { + DevWarning( 2, "Invalid file location : %s\n", szFile ); + return NULL; + } + + unsigned int size = g_pFullFileSystem->Size( pszFullName, SCRIPT_RW_PATH_ID ); + if ( size >= SCRIPT_MAX_FILE_READ_SIZE ) + { + DevWarning( 2, "File '%s' (from '%s') is too large for a ScriptFileRead ( %s / %u bytes )\n", pszFullName, szFile, V_pretifymem(size,2,true), SCRIPT_MAX_FILE_READ_SIZE ); + return NULL; + } + + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + if ( !g_pFullFileSystem->ReadFile( pszFullName, SCRIPT_RW_PATH_ID, buf, SCRIPT_MAX_FILE_READ_SIZE ) ) + { + return NULL; + } + + // first time calling, allocate + if ( !m_pszReturnReadFile ) + m_pszReturnReadFile = new char[SCRIPT_MAX_FILE_READ_SIZE]; + + V_strncpy( const_cast(m_pszReturnReadFile), (const char*)buf.Base(), buf.Size() ); + buf.Purge(); + return m_pszReturnReadFile; +} + +//----------------------------------------------------------------------------- +// Get the checksum of any file. Can be used to check the existence or validity of a file. +// Returns unsigned int as hex string. +//----------------------------------------------------------------------------- +/* +const char *CScriptReadWriteFile::CRC32_Checksum( const char *szFilename ) +{ + CUtlBuffer buf( 0, 0, CUtlBuffer::READ_ONLY ); + if ( !g_pFullFileSystem->ReadFile( szFilename, NULL, buf ) ) + return NULL; + + // first time calling, allocate + if ( !m_pszReturnCRC32 ) + m_pszReturnCRC32 = new char[9]; // 'FFFFFFFF\0' + + V_snprintf( const_cast(m_pszReturnCRC32), 9, "%X", CRC32_ProcessSingleBuffer( buf.Base(), buf.Size()-1 ) ); + buf.Purge(); + + return m_pszReturnCRC32; +} +*/ +#undef SCRIPT_MAX_FILE_READ_SIZE +#undef SCRIPT_MAX_FILE_WRITE_SIZE +#undef SCRIPT_RW_PATH_ID +#undef SCRIPT_RW_FULL_PATH_FMT + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +#ifndef CLIENT_DLL +class CNetMsgScriptHelper +{ +private: + CRecipientFilter filter; + bf_write message; + byte data_msg[ MAX_USER_MSG_DATA ]; + + inline void SendMsg( bf_write *bf ) + { + bf_read buffer = bf_read(); + buffer.StartReading( message.GetData(), message.m_nDataBytes ); + bf->WriteBitsFromBuffer( &buffer, message.GetNumBitsWritten() ); + engine->MessageEnd(); + } + +public: + inline void Reset() + { + message.StartWriting( data_msg, sizeof(data_msg) ); + filter.Reset(); + } + + void SendUserMessage( HSCRIPT player, const char *msg, bool bReliable ) + { + int msg_type = usermessages->LookupUserMessage(msg); + if ( msg_type == -1 ) + { + g_pScriptVM->RaiseException("UserMessageBegin: Unregistered message"); + return; + } + + CBaseEntity *pPlayer = ToEnt(player); + if ( pPlayer ) + { + filter.AddRecipient( (CBasePlayer*)pPlayer ); + } + + if ( bReliable ) + { + filter.MakeReliable(); + } + + SendMsg( engine->UserMessageBegin( &filter, msg_type ) ); + } + + void SendEntityMessage( HSCRIPT hEnt, bool bReliable ) + { + CBaseEntity *entity = ToEnt(hEnt); + if ( !entity ) + { + g_pScriptVM->RaiseException("EntityMessageBegin: invalid entity"); + return; + } + + SendMsg( engine->EntityMessageBegin( entity->entindex(), entity->GetServerClass(), bReliable ) ); + } + +public: + void AddRecipient( HSCRIPT player ) + { + CBaseEntity *pPlayer = ToEnt(player); + if ( pPlayer ) + { + filter.AddRecipient( (CBasePlayer*)pPlayer ); + } + } + + void AddRecipientsByPVS( const Vector &pos ) + { + filter.AddRecipientsByPVS(pos); + } + + void AddAllPlayers() + { + filter.AddAllPlayers(); + } + +public: + void WriteByte( int iValue ) { message.WriteByte( iValue ); } + void WriteChar( int iValue ) { message.WriteChar( iValue ); } + void WriteShort( int iValue ) { message.WriteShort( iValue ); } + void WriteWord( int iValue ) { message.WriteWord( iValue ); } + void WriteLong( int iValue ) { message.WriteLong( iValue ); } + void WriteFloat( float flValue ) { message.WriteFloat( flValue ); } + void WriteAngle( float flValue ) { message.WriteBitAngle( flValue, 8 ); } + void WriteCoord( float flValue ) { message.WriteBitCoord( flValue ); } + void WriteVec3Coord( const Vector& rgflValue ) { message.WriteBitVec3Coord( rgflValue ); } + void WriteVec3Normal( const Vector& rgflValue ) { message.WriteBitVec3Normal( rgflValue ); } + void WriteAngles( const QAngle& rgflValue ) { message.WriteBitAngles( rgflValue ); } + void WriteString( const char *sz ) { message.WriteString( sz ); } + void WriteEntity( int iValue ) { message.WriteShort( iValue ); } + void WriteBool( bool bValue ) { message.WriteOneBit( bValue ? 1 : 0 ); } + void WriteEHandle( HSCRIPT hEnt ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + long iEncodedEHandle; + if ( pEnt ) + { + EHANDLE hEnt = pEnt; + int iSerialNum = hEnt.GetSerialNumber() & (1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS) - 1; + iEncodedEHandle = hEnt.GetEntryIndex() | (iSerialNum << MAX_EDICT_BITS); + } + else + { + iEncodedEHandle = INVALID_NETWORKED_EHANDLE_VALUE; + } + message.WriteLong( iEncodedEHandle ); + } + +} g_ScriptNetMsg; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "NetworkMessages" ) + DEFINE_SCRIPTFUNC( Reset, "" ) + DEFINE_SCRIPTFUNC( SendUserMessage, "" ) + DEFINE_SCRIPTFUNC( SendEntityMessage, "" ) + DEFINE_SCRIPTFUNC( AddRecipient, "" ) + DEFINE_SCRIPTFUNC( AddRecipientsByPVS, "" ) + DEFINE_SCRIPTFUNC( AddAllPlayers, "" ) + DEFINE_SCRIPTFUNC( WriteByte, "" ) + DEFINE_SCRIPTFUNC( WriteChar, "" ) + DEFINE_SCRIPTFUNC( WriteShort, "" ) + DEFINE_SCRIPTFUNC( WriteWord, "" ) + DEFINE_SCRIPTFUNC( WriteLong, "" ) + DEFINE_SCRIPTFUNC( WriteFloat, "" ) + DEFINE_SCRIPTFUNC( WriteAngle, "" ) + DEFINE_SCRIPTFUNC( WriteCoord, "" ) + DEFINE_SCRIPTFUNC( WriteVec3Coord, "" ) + DEFINE_SCRIPTFUNC( WriteVec3Normal, "" ) + DEFINE_SCRIPTFUNC( WriteAngles, "" ) + DEFINE_SCRIPTFUNC( WriteString, "" ) + DEFINE_SCRIPTFUNC( WriteEntity, "" ) + DEFINE_SCRIPTFUNC( WriteEHandle, "" ) + DEFINE_SCRIPTFUNC( WriteBool, "" ) +END_SCRIPTDESC(); + +#endif // !CLIENT_DLL + //============================================================================= //============================================================================= @@ -1032,10 +1989,25 @@ void RegisterSharedScriptFunctions() ScriptRegisterFunction( g_pScriptVM, EntIndexToHScript, "Returns the script handle for the given entity index." ); ScriptRegisterFunction( g_pScriptVM, PrecacheEntityFromTable, "Precache an entity from KeyValues in a table." ); ScriptRegisterFunction( g_pScriptVM, SpawnEntityFromTable, "Native function for entity spawning." ); +#endif // !CLIENT_DLL + + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::SaveTable, "SaveTable", "Store a table with primitive values that will persist across level transitions and save loads." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::RestoreTable, "RestoreTable", "Retrieves a table from storage. Write into input table." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::ClearSavedTable, "ClearSavedTable", "Removes the table with the given context." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::ScriptFileWrite, "StringToFile", "Stores the string into the file" ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::ScriptFileRead, "FileToString", "Returns the string from the file, null if no file or file is too big." ); + + ScriptRegisterFunction( g_pScriptVM, ListenToGameEvent, "Register as a listener for a game event from script." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptGameEventListener::StopListeningToGameEvent, "StopListeningToGameEvent", "Stop the specified event listener." ); + ScriptRegisterFunctionNamed( g_pScriptVM, CScriptGameEventListener::StopListeningToAllGameEvents, "StopListeningToAllGameEvents", "Stop listening to all game events within a specific context." ); + ScriptRegisterFunction( g_pScriptVM, FireGameEvent, "Fire a game event." ); +#ifndef CLIENT_DLL + ScriptRegisterFunction( g_pScriptVM, FireGameEventLocal, "Fire a game event without broadcasting to the client." ); #endif g_pScriptVM->RegisterInstance( &g_ScriptConvarLookup, "Convars" ); g_pScriptVM->RegisterInstance( &g_ScriptNetPropManager, "NetProps" ); + g_pScriptVM->RegisterInstance( &g_ScriptGlobalSys, "GlobalSys" ); //----------------------------------------------------------------------------- // Functions, singletons, etc. unique to Mapbase @@ -1043,10 +2015,15 @@ void RegisterSharedScriptFunctions() g_pScriptVM->RegisterInstance( GetAmmoDef(), "AmmoDef" ); g_pScriptVM->RegisterInstance( &g_ScriptLocalize, "Localize" ); +#ifndef CLIENT_DLL + g_pScriptVM->RegisterInstance( &g_ScriptNetMsg, "NetMsg" ); +#endif //----------------------------------------------------------------------------- - ScriptRegisterFunction( g_pScriptVM, NPrint, "" ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMsg, "Msg", "" ); + ScriptRegisterFunction( g_pScriptVM, NPrint, "Notification print" ); + ScriptRegisterFunction( g_pScriptVM, NXPrint, "Notification print, customised" ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptColorPrint, "printc", "Version of print() which takes a color before the message." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptColorPrintL, "printcl", "Version of printl() which takes a color before the message." ); @@ -1093,6 +2070,7 @@ void RegisterSharedScriptFunctions() ScriptRegisterFunctionNamed( g_pScriptVM, ScriptEntitiesInSphere, "EntitiesInSphere", "Gets all entities which are within a sphere." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptDecalTrace, "DecalTrace", "Creates a dynamic decal based on the given trace info. The trace information can be generated by TraceLineComplex() and the decal name must be from decals_subrect.txt." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptDispatchParticleEffect, "DispatchParticleEffect", "Dispatches a one-off particle system" ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatcherMatch, "Matcher_Match", "Compares a string to a query using Mapbase's matcher system, supporting wildcards, RS matchers, etc." ); ScriptRegisterFunction( g_pScriptVM, Matcher_NamesMatch, "Compares a string to a query using Mapbase's matcher system using wildcards only." ); @@ -1104,8 +2082,14 @@ void RegisterSharedScriptFunctions() ScriptRegisterFunctionNamed( g_pScriptVM, ScriptIsServer, "IsServer", "Returns true if the script is being run on the server." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptIsClient, "IsClient", "Returns true if the script is being run on the client." ); + ScriptRegisterFunction( g_pScriptVM, GetCPUUsage, "Get CPU usage percentage." ); + RegisterMathScriptFunctions(); +#ifndef CLIENT_DLL + CScriptGameEventListener::LoadAllEvents(); +#endif // !CLIENT_DLL + #ifdef CLIENT_DLL VScriptRunScript( "vscript_client", true ); #else diff --git a/sp/src/public/vscript/ivscript.h b/sp/src/public/vscript/ivscript.h index af3e418c..bf8a0de9 100644 --- a/sp/src/public/vscript/ivscript.h +++ b/sp/src/public/vscript/ivscript.h @@ -871,6 +871,11 @@ public: virtual bool ClearValue( HSCRIPT hScope, const char *pszKey ) = 0; bool ClearValue( const char *pszKey) { return ClearValue( NULL, pszKey ); } +#ifdef MAPBASE_VSCRIPT + // virtual void CreateArray(ScriptVariant_t &arr, int size = 0) = 0; + virtual bool ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) = 0; +#endif + //---------------------------------------------------------------------------- virtual void WriteState( CUtlBuffer *pBuffer ) = 0; diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 6628a93e..7e1d0d5d 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -31,7 +31,7 @@ #include "squirrel/squirrel/sqvm.h" #include "squirrel/squirrel/sqclosure.h" - +#include "color.h" #include "tier1/utlbuffer.h" #include @@ -199,6 +199,9 @@ public: virtual bool ClearValue(HSCRIPT hScope, const char* pszKey) override; + // virtual void CreateArray(ScriptVariant_t &arr, int size = 0) override; + virtual bool ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) override; + //---------------------------------------------------------------------------- virtual void WriteState(CUtlBuffer* pBuffer) override; @@ -1362,24 +1365,26 @@ struct SquirrelSafeCheck }; +#define CON_COLOR_VSCRIPT 80,186,255,255 + void printfunc(HSQUIRRELVM SQ_UNUSED_ARG(v), const SQChar* format, ...) { va_list args; + char buffer[2048]; va_start(args, format); - char buffer[256]; - vsprintf(buffer, format, args); - Msg("%s", buffer); + V_vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); + ConColorMsg(Color(CON_COLOR_VSCRIPT), "%s", buffer); } void errorfunc(HSQUIRRELVM SQ_UNUSED_ARG(v), const SQChar* format, ...) { va_list args; + char buffer[2048]; va_start(args, format); - char buffer[256]; - vsprintf(buffer, format, args); - Warning("%s", buffer); + V_vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); + Warning("%s", buffer); } const char * ScriptDataTypeToName(ScriptDataType_t datatype) @@ -1595,20 +1600,8 @@ bool SquirrelVM::Init() if (Run( R"script( - Msg <- print; Warning <- error; - // LocalTime from Source 2 - // function LocalTime() - // { - // local date = ::date(); - // return { - // Hours = date.hour, - // Minutes = date.min, - // Seconds = date.sec - // } - // } - function clamp(val,min,max) { if ( max < min ) @@ -1621,15 +1614,9 @@ bool SquirrelVM::Init() return val; } - function max(a,b) - { - return a > b ? a : b; - } + function max(a,b) return a > b ? a : b - function min(a,b) - { - return a < b ? a : b; - } + function min(a,b) return a < b ? a : b function RemapVal(val, A, B, C, D) { @@ -1643,10 +1630,36 @@ bool SquirrelVM::Init() if ( A == B ) return val >= B ? D : C; local cVal = (val - A) / (B - A); - cVal = ::clamp( cVal, 0.0, 1.0 ); + cVal = (cVal < 0.0) ? 0.0 : (1.0 < cVal) ? 1.0 : cVal; return C + (D - C) * cVal; } + function Approach( target, value, speed ) + { + local delta = target - value + + if( delta > speed ) + value += speed + else if( delta < (-speed) ) + value -= speed + else + value = target + + return value + } + + function AngleDistance( next, cur ) + { + local delta = next - cur + + if ( delta < (-180.0) ) + delta += 360.0 + else if ( delta > 180.0 ) + delta -= 360.0 + + return delta + } + function printl( text ) { return ::print(text + "\n"); @@ -2736,6 +2749,35 @@ bool SquirrelVM::ClearValue(HSCRIPT hScope, const char* pszKey) sq_pop(vm_, 1); return true; } +/* +void SquirrelVM::CreateArray(ScriptVariant_t &arr, int size) +{ + SquirrelSafeCheck safeCheck(vm_); + + HSQOBJECT *obj = new HSQOBJECT; + sq_resetobject(obj); + + sq_newarray(vm_,size); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 1); + + arr = (HSCRIPT)obj; +} +*/ +bool SquirrelVM::ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) +{ + SquirrelSafeCheck safeCheck(vm_); + + HSQOBJECT *arr = (HSQOBJECT*)hArray; + + sq_pushobject(vm_, *arr); + PushVariant(vm_, val); + bool ret = sq_arrayappend(vm_, -2) == SQ_OK; + sq_pop(vm_, 1); + + return ret; +} enum ClassType {