diff --git a/README b/README index 1022c6ed..12ecc545 100644 --- a/README +++ b/README @@ -40,8 +40,8 @@ or complicated code changes accessible and easy to use for level designers and o If you believe any content in Mapbase originates from any leak or unauthorized source (from Valve or otherwise), please contact Blixibon immediately. Mapbase is intended to be usable by everyone, including licensed Source projects and Steam mods. *** -The Alien Swarm SDK was used to backport features and code from newer branches of Source into a Source 2013/Half-Life 2 environment. -Mapbase also implements some of Tony Sergi's code changes from the Source 2007 SDK codebase. Both SDKs are publicly distributed by Valve and are available on Steam. +-- The Alien Swarm SDK was used to backport features and code from newer branches of Source into a Source 2013/Half-Life 2 environment. +-- Mapbase also implements some of Tony Sergi's code changes from the Source 2007 SDK codebase. Both SDKs are publicly distributed by Valve and are available on Steam. Some of the features backported from the Alien Swarm SDK (e.g. game instructor, particle rain) require assets from later versions of Source in order to work properly. The required assets have been backported from Alien Swarm and Left 4 Dead for the release build. They are not available in the code repository. @@ -100,11 +100,13 @@ Direct contributions: - Combine lock hardware on door01_left.mdl created by Kralich (This is asset-based and not reflected in the code) - npc_vehicledriver fixes provided by CrAzY - npc_combine cover behavior patches provided by iohnnyboy +- logic_playmovie icon created by URAKOLOUY5 (This is asset-based and not reflected in the code) == Contributions from samisalreadytaken: =-- https://github.com/mapbase-source/source-sdk-2013/pull/47 (VScript utility/consistency changes) =-- https://github.com/mapbase-source/source-sdk-2013/pull/59 (New VScript functions and singletons based on API documentation in later Source/Source 2 games) =-- https://github.com/mapbase-source/source-sdk-2013/pull/80 (More VScript changes, including support for extremely flexible client/server messaging) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/105 (VScript fixes and optimizations, Vector class extensions, custom convars/commands) //--------------------------------------------------------------------------------------------------------------------------------------------------- @@ -117,6 +119,7 @@ Other sources: -- https://github.com/ValveSoftware/source-sdk-2013/pull/401 (func_rot_button "Starts locked" flag fix) -- https://github.com/ValveSoftware/source-sdk-2013/pull/391 (VBSP func_detail smoothing group fix) -- https://github.com/ValveSoftware/source-sdk-2013/pull/362 (npc_manhack npc_maker fix; Adjusted for formatting and save/restore in Mapbase) +-- https://github.com/Petercov/Source-PlusPlus/commit/ecdf50c48cd31dec4dbdb7fea2d0780e7f0dd8ec (used as a guide for porting the Alien Swarm SDK response system) - https://github.com/momentum-mod/game/blob/1d066180b3bf74830c51e6914d46c40b0bea1fc2/mp/src/game/server/player.cpp#L6543 (spec_goto fix) - Poison zombie barnacle crash fix implemented based on a snippet from HL2: Plus posted by Agent Agrimar on Discord (Mapbase makes the barnacle recognize it as poison just like poison headcrabs) - https://gamebanana.com/skins/172192 (Airboat handling fix; This is asset-based and not reflected in the code) diff --git a/sp/src/game/client/c_baseanimating.cpp b/sp/src/game/client/c_baseanimating.cpp index f9b6486c..640459e5 100644 --- a/sp/src/game/client/c_baseanimating.cpp +++ b/sp/src/game/client/c_baseanimating.cpp @@ -293,6 +293,7 @@ BEGIN_ENT_SCRIPTDESC( C_ClientRagdoll, C_BaseAnimating, "Client-side ragdolls" ) END_SCRIPTDESC(); ScriptHook_t C_BaseAnimating::g_Hook_OnClientRagdoll; +ScriptHook_t C_BaseAnimating::g_Hook_FireEvent; #endif BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-side" ) @@ -346,6 +347,13 @@ BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-si BEGIN_SCRIPTHOOK( C_BaseAnimating::g_Hook_OnClientRagdoll, "OnClientRagdoll", FIELD_VOID, "Called when this entity turns into a client-side ragdoll." ) DEFINE_SCRIPTHOOK_PARAM( "ragdoll", FIELD_HSCRIPT ) END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( C_BaseAnimating::g_Hook_FireEvent, "FireEvent", FIELD_BOOLEAN, "Called when handling animation events. Return false to cancel base handling." ) + DEFINE_SCRIPTHOOK_PARAM( "origin", FIELD_VECTOR ) + DEFINE_SCRIPTHOOK_PARAM( "angles", FIELD_VECTOR ) + DEFINE_SCRIPTHOOK_PARAM( "event", FIELD_INTEGER ) + DEFINE_SCRIPTHOOK_PARAM( "options", FIELD_CSTRING ) + END_SCRIPTHOOK() #endif END_SCRIPTDESC(); @@ -3651,7 +3659,11 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) flEventCycle, gpGlobals->curtime ); } - + +#ifdef MAPBASE_VSCRIPT + if (ScriptHookFireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() ) == false) + continue; +#endif FireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() ); } @@ -3684,6 +3696,11 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) gpGlobals->curtime ); } +#ifdef MAPBASE_VSCRIPT + if (ScriptHookFireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() ) == false) + continue; +#endif + FireEvent( GetAbsOrigin(), GetAbsAngles(), pevent[ i ].event, pevent[ i ].pszOptions() ); } } @@ -3691,6 +3708,26 @@ void C_BaseAnimating::DoAnimationEvents( CStudioHdr *pStudioHdr ) m_flPrevEventCycle = flEventCycle; } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool C_BaseAnimating::ScriptHookFireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ) +{ + if (m_ScriptScope.IsInitialized() && g_Hook_FireEvent.CanRunInScope(m_ScriptScope)) + { + // origin, angles, event, options + ScriptVariant_t args[] = { origin, angles, event, options }; + ScriptVariant_t returnValue = true; + g_Hook_FireEvent.Call( m_ScriptScope, &returnValue, args ); + + return returnValue.m_bool; + } + + return true; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Parses a muzzle effect event and sends it out for drawing // Input : *options - event parameters in text format diff --git a/sp/src/game/client/c_baseanimating.h b/sp/src/game/client/c_baseanimating.h index 6f48ac1a..50b3659b 100644 --- a/sp/src/game/client/c_baseanimating.h +++ b/sp/src/game/client/c_baseanimating.h @@ -164,6 +164,10 @@ public: virtual void FireObsoleteEvent( const Vector& origin, const QAngle& angles, int event, const char *options ); virtual const char* ModifyEventParticles( const char* token ) { return token; } +#ifdef MAPBASE_VSCRIPT + bool ScriptHookFireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ); +#endif + #if defined ( SDK_DLL ) || defined ( HL2MP ) virtual void ResetEventsParity() { m_nPrevResetEventsParity = -1; } // used to force animation events to function on players so the muzzleflashes and other events occur // so new functions don't have to be made to parse the models like CSS does in ProcessMuzzleFlashEvent @@ -477,6 +481,7 @@ public: HSCRIPT ScriptBecomeRagdollOnClient(); static ScriptHook_t g_Hook_OnClientRagdoll; + static ScriptHook_t g_Hook_FireEvent; float ScriptGetPoseParameter(const char* szName); #endif diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index b6253468..a3b40378 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(); @@ -578,6 +586,7 @@ BEGIN_RECV_TABLE_NOBASE(C_BaseEntity, DT_BaseEntity) RecvPropInt(RECVINFO(m_clrRender)), #ifdef MAPBASE RecvPropInt(RECVINFO(m_iViewHideFlags)), + RecvPropBool(RECVINFO(m_bDisableFlashlight)), #endif RecvPropInt(RECVINFO(m_iTeamNum)), RecvPropInt(RECVINFO(m_CollisionGroup)), @@ -1333,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 } } @@ -1683,6 +1701,11 @@ bool C_BaseEntity::ShouldReceiveProjectedTextures( int flags ) if ( IsEffectActive( EF_NODRAW ) ) return false; +#ifdef MAPBASE + if ( m_bDisableFlashlight ) + return false; +#endif + if( flags & SHADOW_FLAGS_FLASHLIGHT ) { if ( GetRenderMode() > kRenderNormal && GetRenderColor().a == 0 ) diff --git a/sp/src/game/client/c_baseentity.h b/sp/src/game/client/c_baseentity.h index 598b88c7..dabfbbd9 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 @@ public: #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); @@ -1378,6 +1389,7 @@ public: #ifdef MAPBASE int m_iViewHideFlags; + bool m_bDisableFlashlight; #endif private: @@ -1518,6 +1530,15 @@ protected: 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_movie_display.cpp b/sp/src/game/client/c_movie_display.cpp new file mode 100644 index 00000000..2285b97e --- /dev/null +++ b/sp/src/game/client/c_movie_display.cpp @@ -0,0 +1,26 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// +//=====================================================================================// +#include "cbase.h" +#include "c_movie_display.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +IMPLEMENT_CLIENTCLASS_DT( C_MovieDisplay, DT_MovieDisplay, CMovieDisplay ) + RecvPropBool( RECVINFO( m_bEnabled ) ), + RecvPropBool( RECVINFO( m_bLooping ) ), + RecvPropString( RECVINFO( m_szMovieFilename ) ), + RecvPropString( RECVINFO( m_szGroupName ) ), +END_RECV_TABLE() + +C_MovieDisplay::C_MovieDisplay() +{ +} + +C_MovieDisplay::~C_MovieDisplay() +{ +} diff --git a/sp/src/game/client/c_movie_display.h b/sp/src/game/client/c_movie_display.h new file mode 100644 index 00000000..d133e82e --- /dev/null +++ b/sp/src/game/client/c_movie_display.h @@ -0,0 +1,34 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#ifndef C_MOVIE_DISPLAY_H +#define C_MOVIE_DISPLAY_H + +#include "cbase.h" + +class C_MovieDisplay : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_MovieDisplay, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + C_MovieDisplay(); + ~C_MovieDisplay(); + + bool IsEnabled( void ) const { return m_bEnabled; } + bool IsLooping( void ) const { return m_bLooping; } + + const char *GetMovieFilename( void ) const { return m_szMovieFilename; } + const char *GetGroupName( void ) const { return m_szGroupName; } + +private: + bool m_bEnabled; + bool m_bLooping; + char m_szMovieFilename[128]; + char m_szGroupName[128]; +}; + +#endif //C_MOVIE_DISPLAY_H \ No newline at end of file diff --git a/sp/src/game/client/c_world.h b/sp/src/game/client/c_world.h index 2174f32b..12366490 100644 --- a/sp/src/game/client/c_world.h +++ b/sp/src/game/client/c_world.h @@ -49,6 +49,8 @@ public: #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/cdll_client_int.cpp b/sp/src/game/client/cdll_client_int.cpp index 6fd1c711..ea23d800 100644 --- a/sp/src/game/client/cdll_client_int.cpp +++ b/sp/src/game/client/cdll_client_int.cpp @@ -274,6 +274,8 @@ void ProcessCacheUsedMaterials() } } +void VGui_ClearVideoPanels(); + // String tables INetworkStringTable *g_pStringTableParticleEffectNames = NULL; INetworkStringTable *g_StringTableEffectDispatch = NULL; @@ -1217,6 +1219,8 @@ void CHLClient::Shutdown( void ) g_pSixenseInput = NULL; #endif + VGui_ClearVideoPanels(); + C_BaseAnimating::ShutdownBoneSetupThreadPool(); ClientWorldFactoryShutdown(); diff --git a/sp/src/game/client/client_mapbase.vpc b/sp/src/game/client/client_mapbase.vpc index 243324f1..797325a0 100644 --- a/sp/src/game/client/client_mapbase.vpc +++ b/sp/src/game/client/client_mapbase.vpc @@ -31,6 +31,9 @@ $Project $File "c_postprocesscontroller.cpp" $File "c_postprocesscontroller.h" $File "c_env_dof_controller.cpp" + $File "c_movie_display.cpp" + $File "c_movie_display.h" + $File "vgui_movie_display.cpp" $Folder "Mapbase" { @@ -57,6 +60,7 @@ $Project $File "mapbase\c_func_fake_worldportal.cpp" $File "mapbase\c_func_fake_worldportal.h" $File "mapbase\c_point_glow.cpp" + $File "mapbase\c_vgui_text_display.cpp" } $Folder "HL2 DLL" diff --git a/sp/src/game/client/mapbase/c_vgui_text_display.cpp b/sp/src/game/client/mapbase/c_vgui_text_display.cpp new file mode 100644 index 00000000..c3847ac0 --- /dev/null +++ b/sp/src/game/client/mapbase/c_vgui_text_display.cpp @@ -0,0 +1,259 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Displays easy, flexible VGui text. Mapbase equivalent of point_worldtext. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "panelmetaclassmgr.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include +#include +#include +#include "ienginevgui.h" +#include "c_vguiscreen.h" +#include "vgui_bitmapbutton.h" +#include "vgui_bitmappanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// vgui_text_display +//----------------------------------------------------------------------------- +class C_VGuiTextDisplay : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_VGuiTextDisplay, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + C_VGuiTextDisplay(); + ~C_VGuiTextDisplay(); + + virtual void PostDataUpdate( DataUpdateType_t updateType ); + + bool IsEnabled( void ) const { return m_bEnabled; } + + const char *GetDisplayText( void ) const { return m_szDisplayText; } + const char *GetFontName( void ) const { return m_szFont; } + int GetResolution( void ) const { return m_iResolution; } + vgui::Label::Alignment GetContentAlignment() const { return m_iContentAlignment; } + + bool NeedsTextUpdate() { return m_bTextNeedsUpdate; } + void UpdatedText() { m_bTextNeedsUpdate = false; } + +private: + bool m_bEnabled; + char m_szDisplayText[256]; + vgui::Label::Alignment m_iContentAlignment; + char m_szFont[64]; + int m_iResolution; + + bool m_bTextNeedsUpdate; +}; + +IMPLEMENT_CLIENTCLASS_DT( C_VGuiTextDisplay, DT_VGuiTextDisplay, CVGuiTextDisplay ) + RecvPropBool( RECVINFO( m_bEnabled ) ), + RecvPropString( RECVINFO( m_szDisplayText ) ), + RecvPropInt( RECVINFO( m_iContentAlignment ) ), + RecvPropString( RECVINFO( m_szFont ) ), + RecvPropInt( RECVINFO( m_iResolution ) ), +END_RECV_TABLE() + +C_VGuiTextDisplay::C_VGuiTextDisplay() +{ +} + +C_VGuiTextDisplay::~C_VGuiTextDisplay() +{ +} + +void C_VGuiTextDisplay::PostDataUpdate( DataUpdateType_t updateType ) +{ + BaseClass::PostDataUpdate( updateType ); + + // For now, always update + m_bTextNeedsUpdate = true; +} + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Control screen +//----------------------------------------------------------------------------- +class C_TextDisplayPanel : public CVGuiScreenPanel +{ + DECLARE_CLASS( C_TextDisplayPanel, CVGuiScreenPanel ); + +public: + C_TextDisplayPanel( vgui::Panel *parent, const char *panelName ); + ~C_TextDisplayPanel( void ); + + virtual void ApplySchemeSettings( IScheme *pScheme ); + + void UpdateText(); + + virtual bool Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ); + virtual void OnTick( void ); + virtual void Paint( void ); + +private: + + CHandle m_hVGUIScreen; + CHandle m_hScreenEntity; + + // VGUI specifics + Label *m_pDisplayTextLabel; +}; + +DECLARE_VGUI_SCREEN_FACTORY( C_TextDisplayPanel, "text_display_panel" ); + +CUtlVector g_TextDisplays; + +//----------------------------------------------------------------------------- +// Constructor: +//----------------------------------------------------------------------------- +C_TextDisplayPanel::C_TextDisplayPanel( vgui::Panel *parent, const char *panelName ) +: BaseClass( parent, "C_TextDisplayPanel"/*, vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/WorldTextPanel.res", "WorldTextPanel" )*/ ) +{ + // Add ourselves to the global list of movie displays + g_TextDisplays.AddToTail( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Clean up the movie +//----------------------------------------------------------------------------- +C_TextDisplayPanel::~C_TextDisplayPanel( void ) +{ + // Remove ourselves from the global list of movie displays + g_TextDisplays.FindAndRemove( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Setup our scheme +//----------------------------------------------------------------------------- +void C_TextDisplayPanel::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + /* + m_pDisplayTextLabel->SetFgColor( Color( 255, 255, 255, 255 ) ); + m_pDisplayTextLabel->SetText( "" ); + m_pDisplayTextLabel->SetVisible( false ); + */ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_TextDisplayPanel::UpdateText() +{ + color32 clr = m_hScreenEntity->GetRenderColor(); + + m_pDisplayTextLabel->SetFgColor( Color( clr.r, clr.g, clr.b, clr.a ) ); + m_pDisplayTextLabel->SetText( m_hScreenEntity->GetDisplayText() ); + + //SetSize( m_hScreenEntity->GetTextSize(), m_hScreenEntity->GetTextSize() ); + SetSize( m_hScreenEntity->GetResolution(), m_hScreenEntity->GetResolution() ); + m_pDisplayTextLabel->SetSize( m_hScreenEntity->GetResolution(), m_hScreenEntity->GetResolution() ); + //m_pDisplayTextLabel->SetSize( m_hScreenEntity->GetTextSize(), m_hScreenEntity->GetTextSize() ); + + Label::Alignment iAlignment = m_hScreenEntity->GetContentAlignment(); + m_pDisplayTextLabel->SetContentAlignment( iAlignment ); + + bool bWrap = true; + bool bCenterWrap = false; + switch (iAlignment) + { + // Center wrap if centered + case Label::Alignment::a_north: + case Label::Alignment::a_center: + case Label::Alignment::a_south: + bCenterWrap = true; + break; + + // HACKHACK: Don't wrap if using an east alignment + case Label::Alignment::a_northeast: + case Label::Alignment::a_east: + case Label::Alignment::a_southeast: + bWrap = false; + break; + } + + m_pDisplayTextLabel->SetWrap( bWrap ); + m_pDisplayTextLabel->SetCenterWrap( bCenterWrap ); + + //Msg( "Resolution is %i\n", m_hScreenEntity->GetResolution() ); + + const char *pszFontName = m_hScreenEntity->GetFontName(); + if (pszFontName && pszFontName[0] != '\0') + { + HFont font = scheme()->GetIScheme( GetScheme() )->GetFont( pszFontName ); + m_pDisplayTextLabel->SetFont( font ); + } + + m_pDisplayTextLabel->SetVisible( true ); +} + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +bool C_TextDisplayPanel::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ) +{ + if ( !BaseClass::Init( pKeyValues, pInitData ) ) + return false; + + // Make sure we get ticked... + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + m_pDisplayTextLabel = dynamic_cast(FindChildByName( "TextDisplay" )); + + // Save this for simplicity later on + m_hVGUIScreen = dynamic_cast( GetEntity() ); + if ( m_hVGUIScreen != NULL ) + { + // Also get the associated entity + m_hScreenEntity = dynamic_cast(m_hVGUIScreen->GetOwnerEntity()); + UpdateText(); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Update the display string +//----------------------------------------------------------------------------- +void C_TextDisplayPanel::OnTick() +{ + if (m_hScreenEntity->NeedsTextUpdate()) + { + UpdateText(); + m_hScreenEntity->UpdatedText(); + } + + BaseClass::OnTick(); +} + +ConVar r_vguitext_bg( "r_vguitext_bg", "0" ); + +//----------------------------------------------------------------------------- +// Purpose: Update and draw the frame +//----------------------------------------------------------------------------- +void C_TextDisplayPanel::Paint( void ) +{ + // Black out the background (we could omit drawing under the video surface, but this is straight-forward) + if ( r_vguitext_bg.GetBool() ) + { + surface()->DrawSetColor( 0, 0, 0, 255 ); + surface()->DrawFilledRect( 0, 0, GetWide(), GetTall() ); + + //surface()->DrawSetColor( 64, 64, 64, 255 ); + //surface()->DrawFilledRect( 0, 0, m_pDisplayTextLabel->GetWide(), m_pDisplayTextLabel->GetTall() ); + } + + // Parent's turn + BaseClass::Paint(); +} diff --git a/sp/src/game/client/panelmetaclassmgr.cpp b/sp/src/game/client/panelmetaclassmgr.cpp index 2b36cc44..6f0b50d0 100644 --- a/sp/src/game/client/panelmetaclassmgr.cpp +++ b/sp/src/game/client/panelmetaclassmgr.cpp @@ -234,14 +234,6 @@ CPanelMetaClassMgrImp::CPanelMetaClassMgrImp() : m_PanelTypeDict( true, 0, 32 ) CPanelMetaClassMgrImp::~CPanelMetaClassMgrImp() { -#ifdef MAPBASE // VDC Memory Leak Fixes - while (m_MetaClassKeyValues.Count()>0) - { - if (m_MetaClassKeyValues[0]) - m_MetaClassKeyValues[0]->deleteThis(); - m_MetaClassKeyValues.RemoveAt(0); - } -#endif } diff --git a/sp/src/game/client/vgui_movie_display.cpp b/sp/src/game/client/vgui_movie_display.cpp new file mode 100644 index 00000000..0f474323 --- /dev/null +++ b/sp/src/game/client/vgui_movie_display.cpp @@ -0,0 +1,437 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#include "cbase.h" +#include "c_vguiscreen.h" +#include "vgui_controls/Label.h" +#include "vgui_BitmapPanel.h" +#include +#include "c_slideshow_display.h" +#include "ienginevgui.h" +#include "fmtstr.h" +#include "vgui_controls/ImagePanel.h" +#include +#include "video/ivideoservices.h" +#include "engine/ienginesound.h" +#include "VGUIMatSurface/IMatSystemSurface.h" +#include "c_movie_display.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +using namespace vgui; + +struct VideoPlaybackInfo_t +{ + VideoPlaybackInfo_t( void ) : + m_pMaterial ( NULL ), + m_nSourceHeight(0), m_nSourceWidth(0), + m_flU(0.0f),m_flV(0.0f) {} + + IMaterial *m_pMaterial; + int m_nSourceHeight, m_nSourceWidth; // Source movie's dimensions + float m_flU, m_flV; // U,V ranges for video on its sheet +}; + +//----------------------------------------------------------------------------- +// Control screen +//----------------------------------------------------------------------------- +class CMovieDisplayScreen : public CVGuiScreenPanel +{ + DECLARE_CLASS( CMovieDisplayScreen, CVGuiScreenPanel ); + +public: + CMovieDisplayScreen( vgui::Panel *parent, const char *panelName ); + ~CMovieDisplayScreen( void ); + + virtual void ApplySchemeSettings( IScheme *pScheme ); + + virtual bool Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ); + virtual void OnTick( void ); + virtual void Paint( void ); + +private: + bool IsActive( void ); + + void SetupMovie( void ); + void UpdateMovie( void ); + bool BeginPlayback( const char *pFilename ); + void CalculatePlaybackDimensions( int nSrcWidth, int nSrcHeight ); + + inline void GetPanelPos( int &xpos, int &ypos ) + { + xpos = ( (float) ( GetWide() - m_nPlaybackWidth ) / 2 ); + ypos = ( (float) ( GetTall() - m_nPlaybackHeight ) / 2 ); + } + +private: + + // BINK playback info + IVideoMaterial *m_VideoMaterial; + VideoPlaybackInfo_t m_playbackInfo; + CHandle m_hVGUIScreen; + CHandle m_hScreenEntity; + + int m_nTextureId; + int m_nPlaybackHeight; // Playback dimensions (proper ration adjustments) + int m_nPlaybackWidth; + bool m_bBlackBackground; + bool m_bSlaved; + bool m_bInitialized; + + bool m_bLastActiveState; // HACK: I'd rather get a real callback... + + // VGUI specifics + Label *m_pDisplayTextLabel; + + Color m_cDefault; + Color m_cInvisible; + + bool bIsAlreadyVisible; +}; + +DECLARE_VGUI_SCREEN_FACTORY( CMovieDisplayScreen, "movie_display_screen" ); + +CUtlVector g_MovieDisplays; + +//----------------------------------------------------------------------------- +// Constructor: +//----------------------------------------------------------------------------- +CMovieDisplayScreen::CMovieDisplayScreen( vgui::Panel *parent, const char *panelName ) +: BaseClass( parent, "CMovieDisplayScreen", vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/MovieDisplayScreen.res", "MovieDisplayScreen" ) ) +{ + m_pDisplayTextLabel = new vgui::Label( this, "NumberDisplay", "testing!"); + + m_VideoMaterial = NULL; + m_nTextureId = -1; + m_bBlackBackground = true; + m_bSlaved = false; + m_bInitialized = false; + + // Add ourselves to the global list of movie displays + g_MovieDisplays.AddToTail( this ); + + m_bLastActiveState = IsActive(); +} + +//----------------------------------------------------------------------------- +// Purpose: Clean up the movie +//----------------------------------------------------------------------------- +CMovieDisplayScreen::~CMovieDisplayScreen( void ) +{ + if ( g_pVideo != NULL && m_VideoMaterial != NULL ) + { + g_pVideo->DestroyVideoMaterial( m_VideoMaterial ); + m_VideoMaterial = NULL; + } + + // Clean up our texture reference + g_pMatSystemSurface->DestroyTextureID( m_nTextureId ); + + // Remove ourselves from the global list of movie displays + g_MovieDisplays.FindAndRemove( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Setup our scheme +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::ApplySchemeSettings( IScheme *pScheme ) +{ + assert( pScheme ); + + m_cDefault = Color( 255, 255, 255, 255 ); + m_cInvisible = Color( 0, 0, 0, 0 ); + + m_pDisplayTextLabel->SetFgColor( m_cDefault ); + m_pDisplayTextLabel->SetText( "" ); + m_pDisplayTextLabel->SetVisible( false ); +} + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +bool CMovieDisplayScreen::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ) +{ + // Make sure we get ticked... + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + if ( !BaseClass::Init( pKeyValues, pInitData ) ) + return false; + + // Save this for simplicity later on + m_hVGUIScreen = dynamic_cast( GetEntity() ); + if ( m_hVGUIScreen != NULL ) + { + // Also get the associated entity + m_hScreenEntity = dynamic_cast(m_hVGUIScreen->GetOwnerEntity()); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Helper function to check our active state +//----------------------------------------------------------------------------- +bool CMovieDisplayScreen::IsActive( void ) +{ + bool bScreenActive = false; + if ( m_hVGUIScreen != NULL ) + { + bScreenActive = m_hVGUIScreen->IsActive(); + } + + return bScreenActive; +} + +//----------------------------------------------------------------------------- +// Purpose: Either become the master of a group of screens, or become a slave to another +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::SetupMovie( void ) +{ + // Only bother if we haven't been setup yet + if ( m_bInitialized ) + return; + + const char *szGroupName = m_hScreenEntity->GetGroupName(); + + CMovieDisplayScreen *pMasterScreen = NULL; + for ( int i = 0; i < g_MovieDisplays.Count(); i++ ) + { + // Must be valid and not us + if ( g_MovieDisplays[i] == NULL || g_MovieDisplays[i] == this ) + continue; + + // Must have an associated movie entity + if ( g_MovieDisplays[i]->m_hScreenEntity == NULL ) + continue; + + // Must have a group name to care + if ( szGroupName[0] == NULL ) + continue; + + // Group names must match! + // FIXME: Use an ID instead? + const char *szTestGroupName = g_MovieDisplays[i]->m_hScreenEntity->GetGroupName(); + if ( Q_strnicmp( szTestGroupName, szGroupName, 128 ) ) + continue; + + // See if we've found a master display + if ( g_MovieDisplays[i]->m_bInitialized && g_MovieDisplays[i]->m_bSlaved == false ) + { + m_bSlaved = true; + + // Share the info from the master + m_playbackInfo = g_MovieDisplays[i]->m_playbackInfo; + + // We need to calculate our own playback dimensions as we may be a different size than our parent + CalculatePlaybackDimensions( m_playbackInfo.m_nSourceWidth, m_playbackInfo.m_nSourceHeight ); + + // Bind our texture + m_nTextureId = surface()->CreateNewTextureID( true ); + g_pMatSystemSurface->DrawSetTextureMaterial( m_nTextureId, m_playbackInfo.m_pMaterial ); + + // Hold this as the master screen + pMasterScreen = g_MovieDisplays[i]; + break; + } + } + + // We need to try again, we have no screen entity! + if ( m_hScreenEntity == NULL ) + return; + + // No master found, become one + if ( pMasterScreen == NULL ) + { + const char *szFilename = m_hScreenEntity->GetMovieFilename(); + BeginPlayback( szFilename ); + m_bSlaved = false; + } + + // Done + m_bInitialized = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Deal with the details of the video playback +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::UpdateMovie( void ) +{ + // Only the master in a group updates the bink file + if ( m_bSlaved ) + return; + + if ( m_VideoMaterial == NULL ) + return; + + // Get the current activity state of the screen + bool bScreenActive = IsActive(); + + // Pause if the game has paused + if ( engine->IsPaused() || engine->Con_IsVisible() ) + { + bScreenActive = false; + } + + // See if we've changed our activity state + if ( bScreenActive != m_bLastActiveState ) + { + m_VideoMaterial->SetPaused( !bScreenActive ); + } + + // Updated + m_bLastActiveState = bScreenActive; + + // Update the frame if we're currently enabled + if ( bScreenActive ) + { + // Update our frame + if ( m_VideoMaterial->Update() == false ) + { + // Issue a close command + // OnVideoOver(); + // StopPlayback(); + } + } +} + +//----------------------------------------------------------------------------- +// Update the display string +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::OnTick() +{ + BaseClass::OnTick(); + + // Create our playback or slave to another screen already playing + SetupMovie(); + + // Now update the movie + UpdateMovie(); +} + +//----------------------------------------------------------------------------- +// Purpose: Adjust the playback dimensions to properly account for our screen dimensions +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::CalculatePlaybackDimensions( int nSrcWidth, int nSrcHeight ) +{ + float flFrameRatio = ( (float) GetWide() / (float) GetTall() ); + float flVideoRatio = ( (float) nSrcWidth / (float) nSrcHeight ); + + if ( flVideoRatio > flFrameRatio ) + { + m_nPlaybackWidth = GetWide(); + m_nPlaybackHeight = ( GetWide() / flVideoRatio ); + } + else if ( flVideoRatio < flFrameRatio ) + { + m_nPlaybackWidth = ( GetTall() * flVideoRatio ); + m_nPlaybackHeight = GetTall(); + } + else + { + m_nPlaybackWidth = GetWide(); + m_nPlaybackHeight = GetTall(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Begins playback of a movie +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CMovieDisplayScreen::BeginPlayback( const char *pFilename ) +{ + // need working video services + if ( g_pVideo == NULL ) + return false; + + // Create a new video material + if ( m_VideoMaterial != NULL ) + { + g_pVideo->DestroyVideoMaterial( m_VideoMaterial ); + m_VideoMaterial = NULL; + } + + // Create a globally unique name for this material + char szMaterialName[256]; + + // Append our group name if we have one + const char *szGroupName = m_hScreenEntity->GetGroupName(); + if ( szGroupName[0] != NULL ) + { + Q_snprintf( szMaterialName, sizeof(szMaterialName), "%s_%s", pFilename, szGroupName ); + } + else + { + Q_strncpy( szMaterialName, pFilename, sizeof(szMaterialName) ); + } + + const char *pszMaterialName = CFmtStrN<128>( "VideoMaterial_", m_hScreenEntity->GetEntityName() ); + m_VideoMaterial = g_pVideo->CreateVideoMaterial( pszMaterialName, pFilename, "GAME", + VideoPlaybackFlags::DEFAULT_MATERIAL_OPTIONS, + VideoSystem::DETERMINE_FROM_FILE_EXTENSION/*, m_bAllowAlternateMedia*/ ); + + if ( m_VideoMaterial == NULL ) + return false; + + m_VideoMaterial->SetMuted( true ); // FIXME: Allow? + + if ( m_hScreenEntity->IsLooping() ) + { + m_VideoMaterial->SetLooping( true ); + } + + if ( m_VideoMaterial->HasAudio() ) + { + // We want to be the sole audio source + enginesound->NotifyBeginMoviePlayback(); + } + + // Get our basic info from the movie + m_VideoMaterial->GetVideoImageSize( &m_playbackInfo.m_nSourceWidth, &m_playbackInfo.m_nSourceHeight ); + m_VideoMaterial->GetVideoTexCoordRange( &m_playbackInfo.m_flU, &m_playbackInfo.m_flV ); + m_playbackInfo.m_pMaterial = m_VideoMaterial->GetMaterial(); + + // Get our playback dimensions + CalculatePlaybackDimensions( m_playbackInfo.m_nSourceWidth, m_playbackInfo.m_nSourceHeight ); + + // Bind our texture + m_nTextureId = surface()->CreateNewTextureID( true ); + g_pMatSystemSurface->DrawSetTextureMaterial( m_nTextureId, m_playbackInfo.m_pMaterial ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Update and draw the frame +//----------------------------------------------------------------------------- +void CMovieDisplayScreen::Paint( void ) +{ + // Masters must keep the video updated + if ( m_bSlaved == false && m_VideoMaterial == NULL ) + { + BaseClass::Paint(); + return; + } + + // Sit in the "center" + int xpos, ypos; + GetPanelPos( xpos, ypos ); + + // Black out the background (we could omit drawing under the video surface, but this is straight-forward) + if ( m_bBlackBackground ) + { + surface()->DrawSetColor( 0, 0, 0, 255 ); + surface()->DrawFilledRect( 0, 0, GetWide(), GetTall() ); + } + + // Draw it + surface()->DrawSetTexture( m_nTextureId ); + surface()->DrawSetColor( 255, 255, 255, 255 ); + surface()->DrawTexturedSubRect( xpos, ypos, xpos+m_nPlaybackWidth, ypos+m_nPlaybackHeight, 0.0f, 0.0f, m_playbackInfo.m_flU, m_playbackInfo.m_flV ); + + // Parent's turn + BaseClass::Paint(); +} diff --git a/sp/src/game/client/vgui_video.cpp b/sp/src/game/client/vgui_video.cpp index 6df78893..17e28aa2 100644 --- a/sp/src/game/client/vgui_video.cpp +++ b/sp/src/game/client/vgui_video.cpp @@ -16,20 +16,59 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +using namespace vgui; + +static CUtlVector< VideoPanel * > g_vecVideoPanels; + +// Thiis is a hack due to the fact that the user can type quit with the video panel up, but it's parented to the GameUI dll root panel, which is already gone so +// we would crash in the destructor +void VGui_ClearVideoPanels() +{ + for ( int i = g_vecVideoPanels.Count() - 1; i >= 0; --i ) + { + if ( g_vecVideoPanels[ i ] ) + { + delete g_vecVideoPanels[ i ]; + } + } + g_vecVideoPanels.RemoveAll(); +} + +struct VideoPanelParms_t +{ + VideoPanelParms_t( bool _interrupt = true, bool _loop = false, bool _mute = false ) + { + bAllowInterrupt = _interrupt; + bLoop = _loop; + bMute = _mute; + } + + bool bAllowInterrupt; + bool bLoop; + bool bMute; + + //float flFadeIn; + //float flFadeOut; +}; VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHeight, unsigned int nWidth, bool allowAlternateMedia ) : BaseClass( NULL, "VideoPanel" ), m_VideoMaterial( NULL ), m_nPlaybackWidth( 0 ), m_nPlaybackHeight( 0 ), - m_bAllowAlternateMedia( allowAlternateMedia ) + m_nShutdownCount( 0 ), + m_bLooping( false ), + m_bStopAllSounds( true ), + m_bAllowInterruption( true ), + m_bAllowAlternateMedia( allowAlternateMedia ), + m_bStarted( false ) { - #ifdef MAPBASE vgui::VPANEL pParent = enginevgui->GetPanel( PANEL_ROOT ); #else vgui::VPANEL pParent = enginevgui->GetPanel( PANEL_GAMEUIDLL ); #endif + SetParent( pParent ); SetVisible( false ); @@ -53,6 +92,11 @@ VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHe SetScheme(vgui::scheme()->LoadSchemeFromFile( "resource/VideoPanelScheme.res", "VideoPanelScheme")); LoadControlSettings("resource/UI/VideoPanel.res"); + + // Let us update + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + g_vecVideoPanels.AddToTail( this ); } //----------------------------------------------------------------------------- @@ -60,6 +104,8 @@ VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHe //----------------------------------------------------------------------------- VideoPanel::~VideoPanel( void ) { + g_vecVideoPanels.FindAndRemove( this ); + SetParent( (vgui::Panel *) NULL ); // Shut down this video, destroy the video material @@ -70,13 +116,39 @@ VideoPanel::~VideoPanel( void ) } } +//----------------------------------------------------------------------------- +// Purpose: Keeps a tab on when the movie is ending and allows a frame to pass to prevent threading issues +//----------------------------------------------------------------------------- +void VideoPanel::OnTick( void ) +{ + if ( m_nShutdownCount > 0 ) + { + m_nShutdownCount++; + + if ( m_nShutdownCount > 10 ) + { + OnClose(); + m_nShutdownCount = 0; + } + } + + BaseClass::OnTick(); +} + +void VideoPanel::OnVideoOver() +{ + StopPlayback(); +} + //----------------------------------------------------------------------------- // Purpose: Begins playback of a movie // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool VideoPanel::BeginPlayback( const char *pFilename ) { - // Who the heck hacked this in? + if ( !pFilename || pFilename[ 0 ] == '\0' ) + return false; + #ifdef _X360 XVIDEO_MODE videoMode; XGetVideoMode( &videoMode ); @@ -106,9 +178,25 @@ bool VideoPanel::BeginPlayback( const char *pFilename ) if ( m_VideoMaterial == NULL ) return false; + if ( m_bLooping ) + { + m_VideoMaterial->SetLooping( true ); + } + +#ifdef MAPBASE + if ( m_bMuted ) + { + m_VideoMaterial->SetMuted( true ); + } +#endif + + m_bStarted = true; + // We want to be the sole audio source - // FIXME: This may not always be true! - enginesound->NotifyBeginMoviePlayback(); + if ( m_bStopAllSounds ) + { + enginesound->NotifyBeginMoviePlayback(); + } int nWidth, nHeight; m_VideoMaterial->GetVideoImageSize( &nWidth, &nHeight ); @@ -168,9 +256,10 @@ void VideoPanel::DoModal( void ) //----------------------------------------------------------------------------- void VideoPanel::OnKeyCodeTyped( vgui::KeyCode code ) { - if ( code == KEY_ESCAPE ) + bool bInterruptKeyPressed = ( code == KEY_ESCAPE ); + if ( m_bAllowInterruption && bInterruptKeyPressed ) { - OnClose(); + StopPlayback(); } else { @@ -181,34 +270,54 @@ void VideoPanel::OnKeyCodeTyped( vgui::KeyCode code ) //----------------------------------------------------------------------------- // Purpose: Handle keys that should cause us to close //----------------------------------------------------------------------------- -void VideoPanel::OnKeyCodePressed( vgui::KeyCode code ) +void VideoPanel::OnKeyCodePressed( vgui::KeyCode keycode ) { + vgui::KeyCode code = GetBaseButtonCode( keycode ); + + // All these keys will interrupt playback + bool bInterruptKeyPressed = ( code == KEY_ESCAPE || + code == KEY_BACKQUOTE || + code == KEY_SPACE || + code == KEY_ENTER || + code == KEY_XBUTTON_A || + code == KEY_XBUTTON_B || + code == KEY_XBUTTON_X || + code == KEY_XBUTTON_Y || + code == KEY_XBUTTON_START || + code == KEY_XBUTTON_BACK ); + // These keys cause the panel to shutdown - if ( code == KEY_ESCAPE || - code == KEY_BACKQUOTE || - code == KEY_SPACE || - code == KEY_ENTER || - code == KEY_XBUTTON_A || - code == KEY_XBUTTON_B || - code == KEY_XBUTTON_X || - code == KEY_XBUTTON_Y || - code == KEY_XBUTTON_START || - code == KEY_XBUTTON_BACK ) + if ( m_bAllowInterruption && bInterruptKeyPressed ) { - OnClose(); + StopPlayback(); } else { - BaseClass::OnKeyCodePressed( code ); + BaseClass::OnKeyCodePressed( keycode ); } } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VideoPanel::StopPlayback( void ) +{ + SetVisible( false ); + + // Start the deferred shutdown process + m_nShutdownCount = 1; +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void VideoPanel::OnClose( void ) { - enginesound->NotifyEndMoviePlayback(); + if ( m_bStopAllSounds ) + { + enginesound->NotifyEndMoviePlayback(); + } + BaseClass::OnClose(); if ( vgui::input()->GetAppModalSurface() == GetVPanel() ) @@ -224,7 +333,6 @@ void VideoPanel::OnClose( void ) engine->ClientCmd( m_szExitCommand ); } - SetVisible( false ); MarkForDeletion(); } @@ -247,26 +355,52 @@ void VideoPanel::Paint( void ) if ( m_VideoMaterial == NULL ) return; + float alpha = ((float)GetFgColor()[3]/255.0f); +#ifdef MAPBASE + if (m_flFadeIn != 0.0f || m_flFadeOut != 0.0f) + { + // GetCurrentVideoTime() and GetVideoDuration() are borked + float flFrameCount = m_VideoMaterial->GetFrameCount(); + float flEnd = flFrameCount / m_VideoMaterial->GetVideoFrameRate().GetFPS(); + float flTime = ((float)(m_VideoMaterial->GetCurrentFrame()) / flFrameCount) * flEnd; + float flFadeOutDelta = (flEnd - m_flFadeOut); + + if (flTime <= m_flFadeIn) + { + alpha = (flTime / m_flFadeIn); + } + else if (flTime >= flFadeOutDelta) + { + alpha = (1.0f - ((flTime - flFadeOutDelta) / m_flFadeOut)); + } + } +#endif + if ( m_VideoMaterial->Update() == false ) { // Issue a close command OnVideoOver(); - OnClose(); + //OnClose(); } // Sit in the "center" int xpos, ypos; GetPanelPos( xpos, ypos ); + LocalToScreen( xpos, ypos ); // Black out the background (we could omit drawing under the video surface, but this is straight-forward) if ( m_bBlackBackground ) { - vgui::surface()->DrawSetColor( 0, 0, 0, 255 ); + vgui::surface()->DrawSetColor( 0, 0, 0, alpha * 255.0f ); vgui::surface()->DrawFilledRect( 0, 0, GetWide(), GetTall() ); } // Draw the polys to draw this out CMatRenderContextPtr pRenderContext( materials ); + +#ifdef MAPBASE + pRenderContext->ClearColor4ub( 255, 255, 255, alpha * 255.0f ); +#endif pRenderContext->MatrixMode( MATERIAL_VIEW ); pRenderContext->PushMatrix(); @@ -306,8 +440,6 @@ void VideoPanel::Paint( void ) flTopY = FLerp( 1, -1, 0, vh ,flTopY ); flBottomY = FLerp( 1, -1, 0, vh, flBottomY ); - float alpha = ((float)GetFgColor()[3]/255.0f); - for ( int corner=0; corner<4; corner++ ) { bool bLeft = (corner==0) || (corner==3); @@ -340,16 +472,37 @@ void VideoPanel::Paint( void ) bool VideoPanel_Create( unsigned int nXPos, unsigned int nYPos, unsigned int nWidth, unsigned int nHeight, const char *pVideoFilename, - const char *pExitCommand /*= NULL*/) + const char *pExitCommand /*= NULL*/, + const VideoPanelParms_t &parms ) { // Create the base video panel - VideoPanel *pVideoPanel = new VideoPanel( nXPos, nYPos, nHeight, nWidth, false ); + VideoPanel *pVideoPanel = new VideoPanel( nXPos, nYPos, nHeight, nWidth ); if ( pVideoPanel == NULL ) return false; + // Toggle if we want the panel to allow interruption + pVideoPanel->SetAllowInterrupt( parms.bAllowInterrupt ); + // Set the command we'll call (if any) when the video is interrupted or completes pVideoPanel->SetExitCommand( pExitCommand ); +#ifdef MAPBASE + // Toggle if we want the panel to loop (inspired by Portal 2) + pVideoPanel->SetLooping( parms.bLoop ); + + // Toggle if we want the panel to be muted + pVideoPanel->SetMuted( parms.bMute ); + + // TODO: Unique "Stop All Sounds" parameter + if (parms.bMute) + { + pVideoPanel->SetStopAllSounds( false ); + } + + // Fade parameters (unfinished) + //pVideoPanel->SetFade( parms.flFadeIn, parms.flFadeOut ); +#endif + // Start it going if ( pVideoPanel->BeginPlayback( pVideoFilename ) == false ) { @@ -364,8 +517,29 @@ bool VideoPanel_Create( unsigned int nXPos, unsigned int nYPos, } //----------------------------------------------------------------------------- -// Purpose: Used to launch a video playback (Debug) - -// user must include file extension +// Purpose: Create a video panel with the supplied commands +//----------------------------------------------------------------------------- +void CreateVideoPanel( const char *lpszFilename, const char *lpszExitCommand, int nWidth, int nHeight, VideoPanelParms_t &parms ) +{ + char strFullpath[MAX_PATH]; + Q_strncpy( strFullpath, "media/", MAX_PATH ); // Assume we must play out of the media directory + char strFilename[MAX_PATH]; + Q_StripExtension( lpszFilename, strFilename, MAX_PATH ); + Q_strncat( strFullpath, lpszFilename, MAX_PATH ); + + // Use the full screen size if they haven't specified an override + unsigned int nScreenWidth = ( nWidth != 0 ) ? nWidth : ScreenWidth(); + unsigned int nScreenHeight = ( nHeight != 0 ) ? nHeight : ScreenHeight(); + + // Create the panel and go! + if ( VideoPanel_Create( 0, 0, nScreenWidth, nScreenHeight, strFullpath, lpszExitCommand, parms ) == false ) + { + Warning( "Unable to play video: %s\n", strFullpath ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Used to launch a video playback //----------------------------------------------------------------------------- CON_COMMAND( playvideo, "Plays a video: [width height]" ) @@ -375,30 +549,32 @@ CON_COMMAND( playvideo, "Plays a video: [width height]" ) unsigned int nScreenWidth = Q_atoi( args[2] ); unsigned int nScreenHeight = Q_atoi( args[3] ); - - char strFullpath[MAX_PATH]; - Q_strncpy( strFullpath, "media/", MAX_PATH ); // Assume we must play out of the media directory - char strFilename[MAX_PATH]; - Q_StripExtension( args[1], strFilename, MAX_PATH ); - Q_strncat( strFullpath, args[1], MAX_PATH ); - - if ( nScreenWidth == 0 ) - { - nScreenWidth = ScreenWidth(); - } - - if ( nScreenHeight == 0 ) - { - nScreenHeight = ScreenHeight(); - } - // Create the panel and go! - if ( VideoPanel_Create( 0, 0, nScreenWidth, nScreenHeight, strFullpath ) == false ) - { - Warning( "Unable to play video: %s\n", strFullpath ); - } + // New struct; functionally identical + VideoPanelParms_t parms; + + CreateVideoPanel( args[1], NULL, nScreenWidth, nScreenHeight, parms ); } +//----------------------------------------------------------------------------- +// Purpose: Used to launch a video playback +//----------------------------------------------------------------------------- + +CON_COMMAND( playvideo_nointerrupt, "Plays a video without ability to skip: [width height]" ) +{ + if ( args.ArgC() < 2 ) + return; + + unsigned int nScreenWidth = Q_atoi( args[2] ); + unsigned int nScreenHeight = Q_atoi( args[3] ); + + // New struct; functionally identical + VideoPanelParms_t parms( false ); + + CreateVideoPanel( args[1], NULL, nScreenWidth, nScreenHeight, parms ); +} + + //----------------------------------------------------------------------------- // Purpose: Used to launch a video playback and fire a command on completion //----------------------------------------------------------------------------- @@ -408,21 +584,78 @@ CON_COMMAND( playvideo_exitcommand, "Plays a video and fires and exit command wh if ( args.ArgC() < 2 ) return; - unsigned int nScreenWidth = ScreenWidth(); - unsigned int nScreenHeight = ScreenHeight(); - - char strFullpath[MAX_PATH]; - Q_strncpy( strFullpath, "media/", MAX_PATH ); // Assume we must play out of the media directory - char strFilename[MAX_PATH]; - Q_StripExtension( args[1], strFilename, MAX_PATH ); - Q_strncat( strFullpath, args[1], MAX_PATH ); - + // Pull out the exit command we want to use char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); - // Create the panel and go! - if ( VideoPanel_Create( 0, 0, nScreenWidth, nScreenHeight, strFullpath, pExitCommand ) == false ) + // New struct; functionally identical + VideoPanelParms_t parms; + + CreateVideoPanel( args[1], pExitCommand, 0, 0, parms ); +} + +//----------------------------------------------------------------------------- +// Purpose: Used to launch a video playback and fire a command on completion +//----------------------------------------------------------------------------- + +CON_COMMAND( playvideo_exitcommand_nointerrupt, "Plays a video (without interruption) and fires and exit command when it is stopped or finishes: " ) +{ + if ( args.ArgC() < 2 ) + return; + + // Pull out the exit command we want to use + char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); + + // New struct; functionally identical + VideoPanelParms_t parms( false ); + + CreateVideoPanel( args[1], pExitCommand, 0, 0, parms ); +} + +//----------------------------------------------------------------------------- +// Purpose: Cause all playback to stop +//----------------------------------------------------------------------------- + +CON_COMMAND( stopvideos, "Stops all videos playing to the screen" ) +{ + FOR_EACH_VEC( g_vecVideoPanels, itr ) { - Warning( "Unable to play video: %s\n", strFullpath ); - engine->ClientCmd( pExitCommand ); + g_vecVideoPanels[itr]->StopPlayback(); } } + +//----------------------------------------------------------------------------- +// Purpose: Used to launch a video playback and fire a command on completion +//----------------------------------------------------------------------------- + +CON_COMMAND( playvideo_complex, "Plays a video with various parameters to simplify logic_playmovie: " ) +{ + if ( args.ArgC() < 2 ) + return; + + // Pull out the exit command we want to use + char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); + + // Parameters + VideoPanelParms_t parms; + + if (args.ArgC() >= 3) + parms.bAllowInterrupt = atoi( args[3] ) != 0; + if (args.ArgC() >= 4) + parms.bLoop = atoi( args[4] ) != 0; + if (args.ArgC() >= 5) + parms.bMute = atoi( args[5] ) != 0; + + //if (args.ArgC() >= 5) + // parms.flFadeIn = atof( args[5] ); + //if (args.ArgC() >= 6) + // parms.flFadeOut = atof( args[6] ); + + // Stop a softlock + if (parms.bAllowInterrupt == false && parms.bLoop) + { + Warning( "WARNING: Tried to play video set to be uninterruptible and looping. This would cause a softlock because the video loops forever and there's no way to stop it.\n" ); + return; + } + + CreateVideoPanel( args[1], pExitCommand, 0, 0, parms ); +} diff --git a/sp/src/game/client/vgui_video.h b/sp/src/game/client/vgui_video.h index 61a980b1..b413d832 100644 --- a/sp/src/game/client/vgui_video.h +++ b/sp/src/game/client/vgui_video.h @@ -45,14 +45,22 @@ public: } bool BeginPlayback( const char *pFilename ); + void StopPlayback( void ); void SetBlackBackground( bool bBlack ){ m_bBlackBackground = bBlack; } + void SetAllowInterrupt( bool bAllowInterrupt ) { m_bAllowInterruption = bAllowInterrupt; } + void SetStopAllSounds( bool bStopAllSounds ) { m_bStopAllSounds = bStopAllSounds; } +#ifdef MAPBASE + void SetLooping( bool bLooping ) { m_bLooping = bLooping; } + void SetMuted( bool bMuted ) { m_bMuted = bMuted; } + void SetFade( float flStartFade, float flEndFade ) { m_flFadeIn = flStartFade; m_flFadeOut = flEndFade; } +#endif protected: - virtual void OnTick( void ) { BaseClass::OnTick(); } + virtual void OnTick( void ); virtual void OnCommand( const char *pcCommand ) { BaseClass::OnCommand( pcCommand ); } - virtual void OnVideoOver(){} + virtual void OnVideoOver(); protected: IVideoMaterial *m_VideoMaterial; @@ -65,8 +73,19 @@ protected: float m_flU; // U,V ranges for video on its sheet float m_flV; + bool m_bLooping; +#ifdef MAPBASE + float m_flFadeIn; + float m_flFadeOut; + bool m_bMuted; +#endif + bool m_bStopAllSounds; + bool m_bAllowInterruption; bool m_bBlackBackground; bool m_bAllowAlternateMedia; + int m_nShutdownCount; + + bool m_bStarted; }; diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index 58b5431c..50c66a92 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 5fce6ff5..505395da 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/BaseAnimatingOverlay.cpp b/sp/src/game/server/BaseAnimatingOverlay.cpp index 36534bc0..c1d7b883 100644 --- a/sp/src/game/server/BaseAnimatingOverlay.cpp +++ b/sp/src/game/server/BaseAnimatingOverlay.cpp @@ -427,6 +427,11 @@ void CAnimationLayer::DispatchAnimEvents( CBaseAnimating *eventHandler, CBaseAni event.eventtime = pOwner->m_flAnimTime + (flCycle - m_flCycle) / flCycleRate + pOwner->GetAnimTimeInterval(); } +#ifdef MAPBASE_VSCRIPT + if (eventHandler->m_ScriptScope.IsInitialized() && eventHandler->ScriptHookHandleAnimEvent( &event ) == false) + continue; +#endif + // Msg( "dispatch %d (%d : %.2f)\n", index - 1, event.event, event.eventtime ); eventHandler->HandleAnimEvent( &event ); } diff --git a/sp/src/game/server/EnvMessage.cpp b/sp/src/game/server/EnvMessage.cpp index 3f47c2ea..6de81783 100644 --- a/sp/src/game/server/EnvMessage.cpp +++ b/sp/src/game/server/EnvMessage.cpp @@ -212,6 +212,10 @@ static ConCommand creditsdone("creditsdone", CreditsDone_f ); extern ConVar sv_unlockedchapters; +#ifdef MAPBASE +extern int Mapbase_GetChapterCount(); +#endif + void CCredits::OnRestore() { BaseClass::OnRestore(); @@ -226,6 +230,10 @@ void CCredits::OnRestore() void CCredits::RollOutroCredits() { +#ifdef MAPBASE + // Don't set this if we're using Mapbase chapters or if sv_unlockedchapters is already greater than 15 + if (Mapbase_GetChapterCount() <= 0 && sv_unlockedchapters.GetInt() < 15) +#endif sv_unlockedchapters.SetValue( "15" ); CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 6333ed86..0f4e6a35 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -303,10 +303,11 @@ CSimpleSimTimer CAI_BaseNPC::m_AnyUpdateEnemyPosTimer; #ifdef MAPBASE_VSCRIPT // TODO: Better placement? -ScriptHook_t g_Hook_QueryHearSound; -ScriptHook_t g_Hook_QuerySeeEntity; -ScriptHook_t g_Hook_TranslateActivity; -ScriptHook_t g_Hook_TranslateSchedule; +ScriptHook_t CAI_BaseNPC::g_Hook_QueryHearSound; +ScriptHook_t CAI_BaseNPC::g_Hook_QuerySeeEntity; +ScriptHook_t CAI_BaseNPC::g_Hook_TranslateActivity; +ScriptHook_t CAI_BaseNPC::g_Hook_TranslateSchedule; +ScriptHook_t CAI_BaseNPC::g_Hook_GetActualShootPosition; #endif // @@ -10687,6 +10688,19 @@ void CAI_BaseNPC::CollectShotStats( const Vector &vecShootOrigin, const Vector & //----------------------------------------------------------------------------- Vector CAI_BaseNPC::GetActualShootPosition( const Vector &shootOrigin ) { +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_GetActualShootPosition.CanRunInScope(m_ScriptScope)) + { + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { shootOrigin, ToHScript( GetEnemy() ) }; + if (g_Hook_GetActualShootPosition.Call( m_ScriptScope, &functionReturn, args )) + { + if (functionReturn.m_type == FIELD_VECTOR && functionReturn.m_pVector->LengthSqr() != 0.0f) + return *functionReturn.m_pVector; + } + } +#endif + // Project the target's location into the future. Vector vecEnemyLKP = GetEnemyLKP(); Vector vecEnemyOffset = GetEnemy()->BodyTarget( shootOrigin ) - GetEnemy()->GetAbsOrigin(); @@ -12075,20 +12089,24 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC // // Hooks // - BEGIN_SCRIPTHOOK( g_Hook_QueryHearSound, "QueryHearSound", FIELD_BOOLEAN, "Called when the NPC is deciding whether to hear a CSound or not." ) + BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_QueryHearSound, "QueryHearSound", FIELD_BOOLEAN, "Called when the NPC is deciding whether to hear a CSound or not." ) DEFINE_SCRIPTHOOK_PARAM( "sound", FIELD_HSCRIPT ) END_SCRIPTHOOK() - BEGIN_SCRIPTHOOK( g_Hook_QuerySeeEntity, "QuerySeeEntity", FIELD_BOOLEAN, "Called when the NPC is deciding whether to see an entity or not." ) + BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_QuerySeeEntity, "QuerySeeEntity", FIELD_BOOLEAN, "Called when the NPC is deciding whether to see an entity or not." ) DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) END_SCRIPTHOOK() - BEGIN_SCRIPTHOOK( g_Hook_TranslateActivity, "NPC_TranslateActivity", FIELD_VARIANT, "Called when the NPC is translating their current activity. The activity is provided in both string and ID form. Should return either an activity string or an activity ID. Return -1 to not translate." ) + BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_TranslateActivity, "NPC_TranslateActivity", FIELD_VARIANT, "Called when the NPC is translating their current activity. The activity is provided in both string and ID form. Should return either an activity string or an activity ID. Return -1 to not translate." ) DEFINE_SCRIPTHOOK_PARAM( "activity", FIELD_CSTRING ) DEFINE_SCRIPTHOOK_PARAM( "activity_id", FIELD_INTEGER ) END_SCRIPTHOOK() - BEGIN_SCRIPTHOOK( g_Hook_TranslateSchedule, "NPC_TranslateSchedule", FIELD_VARIANT, "Called when the NPC is translating their current schedule. The schedule is provided in both string and ID form. Should return either a schedule string or a schedule ID. Return -1 to not translate." ) + BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_TranslateSchedule, "NPC_TranslateSchedule", FIELD_VARIANT, "Called when the NPC is translating their current schedule. The schedule is provided in both string and ID form. Should return either a schedule string or a schedule ID. Return -1 to not translate." ) DEFINE_SCRIPTHOOK_PARAM( "schedule", FIELD_CSTRING ) DEFINE_SCRIPTHOOK_PARAM( "schedule_id", FIELD_INTEGER ) END_SCRIPTHOOK() + BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_GetActualShootPosition, "GetActualShootPosition", FIELD_VOID, "Called when the NPC is getting their actual shoot position, using the default shoot position as the parameter. (NOTE: NPCs which override this themselves might not always use this hook!)" ) + DEFINE_SCRIPTHOOK_PARAM( "shootOrigin", FIELD_VECTOR ) + DEFINE_SCRIPTHOOK_PARAM( "target", FIELD_HSCRIPT ) + END_SCRIPTHOOK() END_SCRIPTDESC(); #endif diff --git a/sp/src/game/server/ai_basenpc.h b/sp/src/game/server/ai_basenpc.h index 74ef79bf..2a2ad03c 100644 --- a/sp/src/game/server/ai_basenpc.h +++ b/sp/src/game/server/ai_basenpc.h @@ -98,6 +98,11 @@ extern bool AIStrongOpt( void ); #ifdef MAPBASE // Defines Mapbase's extended NPC response system usage. #define EXPANDED_RESPONSE_SYSTEM_USAGE + +// Use the model keyvalue if it is defined +#define DefaultOrCustomModel(defaultModel) GetModelName() != NULL_STRING ? STRING(GetModelName()) : defaultModel +#else +#define DefaultOrCustomModel() defaultModel #endif #ifdef EXPANDED_RESPONSE_SYSTEM_USAGE @@ -1237,6 +1242,8 @@ public: int ScriptGetActivityID() { return GetActivity(); } void ScriptSetActivity( const char *szActivity ) { SetActivity( (Activity)GetActivityID( szActivity ) ); } void ScriptSetActivityID( int iActivity ) { SetActivity((Activity)iActivity); } + int ScriptTranslateActivity( const char *szActivity ) { return TranslateActivity( (Activity)GetActivityID( szActivity ) ); } + int ScriptTranslateActivityID( int iActivity ) { return TranslateActivity( (Activity)iActivity ); } const char* VScriptGetSchedule(); int VScriptGetScheduleID(); @@ -2304,6 +2311,15 @@ public: CUtlVector m_ScheduleHistory; #endif//AI_MONITOR_FOR_OSCILLATION +#ifdef MAPBASE_VSCRIPT + static ScriptHook_t g_Hook_QueryHearSound; + static ScriptHook_t g_Hook_QuerySeeEntity; + static ScriptHook_t g_Hook_TranslateActivity; + static ScriptHook_t g_Hook_TranslateSchedule; + static ScriptHook_t g_Hook_GetActualShootPosition; + static ScriptHook_t g_Hook_OverrideMove; +#endif + private: // Break into pieces! diff --git a/sp/src/game/server/ai_speech.cpp b/sp/src/game/server/ai_speech.cpp index 60190744..dafad41e 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/baseanimating.cpp b/sp/src/game/server/baseanimating.cpp index f16744f1..788edc27 100644 --- a/sp/src/game/server/baseanimating.cpp +++ b/sp/src/game/server/baseanimating.cpp @@ -284,6 +284,7 @@ END_SEND_TABLE() #ifdef MAPBASE_VSCRIPT ScriptHook_t CBaseAnimating::g_Hook_OnServerRagdoll; +ScriptHook_t CBaseAnimating::g_Hook_HandleAnimEvent; #endif BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" ) @@ -342,6 +343,10 @@ BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" ) DEFINE_SCRIPTHOOK_PARAM( "ragdoll", FIELD_HSCRIPT ) DEFINE_SCRIPTHOOK_PARAM( "submodel", FIELD_BOOLEAN ) END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( CBaseAnimating::g_Hook_HandleAnimEvent, "HandleAnimEvent", FIELD_BOOLEAN, "Called when handling animation events. Return false to cancel base handling." ) + DEFINE_SCRIPTHOOK_PARAM( "event", FIELD_HSCRIPT ) + END_SCRIPTHOOK() #endif END_SCRIPTDESC(); @@ -1243,6 +1248,11 @@ void CBaseAnimating::DispatchAnimEvents ( CBaseAnimating *eventHandler ) event.eventtime = m_flAnimTime + (flCycle - GetCycle()) / flCycleRate + GetAnimTimeInterval(); } +#ifdef MAPBASE_VSCRIPT + if (eventHandler->ScriptHookHandleAnimEvent( &event ) == false) + continue; +#endif + /* if (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) { @@ -1273,6 +1283,29 @@ void CBaseAnimating::DispatchAnimEvents ( CBaseAnimating *eventHandler ) } } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseAnimating::ScriptHookHandleAnimEvent( animevent_t *pEvent ) +{ + if (m_ScriptScope.IsInitialized() && g_Hook_HandleAnimEvent.CanRunInScope(m_ScriptScope)) + { + HSCRIPT hEvent = g_pScriptVM->RegisterInstance( pEvent ); + + // event + ScriptVariant_t args[] = { hEvent }; + ScriptVariant_t returnValue = true; + g_Hook_HandleAnimEvent.Call( m_ScriptScope, &returnValue, args ); + + g_pScriptVM->RemoveInstance( hEvent ); + return returnValue.m_bool; + } + + return true; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/baseanimating.h b/sp/src/game/server/baseanimating.h index 8513d886..8f0e0aa8 100644 --- a/sp/src/game/server/baseanimating.h +++ b/sp/src/game/server/baseanimating.h @@ -144,6 +144,9 @@ public: bool HasAnimEvent( int nSequence, int nEvent ); virtual void DispatchAnimEvents ( CBaseAnimating *eventHandler ); // Handle events that have happend since last time called up until X seconds into the future virtual void HandleAnimEvent( animevent_t *pEvent ); +#ifdef MAPBASE_VSCRIPT + bool ScriptHookHandleAnimEvent( animevent_t *pEvent ); +#endif int LookupPoseParameter( CStudioHdr *pStudioHdr, const char *szName ); inline int LookupPoseParameter( const char *szName ) { return LookupPoseParameter(GetModelPtr(), szName); } @@ -211,6 +214,7 @@ public: void SetSkin( int iSkin ) { m_nSkin = iSkin; } static ScriptHook_t g_Hook_OnServerRagdoll; + static ScriptHook_t g_Hook_HandleAnimEvent; #endif // These return the attachment in the space of the entity diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index 59b9eae8..1b71f9fd 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -284,6 +284,7 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CBaseEntity, DT_BaseEntity ) #ifdef MAPBASE // Keep consistent with VIEW_ID_COUNT in viewrender.h SendPropInt (SENDINFO(m_iViewHideFlags), 9, SPROP_UNSIGNED ), + SendPropBool (SENDINFO(m_bDisableFlashlight) ), #endif SendPropInt (SENDINFO(m_iTeamNum), TEAMNUM_NUM_BITS, 0), SendPropInt (SENDINFO(m_CollisionGroup), 5, SPROP_UNSIGNED), @@ -1915,6 +1916,7 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_GLOBAL_KEYFIELD( m_nModelIndex, FIELD_SHORT, "modelindex" ), #ifdef MAPBASE DEFINE_KEYFIELD( m_iViewHideFlags, FIELD_INTEGER, "viewhideflags" ), + DEFINE_KEYFIELD( m_bDisableFlashlight, FIELD_BOOLEAN, "disableflashlight" ), #endif #if !defined( NO_ENTITY_PREDICTION ) // DEFINE_FIELD( m_PredictableID, CPredictableId ), @@ -2148,6 +2150,8 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveEffects", InputRemoveEffects ), DEFINE_INPUTFUNC( FIELD_VOID, "EnableDraw", InputDrawEntity ), DEFINE_INPUTFUNC( FIELD_VOID, "DisableDraw", InputUndrawEntity ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnableReceivingFlashlight", InputEnableReceivingFlashlight ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableReceivingFlashlight", InputDisableReceivingFlashlight ), DEFINE_INPUTFUNC( FIELD_INTEGER, "AddEFlags", InputAddEFlags ), DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveEFlags", InputRemoveEFlags ), DEFINE_INPUTFUNC( FIELD_INTEGER, "AddSolidFlags", InputAddSolidFlags ), @@ -2218,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.") @@ -2262,6 +2267,9 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC( ApplyAbsVelocityImpulse, "" ) DEFINE_SCRIPTFUNC( ApplyLocalAngularVelocityImpulse, "" ) + + DEFINE_SCRIPTFUNC( BodyTarget, "" ) + DEFINE_SCRIPTFUNC( HeadTarget, "" ) #endif DEFINE_SCRIPTFUNC_NAMED( GetAbsVelocity, "GetVelocity", "" ) @@ -2278,11 +2286,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", "" ) @@ -2583,10 +2591,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 } } @@ -8264,6 +8272,22 @@ void CBaseEntity::InputUndrawEntity( inputdata_t& inputdata ) AddEffects(EF_NODRAW); } +//----------------------------------------------------------------------------- +// Purpose: Inspired by the Portal 2 input of the same name. +//----------------------------------------------------------------------------- +void CBaseEntity::InputEnableReceivingFlashlight( inputdata_t& inputdata ) +{ + m_bDisableFlashlight = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Inspired by the Portal 2 input of the same name. +//----------------------------------------------------------------------------- +void CBaseEntity::InputDisableReceivingFlashlight( inputdata_t& inputdata ) +{ + m_bDisableFlashlight = true; +} + //----------------------------------------------------------------------------- // Purpose: Adds eflags. //----------------------------------------------------------------------------- @@ -8644,173 +8668,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 c0956a87..a96d443b 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 @@ -762,6 +762,8 @@ public: void InputRemoveEffects( inputdata_t &inputdata ); void InputDrawEntity( inputdata_t &inputdata ); void InputUndrawEntity( inputdata_t &inputdata ); + void InputEnableReceivingFlashlight( inputdata_t &inputdata ); + void InputDisableReceivingFlashlight( inputdata_t &inputdata ); void InputAddEFlags( inputdata_t &inputdata ); void InputRemoveEFlags( inputdata_t &inputdata ); void InputAddSolidFlags( inputdata_t &inputdata ); @@ -928,6 +930,9 @@ public: // // This was partly inspired by Underhell's keyvalue that allows entities to only render in mirrors and cameras. CNetworkVar( int, m_iViewHideFlags ); + + // Disables receiving projected textures. Based on a keyvalue from later Source games. + CNetworkVar( bool, m_bDisableFlashlight ); #endif // was pev->rendercolor @@ -2003,7 +2008,7 @@ public: void ScriptStopThink(); void ScriptContextThink(); private: - CUtlVector< scriptthinkfunc_t > m_ScriptThinkFuncs; + CUtlVector< scriptthinkfunc_t* > m_ScriptThinkFuncs; public: #endif const char* GetScriptId(); @@ -2034,8 +2039,10 @@ public: 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); } @@ -2061,6 +2068,7 @@ public: 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/bmodels.cpp b/sp/src/game/server/bmodels.cpp index 2a74df6c..e8291f8f 100644 --- a/sp/src/game/server/bmodels.cpp +++ b/sp/src/game/server/bmodels.cpp @@ -450,6 +450,11 @@ protected: bool m_bSolidBsp; // Brush is SOLID_BSP +#ifdef MAPBASE + int m_iMinPitch = 30; // FANPITCHMIN + int m_iMaxPitch = 100; // FANPITCHMAX +#endif + public: Vector m_vecClientOrigin; QAngle m_vecClientAngles; @@ -472,6 +477,10 @@ BEGIN_DATADESC( CFuncRotating ) DEFINE_FIELD( m_angStart, FIELD_VECTOR ), DEFINE_FIELD( m_bStopAtStartPos, FIELD_BOOLEAN ), DEFINE_KEYFIELD( m_bSolidBsp, FIELD_BOOLEAN, "solidbsp" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_iMinPitch, FIELD_INTEGER, "minpitch" ), + DEFINE_KEYFIELD( m_iMaxPitch, FIELD_INTEGER, "maxpitch" ), +#endif // Function Pointers DEFINE_FUNCTION( SpinUpMove ), @@ -823,8 +832,14 @@ void CFuncRotating::HurtTouch ( CBaseEntity *pOther ) } +#ifdef MAPBASE +// In Mapbase, use the keyvalues instead +#define FANPITCHMIN m_iMinPitch +#define FANPITCHMAX m_iMaxPitch +#else #define FANPITCHMIN 30 #define FANPITCHMAX 100 +#endif //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/cbase.cpp b/sp/src/game/server/cbase.cpp index 81050538..d98ea9cd 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/envmicrophone.cpp b/sp/src/game/server/envmicrophone.cpp index ee78f87f..29a59ead 100644 --- a/sp/src/game/server/envmicrophone.cpp +++ b/sp/src/game/server/envmicrophone.cpp @@ -48,6 +48,7 @@ BEGIN_DATADESC( CEnvMicrophone ) DEFINE_KEYFIELD(m_iszLandmarkName, FIELD_STRING, "landmark"), DEFINE_FIELD(m_hLandmark, FIELD_EHANDLE), DEFINE_KEYFIELD(m_flPitchScale, FIELD_FLOAT, "PitchScale"), + DEFINE_KEYFIELD(m_flVolumeScale, FIELD_FLOAT, "VolumeScale"), DEFINE_KEYFIELD(m_nChannel, FIELD_INTEGER, "channel"), #endif // DEFINE_FIELD(m_bAvoidFeedback, FIELD_BOOLEAN), // DONT SAVE @@ -61,6 +62,7 @@ BEGIN_DATADESC( CEnvMicrophone ) #ifdef MAPBASE DEFINE_INPUTFUNC(FIELD_INTEGER, "SetDSPPreset", InputSetDSPPreset), DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPitchScale", InputSetPitchScale ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetVolumeScale", InputSetVolumeScale ), DEFINE_INPUTFUNC( FIELD_INTEGER, "SetChannel", InputSetChannel ), #endif @@ -272,6 +274,15 @@ void CEnvMicrophone::InputSetPitchScale( inputdata_t &inputdata ) m_flPitchScale = inputdata.value.Float(); } +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CEnvMicrophone::InputSetVolumeScale( inputdata_t &inputdata ) +{ + m_flVolumeScale = inputdata.value.Float(); +} + //----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - @@ -545,11 +556,13 @@ MicrophoneResult_t CEnvMicrophone::SoundPlayed( int entindex, const char *soundn EmitSound_t ep; #ifdef MAPBASE ep.m_nChannel = m_nChannel; + if (m_flVolumeScale != 1.0f) + ep.m_flVolume = (flVolume * m_flVolumeScale); #else ep.m_nChannel = CHAN_STATIC; + ep.m_flVolume = flVolume; #endif ep.m_pSoundName = soundname; - ep.m_flVolume = flVolume; ep.m_SoundLevel = soundlevel; ep.m_nFlags = iFlags; #ifdef MAPBASE diff --git a/sp/src/game/server/envmicrophone.h b/sp/src/game/server/envmicrophone.h index cf8e729e..11527d58 100644 --- a/sp/src/game/server/envmicrophone.h +++ b/sp/src/game/server/envmicrophone.h @@ -57,6 +57,7 @@ public: #ifdef MAPBASE void InputSetDSPPreset( inputdata_t &inputdata ); void InputSetPitchScale( inputdata_t &inputdata ); + void InputSetVolumeScale( inputdata_t &inputdata ); void InputSetChannel( inputdata_t &inputdata ); #endif @@ -88,6 +89,7 @@ private: string_t m_iszLandmarkName; EHANDLE m_hLandmark; float m_flPitchScale = 1.0f; + float m_flVolumeScale = 1.0f; int m_nChannel = CHAN_STATIC; #endif diff --git a/sp/src/game/server/eventqueue.h b/sp/src/game/server/eventqueue.h index 61b0d252..d5cc2d5f 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 @@ public: #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/hl2/combine_mine.cpp b/sp/src/game/server/hl2/combine_mine.cpp index 104c0b0d..4e86eb85 100644 --- a/sp/src/game/server/hl2/combine_mine.cpp +++ b/sp/src/game/server/hl2/combine_mine.cpp @@ -91,6 +91,7 @@ BEGIN_DATADESC( CBounceBomb ) DEFINE_KEYFIELD( m_iInitialState, FIELD_INTEGER, "InitialState" ), DEFINE_KEYFIELD( m_bCheapWarnSound, FIELD_BOOLEAN, "CheapWarnSound" ), DEFINE_KEYFIELD( m_iLOSMask, FIELD_INTEGER, "LOSMask" ), + DEFINE_INPUT( m_bUnavoidable, FIELD_BOOLEAN, "SetUnavoidable" ), #endif DEFINE_KEYFIELD( m_iModification, FIELD_INTEGER, "Modification" ), @@ -1485,6 +1486,18 @@ CBasePlayer *CBounceBomb::HasPhysicsAttacker( float dt ) return NULL; } +//--------------------------------------------------------- +//--------------------------------------------------------- +bool CBounceBomb::ShouldBeAvoidedByCompanions() +{ +#ifdef MAPBASE + if (m_bUnavoidable) + return false; +#endif + + return !IsPlayerPlaced() && IsAwake(); +} + //--------------------------------------------------------- //--------------------------------------------------------- void CBounceBomb::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ) diff --git a/sp/src/game/server/hl2/combine_mine.h b/sp/src/game/server/hl2/combine_mine.h index fdcd68da..4495afb3 100644 --- a/sp/src/game/server/hl2/combine_mine.h +++ b/sp/src/game/server/hl2/combine_mine.h @@ -72,6 +72,9 @@ public: bool IsPlayerPlaced() { return m_bPlacedByPlayer; } + // Determines whether companions should treat the mine as a navigation obstacle and avoid it + bool ShouldBeAvoidedByCompanions(); + bool CreateVPhysics() { VPhysicsInitNormal( SOLID_VPHYSICS, 0, false ); @@ -125,6 +128,8 @@ private: // Allows control over the mask used in LOS int m_iLOSMask; + + bool m_bUnavoidable; #endif bool m_bPlacedByPlayer; diff --git a/sp/src/game/server/hl2/hl2_player.cpp b/sp/src/game/server/hl2/hl2_player.cpp index f249c3e6..fe523806 100644 --- a/sp/src/game/server/hl2/hl2_player.cpp +++ b/sp/src/game/server/hl2/hl2_player.cpp @@ -278,6 +278,7 @@ public: CUtlDict m_QueuedKV; int m_MaxArmor = 100; + int m_SuitZoomFOV = 25; #endif bool PassesDamageFilter( const CTakeDamageInfo &info ); @@ -1760,7 +1761,11 @@ void CHL2_Player::ToggleZoom(void) //----------------------------------------------------------------------------- void CHL2_Player::StartZooming( void ) { +#ifdef MAPBASE + int iFOV = GetPlayerProxy() ? GetPlayerProxy()->m_SuitZoomFOV : 25; +#else int iFOV = 25; +#endif if ( SetFOV( this, iFOV, 0.4f ) ) { m_HL2Local.m_bZooming = true; @@ -4615,6 +4620,7 @@ BEGIN_DATADESC( CLogicPlayerProxy ) DEFINE_INPUTFUNC( FIELD_STRING, "SetPlayerModel", InputSetPlayerModel ), DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetPlayerDrawExternally", InputSetPlayerDrawExternally ), DEFINE_INPUT( m_MaxArmor, FIELD_INTEGER, "SetMaxInputArmor" ), + DEFINE_INPUT( m_SuitZoomFOV, FIELD_INTEGER, "SetSuitZoomFOV" ), #endif DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ), END_DATADESC() diff --git a/sp/src/game/server/hl2/npc_antlion.cpp b/sp/src/game/server/hl2/npc_antlion.cpp index d94c658d..66fd8367 100644 --- a/sp/src/game/server/hl2/npc_antlion.cpp +++ b/sp/src/game/server/hl2/npc_antlion.cpp @@ -277,17 +277,17 @@ void CNPC_Antlion::Spawn( void ) #ifdef HL2_EPISODIC if ( IsWorker() ) { - SetModel( ANTLION_WORKER_MODEL ); + SetModel( DefaultOrCustomModel(ANTLION_WORKER_MODEL) ); AddSpawnFlags( SF_NPC_LONG_RANGE ); SetBloodColor( BLOOD_COLOR_ANTLION_WORKER ); } else { - SetModel( ANTLION_MODEL ); + SetModel( DefaultOrCustomModel(ANTLION_MODEL) ); SetBloodColor( BLOOD_COLOR_ANTLION ); } #else - SetModel( ANTLION_MODEL ); + SetModel( DefaultOrCustomModel(ANTLION_MODEL) ); SetBloodColor( BLOOD_COLOR_YELLOW ); #endif // HL2_EPISODIC diff --git a/sp/src/game/server/hl2/npc_antlionguard.cpp b/sp/src/game/server/hl2/npc_antlionguard.cpp index efc5883d..57592617 100644 --- a/sp/src/game/server/hl2/npc_antlionguard.cpp +++ b/sp/src/game/server/hl2/npc_antlionguard.cpp @@ -678,7 +678,7 @@ void CNPC_AntlionGuard::UpdateOnRemove( void ) //----------------------------------------------------------------------------- void CNPC_AntlionGuard::Precache( void ) { - PrecacheModel( ANTLIONGUARD_MODEL ); + PrecacheModel( DefaultOrCustomModel( ANTLIONGUARD_MODEL ) ); PrecacheScriptSound( "NPC_AntlionGuard.Shove" ); PrecacheScriptSound( "NPC_AntlionGuard.HitHard" ); @@ -779,7 +779,7 @@ void CNPC_AntlionGuard::Spawn( void ) { Precache(); - SetModel( ANTLIONGUARD_MODEL ); + SetModel( DefaultOrCustomModel( ANTLIONGUARD_MODEL ) ); // Switch our skin (for now), if we're the cavern guard if ( m_bCavernBreed ) diff --git a/sp/src/game/server/hl2/npc_barnacle.cpp b/sp/src/game/server/hl2/npc_barnacle.cpp index 68b7064c..2ffca3bc 100644 --- a/sp/src/game/server/hl2/npc_barnacle.cpp +++ b/sp/src/game/server/hl2/npc_barnacle.cpp @@ -266,7 +266,7 @@ void CNPC_Barnacle::Spawn() { Precache( ); - SetModel( "models/barnacle.mdl" ); + SetModel( DefaultOrCustomModel( "models/barnacle.mdl" ) ); UTIL_SetSize( this, Vector(-16, -16, -40), Vector(16, 16, 0) ); SetSolid( SOLID_BBOX ); @@ -2375,7 +2375,7 @@ const impactdamagetable_t &CNPC_Barnacle::GetPhysicsImpactDamageTable( void ) //========================================================= void CNPC_Barnacle::Precache() { - PrecacheModel("models/barnacle.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/barnacle.mdl" ) ); // Precache all gibs for ( int i=0; i < ARRAYSIZE(m_szGibNames); i++ ) diff --git a/sp/src/game/server/hl2/npc_headcrab.cpp b/sp/src/game/server/hl2/npc_headcrab.cpp index 22f44d3d..b1b8db81 100644 --- a/sp/src/game/server/hl2/npc_headcrab.cpp +++ b/sp/src/game/server/hl2/npc_headcrab.cpp @@ -2433,7 +2433,7 @@ void CBaseHeadcrab::CreateDust( bool placeDecal ) //----------------------------------------------------------------------------- void CHeadcrab::Precache( void ) { - PrecacheModel( "models/headcrabclassic.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/headcrabclassic.mdl" ) ); PrecacheScriptSound( "NPC_HeadCrab.Gib" ); PrecacheScriptSound( "NPC_HeadCrab.Idle" ); @@ -2455,7 +2455,7 @@ void CHeadcrab::Precache( void ) void CHeadcrab::Spawn( void ) { Precache(); - SetModel( "models/headcrabclassic.mdl" ); + SetModel( DefaultOrCustomModel( "models/headcrabclassic.mdl" ) ); BaseClass::Spawn(); @@ -2570,7 +2570,7 @@ END_DATADESC() //----------------------------------------------------------------------------- void CFastHeadcrab::Precache( void ) { - PrecacheModel( "models/headcrab.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/headcrab.mdl" ) ); PrecacheScriptSound( "NPC_FastHeadcrab.Idle" ); PrecacheScriptSound( "NPC_FastHeadcrab.Alert" ); @@ -2589,7 +2589,7 @@ void CFastHeadcrab::Precache( void ) void CFastHeadcrab::Spawn( void ) { Precache(); - SetModel( "models/headcrab.mdl" ); + SetModel( DefaultOrCustomModel( "models/headcrab.mdl" ) ); BaseClass::Spawn(); @@ -3089,7 +3089,7 @@ void CBlackHeadcrab::TelegraphSound( void ) void CBlackHeadcrab::Spawn( void ) { Precache(); - SetModel( "models/headcrabblack.mdl" ); + SetModel( DefaultOrCustomModel( "models/headcrabblack.mdl" ) ); BaseClass::Spawn(); @@ -3106,7 +3106,7 @@ void CBlackHeadcrab::Spawn( void ) //----------------------------------------------------------------------------- void CBlackHeadcrab::Precache( void ) { - PrecacheModel( "models/headcrabblack.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/headcrabblack.mdl" ) ); PrecacheScriptSound( "NPC_BlackHeadcrab.Telegraph" ); PrecacheScriptSound( "NPC_BlackHeadcrab.Attack" ); diff --git a/sp/src/game/server/hl2/npc_manhack.cpp b/sp/src/game/server/hl2/npc_manhack.cpp index 27dcde85..e221e938 100644 --- a/sp/src/game/server/hl2/npc_manhack.cpp +++ b/sp/src/game/server/hl2/npc_manhack.cpp @@ -2193,9 +2193,9 @@ void CNPC_Manhack::Precache(void) // // Model. // - PrecacheModel("models/manhack.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/manhack.mdl" ) ); PrecacheModel( MANHACK_GLOW_SPRITE ); - PropBreakablePrecacheAll( MAKE_STRING("models/manhack.mdl") ); + PropBreakablePrecacheAll( MAKE_STRING( DefaultOrCustomModel( "models/manhack.mdl" ) ) ); PrecacheScriptSound( "NPC_Manhack.Die" ); PrecacheScriptSound( "NPC_Manhack.Bat" ); @@ -2389,7 +2389,7 @@ void CNPC_Manhack::Spawn(void) AddSpawnFlags( SF_NPC_FADE_CORPSE ); #endif // _XBOX - SetModel( "models/manhack.mdl" ); + SetModel( DefaultOrCustomModel( "models/manhack.mdl" ) ); SetHullType(HULL_TINY_CENTERED); SetHullSizeNormal(); diff --git a/sp/src/game/server/hl2/npc_playercompanion.cpp b/sp/src/game/server/hl2/npc_playercompanion.cpp index 7cfc3a24..76166b1b 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.cpp +++ b/sp/src/game/server/hl2/npc_playercompanion.cpp @@ -3423,7 +3423,7 @@ bool CNPC_PlayerCompanion::OverrideMove( float flInterval ) else if ( pEntity->m_iClassname == iszBounceBomb ) { CBounceBomb *pBomb = static_cast(pEntity); - if ( pBomb && !pBomb->IsPlayerPlaced() && pBomb->IsAwake() ) + if ( pBomb && pBomb->ShouldBeAvoidedByCompanions() ) { UTIL_TraceLine( WorldSpaceCenter(), pEntity->WorldSpaceCenter(), MASK_BLOCKLOS, pEntity, COLLISION_GROUP_NONE, &tr ); if (tr.fraction == 1.0 && !tr.startsolid) diff --git a/sp/src/game/server/hl2/npc_scanner.cpp b/sp/src/game/server/hl2/npc_scanner.cpp index 55716451..aac717c5 100644 --- a/sp/src/game/server/hl2/npc_scanner.cpp +++ b/sp/src/game/server/hl2/npc_scanner.cpp @@ -256,11 +256,11 @@ void CNPC_CScanner::Spawn(void) if( m_bIsClawScanner ) { - SetModel( "models/shield_scanner.mdl"); + SetModel( DefaultOrCustomModel( "models/shield_scanner.mdl" ) ); } else { - SetModel( "models/combine_scanner.mdl"); + SetModel( DefaultOrCustomModel( "models/combine_scanner.mdl" ) ); } m_iHealth = sk_scanner_health.GetFloat(); @@ -565,7 +565,7 @@ void CNPC_CScanner::Precache(void) // Model if( m_bIsClawScanner ) { - PrecacheModel("models/shield_scanner.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/shield_scanner.mdl" ) ); PrecacheModel("models/gibs/Shield_Scanner_Gib1.mdl"); PrecacheModel("models/gibs/Shield_Scanner_Gib2.mdl"); @@ -591,7 +591,7 @@ void CNPC_CScanner::Precache(void) } else { - PrecacheModel("models/combine_scanner.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/combine_scanner.mdl" ) ); PrecacheModel("models/gibs/scanner_gib01.mdl" ); PrecacheModel("models/gibs/scanner_gib02.mdl" ); diff --git a/sp/src/game/server/hl2/npc_stalker.cpp b/sp/src/game/server/hl2/npc_stalker.cpp index 0165c29d..67ed567a 100644 --- a/sp/src/game/server/hl2/npc_stalker.cpp +++ b/sp/src/game/server/hl2/npc_stalker.cpp @@ -278,7 +278,7 @@ void CNPC_Stalker::Spawn( void ) { Precache( ); - SetModel( "models/stalker.mdl" ); + SetModel( DefaultOrCustomModel( "models/stalker.mdl" ) ); SetHullType(HULL_HUMAN); SetHullSizeNormal(); @@ -321,7 +321,7 @@ void CNPC_Stalker::Spawn( void ) //----------------------------------------------------------------------------- void CNPC_Stalker::Precache( void ) { - PrecacheModel("models/stalker.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/stalker.mdl" ) ); PrecacheModel("sprites/laser.vmt"); PrecacheModel("sprites/redglow1.vmt"); diff --git a/sp/src/game/server/hl2/npc_strider.cpp b/sp/src/game/server/hl2/npc_strider.cpp index 5be578f3..a4289fdb 100644 --- a/sp/src/game/server/hl2/npc_strider.cpp +++ b/sp/src/game/server/hl2/npc_strider.cpp @@ -395,6 +395,9 @@ BEGIN_DATADESC( CNPC_Strider ) DEFINE_INPUTFUNC( FIELD_VOID, "EnableMinigun", InputEnableMinigun ), DEFINE_INPUTFUNC( FIELD_FLOAT, "StopShootingMinigunForSeconds", InputStopShootingMinigunForSeconds ), DEFINE_INPUTFUNC( FIELD_VOID, "DisableCrouch", InputDisableCrouch ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "EnableCrouch", InputEnableCrouch ), +#endif DEFINE_INPUTFUNC( FIELD_VOID, "DisableMoveToLOS", InputDisableMoveToLOS ), DEFINE_INPUTFUNC( FIELD_STRING, "DisableCollisionWith", InputDisableCollisionWith ), DEFINE_INPUTFUNC( FIELD_STRING, "EnableCollisionWith", InputEnableCollisionWith ), @@ -2362,6 +2365,15 @@ void CNPC_Strider::InputDisableCrouch( inputdata_t &inputdata ) m_bDontCrouch = true; } +#ifdef MAPBASE +//--------------------------------------------------------- +//--------------------------------------------------------- +void CNPC_Strider::InputEnableCrouch( inputdata_t &inputdata ) +{ + m_bDontCrouch = false; +} +#endif + //--------------------------------------------------------- //--------------------------------------------------------- void CNPC_Strider::InputDisableMoveToLOS( inputdata_t &inputdata ) diff --git a/sp/src/game/server/hl2/npc_strider.h b/sp/src/game/server/hl2/npc_strider.h index a9074599..d5bdd209 100644 --- a/sp/src/game/server/hl2/npc_strider.h +++ b/sp/src/game/server/hl2/npc_strider.h @@ -171,6 +171,9 @@ public: void InputDisableAggressiveBehavior( inputdata_t &inputdata ); void InputStopShootingMinigunForSeconds( inputdata_t &inputdata ); void InputDisableCrouch( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputEnableCrouch( inputdata_t &inputdata ); +#endif void InputDisableMoveToLOS( inputdata_t &inputdata ); void InputExplode( inputdata_t &inputdata ); void InputScaleGroundSpeed( inputdata_t &inputdata ); diff --git a/sp/src/game/server/hl2/npc_turret_ceiling.cpp b/sp/src/game/server/hl2/npc_turret_ceiling.cpp index bc4f6821..0637525a 100644 --- a/sp/src/game/server/hl2/npc_turret_ceiling.cpp +++ b/sp/src/game/server/hl2/npc_turret_ceiling.cpp @@ -304,7 +304,7 @@ CNPC_CeilingTurret::~CNPC_CeilingTurret( void ) //----------------------------------------------------------------------------- void CNPC_CeilingTurret::Precache( void ) { - PrecacheModel( CEILING_TURRET_MODEL ); + PrecacheModel( DefaultOrCustomModel( CEILING_TURRET_MODEL ) ); PrecacheModel( CEILING_TURRET_GLOW_SPRITE ); // Activities @@ -347,7 +347,7 @@ void CNPC_CeilingTurret::Spawn( void ) { Precache(); - SetModel( CEILING_TURRET_MODEL ); + SetModel( DefaultOrCustomModel( CEILING_TURRET_MODEL ) ); BaseClass::Spawn(); diff --git a/sp/src/game/server/hl2/npc_turret_ground.cpp b/sp/src/game/server/hl2/npc_turret_ground.cpp index 8ffd37df..c7dceeae 100644 --- a/sp/src/game/server/hl2/npc_turret_ground.cpp +++ b/sp/src/game/server/hl2/npc_turret_ground.cpp @@ -69,7 +69,7 @@ END_DATADESC() void CNPC_GroundTurret::Precache( void ) { PrecacheModel( GROUNDTURRET_BEAM_SPRITE ); - PrecacheModel( "models/combine_turrets/ground_turret.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/combine_turrets/ground_turret.mdl" ) ); PrecacheScriptSound( "NPC_CeilingTurret.Deploy" ); m_ShotSounds = PrecacheScriptSound( "NPC_FloorTurret.ShotSounds" ); @@ -88,7 +88,7 @@ void CNPC_GroundTurret::Spawn( void ) { Precache(); - UTIL_SetModel( this, "models/combine_turrets/ground_turret.mdl" ); + UTIL_SetModel( this, DefaultOrCustomModel( "models/combine_turrets/ground_turret.mdl" ) ); SetNavType( NAV_FLY ); SetSolid( SOLID_VPHYSICS ); diff --git a/sp/src/game/server/hl2/proto_sniper.cpp b/sp/src/game/server/hl2/proto_sniper.cpp index 5427850a..61460130 100644 --- a/sp/src/game/server/hl2/proto_sniper.cpp +++ b/sp/src/game/server/hl2/proto_sniper.cpp @@ -334,6 +334,10 @@ private: bool IsPlayerAllySniper(); +#ifdef MAPBASE + const Vector &GetPaintCursor() { return m_vecPaintCursor; } +#endif + private: /// This is the variable from which m_flPaintTime gets set. @@ -403,6 +407,9 @@ private: DEFINE_CUSTOM_AI; DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif }; @@ -500,6 +507,26 @@ BEGIN_DATADESC( CProtoSniper ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CProtoSniper, CAI_BaseNPC, "Combine sniper NPC." ) + + DEFINE_SCRIPTFUNC( GetBulletSpeed, "" ) + DEFINE_SCRIPTFUNC( GetBulletOrigin, "" ) + DEFINE_SCRIPTFUNC( ScopeGlint, "" ) + + DEFINE_SCRIPTFUNC( GetPositionParameter, "" ) + DEFINE_SCRIPTFUNC( IsSweepingRandomly, "" ) + DEFINE_SCRIPTFUNC( FindFrustratedShot, "" ) + + DEFINE_SCRIPTFUNC( IsLaserOn, "" ) + DEFINE_SCRIPTFUNC( LaserOn, "" ) + DEFINE_SCRIPTFUNC( LaserOff, "" ) + + DEFINE_SCRIPTFUNC( GetPaintCursor, "Get the point the sniper is currently aiming at." ) + +END_SCRIPTDESC() +#endif + //========================================================= @@ -2588,6 +2615,19 @@ Vector CProtoSniper::DesiredBodyTarget( CBaseEntity *pTarget ) // By default, aim for the center Vector vecTarget = pTarget->WorldSpaceCenter(); +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_GetActualShootPosition.CanRunInScope(m_ScriptScope)) + { + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { GetBulletOrigin(), ToHScript( pTarget ) }; + if (g_Hook_GetActualShootPosition.Call( m_ScriptScope, &functionReturn, args )) + { + if (functionReturn.m_type == FIELD_VECTOR && functionReturn.m_pVector->LengthSqr() != 0.0f) + return *functionReturn.m_pVector; + } + } +#endif + float flTimeSinceLastMiss = gpGlobals->curtime - m_flTimeLastShotMissed; if( pTarget->GetFlags() & FL_CLIENT ) diff --git a/sp/src/game/server/logic_playmovie.cpp b/sp/src/game/server/logic_playmovie.cpp new file mode 100644 index 00000000..4e62f738 --- /dev/null +++ b/sp/src/game/server/logic_playmovie.cpp @@ -0,0 +1,136 @@ +//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: Plays a movie and reports on finish +// +//===========================================================================// + +#include "cbase.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CLogicPlayMovie : public CLogicalEntity +{ +public: + DECLARE_CLASS( CLogicPlayMovie, CLogicalEntity ); + DECLARE_DATADESC(); + + CLogicPlayMovie( void ) { } + ~CLogicPlayMovie( void ) { } + + virtual void Precache( void ); + virtual void Spawn( void ); + +private: + + void InputPlayMovie( inputdata_t &data ); +#ifdef MAPBASE + void InputStopMovie( inputdata_t &data ); +#endif + void InputMovieFinished( inputdata_t &data ); + + string_t m_strMovieFilename; + bool m_bAllowUserSkip; +#ifdef MAPBASE + bool m_bLooping; + bool m_bMuted; + + bool m_bPlayingVideo; +#endif + + COutputEvent m_OnPlaybackFinished; +}; + +LINK_ENTITY_TO_CLASS( logic_playmovie, CLogicPlayMovie ); + +BEGIN_DATADESC( CLogicPlayMovie ) + + DEFINE_KEYFIELD( m_strMovieFilename, FIELD_STRING, "MovieFilename" ), + DEFINE_KEYFIELD( m_bAllowUserSkip, FIELD_BOOLEAN, "allowskip" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bLooping, FIELD_BOOLEAN, "loopvideo" ), + DEFINE_KEYFIELD( m_bMuted, FIELD_BOOLEAN, "mute" ), + + DEFINE_FIELD( m_bPlayingVideo, FIELD_BOOLEAN ), +#endif + + DEFINE_INPUTFUNC( FIELD_VOID, "PlayMovie", InputPlayMovie ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "StopMovie", InputStopMovie ), +#endif + DEFINE_INPUTFUNC( FIELD_VOID, "__MovieFinished", InputMovieFinished ), + + DEFINE_OUTPUT( m_OnPlaybackFinished, "OnPlaybackFinished" ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::Precache( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::Spawn( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::InputPlayMovie( inputdata_t &data ) +{ + // Build the hacked string + + char szClientCmd[256]; + Q_snprintf( szClientCmd, sizeof(szClientCmd), + "playvideo_complex %s \"ent_fire %s __MovieFinished\" %d %d %d\n", + STRING(m_strMovieFilename), + GetEntityNameAsCStr(), + m_bAllowUserSkip, +#ifdef MAPBASE + m_bLooping, + m_bMuted +#else + 0, + 0 +#endif + ); + + // Send it on + engine->ServerCommand( szClientCmd ); + +#ifdef MAPBASE + m_bPlayingVideo = true; +#endif +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::InputStopMovie( inputdata_t &data ) +{ + if (m_bPlayingVideo) + { + // Send it on + engine->ServerCommand( "stopvideos\n" ); + } +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogicPlayMovie::InputMovieFinished( inputdata_t &data ) +{ + // Simply fire our output + m_OnPlaybackFinished.FireOutput( this, this ); + +#ifdef MAPBASE + m_bPlayingVideo = false; +#endif +} diff --git a/sp/src/game/server/mapbase/vgui_text_display.cpp b/sp/src/game/server/mapbase/vgui_text_display.cpp new file mode 100644 index 00000000..84e0ac63 --- /dev/null +++ b/sp/src/game/server/mapbase/vgui_text_display.cpp @@ -0,0 +1,437 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Displays easy, flexible VGui text. Mapbase equivalent of point_worldtext. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "vguiscreen.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define SF_TESTDISPLAY_START_DISABLED (1 << 0) + +//----------------------------------------------------------------------------- +// vgui_text_display +//----------------------------------------------------------------------------- +class CVGuiTextDisplay : public CBaseEntity +{ +public: + + DECLARE_CLASS( CVGuiTextDisplay, CBaseEntity ); + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + CVGuiTextDisplay(); + virtual ~CVGuiTextDisplay(); + + virtual bool KeyValue( const char *szKeyName, const char *szValue ); + + virtual int UpdateTransmitState(); + virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ); + + virtual void Spawn( void ); + virtual void Precache( void ); + virtual void OnRestore( void ); + + void ScreenVisible( bool bVisible ); + + void Disable( void ); + void Enable( void ); + + void InputDisable( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + void InputToggle( inputdata_t &inputdata ); + + void InputSetMessage( inputdata_t &inputdata ); + void InputSetTextAlignment( inputdata_t &inputdata ); + void InputSetFont( inputdata_t &inputdata ); + void InputSetResolution( inputdata_t &inputdata ); + void InputSetTextSize( inputdata_t &inputdata ); + +private: + + // Control panel + void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ); + void GetControlPanelClassName( int nPanelIndex, const char *&pPanelName ); + void SpawnControlPanels( void ); + void RestoreControlPanels( void ); + +private: + CNetworkVar( bool, m_bEnabled ); + + CNetworkString( m_szDisplayText, 256 ); + CNetworkVar( int, m_iContentAlignment ); + CNetworkString( m_szFont, 64 ); + CNetworkVar( int, m_iResolution ); + float m_flTextSize; + + //CNetworkColor32( m_DisplayColor ); // Use render color + + bool m_bDoFullTransmit; + + CHandle m_hScreen; +}; + +LINK_ENTITY_TO_CLASS( vgui_text_display, CVGuiTextDisplay ); + +//----------------------------------------------------------------------------- +// Save/load +//----------------------------------------------------------------------------- +BEGIN_DATADESC( CVGuiTextDisplay ) + + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + + DEFINE_AUTO_ARRAY_KEYFIELD( m_szDisplayText, FIELD_CHARACTER, "message" ), + DEFINE_KEYFIELD( m_iContentAlignment, FIELD_INTEGER, "alignment" ), + DEFINE_AUTO_ARRAY_KEYFIELD( m_szFont, FIELD_CHARACTER, "font" ), + DEFINE_KEYFIELD( m_iResolution, FIELD_INTEGER, "resolution" ), + DEFINE_KEYFIELD( m_flTextSize, FIELD_FLOAT, "textsize" ), + + DEFINE_FIELD( m_bDoFullTransmit, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_hScreen, FIELD_EHANDLE ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetMessage", InputSetMessage ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetTextAlignment", InputSetTextAlignment ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetFont", InputSetFont ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetResolution", InputSetResolution ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPanelSize", InputSetTextSize ), + +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CVGuiTextDisplay, DT_VGuiTextDisplay ) + SendPropBool( SENDINFO( m_bEnabled ) ), + SendPropString( SENDINFO( m_szDisplayText ) ), + SendPropInt( SENDINFO( m_iContentAlignment ) ), + SendPropString( SENDINFO( m_szFont ) ), + SendPropInt( SENDINFO( m_iResolution ) ), +END_SEND_TABLE() + +CVGuiTextDisplay::CVGuiTextDisplay() +{ + m_flTextSize = 100.0f; + m_iResolution = 200; + m_iContentAlignment = 7; // a_south +} + +CVGuiTextDisplay::~CVGuiTextDisplay() +{ + DestroyVGuiScreen( m_hScreen.Get() ); +} + +//----------------------------------------------------------------------------- +// Read in Hammer data +//----------------------------------------------------------------------------- +bool CVGuiTextDisplay::KeyValue( const char *szKeyName, const char *szValue ) +{ + // NOTE: Have to do these separate because they set two values instead of one + if( FStrEq( szKeyName, "angles" ) ) + { + Assert( GetMoveParent() == NULL ); + QAngle angles; + UTIL_StringToVector( angles.Base(), szValue ); + + // Because the vgui screen basis is strange (z is front, y is up, x is right) + // we need to rotate the typical basis before applying it + VMatrix mat, rotation, tmp; + MatrixFromAngles( angles, mat ); + MatrixBuildRotationAboutAxis( rotation, Vector( 0, 1, 0 ), 90 ); + MatrixMultiply( mat, rotation, tmp ); + MatrixBuildRotateZ( rotation, 90 ); + MatrixMultiply( tmp, rotation, mat ); + MatrixToAngles( mat, angles ); + SetAbsAngles( angles ); + } + else if( FStrEq( szKeyName, "message" ) ) + { + Q_strcpy( m_szDisplayText.GetForModify(), szValue ); + } + else if( FStrEq( szKeyName, "font" ) ) + { + Q_strcpy( m_szFont.GetForModify(), szValue ); + } + else if( FStrEq( szKeyName, "color" ) ) + { + // Use render color + return BaseClass::KeyValue( "rendercolor", szValue ); + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CVGuiTextDisplay::UpdateTransmitState() +{ + if ( m_bDoFullTransmit ) + { + m_bDoFullTransmit = false; + return SetTransmitState( FL_EDICT_ALWAYS ); + } + + return SetTransmitState( FL_EDICT_FULLCHECK ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ) +{ + // Are we already marked for transmission? + if ( pInfo->m_pTransmitEdict->Get( entindex() ) ) + return; + + BaseClass::SetTransmit( pInfo, bAlways ); + + // Force our screen to be sent too. + m_hScreen->SetTransmit( pInfo, bAlways ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::Spawn( void ) +{ + Precache(); + + BaseClass::Spawn(); + + m_bEnabled = !HasSpawnFlags( SF_TESTDISPLAY_START_DISABLED ); + + SpawnControlPanels(); + + ScreenVisible( m_bEnabled ); + + m_bDoFullTransmit = true; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::Precache( void ) +{ + BaseClass::Precache(); + + PrecacheVGuiScreen( "text_display_panel" ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::OnRestore( void ) +{ + BaseClass::OnRestore(); + + RestoreControlPanels(); + + ScreenVisible( m_bEnabled ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::ScreenVisible( bool bVisible ) +{ + // Set its active state + m_hScreen->SetActive( bVisible ); + + if ( bVisible ) + { + m_hScreen->RemoveEffects( EF_NODRAW ); + } + else + { + m_hScreen->AddEffects( EF_NODRAW ); + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::Disable( void ) +{ + if ( !m_bEnabled ) + return; + + m_bEnabled = false; + + ScreenVisible( false ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::Enable( void ) +{ + if ( m_bEnabled ) + return; + + m_bEnabled = true; + + ScreenVisible( true ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputDisable( inputdata_t &inputdata ) +{ + Disable(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputEnable( inputdata_t &inputdata ) +{ + Enable(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputToggle( inputdata_t &inputdata ) +{ + m_bEnabled ? Disable() : Enable(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputSetMessage( inputdata_t &inputdata ) +{ + Q_strcpy( m_szDisplayText.GetForModify(), inputdata.value.String() ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputSetTextAlignment( inputdata_t &inputdata ) +{ + m_iContentAlignment = inputdata.value.Int(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputSetFont( inputdata_t &inputdata ) +{ + Q_strcpy( m_szFont.GetForModify(), inputdata.value.String() ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputSetResolution( inputdata_t &inputdata ) +{ + m_iResolution = inputdata.value.Int(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::InputSetTextSize( inputdata_t &inputdata ) +{ + m_flTextSize = inputdata.value.Float(); + + if (m_hScreen) + { + m_hScreen->SetActualSize( m_flTextSize, m_flTextSize ); + m_hScreen->SetLocalOrigin( m_hScreen->CollisionProp()->OBBCenter() * -1.0f ); + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ) +{ + pPanelName = "text_display_panel"; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName ) +{ + pPanelName = "vgui_screen"; +} + +//----------------------------------------------------------------------------- +// This is called by the base object when it's time to spawn the control panels +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::SpawnControlPanels() +{ + int nPanel; + for ( nPanel = 0; true; ++nPanel ) + { + const char *pScreenName; + GetControlPanelInfo( nPanel, pScreenName ); + if (!pScreenName) + continue; + + const char *pScreenClassname; + GetControlPanelClassName( nPanel, pScreenClassname ); + if ( !pScreenClassname ) + continue; + + float flWidth = m_flTextSize; + float flHeight = m_flTextSize; + + CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, this, this, 0 ); + pScreen->ChangeTeam( GetTeamNumber() ); + pScreen->SetActualSize( flWidth, flHeight ); + pScreen->SetLocalOrigin( pScreen->CollisionProp()->OBBCenter() * -1.0f ); + pScreen->SetActive( true ); + pScreen->MakeVisibleOnlyToTeammates( false ); + pScreen->SetTransparency( true ); + m_hScreen = pScreen; + + return; + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CVGuiTextDisplay::RestoreControlPanels( void ) +{ + int nPanel; + for ( nPanel = 0; true; ++nPanel ) + { + const char *pScreenName; + GetControlPanelInfo( nPanel, pScreenName ); + if (!pScreenName) + continue; + + const char *pScreenClassname; + GetControlPanelClassName( nPanel, pScreenClassname ); + if ( !pScreenClassname ) + continue; + + CVGuiScreen *pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( NULL, pScreenClassname ); + + while ( ( pScreen && pScreen->GetOwnerEntity() != this ) || Q_strcmp( pScreen->GetPanelName(), pScreenName ) != 0 ) + { + pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( pScreen, pScreenClassname ); + } + + if ( pScreen ) + { + m_hScreen = pScreen; + m_hScreen->SetActive( true ); + } + + return; + } +} diff --git a/sp/src/game/server/movie_display.cpp b/sp/src/game/server/movie_display.cpp new file mode 100644 index 00000000..5c31fb23 --- /dev/null +++ b/sp/src/game/server/movie_display.cpp @@ -0,0 +1,372 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: Allows movies to be played as a VGUI screen in the world +// +//=====================================================================================// + +#include "cbase.h" +#include "EnvMessage.h" +#include "fmtstr.h" +#include "vguiscreen.h" +#include "filesystem.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +class CMovieDisplay : public CBaseEntity +{ +public: + + DECLARE_CLASS( CMovieDisplay, CBaseEntity ); + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + virtual ~CMovieDisplay(); + + virtual bool KeyValue( const char *szKeyName, const char *szValue ); + + virtual int UpdateTransmitState(); + virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ); + + virtual void Spawn( void ); + virtual void Precache( void ); + virtual void OnRestore( void ); + + void ScreenVisible( bool bVisible ); + + void Disable( void ); + void Enable( void ); + + void InputDisable( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + + void InputSetDisplayText( inputdata_t &inputdata ); + +private: + + // Control panel + void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ); + void GetControlPanelClassName( int nPanelIndex, const char *&pPanelName ); + void SpawnControlPanels( void ); + void RestoreControlPanels( void ); + +private: + CNetworkVar( bool, m_bEnabled ); + CNetworkVar( bool, m_bLooping ); + + CNetworkString( m_szDisplayText, 128 ); + + // Filename of the movie to play + CNetworkString( m_szMovieFilename, 128 ); + string_t m_strMovieFilename; + + // "Group" name. Screens of the same group name will play the same movie at the same time + // Effectively this lets multiple screens tune to the same "channel" in the world + CNetworkString( m_szGroupName, 128 ); + string_t m_strGroupName; + + int m_iScreenWidth; + int m_iScreenHeight; + + bool m_bDoFullTransmit; + + CHandle m_hScreen; +}; + +LINK_ENTITY_TO_CLASS( vgui_movie_display, CMovieDisplay ); + +//----------------------------------------------------------------------------- +// Save/load +//----------------------------------------------------------------------------- +BEGIN_DATADESC( CMovieDisplay ) + + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + + DEFINE_AUTO_ARRAY_KEYFIELD( m_szDisplayText, FIELD_CHARACTER, "displaytext" ), + + DEFINE_AUTO_ARRAY( m_szMovieFilename, FIELD_CHARACTER ), + DEFINE_KEYFIELD( m_strMovieFilename, FIELD_STRING, "moviefilename" ), + + DEFINE_AUTO_ARRAY( m_szGroupName, FIELD_CHARACTER ), + DEFINE_KEYFIELD( m_strGroupName, FIELD_STRING, "groupname" ), + + DEFINE_KEYFIELD( m_iScreenWidth, FIELD_INTEGER, "width" ), + DEFINE_KEYFIELD( m_iScreenHeight, FIELD_INTEGER, "height" ), + DEFINE_KEYFIELD( m_bLooping, FIELD_BOOLEAN, "looping" ), + + DEFINE_FIELD( m_bDoFullTransmit, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_hScreen, FIELD_EHANDLE ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetDisplayText", InputSetDisplayText ), + +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CMovieDisplay, DT_MovieDisplay ) + SendPropBool( SENDINFO( m_bEnabled ) ), + SendPropBool( SENDINFO( m_bLooping ) ), + SendPropString( SENDINFO( m_szMovieFilename ) ), + SendPropString( SENDINFO( m_szGroupName ) ), +END_SEND_TABLE() + +CMovieDisplay::~CMovieDisplay() +{ + DestroyVGuiScreen( m_hScreen.Get() ); +} + +//----------------------------------------------------------------------------- +// Read in Hammer data +//----------------------------------------------------------------------------- +bool CMovieDisplay::KeyValue( const char *szKeyName, const char *szValue ) +{ + // NOTE: Have to do these separate because they set two values instead of one + if( FStrEq( szKeyName, "angles" ) ) + { + Assert( GetMoveParent() == NULL ); + QAngle angles; + UTIL_StringToVector( angles.Base(), szValue ); + + // Because the vgui screen basis is strange (z is front, y is up, x is right) + // we need to rotate the typical basis before applying it + VMatrix mat, rotation, tmp; + MatrixFromAngles( angles, mat ); + MatrixBuildRotationAboutAxis( rotation, Vector( 0, 1, 0 ), 90 ); + MatrixMultiply( mat, rotation, tmp ); + MatrixBuildRotateZ( rotation, 90 ); + MatrixMultiply( tmp, rotation, mat ); + MatrixToAngles( mat, angles ); + SetAbsAngles( angles ); + + return true; + } + + return BaseClass::KeyValue( szKeyName, szValue ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CMovieDisplay::UpdateTransmitState() +{ + if ( m_bDoFullTransmit ) + { + m_bDoFullTransmit = false; + return SetTransmitState( FL_EDICT_ALWAYS ); + } + + return SetTransmitState( FL_EDICT_FULLCHECK ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ) +{ + // Are we already marked for transmission? + if ( pInfo->m_pTransmitEdict->Get( entindex() ) ) + return; + + BaseClass::SetTransmit( pInfo, bAlways ); + + // Force our screen to be sent too. + m_hScreen->SetTransmit( pInfo, bAlways ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::Spawn( void ) +{ + // Move the strings into a networkable form + Q_strcpy( m_szMovieFilename.GetForModify(), m_strMovieFilename.ToCStr() ); + Q_strcpy( m_szGroupName.GetForModify(), m_strGroupName.ToCStr() ); + + Precache(); + + BaseClass::Spawn(); + + m_bEnabled = false; + + SpawnControlPanels(); + + ScreenVisible( m_bEnabled ); + + m_bDoFullTransmit = true; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::Precache( void ) +{ + BaseClass::Precache(); + + PrecacheVGuiScreen( "video_display_screen" ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::OnRestore( void ) +{ + BaseClass::OnRestore(); + + RestoreControlPanels(); + + ScreenVisible( m_bEnabled ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::ScreenVisible( bool bVisible ) +{ + // Set its active state + m_hScreen->SetActive( bVisible ); + + if ( bVisible ) + { + m_hScreen->RemoveEffects( EF_NODRAW ); + } + else + { + m_hScreen->AddEffects( EF_NODRAW ); + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::Disable( void ) +{ + if ( !m_bEnabled ) + return; + + m_bEnabled = false; + + ScreenVisible( false ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::Enable( void ) +{ + if ( m_bEnabled ) + return; + + m_bEnabled = true; + + ScreenVisible( true ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::InputDisable( inputdata_t &inputdata ) +{ + Disable(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::InputEnable( inputdata_t &inputdata ) +{ + Enable(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::InputSetDisplayText( inputdata_t &inputdata ) +{ + Q_strcpy( m_szDisplayText.GetForModify(), inputdata.value.String() ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ) +{ + pPanelName = "movie_display_screen"; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName ) +{ + pPanelName = "vgui_screen"; +} + +//----------------------------------------------------------------------------- +// This is called by the base object when it's time to spawn the control panels +//----------------------------------------------------------------------------- +void CMovieDisplay::SpawnControlPanels() +{ + int nPanel; + for ( nPanel = 0; true; ++nPanel ) + { + const char *pScreenName; + GetControlPanelInfo( nPanel, pScreenName ); + if (!pScreenName) + continue; + + const char *pScreenClassname; + GetControlPanelClassName( nPanel, pScreenClassname ); + if ( !pScreenClassname ) + continue; + + float flWidth = m_iScreenWidth; + float flHeight = m_iScreenHeight; + + CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, this, this, 0 ); + pScreen->ChangeTeam( GetTeamNumber() ); + pScreen->SetActualSize( flWidth, flHeight ); + pScreen->SetActive( true ); + pScreen->MakeVisibleOnlyToTeammates( false ); + pScreen->SetTransparency( true ); + m_hScreen = pScreen; + + return; + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMovieDisplay::RestoreControlPanels( void ) +{ + int nPanel; + for ( nPanel = 0; true; ++nPanel ) + { + const char *pScreenName; + GetControlPanelInfo( nPanel, pScreenName ); + if (!pScreenName) + continue; + + const char *pScreenClassname; + GetControlPanelClassName( nPanel, pScreenClassname ); + if ( !pScreenClassname ) + continue; + + CVGuiScreen *pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( NULL, pScreenClassname ); + + while ( ( pScreen && pScreen->GetOwnerEntity() != this ) || Q_strcmp( pScreen->GetPanelName(), pScreenName ) != 0 ) + { + pScreen = (CVGuiScreen *)gEntList.FindEntityByClassname( pScreen, pScreenClassname ); + } + + if ( pScreen ) + { + m_hScreen = pScreen; + m_hScreen->SetActive( true ); + } + + return; + } +} diff --git a/sp/src/game/server/player.cpp b/sp/src/game/server/player.cpp index 2c57cc36..0f26c252 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/point_spotlight.cpp b/sp/src/game/server/point_spotlight.cpp index 5a854557..d5901714 100644 --- a/sp/src/game/server/point_spotlight.cpp +++ b/sp/src/game/server/point_spotlight.cpp @@ -75,6 +75,12 @@ private: float m_flHDRColorScale; int m_nMinDXLevel; +#ifdef MAPBASE + float m_flHaloScale; + string_t m_iszHaloMaterial; + string_t m_iszSpotlightMaterial; +#endif + public: COutputEvent m_OnOn, m_OnOff; ///< output fires when turned on, off }; @@ -98,6 +104,11 @@ BEGIN_DATADESC( CPointSpotlight ) DEFINE_KEYFIELD( m_flSpotlightGoalWidth,FIELD_FLOAT, "SpotlightWidth"), DEFINE_KEYFIELD( m_flHDRColorScale, FIELD_FLOAT, "HDRColorScale" ), DEFINE_KEYFIELD( m_nMinDXLevel, FIELD_INTEGER, "mindxlevel" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flHaloScale, FIELD_FLOAT, "HaloScale" ), + DEFINE_KEYFIELD( m_iszHaloMaterial, FIELD_STRING, "HaloMaterial" ), + DEFINE_KEYFIELD( m_iszSpotlightMaterial, FIELD_STRING, "SpotlightMaterial" ), +#endif // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "LightOn", InputLightOn ), @@ -127,6 +138,9 @@ CPointSpotlight::CPointSpotlight() #endif m_flHDRColorScale = 1.0f; m_nMinDXLevel = 0; +#ifdef MAPBASE + m_flHaloScale = 60.0f; +#endif } #ifdef MAPBASE @@ -148,8 +162,23 @@ void CPointSpotlight::Precache(void) BaseClass::Precache(); // Sprites. +#ifdef MAPBASE + if (m_iszHaloMaterial == NULL_STRING) + { + m_iszHaloMaterial = AllocPooledString( "sprites/light_glow03.vmt" ); + } + + if (m_iszSpotlightMaterial == NULL_STRING) + { + m_iszSpotlightMaterial = AllocPooledString( "sprites/glow_test02.vmt" ); + } + + m_nHaloSprite = PrecacheModel( STRING( m_iszHaloMaterial ) ); + PrecacheModel( STRING( m_iszSpotlightMaterial ) ); +#else m_nHaloSprite = PrecacheModel("sprites/light_glow03.vmt"); PrecacheModel( "sprites/glow_test02.vmt" ); +#endif } @@ -367,13 +396,21 @@ void CPointSpotlight::SpotlightCreate(void) } //m_hSpotlight = CBeam::BeamCreate( "sprites/spotlight.vmt", m_flSpotlightGoalWidth ); +#ifdef MAPBASE + m_hSpotlight = CBeam::BeamCreate( STRING(m_iszSpotlightMaterial), m_flSpotlightGoalWidth ); +#else m_hSpotlight = CBeam::BeamCreate( "sprites/glow_test02.vmt", m_flSpotlightGoalWidth ); +#endif // Set the temporary spawnflag on the beam so it doesn't save (we'll recreate it on restore) m_hSpotlight->SetHDRColorScale( m_flHDRColorScale ); m_hSpotlight->AddSpawnFlags( SF_BEAM_TEMPORARY ); m_hSpotlight->SetColor( m_clrRender->r, m_clrRender->g, m_clrRender->b ); m_hSpotlight->SetHaloTexture(m_nHaloSprite); +#ifdef MAPBASE + m_hSpotlight->SetHaloScale(m_flHaloScale); +#else m_hSpotlight->SetHaloScale(60); +#endif m_hSpotlight->SetEndWidth(m_flSpotlightGoalWidth); m_hSpotlight->SetBeamFlags( (FBEAM_SHADEOUT|FBEAM_NOTILE) ); m_hSpotlight->SetBrightness( 64 ); diff --git a/sp/src/game/server/sceneentity.cpp b/sp/src/game/server/sceneentity.cpp index 6914fd19..0437c280 100644 --- a/sp/src/game/server/sceneentity.cpp +++ b/sp/src/game/server/sceneentity.cpp @@ -3461,7 +3461,10 @@ void CSceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEv if ( IsMultiplayer() ) break; - DispatchStartPermitResponses( scene, pActor, event ); + if ( pActor ) + { + DispatchStartPermitResponses( scene, pActor, event ); + } } break; default: @@ -3619,7 +3622,10 @@ void CSceneEntity::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEven if ( IsMultiplayer() ) break; - DispatchEndPermitResponses( scene, pActor, event ); + if ( pActor ) + { + DispatchEndPermitResponses( scene, pActor, event ); + } } break; default: diff --git a/sp/src/game/server/server_mapbase.vpc b/sp/src/game/server/server_mapbase.vpc index 37146238..6b2ec779 100644 --- a/sp/src/game/server/server_mapbase.vpc +++ b/sp/src/game/server/server_mapbase.vpc @@ -27,6 +27,8 @@ $Project $File "postprocesscontroller.h" $File "env_dof_controller.cpp" $File "env_dof_controller.h" + $File "logic_playmovie.cpp" + $File "movie_display.cpp" $Folder "Mapbase" { @@ -74,6 +76,7 @@ $Project $File "mapbase\SystemConvarMod.cpp" $File "mapbase\SystemConvarMod.h" $File "mapbase\variant_tools.h" + $File "mapbase\vgui_text_display.cpp" $File "mapbase\logic_eventlistener.cpp" $File "mapbase\logic_register_activator.cpp" diff --git a/sp/src/game/server/vscript_server.cpp b/sp/src/game/server/vscript_server.cpp index ebab60e6..6927b0f1 100644 --- a/sp/src/game/server/vscript_server.cpp +++ b/sp/src/game/server/vscript_server.cpp @@ -110,6 +110,11 @@ public: { 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 deecea7b..deeacf5d 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 390b5ca4..e1072b78 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 f07f3775..cae3f5d9 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 9e8562f9..5aff896f 100644 --- a/sp/src/game/shared/mapbase/mapbase_shared.cpp +++ b/sp/src/game/shared/mapbase/mapbase_shared.cpp @@ -71,7 +71,7 @@ ConVar mapbase_load_actbusy("mapbase_load_actbusy", "1", FCVAR_ARCHIVE, "Should #ifdef GAME_DLL // This cvar should change with each Mapbase update -ConVar mapbase_version( "mapbase_version", "6.2", FCVAR_NONE, "The version of Mapbase currently being used in this mod." ); +ConVar mapbase_version( "mapbase_version", "6.3", FCVAR_NONE, "The version of Mapbase currently being used in this mod." ); extern void MapbaseGameLog_Init(); @@ -178,7 +178,7 @@ public: #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 @@ public: 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 @@ -212,8 +212,11 @@ public: RefreshMapName(); - // Shared Mapbase localization file + // Shared Mapbase scripts to avoid overwriting mod files g_pVGuiLocalize->AddFile( "resource/mapbase_%language%.txt" ); +#ifdef CLIENT_DLL + PanelMetaClassMgr()->LoadMetaClassDefinitionFile( "scripts/vgui_screens_mapbase.txt" ); +#endif } virtual void OnRestore() @@ -352,11 +355,11 @@ public: 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(); } @@ -553,6 +556,11 @@ CUtlVector *Mapbase_GetChapterList() return &g_MapbaseChapterList; } +int Mapbase_GetChapterCount() +{ + return g_MapbaseChapterList.Count(); +} + ThreeState_t Flashlight_GetLegacyVersionKey() { KeyValues *gameinfo = new KeyValues( "GameInfo" ); @@ -591,7 +599,7 @@ public: 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 b03ee0e8..31341fb9 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 ae927a81..fed3ea1b 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp @@ -26,6 +26,7 @@ #include "con_nprint.h" #include "particle_parse.h" +#include "npcevent.h" #include "vscript_funcs_shared.h" #include "vscript_singletons.h" @@ -164,18 +165,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; } //----------------------------------------------------------------------------- @@ -501,6 +502,61 @@ FireBulletsInfo_t *GetFireBulletsInfoFromInfo( HSCRIPT hBulletsInfo ) return HScriptToClass( hBulletsInfo ); } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +CAnimEventTInstanceHelper g_AnimEventTInstanceHelper; + +BEGIN_SCRIPTDESC_ROOT( animevent_t, "Handle for accessing animevent_t info." ) + DEFINE_SCRIPT_INSTANCE_HELPER( &g_AnimEventTInstanceHelper ) +END_SCRIPTDESC(); + +bool CAnimEventTInstanceHelper::Get( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + animevent_t *ani = ((animevent_t *)p); + if (FStrEq( pszKey, "event" )) + variant = ani->event; + else if (FStrEq( pszKey, "options" )) + variant = ani->options; + else if (FStrEq( pszKey, "cycle" )) + variant = ani->cycle; + else if (FStrEq( pszKey, "eventtime" )) + variant = ani->eventtime; + else if (FStrEq( pszKey, "type" )) + variant = ani->type; + else if (FStrEq( pszKey, "source" )) + variant = ToHScript(ani->pSource); + else + return false; + + return true; +} + +bool CAnimEventTInstanceHelper::Set( void *p, const char *pszKey, ScriptVariant_t &variant ) +{ + animevent_t *ani = ((animevent_t *)p); + if (FStrEq( pszKey, "event" )) + ani->event = variant; + else if (FStrEq( pszKey, "options" )) + ani->options = variant; + else if (FStrEq( pszKey, "cycle" )) + ani->cycle = variant; + else if (FStrEq( pszKey, "eventtime" )) + ani->eventtime = variant; + else if (FStrEq( pszKey, "type" )) + ani->type = variant; + else if (FStrEq( pszKey, "source" )) + { + CBaseEntity *pEnt = ToEnt( variant.m_hScript ); + if (pEnt) + ani->pSource = pEnt->GetBaseAnimating(); + } + else + return false; + + return true; +} + //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- @@ -740,20 +796,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); - - // delete info; + engine->Con_NXPrintf( &info, fmt ); } +static float IntervalPerTick() +{ + return gpGlobals->interval_per_tick; +} + +static int GetFrameCount() +{ + return gpGlobals->framecount; +} + + //============================================================================= //============================================================================= @@ -850,6 +915,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_funcs_shared.h b/sp/src/game/shared/mapbase/vscript_funcs_shared.h index ad3192bf..0a38e5a1 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_shared.h +++ b/sp/src/game/shared/mapbase/vscript_funcs_shared.h @@ -121,4 +121,13 @@ private: HSCRIPT m_planeAccessor; }; +//----------------------------------------------------------------------------- +// Exposes animevent_t to VScript +//----------------------------------------------------------------------------- +class CAnimEventTInstanceHelper : public IScriptInstanceHelper +{ + bool Get( void *p, const char *pszKey, ScriptVariant_t &variant ); + bool Set( void *p, const char *pszKey, ScriptVariant_t &variant ); +}; + #endif diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index e001513e..c5e51499 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,89 +390,78 @@ public: 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 -CUtlMap< unsigned int, KeyValues* > CScriptGameEventListener::s_GameEvents( DefLessFunc(unsigned int) ); -#endif -CUtlVectorAutoPurge< CScriptGameEventListener* > CScriptGameEventListener::s_GameEventListeners; -CUtlVector< KeyValues* > CScriptGameEventListener::s_LoadedFiles; +CUtlVectorAutoPurge< CScriptGameEventListener* > CScriptGameEventListener::s_Listeners; StringHashFunctor CScriptGameEventListener::Hash; +#ifdef USE_OLD_EVENT_DESCRIPTORS +CUtlMap< unsigned int, KeyValues* > CScriptGameEventListener::s_GameEvents( DefLessFunc(unsigned int) ); +CUtlVector< KeyValues* > CScriptGameEventListener::s_LoadedFiles; +#endif + + #if _DEBUG #ifdef CLIENT_DLL CON_COMMAND_F( cl_dump_script_game_event_listeners, "Dump all game event listeners created from script.", FCVAR_CHEAT ) { CScriptGameEventListener::DumpEventListeners(); } -#else // GAME_DLL +#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 #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, 0 ); + 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, 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 @@ -1120,7 +1228,7 @@ void CNetMsgScriptHelper::Reset() } //----------------------------------------------------------------------------- -// Create the storage for the reciever callback functions. +// Create the storage for the receiver callback functions. // Functions are handled in the VM, the storage table is here. //----------------------------------------------------------------------------- void CNetMsgScriptHelper::InitPostVM() @@ -1156,7 +1264,7 @@ bool CNetMsgScriptHelper::Init() // IGameSystem //----------------------------------------------------------------------------- void CNetMsgScriptHelper::__MsgFunc_ScriptMsg( bf_read &msg ) { - g_ScriptNetMsg->RecieveMessage( msg ); + g_ScriptNetMsg->ReceiveMessage( msg ); } #endif // CLIENT_DLL @@ -1165,11 +1273,11 @@ void CNetMsgScriptHelper::__MsgFunc_ScriptMsg( bf_read &msg ) // //----------------------------------------------------------------------------- #ifdef GAME_DLL -void CNetMsgScriptHelper::RecieveMessage( bf_read *msg, CBaseEntity *pPlayer ) +void CNetMsgScriptHelper::ReceiveMessage( bf_read *msg, CBaseEntity *pPlayer ) { m_MsgIn = msg; #else -void CNetMsgScriptHelper::RecieveMessage( bf_read &msg ) +void CNetMsgScriptHelper::ReceiveMessage( bf_read &msg ) { m_MsgIn.StartReading( msg.m_pData, msg.m_nDataBytes ); #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 @@ -1247,7 +1357,7 @@ void CNetMsgScriptHelper::Send() //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -void CNetMsgScriptHelper::Recieve( const char *msg, HSCRIPT func ) +void CNetMsgScriptHelper::Receive( const char *msg, HSCRIPT func ) { if ( func ) g_pScriptVM->SetValue( m_Hooks, int( HashStringCaseless(msg) ), func ); @@ -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, "" ) @@ -1592,28 +1701,29 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "N DEFINE_SCRIPTFUNC( Reset, "Reset the current network message buffer" ) DEFINE_SCRIPTFUNC( Start, "Start writing new custom network message" ) - DEFINE_SCRIPTFUNC( Recieve, "Set custom network message callback" ) + DEFINE_SCRIPTFUNC( Receive, "Set custom network message callback" ) + DEFINE_SCRIPTFUNC_NAMED( Receive, "Recieve", SCRIPT_HIDE ) // This was a typo until v6.3 #ifdef GAME_DLL DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the server to the client (max 252 bytes)" ) #else 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 +1742,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 +1755,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 +2161,651 @@ 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_hCompletionCallback = NULL; + 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" ); + + AddOverridable( "save" ); + AddOverridable( "load" ); + + + 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 +2828,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 +2840,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 a3b94f9b..a18d4a38 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) {} @@ -58,9 +61,9 @@ public: void InitPostVM(); #ifdef GAME_DLL - void RecieveMessage( bf_read *msg, CBaseEntity *pPlayer ); + void ReceiveMessage( bf_read *msg, CBaseEntity *pPlayer ); #else - void RecieveMessage( bf_read &msg ); + void ReceiveMessage( bf_read &msg ); #endif void WriteToBuffer( bf_write *bf ); @@ -72,7 +75,7 @@ public: #else void Send(); #endif - void Recieve( const char *msg, HSCRIPT func ); + void Receive( const char *msg, HSCRIPT func ); #ifdef GAME_DLL inline void DoSendUserMsg( CRecipientFilter *filter, int type ); @@ -100,14 +103,14 @@ public: 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 90630a65..f1493ce0 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/usercmd.cpp b/sp/src/game/shared/usercmd.cpp index 032b366c..1607eba3 100644 --- a/sp/src/game/shared/usercmd.cpp +++ b/sp/src/game/shared/usercmd.cpp @@ -335,7 +335,7 @@ void ReadUsercmd( bf_read *buf, CUserCmd *move, CUserCmd *from ) #if defined( MAPBASE_VSCRIPT ) && defined( GAME_DLL ) if ( buf->ReadOneBit() ) { - g_ScriptNetMsg->RecieveMessage( buf, pPlayer ); + g_ScriptNetMsg->ReceiveMessage( buf, pPlayer ); } #endif diff --git a/sp/src/game/shared/vscript_shared.cpp b/sp/src/game/shared/vscript_shared.cpp index c460a48a..86e220b4 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 2c364727..314ee011 100644 --- a/sp/src/public/tier1/convar.h +++ b/sp/src/public/tier1/convar.h @@ -301,6 +301,10 @@ private: ICommandCallback *m_pCommandCallback; }; +#ifdef MAPBASE_VSCRIPT + // Allow late modification of the completion callback. +public: +#endif union { FnCommandCompletionCallback m_fnCompletionCallback; @@ -308,6 +312,9 @@ private: }; 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 c0e35626..cdd955c9 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 a1c1e856..4b3388df 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 @@ public: #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 533d5791..cb01280e 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/vbsp.cpp b/sp/src/utils/vbsp/vbsp.cpp index 11e1547c..21b9db84 100644 --- a/sp/src/utils/vbsp/vbsp.cpp +++ b/sp/src/utils/vbsp/vbsp.cpp @@ -1247,7 +1247,7 @@ int RunVBSP( int argc, char **argv ) } char szCommand[512]; - _snprintf( szCommand, sizeof( szCommand ), "PrintHelp( \"%s\" );", pszArg1 ); + _snprintf( szCommand, sizeof( szCommand ), "__Documentation.PrintHelp( \"%s\" );", pszArg1 ); g_pScriptVM->Run( szCommand ); } else diff --git a/sp/src/utils/vbsp/vscript_funcs_vmfs.cpp b/sp/src/utils/vbsp/vscript_funcs_vmfs.cpp index 3894ddf3..8ef38c81 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 91fa41f6..c8295e02 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 56da8435..9511efa4 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 a2d9fb9a..2629aada 100644 --- a/sp/src/vscript/vscript_bindings_base.h +++ b/sp/src/vscript/vscript_bindings_base.h @@ -35,6 +35,7 @@ public: // 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 cb6c594d..2f189265 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 2c80041b..af605ea1 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -214,7 +214,7 @@ public: 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,10 +450,221 @@ 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) { Vector* v1 = nullptr; @@ -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; + 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); - 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); - - 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 cb929807..f12d385b 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");