diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index d45ed98cec..a3b4037812 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -451,6 +451,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelName, "GetModelName", "Returns the name of the model" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptStopSound, "StopSound", "Stops a sound from this entity." ) 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." ) @@ -472,6 +473,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC_NAMED( GetAbsAngles, "GetAngles", "Get entity pitch, yaw, roll as a vector" ) DEFINE_SCRIPTFUNC_NAMED( SetAbsAngles, "SetAngles", "Set entity pitch, yaw, roll" ) + DEFINE_SCRIPTFUNC( 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" ) @@ -541,7 +543,13 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC_NAMED( IsBaseCombatWeapon, "IsWeapon", "Returns true if this entity is a weapon." ) DEFINE_SCRIPTFUNC( IsWorld, "Returns true if this entity is the world." ) + DEFINE_SCRIPTFUNC( SetModel, "Set client-only entity model" ) + //DEFINE_SCRIPTFUNC_NAMED( ScriptInitializeAsClientEntity, "InitializeAsClientEntity", "" ) + DEFINE_SCRIPTFUNC_NAMED( Remove, "Destroy", "Remove clientside entity" ) DEFINE_SCRIPTFUNC_NAMED( GetEntityIndex, "entindex", "" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetContextThink, "SetContextThink", "Set a think function on this entity." ) + #endif END_SCRIPTDESC(); @@ -1334,6 +1342,15 @@ void C_BaseEntity::Term() { g_pScriptVM->RemoveInstance( m_hScriptInstance ); m_hScriptInstance = NULL; + +#ifdef MAPBASE_VSCRIPT + FOR_EACH_VEC( m_ScriptThinkFuncs, i ) + { + HSCRIPT h = m_ScriptThinkFuncs[i]->m_hfnThink; + if ( h ) g_pScriptVM->ReleaseScript( h ); + } + m_ScriptThinkFuncs.PurgeAndDeleteElements(); +#endif } } diff --git a/sp/src/game/client/c_baseentity.h b/sp/src/game/client/c_baseentity.h index c8890ccb3f..dabfbbd9d6 100644 --- a/sp/src/game/client/c_baseentity.h +++ b/sp/src/game/client/c_baseentity.h @@ -161,6 +161,16 @@ struct thinkfunc_t int m_nLastThinkTick; }; +#ifdef MAPBASE_VSCRIPT +struct scriptthinkfunc_t +{ + int m_nNextThinkTick; + HSCRIPT m_hfnThink; + unsigned short m_iContextHash; + bool m_bNoParam; +}; +#endif + #define CREATE_PREDICTED_ENTITY( className ) \ C_BaseEntity::CreatePredictedEntityByName( className, __FILE__, __LINE__ ); @@ -1173,6 +1183,7 @@ class C_BaseEntity : public IClientEntity #ifdef MAPBASE_VSCRIPT const char* ScriptGetModelName( void ) const { return STRING(GetModelName()); } + void ScriptStopSound(const char* soundname); void ScriptEmitSound(const char* soundname); float ScriptSoundDuration(const char* soundname, const char* actormodel); @@ -1519,6 +1530,15 @@ class C_BaseEntity : public IClientEntity CUtlVector< thinkfunc_t > m_aThinkFunctions; int m_iCurrentThinkContext; +#ifdef MAPBASE_VSCRIPT +public: + void ScriptSetContextThink( const char* szContext, HSCRIPT hFunc, float time ); + void ScriptContextThink(); +private: + CUtlVector< scriptthinkfunc_t* > m_ScriptThinkFuncs; +public: +#endif + // Object eye position Vector m_vecViewOffset; diff --git a/sp/src/game/client/c_world.h b/sp/src/game/client/c_world.h index 2174f32b27..12366490c7 100644 --- a/sp/src/game/client/c_world.h +++ b/sp/src/game/client/c_world.h @@ -49,6 +49,8 @@ class C_World : public C_BaseEntity #endif #ifdef MAPBASE_VSCRIPT + void ClientThink() { ScriptContextThink(); } + // -2 = Use server language ScriptLanguage_t GetScriptLanguage() { return (ScriptLanguage_t)(m_iScriptLanguageClient != -2 ? m_iScriptLanguageClient : m_iScriptLanguageServer); } #endif diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index 58b5431c33..50c66a923a 100644 --- a/sp/src/game/client/vscript_client.cpp +++ b/sp/src/game/client/vscript_client.cpp @@ -461,6 +461,11 @@ bool DoIncludeScript( const char *pszScript, HSCRIPT hScope ) } #ifdef MAPBASE_VSCRIPT +static float FrameTime() +{ + return gpGlobals->frametime; +} + static bool Con_IsVisible() { return engine->Con_IsVisible(); @@ -585,6 +590,7 @@ bool VScriptClientInit() 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." ) ); ScriptRegisterFunction( g_pScriptVM, DoIncludeScript, "Execute a script (internal)" ); #ifdef MAPBASE_VSCRIPT + ScriptRegisterFunction( g_pScriptVM, FrameTime, "Get the time spent on the client in the last frame" ); ScriptRegisterFunction( g_pScriptVM, Con_IsVisible, "Returns true if the console is visible" ); ScriptRegisterFunction( g_pScriptVM, ScreenWidth, "Width of the screen in pixels" ); ScriptRegisterFunction( g_pScriptVM, ScreenHeight, "Height of the screen in pixels" ); diff --git a/sp/src/game/client/vscript_client.nut b/sp/src/game/client/vscript_client.nut index 5fce6ff5ec..505395da74 100644 --- a/sp/src/game/client/vscript_client.nut +++ b/sp/src/game/client/vscript_client.nut @@ -5,18 +5,26 @@ static char g_Script_vscript_client[] = R"vscript( // //============================================================================= +local DoUniqueString = DoUniqueString +local DoDispatchParticleEffect = DoDispatchParticleEffect + function UniqueString( string = "" ) { - return DoUniqueString( string.tostring() ); + return DoUniqueString( "" + string ); } function IncludeScript( name, scope = null ) { - if ( scope == null ) + if ( !scope ) { scope = this; } return ::DoIncludeScript( name, scope ); } +function DispatchParticleEffect( particleName, origin, angles, entity = null ) +{ + DoDispatchParticleEffect( particleName, origin, angles, entity ); +} + )vscript"; \ No newline at end of file diff --git a/sp/src/game/server/ai_speech.cpp b/sp/src/game/server/ai_speech.cpp index 601907448e..dafad41e4a 100644 --- a/sp/src/game/server/ai_speech.cpp +++ b/sp/src/game/server/ai_speech.cpp @@ -1210,7 +1210,7 @@ void CAI_Expresser::SpeechMsg( CBaseEntity *pFlex, const char *pszFormat, ... ) } else { - CGMsg( 1, CON_GROUP_CHOREO "%s", string ); + CGMsg( 1, CON_GROUP_CHOREO, "%s", string ); } UTIL_LogPrintf( string ); } diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index 7284d0d208..963b5355a7 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -2222,6 +2222,7 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC( SetModel, "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelName, "GetModelName", "Returns the name of the model" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptStopSound, "StopSound", "Stops a sound from this entity." ) 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.") @@ -2282,11 +2283,11 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" 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( 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( Remove, "Destroy", "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetOwner, "SetOwner", "" ) DEFINE_SCRIPTFUNC_NAMED( GetTeamNumber, "GetTeam", "" ) DEFINE_SCRIPTFUNC_NAMED( ChangeTeam, "SetTeam", "" ) @@ -2587,10 +2588,10 @@ void CBaseEntity::UpdateOnRemove( void ) #ifdef MAPBASE_VSCRIPT FOR_EACH_VEC( m_ScriptThinkFuncs, i ) { - HSCRIPT h = m_ScriptThinkFuncs[i].m_hfnThink; + HSCRIPT h = m_ScriptThinkFuncs[i]->m_hfnThink; if ( h ) g_pScriptVM->ReleaseScript( h ); } - m_ScriptThinkFuncs.Purge(); + m_ScriptThinkFuncs.PurgeAndDeleteElements(); #endif // MAPBASE_VSCRIPT } } @@ -8664,173 +8665,6 @@ void CBaseEntity::ScriptStopThinkFunction() m_iszScriptThinkFunction = NULL_STRING; SetContextThink( NULL, TICK_NEVER_THINK, "ScriptThink" ); } - - -static inline void ScriptStopContextThink( scriptthinkfunc_t *context ) -{ - g_pScriptVM->ReleaseScript( context->m_hfnThink ); - context->m_hfnThink = NULL; - context->m_nNextThinkTick = TICK_NEVER_THINK; -} - -//----------------------------------------------------------------------------- -// -//----------------------------------------------------------------------------- -void CBaseEntity::ScriptContextThink() -{ - float flNextThink = FLT_MAX; - int nScheduledTick = 0; - - for ( int i = m_ScriptThinkFuncs.Count(); i--; ) - { - scriptthinkfunc_t *cur = &m_ScriptThinkFuncs[i]; - - if ( cur->m_nNextThinkTick == TICK_NEVER_THINK ) - continue; - - if ( cur->m_nNextThinkTick > gpGlobals->tickcount ) - { - // There is more to execute, don't stop thinking if the rest are done. - - // also find the shortest schedule - if ( !nScheduledTick || nScheduledTick > cur->m_nNextThinkTick ) - { - nScheduledTick = cur->m_nNextThinkTick; - } - continue; - } - - ScriptVariant_t varReturn; - - if ( cur->m_bNoParam ) - { - if ( g_pScriptVM->Call( cur->m_hfnThink, NULL, true, &varReturn ) == SCRIPT_ERROR ) - { - ScriptStopContextThink(cur); - m_ScriptThinkFuncs.Remove(i); - continue; - } - } - else - { - if ( g_pScriptVM->Call( cur->m_hfnThink, NULL, true, &varReturn, m_hScriptInstance ) == SCRIPT_ERROR ) - { - ScriptStopContextThink(cur); - m_ScriptThinkFuncs.Remove(i); - continue; - } - } - - float flReturn; - if ( !varReturn.AssignTo( &flReturn ) ) - { - ScriptStopContextThink(cur); - m_ScriptThinkFuncs.Remove(i); - continue; - } - - if ( flReturn < 0.0f ) - { - ScriptStopContextThink(cur); - m_ScriptThinkFuncs.Remove(i); - continue; - } - - // find the shortest delay - if ( flReturn < flNextThink ) - { - flNextThink = flReturn; - } - - cur->m_nNextThinkTick = TIME_TO_TICKS( gpGlobals->curtime + flReturn ); - } - - if ( flNextThink < FLT_MAX ) - { - SetNextThink( gpGlobals->curtime + flNextThink, "ScriptContextThink" ); - } - else if ( nScheduledTick ) - { - SetNextThink( TICKS_TO_TIME( nScheduledTick ), "ScriptContextThink" ); - } - else - { - SetNextThink( TICK_NEVER_THINK, "ScriptContextThink" ); - } -} - -// see ScriptSetThink -static bool s_bScriptContextThinkNoParam = false; - -//----------------------------------------------------------------------------- -// -//----------------------------------------------------------------------------- -void CBaseEntity::ScriptSetContextThink( const char* szContext, HSCRIPT hFunc, float flTime ) -{ - scriptthinkfunc_t th; - V_memset( &th, 0x0, sizeof(scriptthinkfunc_t) ); - unsigned short hash = ( szContext && *szContext ) ? HashString( szContext ) : 0; - bool bFound = false; - - FOR_EACH_VEC( m_ScriptThinkFuncs, i ) - { - scriptthinkfunc_t f = m_ScriptThinkFuncs[i]; - if ( hash == f.m_iContextHash ) - { - th = f; - m_ScriptThinkFuncs.Remove(i); // reorder - bFound = true; - break; - } - } - - if ( hFunc ) - { - float nextthink = gpGlobals->curtime + flTime; - - th.m_bNoParam = s_bScriptContextThinkNoParam; - th.m_hfnThink = hFunc; - th.m_iContextHash = hash; - th.m_nNextThinkTick = TIME_TO_TICKS( nextthink ); - - m_ScriptThinkFuncs.AddToHead( th ); - - int nexttick = GetNextThinkTick( RegisterThinkContext( "ScriptContextThink" ) ); - - // sooner than next think - if ( nexttick <= 0 || nexttick > th.m_nNextThinkTick ) - { - SetContextThink( &CBaseEntity::ScriptContextThink, nextthink, "ScriptContextThink" ); - } - } - // null func input, think exists - else if ( bFound ) - { - ScriptStopContextThink( &th ); - } -} - -//----------------------------------------------------------------------------- -// m_bNoParam and s_bScriptContextThinkNoParam exist only to keep backwards compatibility -// and are an alternative to this script closure: -// -// function CBaseEntity::SetThink( func, time ) -// { -// SetContextThink( "", function(_){ return func() }, time ) -// } -//----------------------------------------------------------------------------- -void CBaseEntity::ScriptSetThink( HSCRIPT hFunc, float time ) -{ - s_bScriptContextThinkNoParam = true; - ScriptSetContextThink( NULL, hFunc, time ); - s_bScriptContextThinkNoParam = false; -} - -void CBaseEntity::ScriptStopThink() -{ - ScriptSetContextThink( NULL, NULL, 0.0f ); -} - #endif // MAPBASE_VSCRIPT //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index a9c69ca329..a96d443bdd 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -341,9 +341,9 @@ struct thinkfunc_t #ifdef MAPBASE_VSCRIPT struct scriptthinkfunc_t { + int m_nNextThinkTick; HSCRIPT m_hfnThink; unsigned short m_iContextHash; - int m_nNextThinkTick; bool m_bNoParam; }; #endif @@ -2008,7 +2008,7 @@ class CBaseEntity : public IServerEntity void ScriptStopThink(); void ScriptContextThink(); private: - CUtlVector< scriptthinkfunc_t > m_ScriptThinkFuncs; + CUtlVector< scriptthinkfunc_t* > m_ScriptThinkFuncs; public: #endif const char* GetScriptId(); @@ -2039,8 +2039,10 @@ class CBaseEntity : public IServerEntity const Vector& ScriptGetAngles(void) { static Vector vec; QAngle qa = GetAbsAngles(); vec.x = qa.x; vec.y = qa.y; vec.z = qa.z; return vec; } #endif +#ifndef MAPBASE_VSCRIPT void ScriptSetSize(const Vector& mins, const Vector& maxs) { UTIL_SetSize(this, mins, maxs); } void ScriptUtilRemove(void) { UTIL_Remove(this); } +#endif 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); } @@ -2066,6 +2068,7 @@ class CBaseEntity : public IServerEntity const char* ScriptGetModelName(void) const; HSCRIPT ScriptGetModelKeyValues(void); + void ScriptStopSound(const char* soundname); void ScriptEmitSound(const char* soundname); float ScriptSoundDuration(const char* soundname, const char* actormodel); diff --git a/sp/src/game/server/cbase.cpp b/sp/src/game/server/cbase.cpp index 8105053879..d98ea9cdda 100644 --- a/sp/src/game/server/cbase.cpp +++ b/sp/src/game/server/cbase.cpp @@ -850,7 +850,7 @@ void CEventQueue::Dump( void ) // Purpose: adds the action into the correct spot in the priority queue, targeting entity via string name //----------------------------------------------------------------------------- #ifdef MAPBASE_VSCRIPT -intptr_t +int #else void #endif @@ -874,6 +874,7 @@ CEventQueue::AddEvent( const char *target, const char *targetInput, variant_t Va AddEvent( newEvent ); #ifdef MAPBASE_VSCRIPT + Assert( sizeof(EventQueuePrioritizedEvent_t*) == sizeof(int) ); return reinterpret_cast(newEvent); // POINTER_TO_INT #endif } @@ -882,7 +883,7 @@ CEventQueue::AddEvent( const char *target, const char *targetInput, variant_t Va // Purpose: adds the action into the correct spot in the priority queue, targeting entity via pointer //----------------------------------------------------------------------------- #ifdef MAPBASE_VSCRIPT -intptr_t +int #else void #endif @@ -906,6 +907,7 @@ CEventQueue::AddEvent( CBaseEntity *target, const char *targetInput, variant_t V AddEvent( newEvent ); #ifdef MAPBASE_VSCRIPT + Assert( sizeof(EventQueuePrioritizedEvent_t*) == sizeof(int) ); return reinterpret_cast(newEvent); // POINTER_TO_INT #endif } @@ -1293,7 +1295,7 @@ void CEventQueue::CancelEventsByInput( CBaseEntity *pTarget, const char *szInput } } -bool CEventQueue::RemoveEvent( intptr_t event ) +bool CEventQueue::RemoveEvent( int event ) { EventQueuePrioritizedEvent_t *pe = reinterpret_cast(event); // INT_TO_POINTER @@ -1310,7 +1312,7 @@ bool CEventQueue::RemoveEvent( intptr_t event ) return false; } -float CEventQueue::GetTimeLeft( intptr_t event ) +float CEventQueue::GetTimeLeft( int event ) { EventQueuePrioritizedEvent_t *pe = reinterpret_cast(event); // INT_TO_POINTER diff --git a/sp/src/game/server/eventqueue.h b/sp/src/game/server/eventqueue.h index 61b0d252a1..d5cc2d5f35 100644 --- a/sp/src/game/server/eventqueue.h +++ b/sp/src/game/server/eventqueue.h @@ -41,8 +41,8 @@ class CEventQueue public: // pushes an event into the queue, targeting a string name (m_iName), or directly by a pointer #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 ); + int AddEvent( const char *target, const char *action, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID = 0 ); + int 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 ); @@ -73,8 +73,8 @@ class CEventQueue #ifdef MAPBASE_VSCRIPT void CancelEventsByInput( CBaseEntity *pTarget, const char *szInput ); - bool RemoveEvent( intptr_t event ); - float GetTimeLeft( intptr_t event ); + bool RemoveEvent( int event ); + float GetTimeLeft( int event ); #endif // MAPBASE_VSCRIPT private: diff --git a/sp/src/game/server/player.cpp b/sp/src/game/server/player.cpp index 2c57cc36cb..0f26c2522e 100644 --- a/sp/src/game/server/player.cpp +++ b/sp/src/game/server/player.cpp @@ -526,8 +526,8 @@ BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseCombatCharacter, "The player entity." ) DEFINE_SCRIPTFUNC( GetButtonForced, "Gets the player's currently forced buttons." ) DEFINE_SCRIPTFUNC( GetFOV, "" ) - DEFINE_SCRIPTFUNC_NAMED( ScriptGetFOVOwner, "GetFOVOwner", "" ) - DEFINE_SCRIPTFUNC_NAMED( ScriptSetFOV, "SetFOV", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFOVOwner, "GetFOVOwner", "Gets current view owner." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetFOV, "SetFOV", "Sets player FOV regardless of view owner." ) DEFINE_SCRIPTFUNC( ViewPunch, "Punches the player's view with the specified vector." ) DEFINE_SCRIPTFUNC( SetMuzzleFlashTime, "Sets the player's muzzle flash time for AI." ) @@ -5251,6 +5251,11 @@ void CBasePlayer::Spawn( void ) m_vecSmoothedVelocity = vec3_origin; InitVCollision( GetAbsOrigin(), GetAbsVelocity() ); + if ( !g_pGameRules->IsMultiplayer() && g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } + #if !defined( TF_DLL ) IGameEvent *event = gameeventmanager->CreateEvent( "player_spawn" ); @@ -5275,11 +5280,6 @@ void CBasePlayer::Spawn( void ) UpdateLastKnownArea(); m_weaponFiredTimer.Invalidate(); - - if ( !g_pGameRules->IsMultiplayer() && g_pScriptVM ) - { - g_pScriptVM->SetValue( "player", GetScriptInstance() ); - } } void CBasePlayer::Activate( void ) diff --git a/sp/src/game/server/vscript_server.cpp b/sp/src/game/server/vscript_server.cpp index ebab60e6e9..6927b0f106 100644 --- a/sp/src/game/server/vscript_server.cpp +++ b/sp/src/game/server/vscript_server.cpp @@ -110,6 +110,11 @@ class CScriptEntityIterator { return ToHScript( gEntList.FindEntityByClassnameWithin( ToEnt( hStartEntity ), szName, vecMins, vecMaxs ) ); } + + HSCRIPT FindByClassNearestFacing( const Vector &origin, const Vector &facing, float threshold, const char *classname ) + { + return ToHScript( gEntList.FindEntityClassNearestFacing( origin, facing, threshold, const_cast(classname) ) ); + } #endif private: } g_ScriptEntityIterator; @@ -132,6 +137,7 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptEntityIterator, "CEntities", SCRIPT_SINGLETO 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" ) + DEFINE_SCRIPTFUNC( FindByClassNearestFacing, "Find the nearest entity along the facing direction from the given origin within the angular threshold with the given classname." ) #endif END_SCRIPTDESC(); @@ -265,11 +271,6 @@ static int MaxPlayers() return gpGlobals->maxClients; } -static float IntervalPerTick() -{ - return gpGlobals->interval_per_tick; -} - static int GetLoadType() { return gpGlobals->eLoadType; @@ -328,7 +329,11 @@ 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 ) { +#ifdef MAPBASE_VSCRIPT return 0; +#else + return; +#endif } if ( *pszAction ) @@ -447,12 +452,6 @@ static float GetEntityIOEventTimeLeft( int event ) { return g_EventQueue.GetTimeLeft(event); } - -// vscript_server.nut adds this to the base CConvars class -static const char *ScriptGetClientConvarValue( const char *pszConVar, int entindex ) -{ - return engine->GetClientConVarValue( entindex, pszConVar ); -} #endif // MAPBASE_VSCRIPT bool VScriptServerInit() @@ -537,7 +536,6 @@ bool VScriptServerInit() #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, GetLoadType, "Get the way the current game was loaded (corresponds to the MapLoad enum)" ); 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." ) ); @@ -545,7 +543,6 @@ bool VScriptServerInit() ScriptRegisterFunction( g_pScriptVM, CancelEntityIOEvent, "Remove entity I/O event." ); ScriptRegisterFunction( g_pScriptVM, GetEntityIOEventTimeLeft, "Get time left on entity I/O event." ); - ScriptRegisterFunction( g_pScriptVM, ScriptGetClientConvarValue, SCRIPT_HIDE ); #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." ); diff --git a/sp/src/game/server/vscript_server.nut b/sp/src/game/server/vscript_server.nut index deecea7b8e..deeacf5dce 100644 --- a/sp/src/game/server/vscript_server.nut +++ b/sp/src/game/server/vscript_server.nut @@ -5,15 +5,14 @@ static char g_Script_vscript_server[] = R"vscript( // //============================================================================= -local DoEntFire = ::DoEntFire -local DoEntFireByInstanceHandle = ::DoEntFireByInstanceHandle -local DoDispatchParticleEffect = ::DoDispatchParticleEffect -local DoUniqueString = ::DoUniqueString -local ScriptGetClientConvarValue = ::ScriptGetClientConvarValue +local DoEntFire = DoEntFire +local DoEntFireByInstanceHandle = DoEntFireByInstanceHandle +local DoDispatchParticleEffect = DoDispatchParticleEffect +local DoUniqueString = DoUniqueString function UniqueString( string = "" ) { - return DoUniqueString( string.tostring() ); + return DoUniqueString( "" + string ); } function EntFire( target, action, value = null, delay = 0.0, activator = null, caller = null ) @@ -36,7 +35,7 @@ function EntFire( target, action, value = null, delay = 0.0, activator = null, c } } - return DoEntFire( target.tostring(), action.tostring(), value.tostring(), delay, activator, caller ); + return DoEntFire( "" + target, "" + action, "" + value, delay, activator, caller ); } function EntFireByHandle( target, action, value = null, delay = 0.0, activator = null, caller = null ) @@ -59,7 +58,7 @@ function EntFireByHandle( target, action, value = null, delay = 0.0, activator = } } - return DoEntFireByInstanceHandle( target, action.tostring(), value.tostring(), delay, activator, caller ); + return DoEntFireByInstanceHandle( target, "" + action, "" + value, delay, activator, caller ); } function DispatchParticleEffect( particleName, origin, angles, entity = null ) @@ -67,12 +66,6 @@ function DispatchParticleEffect( particleName, origin, angles, entity = null ) DoDispatchParticleEffect( particleName, origin, angles, entity ); } -// CConvars is declared within the library -function CConvars::GetClientConvarValue(cvar,idx) -{ - return ScriptGetClientConvarValue(cvar,idx); -} - __Documentation.RegisterHelp( "CConvars::GetClientConvarValue", "CConvars::GetClientConvarValue(string, int)", "Returns the convar value for the entindex as a string. Only works with client convars with the FCVAR_USERINFO flag." ); function __ReplaceClosures( script, scope ) diff --git a/sp/src/game/shared/SoundEmitterSystem.cpp b/sp/src/game/shared/SoundEmitterSystem.cpp index 390b5ca4d8..e1072b78b2 100644 --- a/sp/src/game/shared/SoundEmitterSystem.cpp +++ b/sp/src/game/shared/SoundEmitterSystem.cpp @@ -1226,6 +1226,11 @@ void CBaseEntity::ScriptEmitSound( const char *soundname ) EmitSound( soundname ); } +void CBaseEntity::ScriptStopSound( const char *soundname ) +{ + StopSound( soundname ); +} + float CBaseEntity::ScriptSoundDuration( const char *soundname, const char *actormodel ) { float duration = CBaseEntity::GetSoundDuration( soundname, actormodel ); diff --git a/sp/src/game/shared/baseentity_shared.cpp b/sp/src/game/shared/baseentity_shared.cpp index f07f3775c8..cae3f5d92f 100644 --- a/sp/src/game/shared/baseentity_shared.cpp +++ b/sp/src/game/shared/baseentity_shared.cpp @@ -2712,4 +2712,296 @@ HSCRIPT CBaseEntity::ScriptGetPhysicsObject( void ) else return NULL; } + +static inline void ScriptStopContextThink( scriptthinkfunc_t *context ) +{ + Assert( context->m_hfnThink ); + + g_pScriptVM->ReleaseScript( context->m_hfnThink ); + context->m_hfnThink = NULL; + //context->m_nNextThinkTick = TICK_NEVER_THINK; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptContextThink() +{ + float flNextThink = FLT_MAX; + int nScheduledTick = 0; + + for ( int i = 0; i < m_ScriptThinkFuncs.Count(); ++i ) + { + scriptthinkfunc_t *cur = m_ScriptThinkFuncs[i]; + + if ( cur->m_nNextThinkTick == TICK_NEVER_THINK ) + { + continue; + } + + if ( cur->m_nNextThinkTick > gpGlobals->tickcount ) + { + // There is more to execute, don't stop thinking if the rest are done. + + // Find the shortest schedule + if ( !nScheduledTick || nScheduledTick > cur->m_nNextThinkTick ) + { + nScheduledTick = cur->m_nNextThinkTick; + } + continue; + } + +#ifdef _DEBUG + // going to run the script func + cur->m_nNextThinkTick = 0; +#endif + + ScriptVariant_t varReturn; + + if ( !cur->m_bNoParam ) + { + ScriptVariant_t arg = m_hScriptInstance; + if ( g_pScriptVM->ExecuteFunction( cur->m_hfnThink, &arg, 1, &varReturn, NULL, true ) == SCRIPT_ERROR ) + { + cur->m_nNextThinkTick = TICK_NEVER_THINK; + continue; + } + } + else + { + if ( g_pScriptVM->ExecuteFunction( cur->m_hfnThink, NULL, 0, &varReturn, NULL, true ) == SCRIPT_ERROR ) + { + cur->m_nNextThinkTick = TICK_NEVER_THINK; + continue; + } + } + + if ( cur->m_nNextThinkTick == TICK_NEVER_THINK ) + { + // stopped from script while thinking + continue; + } + + float flReturn; + if ( !varReturn.AssignTo( &flReturn ) ) + { + cur->m_nNextThinkTick = TICK_NEVER_THINK; + continue; + } + + if ( flReturn < 0.0f ) + { + cur->m_nNextThinkTick = TICK_NEVER_THINK; + continue; + } + + if ( flReturn < flNextThink ) + { + flNextThink = flReturn; + } + + cur->m_nNextThinkTick = TIME_TO_TICKS( gpGlobals->curtime + flReturn ); + } + + // deferred safe removal + for ( int i = 0; i < m_ScriptThinkFuncs.Count(); ) + { + if ( m_ScriptThinkFuncs[i]->m_nNextThinkTick == TICK_NEVER_THINK ) + { + ScriptStopContextThink( m_ScriptThinkFuncs[i] ); + delete m_ScriptThinkFuncs[i]; + m_ScriptThinkFuncs.Remove(i); + } + else ++i; + } + + bool bNewNext = flNextThink < FLT_MAX; + +#ifdef _DEBUG +#ifdef GAME_DLL + int nNextThinkTick = GetNextThinkTick("ScriptContextThink"); // -1 +#else + int nNextThinkTick = GetNextThinkTick(); // 0 +#endif + if ( ( nNextThinkTick <= 0 ) || ( nNextThinkTick >= nScheduledTick ) || ( nNextThinkTick == gpGlobals->tickcount ) ) + { +#endif + if ( nScheduledTick ) + { + float flScheduledTime = TICKS_TO_TIME( nScheduledTick ); + + if ( bNewNext ) + { + flNextThink = min( gpGlobals->curtime + flNextThink, flScheduledTime ); + } + else + { + flNextThink = flScheduledTime; + } + } + else + { + if ( bNewNext ) + { + flNextThink = gpGlobals->curtime + flNextThink; + } + else + { +#ifdef GAME_DLL + flNextThink = TICK_NEVER_THINK; +#else + flNextThink = CLIENT_THINK_NEVER; +#endif + } + } +#ifdef _DEBUG + } + else + { + // Next think was set (from script) to a sooner tick while thinking? + Assert(0); + + if ( nScheduledTick ) + { + int nNextSchedule = min( nScheduledTick, nNextThinkTick ); + float flNextSchedule = TICKS_TO_TIME( nNextSchedule ); + + if ( bNewNext ) + { + flNextThink = min( gpGlobals->curtime + flNextThink, flNextSchedule ); + } + else + { + flNextThink = flNextSchedule; + } + } + else + { + float nextthink = TICKS_TO_TIME( nNextThinkTick ); + + if ( bNewNext ) + { + flNextThink = min( gpGlobals->curtime + flNextThink, nextthink ); + } + else + { + flNextThink = nextthink; + } + } + } +#endif + +#ifdef GAME_DLL + SetNextThink( flNextThink, "ScriptContextThink" ); +#else + SetNextClientThink( flNextThink ); +#endif +} + +// see ScriptSetThink +static bool s_bScriptContextThinkNoParam = false; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptSetContextThink( const char* szContext, HSCRIPT hFunc, float flTime ) +{ +#ifdef CLIENT_DLL + // Context thinking is not yet supported on client, entities can only have 1 think function. + // C_World does not have one by default, so it is safe to set its. + if ( !IsWorld() ) + { + g_pScriptVM->RaiseException("SetContextThink is only supported on C_World"); + return; + } +#endif + + scriptthinkfunc_t *pf = NULL; + unsigned short hash = ( szContext && *szContext ) ? HashString( szContext ) : 0; + + FOR_EACH_VEC( m_ScriptThinkFuncs, i ) + { + scriptthinkfunc_t *f = m_ScriptThinkFuncs[i]; + if ( hash == f->m_iContextHash ) + { + pf = f; + break; + } + } + + if ( hFunc ) + { + // add new + if ( !pf ) + { + pf = new scriptthinkfunc_t; + + m_ScriptThinkFuncs.SetGrowSize(1); + m_ScriptThinkFuncs.AddToTail( pf ); + + pf->m_bNoParam = s_bScriptContextThinkNoParam; + pf->m_iContextHash = hash; + } + // update existing + else + { +#ifdef _DEBUG + if ( pf->m_nNextThinkTick == 0 ) + { + Warning("Script think ('%s') was changed while it was thinking!\n", szContext); + } +#endif + g_pScriptVM->ReleaseScript( pf->m_hfnThink ); + } + + float nextthink = gpGlobals->curtime + flTime; + + pf->m_hfnThink = hFunc; + pf->m_nNextThinkTick = TIME_TO_TICKS( nextthink ); + +#ifdef GAME_DLL + int nexttick = GetNextThinkTick( RegisterThinkContext( "ScriptContextThink" ) ); +#else + int nexttick = GetNextThinkTick(); +#endif + + // sooner than next think + if ( nexttick <= 0 || nexttick > pf->m_nNextThinkTick ) + { +#ifdef GAME_DLL + SetContextThink( &CBaseEntity::ScriptContextThink, nextthink, "ScriptContextThink" ); +#else + SetNextClientThink( nextthink ); +#endif + } + } + // null func input, think exists + else if ( pf ) + { + pf->m_nNextThinkTick = TICK_NEVER_THINK; + } +} + +//----------------------------------------------------------------------------- +// m_bNoParam and s_bScriptContextThinkNoParam exist only to keep backwards compatibility +// and are an alternative to this script closure: +// +// function CBaseEntity::SetThink( func, time ) +// { +// SetContextThink( "", function(_){ return func() }, time ) +// } +//----------------------------------------------------------------------------- +#ifndef CLIENT_DLL +void CBaseEntity::ScriptSetThink( HSCRIPT hFunc, float time ) +{ + s_bScriptContextThinkNoParam = true; + ScriptSetContextThink( NULL, hFunc, time ); + s_bScriptContextThinkNoParam = false; +} + +void CBaseEntity::ScriptStopThink() +{ + ScriptSetContextThink( NULL, NULL, 0.0f ); +} +#endif #endif diff --git a/sp/src/game/shared/mapbase/mapbase_shared.cpp b/sp/src/game/shared/mapbase/mapbase_shared.cpp index 9e8562f974..6a89b30f21 100644 --- a/sp/src/game/shared/mapbase/mapbase_shared.cpp +++ b/sp/src/game/shared/mapbase/mapbase_shared.cpp @@ -178,7 +178,7 @@ class CMapbaseSystem : public CAutoGameSystem #ifdef GAME_DLL if (g_bMapContainsCustomTalker && mapbase_flush_talker.GetBool()) { - CGMsg( 1, "Mapbase Misc.", "Mapbase: Reloading response system to flush custom talker\n" ); + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "Mapbase: Reloading response system to flush custom talker\n" ); ReloadResponseSystem(); g_bMapContainsCustomTalker = false; } @@ -188,7 +188,7 @@ class CMapbaseSystem : public CAutoGameSystem virtual void LevelInitPreEntity() { #ifdef GAME_DLL - CGMsg( 0, "Mapbase Misc.", "Mapbase system loaded\n" ); + CGMsg( 0, CON_GROUP_MAPBASE_MISC, "Mapbase system loaded\n" ); #endif // Checks gameinfo.txt for Mapbase-specific options @@ -352,11 +352,11 @@ class CMapbaseSystem : public CAutoGameSystem return; } - CGMsg( 1, "Mapbase Misc.", "===== Mapbase Manifest: Loading manifest file %s =====\n", file ); + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "===== Mapbase Manifest: Loading manifest file %s =====\n", file ); AddManifestFile(pKV, false); - CGMsg( 1, "Mapbase Misc.", "==============================================================================\n" ); + CGMsg( 1, CON_GROUP_MAPBASE_MISC, "==============================================================================\n" ); pKV->deleteThis(); } @@ -591,7 +591,7 @@ class CMapbaseManifestEntity : public CPointEntity const char *scriptfile = STRING(m_target); if ( filesystem->FileExists( scriptfile, "MOD" ) ) { - CGMsg(0, "Mapbase Misc.", "Mapbase: Adding manifest file \"%s\"\n", scriptfile); + CGMsg(0, CON_GROUP_MAPBASE_MISC, "Mapbase: Adding manifest file \"%s\"\n", scriptfile); g_MapbaseSystem.AddManifestFile(scriptfile); } else diff --git a/sp/src/game/shared/mapbase/vscript_consts_shared.cpp b/sp/src/game/shared/mapbase/vscript_consts_shared.cpp index b03ee0e839..31341fb9be 100644 --- a/sp/src/game/shared/mapbase/vscript_consts_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_consts_shared.cpp @@ -66,7 +66,7 @@ BEGIN_SCRIPTENUM( RenderMode, "Render modes used by Get/SetRenderMode" ) DEFINE_ENUMCONST_NAMED( kRenderTransAdd, "Additive", "" ) DEFINE_ENUMCONST_NAMED( kRenderEnvironmental, "Environmental", "" ) DEFINE_ENUMCONST_NAMED( kRenderTransAddFrameBlend, "AdditiveFractionalFrame", "" ) - DEFINE_ENUMCONST_NAMED( kRenderTransAlphaAdd, "Alpha Add", "" ) + DEFINE_ENUMCONST_NAMED( kRenderTransAlphaAdd, "AlphaAdd", "" ) DEFINE_ENUMCONST_NAMED( kRenderWorldGlow, "WorldSpaceGlow", "" ) DEFINE_ENUMCONST_NAMED( kRenderNone, "None", "" ) diff --git a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp index ae927a8123..3581818a84 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp @@ -164,18 +164,18 @@ HSCRIPT SpawnEntityFromTable( const char *pszClassname, HSCRIPT hKV ) HSCRIPT EntIndexToHScript( int index ) { #ifdef GAME_DLL - edict_t *e = INDEXENT(index); - if ( e && !e->IsFree() ) - { - return ToHScript( GetContainingEntity( e ) ); - } + edict_t *e = INDEXENT(index); + if ( e && !e->IsFree() ) + { + return ToHScript( GetContainingEntity( e ) ); + } #else // CLIENT_DLL - if ( index < NUM_ENT_ENTRIES ) - { - return ToHScript( CBaseEntity::Instance( index ) ); - } + if ( index < NUM_ENT_ENTRIES ) + { + return ToHScript( CBaseEntity::Instance( index ) ); + } #endif - return NULL; + return NULL; } //----------------------------------------------------------------------------- @@ -740,20 +740,29 @@ void NPrint( int pos, const char* 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; + con_nprint_t info; + + 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; - 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 ); +} - engine->Con_NXPrintf(info, fmt); +static float IntervalPerTick() +{ + return gpGlobals->interval_per_tick; +} - // delete info; +static int GetFrameCount() +{ + return gpGlobals->framecount; } + //============================================================================= //============================================================================= @@ -850,6 +859,9 @@ void RegisterSharedScriptFunctions() #endif 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, IntervalPerTick, "Simulation tick interval" ); + ScriptRegisterFunction( g_pScriptVM, GetFrameCount, "Absolute frame counter" ); + //ScriptRegisterFunction( g_pScriptVM, GetTickCount, "Simulation ticks" ); RegisterScriptSingletons(); } diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index e001513e9b..4a8a2318e0 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -23,6 +23,15 @@ #include "igameevents.h" #include "engine/ivdebugoverlay.h" +#ifdef CLIENT_DLL +#include "IEffects.h" +#include "fx.h" +#include "itempents.h" +#include "c_te_legacytempents.h" +#include "iefx.h" +#include "dlight.h" +#endif + #include "vscript_singletons.h" // memdbgon must be the last include file in a .cpp file!!! @@ -355,20 +364,25 @@ END_SCRIPTDESC(); // Game Event Listener // Based on Source 2 API //============================================================================= + +// Define to use the older code that loads all events manually independent from the game event manager. +// Otherwise access event descriptors directly from engine. +//#define USE_OLD_EVENT_DESCRIPTORS 1 + class CScriptGameEventListener : public IGameEventListener2, public CAutoGameSystem { public: - CScriptGameEventListener() : m_bActive(false) {} + CScriptGameEventListener() : m_bActive(false) /*, m_nEventTick(0)*/ {} ~CScriptGameEventListener() { StopListeningForEvent(); } - intptr_t ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ); + int ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ); void StopListeningForEvent(); public: - static bool StopListeningToGameEvent( intptr_t listener ); + static bool StopListeningToGameEvent( int listener ); static void StopListeningToAllGameEvents( const char* szContext ); public: @@ -376,35 +390,58 @@ class CScriptGameEventListener : public IGameEventListener2, public CAutoGameSys void LevelShutdownPreEntity(); private: - bool m_bActive; - unsigned int m_iContextHash; + //int m_index; HSCRIPT m_hCallback; + unsigned int m_iContextHash; + bool m_bActive; + //int m_nEventTick; static StringHashFunctor Hash; static inline unsigned int HashContext( const char* c ) { return (c && *c) ? Hash(c) : 0; } + inline int GetIndex() + { + Assert( sizeof(CScriptGameEventListener*) == sizeof(int) ); + return reinterpret_cast(this); + } + public: - static void DumpEventListeners(); -#ifndef CLIENT_DLL + enum // event data types, dependant on engine definitions + { + TYPE_LOCAL = 0, + TYPE_STRING = 1, + TYPE_FLOAT = 2, + TYPE_LONG = 3, + TYPE_SHORT = 4, + TYPE_BYTE = 5, + TYPE_BOOL = 6 + }; + static void WriteEventData( IGameEvent *event, HSCRIPT hTable ); + +#ifdef USE_OLD_EVENT_DESCRIPTORS static void LoadAllEvents(); static void LoadEventsFromFile( const char *filename, const char *pathID = NULL ); - static void WriteEventData( IGameEvent *event, HSCRIPT hTable ); -#endif // !CLIENT_DLL + static CUtlMap< unsigned int, KeyValues* > s_GameEvents; + static CUtlVector< KeyValues* > s_LoadedFiles; +#endif public: -#ifndef CLIENT_DLL - static CUtlMap< unsigned int, KeyValues* > s_GameEvents; + //static int g_nIndexCounter; + static CUtlVectorAutoPurge< CScriptGameEventListener* > s_Listeners; +#if _DEBUG + static void DumpEventListeners(); #endif - static CUtlVectorAutoPurge< CScriptGameEventListener* > s_GameEventListeners; - static CUtlVector< KeyValues* > s_LoadedFiles; + }; -#ifndef CLIENT_DLL +CUtlVectorAutoPurge< CScriptGameEventListener* > CScriptGameEventListener::s_Listeners; +StringHashFunctor CScriptGameEventListener::Hash; + +#ifdef USE_OLD_EVENT_DESCRIPTORS CUtlMap< unsigned int, KeyValues* > CScriptGameEventListener::s_GameEvents( DefLessFunc(unsigned int) ); -#endif -CUtlVectorAutoPurge< CScriptGameEventListener* > CScriptGameEventListener::s_GameEventListeners; CUtlVector< KeyValues* > CScriptGameEventListener::s_LoadedFiles; -StringHashFunctor CScriptGameEventListener::Hash; +#endif + #if _DEBUG #ifdef CLIENT_DLL @@ -412,53 +449,19 @@ CON_COMMAND_F( cl_dump_script_game_event_listeners, "Dump all game event listene { CScriptGameEventListener::DumpEventListeners(); } -#else // GAME_DLL +#else 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() -{ - CGMsg( 0, CON_GROUP_VSCRIPT, "--- Script game event listener dump start\n" ); - FOR_EACH_VEC( s_GameEventListeners, i ) - { - CGMsg( 0, CON_GROUP_VSCRIPT, " %d (0x%p) %d : %d\n", i,s_GameEventListeners[i], - s_GameEventListeners[i], - s_GameEventListeners[i]->m_iContextHash ); - } - CGMsg( 0, CON_GROUP_VSCRIPT, "--- 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; -} +#ifdef USE_OLD_EVENT_DESCRIPTORS //----------------------------------------------------------------------------- // Executed in LevelInitPreEntity //----------------------------------------------------------------------------- -#ifndef CLIENT_DLL void CScriptGameEventListener::LoadAllEvents() { // Listed in the same order they are loaded in GameEventManager @@ -506,8 +509,8 @@ void CScriptGameEventListener::LoadEventsFromFile( const char *filename, const c 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. + int count = 0; + for ( KeyValues *key = pKV->GetFirstSubKey(); key; key = key->GetNextKey() ) { for ( KeyValues *sub = key->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) @@ -515,13 +518,29 @@ void CScriptGameEventListener::LoadEventsFromFile( const char *filename, const c 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" ) ) + if ( !V_stricmp( szVal, "string" ) ) + { + sub->SetInt( NULL, TYPE_STRING ); + } + else if ( !V_stricmp( szVal, "bool" ) ) + { + sub->SetInt( NULL, TYPE_BOOL ); + } + else if ( !V_stricmp( szVal, "byte" ) ) + { + sub->SetInt( NULL, TYPE_BYTE ); + } + else if ( !V_stricmp( szVal, "short" ) ) { - sub->SetInt( NULL, 0 ); + sub->SetInt( NULL, TYPE_SHORT ); + } + else if ( !V_stricmp( szVal, "long" ) ) + { + sub->SetInt( NULL, TYPE_LONG ); } else if ( !V_stricmp( szVal, "float" ) ) { - sub->SetFloat( NULL, 0.0f ); + sub->SetInt( NULL, TYPE_FLOAT ); } } // none : value is not networked @@ -537,42 +556,125 @@ void CScriptGameEventListener::LoadEventsFromFile( const char *filename, const c // Replace key so modevents can overwrite gameevents. // It does not check for hash collisions, however. s_GameEvents.InsertOrReplace( Hash( key->GetName() ), key ); + ++count; } // Store files (allocated KV) s_LoadedFiles.AddToTail( pKV ); - CGMsg( 2, CON_GROUP_VSCRIPT, "CScriptGameEventListener::LoadEventsFromFile: Loaded [%s]%s\n", pathID, filename ); + CGMsg( 2, CON_GROUP_VSCRIPT, "CScriptGameEventListener::LoadEventsFromFile: Loaded [%s]%s (%i)\n", pathID, filename, count ); +} +#endif + +#if _DEBUG +void CScriptGameEventListener::DumpEventListeners() +{ + CGMsg( 0, CON_GROUP_VSCRIPT, "--- Script game event listener dump start\n" ); + CGMsg( 0, CON_GROUP_VSCRIPT, "# ADDRESS ID CONTEXT\n" ); + FOR_EACH_VEC( s_Listeners, i ) + { + CGMsg( 0, CON_GROUP_VSCRIPT, " %d (0x%p) %d : %u\n", i, + (void*)s_Listeners[i], + s_Listeners[i]->GetIndex(), + s_Listeners[i]->m_iContextHash ); + } + CGMsg( 0, CON_GROUP_VSCRIPT, "--- Script game event listener dump end\n" ); +} +#endif + +void CScriptGameEventListener::LevelShutdownPreEntity() +{ + s_Listeners.FindAndFastRemove(this); + delete this; +} + +void CScriptGameEventListener::FireGameEvent( IGameEvent *event ) +{ + //m_nEventTick = gpGlobals->tickcount; + ScriptVariant_t hTable; + g_pScriptVM->CreateTable( hTable ); + WriteEventData( event, hTable ); + g_pScriptVM->SetValue( hTable, "game_event_listener", GetIndex() ); + // g_pScriptVM->SetValue( hTable, "game_event_name", event->GetName() ); + g_pScriptVM->ExecuteFunction( m_hCallback, &hTable, 1, NULL, NULL, true ); + g_pScriptVM->ReleaseScript( hTable ); } +struct CGameEventDescriptor +{ + byte _0[36]; + KeyValues *m_pEventKeys; + //byte _1[22]; +}; + +class CGameEvent__// : public IGameEvent +{ +public: + virtual ~CGameEvent__(); // [0] + CGameEventDescriptor *m_pDescriptor; // 0x04 + //KeyValues *m_pEventData; // 0x08 +}; + //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CScriptGameEventListener::WriteEventData( IGameEvent *event, HSCRIPT hTable ) { +#ifdef USE_OLD_EVENT_DESCRIPTORS int i = s_GameEvents.Find( Hash( event->GetName() ) ); - if ( i != s_GameEvents.InvalidIndex() ) + if ( i == s_GameEvents.InvalidIndex() ) + return; + KeyValues *pKV = s_GameEvents[i]; +#endif + +#if defined(_DEBUG) && !defined(USE_OLD_EVENT_DESCRIPTORS) + try { - KeyValues *pKV = s_GameEvents[i]; - for ( KeyValues *sub = pKV->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) +#endif + +#if !defined(USE_OLD_EVENT_DESCRIPTORS) + KeyValues *pKV = reinterpret_cast< CGameEvent__* >(event)->m_pDescriptor->m_pEventKeys; +#endif + + for ( KeyValues *sub = pKV->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) + { + const char *szKey = sub->GetName(); + switch ( sub->GetInt() ) { - 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 ); - } + case TYPE_LOCAL: + case TYPE_STRING: g_pScriptVM->SetValue( hTable, szKey, event->GetString( szKey ) ); break; + case TYPE_FLOAT: g_pScriptVM->SetValue( hTable, szKey, event->GetFloat ( szKey ) ); break; + case TYPE_BOOL: g_pScriptVM->SetValue( hTable, szKey, event->GetBool ( szKey ) ); break; + default: g_pScriptVM->SetValue( hTable, szKey, event->GetInt ( szKey ) ); } } + +#if defined(_DEBUG) && !defined(USE_OLD_EVENT_DESCRIPTORS) + // Access a bunch of KeyValues functions to validate it is the correct address. + // This may not always throw an exception when it is incorrect, but eventually it will. + } + catch (...) + { + // CGameEvent or CGameEventDescriptor offsets did not match! + // This should mean these were modified in engine.dll. + // + // Implement this utility yourself by adding a function to get event descriptor keys + // either on CGameEventManager or on CGameEvent interfaces. + // On CGameEventManager downcast IGameEvent input to CGameEvent, then return event->descriptor->keys + // On CGameEvent return (member) descriptor->keys + // + // Finally assign it to pKV above. + + Warning("CScriptGameEventListener::WriteEventData internal error\n"); + Assert(0); + } +#endif } -#endif // !CLIENT_DLL //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -intptr_t CScriptGameEventListener::ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ) +int CScriptGameEventListener::ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ) { bool bValid; @@ -590,14 +692,14 @@ intptr_t CScriptGameEventListener::ListenToGameEvent( const char* szEvent, HSCRI m_hCallback = hFunc; m_bActive = true; - s_GameEventListeners.AddToTail( this ); + s_Listeners.AddToTail( this ); - return reinterpret_cast( this ); // POINTER_TO_INT + return GetIndex(); } else { delete this; - return 0x0; + return 0; } } @@ -610,29 +712,31 @@ void CScriptGameEventListener::StopListeningForEvent() return; if ( g_pScriptVM ) - { g_pScriptVM->ReleaseScript( m_hCallback ); - } - else - { - // AssertMsg( !m_hCallback, "LEAK (0x%p)\n", (void*)m_hCallback ); - } m_hCallback = NULL; m_bActive = false; if ( gameeventmanager ) gameeventmanager->RemoveListener( this ); + + // Event listeners are iterated forwards in the game event manager, + // removing while iterating will cause it to skip one listener. + // This could be prevented by writing a custom game event manager. + //if ( m_nEventTick == gpGlobals->tickcount ) + //{ + // Warning("CScriptGameEventListener stopped in the same frame it was fired. This will break other event listeners!\n"); + //} } //----------------------------------------------------------------------------- // Stop the specified event listener. //----------------------------------------------------------------------------- -bool CScriptGameEventListener::StopListeningToGameEvent( intptr_t listener ) +bool CScriptGameEventListener::StopListeningToGameEvent( int listener ) { CScriptGameEventListener *p = reinterpret_cast(listener); // INT_TO_POINTER - bool bRemoved = s_GameEventListeners.FindAndFastRemove(p); + bool bRemoved = s_Listeners.FindAndFastRemove(p); if ( bRemoved ) { delete p; @@ -649,12 +753,12 @@ void CScriptGameEventListener::StopListeningToAllGameEvents( const char* szConte unsigned int hash = HashContext( szContext ); // Iterate from the end so they can be safely removed as they are deleted - for ( int i = s_GameEventListeners.Count(); i--; ) + for ( int i = s_Listeners.Count(); i--; ) { - CScriptGameEventListener *pCur = s_GameEventListeners[i]; + CScriptGameEventListener *pCur = s_Listeners[i]; if ( pCur->m_iContextHash == hash ) { - s_GameEventListeners.Remove(i); // keep list order + s_Listeners.Remove(i); // keep list order delete pCur; } } @@ -805,11 +909,13 @@ StringHashFunctor CScriptSaveRestoreUtil::Hash; void CScriptSaveRestoreUtil::SaveTable( const char *szId, HSCRIPT hTable ) { KeyValues *pKV; - int idx = m_Lookup.Find( Hash(szId) ); + unsigned int hash = Hash(szId); + + int idx = m_Lookup.Find( hash ); if ( idx == m_Lookup.InvalidIndex() ) { pKV = new KeyValues("ScriptSavedTable"); - m_Lookup.Insert( Hash(szId), pKV ); + m_Lookup.Insert( hash, pKV ); } else { @@ -1095,8 +1201,10 @@ HSCRIPT CScriptReadWriteFile::KeyValuesRead( const char *szFile ) #ifdef GAME_DLL #define m_MsgIn_() m_MsgIn-> +#define DLL_LOC_STR "[Server]" #else #define m_MsgIn_() m_MsgIn. +#define DLL_LOC_STR "[Client]" #endif @@ -1179,7 +1287,7 @@ void CNetMsgScriptHelper::RecieveMessage( bf_read &msg ) // Don't do anything if there's no VM here. This can happen if a message from the server goes to a VM-less client, or vice versa. if ( !g_pScriptVM ) { - CGWarning( 0, CON_GROUP_VSCRIPT, "CNetMsgScriptHelper: No VM on receiving side\n" ); + CGWarning( 0, CON_GROUP_VSCRIPT, DLL_LOC_STR " CNetMsgScriptHelper: No VM on receiving side\n" ); return; } @@ -1192,13 +1300,13 @@ void CNetMsgScriptHelper::RecieveMessage( bf_read &msg ) if ( g_pScriptVM->ExecuteFunction( hfn, NULL, 0, NULL, NULL, true ) == SCRIPT_ERROR ) #endif { - DevWarning( 3, "NetMsg: invalid callback for '%d'\n", hash ); + DevWarning( 2, DLL_LOC_STR " NetMsg: invalid callback [%d]\n", hash ); } g_pScriptVM->ReleaseValue( hfn ); } else { - DevWarning( 3, "NetMsg hook not found for '%d'\n", hash ); + DevWarning( 2, DLL_LOC_STR " NetMsg hook not found [%d]\n", hash ); } } @@ -1230,6 +1338,8 @@ void CNetMsgScriptHelper::Send( HSCRIPT player, bool bReliable ) m_filter.MakeReliable(); } + Assert( usermessages->LookupUserMessage( "ScriptMsg" ) != -1 ); + DoSendUserMsg( &m_filter, usermessages->LookupUserMessage( "ScriptMsg" ) ); } #else // CLIENT_DLL @@ -1428,9 +1538,7 @@ void CNetMsgScriptHelper::WriteBool( bool bValue ) void CNetMsgScriptHelper::WriteEntity( HSCRIPT hEnt ) { CBaseEntity *p = ToEnt(hEnt); - int i; - if (p) i = p->entindex(); - else i = -1; + int i = p ? p->entindex() : -1; m_MsgOut.WriteSBitLong( i, MAX_EDICT_BITS ); } @@ -1583,6 +1691,7 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "N DEFINE_SCRIPTFUNC( SendUserMessage, "Send a usermessage from the server to the client" ) DEFINE_SCRIPTFUNC( SendEntityMessage, "Send a message from a server side entity to its client side counterpart" ) DEFINE_SCRIPTFUNC( AddRecipient, "" ) + //DEFINE_SCRIPTFUNC( RemoveRecipient, "" ) DEFINE_SCRIPTFUNC( AddRecipientsByPVS, "" ) DEFINE_SCRIPTFUNC( AddRecipientsByPAS, "" ) DEFINE_SCRIPTFUNC( AddAllPlayers, "" ) @@ -1599,21 +1708,21 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "N DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the client to the server (max 2045 bytes)" ) #endif - DEFINE_SCRIPTFUNC( WriteInt, "" ) - DEFINE_SCRIPTFUNC( WriteUInt, "" ) + DEFINE_SCRIPTFUNC( WriteInt, "variable bit signed int" ) + DEFINE_SCRIPTFUNC( WriteUInt, "variable bit unsigned int" ) DEFINE_SCRIPTFUNC( WriteByte, "8 bit unsigned char" ) DEFINE_SCRIPTFUNC( WriteChar, "8 bit char" ) DEFINE_SCRIPTFUNC( WriteShort, "16 bit short" ) DEFINE_SCRIPTFUNC( WriteWord, "16 bit unsigned short" ) DEFINE_SCRIPTFUNC( WriteLong, "32 bit long" ) - DEFINE_SCRIPTFUNC( WriteFloat, "" ) + DEFINE_SCRIPTFUNC( WriteFloat, "32 bit float" ) DEFINE_SCRIPTFUNC( WriteNormal, "12 bit" ) DEFINE_SCRIPTFUNC( WriteAngle, "8 bit unsigned char" ) DEFINE_SCRIPTFUNC( WriteCoord, "" ) DEFINE_SCRIPTFUNC( WriteVec3Coord, "" ) DEFINE_SCRIPTFUNC( WriteVec3Normal, "27 bit" ) DEFINE_SCRIPTFUNC( WriteAngles, "" ) - DEFINE_SCRIPTFUNC( WriteString, "max 512 bytes at once" ) + DEFINE_SCRIPTFUNC( WriteString, "" ) DEFINE_SCRIPTFUNC( WriteBool, "1 bit" ) DEFINE_SCRIPTFUNC( WriteEntity, "11 bit (entindex)" ) DEFINE_SCRIPTFUNC( WriteEHandle, "32 bit long" ) @@ -1632,7 +1741,7 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "N DEFINE_SCRIPTFUNC( ReadVec3Coord, "" ) DEFINE_SCRIPTFUNC( ReadVec3Normal, "" ) DEFINE_SCRIPTFUNC( ReadAngles, "" ) - DEFINE_SCRIPTFUNC( ReadString, "" ) + DEFINE_SCRIPTFUNC( ReadString, "max 512 bytes at once" ) DEFINE_SCRIPTFUNC( ReadBool, "" ) DEFINE_SCRIPTFUNC( ReadEntity, "" ) DEFINE_SCRIPTFUNC( ReadEHandle, "" ) @@ -1645,10 +1754,7 @@ END_SCRIPTDESC(); #define RETURN_IF_CANNOT_DRAW_OVERLAY\ if (engine->IsPaused())\ - {\ - CGWarning( 1, CON_GROUP_VSCRIPT, "debugoverlay: cannot draw while the game is paused!\n");\ - return;\ - } + return; class CDebugOverlayScriptHelper { public: @@ -2054,6 +2160,647 @@ END_SCRIPTDESC(); +//============================================================================= +// ConVars +//============================================================================= +class CScriptConCommand : public ICommandCallback, public ICommandCompletionCallback +{ +public: + ~CScriptConCommand() + { + Unregister(); + delete m_cmd; + } + + CScriptConCommand( const char *name, HSCRIPT fn, const char *helpString, int flags ) + { + m_cmd = new ConCommand( name, this, helpString, flags, 0 ); + m_hCallback = fn; + m_nCmdNameLen = V_strlen(name) + 1; + + Assert( m_nCmdNameLen - 1 <= 128 ); + } + + void CommandCallback( const CCommand &command ) + { + int count = command.ArgC(); + ScriptVariant_t *vArgv = (ScriptVariant_t*)stackalloc( sizeof(ScriptVariant_t) * count ); + for ( int i = 0; i < count; ++i ) + { + vArgv[i] = command[i]; + } + if ( g_pScriptVM->ExecuteFunction( m_hCallback, vArgv, count, NULL, NULL, true ) == SCRIPT_ERROR ) + { + DevWarning( 1, "CScriptConCommand: invalid callback for '%s'\n", command[0] ); + } + } + + int CommandCompletionCallback( const char *partial, CUtlVector< CUtlString > &commands ) + { + Assert( g_pScriptVM ); + Assert( m_hCompletionCallback ); + + ScriptVariant_t hArray; + g_pScriptVM->CreateArray( hArray ); + + // split command name from partial, pass both separately to the script function + char *cmdname = (char*)stackalloc( m_nCmdNameLen ); + V_memcpy( cmdname, partial, m_nCmdNameLen - 1 ); + cmdname[ m_nCmdNameLen - 1 ] = 0; + + char argPartial[256]; + V_StrRight( partial, V_strlen(partial) - m_nCmdNameLen, argPartial, sizeof(argPartial) ); + + ScriptVariant_t args[3] = { cmdname, argPartial, hArray }; + if ( g_pScriptVM->ExecuteFunction( m_hCompletionCallback, args, 3, NULL, NULL, true ) == SCRIPT_ERROR ) + { + DevWarning( 1, "CScriptConCommand: invalid command completion callback for '%s'\n", cmdname ); + g_pScriptVM->ReleaseScript( hArray ); + return 0; + } + + int count = 0; + ScriptVariant_t val; + int it = -1; + while ( ( it = g_pScriptVM->GetKeyValue( hArray, it, NULL, &val ) ) != -1 ) + { + if ( val.m_type == FIELD_CSTRING ) + { + CUtlString s = val.m_pszString; + //s.SetLength( COMMAND_COMPLETION_ITEM_LENGTH - 1 ); + commands.AddToTail( s ); + ++count; + } + g_pScriptVM->ReleaseValue(val); + + if ( count == COMMAND_COMPLETION_MAXITEMS ) + break; + } + g_pScriptVM->ReleaseScript( hArray ); + return count; + } + + void SetCompletionCallback( HSCRIPT fn ) + { + if ( m_hCompletionCallback ) + g_pScriptVM->ReleaseScript( m_hCompletionCallback ); + + if (fn) + { + if ( !m_cmd->IsRegistered() ) + return; + + m_cmd->m_pCommandCompletionCallback = this; + m_cmd->m_bHasCompletionCallback = true; + m_hCompletionCallback = fn; + } + else + { + m_cmd->m_pCommandCompletionCallback = NULL; + m_cmd->m_bHasCompletionCallback = false; + m_hCompletionCallback = NULL; + } + } + + void SetCallback( HSCRIPT fn ) + { + if (fn) + { + if ( !m_cmd->IsRegistered() ) + Register(); + + if ( m_hCallback ) + g_pScriptVM->ReleaseScript( m_hCallback ); + m_hCallback = fn; + } + else + { + Unregister(); + } + } + + inline void Unregister() + { + if ( g_pCVar && m_cmd->IsRegistered() ) + g_pCVar->UnregisterConCommand( m_cmd ); + + if ( g_pScriptVM ) + { + if ( m_hCallback ) + { + g_pScriptVM->ReleaseScript( m_hCallback ); + m_hCallback = NULL; + } + + SetCompletionCallback( NULL ); + } + } + + inline void Register() + { + if ( g_pCVar ) + g_pCVar->RegisterConCommand( m_cmd ); + } + + HSCRIPT m_hCallback; + HSCRIPT m_hCompletionCallback; + int m_nCmdNameLen; + ConCommand *m_cmd; +}; + +class CScriptConVar +{ +public: + ~CScriptConVar() + { + Unregister(); + delete m_cvar; + } + + CScriptConVar( const char *pName, const char *pDefaultValue, const char *pHelpString, int flags/*, float fMin, float fMax*/ ) + { + m_cvar = new ConVar( pName, pDefaultValue, flags, pHelpString ); + } + + inline void Unregister() + { + if ( g_pCVar && m_cvar->IsRegistered() ) + g_pCVar->UnregisterConCommand( m_cvar ); + } + + ConVar *m_cvar; +}; + +class CScriptConvarAccessor : public CAutoGameSystem +{ +public: + static CUtlMap< unsigned int, bool > g_ConVarsBlocked; + static CUtlMap< unsigned int, bool > g_ConCommandsOverridable; + static CUtlMap< unsigned int, CScriptConCommand* > g_ScriptConCommands; + static CUtlMap< unsigned int, CScriptConVar* > g_ScriptConVars; + static inline unsigned int Hash( const char*sz ){ return HashStringCaseless(sz); } + +public: + inline void AddOverridable( const char *name ) + { + g_ConCommandsOverridable.InsertOrReplace( Hash(name), true ); + } + + inline bool IsOverridable( unsigned int hash ) + { + int idx = g_ConCommandsOverridable.Find( hash ); + if ( idx == g_ConCommandsOverridable.InvalidIndex() ) + return false; + return g_ConCommandsOverridable[idx]; + } + + inline void AddBlockedConVar( const char *name ) + { + g_ConVarsBlocked.InsertOrReplace( Hash(name), true ); + } + + inline bool IsBlockedConvar( const char *name ) + { + int idx = g_ConVarsBlocked.Find( Hash(name) ); + if ( idx == g_ConVarsBlocked.InvalidIndex() ) + return false; + return g_ConVarsBlocked[idx]; + } + +public: + void RegisterCommand( const char *name, HSCRIPT fn, const char *helpString, int flags ); + void SetCompletionCallback( const char *name, HSCRIPT fn ); + void UnregisterCommand( const char *name ); + void RegisterConvar( const char *name, const char *pDefaultValue, const char *helpString, int flags ); + + HSCRIPT GetCommandClient() + { +#ifdef GAME_DLL + return ToHScript( UTIL_GetCommandClient() ); +#else + return ToHScript( C_BasePlayer::GetLocalPlayer() ); +#endif + } +#ifdef GAME_DLL + const char *GetClientConvarValue( int index, const char* cvar ) + { + return engine->GetClientConVarValue( index, cvar ); + } +#endif +public: + bool Init(); + + void LevelShutdownPostEntity() + { + g_ScriptConCommands.PurgeAndDeleteElements(); + g_ScriptConVars.PurgeAndDeleteElements(); + } + +public: + float GetFloat( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) + return NULL; + return cvar.GetFloat(); + } + + int GetInt( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) + return NULL; + return cvar.GetInt(); + } + + bool GetBool( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) + return NULL; + return cvar.GetBool(); + } + + const char *GetStr( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) + return NULL; + return cvar.GetString(); + } + + const char *GetDefaultValue( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + return cvar.GetDefault(); + } + + bool IsFlagSet( const char *pszConVar, int nFlags ) + { + ConVarRef cvar( pszConVar ); + return cvar.IsFlagSet( nFlags ); + } + + void SetFloat( const char *pszConVar, float value ) + { + SetValue( pszConVar, value ); + } + + void SetInt( const char *pszConVar, int value ) + { + SetValue( pszConVar, value ); + } + + void SetBool( const char *pszConVar, bool value ) + { + SetValue( pszConVar, value ); + } + + void SetStr( const char *pszConVar, const char *value ) + { + SetValue( pszConVar, value ); + } + + template + void SetValue( const char *pszConVar, T value ) + { + ConVarRef cvar( pszConVar ); + if ( !cvar.IsValid() ) + return; + + if ( cvar.IsFlagSet( FCVAR_NOT_CONNECTED | FCVAR_SERVER_CANNOT_QUERY ) ) + return; + + if ( IsBlockedConvar( pszConVar ) ) + return; + + cvar.SetValue( value ); + } + +} g_ScriptConvarAccessor; + + +CUtlMap< unsigned int, bool > CScriptConvarAccessor::g_ConVarsBlocked( DefLessFunc(unsigned int) ); +CUtlMap< unsigned int, bool > CScriptConvarAccessor::g_ConCommandsOverridable( DefLessFunc(unsigned int) ); +CUtlMap< unsigned int, CScriptConCommand* > CScriptConvarAccessor::g_ScriptConCommands( DefLessFunc(unsigned int) ); +CUtlMap< unsigned int, CScriptConVar* > CScriptConvarAccessor::g_ScriptConVars( DefLessFunc(unsigned int) ); + +void CScriptConvarAccessor::RegisterCommand( const char *name, HSCRIPT fn, const char *helpString, int flags ) +{ + unsigned int hash = Hash(name); + int idx = g_ScriptConCommands.Find(hash); + if ( idx == g_ScriptConCommands.InvalidIndex() ) + { + if ( g_pCVar->FindVar(name) || ( g_pCVar->FindCommand(name) && !IsOverridable(hash) ) ) + { + DevWarning( 1, "CScriptConvarAccessor::RegisterCommand unable to register blocked ConCommand: %s\n", name ); + return; + } + + if ( !fn ) + return; + + CScriptConCommand *p = new CScriptConCommand( name, fn, helpString, flags ); + g_ScriptConCommands.Insert( hash, p ); + } + else + { + CScriptConCommand *pCmd = g_ScriptConCommands[idx]; + pCmd->SetCallback( fn ); + pCmd->m_cmd->AddFlags( flags ); + //CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptConvarAccessor::RegisterCommand replacing command already registered: %s\n", name ); + } +} + +void CScriptConvarAccessor::SetCompletionCallback( const char *name, HSCRIPT fn ) +{ + unsigned int hash = Hash(name); + int idx = g_ScriptConCommands.Find(hash); + if ( idx != g_ScriptConCommands.InvalidIndex() ) + { + g_ScriptConCommands[idx]->SetCompletionCallback( fn ); + } +} + +void CScriptConvarAccessor::UnregisterCommand( const char *name ) +{ + unsigned int hash = Hash(name); + int idx = g_ScriptConCommands.Find(hash); + if ( idx != g_ScriptConCommands.InvalidIndex() ) + { + g_ScriptConCommands[idx]->Unregister(); + } +} + +void CScriptConvarAccessor::RegisterConvar( const char *name, const char *pDefaultValue, const char *helpString, int flags ) +{ + Assert( g_pCVar ); + unsigned int hash = Hash(name); + int idx = g_ScriptConVars.Find(hash); + if ( idx == g_ScriptConVars.InvalidIndex() ) + { + if ( g_pCVar->FindVar(name) || g_pCVar->FindCommand(name) ) + { + DevWarning( 1, "CScriptConvarAccessor::RegisterConvar unable to register blocked ConCommand: %s\n", name ); + return; + } + + CScriptConVar *p = new CScriptConVar( name, pDefaultValue, helpString, flags ); + g_ScriptConVars.Insert( hash, p ); + } + else + { + g_ScriptConVars[idx]->m_cvar->AddFlags( flags ); + //CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptConvarAccessor::RegisterConvar convar %s already registered\n", name ); + } +} + +bool CScriptConvarAccessor::Init() +{ + static bool bExecOnce = false; + if ( bExecOnce ) + return true; + bExecOnce = true; + + AddOverridable( "+attack" ); + AddOverridable( "+attack2" ); + AddOverridable( "+attack3" ); + AddOverridable( "+forward" ); + AddOverridable( "+back" ); + AddOverridable( "+moveleft" ); + AddOverridable( "+moveright" ); + AddOverridable( "+use" ); + AddOverridable( "+jump" ); + AddOverridable( "+zoom" ); + AddOverridable( "+reload" ); + AddOverridable( "+speed" ); + AddOverridable( "+walk" ); + AddOverridable( "+duck" ); + AddOverridable( "+strafe" ); + AddOverridable( "+alt1" ); + AddOverridable( "+alt2" ); + AddOverridable( "+grenade1" ); + AddOverridable( "+grenade2" ); + AddOverridable( "+showscores" ); + + AddOverridable( "-attack" ); + AddOverridable( "-attack2" ); + AddOverridable( "-attack3" ); + AddOverridable( "-forward" ); + AddOverridable( "-back" ); + AddOverridable( "-moveleft" ); + AddOverridable( "-moveright" ); + AddOverridable( "-use" ); + AddOverridable( "-jump" ); + AddOverridable( "-zoom" ); + AddOverridable( "-reload" ); + AddOverridable( "-speed" ); + AddOverridable( "-walk" ); + AddOverridable( "-duck" ); + AddOverridable( "-strafe" ); + AddOverridable( "-alt1" ); + AddOverridable( "-alt2" ); + AddOverridable( "-grenade1" ); + AddOverridable( "-grenade2" ); + AddOverridable( "-showscores" ); + + AddOverridable( "toggle_duck" ); + AddOverridable( "lastinv" ); + AddOverridable( "invnext" ); + AddOverridable( "invprev" ); + AddOverridable( "phys_swap" ); + AddOverridable( "slot1" ); + AddOverridable( "slot2" ); + AddOverridable( "slot3" ); + AddOverridable( "slot4" ); + AddOverridable( "slot5" ); + AddOverridable( "slot6" ); + AddOverridable( "slot7" ); + + + AddBlockedConVar( "con_enable" ); + AddBlockedConVar( "cl_allowdownload" ); + AddBlockedConVar( "cl_allowupload" ); + AddBlockedConVar( "cl_downloadfilter" ); + + return true; +} + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptConvarAccessor, "CConvars", SCRIPT_SINGLETON "Provides an interface to convars." ) + DEFINE_SCRIPTFUNC( RegisterConvar, "register a new console variable." ) + DEFINE_SCRIPTFUNC( RegisterCommand, "register a console command." ) + DEFINE_SCRIPTFUNC( SetCompletionCallback, "callback is called with 3 parameters (cmdname, partial, commands), user strings must be appended to 'commands' array" ) + DEFINE_SCRIPTFUNC( UnregisterCommand, "unregister a console command." ) + DEFINE_SCRIPTFUNC( GetCommandClient, "returns the player who issued this console command." ) +#ifdef GAME_DLL + DEFINE_SCRIPTFUNC( GetClientConvarValue, "Get a convar keyvalue for a specified client" ) +#endif + DEFINE_SCRIPTFUNC( GetFloat, "Returns the convar as a float. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( GetInt, "Returns the convar as an int. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( GetBool, "Returns the convar as a bool. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( GetStr, "Returns the convar as a string. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( GetDefaultValue, "Returns the convar's default value as a string. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( IsFlagSet, "Returns the convar's flags. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( SetFloat, "Sets the value of the convar as a float." ) + DEFINE_SCRIPTFUNC( SetInt, "Sets the value of the convar as an int." ) + DEFINE_SCRIPTFUNC( SetBool, "Sets the value of the convar as a bool." ) + DEFINE_SCRIPTFUNC( SetStr, "Sets the value of the convar as a string." ) +END_SCRIPTDESC(); + + +//============================================================================= +// Effects +// (Unique to mapbase) +// +// At the moment only clientside until a filtering method on server is finalised. +// +// TEs most of the time call IEffects (g_pEffects) or ITempEnts (tempents) on client, +// but they also record for tools recording mode. +// +// On client no TE is suppressed. +// TE flags are found at tempent.h +// +// TODO: +//============================================================================= +#ifdef CLIENT_DLL + +class CEffectsScriptHelper +{ +private: + C_RecipientFilter filter; + +public: + void DynamicLight( int index, const Vector& origin, int r, int g, int b, int exponent, + float radius, float die, float decay, int style = 0, int flags = 0 ) + { + //te->DynamicLight( filter, delay, &origin, r, g, b, exponent, radius, die, decay ); + dlight_t *dl = effects->CL_AllocDlight( index ); + dl->origin = origin; + dl->color.r = r; + dl->color.g = g; + dl->color.b = b; + dl->color.exponent = exponent; + dl->radius = radius; + dl->die = gpGlobals->curtime + die; + dl->decay = decay; + dl->style = style; + dl->flags = flags; + } + + void Explosion( const Vector& pos, float scale, int radius, int magnitude, int flags ) + { + filter.AddAllPlayers(); + // framerate, modelindex, normal and materialtype are unused + // radius for ragdolls + extern short g_sModelIndexFireball; + te->Explosion( filter, 0.0f, &pos, g_sModelIndexFireball, scale, 15, flags, radius, magnitude, &vec3_origin ); + } + +// void FXExplosion( const Vector& pos, const Vector& normal, int materialType = 'C' ) +// { +// // just the particles +// // materialtype only for debris. can be 'C','W' or anything else. +// FX_Explosion( const_cast(pos), const_cast(normal), materialType ); +// } + +// void ConcussiveExplosion( const Vector& pos, const Vector& normal ) +// { +// FX_ConcussiveExplosion( const_cast(pos), const_cast(normal) ); +// } + +// void MicroExplosion( const Vector& pos, const Vector& normal ) +// { +// FX_MicroExplosion( const_cast(pos), const_cast(normal) ); +// } + +// void MuzzleFlash( int type, HSCRIPT hEntity, int attachment, bool firstPerson ) +// { +// C_BaseEntity *p = ToEnt(hEntity); +// ClientEntityHandle_t ent = p ? (ClientEntityList().EntIndexToHandle)( p->entindex() ) : NULL;; +// tempents->MuzzleFlash( type, ent, attachment, firstPerson ); +// } + + void Sparks( const Vector& pos, int nMagnitude, int nTrailLength, const Vector& pDir ) + { + //te->Sparks( filter, delay, &pos, nMagnitude, nTrailLength, &pDir ); + //g_pEffects->Sparks( pos, nMagnitude, nTrailLength, &pDir ); + FX_ElectricSpark( pos, nMagnitude, nTrailLength, &pDir ); + } + + void MetalSparks( const Vector& pos, const Vector& dir ) + { + //g_pEffects->MetalSparks( pos, dir ); + FX_MetalSpark( pos, dir, dir ); + } + +// void Smoke( const Vector& pos, float scale, int framerate) +// { +// extern short g_sModelIndexSmoke; +// //te->Smoke( filter, 0.0, &pos, g_sModelIndexSmoke, scale * 10.0f, framerate ); +// g_pEffects->Smoke( pos, g_sModelIndexSmoke, scale, framerate ); +// } + + void Dust( const Vector &pos, const Vector &dir, float size, float speed ) + { + //te->Dust( filter, delay, pos, dir, size, speed ); + //g_pEffects->Dust( pos, dir, size, speed ); + FX_Dust( pos, dir, size, speed ); + } + + void Bubbles( const Vector &mins, const Vector &maxs, float height, int modelindex, int count, float speed ) + { + //int bubbles = modelinfo->GetModelIndex( "sprites/bubble.vmt" ); + //te->Bubbles( filter, delay, &mins, &maxs, height, modelindex, count, speed ); + tempents->Bubbles( mins, maxs, height, modelindex, count, speed ); + } + +// void Fizz( const Vector& mins, const Vector& maxs, int modelIndex, int density, int current/*, int flags*/ ) +// { +// //te->Fizz( filter, delay, ent, modelindex, density, current ); +// //tempents->FizzEffect( ToEnt(ent), modelindex, density, current ); +// } + + void Sprite( const Vector &pos, const Vector &dir, float scale, int modelIndex, int rendermode, + int renderfx, int brightness, float life, int flags ) + { + //te->Sprite( filter, delay, &pos, modelindex, size, brightness ); + float a = (1.0 / 255.0) * brightness; + tempents->TempSprite( pos, dir, scale, modelIndex, rendermode, renderfx, a, life, flags ); + } + +// void PhysicsProp( float delay, int modelindex, int skin, const Vector& pos, const QAngle &angles, +// const Vector& vel, int flags, int effects ) +// { +// //te->PhysicsProp( filter, delay, modelindex, skin, pos, angles, vel, flags, effects ); +// tempents->PhysicsProp( modelindex, skin, pos, angles, vel, flags, effects ); +// } + + void ClientProjectile( const Vector& vecOrigin, const Vector& vecVelocity, const Vector& vecAccel, int modelindex, + int lifetime, HSCRIPT pOwner, const char *pszImpactEffect = NULL, const char *pszParticleEffect = NULL ) + { + //te->ClientProjectile( filter, delay, &vecOrigin, &vecVelocity, modelindex, lifetime, ToEnt(pOwner) ); + if ( pszImpactEffect && !(*pszImpactEffect) ) + pszImpactEffect = NULL; + if ( pszParticleEffect && !(*pszParticleEffect) ) + pszParticleEffect = NULL; + tempents->ClientProjectile( vecOrigin, vecVelocity, vecAccel, modelindex, lifetime, ToEnt(pOwner), pszImpactEffect, pszParticleEffect ); + } + +} g_ScriptEffectsHelper; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CEffectsScriptHelper, "CEffects", SCRIPT_SINGLETON "" ) + DEFINE_SCRIPTFUNC( DynamicLight, "" ) + DEFINE_SCRIPTFUNC( Explosion, "" ) + DEFINE_SCRIPTFUNC( Sparks, "" ) + DEFINE_SCRIPTFUNC( MetalSparks, "" ) + DEFINE_SCRIPTFUNC( Dust, "" ) + DEFINE_SCRIPTFUNC( Bubbles, "" ) + DEFINE_SCRIPTFUNC( Sprite, "" ) + DEFINE_SCRIPTFUNC( ClientProjectile, "" ) +END_SCRIPTDESC(); +#endif + + void RegisterScriptSingletons() { ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::SaveTable, "SaveTable", "Store a table with primitive values that will persist across level transitions and save loads." ); @@ -2076,6 +2823,10 @@ void RegisterScriptSingletons() g_pScriptVM->RegisterInstance( &g_ScriptLocalize, "Localize" ); g_pScriptVM->RegisterInstance( g_ScriptNetMsg, "NetMsg" ); g_pScriptVM->RegisterInstance( &g_ScriptDebugOverlay, "debugoverlay" ); + g_pScriptVM->RegisterInstance( &g_ScriptConvarAccessor, "Convars" ); +#ifdef CLIENT_DLL + g_pScriptVM->RegisterInstance( &g_ScriptEffectsHelper, "effects" ); +#endif // Singletons not unique to VScript (not declared or defined here) g_pScriptVM->RegisterInstance( GameRules(), "GameRules" ); @@ -2084,9 +2835,9 @@ void RegisterScriptSingletons() g_pScriptVM->RegisterInstance( &g_AI_SquadManager, "Squads" ); #endif -#ifndef CLIENT_DLL +#ifdef USE_OLD_EVENT_DESCRIPTORS CScriptGameEventListener::LoadAllEvents(); -#endif // !CLIENT_DLL +#endif g_ScriptNetMsg->InitPostVM(); } diff --git a/sp/src/game/shared/mapbase/vscript_singletons.h b/sp/src/game/shared/mapbase/vscript_singletons.h index a3b94f9bb8..04568decf4 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.h +++ b/sp/src/game/shared/mapbase/vscript_singletons.h @@ -5,8 +5,8 @@ // $NoKeywords: $ //============================================================================= -#ifndef VSCRIPT_SINGLETONS -#define VSCRIPT_SINGLETONS +#ifndef VSCRIPT_SINGLETONS_H +#define VSCRIPT_SINGLETONS_H #ifdef _WIN32 #pragma once #endif @@ -29,21 +29,24 @@ class CNetMsgScriptHelper : public CAutoGameSystem class CNetMsgScriptHelper #endif { +#ifdef CLIENT_DLL +public: + bool m_bWriteReady; // dt ready to send +#endif + private: #ifdef GAME_DLL - CRecipientFilter m_filter; bf_read *m_MsgIn; + CRecipientFilter m_filter; #else bf_read m_MsgIn; #endif + HSCRIPT m_Hooks; bf_write m_MsgOut; byte m_MsgData[ PAD_NUMBER( SCRIPT_NETMSG_DATA_SIZE, 4 ) ]; - HSCRIPT m_Hooks; public: #ifdef CLIENT_DLL - bool m_bWriteReady; // dt ready to send - CNetMsgScriptHelper() : m_Hooks(NULL), m_bWriteReady(false) {} #else CNetMsgScriptHelper() : m_Hooks(NULL) {} @@ -100,14 +103,14 @@ class CNetMsgScriptHelper void WriteShort( int iValue ); // 16 bit short void WriteWord( int iValue ); // 16 bit unsigned short void WriteLong( int iValue ); // 32 bit long - void WriteFloat( float flValue ); - void WriteNormal( float flValue ); // 12 bit + void WriteFloat( float flValue ); // 32 bit float + void WriteNormal( float flValue ); // 12 bit (1 + NORMAL_FRACTIONAL_BITS) void WriteAngle( float flValue ); // 8 bit unsigned char void WriteCoord( float flValue ); void WriteVec3Coord( const Vector& rgflValue ); void WriteVec3Normal( const Vector& rgflValue ); // 27 bit ( 3 + 2 * (1 + NORMAL_FRACTIONAL_BITS) ) void WriteAngles( const QAngle& rgflValue ); - void WriteString( const char *sz ); // max 512 bytes at once + void WriteString( const char *sz ); void WriteBool( bool bValue ); // 1 bit void WriteEntity( HSCRIPT hEnt ); // 11 bit (entindex) void WriteEHandle( HSCRIPT hEnt ); // 32 bit long diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.h b/sp/src/game/shared/mapbase/weapon_custom_scripted.h index 90630a653b..f1493ce019 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.h +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.h @@ -5,8 +5,8 @@ // $NoKeywords: $ //============================================================================= -#ifndef VSCRIPT_FUNCS_MATH -#define VSCRIPT_FUNCS_MATH +#ifndef WEAPON_CUSTOM_SCRIPTED_H +#define WEAPON_CUSTOM_SCRIPTED_H #ifdef _WIN32 #pragma once #endif diff --git a/sp/src/game/shared/vscript_shared.cpp b/sp/src/game/shared/vscript_shared.cpp index c460a48a80..86e220b47d 100644 --- a/sp/src/game/shared/vscript_shared.cpp +++ b/sp/src/game/shared/vscript_shared.cpp @@ -440,13 +440,17 @@ ISaveRestoreBlockHandler *GetVScriptSaveRestoreBlockHandler() bool CBaseEntityScriptInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) { CBaseEntity *pEntity = (CBaseEntity *)p; +#ifdef CLIENT_DLL + if ( pEntity->GetEntityName() && pEntity->GetEntityName()[0] ) +#else if ( pEntity->GetEntityName() != NULL_STRING ) +#endif { - V_snprintf( pBuf, bufSize, "([%d] %s: %s)", pEntity->entindex(), STRING(pEntity->m_iClassname), STRING( pEntity->GetEntityName() ) ); + V_snprintf( pBuf, bufSize, "([%d] %s: %s)", pEntity->entindex(), pEntity->GetClassname(), STRING( pEntity->GetEntityName() ) ); } else { - V_snprintf( pBuf, bufSize, "([%d] %s)", pEntity->entindex(), STRING(pEntity->m_iClassname) ); + V_snprintf( pBuf, bufSize, "([%d] %s)", pEntity->entindex(), pEntity->GetClassname() ); } return true; } diff --git a/sp/src/public/tier1/convar.h b/sp/src/public/tier1/convar.h index 2c3647277d..314ee011c2 100644 --- a/sp/src/public/tier1/convar.h +++ b/sp/src/public/tier1/convar.h @@ -301,6 +301,10 @@ friend class CCvar; ICommandCallback *m_pCommandCallback; }; +#ifdef MAPBASE_VSCRIPT + // Allow late modification of the completion callback. +public: +#endif union { FnCommandCompletionCallback m_fnCompletionCallback; @@ -308,6 +312,9 @@ friend class CCvar; }; bool m_bHasCompletionCallback : 1; +#ifdef MAPBASE_VSCRIPT +private: +#endif bool m_bUsingNewCommandCallback : 1; bool m_bUsingCommandCallbackInterface : 1; }; diff --git a/sp/src/public/tier1/mapbase_con_groups.h b/sp/src/public/tier1/mapbase_con_groups.h index c0e356267e..cdd955c970 100644 --- a/sp/src/public/tier1/mapbase_con_groups.h +++ b/sp/src/public/tier1/mapbase_con_groups.h @@ -17,21 +17,23 @@ //static const Color CON_COLOR_DEV_VERBOSE( 192, 128, 192, 255 ); // General -#define CON_GROUP_MAPBASE_MISC "Mapbase Misc." -#define CON_GROUP_PHYSICS "Physics" +#define CON_GROUP_MAPBASE_MISC 0 // "Mapbase Misc." +#define CON_GROUP_PHYSICS 1 // "Physics" // Server -#define CON_GROUP_IO_SYSTEM "I/O System" -#define CON_GROUP_NPC_AI "NPC AI" -#define CON_GROUP_NPC_SCRIPTS "NPC Scripts" -#define CON_GROUP_CHOREO "Choreo" +#define CON_GROUP_IO_SYSTEM 2 // "Entity I/O" +#define CON_GROUP_NPC_AI 3 // "NPC AI" +#define CON_GROUP_NPC_SCRIPTS 4 // "NPC Scripts" +#define CON_GROUP_CHOREO 5 // "Choreo" // VScript -#define CON_GROUP_VSCRIPT "VScript" -#define CON_GROUP_VSCRIPT_PRINT "VScript Print" +#define CON_GROUP_VSCRIPT 6 // "VScript" +#define CON_GROUP_VSCRIPT_PRINT 7 // "VScript Print" + +#define CON_GROUP_MAX 8 // must always be at the end // Mapbase console group message. -void CGMsg( int level, const char *pszGroup, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +void CGMsg( int level, int nGroup, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 3, 4 ); #define CGWarning CGMsg diff --git a/sp/src/public/vscript/ivscript.h b/sp/src/public/vscript/ivscript.h index a1c1e85613..4b3388df76 100644 --- a/sp/src/public/vscript/ivscript.h +++ b/sp/src/public/vscript/ivscript.h @@ -749,9 +749,6 @@ struct ScriptEnumDesc_t #define DEFINE_SCRIPTHOOK_PARAM( paramName, type ) pHook->AddParameter( paramName, type ); -// Define actual parameters instead of global variables -#define DEFINE_SCRIPTHOOK_REALPARAM( paramName, type ) - #define END_SCRIPTHOOK() \ pDesc->m_Hooks.AddToTail(pHook); \ } @@ -944,7 +941,7 @@ class IScriptVM #endif #ifdef MAPBASE_VSCRIPT - // virtual void CreateArray(ScriptVariant_t &arr, int size = 0) = 0; + virtual void CreateArray(ScriptVariant_t &arr, int size = 0) = 0; virtual bool ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) = 0; #endif diff --git a/sp/src/tier1/mapbase_con_groups.cpp b/sp/src/tier1/mapbase_con_groups.cpp index 533d57911d..cb01280e69 100644 --- a/sp/src/tier1/mapbase_con_groups.cpp +++ b/sp/src/tier1/mapbase_con_groups.cpp @@ -65,46 +65,35 @@ DEFINE_CON_GROUP_CVAR( vscript_print, "80 186 255", "Messages from VScript's 'pr //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -#define DEFINE_CON_GROUP(name, codename) { name, &con_group_##codename##_color } +#define DEFINE_CON_GROUP(id, name, codename) { name, &con_group_##codename##_color } -ConGroup_t g_ConGroups[] = { +ConGroup_t g_ConGroups[CON_GROUP_MAX] = { // General - DEFINE_CON_GROUP( CON_GROUP_MAPBASE_MISC, mapbase_misc ), - DEFINE_CON_GROUP( CON_GROUP_PHYSICS, physics ), + DEFINE_CON_GROUP( CON_GROUP_MAPBASE_MISC, "Mapbase misc.", mapbase_misc ), + DEFINE_CON_GROUP( CON_GROUP_PHYSICS, "Physics", physics ), // Server - DEFINE_CON_GROUP( CON_GROUP_IO_SYSTEM, inputoutput ), - DEFINE_CON_GROUP( CON_GROUP_NPC_AI, npc_ai ), - DEFINE_CON_GROUP( CON_GROUP_NPC_SCRIPTS, npc_scripts ), - DEFINE_CON_GROUP( CON_GROUP_CHOREO, choreo ), + DEFINE_CON_GROUP( CON_GROUP_IO_SYSTEM, "Entity IO", inputoutput ), + DEFINE_CON_GROUP( CON_GROUP_NPC_AI, "NPC AI", npc_ai ), + DEFINE_CON_GROUP( CON_GROUP_NPC_SCRIPTS, "NPC scripts", npc_scripts ), + DEFINE_CON_GROUP( CON_GROUP_CHOREO, "Choreo", choreo ), // VScript - DEFINE_CON_GROUP( CON_GROUP_VSCRIPT, vscript ), - DEFINE_CON_GROUP( CON_GROUP_VSCRIPT_PRINT, vscript_print ), + DEFINE_CON_GROUP( CON_GROUP_VSCRIPT, "VScript", vscript ), + DEFINE_CON_GROUP( CON_GROUP_VSCRIPT_PRINT, "VScript print", vscript_print ), }; void CV_ColorChanged( IConVar *pConVar, const char *pOldString, float flOldValue ) { - for (int i = 0; i < ARRAYSIZE( g_ConGroups ); i++) + for (int i = 0; i < CON_GROUP_MAX; i++) { // Reset the alpha to indicate it needs to be refreshed g_ConGroups[i]._clr[3] = 0; } } -ConGroup_t *FindConGroup( const char *pszGroup ) -{ - for (int i = 0; i < ARRAYSIZE( g_ConGroups ); i++) - { - if (V_strcmp(pszGroup, g_ConGroups[i].pszName) == 0) - return &g_ConGroups[i]; - } - - return NULL; -} - //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -113,7 +102,7 @@ ConVar con_group_include_name( "con_group_include_name", "0", FCVAR_NONE, "Inclu CON_COMMAND( con_group_list, "Prints a list of all console groups." ) { Msg( "============================================================\n" ); - for (int i = 0; i < ARRAYSIZE( g_ConGroups ); i++) + for (int i = 0; i < CON_GROUP_MAX; i++) { Msg( " # " ); ConColorMsg( g_ConGroups[i].GetColor(), "%s", g_ConGroups[i].pszName ); @@ -146,7 +135,7 @@ CON_COMMAND( con_group_toggle, "Toggles a console group." ) } */ -void CGMsg( int level, const char *pszGroup, const tchar* pMsg, ... ) +void CGMsg( int level, int nGroup, const tchar* pMsg, ... ) { // Return early if we're not at this level if (!IsSpewActive("developer", level)) @@ -158,22 +147,21 @@ void CGMsg( int level, const char *pszGroup, const tchar* pMsg, ... ) Q_vsnprintf( string, sizeof(string), pMsg, argptr ); va_end( argptr ); - ConGroup_t *pGroup = FindConGroup( pszGroup ); - if (pGroup) + Assert( nGroup >= 0 ); + Assert( nGroup < CON_GROUP_MAX ); + + ConGroup_t *pGroup = &g_ConGroups[nGroup]; + + /*if (pGroup->bDisabled) { - /*if (pGroup->bDisabled) - { - // Do nothing - } - else*/ if (con_group_include_name.GetBool()) - { - ConColorMsg( level, pGroup->GetColor(), "[%s] %s", pGroup->pszName, string ); - } - else - { - ConColorMsg( level, pGroup->GetColor(), string ); - } + // Do nothing + } + else*/ if (con_group_include_name.GetBool()) + { + ConColorMsg(level, pGroup->GetColor(), "[%s] %s", pGroup->pszName, string); } else - DevMsg( level, string ); + { + ConColorMsg(level, pGroup->GetColor(), string); + } } diff --git a/sp/src/utils/vbsp/vscript_funcs_vmfs.cpp b/sp/src/utils/vbsp/vscript_funcs_vmfs.cpp index 3894ddf35a..8ef38c8140 100644 --- a/sp/src/utils/vbsp/vscript_funcs_vmfs.cpp +++ b/sp/src/utils/vbsp/vscript_funcs_vmfs.cpp @@ -83,6 +83,7 @@ static HSCRIPT VMFKV_LoadFromFile( const char *szFile ) KeyValues *pKV = new KeyValues( szFile ); if ( !pKV->LoadFromFile( g_pFullFileSystem, pszFullName, NULL ) ) { + pKV->deleteThis(); return NULL; } diff --git a/sp/src/utils/vbsp/vscript_vbsp.cpp b/sp/src/utils/vbsp/vscript_vbsp.cpp index 91fa41f68a..c8295e0205 100644 --- a/sp/src/utils/vbsp/vscript_vbsp.cpp +++ b/sp/src/utils/vbsp/vscript_vbsp.cpp @@ -11,6 +11,7 @@ #include "vbsp.h" #include "map.h" #include "fgdlib/fgdlib.h" +#include "convar.h" #include "vscript_vbsp.h" #include "vscript_vbsp.nut" @@ -183,6 +184,28 @@ BEGIN_SCRIPTDESC_ROOT( CMapFile, "Map file" ) END_SCRIPTDESC(); + +static float cvar_getf( const char* sz ) +{ + ConVarRef cvar(sz); + if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) + return NULL; + return cvar.GetFloat(); +} + +static bool cvar_setf( const char* sz, float val ) +{ + ConVarRef cvar(sz); + if ( !cvar.IsValid() ) + return false; + + if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) + return false; + + cvar.SetValue(val); + return true; +} + static const char *GetSource() { return source; @@ -244,6 +267,9 @@ bool VScriptVBSPInit() { Log( "VSCRIPT VBSP: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); + ScriptRegisterFunction( g_pScriptVM, cvar_getf, "Gets the value of the given cvar, as a float." ); + ScriptRegisterFunction( g_pScriptVM, cvar_setf, "Sets the value of the given cvar, as a float." ); + ScriptRegisterFunction( g_pScriptVM, GetSource, "Gets the base directory of the first map loaded." ); ScriptRegisterFunction( g_pScriptVM, GetMapBase, "Gets the base name of the first map loaded." ); ScriptRegisterFunction( g_pScriptVM, GetMainMap, "Gets the first map loaded." ); diff --git a/sp/src/vscript/vscript_bindings_base.cpp b/sp/src/vscript/vscript_bindings_base.cpp index 56da843508..9511efa408 100644 --- a/sp/src/vscript/vscript_bindings_base.cpp +++ b/sp/src/vscript/vscript_bindings_base.cpp @@ -50,100 +50,6 @@ static void ScriptColorPrintL( int r, int g, int b, const char *pszMsg ) ConColorMsg( clr, "%s\n", pszMsg ); } -//============================================================================= -// -// Convar Lookup -// -//============================================================================= -class CScriptConvarLookup -{ -public: - - float GetFloat( const char *pszConVar ) - { - ConVarRef cvar( pszConVar ); - return cvar.GetFloat(); - } - - int GetInt( const char *pszConVar ) - { - ConVarRef cvar( pszConVar ); - return cvar.GetInt(); - } - - bool GetBool( const char *pszConVar ) - { - ConVarRef cvar( pszConVar ); - return cvar.GetBool(); - } - - const char *GetStr( const char *pszConVar ) - { - ConVarRef cvar( pszConVar ); - return cvar.GetString(); - } - - const char *GetDefaultValue( const char *pszConVar ) - { - ConVarRef cvar( pszConVar ); - return cvar.GetDefault(); - } - - bool IsFlagSet( const char *pszConVar, int nFlags ) - { - ConVarRef cvar( pszConVar ); - return cvar.IsFlagSet( nFlags ); - } - - void SetFloat( const char *pszConVar, float value ) - { - SetValue( pszConVar, value ); - } - - void SetInt( const char *pszConVar, int value ) - { - SetValue( pszConVar, value ); - } - - void SetBool( const char *pszConVar, bool value ) - { - SetValue( pszConVar, value ); - } - - void SetStr( const char *pszConVar, const char *value ) - { - SetValue( pszConVar, value ); - } - - template - void SetValue( const char *pszConVar, T value ) - { - ConVarRef cvar( pszConVar ); - if (!cvar.IsValid()) - return; - - // FCVAR_NOT_CONNECTED can be used to protect specific convars from nefarious interference - if (cvar.IsFlagSet(FCVAR_NOT_CONNECTED)) - return; - - cvar.SetValue( value ); - } - -private: -} g_ScriptConvarLookup; - -BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptConvarLookup, "CConvars", SCRIPT_SINGLETON "Provides an interface for getting and setting convars." ) - DEFINE_SCRIPTFUNC( GetFloat, "Returns the convar as a float. May return null if no such convar." ) - DEFINE_SCRIPTFUNC( GetInt, "Returns the convar as an int. May return null if no such convar." ) - DEFINE_SCRIPTFUNC( GetBool, "Returns the convar as a bool. May return null if no such convar." ) - DEFINE_SCRIPTFUNC( GetStr, "Returns the convar as a string. May return null if no such convar." ) - DEFINE_SCRIPTFUNC( GetDefaultValue, "Returns the convar's default value as a string. May return null if no such convar." ) - DEFINE_SCRIPTFUNC( IsFlagSet, "Returns the convar's flags. May return null if no such convar." ) - DEFINE_SCRIPTFUNC( SetFloat, "Sets the value of the convar as a float." ) - DEFINE_SCRIPTFUNC( SetInt, "Sets the value of the convar as an int." ) - DEFINE_SCRIPTFUNC( SetBool, "Sets the value of the convar as a bool." ) - DEFINE_SCRIPTFUNC( SetStr, "Sets the value of the convar as a string." ) -END_SCRIPTDESC(); //============================================================================= // @@ -203,6 +109,7 @@ BEGIN_SCRIPTDESC_ROOT( CScriptKeyValues, "Wrapper class over KeyValues instance" DEFINE_SCRIPTFUNC_NAMED( ScriptReleaseKeyValues, "ReleaseKeyValues", "Given a root KeyValues object, release its contents" ); DEFINE_SCRIPTFUNC( TableToSubKeys, "Converts a script table to KeyValues." ); + DEFINE_SCRIPTFUNC( SubKeysToTable, "Converts to script table." ); DEFINE_SCRIPTFUNC_NAMED( ScriptFindOrCreateKey, "FindOrCreateKey", "Given a KeyValues object and a key name, find or create a KeyValues object associated with the key name" ); @@ -319,6 +226,19 @@ void CScriptKeyValues::TableToSubKeys( HSCRIPT hTable ) } } +void CScriptKeyValues::SubKeysToTable( HSCRIPT hTable ) +{ + FOR_EACH_SUBKEY( m_pKeyValues, 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; + } + } +} + HSCRIPT CScriptKeyValues::ScriptFindOrCreateKey( const char *pszName ) { KeyValues *pKeyValues = m_pKeyValues->FindKey(pszName, true); @@ -529,7 +449,6 @@ void RegisterBaseBindings( IScriptVM *pVM ) //----------------------------------------------------------------------------- - pVM->RegisterInstance( &g_ScriptConvarLookup, "Convars" ); pVM->RegisterInstance( &g_ScriptGlobalSys, "GlobalSys" ); //----------------------------------------------------------------------------- diff --git a/sp/src/vscript/vscript_bindings_base.h b/sp/src/vscript/vscript_bindings_base.h index a2d9fb9a02..2629aada48 100644 --- a/sp/src/vscript/vscript_bindings_base.h +++ b/sp/src/vscript/vscript_bindings_base.h @@ -35,6 +35,7 @@ class CScriptKeyValues // Functions below are new with Mapbase void TableToSubKeys( HSCRIPT hTable ); + void SubKeysToTable( HSCRIPT hTable ); HSCRIPT ScriptFindOrCreateKey( const char *pszName ); diff --git a/sp/src/vscript/vscript_bindings_math.cpp b/sp/src/vscript/vscript_bindings_math.cpp index cb6c594df0..2f189265fc 100644 --- a/sp/src/vscript/vscript_bindings_math.cpp +++ b/sp/src/vscript/vscript_bindings_math.cpp @@ -181,7 +181,7 @@ END_SCRIPTDESC(); bool CScriptQuaternionInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) { Quaternion *pQuat = ((Quaternion *)p); - V_snprintf( pBuf, bufSize, "(quaternion: (%f, %f, %f, %f))", pQuat->x, pQuat->y, pQuat->z, pQuat->w ); + V_snprintf( pBuf, bufSize, "(Quaternion %p [%f %f %f %f])", (void*)pQuat, pQuat->x, pQuat->y, pQuat->z, pQuat->w ); return true; } @@ -390,6 +390,11 @@ float ScriptCalcDistanceToLineSegment( const Vector &point, const Vector &vLineA return CalcDistanceToLineSegment( point, vLineA, vLineB ); } +inline float ScriptExponentialDecay( float decayTo, float decayTime, float dt ) +{ + return ExponentialDecay( decayTo, decayTime, dt ); +} + void RegisterMathBaseBindings( IScriptVM *pVM ) { ScriptRegisterConstantNamed( pVM, ((float)(180.f / M_PI_F)), "RAD2DEG", "" ); @@ -453,4 +458,12 @@ void RegisterMathBaseBindings( IScriptVM *pVM ) ScriptRegisterFunctionNamed( pVM, ScriptCalcClosestPointOnLine, "CalcClosestPointOnLine", "Returns the closest point on a line." ); ScriptRegisterFunctionNamed( pVM, ScriptCalcDistanceToLineSegment, "CalcDistanceToLineSegment", "Returns the distance to a line segment." ); ScriptRegisterFunctionNamed( pVM, ScriptCalcClosestPointOnLineSegment, "CalcClosestPointOnLineSegment", "Returns the closest point on a line segment." ); + + ScriptRegisterFunction( pVM, SimpleSplineRemapVal, "remaps a value in [startInterval, startInterval+rangeInterval] from linear to spline using SimpleSpline" ); + ScriptRegisterFunction( pVM, SimpleSplineRemapValClamped, "remaps a value in [startInterval, startInterval+rangeInterval] from linear to spline using SimpleSpline" ); + ScriptRegisterFunction( pVM, Bias, "The curve is biased towards 0 or 1 based on biasAmt, which is between 0 and 1." ); + ScriptRegisterFunction( pVM, Gain, "Gain is similar to Bias, but biasAmt biases towards or away from 0.5." ); + ScriptRegisterFunction( pVM, SmoothCurve, "SmoothCurve maps a 0-1 value into another 0-1 value based on a cosine wave" ); + ScriptRegisterFunction( pVM, SmoothCurve_Tweak, "SmoothCurve peaks at flPeakPos, flPeakSharpness controls the sharpness of the peak" ); + ScriptRegisterFunctionNamed( pVM, ScriptExponentialDecay, "ExponentialDecay", "decayTo is factor the value should decay to in decayTime" ); } diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 2c80041b21..af605ea112 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -214,7 +214,7 @@ class SquirrelVM : public IScriptVM virtual bool ClearValue(HSCRIPT hScope, const char* pszKey) override; virtual bool ClearValue( HSCRIPT hScope, ScriptVariant_t pKey ) override; - // virtual void CreateArray(ScriptVariant_t &arr, int size = 0) override; + virtual void CreateArray(ScriptVariant_t &arr, int size = 0) override; virtual bool ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) override; //---------------------------------------------------------------------------- @@ -270,7 +270,7 @@ namespace SQVector return 0; } - SQInteger Get(HSQUIRRELVM vm) + SQInteger _get(HSQUIRRELVM vm) { const char* key = nullptr; sq_getstring(vm, 2, &key); @@ -296,7 +296,7 @@ namespace SQVector return 1; } - SQInteger Set(HSQUIRRELVM vm) + SQInteger _set(HSQUIRRELVM vm) { const char* key = nullptr; sq_getstring(vm, 2, &key); @@ -328,7 +328,7 @@ namespace SQVector return 0; } - SQInteger Add(HSQUIRRELVM vm) + SQInteger _add(HSQUIRRELVM vm) { Vector* v1 = nullptr; Vector* v2 = nullptr; @@ -350,7 +350,7 @@ namespace SQVector return 1; } - SQInteger Sub(HSQUIRRELVM vm) + SQInteger _sub(HSQUIRRELVM vm) { Vector* v1 = nullptr; Vector* v2 = nullptr; @@ -372,23 +372,20 @@ namespace SQVector return 1; } - SQInteger Multiply(HSQUIRRELVM vm) + SQInteger _multiply(HSQUIRRELVM vm) { Vector* v1 = nullptr; if (sq_gettop(vm) != 2 || SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) { - return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); } - SQObjectType paramType = sq_gettype(vm, 2); - float s = 0.0; Vector* v2 = nullptr; - if ((paramType & SQOBJECT_NUMERIC) && - SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) + if ( SQ_SUCCEEDED(sq_getfloat(vm, 2, &s)) ) { sq_getclass(vm, 1); sq_createinstance(vm, -1); @@ -399,8 +396,7 @@ namespace SQVector return 1; } - else if (paramType == OT_INSTANCE && - SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + else if ( SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR)) ) { sq_getclass(vm, 1); sq_createinstance(vm, -1); @@ -413,27 +409,24 @@ namespace SQVector } else { - return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); } } - SQInteger Divide(HSQUIRRELVM vm) + SQInteger _divide(HSQUIRRELVM vm) { Vector* v1 = nullptr; if (sq_gettop(vm) != 2 || SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) { - return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); } - SQObjectType paramType = sq_gettype(vm, 2); - float s = 0.0; Vector* v2 = nullptr; - if ((paramType & SQOBJECT_NUMERIC) && - SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) + if ( SQ_SUCCEEDED(sq_getfloat(vm, 2, &s)) ) { sq_getclass(vm, 1); sq_createinstance(vm, -1); @@ -444,8 +437,7 @@ namespace SQVector return 1; } - else if (paramType == OT_INSTANCE && - SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + else if ( SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR)) ) { sq_getclass(vm, 1); sq_createinstance(vm, -1); @@ -458,8 +450,219 @@ namespace SQVector } else { - return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); + } + } + + SQInteger _unm(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + v1->Negate(); + + return 1; + } + + // multi purpose - copy from input vector, or init with 3 float input + SQInteger Set(HSQUIRRELVM vm) + { + SQInteger top = sq_gettop(vm); + Vector* v1 = nullptr; + + if ( top < 2 || SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) ) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + Vector* v2 = nullptr; + + if ( SQ_SUCCEEDED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR)) ) + { + if ( top != 2 ) + return sq_throwerror(vm, "Expected (Vector, Vector)"); + + VectorCopy( *v2, *v1 ); + sq_remove( vm, -1 ); + + return 1; + } + + float x, y, z; + + if ( top == 4 && + SQ_SUCCEEDED(sq_getfloat(vm, 2, &x)) && + SQ_SUCCEEDED(sq_getfloat(vm, 3, &y)) && + SQ_SUCCEEDED(sq_getfloat(vm, 4, &z)) ) + { + v1->Init( x, y, z ); + sq_pop( vm, 3 ); + + return 1; + } + + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + SQInteger Add(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + VectorAdd( *v1, *v2, *v1 ); + sq_remove( vm, -1 ); + + return 1; + } + + SQInteger Subtract(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + VectorSubtract( *v1, *v2, *v1 ); + sq_remove( vm, -1 ); + + return 1; + } + + SQInteger Multiply(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); + } + + Vector* v2 = nullptr; + + if ( SQ_SUCCEEDED(sq_getinstanceup( vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR )) ) + { + VectorMultiply( *v1, *v2, *v1 ); + sq_remove( vm, -1 ); + + return 1; + } + + float flInput; + + if ( SQ_SUCCEEDED(sq_getfloat( vm, 2, &flInput )) ) + { + VectorMultiply( *v1, flInput, *v1 ); + sq_remove( vm, -1 ); + + return 1; + } + + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); + } + + SQInteger Divide(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); + } + + Vector* v2 = nullptr; + + if ( SQ_SUCCEEDED(sq_getinstanceup( vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR )) ) + { + VectorDivide( *v1, *v2, *v1 ); + sq_remove( vm, -1 ); + + return 1; } + + float flInput; + + if ( SQ_SUCCEEDED(sq_getfloat( vm, 2, &flInput )) ) + { + VectorDivide( *v1, flInput, *v1 ); + sq_remove( vm, -1 ); + + return 1; + } + + return sq_throwerror(vm, "Expected (Vector, Vector|float)"); + } + + SQInteger DistTo(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + sq_pushfloat( vm, v1->DistTo(*v2) ); + + return 1; + } + + SQInteger DistToSqr(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + sq_pushfloat( vm, v1->DistToSqr(*v2) ); + + return 1; + } + + SQInteger IsEqualTo(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) < 2 || // bother checking > 3? + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR)) ) + { + return sq_throwerror(vm, "Expected (Vector, Vector, float)"); + } + + float tolerance = 0.0f; + sq_getfloat( vm, 3, &tolerance ); + + sq_pushbool( vm, VectorsAreEqual( *v1, *v2, tolerance ) ); + + return 1; } SQInteger Length(HSQUIRRELVM vm) @@ -557,30 +760,23 @@ namespace SQVector SQInteger Scale(HSQUIRRELVM vm) { Vector* v1 = nullptr; + float s = 0.0f; if (sq_gettop(vm) != 2 || - SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) { return sq_throwerror(vm, "Expected (Vector, float)"); } - float s = 0.0; - - if (SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) - { - sq_getclass(vm, 1); - sq_createinstance(vm, -1); - SQUserPointer p; - sq_getinstanceup(vm, -1, &p, 0); - new(p) Vector((*v1) * s); - sq_remove(vm, -2); + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) * s); + sq_remove(vm, -2); - return 1; - } - else - { - return sq_throwerror(vm, "Expected (Vector, float)"); - } + return 1; } SQInteger Dot(HSQUIRRELVM vm) @@ -613,6 +809,42 @@ namespace SQVector return 1; } + SQInteger FromKVString(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + const char* szInput; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getstring(vm, 2, &szInput)) ) + { + return sq_throwerror(vm, "Expected (Vector, string)"); + } + + float x = 0.0f, y = 0.0f, z = 0.0f; + + if ( sscanf( szInput, "%f %f %f", &x, &y, &z ) < 3 ) // UTIL_StringToVector + { + // Don't throw, return null while invalidating the input vector. + // This allows the user to easily check for input errors without halting. + //return sq_throwerror(vm, "invalid KV string"); + + sq_pushnull(vm); + *v1 = vec3_invalid; + + return 1; + } + + v1->x = x; + v1->y = y; + v1->z = z; + + // return input vector + sq_remove( vm, -1 ); + + return 1; + } + SQInteger Cross(HSQUIRRELVM vm) { Vector* v1 = nullptr; @@ -635,6 +867,25 @@ namespace SQVector return 1; } + SQInteger WithinAABox(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* mins = nullptr; + Vector* maxs = nullptr; + + if (sq_gettop(vm) != 3 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&mins, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 3, (SQUserPointer*)&maxs, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector, Vector)"); + } + + sq_pushbool( vm, v1->WithinAABox( *mins, *maxs ) ); + + return 1; + } + SQInteger ToString(HSQUIRRELVM vm) { Vector* v1 = nullptr; @@ -645,7 +896,7 @@ namespace SQVector return sq_throwerror(vm, "Expected (Vector)"); } - sqstd_pushstringf(vm, "(vector: (%f, %f, %f))", v1->x, v1->y, v1->z); + sqstd_pushstringf(vm, "(Vector %p [%f %f %f])", (void*)v1, v1->x, v1->y, v1->z); return 1; } @@ -701,22 +952,33 @@ namespace SQVector static const SQRegFunction funcs[] = { {_SC("constructor"), Construct,0,nullptr}, - {_SC("_get"), Get, 2, _SC(".s")}, - {_SC("_set"), Set, 3, _SC(".sn")}, - {_SC("_add"), Add, 2, _SC("..")}, - {_SC("_sub"), Sub, 2, _SC("..")}, - {_SC("_mul"), Multiply, 2, _SC("..")}, - {_SC("_div"), Divide, 2, _SC("..")}, + {_SC("_get"), _get, 2, _SC(".s")}, + {_SC("_set"), _set, 3, _SC(".sn")}, + {_SC("_add"), _add, 2, _SC("..")}, + {_SC("_sub"), _sub, 2, _SC("..")}, + {_SC("_mul"), _multiply, 2, _SC("..")}, + {_SC("_div"), _divide, 2, _SC("..")}, + {_SC("_unm"), _unm, 1, _SC(".")}, + {_SC("Set"), Set, -2, _SC("..nn")}, + {_SC("Add"), Add, 2, _SC("..")}, + {_SC("Subtract"), Subtract, 2, _SC("..")}, + {_SC("Multiply"), Multiply, 2, _SC("..")}, + {_SC("Divide"), Divide, 2, _SC("..")}, + {_SC("DistTo"), DistTo, 2, _SC("..")}, + {_SC("DistToSqr"), DistToSqr, 2, _SC("..")}, + {_SC("IsEqualTo"), IsEqualTo, -2, _SC("..n")}, {_SC("Length"), Length, 1, _SC(".")}, {_SC("LengthSqr"), LengthSqr, 1, _SC(".")}, {_SC("Length2D"), Length2D, 1, _SC(".")}, {_SC("Length2DSqr"), Length2DSqr, 1, _SC(".")}, {_SC("Normalized"), Normalized, 1, _SC(".")}, {_SC("Norm"), Norm, 1, _SC(".")}, - {_SC("Scale"), Scale, 2, _SC("..")}, + {_SC("Scale"), Scale, 2, _SC(".n")}, // identical to _multiply {_SC("Dot"), Dot, 2, _SC("..")}, {_SC("Cross"), Cross, 2, _SC("..")}, + {_SC("WithinAABox"), WithinAABox, 3, _SC("...")}, {_SC("ToKVString"), ToKVString, 1, _SC(".")}, + {_SC("FromKVString"), FromKVString, 2, _SC(".s")}, {_SC("_tostring"), ToString, 1, _SC(".")}, {_SC("_typeof"), TypeOf, 1, _SC(".")}, {_SC("_nexti"), Nexti, 2, _SC("..")}, @@ -2698,7 +2960,7 @@ bool SquirrelVM::ClearValue(HSCRIPT hScope, ScriptVariant_t pKey) return true; } -/* + void SquirrelVM::CreateArray(ScriptVariant_t &arr, int size) { SquirrelSafeCheck safeCheck(vm_); @@ -2713,16 +2975,14 @@ void SquirrelVM::CreateArray(ScriptVariant_t &arr, int size) arr = (HSCRIPT)obj; } -*/ + bool SquirrelVM::ArrayAppend(HSCRIPT hArray, const ScriptVariant_t &val) { SquirrelSafeCheck safeCheck(vm_); - HSQOBJECT arr = *(HSQOBJECT*)hArray; - if ( !sq_isarray(arr) ) - return false; + HSQOBJECT *arr = (HSQOBJECT*)hArray; - sq_pushobject(vm_, arr); + sq_pushobject(vm_, *arr); PushVariant(vm_, val); bool ret = sq_arrayappend(vm_, -2) == SQ_OK; sq_pop(vm_, 1); diff --git a/sp/src/vscript/vscript_squirrel.nut b/sp/src/vscript/vscript_squirrel.nut index cb929807fe..f12d385b97 100644 --- a/sp/src/vscript/vscript_squirrel.nut +++ b/sp/src/vscript/vscript_squirrel.nut @@ -65,6 +65,22 @@ function AngleDistance( next, cur ) return delta } +function FLerp( f1, f2, i1, i2, x ) +{ + return f1+(f2-f1)*(x-i1)/(i2-i1); +} + +function Lerp( f, A, B ) +{ + return A + ( B - A ) * f +} + +function SimpleSpline( f ) +{ + local ff = f * f; + return 3.0 * ff - 2.0 * ff * f; +} + function printl( text ) { return ::print(text + "\n");